cs-events


Namecs-events JSON
Version 0.5.0 PyPI version JSON
download
home_pagehttps://github.com/wise0704/python-cs-events
SummaryC#-style event handling mechanism for Python
upload_time2024-05-19 22:31:52
maintainerNone
docs_urlNone
authorDaniel Jeong
requires_python<4.0,>=3.10
licenseMIT
keywords python event c#
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # C#-Style Event Handling Mechanism for Python

<p align="center">
    <a href="https://pypi.org/project/cs-events/">
        <img alt="pypi"
        src="https://img.shields.io/pypi/v/cs-events?logo=pypi&logoColor=EEE" /></a>
    <a href="https://pypi.org/project/cs-events/">
        <img alt="status"
        src="https://img.shields.io/pypi/status/cs-events" /></a>
    <a href="https://pypistats.org/packages/cs-events">
        <img alt="downloads"
        src="https://img.shields.io/pypi/dm/cs-events" /></a>
    <a href="https://www.python.org/downloads/">
        <img alt="python"
        src="https://img.shields.io/pypi/pyversions/cs-events?logo=python&logoColor=yellow" /></a>
    <a href="https://github.com/wise0704/python-cs-events/blob/master/LICENSE">
        <img alt="license"
        src="https://img.shields.io/pypi/l/cs-events?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCIgc3Ryb2tlPSIjRkZGIj48cGF0aCBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMiIgZD0ibTMgNiAzIDFtMCAwLTMgOWE1LjAwMiA1LjAwMiAwIDAgMCA2LjAwMSAwTTYgN2wzIDlNNiA3bDYtMm02IDIgMy0xbS0zIDEtMyA5YTUuMDAyIDUuMDAyIDAgMCAwIDYuMDAxIDBNMTggN2wzIDltLTMtOS02LTJtMC0ydjJtMCAxNlY1bTAgMTZIOW0zIDBoMyIvPjwvc3ZnPg==" /></a>
    <br/>
    <a href="https://github.com/wise0704/python-cs-events/actions/workflows/python-package.yml">
        <img alt="build"
        src="https://img.shields.io/github/actions/workflow/status/wise0704/python-cs-events/python-package.yml?logo=pytest" /></a>
    <a href="https://github.com/wise0704/python-cs-events/issues">
        <img alt="issues"
        src="https://img.shields.io/github/issues/wise0704/python-cs-events?logo=github" /></a>
    <a href="https://github.com/wise0704/python-cs-events/pulls">
        <img alt="pull requests"
        src="https://img.shields.io/github/issues-pr/wise0704/python-cs-events?logo=github" /></a>
</p>

C# provides a very simple syntax using the [observer pattern](https://en.wikipedia.org/wiki/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:

```python
from events import Event

item_changed = Event[str, object]()
```

> C# naming convention prefers present/past participles (`changing`/`changed`) instead of `on`+infinitive (`on_change`) for events.

Handlers can subscribe to and unsubscribe from the event with the same syntax:

```python
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:

```python
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:

```python
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**:

```python
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:

```C#
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:

```python
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:

```python
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:

```python
@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`](https://pypi.org/project/pip/):

```console
pip install cs-events
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/wise0704/python-cs-events",
    "name": "cs-events",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.10",
    "maintainer_email": null,
    "keywords": "python, event, c#",
    "author": "Daniel Jeong",
    "author_email": "wise0704@outlook.com",
    "download_url": "https://files.pythonhosted.org/packages/c6/06/b0c092039e877f85a4db8e6524a17a5fa426f03fc3370d52990a913c6020/cs_events-0.5.0.tar.gz",
    "platform": null,
    "description": "# C#-Style Event Handling Mechanism for Python\n\n<p align=\"center\">\n    <a href=\"https://pypi.org/project/cs-events/\">\n        <img alt=\"pypi\"\n        src=\"https://img.shields.io/pypi/v/cs-events?logo=pypi&logoColor=EEE\" /></a>\n    <a href=\"https://pypi.org/project/cs-events/\">\n        <img alt=\"status\"\n        src=\"https://img.shields.io/pypi/status/cs-events\" /></a>\n    <a href=\"https://pypistats.org/packages/cs-events\">\n        <img alt=\"downloads\"\n        src=\"https://img.shields.io/pypi/dm/cs-events\" /></a>\n    <a href=\"https://www.python.org/downloads/\">\n        <img alt=\"python\"\n        src=\"https://img.shields.io/pypi/pyversions/cs-events?logo=python&logoColor=yellow\" /></a>\n    <a href=\"https://github.com/wise0704/python-cs-events/blob/master/LICENSE\">\n        <img alt=\"license\"\n        src=\"https://img.shields.io/pypi/l/cs-events?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCIgc3Ryb2tlPSIjRkZGIj48cGF0aCBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMiIgZD0ibTMgNiAzIDFtMCAwLTMgOWE1LjAwMiA1LjAwMiAwIDAgMCA2LjAwMSAwTTYgN2wzIDlNNiA3bDYtMm02IDIgMy0xbS0zIDEtMyA5YTUuMDAyIDUuMDAyIDAgMCAwIDYuMDAxIDBNMTggN2wzIDltLTMtOS02LTJtMC0ydjJtMCAxNlY1bTAgMTZIOW0zIDBoMyIvPjwvc3ZnPg==\" /></a>\n    <br/>\n    <a href=\"https://github.com/wise0704/python-cs-events/actions/workflows/python-package.yml\">\n        <img alt=\"build\"\n        src=\"https://img.shields.io/github/actions/workflow/status/wise0704/python-cs-events/python-package.yml?logo=pytest\" /></a>\n    <a href=\"https://github.com/wise0704/python-cs-events/issues\">\n        <img alt=\"issues\"\n        src=\"https://img.shields.io/github/issues/wise0704/python-cs-events?logo=github\" /></a>\n    <a href=\"https://github.com/wise0704/python-cs-events/pulls\">\n        <img alt=\"pull requests\"\n        src=\"https://img.shields.io/github/issues-pr/wise0704/python-cs-events?logo=github\" /></a>\n</p>\n\nC# provides a very simple syntax using the [observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) for its event handling system.\nThe aim of this project is to implement the pattern in python as similarly as possible.\n\nIn C#, an \"event\" is a field or a property of the delegate type `EventHandler`.\nSince delegates in C# can be combined and removed with `+=` and `-=` operators,\nevent handlers can easily subscribe to or unsubscribe from the event using those operators.\n\nPython does not support an addition of two `Callable` types.\nSo the `Event[**TArgs]` class is provided to mimic delegates:\n\n```python\nfrom events import Event\n\nitem_changed = Event[str, object]()\n```\n\n> C# naming convention prefers present/past participles (`changing`/`changed`) instead of `on`+infinitive (`on_change`) for events.\n\nHandlers can subscribe to and unsubscribe from the event with the same syntax:\n\n```python\ndef item_changed_handler(key: str, value: object) -> None:\n    ...\n\nitem_changed += item_changed_handler\nitem_changed -= item_changed_handler\n```\n\nAn event can be raised by simply invoking it with the arguments:\n\n```python\nitem_changed(\"info\", obj)\n```\n\nSince `Event` acts just like a delegate from C#, it is not required to be bound to a class or an instance object.\nThis 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.\n\nAn example class with event fields may look like this:\n\n```python\nclass EventExample:\n    def __init__(self) -> None:\n        self.__value = \"\"\n        self.updated: Event[str] = Event()\n\n    def update(self, value: str) -> None:\n        if self.__value != value:\n            self.__value = value\n            self.updated(value)\n\nobj = EventExample()\nobj.updated += lambda value: print(f\"obj.{value=}\")\nobj.update(\"new value\")\n```\n\nA class decorator `@events` is provided as a shortcut for **event fields**:\n\n```python\nfrom events import Event, events\n\n@events\nclass EventFieldsExample:\n    item_added: Event[object]\n    item_removed: Event[object]\n    item_updated: Event[str, object]\n```\n\nC# also provides **event properties** with `add` and `remove` accessors:\n\n```C#\npublic event EventHandler<ItemChangedEventArgs> ItemChanged\n{\n    add { ... }\n    remove { ... }\n}\n```\n\nThis 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.\n\nThe `@event[**TArgs]` decorator and the `accessors[**TArgs]` type are provided to support this feature:\n\n```python\nfrom events import accessors, event, EventHandler\n\nclass EventPropertyExample:\n    @event[str, object]\n    def item_changed() -> accessors[str, object]:\n        def add(self: Self, value: EventHandler[str, object]) -> None: ...\n        def remove(self: Self, value: EventHandler[str, object]) -> None: ...\n        return (add, remove)\n```\n\nFurthermore, 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#.\n\nA typical usage of `EventHandlerList` in C# can be translated directly into the python code:\n\n```python\nclass EventPropertyExample:\n    __event_item_changed: Final = object()\n\n    def __init__(self) -> None:\n        self.__events = EventHandlerList()\n\n    @event  # [str, object] is inferred\n    def item_changed():  # -> accessors[str, object] is inferred\n        def add(self: Self, value: EventHandler[str, object]) -> None:\n            self.__events.add_handler(self.__event_item_changed, value)\n\n        def remove(self: Self, value: EventHandler[str, object]) -> None:\n            self.__events.remove_handler(self.__event_item_changed, value)\n\n        return (add, remove)\n\n    def _on_item_changed(self, key: str, value: object) -> None:\n        handler = self.__events[self.__event_item_changed]\n        if handler:\n            handler(key, value)\n```\n\nThe class decorator `@events` also provides a shortcut for event properties.\nThe above code can be shortened to:\n\n```python\n@events(collection=\"__events\")\nclass EventPropertyExample:\n    item_changed: event[str, object]\n\n    def __init__(self) -> None:\n        self.__events = EventHandlerList()\n\n    def _on_item_changed(self, key: str, value: object) -> None:\n        self.__events.invoke(\"item_changed\", key, value)\n```\n\n## Installation\n\nInstall using [`pip`](https://pypi.org/project/pip/):\n\n```console\npip install cs-events\n```\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "C#-style event handling mechanism for Python",
    "version": "0.5.0",
    "project_urls": {
        "Homepage": "https://github.com/wise0704/python-cs-events",
        "Issues": "https://github.com/wise0704/python-cs-events/issues",
        "Repository": "https://github.com/wise0704/python-cs-events"
    },
    "split_keywords": [
        "python",
        " event",
        " c#"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ae10136d9eaa4e465bef83edd166fa53ddf02211e2f6dcea7be9a49c25da6a25",
                "md5": "cf769685e5fa7b34f7a2dc2dbaa08710",
                "sha256": "851bb8a4c3adf4841622ae561d40e7098ee82c86fedc3b235157a007228714ef"
            },
            "downloads": -1,
            "filename": "cs_events-0.5.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "cf769685e5fa7b34f7a2dc2dbaa08710",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.10",
            "size": 12467,
            "upload_time": "2024-05-19T22:31:50",
            "upload_time_iso_8601": "2024-05-19T22:31:50.360145Z",
            "url": "https://files.pythonhosted.org/packages/ae/10/136d9eaa4e465bef83edd166fa53ddf02211e2f6dcea7be9a49c25da6a25/cs_events-0.5.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c606b0c092039e877f85a4db8e6524a17a5fa426f03fc3370d52990a913c6020",
                "md5": "72854bc1cae436710a39f5d44508d9e0",
                "sha256": "ba9183ccda2e7c22e8556fa6185f449085e9ba75ab444b0115c9539b341bdc34"
            },
            "downloads": -1,
            "filename": "cs_events-0.5.0.tar.gz",
            "has_sig": false,
            "md5_digest": "72854bc1cae436710a39f5d44508d9e0",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.10",
            "size": 12184,
            "upload_time": "2024-05-19T22:31:52",
            "upload_time_iso_8601": "2024-05-19T22:31:52.048268Z",
            "url": "https://files.pythonhosted.org/packages/c6/06/b0c092039e877f85a4db8e6524a17a5fa426f03fc3370d52990a913c6020/cs_events-0.5.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-05-19 22:31:52",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "wise0704",
    "github_project": "python-cs-events",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "cs-events"
}
        
Elapsed time: 0.35319s