text: """
  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 hasattr(func, 'view_class'):
              func = func.view_class
          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)
  