Louie-1.1/0000755000175000017500000000000010367467325012667 5ustar gldnspudgldnspudLouie-1.1/doc/0000755000175000017500000000000010367467325013434 5ustar gldnspudgldnspudLouie-1.1/doc/contributors.txt0000644000175000017500000000034710343350020016707 0ustar gldnspudgldnspud============== Contributors ============== The following people have contributed code to PyDispatcher and Louie: - Patrick K. O'Brien - Mike C. Fletcher - Matthew R. Scott .. Local Variables: mode: rst End: Louie-1.1/doc/template/0000755000175000017500000000000010367467325015247 5ustar gldnspudgldnspudLouie-1.1/doc/template/layout.html0000644000175000017500000000715410343352420017440 0ustar gldnspudgldnspud ${title}

BerliOS Developer
        Logo

Louie-1.1/doc/template/layout.css0000644000175000017500000000407710343352420017265 0ustar gldnspudgldnspud@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.css0000644000175000017500000000755410343352420017737 0ustar gldnspudgldnspud/* 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.txt0000644000175000017500000000373310351547423015301 0ustar gldnspudgldnspud======= 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.txt0000644000175000017500000000453110343351446015300 0ustar gldnspudgldnspud======= 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.txt0000644000175000017500000000255110351547365015604 0ustar gldnspudgldnspud========= 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/0000755000175000017500000000000010367467325014004 5ustar gldnspudgldnspudLouie-1.1/louie/test/0000755000175000017500000000000010367467325014763 5ustar gldnspudgldnspudLouie-1.1/louie/test/fixture.py0000644000175000017500000000000010343130373016771 0ustar gldnspudgldnspudLouie-1.1/louie/test/test_dispatcher.py0000644000175000017500000001147710345321060020511 0ustar gldnspudgldnspudimport 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.py0000644000175000017500000000012610343130373017141 0ustar gldnspudgldnspudimport sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) Louie-1.1/louie/test/test_robustapply.py0000644000175000017500000000137710343130373020750 0ustar gldnspudgldnspudimport 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.py0000644000175000017500000000416210343130373017772 0ustar gldnspudgldnspudimport 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.py0000644000175000017500000000770310367466260017676 0ustar gldnspudgldnspud"""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__.py0000644000175000017500000000000010343130373017042 0ustar gldnspudgldnspudLouie-1.1/louie/error.py0000644000175000017500000000110110367466260015475 0ustar gldnspudgldnspud"""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.py0000644000175000017500000000223710343130373015622 0ustar gldnspudgldnspud"""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.py0000644000175000017500000000414410343362411016725 0ustar gldnspudgldnspud"""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.py0000644000175000017500000004633210351551410016472 0ustar gldnspudgldnspud"""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.py0000644000175000017500000001567310345321061015763 0ustar gldnspudgldnspud"""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__.py0000644000175000017500000000162610346376067016121 0ustar gldnspudgldnspud__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.py0000644000175000017500000000122710343130373015615 0ustar gldnspudgldnspud"""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.py0000644000175000017500000000016110367466260016036 0ustar gldnspudgldnspud"""Louie version information.""" NAME = 'Louie' DESCRIPTION = 'Signal dispatching mechanism' VERSION = '1.1' Louie-1.1/louie/plugin.py0000644000175000017500000000626210367466260015657 0ustar gldnspudgldnspud"""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-INFO0000644000175000017500000000165010367467325013766 0ustar gldnspudgldnspudMetadata-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.cfg0000644000175000017500000000112310367466760014507 0ustar gldnspudgldnspud[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.py0000644000175000017500000000245410351551016014366 0ustar gldnspudgldnspudfrom 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.py0000644000175000017500000001777510343150732015101 0ustar gldnspudgldnspud#!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/0000755000175000017500000000000010367467325015436 5ustar gldnspudgldnspudLouie-1.1/Louie.egg-info/PKG-INFO0000644000175000017500000000165010367467325016535 0ustar gldnspudgldnspudMetadata-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.txt0000644000175000017500000000120610367467325017321 0ustar gldnspudgldnspudez_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.txt0000644000175000017500000000000610367467325020164 0ustar gldnspudgldnspudlouie Louie-1.1/Louie.egg-info/not-zip-safe0000644000175000017500000000000010367465373017664 0ustar gldnspudgldnspudLouie-1.1/Louie.egg-info/requires.txt0000644000175000017500000000001510367467325020032 0ustar gldnspudgldnspudnose >= 0.8.3