5858from .signals import request_tearing_down
5959from .templating import DispatchingJinjaLoader
6060from .templating import Environment
61- from .typing import AfterRequestCallable
6261from .typing import BeforeFirstRequestCallable
63- from .typing import BeforeRequestCallable
6462from .typing import ResponseReturnValue
6563from .typing import TeardownCallable
66- from .typing import TemplateContextProcessorCallable
6764from .typing import TemplateFilterCallable
6865from .typing import TemplateGlobalCallable
6966from .typing import TemplateTestCallable
70- from .typing import URLDefaultCallable
71- from .typing import URLValuePreprocessorCallable
7267from .wrappers import Request
7368from .wrappers import Response
7469
@@ -745,20 +740,21 @@ def update_template_context(self, context: dict) -> None:
745740 :param context: the context as a dictionary that is updated in place
746741 to add extra variables.
747742 """
748- funcs : t .Iterable [
749- TemplateContextProcessorCallable
750- ] = self . template_context_processors [ None ]
751- reqctx = _request_ctx_stack . top
752- if reqctx is not None :
753- for bp in request . blueprints :
754- if bp in self . template_context_processors :
755- funcs = chain ( funcs , self . template_context_processors [ bp ])
743+ names : t .Iterable [t . Optional [ str ]] = ( None ,)
744+
745+ # A template may be rendered outside a request context.
746+ if request :
747+ names = chain ( names , reversed ( request . blueprints ))
748+
749+ # The values passed to render_template take precedence. Keep a
750+ # copy to re-apply after all context functions.
756751 orig_ctx = context .copy ()
757- for func in funcs :
758- context .update (func ())
759- # make sure the original values win. This makes it possible to
760- # easier add new variables in context processors without breaking
761- # existing views.
752+
753+ for name in names :
754+ if name in self .template_context_processors :
755+ for func in self .template_context_processors [name ]:
756+ context .update (func ())
757+
762758 context .update (orig_ctx )
763759
764760 def make_shell_context (self ) -> dict :
@@ -1278,9 +1274,10 @@ def _find_error_handler(
12781274 class, or ``None`` if a suitable handler is not found.
12791275 """
12801276 exc_class , code = self ._get_exc_class_and_code (type (e ))
1277+ names = (* request .blueprints , None )
12811278
1282- for c in [ code , None ] if code is not None else [ None ] :
1283- for name in chain ( request . blueprints , [ None ]) :
1279+ for c in ( code , None ) if code is not None else ( None ,) :
1280+ for name in names :
12841281 handler_map = self .error_handler_spec [name ][c ]
12851282
12861283 if not handler_map :
@@ -1800,17 +1797,19 @@ def inject_url_defaults(self, endpoint: str, values: dict) -> None:
18001797
18011798 .. versionadded:: 0.7
18021799 """
1803- funcs : t .Iterable [URLDefaultCallable ] = self . url_default_functions [ None ]
1800+ names : t .Iterable [t . Optional [ str ]] = ( None ,)
18041801
1802+ # url_for may be called outside a request context, parse the
1803+ # passed endpoint instead of using request.blueprints.
18051804 if "." in endpoint :
1806- # This is called by url_for, which can be called outside a
1807- # request, can't use request.blueprints.
1808- bps = _split_blueprint_path (endpoint .rpartition ("." )[0 ])
1809- bp_funcs = chain .from_iterable (self .url_default_functions [bp ] for bp in bps )
1810- funcs = chain (funcs , bp_funcs )
1805+ names = chain (
1806+ names , reversed (_split_blueprint_path (endpoint .rpartition ("." )[0 ]))
1807+ )
18111808
1812- for func in funcs :
1813- func (endpoint , values )
1809+ for name in names :
1810+ if name in self .url_default_functions :
1811+ for func in self .url_default_functions [name ]:
1812+ func (endpoint , values )
18141813
18151814 def handle_url_build_error (
18161815 self , error : Exception , endpoint : str , values : dict
@@ -1845,24 +1844,20 @@ def preprocess_request(self) -> t.Optional[ResponseReturnValue]:
18451844 value is handled as if it was the return value from the view, and
18461845 further request handling is stopped.
18471846 """
1847+ names = (None , * reversed (request .blueprints ))
18481848
1849- funcs : t .Iterable [URLValuePreprocessorCallable ] = self .url_value_preprocessors [
1850- None
1851- ]
1852- for bp in request .blueprints :
1853- if bp in self .url_value_preprocessors :
1854- funcs = chain (funcs , self .url_value_preprocessors [bp ])
1855- for func in funcs :
1856- func (request .endpoint , request .view_args )
1857-
1858- funcs : t .Iterable [BeforeRequestCallable ] = self .before_request_funcs [None ]
1859- for bp in request .blueprints :
1860- if bp in self .before_request_funcs :
1861- funcs = chain (funcs , self .before_request_funcs [bp ])
1862- for func in funcs :
1863- rv = self .ensure_sync (func )()
1864- if rv is not None :
1865- return rv
1849+ for name in names :
1850+ if name in self .url_value_preprocessors :
1851+ for url_func in self .url_value_preprocessors [name ]:
1852+ url_func (request .endpoint , request .view_args )
1853+
1854+ for name in names :
1855+ if name in self .before_request_funcs :
1856+ for before_func in self .before_request_funcs [name ]:
1857+ rv = self .ensure_sync (before_func )()
1858+
1859+ if rv is not None :
1860+ return rv
18661861
18671862 return None
18681863
@@ -1880,16 +1875,18 @@ def process_response(self, response: Response) -> Response:
18801875 instance of :attr:`response_class`.
18811876 """
18821877 ctx = _request_ctx_stack .top
1883- funcs : t .Iterable [AfterRequestCallable ] = ctx ._after_request_functions
1884- for bp in request .blueprints :
1885- if bp in self .after_request_funcs :
1886- funcs = chain (funcs , reversed (self .after_request_funcs [bp ]))
1887- if None in self .after_request_funcs :
1888- funcs = chain (funcs , reversed (self .after_request_funcs [None ]))
1889- for handler in funcs :
1890- response = self .ensure_sync (handler )(response )
1878+
1879+ for func in ctx ._after_request_functions :
1880+ response = self .ensure_sync (func )(response )
1881+
1882+ for name in chain (request .blueprints , (None ,)):
1883+ if name in self .after_request_funcs :
1884+ for func in reversed (self .after_request_funcs [name ]):
1885+ response = self .ensure_sync (func )(response )
1886+
18911887 if not self .session_interface .is_null_session (ctx .session ):
18921888 self .session_interface .save_session (self , ctx .session , response )
1889+
18931890 return response
18941891
18951892 def do_teardown_request (
@@ -1917,14 +1914,12 @@ def do_teardown_request(
19171914 """
19181915 if exc is _sentinel :
19191916 exc = sys .exc_info ()[1 ]
1920- funcs : t .Iterable [TeardownCallable ] = reversed (
1921- self .teardown_request_funcs [None ]
1922- )
1923- for bp in request .blueprints :
1924- if bp in self .teardown_request_funcs :
1925- funcs = chain (funcs , reversed (self .teardown_request_funcs [bp ]))
1926- for func in funcs :
1927- self .ensure_sync (func )(exc )
1917+
1918+ for name in chain (request .blueprints , (None ,)):
1919+ if name in self .teardown_request_funcs :
1920+ for func in reversed (self .teardown_request_funcs [name ]):
1921+ self .ensure_sync (func )(exc )
1922+
19281923 request_tearing_down .send (self , exc = exc )
19291924
19301925 def do_teardown_appcontext (
@@ -1946,8 +1941,10 @@ def do_teardown_appcontext(
19461941 """
19471942 if exc is _sentinel :
19481943 exc = sys .exc_info ()[1 ]
1944+
19491945 for func in reversed (self .teardown_appcontext_funcs ):
19501946 self .ensure_sync (func )(exc )
1947+
19511948 appcontext_tearing_down .send (self , exc = exc )
19521949
19531950 def app_context (self ) -> AppContext :
0 commit comments