Browse Source

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 years ago
parent
commit
7c49f3c38a

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

@@ -13,11 +13,11 @@
 #     * Neither the name of the <organization> nor the
 #     * Neither the name of the <organization> nor the
 #       names of its contributors may be used to endorse or promote products
 #       names of its contributors may be used to endorse or promote products
 #       derived from this software without specific prior written permission.
 #       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
 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 # 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
 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 # 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.
 # 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
 """ 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
 from grass.imaging import images2ims
 
 
 
 
@@ -48,42 +50,45 @@ def _cleanDir(tempDir):
         try:
         try:
             shutil.rmtree(tempDir)
             shutil.rmtree(tempDir)
         except Exception:
         except Exception:
-            time.sleep(0.2) # Give OS time to free sources
+            time.sleep(0.2)  # Give OS time to free sources
         else:
         else:
             break
             break
     else:
     else:
         print("Oops, could not fully clean up temporary files.")
         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.
     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.
     between 0 and 1 for float types.
-    
+
     Requires the "ffmpeg" application:
     Requires the "ffmpeg" application:
       * Most linux users can install using their package manager
       * Most linux users can install using their package manager
       * There is a windows installer on the visvis website
       * 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
     # Get fps
     try:
     try:
         fps = float(1.0/duration)
         fps = float(1.0/duration)
     except Exception:
     except Exception:
         raise ValueError("Invalid duration parameter for writeAvi.")
         raise ValueError("Invalid duration parameter for writeAvi.")
-    
+
     # Determine temp dir and create images
     # 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
     # Determine formatter
     N = len(images)
     N = len(images)
     formatter = '%04d'
     formatter = '%04d'
@@ -93,21 +98,21 @@ def writeAvi(filename, images, duration=0.1, encoding='mpeg4',
         formatter = '%02d'
         formatter = '%02d'
     elif N < 1000:
     elif N < 1000:
         formatter = '%03d'
         formatter = '%03d'
-    
+
     # Compile command to create avi
     # Compile command to create avi
     command = "ffmpeg -r %i %s " % (int(fps), inputOptions)
     command = "ffmpeg -r %i %s " % (int(fps), inputOptions)
     command += "-i im%s.png " % (formatter,)
     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"
     command += "output.avi"
-    
+
     # Run ffmpeg
     # Run ffmpeg
     S = subprocess.Popen(command, shell=True, cwd=tempDir,
     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
     # Show what ffmpeg has to say
     outPut = S.stdout.read()
     outPut = S.stdout.read()
-    
-    if S.wait():    
+
+    if S.wait():
         # An error occured, show
         # An error occured, show
         print(outPut)
         print(outPut)
         print(S.stderr.read())
         print(S.stderr.read())
@@ -122,37 +127,37 @@ def writeAvi(filename, images, duration=0.1, encoding='mpeg4',
 
 
 
 
 def readAvi(filename, asNumpy=True):
 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:
     Requires the "ffmpeg" application:
       * Most linux users can install using their package manager
       * Most linux users can install using their package manager
       * There is a windows installer on the visvis website
       * There is a windows installer on the visvis website
-    
+
+    :param str filename: name of input movie file
+    :param bool asNumpy:
     """
     """
-    
+
     # Check whether it exists
     # Check whether it exists
     if not os.path.isfile(filename):
     if not os.path.isfile(filename):
         raise IOError('File not found: '+str(filename))
         raise IOError('File not found: '+str(filename))
-    
+
     # Determine temp dir, make sure it exists
     # 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):
     if not os.path.isdir(tempDir):
         os.makedirs(tempDir)
         os.makedirs(tempDir)
-    
+
     # Copy movie there
     # Copy movie there
     shutil.copy(filename, os.path.join(tempDir, 'input.avi'))
     shutil.copy(filename, os.path.join(tempDir, 'input.avi'))
-    
+
     # Run ffmpeg
     # Run ffmpeg
     command = "ffmpeg -i input.avi im%d.jpg"
     command = "ffmpeg -i input.avi im%d.jpg"
     S = subprocess.Popen(command, shell=True, cwd=tempDir,
     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
     # Show what mencodec has to say
     outPut = S.stdout.read()
     outPut = S.stdout.read()
-    
-    if S.wait():    
+
+    if S.wait():
         # An error occured, show
         # An error occured, show
         print(outPut)
         print(outPut)
         print(S.stderr.read())
         print(S.stderr.read())
@@ -164,6 +169,6 @@ def readAvi(filename, asNumpy=True):
         images = images2ims.readIms(os.path.join(tempDir, 'im*.jpg'), asNumpy)
         images = images2ims.readIms(os.path.join(tempDir, 'im*.jpg'), asNumpy)
         # Clean up
         # Clean up
         _cleanDir(tempDir)
         _cleanDir(tempDir)
-    
+
     # Done
     # Done
     return images
     return images

File diff suppressed because it is too large
+ 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
 #     * Neither the name of the <organization> nor the
 #       names of its contributors may be used to endorse or promote products
 #       names of its contributors may be used to endorse or promote products
 #       derived from this software without specific prior written permission.
 #       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
 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 # 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
 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
@@ -36,69 +36,69 @@ import os
 try:
 try:
     import numpy as np
     import numpy as np
 except ImportError:
 except ImportError:
-    np = None    
+    np = None
 
 
 try:
 try:
     import PIL
     import PIL
-    from PIL import Image    
 except ImportError:
 except ImportError:
     PIL = None
     PIL = None
 
 
 
 
 def checkImages(images):
 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.
     The same for all movie formats.
-    """ 
+
+    :param images:
+    """
     # Init results
     # Init results
     images2 = []
     images2 = []
-    
+
     for im in images:
     for im in images:
         if PIL and isinstance(im, PIL.Image.Image):
         if PIL and isinstance(im, PIL.Image.Image):
             # We assume PIL images are allright
             # We assume PIL images are allright
             images2.append(im)
             images2.append(im)
-        
+
         elif np and isinstance(im, np.ndarray):
         elif np and isinstance(im, np.ndarray):
             # Check and convert dtype
             # Check and convert dtype
             if im.dtype == np.uint8:
             if im.dtype == np.uint8:
-                images2.append(im) # Ok
+                images2.append(im)  # Ok
             elif im.dtype in [np.float32, np.float64]:
             elif im.dtype in [np.float32, np.float64]:
                 theMax = im.max()
                 theMax = im.max()
                 if theMax > 128 and theMax < 300:
                 if theMax > 128 and theMax < 300:
-                    pass # assume 0:255
+                    pass  # assume 0:255
                 else:
                 else:
                     im = im.copy()
                     im = im.copy()
-                    im[im<0] = 0
-                    im[im>1] = 1
+                    im[im < 0] = 0
+                    im[im > 1] = 1
                     im *= 255
                     im *= 255
-                images2.append( im.astype(np.uint8) )
+                images2.append(im.astype(np.uint8))
             else:
             else:
                 im = im.astype(np.uint8)
                 im = im.astype(np.uint8)
                 images2.append(im)
                 images2.append(im)
             # Check size
             # Check size
             if im.ndim == 2:
             if im.ndim == 2:
-                pass # ok
+                pass  # ok
             elif im.ndim == 3:
             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.')
                     raise ValueError('This array can not represent an image.')
             else:
             else:
                 raise ValueError('This array can not represent an image.')
                 raise ValueError('This array can not represent an image.')
         else:
         else:
             raise ValueError('Invalid image type: ' + str(type(im)))
             raise ValueError('Invalid image type: ' + str(type(im)))
-    
+
     # Done
     # Done
     return images2
     return images2
 
 
 
 
 def _getFilenameParts(filename):
 def _getFilenameParts(filename):
     if '*' in filename:
     if '*' in filename:
-        return tuple( filename.split('*',1) )
+        return tuple(filename.split('*', 1))
     else:
     else:
         return os.path.splitext(filename)
         return os.path.splitext(filename)
 
 
 
 
 def _getFilenameWithFormatter(filename, N):
 def _getFilenameWithFormatter(filename, N):
-    
+
     # Determine sequence number formatter
     # Determine sequence number formatter
     formatter = '%04i'
     formatter = '%04i'
     if N < 10:
     if N < 10:
@@ -107,11 +107,11 @@ def _getFilenameWithFormatter(filename, N):
         formatter = '%02i'
         formatter = '%02i'
     elif N < 1000:
     elif N < 1000:
         formatter = '%03i'
         formatter = '%03i'
-    
+
     # Insert sequence number formatter
     # Insert sequence number formatter
     part1, part2 = _getFilenameParts(filename)
     part1, part2 = _getFilenameParts(filename)
     return part1 + formatter + part2
     return part1 + formatter + part2
-    
+
 
 
 def _getSequenceNumber(filename, part1, part2):
 def _getSequenceNumber(filename, part1, part2):
     # Get string bit
     # Get string bit
@@ -128,83 +128,84 @@ def _getSequenceNumber(filename, part1, part2):
 
 
 
 
 def writeIms(filename, images):
 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.
     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.
     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.
     between 0 and 1 for float types.
-    
+
+    :param filename:
+    :param images:
     """
     """
-    
+
     # Check PIL
     # Check PIL
     if PIL is None:
     if PIL is None:
         raise RuntimeError("Need PIL to write series of image files.")
         raise RuntimeError("Need PIL to write series of image files.")
-    
+
     # Check images
     # Check images
     images = checkImages(images)
     images = checkImages(images)
-    
+
     # Get dirname and filename
     # Get dirname and filename
     filename = os.path.abspath(filename)
     filename = os.path.abspath(filename)
     dirname, filename = os.path.split(filename)
     dirname, filename = os.path.split(filename)
-    
+
     # Create dir(s) if we need to
     # Create dir(s) if we need to
     if not os.path.isdir(dirname):
     if not os.path.isdir(dirname):
         os.makedirs(dirname)
         os.makedirs(dirname)
-    
+
     # Insert formatter
     # Insert formatter
     filename = _getFilenameWithFormatter(filename, len(images))
     filename = _getFilenameWithFormatter(filename, len(images))
-    
+
     # Write
     # Write
     seq = 0
     seq = 0
     for frame in images:
     for frame in images:
         seq += 1
         seq += 1
         # Get filename
         # Get filename
-        fname = os.path.join(dirname, filename%seq)
+        fname = os.path.join(dirname, filename % seq)
         # Write image
         # Write image
         if np and isinstance(frame, np.ndarray):
         if np and isinstance(frame, np.ndarray):
-            frame =  PIL.Image.fromarray(frame)        
+            frame = PIL.Image.fromarray(frame)
         frame.save(fname)
         frame.save(fname)
 
 
 
 
-
 def readIms(filename, asNumpy=True):
 def readIms(filename, asNumpy=True):
     """ 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.
     list of numpy arrays, or, if asNumpy is false, a list if PIL images.
-    
+
+    :param filename:
+    :param bool asNumpy:
     """
     """
-    
+
     # Check PIL
     # Check PIL
     if PIL is None:
     if PIL is None:
         raise RuntimeError("Need PIL to read a series of image files.")
         raise RuntimeError("Need PIL to read a series of image files.")
-    
+
     # Check Numpy
     # Check Numpy
     if asNumpy and np is None:
     if asNumpy and np is None:
         raise RuntimeError("Need Numpy to return numpy arrays.")
         raise RuntimeError("Need Numpy to return numpy arrays.")
-    
+
     # Get dirname and filename
     # Get dirname and filename
     filename = os.path.abspath(filename)
     filename = os.path.abspath(filename)
     dirname, filename = os.path.split(filename)
     dirname, filename = os.path.split(filename)
-    
+
     # Check dir exists
     # Check dir exists
     if not os.path.isdir(dirname):
     if not os.path.isdir(dirname):
         raise IOError('Directory not found: '+str(dirname))
         raise IOError('Directory not found: '+str(dirname))
-    
+
     # Get two parts of the filename
     # Get two parts of the filename
     part1, part2 = _getFilenameParts(filename)
     part1, part2 = _getFilenameParts(filename)
-    
+
     # Init images
     # Init images
     images = []
     images = []
-    
+
     # Get all files in directory
     # Get all files in directory
     for fname in os.listdir(dirname):
     for fname in os.listdir(dirname):
         if fname.startswith(part1) and fname.endswith(part2):
         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)
             # Get Pil image and store copy (to prevent keeping the file)
             im = PIL.Image.open(os.path.join(dirname, fname))
             im = PIL.Image.open(os.path.join(dirname, fname))
             images.append((im.copy(), nr))
             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]
     images = [im[0] for im in images]
-    
+
     # Convert to numpy if needed
     # Convert to numpy if needed
     if asNumpy:
     if asNumpy:
         images2 = images
         images2 = images
@@ -228,10 +229,10 @@ def readIms(filename, asNumpy=True):
                 im = im.convert()
                 im = im.convert()
             # Make numpy array
             # Make numpy array
             a = np.asarray(im)
             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")
                 raise MemoryError("Too little memory to convert PIL image to array")
             # Add
             # Add
             images.append(a)
             images.append(a)
-    
+
     # Done
     # Done
     return images
     return images

File diff suppressed because it is too large
+ 319 - 318
lib/python/imaging/images2swf.py


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

@@ -6,24 +6,24 @@ system.
 
 
 Module attributes of note:
 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:
 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
 from __future__ import generators
 import weakref
 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"
 __cvsid__ = "Id: dispatcher.py,v 1.1 2010/03/30 15:45:55 mcfletch Exp"
 __version__ = "Revision: 1.1"
 __version__ = "Revision: 1.1"
 
 
+
 class _Parameter:
 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):
 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()
 Any = _Any()
 
 
+
 class _Anonymous(_Parameter):
 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()
 Anonymous = _Anonymous()
 
 
 WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
 WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
@@ -76,419 +79,428 @@ sendersBack = {}
 
 
 
 
 def connect(receiver, signal=Any, sender=Any, weak=True):
 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):
 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):
 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):
 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):
 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
            # The order of replacing many chnage, so both variables need
            # to be checked.
            # 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):
 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):
 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):
 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
 """Error types for dispatcher mechanism
 """
 """
 
 
+
 class DispatcherError(Exception):
 class DispatcherError(Exception):
-	"""Base class for all Dispatcher errors"""
+    """Base class for all Dispatcher errors"""
+
+
 class DispatcherKeyError(KeyError, DispatcherError):
 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.dispatcher import Any, Anonymous, liveReceivers, getAllReceivers
 from grass.pydispatch.robustapply import robustApply
 from grass.pydispatch.robustapply import robustApply
 
 
+
 def sendRobust(
 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'
     im_code = 'im_code'
     func_code = 'func_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):
 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:
 else:
     im_func = 'im_func'
     im_func = 'im_func'
     im_self = 'im_self'
     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):
 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
     Should work on the most of Python implementations where name of lambda
     function is not unique.
     function is not unique.
-    
+
     >>> mylambda = lambda x: x*x
     >>> mylambda = lambda x: x*x
     >>> _islambda(mylambda)
     >>> _islambda(mylambda)
     True
     True
@@ -173,7 +173,7 @@ class Signal(object):
         DispatcherKeyError: 'No receivers found for signal <__main__.Signal object at 0x...> from sender _Any'
         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.
         Disconnecting the non-exiting or unknown handler will result in error.
-        
+
         >>> signal1.disconnect(some_function)
         >>> signal1.disconnect(some_function)
         Traceback (most recent call last):
         Traceback (most recent call last):
         NameError: name 'some_function' is not defined
         NameError: name 'some_function' is not defined