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 asyncio.futures import Future
|
||||||
from collections import defaultdict, deque
|
from collections import defaultdict, deque
|
||||||
from contextlib import suppress
|
from contextlib import contextmanager, suppress
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from inspect import isawaitable
|
from inspect import isawaitable
|
||||||
from os import environ
|
from os import environ
|
||||||
|
@ -33,6 +33,7 @@ from typing import (
|
||||||
Deque,
|
Deque,
|
||||||
Dict,
|
Dict,
|
||||||
Iterable,
|
Iterable,
|
||||||
|
Iterator,
|
||||||
List,
|
List,
|
||||||
Optional,
|
Optional,
|
||||||
Set,
|
Set,
|
||||||
|
@ -433,14 +434,15 @@ class Sanic(StaticHandleMixin, BaseSanic, StartupMixin, metaclass=TouchUpMeta):
|
||||||
|
|
||||||
ctx = params.pop("route_context")
|
ctx = params.pop("route_context")
|
||||||
|
|
||||||
routes = self.router.add(**params)
|
with self.amend():
|
||||||
if isinstance(routes, Route):
|
routes = self.router.add(**params)
|
||||||
routes = [routes]
|
if isinstance(routes, Route):
|
||||||
|
routes = [routes]
|
||||||
|
|
||||||
for r in routes:
|
for r in routes:
|
||||||
r.extra.websocket = websocket
|
r.extra.websocket = websocket
|
||||||
r.extra.static = params.get("static", False)
|
r.extra.static = params.get("static", False)
|
||||||
r.ctx.__dict__.update(ctx)
|
r.ctx.__dict__.update(ctx)
|
||||||
|
|
||||||
return routes
|
return routes
|
||||||
|
|
||||||
|
@ -449,17 +451,19 @@ class Sanic(StaticHandleMixin, BaseSanic, StartupMixin, metaclass=TouchUpMeta):
|
||||||
middleware: FutureMiddleware,
|
middleware: FutureMiddleware,
|
||||||
route_names: Optional[List[str]] = None,
|
route_names: Optional[List[str]] = None,
|
||||||
):
|
):
|
||||||
if route_names:
|
with self.amend():
|
||||||
return self.register_named_middleware(
|
if route_names:
|
||||||
middleware.middleware, route_names, middleware.attach_to
|
return self.register_named_middleware(
|
||||||
)
|
middleware.middleware, route_names, middleware.attach_to
|
||||||
else:
|
)
|
||||||
return self.register_middleware(
|
else:
|
||||||
middleware.middleware, middleware.attach_to
|
return self.register_middleware(
|
||||||
)
|
middleware.middleware, middleware.attach_to
|
||||||
|
)
|
||||||
|
|
||||||
def _apply_signal(self, signal: FutureSignal) -> Signal:
|
def _apply_signal(self, signal: FutureSignal) -> Signal:
|
||||||
return self.signal_router.add(*signal)
|
with self.amend():
|
||||||
|
return self.signal_router.add(*signal)
|
||||||
|
|
||||||
def dispatch(
|
def dispatch(
|
||||||
self,
|
self,
|
||||||
|
@ -1520,6 +1524,27 @@ class Sanic(StaticHandleMixin, BaseSanic, StartupMixin, metaclass=TouchUpMeta):
|
||||||
# Lifecycle
|
# 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):
|
def finalize(self):
|
||||||
try:
|
try:
|
||||||
self.router.finalize()
|
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