Add convenience for dynamic changes to routing (#2704)
This commit is contained in:
parent
5ee36fd933
commit
8f265b8169
59
sanic/app.py
59
sanic/app.py
|
@ -16,7 +16,7 @@ from asyncio import (
|
|||
)
|
||||
from asyncio.futures import Future
|
||||
from collections import defaultdict, deque
|
||||
from contextlib import suppress
|
||||
from contextlib import contextmanager, suppress
|
||||
from functools import partial
|
||||
from inspect import isawaitable
|
||||
from os import environ
|
||||
|
@ -33,6 +33,7 @@ from typing import (
|
|||
Deque,
|
||||
Dict,
|
||||
Iterable,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Set,
|
||||
|
@ -433,14 +434,15 @@ class Sanic(StaticHandleMixin, BaseSanic, StartupMixin, metaclass=TouchUpMeta):
|
|||
|
||||
ctx = params.pop("route_context")
|
||||
|
||||
routes = self.router.add(**params)
|
||||
if isinstance(routes, Route):
|
||||
routes = [routes]
|
||||
with self.amend():
|
||||
routes = self.router.add(**params)
|
||||
if isinstance(routes, Route):
|
||||
routes = [routes]
|
||||
|
||||
for r in routes:
|
||||
r.extra.websocket = websocket
|
||||
r.extra.static = params.get("static", False)
|
||||
r.ctx.__dict__.update(ctx)
|
||||
for r in routes:
|
||||
r.extra.websocket = websocket
|
||||
r.extra.static = params.get("static", False)
|
||||
r.ctx.__dict__.update(ctx)
|
||||
|
||||
return routes
|
||||
|
||||
|
@ -449,17 +451,19 @@ class Sanic(StaticHandleMixin, BaseSanic, StartupMixin, metaclass=TouchUpMeta):
|
|||
middleware: FutureMiddleware,
|
||||
route_names: Optional[List[str]] = None,
|
||||
):
|
||||
if route_names:
|
||||
return self.register_named_middleware(
|
||||
middleware.middleware, route_names, middleware.attach_to
|
||||
)
|
||||
else:
|
||||
return self.register_middleware(
|
||||
middleware.middleware, middleware.attach_to
|
||||
)
|
||||
with self.amend():
|
||||
if route_names:
|
||||
return self.register_named_middleware(
|
||||
middleware.middleware, route_names, middleware.attach_to
|
||||
)
|
||||
else:
|
||||
return self.register_middleware(
|
||||
middleware.middleware, middleware.attach_to
|
||||
)
|
||||
|
||||
def _apply_signal(self, signal: FutureSignal) -> Signal:
|
||||
return self.signal_router.add(*signal)
|
||||
with self.amend():
|
||||
return self.signal_router.add(*signal)
|
||||
|
||||
def dispatch(
|
||||
self,
|
||||
|
@ -1520,6 +1524,27 @@ class Sanic(StaticHandleMixin, BaseSanic, StartupMixin, metaclass=TouchUpMeta):
|
|||
# Lifecycle
|
||||
# -------------------------------------------------------------------- #
|
||||
|
||||
@contextmanager
|
||||
def amend(self) -> Iterator[None]:
|
||||
"""
|
||||
If the application has started, this function allows changes
|
||||
to be made to add routes, middleware, and signals.
|
||||
"""
|
||||
if not self.state.is_started:
|
||||
yield
|
||||
else:
|
||||
do_router = self.router.finalized
|
||||
do_signal_router = self.signal_router.finalized
|
||||
if do_router:
|
||||
self.router.reset()
|
||||
if do_signal_router:
|
||||
self.signal_router.reset()
|
||||
yield
|
||||
if do_signal_router:
|
||||
self.signalize(self.config.TOUCHUP)
|
||||
if do_router:
|
||||
self.finalize()
|
||||
|
||||
def finalize(self):
|
||||
try:
|
||||
self.router.finalize()
|
||||
|
|
54
tests/test_late_adds.py
Normal file
54
tests/test_late_adds.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
import pytest
|
||||
|
||||
from sanic import Sanic, text
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def late_app(app: Sanic):
|
||||
app.config.TOUCHUP = False
|
||||
app.get("/")(lambda _: text(""))
|
||||
return app
|
||||
|
||||
|
||||
def test_late_route(late_app: Sanic):
|
||||
@late_app.before_server_start
|
||||
async def late(app: Sanic):
|
||||
@app.get("/late")
|
||||
def handler(_):
|
||||
return text("late")
|
||||
|
||||
_, response = late_app.test_client.get("/late")
|
||||
assert response.status_code == 200
|
||||
assert response.text == "late"
|
||||
|
||||
|
||||
def test_late_middleware(late_app: Sanic):
|
||||
@late_app.get("/late")
|
||||
def handler(request):
|
||||
return text(request.ctx.late)
|
||||
|
||||
@late_app.before_server_start
|
||||
async def late(app: Sanic):
|
||||
@app.on_request
|
||||
def handler(request):
|
||||
request.ctx.late = "late"
|
||||
|
||||
_, response = late_app.test_client.get("/late")
|
||||
assert response.status_code == 200
|
||||
assert response.text == "late"
|
||||
|
||||
|
||||
def test_late_signal(late_app: Sanic):
|
||||
@late_app.get("/late")
|
||||
def handler(request):
|
||||
return text(request.ctx.late)
|
||||
|
||||
@late_app.before_server_start
|
||||
async def late(app: Sanic):
|
||||
@app.signal("http.lifecycle.request")
|
||||
def handler(request):
|
||||
request.ctx.late = "late"
|
||||
|
||||
_, response = late_app.test_client.get("/late")
|
||||
assert response.status_code == 200
|
||||
assert response.text == "late"
|
Loading…
Reference in New Issue
Block a user