cache blueprint path calculation

This commit is contained in:
David Lord 2021-05-20 10:32:28 -07:00 committed by pgjones
parent c2920e2bd9
commit 67b0b7e30d
3 changed files with 56 additions and 30 deletions

View file

@ -36,6 +36,7 @@ from .globals import _request_ctx_stack
from .globals import g
from .globals import request
from .globals import session
from .helpers import _split_blueprint_path
from .helpers import get_debug_flag
from .helpers import get_env
from .helpers import get_flashed_messages
@ -1790,13 +1791,11 @@ class Flask(Scaffold):
funcs: t.Iterable[URLDefaultCallable] = self.url_default_functions[None]
if "." in endpoint:
bps: t.List[str] = [endpoint.rsplit(".", 1)[0]]
while "." in bps[-1]:
bps.append(bps[-1].rpartition(".")[0])
for bp in bps:
funcs = chain(funcs, self.url_default_functions[bp])
# This is called by url_for, which can be called outside a
# request, can't use request.blueprints.
bps = _split_blueprint_path(endpoint.rpartition(".")[0])
bp_funcs = chain.from_iterable(self.url_default_functions[bp] for bp in bps)
funcs = chain(funcs, bp_funcs)
for func in funcs:
func(endpoint, values)

View file

@ -6,6 +6,7 @@ import typing as t
import warnings
from datetime import datetime
from datetime import timedelta
from functools import lru_cache
from functools import update_wrapper
from threading import RLock
@ -821,3 +822,13 @@ def is_ip(value: str) -> bool:
return True
return False
@lru_cache(maxsize=None)
def _split_blueprint_path(name: str) -> t.List[str]:
out: t.List[str] = [name]
if "." in name:
out.extend(_split_blueprint_path(name.rpartition(".")[0]))
return out

View file

@ -1,12 +1,12 @@
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
from . import json
from .globals import current_app
from .helpers import _split_blueprint_path
if t.TYPE_CHECKING:
import typing_extensions as te
@ -60,38 +60,54 @@ class Request(RequestBase):
@property
def endpoint(self) -> t.Optional[str]:
"""The endpoint that matched the request. This in combination with
:attr:`view_args` can be used to reconstruct the same or a
modified URL. If an exception happened when matching, this will
be ``None``.
"""The endpoint that matched the request URL.
This will be ``None`` if matching failed or has not been
performed yet.
This in combination with :attr:`view_args` can be used to
reconstruct the same URL or a modified URL.
"""
if self.url_rule is not None:
return self.url_rule.endpoint
else:
return None
return None
@property
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]
else:
return None
"""The registered name of the current blueprint.
@cached_property
def blueprints(self) -> t.List[str]:
"""The names of the current blueprint upwards through parent
blueprints.
This will be ``None`` if the endpoint is not part of a
blueprint, or if URL matching failed or has not been performed
yet.
This does not necessarily match the name the blueprint was
created with. It may have been nested, or registered with a
different name.
"""
if self.blueprint is None:
endpoint = self.endpoint
if endpoint is not None and "." in endpoint:
return endpoint.rpartition(".")[0]
return None
@property
def blueprints(self) -> t.List[str]:
"""The registered names of the current blueprint upwards through
parent blueprints.
This will be an empty list if there is no current blueprint, or
if URL matching failed.
.. versionadded:: 2.0.1
"""
name = self.blueprint
if name is None:
return []
bps: t.List[str] = [self.blueprint]
while "." in bps[-1]:
bps.append(bps[-1].rpartition(".")[0])
return bps
return _split_blueprint_path(name)
def _load_form_data(self) -> None:
RequestBase._load_form_data(self)