Remove all pre-Django 1.8 workarounds

This patch removes all code workarounds in Horizon for Django versions
before 1.8. As a bonus, this means an extra unit test is run that was
previously skipped.

Change-Id: I63f56953d9beefd3ea596d3e31c6032e936b140c
Partially-Implements: blueprint drop-dj17
This commit is contained in:
Rob Cresswell 2016-01-18 14:49:50 +00:00
parent 6879ea7cb9
commit 83c9e3fead
13 changed files with 29 additions and 295 deletions

View File

@ -40,56 +40,6 @@ class HorizonReporterFilter(SafeExceptionReporterFilter):
def is_active(self, request):
return True
# TODO(gabriel): This bugfix is cribbed from Django's code. When 1.4.1
# is available we can remove this code.
def get_traceback_frame_variables(self, request, tb_frame):
"""Replaces the values of variables marked as sensitive with
stars (*********).
"""
# Loop through the frame's callers to see if the sensitive_variables
# decorator was used.
current_frame = tb_frame.f_back
sensitive_variables = None
while current_frame is not None:
if (current_frame.f_code.co_name == 'sensitive_variables_wrapper'
and 'sensitive_variables_wrapper'
in current_frame.f_locals):
# The sensitive_variables decorator was used, so we take note
# of the sensitive variables' names.
wrapper = current_frame.f_locals['sensitive_variables_wrapper']
sensitive_variables = getattr(wrapper,
'sensitive_variables',
None)
break
current_frame = current_frame.f_back
cleansed = []
if self.is_active(request) and sensitive_variables:
if sensitive_variables == '__ALL__':
# Cleanse all variables
for name, value in tb_frame.f_locals.items():
cleansed.append((name, CLEANSED_SUBSTITUTE))
return cleansed
else:
# Cleanse specified variables
for name, value in tb_frame.f_locals.items():
if name in sensitive_variables:
value = CLEANSED_SUBSTITUTE
elif isinstance(value, HttpRequest):
# Cleanse the request's POST parameters.
value = self.get_request_repr(value)
cleansed.append((name, value))
return cleansed
else:
# Potentially cleanse only the request if it's one of the
# frame variables.
for name, value in tb_frame.f_locals.items():
if isinstance(value, HttpRequest):
# Cleanse the request's POST parameters.
value = self.get_request_repr(value)
cleansed.append((name, value))
return cleansed
class HorizonException(Exception):
"""Base exception class for distinguishing our own exception classes."""

View File

@ -19,19 +19,15 @@ import os
import django
from django.conf import settings
from django.template.engine import Engine
from django.template.loaders.base import Loader as tLoaderCls
from django.utils._os import safe_join # noqa
if django.VERSION >= (1, 9):
from django.template.exceptions import TemplateDoesNotExist
else:
from django.template.base import TemplateDoesNotExist # noqa
if django.get_version() >= '1.8':
from django.template.engine import Engine
from django.template.loaders.base import Loader as tLoaderCls
else:
from django.template.loader import BaseLoader as tLoaderCls # noqa
from django.utils._os import safe_join # noqa
# Set up a cache of the panel directories to search.
panel_template_dirs = {}
@ -65,8 +61,5 @@ class TemplateLoader(tLoaderCls):
raise TemplateDoesNotExist(template_name)
if django.get_version() >= '1.8':
e = Engine()
_loader = TemplateLoader(e)
else:
_loader = TemplateLoader()
e = Engine()
_loader = TemplateLoader(e)

View File

@ -1,71 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import re
from six.moves import html_parser as _HTMLParser
def parse_starttag_patched(self, i):
"""This method is a patched version of the parse_starttag method from
django.utils.html_parser.HTMLParser class, used to patch bug 1273943.
The code is taken from file django/utils/html_parser.py, commit 6bc1b22299.
"""
self.__starttag_text = None
endpos = self.check_for_whole_start_tag(i)
if endpos < 0:
return endpos
rawdata = self.rawdata
self.__starttag_text = rawdata[i:endpos]
# Now parse the data between i+1 and j into a tag and attrs
attrs = []
tagfind = re.compile('([a-zA-Z][-.a-zA-Z0-9:_]*)(?:\s|/(?!>))*')
match = tagfind.match(rawdata, i + 1)
assert match, 'unexpected call to parse_starttag()'
k = match.end()
self.lasttag = tag = match.group(1).lower()
while k < endpos:
m = _HTMLParser.attrfind.match(rawdata, k)
if not m:
break
attrname, rest, attrvalue = m.group(1, 2, 3)
if not rest:
attrvalue = None
elif (attrvalue[:1] == '\'' == attrvalue[-1:] or
attrvalue[:1] == '"' == attrvalue[-1:]):
attrvalue = attrvalue[1:-1]
if attrvalue:
attrvalue = self.unescape(attrvalue)
attrs.append((attrname.lower(), attrvalue))
k = m.end()
end = rawdata[k:endpos].strip()
if end not in (">", "/>"):
lineno, offset = self.getpos()
if "\n" in self.__starttag_text:
lineno = lineno + self.__starttag_text.count("\n")
offset = (len(self.__starttag_text)
- self.__starttag_text.rfind("\n"))
else:
offset = offset + len(self.__starttag_text)
self.error("junk characters in start tag: %r"
% (rawdata[k:endpos][:20],))
if end.endswith('/>'):
# XHTML-style empty tag: <span attr="value" />
self.handle_startendtag(tag, attrs)
else:
self.handle_starttag(tag, attrs)
if tag in self.CDATA_CONTENT_ELEMENTS:
self.set_cdata_mode(tag) # <--------------------------- Changed
return endpos

View File

@ -18,20 +18,11 @@
import os
import socket
import sys
import django
from django.utils import html_parser
from openstack_dashboard.static_settings import get_staticfiles_dirs # noqa
from horizon.test import patches
STATICFILES_DIRS = get_staticfiles_dirs()
# Patch django.utils.html_parser.HTMLParser as a workaround for bug 1273943
if django.get_version() == '1.4' and sys.version_info[:3] > (2, 7, 3):
html_parser.HTMLParser.parse_starttag = patches.parse_starttag_patched
socket.setdefaulttimeout(1)
LOGIN_URL = '/auth/login/'
@ -77,13 +68,7 @@ MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
if django.VERSION >= (1, 8, 0):
MIDDLEWARE_CLASSES += (
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',)
else:
MIDDLEWARE_CLASSES += ('django.middleware.doc.XViewMiddleware',)
MIDDLEWARE_CLASSES += (
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'horizon.middleware.HorizonMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',

View File

@ -1,30 +0,0 @@
# Copyright 2012 Nebula, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from django.core.urlresolvers import reverse as django_reverse
from django.utils.http import urlquote # noqa
from django import VERSION # noqa
def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None,
current_app=None):
if VERSION < (1, 6):
if args:
args = [urlquote(x) for x in args]
if kwargs:
kwargs = dict([(x, urlquote(y)) for x, y in six.iteritems(kwargs)])
return django_reverse(viewname, urlconf, args, kwargs, prefix,
current_app)

View File

@ -18,7 +18,6 @@ import logging
import os
import unittest
import django
from django.core.urlresolvers import reverse
from django import http
from django.test.utils import override_settings
@ -266,18 +265,11 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
res = self.client.get(reverse('horizon:identity:projects:create'))
self.assertTemplateUsed(res, views.WorkflowView.template_name)
if django.VERSION >= (1, 6):
self.assertContains(res, '''
<input class="form-control"
id="id_subnet" min="-1"
name="subnet" type="number" value="10" />
''', html=True)
else:
self.assertContains(res, '''
<input class="form-control"
name="subnet" id="id_subnet"
value="10" type="text" />
''', html=True)
self.assertContains(res, '''
<input class="form-control"
id="id_subnet" min="-1"
name="subnet" type="number" value="10" />
''', html=True)
workflow = res.context['workflow']
self.assertEqual(res.context['workflow'].name,
@ -1463,11 +1455,6 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
self.assertMessageCount(error=2, warning=1)
self.assertRedirectsNoFollow(res, INDEX_URL)
# django 1.7 and later does not handle the thrown keystoneclient
# exception well enough.
# TODO(mrunge): re-check when django-1.8 is stable
@unittest.skipIf(django.VERSION >= (1, 7, 0),
'Currently skipped with Django >= 1.7')
@test.create_stubs({api.keystone: ('get_default_role',
'tenant_get',
'domain_get'),

View File

@ -19,7 +19,6 @@
import collections
import logging
import django
from django.conf import settings
from django.forms import ValidationError # noqa
from django import http
@ -118,13 +117,8 @@ class CreateUserForm(PasswordMixin, BaseUserForm):
"description", "email", "password",
"confirm_password", "project", "role_id",
"enabled"]
# Starting from 1.7 Django uses OrderedDict for fields and keyOrder
# no longer works for it
if django.VERSION >= (1, 7):
self.fields = collections.OrderedDict(
(key, self.fields[key]) for key in ordering)
else:
self.fields.keyOrder = ordering
self.fields = collections.OrderedDict(
(key, self.fields[key]) for key in ordering)
role_choices = [(role.id, role.name) for role in roles]
self.fields['role_id'].choices = role_choices

View File

@ -13,8 +13,8 @@
# under the License.
import logging
import django
from django.conf import settings
from django.core.urlresolvers import reverse
from django import shortcuts
from django import template
from django.template import defaultfilters as filters
@ -26,27 +26,16 @@ from django.utils.translation import ungettext_lazy
from horizon import exceptions
from horizon import messages
from horizon import tables
from horizon.utils.urlresolvers import reverse # noqa
from openstack_dashboard import api
from openstack_dashboard.api import swift
from openstack_dashboard.dashboards.project.containers import utils
LOG = logging.getLogger(__name__)
static_url = getattr(settings, 'STATIC_URL', '/static/')
LOADING_IMAGE = '<img src="%s/dashboard/img/loading.gif" />' % static_url
def _escape_full_url(url):
# NOTE (lhcheng): In Django 1.8, HttpRequest.get_full_path()
# method now escapes unsafe characters
# TODO(robcresswell): Remove in M, once django 1.7 is dropped
if django.VERSION < (1, 8):
return http.urlquote(url)
return url
class ViewContainer(tables.LinkAction):
name = "view"
verbose_name = _("View Details")
@ -292,21 +281,6 @@ class ContainersTable(tables.DataTable):
def get_object_id(self, container):
return container.name
def get_absolute_url(self):
url = super(ContainersTable, self).get_absolute_url()
return _escape_full_url(url)
def get_full_url(self):
"""Returns the encoded absolute URL path with its query string.
This is used for the POST action attribute on the form element
wrapping the table. We use this method to persist the
pagination marker.
"""
url = super(ContainersTable, self).get_full_url()
return _escape_full_url(url)
class ViewObject(tables.LinkAction):
name = "view"
@ -361,10 +335,6 @@ class DeleteObject(tables.DeleteAction):
obj_id = obj_id[(len(container_name) + 1):] + "/"
api.swift.swift_delete_object(request, container_name, obj_id)
def get_success_url(self, request):
url = super(DeleteObject, self).get_success_url(request)
return _escape_full_url(url)
class DeleteMultipleObjects(DeleteObject):
name = "delete_multiple_objects"
@ -462,18 +432,3 @@ class ObjectsTable(tables.DataTable):
data_types = ("subfolders", "objects")
browser_table = "content"
footer = False
def get_absolute_url(self):
url = super(ObjectsTable, self).get_absolute_url()
return _escape_full_url(url)
def get_full_url(self):
"""Returns the encoded absolute URL path with its query string.
This is used for the POST action attribute on the form element
wrapping the table. We use this method to persist the
pagination marker.
"""
url = super(ObjectsTable, self).get_full_url()
return _escape_full_url(url)

View File

@ -19,8 +19,8 @@
import copy
import tempfile
import django
from django.core.files.uploadedfile import InMemoryUploadedFile # noqa
from django.core.urlresolvers import reverse
from django import http
from django.utils import http as utils_http
@ -34,9 +34,6 @@ from openstack_dashboard.dashboards.project.containers import utils
from openstack_dashboard.dashboards.project.containers import views
from openstack_dashboard.test import helpers as test
from horizon.utils.urlresolvers import reverse # noqa
CONTAINER_NAME_1 = u"container one%\u6346"
CONTAINER_NAME_2 = u"container_two\u6346"
CONTAINER_NAME_1_QUOTED = utils_http.urlquote(CONTAINER_NAME_1)
@ -356,14 +353,9 @@ class SwiftTests(test.TestCase):
res = self.client.get(download_url)
self.assertTrue(res.has_header('Content-Disposition'))
if django.VERSION >= (1, 5):
self.assertEqual(b''.join(res.streaming_content), _data)
self.assertNotContains(res, INVALID_CONTAINER_NAME_1)
self.assertNotContains(res, INVALID_CONTAINER_NAME_2)
else:
self.assertEqual(res.content, _data)
self.assertNotContains(res, INVALID_CONTAINER_NAME_1)
self.assertNotContains(res, INVALID_CONTAINER_NAME_2)
self.assertEqual(b''.join(res.streaming_content), _data)
self.assertNotContains(res, INVALID_CONTAINER_NAME_1)
self.assertNotContains(res, INVALID_CONTAINER_NAME_2)
# Check that the returned Content-Disposition filename is well
# surrounded by double quotes and with commas removed

View File

@ -22,6 +22,7 @@ Views for managing Swift containers.
import os
from django.core.urlresolvers import reverse
from django import http
from django.utils.functional import cached_property # noqa
from django.utils.translation import ugettext_lazy as _
@ -32,7 +33,6 @@ from horizon import browsers
from horizon import exceptions
from horizon import forms
from horizon.utils import memoized
from horizon.utils.urlresolvers import reverse # noqa
from openstack_dashboard import api
from openstack_dashboard.api import swift

View File

@ -13,7 +13,6 @@
import json
import re
import django
from django.conf import settings
from django.core import exceptions
from django.core.urlresolvers import reverse
@ -565,18 +564,11 @@ class StackTests(test.TestCase):
'id="id___param_param1" '
'name="__param_param1" '
'type="text" />', html=True)
if django.VERSION >= (1, 6):
self.assertContains(res,
'<input class="form-control" '
'id="id___param_param2" '
'name="__param_param2" '
'type="number" />', html=True)
else:
self.assertContains(res,
'<input class="form-control" '
'id="id___param_param2" '
'name="__param_param2" '
'type="text" />', html=True)
self.assertContains(res,
'<input class="form-control" '
'id="id___param_param2" '
'name="__param_param2" '
'type="number" />', html=True)
self.assertContains(res,
'<input class="form-control" '
'id="id___param_param3" '

View File

@ -15,7 +15,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import django
from django.core.urlresolvers import reverse
from django.forms import widgets
from django import http
@ -672,14 +672,8 @@ class VolumeViewTests(test.TestCase):
formData, follow=True)
self.assertEqual(res.redirect_chain, [])
# in django 1.6 filesizeformat replaces all spaces with
# non-breaking space characters
if django.VERSION >= (1, 6):
msg = (u"The volume size cannot be less than the "
u"image size (20.0\xa0GB)")
else:
msg = (u"The volume size cannot be less than the "
u"image size (20.0 GB)")
msg = (u"The volume size cannot be less than the "
u"image size (20.0\xa0GB)")
self.assertFormError(res, 'form', None, msg)

View File

@ -21,7 +21,6 @@ import os
import sys
import warnings
import django
from django.utils.translation import ugettext_lazy as _
from openstack_dashboard import exceptions
@ -104,13 +103,7 @@ MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
if django.VERSION >= (1, 8, 0):
MIDDLEWARE_CLASSES += (
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',)
else:
MIDDLEWARE_CLASSES += ('django.middleware.doc.XViewMiddleware',)
MIDDLEWARE_CLASSES += (
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'horizon.middleware.HorizonMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',