Package: observer-hooks
observer_hooksAn easy way to attach side effects to functions and methods.
Intended to accomplish something similar to the "observer pattern" in simple context.For methods, an instance member to hold weak references to functions and methods is automatically created. By default
the instance member is the same name as the method with an underscore prepended. This can be changed with the event_name
argument.Side effects can be chained normally.Installpip install observer-hooksDocumentation(wip)UsageAttach a side effect to a function:fromobserver_hooksimportnotify@notify()defsome_function(param):returnf"param:{param}"defside_effect(param):print(param)some_function.subscribe(side_effect)assertsome_function(1)=='param: 1'# this will print "1"Attach a side effect to a method:fromobserver_hooksimportnotifyclassA:@notify()defnotify_some_action(self):passclassB:def__init__(self,a:A):self.a=aa.notify_some_action.subscribe(self.a_some_action)defa_some_action(self):print('received')b=B(A())b.a.notify_some_action()# this will print "received"Example withslots:fromobserver_hooksimportnotifyclassA:__slots__='_notify_some_action',@notify()defnotify_some_action(self):passdefa_some_action():print('received')a=A()a.notify_some_action.subscribe(a_some_action)a.notify_some_action()# this will print "received"Block certain side effects from firing:fromobserver_hooksimportnotifyfromobserver_hooks.block_eventsimportBlockSideEffectsclassA:__slots__='_notify_some_action',@notify()defnotify_some_action(self):passdefa_some_action():print('blocked')defa_some_action2():print('not blocked')a=A()a.notify_some_action.subscribe(a_some_action)a.notify_some_action.subscribe(a_some_action2)withBlockSideEffects(a.notify_some_action,only=(a_some_action,)):a.notify_some_action()The HardRefEventHandler will hold non-weak references to lambda and partial functionsfromobserver_hooksimportnotify,HardRefEventHandler@notify(handler_t=HardRefEventHandler)defnotify_some_action():return'return value'defsome_action():print('Hi')defscope():notify_some_action.subscribe(lambda:some_action())scope()print(notify_some_action())The "pass_ref" parameter will pass the "self" reference to side-effect functions and the switch_event_handler method on descriptors will switch the event handler typefromobserver_hooksimportnotify,HardRefEventHandlerclassSomeClass:@notify(pass_ref=True)defmethod(self):passs=SomeClass()defmethod(other):assertotherisss.method.switch_event_handler(HardRefEventHandler())s.method.subscribe(method)s.method()Inherit or copy parameters from a superclasses descriptor with 'notify_copy_super'fromobserver_hooksimportnotify,notify_copy_superclassA:@notify(pass_ref=True,no_origin=True,auto_fire=False)defmethod(self):print('this wont print')classB(A):passclassC(B):@notify_copy_super(auto_fire=True)defmethod(self):print('neither will this')defside_effect(x):print('hi',x)c=C()c.method.subscribe(side_effect)c.method()Notes:The parameter "auto_fire", when set to false, will disable all side effects and instead the .emit() function can be used to manually trigger the side effectsRedefined methods in child classes also need a decorator to retain the behavior and will override behavior to the specifications of the new decoratorInherited methods will only fire once even if they are re-defined and super is calledInherited methods do not need to be re-definedFuture plansImplement access to the originating functions return valueEither a member of the object that replaces the function for non-thread safe applications or a special parameter