Behaviour-based interface/protocol implementation?

A

Alan Franzoni

Hello,
I'd like to have a system which lets me do certain actions if the
duck-type of a certain objects matches what I expect, i.e. I'd like to
have a formalization of what it's sometimes done through getattr()
calls:

if getattr(myobj, "somemethod", None) is not None:
myobj.somemethod(somevalue)


The above approach is sometimes too naive, because a) clutters code
with many getattr calls and ifs b) I might want to check for multiple
attributes and c) only checks for the method name and not for the
method's signature.

After looking at PyProtocols, zope.interface and python's own abc
module, I'm left with a doubt: does any behaviour-based "interface
testing" system exist for Python?


I mean:
all these three libraries use a register-based or inheritance-based
approach; in abc, if I want instances of a class of mine "FooClass" to
be "BarInterface" instances, I can either a) inherit from BarInterface
or b) run "BarInterface.register(FooClass)".

This poses some issues in a purely duck-typed context, IMHO. If an
object perfectly satisfies the "BarInterface" signature, but it
doesn't inherit from MyInterface and its class wasn't registered as a
BarInterface implementor, the check "isinstance(myobj, BarInterface)"
will yield a False result.

This might not be a big deal if a) python >= 2.6 is used b) just
checking for builtin interfaces - e.g. those defined in the
"collections" module is required and c) you just require checking on
basic types or on python builtin types (which correctly register
builtin ABCs).

What happens if I define my own ABC for my own purpose? There might be
builtin objects, or third party libraries, which already offer objects
that satisfy such interface, but I'd need to import such modules and
register such classes as implementing my ABC, which is suboptimal.

What I'd like to do is:

class MyType(object):
def someMethod(self, a, b):
pass

def otherMethod(self):
pass

class OtherType(object):
def someMethod(self):
pass

def otherMethod(self):
pass


@DuckType
class MyDuckType(object):
def someMethod(self, a, b):
pass

def otherMethod(self):
pass

class TestDuckTypes(TestCase):
def test_mytype_is_compatible_with_ducktype(self):
myobj = MyType()
self.assertEquals(True, MyDuckType.maybe_implemented_by(myobj))

def test_othertype_is_not_compatible_with_ducktype(self):
myobj = OtherType()
self.assertEquals(False, MyDuckType.maybe_implemented_by(myobj))




I'd like to do a kind of runtime-check for signatures. Of course there
couldn't be an absolute certainty of interface implementation, because
a runtime dynamic proxy method (accepting *args and **kwargs in its
signature, as an example) might just fool my signature check.

So, my questions are:

a) does anything like that already exist in the python ecosystem?
b) can anybody see any flaw either in what I'd like to do ("you
shouldn't do that because...") or in the way I want to do it ("It
won't work because...")
 
C

Carl Banks

Hello,
I'd like to have a system which lets me do certain actions if the
duck-type of a certain objects matches what I expect, i.e. I'd like to
have a formalization of what it's sometimes done through getattr()
calls:

if getattr(myobj, "somemethod", None) is not None:
    myobj.somemethod(somevalue)

The above approach is sometimes too naive, because a) clutters code
with many getattr calls and ifs b) I might want to check for multiple
attributes and c) only checks for the method name and not for the
method's signature.

Write some kind of signature proxy to do it.

class SomeSignature(object):
def __init__(self,target):
self.target = target
def somemethod(self,value):
if hasattr(self.target,"somemethod"):
self.target.somemethod(value)

SomeSignature(myobj).somemethod(somevalue)

Generalizing the proxy to easily accommodate all kinds of signatures
to your heart's delight left as an exercise (and it's likley to
involve metaclass programming).
After looking at PyProtocols, zope.interface and python's own abc
module, I'm left with a doubt: does any behaviour-based "interface
testing" system exist for Python?

Based on this thread, you have quite specific requirements, so it's
doubtful someone else has implemented exactly what you want.


And because it hasn't been mentioned in this thread yet--surprisingly--
many people in Python prefer the EAFP strategy, "Easier to Ask
Forgiveness than Permission", that is, to just do it and handle the
resulting exception:

try:
myobj.somemethod(somevalue)
except AttributeError:
# doesn't fit signature, so do nothing
pass

Personally, I have my doubts about the method in general. It's
definitely preferrable in many cases (like opening a file, where it
avoids a race condition) but for something like this I don't see it
working out too well. But I'm just throwing it out there.


Carl Banks
 
A

Alan Franzoni

Write some kind of signature proxy to do it.

I don't have a specific implementation idea yet, I see how that grows.
Based on this thread, you have quite specific requirements, so it's
doubtful someone else has implemented exactly what you want.

Yes, but asking is better than blinding reimplementing :)
And because it hasn't been mentioned in this thread yet--surprisingly--
many people in Python prefer the EAFP strategy, "Easier to Ask
Forgiveness than Permission", that is, to just do it and handle the
resulting exception:

try:
   myobj.somemethod(somevalue)
except AttributeError:
   # doesn't fit signature, so do nothing
   pass

Sure. That's an approach. But this has drawbacks.

- it violates the CQS principle:
http://en.wikipedia.org/wiki/Command-query_separation

- Maybe my interface has not just a single method, and I might want to
call multiple methods on my object. I need to check for all signatures
before proceeding.

- When calling the method, if an exception is raised - either
AttributeError or TypeError most of the times - it could not be
generated from my own call, but from a call deeper into the stack;
It's easy to just think "that object doesn't support that interface" I
could mistake an object for not supporting an interface, and I could
silently swallow a true runtime exception instead. Maybe some stack
analysis could be performed, but I'd prefer to check as much as I can
*before* calling.

- Sometimes I'd like to group objects depending on their *behaviour*
(not their type), and then feed them somewhere else without actually
calling their methods. If I can know their behaviour just after
they've been called, it might be just too late.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top