Package: cs-events
C#-Style Event Handling Mechanism for PythonC# provides a very simple syntax using theobserver patternfor its event handling system.
The aim of this project is to implement the pattern in python as similarly as possible.In C#, an "event" is a field or a property of the delegate typeEventHandler.
Since delegates in C# can be combined and removed with+=and-=operators,
event handlers can easily subscribe to or unsubscribe from the event using those operators.Python does not support an addition of twoCallabletypes.
So theEvent[**TArgs]class is provided to mimic delegates:fromeventsimportEventchanged=Event[str,object]()C# naming convention prefers present/past participles (changing/changed) instead ofon+infinitive (on_change) for events.Handlers can subscribe to and unsubscribe from the event with the same syntax:defchanged_handler(key:str,value:object)->None:...changed+=changed_handlerchanged-=changed_handlerAn event can be raised by simply invoking it with the arguments:changed("state",obj)SinceEventacts just like a delegate from C#, it is not required to be bound to a class or an instance object.
This is the major difference to other packages that try to implement the C#-style event system, which usually revolve around a container object for events.An example class with event fields may look like this:classEventExample:def__init__(self)->None:self.updated:Event[str]=Event()self.__value:str=""defupdate(self,value:str)->None:ifself.__value!=value:self.__value=valueself.updated(value)obj=EventExample()obj.updated+=lambdavalue:print(f"obj.{value=}")obj.update("new value")A class decorator@eventsis provided as a shortcut for event fields:fromeventsimportEvent,events@eventsclassEventFieldsExample:item_added:Event[object]item_removed:Event[object]item_updated:Event[object,str]C# also provides event properties withaddandremoveaccessors:publiceventEventHandler<T>Changed{add{...}remove{...}}This feature is useful for classes that do not actually own the events, but need to forward the subscriptions to the underlying object that do own the events.The@event[**TArgs]decorator and theaccessors[**TArgs]type are provided to support this feature:fromeventsimportaccessors,event,EventHandlerclassEventPropertyExample:@event[str,object]defchanged()->accessors[str,object]:defadd(self:Self,value:EventHandler[str,object])->None:...defremove(self:Self,value:EventHandler[str,object])->None:...return(add,remove)Furthermore, theEventHandlerCollectioninterface is provided to support the functionalities ofSystem.ComponentModel.EventHandlerListclass from C#, along with the two implementationsEventHandlerListandEventHandlerDictusing a linked list and a dictionary respectively. The behaviour ofEventHandlerListis exactly the same as of its counterpart from C#.A typical usage ofEventHandlerListin C# can be translated directly into the python code:classEventPropertyExample:__event_changed:Final=object()@event# [str, object] is inferreddefchanged():# -> accessors[str, object] is inferreddefadd(self:Self,value:EventHandler[str,object])->None:self.__events.add_handler(self.__event_changed,value)defremove(self:Self,value:EventHandler[str,object])->None:self.__events.remove_handler(self.__event_changed,value)return(add,remove)def__init__(self)->None:self.__events=EventHandlerList()defperform_change(self,key:str,value:object)->None:handler=self.__events[self.__event_changed]ifhandler:handler(key,value)The class decorator@eventsalso provides a shortcut for event properties.
The above code can be shortened to:@events(collection="__events")classEventPropertyExample:changed:event[str,object]def__init__(self)->None:self.__events=EventHandlerList()defperform_change(self,key:str,value:object)->None:self.__events.invoke("changed",key,value)InstallationInstall usingpip:pip install cs-events