Metadata-Version: 1.1
Name: evproc
Version: 0.1.1
Summary: Event Processor Framework
Home-page: https://github.com/klmitch/evproc
Author: Kevin L. Mitchell
Author-email: kevin.mitchell@rackspace.com
License: UNKNOWN
Description: =========================
        Event Processor Framework
        =========================
        
        EvProc is a framework for building complex event processors.  There
        are many similar frameworks available, so why EvProc?  EvProc provides
        a few advantages.  First, a ``@want()`` decorator is available to
        pre-filter events, as opposed to registering an event handler on
        specific events.  (Filtering works primarily with an event name,
        allowing flexibility, but it is also possible to register arbitrary
        filter functions, which can evaluate an event to determine whether to
        call the event handler.)  A second advantage is the ability to
        register event handlers using the "entrypoint" support of
        ``setuptools``, allowing extensibility.  Finally, EvProc provides
        limited inter-handler communication, using the ``proc`` property of
        ``Event`` instances, and backs this up with the ability to specify
        ordering among event handlers using requirements.
        
        Defining Events
        ===============
        
        An *event* in EvProc is an instance of a subclass of ``evproc.Event``.
        The ``Event`` class is an abstract class; subclasses must implement a
        ``name`` property, the contents of which will be a unique string
        naming the event.  The constructor takes a single optional argument, a
        ``ctxt`` argument, which may be used to pass a context in to event
        handlers.  The constructor may, of course, be extended as required to
        provide any necessary information about the event, such as a specific
        resource that the event occurred on.
        
        The ``Event`` class also provides a special ``proc`` property.  This
        property may be used by event handlers to store data for use by other
        event handlers, by setting attributes.  To minimize the possibility of
        two unrelated handlers attempting to manipulate the same attribute,
        the ``proc`` property is *namespaced*; that is, attributes on ``proc``
        may not be set, but attributes on those attributes may.
        
        As an example of the use of the ``proc`` property, consider two event
        handlers that wish to communicate with each other.  Assume that these
        handlers interact to perform testing; perhaps one handler selects test
        parameters, and the second handler actually performs the test (e.g.,
        there may be multiple tests to perform).  The first handler could set,
        say, ``proc.tests.args`` and ``proc.tests.kwargs``, and the second
        handler would then retrieve the values it needed.
        
        Event Handlers
        ==============
        
        An *event handler* is simply a function that will be called with two
        arguments; the first argument will be an instance of
        ``evproc.Processor`` (described below), and the second will be an
        instance of ``evproc.Event`` (described above).  The event handler may
        perform any code necessary to process the event.  Each event handler
        must have a unique name; by default, the name will be the same as the
        function name, but this may be overridden when registering the event
        handler.  Event handlers may also be loaded from ``setuptools``
        entrypoints; in this case, the name used is the entrypoint name.
        
        There are three optional decorators that may be used on an event
        handler function.  The first is the ``@evproc.want()`` decorator,
        which may be called with one or more event names; the handler function
        will only be called if the event being handled matches one of these
        event names.  The decorator may also be passed one or more *filter
        functions*; in this case, the event handler will only be called if all
        of the filter functions return ``True``.  If event names are also
        passed, then the event must also match one of those names.  The
        ``@want()`` decorator may be used multiple times to specify other sets
        of filters; the event need only match *one* filter specified by
        ``@want()``.  For instance, consider this example::
        
            @evproc.want('ev1', 'ev2')
            @evproc.want('ev3', lambda ev: ev.resource.name == 'resource')
            def handler(proc, ev):
                ...
        
        This handler will be called for all events with the names "ev1" and
        "ev2", but will only be called for the event "ev3" if
        ``ev.resource.name`` contains the value ``"resource"``.
        
        Event handlers are able to interact with each other, as mentioned
        above.  To do this, it is necessary to enforce certain ordering
        guarantees on the event handler.  This is controlled by the
        ``@evproc.requires()`` and ``@evproc.required_by()`` decorators.
        These decorators take the names of one or more event handlers (names
        are set at registration time, as mentioned above).  The
        ``@requires()`` decorator is used to indicate that the specified
        functions must run *before* the decorated function, while the
        ``@required_by()`` decorator is used to indicate that the specified
        function require the *decorated* function to be run first.  In both
        cases, the ``@want()`` decorators of the functions must be compatible.
        
        The ``@requires()`` and ``@required_by()`` decorators are used to
        define a *dependency graph*, which is then topologically sorted to
        ensure that the handler functions are called in the correct order.  As
        an example, consider the split test functions mentioned above.  We
        could declare the functions like so::
        
            def test_prepare(proc, ev):
                ...
                ev.tests.args = args
                ev.tests.kwargs = kwargs
        
            @evproc.requires('test_prepare')
            def test_run(proc, ev):
                args = ev.tests.args
                kwargs = ev.tests.kwargs
                intermediate = getattr(ev.tests, 'auxiliary', [])
                ...
        
            @evproc.required_by('test_run')
            @evproc.requires('test_prepare')
            def test_auxiliary(proc, ev):
                ...
                ev.tests.auxiliary = results
        
        In this example, the ``test_prepare()`` handler function would be
        called first, followed by the ``test_auxiliary()`` handler function,
        and finally the ``test_run()`` handler function would be called.
        
        The Event Processor
        ===================
        
        The ``evproc.Processor`` class is responsible for processing events.
        To use EvProc, instantiate a ``Processor`` instance and use its
        ``register()`` or ``load_from()`` methods to declare event handler
        functions.  Then, simply pass ``Event`` instances to the ``process()``
        method to invoke the event processors in the correct order.
        
        The ``Processor.register()`` method may be used to register individual
        event handler functions.  By default, the function's declared name
        (``func.__name__``) is used as the handler name, but this may be
        overridden by passing the optional ``name`` parameter to
        ``register()``.
        
        To load event handlers from a ``setuptools`` entrypoint, use the
        ``Processor.load_from()`` method.  This method takes, as its sole
        argument, the entrypoint group name; as an example, if one installed
        application has a ``setup.py`` containing::
        
            entry_points={
                'app.handlers': [
                    'test_prepare = app:test_prepare',
                    'test_run = app.test_run',
                ],
            }
        
        And if a second installed application has the following in its
        ``setup.py``::
        
            entry_points={
                'app.handlers': [
                    'test_auxiliary = otherapp:test_auxiliary',
                ],
            }
        
        Then all three handler functions could be loaded into the
        ``Processor`` instance ``proc`` with the following call::
        
            proc.load_from('app.handlers')
        
        The ``Processor.process()`` method may be called as many times as
        necessary.  In fact, most event-driven applications consist of a loop
        which constructs ``Event`` instances, then passes them to the
        ``Processor.process()`` method.  A full application could look
        something like the following::
        
            def main():
                proc = evproc.Processor()
                proc.load_from('app.handlers')
        
                while True:
                    # Construct event objects
                    ...
                    ev = AppEvent(...)
        
                    # Process the event
                    proc.process(ev)
        
        Stop Processing
        ---------------
        
        It may be necessary for one event processor to stop all event
        processing.  This could, for instance, be used by a processor that
        performs an authorization check if the event fails that check.  To
        allow this, an event processor may raise the ``evproc.StopProcessing``
        exception.  The ``StopProcessing`` exception may be initialized with a
        ``retval`` parameter that will become the return value of
        ``Processor.process()`` (which normally returns ``None``).
        
        Conclusion
        ==========
        
        EvProc provides an easy to extend event processing framework, capable
        of not only calling event handler functions, but of ensuring certain
        ordering constraints and limited inter-handler communication.  The
        ability to use ``setuptools`` entrypoints allows new event handlers to
        be inserted into the event processing loop easily without having to
        modify the original application, and the ordering constraints can
        allow such inserted event handlers to interact with the existing ones
        just as easily.
        
        
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
