58
58
from .signals import request_tearing_down
59
59
from .templating import DispatchingJinjaLoader
60
60
from .templating import Environment
61
- from .typing import AfterRequestCallable
62
61
from .typing import BeforeFirstRequestCallable
63
- from .typing import BeforeRequestCallable
64
62
from .typing import ResponseReturnValue
65
63
from .typing import TeardownCallable
66
- from .typing import TemplateContextProcessorCallable
67
64
from .typing import TemplateFilterCallable
68
65
from .typing import TemplateGlobalCallable
69
66
from .typing import TemplateTestCallable
70
- from .typing import URLDefaultCallable
71
- from .typing import URLValuePreprocessorCallable
72
67
from .wrappers import Request
73
68
from .wrappers import Response
74
69
@@ -745,20 +740,21 @@ def update_template_context(self, context: dict) -> None:
745
740
:param context: the context as a dictionary that is updated in place
746
741
to add extra variables.
747
742
"""
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.
756
751
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
+
762
758
context .update (orig_ctx )
763
759
764
760
def make_shell_context (self ) -> dict :
@@ -1278,9 +1274,10 @@ def _find_error_handler(
1278
1274
class, or ``None`` if a suitable handler is not found.
1279
1275
"""
1280
1276
exc_class , code = self ._get_exc_class_and_code (type (e ))
1277
+ names = (* request .blueprints , None )
1281
1278
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 :
1284
1281
handler_map = self .error_handler_spec [name ][c ]
1285
1282
1286
1283
if not handler_map :
@@ -1800,17 +1797,19 @@ def inject_url_defaults(self, endpoint: str, values: dict) -> None:
1800
1797
1801
1798
.. versionadded:: 0.7
1802
1799
"""
1803
- funcs : t .Iterable [URLDefaultCallable ] = self . url_default_functions [ None ]
1800
+ names : t .Iterable [t . Optional [ str ]] = ( None ,)
1804
1801
1802
+ # url_for may be called outside a request context, parse the
1803
+ # passed endpoint instead of using request.blueprints.
1805
1804
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
+ )
1811
1808
1812
- for func in funcs :
1813
- func (endpoint , values
F438
)
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 )
1814
1813
1815
1814
def handle_url_build_error (
1816
1815
self , error : Exception , endpoint : str , values : dict
@@ -1845,24 +1844,20 @@ def preprocess_request(self) -> t.Optional[ResponseReturnValue]:
1845
1844
value is handled as if it was the return value from the view, and
1846
1845
further request handling is stopped.
1847
1846
"""
1847
+ names = (None , * reversed (request .blueprints ))
1848
1848
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
1866
1861
1867
1862
return None
1868
1863
@@ -1880,16 +1875,18 @@ def process_response(self, response: Response) -> Response:
1880
1875
instance of :attr:`response_class`.
1881
1876
"""
1882
1877
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
+
1891
1887
if not self .session_interface .is_null_session (ctx .session ):
1892
1888
self .session_interface .save_session (self , ctx .session , response )
1889
+
1893
1890
return response
1894
1891
1895
1892
def do_teardown_request (
@@ -1917,14 +1914,12 @@ def do_teardown_request(
1917
1914
"""
1918
1915
if exc is _sentinel :
1919
1916
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
+
1928
1923
request_tearing_down .send (self , exc = exc )
1929
1924
1930
1925
def do_teardown_appcontext (
@@ -1946,8 +1941,10 @@ def do_teardown_appcontext(
1946
1941
"""
1947
1942
if exc is _sentinel :
1948
1943
exc = sys .exc_info ()[1 ]
1944
+
1949
1945
for func in reversed (self .teardown_appcontext_funcs ):
1950
1946
self .ensure_sync (func )(exc )
1947
+
1951
1948
appcontext_tearing_down .send (self , exc = exc )
1952
1949
1953
1950
def app_context (self ) -> AppContext :
0 commit comments