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:
parent
c330f938c8
commit
1b8336fbe8
@ -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']
|
||||||
|
@ -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
|
||||||
|
@ -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__)
|
||||||
|
@ -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]
|
||||||
|
@ -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')
|
||||||
|
|
||||||
|
@ -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
12
requirements-py3.txt
Normal 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
|
5
tox.ini
5
tox.ini
@ -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}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user