[GRASS-SVN] r55316 - in grass/trunk/lib/python: . pydispatch

svn_grass at osgeo.org svn_grass at osgeo.org
Mon Mar 11 08:21:57 PDT 2013


Author: wenzeslaus
Date: 2013-03-11 08:21:57 -0700 (Mon, 11 Mar 2013)
New Revision: 55316

Added:
   grass/trunk/lib/python/pydispatch/
   grass/trunk/lib/python/pydispatch/Makefile
   grass/trunk/lib/python/pydispatch/PKG-INFO
   grass/trunk/lib/python/pydispatch/__init__.py
   grass/trunk/lib/python/pydispatch/dispatcher.py
   grass/trunk/lib/python/pydispatch/errors.py
   grass/trunk/lib/python/pydispatch/license.txt
   grass/trunk/lib/python/pydispatch/pydispatchlib.dox
   grass/trunk/lib/python/pydispatch/robust.py
   grass/trunk/lib/python/pydispatch/robustapply.py
   grass/trunk/lib/python/pydispatch/saferef.py
Modified:
   grass/trunk/lib/python/Makefile
Log:
libpython: pydispacher library (PyDispatcher 2.0.3 source code)

Modified: grass/trunk/lib/python/Makefile
===================================================================
--- grass/trunk/lib/python/Makefile	2013-03-11 14:26:59 UTC (rev 55315)
+++ grass/trunk/lib/python/Makefile	2013-03-11 15:21:57 UTC (rev 55316)
@@ -7,7 +7,7 @@
 
 PYDIR = $(ETC)/python/grass
 
-SUBDIRS = script ctypes temporal pygrass
+SUBDIRS = script ctypes temporal pygrass pydispatch
 
 default: $(PYDIR)/__init__.py
 	$(MAKE) subdirs

Added: grass/trunk/lib/python/pydispatch/Makefile
===================================================================
--- grass/trunk/lib/python/pydispatch/Makefile	                        (rev 0)
+++ grass/trunk/lib/python/pydispatch/Makefile	2013-03-11 15:21:57 UTC (rev 55316)
@@ -0,0 +1,32 @@
+MODULE_TOPDIR = ../../..
+
+include $(MODULE_TOPDIR)/include/Make/Other.make
+include $(MODULE_TOPDIR)/include/Make/Python.make
+include $(MODULE_TOPDIR)/include/Make/Doxygen.make
+
+PYDIR = $(ETC)/python
+GDIR = $(PYDIR)/grass
+DSTDIR = $(GDIR)/pydispatch
+
+MODULES = dispatcher errors robustapply robust saferef signals
+
+
+PYFILES := $(patsubst %,$(DSTDIR)/%.py,$(MODULES) __init__)
+PYCFILES := $(patsubst %,$(DSTDIR)/%.pyc,$(MODULES) __init__)
+
+default: $(PYFILES) $(PYCFILES) $(GDIR)/__init__.py $(GDIR)/__init__.pyc
+
+$(PYDIR):
+	$(MKDIR) $@
+
+$(GDIR): | $(PYDIR)
+	$(MKDIR) $@
+
+$(DSTDIR): | $(GDIR)
+	$(MKDIR) $@
+
+$(DSTDIR)/%: % | $(DSTDIR)
+	$(INSTALL_DATA) $< $@
+
+#doxygen:
+DOXNAME = pydispatchlib

Added: grass/trunk/lib/python/pydispatch/PKG-INFO
===================================================================
--- grass/trunk/lib/python/pydispatch/PKG-INFO	                        (rev 0)
+++ grass/trunk/lib/python/pydispatch/PKG-INFO	2013-03-11 15:21:57 UTC (rev 55316)
@@ -0,0 +1,27 @@
+Metadata-Version: 1.0
+Name: PyDispatcher
+Version: 2.0.3
+Summary: Multi-producer-multi-consumer signal dispatching mechanism
+Home-page: http://pydispatcher.sourceforge.net
+Author: Mike C. Fletcher
+Author-email: pydispatcher-devel at lists.sourceforge.net
+License: BSD-style, see license.txt for details
+Description: Dispatcher mechanism for creating event models
+        
+        PyDispatcher is an enhanced version of Patrick K. O'Brien's
+        original dispatcher.py module.  It provides the Python
+        programmer with a robust mechanism for event routing within
+        various application contexts.
+        
+        Included in the package are the robustapply and saferef
+        modules, which provide the ability to selectively apply
+        arguments to callable objects and to reference instance
+        methods using weak-references.
+        
+Keywords: dispatcher,dispatch,pydispatch,event,signal,sender,receiver,propagate,multi-consumer,multi-producer,saferef,robustapply,apply
+Platform: Any
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Intended Audience :: Developers

Added: grass/trunk/lib/python/pydispatch/__init__.py
===================================================================
--- grass/trunk/lib/python/pydispatch/__init__.py	                        (rev 0)
+++ grass/trunk/lib/python/pydispatch/__init__.py	2013-03-11 15:21:57 UTC (rev 55316)
@@ -0,0 +1,6 @@
+"""Multi-consumer multi-producer dispatching mechanism
+"""
+__version__ = "2.0.3"
+__author__ = "Patrick K. O'Brien"
+__license__ = "BSD-style, see license.txt for details"
+

Added: grass/trunk/lib/python/pydispatch/dispatcher.py
===================================================================
--- grass/trunk/lib/python/pydispatch/dispatcher.py	                        (rev 0)
+++ grass/trunk/lib/python/pydispatch/dispatcher.py	2013-03-11 15:21:57 UTC (rev 55316)
@@ -0,0 +1,492 @@
+"""Multiple-producer-multiple-consumer signal-dispatching
+
+dispatcher is the core of the PyDispatcher system,
+providing the primary API and the core logic for the
+system.
+
+Module attributes of note:
+
+	Any -- Singleton used to signal either "Any Sender" or
+		"Any Signal".  See documentation of the _Any class.
+	Anonymous -- Singleton used to signal "Anonymous Sender"
+		See documentation of the _Anonymous class.
+
+Internal attributes:
+	WEAKREF_TYPES -- tuple of types/classes which represent
+		weak references to receivers, and thus must be de-
+		referenced on retrieval to retrieve the callable
+		object
+	connections -- { senderkey (id) : { signal : [receivers...]}}
+	senders -- { senderkey (id) : weakref(sender) }
+		used for cleaning up sender references on sender
+		deletion
+	sendersBack -- { receiverkey (id) : [senderkey (id)...] }
+		used for cleaning up receiver references on receiver
+		deletion, (considerably speeds up the cleanup process
+		vs. the original code.)
+"""
+from __future__ import generators
+import weakref
+from pydispatch import saferef, robustapply, errors
+
+__author__ = "Patrick K. O'Brien <pobrien at orbtech.com>"
+__cvsid__ = "$Id: dispatcher.py,v 1.1 2010/03/30 15:45:55 mcfletch Exp $"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+class _Parameter:
+	"""Used to represent default parameter values."""
+	def __repr__(self):
+		return self.__class__.__name__
+
+class _Any(_Parameter):
+	"""Singleton used to signal either "Any Sender" or "Any Signal"
+
+	The Any object can be used with connect, disconnect,
+	send, or sendExact to signal that the parameter given
+	Any should react to all senders/signals, not just
+	a particular sender/signal.
+	"""
+Any = _Any()
+
+class _Anonymous(_Parameter):
+	"""Singleton used to signal "Anonymous Sender"
+
+	The Anonymous object 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.
+	"""
+Anonymous = _Anonymous()
+
+WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
+
+connections = {}
+senders = {}
+sendersBack = {}
+
+
+def connect(receiver, signal=Any, 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.safeRef() 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 Any, receiver will receive any signal from the
+		indicated sender (which might also be Any, but is not
+		necessarily Any).
+		
+		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/sendExact 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 errors.DispatcherTypeError(
+			'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
+		)
+	if weak:
+		receiver = saferef.safeRef(receiver, onDelete=_removeReceiver)
+	senderkey = id(sender)
+	if senderkey in connections:
+		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):
+			_removeSender(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:
+			weakSender = weakref.ref(sender, remove)
+			senders[senderkey] = weakSender
+		except:
+			pass
+		
+	receiverID = id(receiver)
+	# get current set, remove any current references to
+	# this receiver in the set, including back-references
+	if signal in signals:
+		receivers = signals[signal]
+		_removeOldBackRefs(senderkey, signal, receiver, receivers)
+	else:
+		receivers = signals[signal] = []
+	try:
+		current = sendersBack.get( receiverID )
+		if current is None:
+			sendersBack[ receiverID ] = current = []
+		if senderkey not in current:
+			current.append(senderkey)
+	except:
+		pass
+
+	receivers.append(receiver)
+
+
+
+def disconnect(receiver, signal=Any, 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 errors.DispatcherTypeError(
+			'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
+		)
+	if weak: receiver = saferef.safeRef(receiver)
+	senderkey = id(sender)
+	try:
+		signals = connections[senderkey]
+		receivers = signals[signal]
+	except KeyError:
+		raise errors.DispatcherKeyError(
+			"""No receivers found for signal %r from sender %r""" %(
+				signal,
+				sender
+			)
+		)
+	try:
+		# also removes from receivers
+		_removeOldBackRefs(senderkey, signal, receiver, receivers)
+	except ValueError:
+		raise errors.DispatcherKeyError(
+			"""No connection to receiver %s for signal %s from sender %s""" %(
+				receiver,
+				signal,
+				sender
+			)
+		)
+	_cleanupConnections(senderkey, signal)
+
+def getReceivers( sender = Any, signal = Any ):
+	"""Get list of receivers from global tables
+
+	This utility 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 liveReceivers( getReceivers( ...))
+	to retrieve the actual receiver objects as an iterable
+	object.
+	"""
+	try:
+		return connections[id(sender)][signal]
+	except KeyError:
+		return []
+
+def liveReceivers(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:
+				yield receiver
+		else:
+			yield receiver
+
+
+
+def getAllReceivers( sender = Any, signal = Any ):
+	"""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
+	"""
+	receivers = {}
+	for set in (
+		# Get receivers that receive *this* signal from *this* sender.
+		getReceivers( sender, signal ),
+		# Add receivers that receive *any* signal from *this* sender.
+		getReceivers( sender, Any ),
+		# Add receivers that receive *this* signal from *any* sender.
+		getReceivers( Any, signal ),
+		# Add receivers that receive *any* signal from *any* sender.
+		getReceivers( Any, Any ),
+	):
+		for receiver in set:
+			if receiver: # filter out dead instance-method weakrefs
+				try:
+					if receiver not in receivers:
+						receivers[receiver] = 1
+						yield receiver
+				except TypeError:
+					# dead weakrefs raise TypeError on hash...
+					pass
+
+def send(signal=Any, 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 TypeErrors
+		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 liveReceivers(getAllReceivers(sender, signal)):
+		response = robustapply.robustApply(
+			receiver,
+			signal=signal,
+			sender=sender,
+			*arguments,
+			**named
+		)
+		responses.append((receiver, response))
+	return responses
+def sendExact( signal=Any, sender=Anonymous, *arguments, **named ):
+	"""Send signal only to those receivers registered for exact message
+
+	sendExact 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 liveReceivers(getReceivers(sender, signal)):
+		response = robustapply.robustApply(
+			receiver,
+			signal=signal,
+			sender=sender,
+			*arguments,
+			**named
+		)
+		responses.append((receiver, response))
+	return responses
+	
+
+def _removeReceiver(receiver):
+	"""Remove receiver from connections."""
+	if not sendersBack:
+		# During module cleanup the mapping will be replaced with None
+		return False
+	backKey = id(receiver)
+	try:
+		backSet = sendersBack.pop(backKey)
+	except KeyError:
+		return False 
+	else:
+		for senderkey in backSet:
+			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
+					_cleanupConnections(senderkey, signal)
+
+def _cleanupConnections(senderkey, signal):
+	"""Delete any 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.
+					_removeSender(senderkey)
+
+def _removeSender(senderkey):
+	"""Remove senderkey from connections."""
+	_removeBackrefs(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 _removeBackrefs( senderkey):
+	"""Remove all back-references to this senderkey"""
+	try:
+		signals = connections[senderkey]
+	except KeyError:
+		signals = None
+	else:
+		items = signals.items()
+		def allReceivers( ):
+			for signal,set in items:
+				for item in set:
+					yield item
+		for receiver in allReceivers():
+			_killBackref( receiver, senderkey )
+
+def _removeOldBackRefs(senderkey, signal, receiver, receivers):
+	"""Kill old sendersBack 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:
+		oldReceiver = 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 oldReceiver:
+							found = 1
+							break
+		if not found:
+			_killBackref( oldReceiver, senderkey )
+			return True
+		return False
+		
+		
+def _killBackref( receiver, senderkey ):
+	"""Do the actual removal of back reference from receiver to senderkey"""
+	receiverkey = id(receiver)
+	set = sendersBack.get( receiverkey, () )
+	while senderkey in set:
+		try:
+			set.remove( senderkey )
+		except:
+			break
+	if not set:
+		try:
+			del sendersBack[ receiverkey ]
+		except KeyError:
+			pass
+	return True

Added: grass/trunk/lib/python/pydispatch/errors.py
===================================================================
--- grass/trunk/lib/python/pydispatch/errors.py	                        (rev 0)
+++ grass/trunk/lib/python/pydispatch/errors.py	2013-03-11 15:21:57 UTC (rev 55316)
@@ -0,0 +1,10 @@
+"""Error types for dispatcher mechanism
+"""
+
+class DispatcherError(Exception):
+	"""Base class for all Dispatcher errors"""
+class DispatcherKeyError(KeyError, DispatcherError):
+	"""Error raised when unknown (sender,signal) set specified"""
+class DispatcherTypeError(TypeError, DispatcherError):
+	"""Error raised when inappropriate signal-type specified (None)"""
+

Added: grass/trunk/lib/python/pydispatch/license.txt
===================================================================
--- grass/trunk/lib/python/pydispatch/license.txt	                        (rev 0)
+++ grass/trunk/lib/python/pydispatch/license.txt	2013-03-11 15:21:57 UTC (rev 55316)
@@ -0,0 +1,34 @@
+PyDispatcher License
+
+	Copyright (c) 2001-2006, Patrick K. O'Brien and Contributors
+	All rights reserved.
+	
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+	
+		Redistributions of source code must retain the above copyright
+		notice, this list of conditions and the following disclaimer.
+	
+		Redistributions in binary form must reproduce the above
+		copyright notice, this list of conditions and the following
+		disclaimer in the documentation and/or other materials
+		provided with the distribution.
+	
+		The name of Patrick K. O'Brien, or the name of any Contributor,
+		may not be used to endorse or promote products derived from this 
+		software without specific prior written permission.
+	
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+	FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+	COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+	OF THE POSSIBILITY OF SUCH DAMAGE. 
+

Added: grass/trunk/lib/python/pydispatch/pydispatchlib.dox
===================================================================
--- grass/trunk/lib/python/pydispatch/pydispatchlib.dox	                        (rev 0)
+++ grass/trunk/lib/python/pydispatch/pydispatchlib.dox	2013-03-11 15:21:57 UTC (rev 55316)
@@ -0,0 +1,13 @@
+/*! \page pydispach Customized PyDispacher library
+
+by GRASS Development Team (http://grass.osgeo.org)
+
+\section pydispachIntro Introduction
+
+TODO
+
+\section pydispachAuthors Authors
+
+Patrick K. O'Brien, Mike C. Fletcher and Contributors (original authors, see pydispatch/license.txt and pydispatch/PKG-INFO for details)
+
+*/

Added: grass/trunk/lib/python/pydispatch/robust.py
===================================================================
--- grass/trunk/lib/python/pydispatch/robust.py	                        (rev 0)
+++ grass/trunk/lib/python/pydispatch/robust.py	2013-03-11 15:21:57 UTC (rev 55316)
@@ -0,0 +1,57 @@
+"""Module implementing error-catching version of send (sendRobust)"""
+from pydispatch.dispatcher import Any, Anonymous, liveReceivers, getAllReceivers
+from pydispatch.robustapply import robustApply
+
+def sendRobust(
+	signal=Any, 
+	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 TypeErrors
+		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 liveReceivers(getAllReceivers(sender, signal)):
+		try:
+			response = robustApply(
+				receiver,
+				signal=signal,
+				sender=sender,
+				*arguments,
+				**named
+			)
+		except Exception, err:
+			responses.append((receiver, err))
+		else:
+			responses.append((receiver, response))
+	return responses

Added: grass/trunk/lib/python/pydispatch/robustapply.py
===================================================================
--- grass/trunk/lib/python/pydispatch/robustapply.py	                        (rev 0)
+++ grass/trunk/lib/python/pydispatch/robustapply.py	2013-03-11 15:21:57 UTC (rev 55316)
@@ -0,0 +1,57 @@
+"""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.
+"""
+import sys
+if sys.hexversion >= 0x3000000:
+    im_func = '__func__'
+    im_self = '__self__'
+    im_code = '__code__'
+    func_code = '__code__'
+else:
+    im_func = 'im_func'
+    im_self = 'im_self'
+    im_code = 'im_code'
+    func_code = 'func_code'
+
+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__'):
+		# Reassign receiver to the actual method that will be called.
+		if hasattr( receiver.__call__, im_func) or hasattr( receiver.__call__, im_code):
+			receiver = receiver.__call__
+	if hasattr( receiver, im_func ):
+		# an instance-method...
+		return receiver, getattr(getattr(receiver, im_func), func_code), 1
+	elif not hasattr( receiver, func_code):
+		raise ValueError('unknown reciever type %s %s'%(receiver, type(receiver)))
+	return receiver, getattr(receiver,func_code), 0
+
+def robustApply(receiver, *arguments, **named):
+	"""Call receiver with arguments and an appropriate subset of named
+	"""
+	receiver, codeObject, startIndex = function( receiver )
+	acceptable = codeObject.co_varnames[startIndex+len(arguments):codeObject.co_argcount]
+	for name in codeObject.co_varnames[startIndex:startIndex+len(arguments)]:
+		if name in named:
+			raise TypeError(
+				"""Argument %r specified both positionally and as a keyword for calling %r"""% (
+					name, receiver,
+				)
+			)
+	if not (codeObject.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)

Added: grass/trunk/lib/python/pydispatch/saferef.py
===================================================================
--- grass/trunk/lib/python/pydispatch/saferef.py	                        (rev 0)
+++ grass/trunk/lib/python/pydispatch/saferef.py	2013-03-11 15:21:57 UTC (rev 55316)
@@ -0,0 +1,171 @@
+"""Refactored "safe reference" from dispatcher.py"""
+import weakref, traceback, sys
+
+if sys.hexversion >= 0x3000000:
+    im_func = '__func__'
+    im_self = '__self__'
+else:
+    im_func = 'im_func'
+    im_self = 'im_self'
+def safeRef(target, onDelete = 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.
+	onDelete -- 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 getattr(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), """safeRef target %r has %s, but no %s, don't know how to create reference"""%( target,im_self,im_func)
+			reference = BoundMethodWeakref(
+				target=target,
+				onDelete=onDelete
+			)
+			return reference
+	if onDelete is not None:
+		return weakref.ref(target, onDelete)
+	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 calculateKey method applied to the
+			target instance method
+		deletionMethods -- 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 onDelete parameters of safeRef calls.
+		weakSelf -- weak reference to the target object
+		weakFunc -- weak reference to the target function
+
+	Class Attributes:
+		_allInstances -- class attribute pointing to all live
+			BoundMethodWeakref objects indexed by the class's
+			calculateKey(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.
+
+	"""
+	_allInstances = weakref.WeakValueDictionary()
+	def __new__( cls, target, onDelete=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
+		deletionMethods attribute updated.  Otherwise the
+		new instance is created and registered in the table
+		of already-referenced methods.
+		"""
+		key = cls.calculateKey(target)
+		current =cls._allInstances.get(key)
+		if current is not None:
+			current.deletionMethods.append( onDelete)
+			return current
+		else:
+			base = super( BoundMethodWeakref, cls).__new__( cls )
+			cls._allInstances[key] = base
+			base.__init__( target, onDelete, *arguments,**named)
+			return base
+	def __init__(self, target, onDelete=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:
+				target.<im_func>.__get__( target.<im_self> )
+			which is true of built-in instance methods.
+		onDelete -- 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.deletionMethods[:]
+			del self.deletionMethods[:]
+			try:
+				del self.__class__._allInstances[ self.key ]
+			except KeyError:
+				pass
+			for function in methods:
+				try:
+					if hasattr(function, '__call__' ):
+						function( self )
+				except Exception, e:
+					try:
+						traceback.print_exc()
+					except AttributeError:
+						print '''Exception during saferef %s cleanup function %s: %s'''%(
+							self, function, e
+						)
+		self.deletionMethods = [onDelete]
+		self.key = self.calculateKey( target )
+		self.weakSelf = weakref.ref(getattr(target,im_self), remove)
+		self.weakFunc = weakref.ref(getattr(target,im_func), remove)
+		self.selfName = getattr(target,im_self).__class__.__name__
+		self.funcName = str(getattr(target,im_func).__name__)
+	def calculateKey( 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(getattr(target,im_self)),id(getattr(target,im_func)))
+	calculateKey = classmethod( calculateKey )
+	def __str__(self):
+		"""Give a friendly representation of the object"""
+		return """%s( %s.%s )"""%(
+			self.__class__.__name__,
+			self.selfName,
+			self.funcName,
+		)
+	__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.weakSelf()
+		if target is not None:
+			function = self.weakFunc()
+			if function is not None:
+				return function.__get__(target)
+		return None



More information about the grass-commit mailing list