def testInterfaceCheckRequiresInterface() -> None: class M3: def m3(self, *args, **kwargs): "" with pytest.raises(InterfaceError): AssertImplements(M3(), M3) # type:ignore[arg-type] with pytest.raises(InterfaceError): IsImplementation(M3(), M3) # type:ignore[arg-type] assert IsImplementation(M3(), _InterfM3, requires_declaration=False) assert not IsImplementation(M3(), _InterfM3, requires_declaration=True) with pytest.raises(AssertionError): AssertImplements(M3(), _InterfM3, requires_declaration=True)
def __eq__(self, other: Any) -> bool: if not IsImplementation(other, IUnitSystem): return False return (self.GetId() == other.GetId() and self.GetCaption() == other.GetCaption() and self.GetUnitsMapping() == other.GetUnitsMapping() and self.IsReadOnly() == other.IsReadOnly())
def testDeclareClassImplements() -> None: class I1(Interface): def M1(self): "" class I2(Interface): def M2(self): "" class C0: "" class C1: def M1(self): "" class C2: def M2(self): "" @ImplementsInterface(I2) class C2B: def M2(self): "" class C12B(C1, C2B): "" with pytest.raises(AssertionError): DeclareClassImplements(C0, I1) assert IsImplementation(C1, I1, requires_declaration=False) == True DeclareClassImplements(C1, I1) assert IsImplementation(C1, I1) == True AssertImplements(C1, I1) # C1 is parent of C12B, and, above, it was declared that C1 implements I1, # so C12B should automatically implement I1. assert IsImplementation(C12B, I1) == True # automatic assert (IsImplementation(C12B, I2) == True ) # inheritance for Implements still works automatically DeclareClassImplements(C12B, I1) assert IsImplementation(C12B, I1) == True # now it implements assert IsImplementation(C12B, I2) == True DeclareClassImplements(C2, I2) assert IsImplementation(C2, I2) == True AssertImplements(C2, I2) # Exception: if I define a class *after* using DeclareClassImplements in the base, it works: class C12(C1, C2): "" AssertImplements(C12, I1) AssertImplements(C12, I2)
def testClassImplementMethod() -> None: """ Tests replacing a method that implements an interface with a class. The class must be derived from "Method" in order to be accepted as a valid implementation. """ @ImplementsInterface(_InterfM1) class My: def m1(self): "" class MyRightMethod(Method): def __call__(self): "" class MyWrongMethod: def __call__(self): "" # NOTE: It doesn't matter runtime modifications in the instance, what is really being tested # is the *class* of the object (My) is what is really being tested. m = My() m.m1 = MyWrongMethod() # type:ignore[assignment] assert IsImplementation(m, _InterfM1) == True m.m1 = MyRightMethod() # type:ignore[assignment] assert IsImplementation(m, _InterfM1) == True # NOTE: Testing behaviour of private methods here. from oop_ext.interface._interface import _IsImplementationFullChecking m = My() m.m1 = MyWrongMethod() # type:ignore[assignment] assert not _IsImplementationFullChecking(m, _InterfM1) m.m1 = MyRightMethod() # type:ignore[assignment] assert _IsImplementationFullChecking(m, _InterfM1) del m.m1 assert IsImplementation(m, _InterfM1) == True
def testIsImplementationWithSubclasses() -> None: """ Checks if the IsImplementation method works with subclasses interfaces. Given that an interface I2 inherits from I1 of a given object declared that it implements I2 then it is implicitly declaring that implements I1. """ @ImplementsInterface(_InterfM2) class My2: def m2(self): "" @ImplementsInterface(_InterfM3) class My3: def m3(self, arg1, arg2): "" @ImplementsInterface(_InterfM4) class My4: def m3(self, arg1, arg2): "" def m4(self): "" m2 = My2() m3 = My3() m4 = My4() # My2 assert IsImplementation(m2, _InterfM3) == False # My3 assert IsImplementation(m3, _InterfM3) == True assert IsImplementation(m3, _InterfM4) == False # My4 assert IsImplementation(m4, _InterfM4) == True assert IsImplementation(m4, _InterfM3) == True # When wrapped in an m4 interface it should still accept m3 as a declared interface wrapped_intef_m4 = _InterfM4(m4) assert IsImplementation(wrapped_intef_m4, _InterfM4) == True assert IsImplementation(wrapped_intef_m4, _InterfM3) == True
def testBasics() -> None: class I(Interface): def foo(self, a, b=None): "" def bar(self): "" @ImplementsInterface(I) class C: def foo(self, a, b=None): "" def bar(self): "" class C2: def foo(self, a, b=None): "" def bar(self): "" class D: "" assert IsImplementation(I(C()), I) == True # OK assert IsImplementation(C, I) == True # OK # C2 shouldn't need to declare `@ImplementsInterface(I)` assert IsImplementation(C2, I, requires_declaration=False) == True assert IsImplementation(C2, I, requires_declaration=True) == False assert not IsImplementation(D, I) == True # nope assert I(C) is C # type:ignore[comparison-overlap] assert I(C2) is C2 # type:ignore[comparison-overlap] with pytest.raises(InterfaceError): I() with pytest.raises(BadImplementationError): I(D) # Now declare that C2 implements I DeclareClassImplements(C2, I) assert (IsImplementation(C2, I, requires_declaration=True) == True ) # Even if it doesn't declare
def testImplementsInterfaceAsBoolError() -> None: """ Test if the common erroneous use of interface.ImplementsInterface() instead of interface.IsImplementation() to test if an object implements an interface correctly raises a RuntimeError. """ class I1(Interface): def M1(self): pass @ImplementsInterface(I1) class C1: def M1(self): pass obj = C1() assert IsImplementation(obj, I1) with pytest.raises(RuntimeError): if ImplementsInterface(obj, I1): # type:ignore[arg-type] pytest.fail('Managed to test "if ImplementsInterface(obj, I1):"')
def testIsImplementationWithBuiltInObjects() -> None: my_number = 10 assert IsImplementation(my_number, _InterfM1) == False
def testAttributes() -> None: class IZoo(Interface): zoo = Attribute(int) class I(Interface): foo = Attribute(int) bar = Attribute(str) foobar = Attribute(int, None) a_zoo = Attribute(IZoo) @ImplementsInterface(IZoo) class Zoo: pass # NOTE: This class 'C' doesn't REALLY implements 'I', although it says so. The problem is # that there's a flaw with attributes *not being checked*. # In fact: Attributes should not be in the (Abstract) properties COULD be in # the interface, but they SHOULD NOT be type-checked (because it involves a # getter call, and this affects runtime behaviour). # This should be reviewed later. @ImplementsInterface(I) class C: pass c1 = C() c1.foo = 10 # type:ignore[attr-defined] c1.bar = "hello" # type:ignore[attr-defined] c1.foobar = 20 # type:ignore[attr-defined] a_zoo = Zoo() a_zoo.zoo = 99 # type:ignore[attr-defined] c1.a_zoo = a_zoo # type:ignore[attr-defined] c2 = C() assert IsImplementation(C, I) == True assert IsImplementation(c1, I) == True assert IsImplementation(c2, I) == True # NOTE: Testing private methods here # If we do a deprecated "full check", then its behaviour is a little bit more correct. from oop_ext.interface._interface import _IsImplementationFullChecking assert not _IsImplementationFullChecking( C, I) == True # only works with instances assert _IsImplementationFullChecking(c1, I) == True # OK, has attributes assert (not _IsImplementationFullChecking(c2, I) == True ) # not all the attributes necessary # must not be true if including an object that doesn't implement IZoo interface expected for # a_zoo attribute c1.a_zoo = "wrong" # type:ignore[attr-defined] assert not _IsImplementationFullChecking( c1, I) == True # failed, invalid attr type c1.a_zoo = a_zoo # type:ignore[attr-defined] # test if we can set foobar to None c1.foobar = None # type:ignore[attr-defined] assert IsImplementation(c1, I) == True # OK c1.foobar = "hello" # type:ignore[attr-defined] assert not _IsImplementationFullChecking( c1, I) == True # failed, invalid attr type