| callbacks (version 0.1) | index /Users/dewitt/svn/python-callbacks/callbacks.py |
A library for creating, registering, and running callbacks and closures.
Features include:
Scopes: Callbacks can be registered in instance, class, module or
global scope.
Priority levels: Callbacks can be given a priority, where higher
priority callbacks will be run before lower priority callbacks.
Automatic requeuing: Callbacks can be marked as 'permanent' to be
requeued automatically after being executed.
Callback chaining: Callbacks can be chained such that the result of
the previous callback is used as the input for the next callback.
Genericity: Any callable object (a Callback instance, a function, a
anonymous lambda function, or a class-based closure) can be used as
a callback.
Iterators: Callbacks can be invoked automatically via
RunAllCallbacks and RunCallbackChain, or iterated over manually with
CallbackIterator and CopyFirstCallbackIterator.
Overview:
A callback is callable code that can be registered and executed at
some point in the future in a different context. The callback
pattern allows a library to handle arbitrary clients without knowing
in advance what they will be.
The callback registration mechanism that allows client code to tell
a class or module about a method that should be executed at a point
in time in the future.
The library is used by two distinct groups: classes or modules that
wish to register and execute callbacks, and clients that wish to
register their code to be called back.
Classes or modules that register and run callbacks should first
write a wrapper function around the RegisterCallback method. This
method will be invoked by other classes and code that want to be
called back at some point in the future. E.g.:
>>> from callbacks import RegisterCallback, RunAllCallbacks
>>>
>>> class CallbackHost(object):
... def RegisterSomeCallback(self, callback):
... '''Registers the callback to be invoked by RunAllCallbacks.'''
... RegisterCallback(self, 'some callback', callback)
...
... def RunTheCallbacks(self):
... '''Returns the results (in a list) of each registed callback.'''
... return RunAllCallbacks(self, 'some callback')
To use the registration methods, clients will provide a callback
method in the form of a function, an anonymous lambda function, a
class-based callable closure, or a Callback, PermanentCallback, or
TemporaryCallback (or subclass) instance.
After the callbacks have been registered, the class can run all of the
registed callbacks automatically by executing the RunAllCallbacks
method. E.g.:
>>> from callbacks import PermanentCallback, Callback
>>> def MyCallback():
... return 'MyCallback'
>>> host = CallbackHost()
>>> callback = PermanentCallback(MyCallback)
>>> host.RegisterSomeCallback(callback)
>>> host.RunTheCallbacks()
['MyCallback']
Class-based closures can also be registered:
>>> class MyClosure(object):
... def __init__(self, string):
... self.string = string
... def __call__(self):
... return self.string
>>> callback = PermanentCallback(MyClosure('MyClosure'))
>>> host.RegisterSomeCallback(callback)
>>> host.RunTheCallbacks()
['MyCallback', 'MyClosure']
As can higher-priority callbacks:
>>> callback = PermanentCallback(MyClosure('High priority'), priority=10)
>>> host.RegisterSomeCallback(callback)
>>> host.RunTheCallbacks()
['High priority', 'MyCallback', 'MyClosure']
By default, callbacks expire after being run once:
>>> callback = Callback(MyClosure('Run once'))
>>> host.RegisterSomeCallback(callback)
>>> host.RunTheCallbacks()
['High priority', 'MyCallback', 'MyClosure', 'Run once']
>>> host.RunTheCallbacks()
['High priority', 'MyCallback', 'MyClosure']
Another Example:
Imagine a TextProcessor class that is used to modify text. The
TextProcessor want to allow callers to provide their own text
processing routines, and does so via a RegisterProcessor method.
When the Process method is invoked, so are the registered text
processors.
>>> import callbacks
>>> class TextProcessor(object):
... '''A class that modifies text according to registered callbacks.'''
...
... def RegisterProcessor(self, callback):
... '''Registers a callback to be invoked when the text.'''
... callbacks.RegisterCallback(self, 'process', callback)
...
... def Process(self, text):
... '''Runs all of the registered callbacks to process the text.'''
... return callbacks.RunCallbackChain(self, 'process', text)
And defined elsewhere:
>>> import re
>>> def Capitalize(text):
... '''Returns the text with the first character capitalized.'''
... return text.capitalize()
>>> def CompactWhitespace(text):
... '''Returns the text with successive whitespace characters collapsed.'''
... return re.sub('\s+', ' ', text)
>>> processor = TextProcessor()
>>> processor.RegisterProcessor(Capitalize)
>>> processor.RegisterProcessor(CompactWhitespace)
>>> processor.Process('four score and seven years ago...')
'Four score and seven years ago...'
Limitations and TODOs:
Function signatures are not validated. Helper code could be written
to match callback signatures against a function specification or
against a reference function.
Exceptions raised by callbacks are not handled automatically yet.
Automatic registration and execution wrappers could be created to
simplify the one-line boilerplate functions.
| Modules | ||||||
| ||||||
| Classes | ||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||
| Functions | ||
| ||
| Data | ||
| __author__ = 'dewitt@unto.net' __version__ = '0.1' | ||
| Author | ||
| dewitt@unto.net | ||