diff options
| author | Jason Madden <jamadden@gmail.com> | 2020-03-05 14:38:39 -0600 |
|---|---|---|
| committer | Jason Madden <jamadden@gmail.com> | 2020-03-15 09:56:14 -0500 |
| commit | 024f6432270afd021da2d9fff5c3f496f788e54d (patch) | |
| tree | d9732ae94de818f2e3ea8ac144e1b932cbefa133 /src/zope/interface/common | |
| parent | 354faccebd5b612a2ac8e081a7e5d2f7fb1089c1 (diff) | |
| download | zope-interface-issue21.tar.gz | |
Use C3 (mostly) to compute IRO.issue21
Fixes #21
The 'mostly' is because interfaces are used in cases that C3 forbids;
when there's a conflict, we fallback to the legacy algorithm. It turns
out there are few conflicts (13K out of 149K total orderings in Plone).
I hoped the fix for #8 might shake out automatically, but it didn't.
Optimize the extremely common case of a __bases__ of length one.
In the benchmark, 4/5 of the interfaces and related objects have a base of length one.
Fix the bad IROs in the bundled ABC interfaces, and implement a way to get warnings or errors.
In running plone/buildout.coredev and tracking the RO requests, the
stats for equal, not equal, and inconsistent-so-fallback, I got
{'ros': 148868, 'eq': 138461, 'ne': 10407, 'inconsistent': 12934}
Add the interface module to the Attribute str.
This was extremely helpful tracking down the Plone problem; IDate is defined in multiple modules.
Diffstat (limited to 'src/zope/interface/common')
| -rw-r--r-- | src/zope/interface/common/__init__.py | 16 | ||||
| -rw-r--r-- | src/zope/interface/common/builtins.py | 1 | ||||
| -rw-r--r-- | src/zope/interface/common/collections.py | 4 | ||||
| -rw-r--r-- | src/zope/interface/common/mapping.py | 6 | ||||
| -rw-r--r-- | src/zope/interface/common/tests/__init__.py | 31 | ||||
| -rw-r--r-- | src/zope/interface/common/tests/test_collections.py | 4 |
6 files changed, 48 insertions, 14 deletions
diff --git a/src/zope/interface/common/__init__.py b/src/zope/interface/common/__init__.py index acbc581..a8bedf0 100644 --- a/src/zope/interface/common/__init__.py +++ b/src/zope/interface/common/__init__.py @@ -121,19 +121,20 @@ class ABCInterfaceClass(InterfaceClass): # go ahead and give us a name to ease debugging. self.__name__ = name extra_classes = attrs.pop('extra_classes', ()) + ignored_classes = attrs.pop('ignored_classes', ()) if 'abc' not in attrs: # Something like ``IList(ISequence)``: We're extending # abc interfaces but not an ABC interface ourself. - self.__class__ = InterfaceClass InterfaceClass.__init__(self, name, bases, attrs) - for cls in extra_classes: - classImplements(cls, self) + ABCInterfaceClass.__register_classes(self, extra_classes, ignored_classes) + self.__class__ = InterfaceClass return based_on = attrs.pop('abc') self.__abc = based_on self.__extra_classes = tuple(extra_classes) + self.__ignored_classes = tuple(ignored_classes) assert name[1:] == based_on.__name__, (name, based_on) methods = { @@ -216,11 +217,14 @@ class ABCInterfaceClass(InterfaceClass): method.positional = method.positional[1:] return method - def __register_classes(self): + def __register_classes(self, conformers=None, ignored_classes=None): # Make the concrete classes already present in our ABC's registry # declare that they implement this interface. - - for cls in self.getRegisteredConformers(): + conformers = conformers if conformers is not None else self.getRegisteredConformers() + ignored = ignored_classes if ignored_classes is not None else self.__ignored_classes + for cls in conformers: + if cls in ignored: + continue classImplements(cls, self) def getABC(self): diff --git a/src/zope/interface/common/builtins.py b/src/zope/interface/common/builtins.py index 9262340..a07c0a3 100644 --- a/src/zope/interface/common/builtins.py +++ b/src/zope/interface/common/builtins.py @@ -37,7 +37,6 @@ __all__ = [ ] # pylint:disable=no-self-argument - class IList(collections.IMutableSequence): """ Interface for :class:`list` diff --git a/src/zope/interface/common/collections.py b/src/zope/interface/common/collections.py index 9731069..6c0496e 100644 --- a/src/zope/interface/common/collections.py +++ b/src/zope/interface/common/collections.py @@ -177,6 +177,10 @@ class ISequence(IReversible, ICollection): abc = abc.Sequence extra_classes = (UserString,) + # On Python 2, basestring is registered as an ISequence, and + # its subclass str is an IByteString. If we also register str as + # an ISequence, that tends to lead to inconsistent resolution order. + ignored_classes = (basestring,) if str is bytes else () # pylint:disable=undefined-variable @optional def __reversed__(): diff --git a/src/zope/interface/common/mapping.py b/src/zope/interface/common/mapping.py index 13fa317..de56cf8 100644 --- a/src/zope/interface/common/mapping.py +++ b/src/zope/interface/common/mapping.py @@ -43,7 +43,7 @@ class IItemMapping(Interface): """ -class IReadMapping(IItemMapping, collections.IContainer): +class IReadMapping(collections.IContainer, IItemMapping): """ Basic mapping interface. @@ -72,7 +72,7 @@ class IWriteMapping(Interface): """Set a new item in the mapping.""" -class IEnumerableMapping(IReadMapping, collections.ISized): +class IEnumerableMapping(collections.ISized, IReadMapping): """ Mapping objects whose items can be enumerated. @@ -171,7 +171,7 @@ class IExtendedWriteMapping(IWriteMapping): class IFullMapping( collections.IMutableMapping, - IExtendedReadMapping, IExtendedWriteMapping, IClonableMapping, IMapping): + IExtendedReadMapping, IExtendedWriteMapping, IClonableMapping, IMapping,): """ Full mapping interface. diff --git a/src/zope/interface/common/tests/__init__.py b/src/zope/interface/common/tests/__init__.py index 059e46c..ade2bf3 100644 --- a/src/zope/interface/common/tests/__init__.py +++ b/src/zope/interface/common/tests/__init__.py @@ -38,7 +38,8 @@ def iter_abc_interfaces(predicate=lambda iface: True): if not predicate(iface): continue - registered = list(iface.getRegisteredConformers()) + registered = set(iface.getRegisteredConformers()) + registered -= set(iface._ABCInterfaceClass__ignored_classes) if registered: yield iface, registered @@ -50,24 +51,46 @@ def add_abc_interface_tests(cls, module): def add_verify_tests(cls, iface_classes_iter): + cls.maxDiff = None for iface, registered_classes in iface_classes_iter: 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__ + suffix = "%s_%s_%s" % ( + stdlib_class.__name__, + iface.__module__.replace('.', '_'), + iface.__name__ + ) + name = 'test_auto_' + suffix test.__name__ = name - assert not hasattr(cls, name) + assert not hasattr(cls, name), (name, list(cls.__dict__)) setattr(cls, name, test) + def test_ro(self, stdlib_class=stdlib_class, iface=iface): + from zope.interface import ro + from zope.interface import implementedBy + self.assertEqual( + tuple(ro.ro(iface, strict=True)), + iface.__sro__) + implements = implementedBy(stdlib_class) + strict = stdlib_class not in self.NON_STRICT_RO + self.assertEqual( + tuple(ro.ro(implements, strict=strict)), + implements.__sro__) + + name = 'test_auto_ro_' + suffix + test_ro.__name__ = name + assert not hasattr(cls, name) + setattr(cls, name, test_ro) class VerifyClassMixin(unittest.TestCase): verifier = staticmethod(verifyClass) UNVERIFIABLE = () + NON_STRICT_RO = () def _adjust_object_before_verify(self, iface, x): return x diff --git a/src/zope/interface/common/tests/test_collections.py b/src/zope/interface/common/tests/test_collections.py index 32ab801..f06e12e 100644 --- a/src/zope/interface/common/tests/test_collections.py +++ b/src/zope/interface/common/tests/test_collections.py @@ -17,6 +17,7 @@ try: except ImportError: import collections as abc from collections import deque +from collections import OrderedDict try: @@ -118,6 +119,9 @@ class TestVerifyClass(VerifyClassMixin, unittest.TestCase): type({}.viewitems()), type({}.viewkeys()), }) + NON_STRICT_RO = { + OrderedDict + } add_abc_interface_tests(TestVerifyClass, collections.ISet.__module__) |
