Compatibility with Python 3

Make the tests run under Python 3.4.
Tests now work with random hash seed.

Change-Id: I5f9f57e0bef2c015ab2ff4b5732ccdade5d3bfbe
Closes-Bug: 1417279
Closes-Bug: 1417285
This commit is contained in:
Grzegorz Grasza 2015-02-04 15:59:34 +01:00 committed by Dan Smith
parent c330f938c8
commit 1b8336fbe8
8 changed files with 60 additions and 39 deletions

View File

@ -59,7 +59,7 @@ def make_class_properties(cls):
for name, field in supercls.fields.items():
if name not in cls.fields:
cls.fields[name] = field
for name, field in cls.fields.iteritems():
for name, field in six.iteritems(cls.fields):
if not isinstance(field, fields.Field):
raise exception.ObjectFieldInvalid(
field=name, objname=cls.obj_name())
@ -558,7 +558,7 @@ class VersionedObject(object):
@property
def obj_fields(self):
return self.fields.keys() + self.obj_extra_fields
return list(self.fields.keys()) + self.obj_extra_fields
class VersionedObjectDictCompat(object):
@ -721,8 +721,8 @@ class ObjectListBase(object):
"""List index of value."""
return self.objects.index(value)
def sort(self, cmp=None, key=None, reverse=False):
self.objects.sort(cmp=cmp, key=key, reverse=reverse)
def sort(self, key=None, reverse=False):
self.objects.sort(key=key, reverse=reverse)
def obj_make_compatible(self, primitive, target_version):
primitives = primitive['objects']

View File

@ -28,6 +28,7 @@ import sys
from oslo_config import cfg
from oslo_utils import excutils
import six
import webob.exc
from oslo_versionedobjects._i18n import _, _LE
@ -126,7 +127,7 @@ class VersionedObjectsException(Exception):
LOG.error("%s: %s" % (name, value)) # noqa
if CONF.fatal_exception_format_errors:
raise exc_info[0], exc_info[1], exc_info[2]
raise six.reraise(*exc_info)
else:
# at least get the core message out if something happened
message = self.msg_fmt

View File

@ -132,13 +132,8 @@ class Field(object):
self._read_only = read_only
def __repr__(self):
args = {
'nullable': self._nullable,
'default': self._default,
}
return '%s(%s)' % (self._type.__class__.__name__,
','.join(['%s=%s' % (k, v)
for k, v in args.items()]))
return '%s(default=%s,nullable=%s)' % (self._type.__class__.__name__,
self._default, self._nullable)
@property
def nullable(self):
@ -238,9 +233,10 @@ class String(FieldType):
@staticmethod
def coerce(obj, attr, value):
# FIXME(danms): We should really try to avoid the need to do this
if isinstance(value, (six.string_types, int, long, float,
datetime.datetime)):
return unicode(value)
accepted_types = six.integer_types + (float, six.string_types,
datetime.datetime)
if isinstance(value, accepted_types):
return six.text_type(value)
else:
raise ValueError(_('A string is required here, not %s') %
value.__class__.__name__)

View File

@ -16,6 +16,7 @@ import datetime
import iso8601
from oslo_utils import timeutils
import six
from oslo_versionedobjects import base as obj_base
from oslo_versionedobjects import fields
@ -72,8 +73,10 @@ class TestString(TestField):
def setUp(self):
super(TestField, self).setUp()
self.field = fields.StringField()
self.coerce_good_values = [('foo', 'foo'), (1, '1'), (1L, '1'),
(True, 'True')]
self.coerce_good_values = [
('foo', 'foo'), (1, '1'), (1.0, '1.0'), (True, 'True')]
if six.PY2:
self.coerce_good_values += [(long(1), '1')]
self.coerce_bad_values = [None]
self.to_primitive_values = self.coerce_good_values[0:1]
self.from_primitive_values = self.coerce_good_values[0:1]

View File

@ -20,6 +20,7 @@ import inspect
import logging
import os
import pprint
import six
import mock
from oslo_context import context
@ -569,14 +570,13 @@ class _TestObject(object):
'foo', '1.0')
def test_obj_class_from_name_supported_version(self):
error = None
self.assertRaises(exception.IncompatibleObjectVersion,
base.VersionedObject.obj_class_from_name,
'MyObj', '1.25')
try:
base.VersionedObject.obj_class_from_name('MyObj', '1.25')
except exception.IncompatibleObjectVersion as error:
pass
self.assertIsNotNone(error)
self.assertEqual('1.6', error.kwargs['supported'])
self.assertEqual('1.6', error.kwargs['supported'])
def test_orphaned_object(self):
obj = MyObj.query(self.context)
@ -665,7 +665,7 @@ class _TestObject(object):
'versioned_object.namespace': 'versionedobjects',
'versioned_object.version': '1.6',
'versioned_object.changes': [
'deleted', 'created_at', 'deleted_at', 'updated_at',
'created_at', 'deleted', 'deleted_at', 'updated_at',
],
'versioned_object.data': {
'created_at': timeutils.isotime(dt),
@ -674,7 +674,10 @@ class _TestObject(object):
'deleted': False,
},
}
self.assertEqual(obj.obj_to_primitive(), expected)
got = obj.obj_to_primitive()
got['versioned_object.changes'].sort()
self.assertEqual(got, expected)
def test_contains(self):
obj = MyObj()
@ -707,7 +710,7 @@ class _TestObject(object):
self.assertRaises(AttributeError, obj.get, 'nothing', 3)
def test_object_inheritance(self):
base_fields = base.VersionedPersistentObject.fields.keys()
base_fields = list(base.VersionedPersistentObject.fields.keys())
myobj_fields = (['foo', 'bar', 'missing',
'readonly', 'rel_object', 'rel_objects'] +
base_fields)
@ -1119,11 +1122,11 @@ class TestObjectSerializer(_BaseTestCase):
thing = {'key': obj}
primitive = ser.serialize_entity(self.context, thing)
self.assertEqual(1, len(primitive))
for item in primitive.itervalues():
for item in six.itervalues(primitive):
self.assertNotIsInstance(item, base.VersionedObject)
thing2 = ser.deserialize_entity(self.context, primitive)
self.assertEqual(1, len(thing2))
for item in thing2.itervalues():
for item in six.itervalues(thing2):
self.assertIsInstance(item, MyObj)
# object-action updates dict case
@ -1172,7 +1175,8 @@ class TestObjectVersions(test.TestCase):
"""Follow a chain of remotable things down to the original function."""
if isinstance(thing, classmethod):
return self._find_remotable_method(cls, thing.__get__(None, cls))
elif inspect.ismethod(thing) and hasattr(thing, 'remotable'):
elif (inspect.ismethod(thing)
or inspect.isfunction(thing)) and hasattr(thing, 'remotable'):
return self._find_remotable_method(cls, thing.original_fn,
parent_was_remotable=True)
elif parent_was_remotable:
@ -1185,12 +1189,13 @@ class TestObjectVersions(test.TestCase):
def _get_fingerprint(self, obj_name):
obj_class = base.VersionedObjectRegistry.obj_classes()[obj_name][0]
fields = obj_class.fields.items()
fields = list(obj_class.fields.items())
fields.sort()
methods = []
for name in dir(obj_class):
thing = getattr(obj_class, name)
if inspect.ismethod(thing) or isinstance(thing, classmethod):
if inspect.ismethod(thing) or inspect.isfunction(thing) \
or isinstance(thing, classmethod):
method = self._find_remotable_method(obj_class, thing)
if method:
methods.append((name, inspect.getargspec(method)))
@ -1204,13 +1209,13 @@ class TestObjectVersions(test.TestCase):
relevant_data = (fields, methods, obj_class.child_versions)
else:
relevant_data = (fields, methods)
fingerprint = '%s-%s' % (obj_class.VERSION,
hashlib.md5(str(relevant_data)).hexdigest())
fingerprint = '%s-%s' % (obj_class.VERSION, hashlib.md5(
six.binary_type(repr(relevant_data).encode())).hexdigest())
return fingerprint
def test_versions(self):
fingerprints = {}
for obj_name in base.VersionedObjectRegistry.obj_classes():
for obj_name in sorted(base.VersionedObjectRegistry.obj_classes()):
obj_classes = base.VersionedObjectRegistry._registry._obj_classes
obj_cls = obj_classes[obj_name][0]
if is_test_object(obj_cls):
@ -1218,8 +1223,8 @@ class TestObjectVersions(test.TestCase):
fingerprints[obj_name] = self._get_fingerprint(obj_name)
if os.getenv('GENERATE_HASHES'):
file('object_hashes.txt', 'w').write(
pprint.pformat(fingerprints))
with open('object_hashes.txt', 'w') as fp:
fp.write(pprint.pformat(fingerprints))
raise test.TestingException(
'Generated hashes in object_hashes.txt')

View File

@ -17,6 +17,7 @@
"""Utilities and helper functions."""
import functools
import logging
import six
@ -32,7 +33,7 @@ def convert_version_to_int(version):
if isinstance(version, six.string_types):
version = convert_version_to_tuple(version)
if isinstance(version, tuple):
return reduce(lambda x, y: (x * 1000) + y, version)
return functools.reduce(lambda x, y: (x * 1000) + y, version)
except Exception:
msg = _("Hypervisor version %s is invalid.") % version
raise exception.VersionedObjectsException(msg)
@ -44,9 +45,9 @@ def convert_version_to_str(version_int):
while version_int != 0:
version_number = version_int - (version_int // factor * factor)
version_numbers.insert(0, str(version_number))
version_int = version_int / factor
version_int = version_int // factor
return reduce(lambda x, y: "%s.%s" % (x, y), version_numbers)
return '.'.join(map(str, version_numbers))
def convert_version_to_tuple(version_str):

12
requirements-py3.txt Normal file
View File

@ -0,0 +1,12 @@
eventlet>=0.16.1
six>=1.7.0
Babel>=1.3
netaddr>=0.7.12
oslo.concurrency>=1.4.1 # Apache-2.0
oslo.context>=0.1.0 # Apache-2.0
oslo.messaging>=1.4.0,!=1.5.0
oslo.serialization>=1.2.0 # Apache-2.0
oslo.utils>=1.2.0 # Apache-2.0
iso8601>=0.1.9
oslo.log>=0.1.0
oslo.i18n>=1.3.0 # Apache-2.0

View File

@ -14,7 +14,6 @@ install_command = pip install -U {opts} {packages}
# fails with a random hash seed, so set PYTHONHASHSEED.
setenv =
VIRTUAL_ENV={envdir}
PYTHONHASHSEED=0
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = python setup.py testr --slowest --testr-args='{posargs}'
@ -22,6 +21,10 @@ commands = python setup.py testr --slowest --testr-args='{posargs}'
[testenv:pep8]
commands = flake8
[testenv:py34]
deps = -r{toxinidir}/requirements-py3.txt
-r{toxinidir}/test-requirements.txt
[testenv:venv]
commands = {posargs}