summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2020-02-11 12:27:10 -0600
committerJason Madden <jamadden@gmail.com>2020-02-17 07:01:03 -0600
commit5cda166377889ad1603832f28f75b02ef335f28e (patch)
treedaba5f4d7527960a15ace84543d24d52143b4bbe /src
parent653e24f53650810bb6a8ff401477e0e03ab84aa0 (diff)
downloadzope-interface-5cda166377889ad1603832f28f75b02ef335f28e.tar.gz
Add numbers ABC interfaces.
Diffstat (limited to 'src')
-rw-r--r--src/zope/interface/common/collections.py1
-rw-r--r--src/zope/interface/common/numbers.py83
-rw-r--r--src/zope/interface/common/tests/__init__.py58
-rw-r--r--src/zope/interface/common/tests/test_collections.py43
-rw-r--r--src/zope/interface/common/tests/test_numbers.py53
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()