diff options
| author | Adam Johnson <me@adamj.eu> | 2024-02-26 10:41:15 +0000 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2024-03-05 13:09:10 +0100 |
| commit | eff21d8e7a1cb297aedf1c702668b590a1b618f3 (patch) | |
| tree | c9f25114db9f3ee3373690d32e30aec9bceddd03 /django | |
| parent | 241adf678f8d6f0d6ec62d0bed5e32611a3fb75c (diff) | |
Fixed #35252 -- Optimized _route_to_regex().
co-authored-by: Nick Pope <nick@nickpope.me.uk>
Diffstat (limited to 'django')
| -rw-r--r-- | django/urls/converters.py | 8 | ||||
| -rw-r--r-- | django/urls/resolvers.py | 46 |
2 files changed, 27 insertions, 27 deletions
diff --git a/django/urls/converters.py b/django/urls/converters.py index 9b44430580..b36cde1497 100644 --- a/django/urls/converters.py +++ b/django/urls/converters.py @@ -68,11 +68,11 @@ def register_converter(converter, type_name): REGISTERED_CONVERTERS[type_name] = converter() get_converters.cache_clear() + from django.urls.resolvers import _route_to_regex + + _route_to_regex.cache_clear() + @functools.cache def get_converters(): return {**DEFAULT_CONVERTERS, **REGISTERED_CONVERTERS} - - -def get_converter(raw_converter): - return get_converters()[raw_converter] diff --git a/django/urls/resolvers.py b/django/urls/resolvers.py index 1b26aed8c1..c667d7f268 100644 --- a/django/urls/resolvers.py +++ b/django/urls/resolvers.py @@ -26,7 +26,7 @@ from django.utils.http import RFC3986_SUBDELIMS, escape_leading_slashes from django.utils.regex_helper import _lazy_re_compile, normalize from django.utils.translation import get_language -from .converters import get_converter +from .converters import get_converters from .exceptions import NoReverseMatch, Resolver404 from .utils import get_callable @@ -243,7 +243,10 @@ _PATH_PARAMETER_COMPONENT_RE = _lazy_re_compile( r"<(?:(?P<converter>[^>:]+):)?(?P<parameter>[^>]+)>" ) +whitespace_set = frozenset(string.whitespace) + +@functools.lru_cache def _route_to_regex(route, is_endpoint): """ Convert a path pattern into a regular expression. Return the regular @@ -251,40 +254,37 @@ def _route_to_regex(route, is_endpoint): For example, 'foo/<int:pk>' returns '^foo\\/(?P<pk>[0-9]+)' and {'pk': <django.urls.converters.IntConverter>}. """ - original_route = route parts = ["^"] + all_converters = get_converters() converters = {} - while True: - match = _PATH_PARAMETER_COMPONENT_RE.search(route) - if not match: - parts.append(re.escape(route)) - break - elif not set(match.group()).isdisjoint(string.whitespace): + previous_end = 0 + for match_ in _PATH_PARAMETER_COMPONENT_RE.finditer(route): + if not whitespace_set.isdisjoint(match_[0]): raise ImproperlyConfigured( - "URL route '%s' cannot contain whitespace in angle brackets " - "<…>." % original_route + f"URL route {route!r} cannot contain whitespace in angle brackets <…>." ) - parts.append(re.escape(route[: match.start()])) - route = route[match.end() :] - parameter = match["parameter"] + # Default to make converter "str" if unspecified (parameter always + # matches something). + raw_converter, parameter = match_.groups(default="str") if not parameter.isidentifier(): raise ImproperlyConfigured( - "URL route '%s' uses parameter name %r which isn't a valid " - "Python identifier." % (original_route, parameter) + f"URL route {route!r} uses parameter name {parameter!r} which " + "isn't a valid Python identifier." ) - raw_converter = match["converter"] - if raw_converter is None: - # If a converter isn't specified, the default is `str`. - raw_converter = "str" try: - converter = get_converter(raw_converter) + converter = all_converters[raw_converter] except KeyError as e: raise ImproperlyConfigured( - "URL route %r uses invalid converter %r." - % (original_route, raw_converter) + f"URL route {route!r} uses invalid converter {raw_converter!r}." ) from e converters[parameter] = converter - parts.append("(?P<" + parameter + ">" + converter.regex + ")") + + start, end = match_.span() + parts.append(re.escape(route[previous_end:start])) + previous_end = end + parts.append(f"(?P<{parameter}>{converter.regex})") + + parts.append(re.escape(route[previous_end:])) if is_endpoint: parts.append(r"\Z") return "".join(parts), converters |
