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/declarations.py | |
| 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/declarations.py')
| -rw-r--r-- | src/zope/interface/declarations.py | 86 |
1 files changed, 72 insertions, 14 deletions
diff --git a/src/zope/interface/declarations.py b/src/zope/interface/declarations.py index 9c15b9b..1e9a2ea 100644 --- a/src/zope/interface/declarations.py +++ b/src/zope/interface/declarations.py @@ -449,35 +449,79 @@ def classImplementsOnly(cls, *interfaces): def classImplements(cls, *interfaces): - """Declare additional interfaces implemented for instances of a class + """ + Declare additional interfaces implemented for instances of a class - The arguments after the class are one or more interfaces or - interface specifications (`~zope.interface.interfaces.IDeclaration` objects). + The arguments after the class are one or more interfaces or + interface specifications (`~zope.interface.interfaces.IDeclaration` objects). - The interfaces given (including the interfaces in the specifications) - are added to any interfaces previously declared. + The interfaces given (including the interfaces in the specifications) + are added to any interfaces previously declared. An effort is made to + keep a consistent C3 resolution order, but this cannot be guaranteed. + + .. versionchanged:: 5.0.0 + Each individual interface in *interfaces* may be added to either the + beginning or end of the list of interfaces declared for *cls*, + based on inheritance, in order to try to maintain a consistent + resolution order. Previously, all interfaces were added to the end. """ spec = implementedBy(cls) - spec.declared += tuple(_normalizeargs(interfaces)) + interfaces = tuple(_normalizeargs(interfaces)) + + before = [] + after = [] + + # Take steps to try to avoid producing an invalid resolution + # order, while still allowing for BWC (in the past, we always + # appended) + for iface in interfaces: + for b in spec.declared: + if iface.extends(b): + before.append(iface) + break + else: + after.append(iface) + _classImplements_ordered(spec, tuple(before), tuple(after)) - # compute the bases - bases = [] - seen = {} - for b in spec.declared: + +def classImplementsFirst(cls, iface): + """ + Declare that instances of *cls* additionally provide *iface*. + + The second argument is an interface or interface specification. + It is added as the highest priority (first in the IRO) interface; + no attempt is made to keep a consistent resolution order. + + .. versionadded:: 5.0.0 + """ + spec = implementedBy(cls) + _classImplements_ordered(spec, (iface,), ()) + + +def _classImplements_ordered(spec, before=(), after=()): + # eliminate duplicates + new_declared = [] + seen = set() + for b in before + spec.declared + after: if b not in seen: - seen[b] = 1 - bases.append(b) + new_declared.append(b) + seen.add(b) - if spec.inherit is not None: + spec.declared = tuple(new_declared) + + # compute the bases + bases = new_declared # guaranteed no dupes + if spec.inherit is not None: for c in spec.inherit.__bases__: b = implementedBy(c) if b not in seen: - seen[b] = 1 + seen.add(b) bases.append(b) spec.__bases__ = tuple(bases) + def _implements_advice(cls): interfaces, classImplements = cls.__dict__['__implements_advice_data__'] del cls.__implements_advice_data__ @@ -664,6 +708,13 @@ class Provides(Declaration): # Really named ProvidesClass self._cls = cls Declaration.__init__(self, *(interfaces + (implementedBy(cls), ))) + def __repr__(self): + return "<%s.%s for %s>" % ( + self.__class__.__module__, + self.__class__.__name__, + self._cls, + ) + def __reduce__(self): return Provides, self.__args @@ -794,6 +845,13 @@ class ClassProvides(Declaration, ClassProvidesBase): self.__args = (cls, metacls, ) + interfaces Declaration.__init__(self, *(interfaces + (implementedBy(metacls), ))) + def __repr__(self): + return "<%s.%s for %s>" % ( + self.__class__.__module__, + self.__class__.__name__, + self._cls, + ) + def __reduce__(self): return self.__class__, self.__args |
