From 14cf62eb3b70611a7069ba4761bf625d4eaf82a1 Mon Sep 17 00:00:00 2001 From: Andrey Kurilin Date: Mon, 27 Jan 2014 10:12:37 +0200 Subject: [PATCH] Sync latest apiclient code from Oslo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We needs to update apiclient code to have ability to use it in py3 environment. Сhanges from oslo-incubator related to apiclient ===================================================================== Change 8575d87 - Removed copyright from empty files Change bbaf317 - Use encode() instead of strutils.safe_encode() in fake_client Change 41dc2b4 - Encode response from FakeHTTPClient Change e6494c2 - Use six.iteritems to make dict work on Python2/3 Change 0d8f18b - Use urlutils functions instead of urllib/urllib2 Change 16fb43b - Replace data structures' attribute with six module Change 9d0ec6a - Use six.iteritems for python 3.3 support in apiclient module Change 12bcdb7 - Remove vim header Change 4c22556 - Use py3kcompat urlutils functions instead of urlparse Change 3970d46 - Fix typos in oslo Change 1771a77 - Adjust import order according to PEP8 imports rule Change da611e6 - Transform the for loop to expression Change 4bfb7a2 - Apply six for metaclass Change eca62f7 - Changed header from LLC to Foundation based on trademark policies Change-Id: Ia87807828f344e463fa308413bd627dc57d1a41d --- .../openstack/common/apiclient/__init__.py | 16 ----- .../openstack/common/apiclient/auth.py | 16 ++--- .../openstack/common/apiclient/base.py | 17 +++-- .../openstack/common/apiclient/client.py | 4 +- .../openstack/common/apiclient/exceptions.py | 30 ++++----- .../openstack/common/apiclient/fake_client.py | 9 +-- .../openstack/common/py3kcompat/__init__.py | 0 .../openstack/common/py3kcompat/urlutils.py | 67 +++++++++++++++++++ openstack-common.conf | 1 + 9 files changed, 101 insertions(+), 59 deletions(-) create mode 100644 cinderclient/openstack/common/py3kcompat/__init__.py create mode 100644 cinderclient/openstack/common/py3kcompat/urlutils.py diff --git a/cinderclient/openstack/common/apiclient/__init__.py b/cinderclient/openstack/common/apiclient/__init__.py index d5d002224..e69de29bb 100644 --- a/cinderclient/openstack/common/apiclient/__init__.py +++ b/cinderclient/openstack/common/apiclient/__init__.py @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack Foundation -# All Rights Reserved. -# -# 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. diff --git a/cinderclient/openstack/common/apiclient/auth.py b/cinderclient/openstack/common/apiclient/auth.py index 374d20b67..1a713b0e1 100644 --- a/cinderclient/openstack/common/apiclient/auth.py +++ b/cinderclient/openstack/common/apiclient/auth.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2013 OpenStack Foundation # Copyright 2013 Spanish National Research Council. # All Rights Reserved. @@ -21,17 +19,14 @@ import abc import argparse -import logging import os +import six from stevedore import extension from cinderclient.openstack.common.apiclient import exceptions -logger = logging.getLogger(__name__) - - _discovered_plugins = {} @@ -59,7 +54,7 @@ def load_auth_system_opts(parser): """ group = parser.add_argument_group("Common auth options") BaseAuthPlugin.add_common_opts(group) - for name, auth_plugin in _discovered_plugins.iteritems(): + for name, auth_plugin in six.iteritems(_discovered_plugins): group = parser.add_argument_group( "Auth-system '%s' options" % name, conflict_handler="resolve") @@ -75,7 +70,7 @@ def load_plugin(auth_system): def load_plugin_from_args(args): - """Load requred plugin and populate it with options. + """Load required plugin and populate it with options. Try to guess auth system if it is not specified. Systems are tried in alphabetical order. @@ -90,7 +85,7 @@ def load_plugin_from_args(args): plugin.sufficient_options() return plugin - for plugin_auth_system in sorted(_discovered_plugins.iterkeys()): + for plugin_auth_system in sorted(six.iterkeys(_discovered_plugins)): plugin_class = _discovered_plugins[plugin_auth_system] plugin = plugin_class() plugin.parse_opts(args) @@ -102,6 +97,7 @@ def load_plugin_from_args(args): raise exceptions.AuthPluginOptionsMissing(["auth_system"]) +@six.add_metaclass(abc.ABCMeta) class BaseAuthPlugin(object): """Base class for authentication plugins. @@ -109,8 +105,6 @@ class BaseAuthPlugin(object): method to be a valid plugin. """ - __metaclass__ = abc.ABCMeta - auth_system = None opt_names = [] common_opt_names = [ diff --git a/cinderclient/openstack/common/apiclient/base.py b/cinderclient/openstack/common/apiclient/base.py index caef843ad..e101f4c65 100644 --- a/cinderclient/openstack/common/apiclient/base.py +++ b/cinderclient/openstack/common/apiclient/base.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2012 Grid Dynamics @@ -26,9 +24,11 @@ Base utilities to build API operation managers and objects on top of. # pylint: disable=E1102 import abc -import urllib + +import six from cinderclient.openstack.common.apiclient import exceptions +from cinderclient.openstack.common.py3kcompat import urlutils from cinderclient.openstack.common import strutils @@ -201,11 +201,10 @@ class BaseManager(HookableMixin): return self.client.delete(url) +@six.add_metaclass(abc.ABCMeta) class ManagerWithFind(BaseManager): """Manager with additional `find()`/`findall()` methods.""" - __metaclass__ = abc.ABCMeta - @abc.abstractmethod def list(self): pass @@ -292,7 +291,7 @@ class CrudManager(BaseManager): def _filter_kwargs(self, kwargs): """Drop null values and handle ids.""" - for key, ref in kwargs.copy().iteritems(): + for key, ref in six.iteritems(kwargs.copy()): if ref is None: kwargs.pop(key) else: @@ -328,7 +327,7 @@ class CrudManager(BaseManager): return self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), - 'query': '?%s' % urllib.urlencode(kwargs) if kwargs else '', + 'query': '?%s' % urlutils.urlencode(kwargs) if kwargs else '', }, self.collection_key) @@ -367,7 +366,7 @@ class CrudManager(BaseManager): rl = self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), - 'query': '?%s' % urllib.urlencode(kwargs) if kwargs else '', + 'query': '?%s' % urlutils.urlencode(kwargs) if kwargs else '', }, self.collection_key) num = len(rl) @@ -446,7 +445,7 @@ class Resource(object): return None def _add_details(self, info): - for (k, v) in info.iteritems(): + for (k, v) in six.iteritems(info): try: setattr(self, k, v) self._info[k] = v diff --git a/cinderclient/openstack/common/apiclient/client.py b/cinderclient/openstack/common/apiclient/client.py index 77d4579eb..da2e17738 100644 --- a/cinderclient/openstack/common/apiclient/client.py +++ b/cinderclient/openstack/common/apiclient/client.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2011 Piston Cloud Computing, Inc. @@ -52,7 +50,7 @@ class HTTPClient(object): services (e.g., for compute and image clients); - reissue authentication request for expired tokens; - encode/decode JSON bodies; - - raise exeptions on HTTP errors; + - raise exceptions on HTTP errors; - pluggable authentication; - store authentication information in a keyring; - store time spent for requests; diff --git a/cinderclient/openstack/common/apiclient/exceptions.py b/cinderclient/openstack/common/apiclient/exceptions.py index b03def77c..4776d5872 100644 --- a/cinderclient/openstack/common/apiclient/exceptions.py +++ b/cinderclient/openstack/common/apiclient/exceptions.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 Nebula, Inc. # Copyright 2013 Alessio Ababilov @@ -22,8 +20,11 @@ Exception definitions. """ +import inspect import sys +import six + class ClientException(Exception): """The base exception class for all exceptions this library raises. @@ -59,6 +60,11 @@ class AuthorizationFailure(ClientException): pass +class ConnectionRefused(ClientException): + """Cannot connect to API service.""" + pass + + class AuthPluginOptionsMissing(AuthorizationFailure): """Auth plugin misses some options.""" def __init__(self, opt_names): @@ -387,20 +393,12 @@ class HttpVersionNotSupported(HttpServerError): message = "HTTP Version Not Supported" -# In Python 2.4 Exception is old-style and thus doesn't have a __subclasses__() -# so we can do this: -# _code_map = dict((c.http_status, c) -# for c in HttpError.__subclasses__()) -_code_map = {} -for obj in sys.modules[__name__].__dict__.values(): - if isinstance(obj, type): - try: - http_status = obj.http_status - except AttributeError: - pass - else: - if http_status: - _code_map[http_status] = obj +# _code_map contains all the classes that have http_status attribute. +_code_map = dict( + (getattr(obj, 'http_status', None), obj) + for name, obj in six.iteritems(vars(sys.modules[__name__])) + if inspect.isclass(obj) and getattr(obj, 'http_status', False) +) def from_response(response, method, url): diff --git a/cinderclient/openstack/common/apiclient/fake_client.py b/cinderclient/openstack/common/apiclient/fake_client.py index 914cebdbc..2d1c0aa16 100644 --- a/cinderclient/openstack/common/apiclient/fake_client.py +++ b/cinderclient/openstack/common/apiclient/fake_client.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2013 OpenStack Foundation # All Rights Reserved. # @@ -27,11 +25,12 @@ places where actual behavior differs from the spec. # pylint: disable=W0102 import json -import urlparse import requests +import six from cinderclient.openstack.common.apiclient import client +from cinderclient.openstack.common.py3kcompat import urlutils def assert_has_keys(dct, required=[], optional=[]): @@ -63,6 +62,8 @@ class TestResponse(requests.Response): else: self._content = text default_headers = {} + if six.PY3 and isinstance(self._content, six.string_types): + self._content = self._content.encode('utf-8', 'strict') self.headers = data.get('headers') or default_headers else: self.status_code = data @@ -146,7 +147,7 @@ class FakeHTTPClient(client.HTTPClient): "text": fixture[1]}) # Call the method - args = urlparse.parse_qsl(urlparse.urlparse(url)[4]) + args = urlutils.parse_qsl(urlutils.urlparse(url)[4]) kwargs.update(args) munged_url = url.rsplit('?', 1)[0] munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_') diff --git a/cinderclient/openstack/common/py3kcompat/__init__.py b/cinderclient/openstack/common/py3kcompat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cinderclient/openstack/common/py3kcompat/urlutils.py b/cinderclient/openstack/common/py3kcompat/urlutils.py new file mode 100644 index 000000000..84e457a44 --- /dev/null +++ b/cinderclient/openstack/common/py3kcompat/urlutils.py @@ -0,0 +1,67 @@ +# +# Copyright 2013 Canonical Ltd. +# All Rights Reserved. +# +# 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. +# + +""" +Python2/Python3 compatibility layer for OpenStack +""" + +import six + +if six.PY3: + # python3 + import urllib.error + import urllib.parse + import urllib.request + + urlencode = urllib.parse.urlencode + urljoin = urllib.parse.urljoin + quote = urllib.parse.quote + quote_plus = urllib.parse.quote_plus + parse_qsl = urllib.parse.parse_qsl + unquote = urllib.parse.unquote + unquote_plus = urllib.parse.unquote_plus + urlparse = urllib.parse.urlparse + urlsplit = urllib.parse.urlsplit + urlunsplit = urllib.parse.urlunsplit + SplitResult = urllib.parse.SplitResult + + urlopen = urllib.request.urlopen + URLError = urllib.error.URLError + pathname2url = urllib.request.pathname2url +else: + # python2 + import urllib + import urllib2 + import urlparse + + urlencode = urllib.urlencode + quote = urllib.quote + quote_plus = urllib.quote_plus + unquote = urllib.unquote + unquote_plus = urllib.unquote_plus + + parse = urlparse + parse_qsl = parse.parse_qsl + urljoin = parse.urljoin + urlparse = parse.urlparse + urlsplit = parse.urlsplit + urlunsplit = parse.urlunsplit + SplitResult = parse.SplitResult + + urlopen = urllib2.urlopen + URLError = urllib2.URLError + pathname2url = urllib.pathname2url diff --git a/openstack-common.conf b/openstack-common.conf index 4ee0ff329..651178a63 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -2,6 +2,7 @@ # The list of modules to copy from openstack-common module=apiclient +module=py3kcompat module=strutils module=install_venv_common