I am trying something fairly simple. I want to write a decorator which turns a class method in an event handler using pydispatcher. Here is my down-to-the-essentials approach:
from pydispatch.dispatcher import connect,send,Any
SIGNAL = "test"
def triggered(func):
connect(func,signal=SIGNAL,sender = Any)
return func
class P(object):
@triggered
def a(self,*args,**kwargs):
print "yay"
if __name__ == "__main__":
p = P()
send(signal=SIGNAL,sender="")
This throws the error:
Traceback (most recent call last):
File "util.py", line 35, in <module>
send(signal=SIGNAL,sender="")
File "/usr/local/lib/python2.7/dist-packages/pydispatch/dispatcher.py", line 338, in send
**named
File "/usr/local/lib/python2.7/dist-packages/pydispatch/robustapply.py", line 55, in robustApply
return receiver(*arguments, **named)
TypeError: a() takes at least 1 argument (0 given)
Any ideas?
You registered a function in a class. This means that just the function is registered, not a bound method. Functions are not bound to an instance, so no self
argument is being passed in.
You cannot hope to use the decorator on regular methods in a class definition, because the required context (an instance) is not available at that time.
You'd have to create an instance first, then register the method bound to that instance for this to work:
class P(object):
def a(self, *args, **kwargs):
print "yay"
if __name__ == "__main__":
p = P()
triggered(p.a)
send(signal=SIGNAL,sender="")
Now triggered()
registers the p.a
bound method; calling that method will then pass in p
as the self
argument when called.
You could alternatively make the a
method a static or class method. A static method is essentially just a function; it never takes a self
argument. This means you can register it as a signal handler, but you'll never get the benefits of using a class:
class P(object):
@staticmethod
@triggered
def a(*args, **kwargs):
print "yay"
If you make it a classmethod, you'll get the class passed in (still not an instance), and you'll have to do the registering outside the class to ensure you get a bound method:
class P(object):
@classmethod
def a(cls, *args, **kwargs):
print "yay"
triggered(P.a)