summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorAdam Johnson <me@adamj.eu>2024-02-26 10:41:15 +0000
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2024-03-05 13:09:10 +0100
commiteff21d8e7a1cb297aedf1c702668b590a1b618f3 (patch)
treec9f25114db9f3ee3373690d32e30aec9bceddd03 /django
parent241adf678f8d6f0d6ec62d0bed5e32611a3fb75c (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.py8
-rw-r--r--django/urls/resolvers.py46
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