From 4cfdc71d7077034381e3ac83073b37cacc3f5757 Mon Sep 17 00:00:00 2001 From: Charles Short Date: Mon, 26 Mar 2018 14:05:14 +0000 Subject: [PATCH] Make monasca-ui python3 compatible In order to make monasca-ui python3 compatible this patch set: - Add six to requirements.txt - Add py35 to tox.ini - Replace dict.iteritems() with six.iteritems(dict) - Replace prit msg with print(msg) - Replace unicode with six.text_type(msg) - Replaces urlparse with six.moves.urllib - Python 3 getting the keys() of a dict returns a dict_keys object instead of a list - python 2.x calling keys makes a copy of the key that you can iterate over while modifying the dict. This doesn't work in python 3.x because keys returns an iterator instead of a list. Another way is to use list to force a copy of the keys to be made. Story: 2000975 Task: 4129 Signed-off-by: Charles Short Change-Id: Ibc644a734edceea0b36f2df2c73300d1e4db925f --- monitoring/alarmdefs/views.py | 3 ++- monitoring/alarms/forms.py | 9 +++++---- monitoring/alarms/tables.py | 2 +- monitoring/alarms/views.py | 5 +++-- monitoring/overview/tests.py | 2 +- monitoring/overview/views.py | 30 +++++++++++++++--------------- requirements.txt | 1 + tools/install_venv.py | 32 +++++++++++++++++--------------- tox.ini | 2 +- 9 files changed, 46 insertions(+), 40 deletions(-) diff --git a/monitoring/alarmdefs/views.py b/monitoring/alarmdefs/views.py index 5dcf6cd0..49d4ebba 100644 --- a/monitoring/alarmdefs/views.py +++ b/monitoring/alarmdefs/views.py @@ -19,6 +19,7 @@ from django.core.paginator import Paginator, EmptyPage from django.core.urlresolvers import reverse_lazy, reverse # noqa from django.utils.translation import ugettext as _ # noqa from django.views.generic import TemplateView # noqa +import six from horizon import exceptions from horizon import forms @@ -154,7 +155,7 @@ class AlarmDetailView(TemplateView): except exc.HttpError: msg = _("Notification %s has already been deleted.") % id notifications.append({"id": id, - "name": unicode(msg), + "name": six.text_type(msg), "type": "", "address": ""}) diff --git a/monitoring/alarms/forms.py b/monitoring/alarms/forms.py index 638afdde..06083f04 100644 --- a/monitoring/alarms/forms.py +++ b/monitoring/alarms/forms.py @@ -19,6 +19,7 @@ from django import forms as django_forms from django.template.loader import get_template from django.utils import html from django.utils.translation import ugettext_lazy as _ # noqa +import six from horizon import exceptions from horizon import forms @@ -116,7 +117,7 @@ class NotificationCreateWidget(forms.Select): output = '' output += '' % \ - unicode(_("Name")) + six.text_type(_("Name")) if value: idx = 1 for notification in value: @@ -143,7 +144,7 @@ class NotificationCreateWidget(forms.Select): output += '' output += '' output += '
%s
X
' - label = unicode(_("+ Add more")) + label = six.text_type(_("+ Add more")) output += '%s' % (label) return html.format_html(output) @@ -221,10 +222,10 @@ class BaseAlarmForm(forms.SelfHandlingForm): if notification_choices: if len(notification_choices) > 1: notification_choices.insert( - 0, ("", unicode(_("Select Notification")))) + 0, ("", six.text_type(_("Select Notification")))) else: notification_choices.insert( - 0, ("", unicode(_("No notifications available.")))) + 0, ("", six.text_type(_("No notifications available.")))) self.fields['notifications'].choices = notification_choices diff --git a/monitoring/alarms/tables.py b/monitoring/alarms/tables.py index 1a7eb5ed..f33c7868 100644 --- a/monitoring/alarms/tables.py +++ b/monitoring/alarms/tables.py @@ -166,7 +166,7 @@ class GraphMetric(tables.LinkAction): metric = datum['metrics'][0]['name'] dimensions = datum['metrics'][0].get('dimensions', {}) query = "?metric=%s" % metric - for key, value in dimensions.iteritems(): + for key, value in dimensions.items(): query += "&dim_%s=%s" % (key, value) except AttributeError: # Catches case where Grafana 2 is not enabled. diff --git a/monitoring/alarms/views.py b/monitoring/alarms/views.py index c932cfe3..2c1fe46e 100644 --- a/monitoring/alarms/views.py +++ b/monitoring/alarms/views.py @@ -25,6 +25,7 @@ from django.utils.dateparse import parse_datetime from django.utils.translation import ugettext as _ # noqa from django.utils.translation import ugettext_lazy from django.views.generic import View # noqa +import six from horizon import exceptions from horizon import forms @@ -100,12 +101,12 @@ def generate_status(request): service_alarms = alarms_by_service.setdefault(service, []) service_alarms.append(a) for row in SERVICES: - row['name'] = unicode(row['name']) + row['name'] = six.text_type(row['name']) for service in row['services']: service_alarms = alarms_by_service.get(service['name'], []) service['class'] = get_status(service_alarms) service['icon'] = get_icon(service['class']) - service['display'] = unicode(service['display']) + service['display'] = six.text_type(service['display']) return SERVICES diff --git a/monitoring/overview/tests.py b/monitoring/overview/tests.py index f8375cc2..33287b54 100644 --- a/monitoring/overview/tests.py +++ b/monitoring/overview/tests.py @@ -43,7 +43,7 @@ class KibanaProxyViewTest(helpers.TestCase): def test_get_relative_url_with_unicode(self): """Tests if it properly converts multibyte characters.""" - import urlparse + from six.moves.urllib import parse as urlparse self.view.request = self.request_factory.get( '/', data={'a': 1, 'b': 2} diff --git a/monitoring/overview/views.py b/monitoring/overview/views.py index f2c3fc52..e1ac80b4 100644 --- a/monitoring/overview/views.py +++ b/monitoring/overview/views.py @@ -16,8 +16,6 @@ import base64 import copy import json import logging -import urllib -import urllib2 from django import http from django.contrib import messages @@ -29,6 +27,8 @@ from django.views.decorators.csrf import csrf_exempt from django.views.generic import TemplateView # noqa from openstack_auth import utils as auth_utils from openstack_dashboard import policy +import six +from six.moves import urllib from horizon import exceptions @@ -79,8 +79,8 @@ def get_dashboard_links(request): non_project_keys = {'fileName', 'title'} try: for project_link in settings.DASHBOARDS: - key = project_link.keys()[0] - value = project_link.values()[0] + key = list(project_link)[0] + value = list(project_link.values())[0] if key in non_project_keys: # # we're not indexed by project, just return @@ -129,8 +129,8 @@ def get_monitoring_services(request): non_project_keys = {'name', 'groupBy'} try: for group in settings.MONITORING_SERVICES: - key = group.keys()[0] - value = group.values()[0] + key = list(group.keys())[0] + value = list(group.values())[0] if key in non_project_keys: # # we're not indexed by project, just return @@ -194,7 +194,7 @@ def generate_status(request): service_alarms.append(a) monitoring_services = copy.deepcopy(get_monitoring_services(request)) for row in monitoring_services: - row['name'] = unicode(row['name']) + row['name'] = six.text_type(row['name']) if 'groupBy' in row: alarms_by_group = {} for a in alarms: @@ -220,7 +220,7 @@ def generate_status(request): service_alarms = alarms_by_service.get(service['name'], []) service['class'] = get_status(service_alarms) service['icon'] = get_icon(service['class']) - service['display'] = unicode(service['display']) + service['display'] = six.text_type(service['display']) return monitoring_services @@ -271,7 +271,7 @@ class MonascaProxyView(TemplateView): if len(dimension_name_value) == 2: name = dimension_name_value[0].encode('utf8') value = dimension_name_value[1].encode('utf8') - dim_dict[name] = urllib.unquote(value) + dim_dict[name] = urllib.parse.unquote(value) else: raise Exception('Dimensions are malformed') @@ -329,10 +329,10 @@ class StatusView(TemplateView): content_type='application/json') -class _HttpMethodRequest(urllib2.Request): +class _HttpMethodRequest(urllib.request.Request): def __init__(self, method, url, **kwargs): - urllib2.Request.__init__(self, url, **kwargs) + urllib.request.Request.__init__(self, url, **kwargs) self.method = method def get_method(self): @@ -359,15 +359,15 @@ class KibanaProxyView(generic.View): method, proxy_request_url, data=data, headers=headers ) try: - response = urllib2.urlopen(proxy_request) + response = urllib.request.urlopen(proxy_request) - except urllib2.HTTPError as e: + except urllib.error.HTTPError as e: return http.HttpResponse( e.read(), status=e.code, content_type=e.hdrs['content-type'] ) - except urllib2.URLError as e: + except urllib.error.URLError as e: return http.HttpResponse(e.reason, 404) else: @@ -406,7 +406,7 @@ class KibanaProxyView(generic.View): return self.read(request.method, url, request.body, headers) def get_relative_url(self, url): - url = urllib.quote(url.encode('utf-8')) + url = urllib.parse.quote(url.encode('utf-8')) params_str = self.request.GET.urlencode() if params_str: diff --git a/requirements.txt b/requirements.txt index cdd13a9f..c0916c8a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ oslo.log>=3.36.0 # Apache-2.0 python-monascaclient>=1.7.0 # Apache-2.0 Django<2.0,>=1.8 # BSD horizon>=13.0.0 # Apache-2.0 +six>=1.10.0 # MIT diff --git a/tools/install_venv.py b/tools/install_venv.py index 67e3a1b9..547a4470 100644 --- a/tools/install_venv.py +++ b/tools/install_venv.py @@ -22,6 +22,8 @@ Installation script for the OpenStack Dashboard development virtualenv. """ +from __future__ import print_function + import os import subprocess import sys @@ -69,12 +71,12 @@ HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'], def check_dependencies(): """Make sure virtualenv is in the path.""" - print 'Checking dependencies...' + print('Checking dependencies...') if not HAS_VIRTUALENV: - print 'Virtual environment not found.' + print('Virtual environment not found.') # Try installing it via easy_install... if HAS_EASY_INSTALL: - print 'Installing virtualenv via easy_install...', + print('Installing virtualenv via easy_install...', end=' ') run_command(['easy_install', 'virtualenv'], die_message='easy_install failed to install virtualenv' '\ndevelopment requires virtualenv, please' @@ -84,28 +86,28 @@ def check_dependencies(): ' requires virtualenv, please install it using your' ' favorite package management tool and ensure' ' virtualenv is in your path') - print 'virtualenv installation done.' + print('virtualenv installation done.') else: die('easy_install not found.\n\nInstall easy_install' ' (python-setuptools in ubuntu) or virtualenv by hand,' ' then rerun.') - print 'dependency check done.' + print('dependency check done.') def create_virtualenv(venv=VENV): """Creates the virtual environment and installs PIP only into the virtual environment """ - print 'Creating venv...', + print('Creating venv...', end=' ') run_command(['virtualenv', '-q', '--no-site-packages', VENV]) - print 'done.' - print 'Installing pip in virtualenv...', + print('done.') + print('Installing pip in virtualenv...', end=' ') if not run_command([WITH_VENV, 'easy_install', 'pip']).strip(): die("Failed to install pip.") - print 'done.' - print 'Installing distribute in virtualenv...' + print('done.') + print('Installing distribute in virtualenv...') pip_install('distribute>=0.6.24') - print 'done.' + print('done.') def pip_install(*args): @@ -114,8 +116,8 @@ def pip_install(*args): def install_dependencies(venv=VENV): - print "Installing dependencies..." - print "(This may take several minutes, don't panic)" + print("Installing dependencies...") + print("(This may take several minutes, don't panic)") pip_install('-r', TEST_REQUIRES) pip_install('-r', PIP_REQUIRES) @@ -127,7 +129,7 @@ def install_dependencies(venv=VENV): def install_horizon(): - print 'Installing horizon module in development mode...' + print('Installing horizon module in development mode...') run_command([WITH_VENV, 'python', 'setup.py', 'develop'], cwd=ROOT) @@ -140,7 +142,7 @@ can run: $ source .venv/bin/activate """ - print summary + print(summary) def main(): diff --git a/tox.ini b/tox.ini index ef0100ba..19dc9bb6 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,pep8 +envlist = py27,pep8,py35 minversion = 2.6 skipsdist = True