############################################################################## # # Copyright (c) 2003 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. # ############################################################################## """Test the new API for making and checking interface declarations """ import unittest from zope.interface.interface import InterfaceClass from zope.interface.tests import MissingSomeAttrs from zope.interface.tests import OptimizationTestMixin from zope.interface.tests import SubclassableMixin from zope.interface.tests.test_interface import \ NameAndModuleComparisonTestsMixin # pylint:disable=inherit-non-class,too-many-lines,protected-access # pylint:disable=blacklisted-name,attribute-defined-outside-init IFoo = InterfaceClass('IFoo') class Foo: pass class FooImplementedNone: __implemented__ = None class FooNoCall: __implemented__ = None def __call__(self): raise NotImplementedError() class _Py3ClassAdvice: def _run_generated_code( self, code, globs, locs, fails_under_py3k=True, ): # pylint:disable=exec-used,no-member import warnings with warnings.catch_warnings(record=True) as _: warnings.resetwarnings() try: exec(code, globs, locs) except TypeError: return False else: if fails_under_py3k: self.fail("Didn't raise TypeError") return None class NamedTests(unittest.TestCase): def test_class(self): from zope.interface.declarations import named @named('foo') class Foo: pass self.assertEqual( Foo.__component_name__, 'foo' ) # pylint:disable=no-member def test_function(self): from zope.interface.declarations import named @named('foo') def doFoo(o): raise NotImplementedError() self.assertEqual(doFoo.__component_name__, 'foo') def test_instance(self): from zope.interface.declarations import named class Foo: pass foo = Foo() named('foo')(foo) self.assertEqual( foo.__component_name__, 'foo' ) # pylint:disable=no-member class EmptyDeclarationTests(unittest.TestCase): # Tests that should pass for all objects that are empty # declarations. This includes a Declaration explicitly created # that way, and the empty ImmutableDeclaration. def _getEmpty(self): from zope.interface.declarations import Declaration return Declaration() def test___iter___empty(self): decl = self._getEmpty() self.assertEqual(list(decl), []) def test_flattened_empty(self): from zope.interface.interface import Interface decl = self._getEmpty() self.assertEqual(list(decl.flattened()), [Interface]) def test___contains___empty(self): from zope.interface.interface import Interface decl = self._getEmpty() self.assertNotIn(Interface, decl) def test_extends_empty(self): from zope.interface.interface import Interface decl = self._getEmpty() self.assertTrue(decl.extends(Interface)) self.assertTrue(decl.extends(Interface, strict=True)) def test_interfaces_empty(self): decl = self._getEmpty() iface_list = list(decl.interfaces()) self.assertEqual(iface_list, []) def test___sro___(self): from zope.interface.interface import Interface decl = self._getEmpty() self.assertEqual(decl.__sro__, (decl, Interface,)) def test___iro___(self): from zope.interface.interface import Interface decl = self._getEmpty() self.assertEqual(decl.__iro__, (Interface,)) def test_get(self): decl = self._getEmpty() self.assertIsNone(decl.get('attr')) self.assertEqual(decl.get('abc', 'def'), 'def') # It's a positive cache only (when it even exists) # so this added nothing. self.assertFalse(decl._v_attrs) def test_changed_w_existing__v_attrs(self): decl = self._getEmpty() decl._v_attrs = object() decl.changed(decl) self.assertFalse(decl._v_attrs) class DeclarationTests(EmptyDeclarationTests): def _getTargetClass(self): from zope.interface.declarations import Declaration return Declaration def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_ctor_no_bases(self): decl = self._makeOne() self.assertEqual(list(decl.__bases__), []) def test_ctor_w_interface_in_bases(self): IFoo = InterfaceClass('IFoo') decl = self._makeOne(IFoo) self.assertEqual(list(decl.__bases__), [IFoo]) def test_ctor_w_implements_in_bases(self): from zope.interface.declarations import Implements impl = Implements() decl = self._makeOne(impl) self.assertEqual(list(decl.__bases__), [impl]) def test_changed_wo_existing__v_attrs(self): decl = self._makeOne() decl.changed(decl) # doesn't raise self.assertIsNone(decl._v_attrs) def test___contains__w_self(self): decl = self._makeOne() self.assertNotIn(decl, decl) def test___contains__w_unrelated_iface(self): IFoo = InterfaceClass('IFoo') decl = self._makeOne() self.assertNotIn(IFoo, decl) def test___contains__w_base_interface(self): IFoo = InterfaceClass('IFoo') decl = self._makeOne(IFoo) self.assertIn(IFoo, decl) def test___iter___single_base(self): IFoo = InterfaceClass('IFoo') decl = self._makeOne(IFoo) self.assertEqual(list(decl), [IFoo]) def test___iter___multiple_bases(self): IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') decl = self._makeOne(IFoo, IBar) self.assertEqual(list(decl), [IFoo, IBar]) def test___iter___inheritance(self): IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) decl = self._makeOne(IBar) self.assertEqual(list(decl), [IBar]) # IBar.interfaces() omits bases def test___iter___w_nested_sequence_overlap(self): IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') decl = self._makeOne(IBar, (IFoo, IBar)) self.assertEqual(list(decl), [IBar, IFoo]) def test_flattened_single_base(self): from zope.interface.interface import Interface IFoo = InterfaceClass('IFoo') decl = self._makeOne(IFoo) self.assertEqual(list(decl.flattened()), [IFoo, Interface]) def test_flattened_multiple_bases(self): from zope.interface.interface import Interface IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') decl = self._makeOne(IFoo, IBar) self.assertEqual(list(decl.flattened()), [IFoo, IBar, Interface]) def test_flattened_inheritance(self): from zope.interface.interface import Interface IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) decl = self._makeOne(IBar) self.assertEqual(list(decl.flattened()), [IBar, IFoo, Interface]) def test_flattened_w_nested_sequence_overlap(self): from zope.interface.interface import Interface IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') # This is the same as calling ``Declaration(IBar, IFoo, IBar)`` # which doesn't make much sense, but here it is. In older # versions of zope.interface, the __iro__ would have been # IFoo, IBar, Interface, which especially makes no sense. decl = self._makeOne(IBar, (IFoo, IBar)) # Note that decl.__iro__ has IFoo first. self.assertEqual(list(decl.flattened()), [IBar, IFoo, Interface]) def test___sub___unrelated_interface(self): IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') before = self._makeOne(IFoo) after = before - IBar self.assertIsInstance(after, self._getTargetClass()) self.assertEqual(list(after), [IFoo]) def test___sub___related_interface(self): IFoo = InterfaceClass('IFoo') before = self._makeOne(IFoo) after = before - IFoo self.assertEqual(list(after), []) def test___sub___related_interface_by_inheritance(self): IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) before = self._makeOne(IBar) after = before - IBar self.assertEqual(list(after), []) def test___add___unrelated_interface(self): IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') before = self._makeOne(IFoo) after = before + IBar self.assertIsInstance(after, self._getTargetClass()) self.assertEqual(list(after), [IFoo, IBar]) def test___add___related_interface(self): IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') IBaz = InterfaceClass('IBaz') before = self._makeOne(IFoo, IBar) other = self._makeOne(IBar, IBaz) after = before + other self.assertEqual(list(after), [IFoo, IBar, IBaz]) def test___add___overlapping_interface(self): # The derived interfaces end up with higher priority, and # don't produce a C3 resolution order violation. This # example produced a C3 error, and the resulting legacy order # used to be wrong ([IBase, IDerived] instead of # the other way). from zope.interface import Interface from zope.interface import ro from zope.interface.tests.test_ro import C3Setting IBase = InterfaceClass('IBase') IDerived = InterfaceClass('IDerived', (IBase,)) with C3Setting(ro.C3.STRICT_IRO, True): base = self._makeOne(IBase) after = base + IDerived self.assertEqual(after.__iro__, (IDerived, IBase, Interface)) self.assertEqual(after.__bases__, (IDerived, IBase)) self.assertEqual(list(after), [IDerived, IBase]) def test___add___overlapping_interface_implementedBy(self): # Like test___add___overlapping_interface, but pulling # in a realistic example. This one previously produced a # C3 error, but the resulting legacy order was (somehow) # correct. from zope.interface import Interface from zope.interface import implementedBy from zope.interface import implementer from zope.interface import ro from zope.interface.tests.test_ro import C3Setting class IBase(Interface): pass class IDerived(IBase): pass @implementer(IBase) class Base: pass with C3Setting(ro.C3.STRICT_IRO, True): after = implementedBy(Base) + IDerived self.assertEqual(after.__sro__, (after, IDerived, IBase, Interface)) self.assertEqual(after.__bases__, (IDerived, IBase)) self.assertEqual(list(after), [IDerived, IBase]) class TestImmutableDeclaration(EmptyDeclarationTests): def _getTargetClass(self): from zope.interface.declarations import _ImmutableDeclaration return _ImmutableDeclaration def _getEmpty(self): from zope.interface.declarations import _empty return _empty def test_pickle(self): import pickle copied = pickle.loads(pickle.dumps(self._getEmpty())) self.assertIs(copied, self._getEmpty()) def test_singleton(self): self.assertIs( self._getTargetClass()(), self._getEmpty() ) def test__bases__(self): self.assertEqual(self._getEmpty().__bases__, ()) def test_change__bases__(self): empty = self._getEmpty() empty.__bases__ = () self.assertEqual(self._getEmpty().__bases__, ()) with self.assertRaises(TypeError): empty.__bases__ = (1,) def test_dependents(self): empty = self._getEmpty() deps = empty.dependents self.assertEqual({}, deps) # Doesn't change the return. deps[1] = 2 self.assertEqual({}, empty.dependents) def test_changed(self): # Does nothing, has no visible side-effects self._getEmpty().changed(None) def test_extends_always_false(self): self.assertFalse(self._getEmpty().extends(self)) self.assertFalse(self._getEmpty().extends(self, strict=True)) self.assertFalse(self._getEmpty().extends(self, strict=False)) def test_get_always_default(self): self.assertIsNone(self._getEmpty().get('name')) self.assertEqual(self._getEmpty().get('name', 42), 42) def test_v_attrs(self): decl = self._getEmpty() self.assertEqual(decl._v_attrs, {}) decl._v_attrs['attr'] = 42 self.assertEqual(decl._v_attrs, {}) self.assertIsNone(decl.get('attr')) attrs = decl._v_attrs = {} attrs['attr'] = 42 self.assertEqual(decl._v_attrs, {}) self.assertIsNone(decl.get('attr')) class TestImplements(NameAndModuleComparisonTestsMixin, unittest.TestCase): def _getTargetClass(self): from zope.interface.declarations import Implements return Implements def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def _makeOneToCompare(self): from zope.interface.declarations import implementedBy class A: pass return implementedBy(A) def test_ctor_no_bases(self): impl = self._makeOne() self.assertEqual(impl.inherit, None) self.assertEqual(impl.declared, ()) self.assertEqual(impl.__name__, '?') self.assertEqual(list(impl.__bases__), []) def test___repr__(self): impl = self._makeOne() impl.__name__ = 'Testing' self.assertEqual(repr(impl), 'classImplements(Testing)') def test___reduce__(self): from zope.interface.declarations import implementedBy impl = self._makeOne() self.assertEqual(impl.__reduce__(), (implementedBy, (None,))) def test_sort(self): from zope.interface.declarations import implementedBy class A: pass class B: pass IFoo = InterfaceClass('IFoo') self.assertEqual(implementedBy(A), implementedBy(A)) self.assertEqual(hash(implementedBy(A)), hash(implementedBy(A))) self.assertLess(implementedBy(A), None) self.assertGreater( None, implementedBy(A) ) self.assertLess(implementedBy(A), implementedBy(B)) self.assertGreater(implementedBy(A), IFoo) self.assertLessEqual(implementedBy(A), implementedBy(B)) self.assertGreaterEqual(implementedBy(A), IFoo) self.assertNotEqual(implementedBy(A), IFoo) def test_proxy_equality(self): # https://github.com/zopefoundation/zope.interface/issues/55 from zope.interface.declarations import implementedBy class Proxy: def __init__(self, wrapped): self._wrapped = wrapped def __getattr__(self, name): raise NotImplementedError() def __eq__(self, other): return self._wrapped == other def __ne__(self, other): return self._wrapped != other class A: pass class B: pass implementedByA = implementedBy(A) implementedByB = implementedBy(B) proxy = Proxy(implementedByA) # The order of arguments to the operators matters, # test both self.assertEqual( implementedByA, implementedByA ) self.assertNotEqual(implementedByA, implementedByB) self.assertNotEqual(implementedByB, implementedByA) self.assertEqual(proxy, implementedByA) self.assertEqual(implementedByA, proxy) self.assertEqual(proxy, implementedByA) self.assertEqual(implementedByA, proxy) self.assertNotEqual(proxy, implementedByB) self.assertNotEqual(implementedByB, proxy) def test_changed_deletes_super_cache(self): impl = self._makeOne() self.assertIsNone(impl._super_cache) self.assertNotIn('_super_cache', impl.__dict__) impl._super_cache = 42 self.assertIn('_super_cache', impl.__dict__) impl.changed(None) self.assertIsNone(impl._super_cache) self.assertNotIn('_super_cache', impl.__dict__) def test_changed_does_not_add_super_cache(self): impl = self._makeOne() self.assertIsNone(impl._super_cache) self.assertNotIn('_super_cache', impl.__dict__) impl.changed(None) self.assertIsNone(impl._super_cache) self.assertNotIn('_super_cache', impl.__dict__) class Test_implementedByFallback(unittest.TestCase): def _getTargetClass(self): # pylint:disable=no-name-in-module from zope.interface.declarations import implementedByFallback return implementedByFallback _getFallbackClass = _getTargetClass def _callFUT(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_dictless_wo_existing_Implements_wo_registrations(self): class Foo: __slots__ = ('__implemented__',) foo = Foo() foo.__implemented__ = None self.assertEqual(list(self._callFUT(foo)), []) def test_dictless_wo_existing_Implements_cant_assign___implemented__(self): class Foo: def _get_impl(self): raise NotImplementedError() def _set_impl(self, val): raise TypeError __implemented__ = property(_get_impl, _set_impl) def __call__(self): # act like a factory raise NotImplementedError() foo = Foo() self.assertRaises(TypeError, self._callFUT, foo) def test_dictless_wo_existing_Implements_w_registrations(self): from zope.interface import declarations class Foo: __slots__ = ('__implemented__',) foo = Foo() foo.__implemented__ = None reg = object() with _MonkeyDict(declarations, 'BuiltinImplementationSpecifications') as specs: specs[foo] = reg self.assertIs(self._callFUT(foo), reg) def test_dictless_w_existing_Implements(self): from zope.interface.declarations import Implements impl = Implements() class Foo: __slots__ = ('__implemented__',) foo = Foo() foo.__implemented__ = impl self.assertIs(self._callFUT(foo), impl) def test_dictless_w_existing_not_Implements(self): class Foo: __slots__ = ('__implemented__',) foo = Foo() IFoo = InterfaceClass('IFoo') foo.__implemented__ = (IFoo,) self.assertEqual(list(self._callFUT(foo)), [IFoo]) def test_w_existing_attr_as_Implements(self): from zope.interface.declarations import Implements impl = Implements() class Foo: __implemented__ = impl self.assertIs(self._callFUT(Foo), impl) def test_builtins_added_to_cache(self): from zope.interface import declarations from zope.interface.declarations import Implements with _MonkeyDict(declarations, 'BuiltinImplementationSpecifications') as specs: self.assertEqual(list(self._callFUT(tuple)), []) self.assertEqual(list(self._callFUT(list)), []) self.assertEqual(list(self._callFUT(dict)), []) for typ in (tuple, list, dict): spec = specs[typ] self.assertIsInstance(spec, Implements) self.assertEqual(repr(spec), 'classImplements(%s)' % (typ.__name__,)) def test_builtins_w_existing_cache(self): from zope.interface import declarations t_spec, l_spec, d_spec = object(), object(), object() with _MonkeyDict(declarations, 'BuiltinImplementationSpecifications') as specs: specs[tuple] = t_spec specs[list] = l_spec specs[dict] = d_spec self.assertIs(self._callFUT(tuple), t_spec) self.assertIs(self._callFUT(list), l_spec) self.assertIs(self._callFUT(dict), d_spec) def test_oldstyle_class_no_assertions(self): # TODO: Figure out P3 story class Foo: pass self.assertEqual(list(self._callFUT(Foo)), []) def test_no_assertions(self): # TODO: Figure out P3 story class Foo: pass self.assertEqual(list(self._callFUT(Foo)), []) def test_w_None_no_bases_not_factory(self): class Foo: __implemented__ = None foo = Foo() self.assertRaises(TypeError, self._callFUT, foo) def test_w_None_no_bases_w_factory(self): from zope.interface.declarations import objectSpecificationDescriptor from zope.interface.tests.test_declarations import FooNoCall foo = FooNoCall() foo.__name__ = 'foo' spec = self._callFUT(foo) self.assertEqual(spec.__name__, 'zope.interface.tests.test_declarations.foo') self.assertIs(spec.inherit, foo) self.assertIs(foo.__implemented__, spec) self.assertIs( foo.__providedBy__, objectSpecificationDescriptor ) # pylint:disable=no-member self.assertNotIn('__provides__', foo.__dict__) def test_w_None_no_bases_w_class(self): from zope.interface.declarations import ClassProvides from zope.interface.tests.test_declarations import FooImplementedNone spec = self._callFUT(FooImplementedNone) self.assertEqual( spec.__name__, 'zope.interface.tests.test_declarations.FooImplementedNone') self.assertIs(spec.inherit, FooImplementedNone) self.assertIs(FooImplementedNone.__implemented__, spec) self.assertIsInstance( FooImplementedNone.__providedBy__, ClassProvides ) # pylint:disable=no-member self.assertIsInstance( FooImplementedNone.__provides__, ClassProvides ) # pylint:disable=no-member self.assertEqual( FooImplementedNone.__provides__, FooImplementedNone.__providedBy__ ) # pylint:disable=no-member def test_w_existing_Implements(self): from zope.interface.declarations import Implements impl = Implements() class Foo: __implemented__ = impl self.assertIs(self._callFUT(Foo), impl) def test_super_when_base_implements_interface(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass @implementer(IBase) class Base: pass @implementer(IDerived) class Derived(Base): pass self.assertEqual(list(self._callFUT(Derived)), [IDerived, IBase]) sup = super(Derived, Derived) self.assertEqual(list(self._callFUT(sup)), [IBase]) def test_super_when_base_implements_interface_diamond(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass @implementer(IBase) class Base: pass class Child1(Base): pass class Child2(Base): pass @implementer(IDerived) class Derived(Child1, Child2): pass self.assertEqual(list(self._callFUT(Derived)), [IDerived, IBase]) sup = super(Derived, Derived) self.assertEqual(list(self._callFUT(sup)), [IBase]) def test_super_when_parent_implements_interface_diamond(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass class Base: pass class Child1(Base): pass @implementer(IBase) class Child2(Base): pass @implementer(IDerived) class Derived(Child1, Child2): pass self.assertEqual( Derived.__mro__, (Derived, Child1, Child2, Base, object) ) self.assertEqual(list(self._callFUT(Derived)), [IDerived, IBase]) sup = super(Derived, Derived) fut = self._callFUT(sup) self.assertEqual(list(fut), [IBase]) self.assertIsNone(fut._dependents) def test_super_when_base_doesnt_implement_interface(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass class Base: pass @implementer(IDerived) class Derived(Base): pass self.assertEqual(list(self._callFUT(Derived)), [IDerived]) sup = super(Derived, Derived) self.assertEqual(list(self._callFUT(sup)), []) def test_super_when_base_is_object(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass @implementer(IDerived) class Derived: pass self.assertEqual(list(self._callFUT(Derived)), [IDerived]) sup = super(Derived, Derived) self.assertEqual(list(self._callFUT(sup)), []) def test_super_multi_level_multi_inheritance(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IM1(Interface): pass class IM2(Interface): pass class IDerived(IBase): pass class IUnrelated(Interface): pass @implementer(IBase) class Base: pass @implementer(IM1) class M1(Base): pass @implementer(IM2) class M2(Base): pass @implementer(IDerived, IUnrelated) class Derived(M1, M2): pass d = Derived sd = super(Derived, Derived) sm1 = super(M1, Derived) sm2 = super(M2, Derived) self.assertEqual(list(self._callFUT(d)), [IDerived, IUnrelated, IM1, IBase, IM2]) self.assertEqual(list(self._callFUT(sd)), [IM1, IBase, IM2]) self.assertEqual(list(self._callFUT(sm1)), [IM2, IBase]) self.assertEqual(list(self._callFUT(sm2)), [IBase]) class Test_implementedBy(Test_implementedByFallback, OptimizationTestMixin): # Repeat tests for C optimizations def _getTargetClass(self): from zope.interface.declarations import implementedBy return implementedBy class _ImplementsTestMixin: FUT_SETS_PROVIDED_BY = True def _callFUT(self, cls, iface): # Declare that *cls* implements *iface*; return *cls* raise NotImplementedError def _check_implementer(self, Foo, orig_spec=None, spec_name=__name__ + '.Foo', inherit="not given"): from zope.interface.declarations import ClassProvides IFoo = InterfaceClass('IFoo') returned = self._callFUT(Foo, IFoo) self.assertIs(returned, Foo) spec = Foo.__implemented__ if orig_spec is not None: self.assertIs(spec, orig_spec) self.assertEqual(spec.__name__, spec_name) inherit = Foo if inherit == "not given" else inherit self.assertIs(spec.inherit, inherit) self.assertIs(Foo.__implemented__, spec) if self.FUT_SETS_PROVIDED_BY: self.assertIsInstance(Foo.__providedBy__, ClassProvides) self.assertIsInstance(Foo.__provides__, ClassProvides) self.assertEqual(Foo.__provides__, Foo.__providedBy__) return Foo, IFoo def test_class(self): class Foo: pass self._check_implementer(Foo) class Test_classImplementsOnly(_ImplementsTestMixin, unittest.TestCase): FUT_SETS_PROVIDED_BY = False def _callFUT(self, cls, iface): from zope.interface.declarations import classImplementsOnly classImplementsOnly(cls, iface) return cls def test_w_existing_Implements(self): from zope.interface.declarations import Implements IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') impl = Implements(IFoo) impl.declared = (IFoo,) class Foo: __implemented__ = impl impl.inherit = Foo self._callFUT(Foo, IBar) # Same spec, now different values self.assertIs(Foo.__implemented__, impl) self.assertEqual(impl.inherit, None) self.assertEqual(impl.declared, (IBar,)) def test_class(self): from zope.interface.declarations import Implements IBar = InterfaceClass('IBar') old_spec = Implements(IBar) class Foo: __implemented__ = old_spec self._check_implementer(Foo, old_spec, '?', inherit=None) def test_redundant_with_super_still_implements(self): Base, IBase = self._check_implementer( type('Foo', (object,), {}), inherit=None, ) class Child(Base): pass self._callFUT(Child, IBase) self.assertTrue(IBase.implementedBy(Child)) class Test_classImplements(_ImplementsTestMixin, unittest.TestCase): def _callFUT(self, cls, iface): from zope.interface.declarations import classImplements result = classImplements( cls, iface ) # pylint:disable=assignment-from-no-return self.assertIsNone(result) return cls def __check_implementer_redundant(self, Base): # If we @implementer exactly what was already present, we write no # declared attributes on the parent (we still set everything, though) Base, IBase = self._check_implementer(Base) class Child(Base): pass returned = self._callFUT(Child, IBase) self.assertIn('__implemented__', returned.__dict__) self.assertNotIn('__providedBy__', returned.__dict__) self.assertIn('__provides__', returned.__dict__) spec = Child.__implemented__ self.assertEqual(spec.declared, ()) self.assertEqual(spec.inherit, Child) self.assertTrue(IBase.providedBy(Child())) def test_redundant_implementer_empty_class_declarations(self): class Foo: pass self.__check_implementer_redundant(Foo) def test_redundant_implementer_Interface(self): from zope.interface import Interface from zope.interface import implementedBy from zope.interface import ro from zope.interface.tests.test_ro import C3Setting class Foo: pass with C3Setting(ro.C3.STRICT_IRO, False): self._callFUT(Foo, Interface) self.assertEqual(list(implementedBy(Foo)), [Interface]) class Baz(Foo): pass self._callFUT(Baz, Interface) self.assertEqual(list(implementedBy(Baz)), [Interface]) def _order_for_two(self, applied_first, applied_second): return (applied_first, applied_second) def test_w_existing_Implements(self): from zope.interface.declarations import Implements IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') impl = Implements(IFoo) impl.declared = (IFoo,) class Foo: __implemented__ = impl impl.inherit = Foo self._callFUT(Foo, IBar) # Same spec, now different values self.assertIs(Foo.__implemented__, impl) self.assertEqual(impl.inherit, Foo) self.assertEqual(impl.declared, self._order_for_two(IFoo, IBar)) def test_w_existing_Implements_w_bases(self): from zope.interface.declarations import Implements IRoot = InterfaceClass('IRoot') ISecondRoot = InterfaceClass('ISecondRoot') IExtendsRoot = InterfaceClass('IExtendsRoot', (IRoot,)) impl_root = Implements.named('Root', IRoot) impl_root.declared = (IRoot,) class Root1: __implemented__ = impl_root class Root2: __implemented__ = impl_root impl_extends_root = Implements.named('ExtendsRoot1', IExtendsRoot) impl_extends_root.declared = (IExtendsRoot,) class ExtendsRoot(Root1, Root2): __implemented__ = impl_extends_root impl_extends_root.inherit = ExtendsRoot self._callFUT(ExtendsRoot, ISecondRoot) # Same spec, now different values self.assertIs(ExtendsRoot.__implemented__, impl_extends_root) self.assertEqual(impl_extends_root.inherit, ExtendsRoot) self.assertEqual(impl_extends_root.declared, self._order_for_two(IExtendsRoot, ISecondRoot,)) self.assertEqual( impl_extends_root.__bases__, self._order_for_two(IExtendsRoot, ISecondRoot) + (impl_root,) ) class Test_classImplementsFirst(Test_classImplements): def _callFUT(self, cls, iface): from zope.interface.declarations import classImplementsFirst result = classImplementsFirst( cls, iface ) # pylint:disable=assignment-from-no-return self.assertIsNone(result) return cls def _order_for_two(self, applied_first, applied_second): return (applied_second, applied_first) class Test__implements_advice(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.declarations import _implements_advice return _implements_advice(*args, **kw) def test_no_existing_implements(self): from zope.interface.declarations import Implements from zope.interface.declarations import classImplements IFoo = InterfaceClass('IFoo') class Foo: __implements_advice_data__ = ((IFoo,), classImplements) self._callFUT(Foo) self.assertNotIn('__implements_advice_data__', Foo.__dict__) self.assertIsInstance( Foo.__implemented__, Implements ) # pylint:disable=no-member self.assertEqual( list(Foo.__implemented__), [IFoo] ) # pylint:disable=no-member class Test_implementer(Test_classImplements): def _getTargetClass(self): from zope.interface.declarations import implementer return implementer def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def _callFUT(self, cls, *ifaces): decorator = self._makeOne(*ifaces) return decorator(cls) def test_nonclass_cannot_assign_attr(self): IFoo = InterfaceClass('IFoo') decorator = self._makeOne(IFoo) self.assertRaises(TypeError, decorator, object()) def test_nonclass_can_assign_attr(self): IFoo = InterfaceClass('IFoo') from zope.interface.tests.test_declarations import Foo foo = Foo() decorator = self._makeOne(IFoo) returned = decorator(foo) self.assertIs(returned, foo) spec = foo.__implemented__ # pylint:disable=no-member self.assertEqual( spec.__name__, 'zope.interface.tests.test_declarations.?' ) self.assertIsNone(spec.inherit,) self.assertIs(foo.__implemented__, spec) # pylint:disable=no-member def test_does_not_leak_on_unique_classes(self): # Make sure nothing is hanging on to the class or Implements # object after they go out of scope. There was briefly a bug # in 5.x that caused SpecificationBase._bases (in C) to not be # traversed or cleared. # https://github.com/zopefoundation/zope.interface/issues/216 import gc IFoo = InterfaceClass('IFoo') begin_count = len(gc.get_objects()) for _ in range(1900): class TestClass: pass self._callFUT(TestClass, IFoo) gc.collect() end_count = len(gc.get_objects()) # How many new objects might still be around? In all currently # tested interpreters, there aren't any, so our counts should # match exactly. When the bug existed, in a steady state, the loop # would grow by two objects each iteration fudge_factor = 0 self.assertLessEqual(end_count, begin_count + fudge_factor) class Test_implementer_only(Test_classImplementsOnly): def _getTargetClass(self): from zope.interface.declarations import implementer_only return implementer_only def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def _callFUT(self, cls, iface): decorator = self._makeOne(iface) return decorator(cls) def test_function(self): IFoo = InterfaceClass('IFoo') decorator = self._makeOne(IFoo) def _function(): raise NotImplementedError() self.assertRaises(ValueError, decorator, _function) def test_method(self): IFoo = InterfaceClass('IFoo') decorator = self._makeOne(IFoo) class Bar: def _method(self): raise NotImplementedError() self.assertRaises(ValueError, decorator, Bar._method) class ProvidesClassTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.declarations import ProvidesClass return ProvidesClass def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_simple_class_one_interface(self): IFoo = InterfaceClass("IFoo") class Foo: pass spec = self._makeOne(Foo, IFoo) self.assertEqual(list(spec), [IFoo]) def test___reduce__(self): from zope.interface.declarations import Provides # the function IFoo = InterfaceClass("IFoo") class Foo: pass spec = self._makeOne(Foo, IFoo) klass, args = spec.__reduce__() self.assertIs(klass, Provides) self.assertEqual(args, (Foo, IFoo)) def test___get___class(self): IFoo = InterfaceClass("IFoo") class Foo: pass spec = self._makeOne(Foo, IFoo) Foo.__provides__ = spec self.assertIs(Foo.__provides__, spec) def test___get___instance(self): IFoo = InterfaceClass("IFoo") class Foo: pass spec = self._makeOne(Foo, IFoo) Foo.__provides__ = spec def _test(): foo = Foo() return foo.__provides__ self.assertRaises(AttributeError, _test) class ProvidesClassStrictTests(ProvidesClassTests): # Tests that require the strict C3 resolution order. def _getTargetClass(self): ProvidesClass = super()._getTargetClass() class StrictProvides(ProvidesClass): def _do_calculate_ro(self, base_mros): return ProvidesClass._do_calculate_ro( self, base_mros=base_mros, strict=True, ) return StrictProvides def test_overlapping_interfaces_corrected(self): # Giving Provides(cls, IFace), where IFace is already # provided by cls, doesn't produce invalid resolution orders. from zope.interface import Interface from zope.interface import implementedBy from zope.interface import implementer class IBase(Interface): pass @implementer(IBase) class Base: pass spec = self._makeOne(Base, IBase) self.assertEqual(spec.__sro__, ( spec, implementedBy(Base), IBase, implementedBy(object), Interface )) class TestProvidesClassRepr(unittest.TestCase): def _getTargetClass(self): from zope.interface.declarations import ProvidesClass return ProvidesClass def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test__repr__(self): IFoo = InterfaceClass("IFoo") assert IFoo.__name__ == 'IFoo' assert IFoo.__module__ == __name__ assert repr(IFoo) == f'' IBar = InterfaceClass("IBar") inst = self._makeOne(type(self), IFoo, IBar) self.assertEqual( repr(inst), "directlyProvides(TestProvidesClassRepr, IFoo, IBar)" ) def test__repr__module_provides_typical_use(self): # as created through a ``moduleProvides()`` statement # in a module body from zope.interface.tests import dummy provides = dummy.__provides__ # pylint:disable=no-member self.assertEqual( repr(provides), "directlyProvides(" "sys.modules['zope.interface.tests.dummy'], " "IDummyModule)" ) def test__repr__module_after_pickle(self): # It doesn't matter, these objects can't be pickled. import pickle from zope.interface.tests import dummy provides = dummy.__provides__ # pylint:disable=no-member for proto in range(pickle.HIGHEST_PROTOCOL + 1): with self.assertRaises(pickle.PicklingError): pickle.dumps(provides, proto) def test__repr__directlyProvides_module(self): import sys from zope.interface.declarations import alsoProvides from zope.interface.declarations import directlyProvides from zope.interface.tests import dummy from zope.interface.tests.test_declarations import IFoo IBar = InterfaceClass('IBar') orig_provides = dummy.__provides__ # pylint:disable=no-member del dummy.__provides__ # pylint:disable=no-member self.addCleanup(setattr, dummy, '__provides__', orig_provides) directlyProvides(dummy, IFoo) provides = dummy.__provides__ # pylint:disable=no-member self.assertEqual( repr(provides), "directlyProvides(sys.modules['zope.interface.tests.dummy'], IFoo)" ) alsoProvides(dummy, IBar) provides = dummy.__provides__ # pylint:disable=no-member self.assertEqual( repr(provides), "directlyProvides(" "sys.modules['zope.interface.tests.dummy'], " "IFoo, IBar)" ) # If we make this module also provide IFoo and IBar, then the repr # lists both names. my_module = sys.modules[__name__] assert not hasattr(my_module, '__provides__') directlyProvides(my_module, IFoo, IBar) self.addCleanup(delattr, my_module, '__provides__') self.assertIs(my_module.__provides__, provides) self.assertEqual( repr(provides), "directlyProvides(('zope.interface.tests.dummy', " "'interface.tests.test_declarations'), " "IFoo, IBar)" ) def test__repr__module_provides_cached_shared(self): from zope.interface.declarations import ModuleType IFoo = InterfaceClass("IFoo") inst = self._makeOne(ModuleType, IFoo) inst._v_module_names += ('some.module',) inst._v_module_names += ('another.module',) self.assertEqual( repr(inst), "directlyProvides(('some.module', 'another.module'), IFoo)" ) def test__repr__duplicate_names(self): IFoo = InterfaceClass("IFoo", __module__='mod1') IFoo2 = InterfaceClass("IFoo", __module__='mod2') IBaz = InterfaceClass("IBaz") inst = self._makeOne(type(self), IFoo, IBaz, IFoo2) self.assertEqual( repr(inst), "directlyProvides(TestProvidesClassRepr, IFoo, IBaz, mod2.IFoo)" ) def test__repr__implementedBy_in_interfaces(self): from zope.interface import Interface from zope.interface import implementedBy class IFoo(Interface): "Does nothing" class Bar: "Does nothing" impl = implementedBy(type(self)) inst = self._makeOne(Bar, IFoo, impl) self.assertEqual( repr(inst), 'directlyProvides(' 'Bar, IFoo, ' 'classImplements(TestProvidesClassRepr))' ) def test__repr__empty_interfaces(self): inst = self._makeOne(type(self)) self.assertEqual( repr(inst), 'directlyProvides(TestProvidesClassRepr)', ) def test__repr__non_class(self): def str___dont_call_me(): self.fail("Should not call str") class Object: __bases__ = () __str__ = str___dont_call_me def __repr__(self): return '' inst = self._makeOne(Object()) self.assertEqual( repr(inst), 'directlyProvides()', ) def test__repr__providedBy_from_class(self): from zope.interface.declarations import implementer from zope.interface.declarations import providedBy IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass inst = providedBy(Foo()) self.assertEqual( repr(inst), 'classImplements(Foo, IFoo)' ) def test__repr__providedBy_alsoProvides(self): from zope.interface.declarations import alsoProvides from zope.interface.declarations import implementer from zope.interface.declarations import providedBy IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") @implementer(IFoo) class Foo: pass foo = Foo() alsoProvides(foo, IBar) inst = providedBy(foo) self.assertEqual( repr(inst), "directlyProvides(Foo, IBar, classImplements(Foo, IFoo))" ) class Test_Provides(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.declarations import Provides return Provides(*args, **kw) def test_no_cached_spec(self): from zope.interface import declarations IFoo = InterfaceClass("IFoo") cache = {} class Foo: pass with _Monkey(declarations, InstanceDeclarations=cache): spec = self._callFUT(Foo, IFoo) self.assertEqual(list(spec), [IFoo]) self.assertIs(cache[(Foo, IFoo)], spec) def test_w_cached_spec(self): from zope.interface import declarations IFoo = InterfaceClass("IFoo") prior = object() class Foo: pass cache = {(Foo, IFoo): prior} with _Monkey(declarations, InstanceDeclarations=cache): spec = self._callFUT(Foo, IFoo) self.assertIs(spec, prior) class Test_directlyProvides(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.declarations import directlyProvides return directlyProvides(*args, **kw) def test_w_normal_object(self): from zope.interface.declarations import ProvidesClass IFoo = InterfaceClass("IFoo") class Foo: pass obj = Foo() self._callFUT(obj, IFoo) self.assertIsInstance( obj.__provides__, ProvidesClass ) # pylint:disable=no-member self.assertEqual( list(obj.__provides__), [IFoo] ) # pylint:disable=no-member def test_w_class(self): from zope.interface.declarations import ClassProvides IFoo = InterfaceClass("IFoo") class Foo: pass self._callFUT(Foo, IFoo) self.assertIsInstance( Foo.__provides__, ClassProvides ) # pylint:disable=no-member self.assertEqual( list(Foo.__provides__), [IFoo] ) # pylint:disable=no-member def test_w_classless_object(self): from zope.interface.declarations import ProvidesClass IFoo = InterfaceClass("IFoo") the_dict = {} class Foo: def __getattribute__(self, name): # Emulate object w/o any class if name == '__class__': return None raise NotImplementedError(name) def __setattr__(self, name, value): the_dict[name] = value obj = Foo() self._callFUT(obj, IFoo) self.assertIsInstance(the_dict['__provides__'], ProvidesClass) self.assertEqual(list(the_dict['__provides__']), [IFoo]) class Test_alsoProvides(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.declarations import alsoProvides return alsoProvides(*args, **kw) def test_wo_existing_provides(self): from zope.interface.declarations import ProvidesClass IFoo = InterfaceClass("IFoo") class Foo: pass obj = Foo() self._callFUT(obj, IFoo) self.assertIsInstance( obj.__provides__, ProvidesClass ) # pylint:disable=no-member self.assertEqual( list(obj.__provides__), [IFoo] ) # pylint:disable=no-member def test_w_existing_provides(self): from zope.interface.declarations import ProvidesClass from zope.interface.declarations import directlyProvides IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") class Foo: pass obj = Foo() directlyProvides(obj, IFoo) self._callFUT(obj, IBar) self.assertIsInstance( obj.__provides__, ProvidesClass ) # pylint:disable=no-member self.assertEqual( list(obj.__provides__), [IFoo, IBar] ) # pylint:disable=no-member class Test_noLongerProvides(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.declarations import noLongerProvides return noLongerProvides(*args, **kw) def test_wo_existing_provides(self): IFoo = InterfaceClass("IFoo") class Foo: pass obj = Foo() self._callFUT(obj, IFoo) self.assertEqual( list(obj.__provides__), [] ) # pylint:disable=no-member def test_w_existing_provides_hit(self): from zope.interface.declarations import directlyProvides IFoo = InterfaceClass("IFoo") class Foo: pass obj = Foo() directlyProvides(obj, IFoo) self._callFUT(obj, IFoo) self.assertEqual( list(obj.__provides__), [] ) # pylint:disable=no-member def test_w_existing_provides_miss(self): from zope.interface.declarations import directlyProvides IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") class Foo: pass obj = Foo() directlyProvides(obj, IFoo) self._callFUT(obj, IBar) self.assertEqual( list(obj.__provides__), [IFoo] ) # pylint:disable=no-member def test_w_iface_implemented_by_class(self): from zope.interface.declarations import implementer IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass obj = Foo() self.assertRaises(ValueError, self._callFUT, obj, IFoo) class ClassProvidesBaseFallbackTests(unittest.TestCase): def _getTargetClass(self): # pylint:disable=no-name-in-module from zope.interface.declarations import ClassProvidesBaseFallback return ClassProvidesBaseFallback def _makeOne(self, klass, implements): # Don't instantiate directly: the C version can't have attributes # assigned. class Derived(self._getTargetClass()): def __init__(self, k, i): self._cls = k self._implements = i return Derived(klass, implements) def test_w_same_class_via_class(self): IFoo = InterfaceClass("IFoo") class Foo: pass cpbp = Foo.__provides__ = self._makeOne(Foo, IFoo) self.assertIs(Foo.__provides__, cpbp) def test_w_same_class_via_instance(self): IFoo = InterfaceClass("IFoo") class Foo: pass foo = Foo() Foo.__provides__ = self._makeOne(Foo, IFoo) self.assertIs(foo.__provides__, IFoo) def test_w_different_class(self): IFoo = InterfaceClass("IFoo") class Foo: pass class Bar(Foo): pass bar = Bar() Foo.__provides__ = self._makeOne(Foo, IFoo) self.assertRaises(AttributeError, getattr, Bar, '__provides__') self.assertRaises(AttributeError, getattr, bar, '__provides__') class ClassProvidesBaseTests( OptimizationTestMixin, ClassProvidesBaseFallbackTests, SubclassableMixin, ): # Repeat tests for C optimizations def _getTargetClass(self): from zope.interface.declarations import ClassProvidesBase return ClassProvidesBase def _getFallbackClass(self): # pylint:disable=no-name-in-module from zope.interface.declarations import ClassProvidesBaseFallback return ClassProvidesBaseFallback class ClassProvidesTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.declarations import ClassProvides return ClassProvides def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_w_simple_metaclass(self): from zope.interface.declarations import implementer IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") @implementer(IFoo) class Foo: pass cp = Foo.__provides__ = self._makeOne(Foo, type(Foo), IBar) self.assertIs(Foo.__provides__, cp) self.assertEqual(list(Foo().__provides__), [IFoo]) def test___reduce__(self): from zope.interface.declarations import implementer IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") @implementer(IFoo) class Foo: pass cp = Foo.__provides__ = self._makeOne(Foo, type(Foo), IBar) self.assertEqual(cp.__reduce__(), (type(cp), (Foo, type(Foo), IBar))) class ClassProvidesStrictTests(ClassProvidesTests): # Tests that require the strict C3 resolution order. def _getTargetClass(self): ClassProvides = super()._getTargetClass() class StrictClassProvides(ClassProvides): def _do_calculate_ro(self, base_mros): return ClassProvides._do_calculate_ro( self, base_mros=base_mros, strict=True ) return StrictClassProvides def test_overlapping_interfaces_corrected(self): # Giving ClassProvides(cls, metaclass, IFace), where IFace is already # provided by metacls, doesn't produce invalid resolution orders. from zope.interface import Interface from zope.interface import implementedBy from zope.interface import implementer class IBase(Interface): pass @implementer(IBase) class metaclass(type): pass cls = metaclass( 'cls', (object,), {} ) spec = self._makeOne(cls, metaclass, IBase) self.assertEqual(spec.__sro__, ( spec, implementedBy(metaclass), IBase, implementedBy(type), implementedBy(object), Interface )) class TestClassProvidesRepr(unittest.TestCase): def _getTargetClass(self): from zope.interface.declarations import ClassProvides return ClassProvides def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test__repr__empty(self): inst = self._makeOne(type(self), type) self.assertEqual( repr(inst), "directlyProvides(TestClassProvidesRepr)" ) def test__repr__providing_one(self): from zope.interface import Interface class IFoo(Interface): "Does nothing" inst = self._makeOne(type(self), type, IFoo) self.assertEqual( repr(inst), "directlyProvides(TestClassProvidesRepr, IFoo)" ) def test__repr__duplicate_names(self): IFoo = InterfaceClass("IFoo", __module__='mod1') IFoo2 = InterfaceClass("IFoo", __module__='mod2') IBaz = InterfaceClass("IBaz") inst = self._makeOne(type(self), type, IFoo, IBaz, IFoo2) self.assertEqual( repr(inst), "directlyProvides(TestClassProvidesRepr, IFoo, IBaz, mod2.IFoo)" ) def test__repr__implementedBy(self): from zope.interface.declarations import implementedBy from zope.interface.declarations import implementer IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass inst = implementedBy(Foo) self.assertEqual( repr(inst), 'classImplements(Foo, IFoo)' ) def test__repr__implementedBy_generic_callable(self): from zope.interface.declarations import implementedBy # We can't get a __name__ by default, so we get a # module name and a question mark class Callable: def __call__(self): return self inst = implementedBy(Callable()) self.assertEqual( repr(inst), f'classImplements({__name__}.?)' ) c = Callable() c.__name__ = 'Callable' inst = implementedBy(c) self.assertEqual( repr(inst), 'classImplements(Callable)' ) class Test_directlyProvidedBy(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.declarations import directlyProvidedBy return directlyProvidedBy(*args, **kw) def test_wo_declarations_in_class_or_instance(self): class Foo: pass foo = Foo() self.assertEqual(list(self._callFUT(foo)), []) def test_w_declarations_in_class_but_not_instance(self): from zope.interface.declarations import implementer IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass foo = Foo() self.assertEqual(list(self._callFUT(foo)), []) def test_w_declarations_in_instance_but_not_class(self): from zope.interface.declarations import directlyProvides IFoo = InterfaceClass("IFoo") class Foo: pass foo = Foo() directlyProvides(foo, IFoo) self.assertEqual(list(self._callFUT(foo)), [IFoo]) def test_w_declarations_in_instance_and_class(self): from zope.interface.declarations import directlyProvides from zope.interface.declarations import implementer IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") @implementer(IFoo) class Foo: pass foo = Foo() directlyProvides(foo, IBar) self.assertEqual(list(self._callFUT(foo)), [IBar]) class Test_provider(unittest.TestCase): def _getTargetClass(self): from zope.interface.declarations import provider return provider def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_w_class(self): from zope.interface.declarations import ClassProvides IFoo = InterfaceClass("IFoo") @self._makeOne(IFoo) class Foo: pass self.assertIsInstance( Foo.__provides__, ClassProvides ) # pylint:disable=no-member self.assertEqual( list(Foo.__provides__), [IFoo] ) # pylint:disable=no-member class Test_moduleProvides(unittest.TestCase): # pylint:disable=exec-used def test_called_from_function(self): from zope.interface.declarations import moduleProvides IFoo = InterfaceClass("IFoo") globs = {'__name__': 'zope.interface.tests.foo', 'moduleProvides': moduleProvides, 'IFoo': IFoo} locs = {} CODE = "\n".join([ 'def foo():', ' moduleProvides(IFoo)' ]) exec(CODE, globs, locs) foo = locs['foo'] self.assertRaises(TypeError, foo) def test_called_from_class(self): from zope.interface.declarations import moduleProvides IFoo = InterfaceClass("IFoo") globs = {'__name__': 'zope.interface.tests.foo', 'moduleProvides': moduleProvides, 'IFoo': IFoo} locs = {} CODE = "\n".join([ 'class Foo(object):', ' moduleProvides(IFoo)', ]) with self.assertRaises(TypeError): exec(CODE, globs, locs) def test_called_once_from_module_scope(self): from zope.interface.declarations import moduleProvides IFoo = InterfaceClass("IFoo") globs = {'__name__': 'zope.interface.tests.foo', 'moduleProvides': moduleProvides, 'IFoo': IFoo} CODE = "\n".join([ 'moduleProvides(IFoo)', ]) exec(CODE, globs) spec = globs['__provides__'] self.assertEqual(list(spec), [IFoo]) def test_called_twice_from_module_scope(self): from zope.interface.declarations import moduleProvides IFoo = InterfaceClass("IFoo") globs = {'__name__': 'zope.interface.tests.foo', 'moduleProvides': moduleProvides, 'IFoo': IFoo} CODE = "\n".join([ 'moduleProvides(IFoo)', 'moduleProvides(IFoo)', ]) with self.assertRaises(TypeError): exec(CODE, globs) class Test_getObjectSpecificationFallback(unittest.TestCase): def _getFallbackClass(self): # pylint:disable=no-name-in-module from zope.interface.declarations import getObjectSpecificationFallback return getObjectSpecificationFallback _getTargetClass = _getFallbackClass def _callFUT(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_wo_existing_provides_classless(self): the_dict = {} class Foo: def __getattribute__(self, name): # Emulate object w/o any class if name == '__class__': raise AttributeError(name) try: return the_dict[name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): raise NotImplementedError() foo = Foo() spec = self._callFUT(foo) self.assertEqual(list(spec), []) def test_existing_provides_is_spec(self): from zope.interface.declarations import directlyProvides IFoo = InterfaceClass("IFoo") def foo(): raise NotImplementedError() directlyProvides(foo, IFoo) spec = self._callFUT(foo) self.assertIs(spec, foo.__provides__) # pylint:disable=no-member def test_existing_provides_is_not_spec(self): def foo(): raise NotImplementedError() foo.__provides__ = object() # not a valid spec spec = self._callFUT(foo) self.assertEqual(list(spec), []) def test_existing_provides(self): from zope.interface.declarations import directlyProvides IFoo = InterfaceClass("IFoo") class Foo: pass foo = Foo() directlyProvides(foo, IFoo) spec = self._callFUT(foo) self.assertEqual(list(spec), [IFoo]) def test_wo_provides_on_class_w_implements(self): from zope.interface.declarations import implementer IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass foo = Foo() spec = self._callFUT(foo) self.assertEqual(list(spec), [IFoo]) def test_wo_provides_on_class_wo_implements(self): class Foo: pass foo = Foo() spec = self._callFUT(foo) self.assertEqual(list(spec), []) def test_catches_only_AttributeError_on_provides(self): MissingSomeAttrs.test_raises( self, self._callFUT, expected_missing='__provides__' ) def test_catches_only_AttributeError_on_class(self): MissingSomeAttrs.test_raises( self, self._callFUT, expected_missing='__class__', __provides__=None, ) def test_raises_AttrError_w_provides_fails_type_check_AttrError(self): # isinstance(ob.__provides__, SpecificationBase) is not # protected inside any kind of block. class Foo: __provides__ = MissingSomeAttrs(AttributeError) # isinstance() ignores AttributeError on __class__ self._callFUT(Foo()) def test_raises_AttrError_w_provides_fails_type_check_RuntimeError(self): # isinstance(ob.__provides__, SpecificationBase) is not # protected inside any kind of block. class Foo: __provides__ = MissingSomeAttrs(RuntimeError) with self.assertRaises(RuntimeError) as exc: self._callFUT(Foo()) self.assertEqual('__class__', exc.exception.args[0]) class Test_getObjectSpecification(Test_getObjectSpecificationFallback, OptimizationTestMixin): # Repeat tests for C optimizations def _getTargetClass(self): from zope.interface.declarations import getObjectSpecification return getObjectSpecification class Test_providedByFallback(unittest.TestCase): def _getFallbackClass(self): # pylint:disable=no-name-in-module from zope.interface.declarations import providedByFallback return providedByFallback _getTargetClass = _getFallbackClass def _callFUT(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_wo_providedBy_on_class_wo_implements(self): class Foo: pass foo = Foo() spec = self._callFUT(foo) self.assertEqual(list(spec), []) def test_w_providedBy_valid_spec(self): from zope.interface.declarations import Provides IFoo = InterfaceClass("IFoo") class Foo: pass foo = Foo() foo.__providedBy__ = Provides(Foo, IFoo) spec = self._callFUT(foo) self.assertEqual(list(spec), [IFoo]) def test_w_providedBy_invalid_spec(self): class Foo: pass foo = Foo() foo.__providedBy__ = object() spec = self._callFUT(foo) self.assertEqual(list(spec), []) def test_w_providedBy_invalid_spec_class_w_implements(self): from zope.interface.declarations import implementer IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass foo = Foo() foo.__providedBy__ = object() spec = self._callFUT(foo) self.assertEqual(list(spec), [IFoo]) def test_w_providedBy_invalid_spec_w_provides_no_provides_on_class(self): class Foo: pass foo = Foo() foo.__providedBy__ = object() expected = foo.__provides__ = object() spec = self._callFUT(foo) self.assertIs(spec, expected) def test_w_providedBy_invalid_spec_w_provides_diff_provides_on_class(self): class Foo: pass foo = Foo() foo.__providedBy__ = object() expected = foo.__provides__ = object() Foo.__provides__ = object() spec = self._callFUT(foo) self.assertIs(spec, expected) def test_w_providedBy_invalid_spec_w_provides_same_provides_on_class(self): from zope.interface.declarations import implementer IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass foo = Foo() foo.__providedBy__ = object() foo.__provides__ = Foo.__provides__ = object() spec = self._callFUT(foo) self.assertEqual(list(spec), [IFoo]) def test_super_when_base_implements_interface(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass @implementer(IBase) class Base: pass @implementer(IDerived) class Derived(Base): pass derived = Derived() self.assertEqual(list(self._callFUT(derived)), [IDerived, IBase]) sup = super(Derived, derived) fut = self._callFUT(sup) self.assertIsNone(fut._dependents) self.assertEqual(list(fut), [IBase]) def test_super_when_base_doesnt_implement_interface(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass class Base: pass @implementer(IDerived) class Derived(Base): pass derived = Derived() self.assertEqual(list(self._callFUT(derived)), [IDerived]) sup = super(Derived, derived) self.assertEqual(list(self._callFUT(sup)), []) def test_super_when_base_is_object(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass @implementer(IDerived) class Derived: pass derived = Derived() self.assertEqual(list(self._callFUT(derived)), [IDerived]) sup = super(Derived, derived) fut = self._callFUT(sup) self.assertIsNone(fut._dependents) self.assertEqual(list(fut), []) def test_super_when_object_directly_provides(self): from zope.interface import Interface from zope.interface.declarations import directlyProvides from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass @implementer(IBase) class Base: pass class Derived(Base): pass derived = Derived() self.assertEqual(list(self._callFUT(derived)), [IBase]) directlyProvides(derived, IDerived) self.assertEqual(list(self._callFUT(derived)), [IDerived, IBase]) sup = super(Derived, derived) fut = self._callFUT(sup) self.assertIsNone(fut._dependents) self.assertEqual(list(fut), [IBase]) def test_super_multi_level_multi_inheritance(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IM1(Interface): pass class IM2(Interface): pass class IDerived(IBase): pass class IUnrelated(Interface): pass @implementer(IBase) class Base: pass @implementer(IM1) class M1(Base): pass @implementer(IM2) class M2(Base): pass @implementer(IDerived, IUnrelated) class Derived(M1, M2): pass d = Derived() sd = super(Derived, d) sm1 = super(M1, d) sm2 = super(M2, d) self.assertEqual(list(self._callFUT(d)), [IDerived, IUnrelated, IM1, IBase, IM2]) self.assertEqual(list(self._callFUT(sd)), [IM1, IBase, IM2]) self.assertEqual(list(self._callFUT(sm1)), [IM2, IBase]) self.assertEqual(list(self._callFUT(sm2)), [IBase]) def test_catches_only_AttributeError_on_providedBy(self): MissingSomeAttrs.test_raises(self, self._callFUT, expected_missing='__providedBy__', __class__=object) def test_catches_only_AttributeError_on_class(self): # isinstance() tries to get the __class__, which is non-obvious, # so it must be protected too. MissingSomeAttrs.test_raises( self, self._callFUT, expected_missing='__class__') class Test_providedBy(Test_providedByFallback, OptimizationTestMixin): # Repeat tests for C optimizations def _getTargetClass(self): from zope.interface.declarations import providedBy return providedBy class ObjectSpecificationDescriptorFallbackTests(unittest.TestCase): def _getFallbackClass(self): # pylint:disable=no-name-in-module from zope.interface.declarations import \ ObjectSpecificationDescriptorFallback return ObjectSpecificationDescriptorFallback _getTargetClass = _getFallbackClass def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_accessed_via_class(self): from zope.interface.declarations import Provides IFoo = InterfaceClass("IFoo") class Foo: pass Foo.__provides__ = Provides(Foo, IFoo) Foo.__providedBy__ = self._makeOne() self.assertEqual(list(Foo.__providedBy__), [IFoo]) def test_accessed_via_inst_wo_provides(self): from zope.interface.declarations import Provides from zope.interface.declarations import implementer IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") @implementer(IFoo) class Foo: pass Foo.__provides__ = Provides(Foo, IBar) Foo.__providedBy__ = self._makeOne() foo = Foo() self.assertEqual(list(foo.__providedBy__), [IFoo]) def test_accessed_via_inst_w_provides(self): from zope.interface.declarations import Provides from zope.interface.declarations import directlyProvides from zope.interface.declarations import implementer IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") IBaz = InterfaceClass("IBaz") @implementer(IFoo) class Foo: pass Foo.__provides__ = Provides(Foo, IBar) Foo.__providedBy__ = self._makeOne() foo = Foo() directlyProvides(foo, IBaz) self.assertEqual(list(foo.__providedBy__), [IBaz, IFoo]) def test_arbitrary_exception_accessing_provides_not_caught(self): class MyException(Exception): pass class Foo: __providedBy__ = self._makeOne() @property def __provides__(self): raise MyException foo = Foo() with self.assertRaises(MyException): getattr(foo, '__providedBy__') def test_AttributeError_accessing_provides_caught(self): class MyException(Exception): pass class Foo: __providedBy__ = self._makeOne() @property def __provides__(self): raise AttributeError foo = Foo() provided = getattr(foo, '__providedBy__') self.assertIsNotNone(provided) def test_None_in__provides__overrides(self): from zope.interface import Interface from zope.interface import implementer class IFoo(Interface): pass @implementer(IFoo) class Foo: @property def __provides__(self): return None Foo.__providedBy__ = self._makeOne() provided = getattr(Foo(), '__providedBy__') self.assertIsNone(provided) class ObjectSpecificationDescriptorTests( ObjectSpecificationDescriptorFallbackTests, OptimizationTestMixin, SubclassableMixin, ): # Repeat tests for C optimizations def _getTargetClass(self): from zope.interface.declarations import ObjectSpecificationDescriptor return ObjectSpecificationDescriptor # Test _normalizeargs through its callers. class _Monkey: # context-manager for replacing module names in the scope of a test. def __init__(self, module, **kw): self.module = module self.to_restore = {key: getattr(module, key) for key in kw} for key, value in kw.items(): setattr(module, key, value) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): for key, value in self.to_restore.items(): setattr(self.module, key, value) class _MonkeyDict: # context-manager for restoring a dict w/in a module in the scope of a # test. def __init__(self, module, attrname, **kw): self.module = module self.target = getattr(module, attrname) self.to_restore = self.target.copy() self.target.clear() self.target.update(kw) def __enter__(self): return self.target def __exit__(self, exc_type, exc_val, exc_tb): self.target.clear() self.target.update(self.to_restore)