python-interface icon indicating copy to clipboard operation
python-interface copied to clipboard

Check if interface is implemented

Open ppwfx opened this issue 6 years ago • 11 comments

nice module! is there a way to check at runtime if an interface is implemented by a class / instace?

ppwfx avatar Nov 08 '17 15:11 ppwfx

Hi @21stio. Thanks for your interest in inferface!

There isn't currently a public API for checking if an object/class implements an interface. The closest thing is Interface.verify(type), which is what gets called at class creation time when you declare that you implement an interface, but that function is fairly expensive when it fails, because it spends a lot of effort building nice error messages.

I think it makes sense to add a function that just gives back a boolean indicating whether an object/type implements an interface (though I confess I haven't actually needed such a function yet in using interface, which is why it currently doesn't exist). What would you expect the behavior of such a function to be? If we added a function named something like check_implements (actual name TBD), I can think of a few potential axes of variation.

  1. Would check_implements(obj, IFace) expect obj to be a type or an instance? Would we want separate methods for objects and types (analogous to the built-in isinstance/issubclass functions)?
  2. Would check_implements just verify that the object it receives was declared as implementing IFace, or would it dynamically verify that the interface is actually implemented? The former would likely be much faster, but I could imagine use-cases for both behaviors.

ssanderson avatar Nov 14 '17 17:11 ssanderson

This module makes perfect sense! I too would really like that boolean check method. My suggestion:

  1. instance_implements(obj, IFace) and class_implements(cls, IFace) (analogous to the built-in isinstance/issubclass functions)
  2. Since the actual implementation is checked at class creation, wouldn't it suffice to do the former check, i.e. check that it was declared as implementing IFace?

dremok avatar Feb 13 '18 12:02 dremok

I just ended up using classes with methods that throw NotImplemented when not being overwritten. This validation only happens when the method is called tho.

Like this you have full is_instance support, ides let you override methods etc.

just my 2 cents

ppwfx avatar Feb 13 '18 14:02 ppwfx

Since the actual implementation is checked at class creation, wouldn't it suffice to do the former check, i.e. check that it was declared as implementing IFace?

That would work in most cases, but it wouldn't work if you dynamically added or removed interface methods from an object's type after the type was created (class decorators like the new dataclass decorator in the stdlib often do this, for example).

ssanderson avatar Feb 13 '18 17:02 ssanderson

@dremok @21stio I'd be curious to see if you have concrete examples where you think this functionality would be useful. My general belief is that idiomatic usage of interface shouldn't require runtime interface checks. My usage pattern usually looks like:

class SomeInterface(Interface):
    pass

class SomeImplementation(implements(SomeInterface)):
    ....

class SomeOtherImplementation(implements(SomeInterface)):
    ...

def some_function(arg):  # expects arg to implement SomeInterface
    # Call interface methods on arg.

For this kind of usage, the only thing I could imagine wanting to do with instance_implements and class_implements is use them for assertions, but it's generally more idiomatic to just try to use the object and fail with an AttributeError/TypeError if the received object is incorrect.

ssanderson avatar Feb 13 '18 17:02 ssanderson

@ssanderson Ah, you're right about dynamically added methods, didn't think about that.

Assertion is exactly what I was planning on using these methods for. I have a unit test which calls a method which returns a list of stuff, where the objects are implementing different interfaces. Then I wanted to do different assertions depending on the interface type. But I guess I should really redesign, or just ask for forgiveness instead of permission...

dremok avatar Feb 14 '18 11:02 dremok

that's exactly the same use case I had

ppwfx avatar Feb 14 '18 12:02 ppwfx

Hi @21stio. Thanks for your interest in inferface!

There isn't currently a public API for checking if an object/class implements an interface. The closest thing is Interface.verify(type), which is what gets called at class creation time when you declare that you implement an interface, but that function is fairly expensive when it fails, because it spends a lot of effort building nice error messages.

I think it makes sense to add a function that just gives back a boolean indicating whether an object/type implements an interface (though I confess I haven't actually needed such a function yet in using interface, which is why it currently doesn't exist). What would you expect the behavior of such a function to be? If we added a function named something like check_implements (actual name TBD), I can think of a few potential axes of variation.

  1. Would check_implements(obj, IFace) expect obj to be a type or an instance? Would we want separate methods for objects and types (analogous to the built-in isinstance/issubclass functions)?
  2. Would check_implements just verify that the object it receives was declared as implementing IFace, or would it dynamically verify that the interface is actually implemented? The former would likely be much faster, but I could imagine use-cases for both behaviors.

I was actually expecting that the standard isinstance function would work for checking if an object implements a certain interface

FastGeert avatar Jun 26 '19 13:06 FastGeert

Hi,

Am I reviving a dead thread? Hope @ssanderson is still around to react.

In any case, one example I can come up with that does require such a checking method: when external code that wasn't written using your Interface needs to be checked if it has the necessary methods. Imagine some public protocol that has several implementations that need to work together - your code may use Interface but someone else's code doesn't. This is pretty straightforward with the subclasshook/check that's used with abc.

BtW, I really like your implementation of interfaces. I was not satisfied with abc and kept thinking why a better implementation didn't exist, then one day I searched around a bit and found this project. Awesome!

zEdS15B3GCwq avatar Feb 19 '20 01:02 zEdS15B3GCwq

In any case, one example I can come up with that does require such a checking method: when external code that wasn't written using your Interface needs to be checked if it has the necessary methods. Imagine some public protocol that has several implementations that need to work together - your code may use Interface but someone else's code doesn't. This is pretty straightforward with the subclasshook/check that's used with abc.

I think checking implementations on types that are outside your control is a reasonable use-case. Something like the following seems reasonable to me, for example:

class MyInterface(Interface):
    # Declare methods

class MyImpl(implements(MyInterface)):
    # Implement methods.

from not_my_module import OtherImpl

# Raises a TypeError if OtherImpl isn't a valid implementation of MyInterface.
expect_type_implements(OtherImpl, MyInterface)

I'm less confident that runtime checking on instances is a reasonable thing to support, e.g.:

class MyInterface(Interface):
    ...

def my_function(obj):
    """``obj`` must implement MyInterface
    """
    expect_instance_implements(obj, MyInterface)
    # Use interface methods.

ssanderson avatar Feb 19 '20 19:02 ssanderson

BtW, I really like your implementation of interfaces.

Thanks!

ssanderson avatar Feb 19 '20 19:02 ssanderson