diff options
| author | Jason Madden <jamadden@gmail.com> | 2020-02-11 12:27:10 -0600 |
|---|---|---|
| committer | Jason Madden <jamadden@gmail.com> | 2020-02-17 07:01:03 -0600 |
| commit | 5cda166377889ad1603832f28f75b02ef335f28e (patch) | |
| tree | daba5f4d7527960a15ace84543d24d52143b4bbe /src | |
| parent | 653e24f53650810bb6a8ff401477e0e03ab84aa0 (diff) | |
| download | zope-interface-5cda166377889ad1603832f28f75b02ef335f28e.tar.gz | |
Add numbers ABC interfaces.
Diffstat (limited to 'src')
| -rw-r--r-- | src/zope/interface/common/collections.py | 1 | ||||
| -rw-r--r-- | src/zope/interface/common/numbers.py | 83 | ||||
| -rw-r--r-- | src/zope/interface/common/tests/__init__.py | 58 | ||||
| -rw-r--r-- | src/zope/interface/common/tests/test_collections.py | 43 | ||||
| -rw-r--r-- | src/zope/interface/common/tests/test_numbers.py | 53 |
5 files changed, 199 insertions, 39 deletions
diff --git a/src/zope/interface/common/collections.py b/src/zope/interface/common/collections.py index 6f5969d..6e21518 100644 --- a/src/zope/interface/common/collections.py +++ b/src/zope/interface/common/collections.py @@ -139,7 +139,6 @@ class ISized(ABCInterface): # ICallable is not defined because there's no standard signature. - class ICollection(ISized, IIterable, IContainer): diff --git a/src/zope/interface/common/numbers.py b/src/zope/interface/common/numbers.py new file mode 100644 index 0000000..e62ca30 --- /dev/null +++ b/src/zope/interface/common/numbers.py @@ -0,0 +1,83 @@ +############################################################################## +# Copyright (c) 2020 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +############################################################################## +""" +Interface definitions paralleling the abstract base classes defined in +:mod:`numbers`. + +After this module is imported, the standard library types will declare +that they implement the appropriate interface. + +.. versionadded:: 5.0.0 +""" +from __future__ import absolute_import + +import numbers as abc + +from zope.interface.common import ABCInterface +from zope.interface.common import optional + +from zope.interface._compat import PYTHON2 as PY2 + +# pylint:disable=inherit-non-class, +# pylint:disable=no-self-argument,no-method-argument +# pylint:disable=unexpected-special-method-signature +# pylint:disable=no-value-for-parameter + + +class INumber(ABCInterface): + abc = abc.Number + + +class IComplex(INumber): + abc = abc.Complex + + @optional + def __complex__(): + """ + Rarely implemented, even in builtin types. + """ + if PY2: + @optional + def __eq__(other): + """ + The interpreter may supply one through complicated rules. + """ + + __ne__ = __eq__ + +class IReal(IComplex): + abc = abc.Real + + @optional + def __complex__(): + """ + Rarely implemented, even in builtin types. + """ + + __floor__ = __ceil__ = __complex__ + + if PY2: + @optional + def __le__(other): + """ + The interpreter may supply one through complicated rules. + """ + + __lt__ = __le__ + + +class IRational(IReal): + abc = abc.Rational + + +class IIntegral(IRational): + abc = abc.Integral diff --git a/src/zope/interface/common/tests/__init__.py b/src/zope/interface/common/tests/__init__.py index b711d36..3fe3882 100644 --- a/src/zope/interface/common/tests/__init__.py +++ b/src/zope/interface/common/tests/__init__.py @@ -1,2 +1,58 @@ +############################################################################## +# Copyright (c) 2020 Zope Foundation and Contributors. +# All Rights Reserved. # -# This file is necessary to make this directory a package. +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +############################################################################## + + +from zope.interface.common import ABCInterface +from zope.interface.common import ABCInterfaceClass + + +def iter_abc_interfaces(predicate=lambda iface: True): + # Iterate ``(iface, classes)``, where ``iface`` is a descendent of + # the ABCInterfaceClass passing the *predicate* and ``classes`` is + # an iterable of classes registered to conform to that interface. + # + # Note that some builtin classes are registered for two distinct + # parts of the ABC/interface tree. For example, bytearray is both ByteString + # and MutableSequence. + seen = set() + stack = list(ABCInterface.dependents) # subclasses, but also implementedBy objects + while stack: + iface = stack.pop(0) + if iface in seen or not isinstance(iface, ABCInterfaceClass): + continue + seen.add(iface) + stack.extend(list(iface.dependents)) + if not predicate(iface): + continue + + registered = list(iface.getRegisteredConformers()) + if registered: + yield iface, registered + + +def add_abc_interface_tests(cls, module): + def predicate(iface): + return iface.__module__ == module + + for iface, registered_classes in iter_abc_interfaces(predicate): + for stdlib_class in registered_classes: + + def test(self, stdlib_class=stdlib_class, iface=iface): + if stdlib_class in self.UNVERIFIABLE or stdlib_class.__name__ in self.UNVERIFIABLE: + self.skipTest("Unable to verify %s" % stdlib_class) + + self.assertTrue(self.verify(iface, stdlib_class)) + + name = 'test_auto_' + stdlib_class.__name__ + '_' + iface.__name__ + test.__name__ = name + assert not hasattr(cls, name) + setattr(cls, name, test) diff --git a/src/zope/interface/common/tests/test_collections.py b/src/zope/interface/common/tests/test_collections.py index 8fd1d0c..6b142ee 100644 --- a/src/zope/interface/common/tests/test_collections.py +++ b/src/zope/interface/common/tests/test_collections.py @@ -25,8 +25,7 @@ except ImportError: from zope.interface.verify import verifyClass from zope.interface.verify import verifyObject -from zope.interface.common import ABCInterface -from zope.interface.common import ABCInterfaceClass + # Note that importing z.i.c.collections does work on import. from zope.interface.common import collections @@ -34,23 +33,7 @@ from zope.interface.common import collections from zope.interface._compat import PYPY from zope.interface._compat import PYTHON2 as PY2 -def walk_abc_interfaces(): - # Note that some builtin classes are registered for two distinct - # parts of the ABC/interface tree. For example, bytearray is both ByteString - # and MutableSequence. - seen = set() - stack = list(ABCInterface.dependents) # subclasses, but also implementedBy objects - while stack: - iface = stack.pop(0) - if iface in seen or not isinstance(iface, ABCInterfaceClass): - continue - seen.add(iface) - stack.extend(list(iface.dependents)) - - registered = list(iface.getRegisteredConformers()) - if registered: - yield iface, registered - +from . import add_abc_interface_tests class TestVerifyClass(unittest.TestCase): @@ -81,7 +64,7 @@ class TestVerifyClass(unittest.TestCase): # about third-party code here, just standard library types. We start with a # blacklist of things to exclude, but if that gets out of hand we can figure # out a better whitelisting. - _UNVERIFIABLE = { + UNVERIFIABLE = { # This is declared to be an ISequence, but is missing lots of methods, # including some that aren't part of a language protocol, such as # ``index`` and ``count``. @@ -97,7 +80,7 @@ class TestVerifyClass(unittest.TestCase): } if PYPY: - _UNVERIFIABLE.update({ + UNVERIFIABLE.update({ # collections.deque.pop() doesn't support the index= argument to # MutableSequence.pop(). We can't verify this on CPython because we can't # get the signature, but on PyPy we /can/ get the signature, and of course @@ -109,7 +92,7 @@ class TestVerifyClass(unittest.TestCase): if PY2: # pylint:disable=undefined-variable,no-member # There are a lot more types that are fundamentally unverifiable on Python 2. - _UNVERIFIABLE.update({ + UNVERIFIABLE.update({ # Missing several key methods like __getitem__ basestring, # Missing __iter__ and __contains__, hard to construct. @@ -123,22 +106,8 @@ class TestVerifyClass(unittest.TestCase): str, }) - @classmethod - def gen_tests(cls): - for iface, registered_classes in walk_abc_interfaces(): - for stdlib_class in registered_classes: - if stdlib_class in cls._UNVERIFIABLE or stdlib_class.__name__ in cls._UNVERIFIABLE: - continue - - def test(self, stdlib_class=stdlib_class, iface=iface): - self.assertTrue(self.verify(iface, stdlib_class)) - - name = 'test_auto_' + stdlib_class.__name__ + '_' + iface.__name__ - test.__name__ = name - assert not hasattr(cls, name) - setattr(cls, name, test) +add_abc_interface_tests(TestVerifyClass, collections.ISet.__module__) -TestVerifyClass.gen_tests() class TestVerifyObject(TestVerifyClass): diff --git a/src/zope/interface/common/tests/test_numbers.py b/src/zope/interface/common/tests/test_numbers.py new file mode 100644 index 0000000..7400838 --- /dev/null +++ b/src/zope/interface/common/tests/test_numbers.py @@ -0,0 +1,53 @@ +############################################################################## +# Copyright (c) 2020 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +############################################################################## + + +import unittest +import numbers as abc + +from zope.interface.verify import verifyClass +from zope.interface.verify import verifyObject + +# Note that importing z.i.c.numbers does work on import. +from zope.interface.common import numbers + +from . import add_abc_interface_tests + + +class TestVerifyClass(unittest.TestCase): + verifier = staticmethod(verifyClass) + UNVERIFIABLE = () + + def _adjust_object_before_verify(self, iface, x): + return x + + def verify(self, iface, klass, **kwargs): + return self.verifier(iface, + self._adjust_object_before_verify(iface, klass), + **kwargs) + + def test_int(self): + self.assertIsInstance(int(), abc.Integral) + self.assertTrue(self.verify(numbers.IIntegral, int)) + + def test_float(self): + self.assertIsInstance(float(), abc.Real) + self.assertTrue(self.verify(numbers.IReal, float)) + +add_abc_interface_tests(TestVerifyClass, numbers.INumber.__module__) + + +class TestVerifyObject(TestVerifyClass): + verifier = staticmethod(verifyObject) + + def _adjust_object_before_verify(self, iface, x): + return x() |
