Skip to content
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
12 changes: 10 additions & 2 deletions litestar/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ def _create_route_handler_method_map(

return route_map

def _build_routes(self, route_handlers: Iterable[BaseRouteHandler]) -> list[HTTPRoute | ASGIRoute | WebSocketRoute]:
def _build_routes(self, route_handlers: Iterable[BaseRouteHandler]) -> list[HTTPRoute | ASGIRoute | WebSocketRoute]: # noqa: C901
"""Create routes for all the handlers"""
routes: list[HTTPRoute | ASGIRoute | WebSocketRoute] = []

Expand All @@ -683,11 +683,19 @@ def _build_routes(self, route_handlers: Iterable[BaseRouteHandler]) -> list[HTTP
HTTPRoute(path=path, route_handlers=_maybe_add_options_handler(path, http_handlers, root=self))
)

registered_route_handlers = set()

for finalized_route in routes:
route_handlers = get_route_handlers(finalized_route)

for route_handler in route_handlers:
route_handler.on_registration(route=finalized_route, app=self)
# handlers can be registered multiple times, if they define multiple
# paths. in these cases, we don't want to call 'on_registration' more
# than once, so we keep track of the handlers we've already called this
# hook
if route_handler not in registered_route_handlers:
route_handler.on_registration(route=finalized_route, app=self)
registered_route_handlers.add(route_handler)

for plugin in self.plugins.receive_route:
plugin.receive_route(finalized_route)
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import pytest
from click import Group
from pytest import MonkeyPatch
from pytest_mock import MockerFixture

from litestar import Litestar, MediaType, Request, Response, get
from litestar.config.app import AppConfig, ExperimentalFeatures
Expand All @@ -22,6 +23,7 @@
LitestarWarning,
NotFoundException,
)
from litestar.handlers import BaseRouteHandler
from litestar.plugins import CLIPlugin
from litestar.status_codes import HTTP_200_OK, HTTP_400_BAD_REQUEST, HTTP_500_INTERNAL_SERVER_ERROR
from litestar.testing import TestClient, create_test_client
Expand Down Expand Up @@ -424,3 +426,24 @@ def handler(scope: Scope) -> None:
client.get("/")

mock.assert_called_once_with(app)


def test_handler_registration_on_registration_called_only_once(mocker: MockerFixture) -> None:
mock_on_registration = mocker.spy(BaseRouteHandler, "on_registration")

@get(["/a", "/b"], name="hello_this_is_a_test")
async def handler() -> None:
pass

Litestar([handler], openapi_config=None)

assert (
len(
[
c
for c in mock_on_registration.call_args_list
if isinstance(c.args[0], BaseRouteHandler) and c.args[0].name == "hello_this_is_a_test"
]
)
== 1
)
Loading