Python: Using API Event Handlers with OOP - python

I am trying to build some UI panels for an Eclipse based tool. The API for the tool has a mechanism for event handling based on decorators, so for example, the following ties callbackOpen to the opening of a_panel_object:
#panelOpenHandler(a_panel_object)
def callbackOpen(event):
print "opening HERE!!"
This works fine, but I wanted to wrap all of my event handlers and actual data processing for the panel behind a class. Ideally I would like to do something like:
class Test(object):
def __init__(self):
# initialise some data here
#panelOpenHandler(a_panel_object)
def callbackOpen(self, event):
print "opening HERE!!"
But this doesn't work, I think probably because I am giving it a callback that takes both self and event, when the decorator is only supplying event when it calls the function internally (note: I have no access to source code on panelOpenHandler, and it is not very well documented...also, any error messages are getting swallowed by Eclipse / jython somewhere).
Is there any way that I can use a library decorator that provides one argument to the function being decorated on a function that takes more than one argument? Can I use lambdas in some way to bind the self argument and make it implicit?
I've tried to incorporate some variation of the approaches here and here, but I don't think that it's quite the same problem.

Your decorator apparently registers a function to be called later. As such, it's completely inappropriate for use on a class method, since it will have no idea of which instance of the class to invoke the method on.
The only way you'd be able to do this would be to manually register a bound method from a particular class instance - this cannot be done using the decorator syntax. For example, put this somewhere after the definition of your class:
panelOpenHandler(context.controls.PerformanceTuneDemoPanel)(Test().callbackOpen)

I found a work around for this problem. I'm not sure if there is a more elegant solution, but basically the problem boiled down to having to expose a callback function to global() scope, and then decorate it with the API decorator using f()(g) syntax.
Therefore, I wrote a base class (CallbackRegisterer), which offers the bindHandler() method to any derived classes - this method wraps a function and gives it a unique id per instance of CallbackRegisterer (I am opening a number of UI Panels at the same time):
class CallbackRegisterer(object):
__count = 0
#classmethod
def _instanceCounter(cls):
CallbackRegisterer.__count += 1
return CallbackRegisterer.__count
def __init__(self):
"""
Constructor
#param eq_instance 0=playback 1=record 2=sidetone.
"""
self._id = self._instanceCounter()
print "instantiating #%d instance of %s" % (self._id, self._getClassName())
def bindHandler(self, ui_element, callback, callback_args = [], handler_type = None,
initialize = False, forward_event_args = False, handler_id = None):
proxy = lambda *args: self._handlerProxy(callback, args, callback_args, forward_event_args)
handler_name = callback.__name__ + "_" + str(self._id)
if handler_id is not None:
handler_name += "_" + str(handler_id)
globals()[handler_name] = proxy
# print "handler_name: %s" % handler_name
handler_type(ui_element)(proxy)
if initialize:
proxy()
def _handlerProxy(self, callback, event_args, callback_args, forward_event_args):
try:
if forward_event_args:
new_args = [x for x in event_args]
new_args.extend(callback_args)
callback(*new_args)
else:
callback(*callback_args)
except:
print "exception in callback???"
self.log.exception('In event callback')
raise
def _getClassName(self):
return self.__class__.__name__
I can then derive a class from this and pass in my callback, which will be correctly decorated using the API decorator:
class Panel(CallbackRegisterer):
def __init__(self):
super(Panel, self).__init__()
# can bind from sub classes of Panel as well - different class name in handle_name
self.bindHandler(self.controls.test_button, self._testButtonCB, handler_type = valueChangeHandler)
# can bind multiple versions of same function for repeated ui elements, etc.
for idx in range(0, 10):
self.bindHandler(self.controls["check_box_"+str(idx)], self._testCheckBoxCB,
callback_args = [idx], handler_type = valueChangeHandler, handler_id = idx)
def _testCheckBoxCB(self, *args):
check_box_id = args[0]
print "in _testCheckBoxCB #%d" % check_box_id
def _testButtonCB(self):
"""
Handler for test button
"""
print "in _testButtonCB"
panel = Panel()
Note, that I can also derive further sub-classes from Panel, and any callbacks bound there will get their own unique handler_name, based on class name string.

Related

Instance sharing across classes

I have this python script which has a listener class and a main class. A serial stream is created in the main class and a listener instance is created in the main class. The whole purpose of the listener is to send a message on the serial port when a listened property has changed. The problem is that the listener doesn't have access to the output stream created in the main class. The listener does an abrupt return when trying to execute the outputStream.write statement How can I give the listener access to the output stream?
import purejavacomm
import java.beansSensorListener
class MyListener(java.beans.PropertyChangeListener):
def propertyChange(self, event):
if (< some property has changed >) :
self.outputStream.write(message) # send notice on serial port
return
class MainClass(jmri.jmrit.automat.AbstractAutomaton) :
def __init__(self) :
self.portID = purejavacomm.CommPortIdentifier.getPortIdentifier("COM3")
self.port = self.portID.open("SerialCom", 50)
self.outputStream = self.port.getOutputStream()
return
def init(self) :
myListener = MyListener()
deviceList = devices.getNamedBeanSet()
for device in deviceList :
device.addPropertyChangeListener(myListener)
return
a = MainClass()
a.start();
It seems that really MyListener should either have a stream passed into the constructor or the callback (using lambda expression).
First, which is probably the cleanest way, would look like this:
def init(self) :
myListener = MyListener(self.outputStream)
Second version: device.addPropertyChangeListener(lambda x: myListener(x, self.outputStream)) Both need changes in the MyListener class to either constructor, or definition of propertyChange.
If you are set of sharing an instance of here are two options.
If you can modify arguments to MyListener constructor you pass in instance of MainClass to it:
def init(self) :
myListener = MyListener(self)
If not, a little bit more hacky way of doing this is by making MainClass a singelton (see example here), and then calling it's constructor within MyListener

Using objects as functions

I'm going through a Python OOPs book by Dusty Phillips. I fail to understand a particular program in the book, chapter 7 - Python Object-oriented Shortcuts. The extended version of the code is available here
Although the program comes under the topic Functions are objects too, the provided program also uses a strange code, which i feel, more of imply the opposite (using objects as functions).
I have pointed out the line in the code where i have the doubt. How is that variable callback of TimedEvent used like a function Timer class ? What is going on here in this part.
import datetime
import time
class TimedEvent:
def __init__(self, endtime, callback):
self.endtime = endtime
self.callback = callback
def ready(self):
return self.endtime <= datetime.datetime.now()
class Timer:
def __init__(self):
self.events = []
def call_after(self, delay, callback):
end_time = datetime.datetime.now() + \
datetime.timedelta(seconds=delay)
self.events.append(TimedEvent(end_time, callback))
def run(self):
while True:
ready_events = (e for e in self.events if e.ready())
for event in ready_events:
event.callback(self) ----------------> Doubt
self.events.remove(event)
time.sleep(0.5)
Both are true
functions are objects: do a dir(f) on a function to view its attributes
objects can be used as functions: just add __call__(self, ...) method and use the object like a function.
In general things that can be called using a syntax like whatever(x, y, z) are called callables.
What the example is trying to show is that methods are just object attributes that are also callables. Just like you can write obj.x = 5, you can also write obj.f = some_function.
Yes, that example indeed shows that functions are object. You can assign an object to the callback attribute and this tries to show that the object you assign to callback can be a function.
class TimedEvent:
def __init__(self, endtime, callback):
self.endtime = endtime
self.callback = callback
What is missing to make it clear is the initialization. You could, for example, do this:
def print_current_time():
print(datetime.datetime.now().isoformat())
event = TimedEvent(endtime, print_current_time)
event.callback()
This will actually call print_current_time, because event.callback is print_current_time.

Python: Passing a class member function to another class's callback

Can I pass class A into class B so that B can run a callback using A's member function?
I am trying to write a Python leg class for a robot I am building. I am using a Raspberry Pi as the main computer, and Martin O'Hanlon's KY040 rotary encoder library KY040 to detect each 1/4 rotation of the leg. To this end, I watch for the first of several clicks, sleep for a short time, stop the servo, and now a 1/4 rotation has been achieved. In standalone, unthreaded code this works fine, but creating a class has been a challenge.
Details:
A threaded sentinel loop watches a boolean (quarterTurn) to signal that a rotation must be carried out.
def run(self):
print "leg running"
while self._running:
sleep(.0001)
if self.quarterTurn:
print "quarterTurn is: " + str(self.quarterTurn)
self.qTurn(self.quarterCount)
qTurn accesses a pwm controller to activate the motors, and reset quarterTurn to false.
def qTurn(self, quarters):
count = 0
while count < quarters:
sleep(.0001)
self.setMotor(self.maxPulse)
if self.ClickedOnce:
count = count + 1
sleep(.17)
self.parkMotor()
sleep(.04)
self.clickedOnce = False
self.quarterTurn = False
The trick is that O'Hanlon's class is already threaded. On one hand, it is convenient, on the other, it makes my class more complex. The KY040 makes use of a callback function to provide feedback, but using this within my class is the source of my trouble.
I need the callback to modify a a boolean in my leg class, but this function is only called by the KY040 class, which tries to pass itself into the function.
def rotaryChange(self, pin):
self.clickedOnce = True
Since the code is open source (thank you, O'Hanlon), I thought I could modify the constructor of the KY040 to let me pass my leg class into it, so that I could modify the correct data.
O'Hanlon's Original Constructor:
def __init__(self, clockPin, dataPin, switchPin=None, rotaryCallback=None, switchCallback=None,rotaryBouncetime=250, switchBouncetime=300):
# persist values
self.clockPin = clockPin
self.dataPin = dataPin
self.switchPin = switchPin
self.rotaryCallback = rotaryCallback
self.switchCallback = switchCallback
self.rotaryBouncetime = rotaryBouncetime
self.switchBouncetime = switchBouncetime
#setup pins
GPIO.setup(clockPin, GPIO.IN)
GPIO.setup(dataPin, GPIO.IN)
if None != self.switchPin:
GPIO.setup(switchPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
I added a "host" variable, into which I pass the leg class:
def __init__(self, clockPin, dataPin, switchPin=None, rotaryCallback=None, switchCallback=None, host=None, rotaryBouncetime=250, switchBouncetime=300):
# persist values
self.clockPin = clockPin
self.dataPin = dataPin
self.switchPin = switchPin
self.rotaryCallback = rotaryCallback
self.switchCallback = switchCallback
self.rotaryBouncetime = rotaryBouncetime
self.switchBouncetime = switchBouncetime
# My Change
self.host = host
#setup pins
GPIO.setup(clockPin, GPIO.IN)
GPIO.setup(dataPin, GPIO.IN)
if None != self.switchPin:
GPIO.setup(switchPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
The modified constructor would be called like so:
self.encoder = KY040(self.clockPin, self.dataPin, rotaryCallback=self.rotaryChange, host=self)
O'Hanlon's callback now passes the host along:
def _clockCallback(self, pin):
# My change
self.rotaryCallback(pin, self.host)
My new callback:
def rotaryChange(pin, host):
host.clickedOnce = True
Unfortunately, after making sure the modified code is installed with the setup script, it doesn't seem to acknowledge my new additions. I run my program and receive the follwing error:
Traceback (most recent call last):
File "ctf.py", line 18, in <module>
LR = leg.leg(lr_chan, lr_max, lr_park, lr_clk, lr_data);
File "/home/[user]/hexacrescentapod/leg.py", line 47, in __init__
self.encoder = KY040(self.clockPin, self.dataPin,
rotaryCallback=self.rotaryChange, host=self)
TypeError: __init__() got an unexpected keyword argument 'host'
This is a little confusing because of your wording. Are you actually trying to pass a class in as you say, or an instance of that class as you seem to be doing? Which class is rotaryChange defined in?
Anyway, it looks like what you're actually trying to do is pass self.rotaryChange as a callback.
This already works, without any changes. self.rotaryChange is a bound method, meaning it knows what that self was when it was created, and will pass it when it's called. This may be easier to see with an example:
>>> class Spam:
... def eggs(self):
... pass
>>> spam = Spam()
>>> spam
<__main__.Spam at 0x119947630>
>>> spam.eggs
<bound method Spam.eggs of <__main__.Spam object at 0x119947630>>
Notice that it's a bound method of the spam object. When you call spam.eggs(), that spam object will be passed as the self argument.
This means you don't need to pass a host in, because it's already available as self. And, since that's the only thing you do with host, you don't need to pass around host in the first place. Which means you can revert all of your changes to the library code.
You do need to define your callback method as a proper method, with self as the first argument. But that's it. Then you can just pass rotaryCallback=self.rotaryChange to the constructor, and everything will work.
At a first look, it looks like your new callback is missing a self field?
The original function was
def rotaryChange(self, pin):
self.clickedOnce = True
But your implementation is:
def rotaryChange(pin, host):
host.clickedOnce = True
If this function sits inside a class it needs to have a self parameter

Decorator to register Python methods in PyCLIPS

I make use of PyCLIPS to integrate CLIPS into Python. Python methods are registered in CLIPS using clips.RegisterPythonFunction(method, optional-name). Since I have to register several functions and want to keep the code clear, I am looking for a decorator to do the registration.
This is how it is done now:
class CLIPS(object):
...
def __init__(self, data):
self.data = data
clips.RegisterPythonFunction(self.pyprint, "pyprint")
def pyprint(self, value):
print self.data, "".join(map(str, value))
and this is how I would like to do it:
class CLIPS(object):
...
def __init__(self, data):
self.data = data
#clips.RegisterPythonFunction(self.pyprint, "pyprint")
#clips_callable
def pyprint(self, value):
print self.data, "".join(map(str, value))
It keeps the coding of the methods and registering them in one place.
NB: I use this in a multiprocessor set-up in which the CLIPS process runs in a separate process like this:
import clips
import multiprocessing
class CLIPS(object):
def __init__(self, data):
self.environment = clips.Environment()
self.data = data
clips.RegisterPythonFunction(self.pyprint, "pyprint")
self.environment.Load("test.clp")
def Run(self, cycles=None):
self.environment.Reset()
self.environment.Run()
def pyprint(self, value):
print self.data, "".join(map(str, value))
class CLIPSProcess(multiprocessing.Process):
def run(self):
p = multiprocessing.current_process()
self.c = CLIPS("%s %s" % (p.name, p.pid))
self.c.Run()
if __name__ == "__main__":
p = multiprocessing.current_process()
c = CLIPS("%s %s" % (p.name, p.pid))
c.Run()
# Now run CLIPS from another process
cp = CLIPSProcess()
cp.start()
it should be fairly simple to do like this:
# mock clips for testing
class clips:
#staticmethod
def RegisterPythonFunction(func, name):
print "register: ", func, name
def clips_callable(fnc):
clips.RegisterPythonFunction(fnc, fnc.__name__)
return fnc
#clips_callable
def test(self):
print "test"
test()
edit: if used on a class method it will register the unbound method only. So it won't work if the function will be called without an instance of the class as the first argument. Therefore this would be usable to register module level functions, but not class methods. To do that, you'll have to register them in __init__.
It seems that the elegant solution proposed by mata wouldn't work because the CLIPS environment should be initialized before registering methods to it.
I'm not a Python expert, but from some searching it seems that combination of inspect.getmembers() and hasattr() will do the trick for you - you could loop all members of your class, and register the ones that have the #clips_callable attribute to CLIPS.
Got it working now by using a decorator to set an attribute on the method to be registered in CLIPS and using inspect in init to fetch the methods and register them. Could have used some naming strategy as well, but I prefer using a decorator to make the registering more explicit. Python functions can be registered before initializing a CLIPS environment. This is what I have done.
import inspect
def clips_callable(func):
from functools import wraps
#wraps(func)
def wrapper(*__args,**__kw):
return func(*__args,**__kw)
setattr(wrapper, "clips_callable", True)
return wrapper
class CLIPS(object):
def __init__(self, data):
members = inspect.getmembers(self, inspect.ismethod)
for name, method in members:
try:
if method.clips_callable:
clips.RegisterPythonFunction(method, name)
except:
pass
...
#clips_callable
def pyprint(self, value):
print self.data, "".join(map(str, value))
For completeness, the CLIPS code in test.clp is included below.
(defrule MAIN::start-me-up
=>
(python-call pyprint "Hello world")
)
If somebody knows a more elegant approach, please let me know.

Is it possible to maintain "boundness" of a method when passing it as an object outside its class

I'm trying to write a library that will register an arbitrary list of service calls from multiple service endpoints to a container. I intend to implement the service calls in classes written one per service. Is there a way to maintain the boundedness of the methods from the service classes when registering them to the container (so they will still have access to the instance data of their owning object instance), or must I register the whole object then write some sort of pass through in the container class with __getattr__ or some such to access the methods within instance context?
container:
class ServiceCalls(object):
def __init__(self):
self._service_calls = {}
def register_call(self, name, call):
if name not in self._service_calls:
self._service_calls[name] = call
def __getattr__(self, name):
if name in self._service_calls:
return self._service_calls[name]
services:
class FooSvc(object):
def __init__(self, endpoint):
self.endpoint = endpoint
def fooize(self, *args, **kwargs):
#call fooize service call with args/kwargs utilizing self.endpoint
def fooify(self, *args, **kwargs):
#call fooify service call with args/kwargs utilizing self.endpoint
class BarSvc(object):
def __init__(self, endpoint):
self.endpoint = endpoint
def barize(self, *args, **kwargs):
#call barize service call with args/kwargs utilizing self.endpoint
def barify(self, *args, **kwargs):
#call barify service call with args/kwargs utilizing self.endpoint
implementation code:
foosvc = FooSvc('fooendpoint')
barsvc = BarSvc('barendpoint')
calls = ServiceCalls()
calls.register('fooize', foosvc.fooize)
calls.register('fooify', foosvc.fooify)
calls.register('barize', barsvc.barize)
calls.register('barify', barsvc.barify)
calls.fooize(args)
I think this answers your question:
In [2]: f = 1 .__add__
In [3]: f(3)
Out[3]: 4
You won't need the staticmethod function when adding these functions to classes, because they are effectively already "staticed".
What you are trying to do will work fine, as you can see by running your own code. :)
The object foosvc.fooize is called a "bound method" in Python, and it contains both, a reference to foosvc and to the function FooSvc.fooize. If you call the bound method, the reference to self will be passed implicitly as the first paramater.
On a side note, __getattr__() shouldn't silently return None for invalid attribute names. Better use this:
def __getattr__(self, name):
try:
return self._service_calls[name]
except KeyError:
raise AttributeError
I don't understand the use case for this -- it seems to me that the easy, simple, idiomatic way to accomplish this is to just pass in an object.
But: program to the interface, not the implementation. Only assume that the object has the method you need -- don't touch the internals or any other methods.

Categories