Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add docstrings #58

Merged
merged 4 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 81 additions & 1 deletion fastapi_events/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,87 @@ def dispatch(
payload_schema_dump: bool = True
) -> None:
"""
A wrapper of the main dispatcher function with additional checks.
Dispatches an event. This is a wrapper of the main dispatcher function with additional checks.

### Args

:param event_name_or_model: Event name or pydantic model. If `event_name` is not provided, \
the event name will be derived from the model's `__event_name__` attribute.
:param payload: Event payload.
:param event_name: Event name. If provided, overrides the event name derived from the event model.
:param validate_payload: Validate payload with its registered payload schema. Pydantic payloads \
are dumped and re-instantiated.
:param payload_schema_cls_dict_args: A dict of args passed to the pydantic dump function. Defaults \
to `{"exclude_unset": True}`. See \
[pydantic v1](https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict) or \
[pydantic v2](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_dump) \
docs for more details.
:param payload_schema_registry: Optional payload schema registry. If omitted, the default registry \
will be used.
:param middleware_id: Optional custom middleware identifier.
:param payload_schema_dump: Dump pydantic model payloads and validated payloads to dict before \
calling event handlers.

### Exceptions

:raises MultiplePayloadsDetectedDuringDispatch: If `event_name_or_model` is a pydantic model and \
`payload` is provided.
:raises MissingEventNameDuringDispatch: If `event_name` is not provided and the event model does \
not have an `__event_name__` attribute.

### Examples

Events have a name and optional payload. The payload can be any python object.
```python
from fastapi_events.dispatcher import dispatch

# With a string event name and dict payload (payload has no validation)
dispatch("user_created", {"user_id": 1})
```

Register a payload schema with the event name to validate the payload.
```python
from fastapi_events.registry.payload_schema import registry

@registry.register("user_created")
class UserCreated(pydantic.BaseModel):
user_id: int

# The payload will be validated with the registered payload schema
dispatch("user_created", {"user_id": 1})
```

Alternatively, the payload can be a pydantic model instance. The payload will be dumped to dict \
before being passed to handlers.
- If `validate_payload` is True, it will be re-instantiated using the registered payload schema \
before being dumped to dict again.
- If `payload_schema_dump` is False, the payload will be passed to handlers as an instance of the \
payload schema.
- Set both to false if you want to pass the payload instance untouched to the handlers.

```python
dispatch("user_created", UserCreated(user_id=1))
```

If a pydantic model is passed as the payload, the event name will be derived from the model's \
`__event_name__` attribute.
```python
class UserCreated(pydantic.BaseModel):
__event_name__ = "user_created"
user_id: int

dispatch(UserCreated(user_id=1))
```

Providing an explicit event name will override the derived event name from the model.
```python
class UserCreated(pydantic.BaseModel):
__event_name__ = "user_created"
user_id: int

# `__event_name__` will be overridden, and only handlers for "user_updated" will be called.
dispatch(UserCreated(user_id=1), event_name="user_updated")
```
"""
# Handle invalid arguments
_check_for_multiple_payloads(event_name_or_model=event_name_or_model, payload=payload)
Expand Down
20 changes: 20 additions & 0 deletions fastapi_events/handlers/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,26 @@ def __init__(self):
self._registry = {}

def register(self, _func=None, event_name="*"):
"""
Register a handler for an event. The handler will receive a tuple of event name and payload as its only argument.

### Args

:param _func: The function to be registered as a handler. Typically, you would use `register` as a decorator and omit this argument.
:param event_name: The name of the event to be associated with the handler. Use "*", the default value, to match all events.

### Examples

Register a handler as a decorator:
```python
from fastapi_events.handlers.local import local_handler
from fastapi_events.typing import Event

@local_handler.register(event_name="my_event")
async def my_event_handler(event: Event):
event_name, payload = event
print(f"Received event {event_name} with payload {payload}") ```
"""
def _wrap(func):
self._register_handler(event_name, func)
return func
Expand Down
36 changes: 36 additions & 0 deletions fastapi_events/registry/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,42 @@ class BaseEventPayloadSchemaRegistry(UserDict, metaclass=ABCMeta):
"""

def register(self, _schema=None, event_name=None):
"""
Registers a payload schema, used to validate event payload during dispatch.
Schemas must be a subclass of `pydantic.BaseModel`.

### Args

:param _schema: A Pydantic schema to be registered. Typically, you would use `register` as a decorator and omit this argument.
:param event_name: The name of the event to be associated with the schema. If provided, overrides the `__event_name__` attribute of the schema.

### Exceptions

:raises MissingEventNameDuringRegistration: If `event_name` is not provided and the schema does not have an `__event_name__` attribute.
:raises AssertionError: If the schema is not a subclass of `pydantic.BaseModel`.

### Examples

Provide an event name as a decorator argument:
```python
from fastapi_events.registry.payload_schema import registry

@registry.register(event_name="my_event")
class MyEventPayloadSchema(pydantic.BaseModel):
id: int
name: str
```

Or use the `__event_name__` attribute in the schema to have the event name inferred from the schema itself:
```python
@registry.register
class MyEventPayloadSchema(pydantic.BaseModel):
__event_name__ = "my_event"
id: int
name: str
```
"""

def _derive_event_name(_schema):
"""
this modifies `event_name` in the scope
Expand Down
Loading