Fix the request blueprint method

Previously for a nested blueprint it would return the parent_name.name
rather than the name of the current blueprint. This is fixed and
another blueprints method is added in order to get the parent_name and
name. The latter is made use of in the app.

I've used a cached_property as this method is called a few times per
request (by the app methods).
This commit is contained in:
pgjones 2021-05-15 16:16:37 +01:00
parent f64fff6476
commit fdcc62be82
3 changed files with 25 additions and 14 deletions

View file

@ -23,7 +23,9 @@ Unreleased
- Roll back a change to the order that URL matching was done. The
URL is again matched after the session is loaded, so the session is
available in custom URL converters. :issue:`4053`
- Fix the request ``blueprint`` method to ensure it returns the
current blueprint name. A ``blueprints`` method is added to
replace any functionality lost in this fix. :pr:`4059`
Version 2.0.0
-------------

View file

@ -747,7 +747,7 @@ class Flask(Scaffold):
] = self.template_context_processors[None]
reqctx = _request_ctx_stack.top
if reqctx is not None:
for bp in self._request_blueprints():
for bp in request.blueprints:
if bp in self.template_context_processors:
funcs = chain(funcs, self.template_context_processors[bp])
orig_ctx = context.copy()
@ -1261,7 +1261,7 @@ class Flask(Scaffold):
exc_class, code = self._get_exc_class_and_code(type(e))
for c in [code, None]:
for name in chain(self._request_blueprints(), [None]):
for name in chain(request.blueprints, [None]):
handler_map = self.error_handler_spec[name][c]
if not handler_map:
@ -1825,14 +1825,14 @@ class Flask(Scaffold):
funcs: t.Iterable[URLValuePreprocessorCallable] = self.url_value_preprocessors[
None
]
for bp in self._request_blueprints():
for bp in request.blueprints:
if bp in self.url_value_preprocessors:
funcs = chain(funcs, self.url_value_preprocessors[bp])
for func in funcs:
func(request.endpoint, request.view_args)
funcs: t.Iterable[BeforeRequestCallable] = self.before_request_funcs[None]
for bp in self._request_blueprints():
for bp in request.blueprints:
if bp in self.before_request_funcs:
funcs = chain(funcs, self.before_request_funcs[bp])
for func in funcs:
@ -1857,7 +1857,7 @@ class Flask(Scaffold):
"""
ctx = _request_ctx_stack.top
funcs: t.Iterable[AfterRequestCallable] = ctx._after_request_functions
for bp in self._request_blueprints():
for bp in request.blueprints:
if bp in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
if None in self.after_request_funcs:
@ -1896,7 +1896,7 @@ class Flask(Scaffold):
funcs: t.Iterable[TeardownCallable] = reversed(
self.teardown_request_funcs[None]
)
for bp in self._request_blueprints():
for bp in request.blueprints:
if bp in self.teardown_request_funcs:
funcs = chain(funcs, reversed(self.teardown_request_funcs[bp]))
for func in funcs:
@ -2068,9 +2068,3 @@ class Flask(Scaffold):
wrapped to apply middleware.
"""
return self.wsgi_app(environ, start_response)
def _request_blueprints(self) -> t.Iterable[str]:
if _request_ctx_stack.top.request.blueprint is None:
return []
else:
return reversed(_request_ctx_stack.top.request.blueprint.split("."))

View file

@ -1,6 +1,7 @@
import typing as t
from werkzeug.exceptions import BadRequest
from werkzeug.utils import cached_property
from werkzeug.wrappers import Request as RequestBase
from werkzeug.wrappers import Response as ResponseBase
@ -73,10 +74,24 @@ class Request(RequestBase):
def blueprint(self) -> t.Optional[str]:
"""The name of the current blueprint"""
if self.url_rule and "." in self.url_rule.endpoint:
return self.url_rule.endpoint.rsplit(".", 1)[0]
return self.url_rule.endpoint.split(".")[-2]
else:
return None
@cached_property
def blueprints(self) -> t.List[str]:
"""Return the names of the current blueprints.
The returned list is ordered from the current blueprint,
upwards through parent blueprints.
"""
if self.url_rule and "." in self.url_rule.endpoint:
bps = self.url_rule.endpoint.split(".")[:-1]
bps.reverse()
return bps
else:
return []
def _load_form_data(self) -> None:
RequestBase._load_form_data(self)