From da7cd17a2b0d46f42c2a03db7a2df8ee68b6594a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Feb 2020 11:25:46 +0000 Subject: [PATCH] Drop use of six We no longer need much of the functionality this was providing. Change-Id: I0f625b714950a712249757906974715358f9e6c6 Signed-off-by: Stephen Finucane --- oslo_versionedobjects/base.py | 41 +++---------- oslo_versionedobjects/exception.py | 11 ++-- oslo_versionedobjects/fields.py | 65 +++++++++------------ oslo_versionedobjects/fixture.py | 18 +++--- oslo_versionedobjects/test.py | 6 +- oslo_versionedobjects/tests/test_fields.py | 7 +-- oslo_versionedobjects/tests/test_fixture.py | 7 +-- oslo_versionedobjects/tests/test_objects.py | 25 ++------ requirements.txt | 1 - 9 files changed, 61 insertions(+), 120 deletions(-) diff --git a/oslo_versionedobjects/base.py b/oslo_versionedobjects/base.py index cc0abb90..b412b301 100644 --- a/oslo_versionedobjects/base.py +++ b/oslo_versionedobjects/base.py @@ -16,25 +16,20 @@ import abc import collections +from collections import abc as collections_abc import copy +import functools import logging import warnings import oslo_messaging as messaging -from oslo_utils import encodeutils from oslo_utils import excutils from oslo_utils import versionutils as vutils -import six from oslo_versionedobjects._i18n import _ from oslo_versionedobjects import exception from oslo_versionedobjects import fields as obj_fields -if six.PY3: - from collections import abc as collections_abc -else: - import collections as collections_abc - LOG = logging.getLogger('object') @@ -172,7 +167,7 @@ class VersionedObjectRegistry(object): # requested action and the result will be returned here. def remotable_classmethod(fn): """Decorator for remotable classmethods.""" - @six.wraps(fn) + @functools.wraps(fn) def wrapper(cls, context, *args, **kwargs): if cls.indirection_api: version_manifest = obj_tree_get_versions(cls.obj_name()) @@ -204,7 +199,7 @@ def remotable_classmethod(fn): # "orphaned" and remotable methods cannot be called. def remotable(fn): """Decorator for remotable object methods.""" - @six.wraps(fn) + @functools.wraps(fn) def wrapper(self, *args, **kwargs): ctxt = self._context if ctxt is None: @@ -319,8 +314,6 @@ class VersionedObject(object): field.stringify(getattr(self, name)) or '')) for name, field in sorted(self.fields.items())])) - if six.PY2: - repr_str = encodeutils.safe_encode(repr_str, incoming='utf-8') return repr_str def __contains__(self, name): @@ -748,33 +741,16 @@ class VersionedObjectDictCompat(object): name in self.obj_extra_fields): yield name - iterkeys = __iter__ + keys = __iter__ - def itervalues(self): + def values(self): for name in self: yield getattr(self, name) - def iteritems(self): + def items(self): for name in self: yield name, getattr(self, name) - if six.PY3: - # NOTE(haypo): Python 3 dictionaries don't have iterkeys(), - # itervalues() or iteritems() methods. These methods are provided to - # ease the transition from Python 2 to Python 3. - keys = iterkeys - values = itervalues - items = iteritems - else: - def keys(self): - return list(self.iterkeys()) - - def values(self): - return list(self.itervalues()) - - def items(self): - return list(self.iteritems()) - def __getitem__(self, name): return getattr(self, name) @@ -983,8 +959,7 @@ class VersionedObjectSerializer(messaging.NoOpSerializer): return entity -@six.add_metaclass(abc.ABCMeta) -class VersionedObjectIndirectionAPI(object): +class VersionedObjectIndirectionAPI(object, metaclass=abc.ABCMeta): def object_action(self, context, objinst, objmethod, args, kwargs): """Perform an action on a VersionedObject instance. diff --git a/oslo_versionedobjects/exception.py b/oslo_versionedobjects/exception.py index 1541bcfa..7b5be346 100644 --- a/oslo_versionedobjects/exception.py +++ b/oslo_versionedobjects/exception.py @@ -22,13 +22,12 @@ SHOULD include dedicated exception logging. """ +import functools import inspect import logging -import sys from oslo_config import cfg from oslo_utils import excutils -import six import webob.exc from oslo_versionedobjects._i18n import _ @@ -81,7 +80,7 @@ def wrap_exception(notifier=None, get_notifier=None): payload.update({'args': cleansed}) # If f has multiple decorators, they must use - # six.wraps to ensure the name is + # functools.wraps to ensure the name is # propagated. event_type = f.__name__ @@ -89,7 +88,7 @@ def wrap_exception(notifier=None, get_notifier=None): event_type, payload) - return six.wraps(f)(wrapped) + return functools.wraps(f)(wrapped) return inner @@ -118,9 +117,7 @@ class VersionedObjectsException(Exception): if not message: try: message = self.msg_fmt % kwargs - except Exception: - exc_info = sys.exc_info() # kwargs doesn't match a variable in the message # log the issue and the kwargs LOG.exception('Exception in string format operation') @@ -128,7 +125,7 @@ class VersionedObjectsException(Exception): LOG.error("%s: %s" % (name, value)) # noqa if CONF.oslo_versionedobjects.fatal_exception_format_errors: - raise six.reraise(*exc_info) + raise else: # at least get the core message out if something happened message = self.msg_fmt diff --git a/oslo_versionedobjects/fields.py b/oslo_versionedobjects/fields.py index 480ff182..4952d8db 100644 --- a/oslo_versionedobjects/fields.py +++ b/oslo_versionedobjects/fields.py @@ -13,14 +13,7 @@ # under the License. import abc -# TODO(smcginnis) update this once six has support for collections.abc -# (https://github.com/benjaminp/six/pull/241) or clean up once we drop py2.7. -try: - from collections.abc import Iterable - from collections.abc import Mapping -except ImportError: - from collections import Iterable - from collections import Mapping +from collections import abc as collections_abc import datetime from distutils import versionpredicate import re @@ -32,7 +25,6 @@ import iso8601 import netaddr from oslo_utils import strutils from oslo_utils import timeutils -import six from oslo_versionedobjects._i18n import _ from oslo_versionedobjects import _utils @@ -61,8 +53,7 @@ class ElementTypeError(TypeError): }) -@six.add_metaclass(abc.ABCMeta) -class AbstractFieldType(object): +class AbstractFieldType(object, metaclass=abc.ABCMeta): @abc.abstractmethod def coerce(self, obj, attr, value): """This is called to coerce (if possible) a value on assignment. @@ -153,9 +144,13 @@ class Field(object): def __repr__(self): if isinstance(self._default, set): + # TODO(stephenfin): Drop this when we switch from + # 'inspect.getargspec' to 'inspect.getfullargspec', since our + # hashes will have to change anyway # make a py27 and py35 compatible representation. See bug 1771804 - default = 'set([%s])' % ','.join(sorted([six.text_type(v) - for v in self._default])) + default = 'set([%s])' % ','.join( + sorted([str(v) for v in self._default]) + ) else: default = str(self._default) return '%s(default=%s,nullable=%s)' % (self._type.__class__.__name__, @@ -269,14 +264,13 @@ class String(FieldType): @staticmethod def coerce(obj, attr, value): # FIXME(danms): We should really try to avoid the need to do this - accepted_types = six.integer_types + (float, six.string_types, - datetime.datetime) + accepted_types = (int, float, str, datetime.datetime) if isinstance(value, accepted_types): - return six.text_type(value) - else: - raise ValueError(_('A string is required in field %(attr)s, ' - 'not a %(type)s') % - {'attr': attr, 'type': type(value).__name__}) + return str(value) + + raise ValueError(_('A string is required in field %(attr)s, ' + 'not a %(type)s') % + {'attr': attr, 'type': type(value).__name__}) @staticmethod def stringify(value): @@ -391,7 +385,7 @@ class MACAddress(StringPattern): @staticmethod def coerce(obj, attr, value): - if isinstance(value, six.string_types): + if isinstance(value, str): lowered = value.lower().replace('-', ':') if MACAddress._REGEX.match(lowered): return lowered @@ -405,7 +399,7 @@ class PCIAddress(StringPattern): @staticmethod def coerce(obj, attr, value): - if isinstance(value, six.string_types): + if isinstance(value, str): newvalue = value.lower() if PCIAddress._REGEX.match(newvalue): return newvalue @@ -474,7 +468,7 @@ class DateTime(FieldType): super(DateTime, self).__init__(*args, **kwargs) def coerce(self, obj, attr, value): - if isinstance(value, six.string_types): + if isinstance(value, str): # NOTE(danms): Being tolerant of isotime strings here will help us # during our objects transition value = timeutils.parse_isotime(value) @@ -513,7 +507,7 @@ class IPAddress(StringPattern): try: return netaddr.IPAddress(value) except netaddr.AddrFormatError as e: - raise ValueError(six.text_type(e)) + raise ValueError(str(e)) def from_primitive(self, obj, attr, value): return self.coerce(obj, attr, value) @@ -572,7 +566,7 @@ class IPNetwork(IPAddress): try: return netaddr.IPNetwork(value) except netaddr.AddrFormatError as e: - raise ValueError(six.text_type(e)) + raise ValueError(str(e)) class IPV4Network(IPNetwork): @@ -586,7 +580,7 @@ class IPV4Network(IPNetwork): try: return netaddr.IPNetwork(value, version=4) except netaddr.AddrFormatError as e: - raise ValueError(six.text_type(e)) + raise ValueError(str(e)) class IPV6Network(IPNetwork): @@ -600,7 +594,7 @@ class IPV6Network(IPNetwork): try: return netaddr.IPNetwork(value, version=6) except netaddr.AddrFormatError as e: - raise ValueError(six.text_type(e)) + raise ValueError(str(e)) def _create_pattern(self): ipv6seg = '[0-9a-fA-F]{1,4}' @@ -651,8 +645,8 @@ class CompoundFieldType(FieldType): class List(CompoundFieldType): def coerce(self, obj, attr, value): - if (not isinstance(value, Iterable) or - isinstance(value, six.string_types + (Mapping,))): + if (not isinstance(value, collections_abc.Iterable) or + isinstance(value, (str, collections_abc.Mapping))): raise ValueError(_('A list is required in field %(attr)s, ' 'not a %(type)s') % {'attr': attr, 'type': type(value).__name__}) @@ -719,7 +713,7 @@ class DictProxyField(object): This will take care of the conversion while the dict field will make sure that we store the raw json-serializable data on the object. - key_type should return a type that unambiguously responds to six.text_type + key_type should return a type that unambiguously responds to str so that calling key_type on it yields the same thing. """ def __init__(self, dict_field_name, key_type=int): @@ -738,8 +732,7 @@ class DictProxyField(object): if val is None: setattr(obj, self._fld_name, val) else: - setattr(obj, self._fld_name, - {six.text_type(k): v for k, v in val.items()}) + setattr(obj, self._fld_name, {str(k): v for k, v in val.items()}) class Set(CompoundFieldType): @@ -1296,12 +1289,8 @@ class CoercedDict(CoercedCollectionMixin, dict): return res def _coerce_item(self, key, item): - if not isinstance(key, six.string_types): - # NOTE(guohliu) In order to keep compatibility with python3 - # we need to use six.string_types rather than basestring here, - # since six.string_types is a tuple, so we need to pass the - # real type in. - raise KeyTypeError(six.string_types[0], key) + if not isinstance(key, str): + raise KeyTypeError(str, key) if hasattr(self, "_element_type") and self._element_type is not None: att_name = "%s[%s]" % (self._field, key) return self._element_type.coerce(self._obj, att_name, item) diff --git a/oslo_versionedobjects/fixture.py b/oslo_versionedobjects/fixture.py index 0e2812be..1b9b0b8e 100644 --- a/oslo_versionedobjects/fixture.py +++ b/oslo_versionedobjects/fixture.py @@ -28,7 +28,6 @@ import inspect import logging import mock from oslo_utils import versionutils as vutils -import six import fixtures from oslo_versionedobjects import base @@ -131,7 +130,7 @@ class FakeIndirectionAPI(base.VersionedObjectIndirectionAPI): objinst = self._ser.deserialize_entity( context, self._ser.serialize_entity( context, objinst)) - objmethod = six.text_type(objmethod) + objmethod = str(objmethod) args, kwargs = self._canonicalize_args(context, args, kwargs) original = objinst.obj_clone() with mock.patch('oslo_versionedobjects.base.VersionedObject.' @@ -143,9 +142,9 @@ class FakeIndirectionAPI(base.VersionedObjectIndirectionAPI): def object_class_action(self, context, objname, objmethod, objver, args, kwargs): - objname = six.text_type(objname) - objmethod = six.text_type(objmethod) - objver = six.text_type(objver) + objname = str(objname) + objmethod = str(objmethod) + objver = str(objver) args, kwargs = self._canonicalize_args(context, args, kwargs) cls = base.VersionedObject.obj_class_from_name(objname, objver) with mock.patch('oslo_versionedobjects.base.VersionedObject.' @@ -158,10 +157,9 @@ class FakeIndirectionAPI(base.VersionedObjectIndirectionAPI): def object_class_action_versions(self, context, objname, objmethod, object_versions, args, kwargs): - objname = six.text_type(objname) - objmethod = six.text_type(objmethod) - object_versions = {six.text_type(o): six.text_type(v) - for o, v in object_versions.items()} + objname = str(objname) + objmethod = str(objmethod) + object_versions = {str(o): str(v) for o, v in object_versions.items()} args, kwargs = self._canonicalize_args(context, args, kwargs) objver = object_versions[objname] cls = base.VersionedObject.obj_class_from_name(objname, objver) @@ -247,7 +245,7 @@ class ObjectVersionChecker(object): relevant_data += extra_data_func(obj_class) fingerprint = '%s-%s' % (obj_class.VERSION, hashlib.md5( - six.binary_type(repr(relevant_data).encode())).hexdigest()) + bytes(repr(relevant_data).encode())).hexdigest()) return fingerprint def get_hashes(self, extra_data_func=None): diff --git a/oslo_versionedobjects/test.py b/oslo_versionedobjects/test.py index 0fd83cf4..e17d5cd7 100644 --- a/oslo_versionedobjects/test.py +++ b/oslo_versionedobjects/test.py @@ -23,6 +23,7 @@ Some black magic for inline callbacks. import eventlet # noqa eventlet.monkey_patch(os=False) # noqa +import functools import inspect import mock import os @@ -32,7 +33,6 @@ from oslo_concurrency import lockutils from oslo_config import cfg from oslo_config import fixture as config_fixture from oslo_log.fixture import logging_error -import six import testtools from oslo_versionedobjects.tests import obj_fixtures @@ -54,7 +54,7 @@ class skipIf(object): condition = self.condition reason = self.reason if inspect.isfunction(func_or_cls): - @six.wraps(func_or_cls) + @functools.wraps(func_or_cls) def wrapped(*args, **kwargs): if condition: raise testtools.TestCase.skipException(reason) @@ -64,7 +64,7 @@ class skipIf(object): elif inspect.isclass(func_or_cls): orig_func = getattr(func_or_cls, 'setUp') - @six.wraps(orig_func) + @functools.wraps(orig_func) def new_func(self, *args, **kwargs): if condition: raise testtools.TestCase.skipException(reason) diff --git a/oslo_versionedobjects/tests/test_fields.py b/oslo_versionedobjects/tests/test_fields.py index 56561aa9..fd3cf010 100644 --- a/oslo_versionedobjects/tests/test_fields.py +++ b/oslo_versionedobjects/tests/test_fields.py @@ -19,7 +19,6 @@ import warnings import iso8601 import mock import netaddr -import six import testtools from oslo_versionedobjects import _utils @@ -159,8 +158,6 @@ class TestString(TestField): self.field = fields.StringField() self.coerce_good_values = [ ('foo', 'foo'), (1, '1'), (1.0, '1.0'), (True, 'True')] - if six.PY2: - self.coerce_good_values += [(long(1), '1')] # noqa 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] @@ -1061,7 +1058,7 @@ class TestObject(TestField): ex = self.assertRaises(ValueError, self.field.coerce, 'obj', 'attr', [{}]) self.assertEqual('An object of type TestableObject is required ' - 'in field attr, not a list', six.text_type(ex)) + 'in field attr, not a list', str(ex)) def test_get_schema(self): self.assertEqual( @@ -1231,7 +1228,7 @@ class TestIPV6Network(TestField): self.assertNotRegex(str(invalid_val), pattern) -class FakeCounter(six.Iterator): +class FakeCounter(object): def __init__(self): self.n = 0 diff --git a/oslo_versionedobjects/tests/test_fixture.py b/oslo_versionedobjects/tests/test_fixture.py index 0a0c2264..bb8195fc 100644 --- a/oslo_versionedobjects/tests/test_fixture.py +++ b/oslo_versionedobjects/tests/test_fixture.py @@ -19,7 +19,6 @@ import hashlib import iso8601 import mock -import six from oslo_versionedobjects import base from oslo_versionedobjects import exception @@ -538,7 +537,7 @@ class TestObjectVersionChecker(test.TestCase): exp_methods = sorted([('remotable_method', argspec), ('remotable_classmethod', argspec)]) expected_relevant_data = (exp_fields, exp_methods) - expected_hash = hashlib.md5(six.binary_type(repr( + expected_hash = hashlib.md5(bytes(repr( expected_relevant_data).encode())).hexdigest() expected_fp = '%s-%s' % (MyObject.VERSION, expected_hash) @@ -564,7 +563,7 @@ class TestObjectVersionChecker(test.TestCase): child_versions.items())) exp_relevant_data = (exp_fields, exp_methods, exp_child_versions) - expected_hash = hashlib.md5(six.binary_type(repr( + expected_hash = hashlib.md5(bytes(repr( exp_relevant_data).encode())).hexdigest() expected_fp = '%s-%s' % (MyObject.VERSION, expected_hash) @@ -594,7 +593,7 @@ class TestObjectVersionChecker(test.TestCase): exp_extra_data = ExtraDataObj exp_relevant_data = (exp_fields, exp_methods, exp_extra_data) - expected_hash = hashlib.md5(six.binary_type(repr( + expected_hash = hashlib.md5(bytes(repr( exp_relevant_data).encode())).hexdigest() expected_fp = '%s-%s' % (ExtraDataObj.VERSION, expected_hash) diff --git a/oslo_versionedobjects/tests/test_objects.py b/oslo_versionedobjects/tests/test_objects.py index 9598cd51..22f62d74 100644 --- a/oslo_versionedobjects/tests/test_objects.py +++ b/oslo_versionedobjects/tests/test_objects.py @@ -17,7 +17,6 @@ import datetime import jsonschema import logging import pytz -import six import mock from oslo_context import context @@ -858,16 +857,10 @@ class _TestObject(object): ['bar', 'foo']) self.assertEqual(sorted(obj.keys()), ['bar', 'foo']) - self.assertEqual(sorted(obj.iterkeys()), - ['bar', 'foo']) self.assertEqual(sorted(obj.values(), key=str), [123, u'text']) - self.assertEqual(sorted(obj.itervalues(), key=str), - [123, u'text']) self.assertEqual(sorted(obj.items()), [('bar', u'text'), ('foo', 123)]) - self.assertEqual(sorted(list(obj.iteritems())), - [('bar', u'text'), ('foo', 123)]) self.assertEqual(dict(obj), {'foo': 123, 'bar': u'text'}) @@ -1204,16 +1197,10 @@ class _TestObject(object): def test_obj_repr_unicode(self): obj = MyObj(bar=u'\u0191\u01A1\u01A1') # verify the unicode string has been encoded as ASCII if on python 2 - if six.PY2: - self.assertEqual("MyObj(bar='\xc6\x91\xc6\xa1\xc6\xa1',foo=," - "missing=,mutable_default=,readonly=," - "rel_object=,rel_objects=,timestamp=)", - repr(obj)) - else: - self.assertEqual("MyObj(bar='\u0191\u01A1\u01A1',foo=," - "missing=,mutable_default=,readonly=," - "rel_object=,rel_objects=,timestamp=)", - repr(obj)) + self.assertEqual("MyObj(bar='\u0191\u01A1\u01A1',foo=," + "missing=,mutable_default=,readonly=," + "rel_object=,rel_objects=,timestamp=)", + repr(obj)) def test_obj_make_obj_compatible_with_relationships(self): subobj = MyOwnedObject(baz=1) @@ -1996,11 +1983,11 @@ class TestObjectSerializer(_BaseTestCase): thing = {'key': obj} primitive = ser.serialize_entity(self.context, thing) self.assertEqual(1, len(primitive)) - for item in six.itervalues(primitive): + for item in primitive.values(): self.assertNotIsInstance(item, base.VersionedObject) thing2 = ser.deserialize_entity(self.context, primitive) self.assertEqual(1, len(thing2)) - for item in six.itervalues(thing2): + for item in thing2.values(): self.assertIsInstance(item, MyObj) # object-action updates dict case diff --git a/requirements.txt b/requirements.txt index 0f9e9e39..8eb7ca53 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,6 @@ # 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. -six>=1.10.0 # MIT oslo.concurrency>=3.26.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0 oslo.context>=2.19.2 # Apache-2.0