Skip to content

Commit

Permalink
Merge pull request #31 from volfpeter/generic-RequestComponentSelecto…
Browse files Browse the repository at this point in the history
…r-protocol

Generic RequestComponentSelector protocol
  • Loading branch information
volfpeter authored Sep 3, 2024
2 parents 154a064 + 28bcfcd commit 256b12f
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 17 deletions.
5 changes: 4 additions & 1 deletion docs/migrations/1-to-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@

Version 2 added exception rendering as the main feature, which required some API changes. Here is how you should upgrade your code to be compatible with version 2:

- If you have custom `RequestComponentSelector` implementations, then please add the new argument to the `get_component_id()` method. Well-behaved `RequestComponentSelector`s that don's support error rendering should reraise the received error if it's not `None` (although not doing so will not break anything as result and errors are clearly separated in `Jinja` and the core decorators don't rely on this protocol).
- If you have custom `RequestComponentSelector` implementations:
- Rename `get_component_id()` methods to `get_component()` and add the new `error: Exception | None` argument to the methods.
- Well-behaved `RequestComponentSelector`s that dont's support error rendering should reraise the received error if it's not `None` (although not doing so will not break anything as result and errors are clearly separated in `Jinja` and the core decorators don't rely on this protocol).
- If you've overridden any of the protected methods of `Jinja`, please go through this PR so you can upgrade your custom implementation.
- If you've written a custom integration, just add the required generic type to ComponentSelector type hints.
18 changes: 9 additions & 9 deletions fasthx/jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ class TemplateHeader:
This class can also handle route errors if the `error` property is set.
Implements:
- `RequestComponentSelector`.
- `RequestComponentSelector[str]`.
"""

header: str
Expand All @@ -235,7 +235,7 @@ def __post_init__(self) -> None:
{k.lower(): v for k, v in self.templates.items()},
)

def get_component_id(self, request: Request, error: Exception | None) -> str:
def get_component(self, request: Request, error: Exception | None) -> str:
"""
Returns the name of the template that was requested by the client.
Expand Down Expand Up @@ -275,9 +275,9 @@ class Jinja:

def hx(
self,
template: ComponentSelector,
template: ComponentSelector[str],
*,
error_template: ComponentSelector | None = None,
error_template: ComponentSelector[str] | None = None,
no_data: bool = False,
make_context: JinjaContextFactory | None = None,
prefix: str | None = None,
Expand Down Expand Up @@ -309,9 +309,9 @@ def hx(

def page(
self,
template: ComponentSelector,
template: ComponentSelector[str],
*,
error_template: ComponentSelector | None = None,
error_template: ComponentSelector[str] | None = None,
make_context: JinjaContextFactory | None = None,
prefix: str | None = None,
) -> Callable[[MaybeAsyncFunc[P, Any]], Callable[P, Coroutine[None, None, Any | Response]]]:
Expand All @@ -338,7 +338,7 @@ def page(

def _make_render_function(
self,
template: ComponentSelector,
template: ComponentSelector[str],
*,
make_context: JinjaContextFactory,
prefix: str | None,
Expand Down Expand Up @@ -394,7 +394,7 @@ def _make_response(

def _resolve_template_name(
self,
template: ComponentSelector,
template: ComponentSelector[str],
*,
error: Exception | None = None,
prefix: str | None,
Expand All @@ -416,7 +416,7 @@ def _resolve_template_name(
"""
if isinstance(template, RequestComponentSelector):
try:
result = template.get_component_id(request, error)
result = template.get_component(request, error)
except KeyError as e:
raise ValueError("Failed to resolve template name from request.") from e
elif isinstance(template, str):
Expand Down
13 changes: 7 additions & 6 deletions fasthx/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

P = ParamSpec("P")
T = TypeVar("T")
Tco = TypeVar("Tco", covariant=True)
Tcontra = TypeVar("Tcontra", contravariant=True)

MaybeAsyncFunc = Callable[P, T] | Callable[P, Coroutine[Any, Any, T]]
Expand Down Expand Up @@ -70,16 +71,16 @@ def __call__(self, *, route_result: Any, route_context: dict[str, Any]) -> dict[


@runtime_checkable
class RequestComponentSelector(Protocol):
class RequestComponentSelector(Protocol[Tco]):
"""
Component selector protocol that loads the required component's ID from the request.
Component selector protocol that uses the request to select the component that will be rendered.
The protocol is runtime-checkable, so it can be used in `isinstance()`, `issubclass()` calls.
"""

def get_component_id(self, request: Request, error: Exception | None) -> str:
def get_component(self, request: Request, error: Exception | None) -> Tco:
"""
Returns the identifier of the component that was requested by the client.
Returns the component that was requested by the client.
The caller should ensure that `error` will be the exception that was raised by the
route or `None` if the route returned normally.
Expand All @@ -89,7 +90,7 @@ def get_component_id(self, request: Request, error: Exception | None) -> str:
```python
class MyComponentSelector:
def get_component_id(self, request: Request, error: Exception | None) -> str:
def get_component(self, request: Request, error: Exception | None) -> str:
if error is not None:
raise error
Expand All @@ -104,5 +105,5 @@ def get_component_id(self, request: Request, error: Exception | None) -> str:
...


ComponentSelector: TypeAlias = str | RequestComponentSelector
ComponentSelector: TypeAlias = str | RequestComponentSelector[T]
"""Type alias for known component selectors."""
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "fasthx"
version = "2.0.0-rc1"
version = "2.0.0-rc2"
description = "FastAPI data APIs with HTMX support."
authors = ["Peter Volf <do.volfp@gmail.com>"]
readme = "README.md"
Expand Down

0 comments on commit 256b12f

Please sign in to comment.