Python2: Fix metaclass, super() and OrderedMeta
* Add six dependency * Replace "metaclass=" syntax with six.with_metaclass() * Add parameters to super() * Only define OrderedMeta on Python 3 Related-Bug: 1726399 Change-Id: I21800320ef23cc95fc07278864e73b64bb7d6d08
This commit is contained in:
parent
b73c938a93
commit
c1d38f741b
@ -19,6 +19,7 @@ try:
|
||||
import funcsigs as inspect # Python 2.7
|
||||
except ImportError:
|
||||
import inspect
|
||||
import six
|
||||
|
||||
from ospurge import exceptions
|
||||
|
||||
@ -30,7 +31,7 @@ if TYPE_CHECKING: # pragma: no cover
|
||||
|
||||
class MatchSignaturesMeta(type):
|
||||
def __init__(self, clsname, bases, clsdict):
|
||||
super().__init__(clsname, bases, clsdict)
|
||||
super(MatchSignaturesMeta, self).__init__(clsname, bases, clsdict)
|
||||
sup = super(self, self) # type: ignore # See python/mypy #857
|
||||
for name, value in clsdict.items():
|
||||
if name.startswith('_') or not callable(value):
|
||||
@ -47,37 +48,43 @@ class MatchSignaturesMeta(type):
|
||||
value_name, prev_sig, val_sig)
|
||||
|
||||
|
||||
class OrderedMeta(type):
|
||||
def __new__(cls, clsname, bases, clsdict):
|
||||
ordered_methods = cls.ordered_methods
|
||||
allowed_next_methods = list(ordered_methods)
|
||||
for name, value in clsdict.items():
|
||||
if name not in ordered_methods:
|
||||
continue
|
||||
if six.PY3:
|
||||
class OrderedMeta(type):
|
||||
def __new__(cls, clsname, bases, clsdict):
|
||||
ordered_methods = cls.ordered_methods
|
||||
allowed_next_methods = list(ordered_methods)
|
||||
for name, value in clsdict.items():
|
||||
if name not in ordered_methods:
|
||||
continue
|
||||
|
||||
if name not in allowed_next_methods:
|
||||
value_name = getattr(value, '__qualname__', value.__name__)
|
||||
logging.warning(
|
||||
"Method %s not defined at the correct location. Methods "
|
||||
"in class %s must be defined in the following order %r",
|
||||
value_name, clsname, ordered_methods
|
||||
)
|
||||
continue # pragma: no cover
|
||||
if name not in allowed_next_methods:
|
||||
value_name = value.__qualname__
|
||||
logging.warning(
|
||||
"Method %s not defined at the correct location."
|
||||
" Methods in class %s must be defined in the following"
|
||||
" order %r",
|
||||
value_name, clsname, ordered_methods
|
||||
)
|
||||
continue # pragma: no cover
|
||||
|
||||
_slice = slice(allowed_next_methods.index(name) + 1, None)
|
||||
allowed_next_methods = allowed_next_methods[_slice]
|
||||
_slice = slice(allowed_next_methods.index(name) + 1, None)
|
||||
allowed_next_methods = allowed_next_methods[_slice]
|
||||
|
||||
# Cast to dict is required. We can't pass an OrderedDict here.
|
||||
return super().__new__(cls, clsname, bases, dict(clsdict))
|
||||
# Cast to dict is required. We can't pass an OrderedDict here.
|
||||
return super().__new__(cls, clsname, bases, dict(clsdict))
|
||||
|
||||
@classmethod
|
||||
def __prepare__(cls, clsname, bases):
|
||||
return collections.OrderedDict()
|
||||
@classmethod
|
||||
def __prepare__(cls, clsname, bases):
|
||||
return collections.OrderedDict()
|
||||
|
||||
|
||||
class CodingStyleMixin(OrderedMeta, MatchSignaturesMeta, abc.ABCMeta):
|
||||
ordered_methods = ['order', 'check_prerequisite', 'list', 'should_delete',
|
||||
'delete', 'to_string']
|
||||
class CodingStyleMixin(OrderedMeta, MatchSignaturesMeta, abc.ABCMeta):
|
||||
ordered_methods = ['order', 'check_prerequisite', 'list',
|
||||
'should_delete', 'delete', 'to_string']
|
||||
else: # pragma: no cover here
|
||||
# OrderedMeta is not supported on Python 2. Class members are unordered in
|
||||
# Python 2 and __prepare__() was introduced in Python 3.
|
||||
class CodingStyleMixin(MatchSignaturesMeta, abc.ABCMeta):
|
||||
pass
|
||||
|
||||
|
||||
class BaseServiceResource(object):
|
||||
@ -87,11 +94,12 @@ class BaseServiceResource(object):
|
||||
self.options = None # type: Optional[argparse.Namespace]
|
||||
|
||||
|
||||
class ServiceResource(BaseServiceResource, metaclass=CodingStyleMixin):
|
||||
class ServiceResource(six.with_metaclass(CodingStyleMixin,
|
||||
BaseServiceResource)):
|
||||
ORDER = None # type: int
|
||||
|
||||
def __init__(self, creds_manager):
|
||||
super().__init__()
|
||||
super(ServiceResource, self).__init__()
|
||||
if self.ORDER is None:
|
||||
raise ValueError(
|
||||
'Class {}.{} must override the "ORDER" class attribute'.format(
|
||||
|
@ -16,6 +16,8 @@ from typing import Dict
|
||||
from typing import Iterable
|
||||
from typing import Optional
|
||||
|
||||
import six
|
||||
|
||||
from ospurge.main import CredentialsManager # noqa: F401
|
||||
|
||||
|
||||
@ -48,7 +50,7 @@ class BaseServiceResource(object):
|
||||
...
|
||||
|
||||
|
||||
class ServiceResource(BaseServiceResource, metaclass=CodingStyleMixin):
|
||||
class ServiceResource(six.with_metaclass(CodingStyleMixin, BaseServiceResource)):
|
||||
def __init__(self, creds_manager: 'CredentialsManager') -> None:
|
||||
...
|
||||
|
||||
|
@ -11,6 +11,8 @@
|
||||
# under the License.
|
||||
import time
|
||||
|
||||
import six
|
||||
|
||||
from ospurge import exceptions
|
||||
from ospurge.resources import base
|
||||
from ospurge.tests import mock
|
||||
@ -41,7 +43,7 @@ class WrongMethodDefOrder(Exception):
|
||||
|
||||
@mock.patch('logging.warning', mock.Mock(side_effect=SignatureMismatch))
|
||||
class TestMatchSignaturesMeta(unittest.TestCase):
|
||||
class Test(metaclass=base.MatchSignaturesMeta):
|
||||
class Test(six.with_metaclass(base.MatchSignaturesMeta)):
|
||||
def a(self, arg1):
|
||||
pass
|
||||
|
||||
@ -94,46 +96,48 @@ class TestMatchSignaturesMeta(unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
@mock.patch('logging.warning', mock.Mock(side_effect=WrongMethodDefOrder))
|
||||
class TestOrderedMeta(unittest.TestCase):
|
||||
class Test(base.OrderedMeta):
|
||||
ordered_methods = ['a', 'b']
|
||||
# OrderedMeta requires Python 3
|
||||
if six.PY3:
|
||||
@mock.patch('logging.warning', mock.Mock(side_effect=WrongMethodDefOrder))
|
||||
class TestOrderedMeta(unittest.TestCase):
|
||||
class Test(base.OrderedMeta):
|
||||
ordered_methods = ['a', 'b']
|
||||
|
||||
def test_nominal(self):
|
||||
class Foo1(metaclass=self.Test):
|
||||
def a(self):
|
||||
pass
|
||||
def test_nominal(self):
|
||||
class Foo1(six.with_metaclass(self.Test)):
|
||||
def a(self):
|
||||
pass
|
||||
|
||||
class Foo2(metaclass=self.Test):
|
||||
def b(self):
|
||||
pass
|
||||
|
||||
class Foo3(metaclass=self.Test):
|
||||
def a(self):
|
||||
pass
|
||||
|
||||
def b(self):
|
||||
pass
|
||||
|
||||
class Foo4(metaclass=self.Test):
|
||||
def a(self):
|
||||
pass
|
||||
|
||||
def other(self):
|
||||
pass
|
||||
|
||||
def b(self):
|
||||
pass
|
||||
|
||||
def test_wrong_order(self):
|
||||
with self.assertRaises(WrongMethodDefOrder):
|
||||
class Foo(metaclass=self.Test):
|
||||
class Foo2(six.with_metaclass(self.Test)):
|
||||
def b(self):
|
||||
pass
|
||||
|
||||
class Foo3(six.with_metaclass(self.Test)):
|
||||
def a(self):
|
||||
pass
|
||||
|
||||
def b(self):
|
||||
pass
|
||||
|
||||
class Foo4(six.with_metaclass(self.Test)):
|
||||
def a(self):
|
||||
pass
|
||||
|
||||
def other(self):
|
||||
pass
|
||||
|
||||
def b(self):
|
||||
pass
|
||||
|
||||
def test_wrong_order(self):
|
||||
with self.assertRaises(WrongMethodDefOrder):
|
||||
class Foo(six.with_metaclass(self.Test)):
|
||||
def b(self):
|
||||
pass
|
||||
|
||||
def a(self):
|
||||
pass
|
||||
|
||||
|
||||
class TestServiceResource(unittest.TestCase):
|
||||
def test_init_without_order_attr(self):
|
||||
|
@ -1,5 +1,6 @@
|
||||
os-client-config>=1.22.0 # Apache-2.0
|
||||
pbr>=1.8 # Apache-2.0
|
||||
six
|
||||
shade>=1.13.1
|
||||
typing>=3.5.2.2 # PSF
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user