mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	Fixed #35252 -- Optimized _route_to_regex().
co-authored-by: Nick Pope <nick@nickpope.me.uk>
This commit is contained in:
		
				
					committed by
					
						 Mariusz Felisiak
						Mariusz Felisiak
					
				
			
			
				
	
			
			
			
						parent
						
							241adf678f
						
					
				
				
					commit
					eff21d8e7a
				
			| @@ -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] | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -393,6 +393,9 @@ Miscellaneous | ||||
|   :py:class:`html.parser.HTMLParser` subclasses. This results in a more robust | ||||
|   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: | ||||
|  | ||||
| Features deprecated in 5.1 | ||||
|   | ||||
| @@ -246,14 +246,12 @@ class SimplifiedURLTests(SimpleTestCase): | ||||
|             path("foo", EmptyCBV()) | ||||
|  | ||||
|     def test_whitespace_in_route(self): | ||||
|         msg = ( | ||||
|             "URL route 'space/<int:num>/extra/<str:%stest>' cannot contain " | ||||
|             "whitespace in angle brackets <…>" | ||||
|         ) | ||||
|         msg = "URL route %r cannot contain whitespace in angle brackets <…>" | ||||
|         for whitespace in string.whitespace: | ||||
|             with self.subTest(repr(whitespace)): | ||||
|                 with self.assertRaisesMessage(ImproperlyConfigured, msg % whitespace): | ||||
|                     path("space/<int:num>/extra/<str:%stest>" % whitespace, empty_view) | ||||
|                 route = "space/<int:num>/extra/<str:%stest>" % whitespace | ||||
|                 with self.assertRaisesMessage(ImproperlyConfigured, msg % route): | ||||
|                     path(route, empty_view) | ||||
|         # Whitespaces are valid in paths. | ||||
|         p = path("space%s/<int:num>/" % string.whitespace, empty_view) | ||||
|         match = p.resolve("space%s/1/" % string.whitespace) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user