Louie-1.1/doc/template/layout.css 0000644 0001750 0001750 00000004077 10343352420 017265 0 ustar gldnspud gldnspud @import url("pudge.css");
@import url("almodovar.css");
/* Basic Style
----------------------------------- */
h1.pudge-member-page-heading {
font-size: 300%;
}
h4.pudge-member-page-subheading {
font-size: 130%;
font-style: italic;
margin-top: -2.0em;
margin-left: 2em;
margin-bottom: .3em;
color: #aaa; /* #FF5000 */
}
p.pudge-member-blurb {
font-style: italic;
font-weight: bold;
font-size: 120%;
margin-top: 0.2em;
color: #999;
}
p.pudge-member-parent-link {
margin-top: 0;
}
/*div.pudge-module-doc {
max-width: 45em;
}*/
div.pudge-section {
margin-left: 2em;
max-width: 45em;
}
/* Section Navigation
----------------------------------- */
div#pudge-section-nav
{
margin: 1em 0 1.5em 0;
padding: 0;
height: 20px;
}
div#pudge-section-nav ul {
border: 0;
margin: 0;
padding: 0;
list-style-type: none;
text-align: center;
border-right: 1px solid #aaa;
}
div#pudge-section-nav ul li
{
display: block;
float: left;
text-align: center;
padding: 0;
margin: 0;
}
div#pudge-section-nav ul li .pudge-section-link,
div#pudge-section-nav ul li .pudge-missing-section-link
{
background: #aaa;
width: 9em;
height: 1.8em;
border: 1px solid #bbb;
padding: 0;
margin: 0 0 10px 0;
color: #ddd;
text-decoration: none;
display: block;
text-align: center;
font: 11px/20px "Verdana", "Lucida Grande";
cursor: hand;
text-transform: lowercase;
}
div#pudge-section-nav ul li a:hover {
color: #000;
background: #fff;
}
div#pudge-section-nav ul li .pudge-section-link {
background: #888;
color: #eee;
border: 1px solid #bbb;
}
/* Module Lists
----------------------------------- */
dl.pudge-module-list dt {
font-style: normal;
font-size: 110%;
}
dl.pudge-module-list dd {
color: #555;
}
/* Misc Overrides */
.rst-doc p.topic-title a {
color: #777;
}
.rst-doc ul.auto-toc a,
.rst-doc div.contents a {
color: #333;
}
pre { background: #eee; }
.rst-doc dl dt {
color: #444;
margin-top: 1em;
font-weight: bold;
}
.rst-doc dl dd {
margin-top: .2em;
}
.rst-doc hr {
display: block;
margin: 2em 0;
} Louie-1.1/doc/template/almodovar.css 0000644 0001750 0001750 00000007554 10343352420 017737 0 ustar gldnspud gldnspud /*
Theme Name: Almodovar
Theme URI: http://blog.ratterobert.com/archiv/2005/03/09/almodovar/
Description: Das Theme basiert im Ursprung auf Michael Heilemanns Kubrick-Template und ist von dem einen oder anderen Gimmick anderer sehr guter Templates inspiriert worden.
Version: 0.7
Author: ratte / robert
Author URI: http://blog.ratterobert.com/
*/
/* Begin Typography & Colors */
body {
font-size: 75%;
font-family: 'Lucida Grande', 'Trebuchet MS', 'Bitstream Vera Sans', Sans-Serif;
background-color: #d5d6d7;
color: #333;
text-align: center;
}
#page {
background-color: #fff;
border: 1px solid #999;
text-align: left;
}
#content {
font-size: 1.2em;
margin: 0;
}
#content p,
#content ul,
#content blockquote {
line-height: 1.6em;
}
#footer {
border-top: 1px solid #000;
margin-top: 2em;
}
small {
font-family: 'Trebuchet MS', Arial, Helvetica, Sans-Serif;
font-size: 0.9em;
line-height: 1.5em;
}
h1, h2, h3 {
font-family: 'Trebuchet MS', 'Lucida Grande', Verdana, Arial, Sans-Serif;
font-weight: bold;
margin-top: .7em;
margin-bottom: .7em;
}
h1 {
font-size: 2.5em;
}
h2 {
font-size: 2em;
}
h3 {
font-size: 1.5em;
}
h1, h2, h3 {
color: #777;
}
h1 a, h2 a, h3 a {
color: #777;
}
h1, h1 a, h1 a:hover, h1 a:visited,
h2, h2 a, h2 a:hover, h2 a:visited,
h3, h3 a, h3 a:hover, h3 a:visited,
cite {
text-decoration: none;
}
#content p a:visited {
color: #c63
font-weight: normal;
}
small, blockquote, strike {
color: #777;
}
#links ul ul li, #links li {
list-style: none;
}
code {
font: 1.1em 'Courier', 'Courier New', Fixed;
}
acronym, abbr, span.caps {
font-size: 0.9em;
letter-spacing: .07em;
}
a {
color: #FF5000;
text-decoration: none;
font-weight: bold;
}
a:hover {
color: #FF8000;
}
/* Special case doc-title */
h1.doc-title {
text-transform: lowercase;
font-size: 4em;
margin: 0;
}
h1.doc-title a {
display: block;
padding-left: 0.8em;
padding-bottom: .5em;
padding-top: .5em;
margin: 0;
border-bottom: 1px #fff solid;
}
h1.doc-title,
h1.doc-title a,
h1.doc-title a:visited,
h1.doc-title a:hover {
text-decoration: none;
color: #FF5000;
}
/* End Typography & Colors */
/* Begin Structure */
body {
margin: 0;
padding: 0;
}
#page {
background-color: white;
margin: 0 auto 0 9em;
padding: 0;
max-width: 60em;
border: 1px solid #959596;
}
* html #page {
width: 60em;
}
#content {
margin: 0 1em 0 3em;
}
#content h1 {
margin-left: 0;
}
#footer {
padding: 0 0 0 1px;
margin: 0;
margin-top: 1.5em;
clear: both;
}
#footer p {
margin: 1em;
}
/* End Structure */
/* Begin Headers */
.description {
text-align: center;
}
/* End Headers */
/* Begin Form Elements */
#searchform {
margin: 1em auto;
text-align: right;
}
#searchform #s {
width: 100px;
padding: 2px;
}
#searchsubmit {
padding: 1px;
}
/* End Form Elements */
/* Begin Various Tags & Classes */
acronym, abbr, span.caps {
cursor: help;
}
acronym, abbr {
border-bottom: 1px dashed #999;
}
blockquote {
margin: 15px 30px 0 10px;
padding-left: 20px;
border-left: 5px solid #CCC;
}
blockquote cite {
margin: 5px 0 0;
display: block;
}
hr {
display: none;
}
a img {
border: none;
}
.navigation {
display: block;
text-align: center;
margin-top: 10px;
margin-bottom: 60px;
}
/* End Various Tags & Classes*/
span a { color: #CCC; }
span a:hover { color: #FF5000; }
#navcontainer {
margin-top: 0px;
padding-top: 0px;
width: 100%;
background-color: #CCC;
text-align: right;
}
#navlist ul {
margin-left: 0;
margin-right: 5px;
padding-left: 0;
white-space: nowrap;
}
#navlist li {
display: inline;
list-style-type: none;
}
#navlist a {
padding: 3px 10px;
color: #fff;
background-color: #999;
text-decoration: none;
border: 1px solid #CCC;
font-weight: normal;
}
#navlist a:hover {
color: #000;
background-color: #FFF;
text-decoration: none;
font-weight: normal;
}
Louie-1.1/doc/index.txt 0000644 0001750 0001750 00000003733 10351547423 015301 0 ustar gldnspud gldnspud =======
Louie
=======
Louie provides Python programmers with a straightforward way to
dispatch signals between objects in a wide variety of contexts. It is
based on PyDispatcher_, which in turn was based on a highly-rated
recipe_ in the Python Cookbook.
Louie is licensed under `The BSD License`_.
.. _PyDispatcher: http://pydispatcher.sf.net/
.. _recipe: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/87056
.. _The BSD License: http://opensource.org/licenses/bsd-license.php
.. sectnum::
.. contents::
Louie Requirements
==================
- Python 2.3 or higher.
Installing Louie
================
Louie uses `easy_install`_ for installation, and is distributed via
the `Python Cheese Shop`_.
.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall
.. _Python Cheese Shop: http://cheeseshop.python.org/pypi/Louie
If you already have `easy_install`, run this command::
easy_install Louie
If that failed, you do not have `easy_install`. To install that,
download `ez_setup.py`_, then run this command::
python ez_setup.py setuptools
.. _ez_setup.py: http://peak.telecommunity.com/dist/ez_setup.py
Then, run the `easy_install` command given above again.
Upgrading Louie
===============
Run this command to upgrade Louie to the latest release::
easy_install -U Louie
Development
===========
You can track the latest changes in Louie using the Subversion
repository or using regularly-built snapshots.
Using easy_install
------------------
Provided you already have `easy_install`, run this command::
easy_install -f http://louie.berlios.de/dist/ Louie
Using Subversion
----------------
Check out the Louie trunk as per the `Berlios usage information
`__.
Run this command inside your checkout path::
python setup.py install
Alternatively, you may use Louie directly from your checkout path
using this command::
python setup.py develop
..
Local Variables:
mode: rst
End:
Louie-1.1/doc/about.txt 0000644 0001750 0001750 00000004531 10343351446 015300 0 ustar gldnspud gldnspud =======
About
=======
Louie is based on PyDispatcher_. Here is a very good detailed
description of what both PyDispatcher and Louie offer you, based on
the description available at the PyDispatcher website.
.. _PyDispatcher: http://pydispatcher.sf.net/
.. sectnum::
.. contents::
PyDispatcher provides a multiple-producer-multiple-consumer
signal-registration and routing infrastructure, suitable for use in
multiple contexts.
The dispatcher mechanism is particularly useful when constructing
Model-View-Controller style applications where it is not desirable to
have the Model objects aware of the event model.
To be more concrete about what PyDispatcher does for you, here are
some specifics:
* A centralized service delivers messages to registered objects in the
local process. You can register any number of functions or other
callable objects which can receive signals from senders.
- Registration can be for any sender, particular sending objects, or
"anonymous" messages (messages where the sender is None).
- Registration can be for all signals, or particular signals.
- A single signal will be delivered to all appropriate registered
receivers, so that multiple registrations do not interfere with
each other.
- The sender or receiver need not be be dispatcher-aware. Any
Python object, except for `None`, can act as a sender, and any
callable object can act as a receiver.
- The system uses weak references to receivers wherever possible.
- Object lifetimes are not affected by PyDispatcher registrations.
When your object goes away, the registrations related to the
object also go away.
- References to common transient objects (instance methods in
particular) are stored as compound weak references.
- Weak references can be disabled on a registration-by-registration
basis.
* It allows rich signal types. Signals must simply be hashable
objects; they are otherwise opaque to the dispatch mechanism.
* Positional and keyword arguments may be attached to a signal when
sending. For each receiver, PyDispatcher sends each receiver the
arguments that they expect; other arguments are silently dropped.
Thus, receivers can be general in nature, even ignoring all
arguments, or they can be specific, accepting whichever arguments it
needs.
..
Local Variables:
mode: rst
End:
Louie-1.1/doc/changes.txt 0000644 0001750 0001750 00000002551 10351547365 015604 0 ustar gldnspud gldnspud =========
Changes
=========
This document provides a detailed list of changes made to Louie,
including differences between PyDispatcher_ and the initial release of
Louie.
.. _PyDispatcher: http://cheeseshop.python.org/pypi/PyDispatcher
.. sectnum::
.. contents::
Changes from PyDispatcher to Louie 1.0
======================================
Packaging and Distribution
--------------------------
- Louie uses setuptools_ for managing its placement in the Python
package hierarchy.
.. _setuptools: http://www.python.org/pypi/setuptools
Naming and Importing
--------------------
- The package name for Louie is `louie`.
- The preferred way of using Louie is to only import the `louie`
package, e.g.::
import louie
louie.connect(...)
louie.send(...)
- Function and method names are lowercase_with_underscores, to conform
to PEP-0008.
Plug-ins
--------
* Louie provides globally-registered plug-ins that augment various
aspects of Louie's operation.
* Available plug-ins include the following:
- `QtWidgetPlugin` knows how to handle Qt widgets that still exist
as Python objects, but whose C++ objects have been destroyed.
- `TwistedDispatchPlugin` converts Louie's default synchronous
signal dispatching behavior to an asynchronous behavior based on
Twisted Deferred objects.
..
Local Variables:
mode: rst
End:
Louie-1.1/louie/ 0000755 0001750 0001750 00000000000 10367467325 014004 5 ustar gldnspud gldnspud Louie-1.1/louie/test/ 0000755 0001750 0001750 00000000000 10367467325 014763 5 ustar gldnspud gldnspud Louie-1.1/louie/test/fixture.py 0000644 0001750 0001750 00000000000 10343130373 016771 0 ustar gldnspud gldnspud Louie-1.1/louie/test/test_dispatcher.py 0000644 0001750 0001750 00000011477 10345321060 020511 0 ustar gldnspud gldnspud import unittest
import louie
from louie import dispatcher
def x(a):
return a
class Dummy(object):
pass
class Callable(object):
def __call__(self, a):
return a
def a(self, a):
return a
class TestDispatcher(unittest.TestCase):
def setUp(self):
louie.reset()
def _isclean(self):
"""Assert that everything has been cleaned up automatically"""
assert len(dispatcher.senders_back) == 0, dispatcher.senders_back
assert len(dispatcher.connections) == 0, dispatcher.connections
assert len(dispatcher.senders) == 0, dispatcher.senders
def test_Exact(self):
a = Dummy()
signal = 'this'
louie.connect(x, signal, a)
expected = [(x, a)]
result = louie.send('this', a, a=a)
assert result == expected, (
"Send didn't return expected result:\n\texpected:%s\n\tgot:%s"
% (expected, result))
louie.disconnect(x, signal, a)
assert len(list(louie.get_all_receivers(a, signal))) == 0
self._isclean()
def test_AnonymousSend(self):
a = Dummy()
signal = 'this'
louie.connect(x, signal)
expected = [(x, a)]
result = louie.send(signal, None, a=a)
assert result == expected, (
"Send didn't return expected result:\n\texpected:%s\n\tgot:%s"
% (expected, result))
louie.disconnect(x, signal)
assert len(list(louie.get_all_receivers(None, signal))) == 0
self._isclean()
def test_AnyRegistration(self):
a = Dummy()
signal = 'this'
louie.connect(x, signal, louie.Any)
expected = [(x, a)]
result = louie.send('this', object(), a=a)
assert result == expected, (
"Send didn't return expected result:\n\texpected:%s\n\tgot:%s"
% (expected, result))
louie.disconnect(x, signal, louie.Any)
expected = []
result = louie.send('this', object(), a=a)
assert result == expected, (
"Send didn't return expected result:\n\texpected:%s\n\tgot:%s"
% (expected, result))
assert len(list(louie.get_all_receivers(louie.Any, signal))) == 0
self._isclean()
def test_AllRegistration(self):
a = Dummy()
signal = 'this'
louie.connect(x, louie.All, a)
expected = [(x, a)]
result = louie.send('this', a, a=a)
assert result == expected, (
"Send didn't return expected result:\n\texpected:%s\n\tgot:%s"
% (expected, result))
louie.disconnect(x, louie.All, a)
assert len(list(louie.get_all_receivers(a, louie.All))) == 0
self._isclean()
def test_GarbageCollected(self):
a = Callable()
b = Dummy()
signal = 'this'
louie.connect(a.a, signal, b)
expected = []
del a
result = louie.send('this', b, a=b)
assert result == expected, (
"Send didn't return expected result:\n\texpected:%s\n\tgot:%s"
% (expected, result))
assert len(list(louie.get_all_receivers(b, signal))) == 0, (
"Remaining handlers: %s" % (louie.get_all_receivers(b, signal),))
self._isclean()
def test_GarbageCollectedObj(self):
class x:
def __call__(self, a):
return a
a = Callable()
b = Dummy()
signal = 'this'
louie.connect(a, signal, b)
expected = []
del a
result = louie.send('this', b, a=b)
assert result == expected, (
"Send didn't return expected result:\n\texpected:%s\n\tgot:%s"
% (expected, result))
assert len(list(louie.get_all_receivers(b, signal))) == 0, (
"Remaining handlers: %s" % (louie.get_all_receivers(b, signal),))
self._isclean()
def test_MultipleRegistration(self):
a = Callable()
b = Dummy()
signal = 'this'
louie.connect(a, signal, b)
louie.connect(a, signal, b)
louie.connect(a, signal, b)
louie.connect(a, signal, b)
louie.connect(a, signal, b)
louie.connect(a, signal, b)
result = louie.send('this', b, a=b)
assert len(result) == 1, result
assert len(list(louie.get_all_receivers(b, signal))) == 1, (
"Remaining handlers: %s" % (louie.get_all_receivers(b, signal),))
del a
del b
del result
self._isclean()
def test_robust(self):
"""Test the sendRobust function."""
def fails():
raise ValueError('this')
a = object()
signal = 'this'
louie.connect(fails, louie.All, a)
result = louie.send_robust('this', a, a=a)
err = result[0][1]
assert isinstance(err, ValueError)
assert err.args == ('this', )
Louie-1.1/louie/test/conftest.py 0000644 0001750 0001750 00000000126 10343130373 017141 0 ustar gldnspud gldnspud import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
Louie-1.1/louie/test/test_robustapply.py 0000644 0001750 0001750 00000001377 10343130373 020750 0 ustar gldnspud gldnspud import unittest
from louie.robustapply import robust_apply
def no_argument():
pass
def one_argument(blah):
pass
def two_arguments(blah, other):
pass
class TestRobustApply(unittest.TestCase):
def test_01(self):
robust_apply(no_argument, no_argument)
def test_02(self):
self.assertRaises(TypeError, robust_apply, no_argument, no_argument,
'this' )
def test_03(self):
self.assertRaises(TypeError, robust_apply, one_argument, one_argument)
def test_04(self):
"""Raise error on duplication of a particular argument"""
self.assertRaises(TypeError, robust_apply, one_argument, one_argument,
'this', blah='that')
Louie-1.1/louie/test/test_saferef.py 0000644 0001750 0001750 00000004162 10343130373 017772 0 ustar gldnspud gldnspud import unittest
from louie.saferef import safe_ref
class _Sample1(object):
def x(self):
pass
def _sample2(obj):
pass
class _Sample3(object):
def __call__(self, obj):
pass
class TestSaferef(unittest.TestCase):
# XXX: The original tests had a test for closure, and it had an
# off-by-one problem, perhaps due to scope issues. It has been
# removed from this test suite.
def setUp(self):
ts = []
ss = []
for x in xrange(5000):
t = _Sample1()
ts.append(t)
s = safe_ref(t.x, self._closure)
ss.append(s)
ts.append(_sample2)
ss.append(safe_ref(_sample2, self._closure))
for x in xrange(30):
t = _Sample3()
ts.append(t)
s = safe_ref(t, self._closure)
ss.append(s)
self.ts = ts
self.ss = ss
self.closure_count = 0
def tearDown(self):
if hasattr(self, 'ts'):
del self.ts
if hasattr(self, 'ss'):
del self.ss
def test_In(self):
"""Test the `in` operator for safe references (cmp)"""
for t in self.ts[:50]:
assert safe_ref(t.x) in self.ss
def test_Valid(self):
"""Test that the references are valid (return instance methods)"""
for s in self.ss:
assert s()
def test_ShortCircuit(self):
"""Test that creation short-circuits to reuse existing references"""
sd = {}
for s in self.ss:
sd[s] = 1
for t in self.ts:
if hasattr(t, 'x'):
assert sd.has_key(safe_ref(t.x))
else:
assert sd.has_key(safe_ref(t))
def test_Representation(self):
"""Test that the reference object's representation works
XXX Doesn't currently check the results, just that no error
is raised
"""
repr(self.ss[-1])
def _closure(self, ref):
"""Dumb utility mechanism to increment deletion counter"""
self.closure_count += 1
Louie-1.1/louie/test/test_plugin.py 0000644 0001750 0001750 00000007703 10367466260 017676 0 ustar gldnspud gldnspud """Louie plugin tests."""
import unittest
import louie
try:
import qt
if not hasattr(qt.qApp, 'for_testing'):
_app = qt.QApplication([])
_app.for_testing = True
qt.qApp = _app
except ImportError:
qt = None
class ReceiverBase(object):
def __init__(self):
self.args = []
self.live = True
def __call__(self, arg):
self.args.append(arg)
class Receiver1(ReceiverBase):
pass
class Receiver2(ReceiverBase):
pass
class Plugin1(louie.Plugin):
def is_live(self, receiver):
"""ReceiverBase instances are only live if their `live`
attribute is True"""
if isinstance(receiver, ReceiverBase):
return receiver.live
return True
class Plugin2(louie.Plugin):
def is_live(self, receiver):
"""Pretend all Receiver2 instances are not live."""
if isinstance(receiver, Receiver2):
return False
return True
def test_only_one_instance():
louie.reset()
plugin1a = Plugin1()
plugin1b = Plugin1()
louie.install_plugin(plugin1a)
# XXX: Move these tests into test cases so we can use unittest's
# 'assertRaises' method.
try:
louie.install_plugin(plugin1b)
except louie.error.PluginTypeError:
pass
else:
raise Exception('PluginTypeError not raised')
def test_is_live():
louie.reset()
# Create receivers.
receiver1a = Receiver1()
receiver1b = Receiver1()
receiver2a = Receiver2()
receiver2b = Receiver2()
# Connect signals.
louie.connect(receiver1a, 'sig')
louie.connect(receiver1b, 'sig')
louie.connect(receiver2a, 'sig')
louie.connect(receiver2b, 'sig')
# Check reception without plugins.
louie.send('sig', arg='foo')
assert receiver1a.args == ['foo']
assert receiver1b.args == ['foo']
assert receiver2a.args == ['foo']
assert receiver2b.args == ['foo']
# Install plugin 1.
plugin1 = Plugin1()
louie.install_plugin(plugin1)
# Make some receivers not live.
receiver1a.live = False
receiver2b.live = False
# Check reception.
louie.send('sig', arg='bar')
assert receiver1a.args == ['foo']
assert receiver1b.args == ['foo', 'bar']
assert receiver2a.args == ['foo', 'bar']
assert receiver2b.args == ['foo']
# Remove plugin 1, install plugin 2.
plugin2 = Plugin2()
louie.remove_plugin(plugin1)
louie.install_plugin(plugin2)
# Check reception.
louie.send('sig', arg='baz')
assert receiver1a.args == ['foo', 'baz']
assert receiver1b.args == ['foo', 'bar', 'baz']
assert receiver2a.args == ['foo', 'bar']
assert receiver2b.args == ['foo']
# Install plugin 1 alongside plugin 2.
louie.install_plugin(plugin1)
# Check reception.
louie.send('sig', arg='fob')
assert receiver1a.args == ['foo', 'baz']
assert receiver1b.args == ['foo', 'bar', 'baz', 'fob']
assert receiver2a.args == ['foo', 'bar']
assert receiver2b.args == ['foo']
if qt is not None:
def test_qt_plugin():
louie.reset()
# Create receivers.
class Receiver(qt.QWidget):
def __init__(self):
qt.QObject.__init__(self)
self.args = []
def receive(self, arg):
self.args.append(arg)
receiver1 = Receiver()
receiver2 = Receiver()
# Connect signals.
louie.connect(receiver1.receive, 'sig')
louie.connect(receiver2.receive, 'sig')
# Destroy receiver2 so only a shell is left.
receiver2.close(True)
# Check reception without plugins.
louie.send('sig', arg='foo')
assert receiver1.args == ['foo']
assert receiver2.args == ['foo']
# Install plugin.
plugin = louie.QtWidgetPlugin()
louie.install_plugin(plugin)
# Check reception with plugins.
louie.send('sig', arg='bar')
assert receiver1.args == ['foo', 'bar']
assert receiver2.args == ['foo']
Louie-1.1/louie/test/__init__.py 0000644 0001750 0001750 00000000000 10343130373 017042 0 ustar gldnspud gldnspud Louie-1.1/louie/error.py 0000644 0001750 0001750 00000001101 10367466260 015475 0 ustar gldnspud gldnspud """Error types for Louie."""
class LouieError(Exception):
"""Base class for all Louie errors"""
class DispatcherError(LouieError):
"""Base class for all Dispatcher errors"""
class DispatcherKeyError(KeyError, DispatcherError):
"""Error raised when unknown (sender, signal) specified"""
class DispatcherTypeError(TypeError, DispatcherError):
"""Error raised when inappropriate signal-type specified (None)"""
class PluginTypeError(TypeError, LouieError):
"""Error raise when trying to install more than one plugin of a
certain type."""
Louie-1.1/louie/sender.py 0000644 0001750 0001750 00000002237 10343130373 015622 0 ustar gldnspud gldnspud """Sender classes."""
class _SENDER(type):
"""Base metaclass for sender classes."""
def __str__(cls):
return '' % (cls.__name__, )
class Any(object):
"""Used to represent either 'any sender'.
The Any class can be used with connect, disconnect, send, or
sendExact to denote that the sender paramater should react to any
sender, not just a particular sender.
"""
__metaclass__ = _SENDER
class Anonymous(object):
"""Singleton used to signal 'anonymous sender'.
The Anonymous class is used to signal that the sender of a message
is not specified (as distinct from being 'any sender').
Registering callbacks for Anonymous will only receive messages
sent without senders. Sending with anonymous will only send
messages to those receivers registered for Any or Anonymous.
Note: The default sender for connect is Any, while the default
sender for send is Anonymous. This has the effect that if you do
not specify any senders in either function then all messages are
routed as though there was a single sender (Anonymous) being used
everywhere.
"""
__metaclass__ = _SENDER
Louie-1.1/louie/robustapply.py 0000644 0001750 0001750 00000004144 10343362411 016725 0 ustar gldnspud gldnspud """Robust apply mechanism.
Provides a function 'call', which can sort out what arguments a given
callable object can take, and subset the given arguments to match only
those which are acceptable.
"""
def function(receiver):
"""Get function-like callable object for given receiver.
returns (function_or_method, codeObject, fromMethod)
If fromMethod is true, then the callable already has its first
argument bound.
"""
if hasattr(receiver, '__call__'):
# receiver is a class instance; assume it is callable.
# Reassign receiver to the actual method that will be called.
c = receiver.__call__
if hasattr(c, 'im_func') or hasattr(c, 'im_code'):
receiver = c
if hasattr(receiver, 'im_func'):
# receiver is an instance-method.
return receiver, receiver.im_func.func_code, 1
elif not hasattr(receiver, 'func_code'):
raise ValueError(
'unknown reciever type %s %s' % (receiver, type(receiver)))
return receiver, receiver.func_code, 0
def robust_apply(receiver, signature, *arguments, **named):
"""Call receiver with arguments and appropriate subset of named.
``signature`` is the callable used to determine the call signature
of the receiver, in case ``receiver`` is a callable wrapper of the
actual receiver."""
signature, code_object, startIndex = function(signature)
acceptable = code_object.co_varnames[
startIndex + len(arguments):
code_object.co_argcount
]
for name in code_object.co_varnames[
startIndex:startIndex + len(arguments)
]:
if named.has_key(name):
raise TypeError(
'Argument %r specified both positionally '
'and as a keyword for calling %r'
% (name, signature)
)
if not (code_object.co_flags & 8):
# fc does not have a **kwds type parameter, therefore
# remove unacceptable arguments.
for arg in named.keys():
if arg not in acceptable:
del named[arg]
return receiver(*arguments, **named)
Louie-1.1/louie/dispatcher.py 0000644 0001750 0001750 00000046332 10351551410 016472 0 ustar gldnspud gldnspud """Multiple-producer-multiple-consumer signal-dispatching.
``dispatcher`` is the core of Louie, providing the primary API and the
core logic for the system.
Internal attributes:
- ``WEAKREF_TYPES``: Tuple of types/classes which represent weak
references to receivers, and thus must be dereferenced on retrieval
to retrieve the callable object
- ``connections``::
{ senderkey (id) : { signal : [receivers...] } }
- ``senders``: Used for cleaning up sender references on sender
deletion::
{ senderkey (id) : weakref(sender) }
- ``senders_back``: Used for cleaning up receiver references on receiver
deletion::
{ receiverkey (id) : [senderkey (id)...] }
"""
import os
import weakref
try:
set
except NameError:
from sets import Set as set, ImmutableSet as frozenset
from louie import error
from louie import robustapply
from louie import saferef
from louie.sender import Any, Anonymous
from louie.signal import All
# Support for statistics.
if __debug__:
connects = 0
disconnects = 0
sends = 0
def print_stats():
print ('\n'
'Louie connects: %i\n'
'Louie disconnects: %i\n'
'Louie sends: %i\n'
'\n') % (connects, disconnects, sends)
if 'PYDISPATCH_STATS' in os.environ:
import atexit
atexit.register(print_stats)
WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
connections = {}
senders = {}
senders_back = {}
plugins = []
def reset():
"""Reset the state of Louie.
Useful during unit testing. Should be avoided otherwise.
"""
global connections, senders, senders_back, plugins
connections = {}
senders = {}
senders_back = {}
plugins = []
def connect(receiver, signal=All, sender=Any, weak=True):
"""Connect ``receiver`` to ``sender`` for ``signal``.
- ``receiver``: A callable Python object which is to receive
messages/signals/events. Receivers must be hashable objects.
If weak is ``True``, then receiver must be weak-referencable (more
precisely ``saferef.safe_ref()`` must be able to create a
reference to the receiver).
Receivers are fairly flexible in their specification, as the
machinery in the ``robustapply`` module takes care of most of the
details regarding figuring out appropriate subsets of the sent
arguments to apply to a given receiver.
Note: If ``receiver`` is itself a weak reference (a callable), it
will be de-referenced by the system's machinery, so *generally*
weak references are not suitable as receivers, though some use
might be found for the facility whereby a higher-level library
passes in pre-weakrefed receiver references.
- ``signal``: The signal to which the receiver should respond.
If ``All``, receiver will receive all signals from the indicated
sender (which might also be ``All``, but is not necessarily
``All``).
Otherwise must be a hashable Python object other than ``None``
(``DispatcherError`` raised on ``None``).
- ``sender``: The sender to which the receiver should respond.
If ``Any``, receiver will receive the indicated signals from any
sender.
If ``Anonymous``, receiver will only receive indicated signals
from ``send``/``send_exact`` which do not specify a sender, or
specify ``Anonymous`` explicitly as the sender.
Otherwise can be any python object.
- ``weak``: Whether to use weak references to the receiver.
By default, the module will attempt to use weak references to
the receiver objects. If this parameter is ``False``, then strong
references will be used.
Returns ``None``, may raise ``DispatcherTypeError``.
"""
if signal is None:
raise error.DispatcherTypeError(
'Signal cannot be None (receiver=%r sender=%r)'
% (receiver, sender))
if weak:
receiver = saferef.safe_ref(receiver, on_delete=_remove_receiver)
senderkey = id(sender)
if connections.has_key(senderkey):
signals = connections[senderkey]
else:
connections[senderkey] = signals = {}
# Keep track of senders for cleanup.
# Is Anonymous something we want to clean up?
if sender not in (None, Anonymous, Any):
def remove(object, senderkey=senderkey):
_remove_sender(senderkey=senderkey)
# Skip objects that can not be weakly referenced, which means
# they won't be automatically cleaned up, but that's too bad.
try:
weak_sender = weakref.ref(sender, remove)
senders[senderkey] = weak_sender
except:
pass
receiver_id = id(receiver)
# get current set, remove any current references to
# this receiver in the set, including back-references
if signals.has_key(signal):
receivers = signals[signal]
_remove_old_back_refs(senderkey, signal, receiver, receivers)
else:
receivers = signals[signal] = []
try:
current = senders_back.get(receiver_id)
if current is None:
senders_back[receiver_id] = current = []
if senderkey not in current:
current.append(senderkey)
except:
pass
receivers.append(receiver)
# Update stats.
if __debug__:
global connects
connects += 1
def disconnect(receiver, signal=All, sender=Any, weak=True):
"""Disconnect ``receiver`` from ``sender`` for ``signal``.
- ``receiver``: The registered receiver to disconnect.
- ``signal``: The registered signal to disconnect.
- ``sender``: The registered sender to disconnect.
- ``weak``: The weakref state to disconnect.
``disconnect`` reverses the process of ``connect``, the semantics for
the individual elements are logically equivalent to a tuple of
``(receiver, signal, sender, weak)`` used as a key to be deleted
from the internal routing tables. (The actual process is slightly
more complex but the semantics are basically the same).
Note: Using ``disconnect`` is not required to cleanup routing when
an object is deleted; the framework will remove routes for deleted
objects automatically. It's only necessary to disconnect if you
want to stop routing to a live object.
Returns ``None``, may raise ``DispatcherTypeError`` or
``DispatcherKeyError``.
"""
if signal is None:
raise error.DispatcherTypeError(
'Signal cannot be None (receiver=%r sender=%r)'
% (receiver, sender))
if weak:
receiver = saferef.safe_ref(receiver)
senderkey = id(sender)
try:
signals = connections[senderkey]
receivers = signals[signal]
except KeyError:
raise error.DispatcherKeyError(
'No receivers found for signal %r from sender %r'
% (signal, sender)
)
try:
# also removes from receivers
_remove_old_back_refs(senderkey, signal, receiver, receivers)
except ValueError:
raise error.DispatcherKeyError(
'No connection to receiver %s for signal %s from sender %s'
% (receiver, signal, sender)
)
_cleanup_connections(senderkey, signal)
# Update stats.
if __debug__:
global disconnects
disconnects += 1
def get_receivers(sender=Any, signal=All):
"""Get list of receivers from global tables.
This function allows you to retrieve the raw list of receivers
from the connections table for the given sender and signal pair.
Note: There is no guarantee that this is the actual list stored in
the connections table, so the value should be treated as a simple
iterable/truth value rather than, for instance a list to which you
might append new records.
Normally you would use ``live_receivers(get_receivers(...))`` to
retrieve the actual receiver objects as an iterable object.
"""
try:
return connections[id(sender)][signal]
except KeyError:
return []
def live_receivers(receivers):
"""Filter sequence of receivers to get resolved, live receivers.
This is a generator which will iterate over the passed sequence,
checking for weak references and resolving them, then returning
all live receivers.
"""
for receiver in receivers:
if isinstance(receiver, WEAKREF_TYPES):
# Dereference the weak reference.
receiver = receiver()
if receiver is not None:
# Check installed plugins to make sure this receiver is
# live.
live = True
for plugin in plugins:
if not plugin.is_live(receiver):
live = False
break
if live:
yield receiver
def get_all_receivers(sender=Any, signal=All):
"""Get list of all receivers from global tables.
This gets all receivers which should receive the given signal from
sender, each receiver should be produced only once by the
resulting generator.
"""
yielded = set()
for receivers in (
# Get receivers that receive *this* signal from *this* sender.
get_receivers(sender, signal),
# Add receivers that receive *all* signals from *this* sender.
get_receivers(sender, All),
# Add receivers that receive *this* signal from *any* sender.
get_receivers(Any, signal),
# Add receivers that receive *all* signals from *any* sender.
get_receivers(Any, All),
):
for receiver in receivers:
if receiver: # filter out dead instance-method weakrefs
try:
if not receiver in yielded:
yielded.add(receiver)
yield receiver
except TypeError:
# dead weakrefs raise TypeError on hash...
pass
def send(signal=All, sender=Anonymous, *arguments, **named):
"""Send ``signal`` from ``sender`` to all connected receivers.
- ``signal``: (Hashable) signal value; see ``connect`` for details.
- ``sender``: The sender of the signal.
If ``Any``, only receivers registered for ``Any`` will receive the
message.
If ``Anonymous``, only receivers registered to receive messages
from ``Anonymous`` or ``Any`` will receive the message.
Otherwise can be any Python object (normally one registered with
a connect if you actually want something to occur).
- ``arguments``: Positional arguments which will be passed to *all*
receivers. Note that this may raise ``TypeError`` if the receivers
do not allow the particular arguments. Note also that arguments
are applied before named arguments, so they should be used with
care.
- ``named``: Named arguments which will be filtered according to the
parameters of the receivers to only provide those acceptable to
the receiver.
Return a list of tuple pairs ``[(receiver, response), ...]``
If any receiver raises an error, the error propagates back through
send, terminating the dispatch loop, so it is quite possible to
not have all receivers called if a raises an error.
"""
# Call each receiver with whatever arguments it can accept.
# Return a list of tuple pairs [(receiver, response), ... ].
responses = []
for receiver in live_receivers(get_all_receivers(sender, signal)):
# Wrap receiver using installed plugins.
original = receiver
for plugin in plugins:
receiver = plugin.wrap_receiver(receiver)
response = robustapply.robust_apply(
receiver, original,
signal=signal,
sender=sender,
*arguments,
**named
)
responses.append((receiver, response))
# Update stats.
if __debug__:
global sends
sends += 1
return responses
def send_minimal(signal=All, sender=Anonymous, *arguments, **named):
"""Like ``send``, but does not attach ``signal`` and ``sender``
arguments to the call to the receiver."""
# Call each receiver with whatever arguments it can accept.
# Return a list of tuple pairs [(receiver, response), ... ].
responses = []
for receiver in live_receivers(get_all_receivers(sender, signal)):
# Wrap receiver using installed plugins.
original = receiver
for plugin in plugins:
receiver = plugin.wrap_receiver(receiver)
response = robustapply.robust_apply(
receiver, original,
*arguments,
**named
)
responses.append((receiver, response))
# Update stats.
if __debug__:
global sends
sends += 1
return responses
def send_exact(signal=All, sender=Anonymous, *arguments, **named):
"""Send ``signal`` only to receivers registered for exact message.
``send_exact`` allows for avoiding ``Any``/``Anonymous`` registered
handlers, sending only to those receivers explicitly registered
for a particular signal on a particular sender.
"""
responses = []
for receiver in live_receivers(get_receivers(sender, signal)):
# Wrap receiver using installed plugins.
original = receiver
for plugin in plugins:
receiver = plugin.wrap_receiver(receiver)
response = robustapply.robust_apply(
receiver, original,
signal=signal,
sender=sender,
*arguments,
**named
)
responses.append((receiver, response))
return responses
def send_robust(signal=All, sender=Anonymous, *arguments, **named):
"""Send ``signal`` from ``sender`` to all connected receivers catching
errors
- ``signal``: (Hashable) signal value, see connect for details
- ``sender``: The sender of the signal.
If ``Any``, only receivers registered for ``Any`` will receive the
message.
If ``Anonymous``, only receivers registered to receive messages
from ``Anonymous`` or ``Any`` will receive the message.
Otherwise can be any Python object (normally one registered with
a connect if you actually want something to occur).
- ``arguments``: Positional arguments which will be passed to *all*
receivers. Note that this may raise ``TypeError`` if the receivers
do not allow the particular arguments. Note also that arguments
are applied before named arguments, so they should be used with
care.
- ``named``: Named arguments which will be filtered according to the
parameters of the receivers to only provide those acceptable to
the receiver.
Return a list of tuple pairs ``[(receiver, response), ... ]``
If any receiver raises an error (specifically, any subclass of
``Exception``), the error instance is returned as the result for
that receiver.
"""
# Call each receiver with whatever arguments it can accept.
# Return a list of tuple pairs [(receiver, response), ... ].
responses = []
for receiver in live_receivers(get_all_receivers(sender, signal)):
original = receiver
for plugin in plugins:
receiver = plugin.wrap_receiver(receiver)
try:
response = robustapply.robust_apply(
receiver, original,
signal=signal,
sender=sender,
*arguments,
**named
)
except Exception, err:
responses.append((receiver, err))
else:
responses.append((receiver, response))
return responses
def _remove_receiver(receiver):
"""Remove ``receiver`` from connections."""
if not senders_back:
# During module cleanup the mapping will be replaced with None.
return False
backKey = id(receiver)
for senderkey in senders_back.get(backKey, ()):
try:
signals = connections[senderkey].keys()
except KeyError:
pass
else:
for signal in signals:
try:
receivers = connections[senderkey][signal]
except KeyError:
pass
else:
try:
receivers.remove(receiver)
except Exception:
pass
_cleanup_connections(senderkey, signal)
try:
del senders_back[backKey]
except KeyError:
pass
def _cleanup_connections(senderkey, signal):
"""Delete empty signals for ``senderkey``. Delete ``senderkey`` if
empty."""
try:
receivers = connections[senderkey][signal]
except:
pass
else:
if not receivers:
# No more connected receivers. Therefore, remove the signal.
try:
signals = connections[senderkey]
except KeyError:
pass
else:
del signals[signal]
if not signals:
# No more signal connections. Therefore, remove the sender.
_remove_sender(senderkey)
def _remove_sender(senderkey):
"""Remove ``senderkey`` from connections."""
_remove_back_refs(senderkey)
try:
del connections[senderkey]
except KeyError:
pass
# Senderkey will only be in senders dictionary if sender
# could be weakly referenced.
try:
del senders[senderkey]
except:
pass
def _remove_back_refs(senderkey):
"""Remove all back-references to this ``senderkey``."""
try:
signals = connections[senderkey]
except KeyError:
signals = None
else:
for signal, receivers in signals.iteritems():
for receiver in receivers:
_kill_back_ref(receiver, senderkey)
def _remove_old_back_refs(senderkey, signal, receiver, receivers):
"""Kill old ``senders_back`` references from ``receiver``.
This guards against multiple registration of the same receiver for
a given signal and sender leaking memory as old back reference
records build up.
Also removes old receiver instance from receivers.
"""
try:
index = receivers.index(receiver)
# need to scan back references here and remove senderkey
except ValueError:
return False
else:
old_receiver = receivers[index]
del receivers[index]
found = 0
signals = connections.get(signal)
if signals is not None:
for sig, recs in connections.get(signal, {}).iteritems():
if sig != signal:
for rec in recs:
if rec is old_receiver:
found = 1
break
if not found:
_kill_back_ref(old_receiver, senderkey)
return True
return False
def _kill_back_ref(receiver, senderkey):
"""Do actual removal of back reference from ``receiver`` to
``senderkey``."""
receiverkey = id(receiver)
senders = senders_back.get(receiverkey, ())
while senderkey in senders:
try:
senders.remove(senderkey)
except:
break
if not senders:
try:
del senders_back[receiverkey]
except KeyError:
pass
return True
Louie-1.1/louie/saferef.py 0000644 0001750 0001750 00000015673 10345321061 015763 0 ustar gldnspud gldnspud """Refactored 'safe reference from dispatcher.py"""
import weakref
import traceback
def safe_ref(target, on_delete=None):
"""Return a *safe* weak reference to a callable target.
- ``target``: The object to be weakly referenced, if it's a bound
method reference, will create a BoundMethodWeakref, otherwise
creates a simple weakref.
- ``on_delete``: If provided, will have a hard reference stored to
the callable to be called after the safe reference goes out of
scope with the reference object, (either a weakref or a
BoundMethodWeakref) as argument.
"""
if hasattr(target, 'im_self'):
if target.im_self is not None:
# Turn a bound method into a BoundMethodWeakref instance.
# Keep track of these instances for lookup by disconnect().
assert hasattr(target, 'im_func'), (
"safe_ref target %r has im_self, but no im_func, "
"don't know how to create reference"
% target
)
reference = BoundMethodWeakref(target=target, on_delete=on_delete)
return reference
if callable(on_delete):
return weakref.ref(target, on_delete)
else:
return weakref.ref(target)
class BoundMethodWeakref(object):
"""'Safe' and reusable weak references to instance methods.
BoundMethodWeakref objects provide a mechanism for referencing a
bound method without requiring that the method object itself
(which is normally a transient object) is kept alive. Instead,
the BoundMethodWeakref object keeps weak references to both the
object and the function which together define the instance method.
Attributes:
- ``key``: The identity key for the reference, calculated by the
class's calculate_key method applied to the target instance method.
- ``deletion_methods``: Sequence of callable objects taking single
argument, a reference to this object which will be called when
*either* the target object or target function is garbage
collected (i.e. when this object becomes invalid). These are
specified as the on_delete parameters of safe_ref calls.
- ``weak_self``: Weak reference to the target object.
- ``weak_func``: Weak reference to the target function.
Class Attributes:
- ``_all_instances``: Class attribute pointing to all live
BoundMethodWeakref objects indexed by the class's
calculate_key(target) method applied to the target objects.
This weak value dictionary is used to short-circuit creation so
that multiple references to the same (object, function) pair
produce the same BoundMethodWeakref instance.
"""
_all_instances = weakref.WeakValueDictionary()
def __new__(cls, target, on_delete=None, *arguments, **named):
"""Create new instance or return current instance.
Basically this method of construction allows us to
short-circuit creation of references to already- referenced
instance methods. The key corresponding to the target is
calculated, and if there is already an existing reference,
that is returned, with its deletion_methods attribute updated.
Otherwise the new instance is created and registered in the
table of already-referenced methods.
"""
key = cls.calculate_key(target)
current = cls._all_instances.get(key)
if current is not None:
current.deletion_methods.append(on_delete)
return current
else:
base = super(BoundMethodWeakref, cls).__new__(cls)
cls._all_instances[key] = base
base.__init__(target, on_delete, *arguments, **named)
return base
def __init__(self, target, on_delete=None):
"""Return a weak-reference-like instance for a bound method.
- ``target``: The instance-method target for the weak reference,
must have im_self and im_func attributes and be
reconstructable via the following, which is true of built-in
instance methods::
target.im_func.__get__( target.im_self )
- ``on_delete``: Optional callback which will be called when
this weak reference ceases to be valid (i.e. either the
object or the function is garbage collected). Should take a
single argument, which will be passed a pointer to this
object.
"""
def remove(weak, self=self):
"""Set self.isDead to True when method or instance is destroyed."""
methods = self.deletion_methods[:]
del self.deletion_methods[:]
try:
del self.__class__._all_instances[self.key]
except KeyError:
pass
for function in methods:
try:
if callable(function):
function(self)
except Exception:
try:
traceback.print_exc()
except AttributeError, e:
print ('Exception during saferef %s '
'cleanup function %s: %s' % (self, function, e))
self.deletion_methods = [on_delete]
self.key = self.calculate_key(target)
self.weak_self = weakref.ref(target.im_self, remove)
self.weak_func = weakref.ref(target.im_func, remove)
self.self_name = str(target.im_self)
self.func_name = str(target.im_func.__name__)
def calculate_key(cls, target):
"""Calculate the reference key for this reference.
Currently this is a two-tuple of the id()'s of the target
object and the target function respectively.
"""
return (id(target.im_self), id(target.im_func))
calculate_key = classmethod(calculate_key)
def __str__(self):
"""Give a friendly representation of the object."""
return "%s(%s.%s)" % (
self.__class__.__name__,
self.self_name,
self.func_name,
)
__repr__ = __str__
def __nonzero__(self):
"""Whether we are still a valid reference."""
return self() is not None
def __cmp__(self, other):
"""Compare with another reference."""
if not isinstance(other, self.__class__):
return cmp(self.__class__, type(other))
return cmp(self.key, other.key)
def __call__(self):
"""Return a strong reference to the bound method.
If the target cannot be retrieved, then will return None,
otherwise returns a bound instance method for our object and
function.
Note: You may call this method any number of times, as it does
not invalidate the reference.
"""
target = self.weak_self()
if target is not None:
function = self.weak_func()
if function is not None:
return function.__get__(target)
return None
Louie-1.1/louie/__init__.py 0000644 0001750 0001750 00000001626 10346376067 016121 0 ustar gldnspud gldnspud __all__ = [
'dispatcher',
'error',
'plugin',
'robustapply',
'saferef',
'sender',
'signal',
'version',
'connect',
'disconnect',
'get_all_receivers',
'reset',
'send',
'send_exact',
'send_minimal',
'send_robust',
'install_plugin',
'remove_plugin',
'Plugin',
'QtWidgetPlugin',
'TwistedDispatchPlugin',
'Anonymous',
'Any',
'All',
'Signal',
]
import louie.dispatcher, louie.error, louie.plugin, louie.robustapply, \
louie.saferef, louie.sender, louie.signal, louie.version
from louie.dispatcher import \
connect, disconnect, get_all_receivers, reset, \
send, send_exact, send_minimal, send_robust
from louie.plugin import \
install_plugin, remove_plugin, Plugin, \
QtWidgetPlugin, TwistedDispatchPlugin
from louie.sender import Anonymous, Any
from louie.signal import All, Signal
Louie-1.1/louie/signal.py 0000644 0001750 0001750 00000001227 10343130373 015615 0 ustar gldnspud gldnspud """Signal class.
This class is provided as a way to consistently define and document
signal types. Signal classes also have a useful string
representation.
Louie does not require you to use a subclass of Signal for signals.
"""
class _SIGNAL(type):
"""Base metaclass for signal classes."""
def __str__(cls):
return '' % (cls.__name__, )
class Signal(object):
__metaclass__ = _SIGNAL
class All(Signal):
"""Used to represent 'all signals'.
The All class can be used with connect, disconnect, send, or
sendExact to denote that the signal should react to all signals,
not just a particular signal.
"""
Louie-1.1/louie/version.py 0000644 0001750 0001750 00000000161 10367466260 016036 0 ustar gldnspud gldnspud """Louie version information."""
NAME = 'Louie'
DESCRIPTION = 'Signal dispatching mechanism'
VERSION = '1.1'
Louie-1.1/louie/plugin.py 0000644 0001750 0001750 00000006262 10367466260 015657 0 ustar gldnspud gldnspud """Common plugins for Louie."""
from louie import dispatcher
from louie import error
def install_plugin(plugin):
cls = plugin.__class__
for p in dispatcher.plugins:
if p.__class__ is cls:
raise error.PluginTypeError(
'Plugin of type %r already installed.' % cls)
dispatcher.plugins.append(plugin)
def remove_plugin(plugin):
dispatcher.plugins.remove(plugin)
class Plugin(object):
"""Base class for Louie plugins.
Plugins are used to extend or alter the behavior of Louie
in a uniform way without having to modify the Louie code
itself.
"""
def is_live(self, receiver):
"""Return True if the receiver is still live.
Only called for receivers who have already been determined to
be live by default Louie semantics.
"""
return True
def wrap_receiver(self, receiver):
"""Return a callable that passes arguments to the receiver.
Useful when you want to change the behavior of all receivers.
"""
return receiver
class QtWidgetPlugin(Plugin):
"""A Plugin for Louie that knows how to handle Qt widgets
when using PyQt built with SIP 4 or higher.
Weak references are not useful when dealing with QWidget
instances, because even after a QWidget is closed and destroyed,
only the C++ object is destroyed. The Python 'shell' object
remains, but raises a RuntimeError when an attempt is made to call
an underlying QWidget method.
This plugin alleviates this behavior, and if a QWidget instance is
found that is just an empty shell, it prevents Louie from
dispatching to any methods on those objects.
"""
def __init__(self):
try:
import qt
except ImportError:
self.is_live = self._is_live_no_qt
else:
self.qt = qt
def is_live(self, receiver):
"""If receiver is a method on a QWidget, only return True if
it hasn't been destroyed."""
if (hasattr(receiver, 'im_self') and
isinstance(receiver.im_self, self.qt.QWidget)
):
try:
receiver.im_self.x()
except RuntimeError:
return False
return True
def _is_live_no_qt(self, receiver):
return True
class TwistedDispatchPlugin(Plugin):
"""Plugin for Louie that wraps all receivers in callables
that return Twisted Deferred objects.
When the wrapped receiver is called, it adds a call to the actual
receiver to the reactor event loop, and returns a Deferred that is
called back with the result.
"""
def __init__(self):
# Don't import reactor ourselves, but make access to it
# easier.
from twisted import internet
from twisted.internet.defer import Deferred
self._internet = internet
self._Deferred = Deferred
def wrap_receiver(self, receiver):
def wrapper(*args, **kw):
d = self._Deferred()
def called(dummy):
return receiver(*args, **kw)
d.addCallback(called)
self._internet.reactor.callLater(0, d.callback, None)
return d
return wrapper
Louie-1.1/PKG-INFO 0000644 0001750 0001750 00000001650 10367467325 013766 0 ustar gldnspud gldnspud Metadata-Version: 1.0
Name: Louie
Version: 1.1
Summary: Signal dispatching mechanism
Home-page: http://louie.berlios.de/
Author: Patrick K. O'Brien and contributors
Author-email: louie-users@lists.berlios.de
License: BSD
Download-URL: http://cheeseshop.python.org/pypi/Louie
Description: Louie provides Python programmers with a straightforward way to
dispatch signals between objects in a wide variety of contexts. It is
based on PyDispatcher_, which in turn was based on a highly-rated
recipe_ in the Python Cookbook.
.. _PyDispatcher: http://pydispatcher.sf.net/
.. _recipe: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/87056
Platform: UNKNOWN
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Louie-1.1/setup.cfg 0000644 0001750 0001750 00000001123 10367466760 014507 0 ustar gldnspud gldnspud [announce]
recipients=python-announce-list@python.org
reply-to=mscott@goldenspud.com
[publish]
dist-dest=rsync://shell.berlios.de/home/groups/louie/htdocs/dist/
doc-dir=doc/html
doc-dest=rsync://shell.berlios.de/home/groups/louie/htdocs/
make-dirs=0
[pudge]
modules=louie
docs=doc/*.txt
dest=doc/html
title=Louie
license=gnu
template_dir=doc/template
organization=Louie
organization_url=http://louie.berlios.de/
mailing_list_url=http://developer.berlios.de/mail/?group_id=5432
settings=project_page=http://developer.berlios.de/projects/louie/
project_page_title=Berlios Project Page
Louie-1.1/setup.py 0000644 0001750 0001750 00000002454 10351551016 014366 0 ustar gldnspud gldnspud from setuptools import setup, find_packages
try:
import buildutils
except ImportError:
pass
from louie import version
setup(
name=version.NAME,
version=version.VERSION,
description=version.DESCRIPTION,
long_description="""\
Louie provides Python programmers with a straightforward way to
dispatch signals between objects in a wide variety of contexts. It is
based on PyDispatcher_, which in turn was based on a highly-rated
recipe_ in the Python Cookbook.
.. _PyDispatcher: http://pydispatcher.sf.net/
.. _recipe: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/87056
""",
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules',
],
keywords='',
author="Patrick K. O'Brien and contributors",
author_email='louie-users@lists.berlios.de',
url='http://louie.berlios.de/',
download_url='http://cheeseshop.python.org/pypi/Louie',
license='BSD',
packages=find_packages(exclude=['doc', 'ez_setup', 'examples', 'tests']),
install_requires=[
'nose >= 0.8.3',
],
zip_safe=False,
package_data={
# -*- package_data: -*-
},
test_suite = 'nose.collector',
)
Louie-1.1/ez_setup.py 0000644 0001750 0001750 00000017775 10343150732 015101 0 ustar gldnspud gldnspud #!python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import sys
DEFAULT_VERSION = "0.6a8"
DEFAULT_URL = "http://cheeseshop.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = {
'setuptools-0.5a13-py2.3.egg': '85edcf0ef39bab66e130d3f38f578c86',
'setuptools-0.5a13-py2.4.egg': 'ede4be600e3890e06d4ee5e0148e092a',
'setuptools-0.6a1-py2.3.egg': 'ee819a13b924d9696b0d6ca6d1c5833d',
'setuptools-0.6a1-py2.4.egg': '8256b5f1cd9e348ea6877b5ddd56257d',
'setuptools-0.6a2-py2.3.egg': 'b98da449da411267c37a738f0ab625ba',
'setuptools-0.6a2-py2.4.egg': 'be5b88bc30aed63fdefd2683be135c3b',
'setuptools-0.6a3-py2.3.egg': 'ee0e325de78f23aab79d33106dc2a8c8',
'setuptools-0.6a3-py2.4.egg': 'd95453d525a456d6c23e7a5eea89a063',
'setuptools-0.6a4-py2.3.egg': 'e958cbed4623bbf47dd1f268b99d7784',
'setuptools-0.6a4-py2.4.egg': '7f33c3ac2ef1296f0ab4fac1de4767d8',
'setuptools-0.6a5-py2.3.egg': '748408389c49bcd2d84f6ae0b01695b1',
'setuptools-0.6a5-py2.4.egg': '999bacde623f4284bfb3ea77941d2627',
'setuptools-0.6a6-py2.3.egg': '7858139f06ed0600b0d9383f36aca24c',
'setuptools-0.6a6-py2.4.egg': 'c10d20d29acebce0dc76219dc578d058',
'setuptools-0.6a7-py2.3.egg': 'cfc4125ddb95c07f9500adc5d6abef6f',
'setuptools-0.6a7-py2.4.egg': 'c6d62dab4461f71aed943caea89e6f20',
'setuptools-0.6a8-py2.3.egg': '2f18eaaa3f544f5543ead4a68f3b2e1a',
'setuptools-0.6a8-py2.4.egg': '799018f2894f14c9f8bcb2b34e69b391',
}
import sys, os
def _validate_md5(egg_name, data):
if egg_name in md5_data:
from md5 import md5
digest = md5(data).hexdigest()
if digest != md5_data[egg_name]:
print >>sys.stderr, (
"md5 validation of %s failed! (Possible download problem?)"
% egg_name
)
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
download_delay=15
):
"""Automatically find/download setuptools and make it available on sys.path
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end with
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
it is not already available. If `download_delay` is specified, it should
be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
try:
import setuptools
if setuptools.__version__ == '0.0.1':
print >>sys.stderr, (
"You have an obsolete version of setuptools installed. Please\n"
"remove it from your system entirely before rerunning this script."
)
sys.exit(2)
except ImportError:
egg = download_setuptools(version, download_base, to_dir, download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
import pkg_resources
try:
pkg_resources.require("setuptools>="+version)
except pkg_resources.VersionConflict:
# XXX could we install in a subprocess here?
print >>sys.stderr, (
"The required version of setuptools (>=%s) is not available, and\n"
"can't be installed while this script is running. Please install\n"
" a more recent version first."
) % version
sys.exit(2)
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
from distutils import log
if delay:
log.warn("""
---------------------------------------------------------------------------
This script requires setuptools version %s to run (even to display
help). I will attempt to download it for you (from
%s), but
you may need to enable firewall access for this script first.
I will start the download in %d seconds.
---------------------------------------------------------------------------""",
version, download_base, delay
); from time import sleep; sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
import setuptools
except ImportError:
import tempfile, shutil
tmpdir = tempfile.mkdtemp(prefix="easy_install-")
try:
egg = download_setuptools(version, to_dir=tmpdir, delay=0)
sys.path.insert(0,egg)
from setuptools.command.easy_install import main
main(list(argv)+[egg])
finally:
shutil.rmtree(tmpdir)
else:
if setuptools.__version__ == '0.0.1':
# tell the user to uninstall obsolete version
use_setuptools(version)
req = "setuptools>="+version
import pkg_resources
try:
pkg_resources.require(req)
except pkg_resources.VersionConflict:
try:
from setuptools.command.easy_install import main
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print "Setuptools version",version,"or greater has been installed."
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames):
"""Update our built-in md5 registry"""
import re
from md5 import md5
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print >>sys.stderr, "Internal error!"
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])
Louie-1.1/Louie.egg-info/ 0000755 0001750 0001750 00000000000 10367467325 015436 5 ustar gldnspud gldnspud Louie-1.1/Louie.egg-info/PKG-INFO 0000644 0001750 0001750 00000001650 10367467325 016535 0 ustar gldnspud gldnspud Metadata-Version: 1.0
Name: Louie
Version: 1.1
Summary: Signal dispatching mechanism
Home-page: http://louie.berlios.de/
Author: Patrick K. O'Brien and contributors
Author-email: louie-users@lists.berlios.de
License: BSD
Download-URL: http://cheeseshop.python.org/pypi/Louie
Description: Louie provides Python programmers with a straightforward way to
dispatch signals between objects in a wide variety of contexts. It is
based on PyDispatcher_, which in turn was based on a highly-rated
recipe_ in the Python Cookbook.
.. _PyDispatcher: http://pydispatcher.sf.net/
.. _recipe: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/87056
Platform: UNKNOWN
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Louie-1.1/Louie.egg-info/SOURCES.txt 0000644 0001750 0001750 00000001206 10367467325 017321 0 ustar gldnspud gldnspud ez_setup.py
setup.cfg
setup.py
Louie.egg-info/PKG-INFO
Louie.egg-info/SOURCES.txt
Louie.egg-info/not-zip-safe
Louie.egg-info/requires.txt
Louie.egg-info/top_level.txt
doc/about.txt
doc/changes.txt
doc/contributors.txt
doc/index.txt
doc/template/almodovar.css
doc/template/layout.css
doc/template/layout.html
louie/__init__.py
louie/dispatcher.py
louie/error.py
louie/plugin.py
louie/robustapply.py
louie/saferef.py
louie/sender.py
louie/signal.py
louie/version.py
louie/test/__init__.py
louie/test/conftest.py
louie/test/fixture.py
louie/test/test_dispatcher.py
louie/test/test_plugin.py
louie/test/test_robustapply.py
louie/test/test_saferef.py
Louie-1.1/Louie.egg-info/top_level.txt 0000644 0001750 0001750 00000000006 10367467325 020164 0 ustar gldnspud gldnspud louie
Louie-1.1/Louie.egg-info/not-zip-safe 0000644 0001750 0001750 00000000000 10367465373 017664 0 ustar gldnspud gldnspud Louie-1.1/Louie.egg-info/requires.txt 0000644 0001750 0001750 00000000015 10367467325 020032 0 ustar gldnspud gldnspud nose >= 0.8.3