mirror of https://github.com/django/django.git
Fixed #35252 -- Optimized _route_to_regex().
co-authored-by: Nick Pope <nick@nickpope.me.uk>
This commit is contained in:
parent
241adf678f
commit
eff21d8e7a
|
@ -68,11 +68,11 @@ def register_converter(converter, type_name):
|
||||||
REGISTERED_CONVERTERS[type_name] = converter()
|
REGISTERED_CONVERTERS[type_name] = converter()
|
||||||
get_converters.cache_clear()
|
get_converters.cache_clear()
|
||||||
|
|
||||||
|
from django.urls.resolvers import _route_to_regex
|
||||||
|
|
||||||
|
_route_to_regex.cache_clear()
|
||||||
|
|
||||||
|
|
||||||
@functools.cache
|
@functools.cache
|
||||||
def get_converters():
|
def get_converters():
|
||||||
return {**DEFAULT_CONVERTERS, **REGISTERED_CONVERTERS}
|
return {**DEFAULT_CONVERTERS, **REGISTERED_CONVERTERS}
|
||||||
|
|
||||||
|
|
||||||
def get_converter(raw_converter):
|
|
||||||
return get_converters()[raw_converter]
|
|
||||||
|
|
|
@ -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.regex_helper import _lazy_re_compile, normalize
|
||||||
from django.utils.translation import get_language
|
from django.utils.translation import get_language
|
||||||
|
|
||||||
from .converters import get_converter
|
from .converters import get_converters
|
||||||
from .exceptions import NoReverseMatch, Resolver404
|
from .exceptions import NoReverseMatch, Resolver404
|
||||||
from .utils import get_callable
|
from .utils import get_callable
|
||||||
|
|
||||||
|
@ -243,7 +243,10 @@ _PATH_PARAMETER_COMPONENT_RE = _lazy_re_compile(
|
||||||
r"<(?:(?P<converter>[^>:]+):)?(?P<parameter>[^>]+)>"
|
r"<(?:(?P<converter>[^>:]+):)?(?P<parameter>[^>]+)>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
whitespace_set = frozenset(string.whitespace)
|
||||||
|
|
||||||
|
|
||||||
|
@functools.lru_cache
|
||||||
def _route_to_regex(route, is_endpoint):
|
def _route_to_regex(route, is_endpoint):
|
||||||
"""
|
"""
|
||||||
Convert a path pattern into a regular expression. Return the regular
|
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]+)'
|
For example, 'foo/<int:pk>' returns '^foo\\/(?P<pk>[0-9]+)'
|
||||||
and {'pk': <django.urls.converters.IntConverter>}.
|
and {'pk': <django.urls.converters.IntConverter>}.
|
||||||
"""
|
"""
|
||||||
original_route = route
|
|
||||||
parts = ["^"]
|
parts = ["^"]
|
||||||
|
all_converters = get_converters()
|
||||||
converters = {}
|
converters = {}
|
||||||
while True:
|
previous_end = 0
|
||||||
match = _PATH_PARAMETER_COMPONENT_RE.search(route)
|
for match_ in _PATH_PARAMETER_COMPONENT_RE.finditer(route):
|
||||||
if not match:
|
if not whitespace_set.isdisjoint(match_[0]):
|
||||||
parts.append(re.escape(route))
|
|
||||||
break
|
|
||||||
elif not set(match.group()).isdisjoint(string.whitespace):
|
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"URL route '%s' cannot contain whitespace in angle brackets "
|
f"URL route {route!r} cannot contain whitespace in angle brackets <…>."
|
||||||
"<…>." % original_route
|
|
||||||
)
|
)
|
||||||
parts.append(re.escape(route[: match.start()]))
|
# Default to make converter "str" if unspecified (parameter always
|
||||||
route = route[match.end() :]
|
# matches something).
|
||||||
parameter = match["parameter"]
|
raw_converter, parameter = match_.groups(default="str")
|
||||||
if not parameter.isidentifier():
|
if not parameter.isidentifier():
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"URL route '%s' uses parameter name %r which isn't a valid "
|
f"URL route {route!r} uses parameter name {parameter!r} which "
|
||||||
"Python identifier." % (original_route, parameter)
|
"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:
|
try:
|
||||||
converter = get_converter(raw_converter)
|
converter = all_converters[raw_converter]
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"URL route %r uses invalid converter %r."
|
f"URL route {route!r} uses invalid converter {raw_converter!r}."
|
||||||
% (original_route, raw_converter)
|
|
||||||
) from e
|
) from e
|
||||||
converters[parameter] = converter
|
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:
|
if is_endpoint:
|
||||||
parts.append(r"\Z")
|
parts.append(r"\Z")
|
||||||
return "".join(parts), converters
|
return "".join(parts), converters
|
||||||
|
|
|
@ -393,6 +393,9 @@ Miscellaneous
|
||||||
:py:class:`html.parser.HTMLParser` subclasses. This results in a more robust
|
:py:class:`html.parser.HTMLParser` subclasses. This results in a more robust
|
||||||
and faster operation, but there may be small differences in the output.
|
and faster operation, but there may be small differences in the output.
|
||||||
|
|
||||||
|
* The undocumented ``django.urls.converters.get_converter()`` function is
|
||||||
|
removed.
|
||||||
|
|
||||||
.. _deprecated-features-5.1:
|
.. _deprecated-features-5.1:
|
||||||
|
|
||||||
Features deprecated in 5.1
|
Features deprecated in 5.1
|
||||||
|
|
|
@ -246,14 +246,12 @@ class SimplifiedURLTests(SimpleTestCase):
|
||||||
path("foo", EmptyCBV())
|
path("foo", EmptyCBV())
|
||||||
|
|
||||||
def test_whitespace_in_route(self):
|
def test_whitespace_in_route(self):
|
||||||
msg = (
|
msg = "URL route %r cannot contain whitespace in angle brackets <…>"
|
||||||
"URL route 'space/<int:num>/extra/<str:%stest>' cannot contain "
|
|
||||||
"whitespace in angle brackets <…>"
|
|
||||||
)
|
|
||||||
for whitespace in string.whitespace:
|
for whitespace in string.whitespace:
|
||||||
with self.subTest(repr(whitespace)):
|
with self.subTest(repr(whitespace)):
|
||||||
with self.assertRaisesMessage(ImproperlyConfigured, msg % whitespace):
|
route = "space/<int:num>/extra/<str:%stest>" % whitespace
|
||||||
path("space/<int:num>/extra/<str:%stest>" % whitespace, empty_view)
|
with self.assertRaisesMessage(ImproperlyConfigured, msg % route):
|
||||||
|
path(route, empty_view)
|
||||||
# Whitespaces are valid in paths.
|
# Whitespaces are valid in paths.
|
||||||
p = path("space%s/<int:num>/" % string.whitespace, empty_view)
|
p = path("space%s/<int:num>/" % string.whitespace, empty_view)
|
||||||
match = p.resolve("space%s/1/" % string.whitespace)
|
match = p.resolve("space%s/1/" % string.whitespace)
|
||||||
|
|
Loading…
Reference in New Issue