Ajoutez des fichiers projet.
This commit is contained in:
23
venv/Lib/site-packages/django/urls/__init__.py
Normal file
23
venv/Lib/site-packages/django/urls/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from .base import (
|
||||
clear_script_prefix, clear_url_caches, get_script_prefix, get_urlconf,
|
||||
is_valid_path, resolve, reverse, reverse_lazy, set_script_prefix,
|
||||
set_urlconf, translate_url,
|
||||
)
|
||||
from .conf import include, path, re_path
|
||||
from .converters import register_converter
|
||||
from .exceptions import NoReverseMatch, Resolver404
|
||||
from .resolvers import (
|
||||
LocalePrefixPattern, ResolverMatch, URLPattern, URLResolver,
|
||||
get_ns_resolver, get_resolver,
|
||||
)
|
||||
from .utils import get_callable, get_mod_func
|
||||
|
||||
__all__ = [
|
||||
'LocalePrefixPattern', 'NoReverseMatch', 'URLPattern',
|
||||
'URLResolver', 'Resolver404', 'ResolverMatch', 'clear_script_prefix',
|
||||
'clear_url_caches', 'get_callable', 'get_mod_func', 'get_ns_resolver',
|
||||
'get_resolver', 'get_script_prefix', 'get_urlconf', 'include',
|
||||
'is_valid_path', 'path', 're_path', 'register_converter', 'resolve',
|
||||
'reverse', 'reverse_lazy', 'set_script_prefix', 'set_urlconf',
|
||||
'translate_url',
|
||||
]
|
179
venv/Lib/site-packages/django/urls/base.py
Normal file
179
venv/Lib/site-packages/django/urls/base.py
Normal file
@@ -0,0 +1,179 @@
|
||||
from urllib.parse import unquote, urlsplit, urlunsplit
|
||||
|
||||
from asgiref.local import Local
|
||||
|
||||
from django.utils.functional import lazy
|
||||
from django.utils.translation import override
|
||||
|
||||
from .exceptions import NoReverseMatch, Resolver404
|
||||
from .resolvers import _get_cached_resolver, get_ns_resolver, get_resolver
|
||||
from .utils import get_callable
|
||||
|
||||
# SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for
|
||||
# the current thread (which is the only one we ever access), it is assumed to
|
||||
# be empty.
|
||||
_prefixes = Local()
|
||||
|
||||
# Overridden URLconfs for each thread are stored here.
|
||||
_urlconfs = Local()
|
||||
|
||||
|
||||
def resolve(path, urlconf=None):
|
||||
if urlconf is None:
|
||||
urlconf = get_urlconf()
|
||||
return get_resolver(urlconf).resolve(path)
|
||||
|
||||
|
||||
def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None):
|
||||
if urlconf is None:
|
||||
urlconf = get_urlconf()
|
||||
resolver = get_resolver(urlconf)
|
||||
args = args or []
|
||||
kwargs = kwargs or {}
|
||||
|
||||
prefix = get_script_prefix()
|
||||
|
||||
if not isinstance(viewname, str):
|
||||
view = viewname
|
||||
else:
|
||||
*path, view = viewname.split(':')
|
||||
|
||||
if current_app:
|
||||
current_path = current_app.split(':')
|
||||
current_path.reverse()
|
||||
else:
|
||||
current_path = None
|
||||
|
||||
resolved_path = []
|
||||
ns_pattern = ''
|
||||
ns_converters = {}
|
||||
for ns in path:
|
||||
current_ns = current_path.pop() if current_path else None
|
||||
# Lookup the name to see if it could be an app identifier.
|
||||
try:
|
||||
app_list = resolver.app_dict[ns]
|
||||
# Yes! Path part matches an app in the current Resolver.
|
||||
if current_ns and current_ns in app_list:
|
||||
# If we are reversing for a particular app, use that
|
||||
# namespace.
|
||||
ns = current_ns
|
||||
elif ns not in app_list:
|
||||
# The name isn't shared by one of the instances (i.e.,
|
||||
# the default) so pick the first instance as the default.
|
||||
ns = app_list[0]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if ns != current_ns:
|
||||
current_path = None
|
||||
|
||||
try:
|
||||
extra, resolver = resolver.namespace_dict[ns]
|
||||
resolved_path.append(ns)
|
||||
ns_pattern = ns_pattern + extra
|
||||
ns_converters.update(resolver.pattern.converters)
|
||||
except KeyError as key:
|
||||
if resolved_path:
|
||||
raise NoReverseMatch(
|
||||
"%s is not a registered namespace inside '%s'" %
|
||||
(key, ':'.join(resolved_path))
|
||||
)
|
||||
else:
|
||||
raise NoReverseMatch("%s is not a registered namespace" % key)
|
||||
if ns_pattern:
|
||||
resolver = get_ns_resolver(ns_pattern, resolver, tuple(ns_converters.items()))
|
||||
|
||||
return resolver._reverse_with_prefix(view, prefix, *args, **kwargs)
|
||||
|
||||
|
||||
reverse_lazy = lazy(reverse, str)
|
||||
|
||||
|
||||
def clear_url_caches():
|
||||
get_callable.cache_clear()
|
||||
_get_cached_resolver.cache_clear()
|
||||
get_ns_resolver.cache_clear()
|
||||
|
||||
|
||||
def set_script_prefix(prefix):
|
||||
"""
|
||||
Set the script prefix for the current thread.
|
||||
"""
|
||||
if not prefix.endswith('/'):
|
||||
prefix += '/'
|
||||
_prefixes.value = prefix
|
||||
|
||||
|
||||
def get_script_prefix():
|
||||
"""
|
||||
Return the currently active script prefix. Useful for client code that
|
||||
wishes to construct their own URLs manually (although accessing the request
|
||||
instance is normally going to be a lot cleaner).
|
||||
"""
|
||||
return getattr(_prefixes, "value", '/')
|
||||
|
||||
|
||||
def clear_script_prefix():
|
||||
"""
|
||||
Unset the script prefix for the current thread.
|
||||
"""
|
||||
try:
|
||||
del _prefixes.value
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def set_urlconf(urlconf_name):
|
||||
"""
|
||||
Set the URLconf for the current thread (overriding the default one in
|
||||
settings). If urlconf_name is None, revert back to the default.
|
||||
"""
|
||||
if urlconf_name:
|
||||
_urlconfs.value = urlconf_name
|
||||
else:
|
||||
if hasattr(_urlconfs, "value"):
|
||||
del _urlconfs.value
|
||||
|
||||
|
||||
def get_urlconf(default=None):
|
||||
"""
|
||||
Return the root URLconf to use for the current thread if it has been
|
||||
changed from the default one.
|
||||
"""
|
||||
return getattr(_urlconfs, "value", default)
|
||||
|
||||
|
||||
def is_valid_path(path, urlconf=None):
|
||||
"""
|
||||
Return the ResolverMatch if the given path resolves against the default URL
|
||||
resolver, False otherwise. This is a convenience method to make working
|
||||
with "is this a match?" cases easier, avoiding try...except blocks.
|
||||
"""
|
||||
try:
|
||||
return resolve(path, urlconf)
|
||||
except Resolver404:
|
||||
return False
|
||||
|
||||
|
||||
def translate_url(url, lang_code):
|
||||
"""
|
||||
Given a URL (absolute or relative), try to get its translated version in
|
||||
the `lang_code` language (either by i18n_patterns or by translated regex).
|
||||
Return the original URL if no translated version is found.
|
||||
"""
|
||||
parsed = urlsplit(url)
|
||||
try:
|
||||
# URL may be encoded.
|
||||
match = resolve(unquote(parsed.path))
|
||||
except Resolver404:
|
||||
pass
|
||||
else:
|
||||
to_be_reversed = "%s:%s" % (match.namespace, match.url_name) if match.namespace else match.url_name
|
||||
with override(lang_code):
|
||||
try:
|
||||
url = reverse(to_be_reversed, args=match.args, kwargs=match.kwargs)
|
||||
except NoReverseMatch:
|
||||
pass
|
||||
else:
|
||||
url = urlunsplit((parsed.scheme, parsed.netloc, url, parsed.query, parsed.fragment))
|
||||
return url
|
85
venv/Lib/site-packages/django/urls/conf.py
Normal file
85
venv/Lib/site-packages/django/urls/conf.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""Functions for use in URLsconfs."""
|
||||
from functools import partial
|
||||
from importlib import import_module
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from .resolvers import (
|
||||
LocalePrefixPattern, RegexPattern, RoutePattern, URLPattern, URLResolver,
|
||||
)
|
||||
|
||||
|
||||
def include(arg, namespace=None):
|
||||
app_name = None
|
||||
if isinstance(arg, tuple):
|
||||
# Callable returning a namespace hint.
|
||||
try:
|
||||
urlconf_module, app_name = arg
|
||||
except ValueError:
|
||||
if namespace:
|
||||
raise ImproperlyConfigured(
|
||||
'Cannot override the namespace for a dynamic module that '
|
||||
'provides a namespace.'
|
||||
)
|
||||
raise ImproperlyConfigured(
|
||||
'Passing a %d-tuple to include() is not supported. Pass a '
|
||||
'2-tuple containing the list of patterns and app_name, and '
|
||||
'provide the namespace argument to include() instead.' % len(arg)
|
||||
)
|
||||
else:
|
||||
# No namespace hint - use manually provided namespace.
|
||||
urlconf_module = arg
|
||||
|
||||
if isinstance(urlconf_module, str):
|
||||
urlconf_module = import_module(urlconf_module)
|
||||
patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
|
||||
app_name = getattr(urlconf_module, 'app_name', app_name)
|
||||
if namespace and not app_name:
|
||||
raise ImproperlyConfigured(
|
||||
'Specifying a namespace in include() without providing an app_name '
|
||||
'is not supported. Set the app_name attribute in the included '
|
||||
'module, or pass a 2-tuple containing the list of patterns and '
|
||||
'app_name instead.',
|
||||
)
|
||||
namespace = namespace or app_name
|
||||
# Make sure the patterns can be iterated through (without this, some
|
||||
# testcases will break).
|
||||
if isinstance(patterns, (list, tuple)):
|
||||
for url_pattern in patterns:
|
||||
pattern = getattr(url_pattern, 'pattern', None)
|
||||
if isinstance(pattern, LocalePrefixPattern):
|
||||
raise ImproperlyConfigured(
|
||||
'Using i18n_patterns in an included URLconf is not allowed.'
|
||||
)
|
||||
return (urlconf_module, app_name, namespace)
|
||||
|
||||
|
||||
def _path(route, view, kwargs=None, name=None, Pattern=None):
|
||||
from django.views import View
|
||||
|
||||
if isinstance(view, (list, tuple)):
|
||||
# For include(...) processing.
|
||||
pattern = Pattern(route, is_endpoint=False)
|
||||
urlconf_module, app_name, namespace = view
|
||||
return URLResolver(
|
||||
pattern,
|
||||
urlconf_module,
|
||||
kwargs,
|
||||
app_name=app_name,
|
||||
namespace=namespace,
|
||||
)
|
||||
elif callable(view):
|
||||
pattern = Pattern(route, name=name, is_endpoint=True)
|
||||
return URLPattern(pattern, view, kwargs, name)
|
||||
elif isinstance(view, View):
|
||||
view_cls_name = view.__class__.__name__
|
||||
raise TypeError(
|
||||
f'view must be a callable, pass {view_cls_name}.as_view(), not '
|
||||
f'{view_cls_name}().'
|
||||
)
|
||||
else:
|
||||
raise TypeError('view must be a callable or a list/tuple in the case of include().')
|
||||
|
||||
|
||||
path = partial(_path, Pattern=RoutePattern)
|
||||
re_path = partial(_path, Pattern=RegexPattern)
|
66
venv/Lib/site-packages/django/urls/converters.py
Normal file
66
venv/Lib/site-packages/django/urls/converters.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import uuid
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
class IntConverter:
|
||||
regex = '[0-9]+'
|
||||
|
||||
def to_python(self, value):
|
||||
return int(value)
|
||||
|
||||
def to_url(self, value):
|
||||
return str(value)
|
||||
|
||||
|
||||
class StringConverter:
|
||||
regex = '[^/]+'
|
||||
|
||||
def to_python(self, value):
|
||||
return value
|
||||
|
||||
def to_url(self, value):
|
||||
return value
|
||||
|
||||
|
||||
class UUIDConverter:
|
||||
regex = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
|
||||
|
||||
def to_python(self, value):
|
||||
return uuid.UUID(value)
|
||||
|
||||
def to_url(self, value):
|
||||
return str(value)
|
||||
|
||||
|
||||
class SlugConverter(StringConverter):
|
||||
regex = '[-a-zA-Z0-9_]+'
|
||||
|
||||
|
||||
class PathConverter(StringConverter):
|
||||
regex = '.+'
|
||||
|
||||
|
||||
DEFAULT_CONVERTERS = {
|
||||
'int': IntConverter(),
|
||||
'path': PathConverter(),
|
||||
'slug': SlugConverter(),
|
||||
'str': StringConverter(),
|
||||
'uuid': UUIDConverter(),
|
||||
}
|
||||
|
||||
|
||||
REGISTERED_CONVERTERS = {}
|
||||
|
||||
|
||||
def register_converter(converter, type_name):
|
||||
REGISTERED_CONVERTERS[type_name] = converter()
|
||||
get_converters.cache_clear()
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def get_converters():
|
||||
return {**DEFAULT_CONVERTERS, **REGISTERED_CONVERTERS}
|
||||
|
||||
|
||||
def get_converter(raw_converter):
|
||||
return get_converters()[raw_converter]
|
9
venv/Lib/site-packages/django/urls/exceptions.py
Normal file
9
venv/Lib/site-packages/django/urls/exceptions.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.http import Http404
|
||||
|
||||
|
||||
class Resolver404(Http404):
|
||||
pass
|
||||
|
||||
|
||||
class NoReverseMatch(Exception):
|
||||
pass
|
729
venv/Lib/site-packages/django/urls/resolvers.py
Normal file
729
venv/Lib/site-packages/django/urls/resolvers.py
Normal file
@@ -0,0 +1,729 @@
|
||||
"""
|
||||
This module converts requested URLs to callback view functions.
|
||||
|
||||
URLResolver is the main class here. Its resolve() method takes a URL (as
|
||||
a string) and returns a ResolverMatch object which provides access to all
|
||||
attributes of the resolved URL match.
|
||||
"""
|
||||
import functools
|
||||
import inspect
|
||||
import re
|
||||
import string
|
||||
from importlib import import_module
|
||||
from pickle import PicklingError
|
||||
from urllib.parse import quote
|
||||
|
||||
from asgiref.local import Local
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.checks import Error, Warning
|
||||
from django.core.checks.urls import check_resolver
|
||||
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.functional import cached_property
|
||||
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 .exceptions import NoReverseMatch, Resolver404
|
||||
from .utils import get_callable
|
||||
|
||||
|
||||
class ResolverMatch:
|
||||
def __init__(self, func, args, kwargs, url_name=None, app_names=None, namespaces=None, route=None, tried=None):
|
||||
self.func = func
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.url_name = url_name
|
||||
self.route = route
|
||||
self.tried = tried
|
||||
|
||||
# If a URLRegexResolver doesn't have a namespace or app_name, it passes
|
||||
# in an empty value.
|
||||
self.app_names = [x for x in app_names if x] if app_names else []
|
||||
self.app_name = ':'.join(self.app_names)
|
||||
self.namespaces = [x for x in namespaces if x] if namespaces else []
|
||||
self.namespace = ':'.join(self.namespaces)
|
||||
|
||||
if not hasattr(func, '__name__'):
|
||||
# A class-based view
|
||||
self._func_path = func.__class__.__module__ + '.' + func.__class__.__name__
|
||||
else:
|
||||
# A function-based view
|
||||
self._func_path = func.__module__ + '.' + func.__name__
|
||||
|
||||
view_path = url_name or self._func_path
|
||||
self.view_name = ':'.join(self.namespaces + [view_path])
|
||||
|
||||
def __getitem__(self, index):
|
||||
return (self.func, self.args, self.kwargs)[index]
|
||||
|
||||
def __repr__(self):
|
||||
if isinstance(self.func, functools.partial):
|
||||
func = repr(self.func)
|
||||
else:
|
||||
func = self._func_path
|
||||
return (
|
||||
'ResolverMatch(func=%s, args=%r, kwargs=%r, url_name=%r, '
|
||||
'app_names=%r, namespaces=%r, route=%r)' % (
|
||||
func, self.args, self.kwargs, self.url_name,
|
||||
self.app_names, self.namespaces, self.route,
|
||||
)
|
||||
)
|
||||
|
||||
def __reduce_ex__(self, protocol):
|
||||
raise PicklingError(f'Cannot pickle {self.__class__.__qualname__}.')
|
||||
|
||||
|
||||
def get_resolver(urlconf=None):
|
||||
if urlconf is None:
|
||||
urlconf = settings.ROOT_URLCONF
|
||||
return _get_cached_resolver(urlconf)
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def _get_cached_resolver(urlconf=None):
|
||||
return URLResolver(RegexPattern(r'^/'), urlconf)
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def get_ns_resolver(ns_pattern, resolver, converters):
|
||||
# Build a namespaced resolver for the given parent URLconf pattern.
|
||||
# This makes it possible to have captured parameters in the parent
|
||||
# URLconf pattern.
|
||||
pattern = RegexPattern(ns_pattern)
|
||||
pattern.converters = dict(converters)
|
||||
ns_resolver = URLResolver(pattern, resolver.url_patterns)
|
||||
return URLResolver(RegexPattern(r'^/'), [ns_resolver])
|
||||
|
||||
|
||||
class LocaleRegexDescriptor:
|
||||
def __init__(self, attr):
|
||||
self.attr = attr
|
||||
|
||||
def __get__(self, instance, cls=None):
|
||||
"""
|
||||
Return a compiled regular expression based on the active language.
|
||||
"""
|
||||
if instance is None:
|
||||
return self
|
||||
# As a performance optimization, if the given regex string is a regular
|
||||
# string (not a lazily-translated string proxy), compile it once and
|
||||
# avoid per-language compilation.
|
||||
pattern = getattr(instance, self.attr)
|
||||
if isinstance(pattern, str):
|
||||
instance.__dict__['regex'] = instance._compile(pattern)
|
||||
return instance.__dict__['regex']
|
||||
language_code = get_language()
|
||||
if language_code not in instance._regex_dict:
|
||||
instance._regex_dict[language_code] = instance._compile(str(pattern))
|
||||
return instance._regex_dict[language_code]
|
||||
|
||||
|
||||
class CheckURLMixin:
|
||||
def describe(self):
|
||||
"""
|
||||
Format the URL pattern for display in warning messages.
|
||||
"""
|
||||
description = "'{}'".format(self)
|
||||
if self.name:
|
||||
description += " [name='{}']".format(self.name)
|
||||
return description
|
||||
|
||||
def _check_pattern_startswith_slash(self):
|
||||
"""
|
||||
Check that the pattern does not begin with a forward slash.
|
||||
"""
|
||||
regex_pattern = self.regex.pattern
|
||||
if not settings.APPEND_SLASH:
|
||||
# Skip check as it can be useful to start a URL pattern with a slash
|
||||
# when APPEND_SLASH=False.
|
||||
return []
|
||||
if regex_pattern.startswith(('/', '^/', '^\\/')) and not regex_pattern.endswith('/'):
|
||||
warning = Warning(
|
||||
"Your URL pattern {} has a route beginning with a '/'. Remove this "
|
||||
"slash as it is unnecessary. If this pattern is targeted in an "
|
||||
"include(), ensure the include() pattern has a trailing '/'.".format(
|
||||
self.describe()
|
||||
),
|
||||
id="urls.W002",
|
||||
)
|
||||
return [warning]
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
class RegexPattern(CheckURLMixin):
|
||||
regex = LocaleRegexDescriptor('_regex')
|
||||
|
||||
def __init__(self, regex, name=None, is_endpoint=False):
|
||||
self._regex = regex
|
||||
self._regex_dict = {}
|
||||
self._is_endpoint = is_endpoint
|
||||
self.name = name
|
||||
self.converters = {}
|
||||
|
||||
def match(self, path):
|
||||
match = (
|
||||
self.regex.fullmatch(path)
|
||||
if self._is_endpoint and self.regex.pattern.endswith('$')
|
||||
else self.regex.search(path)
|
||||
)
|
||||
if match:
|
||||
# If there are any named groups, use those as kwargs, ignoring
|
||||
# non-named groups. Otherwise, pass all non-named arguments as
|
||||
# positional arguments.
|
||||
kwargs = match.groupdict()
|
||||
args = () if kwargs else match.groups()
|
||||
kwargs = {k: v for k, v in kwargs.items() if v is not None}
|
||||
return path[match.end():], args, kwargs
|
||||
return None
|
||||
|
||||
def check(self):
|
||||
warnings = []
|
||||
warnings.extend(self._check_pattern_startswith_slash())
|
||||
if not self._is_endpoint:
|
||||
warnings.extend(self._check_include_trailing_dollar())
|
||||
return warnings
|
||||
|
||||
def _check_include_trailing_dollar(self):
|
||||
regex_pattern = self.regex.pattern
|
||||
if regex_pattern.endswith('$') and not regex_pattern.endswith(r'\$'):
|
||||
return [Warning(
|
||||
"Your URL pattern {} uses include with a route ending with a '$'. "
|
||||
"Remove the dollar from the route to avoid problems including "
|
||||
"URLs.".format(self.describe()),
|
||||
id='urls.W001',
|
||||
)]
|
||||
else:
|
||||
return []
|
||||
|
||||
def _compile(self, regex):
|
||||
"""Compile and return the given regular expression."""
|
||||
try:
|
||||
return re.compile(regex)
|
||||
except re.error as e:
|
||||
raise ImproperlyConfigured(
|
||||
'"%s" is not a valid regular expression: %s' % (regex, e)
|
||||
) from e
|
||||
|
||||
def __str__(self):
|
||||
return str(self._regex)
|
||||
|
||||
|
||||
_PATH_PARAMETER_COMPONENT_RE = _lazy_re_compile(
|
||||
r'<(?:(?P<converter>[^>:]+):)?(?P<parameter>[^>]+)>'
|
||||
)
|
||||
|
||||
|
||||
def _route_to_regex(route, is_endpoint=False):
|
||||
"""
|
||||
Convert a path pattern into a regular expression. Return the regular
|
||||
expression and a dictionary mapping the capture names to the converters.
|
||||
For example, 'foo/<int:pk>' returns '^foo\\/(?P<pk>[0-9]+)'
|
||||
and {'pk': <django.urls.converters.IntConverter>}.
|
||||
"""
|
||||
original_route = route
|
||||
parts = ['^']
|
||||
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):
|
||||
raise ImproperlyConfigured(
|
||||
"URL route '%s' cannot contain whitespace in angle brackets "
|
||||
"<…>." % original_route
|
||||
)
|
||||
parts.append(re.escape(route[:match.start()]))
|
||||
route = route[match.end():]
|
||||
parameter = match['parameter']
|
||||
if not parameter.isidentifier():
|
||||
raise ImproperlyConfigured(
|
||||
"URL route '%s' uses parameter name %r which isn't a valid "
|
||||
"Python identifier." % (original_route, parameter)
|
||||
)
|
||||
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)
|
||||
except KeyError as e:
|
||||
raise ImproperlyConfigured(
|
||||
'URL route %r uses invalid converter %r.'
|
||||
% (original_route, raw_converter)
|
||||
) from e
|
||||
converters[parameter] = converter
|
||||
parts.append('(?P<' + parameter + '>' + converter.regex + ')')
|
||||
if is_endpoint:
|
||||
parts.append(r'\Z')
|
||||
return ''.join(parts), converters
|
||||
|
||||
|
||||
class RoutePattern(CheckURLMixin):
|
||||
regex = LocaleRegexDescriptor('_route')
|
||||
|
||||
def __init__(self, route, name=None, is_endpoint=False):
|
||||
self._route = route
|
||||
self._regex_dict = {}
|
||||
self._is_endpoint = is_endpoint
|
||||
self.name = name
|
||||
self.converters = _route_to_regex(str(route), is_endpoint)[1]
|
||||
|
||||
def match(self, path):
|
||||
match = self.regex.search(path)
|
||||
if match:
|
||||
# RoutePattern doesn't allow non-named groups so args are ignored.
|
||||
kwargs = match.groupdict()
|
||||
for key, value in kwargs.items():
|
||||
converter = self.converters[key]
|
||||
try:
|
||||
kwargs[key] = converter.to_python(value)
|
||||
except ValueError:
|
||||
return None
|
||||
return path[match.end():], (), kwargs
|
||||
return None
|
||||
|
||||
def check(self):
|
||||
warnings = self._check_pattern_startswith_slash()
|
||||
route = self._route
|
||||
if '(?P<' in route or route.startswith('^') or route.endswith('$'):
|
||||
warnings.append(Warning(
|
||||
"Your URL pattern {} has a route that contains '(?P<', begins "
|
||||
"with a '^', or ends with a '$'. This was likely an oversight "
|
||||
"when migrating to django.urls.path().".format(self.describe()),
|
||||
id='2_0.W001',
|
||||
))
|
||||
return warnings
|
||||
|
||||
def _compile(self, route):
|
||||
return re.compile(_route_to_regex(route, self._is_endpoint)[0])
|
||||
|
||||
def __str__(self):
|
||||
return str(self._route)
|
||||
|
||||
|
||||
class LocalePrefixPattern:
|
||||
def __init__(self, prefix_default_language=True):
|
||||
self.prefix_default_language = prefix_default_language
|
||||
self.converters = {}
|
||||
|
||||
@property
|
||||
def regex(self):
|
||||
# This is only used by reverse() and cached in _reverse_dict.
|
||||
return re.compile(self.language_prefix)
|
||||
|
||||
@property
|
||||
def language_prefix(self):
|
||||
language_code = get_language() or settings.LANGUAGE_CODE
|
||||
if language_code == settings.LANGUAGE_CODE and not self.prefix_default_language:
|
||||
return ''
|
||||
else:
|
||||
return '%s/' % language_code
|
||||
|
||||
def match(self, path):
|
||||
language_prefix = self.language_prefix
|
||||
if path.startswith(language_prefix):
|
||||
return path[len(language_prefix):], (), {}
|
||||
return None
|
||||
|
||||
def check(self):
|
||||
return []
|
||||
|
||||
def describe(self):
|
||||
return "'{}'".format(self)
|
||||
|
||||
def __str__(self):
|
||||
return self.language_prefix
|
||||
|
||||
|
||||
class URLPattern:
|
||||
def __init__(self, pattern, callback, default_args=None, name=None):
|
||||
self.pattern = pattern
|
||||
self.callback = callback # the view
|
||||
self.default_args = default_args or {}
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %s>' % (self.__class__.__name__, self.pattern.describe())
|
||||
|
||||
def check(self):
|
||||
warnings = self._check_pattern_name()
|
||||
warnings.extend(self.pattern.check())
|
||||
warnings.extend(self._check_callback())
|
||||
return warnings
|
||||
|
||||
def _check_pattern_name(self):
|
||||
"""
|
||||
Check that the pattern name does not contain a colon.
|
||||
"""
|
||||
if self.pattern.name is not None and ":" in self.pattern.name:
|
||||
warning = Warning(
|
||||
"Your URL pattern {} has a name including a ':'. Remove the colon, to "
|
||||
"avoid ambiguous namespace references.".format(self.pattern.describe()),
|
||||
id="urls.W003",
|
||||
)
|
||||
return [warning]
|
||||
else:
|
||||
return []
|
||||
|
||||
def _check_callback(self):
|
||||
from django.views import View
|
||||
|
||||
view = self.callback
|
||||
if inspect.isclass(view) and issubclass(view, View):
|
||||
return [Error(
|
||||
'Your URL pattern %s has an invalid view, pass %s.as_view() '
|
||||
'instead of %s.' % (
|
||||
self.pattern.describe(),
|
||||
view.__name__,
|
||||
view.__name__,
|
||||
),
|
||||
id='urls.E009',
|
||||
)]
|
||||
return []
|
||||
|
||||
def resolve(self, path):
|
||||
match = self.pattern.match(path)
|
||||
if match:
|
||||
new_path, args, kwargs = match
|
||||
# Pass any extra_kwargs as **kwargs.
|
||||
kwargs.update(self.default_args)
|
||||
return ResolverMatch(self.callback, args, kwargs, self.pattern.name, route=str(self.pattern))
|
||||
|
||||
@cached_property
|
||||
def lookup_str(self):
|
||||
"""
|
||||
A string that identifies the view (e.g. 'path.to.view_function' or
|
||||
'path.to.ClassBasedView').
|
||||
"""
|
||||
callback = self.callback
|
||||
if isinstance(callback, functools.partial):
|
||||
callback = callback.func
|
||||
if hasattr(callback, 'view_class'):
|
||||
callback = callback.view_class
|
||||
elif not hasattr(callback, '__name__'):
|
||||
return callback.__module__ + "." + callback.__class__.__name__
|
||||
return callback.__module__ + "." + callback.__qualname__
|
||||
|
||||
|
||||
class URLResolver:
|
||||
def __init__(self, pattern, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
|
||||
self.pattern = pattern
|
||||
# urlconf_name is the dotted Python path to the module defining
|
||||
# urlpatterns. It may also be an object with an urlpatterns attribute
|
||||
# or urlpatterns itself.
|
||||
self.urlconf_name = urlconf_name
|
||||
self.callback = None
|
||||
self.default_kwargs = default_kwargs or {}
|
||||
self.namespace = namespace
|
||||
self.app_name = app_name
|
||||
self._reverse_dict = {}
|
||||
self._namespace_dict = {}
|
||||
self._app_dict = {}
|
||||
# set of dotted paths to all functions and classes that are used in
|
||||
# urlpatterns
|
||||
self._callback_strs = set()
|
||||
self._populated = False
|
||||
self._local = Local()
|
||||
|
||||
def __repr__(self):
|
||||
if isinstance(self.urlconf_name, list) and self.urlconf_name:
|
||||
# Don't bother to output the whole list, it can be huge
|
||||
urlconf_repr = '<%s list>' % self.urlconf_name[0].__class__.__name__
|
||||
else:
|
||||
urlconf_repr = repr(self.urlconf_name)
|
||||
return '<%s %s (%s:%s) %s>' % (
|
||||
self.__class__.__name__, urlconf_repr, self.app_name,
|
||||
self.namespace, self.pattern.describe(),
|
||||
)
|
||||
|
||||
def check(self):
|
||||
messages = []
|
||||
for pattern in self.url_patterns:
|
||||
messages.extend(check_resolver(pattern))
|
||||
messages.extend(self._check_custom_error_handlers())
|
||||
return messages or self.pattern.check()
|
||||
|
||||
def _check_custom_error_handlers(self):
|
||||
messages = []
|
||||
# All handlers take (request, exception) arguments except handler500
|
||||
# which takes (request).
|
||||
for status_code, num_parameters in [(400, 2), (403, 2), (404, 2), (500, 1)]:
|
||||
try:
|
||||
handler = self.resolve_error_handler(status_code)
|
||||
except (ImportError, ViewDoesNotExist) as e:
|
||||
path = getattr(self.urlconf_module, 'handler%s' % status_code)
|
||||
msg = (
|
||||
"The custom handler{status_code} view '{path}' could not be imported."
|
||||
).format(status_code=status_code, path=path)
|
||||
messages.append(Error(msg, hint=str(e), id='urls.E008'))
|
||||
continue
|
||||
signature = inspect.signature(handler)
|
||||
args = [None] * num_parameters
|
||||
try:
|
||||
signature.bind(*args)
|
||||
except TypeError:
|
||||
msg = (
|
||||
"The custom handler{status_code} view '{path}' does not "
|
||||
"take the correct number of arguments ({args})."
|
||||
).format(
|
||||
status_code=status_code,
|
||||
path=handler.__module__ + '.' + handler.__qualname__,
|
||||
args='request, exception' if num_parameters == 2 else 'request',
|
||||
)
|
||||
messages.append(Error(msg, id='urls.E007'))
|
||||
return messages
|
||||
|
||||
def _populate(self):
|
||||
# Short-circuit if called recursively in this thread to prevent
|
||||
# infinite recursion. Concurrent threads may call this at the same
|
||||
# time and will need to continue, so set 'populating' on a
|
||||
# thread-local variable.
|
||||
if getattr(self._local, 'populating', False):
|
||||
return
|
||||
try:
|
||||
self._local.populating = True
|
||||
lookups = MultiValueDict()
|
||||
namespaces = {}
|
||||
apps = {}
|
||||
language_code = get_language()
|
||||
for url_pattern in reversed(self.url_patterns):
|
||||
p_pattern = url_pattern.pattern.regex.pattern
|
||||
if p_pattern.startswith('^'):
|
||||
p_pattern = p_pattern[1:]
|
||||
if isinstance(url_pattern, URLPattern):
|
||||
self._callback_strs.add(url_pattern.lookup_str)
|
||||
bits = normalize(url_pattern.pattern.regex.pattern)
|
||||
lookups.appendlist(
|
||||
url_pattern.callback,
|
||||
(bits, p_pattern, url_pattern.default_args, url_pattern.pattern.converters)
|
||||
)
|
||||
if url_pattern.name is not None:
|
||||
lookups.appendlist(
|
||||
url_pattern.name,
|
||||
(bits, p_pattern, url_pattern.default_args, url_pattern.pattern.converters)
|
||||
)
|
||||
else: # url_pattern is a URLResolver.
|
||||
url_pattern._populate()
|
||||
if url_pattern.app_name:
|
||||
apps.setdefault(url_pattern.app_name, []).append(url_pattern.namespace)
|
||||
namespaces[url_pattern.namespace] = (p_pattern, url_pattern)
|
||||
else:
|
||||
for name in url_pattern.reverse_dict:
|
||||
for matches, pat, defaults, converters in url_pattern.reverse_dict.getlist(name):
|
||||
new_matches = normalize(p_pattern + pat)
|
||||
lookups.appendlist(
|
||||
name,
|
||||
(
|
||||
new_matches,
|
||||
p_pattern + pat,
|
||||
{**defaults, **url_pattern.default_kwargs},
|
||||
{**self.pattern.converters, **url_pattern.pattern.converters, **converters}
|
||||
)
|
||||
)
|
||||
for namespace, (prefix, sub_pattern) in url_pattern.namespace_dict.items():
|
||||
current_converters = url_pattern.pattern.converters
|
||||
sub_pattern.pattern.converters.update(current_converters)
|
||||
namespaces[namespace] = (p_pattern + prefix, sub_pattern)
|
||||
for app_name, namespace_list in url_pattern.app_dict.items():
|
||||
apps.setdefault(app_name, []).extend(namespace_list)
|
||||
self._callback_strs.update(url_pattern._callback_strs)
|
||||
self._namespace_dict[language_code] = namespaces
|
||||
self._app_dict[language_code] = apps
|
||||
self._reverse_dict[language_code] = lookups
|
||||
self._populated = True
|
||||
finally:
|
||||
self._local.populating = False
|
||||
|
||||
@property
|
||||
def reverse_dict(self):
|
||||
language_code = get_language()
|
||||
if language_code not in self._reverse_dict:
|
||||
self._populate()
|
||||
return self._reverse_dict[language_code]
|
||||
|
||||
@property
|
||||
def namespace_dict(self):
|
||||
language_code = get_language()
|
||||
if language_code not in self._namespace_dict:
|
||||
self._populate()
|
||||
return self._namespace_dict[language_code]
|
||||
|
||||
@property
|
||||
def app_dict(self):
|
||||
language_code = get_language()
|
||||
if language_code not in self._app_dict:
|
||||
self._populate()
|
||||
return self._app_dict[language_code]
|
||||
|
||||
@staticmethod
|
||||
def _extend_tried(tried, pattern, sub_tried=None):
|
||||
if sub_tried is None:
|
||||
tried.append([pattern])
|
||||
else:
|
||||
tried.extend([pattern, *t] for t in sub_tried)
|
||||
|
||||
@staticmethod
|
||||
def _join_route(route1, route2):
|
||||
"""Join two routes, without the starting ^ in the second route."""
|
||||
if not route1:
|
||||
return route2
|
||||
if route2.startswith('^'):
|
||||
route2 = route2[1:]
|
||||
return route1 + route2
|
||||
|
||||
def _is_callback(self, name):
|
||||
if not self._populated:
|
||||
self._populate()
|
||||
return name in self._callback_strs
|
||||
|
||||
def resolve(self, path):
|
||||
path = str(path) # path may be a reverse_lazy object
|
||||
tried = []
|
||||
match = self.pattern.match(path)
|
||||
if match:
|
||||
new_path, args, kwargs = match
|
||||
for pattern in self.url_patterns:
|
||||
try:
|
||||
sub_match = pattern.resolve(new_path)
|
||||
except Resolver404 as e:
|
||||
self._extend_tried(tried, pattern, e.args[0].get('tried'))
|
||||
else:
|
||||
if sub_match:
|
||||
# Merge captured arguments in match with submatch
|
||||
sub_match_dict = {**kwargs, **self.default_kwargs}
|
||||
# Update the sub_match_dict with the kwargs from the sub_match.
|
||||
sub_match_dict.update(sub_match.kwargs)
|
||||
# If there are *any* named groups, ignore all non-named groups.
|
||||
# Otherwise, pass all non-named arguments as positional arguments.
|
||||
sub_match_args = sub_match.args
|
||||
if not sub_match_dict:
|
||||
sub_match_args = args + sub_match.args
|
||||
current_route = '' if isinstance(pattern, URLPattern) else str(pattern.pattern)
|
||||
self._extend_tried(tried, pattern, sub_match.tried)
|
||||
return ResolverMatch(
|
||||
sub_match.func,
|
||||
sub_match_args,
|
||||
sub_match_dict,
|
||||
sub_match.url_name,
|
||||
[self.app_name] + sub_match.app_names,
|
||||
[self.namespace] + sub_match.namespaces,
|
||||
self._join_route(current_route, sub_match.route),
|
||||
tried,
|
||||
)
|
||||
tried.append([pattern])
|
||||
raise Resolver404({'tried': tried, 'path': new_path})
|
||||
raise Resolver404({'path': path})
|
||||
|
||||
@cached_property
|
||||
def urlconf_module(self):
|
||||
if isinstance(self.urlconf_name, str):
|
||||
return import_module(self.urlconf_name)
|
||||
else:
|
||||
return self.urlconf_name
|
||||
|
||||
@cached_property
|
||||
def url_patterns(self):
|
||||
# urlconf_module might be a valid set of patterns, so we default to it
|
||||
patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
|
||||
try:
|
||||
iter(patterns)
|
||||
except TypeError as e:
|
||||
msg = (
|
||||
"The included URLconf '{name}' does not appear to have "
|
||||
"any patterns in it. If you see the 'urlpatterns' variable "
|
||||
"with valid patterns in the file then the issue is probably "
|
||||
"caused by a circular import."
|
||||
)
|
||||
raise ImproperlyConfigured(msg.format(name=self.urlconf_name)) from e
|
||||
return patterns
|
||||
|
||||
def resolve_error_handler(self, view_type):
|
||||
callback = getattr(self.urlconf_module, 'handler%s' % view_type, None)
|
||||
if not callback:
|
||||
# No handler specified in file; use lazy import, since
|
||||
# django.conf.urls imports this file.
|
||||
from django.conf import urls
|
||||
callback = getattr(urls, 'handler%s' % view_type)
|
||||
return get_callable(callback)
|
||||
|
||||
def reverse(self, lookup_view, *args, **kwargs):
|
||||
return self._reverse_with_prefix(lookup_view, '', *args, **kwargs)
|
||||
|
||||
def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs):
|
||||
if args and kwargs:
|
||||
raise ValueError("Don't mix *args and **kwargs in call to reverse()!")
|
||||
|
||||
if not self._populated:
|
||||
self._populate()
|
||||
|
||||
possibilities = self.reverse_dict.getlist(lookup_view)
|
||||
|
||||
for possibility, pattern, defaults, converters in possibilities:
|
||||
for result, params in possibility:
|
||||
if args:
|
||||
if len(args) != len(params):
|
||||
continue
|
||||
candidate_subs = dict(zip(params, args))
|
||||
else:
|
||||
if set(kwargs).symmetric_difference(params).difference(defaults):
|
||||
continue
|
||||
if any(kwargs.get(k, v) != v for k, v in defaults.items()):
|
||||
continue
|
||||
candidate_subs = kwargs
|
||||
# Convert the candidate subs to text using Converter.to_url().
|
||||
text_candidate_subs = {}
|
||||
match = True
|
||||
for k, v in candidate_subs.items():
|
||||
if k in converters:
|
||||
try:
|
||||
text_candidate_subs[k] = converters[k].to_url(v)
|
||||
except ValueError:
|
||||
match = False
|
||||
break
|
||||
else:
|
||||
text_candidate_subs[k] = str(v)
|
||||
if not match:
|
||||
continue
|
||||
# WSGI provides decoded URLs, without %xx escapes, and the URL
|
||||
# resolver operates on such URLs. First substitute arguments
|
||||
# without quoting to build a decoded URL and look for a match.
|
||||
# Then, if we have a match, redo the substitution with quoted
|
||||
# arguments in order to return a properly encoded URL.
|
||||
candidate_pat = _prefix.replace('%', '%%') + result
|
||||
if re.search('^%s%s' % (re.escape(_prefix), pattern), candidate_pat % text_candidate_subs):
|
||||
# safe characters from `pchar` definition of RFC 3986
|
||||
url = quote(candidate_pat % text_candidate_subs, safe=RFC3986_SUBDELIMS + '/~:@')
|
||||
# Don't allow construction of scheme relative urls.
|
||||
return escape_leading_slashes(url)
|
||||
# lookup_view can be URL name or callable, but callables are not
|
||||
# friendly in error messages.
|
||||
m = getattr(lookup_view, '__module__', None)
|
||||
n = getattr(lookup_view, '__name__', None)
|
||||
if m is not None and n is not None:
|
||||
lookup_view_s = "%s.%s" % (m, n)
|
||||
else:
|
||||
lookup_view_s = lookup_view
|
||||
|
||||
patterns = [pattern for (_, pattern, _, _) in possibilities]
|
||||
if patterns:
|
||||
if args:
|
||||
arg_msg = "arguments '%s'" % (args,)
|
||||
elif kwargs:
|
||||
arg_msg = "keyword arguments '%s'" % kwargs
|
||||
else:
|
||||
arg_msg = "no arguments"
|
||||
msg = (
|
||||
"Reverse for '%s' with %s not found. %d pattern(s) tried: %s" %
|
||||
(lookup_view_s, arg_msg, len(patterns), patterns)
|
||||
)
|
||||
else:
|
||||
msg = (
|
||||
"Reverse for '%(view)s' not found. '%(view)s' is not "
|
||||
"a valid view function or pattern name." % {'view': lookup_view_s}
|
||||
)
|
||||
raise NoReverseMatch(msg)
|
62
venv/Lib/site-packages/django/urls/utils.py
Normal file
62
venv/Lib/site-packages/django/urls/utils.py
Normal file
@@ -0,0 +1,62 @@
|
||||
import functools
|
||||
from importlib import import_module
|
||||
|
||||
from django.core.exceptions import ViewDoesNotExist
|
||||
from django.utils.module_loading import module_has_submodule
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def get_callable(lookup_view):
|
||||
"""
|
||||
Return a callable corresponding to lookup_view.
|
||||
* If lookup_view is already a callable, return it.
|
||||
* If lookup_view is a string import path that can be resolved to a callable,
|
||||
import that callable and return it, otherwise raise an exception
|
||||
(ImportError or ViewDoesNotExist).
|
||||
"""
|
||||
if callable(lookup_view):
|
||||
return lookup_view
|
||||
|
||||
if not isinstance(lookup_view, str):
|
||||
raise ViewDoesNotExist("'%s' is not a callable or a dot-notation path" % lookup_view)
|
||||
|
||||
mod_name, func_name = get_mod_func(lookup_view)
|
||||
if not func_name: # No '.' in lookup_view
|
||||
raise ImportError("Could not import '%s'. The path must be fully qualified." % lookup_view)
|
||||
|
||||
try:
|
||||
mod = import_module(mod_name)
|
||||
except ImportError:
|
||||
parentmod, submod = get_mod_func(mod_name)
|
||||
if submod and not module_has_submodule(import_module(parentmod), submod):
|
||||
raise ViewDoesNotExist(
|
||||
"Could not import '%s'. Parent module %s does not exist." %
|
||||
(lookup_view, mod_name)
|
||||
)
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
try:
|
||||
view_func = getattr(mod, func_name)
|
||||
except AttributeError:
|
||||
raise ViewDoesNotExist(
|
||||
"Could not import '%s'. View does not exist in module %s." %
|
||||
(lookup_view, mod_name)
|
||||
)
|
||||
else:
|
||||
if not callable(view_func):
|
||||
raise ViewDoesNotExist(
|
||||
"Could not import '%s.%s'. View is not callable." %
|
||||
(mod_name, func_name)
|
||||
)
|
||||
return view_func
|
||||
|
||||
|
||||
def get_mod_func(callback):
|
||||
# Convert 'django.views.news.stories.story_detail' to
|
||||
# ['django.views.news.stories', 'story_detail']
|
||||
try:
|
||||
dot = callback.rindex('.')
|
||||
except ValueError:
|
||||
return callback, ''
|
||||
return callback[:dot], callback[dot + 1:]
|
Reference in New Issue
Block a user