diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/zope/interface/declarations.py | 29 | ||||
| -rw-r--r-- | src/zope/interface/interface.py | 5 | ||||
| -rw-r--r-- | src/zope/interface/tests/test_adapter.py | 18 |
3 files changed, 43 insertions, 9 deletions
diff --git a/src/zope/interface/declarations.py b/src/zope/interface/declarations.py index 97ccd55..04e2cc8 100644 --- a/src/zope/interface/declarations.py +++ b/src/zope/interface/declarations.py @@ -145,13 +145,15 @@ class _ImmutableDeclaration(Declaration): if new_bases != (): raise TypeError("Cannot set non-empty bases on shared empty Declaration.") + # As the immutable empty declaration, we cannot be changed. + # This means there's no logical reason for us to have dependents + # or subscriptions: we'll never notify them. So there's no need for + # us to keep track of any of that. @property def dependents(self): return {} - def changed(self, originally_changed): - # Does nothing, we have no dependents or dependencies - return + changed = subscribe = unsubscribe = lambda self, _ignored: None def interfaces(self): # An empty iterator @@ -163,6 +165,16 @@ class _ImmutableDeclaration(Declaration): def get(self, name, default=None): return default + def weakref(self, callback=None): + # We're a singleton, we never go away. So there's no need to return + # distinct weakref objects here; their callbacks will never + # be called. Instead, we only need to return a callable that + # returns ourself. The easiest one is to return _ImmutableDeclaration + # itself; testing on Python 3.8 shows that's faster than a function that + # returns _empty. (Remember, one goal is to avoid allocating any + # object, and that includes a method.) + return _ImmutableDeclaration + ############################################################################## # @@ -855,18 +867,19 @@ def ObjectSpecification(direct, cls): @_use_c_impl def getObjectSpecification(ob): - - provides = getattr(ob, '__provides__', None) + try: + provides = getattr(ob, '__provides__', None) + except: + provides = None if provides is not None: if isinstance(provides, SpecificationBase): return provides try: cls = ob.__class__ - except AttributeError: + except: # We can't get the class, so just consider provides return _empty - return implementedBy(cls) @@ -879,7 +892,7 @@ def providedBy(ob): # Try to get __providedBy__ try: r = ob.__providedBy__ - except AttributeError: + except: # Not set yet. Fall back to lower-level thing that computes it return getObjectSpecification(ob) diff --git a/src/zope/interface/interface.py b/src/zope/interface/interface.py index 7fa56aa..57998e0 100644 --- a/src/zope/interface/interface.py +++ b/src/zope/interface/interface.py @@ -159,7 +159,10 @@ class InterfaceBase(object): def __call__(self, obj, alternate=_marker): """Adapt an object to the interface """ - conform = getattr(obj, '__conform__', None) + try: + conform = getattr(obj, '__conform__', None) + except: + conform = None if conform is not None: adapter = self._call_conform(conform) if adapter is not None: diff --git a/src/zope/interface/tests/test_adapter.py b/src/zope/interface/tests/test_adapter.py index 25cb4bb..6703a48 100644 --- a/src/zope/interface/tests/test_adapter.py +++ b/src/zope/interface/tests/test_adapter.py @@ -997,6 +997,24 @@ class AdapterLookupBaseTests(unittest.TestCase): result = alb.queryMultiAdapter((foo,), IBar, default=_default) self.assertTrue(result is _default) + def test_queryMultiAdapter_errors_on_attribute_access(self): + # Which leads to using the _empty singleton as "requires" + # argument. See https://github.com/zopefoundation/zope.interface/issues/162 + from zope.interface.interface import InterfaceClass + IFoo = InterfaceClass('IFoo') + registry = self._makeRegistry() + alb = self._makeOne(registry) + alb.lookup = alb._uncached_lookup + class UnexpectedErrorsLikeAcquisition(object): + + def __getattribute__(self, name): + raise RuntimeError("Acquisition does this. Ha-ha!") + + result = alb.queryMultiAdapter( + (UnexpectedErrorsLikeAcquisition(),), + IFoo, + ) + def test_queryMultiAdaptor_factory_miss(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass |
