Browse Source

Fixed #26013 -- Moved django.core.urlresolvers to django.urls.

Thanks to Tim Graham for the review.
Marten Kenbeek 9 years ago
parent
commit
16411b8400
100 changed files with 937 additions and 898 deletions
  1. 1 1
      django/__init__.py
  2. 1 1
      django/conf/urls/__init__.py
  3. 1 1
      django/conf/urls/i18n.py
  4. 1 1
      django/contrib/admin/models.py
  5. 1 1
      django/contrib/admin/options.py
  6. 1 1
      django/contrib/admin/sites.py
  7. 1 1
      django/contrib/admin/templatetags/admin_list.py
  8. 1 1
      django/contrib/admin/templatetags/admin_urls.py
  9. 1 1
      django/contrib/admin/utils.py
  10. 1 1
      django/contrib/admin/views/main.py
  11. 1 1
      django/contrib/admin/widgets.py
  12. 1 1
      django/contrib/admindocs/utils.py
  13. 5 5
      django/contrib/admindocs/views.py
  14. 1 1
      django/contrib/auth/admin.py
  15. 1 1
      django/contrib/auth/views.py
  16. 1 1
      django/contrib/flatpages/models.py
  17. 9 7
      django/contrib/gis/sitemaps/kml.py
  18. 7 6
      django/contrib/sitemaps/__init__.py
  19. 2 3
      django/contrib/sitemaps/views.py
  20. 2 2
      django/core/checks/urls.py
  21. 6 5
      django/core/handlers/base.py
  22. 1 1
      django/core/handlers/wsgi.py
  23. 7 638
      django/core/urlresolvers.py
  24. 3 3
      django/db/models/__init__.py
  25. 3 3
      django/middleware/common.py
  26. 1 1
      django/middleware/csrf.py
  27. 2 2
      django/middleware/locale.py
  28. 7 7
      django/shortcuts.py
  29. 1 1
      django/template/defaulttags.py
  30. 2 3
      django/test/client.py
  31. 1 1
      django/test/signals.py
  32. 1 1
      django/test/utils.py
  33. 20 0
      django/urls/__init__.py
  34. 185 0
      django/urls/base.py
  35. 11 0
      django/urls/exceptions.py
  36. 393 0
      django/urls/resolvers.py
  37. 64 0
      django/urls/utils.py
  38. 1 1
      django/views/debug.py
  39. 1 1
      django/views/generic/base.py
  40. 1 1
      django/views/i18n.py
  41. 1 1
      docs/internals/contributing/writing-code/coding-style.txt
  42. 3 2
      docs/internals/deprecation.txt
  43. 1 1
      docs/intro/tutorial03.txt
  44. 4 4
      docs/intro/tutorial04.txt
  45. 2 2
      docs/intro/tutorial05.txt
  46. 1 1
      docs/ref/class-based-views/generic-date-based.txt
  47. 2 2
      docs/ref/class-based-views/generic-editing.txt
  48. 4 5
      docs/ref/contrib/admin/index.txt
  49. 3 4
      docs/ref/contrib/sitemaps.txt
  50. 1 1
      docs/ref/contrib/syndication.txt
  51. 14 9
      docs/ref/exceptions.txt
  52. 4 4
      docs/ref/models/instances.txt
  53. 6 6
      docs/ref/request-response.txt
  54. 2 2
      docs/ref/templates/builtins.txt
  55. 2 2
      docs/ref/unicode.txt
  56. 27 23
      docs/ref/urlresolvers.txt
  57. 5 5
      docs/releases/1.1.txt
  58. 5 2
      docs/releases/1.10.txt
  59. 6 8
      docs/releases/1.4.11.txt
  60. 2 3
      docs/releases/1.4.12.txt
  61. 2 2
      docs/releases/1.4.14.txt
  62. 2 2
      docs/releases/1.4.txt
  63. 6 8
      docs/releases/1.5.6.txt
  64. 2 2
      docs/releases/1.5.7.txt
  65. 2 2
      docs/releases/1.5.9.txt
  66. 2 2
      docs/releases/1.5.txt
  67. 6 8
      docs/releases/1.6.3.txt
  68. 3 5
      docs/releases/1.6.4.txt
  69. 2 2
      docs/releases/1.6.6.txt
  70. 13 12
      docs/releases/1.6.txt
  71. 1 2
      docs/releases/1.7.3.txt
  72. 4 5
      docs/releases/1.8.txt
  73. 4 6
      docs/releases/1.9.txt
  74. 4 4
      docs/topics/class-based-views/generic-editing.txt
  75. 3 3
      docs/topics/class-based-views/mixins.txt
  76. 3 4
      docs/topics/http/shortcuts.txt
  77. 4 5
      docs/topics/http/urls.txt
  78. 4 5
      docs/topics/i18n/translation.txt
  79. 1 1
      docs/topics/templates.txt
  80. 3 4
      docs/topics/testing/tools.txt
  81. 1 1
      tests/admin_changelist/tests.py
  82. 1 1
      tests/admin_custom_urls/models.py
  83. 1 1
      tests/admin_custom_urls/tests.py
  84. 1 1
      tests/admin_docs/tests.py
  85. 1 1
      tests/admin_inlines/tests.py
  86. 1 1
      tests/admin_utils/test_logentry.py
  87. 1 1
      tests/admin_views/test_adminsite.py
  88. 1 1
      tests/admin_views/tests.py
  89. 1 1
      tests/admin_widgets/tests.py
  90. 1 1
      tests/auth_tests/test_views.py
  91. 1 1
      tests/forms_tests/tests/test_widgets.py
  92. 1 1
      tests/generic_inline_admin/tests.py
  93. 1 1
      tests/generic_views/models.py
  94. 1 1
      tests/generic_views/test_base.py
  95. 1 1
      tests/generic_views/test_edit.py
  96. 1 1
      tests/generic_views/views.py
  97. 1 1
      tests/i18n/patterns/tests.py
  98. 1 1
      tests/messages_tests/base.py
  99. 1 1
      tests/messages_tests/test_mixins.py
  100. 1 1
      tests/messages_tests/urls.py

+ 1 - 1
django/__init__.py

@@ -15,7 +15,7 @@ def setup(set_prefix=True):
     """
     from django.apps import apps
     from django.conf import settings
-    from django.core.urlresolvers import set_script_prefix
+    from django.urls import set_script_prefix
     from django.utils.encoding import force_text
     from django.utils.log import configure_logging
 

+ 1 - 1
django/conf/urls/__init__.py

@@ -2,7 +2,7 @@ import warnings
 from importlib import import_module
 
 from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import (
+from django.urls import (
     LocaleRegexURLResolver, RegexURLPattern, RegexURLResolver,
 )
 from django.utils import six

+ 1 - 1
django/conf/urls/i18n.py

@@ -1,6 +1,6 @@
 from django.conf import settings
 from django.conf.urls import url
-from django.core.urlresolvers import LocaleRegexURLResolver
+from django.urls import LocaleRegexURLResolver
 from django.views.i18n import set_language
 
 

+ 1 - 1
django/contrib/admin/models.py

@@ -3,8 +3,8 @@ from __future__ import unicode_literals
 from django.conf import settings
 from django.contrib.admin.utils import quote
 from django.contrib.contenttypes.models import ContentType
-from django.core.urlresolvers import NoReverseMatch, reverse
 from django.db import models
+from django.urls import NoReverseMatch, reverse
 from django.utils import timezone
 from django.utils.encoding import python_2_unicode_compatible, smart_text
 from django.utils.translation import ugettext, ugettext_lazy as _

+ 1 - 1
django/contrib/admin/options.py

@@ -24,7 +24,6 @@ from django.core.exceptions import (
     FieldDoesNotExist, FieldError, PermissionDenied, ValidationError,
 )
 from django.core.paginator import Paginator
-from django.core.urlresolvers import reverse
 from django.db import models, router, transaction
 from django.db.models.constants import LOOKUP_SEP
 from django.db.models.fields import BLANK_CHOICE_DASH
@@ -37,6 +36,7 @@ from django.forms.widgets import CheckboxSelectMultiple, SelectMultiple
 from django.http import Http404, HttpResponseRedirect
 from django.http.response import HttpResponseBase
 from django.template.response import SimpleTemplateResponse, TemplateResponse
+from django.urls import reverse
 from django.utils import six
 from django.utils.decorators import method_decorator
 from django.utils.encoding import force_text, python_2_unicode_compatible

+ 1 - 1
django/contrib/admin/sites.py

@@ -5,11 +5,11 @@ from django.conf import settings
 from django.contrib.admin import ModelAdmin, actions
 from django.contrib.auth import REDIRECT_FIELD_NAME
 from django.core.exceptions import ImproperlyConfigured, PermissionDenied
-from django.core.urlresolvers import NoReverseMatch, reverse
 from django.db.models.base import ModelBase
 from django.http import Http404, HttpResponseRedirect
 from django.template.engine import Engine
 from django.template.response import TemplateResponse
+from django.urls import NoReverseMatch, reverse
 from django.utils import six
 from django.utils.text import capfirst
 from django.utils.translation import ugettext as _, ugettext_lazy

+ 1 - 1
django/contrib/admin/templatetags/admin_list.py

@@ -11,11 +11,11 @@ from django.contrib.admin.views.main import (
     ALL_VAR, ORDER_VAR, PAGE_VAR, SEARCH_VAR,
 )
 from django.core.exceptions import ObjectDoesNotExist
-from django.core.urlresolvers import NoReverseMatch
 from django.db import models
 from django.template import Library
 from django.template.loader import get_template
 from django.templatetags.static import static
+from django.urls import NoReverseMatch
 from django.utils import formats
 from django.utils.deprecation import RemovedInDjango20Warning
 from django.utils.encoding import force_text

+ 1 - 1
django/contrib/admin/templatetags/admin_urls.py

@@ -1,6 +1,6 @@
 from django import template
 from django.contrib.admin.utils import quote
-from django.core.urlresolvers import Resolver404, get_script_prefix, resolve
+from django.urls import Resolver404, get_script_prefix, resolve
 from django.utils.http import urlencode
 from django.utils.six.moves.urllib.parse import parse_qsl, urlparse, urlunparse
 

+ 1 - 1
django/contrib/admin/utils.py

@@ -6,12 +6,12 @@ from collections import defaultdict
 
 from django.contrib.auth import get_permission_codename
 from django.core.exceptions import FieldDoesNotExist
-from django.core.urlresolvers import NoReverseMatch, reverse
 from django.db import models
 from django.db.models.constants import LOOKUP_SEP
 from django.db.models.deletion import Collector
 from django.db.models.sql.constants import QUERY_TERMS
 from django.forms.utils import pretty_name
+from django.urls import NoReverseMatch, reverse
 from django.utils import formats, six, timezone
 from django.utils.encoding import force_str, force_text, smart_text
 from django.utils.html import format_html

+ 1 - 1
django/contrib/admin/views/main.py

@@ -15,8 +15,8 @@ from django.core.exceptions import (
     FieldDoesNotExist, ImproperlyConfigured, SuspiciousOperation,
 )
 from django.core.paginator import InvalidPage
-from django.core.urlresolvers import reverse
 from django.db import models
+from django.urls import reverse
 from django.utils import six
 from django.utils.encoding import force_text
 from django.utils.http import urlencode

+ 1 - 1
django/contrib/admin/widgets.py

@@ -6,11 +6,11 @@ from __future__ import unicode_literals
 import copy
 
 from django import forms
-from django.core.urlresolvers import reverse
 from django.db.models.deletion import CASCADE
 from django.forms.utils import flatatt
 from django.forms.widgets import RadioFieldRenderer
 from django.template.loader import render_to_string
+from django.urls import reverse
 from django.utils import six
 from django.utils.encoding import force_text
 from django.utils.html import (

+ 1 - 1
django/contrib/admindocs/utils.py

@@ -4,7 +4,7 @@ import re
 from email.errors import HeaderParseError
 from email.parser import HeaderParser
 
-from django.core.urlresolvers import reverse
+from django.urls import reverse
 from django.utils.encoding import force_bytes
 from django.utils.safestring import mark_safe
 

+ 5 - 5
django/contrib/admindocs/views.py

@@ -8,11 +8,11 @@ from django.conf import settings
 from django.contrib import admin
 from django.contrib.admin.views.decorators import staff_member_required
 from django.contrib.admindocs import utils
-from django.core import urlresolvers
 from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
 from django.db import models
 from django.http import Http404
 from django.template.engine import Engine
+from django.urls import get_mod_func, get_resolver, get_urlconf, reverse
 from django.utils.decorators import method_decorator
 from django.utils.inspect import (
     func_accepts_kwargs, func_accepts_var_args, func_has_no_args,
@@ -38,7 +38,7 @@ class BaseAdminDocsView(TemplateView):
         return super(BaseAdminDocsView, self).dispatch(request, *args, **kwargs)
 
     def get_context_data(self, **kwargs):
-        kwargs.update({'root_path': urlresolvers.reverse('admin:index')})
+        kwargs.update({'root_path': reverse('admin:index')})
         kwargs.update(admin.site.each_context(self.request))
         return super(BaseAdminDocsView, self).get_context_data(**kwargs)
 
@@ -147,9 +147,9 @@ class ViewDetailView(BaseAdminDocsView):
 
     def get_context_data(self, **kwargs):
         view = self.kwargs['view']
-        urlconf = urlresolvers.get_urlconf()
-        if urlresolvers.get_resolver(urlconf)._is_callback(view):
-            mod, func = urlresolvers.get_mod_func(view)
+        urlconf = get_urlconf()
+        if get_resolver(urlconf)._is_callback(view):
+            mod, func = get_mod_func(view)
             view_func = getattr(import_module(mod), func)
         else:
             raise Http404

+ 1 - 1
django/contrib/auth/admin.py

@@ -9,10 +9,10 @@ from django.contrib.auth.forms import (
 )
 from django.contrib.auth.models import Group, User
 from django.core.exceptions import PermissionDenied
-from django.core.urlresolvers import reverse
 from django.db import transaction
 from django.http import Http404, HttpResponseRedirect
 from django.template.response import TemplateResponse
+from django.urls import reverse
 from django.utils.decorators import method_decorator
 from django.utils.encoding import force_text
 from django.utils.html import escape

+ 1 - 1
django/contrib/auth/views.py

@@ -13,10 +13,10 @@ from django.contrib.auth.forms import (
 )
 from django.contrib.auth.tokens import default_token_generator
 from django.contrib.sites.shortcuts import get_current_site
-from django.core.urlresolvers import reverse
 from django.http import HttpResponseRedirect, QueryDict
 from django.shortcuts import resolve_url
 from django.template.response import TemplateResponse
+from django.urls import reverse
 from django.utils.deprecation import RemovedInDjango20Warning
 from django.utils.encoding import force_text
 from django.utils.http import is_safe_url, urlsafe_base64_decode

+ 1 - 1
django/contrib/flatpages/models.py

@@ -1,8 +1,8 @@
 from __future__ import unicode_literals
 
 from django.contrib.sites.models import Site
-from django.core.urlresolvers import get_script_prefix
 from django.db import models
+from django.urls import get_script_prefix
 from django.utils.encoding import iri_to_uri, python_2_unicode_compatible
 from django.utils.translation import ugettext_lazy as _
 

+ 9 - 7
django/contrib/gis/sitemaps/kml.py

@@ -1,8 +1,8 @@
 from django.apps import apps
 from django.contrib.gis.db.models.fields import GeometryField
 from django.contrib.sitemaps import Sitemap
-from django.core import urlresolvers
 from django.db import models
+from django.urls import reverse
 
 
 class KMLSitemap(Sitemap):
@@ -56,12 +56,14 @@ class KMLSitemap(Sitemap):
         return self.locations
 
     def location(self, obj):
-        return urlresolvers.reverse('django.contrib.gis.sitemaps.views.%s' % self.geo_format,
-                                    kwargs={'label': obj[0],
-                                            'model': obj[1],
-                                            'field_name': obj[2],
-                                            }
-                                    )
+        return reverse(
+            'django.contrib.gis.sitemaps.views.%s' % self.geo_format,
+            kwargs={
+                'label': obj[0],
+                'model': obj[1],
+                'field_name': obj[2],
+            },
+        )
 
 
 class KMZSitemap(KMLSitemap):

+ 7 - 6
django/contrib/sitemaps/__init__.py

@@ -1,7 +1,8 @@
 from django.apps import apps as django_apps
 from django.conf import settings
-from django.core import paginator, urlresolvers
+from django.core import paginator
 from django.core.exceptions import ImproperlyConfigured
+from django.urls import NoReverseMatch, reverse
 from django.utils import translation
 from django.utils.six.moves.urllib.parse import urlencode
 from django.utils.six.moves.urllib.request import urlopen
@@ -18,17 +19,17 @@ def ping_google(sitemap_url=None, ping_url=PING_URL):
     Alerts Google that the sitemap for the current site has been updated.
     If sitemap_url is provided, it should be an absolute path to the sitemap
     for this site -- e.g., '/sitemap.xml'. If sitemap_url is not provided, this
-    function will attempt to deduce it by using urlresolvers.reverse().
+    function will attempt to deduce it by using urls.reverse().
     """
     if sitemap_url is None:
         try:
             # First, try to get the "index" sitemap URL.
-            sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.views.index')
-        except urlresolvers.NoReverseMatch:
+            sitemap_url = reverse('django.contrib.sitemaps.views.index')
+        except NoReverseMatch:
             try:
                 # Next, try for the "global" sitemap URL.
-                sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.views.sitemap')
-            except urlresolvers.NoReverseMatch:
+                sitemap_url = reverse('django.contrib.sitemaps.views.sitemap')
+            except NoReverseMatch:
                 pass
 
     if sitemap_url is None:

+ 2 - 3
django/contrib/sitemaps/views.py

@@ -3,10 +3,10 @@ from calendar import timegm
 from functools import wraps
 
 from django.contrib.sites.shortcuts import get_current_site
-from django.core import urlresolvers
 from django.core.paginator import EmptyPage, PageNotAnInteger
 from django.http import Http404
 from django.template.response import TemplateResponse
+from django.urls import reverse
 from django.utils.http import http_date
 
 
@@ -32,8 +32,7 @@ def index(request, sitemaps,
         if callable(site):
             site = site()
         protocol = req_protocol if site.protocol is None else site.protocol
-        sitemap_url = urlresolvers.reverse(
-            sitemap_url_name, kwargs={'section': section})
+        sitemap_url = reverse(sitemap_url_name, kwargs={'section': section})
         absolute_url = '%s://%s%s' % (protocol, req_site.domain, sitemap_url)
         sites.append(absolute_url)
         for page in range(2, site.paginator.num_pages + 1):

+ 2 - 2
django/core/checks/urls.py

@@ -5,7 +5,7 @@ from . import Tags, Warning, register
 
 @register(Tags.urls)
 def check_url_config(app_configs, **kwargs):
-    from django.core.urlresolvers import get_resolver
+    from django.urls import get_resolver
     resolver = get_resolver()
     return check_resolver(resolver)
 
@@ -14,7 +14,7 @@ def check_resolver(resolver):
     """
     Recursively check the resolver.
     """
-    from django.core.urlresolvers import RegexURLPattern, RegexURLResolver
+    from django.urls import RegexURLPattern, RegexURLResolver
     warnings = []
     for pattern in resolver.url_patterns:
         if isinstance(pattern, RegexURLResolver):

+ 6 - 5
django/core/handlers/base.py

@@ -7,12 +7,13 @@ import warnings
 
 from django import http
 from django.conf import settings
-from django.core import signals, urlresolvers
+from django.core import signals
 from django.core.exceptions import (
     MiddlewareNotUsed, PermissionDenied, SuspiciousOperation,
 )
 from django.db import connections, transaction
 from django.http.multipartparser import MultiPartParserError
+from django.urls import get_resolver, set_urlconf
 from django.utils import six
 from django.utils.deprecation import RemovedInDjango20Warning
 from django.utils.encoding import force_text
@@ -111,8 +112,8 @@ class BaseHandler(object):
         # variable" exception in the event an exception is raised before
         # resolver is set
         urlconf = settings.ROOT_URLCONF
-        urlresolvers.set_urlconf(urlconf)
-        resolver = urlresolvers.get_resolver(urlconf)
+        set_urlconf(urlconf)
+        resolver = get_resolver(urlconf)
         # Use a flag to check if the response was rendered to prevent
         # multiple renderings or to force rendering if necessary.
         response_is_rendered = False
@@ -128,8 +129,8 @@ class BaseHandler(object):
                 if hasattr(request, 'urlconf'):
                     # Reset url resolver with a custom URLconf.
                     urlconf = request.urlconf
-                    urlresolvers.set_urlconf(urlconf)
-                    resolver = urlresolvers.get_resolver(urlconf)
+                    set_urlconf(urlconf)
+                    resolver = get_resolver(urlconf)
 
                 resolver_match = resolver.resolve(request.path_info)
                 callback, callback_args, callback_kwargs = resolver_match

+ 1 - 1
django/core/handlers/wsgi.py

@@ -12,7 +12,7 @@ from django import http
 from django.conf import settings
 from django.core import signals
 from django.core.handlers import base
-from django.core.urlresolvers import set_script_prefix
+from django.urls import set_script_prefix
 from django.utils import six
 from django.utils.encoding import force_str, force_text
 from django.utils.functional import cached_property

+ 7 - 638
django/core/urlresolvers.py

@@ -1,640 +1,9 @@
-"""
-This module converts requested URLs to callback view functions.
+import warnings
 
-RegexURLResolver 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.
-"""
-from __future__ import unicode_literals
+from django.urls import *  # NOQA
+from django.utils.deprecation import RemovedInDjango20Warning
 
-import functools
-import re
-from importlib import import_module
-from threading import local
-
-from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
-from django.http import Http404
-from django.utils import lru_cache, six
-from django.utils.datastructures import MultiValueDict
-from django.utils.encoding import force_str, force_text, iri_to_uri
-from django.utils.functional import cached_property, lazy
-from django.utils.http import RFC3986_SUBDELIMS, urlquote
-from django.utils.module_loading import module_has_submodule
-from django.utils.regex_helper import normalize
-from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit
-from django.utils.translation import get_language, override
-
-# 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()
-
-
-class ResolverMatch(object):
-    def __init__(self, func, args, kwargs, url_name=None, app_names=None, namespaces=None):
-        self.func = func
-        self.args = args
-        self.kwargs = kwargs
-        self.url_name = url_name
-
-        # 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)
-
-        if namespaces:
-            self.namespaces = [x for x in namespaces if x]
-        else:
-            self.namespaces = []
-        self.namespace = ':'.join(self.namespaces)
-
-        if not hasattr(func, '__name__'):
-            # A class-based view
-            self._func_path = '.'.join([func.__class__.__module__, func.__class__.__name__])
-        else:
-            # A function-based view
-            self._func_path = '.'.join([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):
-        return "ResolverMatch(func=%s, args=%s, kwargs=%s, url_name=%s, app_names=%s, namespaces=%s)" % (
-            self._func_path, self.args, self.kwargs, self.url_name, self.app_names, self.namespaces)
-
-
-class Resolver404(Http404):
-    pass
-
-
-class NoReverseMatch(Exception):
-    pass
-
-
-@lru_cache.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, six.string_types):
-        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
-
-
-@lru_cache.lru_cache(maxsize=None)
-def get_resolver(urlconf=None):
-    if urlconf is None:
-        from django.conf import settings
-        urlconf = settings.ROOT_URLCONF
-    return RegexURLResolver(r'^/', urlconf)
-
-
-@lru_cache.lru_cache(maxsize=None)
-def get_ns_resolver(ns_pattern, resolver):
-    # Build a namespaced resolver for the given parent URLconf pattern.
-    # This makes it possible to have captured parameters in the parent
-    # URLconf pattern.
-    ns_resolver = RegexURLResolver(ns_pattern, resolver.url_patterns)
-    return RegexURLResolver(r'^/', [ns_resolver])
-
-
-def get_mod_func(callback):
-    # Converts '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:]
-
-
-class LocaleRegexProvider(object):
-    """
-    A mixin to provide a default regex property which can vary by active
-    language.
-    """
-    def __init__(self, regex):
-        # regex is either a string representing a regular expression, or a
-        # translatable string (using ugettext_lazy) representing a regular
-        # expression.
-        self._regex = regex
-        self._regex_dict = {}
-
-    @property
-    def regex(self):
-        """
-        Returns a compiled regular expression, depending upon the activated
-        language-code.
-        """
-        language_code = get_language()
-        if language_code not in self._regex_dict:
-            if isinstance(self._regex, six.string_types):
-                regex = self._regex
-            else:
-                regex = force_text(self._regex)
-            try:
-                compiled_regex = re.compile(regex, re.UNICODE)
-            except re.error as e:
-                raise ImproperlyConfigured(
-                    '"%s" is not a valid regular expression: %s' %
-                    (regex, six.text_type(e)))
-
-            self._regex_dict[language_code] = compiled_regex
-        return self._regex_dict[language_code]
-
-
-class RegexURLPattern(LocaleRegexProvider):
-    def __init__(self, regex, callback, default_args=None, name=None):
-        LocaleRegexProvider.__init__(self, regex)
-        self.callback = callback  # the view
-        self.default_args = default_args or {}
-        self.name = name
-
-    def __repr__(self):
-        return force_str('<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern))
-
-    def resolve(self, path):
-        match = 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()
-            if kwargs:
-                args = ()
-            else:
-                args = match.groups()
-            # In both cases, pass any extra_kwargs as **kwargs.
-            kwargs.update(self.default_args)
-
-            return ResolverMatch(self.callback, args, kwargs, self.name)
-
-    @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 not hasattr(callback, '__name__'):
-            return callback.__module__ + "." + callback.__class__.__name__
-        else:
-            return callback.__module__ + "." + callback.__name__
-
-
-class RegexURLResolver(LocaleRegexProvider):
-    def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
-        LocaleRegexProvider.__init__(self, regex)
-        # 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
-
-    def __repr__(self):
-        if isinstance(self.urlconf_name, list) and len(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 str('<%s %s (%s:%s) %s>') % (
-            self.__class__.__name__, urlconf_repr, self.app_name,
-            self.namespace, self.regex.pattern)
-
-    def _populate(self):
-        lookups = MultiValueDict()
-        namespaces = {}
-        apps = {}
-        language_code = get_language()
-        for pattern in reversed(self.url_patterns):
-            if isinstance(pattern, RegexURLPattern):
-                self._callback_strs.add(pattern.lookup_str)
-            p_pattern = pattern.regex.pattern
-            if p_pattern.startswith('^'):
-                p_pattern = p_pattern[1:]
-            if isinstance(pattern, RegexURLResolver):
-                if pattern.namespace:
-                    namespaces[pattern.namespace] = (p_pattern, pattern)
-                    if pattern.app_name:
-                        apps.setdefault(pattern.app_name, []).append(pattern.namespace)
-                else:
-                    parent_pat = pattern.regex.pattern
-                    for name in pattern.reverse_dict:
-                        for matches, pat, defaults in pattern.reverse_dict.getlist(name):
-                            new_matches = normalize(parent_pat + pat)
-                            lookups.appendlist(
-                                name,
-                                (
-                                    new_matches,
-                                    p_pattern + pat,
-                                    dict(defaults, **pattern.default_kwargs),
-                                )
-                            )
-                    for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items():
-                        namespaces[namespace] = (p_pattern + prefix, sub_pattern)
-                    for app_name, namespace_list in pattern.app_dict.items():
-                        apps.setdefault(app_name, []).extend(namespace_list)
-                    self._callback_strs.update(pattern._callback_strs)
-            else:
-                bits = normalize(p_pattern)
-                lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args))
-                if pattern.name is not None:
-                    lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args))
-        self._reverse_dict[language_code] = lookups
-        self._namespace_dict[language_code] = namespaces
-        self._app_dict[language_code] = apps
-        self._populated = True
-
-    @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]
-
-    def _is_callback(self, name):
-        if not self._populated:
-            self._populate()
-        return name in self._callback_strs
-
-    def resolve(self, path):
-        path = force_text(path)  # path may be a reverse_lazy object
-        tried = []
-        match = self.regex.search(path)
-        if match:
-            new_path = path[match.end():]
-            for pattern in self.url_patterns:
-                try:
-                    sub_match = pattern.resolve(new_path)
-                except Resolver404 as e:
-                    sub_tried = e.args[0].get('tried')
-                    if sub_tried is not None:
-                        tried.extend([pattern] + t for t in sub_tried)
-                    else:
-                        tried.append([pattern])
-                else:
-                    if sub_match:
-                        # Merge captured arguments in match with submatch
-                        sub_match_dict = dict(match.groupdict(), **self.default_kwargs)
-                        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 = match.groups() + sub_match.args
-
-                        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
-                        )
-                    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, six.string_types):
-            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:
-            msg = (
-                "The included URLconf '{name}' does not appear to have any "
-                "patterns in it. If you see valid patterns in the file then "
-                "the issue is probably caused by a circular import."
-            )
-            raise ImproperlyConfigured(msg.format(name=self.urlconf_name))
-        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 default
-            # Lazy import, since django.urls imports this file
-            from django.conf import urls
-            callback = getattr(urls, 'handler%s' % view_type)
-        return get_callable(callback), {}
-
-    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()!")
-        text_args = [force_text(v) for v in args]
-        text_kwargs = {k: force_text(v) for (k, v) in kwargs.items()}
-
-        if not self._populated:
-            self._populate()
-
-        possibilities = self.reverse_dict.getlist(lookup_view)
-
-        for possibility, pattern, defaults in possibilities:
-            for result, params in possibility:
-                if args:
-                    if len(args) != len(params):
-                        continue
-                    candidate_subs = dict(zip(params, text_args))
-                else:
-                    if (set(kwargs.keys()) | set(defaults.keys()) != set(params) |
-                            set(defaults.keys())):
-                        continue
-                    matches = True
-                    for k, v in defaults.items():
-                        if kwargs.get(k, v) != v:
-                            matches = False
-                            break
-                    if not matches:
-                        continue
-                    candidate_subs = text_kwargs
-                # 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 % candidate_subs, re.UNICODE):
-                    # safe characters from `pchar` definition of RFC 3986
-                    url = urlquote(candidate_pat % candidate_subs, safe=RFC3986_SUBDELIMS + str('/~:@'))
-                    # Don't allow construction of scheme relative urls.
-                    if url.startswith('//'):
-                        url = '/%%2F%s' % url[2:]
-                    return 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 (possibility, pattern, defaults) in possibilities]
-        raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword "
-                "arguments '%s' not found. %d pattern(s) tried: %s" %
-                             (lookup_view_s, args, kwargs, len(patterns), patterns))
-
-
-class LocaleRegexURLResolver(RegexURLResolver):
-    """
-    A URL resolver that always matches the active language code as URL prefix.
-
-    Rather than taking a regex argument, we just override the ``regex``
-    function to always return the active language-code as regex.
-    """
-    def __init__(self, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
-        super(LocaleRegexURLResolver, self).__init__(
-            None, urlconf_name, default_kwargs, app_name, namespace)
-
-    @property
-    def regex(self):
-        language_code = get_language()
-        if language_code not in self._regex_dict:
-            regex_compiled = re.compile('^%s/' % language_code, re.UNICODE)
-            self._regex_dict[language_code] = regex_compiled
-        return self._regex_dict[language_code]
-
-
-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, six.string_types):
-        view = viewname
-    else:
-        parts = viewname.split(':')
-        parts.reverse()
-        view = parts[0]
-        path = parts[1:]
-
-        if current_app:
-            current_path = current_app.split(':')
-            current_path.reverse()
-        else:
-            current_path = None
-
-        resolved_path = []
-        ns_pattern = ''
-        while path:
-            ns = path.pop()
-            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 just 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
-            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)
-
-    return force_text(iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)))
-
-reverse_lazy = lazy(reverse, six.text_type)
-
-
-def clear_url_caches():
-    get_callable.cache_clear()
-    get_resolver.cache_clear()
-    get_ns_resolver.cache_clear()
-
-
-def set_script_prefix(prefix):
-    """
-    Sets the script prefix for the current thread.
-    """
-    if not prefix.endswith('/'):
-        prefix += '/'
-    _prefixes.value = prefix
-
-
-def get_script_prefix():
-    """
-    Returns 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():
-    """
-    Unsets the script prefix for the current thread.
-    """
-    try:
-        del _prefixes.value
-    except AttributeError:
-        pass
-
-
-def set_urlconf(urlconf_name):
-    """
-    Sets the URLconf for the current thread (overriding the default one in
-    settings). Set to None to 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):
-    """
-    Returns 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):
-    """
-    Returns True 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 unnecessarily indented try...except blocks.
-    """
-    try:
-        resolve(path, urlconf)
-        return True
-    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:
-        match = resolve(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
+warnings.warn(
+    "Importing from django.core.urlresolvers is deprecated in favor of "
+    "django.urls.", RemovedInDjango20Warning, stacklevel=2
+)

+ 3 - 3
django/db/models/__init__.py

@@ -26,15 +26,15 @@ from django.db.models.fields.related import (  # NOQA isort:skip
 
 def permalink(func):
     """
-    Decorator that calls urlresolvers.reverse() to return a URL using
-    parameters returned by the decorated function "func".
+    Decorator that calls urls.reverse() to return a URL using parameters
+    returned by the decorated function "func".
 
     "func" should be a function that returns a tuple in one of the
     following formats:
         (viewname, viewargs)
         (viewname, viewargs, viewkwargs)
     """
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
 
     @wraps(func)
     def inner(*args, **kwargs):

+ 3 - 3
django/middleware/common.py

@@ -3,9 +3,9 @@ import re
 
 from django import http
 from django.conf import settings
-from django.core import urlresolvers
 from django.core.exceptions import PermissionDenied
 from django.core.mail import mail_managers
+from django.urls import is_valid_path
 from django.utils.cache import get_conditional_response, set_response_etag
 from django.utils.encoding import force_text
 from django.utils.six.moves.urllib.parse import urlparse
@@ -74,8 +74,8 @@ class CommonMiddleware(object):
         if settings.APPEND_SLASH and not request.get_full_path().endswith('/'):
             urlconf = getattr(request, 'urlconf', None)
             return (
-                not urlresolvers.is_valid_path(request.path_info, urlconf)
-                and urlresolvers.is_valid_path('%s/' % request.path_info, urlconf)
+                not is_valid_path(request.path_info, urlconf)
+                and is_valid_path('%s/' % request.path_info, urlconf)
             )
         return False
 

+ 1 - 1
django/middleware/csrf.py

@@ -10,7 +10,7 @@ import logging
 import re
 
 from django.conf import settings
-from django.core.urlresolvers import get_callable
+from django.urls import get_callable
 from django.utils.cache import patch_vary_headers
 from django.utils.crypto import constant_time_compare, get_random_string
 from django.utils.encoding import force_text

+ 2 - 2
django/middleware/locale.py

@@ -1,10 +1,10 @@
 "This is the locale selecting middleware that will look at accept headers"
 
 from django.conf import settings
-from django.core.urlresolvers import (
+from django.http import HttpResponseRedirect
+from django.urls import (
     LocaleRegexURLResolver, get_resolver, get_script_prefix, is_valid_path,
 )
-from django.http import HttpResponseRedirect
 from django.utils import translation
 from django.utils.cache import patch_vary_headers
 from django.utils.functional import cached_property

+ 7 - 7
django/shortcuts.py

@@ -3,7 +3,6 @@ This module collects helper functions and classes that "span" multiple levels
 of MVC. In other words, these functions/classes introduce controlled coupling
 for convenience's sake.
 """
-from django.core import urlresolvers
 from django.db.models.base import ModelBase
 from django.db.models.manager import Manager
 from django.db.models.query import QuerySet
@@ -11,6 +10,7 @@ from django.http import (
     Http404, HttpResponse, HttpResponsePermanentRedirect, HttpResponseRedirect,
 )
 from django.template import loader
+from django.urls import NoReverseMatch, reverse
 from django.utils import six
 from django.utils.encoding import force_text
 from django.utils.functional import Promise
@@ -43,8 +43,8 @@ def redirect(to, *args, **kwargs):
 
         * A model: the model's `get_absolute_url()` function will be called.
 
-        * A view name, possibly with arguments: `urlresolvers.reverse()` will
-          be used to reverse-resolve the name.
+        * A view name, possibly with arguments: `urls.reverse()` will be used
+          to reverse-resolve the name.
 
         * A URL, which will be used as-is for the redirect location.
 
@@ -123,8 +123,8 @@ def resolve_url(to, *args, **kwargs):
 
         * A model: the model's `get_absolute_url()` function will be called.
 
-        * A view name, possibly with arguments: `urlresolvers.reverse()` will
-          be used to reverse-resolve the name.
+        * A view name, possibly with arguments: `urls.reverse()` will be used
+          to reverse-resolve the name.
 
         * A URL, which will be returned as-is.
     """
@@ -144,8 +144,8 @@ def resolve_url(to, *args, **kwargs):
 
     # Next try a reverse URL resolution.
     try:
-        return urlresolvers.reverse(to, args=args, kwargs=kwargs)
-    except urlresolvers.NoReverseMatch:
+        return reverse(to, args=args, kwargs=kwargs)
+    except NoReverseMatch:
         # If this is a callable, re-raise.
         if callable(to):
             raise

+ 1 - 1
django/template/defaulttags.py

@@ -426,7 +426,7 @@ class URLNode(Node):
         self.asvar = asvar
 
     def render(self, context):
-        from django.core.urlresolvers import reverse, NoReverseMatch
+        from django.urls import reverse, NoReverseMatch
         args = [arg.resolve(context) for arg in self.args]
         kwargs = {
             smart_text(k, 'ascii'): v.resolve(context)

+ 2 - 3
django/test/client.py

@@ -11,7 +11,6 @@ from io import BytesIO
 
 from django.apps import apps
 from django.conf import settings
-from django.core import urlresolvers
 from django.core.handlers.base import BaseHandler
 from django.core.handlers.wsgi import ISO_8859_1, UTF_8, WSGIRequest
 from django.core.signals import (
@@ -22,6 +21,7 @@ from django.http import HttpRequest, QueryDict, SimpleCookie
 from django.template import TemplateDoesNotExist
 from django.test import signals
 from django.test.utils import ContextList
+from django.urls import resolve
 from django.utils import six
 from django.utils.encoding import force_bytes, force_str, uri_to_iri
 from django.utils.functional import SimpleLazyObject, curry
@@ -477,8 +477,7 @@ class Client(RequestFactory):
             response.json = curry(self._parse_json, response)
 
             # Attach the ResolverMatch instance to the response
-            response.resolver_match = SimpleLazyObject(
-                lambda: urlresolvers.resolve(request['PATH_INFO']))
+            response.resolver_match = SimpleLazyObject(lambda: resolve(request['PATH_INFO']))
 
             # Flatten a single context. Not really necessary anymore thanks to
             # the __getattr__ flattening in ContextList, but has some edge-case

+ 1 - 1
django/test/signals.py

@@ -145,7 +145,7 @@ def complex_setting_changed(**kwargs):
 @receiver(setting_changed)
 def root_urlconf_changed(**kwargs):
     if kwargs['setting'] == 'ROOT_URLCONF':
-        from django.core.urlresolvers import clear_url_caches, set_urlconf
+        from django.urls import clear_url_caches, set_urlconf
         clear_url_caches()
         set_urlconf(None)
 

+ 1 - 1
django/test/utils.py

@@ -12,11 +12,11 @@ from django.apps import apps
 from django.conf import UserSettingsHolder, settings
 from django.core import mail
 from django.core.signals import request_started
-from django.core.urlresolvers import get_script_prefix, set_script_prefix
 from django.db import reset_queries
 from django.http import request
 from django.template import Template
 from django.test.signals import setting_changed, template_rendered
+from django.urls import get_script_prefix, set_script_prefix
 from django.utils import six
 from django.utils.decorators import ContextDecorator
 from django.utils.encoding import force_str

+ 20 - 0
django/urls/__init__.py

@@ -0,0 +1,20 @@
+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 .exceptions import NoReverseMatch, Resolver404
+from .resolvers import (
+    LocaleRegexProvider, LocaleRegexURLResolver, RegexURLPattern,
+    RegexURLResolver, ResolverMatch, get_ns_resolver, get_resolver,
+)
+from .utils import get_callable, get_mod_func
+
+__all__ = [
+    'LocaleRegexProvider', 'LocaleRegexURLResolver', 'NoReverseMatch',
+    'RegexURLPattern', 'RegexURLResolver', 'Resolver404', 'ResolverMatch',
+    'clear_script_prefix', 'clear_url_caches', 'get_callable', 'get_mod_func',
+    'get_ns_resolver', 'get_resolver', 'get_script_prefix', 'get_urlconf',
+    'is_valid_path', 'resolve', 'reverse', 'reverse_lazy', 'set_script_prefix',
+    'set_urlconf', 'translate_url',
+]

+ 185 - 0
django/urls/base.py

@@ -0,0 +1,185 @@
+from __future__ import unicode_literals
+
+from threading import local
+
+from django.utils import six
+from django.utils.encoding import force_text, iri_to_uri
+from django.utils.functional import lazy
+from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit
+from django.utils.translation import override
+
+from .exceptions import NoReverseMatch, Resolver404
+from .resolvers import 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, six.string_types):
+        view = viewname
+    else:
+        parts = viewname.split(':')
+        parts.reverse()
+        view = parts[0]
+        path = parts[1:]
+
+        if current_app:
+            current_path = current_app.split(':')
+            current_path.reverse()
+        else:
+            current_path = None
+
+        resolved_path = []
+        ns_pattern = ''
+        while path:
+            ns = path.pop()
+            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
+            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)
+
+    return force_text(iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)))
+
+reverse_lazy = lazy(reverse, six.text_type)
+
+
+def clear_url_caches():
+    get_callable.cache_clear()
+    get_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 True 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.
+    """
+    from django.urls.base import resolve
+    try:
+        resolve(path, urlconf)
+        return True
+    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.
+    """
+    from django.urls import resolve, reverse
+    parsed = urlsplit(url)
+    try:
+        match = resolve(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

+ 11 - 0
django/urls/exceptions.py

@@ -0,0 +1,11 @@
+from __future__ import unicode_literals
+
+from django.http import Http404
+
+
+class Resolver404(Http404):
+    pass
+
+
+class NoReverseMatch(Exception):
+    pass

+ 393 - 0
django/urls/resolvers.py

@@ -0,0 +1,393 @@
+"""
+This module converts requested URLs to callback view functions.
+
+RegexURLResolver 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.
+"""
+from __future__ import unicode_literals
+
+import functools
+import re
+from importlib import import_module
+
+from django.core.exceptions import ImproperlyConfigured
+from django.utils import lru_cache, six
+from django.utils.datastructures import MultiValueDict
+from django.utils.encoding import force_str, force_text
+from django.utils.functional import cached_property
+from django.utils.http import RFC3986_SUBDELIMS, urlquote
+from django.utils.regex_helper import normalize
+from django.utils.translation import get_language
+
+from .exceptions import NoReverseMatch, Resolver404
+from .utils import get_callable
+
+
+class ResolverMatch(object):
+    def __init__(self, func, args, kwargs, url_name=None, app_names=None, namespaces=None):
+        self.func = func
+        self.args = args
+        self.kwargs = kwargs
+        self.url_name = url_name
+
+        # 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 = '.'.join([func.__class__.__module__, func.__class__.__name__])
+        else:
+            # A function-based view
+            self._func_path = '.'.join([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):
+        return "ResolverMatch(func=%s, args=%s, kwargs=%s, url_name=%s, app_names=%s, namespaces=%s)" % (
+            self._func_path, self.args, self.kwargs, self.url_name,
+            self.app_names, self.namespaces,
+        )
+
+
+@lru_cache.lru_cache(maxsize=None)
+def get_resolver(urlconf=None):
+    if urlconf is None:
+        from django.conf import settings
+        urlconf = settings.ROOT_URLCONF
+    return RegexURLResolver(r'^/', urlconf)
+
+
+@lru_cache.lru_cache(maxsize=None)
+def get_ns_resolver(ns_pattern, resolver):
+    # Build a namespaced resolver for the given parent URLconf pattern.
+    # This makes it possible to have captured parameters in the parent
+    # URLconf pattern.
+    ns_resolver = RegexURLResolver(ns_pattern, resolver.url_patterns)
+    return RegexURLResolver(r'^/', [ns_resolver])
+
+
+class LocaleRegexProvider(object):
+    """
+    A mixin to provide a default regex property which can vary by active
+    language.
+    """
+    def __init__(self, regex):
+        # regex is either a string representing a regular expression, or a
+        # translatable string (using ugettext_lazy) representing a regular
+        # expression.
+        self._regex = regex
+        self._regex_dict = {}
+
+    @property
+    def regex(self):
+        """
+        Return a compiled regular expression based on the activate language.
+        """
+        language_code = get_language()
+        if language_code not in self._regex_dict:
+            regex = self._regex if isinstance(self._regex, six.string_types) else force_text(self._regex)
+            try:
+                compiled_regex = re.compile(regex, re.UNICODE)
+            except re.error as e:
+                raise ImproperlyConfigured(
+                    '"%s" is not a valid regular expression: %s' %
+                    (regex, six.text_type(e))
+                )
+            self._regex_dict[language_code] = compiled_regex
+        return self._regex_dict[language_code]
+
+
+class RegexURLPattern(LocaleRegexProvider):
+    def __init__(self, regex, callback, default_args=None, name=None):
+        LocaleRegexProvider.__init__(self, regex)
+        self.callback = callback  # the view
+        self.default_args = default_args or {}
+        self.name = name
+
+    def __repr__(self):
+        return force_str('<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern))
+
+    def resolve(self, path):
+        match = 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()
+            # In both cases, pass any extra_kwargs as **kwargs.
+            kwargs.update(self.default_args)
+            return ResolverMatch(self.callback, args, kwargs, self.name)
+
+    @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 not hasattr(callback, '__name__'):
+            return callback.__module__ + "." + callback.__class__.__name__
+        else:
+            return callback.__module__ + "." + callback.__name__
+
+
+class RegexURLResolver(LocaleRegexProvider):
+    def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
+        LocaleRegexProvider.__init__(self, regex)
+        # 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
+
+    def __repr__(self):
+        if isinstance(self.urlconf_name, list) and len(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 str('<%s %s (%s:%s) %s>') % (
+            self.__class__.__name__, urlconf_repr, self.app_name,
+            self.namespace, self.regex.pattern,
+        )
+
+    def _populate(self):
+        lookups = MultiValueDict()
+        namespaces = {}
+        apps = {}
+        language_code = get_language()
+        for pattern in reversed(self.url_patterns):
+            if isinstance(pattern, RegexURLPattern):
+                self._callback_strs.add(pattern.lookup_str)
+            p_pattern = pattern.regex.pattern
+            if p_pattern.startswith('^'):
+                p_pattern = p_pattern[1:]
+            if isinstance(pattern, RegexURLResolver):
+                if pattern.namespace:
+                    namespaces[pattern.namespace] = (p_pattern, pattern)
+                    if pattern.app_name:
+                        apps.setdefault(pattern.app_name, []).append(pattern.namespace)
+                else:
+                    parent_pat = pattern.regex.pattern
+                    for name in pattern.reverse_dict:
+                        for matches, pat, defaults in pattern.reverse_dict.getlist(name):
+                            new_matches = normalize(parent_pat + pat)
+                            lookups.appendlist(
+                                name,
+                                (
+                                    new_matches,
+                                    p_pattern + pat,
+                                    dict(defaults, **pattern.default_kwargs),
+                                )
+                            )
+                    for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items():
+                        namespaces[namespace] = (p_pattern + prefix, sub_pattern)
+                    for app_name, namespace_list in pattern.app_dict.items():
+                        apps.setdefault(app_name, []).extend(namespace_list)
+                    self._callback_strs.update(pattern._callback_strs)
+            else:
+                bits = normalize(p_pattern)
+                lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args))
+                if pattern.name is not None:
+                    lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args))
+        self._reverse_dict[language_code] = lookups
+        self._namespace_dict[language_code] = namespaces
+        self._app_dict[language_code] = apps
+        self._populated = True
+
+    @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]
+
+    def _is_callback(self, name):
+        if not self._populated:
+            self._populate()
+        return name in self._callback_strs
+
+    def resolve(self, path):
+        path = force_text(path)  # path may be a reverse_lazy object
+        tried = []
+        match = self.regex.search(path)
+        if match:
+            new_path = path[match.end():]
+            for pattern in self.url_patterns:
+                try:
+                    sub_match = pattern.resolve(new_path)
+                except Resolver404 as e:
+                    sub_tried = e.args[0].get('tried')
+                    if sub_tried is not None:
+                        tried.extend([pattern] + t for t in sub_tried)
+                    else:
+                        tried.append([pattern])
+                else:
+                    if sub_match:
+                        # Merge captured arguments in match with submatch
+                        sub_match_dict = dict(match.groupdict(), **self.default_kwargs)
+                        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 = match.groups() + sub_match.args
+
+                        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,
+                        )
+                    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, six.string_types):
+            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:
+            msg = (
+                "The included URLconf '{name}' does not appear to have any "
+                "patterns in it. If you see valid patterns in the file then "
+                "the issue is probably caused by a circular import."
+            )
+            raise ImproperlyConfigured(msg.format(name=self.urlconf_name))
+        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_with_prefix(self, lookup_view, _prefix, *args, **kwargs):
+        if args and kwargs:
+            raise ValueError("Don't mix *args and **kwargs in call to reverse()!")
+        text_args = [force_text(v) for v in args]
+        text_kwargs = {k: force_text(v) for (k, v) in kwargs.items()}
+
+        if not self._populated:
+            self._populate()
+
+        possibilities = self.reverse_dict.getlist(lookup_view)
+
+        for possibility, pattern, defaults in possibilities:
+            for result, params in possibility:
+                if args:
+                    if len(args) != len(params):
+                        continue
+                    candidate_subs = dict(zip(params, text_args))
+                else:
+                    if (set(kwargs.keys()) | set(defaults.keys()) != set(params) |
+                            set(defaults.keys())):
+                        continue
+                    matches = True
+                    for k, v in defaults.items():
+                        if kwargs.get(k, v) != v:
+                            matches = False
+                            break
+                    if not matches:
+                        continue
+                    candidate_subs = text_kwargs
+                # 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 % candidate_subs, re.UNICODE):
+                    # safe characters from `pchar` definition of RFC 3986
+                    url = urlquote(candidate_pat % candidate_subs, safe=RFC3986_SUBDELIMS + str('/~:@'))
+                    # Don't allow construction of scheme relative urls.
+                    if url.startswith('//'):
+                        url = '/%%2F%s' % url[2:]
+                    return 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 (possibility, pattern, defaults) in possibilities]
+        raise NoReverseMatch(
+            "Reverse for '%s' with arguments '%s' and keyword "
+            "arguments '%s' not found. %d pattern(s) tried: %s" %
+            (lookup_view_s, args, kwargs, len(patterns), patterns)
+        )
+
+
+class LocaleRegexURLResolver(RegexURLResolver):
+    """
+    A URL resolver that always matches the active language code as URL prefix.
+
+    Rather than taking a regex argument, we just override the ``regex``
+    function to always return the active language-code as regex.
+    """
+    def __init__(self, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
+        super(LocaleRegexURLResolver, self).__init__(
+            None, urlconf_name, default_kwargs, app_name, namespace,
+        )
+
+    @property
+    def regex(self):
+        language_code = get_language()
+        if language_code not in self._regex_dict:
+            regex_compiled = re.compile('^%s/' % language_code, re.UNICODE)
+            self._regex_dict[language_code] = regex_compiled
+        return self._regex_dict[language_code]

+ 64 - 0
django/urls/utils.py

@@ -0,0 +1,64 @@
+from __future__ import unicode_literals
+
+from importlib import import_module
+
+from django.core.exceptions import ViewDoesNotExist
+from django.utils import lru_cache, six
+from django.utils.module_loading import module_has_submodule
+
+
+@lru_cache.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, six.string_types):
+        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:]

+ 1 - 1
django/views/debug.py

@@ -5,10 +5,10 @@ import sys
 import types
 
 from django.conf import settings
-from django.core.urlresolvers import Resolver404, resolve
 from django.http import HttpResponse, HttpResponseNotFound
 from django.template import Context, Engine, TemplateDoesNotExist
 from django.template.defaultfilters import force_escape, pprint
+from django.urls import Resolver404, resolve
 from django.utils import lru_cache, six, timezone
 from django.utils.datastructures import MultiValueDict
 from django.utils.encoding import force_bytes, smart_text

+ 1 - 1
django/views/generic/base.py

@@ -5,8 +5,8 @@ from functools import update_wrapper
 
 from django import http
 from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import NoReverseMatch, reverse
 from django.template.response import TemplateResponse
+from django.urls import NoReverseMatch, reverse
 from django.utils import six
 from django.utils.decorators import classonlymethod
 

+ 1 - 1
django/views/i18n.py

@@ -6,8 +6,8 @@ import os
 from django import http
 from django.apps import apps
 from django.conf import settings
-from django.core.urlresolvers import translate_url
 from django.template import Context, Engine
+from django.urls import translate_url
 from django.utils import six
 from django.utils._os import upath
 from django.utils.encoding import smart_text

+ 1 - 1
docs/internals/contributing/writing-code/coding-style.txt

@@ -262,7 +262,7 @@ already been configured).
 So, if there is a module containing some code as follows::
 
     from django.conf import settings
-    from django.core.urlresolvers import get_callable
+    from django.urls import get_callable
 
     default_foo_view = get_callable(settings.FOO_VIEW)
 

+ 3 - 2
docs/internals/deprecation.txt

@@ -128,6 +128,8 @@ details on these changes.
 
 * The ``shell --plain`` option will be removed.
 
+* The ``django.core.urlresolvers`` module will be removed.
+
 .. _deprecation-removed-in-1.10:
 
 1.10
@@ -152,8 +154,7 @@ details on these changes.
 * Using an incorrect count of unpacked values in the ``for`` template tag
   will raise an exception rather than fail silently.
 
-* The ability to :func:`~django.core.urlresolvers.reverse` URLs using a dotted
-  Python path will be removed.
+* The ability to reverse URLs using a dotted Python path will be removed.
 
 * Support for :py:mod:`optparse` will be dropped for custom management commands
   (replaced by :py:mod:`argparse`).

+ 1 - 1
docs/intro/tutorial03.txt

@@ -56,7 +56,7 @@ To get from a URL to a view, Django uses what are known as 'URLconfs'. A
 URLconf maps URL patterns (described as regular expressions) to views.
 
 This tutorial provides basic instruction in the use of URLconfs, and you can
-refer to :mod:`django.core.urlresolvers` for more information.
+refer to :mod:`django.urls` for more information.
 
 Writing more views
 ==================

+ 4 - 4
docs/intro/tutorial04.txt

@@ -71,7 +71,7 @@ create a real version. Add the following to ``polls/views.py``:
 
     from django.shortcuts import get_object_or_404, render
     from django.http import HttpResponseRedirect, HttpResponse
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
 
     from .models import Choice, Question
     # ...
@@ -124,13 +124,13 @@ This code includes a few things we haven't covered yet in this tutorial:
   POST data. This tip isn't specific to Django; it's just good Web
   development practice.
 
-* We are using the :func:`~django.core.urlresolvers.reverse` function in the
+* We are using the :func:`~django.urls.reverse` function in the
   :class:`~django.http.HttpResponseRedirect` constructor in this example.
   This function helps avoid having to hardcode a URL in the view function.
   It is given the name of the view that we want to pass control to and the
   variable portion of the URL pattern that points to that view. In this
   case, using the URLconf we set up in :doc:`Tutorial 3 </intro/tutorial03>`,
-  this :func:`~django.core.urlresolvers.reverse` call will return a string like
+  this :func:`~django.urls.reverse` call will return a string like
   ::
 
     '/polls/3/results/'
@@ -264,7 +264,7 @@ views and use Django's generic views instead. To do so, open the
 
     from django.shortcuts import get_object_or_404, render
     from django.http import HttpResponseRedirect
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
     from django.views import generic
 
     from .models import Choice, Question

+ 2 - 2
docs/intro/tutorial05.txt

@@ -362,7 +362,7 @@ With that ready, we can ask the client to do some work for us::
     404
     >>> # on the other hand we should expect to find something at '/polls/'
     >>> # we'll use 'reverse()' rather than a hardcoded URL
-    >>> from django.core.urlresolvers import reverse
+    >>> from django.urls import reverse
     >>> response = client.get(reverse('polls:index'))
     >>> response.status_code
     200
@@ -447,7 +447,7 @@ Add the following to ``polls/tests.py``:
 .. snippet::
     :filename: polls/tests.py
 
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
 
 and we'll create a shortcut function to create questions as well as a new test
 class:

+ 1 - 1
docs/ref/class-based-views/generic-date-based.txt

@@ -13,7 +13,7 @@ views for displaying drilldown pages for date-based data.
     defined as follows in ``myapp/models.py``::
 
         from django.db import models
-        from django.core.urlresolvers import reverse
+        from django.urls import reverse
 
         class Article(models.Model):
             title = models.CharField(max_length=200)

+ 2 - 2
docs/ref/class-based-views/generic-editing.txt

@@ -15,7 +15,7 @@ editing content:
     Some of the examples on this page assume that an ``Author`` model has been
     defined as follows in ``myapp/models.py``::
 
-        from django.core.urlresolvers import reverse
+        from django.urls import reverse
         from django.db import models
 
         class Author(models.Model):
@@ -227,7 +227,7 @@ DeleteView
     **Example myapp/views.py**::
 
         from django.views.generic.edit import DeleteView
-        from django.core.urlresolvers import reverse_lazy
+        from django.urls import reverse_lazy
         from myapp.models import Author
 
         class AuthorDelete(DeleteView):

+ 4 - 5
docs/ref/contrib/admin/index.txt

@@ -1252,7 +1252,7 @@ subclass::
     For example::
 
         from django.contrib import admin
-        from django.core.urlresolvers import reverse
+        from django.urls import reverse
 
         class PersonAdmin(admin.ModelAdmin):
             def view_on_site(self, obj):
@@ -2883,9 +2883,9 @@ So - if you wanted to get a reference to the Change view for a particular
 ``Choice`` object (from the polls application) in the default admin, you would
 call::
 
-    >>> from django.core import urlresolvers
+    >>> from django.urls import reverse
     >>> c = Choice.objects.get(...)
-    >>> change_url = urlresolvers.reverse('admin:polls_choice_change', args=(c.id,))
+    >>> change_url = reverse('admin:polls_choice_change', args=(c.id,))
 
 This will find the first registered instance of the admin application
 (whatever the instance name), and resolve to the view for changing
@@ -2896,8 +2896,7 @@ that instance as a ``current_app`` hint to the reverse call. For example,
 if you specifically wanted the admin view from the admin instance named
 ``custom``, you would need to call::
 
-    >>> change_url = urlresolvers.reverse('admin:polls_choice_change',
-    ...                                   args=(c.id,), current_app='custom')
+    >>> change_url = reverse('admin:polls_choice_change', args=(c.id,), current_app='custom')
 
 For more details, see the documentation on :ref:`reversing namespaced URLs
 <topics-http-reversing-url-namespaces>`.

+ 3 - 4
docs/ref/contrib/sitemaps.txt

@@ -300,13 +300,12 @@ Sitemap for static views
 
 Often you want the search engine crawlers to index views which are neither
 object detail pages nor flatpages. The solution is to explicitly list URL
-names for these views in ``items`` and call
-:func:`~django.core.urlresolvers.reverse` in the ``location`` method of
-the sitemap. For example::
+names for these views in ``items`` and call :func:`~django.urls.reverse` in
+the ``location`` method of the sitemap. For example::
 
     # sitemaps.py
     from django.contrib import sitemaps
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
 
     class StaticViewSitemap(sitemaps.Sitemap):
         priority = 0.5

+ 1 - 1
docs/ref/contrib/syndication.txt

@@ -53,7 +53,7 @@ This simple example, taken from a hypothetical police beat news site describes
 a feed of the latest five news items::
 
     from django.contrib.syndication.views import Feed
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
     from policebeat.models import NewsItem
 
     class LatestEntriesFeed(Feed):

+ 14 - 9
docs/ref/exceptions.txt

@@ -84,7 +84,7 @@ Django core exception classes are defined in ``django.core.exceptions``.
 .. exception:: ViewDoesNotExist
 
     The :exc:`ViewDoesNotExist` exception is raised by
-    :mod:`django.core.urlresolvers` when a requested view does not exist.
+    :mod:`django.urls` when a requested view does not exist.
 
 ``MiddlewareNotUsed``
 ---------------------
@@ -142,12 +142,18 @@ or model are classified as ``NON_FIELD_ERRORS``. This constant is used
 as a key in dictionaries that otherwise map fields to their respective
 list of errors.
 
-.. currentmodule:: django.core.urlresolvers
+.. currentmodule:: django.urls
 
 URL Resolver exceptions
 =======================
 
-URL Resolver exceptions are defined in ``django.core.urlresolvers``.
+URL Resolver exceptions are defined in ``django.urls``.
+
+.. deprecated:: 1.10
+
+    In older versions, these exceptions are located in
+    ``django.core.urlresolvers``. Importing from the old location will continue
+    to work until Django 2.0.
 
 ``Resolver404``
 ---------------
@@ -155,18 +161,17 @@ URL Resolver exceptions are defined in ``django.core.urlresolvers``.
 .. exception:: Resolver404
 
     The :exc:`Resolver404` exception is raised by
-    :func:`django.core.urlresolvers.resolve()` if the path passed to
-    ``resolve()`` doesn't map to a view. It's a subclass of
-    :class:`django.http.Http404`.
+    :func:`~django.urls.resolve()` if the path passed to ``resolve()`` doesn't
+    map to a view. It's a subclass of :class:`django.http.Http404`.
 
 ``NoReverseMatch``
 ------------------
 
 .. exception:: NoReverseMatch
 
-    The :exc:`NoReverseMatch` exception is raised by
-    :mod:`django.core.urlresolvers` when a matching URL in your URLconf
-    cannot be identified based on the parameters supplied.
+    The :exc:`NoReverseMatch` exception is raised by :mod:`django.urls` when a
+    matching URL in your URLconf cannot be identified based on the parameters
+    supplied.
 
 .. currentmodule:: django.db
 

+ 4 - 4
docs/ref/models/instances.txt

@@ -672,14 +672,14 @@ For example::
     def get_absolute_url(self):
         return "/people/%i/" % self.id
 
-(Whilst this code is correct and simple, it may not be the most portable way to
-write this kind of method. The :func:`~django.core.urlresolvers.reverse`
-function is usually the best approach.)
+While this code is correct and simple, it may not be the most portable way to
+to write this kind of method. The :func:`~django.urls.reverse` function is
+usually the best approach.
 
 For example::
 
     def get_absolute_url(self):
-        from django.core.urlresolvers import reverse
+        from django.urls import reverse
         return reverse('people.views.details', args=[str(self.id)])
 
 One place Django uses ``get_absolute_url()`` is in the admin app. If an object

+ 6 - 6
docs/ref/request-response.txt

@@ -160,11 +160,11 @@ All attributes should be considered read-only, unless stated otherwise.
 
 .. attribute:: HttpRequest.resolver_match
 
-    An instance of :class:`~django.core.urlresolvers.ResolverMatch` representing
-    the resolved url. This attribute is only set after url resolving took place,
-    which means it's available in all views but not in middleware methods which
-    are executed before url resolving takes place (like ``process_request``, you
-    can use ``process_view`` instead).
+    An instance of :class:`~django.urls.ResolverMatch` representing the
+    resolved URL. This attribute is only set after URL resolving took place,
+    which means it's available in all views but not in middleware methods
+    which are executed before URL resolving takes place (like
+    ``process_request()``, you can use ``process_view()`` instead).
 
 Attributes set by application code
 ----------------------------------
@@ -175,7 +175,7 @@ application.
 .. attribute:: HttpRequest.current_app
 
     The :ttag:`url` template tag will use its value as the ``current_app``
-    argument to :func:`~django.core.urlresolvers.reverse()`.
+    argument to :func:`~django.urls.reverse()`.
 
 .. attribute:: HttpRequest.urlconf
 

+ 2 - 2
docs/ref/templates/builtins.txt

@@ -1024,8 +1024,8 @@ such as this:
 The template tag will output the string ``/clients/client/123/``.
 
 Note that if the URL you're reversing doesn't exist, you'll get an
-:exc:`~django.core.urlresolvers.NoReverseMatch` exception raised, which will
-cause your site to display an error page.
+:exc:`~django.urls.NoReverseMatch` exception raised, which will cause your
+site to display an error page.
 
 If you'd like to retrieve a URL without displaying it, you can use a slightly
 different call::

+ 2 - 2
docs/ref/unicode.txt

@@ -290,8 +290,8 @@ Taking care in ``get_absolute_url()``
 
 URLs can only contain ASCII characters. If you're constructing a URL from
 pieces of data that might be non-ASCII, be careful to encode the results in a
-way that is suitable for a URL. The :func:`~django.core.urlresolvers.reverse`
-function handles this for you automatically.
+way that is suitable for a URL. The :func:`~django.urls.reverse` function
+handles this for you automatically.
 
 If you're constructing a URL manually (i.e., *not* using the ``reverse()``
 function), you'll need to take care of the encoding yourself. In this case,

+ 27 - 23
docs/ref/urlresolvers.txt

@@ -1,8 +1,14 @@
-==============================================
-``django.core.urlresolvers`` utility functions
-==============================================
+=================================
+``django.urls`` utility functions
+=================================
 
-.. module:: django.core.urlresolvers
+.. module:: django.urls
+
+.. deprecated:: 1.10
+
+    In older versions, these functions are located in
+    ``django.core.urlresolvers``. Importing from the old location will continue
+    to work until Django 2.0.
 
 reverse()
 ---------
@@ -31,7 +37,7 @@ you can use any of the following to reverse the URL::
 
 If the URL accepts arguments, you may pass them in ``args``. For example::
 
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
 
     def myview(request):
         return HttpResponseRedirect(reverse('arch-summary', args=[1945]))
@@ -44,7 +50,7 @@ You can also pass ``kwargs`` instead of ``args``. For example::
 ``args`` and ``kwargs`` cannot be passed to ``reverse()`` at the same time.
 
 If no match can be made, ``reverse()`` raises a
-:class:`~django.core.urlresolvers.NoReverseMatch` exception.
+:class:`~django.urls.NoReverseMatch` exception.
 
 The ``reverse()`` function can reverse a large variety of regular expression
 patterns for URLs, but not every possible one. The main restriction at the
@@ -103,13 +109,12 @@ corresponding view functions. It has the following signature:
 .. function:: resolve(path, urlconf=None)
 
 ``path`` is the URL path you want to resolve. As with
-:func:`~django.core.urlresolvers.reverse`, you don't need to
-worry about the ``urlconf`` parameter. The function returns a
-:class:`ResolverMatch` object that allows you
-to access various meta-data about the resolved URL.
+:func:`~django.urls.reverse`, you don't need to worry about the ``urlconf``
+parameter. The function returns a :class:`ResolverMatch` object that allows you
+to access various metadata about the resolved URL.
 
 If the URL does not resolve, the function raises a
-:exc:`~django.core.urlresolvers.Resolver404` exception (a subclass of
+:exc:`~django.urls.Resolver404` exception (a subclass of
 :class:`~django.http.Http404`) .
 
 .. class:: ResolverMatch
@@ -175,10 +180,10 @@ A :class:`ResolverMatch` object can also be assigned to a triple::
 
     func, args, kwargs = resolve('/some/path/')
 
-One possible use of :func:`~django.core.urlresolvers.resolve` would be to test
-whether a view would raise a ``Http404`` error before redirecting to it::
+One possible use of :func:`~django.urls.resolve` would be to test whether a
+view would raise a ``Http404`` error before redirecting to it::
 
-    from django.core.urlresolvers import resolve
+    from django.urls import resolve
     from django.http import HttpResponseRedirect, Http404
     from django.utils.six.moves.urllib.parse import urlparse
 
@@ -202,12 +207,11 @@ get_script_prefix()
 
 .. function:: get_script_prefix()
 
-Normally, you should always use :func:`~django.core.urlresolvers.reverse` to
-define URLs within your application. However, if your application constructs
-part of the URL hierarchy itself, you may occasionally need to generate URLs.
-In that case, you need to be able to find the base URL of the Django project
-within its Web server (normally, :func:`~django.core.urlresolvers.reverse`
-takes care of this for you). In that case, you can call
-``get_script_prefix()``, which will return the script prefix portion of the URL
-for your Django project. If your Django project is at the root of its web
-server, this is always ``"/"``.
+Normally, you should always use :func:`~django.urls.reverse` to define URLs
+within your application. However, if your application constructs part of the
+URL hierarchy itself, you may occasionally need to generate URLs. In that
+case, you need to be able to find the base URL of the Django project within
+its Web server (normally, :func:`~django.urls.reverse` takes care of this for
+you). In that case, you can call ``get_script_prefix()``, which will return
+the script prefix portion of the URL for your Django project. If your Django
+project is at the root of its web server, this is always ``"/"``.

+ 5 - 5
docs/releases/1.1.txt

@@ -380,11 +380,11 @@ Other new features and changes introduced since Django 1.0 include:
   order to allow fine-grained control of when and where the CSRF processing
   takes place.
 
-* :func:`~django.core.urlresolvers.reverse` and code which uses it (e.g., the
-  ``{% url %}`` template tag) now works with URLs in Django's administrative
-  site, provided that the admin URLs are set up via ``include(admin.site.urls)``
-  (sending admin requests to the ``admin.site.root`` view still works, but URLs
-  in the admin will not be "reversible" when configured this way).
+* ``reverse()`` and code which uses it (e.g., the ``{% url %}`` template tag)
+  now works with URLs in Django's administrative site, provided that the admin
+  URLs are set up via ``include(admin.site.urls)`` (sending admin requests to
+  the ``admin.site.root`` view still works, but URLs in the admin will not be
+  "reversible" when configured this way).
 
 * The ``include()`` function in Django URLconf modules can now accept sequences
   of URL patterns (generated by ``patterns()``) in addition to module names.

+ 5 - 2
docs/releases/1.10.txt

@@ -460,6 +460,9 @@ Miscellaneous
 * The ``shell --plain`` option is deprecated in favor of ``-i python`` or
   ``--interface python``.
 
+* Importing from the ``django.core.urlresolvers`` module is deprecated in
+  favor of its new location, :mod:`django.urls`.
+
 .. _removed-features-1.10:
 
 Features removed in 1.10
@@ -485,8 +488,8 @@ removed in Django 1.10 (please see the :ref:`deprecation timeline
 * Using an incorrect count of unpacked values in the ``for`` template tag
   raises an exception rather than failing silently.
 
-* The ability to :func:`~django.core.urlresolvers.reverse` URLs using a dotted
-  Python path is removed.
+* The ability to :func:`~django.urls.reverse` URLs using a dotted Python path
+  is removed.
 
 * Support for ``optparse`` is dropped for custom management commands.
 

+ 6 - 8
docs/releases/1.4.11.txt

@@ -16,14 +16,12 @@ Django's URL handling is based on a mapping of regex patterns
 consists of matching a requested URL against those patterns to
 determine the appropriate view to invoke.
 
-Django also provides a convenience function --
-:func:`~django.core.urlresolvers.reverse` -- which performs this process
-in the opposite direction. The ``reverse()`` function takes
-information about a view and returns a URL which would invoke that
-view. Use of ``reverse()`` is encouraged for application developers,
-as the output of ``reverse()`` is always based on the current URL
-patterns, meaning developers do not need to change other code when
-making changes to URLs.
+Django also provides a convenience function -- ``reverse()`` -- which performs
+this process in the opposite direction. The ``reverse()`` function takes
+information about a view and returns a URL which would invoke that view. Use
+of ``reverse()`` is encouraged for application developers, as the output of
+``reverse()`` is always based on the current URL patterns, meaning developers
+do not need to change other code when making changes to URLs.
 
 One argument signature for ``reverse()`` is to pass a dotted Python
 path to the desired view. In this situation, Django will import the

+ 2 - 3
docs/releases/1.4.12.txt

@@ -9,6 +9,5 @@ Django 1.4.12 fixes a regression in the 1.4.11 security release.
 Bugfixes
 ========
 
-* Restored the ability to :meth:`~django.core.urlresolvers.reverse` views
-  created using :func:`functools.partial()`
-  (:ticket:`22486`)
+* Restored the ability to ``reverse()`` views created using
+  :func:`functools.partial()` (:ticket:`22486`).

+ 2 - 2
docs/releases/1.4.14.txt

@@ -6,8 +6,8 @@ Django 1.4.14 release notes
 
 Django 1.4.14 fixes several security issues in 1.4.13.
 
-:func:`~django.core.urlresolvers.reverse()` could generate URLs pointing to other hosts
-=======================================================================================
+``reverse()`` could generate URLs pointing to other hosts
+=========================================================
 
 In certain situations, URL reversing could generate scheme-relative URLs  (URLs
 starting with two slashes), which could unexpectedly redirect a user  to a

+ 2 - 2
docs/releases/1.4.txt

@@ -371,8 +371,8 @@ Django 1.4 to store the wizard's state in the user's cookies.
 ``reverse_lazy``
 ~~~~~~~~~~~~~~~~
 
-A lazily evaluated version of :func:`django.core.urlresolvers.reverse` was
-added to allow using URL reversals before the project's URLconf gets loaded.
+A lazily evaluated version of ``reverse()`` was added to allow using URL
+reversals before the project's URLconf gets loaded.
 
 Translating URL patterns
 ~~~~~~~~~~~~~~~~~~~~~~~~

+ 6 - 8
docs/releases/1.5.6.txt

@@ -15,14 +15,12 @@ Django's URL handling is based on a mapping of regex patterns
 consists of matching a requested URL against those patterns to
 determine the appropriate view to invoke.
 
-Django also provides a convenience function --
-:func:`~django.core.urlresolvers.reverse` -- which performs this process
-in the opposite direction. The ``reverse()`` function takes
-information about a view and returns a URL which would invoke that
-view. Use of ``reverse()`` is encouraged for application developers,
-as the output of ``reverse()`` is always based on the current URL
-patterns, meaning developers do not need to change other code when
-making changes to URLs.
+Django also provides a convenience function -- ``reverse()`` -- which performs
+this process in the opposite direction. The ``reverse()`` function takes
+information about a view and returns a URL which would invoke that view. Use
+of ``reverse()`` is encouraged for application developers, as the output of
+``reverse()`` is always based on the current URL patterns, meaning developers
+do not need to change other code when making changes to URLs.
 
 One argument signature for ``reverse()`` is to pass a dotted Python
 path to the desired view. In this situation, Django will import the

+ 2 - 2
docs/releases/1.5.7.txt

@@ -9,5 +9,5 @@ Django 1.5.7 fixes a regression in the 1.5.6 security release.
 Bugfixes
 ========
 
-* Restored the ability to :meth:`~django.core.urlresolvers.reverse` views
-  created using :func:`functools.partial()` (:ticket:`22486`).
+* Restored the ability to ``reverse()`` views created using
+  :func:`functools.partial()` (:ticket:`22486`).

+ 2 - 2
docs/releases/1.5.9.txt

@@ -6,8 +6,8 @@ Django 1.5.9 release notes
 
 Django 1.5.9 fixes several security issues in 1.5.8.
 
-:func:`~django.core.urlresolvers.reverse()` could generate URLs pointing to other hosts
-=======================================================================================
+``reverse()`` could generate URLs pointing to other hosts
+=========================================================
 
 In certain situations, URL reversing could generate scheme-relative URLs  (URLs
 starting with two slashes), which could unexpectedly redirect a user  to a

+ 2 - 2
docs/releases/1.5.txt

@@ -293,8 +293,8 @@ Django 1.5 also includes several smaller improvements worth noting:
   objects fetched into memory. See :meth:`QuerySet.delete()
   <django.db.models.query.QuerySet.delete>` for details.
 
-* An instance of :class:`~django.core.urlresolvers.ResolverMatch` is stored on
-  the request as ``resolver_match``.
+* An instance of ``ResolverMatch`` is stored on the request as
+  ``resolver_match``.
 
 * By default, all logging messages reaching the ``django`` logger when
   :setting:`DEBUG` is ``True`` are sent to the console (unless you redefine the

+ 6 - 8
docs/releases/1.6.3.txt

@@ -15,14 +15,12 @@ Django's URL handling is based on a mapping of regex patterns
 consists of matching a requested URL against those patterns to
 determine the appropriate view to invoke.
 
-Django also provides a convenience function --
-:func:`~django.core.urlresolvers.reverse` -- which performs this process
-in the opposite direction. The ``reverse()`` function takes
-information about a view and returns a URL which would invoke that
-view. Use of ``reverse()`` is encouraged for application developers,
-as the output of ``reverse()`` is always based on the current URL
-patterns, meaning developers do not need to change other code when
-making changes to URLs.
+Django also provides a convenience function -- ``reverse()`` -- which performs
+this process in the opposite direction. The ``reverse()`` function takes
+information about a view and returns a URL which would invoke that view. Use
+of ``reverse()`` is encouraged for application developers, as the output of
+``reverse()`` is always based on the current URL patterns, meaning developers
+do not need to change other code when making changes to URLs.
 
 One argument signature for ``reverse()`` is to pass a dotted Python
 path to the desired view. In this situation, Django will import the

+ 3 - 5
docs/releases/1.6.4.txt

@@ -13,10 +13,8 @@ Bugfixes
   cookie format of Django 1.4 and earlier to facilitate upgrading to 1.6 from
   1.4 (:ticket:`22426`).
 
-* Restored the ability to :meth:`~django.core.urlresolvers.reverse` views
-  created using :func:`functools.partial()`
-  (:ticket:`22486`).
+* Restored the ability to ``reverse()`` views created using
+  :func:`functools.partial()` (:ticket:`22486`).
 
 * Fixed the ``object_id`` of the ``LogEntry`` that's created after a user
-  password change in the admin
-  (:ticket:`22515`).
+  password change in the admin (:ticket:`22515`).

+ 2 - 2
docs/releases/1.6.6.txt

@@ -6,8 +6,8 @@ Django 1.6.6 release notes
 
 Django 1.6.6 fixes several security issues and bugs in 1.6.5.
 
-:func:`~django.core.urlresolvers.reverse()` could generate URLs pointing to other hosts
-=======================================================================================
+``reverse()`` could generate URLs pointing to other hosts
+=========================================================
 
 In certain situations, URL reversing could generate scheme-relative URLs  (URLs
 starting with two slashes), which could unexpectedly redirect a user  to a

+ 13 - 12
docs/releases/1.6.txt

@@ -583,16 +583,17 @@ be at the end of a line. If they are not, the comments are ignored and
     {{ title }}{# Translators: Extracted and associated with 'Welcome' below #}
     <h1>{% trans "Welcome" %}</h1>
 
-Quoting in :func:`~django.core.urlresolvers.reverse`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Quoting in ``reverse()``
+~~~~~~~~~~~~~~~~~~~~~~~~
 
 When reversing URLs, Django didn't apply :func:`~django.utils.http.urlquote`
 to arguments before interpolating them in URL patterns. This bug is fixed in
 Django 1.6. If you worked around this bug by applying URL quoting before
-passing arguments to :func:`~django.core.urlresolvers.reverse`, this may
-result in double-quoting. If this happens, simply remove the URL quoting from
-your code. You will also have to replace special characters in URLs used in
-:func:`~django.test.SimpleTestCase.assertRedirects` with their encoded versions.
+passing arguments to ``reverse()``, this may result in double-quoting. If this
+happens, simply remove the URL quoting from your code. You will also have to
+replace special characters in URLs used in
+:func:`~django.test.SimpleTestCase.assertRedirects` with their encoded
+versions.
 
 Storage of IP addresses in the comments app
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -902,12 +903,12 @@ Miscellaneous
   stored as ``null``. Previously, storing a ``blank`` value in a field which
   did not allow ``null`` would cause a database exception at runtime.
 
-* If a :class:`~django.core.urlresolvers.NoReverseMatch` exception is raised
-  from a method when rendering a template, it is not silenced. For example,
-  ``{{ obj.view_href }}`` will cause template rendering to fail if
-  ``view_href()`` raises ``NoReverseMatch``. There is no change to the
-  :ttag:`{% url %}<url>` tag, it causes template rendering to fail like always
-  when ``NoReverseMatch`` is raised.
+* If a ``NoReverseMatch`` exception is raised from a method when rendering a
+  template, it is not silenced. For example, ``{{ obj.view_href }}`` will
+  cause template rendering to fail if ``view_href()`` raises 
+  ``NoReverseMatch``. There is no change to the :ttag:`{% url %}<url>` tag, it
+  causes template rendering to fail like always when ``NoReverseMatch`` is
+  raised.
 
 * :meth:`django.test.Client.logout` now calls
   :meth:`django.contrib.auth.logout` which will send the

+ 1 - 2
docs/releases/1.7.3.txt

@@ -82,8 +82,7 @@ Bugfixes
   (:ticket:`23815`).
 
 * Fixed a crash in the ``django.contrib.auth.redirect_to_login`` view when
-  passing a :func:`~django.core.urlresolvers.reverse_lazy` result on Python 3
-  (:ticket:`24097`).
+  passing a ``reverse_lazy()`` result on Python 3 (:ticket:`24097`).
 
 * Added correct formats for Greek (``el``) (:ticket:`23967`).
 

+ 4 - 5
docs/releases/1.8.txt

@@ -1112,9 +1112,8 @@ Miscellaneous
 * The default max size of the Oracle test tablespace has increased from 300M
   (or 200M, before 1.7.2) to 500M.
 
-* :func:`~django.core.urlresolvers.reverse` and
-  :func:`~django.core.urlresolvers.reverse_lazy` now return Unicode strings
-  instead of byte strings.
+* ``reverse()`` and ``reverse_lazy()`` now return Unicode strings instead of
+  byte strings.
 
 * The ``CacheClass`` shim has been removed from all cache backends.
   These aliases were provided for backwards compatibility with Django 1.3.
@@ -1334,8 +1333,8 @@ Using an incorrect count of unpacked values in the :ttag:`for` template tag
 Using an incorrect count of unpacked values in :ttag:`for` tag will raise an
 exception rather than fail silently in Django 1.10.
 
-Passing a dotted path to :func:`~django.core.urlresolvers.reverse()` and :ttag:`url`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Passing a dotted path to ``reverse()`` and :ttag:`url`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Reversing URLs by Python path is an expensive operation as it causes the
 path being reversed to be imported. This behavior has also resulted in a

+ 4 - 6
docs/releases/1.9.txt

@@ -1083,12 +1083,10 @@ Miscellaneous
   :attr:`~django.test.SimpleTestCase.allow_database_queries` class attribute
   to ``True`` on your test class.
 
-* :attr:`ResolverMatch.app_name
-  <django.core.urlresolvers.ResolverMatch.app_name>` was changed to contain
-  the full namespace path in the case of nested namespaces. For consistency
-  with :attr:`ResolverMatch.namespace
-  <django.core.urlresolvers.ResolverMatch.namespace>`, the empty value is now
-  an empty string instead of ``None``.
+* ``ResolverMatch.app_name`` was changed to contain the full namespace path in
+  the case of nested namespaces. For consistency with
+  ``ResolverMatch.namespace``, the empty value is now an empty string instead
+  of ``None``.
 
 * For security hardening, session keys must be at least 8 characters.
 

+ 4 - 4
docs/topics/class-based-views/generic-editing.txt

@@ -98,7 +98,7 @@ First we need to add :meth:`~django.db.models.Model.get_absolute_url()` to our
 .. snippet::
     :filename: models.py
 
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
     from django.db import models
 
     class Author(models.Model):
@@ -115,7 +115,7 @@ here; we don't have to write any logic ourselves:
     :filename: views.py
 
     from django.views.generic.edit import CreateView, UpdateView, DeleteView
-    from django.core.urlresolvers import reverse_lazy
+    from django.urls import reverse_lazy
     from myapp.models import Author
 
     class AuthorCreate(CreateView):
@@ -131,8 +131,8 @@ here; we don't have to write any logic ourselves:
         success_url = reverse_lazy('author-list')
 
 .. note::
-    We have to use :func:`~django.core.urlresolvers.reverse_lazy` here, not
-    just ``reverse`` as the urls are not loaded when the file is imported.
+    We have to use :func:`~django.urls.reverse_lazy` here, not just
+    ``reverse()`` as the urls are not loaded when the file is imported.
 
 The ``fields`` attribute works the same way as the ``fields`` attribute on the
 inner ``Meta`` class on :class:`~django.forms.ModelForm`. Unless you define the

+ 3 - 3
docs/topics/class-based-views/mixins.txt

@@ -225,7 +225,7 @@ We'll demonstrate this with the ``Author`` model we used in the
     :filename: views.py
 
     from django.http import HttpResponseForbidden, HttpResponseRedirect
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
     from django.views.generic import View
     from django.views.generic.detail import SingleObjectMixin
     from books.models import Author
@@ -445,7 +445,7 @@ Our new ``AuthorDetail`` looks like this::
 
     from django import forms
     from django.http import HttpResponseForbidden
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
     from django.views.generic import DetailView
     from django.views.generic.edit import FormMixin
     from books.models import Author
@@ -541,7 +541,7 @@ can find the author we're talking about, and we have to remember to set
 ``template_name`` to ensure that form errors will render the same
 template as ``AuthorDisplay`` is using on ``GET``::
 
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
     from django.http import HttpResponseForbidden
     from django.views.generic import FormView
     from django.views.generic.detail import SingleObjectMixin

+ 3 - 4
docs/topics/http/shortcuts.txt

@@ -103,9 +103,8 @@ This example is equivalent to::
    * A model: the model's :meth:`~django.db.models.Model.get_absolute_url()`
      function will be called.
 
-   * A view name, possibly with arguments: :func:`urlresolvers.reverse
-     <django.core.urlresolvers.reverse>` will be used to reverse-resolve the
-     name.
+   * A view name, possibly with arguments: :func:`~django.urls.reverse` will be
+     used to reverse-resolve the name.
 
    * An absolute or relative URL, which will be used as-is for the redirect
      location.
@@ -131,7 +130,7 @@ You can use the :func:`redirect` function in a number of ways.
 
 2. By passing the name of a view and optionally some positional or
    keyword arguments; the URL will be reverse resolved using the
-   :func:`~django.core.urlresolvers.reverse` method::
+   :func:`~django.urls.reverse` method::
 
         def my_view(request):
             ...

+ 4 - 5
docs/topics/http/urls.txt

@@ -533,8 +533,7 @@ layers where URLs are needed:
 
 * In templates: Using the :ttag:`url` template tag.
 
-* In Python code: Using the :func:`django.core.urlresolvers.reverse`
-  function.
+* In Python code: Using the :func:`~django.urls.reverse` function.
 
 * In higher level code related to handling of URLs of Django model instances:
   The :meth:`~django.db.models.Model.get_absolute_url` method.
@@ -571,7 +570,7 @@ You can obtain these in template code by using:
 
 Or in Python code::
 
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
     from django.http import HttpResponseRedirect
 
     def redirect_to_year(request):
@@ -671,8 +670,8 @@ the fully qualified name into parts and then tries the following lookup:
 
 2. If there is a current application defined, Django finds and returns the URL
    resolver for that instance. The current application can be specified with
-   the ``current_app`` argument to the
-   :func:`~django.core.urlresolvers.reverse()` function.
+   the ``current_app`` argument to the :func:`~django.urls.reverse()`
+   function.
 
    The :ttag:`url` template tag uses the namespace of the currently resolved
    view as the current application in a

+ 4 - 5
docs/topics/i18n/translation.txt

@@ -1360,7 +1360,7 @@ After defining these URL patterns, Django will automatically add the
 language prefix to the URL patterns that were added by the ``i18n_patterns``
 function. Example::
 
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
     from django.utils.translation import activate
 
     >>> activate('en')
@@ -1414,11 +1414,10 @@ URL patterns can also be marked translatable using the
         url(_(r'^news/'), include(news_patterns, namespace='news')),
     )
 
-After you've created the translations, the
-:func:`~django.core.urlresolvers.reverse` function will return the URL in the
-active language. Example::
+After you've created the translations, the :func:`~django.urls.reverse`
+function will return the URL in the active language. Example::
 
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
     from django.utils.translation import activate
 
     >>> activate('en')

+ 1 - 1
docs/topics/templates.txt

@@ -411,7 +411,7 @@ For example, you can create ``myproject/jinja2.py`` with this content::
     from __future__ import absolute_import  # Python 2 only
 
     from django.contrib.staticfiles.storage import staticfiles_storage
-    from django.core.urlresolvers import reverse
+    from django.urls import reverse
 
     from jinja2 import Environment
 

+ 3 - 4
docs/topics/testing/tools.txt

@@ -507,9 +507,8 @@ Specifically, a ``Response`` object has the following attributes:
 
     .. attribute:: resolver_match
 
-        An instance of :class:`~django.core.urlresolvers.ResolverMatch` for the
-        response. You can use the
-        :attr:`~django.core.urlresolvers.ResolverMatch.func` attribute, for
+        An instance of :class:`~django.urls.ResolverMatch` for the response.
+        You can use the :attr:`~django.urls.ResolverMatch.func` attribute, for
         example, to verify the view that served the response::
 
             # my_view here is a function based view
@@ -520,7 +519,7 @@ Specifically, a ``Response`` object has the following attributes:
             self.assertEqual(response.resolver_match.func.__name__, MyView.as_view().__name__)
 
         If the given URL is not found, accessing this attribute will raise a
-        :exc:`~django.core.urlresolvers.Resolver404` exception.
+        :exc:`~django.urls.Resolver404` exception.
 
 You can also use dictionary syntax on the response object to query the value
 of any settings in the HTTP headers. For example, you could determine the

+ 1 - 1
tests/admin_changelist/tests.py

@@ -10,10 +10,10 @@ from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
 from django.contrib.admin.views.main import ALL_VAR, SEARCH_VAR, ChangeList
 from django.contrib.auth.models import User
 from django.contrib.contenttypes.models import ContentType
-from django.core.urlresolvers import reverse
 from django.template import Context, Template
 from django.test import TestCase, ignore_warnings, override_settings
 from django.test.client import RequestFactory
+from django.urls import reverse
 from django.utils import formats, six
 from django.utils.deprecation import RemovedInDjango20Warning
 

+ 1 - 1
tests/admin_custom_urls/models.py

@@ -1,9 +1,9 @@
 from functools import update_wrapper
 
 from django.contrib import admin
-from django.core.urlresolvers import reverse
 from django.db import models
 from django.http import HttpResponseRedirect
+from django.urls import reverse
 from django.utils.encoding import python_2_unicode_compatible
 
 

+ 1 - 1
tests/admin_custom_urls/tests.py

@@ -4,9 +4,9 @@ import datetime
 
 from django.contrib.admin.utils import quote
 from django.contrib.auth.models import User
-from django.core.urlresolvers import reverse
 from django.template.response import TemplateResponse
 from django.test import TestCase, override_settings
+from django.urls import reverse
 
 from .models import Action, Car, Person
 

+ 1 - 1
tests/admin_docs/tests.py

@@ -7,9 +7,9 @@ from django.contrib.admindocs import utils
 from django.contrib.admindocs.views import get_return_data_type
 from django.contrib.auth.models import User
 from django.contrib.sites.models import Site
-from django.core.urlresolvers import reverse
 from django.test import TestCase, modify_settings, override_settings
 from django.test.utils import captured_stderr
+from django.urls import reverse
 
 from .models import Company, Person
 

+ 1 - 1
tests/admin_inlines/tests.py

@@ -7,8 +7,8 @@ from django.contrib.admin.helpers import InlineAdminForm
 from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
 from django.contrib.auth.models import Permission, User
 from django.contrib.contenttypes.models import ContentType
-from django.core.urlresolvers import reverse
 from django.test import RequestFactory, TestCase, override_settings
+from django.urls import reverse
 
 from .admin import InnerInline, site as admin_site
 from .models import (

+ 1 - 1
tests/admin_utils/test_logentry.py

@@ -6,8 +6,8 @@ from django.contrib.admin.models import ADDITION, CHANGE, DELETION, LogEntry
 from django.contrib.admin.utils import quote
 from django.contrib.auth.models import User
 from django.contrib.contenttypes.models import ContentType
-from django.core.urlresolvers import reverse
 from django.test import TestCase, override_settings
+from django.urls import reverse
 from django.utils import six
 from django.utils.encoding import force_bytes
 from django.utils.html import escape

+ 1 - 1
tests/admin_views/test_adminsite.py

@@ -5,9 +5,9 @@ import datetime
 from django.conf.urls import url
 from django.contrib import admin
 from django.contrib.auth.models import User
-from django.core.urlresolvers import reverse
 from django.test import TestCase, override_settings
 from django.test.client import RequestFactory
+from django.urls import reverse
 
 from .models import Article
 

+ 1 - 1
tests/admin_views/tests.py

@@ -23,7 +23,6 @@ from django.contrib.staticfiles.storage import staticfiles_storage
 from django.core import mail
 from django.core.checks import Error
 from django.core.files import temp as tempfile
-from django.core.urlresolvers import NoReverseMatch, resolve, reverse
 from django.forms.utils import ErrorList
 from django.template.loader import render_to_string
 from django.template.response import TemplateResponse
@@ -32,6 +31,7 @@ from django.test import (
     override_settings, skipUnlessDBFeature,
 )
 from django.test.utils import override_script_prefix, patch_logger
+from django.urls import NoReverseMatch, resolve, reverse
 from django.utils import formats, six, translation
 from django.utils._os import upath
 from django.utils.cache import get_max_age

+ 1 - 1
tests/admin_widgets/tests.py

@@ -15,9 +15,9 @@ from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
 from django.contrib.auth.models import User
 from django.core.files.storage import default_storage
 from django.core.files.uploadedfile import SimpleUploadedFile
-from django.core.urlresolvers import reverse
 from django.db.models import CharField, DateField
 from django.test import SimpleTestCase, TestCase, override_settings
+from django.urls import reverse
 from django.utils import six, translation
 
 from . import models

+ 1 - 1
tests/auth_tests/test_views.py

@@ -20,12 +20,12 @@ from django.contrib.auth.views import login as login_view, redirect_to_login
 from django.contrib.sessions.middleware import SessionMiddleware
 from django.contrib.sites.requests import RequestSite
 from django.core import mail
-from django.core.urlresolvers import NoReverseMatch, reverse, reverse_lazy
 from django.db import connection
 from django.http import HttpRequest, QueryDict
 from django.middleware.csrf import CsrfViewMiddleware, get_token
 from django.test import TestCase, override_settings
 from django.test.utils import patch_logger
+from django.urls import NoReverseMatch, reverse, reverse_lazy
 from django.utils.encoding import force_text
 from django.utils.http import urlquote
 from django.utils.six.moves.urllib.parse import ParseResult, urlparse

+ 1 - 1
tests/forms_tests/tests/test_widgets.py

@@ -2,7 +2,6 @@
 from __future__ import unicode_literals
 
 from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
-from django.core.urlresolvers import reverse
 from django.forms import (
     CheckboxSelectMultiple, ClearableFileInput, RadioSelect, TextInput,
 )
@@ -10,6 +9,7 @@ from django.forms.widgets import (
     ChoiceFieldRenderer, ChoiceInput, RadioFieldRenderer,
 )
 from django.test import SimpleTestCase, override_settings
+from django.urls import reverse
 from django.utils import six
 from django.utils.encoding import force_text, python_2_unicode_compatible
 from django.utils.safestring import SafeData

+ 1 - 1
tests/generic_inline_admin/tests.py

@@ -9,12 +9,12 @@ from django.contrib.auth.models import User
 from django.contrib.contenttypes.admin import GenericTabularInline
 from django.contrib.contenttypes.forms import generic_inlineformset_factory
 from django.contrib.contenttypes.models import ContentType
-from django.core.urlresolvers import reverse
 from django.forms.formsets import DEFAULT_MAX_NUM
 from django.forms.models import ModelForm
 from django.test import (
     RequestFactory, SimpleTestCase, TestCase, override_settings,
 )
+from django.urls import reverse
 
 from .admin import MediaInline, MediaPermanentInline, site as admin_site
 from .models import Category, Episode, EpisodePermanent, Media, PhoneNumber

+ 1 - 1
tests/generic_views/models.py

@@ -1,7 +1,7 @@
-from django.core.urlresolvers import reverse
 from django.db import models
 from django.db.models import QuerySet
 from django.db.models.manager import BaseManager
+from django.urls import reverse
 from django.utils.encoding import python_2_unicode_compatible
 
 

+ 1 - 1
tests/generic_views/test_base.py

@@ -4,10 +4,10 @@ import time
 import unittest
 
 from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import resolve
 from django.http import HttpResponse
 from django.test import RequestFactory, SimpleTestCase, override_settings
 from django.test.utils import require_jinja2
+from django.urls import resolve
 from django.views.generic import RedirectView, TemplateView, View
 
 from . import views

+ 1 - 1
tests/generic_views/test_edit.py

@@ -2,9 +2,9 @@ from __future__ import unicode_literals
 
 from django import forms
 from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import reverse
 from django.test import SimpleTestCase, TestCase, override_settings
 from django.test.client import RequestFactory
+from django.urls import reverse
 from django.views.generic.base import View
 from django.views.generic.edit import CreateView, FormMixin, ModelFormMixin
 

+ 1 - 1
tests/generic_views/views.py

@@ -2,7 +2,7 @@ from __future__ import unicode_literals
 
 from django.contrib.auth.decorators import login_required
 from django.core.paginator import Paginator
-from django.core.urlresolvers import reverse, reverse_lazy
+from django.urls import reverse, reverse_lazy
 from django.utils.decorators import method_decorator
 from django.views import generic
 

+ 1 - 1
tests/i18n/patterns/tests.py

@@ -3,12 +3,12 @@ from __future__ import unicode_literals
 import os
 
 from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import clear_url_caches, reverse, translate_url
 from django.http import HttpResponsePermanentRedirect
 from django.middleware.locale import LocaleMiddleware
 from django.template import Context, Template
 from django.test import SimpleTestCase, override_settings
 from django.test.utils import override_script_prefix
+from django.urls import clear_url_caches, reverse, translate_url
 from django.utils import translation
 from django.utils._os import upath
 

+ 1 - 1
tests/messages_tests/base.py

@@ -4,8 +4,8 @@ from django.contrib.messages.api import MessageFailure
 from django.contrib.messages.constants import DEFAULT_LEVELS
 from django.contrib.messages.storage import base, default_storage
 from django.contrib.messages.storage.base import Message
-from django.core.urlresolvers import reverse
 from django.test import modify_settings, override_settings
+from django.urls import reverse
 from django.utils.translation import ugettext_lazy
 
 

+ 1 - 1
tests/messages_tests/test_mixins.py

@@ -1,5 +1,5 @@
-from django.core.urlresolvers import reverse
 from django.test import SimpleTestCase, override_settings
+from django.urls import reverse
 
 from .urls import ContactFormViewWithMsg
 

+ 1 - 1
tests/messages_tests/urls.py

@@ -2,10 +2,10 @@ from django import forms
 from django.conf.urls import url
 from django.contrib import messages
 from django.contrib.messages.views import SuccessMessageMixin
-from django.core.urlresolvers import reverse
 from django.http import HttpResponse, HttpResponseRedirect
 from django.template import engines
 from django.template.response import TemplateResponse
+from django.urls import reverse
 from django.views.decorators.cache import never_cache
 from django.views.generic.edit import FormView
 

Some files were not shown because too many files changed in this diff