Переглянути джерело

lib python: improve documentation; PEP8 cleaning

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@61763 15284696-431f-4ddb-bdfa-cd5b030d7da7
Luca Delucchi 10 роки тому
батько
коміт
7c49f3c38a

+ 51 - 46
lib/python/imaging/images2avi.py

@@ -13,11 +13,11 @@
 #     * Neither the name of the <organization> nor the
 #       names of its contributors may 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" 
+#
+# 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 <COPYRIGHT HOLDER> BE LIABLE FOR ANY 
+# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> 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
@@ -26,7 +26,7 @@
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #
 #
-# changes of this file GRASS (PNG instead of JPG) by Anna Petrasova 2013 
+# changes of this file GRASS (PNG instead of JPG) by Anna Petrasova 2013
 
 """ Module images2avi
 
@@ -38,8 +38,10 @@ http://linux.die.net/man/1/ffmpeg
 
 """
 
-import os, time
-import subprocess, shutil
+import os
+import time
+import subprocess
+import shutil
 from grass.imaging import images2ims
 
 
@@ -48,42 +50,45 @@ def _cleanDir(tempDir):
         try:
             shutil.rmtree(tempDir)
         except Exception:
-            time.sleep(0.2) # Give OS time to free sources
+            time.sleep(0.2)  # Give OS time to free sources
         else:
             break
     else:
         print("Oops, could not fully clean up temporary files.")
 
 
-def writeAvi(filename, images, duration=0.1, encoding='mpeg4', 
-                                        inputOptions='', outputOptions='' ):
-    """ writeAvi(filename, duration=0.1, encoding='mpeg4',
-                    inputOptions='', outputOptions='')
-    
-    Export movie to a AVI file, which is encoded with the given 
-    encoding. Hint for Windows users: the 'msmpeg4v2' codec is 
+def writeAvi(filename, images, duration=0.1, encoding='mpeg4',
+             inputOptions='', outputOptions=''):
+    """Export movie to a AVI file, which is encoded with the given
+    encoding. Hint for Windows users: the 'msmpeg4v2' codec is
     natively supported on Windows.
-    
-    Images should be a list consisting of PIL images or numpy arrays. 
-    The latter should be between 0 and 255 for integer types, and 
+
+    Images should be a list consisting of PIL images or numpy arrays.
+    The latter should be between 0 and 255 for integer types, and
     between 0 and 1 for float types.
-    
+
     Requires the "ffmpeg" application:
       * Most linux users can install using their package manager
       * There is a windows installer on the visvis website
-    
+
+    :param str filename: output filename
+    :param images:
+    :param float duration:
+    :param str encoding: the encoding type
+    :param inputOptions:
+    :param outputOptions:
     """
-    
+
     # Get fps
     try:
         fps = float(1.0/duration)
     except Exception:
         raise ValueError("Invalid duration parameter for writeAvi.")
-    
+
     # Determine temp dir and create images
-    tempDir = os.path.join( os.path.expanduser('~'), '.tempIms')
-    images2ims.writeIms( os.path.join(tempDir, 'im*.png'), images)
-    
+    tempDir = os.path.join(os.path.expanduser('~'), '.tempIms')
+    images2ims.writeIms(os.path.join(tempDir, 'im*.png'), images)
+
     # Determine formatter
     N = len(images)
     formatter = '%04d'
@@ -93,21 +98,21 @@ def writeAvi(filename, images, duration=0.1, encoding='mpeg4',
         formatter = '%02d'
     elif N < 1000:
         formatter = '%03d'
-    
+
     # Compile command to create avi
     command = "ffmpeg -r %i %s " % (int(fps), inputOptions)
     command += "-i im%s.png " % (formatter,)
-    command += "-g 1 -vcodec %s %s " % (encoding, outputOptions) 
+    command += "-g 1 -vcodec %s %s " % (encoding, outputOptions)
     command += "output.avi"
-    
+
     # Run ffmpeg
     S = subprocess.Popen(command, shell=True, cwd=tempDir,
-                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    
+                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
     # Show what ffmpeg has to say
     outPut = S.stdout.read()
-    
-    if S.wait():    
+
+    if S.wait():
         # An error occured, show
         print(outPut)
         print(S.stderr.read())
@@ -122,37 +127,37 @@ def writeAvi(filename, images, duration=0.1, encoding='mpeg4',
 
 
 def readAvi(filename, asNumpy=True):
-    """ readAvi(filename, asNumpy=True)
-    
-    Read images from an AVI (or MPG) movie.
-    
+    """Read images from an AVI (or MPG) movie.
+
     Requires the "ffmpeg" application:
       * Most linux users can install using their package manager
       * There is a windows installer on the visvis website
-    
+
+    :param str filename: name of input movie file
+    :param bool asNumpy:
     """
-    
+
     # Check whether it exists
     if not os.path.isfile(filename):
         raise IOError('File not found: '+str(filename))
-    
+
     # Determine temp dir, make sure it exists
-    tempDir = os.path.join( os.path.expanduser('~'), '.tempIms')
+    tempDir = os.path.join(os.path.expanduser('~'), '.tempIms')
     if not os.path.isdir(tempDir):
         os.makedirs(tempDir)
-    
+
     # Copy movie there
     shutil.copy(filename, os.path.join(tempDir, 'input.avi'))
-    
+
     # Run ffmpeg
     command = "ffmpeg -i input.avi im%d.jpg"
     S = subprocess.Popen(command, shell=True, cwd=tempDir,
-                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    
+                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
     # Show what mencodec has to say
     outPut = S.stdout.read()
-    
-    if S.wait():    
+
+    if S.wait():
         # An error occured, show
         print(outPut)
         print(S.stderr.read())
@@ -164,6 +169,6 @@ def readAvi(filename, asNumpy=True):
         images = images2ims.readIms(os.path.join(tempDir, 'im*.jpg'), asNumpy)
         # Clean up
         _cleanDir(tempDir)
-    
+
     # Done
     return images

Різницю між файлами не показано, бо вона завелика
+ 373 - 397
lib/python/imaging/images2gif.py


+ 59 - 58
lib/python/imaging/images2ims.py

@@ -13,11 +13,11 @@
 #     * Neither the name of the <organization> nor the
 #       names of its contributors may 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" 
+#
+# 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 <COPYRIGHT HOLDER> BE LIABLE FOR ANY 
+# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> 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
@@ -36,69 +36,69 @@ import os
 try:
     import numpy as np
 except ImportError:
-    np = None    
+    np = None
 
 try:
     import PIL
-    from PIL import Image    
 except ImportError:
     PIL = None
 
 
 def checkImages(images):
-    """ checkImages(images)
-    Check numpy images and correct intensity range etc.
+    """Check numpy images and correct intensity range etc.
     The same for all movie formats.
-    """ 
+
+    :param images:
+    """
     # Init results
     images2 = []
-    
+
     for im in images:
         if PIL and isinstance(im, PIL.Image.Image):
             # We assume PIL images are allright
             images2.append(im)
-        
+
         elif np and isinstance(im, np.ndarray):
             # Check and convert dtype
             if im.dtype == np.uint8:
-                images2.append(im) # Ok
+                images2.append(im)  # Ok
             elif im.dtype in [np.float32, np.float64]:
                 theMax = im.max()
                 if theMax > 128 and theMax < 300:
-                    pass # assume 0:255
+                    pass  # assume 0:255
                 else:
                     im = im.copy()
-                    im[im<0] = 0
-                    im[im>1] = 1
+                    im[im < 0] = 0
+                    im[im > 1] = 1
                     im *= 255
-                images2.append( im.astype(np.uint8) )
+                images2.append(im.astype(np.uint8))
             else:
                 im = im.astype(np.uint8)
                 images2.append(im)
             # Check size
             if im.ndim == 2:
-                pass # ok
+                pass  # ok
             elif im.ndim == 3:
-                if im.shape[2] not in [3,4]:
+                if im.shape[2] not in [3, 4]:
                     raise ValueError('This array can not represent an image.')
             else:
                 raise ValueError('This array can not represent an image.')
         else:
             raise ValueError('Invalid image type: ' + str(type(im)))
-    
+
     # Done
     return images2
 
 
 def _getFilenameParts(filename):
     if '*' in filename:
-        return tuple( filename.split('*',1) )
+        return tuple(filename.split('*', 1))
     else:
         return os.path.splitext(filename)
 
 
 def _getFilenameWithFormatter(filename, N):
-    
+
     # Determine sequence number formatter
     formatter = '%04i'
     if N < 10:
@@ -107,11 +107,11 @@ def _getFilenameWithFormatter(filename, N):
         formatter = '%02i'
     elif N < 1000:
         formatter = '%03i'
-    
+
     # Insert sequence number formatter
     part1, part2 = _getFilenameParts(filename)
     return part1 + formatter + part2
-    
+
 
 def _getSequenceNumber(filename, part1, part2):
     # Get string bit
@@ -128,83 +128,84 @@ def _getSequenceNumber(filename, part1, part2):
 
 
 def writeIms(filename, images):
-    """ writeIms(filename, images)
-    
-    Export movie to a series of image files. If the filenenumber 
-    contains an asterix, a sequence number is introduced at its 
-    location. Otherwise the sequence number is introduced right 
+    """Export movie to a series of image files. If the filenenumber
+    contains an asterix, a sequence number is introduced at its
+    location. Otherwise the sequence number is introduced right
     before the final dot.
-    
-    To enable easy creation of a new directory with image files, 
+
+    To enable easy creation of a new directory with image files,
     it is made sure that the full path exists.
-    
-    Images should be a list consisting of PIL images or numpy arrays. 
-    The latter should be between 0 and 255 for integer types, and 
+
+    Images should be a list consisting of PIL images or numpy arrays.
+    The latter should be between 0 and 255 for integer types, and
     between 0 and 1 for float types.
-    
+
+    :param filename:
+    :param images:
     """
-    
+
     # Check PIL
     if PIL is None:
         raise RuntimeError("Need PIL to write series of image files.")
-    
+
     # Check images
     images = checkImages(images)
-    
+
     # Get dirname and filename
     filename = os.path.abspath(filename)
     dirname, filename = os.path.split(filename)
-    
+
     # Create dir(s) if we need to
     if not os.path.isdir(dirname):
         os.makedirs(dirname)
-    
+
     # Insert formatter
     filename = _getFilenameWithFormatter(filename, len(images))
-    
+
     # Write
     seq = 0
     for frame in images:
         seq += 1
         # Get filename
-        fname = os.path.join(dirname, filename%seq)
+        fname = os.path.join(dirname, filename % seq)
         # Write image
         if np and isinstance(frame, np.ndarray):
-            frame =  PIL.Image.fromarray(frame)        
+            frame = PIL.Image.fromarray(frame)
         frame.save(fname)
 
 
-
 def readIms(filename, asNumpy=True):
     """ readIms(filename, asNumpy=True)
-    
-    Read images from a series of images in a single directory. Returns a 
+
+    Read images from a series of images in a single directory. Returns a
     list of numpy arrays, or, if asNumpy is false, a list if PIL images.
-    
+
+    :param filename:
+    :param bool asNumpy:
     """
-    
+
     # Check PIL
     if PIL is None:
         raise RuntimeError("Need PIL to read a series of image files.")
-    
+
     # Check Numpy
     if asNumpy and np is None:
         raise RuntimeError("Need Numpy to return numpy arrays.")
-    
+
     # Get dirname and filename
     filename = os.path.abspath(filename)
     dirname, filename = os.path.split(filename)
-    
+
     # Check dir exists
     if not os.path.isdir(dirname):
         raise IOError('Directory not found: '+str(dirname))
-    
+
     # Get two parts of the filename
     part1, part2 = _getFilenameParts(filename)
-    
+
     # Init images
     images = []
-    
+
     # Get all files in directory
     for fname in os.listdir(dirname):
         if fname.startswith(part1) and fname.endswith(part2):
@@ -213,11 +214,11 @@ def readIms(filename, asNumpy=True):
             # Get Pil image and store copy (to prevent keeping the file)
             im = PIL.Image.open(os.path.join(dirname, fname))
             images.append((im.copy(), nr))
-    
-    # Sort images 
-    images.sort(key=lambda x:x[1])    
+
+    # Sort images
+    images.sort(key=lambda x: x[1])
     images = [im[0] for im in images]
-    
+
     # Convert to numpy if needed
     if asNumpy:
         images2 = images
@@ -228,10 +229,10 @@ def readIms(filename, asNumpy=True):
                 im = im.convert()
             # Make numpy array
             a = np.asarray(im)
-            if len(a.shape)==0:
+            if len(a.shape) == 0:
                 raise MemoryError("Too little memory to convert PIL image to array")
             # Add
             images.append(a)
-    
+
     # Done
     return images

Різницю між файлами не показано, бо вона завелика
+ 319 - 318
lib/python/imaging/images2swf.py


+ 453 - 441
lib/python/pydispatch/dispatcher.py

@@ -6,24 +6,24 @@ 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.
+    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.)
+    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
@@ -33,39 +33,42 @@ __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
 __cvsid__ = "Id: dispatcher.py,v 1.1 2010/03/30 15:45:55 mcfletch Exp"
 __version__ = "Revision: 1.1"
 
+
 class _Parameter:
-	"""Used to represent default parameter values."""
-	def __repr__(self):
-		return self.__class__.__name__
+    """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"
+    """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.
-	"""
+    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.
-	"""
+    """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)
@@ -76,419 +79,428 @@ 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)
-
+    """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 []
+    """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
+    """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
-	
+    """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 or not connections:
-		# During module cleanup the objects will be replaced with None
+    """Remove receiver from connections."""
+    if not sendersBack or not connections:
+        # During module cleanup the objects will be replaced with None
            # The order of replacing many chnage, so both variables need
            # to be checked.
-		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)
+        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)
+    """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 )
+    """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
+    """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

+ 8 - 4
lib/python/pydispatch/errors.py

@@ -1,10 +1,14 @@
 """Error types for dispatcher mechanism
 """
 
+
 class DispatcherError(Exception):
-	"""Base class for all Dispatcher errors"""
+    """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)"""
+    """Error raised when unknown (sender,signal) set specified"""
+
 
+class DispatcherTypeError(TypeError, DispatcherError):
+    """Error raised when inappropriate signal-type specified (None)"""

+ 52 - 51
lib/python/pydispatch/robust.py

@@ -2,56 +2,57 @@
 from grass.pydispatch.dispatcher import Any, Anonymous, liveReceivers, getAllReceivers
 from grass.pydispatch.robustapply import robustApply
 
+
 def sendRobust(
-	signal=Any, 
-	sender=Anonymous, 
-	*arguments, **named
+    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 as err:
-			responses.append((receiver, err))
-		else:
-			responses.append((receiver, response))
-	return responses
+    """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 as err:
+            responses.append((receiver, err))
+        else:
+            responses.append((receiver, response))
+    return responses

+ 37 - 34
lib/python/pydispatch/robustapply.py

@@ -17,41 +17,44 @@ else:
     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)
+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
 
-	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)
+    """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)

+ 173 - 160
lib/python/pydispatch/saferef.py

@@ -7,165 +7,178 @@ if sys.hexversion >= 0x3000000:
 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 )
+
+
+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 as 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
+    """'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 as 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

+ 2 - 2
lib/python/pydispatch/signal.py

@@ -15,7 +15,7 @@ def _islambda(function):
 
     Should work on the most of Python implementations where name of lambda
     function is not unique.
-    
+
     >>> mylambda = lambda x: x*x
     >>> _islambda(mylambda)
     True
@@ -173,7 +173,7 @@ class Signal(object):
         DispatcherKeyError: 'No receivers found for signal <__main__.Signal object at 0x...> from sender _Any'
 
         Disconnecting the non-exiting or unknown handler will result in error.
-        
+
         >>> signal1.disconnect(some_function)
         Traceback (most recent call last):
         NameError: name 'some_function' is not defined