16
16
)
17
17
from asyncio .futures import Future
18
18
from collections import defaultdict , deque
19
- from contextlib import suppress
19
+ from contextlib import contextmanager , suppress
20
20
from functools import partial
21
21
from inspect import isawaitable
22
22
from os import environ
33
33
Deque ,
34
34
Dict ,
35
35
Iterable ,
36
+ Iterator ,
36
37
List ,
37
38
Optional ,
38
39
Set ,
@@ -433,14 +434,15 @@ def _apply_route(self, route: FutureRoute) -> List[Route]:
433
434
434
435
ctx = params .pop ("route_context" )
435
436
436
- routes = self .router .add (** params )
437
- if isinstance (routes , Route ):
438
- routes = [routes ]
437
+ with self .amend ():
438
+ routes = self .router .add (** params )
439
+ if isinstance (routes , Route ):
440
+ routes = [routes ]
439
441
440
- for r in routes :
441
- r .extra .websocket = websocket
442
- r .extra .static = params .get ("static" , False )
443
- r .ctx .__dict__ .update (ctx )
442
+ for r in routes :
443
+ r .extra .websocket = websocket
444
+ r .extra .static = params .get ("static" , False )
445
+ r .ctx .__dict__ .update (ctx )
444
446
445
447
return routes
446
448
@@ -449,17 +451,19 @@ def _apply_middleware(
449
451
middleware : FutureMiddleware ,
450
452
route_names : Optional [List [str ]] = None ,
451
453
):
452
- if route_names :
453
- return self .register_named_middleware (
454
- middleware .middleware , route_names , middleware .attach_to
455
- )
456
- else :
457
- return self .register_middleware (
458
- middleware .middleware , middleware .attach_to
459
- )
454
+ with self .amend ():
455
+ if route_names :
456
+ return self .register_named_middleware (
457
+ middleware .middleware , route_names , middleware .attach_to
458
+ )
459
+ else :
460
+ return self .register_middleware (
461
+ middleware .middleware , middleware .attach_to
462
+ )
460
463
461
464
def _apply_signal (self , signal : FutureSignal ) -> Signal :
462
- return self .signal_router .add (* signal )
465
+ with self .amend ():
466
+ return self .signal_router .add (* signal )
463
467
464
468
def dispatch (
465
469
self ,
@@ -1520,6 +1524,27 @@ def _check_uvloop_conflict(cls) -> None:
1520
1524
# Lifecycle
1521
1525
# -------------------------------------------------------------------- #
1522
1526
1527
+ @contextmanager
1528
+ def amend (self ) -> Iterator [None ]:
1529
+ """
1530
+ If the application has started, this function allows changes
1531
+ to be made to add routes, middleware, and signals.
1532
+ """
1533
+ if not self .state .is_started :
1534
+ yield
1535
+ else :
1536
+ do_router = self .router .finalized
1537
+ do_signal_router = self .signal_router .finalized
1538
+ if do_router :
1539
+ self .router .reset ()
1540
+ if do_signal_router :
1541
+ self .signal_router .reset ()
1542
+ yield
1543
+ if do_signal_router :
1544
+ self .signalize (self .config .TOUCHUP )
1545
+ if do_router :
1546
+ self .finalize ()
1547
+
1523
1548
def finalize (self ):
1524
1549
try :
1525
1550
self .router .finalize ()
0 commit comments