--- /dev/null
+__pycache__/
--- /dev/null
+Copyright (c) 2020 Graph-IT GmbH
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+# Event Module
+
+This is a small Python module that provides classes with the possibility
+to register and emit events.
+
+A more detailed documentation can be found in [doc/index.md](doc/index.md),
+which can also be found at [http://docs.graph-it.com/graphit/event-py](http://docs.graph-it.com/graphit/event-py).
--- /dev/null
+# Event Module
+
+This is a small Python module that provides classes with the possibility
+to register and emit events.
+
+## Installation
+
+The event module can be installed with `pip` directly from our git repository:
+```sh
+$ pip install git+git://git.graph-it.com/graphit/event-py.git
+```
+
+## Usage
+
+The module provides an abstract `EventEmitterInterface` and a class
+`EventEmitterMixin` implementing it.
+
+Classes that want to emit events should extend the `EventEmitterMixin` and
+call `self._emit(event, *args, **kwargs)` at appropriate locations in their
+code.
+The designations of the events can be any kind of hashables.
+
+Listener callbacks can then be registered with the `on(event, callback)`
+method.
+The callbacks should receive exactly the arguments provided in the
+`self._emit` calls.
+A handle to the registration is returned by the call to `on`, which can be
+used to unregister the callback again by the `off(handle)` method.
+
+A simple example can be found in the `example.py` script in the git
+repositiory.
--- /dev/null
+#!/usr/bin/env python3
+from graphit.event import EventEmitterMixin
+
+
+class ExampleEmitter(EventEmitterMixin):
+
+ def emitting_function(self):
+ self._emit('ev1', 'ham')
+ self._emit('ev2')
+ self._emit('ev3', 42)
+
+
+def cb11(message: str):
+ print(f"cb11 reveived '{message}'.")
+
+
+def cb12(message: str):
+ print(f"cb12 reveived '{message}'.")
+
+
+def cb2():
+ print("cb2 called")
+
+
+def cb3(value: int):
+ print(f"cb3 received value {value}.")
+
+
+emitter = ExampleEmitter()
+handle11 = emitter.on('ev1', cb11)
+handle12 = emitter.on('ev1', cb12)
+handle2 = emitter.on('ev2', cb2)
+handle3 = emitter.on('ev3', cb3)
+print("cb11, cb12, cb2 and cb3 are on.")
+emitter.emitting_function()
+emitter.off(handle12)
+emitter.off(handle2)
+print("cb12 and cb2 are off, cb11 and cb3 are still on.")
+emitter.emitting_function()
--- /dev/null
+__all__ = ('EventEmitterInterface', 'EventEmitterMixin')
+
+
+import abc
+import uuid
+
+from typing import Hashable, Callable, MutableMapping, Mapping
+
+EvMap = MutableMapping[Hashable, Hashable]
+CbMap = MutableMapping[Hashable, MutableMapping[Hashable, Callable]]
+
+
+class EventEmitterInterface(abc.ABC):
+
+ @abc.abstractmethod
+ def on(self, event: Hashable, callback: Callable) -> Hashable:
+ '''
+ Registers the given callback for the given event.
+ Returns handle to unregister the given callback.
+ '''
+
+ @abc.abstractmethod
+ def off(self, handle: Hashable) -> bool:
+ '''
+ Unregisters a previously registered callback by the given handle.
+ Returns True on success.
+ '''
+
+ @abc.abstractmethod
+ def _emit(self, event: Hashable, *args, **kwargs) -> None:
+ '''
+ Emits the given event by calling all callbacks registered for this
+ event.
+ '''
+
+
+class EventEmitterMixin(EventEmitterInterface):
+ def on(self, event: Hashable, callback: Callable) -> Hashable:
+ events: EvMap
+ callbacks: CbMap
+ try:
+ events = self._eventEmitterMixinEvents
+ callbacks = self._eventEmitterMixinCallbacks
+ except AttributeError:
+ self._eventEmitterMixinEvents: EvMap = {}
+ self._eventEmitterMixinCallbacks: CbMap = {}
+ events = self._eventEmitterMixinEvents
+ callbacks = self._eventEmitterMixinCallbacks
+
+ if event not in callbacks:
+ callbacks[event] = {}
+
+ handle = uuid.uuid4()
+ while handle in events:
+ handle = uuid.uuid4()
+
+ events[handle] = event
+ callbacks[event][handle] = callback
+
+ return handle
+
+ def off(self, handle: Hashable) -> bool:
+ try:
+ events = self._eventEmitterMixinEvents
+ callbacks = self._eventEmitterMixinCallbacks
+ except AttributeError:
+ return False
+
+ if handle not in events:
+ return False
+ event = events[handle]
+
+ del events[handle]
+ del callbacks[event][handle]
+
+ if not callbacks[event]:
+ del callbacks[event]
+
+ return True
+
+ def _emit(self, event: Hashable, *args, **kwargs) -> None:
+ try:
+ callbacks = self._eventEmitterMixinCallbacks
+ except AttributeError:
+ return
+
+ if event not in callbacks:
+ return
+
+ for callback in callbacks[event].values():
+ callback(*args, **kwargs)
--- /dev/null
+import setuptools
+
+with open("README.md", "r") as readme_file:
+ long_description = readme_file.read()
+
+setuptools.setup(
+ name="graphit-event",
+ version="0.1.0",
+ author="Graph-IT GmbH",
+ author_email="info@graph-it.com",
+ description="Event Module",
+ long_description=long_description,
+ long_description_content_type="text/markdown",
+ url="http://docs.graph-it.com/graphit/event-py",
+ packages=setuptools.find_packages(),
+ setup_requires=[
+ "wheel"
+ ],
+ install_requires=[
+ ],
+ classifiers=[
+ "Programming Language :: Python",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ ],
+)