From a72963bdbd97fd3cf19a42bf1cfa6e74832f0243 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 17 Dec 2017 05:17:43 +0900 Subject: [PATCH] Drop Django <= 1.10 support After Django 2.0 support, we no longer supports Django 1.10 or older (Actually Django 1.10 seems to work though). The current django.VERSION branches are all related to Django 1.10 or older, so we can drop all conditions. py35dj20 job is now voting. blueprint django2-support Change-Id: Iefc0ab1c62c82f2842ec7761a9b981da9351cbd2 --- .zuul.yaml | 4 +- horizon/base.py | 8 +-- horizon/templatetags/shellfilter.py | 6 +- horizon/test/unit/test_base.py | 6 +- openstack_auth/tests/unit/test_auth.py | 21 ++---- .../dashboards/identity/projects/tests.py | 12 +--- .../dashboards/identity/users/tests.py | 70 +++++++++---------- .../dashboards/project/instances/tests.py | 17 ++--- .../dashboards/project/routers/tests.py | 12 +--- .../project/security_groups/tests.py | 20 ++---- .../dashboards/project/volumes/tests.py | 53 ++++---------- openstack_dashboard/test/helpers.py | 13 ++-- .../notes/django-2.0-b37c6e91d20519fa.yaml | 6 ++ 13 files changed, 81 insertions(+), 167 deletions(-) create mode 100644 releasenotes/notes/django-2.0-b37c6e91d20519fa.yaml diff --git a/.zuul.yaml b/.zuul.yaml index d4488ac2f9..a02f1c8d77 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -54,11 +54,11 @@ - project: check: jobs: - - horizon-openstack-tox-py35dj20: - voting: false + - horizon-openstack-tox-py35dj20 - horizon-selenium-headless - horizon-dsvm-tempest-plugin gate: jobs: + - horizon-openstack-tox-py35dj20 - horizon-selenium-headless - horizon-dsvm-tempest-plugin diff --git a/horizon/base.py b/horizon/base.py index 9e60f10ad4..4ed7d19ca8 100644 --- a/horizon/base.py +++ b/horizon/base.py @@ -27,7 +27,6 @@ import inspect import logging import os -import django from django.conf import settings from django.conf.urls import include from django.conf.urls import url @@ -58,12 +57,7 @@ def _decorate_urlconf(urlpatterns, decorator, *args, **kwargs): for pattern in urlpatterns: if getattr(pattern, 'callback', None): decorated = decorator(pattern.callback, *args, **kwargs) - if django.VERSION >= (1, 10): - pattern.callback = decorated - else: - # prior to 1.10 callback was a property and we had - # to modify the private attribute behind the property - pattern._callback = decorated + pattern.callback = decorated if getattr(pattern, 'url_patterns', []): _decorate_urlconf(pattern.url_patterns, decorator, *args, **kwargs) diff --git a/horizon/templatetags/shellfilter.py b/horizon/templatetags/shellfilter.py index 108ca6204b..d3f2ac599b 100644 --- a/horizon/templatetags/shellfilter.py +++ b/horizon/templatetags/shellfilter.py @@ -10,15 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. -import django from django import template from django.template import defaultfilters from django.utils import safestring -if django.VERSION >= (1, 9): - register = template.Library() -else: - register = template.base.Library() +register = template.Library() @register.filter(is_safe=True) diff --git a/horizon/test/unit/test_base.py b/horizon/test/unit/test_base.py index 344c80444f..a0fc9ed755 100644 --- a/horizon/test/unit/test_base.py +++ b/horizon/test/unit/test_base.py @@ -22,7 +22,6 @@ from importlib import import_module import six from six import moves -import django from django.conf import settings from django.contrib.auth.models import User from django.core.exceptions import ImproperlyConfigured @@ -310,10 +309,7 @@ class HorizonTests(BaseHorizonTests): self.client.logout() resp = self.client.get(url) - if django.VERSION >= (1, 9): - self.assertRedirects(resp, settings.TESTSERVER + redirect_url) - else: - self.assertRedirects(resp, redirect_url) + self.assertRedirects(resp, settings.TESTSERVER + redirect_url) # Set SSL settings for test server settings.SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', diff --git a/openstack_auth/tests/unit/test_auth.py b/openstack_auth/tests/unit/test_auth.py index c48702c472..2dd193ce11 100644 --- a/openstack_auth/tests/unit/test_auth.py +++ b/openstack_auth/tests/unit/test_auth.py @@ -13,7 +13,6 @@ import uuid -import django from django.conf import settings from django.contrib import auth from django import test @@ -395,10 +394,7 @@ class OpenStackAuthTestsV2(OpenStackAuthTestsMixin, test.TestCase): response = self.client.get(url, form_data) if next: - if django.VERSION >= (1, 9): - expected_url = next - else: - expected_url = 'http://testserver%s' % next + expected_url = next self.assertEqual(response['location'], expected_url) else: self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) @@ -444,10 +440,7 @@ class OpenStackAuthTestsV2(OpenStackAuthTestsMixin, test.TestCase): response = self.client.get(url, form_data) if next: - if django.VERSION >= (1, 9): - expected_url = next - else: - expected_url = 'http://testserver%s' % next + expected_url = next self.assertEqual(response['location'], expected_url) else: self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) @@ -768,10 +761,7 @@ class OpenStackAuthTestsV3(OpenStackAuthTestsMixin, response = self.client.get(url, form_data) if next: - if django.VERSION >= (1, 9): - expected_url = next - else: - expected_url = 'http://testserver%s' % next + expected_url = next self.assertEqual(response['location'], expected_url) else: self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) @@ -816,10 +806,7 @@ class OpenStackAuthTestsV3(OpenStackAuthTestsMixin, response = self.client.get(url, form_data) if next: - if django.VERSION >= (1, 9): - expected_url = next - else: - expected_url = 'http://testserver%s' % next + expected_url = next self.assertEqual(response['location'], expected_url) else: self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) diff --git a/openstack_dashboard/dashboards/identity/projects/tests.py b/openstack_dashboard/dashboards/identity/projects/tests.py index d5b6d91bd0..26d1bac440 100644 --- a/openstack_dashboard/dashboards/identity/projects/tests.py +++ b/openstack_dashboard/dashboards/identity/projects/tests.py @@ -17,7 +17,6 @@ import logging import os import unittest -import django from django import http from django.test.utils import override_settings from django.urls import reverse @@ -277,14 +276,9 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests): res = self.client.get(reverse('horizon:identity:projects:create')) self.assertTemplateUsed(res, views.WorkflowView.template_name) - if django.VERSION >= (1, 10): - pattern = ('') - else: - pattern = ('') + pattern = ('') self.assertContains(res, pattern, html=True) workflow = res.context['workflow'] diff --git a/openstack_dashboard/dashboards/identity/users/tests.py b/openstack_dashboard/dashboards/identity/users/tests.py index c062cddf7f..b0d6f59e40 100644 --- a/openstack_dashboard/dashboards/identity/users/tests.py +++ b/openstack_dashboard/dashboards/identity/users/tests.py @@ -18,7 +18,6 @@ from socket import timeout as socket_timeout -import django from django import http from django.test.utils import override_settings from django.urls import reverse @@ -238,19 +237,18 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.get_default_role(IgnoreArg()) \ .AndReturn(self.roles.first()) - if django.VERSION >= (1, 9): - if api.keystone.VERSIONS.active >= 3: - api.keystone.tenant_list( - IgnoreArg(), domain=domain_id).AndReturn( - [self.tenants.list(), False]) - else: - api.keystone.tenant_list( - IgnoreArg(), user=None).AndReturn( - [self.tenants.list(), False]) + if api.keystone.VERSIONS.active >= 3: + api.keystone.tenant_list( + IgnoreArg(), domain=domain_id).AndReturn( + [self.tenants.list(), False]) + else: + api.keystone.tenant_list( + IgnoreArg(), user=None).AndReturn( + [self.tenants.list(), False]) - api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) - api.keystone.get_default_role(IgnoreArg()) \ - .AndReturn(self.roles.first()) + api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) + api.keystone.get_default_role(IgnoreArg()) \ + .AndReturn(self.roles.first()) self.mox.ReplayAll() @@ -291,19 +289,18 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.get_default_role(IgnoreArg()) \ .AndReturn(self.roles.first()) - if django.VERSION >= (1, 9): - if api.keystone.VERSIONS.active >= 3: - api.keystone.tenant_list( - IgnoreArg(), domain=domain_id).AndReturn( - [self.tenants.list(), False]) - else: - api.keystone.tenant_list( - IgnoreArg(), user=None).AndReturn( - [self.tenants.list(), False]) + if api.keystone.VERSIONS.active >= 3: + api.keystone.tenant_list( + IgnoreArg(), domain=domain_id).AndReturn( + [self.tenants.list(), False]) + else: + api.keystone.tenant_list( + IgnoreArg(), user=None).AndReturn( + [self.tenants.list(), False]) - api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) - api.keystone.get_default_role(IgnoreArg()) \ - .AndReturn(self.roles.first()) + api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) + api.keystone.get_default_role(IgnoreArg()) \ + .AndReturn(self.roles.first()) self.mox.ReplayAll() @@ -347,19 +344,18 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.get_default_role(IgnoreArg()) \ .AndReturn(self.roles.first()) - if django.VERSION >= (1, 9): - if api.keystone.VERSIONS.active >= 3: - api.keystone.tenant_list( - IgnoreArg(), domain=domain_id).AndReturn( - [self.tenants.list(), False]) - else: - api.keystone.tenant_list( - IgnoreArg(), user=None).AndReturn( - [self.tenants.list(), False]) + if api.keystone.VERSIONS.active >= 3: + api.keystone.tenant_list( + IgnoreArg(), domain=domain_id).AndReturn( + [self.tenants.list(), False]) + else: + api.keystone.tenant_list( + IgnoreArg(), user=None).AndReturn( + [self.tenants.list(), False]) - api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) - api.keystone.get_default_role(IgnoreArg()) \ - .AndReturn(self.roles.first()) + api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) + api.keystone.get_default_role(IgnoreArg()) \ + .AndReturn(self.roles.first()) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index 9a67c827d4..8e2b5cd989 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py @@ -1783,18 +1783,11 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase): # NOTE(adriant): Django 1.11 changes the checked syntax to use html5 # "checked" rather than XHTML's "checked='checked'". - if django.VERSION >= (1, 11): - checked_box = ( - '' - ) - else: - checked_box = ( - '' - ) + checked_box = ( + '' + ) if only_one_network: self.assertContains(res, checked_box, html=True) else: diff --git a/openstack_dashboard/dashboards/project/routers/tests.py b/openstack_dashboard/dashboards/project/routers/tests.py index c9423864df..71d6a4f7bc 100644 --- a/openstack_dashboard/dashboards/project/routers/tests.py +++ b/openstack_dashboard/dashboards/project/routers/tests.py @@ -13,7 +13,6 @@ # under the License. import copy -import django from django import http from django.urls import reverse @@ -590,14 +589,9 @@ class RouterActionTests(RouterMixin, test.TestCase): self.assertTemplateUsed(res, 'project/routers/update.html') self.assertContains(res, 'Router Type') - if django.VERSION >= (1, 10): - pattern = ('') - else: - pattern = ('') + pattern = ('') self.assertContains(res, pattern, html=True) self.assertNotContains(res, 'centralized') diff --git a/openstack_dashboard/dashboards/project/security_groups/tests.py b/openstack_dashboard/dashboards/project/security_groups/tests.py index 95cce0b116..a97332ab60 100644 --- a/openstack_dashboard/dashboards/project/security_groups/tests.py +++ b/openstack_dashboard/dashboards/project/security_groups/tests.py @@ -21,7 +21,6 @@ import cgi from mox3.mox import IsA import six -import django from django.conf import settings from django import http from django.urls import reverse @@ -478,9 +477,7 @@ class SecurityGroupsViewTests(test.TestCase): sec_group_list = self.security_groups.list() rule = self.security_group_rules.first() - api.neutron.security_group_list( - IsA(http.HttpRequest)).AndReturn(sec_group_list) - if django.VERSION >= (1, 9): + for i in range(2): api.neutron.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) @@ -503,13 +500,9 @@ class SecurityGroupsViewTests(test.TestCase): sec_group_list = self.security_groups.list() rule = self.security_group_rules.first() - for i in range(3): + for i in range(6): api.neutron.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) - if django.VERSION >= (1, 9): - for i in range(3): - api.neutron.security_group_list( - IsA(http.HttpRequest)).AndReturn(sec_group_list) self.mox.ReplayAll() @@ -559,10 +552,7 @@ class SecurityGroupsViewTests(test.TestCase): icmp_rule = self.security_group_rules.list()[1] # Call POST 5 times (*2 if Django >= 1.9) - call_post = 5 - if django.VERSION >= (1, 9): - call_post *= 2 - + call_post = 5 * 2 for i in range(call_post): api.neutron.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) @@ -921,9 +911,7 @@ class SecurityGroupsViewTests(test.TestCase): sec_group_list = self.security_groups.list() rule = self.security_group_rules.first() - api.neutron.security_group_list( - IsA(http.HttpRequest)).AndReturn(sec_group_list) - if django.VERSION >= (1, 9): + for i in range(2): api.neutron.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) diff --git a/openstack_dashboard/dashboards/project/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/tests.py index 7571d08d00..ccec5e1620 100644 --- a/openstack_dashboard/dashboards/project/volumes/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/tests.py @@ -17,7 +17,6 @@ import copy import mock import six -import django from django.conf import settings from django.forms import widgets from django.template.defaultfilters import slugify @@ -616,12 +615,8 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): self.assertFormError(res, 'form', None, "The volume size cannot be less than the " "snapshot size (40GiB)") - if django.VERSION >= (1, 9): - self.assertEqual(3, self.mock_volume_type_list.call_count) - self.assertEqual(2, self.mock_volume_type_default.call_count) - else: - self.assertEqual(2, self.mock_volume_type_list.call_count) - self.assertEqual(1, self.mock_volume_type_default.call_count) + self.assertEqual(3, self.mock_volume_type_list.call_count) + self.assertEqual(2, self.mock_volume_type_default.call_count) self.mock_volume_snapshot_get.assert_called_with(test.IsHttpRequest(), str(snapshot.id)) @@ -788,12 +783,8 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): self.assertFormError(res, 'form', None, msg) - if django.VERSION >= (1, 9): - self.assertEqual(3, self.mock_volume_type_list.call_count) - self.assertEqual(2, self.mock_volume_type_default.call_count) - else: - self.assertEqual(2, self.mock_volume_type_list.call_count) - self.assertEqual(1, self.mock_volume_type_default.call_count) + self.assertEqual(3, self.mock_volume_type_list.call_count) + self.assertEqual(2, self.mock_volume_type_default.call_count) self.assertEqual(2, self.mock_tenant_limit_usages.call_count) self.mock_image_get.assert_called_with(test.IsHttpRequest(), @@ -835,14 +826,9 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): self.assertFormError(res, 'form', None, "The volume size cannot be less than the " "image minimum disk size (30GiB)") - if django.VERSION >= (1, 9): - self.assertEqual(3, self.mock_volume_type_list.call_count) - self.assertEqual(2, self.mock_volume_type_default.call_count) - self.assertEqual(2, self.mock_availability_zone_list.call_count) - else: - self.assertEqual(2, self.mock_volume_type_list.call_count) - self.assertEqual(1, self.mock_volume_type_default.call_count) - self.assertEqual(1, self.mock_availability_zone_list.call_count) + self.assertEqual(3, self.mock_volume_type_list.call_count) + self.assertEqual(2, self.mock_volume_type_default.call_count) + self.assertEqual(2, self.mock_availability_zone_list.call_count) self.mock_image_get.assert_called_with(test.IsHttpRequest(), str(image.id)) @@ -904,16 +890,10 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): ' have 20GiB of your quota available.'] self.assertEqual(res.context['form'].errors['__all__'], expected_error) - if django.VERSION >= (1, 9): - self.assertEqual(3, self.mock_volume_type_list.call_count) - self.assertEqual(2, self.mock_volume_type_default.call_count) - self.assertEqual(2, self.mock_volume_list.call_count) - self.assertEqual(2, self.mock_availability_zone_list.call_count) - else: - self.assertEqual(2, self.mock_volume_type_list.call_count) - self.assertEqual(1, self.mock_volume_type_default.call_count) - self.assertEqual(1, self.mock_volume_list.call_count) - self.assertEqual(1, self.mock_availability_zone_list.call_count) + self.assertEqual(3, self.mock_volume_type_list.call_count) + self.assertEqual(2, self.mock_volume_type_default.call_count) + self.assertEqual(2, self.mock_volume_list.call_count) + self.assertEqual(2, self.mock_availability_zone_list.call_count) self.assertEqual(2, self.mock_tenant_limit_usages.call_count) self.mock_volume_snapshot_list.assert_called_with( @@ -963,14 +943,9 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): ' volumes.'] self.assertEqual(res.context['form'].errors['__all__'], expected_error) - if django.VERSION >= (1, 9): - self.assertEqual(3, self.mock_volume_type_list.call_count) - self.assertEqual(2, self.mock_volume_type_default.call_count) - self.assertEqual(2, self.mock_availability_zone_list.call_count) - else: - self.assertEqual(2, self.mock_volume_type_list.call_count) - self.assertEqual(1, self.mock_volume_type_default.call_count) - self.assertEqual(1, self.mock_availability_zone_list.call_count) + self.assertEqual(3, self.mock_volume_type_list.call_count) + self.assertEqual(2, self.mock_volume_type_default.call_count) + self.assertEqual(2, self.mock_availability_zone_list.call_count) self.mock_volume_snapshot_list.assert_called_with( test.IsHttpRequest(), search_opts=SEARCH_OPTS) diff --git a/openstack_dashboard/test/helpers.py b/openstack_dashboard/test/helpers.py index 01cf8610e1..5fdf266e41 100644 --- a/openstack_dashboard/test/helpers.py +++ b/openstack_dashboard/test/helpers.py @@ -25,7 +25,6 @@ import os import traceback import unittest -import django from django.conf import settings from django.contrib.messages.storage import default_storage from django.core.handlers import wsgi @@ -330,14 +329,10 @@ class TestCase(horizon_helpers.TestCase): Asserts that the given response issued a 302 redirect without processing the view which is redirected to. """ - if django.VERSION >= (1, 9): - loc = six.text_type(response._headers.get('location', None)[1]) - loc = http.urlunquote(loc) - expected_url = http.urlunquote(expected_url) - self.assertEqual(loc, expected_url) - else: - self.assertEqual(response._headers.get('location', None), - ('Location', settings.TESTSERVER + expected_url)) + loc = six.text_type(response._headers.get('location', None)[1]) + loc = http.urlunquote(loc) + expected_url = http.urlunquote(expected_url) + self.assertEqual(loc, expected_url) self.assertEqual(response.status_code, 302) def assertNoFormErrors(self, response, context_name="form"): diff --git a/releasenotes/notes/django-2.0-b37c6e91d20519fa.yaml b/releasenotes/notes/django-2.0-b37c6e91d20519fa.yaml new file mode 100644 index 0000000000..b829768d46 --- /dev/null +++ b/releasenotes/notes/django-2.0-b37c6e91d20519fa.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + Django 2.0 support is added as experimental. + Support for Django 1.10 or older releases is dropped. + Django 1.11 (LTS) is still the primary supported Django version.