Welcome to nmevent’s documentation!

nmevent v0.3.2 - 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)
...
>>> def handler(sender, **keywords):
...    print "event occured"
...
>>> example = ExampleClass()
>>> example.event += handler
>>> example.do_something()
event occured

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 Event
>>> 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.

>>> @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)
... 
...    def __init__(self):
...       self._x = None
...       self._y = None
...
>>> def handler(sender, **keywords):
...    print "x changed"
...
>>> example = ExampleClass()
>>> example.x_changed += handler
>>> example.x = 10 # handler gets called
x changed

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.3.2
Added fairly useful functionality to the adapt() function: it can now connect to “nested events”. See the functions’s documentation for more information. It can also disconnect the handlers as well.
v0.3.1
Added docstring tests and fixed all the docstrings so that they would pass. As a result, another problem was found with the event binding. That problem was fixed by adding the InstanceEvent.bind() method to be used mainly by the Property class when raising the “changed” events.
v0.3

Fixed a major bug, which caused an unbound event not to be actually bound when called with an object instance as the first argument.

Added the with_properties() class decorator, which automatically decorates a class with “private” attributes for each property and automatic getters and setters where either one of them is missing.

v0.2.1
Rewritten some unit tests and added new ones. Improved documentation a little bit.
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.1.1
No changes in source code. Improved documentation and unit tests.
v0.1
Initial release.

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 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 last two of the following statements are equivalent:

>>> event.add_handler(handler) # doctest: +SKIP
>>> event += handler # doctest: +SKIP
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 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:
  • eventEvent 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) # doctest: +SKIP
bind(objtype, obj)

Attempts to bind the instance event to an instance.

Note that this method silently fails and returns self when the event is already bound. This is by design and is meant to unify the Event‘s and InstanceEvent‘s protocol.

handlers
CallbackStore object that stores this event’s handlers.
is_bound
True if the event is bound to a sender, False otherwise.
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):
...    @nmevent.nmproperty
...    def x(self):
...       return self._x
...    
...    @x.setter
...    def x(self, value):
...       self._x = value
...    
...    x_changed = nmevent.Event()
...    x.changed = x_changed
...    
...    def __init__(self):
...       self._x = 0
...
>>> def handler(sender, **keywords):
...    print "x changed, old value: %r, new value: %r" % (keywords['old_value'], sender.x)
...
>>> example = Example()
>>> example.x_changed += handler
>>> example.x = 42
x changed, old value: 0, new value: 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 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 = nmevent.Event()
...    x.changed = x_changed
...
...    def __init__(self):
...       self._x = None
...
>>> def handler(sender, **keywords):
...    print "handler called"
...
>>> example = ExampleClass()
>>> example.x_changed += handler # "handler" will be called when the value of x changes
>>> example.x = 10 # value of x changed, "handler" gets called
handler 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 = value
...
...    def __init__(self):
...       self._x = 0
...
>>> def x_changed_handler(sender, **keywords):
...    old_value = keywords['old_value']
...    print "x changed; %r -> %r" % (old_value, sender.x)
...
>>> def property_changed_handler(sender, **keywords):
...    old_value = keywords['old_value']
...    name = keywords['name']
...    print "property '%s' changed, %r -> %r" % (name, old_value, sender.x)
...
>>> example = Example()
>>> example.x_changed += x_changed_handler
>>> example.property_changed += property_changed_handler
>>> example.x = 42
x changed; 0 -> 42
property 'x' changed, 0 -> 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.

nmevent.with_properties(clss)

Decorates a class with automatic “private” attributes.

Parameter:clss – class object to decorate.
Returns:decorated class

For every Property instance within the class’ dictionary, it creates a setter and getter (unless they are already set to a non-None value). These setters and getters use “private” attributes - attributes that the same name as the property prepended with an underscore. In other words, for a property foo, you get an attribute called _foo where the actual value is stored.

Usage:

>>> @nmevent.with_properties
... class Example(object):
...     foo = nmevent.Property()
...     foo_changed = nmevent.Event()
...     foo.changed = foo_changed
...
>>> def on_foo_changed(sender, old_value):
...     print "foo changed"
... 
>>> x = Example()
>>> x.foo_changed += on_foo_changed
>>> x.foo = 42 # on_foo_changed gets called
foo changed

Used together with with_events:

>>> @nmevent.with_events
... @nmevent.with_properties
... class NextExample(object):
...     bar = nmevent.Property()
...
nmevent.adapt(observer, observable, prefix='on_', disconnect=False)

Connects observer’s handlers to subject’s events by their names.

Parameters:
  • observer – object containing methods to connect as handler to the events
  • observable – object containing the events to connect to
  • prefix – prefix the names of handler methods start with
  • disconnect – when this is True, the function disconnects the handlers rather than connecting them
Returns:

list of tuples of events and respective handlers

Handler methods are recognized by their name. Their name must start with the prefix and the continue with the name of the event.

This function can also connect handlers to the events of subject’s attributes, even nested ones. To connect a handler to a “nested event”, separate the attribute names and the event name by double underscore (“__”). For instance, to handle the “z” event of attribute “y” of attribute “x”, name your handler “on_x__y__z” (assuming you use the default prefix “on_“).

>>> class Observer(object):
...    def on_x_happened(self, *senders, **keywords):
...       print "x happened"
... 
...    def on_y_happened(self, *senders, **keywords):
...       print "y happened"
... 
...    def on_attr__x_happened(self, *senders, **keywords):
...       print "attr.x happened"
...
>>> class Observable(object):
...    x_happened = nmevent.Event()
...    y_happened = nmevent.Event()
...
>>> observer = Observer()
>>> observable = Observable()
>>> observable.attr = Observable()
>>> nmevent.adapt(observer, observable) #doctest: +ELLIPSIS
[...]
>>> observable.x_happened()
x happened
>>> observable.y_happened()
y happened
>>> observable.attr.x_happened()
attr.x happened

Indices and tables

Table Of Contents

This Page