C#-Style Event Handling Mechanism for Python
C# provides a very simple syntax using the observer pattern for 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 type EventHandler.
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 two Callable types.
So the Event[**TArgs] class is provided to mimic delegates:
from events import Event
item_changed = 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:
def item_changed_handler(key: str, value: object) -> None:
...
item_changed += item_changed_handler
item_changed -= item_changed_handler
An event can be raised by simply invoking it with the arguments:
item_changed("info", obj)
Since Event acts 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:
class EventExample:
def __init__(self) -> None:
self.__value = ""
self.updated: Event[str] = Event()
def update(self, value: str) -> None:
if self.__value != value:
self.__value = value
self.updated(value)
obj = EventExample()
obj.updated += lambda value: print(f"obj.{value=}")
obj.update("new value")
A class decorator @events is provided as a shortcut for event fields:
from events import Event, events
@events
class EventFieldsExample:
item_added: Event[object]
item_removed: Event[object]
item_updated: Event[str, object]
C# also provides event properties with add and remove accessors:
public event EventHandler<ItemChangedEventArgs> ItemChanged
{
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 the accessors[**TArgs] type are provided to support this feature:
from events import accessors, event, EventHandler
class EventPropertyExample:
@event[str, object]
def item_changed() -> accessors[str, object]:
def add(self: Self, value: EventHandler[str, object]) -> None: ...
def remove(self: Self, value: EventHandler[str, object]) -> None: ...
return (add, remove)
Furthermore, the EventHandlerCollection interface is provided to support the functionalities of System.ComponentModel.EventHandlerList class from C#, along with the two implementations EventHandlerList and EventHandlerDict using a linked list and a dictionary respectively. The behaviour of EventHandlerList is exactly the same as of its counterpart from C#.
A typical usage of EventHandlerList in C# can be translated directly into the python code:
class EventPropertyExample:
__event_item_changed: Final = object()
def __init__(self) -> None:
self.__events = EventHandlerList()
@event # [str, object] is inferred
def item_changed(): # -> accessors[str, object] is inferred
def add(self: Self, value: EventHandler[str, object]) -> None:
self.__events.add_handler(self.__event_item_changed, value)
def remove(self: Self, value: EventHandler[str, object]) -> None:
self.__events.remove_handler(self.__event_item_changed, value)
return (add, remove)
def _on_item_changed(self, key: str, value: object) -> None:
handler = self.__events[self.__event_item_changed]
if handler:
handler(key, value)
The class decorator @events also provides a shortcut for event properties.
The above code can be shortened to:
@events(collection="__events")
class EventPropertyExample:
item_changed: event[str, object]
def __init__(self) -> None:
self.__events = EventHandlerList()
def _on_item_changed(self, key: str, value: object) -> None:
self.__events.invoke("item_changed", key, value)
Installation
Install using pip:
pip install cs-events
Metadata
Release files for cs-events 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cs_events-0.5.0.tar.gz | 12.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cs_events-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.7 kB
Release files / cs_events-0.5.0.tar.gz
| Download URL | cs_events-0.5.0.tar.gz |
|---|---|
| Size | 12.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ba9183ccda2e7c22e8556fa6185f449085e9ba75ab444b0115c9539b341bdc34
|
|
BLAKE2b-256 checksum How to use checksums |
c606b0c092039e877f85a4db8e6524a17a5fa426f03fc3370d52990a913c6020
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.8.2 CPython/3.12.3 Windows/11
|
Release files / cs_events-0.5.0-py3-none-any.whl
| Download URL | cs_events-0.5.0-py3-none-any.whl |
|---|---|
| Size | 12.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
851bb8a4c3adf4841622ae561d40e7098ee82c86fedc3b235157a007228714ef
|
|
BLAKE2b-256 checksum How to use checksums |
ae10136d9eaa4e465bef83edd166fa53ddf02211e2f6dcea7be9a49c25da6a25
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.8.2 CPython/3.12.3 Windows/11
|