
Welcome to nmevent's documentation!
***********************************

nmevent v0.2.1 - C#-like implementation of the Observer pattern

This is a Python module ``nmevent``, simple C#-like implementation of
the Observer pattern (http://en.wikipedia.org/wiki/Observer_pattern).
It's main purpose and goal is to allow developers to use events with
C#-like syntax in their Python classes.


Usage example
=============

The most straightfoward way to use ``nmevent`` is this:

>>> import nmevent
>>> class ExampleClass(object):
...    def __init__(self):
...       self.event = nmevent.Event()
...
...    def _do_something(self):
...       self.event(self)
...
>>> example = ExampleClass()
>>> example.event += handler

It should be noted, that event doesn't necessarily need to be an
object attribute. ``Event`` instance is basically just a callable
object that works as a sort of "dispatch demultiplexer".

This usage, however, isn't very C#-like. In C#, events are declared in
class scope and that's why the ``Event`` class also supports the
descriptor protocol (you can use the same way you use the built-in
``property`` object).

>>> from nmevent import EventSlot
>>> class ExampleClass(object):
...    event = Event()
...
...    def _do_something(self):
...       self.event()
...
>>> def handler(sender, **keywords):
...    pass
...
>>> example = ExampleClass()
>>> example.event += handler

Perhaps this looks even more straightfoward than instantiating
``Event`` in object's constructor, but there's actually lot more going
on under hood this time.

Finally, there is the ``Property`` descriptor and the associated
``nmproperty()`` function decorator, which work very much like the
built-in ``property`` object and decorator, except it can optionally
call a callback function if the property's value changes after calling
the setter function. It can work in tandem with the ``with_events()``
class decorator, which decorates the class with property change events
and connects them to the instances of ``Property`` class. It also
creates events for the built-in ``property`` objects, but you have to
raise the events yourself in the setter function or elsewhere.

>>> import nmevent
>>> @nmevent.with_events
... class ExampleClass(object):
...    @nmevent.nmproperty
...    def x(self):
...       return self._x
...
...    @x.setter
...    def x(self, value):
...       self._x = value
...
...    @property
...    def y(self):
...       return self._y
...
...    @y.setter
...    def y(self, value):
...       old_value, self._y = self._y, value
...       self.y_changed(old_value = old_value)
...
>>> example = ExampleClass()
>>> example.x_changed += handler
>>> example.x = 10 # handler gets called


License
=======

Copyright (c) 2010, Jan Milík.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope the it will be useful, but
WITHOUT ANY WARRANTY; without event the implied warranty of
MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this program.  If not, see
<http://www.gnu.org/licenses/>.


Changes
=======

v0.1
   Initial release.

v0.1.1
   No changes in source code. Improved documentation and unit tests.

v0.2
   Rewritten most of the code. The ``Event`` class now works as a
   descriptor, which eliminated the need for a separate ``EventSlot``
   class and simplified usage. Added ``CallbackStore`` to abstract the
   callback storage.

v0.2.1
   Rewritten some unit tests and added new ones. Improved
   documentation a little bit.


``nmevent`` module
******************

nmevent.EVENTS_ATTRIBUTE

   Value of this module variable is the name of the attribute that is
   used to store event data in object instances when ``Event`` is used
   as an descriptor.

   If the class of your object uses the ``__slots__`` attribute, don't
   forget to include the value of this variable in the sequence you
   assign to ``__slots__``.

   Example:

   >>> class Example(object):
   ...    __slots__ = ('foo', 'bar', nmevent.EVENTS_ATTRIBUTE, )
   ...    event = nmevent.Event()


Types
=====

class class nmevent.Event

   Subject in the observer pattern.

   This class represents the subject in the observer pattern. It keeps
   a collection of handlers, which correspond to the observers in the
   observer pattern.

   Usage:

   >>> class Example(object):
   ...    def __init__(self):
   ...       self.event = Event()
   ...
   ...    def fire(self):
   ...       self.event(self)
   ...

   add_handler(handler)

      Adds a handler (observer) to this event.

      ``__iadd__`` attribute of this class is just an alias of this
      method, so the two following statements are equivalent:

      >>> event.add_handler(handler)
      >>> event += handler

   bind(objtype, obj=None)

      Binds the event to a class and optionally an instance.

   disconnect()

      Disconnects this event from all handlers.

   fire(sender, *args, **keywords)

      Fires this event and calls all of its handlers.

   handlers

      Collection of this event's handlers.

   has_handler(handler)

      Returns True if handler is this event's handler.

      Returns True if the specified handler is contained in the
      collection of this event's handlers.

   remove_handler(handler)

      Removes a handler from this event.

      Removes a handler (observer) from the collection of this event's
      handlers.

class class nmevent.InstanceEvent(event, clss, sender=None)

   Bound or unbound event.

   In Python, unbound actually means bound to a class. Bound means
   bound to both a class and an instance. Instances of this class
   cannot be bound to the  ``None`` object, because it is used to
   indicate that the event is unbound.

   This class is meant to be instantiated either through the
   ``Event``'s descriptor protocol, or by the ``Event.bind()`` method.

   Parameters:
      * *event* -- ``Event`` instance to be bound

      * *clss* -- class the event should be bound to (sender must be
        of this type)

      * *sender* -- sender to bind the event to

   __slots__

      This class uses the ``__slots__`` attribute to save memory, so
      don't try to assign new attributes to its instances.

   im_event

      ``Event`` instance that is bound.

   im_class

      Class object this event is bound to.

   im_sender

      Instance this event is bound to.

      The following condition must be always true:

      >>> isinstance(self.im_sender, self.im_class)

   handlers

      ``CallbackStore`` object that stores this event's handlers.

   is_bound

      ``True`` if the event is bound to a sender, ``False`` otherwise.

class class nmevent.Property(fget=None, fset=None, fdel=None, changed=None, property_changed=None)

   Eventful property descriptor.

   This class is not meant to be used directly by the client code,
   even though nothing stops you from doing so. Instances of this
   class are supposed to be created by the ``nmproperty()`` decorator.

   Parameters:
      * *fget* -- getter function

      * *fget* -- getter function

      * *fset* -- setter function

      * *fdel* -- deleter function

      * *changed* -- value changed notification event

      * *property_changed* -- a value changed notification event

   Usage:

   >>> class Example(object):
   ...    @nmproperty:
   ...    def x(self):
   ...       return self._x
   ...
   ...    @x.setter
   ...    def x(self, value):
   ...       self._x = value
   ...
   >>> example = Example()
   >>> exmaple.x_changed += handler
   >>> example.x = 42

   fget

      Getter function.

      If non-None, this function gets called every time the value of
      the property is retrieved. The return value of this function is
      returned as the value of the property.

   fset

      Setter function.

      If non-None, this function gets called every time the value of
      the property is set. This function is responsible for actually
      storing the value somehow.

      If this attribute is ``None``, the property is considered read-
      only.

   fget

      Deleter function.

   changed

      Value change notification event.

      If non-None, this event will be raised every time this property
      is set to a different value.

   property_changed

      Property value change notification event.

      If none-None, this event will be raised every time this property
      is set to a different value. Unlike the ``changed`` event,
      however, the handlers of this event will also be passed keyword
      ``name``, which will contain the name of this property (see
      ``Property.name``).

      This is can be used when you need to watch for change of any
      property, but need to know which one changed.

      This event has been inspired by the .NET framework's
      ``INotifyPropertyChanged`` interface (see
      http://msdn.microsoft.com/en-
      US/library/system.componentmodel.inotifypropertychanged.aspx)

   deleter(function)

      Method decorator to set the delete function.

      Works exatcly like the built-in @property.deleter.

      Parameter:
         *function* -- the property deleter function

      Returns:
         self

   name

      The name of the property.

      This should be the name of an object's attribute that holds the
      property. The name is guessed by retrieving the name of the
      getter function if present.

   setter(function)

      Sets the setter function and returns self.

      This function is meant to be used as a method decorator, even
      though it can be called directly to set the setter function of
      its property.

      Usage:

      >>> class ExampleClass(object):
      ...    def x(self):
      ...       return self._x
      ...
      ...    # @nmevent.nmproperty could have been used,
      ...    # but this way it is more obvious what x is.
      ...    x = nmevent.Property(x)
      ...
      ...    # This is how it's supposed to be used.
      ...    @x.setter
      ...    def set_x(self, value):
      ...       self._x = value
      ...
      ...    # Also works, but looks ugly.
      ...    x.setter(set_x)
      ...

      Parameter:
         *function* -- the property setter function

      Returns:
         self

class class nmevent.CallbackStore

   Collection of callbacks.

   add(callback)

      Adds a callback to callection.

      Parameter:
         *callback* -- callable object to be added

   call(*args, **keywords)

      Calls all callbacks with the given arguments.

   clear()

      Removes all callbacks from collection.

   contains(callback)

      Returns ``True`` is ``callback`` is in the collection.

      Parameter:
         *callback* -- callback to check for

   count()

      Returns the number of callbacks in the collection.

   remove(callback)

      Removes a callback from the collection.

      Parameter:
         *callback* -- callback to be removed


Functions
=========

nmevent.nmproperty(function)

   Eventful property decorator.

   Creates new ``Property`` object using the decorated method as the
   getter function. Setter and deleter functions can be set by the
   ``Property.setter()`` and ``Property.deleter()`` decorators.

   This decorator is called ``nmproperty()`` to avoid name conflict
   with the built-in *property* function and decorator.

   Usage:

   >>> class ExampleClass(object):
   ...    @nmevent.nmproperty
   ...    def x(self):
   ...       return self._x
   ...    
   ...    @x.setter
   ...    def x(self, value)
   ...       self._x = value
   ...
   ...    x_changed = EventSlot
   ...    x.changed = x_changed
   ...
   >>> example = ExampleClass()
   >>> example.x_changed += handler # "handler" will be called when the value of x changes
   >>> example.x = 10 # value of x changed, "handler" should get called

   The ``Property.changed`` events can be automatically created and
   set by the ``with_events()`` decorator when used on the class.

   Parameter:
      *function* -- function to be used as the property getter
      function

   Returns:
      new *Property* object

nmevent.with_events(clss)

   Decorates a class with some automatic event slots.

   Parameter:
      *clss* -- class object to be decorated

   Returns:
      decorated class

   Automatically adds property change notification events of the name
   "x_changed", where x is the name of the property.

   Usage:

   >>> @nmevent.with_events
   ... class Example(object):
   ...    @nmevent.nmproperty
   ...    def x(self):
   ...       return self._x
   ...
   ...    @x.setter
   ...    def x(self, value):
   ...       self._x
   ...
   ...    def __init__(self):
   ...       self._x = 0
   ...
   >>> def x_changed_handler(self, sender, **keywords):
   ...    old_value = keywords['old_value']
   ...    print "x changed; %r -> %r" % (old_value, sender.x)
   ...
   >>> def property_changed_handler(self, sender, **keywords):
   ...    old_value = keywords['old_value']
   ...    name = keywords['name']
   ...    print "property "%s" changed, %r -> %r" % (old_value, sender.x)
   ...
   >>> example = Example()
   >>> example.x_changed += x_changed_handler
   >>> example.property_changed_handler += property_changed_handler
   >>> example.x = 42

   In the example above, the ``with_events()`` decorator automatically
   decorates the class with an ``x_changed`` event and
   ``property_changed`` event connects them to the instance of
   ``Property`` class created by the ``nmproperty()`` decorator.

   Simply put, the class has ``x_changed`` event and
   ``property_changed`` events that are raised when the value of
   ``Example.x`` changes. ``x_changed`` gets called only when
   ``Example.x`` changes, ``property_changed`` gets called when any
   property changes.


Indices and tables
******************

* *Index*

* *Search Page*
