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

View File

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

View File

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

View File

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

View File

@ -17,6 +17,7 @@
"""Utilities and helper functions.""" """Utilities and helper functions."""
import functools
import logging import logging
import six import six
@ -32,7 +33,7 @@ def convert_version_to_int(version):
if isinstance(version, six.string_types): if isinstance(version, six.string_types):
version = convert_version_to_tuple(version) version = convert_version_to_tuple(version)
if isinstance(version, tuple): 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: except Exception:
msg = _("Hypervisor version %s is invalid.") % version msg = _("Hypervisor version %s is invalid.") % version
raise exception.VersionedObjectsException(msg) raise exception.VersionedObjectsException(msg)
@ -44,9 +45,9 @@ def convert_version_to_str(version_int):
while version_int != 0: while version_int != 0:
version_number = version_int - (version_int // factor * factor) version_number = version_int - (version_int // factor * factor)
version_numbers.insert(0, str(version_number)) 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): 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. # fails with a random hash seed, so set PYTHONHASHSEED.
setenv = setenv =
VIRTUAL_ENV={envdir} VIRTUAL_ENV={envdir}
PYTHONHASHSEED=0
deps = -r{toxinidir}/requirements.txt deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt -r{toxinidir}/test-requirements.txt
commands = python setup.py testr --slowest --testr-args='{posargs}' commands = python setup.py testr --slowest --testr-args='{posargs}'
@ -22,6 +21,10 @@ commands = python setup.py testr --slowest --testr-args='{posargs}'
[testenv:pep8] [testenv:pep8]
commands = flake8 commands = flake8
[testenv:py34]
deps = -r{toxinidir}/requirements-py3.txt
-r{toxinidir}/test-requirements.txt
[testenv:venv] [testenv:venv]
commands = {posargs} commands = {posargs}