-
Notifications
You must be signed in to change notification settings - Fork 110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow to set a robot's base (RCF) #762
Changes from 3 commits
b523e27
4d71696
a4a3e8f
c8819f4
ebd790b
5d63dc1
32addae
5aec18f
452c547
fa473ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,8 +5,6 @@ | |
import functools | ||
import pstats | ||
|
||
from functools import wraps | ||
|
||
try: | ||
from cStringIO import StringIO | ||
except ImportError: | ||
|
@@ -25,7 +23,10 @@ | |
'abstractstaticmethod', | ||
'abstractclassmethod', | ||
'memoize', | ||
'print_profile' | ||
'print_profile', | ||
'observable', | ||
'add_observer', | ||
'remove_observer', | ||
] | ||
|
||
|
||
|
@@ -46,7 +47,6 @@ def __init__(self, function): | |
function.__isabstractmethod__ = True | ||
super(abstractstaticmethod, self).__init__(function) | ||
|
||
|
||
class abstractclassmethod(classmethod): | ||
"""Decorator for declaring a class method abstract. | ||
|
||
|
@@ -117,7 +117,7 @@ def f(n): | |
print(f.__name__) | ||
|
||
""" | ||
@wraps(func) | ||
@functools.wraps(func) | ||
def wrapper(*args, **kwargs): | ||
profile = Profile.Profile() | ||
profile.enable() | ||
|
@@ -136,9 +136,129 @@ def wrapper(*args, **kwargs): | |
return wrapper | ||
|
||
|
||
# ============================================================================== | ||
# Main | ||
# ============================================================================== | ||
def _get_event_key(event_source, event_name): | ||
return '/{}/{}'.format(event_source.__class__.__name__, event_name) | ||
|
||
|
||
def observable(observable_method=None, event_name=None, *args, **kwargs): | ||
"""Decorator to mark a property or method of a class as observable. | ||
|
||
Observable methods/properties send notifications (events) that | ||
other code can listen to and act when they are triggered. This | ||
is useful to decouple classes where a class B depends on events | ||
occurring on class A, but class A should not be in charge of | ||
actively track and update class B on those events. | ||
|
||
Notes | ||
----- | ||
This decorator should only be applied to methods and properties | ||
of a class, not to stand-alone functions. | ||
|
||
Examples | ||
-------- | ||
>>> class Test(object): | ||
... @observable(event_name='init') | ||
... def init(self): | ||
... print('init') | ||
... | ||
>>> def init_observer(ctx): | ||
... print('inside observer') | ||
... | ||
>>> t = Test() | ||
>>> t.init() | ||
init | ||
>>> add_observer(t, 'init', init_observer) | ||
>>> t.init() | ||
init | ||
inside observer | ||
>>> remove_observer(t, 'init', init_observer) | ||
>>> t.init() | ||
init | ||
|
||
Parameters | ||
---------- | ||
observable_method | ||
The method that is to be declared observable. | ||
event_name : [type], optional | ||
The name of the event that will be triggered when the method/property is invoked. | ||
""" | ||
def observable_decorator(func): | ||
@functools.wraps(func) | ||
def wrapper(*args, **kwargs): | ||
_self = args[0] if len(args) else None | ||
if not _self: | ||
raise Exception('Cannot make an observable without a containing class') | ||
|
||
event_observers = None | ||
event_key = _get_event_key(_self, event_name) | ||
|
||
if hasattr(_self, '__event_observers__'): | ||
event_observers = _self.__event_observers__.get(event_key) | ||
|
||
return_value = func(*args, **kwargs) | ||
|
||
if event_observers: | ||
for observer in event_observers: | ||
ctx = EventContext(event_name, *args, **kwargs) | ||
observer(ctx) | ||
|
||
return return_value | ||
return wrapper | ||
if observable_method is None: | ||
return observable_decorator | ||
else: | ||
return observable_decorator(observable_method) | ||
|
||
|
||
def add_observer(event_source, event_name, callback): | ||
"""Add an observer to an event in a class containing observable methods/properties. | ||
|
||
Parameters | ||
---------- | ||
event_source | ||
Object containing ``@observable`` instances. | ||
event_name : str | ||
Name of the event to listen to. | ||
callback : function | ||
Function to execute every time the specified event is fired. | ||
""" | ||
if not hasattr(event_source, '__event_observers__'): | ||
event_source.__event_observers__ = {} | ||
|
||
event_key = _get_event_key(event_source, event_name) | ||
observers = event_source.__event_observers__.get(event_key, set()) | ||
observers.add(callback) | ||
|
||
event_source.__event_observers__[event_key] = observers | ||
|
||
|
||
def remove_observer(event_source, event_name, callback): | ||
"""Remove an observer from an event in a class containing observable methods/properties. | ||
|
||
Parameters | ||
---------- | ||
event_source | ||
Object containing ``@observable`` instances. | ||
event_name : str | ||
Name of the event to listen to. | ||
callback : function | ||
Function to execute every time the specified event is fired. | ||
""" | ||
if not hasattr(event_source, '__event_observers__'): | ||
return | ||
|
||
event_key = _get_event_key(event_source, event_name) | ||
event_observers = event_source.__event_observers__.get(event_key) | ||
|
||
if event_observers: | ||
event_observers.remove(callback) | ||
if not event_observers: | ||
del event_source.__event_observers__[event_key] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see a lot of potential for exceptions being raised with this function. If there is a typo in |
||
|
||
|
||
if __name__ == "__main__": | ||
pass | ||
class EventContext(object): | ||
"""Provides context for the observers of an event.""" | ||
def __init__(self, event_name, *args, **kwargs): | ||
self.event_name = event_name | ||
self.args = args | ||
self.kwargs = kwargs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I trust myself to type 'init' correctly, but I do not trust myself to type 'updated_transformations' correctly. I know this is what tests are for, but somethings are difficult to test (say, did the image of the robot in blender update correctly when the rcf changed?). Is there a nice way of throwing an exception somewhere if the observable event names for a class don't match up with the observed event names or vice versa? or is that something that shouldn't be protected against?