{ "info": { "author": "Yoseph Radding", "author_email": "yoseph@shuttl.io", "bugtrack_url": null, "classifiers": [ "Environment :: Web Environment", "Framework :: Django", "Framework :: Django :: 1.7", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content" ], "description": "# django-event-system [![PyPI version](https://badge.fury.io/py/django-event-system.svg)](https://badge.fury.io/py/django-event-system) [![Build Status](https://travis-ci.org/radding/django-event-system.svg?branch=master)](https://travis-ci.org/radding/django-event-system)\nA great little utility to implement an event system for django\n\n## About\ndjango-event-system utilizes gevent to build out an easy to use event system. This event system uses strings to track events and call event handlers.\n\n### Why not just use django's built in signals?\nUnlike django signals, django-event-system utilizes a string based system for events. This allows developers to listen using regular expressions, and add a slightly nicer interface to interact and deal with events. For example you can create events like `event::db::Model::created`, `event::db::Model::updated`, and `event::db::Model::deleted` and have a listener that listens for `event::db::Model::.*`. This listener will handle all of the defined events. \n\n## Install\ndjango-event-system requires python3 and django 1.7+. To install, just run `pip install django-event-system`\n\n## Getting started\nTo get started add `\"events\"` to your `installed_apps` in `settings.py`. That's really it.\n\n### Important note about gevent and `monkey_patch`\nDjango event system does not use [gevent's monkey patch](http://www.gevent.org/gevent.monkey.html) utility anywhere. If you want to monkey patch (and I recommend you do), you need to do it yourself. \n\n### The Dispatcher\nThe Dispatcher is the central peice to django-event-system, every single event goes through the dispatcher. While there really isn't a need to use the dipsatcher directly, you can. The dispatcher manages the event queue, the handlers, and dispatching the event. \n\n#### Registering Event Handlers\nIn order to start using the dispatcher, you first need to define your handlers:\n\n```python\nfrom events import Dispatcher\n\nDispatcher.RegisterHandler('event::name', handler)\n```\n\nThis will define a handler that will respond to `event::name` events. The handler object must be a callable object (like a function or a class with `__call__` defined). \n\nWhile it is not necessary, I recommend that you use `::` rather than `.` in your event handlers because the string you pass into register handler gets compiled into regular expression. If you want to base events off of a class and its module, there is a utility called `GetPathFromClass` you can use.\n\nYou can also, define a handler that responds to multiple events by using regular expressions:\n\n```python\nfrom events import Dispatcher\n\nDispatcher.RegisterHandler('event::name(.*)', handler)\n```\n\nThis handler will respond to any event that has a tag that begins with `event::name`. This handler will respond to `event::name::some_event` as well as `event::name:some::other::event`\n\n#### Dispatching events\nTo Dispatch an event, all you need to do is call `Dispatcher.Dispatch('event::name')`. This call will call every listener that is waiting for that event.\n\nIf you need to pass arguments to an event handler, all you need to is: `Dispatcher.Dispatch('event::name', *args, **kwargs)`\n\n#### Clearing the queue\nBecause django-event-system is based off of gevent, its possible that the queue doesn't get cleared. If you want to force the queue to be cleared after a function, you should use the `Dispatcher.EnsureQueueIsCleared` decorator. This will run your function first and then clear the queue. If you don't want to use the wrapper, you can use the `Dispatcher.ClearQueue` method instead.\n\n### The `Event` class\nThe `Event` class is really just here to stop you from making mistakes. All the `Event` class does is build a string for your event and gives you a `Dispatch` helper.\n\nFor example:\n\n```python\nfrom events import Event\n\nclass ExampleEvent(Event):\n pass\n```\n\nThis makes an event that will always have the string `::ExampleEvent`. To dispatch an `ExampleEvent`, all you need to do is `ExampleEvent.Dispatch()`. This will actually dispatch a `::ExampleEvent` event.\n\nIf you want to give your event classes a different name, you can just define the `Name` property on the class:\n\n```python\nfrom events import Event\n\nclass ExampleEvent(Event):\n Name = \"ExampleEvent\"\n pass\n```\n\nNow `ExampleEvent.Dispatch()` will dispatch an `ExampleEvent` event.\n\nIf you want to pass data using an `Event` class, use the `__init__` method to capture the input:\n\n```python\nfrom events import Event\n\nclass ExampleEvent(Event):\n def __init__(self, name, value, something):\n self.name = name\n self.value = value\n self.something = something\n pass\n pass\n```\n\nThen when dispatching: `ExampleEvent.Dispatch('name', 'value, 'something')`. This will pass an `ExampleEvent` object to the handler with `name`, `value`, and `something` defined.\n\n### The `EventListener` class\n\nThis class sets up an event listener for you. If you use this class, you don't need to do anything more than define a `listensFor` list and a `handle` method. The `handle` method will recieve the event that triggered the handler:\n\n```python\nfrom events import EventListener\nfrom my.events import ExampleEvent\n\nclass ExampleEventListener(EventListener):\n listensFor = [\n ExampleEvent,\n ]\n\n def handle(self, event):\n # handle the event here\n pass\n pass\n```\n\nThen, when you call `ExampleEvent.Dispatch`, `ExampleEventListener`'s `handle` method will automatically get called, with event refering to the `ExampleEvent` that triggered the handler.\n\nThe `listensFor` list can contain both `Event` objects and strings. These will also be compiled into regular expressions. This allows you to have an `EventListener` listen for all events for example:\n\n\n```python\nfrom events import EventListener\n\nclass ExampleEventListener(EventListener):\n listensFor = [\n '.*',\n ]\n\n def handle(self, event, *args, **kwargs):\n print(\"Event was dispatched: \", event)\n pass\n pass\n```\n\n### The `Observer` class\nIn order to respond to model events, I implemented a Model observer class called `Observer`. Using this model, you can respond to events such as creating, created, updating, updated, deleting, deleted a given model. And Observer has a method that corresponds to those events and recieves a Model object. To use a model Observer, simply inherit from the `Observer` class and define a `observes` model:\n\n```python\nfrom events import Observer\nfrom my.models import Example\n\nclass ExampleModelObserver(Observer):\n observes = Example\n\n def creating(self, eventName, example):\n print('creating a Example with name:', example.name)\n\n def created(self, eventName, example):\n print('created a Example with name:', example.name)\n\n def updating(self, eventName, example):\n print('updating a Example with name:', example.name)\n\n def updated(self, eventName, example):\n print('updated a Example with name:', example.name)\n\n def deleting(self, eventName, example):\n print('deleting a Example with name:', example.name)\n\n def deleted(self, eventName, example):\n print('deleted a Example with name:', example.name)\n```\n\nWhen you preform an action on a Model that is being Observed, an event with the style `events::db::path::to:ModelClass::*` will be created. For example, the event for creating an Example object will be, `events::db::my::models::Example::creating`. You can define other `EventListeners` to listen for these events. For example here is a listener that listens for any model that is created:\n\n```python\nfrom events import EventListener\n\nclass ExampleEventListener(EventListener):\n listensFor = [\n 'events::db::(.*)::created',\n ]\n\n def handle(self, event, example):\n print(\"An Example was created:\", example)\n pass\n pass\n```\n\nOne thing to note is that only objects that have an `Observer` defined will create events. If you want a Model to dispatch events without defining an `Observer`, you can use `RegisterSignalsFor` to map the signals to Events:\n\n```python\nfrom events import RegisterSignalsFor\nfrom my.models import Example\n\nRegisterSignalsFor(Example)\n```\n\nNow the signals will dispatch events. I recommend doing this as early in your application as possible.\n\n#### Registering Observers\nI recommend you keep all of your app observers in an observer directory. Then inside that dir's `__init__.py` import all of your observers. \n\nIn django, if you try to use Models before the application is ready, then you will get the infamous `django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.` exception. To solve this, go to `apps.py` in your django app and define a `ready` method. Inside this method, import your observers like so: `import myapp.observers`. Then in the app's `__init__.py` file put this code: `default_app_config = 'myapp.apps.MyAppConfig'`. Wnen the models are ready, the observers will get registered! So you `apps.py` should look like this:\n\n```python\nfrom django.apps import AppConfig\n\nclass MyAppConfig(AppConfig):\n name = 'myapp'\n\n def ready(self):\n import myapp.observers \n```\n\nand your `__init__.py` looks like this:\n\n```python\ndefault_app_config = 'myapp.apps.MyAppConfig\n```\n\n#### Events for non observed models\nIf the Model doesn't have an observer, no events will be dispatched for that model. In order to make un-observed models dispatch events, use the `RegisterSignalsFor` function. This function juust takes a model class and will register all the signal objects and map them to an event based on the model's name.\n\n### Mapping Signals to Events\nDjango commes with it's own signal ideas. If you want to listen for some event in django, you would use a signal. But, as these signals are object based, its more boiler plate to listen to multiple signals or to listen to a class of events. \n\nBut django is based on signals, so in order to make it easier to just use events instead, django-event-system comes with `SignalToEvent` to help convert a signal to an event. Basically, this just connects a listener to a signal and then dispatches an event. The way this works is like so:\n\n```python\nfrom events import SignalToEvent\nfrom some.signals import signal\n\nSignalToEvent(signal, event='event::from::signal')\n\n# OR\n\nSignalToEvent(signal, event=ExampleEvent)\n```\n\nIf you want to handle events from a specific sender, you can also define a sender on the `SignalToEvent` call:\n\n```python\nSignalToEvent(signal, event=ExampleEvent, sender=SomeSender)\n```\n\nAnd finally, sometimes you just want to have more complex event dispatching. For that you can define a hook:\n\n```python\ndef hook(*args, **kwargs):\n if args[0] == 1:\n return 'event::when::1'\n elif args[1] == 2:\n return 'event::when::2'\n return 'event::when::none'\n\nSignalToEvent(signal, hook=hook)\n```\n\nthe hook function must return an event or a string!\n\n*Note:* Do not call `Event.Dispatch` inside of a hook function. If you do, the event will be dispatched twice!\n\n## Testing\nIf you would like to test that certain events are raised during the course of your unit tests, django-event-system provide several utilies to make that easier.\n\n### `Dispatcher.MocksDispatch` decorator\nThis decorator mocks and puts the Dispatcher into a test mode. In side this environment, you are able to use a function like `Dispatcher.Expect()`.\n\nUsage:\n\n```python\nfrom events.events import Dispatcher\nfrom django.test import TestCase\n\nclass TestExample(TestCase):\n\n @Dispatcher.MocksDispatch\n def test_SignalToEvent(self):\n Dispatcher.Expect('some::event::to::be::fired')\n Dispatcher.Dispatch('some::event::to::be::fired')\n pass\n pass\n```\n\n### `Dispatcher.Expect()`\nThis lets the dispatcher know you are looking for this event, but to not actually call the listeners on the event. If the event is never called, the test will fail and the message will tell you exactly what events where not raised.\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://yoseph.tech/", "keywords": "django events events for django", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "django-event-system", "package_url": "https://pypi.org/project/django-event-system/", "platform": "", "project_url": "https://pypi.org/project/django-event-system/", "project_urls": { "Documentation": "https://github.com/radding/django-events/blob/master/README.md", "Homepage": "http://yoseph.tech/", "Source": "https://github.com/radding/django-events/", "Tracker": "https://github.com/radding/django-events/issues" }, "release_url": "https://pypi.org/project/django-event-system/1.0.1/", "requires_dist": [ "gevent", "greenlet", "pytz" ], "requires_python": ">=3.5", "summary": "A simple package that implements an event system in django", "version": "1.0.1" }, "last_serial": 5983800, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "f566a1ac64f83114a19c9e9cb5d014c3", "sha256": "105d353b9b2e3a2896c456c7fb2698c85dcb567d14d2c74a7e6206e0b6dcc42f" }, "downloads": -1, "filename": "django-event-system-0.1.0.tar.gz", "has_sig": false, "md5_digest": "f566a1ac64f83114a19c9e9cb5d014c3", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3", "size": 9174, "upload_time": "2018-02-23T13:02:37", "url": "https://files.pythonhosted.org/packages/c6/8a/121fc680df2c2f7742b9ef893e8928a75b1b054a65b574d7618c51f3a557/django-event-system-0.1.0.tar.gz" } ], "0.1.0.dev2": [ { "comment_text": "", "digests": { "md5": "d008ad610bd06606f8e125ba1be1db17", "sha256": "43b94d28911c08758b7a24a676d5e18307268b6928e232cb56e15bf5760f2e1a" }, "downloads": -1, "filename": "django-event-system-0.1.0.dev2.tar.gz", "has_sig": false, "md5_digest": "d008ad610bd06606f8e125ba1be1db17", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3", "size": 9179, "upload_time": "2018-02-23T13:04:50", "url": "https://files.pythonhosted.org/packages/be/5f/d62d83e216dcc8c6b65ce3733923ce0568232f1ac73f387c8e12725773f9/django-event-system-0.1.0.dev2.tar.gz" } ], "0.1.0.dev3": [ { "comment_text": "", "digests": { "md5": "2d051e0ccdfa4390c5d9133c280fa10c", "sha256": "0f1781d9d7d26d0a1f6a685751573ca6842a20217673a61e5a1d4188444e1da5" }, "downloads": -1, "filename": "django-event-system-0.1.0.dev3.tar.gz", "has_sig": false, "md5_digest": "2d051e0ccdfa4390c5d9133c280fa10c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 9270, "upload_time": "2018-02-23T13:19:55", "url": "https://files.pythonhosted.org/packages/90/a3/64fda88cb586a2385fb677a66dfa21f461417fa765b5f9d6c3e414f856f3/django-event-system-0.1.0.dev3.tar.gz" } ], "0.1.1.dev1": [ { "comment_text": "", "digests": { "md5": "01ecfb1a15b6a02a8cd21cb36332c1dc", "sha256": "1efadaf1c739eeefebb98ee4fcb910fb4f4efeb4ab1c338f6aa67a0539614b94" }, "downloads": -1, "filename": "django-event-system-0.1.1.dev1.tar.gz", "has_sig": false, "md5_digest": "01ecfb1a15b6a02a8cd21cb36332c1dc", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 9265, "upload_time": "2018-02-24T15:33:12", "url": "https://files.pythonhosted.org/packages/0f/a2/6f9898d155a77c3dbef0e320fc132d17a67d838020cb8dcfa1d1aa192bb5/django-event-system-0.1.1.dev1.tar.gz" } ], "0.2.0.dev1": [ { "comment_text": "", "digests": { "md5": "be2aced9517593fc8a64f622b1151e0a", "sha256": "ffb5ee8494c0ad75d9997732538018351e2db0e9854ae6b747663da57102a2bb" }, "downloads": -1, "filename": "django-event-system-0.2.0.dev1.tar.gz", "has_sig": false, "md5_digest": "be2aced9517593fc8a64f622b1151e0a", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 9742, "upload_time": "2018-02-24T18:13:42", "url": "https://files.pythonhosted.org/packages/86/a4/6d5d6066066ae040cae9bea97449fcf68f91f6aa16c8de19ff686c321e72/django-event-system-0.2.0.dev1.tar.gz" } ], "0.3.0.dev1": [ { "comment_text": "", "digests": { "md5": "6270f180af2112a36823d02ad5d1797c", "sha256": "c46147e6b959f938bcf9798eba821653c4ae56cd1e0bd0fbd1c67dfe9306992e" }, "downloads": -1, "filename": "django-event-system-0.3.0.dev1.tar.gz", "has_sig": false, "md5_digest": "6270f180af2112a36823d02ad5d1797c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 9935, "upload_time": "2018-03-03T15:04:55", "url": "https://files.pythonhosted.org/packages/4a/17/d10f4122933684a0102aa5aada504419276611da5bf4b07c43db0262ab69/django-event-system-0.3.0.dev1.tar.gz" } ], "0.3.1.dev1": [ { "comment_text": "", "digests": { "md5": "462597c06bac08a75e270f7a27835775", "sha256": "6c81435e9ec07b2258ea2843dce77e2c2d8799c40e5da20e724105aba406cc5a" }, "downloads": -1, "filename": "django-event-system-0.3.1.dev1.tar.gz", "has_sig": false, "md5_digest": "462597c06bac08a75e270f7a27835775", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 10185, "upload_time": "2018-03-03T15:24:21", "url": "https://files.pythonhosted.org/packages/2c/51/59dcd2e38b55e59c9f54d9777af4b4d76b7ad951dc225bacfe97e4b4d4dc/django-event-system-0.3.1.dev1.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "086bc24b682f72f8cff7c9ffc7754a1f", "sha256": "7c308fe27a22a9e7daf99133af291e3d0014c0a3378534c812681ad06560e62d" }, "downloads": -1, "filename": "django_event_system-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "086bc24b682f72f8cff7c9ffc7754a1f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 14264, "upload_time": "2019-10-16T14:37:02", "url": "https://files.pythonhosted.org/packages/db/48/3deb574b01ba55bcfb3ebc1834a21a917e39cbda21a4c0b77369b7b06284/django_event_system-1.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d915ad6152fb1ca812074a4a878886ed", "sha256": "e87bc1c399f1eaaaf220db7da66be2005fedf0c5724098d2786f816adb274af8" }, "downloads": -1, "filename": "django-event-system-1.0.1.tar.gz", "has_sig": false, "md5_digest": "d915ad6152fb1ca812074a4a878886ed", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 10913, "upload_time": "2019-10-16T14:37:06", "url": "https://files.pythonhosted.org/packages/86/44/88217b18b8c5be1b2dd068013117eaee2d588f316a052125d697eb7c389d/django-event-system-1.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "086bc24b682f72f8cff7c9ffc7754a1f", "sha256": "7c308fe27a22a9e7daf99133af291e3d0014c0a3378534c812681ad06560e62d" }, "downloads": -1, "filename": "django_event_system-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "086bc24b682f72f8cff7c9ffc7754a1f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 14264, "upload_time": "2019-10-16T14:37:02", "url": "https://files.pythonhosted.org/packages/db/48/3deb574b01ba55bcfb3ebc1834a21a917e39cbda21a4c0b77369b7b06284/django_event_system-1.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d915ad6152fb1ca812074a4a878886ed", "sha256": "e87bc1c399f1eaaaf220db7da66be2005fedf0c5724098d2786f816adb274af8" }, "downloads": -1, "filename": "django-event-system-1.0.1.tar.gz", "has_sig": false, "md5_digest": "d915ad6152fb1ca812074a4a878886ed", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 10913, "upload_time": "2019-10-16T14:37:06", "url": "https://files.pythonhosted.org/packages/86/44/88217b18b8c5be1b2dd068013117eaee2d588f316a052125d697eb7c389d/django-event-system-1.0.1.tar.gz" } ] }