diff --git a/.travis.yml b/.travis.yml index f27f214..a1998d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,10 @@ python: - 3.3 - 3.4 - 3.5 +- 3.6 - pypy - pypy3.3-5.5-alpha +- pypy3.5-5.10.1 env: - DJANGO=1.4.15 - DJANGO=1.6.7 @@ -15,6 +17,7 @@ env: - DJANGO=1.9 - DJANGO=1.10 - DJANGO=1.11 +- DJANGO=2.0 install: - pip install -q gitversion - pip install -q Django==$DJANGO @@ -36,6 +39,10 @@ matrix: env: DJANGO=1.10 - python: 2.6 env: DJANGO=1.11 + - python: 2.6 + env: DJANGO=2.0 + - python: 2.7 + env: DJANGO=2.0 - python: 3.3 env: DJANGO=1.4.15 - python: 3.3 @@ -44,6 +51,8 @@ matrix: env: DJANGO=1.10 - python: 3.3 env: DJANGO=1.11 + - python: 3.3 + env: DJANGO=2.0 - python: 3.4 env: DJANGO=1.4.15 - python: 3.5 @@ -52,6 +61,20 @@ matrix: env: DJANGO=1.6.7 - python: 3.5 env: DJANGO=1.7 + - python: 3.6 + env: DJANGO=1.4.15 + - python: 3.6 + env: DJANGO=1.6.7 + - python: 3.6 + env: DJANGO=1.7 + - python: 3.6 + env: DJANGO=1.8 + - python: 3.6 + env: DJANGO=1.9 + - python: 3.6 + env: DJANGO=1.10 + - python: pypy + env: DJANGO=2.0 - python: pypy3.3-5.5-alpha env: DJANGO=1.4.15 - python: pypy3.3-5.5-alpha @@ -60,6 +83,14 @@ matrix: env: DJANGO=1.10 - python: pypy3.3-5.5-alpha env: DJANGO=1.11 + - python: pypy3.3-5.5-alpha + env: DJANGO=2.0 + - python: pypy3.5-5.10.1 + env: DJANGO=1.4.15 + - python: pypy3.5-5.10.1 + env: DJANGO=1.6.7 + - python: pypy3.5-5.10.1 + env: DJANGO=1.7 deploy: provider: pypi user: LeaChim @@ -70,4 +101,4 @@ deploy: all_branches: true repo: mikebryant/django-autoconfig tags: true - condition: "$DJANGO = 1.9" + condition: "$DJANGO = 1.11" diff --git a/django_autoconfig/contrib/admin/urls.py b/django_autoconfig/contrib/admin/urls.py index 563eb3d..d432df7 100644 --- a/django_autoconfig/contrib/admin/urls.py +++ b/django_autoconfig/contrib/admin/urls.py @@ -1,3 +1,4 @@ +import django from django.conf.urls import include, url try: from django.conf.urls import patterns @@ -7,6 +8,13 @@ def patterns(_, *args): from django.contrib import admin admin.autodiscover() -urlpatterns = patterns('', - url('', include(admin.site.urls)), -) +# admin.site.urls should not be included since Django 1.9 (and breaks since Django 2.0). +# Additionally, urlpatterns shall be an array since Django 1.8. +if django.VERSION >= (1, 9): + urlpatterns = [ + url('', admin.site.urls), + ] +else: + urlpatterns = patterns('', + url('', include(admin.site.urls)), + ) diff --git a/django_autoconfig/tests/app_middleware/autoconfig.py b/django_autoconfig/tests/app_middleware/autoconfig.py index f8b739e..97b2c41 100644 --- a/django_autoconfig/tests/app_middleware/autoconfig.py +++ b/django_autoconfig/tests/app_middleware/autoconfig.py @@ -1,3 +1,12 @@ -SETTINGS = { - 'MIDDLEWARE_CLASSES': ['my.middleware'], -} +import django + + +# Django 2.0 completely removed MIDDLEWARE_CLASSES. MIDDLEWARE shall be used instead. +if django.VERSION >= (2, 0): + SETTINGS = { + 'MIDDLEWARE': ['my.middleware'], + } +else: + SETTINGS = { + 'MIDDLEWARE_CLASSES': ['my.middleware'], + } diff --git a/django_autoconfig/tests/test_autoconfig.py b/django_autoconfig/tests/test_autoconfig.py index 006cd45..882d35e 100644 --- a/django_autoconfig/tests/test_autoconfig.py +++ b/django_autoconfig/tests/test_autoconfig.py @@ -6,8 +6,14 @@ import copy from django.core.exceptions import ImproperlyConfigured -import django.core.urlresolvers -from django.core.urlresolvers import resolve +import django +try: + from django.core import urlresolvers + from django.core.urlresolvers import resolve +except ImportError: + from django import urls as urlresolvers + from django.urls import resolve + from django import test if django.VERSION < (1, 7): @@ -47,16 +53,43 @@ def test_new_setting(self): autoconfig.configure_settings(self.settings_dict) self.assertEqual(self.settings_dict['NEW_LIST_SETTING'], [1, 2, 3]) - def test_list_setting_from_defaults(self): + @unittest.skipIf(django.VERSION >= (2, 0), 'MIDDLEWARE_CLASSES setting was removed in Django 2.0') + def test_list_setting_from_defaults_django_1_x(self): ''' A list setting that exists in the django.conf.settings.global_settings should merge with the default, not replace it entirely. + + Uses MIDDLEWARE_CLASSES used in Django 1.x. ''' self.settings_dict['INSTALLED_APPS'] = ['django_autoconfig.tests.app_middleware'] autoconfig.configure_settings(self.settings_dict) self.assertIn('my.middleware', self.settings_dict['MIDDLEWARE_CLASSES']) self.assertIn('django.middleware.common.CommonMiddleware', self.settings_dict['MIDDLEWARE_CLASSES']) + @unittest.skipIf(django.VERSION < (2, 0), 'MIDDLEWARE setting is empty by default since Django 2.0') + def test_list_setting_from_defaults_django_2_x(self): + ''' + A list setting that exists in the django.conf.settings.global_settings + should merge with the default, not replace it entirely. + + Since Django 2.0, MIDDLEWARE_CLASSES is removed and MIDDLEWARE has empty default. + Simulate non-empty default by replacing the global default value. + ''' + from django.conf import global_settings + + # Overwrite the default settings. + old_middleware = global_settings.MIDDLEWARE + global_settings.MIDDLEWARE = ['django.middleware.common.CommonMiddleware'] + + try: + self.settings_dict['INSTALLED_APPS'] = ['django_autoconfig.tests.app_middleware'] + autoconfig.configure_settings(self.settings_dict) + self.assertIn('my.middleware', self.settings_dict['MIDDLEWARE']) + self.assertIn('django.middleware.common.CommonMiddleware', self.settings_dict['MIDDLEWARE']) + finally: + # Restore default settings to its original value. + global_settings.MIDDLEWARE = old_middleware + def test_no_autoconfig(self): ''' An app with no autoconfig shouldn't break things. @@ -188,7 +221,7 @@ def test_premature_evaluation(self): of the url prior to finishing the settings. ''' self.triggered = False - autoconfig.merge_dictionaries({'LOGIN_URL': '/login/'}, {'LOGIN_URL': django.core.urlresolvers.reverse_lazy('does.not.exist')}) + autoconfig.merge_dictionaries({'LOGIN_URL': '/login/'}, {'LOGIN_URL': urlresolvers.reverse_lazy('does.not.exist')}) self.assertFalse(self.triggered) def test_environment_settings(self): @@ -238,7 +271,9 @@ class ConfigureUrlsTestCase(test.TestCase): def create_urlconf(self, apps, **kwargs): '''Create a urlconf from a list of apps.''' self.urlpatterns = autoconfig.configure_urls(apps, **kwargs) - django.core.urlresolvers._resolver_cache = {} + # The following line previously set urlresolvers._resolver_cache to {}, but _resolver_cache is not available + # since Django 1.7. Using urlresolvers.clear_url_caches() is better way of clearing the cache. + urlresolvers.clear_url_caches() def test_urls(self): '''Test a simple url autoconfiguration.''' @@ -248,13 +283,13 @@ def test_urls(self): def test_blank_urls(self): '''Test a url autoconfiguration with an app with a blank urls.py.''' self.create_urlconf(['django_autoconfig.tests.app_urls', 'django_autoconfig.tests.app_blank_urls']) - with self.assertRaises(django.core.urlresolvers.Resolver404): + with self.assertRaises(urlresolvers.Resolver404): resolve('/django-autoconfig.tests.app-blank-urls/index/', urlconf=self) def test_missing_app_urls(self): '''Test a url autoconfiguration with an app without urls.''' self.create_urlconf(['django_autoconfig.tests.app_urls', 'django_autoconfig.tests.app1']) - with self.assertRaises(django.core.urlresolvers.Resolver404): + with self.assertRaises(urlresolvers.Resolver404): resolve('/django-autoconfig.tests.app1/index/', urlconf=self) def test_broken_urls(self): @@ -265,17 +300,31 @@ def test_broken_urls(self): def test_no_index_view(self): '''Test the index view functionality, if it's not used.''' self.create_urlconf(['django_autoconfig.tests.app_urls']) - with self.assertRaises(django.core.urlresolvers.Resolver404): + with self.assertRaises(urlresolvers.Resolver404): resolve('/', urlconf=self).func - @unittest.skipIf(django.VERSION < (1, 6), 'AUTOCONFIG_INDEX_VIEW needs Django >= 1.6') - def test_broken_index_view(self): - '''Test the index view functionality with a broken view.''' + @unittest.skipIf(django.VERSION < (1, 6) or django.VERSION >= (2, 0), + 'AUTOCONFIG_INDEX_VIEW needs Django >= 1.6; ' + 'Django 2.0 does not silence the NoReverseMatch exception if the pattern does not exist') + def test_broken_index_view_django_1_6_to_1_11(self): + '''Test the index view functionality with a broken view. Works for Django versions between 1.6 and 1.11.''' self.create_urlconf([], index_view='does-not-exist') view = resolve('/', urlconf=self).func response = view(test.RequestFactory().get(path='/')) self.assertEqual(response.status_code, 410) + @unittest.skipIf(django.VERSION < (2, 0), + 'Django 2.0 does not silence the NoReverseMatch exception if the pattern does not exist') + def test_broken_index_view_django_2_x(self): + ''' + Test the index view functionality with a broken view. + In Django 2.0, NoReverseMatch exception is no longer silenced. + ''' + self.create_urlconf([], index_view='does-not-exist') + view = resolve('/', urlconf=self).func + with self.assertRaises(urlresolvers.NoReverseMatch): + view(test.RequestFactory().get(path='/')) + def test_url_prefix_blank(self): '''Test the url prefix mapping works for blank prefixes.''' self.create_urlconf( @@ -285,7 +334,7 @@ def test_url_prefix_blank(self): }, ) resolve('/index/', urlconf=self) - with self.assertRaises(django.core.urlresolvers.Resolver404): + with self.assertRaises(urlresolvers.Resolver404): resolve('/django-autoconfig.tests.app-urls/index/', urlconf=self) def test_url_prefixes(self): diff --git a/setup.py b/setup.py index 7a76635..71ea264 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ import sys INSTALL_REQUIRES = [ - 'django < 1.12', + 'django < 2.1', ] if sys.version_info < (2, 7):