Enable support for python 3.x

suds package is not 3.x compatible, so using suds-jurko only
under 3.x. Using six support to get the rest of the code
compiling and testing fine under 3.x

Depends-On: If91a87ede96a82a2a0d181ee0d4fdc102dc0299d
Change-Id: I78ee208756304b3038ce4fe66b120f6bbbc28738
This commit is contained in:
Davanum Srinivas 2014-09-17 21:06:54 -04:00
parent 4dc0ded767
commit 1ebbc4d2ac
14 changed files with 119 additions and 54 deletions

View File

@ -331,7 +331,7 @@ class VMwareAPISession(object):
LOG.debug("Fault list: %s", excep.fault_list)
fault = excep.fault_list[0]
clazz = exceptions.get_fault_class(fault)
raise clazz(unicode(excep), excep.details)
raise clazz(six.text_type(excep), excep.details)
raise
except exceptions.VimConnectionException:

View File

@ -42,6 +42,12 @@ DUPLICATE_NAME = 'DuplicateName'
class VimException(Exception):
"""The base exception class for all exceptions this library raises."""
if six.PY2:
__str__ = lambda self: six.text_type(self).encode('utf8')
__unicode__ = lambda self: self.description
else:
__str__ = lambda self: self.description
def __init__(self, message, cause=None):
Exception.__init__(self)
if isinstance(message, list):
@ -52,16 +58,14 @@ class VimException(Exception):
self.msg = message
self.cause = cause
def __str__(self):
return unicode(self).encode('utf8')
def __unicode__(self):
@property
def description(self):
# NOTE(jecarey): self.msg and self.cause may be i18n objects
# that do not support str or concatenation, but can be used
# as replacement text.
descr = unicode(self.msg)
descr = six.text_type(self.msg)
if self.cause:
descr += '\nCause: ' + unicode(self.cause)
descr += '\nCause: ' + six.text_type(self.cause)
return descr
@ -92,15 +96,21 @@ class VimFaultException(VimException):
self.fault_list = fault_list
self.details = details
def __unicode__(self):
descr = VimException.__unicode__(self)
if six.PY2:
__unicode__ = lambda self: self.description
else:
__str__ = lambda self: self.description
@property
def description(self):
descr = VimException.description.fget(self)
if self.fault_list:
# fault_list doesn't contain non-ASCII chars, we can use str()
descr += '\nFaults: ' + str(self.fault_list)
if self.details:
# details may contain non-ASCII values
details = '{%s}' % ', '.join(["'%s': '%s'" % (k, v)
for k, v in self.details.iteritems()])
details = '{%s}' % ', '.join(["'%s': '%s'" % (k, v) for k, v in
six.iteritems(self.details)])
descr += '\nDetails: ' + details
return descr

View File

@ -12,11 +12,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import httplib
import logging
import posixpath
import random
import six.moves.http_client as httplib
import six.moves.urllib.parse as urlparse
from oslo.vmware._i18n import _

View File

@ -22,9 +22,9 @@ Refer http://goo.gl/GR2o6U for more details.
import logging
import os
import urllib
import urlparse
import six.moves.urllib.parse as urlparse
import six.moves.urllib.request as urllib
import suds.sax.element as element
from oslo.vmware._i18n import _LW

View File

@ -21,14 +21,13 @@ VMDK files in VMware servers. It also contains a class to read images from
glance server.
"""
import httplib
import logging
import socket
import urllib
import urllib2
import urlparse
import netaddr
import six.moves.http_client as httplib
import six.moves.urllib.parse as urlparse
import six.moves.urllib.request as urllib2
from oslo.utils import excutils
from oslo.vmware._i18n import _, _LE, _LW
@ -176,7 +175,7 @@ class FileWriteHandle(FileHandle):
soap_url = self._get_soap_url(scheme, host, port)
param_list = {'dcPath': data_center_name, 'dsName': datastore_name}
self._url = '%s/folder/%s' % (soap_url, file_path)
self._url = self._url + '?' + urllib.urlencode(param_list)
self._url = self._url + '?' + urlparse.urlencode(param_list)
self.conn = self._create_connection(self._url,
file_size,

View File

@ -17,14 +17,17 @@
Common classes that provide access to vSphere services.
"""
import httplib
import logging
import os
import netaddr
import requests
import six
import six.moves.http_client as httplib
import suds
from suds import cache
from suds import client
from suds import plugin
from suds import transport
from oslo.utils import timeutils
@ -42,7 +45,7 @@ SERVICE_INSTANCE = 'ServiceInstance'
LOG = logging.getLogger(__name__)
class ServiceMessagePlugin(suds.plugin.MessagePlugin):
class ServiceMessagePlugin(plugin.MessagePlugin):
"""Suds plug-in handling some special cases while calling VI SDK."""
def add_attribute_for_value(self, node):
@ -149,7 +152,7 @@ class RequestsTransport(transport.Transport):
return transport.Reply(resp.status_code, resp.headers, resp.content)
class MemoryCache(suds.cache.ObjectCache):
class MemoryCache(cache.ObjectCache):
def __init__(self):
self._cache = {}
@ -187,11 +190,11 @@ class Service(object):
LOG.debug("Creating suds client with soap_url='%s' and wsdl_url='%s'",
self.soap_url, self.wsdl_url)
transport = RequestsTransport(cacert, insecure)
self.client = suds.client.Client(self.wsdl_url,
transport=transport,
location=self.soap_url,
plugins=[ServiceMessagePlugin()],
cache=_CACHE)
self.client = client.Client(self.wsdl_url,
transport=transport,
location=self.soap_url,
plugins=[ServiceMessagePlugin()],
cache=_CACHE)
self._service_content = None
@staticmethod

View File

@ -17,7 +17,7 @@
The VMware API utility module.
"""
import suds
from suds import sudsobject
from oslo.utils import timeutils
@ -29,7 +29,7 @@ def get_moref(value, type_):
:param type_: type of the managed object
:returns: managed object reference with given value and type
"""
moref = suds.sudsobject.Property(value)
moref = sudsobject.Property(value)
moref._type = type_
return moref

23
requirements-py3.txt Normal file
View File

@ -0,0 +1,23 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
stevedore>=1.1.0 # Apache-2.0
netaddr>=0.7.12
# for timeutils
iso8601>=0.1.9
# for jsonutils
six>=1.7.0
oslo.i18n>=1.0.0 # Apache-2.0
oslo.utils>=1.0.0 # Apache-2.0
Babel>=1.3
# for the routing notifier
PyYAML>=3.1.0
suds-jurko>=0.6
eventlet>=0.15.2
requests>=2.2.0,!=2.4.0

View File

@ -354,7 +354,7 @@ class DatastoreURLTestCase(base.TestCase):
ds_url = datastore.DatastoreURL.urlparse(url)
self.assertEqual(path, ds_url.path)
@mock.patch('httplib.HTTPSConnection')
@mock.patch('six.moves.http_client.HTTPSConnection')
def test_connect(self, mock_conn):
dc_path = 'datacenter-1'
ds_name = 'datastore-1'

View File

@ -20,6 +20,7 @@ Unit tests for session management and API invocation classes.
from eventlet import greenthread
import mock
import six
import suds
from oslo.vmware import api
@ -282,7 +283,7 @@ class VMwareAPISessionTest(base.TestCase):
expected_str = "%s\nFaults: %s\nDetails: %s" % (fault_string,
fault_list,
details_str)
self.assertEqual(expected_str, unicode(e))
self.assertEqual(expected_str, six.text_type(e))
self.assertEqual(details, e.details)
def test_invoke_api_with_empty_response(self):
@ -482,7 +483,7 @@ class VMwareAPISessionTest(base.TestCase):
'fake-task')
def test_poll_task_well_known_exceptions(self):
for k, v in exceptions._fault_classes_registry.iteritems():
for k, v in six.iteritems(exceptions._fault_classes_registry):
self._poll_task_well_known_exceptions(k, v)
def test_poll_task_unknown_exception(self):
@ -491,7 +492,7 @@ class VMwareAPISessionTest(base.TestCase):
'RuntimeFault': exceptions.VMwareDriverException
}
for k, v in _unknown_exceptions.iteritems():
for k, v in six.iteritems(_unknown_exceptions):
self._poll_task_well_known_exceptions(k, v)
def _create_subclass_exception(self):

View File

@ -18,10 +18,10 @@ Unit tests for PBM utility methods.
"""
import os
import urllib
import urlparse
import mock
import six.moves.urllib.parse as urlparse
import six.moves.urllib.request as urllib
from oslo.vmware import pbm
from tests import base
@ -91,7 +91,7 @@ class PBMUtilityTest(base.TestCase):
session = mock.Mock()
profile_id = pbm.get_profile_id_by_name(session,
'profile-%s' % (i + 1))
('profile-%s' % 11))
self.assertFalse(profile_id)
get_all_profiles.assert_called_once_with(session)
@ -139,7 +139,7 @@ class PBMUtilityTest(base.TestCase):
datastores.append(self._create_datastore(value))
hubs = []
hub_ids = ds_values[0:len(ds_values) / 2]
hub_ids = ds_values[0:int(len(ds_values) / 2)]
for hub_id in hub_ids:
hub = mock.Mock()
hub.hubId = hub_id

View File

@ -18,6 +18,7 @@ Unit tests for read and write handles for image transfer.
"""
import mock
import six
from oslo.vmware import exceptions
from oslo.vmware import rw_handles
@ -62,7 +63,7 @@ class FileWriteHandleTest(base.TestCase):
vim_cookie.value = 'value'
self._conn = mock.Mock()
patcher = mock.patch('httplib.HTTPConnection')
patcher = mock.patch('six.moves.http_client.HTTPConnection')
self.addCleanup(patcher.stop)
HTTPConnectionMock = patcher.start()
HTTPConnectionMock.return_value = self._conn
@ -87,7 +88,7 @@ class VmdkWriteHandleTest(base.TestCase):
def setUp(self):
super(VmdkWriteHandleTest, self).setUp()
self._conn = mock.Mock()
patcher = mock.patch('httplib.HTTPConnection')
patcher = mock.patch('six.moves.http_client.HTTPConnection')
self.addCleanup(patcher.stop)
HTTPConnectionMock = patcher.start()
HTTPConnectionMock.return_value = self._conn
@ -180,12 +181,14 @@ class VmdkReadHandleTest(base.TestCase):
def setUp(self):
super(VmdkReadHandleTest, self).setUp()
req_patcher = mock.patch('urllib2.Request')
req_patcher = mock.patch(
'six.moves.urllib.request.Request')
self.addCleanup(req_patcher.stop)
RequestMock = req_patcher.start()
RequestMock.return_value = mock.Mock()
urlopen_patcher = mock.patch('urllib2.urlopen')
urlopen_patcher = mock.patch(
'six.moves.urllib.request.urlopen')
self.addCleanup(urlopen_patcher.stop)
urlopen_mock = urlopen_patcher.start()
self._conn = mock.Mock()
@ -281,7 +284,7 @@ class ImageReadHandleTest(base.TestCase):
max_items = 10
item = [1] * 10
class ImageReadIterator:
class ImageReadIterator(six.Iterator):
def __init__(self):
self.num_items = 0
@ -289,12 +292,14 @@ class ImageReadHandleTest(base.TestCase):
def __iter__(self):
return self
def next(self):
def __next__(self):
if (self.num_items < max_items):
self.num_items += 1
return item
raise StopIteration
next = __next__
handle = rw_handles.ImageReadHandle(ImageReadIterator())
for _ in range(0, max_items):
self.assertEqual(item, handle.read(10))

View File

@ -13,10 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import httplib
import mock
import requests
import six
import six.moves.http_client as httplib
import suds
from oslo.vmware import exceptions
@ -408,24 +408,39 @@ class RequestsTransportTest(base.TestCase):
self.assertEqual(mock.sentinel.headers, reply.headers)
self.assertEqual(mock.sentinel.content, reply.message)
@mock.patch('__builtin__.open')
@mock.patch('os.path.getsize')
def test_send_with_local_file_url(self, get_size_mock, open_mock):
def test_send_with_local_file_url(self, get_size_mock):
transport = service.RequestsTransport()
url = 'file:///foo'
request = requests.PreparedRequest()
request.url = url
data = "Hello World"
data = b"Hello World"
get_size_mock.return_value = len(data)
def readinto_mock(buf):
buf[0:] = data
open_mock.return_value = mock.MagicMock(name='file_handle', spec=file)
file_handle = open_mock.return_value.__enter__.return_value
file_handle.readinto.side_effect = readinto_mock
if six.PY3:
builtin_open = 'builtins.open'
open_mock = mock.MagicMock(name='file_handle',
spec=open)
import _io
file_spec = list(set(dir(_io.TextIOWrapper)).union(
set(dir(_io.BytesIO))))
else:
builtin_open = '__builtin__.open'
open_mock = mock.MagicMock(name='file_handle',
spec=file)
file_spec = file
resp = transport.session.send(request)
self.assertEqual(data, resp.content)
file_handle = mock.MagicMock(spec=file_spec)
file_handle.write.return_value = None
file_handle.__enter__.return_value = file_handle
file_handle.readinto.side_effect = readinto_mock
open_mock.return_value = file_handle
with mock.patch(builtin_open, open_mock, create=True):
resp = transport.session.send(request)
self.assertEqual(data, resp.content)

11
tox.ini
View File

@ -1,5 +1,5 @@
[tox]
envlist = py26,py27,py33,py34,pep8
envlist = py26,py27,py33,py34,pypy,pep8
[testenv]
setenv = VIRTUAL_ENV={envdir}
@ -7,6 +7,14 @@ deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = python setup.py testr --slowest --testr-args='{posargs}'
[testenv:py33]
deps = -r{toxinidir}/requirements-py3.txt
-r{toxinidir}/test-requirements.txt
[testenv:py34]
deps = -r{toxinidir}/requirements-py3.txt
-r{toxinidir}/test-requirements.txt
[testenv:pep8]
commands = flake8
@ -31,3 +39,4 @@ exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,__in
[hacking]
import_exceptions = oslo.vmware._i18n
tests.base