eventsystem


Nameeventsystem JSON
Version 1.0.0 PyPI version JSON
download
home_pageNone
SummaryConvenient Event System
upload_time2022-12-06 17:00:40
maintainerNone
docs_urlNone
authorNone
requires_pythonNone
licenseNone
keywords pymitter event system
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Convenient Event System

Create, subscribe and fire events with one line of code. Unlike alternative packages this one provides static typed predefined events with documented signature of event handlers.

**Both ordinary and async event handlers are supported.**

# Usage examples

Briefly, you need to perform two steps:

1. decide between two options: group events in class derived from `EventDispatcher` or just define functions,
2. then decorate each event with `@event`.

----

Let's see how to use the library in more details. Imagine we write some user interface stuff for our super-duper program.

### Single event example

**Step 1: define click event.**

```python
@event
def click(x: int, y: int)->None:
	"""
	Occures when user clicks on our interface.
	
	:param x: mouse horizontal position relative to top left corner.
	:param y: mouse vertical position relative to top left corner.
	"""
	... # no implementation required
```

**Step 2: subscribe click event.**

```python
@click
def on_click(x: int, y: int)->None:
	"""
	Process mouse click.
	"""
	print(f'You have clicked at ({x}; {y}).')
```

**Step 3: fire click event.**

```python
click.trigger(12, 34) # You have clicked at (12; 34).
```

### Events group example

**Step 1: define mouse events group.**

```python
class Mouse(EventDispatcher):
	@event
	def click(x: int, y: int)->None:
		"""
		Occures when user clicks on our interface.
		
		:param x: mouse horizontal position relative to top left corner.
		:param y: mouse vertical position relative to top left corner.
		"""
		... # no implementation required
	@event
	def move(x: int, y: int)->None:
		"""
		Occures when user moves cursor over our interface.
		
		:param x: mouse horizontal position relative to top left corner.
		:param y: mouse vertical position relative to top left corner.
		"""
		...
```

**Step 2: subscribe click event.**

```python
mouse = Mouse()

@mouse.click
def on_click(x: int, y: int)->None:
	"""
	Process mouse click.
	"""
	print(f'You have clicked at ({x}; {y}).')
```

**Step 3: fire click event.**

```python
mouse.click.trigger(12, 34) # You have clicked at (12; 34).
```

# How to create events?

Event are created by using `event` decorator. It can be used with or without parameters.

The event can have arbitrary body that is not actually called. Main feature is to defined event signature hence during subscribing **developer can see help message and type hints with intended event handler signature**.

### Single event bound to default event emitter

The simplest case is to just create an event. So default event emitter is used to manage this event.

```python
@event
def click(x: int, y: int)->None:
	"""
	Occures when user clicks on our interface.
	
	:param x: mouse horizontal position relative to top left corner.
	:param y: mouse vertical position relative to top left corner.
	"""
	...
```

### Single event bound to custom event emitter

Otherwise, you can specify different event emitter passing as argument.

```python
my_event_emitter = EventEmitter()

@event(my_event_emitter)
def click(x: int, y: int)->None:
	"""
	Occures when user clicks on our interface.
	
	:param x: mouse horizontal position relative to top left corner.
	:param y: mouse vertical position relative to top left corner.
	"""
	...
```

### Events group bound to dedicated event emitter

Moreover, you can combine events in group by using classes. There are two different approaches: using default event emitter or dedicate one per each class.

**Creating dedicated event emitter per each event group is recommended.**

Considering the preferred approach you can group event in class derived from `EventDispatcher`. This ensures creation of new event emitter per each class instance.

**Notice that using `event` decorator stays the same way as for non-class functions.**

```python
class Mouse(EventDispatcher):
	@event
	def click(x: int, y: int)->None:
		"""
		Occures when user clicks on our interface.
		
		:param x: mouse horizontal position relative to top left corner.
		:param y: mouse vertical position relative to top left corner.
		"""
		... # no implementation required
	@event(my_event_emitter) # given event emitter is prior to Mouse one 
	def move(x: int, y: int)->None:
		"""
		Occures when user moves cursor over our interface.
		
		:param x: mouse horizontal position relative to top left corner.
		:param y: mouse vertical position relative to top left corner.
		"""
		...
```

Then you can instantiate `Mouse` class and access dedicated event emitter for e.g. further low-level tuning.

```python
mouse = Mouse()

mouse.emitter.max_listeners = 2 # allows up to two handlers
```

When using `event` decorator on `EventDispatcher` subclass method with specified event emitter it takes priority over the one dedicated per class instance.

### Events group bound to default event emitter

In case you do not want to create dedicated event emitter per class instance (*not recommended*) you can just omit deriving from `EventDispatcher`.

In following code default event emitter is used for all events in group.

```python
class Mouse:
	@event
	def click(x: int, y: int)->None:
		"""
		Occures when user clicks on our interface.
		
		:param x: mouse horizontal position relative to top left corner.
		:param y: mouse vertical position relative to top left corner.
		"""
		... # no implementation required
	@event(my_event_emitter) # given event emitter is prior to default one 
	def move(x: int, y: int)->None:
		"""
		Occures when user moves cursor over our interface.
		
		:param x: mouse horizontal position relative to top left corner.
		:param y: mouse vertical position relative to top left corner.
		"""
		...
```

# How to subscribe events?

Event is subscribed by decorating handler. **Handler can be any ordinary or async function (callable).** Decorator also can be used with or without parameters.

**Notice, when using event your IDE can show help message and type hints of intended handler signature.**

### Unlimited event handler

The simplest case is to just decorate handler with event. So handler will be called when event triggers.

```python
@click
def on_click(x: int, y: int)->None:
	"""
	Process mouse click.
	"""
	print(f'You have clicked at ({x}; {y}).')
```

**There is no difference between usage of single events or ones from event groups.** Hence, according to `Mouse` class example the above code can be rewritten in:

```python
@mouse.click
def on_click(x: int, y: int)->None:
	"""
	Process mouse click.
	"""
	print(f'You have clicked at ({x}; {y}).')
```

### Event handler with executions limit

Otherwise, you can specify number of times to handle event. When event fires more times, no further calls will be made to the handler.

```python
@click(2)
def on_click(x: int, y: int)->None:
	"""
	Process mouse click two first times.
	"""
	print(f'You have clicked at ({x}; {y}).')
```

In above case `on_click` will be called only for the first two times.

# How to fire events?

To fire, emit, trigger event you can use `.trigger(...)` method where should pass exactly the same arguments that are defined in event signature.

**Notice, when using event your IDE can show help message and type hints of intended arguments.**

Basically, to trigger an event you can just call `trigger` method. Each active handler will be called with given arguments. You can use any set of arguments, including positional, universal, named, packed and even named packed.

```python
click.trigger(12, 34)
```

Alternatively considering `Mouse` class example:

```python
mouse.click.trigger(12, 34)
```

# Reference

**eventsystem.event(...)**

Event descriptor decorator. Can be used on functions or class methods. Can be used with or without parameters.

When used with single parameter `emitter:EventEmitter` binds given event emitter to decorated event.

When used without parameters (brackets): if method belongs to `EventDispatcher` derived class instance then uses its event emitter, else uses default emitter.

**eventsystem.EventDispatcher(emitter: EventEmitter | None = None)**

Base class to provide event group. Events are described as methods. Each event should have signature of `EventHandler` and decorated with `@event`.

Constructor parameters:

* **emitter** - event emitter to use. If not given creates a new one.

Fields:

* **emitter** - event emitter bound to dispatcher subclass instance.

**eventsystem.Event**

Event interface. Intended to be used as decorator or parametrized decorator for handlers. *Should not be instantiated directly.*

Fields:

* **name** - read only property of event name

Methods:

* **get_emitter() → EventEmitter | None** - get bound event emitter. In most cases, event will have event emitter bound. But in rare cases it can be `None` when no handler descriptor has been decorated yet.

* **trigger(...) → None** - fire the event. All arguments are passed to each handler. Signature must match handler descriptor.

**eventsystem.emitter**

Default event emitter.

**eventsystem.dispatcher**

Default event dispatcher bound to default event emitter.

**eventsystem.EventHandler**

Base event and event handler signature (type annotations). Signature of handler must copy signature of event.

# What event emitter is?

This library us build on top of [pymitter](https://pypi.org/project/pymitter). `EventEmitter` - is a basic building block of that library which actually dispatches events, manages priority, limitations and queues.

Using `EventEmitter` directly you can unsubscribe handlers, get all active handlers or limit them. For detailed tuning read pymitter documentation.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "eventsystem",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "pymitter,event,system",
    "author": null,
    "author_email": "baterflyrity <baterflyrity@yandex.ru>",
    "download_url": "https://files.pythonhosted.org/packages/14/8c/ec87ae2be6fac24a8207eff88a61bbab4eb2b178c79d0408c6cea4d0b050/eventsystem-1.0.0.tar.gz",
    "platform": null,
    "description": "# Convenient Event System\n\nCreate, subscribe and fire events with one line of code. Unlike alternative packages this one provides static typed predefined events with documented signature of event handlers.\n\n**Both ordinary and async event handlers are supported.**\n\n# Usage examples\n\nBriefly, you need to perform two steps:\n\n1. decide between two options: group events in class derived from `EventDispatcher` or just define functions,\n2. then decorate each event with `@event`.\n\n----\n\nLet's see how to use the library in more details. Imagine we write some user interface stuff for our super-duper program.\n\n### Single event example\n\n**Step 1: define click event.**\n\n```python\n@event\ndef click(x: int, y: int)->None:\n\t\"\"\"\n\tOccures when user clicks on our interface.\n\t\n\t:param x: mouse horizontal position relative to top left corner.\n\t:param y: mouse vertical position relative to top left corner.\n\t\"\"\"\n\t... # no implementation required\n```\n\n**Step 2: subscribe click event.**\n\n```python\n@click\ndef on_click(x: int, y: int)->None:\n\t\"\"\"\n\tProcess mouse click.\n\t\"\"\"\n\tprint(f'You have clicked at ({x}; {y}).')\n```\n\n**Step 3: fire click event.**\n\n```python\nclick.trigger(12, 34) # You have clicked at (12; 34).\n```\n\n### Events group example\n\n**Step 1: define mouse events group.**\n\n```python\nclass Mouse(EventDispatcher):\n\t@event\n\tdef click(x: int, y: int)->None:\n\t\t\"\"\"\n\t\tOccures when user clicks on our interface.\n\t\t\n\t\t:param x: mouse horizontal position relative to top left corner.\n\t\t:param y: mouse vertical position relative to top left corner.\n\t\t\"\"\"\n\t\t... # no implementation required\n\t@event\n\tdef move(x: int, y: int)->None:\n\t\t\"\"\"\n\t\tOccures when user moves cursor over our interface.\n\t\t\n\t\t:param x: mouse horizontal position relative to top left corner.\n\t\t:param y: mouse vertical position relative to top left corner.\n\t\t\"\"\"\n\t\t...\n```\n\n**Step 2: subscribe click event.**\n\n```python\nmouse = Mouse()\n\n@mouse.click\ndef on_click(x: int, y: int)->None:\n\t\"\"\"\n\tProcess mouse click.\n\t\"\"\"\n\tprint(f'You have clicked at ({x}; {y}).')\n```\n\n**Step 3: fire click event.**\n\n```python\nmouse.click.trigger(12, 34) # You have clicked at (12; 34).\n```\n\n# How to create events?\n\nEvent are created by using `event` decorator. It can be used with or without parameters.\n\nThe event can have arbitrary body that is not actually called. Main feature is to defined event signature hence during subscribing **developer can see help message and type hints with intended event handler signature**.\n\n### Single event bound to default event emitter\n\nThe simplest case is to just create an event. So default event emitter is used to manage this event.\n\n```python\n@event\ndef click(x: int, y: int)->None:\n\t\"\"\"\n\tOccures when user clicks on our interface.\n\t\n\t:param x: mouse horizontal position relative to top left corner.\n\t:param y: mouse vertical position relative to top left corner.\n\t\"\"\"\n\t...\n```\n\n### Single event bound to custom event emitter\n\nOtherwise, you can specify different event emitter passing as argument.\n\n```python\nmy_event_emitter = EventEmitter()\n\n@event(my_event_emitter)\ndef click(x: int, y: int)->None:\n\t\"\"\"\n\tOccures when user clicks on our interface.\n\t\n\t:param x: mouse horizontal position relative to top left corner.\n\t:param y: mouse vertical position relative to top left corner.\n\t\"\"\"\n\t...\n```\n\n### Events group bound to dedicated event emitter\n\nMoreover, you can combine events in group by using classes. There are two different approaches: using default event emitter or dedicate one per each class.\n\n**Creating dedicated event emitter per each event group is recommended.**\n\nConsidering the preferred approach you can group event in class derived from `EventDispatcher`. This ensures creation of new event emitter per each class instance.\n\n**Notice that using `event` decorator stays the same way as for non-class functions.**\n\n```python\nclass Mouse(EventDispatcher):\n\t@event\n\tdef click(x: int, y: int)->None:\n\t\t\"\"\"\n\t\tOccures when user clicks on our interface.\n\t\t\n\t\t:param x: mouse horizontal position relative to top left corner.\n\t\t:param y: mouse vertical position relative to top left corner.\n\t\t\"\"\"\n\t\t... # no implementation required\n\t@event(my_event_emitter) # given event emitter is prior to Mouse one \n\tdef move(x: int, y: int)->None:\n\t\t\"\"\"\n\t\tOccures when user moves cursor over our interface.\n\t\t\n\t\t:param x: mouse horizontal position relative to top left corner.\n\t\t:param y: mouse vertical position relative to top left corner.\n\t\t\"\"\"\n\t\t...\n```\n\nThen you can instantiate `Mouse` class and access dedicated event emitter for e.g. further low-level tuning.\n\n```python\nmouse = Mouse()\n\nmouse.emitter.max_listeners = 2 # allows up to two handlers\n```\n\nWhen using `event` decorator on `EventDispatcher` subclass method with specified event emitter it takes priority over the one dedicated per class instance.\n\n### Events group bound to default event emitter\n\nIn case you do not want to create dedicated event emitter per class instance (*not recommended*) you can just omit deriving from `EventDispatcher`.\n\nIn following code default event emitter is used for all events in group.\n\n```python\nclass Mouse:\n\t@event\n\tdef click(x: int, y: int)->None:\n\t\t\"\"\"\n\t\tOccures when user clicks on our interface.\n\t\t\n\t\t:param x: mouse horizontal position relative to top left corner.\n\t\t:param y: mouse vertical position relative to top left corner.\n\t\t\"\"\"\n\t\t... # no implementation required\n\t@event(my_event_emitter) # given event emitter is prior to default one \n\tdef move(x: int, y: int)->None:\n\t\t\"\"\"\n\t\tOccures when user moves cursor over our interface.\n\t\t\n\t\t:param x: mouse horizontal position relative to top left corner.\n\t\t:param y: mouse vertical position relative to top left corner.\n\t\t\"\"\"\n\t\t...\n```\n\n# How to subscribe events?\n\nEvent is subscribed by decorating handler. **Handler can be any ordinary or async function (callable).** Decorator also can be used with or without parameters.\n\n**Notice, when using event your IDE can show help message and type hints of intended handler signature.**\n\n### Unlimited event handler\n\nThe simplest case is to just decorate handler with event. So handler will be called when event triggers.\n\n```python\n@click\ndef on_click(x: int, y: int)->None:\n\t\"\"\"\n\tProcess mouse click.\n\t\"\"\"\n\tprint(f'You have clicked at ({x}; {y}).')\n```\n\n**There is no difference between usage of single events or ones from event groups.** Hence, according to `Mouse` class example the above code can be rewritten in:\n\n```python\n@mouse.click\ndef on_click(x: int, y: int)->None:\n\t\"\"\"\n\tProcess mouse click.\n\t\"\"\"\n\tprint(f'You have clicked at ({x}; {y}).')\n```\n\n### Event handler with executions limit\n\nOtherwise, you can specify number of times to handle event. When event fires more times, no further calls will be made to the handler.\n\n```python\n@click(2)\ndef on_click(x: int, y: int)->None:\n\t\"\"\"\n\tProcess mouse click two first times.\n\t\"\"\"\n\tprint(f'You have clicked at ({x}; {y}).')\n```\n\nIn above case `on_click` will be called only for the first two times.\n\n# How to fire events?\n\nTo fire, emit, trigger event you can use `.trigger(...)` method where should pass exactly the same arguments that are defined in event signature.\n\n**Notice, when using event your IDE can show help message and type hints of intended arguments.**\n\nBasically, to trigger an event you can just call `trigger` method. Each active handler will be called with given arguments. You can use any set of arguments, including positional, universal, named, packed and even named packed.\n\n```python\nclick.trigger(12, 34)\n```\n\nAlternatively considering `Mouse` class example:\n\n```python\nmouse.click.trigger(12, 34)\n```\n\n# Reference\n\n**eventsystem.event(...)**\n\nEvent descriptor decorator. Can be used on functions or class methods. Can be used with or without parameters.\n\nWhen used with single parameter `emitter:EventEmitter` binds given event emitter to decorated event.\n\nWhen used without parameters (brackets): if method belongs to `EventDispatcher` derived class instance then uses its event emitter, else uses default emitter.\n\n**eventsystem.EventDispatcher(emitter: EventEmitter | None = None)**\n\nBase class to provide event group. Events are described as methods. Each event should have signature of `EventHandler` and decorated with `@event`.\n\nConstructor parameters:\n\n* **emitter** - event emitter to use. If not given creates a new one.\n\nFields:\n\n* **emitter** - event emitter bound to dispatcher subclass instance.\n\n**eventsystem.Event**\n\nEvent interface. Intended to be used as decorator or parametrized decorator for handlers. *Should not be instantiated directly.*\n\nFields:\n\n* **name** - read only property of event name\n\nMethods:\n\n* **get_emitter() \u2192 EventEmitter | None** - get bound event emitter. In most cases, event will have event emitter bound. But in rare cases it can be `None` when no handler descriptor has been decorated yet.\n\n* **trigger(...) \u2192 None** - fire the event. All arguments are passed to each handler. Signature must match handler descriptor.\n\n**eventsystem.emitter**\n\nDefault event emitter.\n\n**eventsystem.dispatcher**\n\nDefault event dispatcher bound to default event emitter.\n\n**eventsystem.EventHandler**\n\nBase event and event handler signature (type annotations). Signature of handler must copy signature of event.\n\n# What event emitter is?\n\nThis library us build on top of [pymitter](https://pypi.org/project/pymitter). `EventEmitter` - is a basic building block of that library which actually dispatches events, manages priority, limitations and queues.\n\nUsing `EventEmitter` directly you can unsubscribe handlers, get all active handlers or limit them. For detailed tuning read pymitter documentation.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Convenient Event System",
    "version": "1.0.0",
    "split_keywords": [
        "pymitter",
        "event",
        "system"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "md5": "c18da2c260465aa6a5e299d477ea6689",
                "sha256": "e8fe46137afffa8db4a65fa8890862795b2491fda7bd16d470775b9be2ca24a0"
            },
            "downloads": -1,
            "filename": "eventsystem-1.0.0-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c18da2c260465aa6a5e299d477ea6689",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": null,
            "size": 5634,
            "upload_time": "2022-12-06T17:00:32",
            "upload_time_iso_8601": "2022-12-06T17:00:32.856426Z",
            "url": "https://files.pythonhosted.org/packages/ec/89/23ff35324a14b49f237c36db438c2ab40a0946dc3d4918912c19f0f5166a/eventsystem-1.0.0-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "md5": "f2c908159a8bb75e63df7729e935e56b",
                "sha256": "3e8214a67641afbfff6b1892d784bd3f74b3f5d8e625b994c879b71329dbaa6a"
            },
            "downloads": -1,
            "filename": "eventsystem-1.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "f2c908159a8bb75e63df7729e935e56b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 5493,
            "upload_time": "2022-12-06T17:00:40",
            "upload_time_iso_8601": "2022-12-06T17:00:40.403701Z",
            "url": "https://files.pythonhosted.org/packages/14/8c/ec87ae2be6fac24a8207eff88a61bbab4eb2b178c79d0408c6cea4d0b050/eventsystem-1.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2022-12-06 17:00:40",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "lcname": "eventsystem"
}
        
Elapsed time: 0.01543s