Revision: 6276
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6276&view=rev
Author:   fer_perez
Date:     2008-10-19 09:11:30 +0000 (Sun, 19 Oct 2008)

Log Message:
-----------
Workbook updates.

Modified Paths:
--------------
    trunk/py4science/workbook/bessel.tex
    trunk/py4science/workbook/fft_imdenoise.tex
    trunk/py4science/workbook/qsort.tex
    trunk/py4science/workbook/quad_newton.tex
    trunk/py4science/workbook/trapezoid.tex
    trunk/py4science/workbook/update_problems.py
    trunk/py4science/workbook/wallis_pi.tex

Added Paths:
-----------
    trunk/py4science/examples/mkprob.py

Removed Paths:
-------------
    trunk/py4science/examples/mkskel.py

Copied: trunk/py4science/examples/mkprob.py (from rev 6270, 
trunk/py4science/examples/mkskel.py)
===================================================================
--- trunk/py4science/examples/mkprob.py                         (rev 0)
+++ trunk/py4science/examples/mkprob.py 2008-10-19 09:11:30 UTC (rev 6276)
@@ -0,0 +1,325 @@
+#!/usr/bin/env python
+"""Make skeletons out of Python scripts.
+
+Usage:
+
+   mkprob.py [--test]  file1.py file2.py ....
+
+If --test is given, the test suite is run instead.
+
+For each input filename f.py, a pair of output files is generated, f_soln.py
+and f_skel.py.
+
+Source markup is very simple.  The tests in the file show precisely how it
+works, but in summary:
+
+- Pure comment lines with the special marker (#@) are left in the skeleton
+  (only the marker is stripped, but they remain as valid comments).  These are
+  typically used for hints.
+
+- Code lines terminated with the marker are:
+
+  - In the skeleton, replaced by a NotImplementedError call.  Consecutive lines
+  are replaced by a single call.
+
+  - In the solution, kept as is but the marker is removed.
+"""
+
+from __future__ import with_statement
+
+#-----------------------------------------------------------------------------
+# Stdlib imports
+import os
+import re
+import shutil
+import sys
+
+# Third-party imports
+import nose
+
+# Constants
+MARKER = '#@'
+DEL_RE = re.compile(r'''^((\s*)(.*?))\s*%s\s*$''' % MARKER)
+HINT_RE = re.compile(r'''^(?P<space>\s*)%s\s+(?P<hint>.*)$''' % MARKER)
+                       
+
+#-----------------------------------------------------------------------------
+# Main code begins
+
+def src2soln(src):
+    """Remove markers from input source, leaving all else intact.
+
+    Inputs:
+      src : sequence of lines (file-like objects work out of the box)
+    """
+    
+    out = []
+    addline = out.append
+    for line in src:
+        # Check for lines to delete and with hints
+        mdel  = DEL_RE.match(line)
+        mhint = HINT_RE.match(line)
+
+        # All hints are unconditionally removed
+        if mhint is None:
+            if mdel:
+                # if marker is matched in code, strip it and leave the code
+                line = mdel.group(1)+'\n'
+            addline(line)
+            
+    return ''.join(out)
+
+
+def src2skel(src):
+    """Remove markers from input source, replacing marked lines.
+
+    Marked lines are replaced with "raise NotImplementedError" calls that
+    summarize the total number of deleted lines.
+
+    Inputs:
+      src : sequence of lines (file-like objects work out of the box)
+    """
+
+    def flush_buffers(normal_lines,del_lines=0):
+        """Local function to reuse some common code"""
+
+        if state_cur == normal:
+            # add the normal lines
+            out.extend(normal_lines)
+            normal_lines[:] = []
+        else:
+            # Add the summary of 'raise' lines
+
+            # flush counter of code (disabled, we report static summary)
+            #msg = '1 line' if del_lines==1 else ('%s lines' % del_lines)
+            #exc = exc_tpl % msg
+            exc = exc_tpl
+            
+            # Use the last value of 'space'
+            line = '%s%s' % (spaces[0],exc)
+            out.append(line)
+            del_lines = 0
+            spaces[:] = []
+            
+        return del_lines
+    
+    # used to report actual # of lines removed - disabled
+    #exc_tpl = "raise NotImplementedError('%s missing')\n"    
+    exc_tpl = "raise NotImplementedError('insert missing code here')\n"
+    
+    # states for state machine and other initialization
+    normal,delete = 0,1
+    state_cur = normal
+    del_lines = 0  # counter, in case we want to report # of deletions
+    spaces = []
+    normal_lines = []
+    out = []
+    
+    # To remove multiple consecutive lines of input marked for deletion, we
+    # need a small state machine.
+    for line in src:
+        # Check for lines to delete and with hints
+        mdel  = DEL_RE.match(line)
+        mhint = HINT_RE.match(line)
+
+        if mhint:
+            state_new = normal
+            hint = mhint.group('space')+'# ' + mhint.group('hint') +'\n'
+            normal_lines.append(hint)
+        else:
+            if mdel is None:
+                state_new = normal
+                normal_lines.append(line)
+            else:
+                state_new = delete
+                del_lines += 1
+                spaces.append(mdel.group(2))
+            
+        # Flush output only when there's a change of state
+        if state_new != state_cur:
+            del_lines = flush_buffers(normal_lines,del_lines)
+
+        # Update state machine
+        state_cur = state_new
+
+    # Final buffer flush is unconditional
+    flush_buffers(normal_lines)
+    
+    return ''.join(out)
+
+
+def transform_file(fpath,fname_skel,fname_soln):
+    """Run the cleanup routines for a given input, creating skel and soln.
+    """
+
+    # get the mode of the input so that we can create the output files with the
+    # same mode
+    fmode = os.stat(fpath).st_mode
+    
+    with open(fpath) as infile:
+        # Generate the skeleton
+        skel = src2skel(infile)
+        with open(fname_skel,'w') as fskel:
+            fskel.write(skel)
+        os.chmod(fname_skel,fmode)
+        
+        # Reset the input file pointer and generate the solution
+        infile.seek(0)
+        soln = src2soln(infile)
+        with open(fname_soln,'w') as fsoln:
+            fsoln.write(soln)
+        os.chmod(fname_soln,fmode)
+
+#-----------------------------------------------------------------------------
+# Main execution routines
+
+def copyforce(src,dest):
+    """Forceful file link/copy that overwrites destination files."""
+    try:
+        copy = os.link
+    except AttributeError:
+        copy = shutil.copy
+    if os.path.isfile(dest):
+        os.remove(dest)
+    copy(src,dest)
+
+
+def mvforce(src,dest):
+    """Forceful file copy that overwrites destination files."""
+    if os.path.isfile(dest):
+        os.remove(dest)
+    shutil.move(src,dest)
+
+
+def main(argv=None):
+    """Main entry point as a command line script for normal execution"""
+    
+    if argv is None:
+        argv = sys.argv
+
+    # If there are subdirs called skel and soln, we populate them by moving the
+    # generated files there, otherwise they're left in the current dir.
+    skel_dir = 'skel'
+    soln_dir = 'soln'
+    has_skel_dir = os.path.isdir(skel_dir)
+    has_soln_dir = os.path.isdir(soln_dir)
+
+    # First, check that all files are present and abort immediately if any of
+    # them isn't there.
+    for fpath in argv[1:]:
+        if not os.path.isfile(fpath):
+            raise OSError("file %r not found" % fpath)
+
+    # If all files are there, then go ahead and process them unconditionally
+    for fpath in argv[1:]:
+        basename, ext = os.path.splitext(fpath)
+        fname_skel = basename + '_skel' + ext
+        fname_soln = basename + '_soln' + ext
+        transform_file(fpath,fname_skel,fname_soln)
+        # Move files over to final dirs if present
+        if has_skel_dir:
+            mvforce(fname_skel,os.path.join(skel_dir,fname_skel))
+        if has_soln_dir:
+            mvforce(fname_soln,os.path.join(soln_dir,fname_soln))
+
+#-----------------------------------------------------------------------------
+# Tests
+
+def str_match(s1,s2):
+    """Check that two strings are equal ignoring trailing whitespace."""
+    #print '***S1\n',s1,'\n***S2\n',s2  # dbg
+    nose.tools.assert_equal(s1.rstrip(),s2.rstrip())
+    
+
+def test_simple():
+    src = """
+    first line
+    del line  #@
+    second line
+    """
+    srclines = src.splitlines(True)
+    
+    clean = """
+    first line
+    raise NotImplementedError('insert missing code here')
+    second line
+    """
+    
+    cleaned = src2skel(srclines)
+    yield str_match,cleaned,clean
+
+    clean = """
+    first line
+    del line
+    second line
+    """
+    cleaned = src2soln(src.splitlines(True))
+    yield str_match,cleaned,clean
+    
+
+def test_multi():
+    src = """
+    first line
+    #@ Hint: remember that
+    #@ idea we discussed before...
+    del line  #@
+    del line2  #@
+    del line3  #@
+    second line:
+      del line4  #@
+      del line5  #@
+    third line
+
+    some indented code: #@
+      with more... #@
+    """
+    srclines = src.splitlines(True)
+
+    clean = """
+    first line
+    # Hint: remember that
+    # idea we discussed before...
+    raise NotImplementedError('insert missing code here')
+    second line:
+      raise NotImplementedError('insert missing code here')
+    third line
+
+    raise NotImplementedError('insert missing code here')
+    """
+    cleaned = src2skel(srclines)
+    yield str_match,cleaned,clean
+
+    clean = """
+    first line
+    del line
+    del line2
+    del line3
+    second line:
+      del line4
+      del line5
+    third line
+
+    some indented code:
+      with more...
+    """
+    cleaned = src2soln(srclines)
+    yield str_match,cleaned,clean
+
+
[EMAIL PROTECTED]
+def test():
+    """Simple self-contained test runner."""
+    nose.runmodule(__name__,exit=False,
+                   argv=['--doctests',
+                         #'-s',
+                         #'--pdb-failures',
+                         ])
+
+#-----------------------------------------------------------------------------
+# Execution from the command line.
+
+if __name__ == "__main__":
+    if '--test' in sys.argv:
+        test()
+    else:
+        main(sys.argv)


Property changes on: trunk/py4science/examples/mkprob.py
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mergeinfo
   + 

Deleted: trunk/py4science/examples/mkskel.py
===================================================================
--- trunk/py4science/examples/mkskel.py 2008-10-19 09:04:27 UTC (rev 6275)
+++ trunk/py4science/examples/mkskel.py 2008-10-19 09:11:30 UTC (rev 6276)
@@ -1,325 +0,0 @@
-#!/usr/bin/env python
-"""Make skeletons out of Python scripts.
-
-Usage:
-
-   mkskel [--test]  file1.py file2.py ....
-
-If --test is given, the test suite is run instead.
-
-For each input filename f.py, a pair of output files is generated, f_soln.py
-and f_skel.py.
-
-Source markup is very simple.  The tests in the file show precisely how it
-works, but in summary:
-
-- Pure comment lines with the special marker (#@) are left in the skeleton
-  (only the marker is stripped, but they remain as valid comments).  These are
-  typically used for hints.
-
-- Code lines terminated with the marker are:
-
-  - In the skeleton, replaced by a NotImplementedError call.  Consecutive lines
-  are replaced by a single call.
-
-  - In the solution, kept as is but the marker is removed.
-"""
-
-from __future__ import with_statement
-
-#-----------------------------------------------------------------------------
-# Stdlib imports
-import os
-import re
-import shutil
-import sys
-
-# Third-party imports
-import nose
-
-# Constants
-MARKER = '#@'
-DEL_RE = re.compile(r'''^((\s*)(.*?))\s*%s\s*$''' % MARKER)
-HINT_RE = re.compile(r'''^(?P<space>\s*)%s\s+(?P<hint>.*)$''' % MARKER)
-                       
-
-#-----------------------------------------------------------------------------
-# Main code begins
-
-def src2soln(src):
-    """Remove markers from input source, leaving all else intact.
-
-    Inputs:
-      src : sequence of lines (file-like objects work out of the box)
-    """
-    
-    out = []
-    addline = out.append
-    for line in src:
-        # Check for lines to delete and with hints
-        mdel  = DEL_RE.match(line)
-        mhint = HINT_RE.match(line)
-
-        # All hints are unconditionally removed
-        if mhint is None:
-            if mdel:
-                # if marker is matched in code, strip it and leave the code
-                line = mdel.group(1)+'\n'
-            addline(line)
-            
-    return ''.join(out)
-
-
-def src2skel(src):
-    """Remove markers from input source, replacing marked lines.
-
-    Marked lines are replaced with "raise NotImplementedError" calls that
-    summarize the total number of deleted lines.
-
-    Inputs:
-      src : sequence of lines (file-like objects work out of the box)
-    """
-
-    def flush_buffers(normal_lines,del_lines=0):
-        """Local function to reuse some common code"""
-
-        if state_cur == normal:
-            # add the normal lines
-            out.extend(normal_lines)
-            normal_lines[:] = []
-        else:
-            # Add the summary of 'raise' lines
-
-            # flush counter of code (disabled, we report static summary)
-            #msg = '1 line' if del_lines==1 else ('%s lines' % del_lines)
-            #exc = exc_tpl % msg
-            exc = exc_tpl
-            
-            # Use the last value of 'space'
-            line = '%s%s' % (spaces[0],exc)
-            out.append(line)
-            del_lines = 0
-            spaces[:] = []
-            
-        return del_lines
-    
-    # used to report actual # of lines removed - disabled
-    #exc_tpl = "raise NotImplementedError('%s missing')\n"    
-    exc_tpl = "raise NotImplementedError('insert missing code here')\n"
-    
-    # states for state machine and other initialization
-    normal,delete = 0,1
-    state_cur = normal
-    del_lines = 0  # counter, in case we want to report # of deletions
-    spaces = []
-    normal_lines = []
-    out = []
-    
-    # To remove multiple consecutive lines of input marked for deletion, we
-    # need a small state machine.
-    for line in src:
-        # Check for lines to delete and with hints
-        mdel  = DEL_RE.match(line)
-        mhint = HINT_RE.match(line)
-
-        if mhint:
-            state_new = normal
-            hint = mhint.group('space')+'# ' + mhint.group('hint') +'\n'
-            normal_lines.append(hint)
-        else:
-            if mdel is None:
-                state_new = normal
-                normal_lines.append(line)
-            else:
-                state_new = delete
-                del_lines += 1
-                spaces.append(mdel.group(2))
-            
-        # Flush output only when there's a change of state
-        if state_new != state_cur:
-            del_lines = flush_buffers(normal_lines,del_lines)
-
-        # Update state machine
-        state_cur = state_new
-
-    # Final buffer flush is unconditional
-    flush_buffers(normal_lines)
-    
-    return ''.join(out)
-
-
-def transform_file(fpath,fname_skel,fname_soln):
-    """Run the cleanup routines for a given input, creating skel and soln.
-    """
-
-    # get the mode of the input so that we can create the output files with the
-    # same mode
-    fmode = os.stat(fpath).st_mode
-    
-    with open(fpath) as infile:
-        # Generate the skeleton
-        skel = src2skel(infile)
-        with open(fname_skel,'w') as fskel:
-            fskel.write(skel)
-        os.chmod(fname_skel,fmode)
-        
-        # Reset the input file pointer and generate the solution
-        infile.seek(0)
-        soln = src2soln(infile)
-        with open(fname_soln,'w') as fsoln:
-            fsoln.write(soln)
-        os.chmod(fname_soln,fmode)
-
-#-----------------------------------------------------------------------------
-# Main execution routines
-
-def copyforce(src,dest):
-    """Forceful file link/copy that overwrites destination files."""
-    try:
-        copy = os.link
-    except AttributeError:
-        copy = shutil.copy
-    if os.path.isfile(dest):
-        os.remove(dest)
-    copy(src,dest)
-
-
-def mvforce(src,dest):
-    """Forceful file copy that overwrites destination files."""
-    if os.path.isfile(dest):
-        os.remove(dest)
-    shutil.move(src,dest)
-
-
-def main(argv=None):
-    """Main entry point as a command line script for normal execution"""
-    
-    if argv is None:
-        argv = sys.argv
-
-    # If there are subdirs called skel and soln, we populate them by moving the
-    # generated files there, otherwise they're left in the current dir.
-    skel_dir = 'skel'
-    soln_dir = 'soln'
-    has_skel_dir = os.path.isdir(skel_dir)
-    has_soln_dir = os.path.isdir(soln_dir)
-
-    # First, check that all files are present and abort immediately if any of
-    # them isn't there.
-    for fpath in argv[1:]:
-        if not os.path.isfile(fpath):
-            raise OSError("file %r not found" % fpath)
-
-    # If all files are there, then go ahead and process them unconditionally
-    for fpath in argv[1:]:
-        basename, ext = os.path.splitext(fpath)
-        fname_skel = basename + '_skel' + ext
-        fname_soln = basename + '_soln' + ext
-        transform_file(fpath,fname_skel,fname_soln)
-        # Move files over to final dirs if present
-        if has_skel_dir:
-            mvforce(fname_skel,os.path.join(skel_dir,fname_skel))
-        if has_soln_dir:
-            mvforce(fname_soln,os.path.join(soln_dir,fname_soln))
-
-#-----------------------------------------------------------------------------
-# Tests
-
-def str_match(s1,s2):
-    """Check that two strings are equal ignoring trailing whitespace."""
-    #print '***S1\n',s1,'\n***S2\n',s2  # dbg
-    nose.tools.assert_equal(s1.rstrip(),s2.rstrip())
-    
-
-def test_simple():
-    src = """
-    first line
-    del line  #@
-    second line
-    """
-    srclines = src.splitlines(True)
-    
-    clean = """
-    first line
-    raise NotImplementedError('insert missing code here')
-    second line
-    """
-    
-    cleaned = src2skel(srclines)
-    yield str_match,cleaned,clean
-
-    clean = """
-    first line
-    del line
-    second line
-    """
-    cleaned = src2soln(src.splitlines(True))
-    yield str_match,cleaned,clean
-    
-
-def test_multi():
-    src = """
-    first line
-    #@ Hint: remember that
-    #@ idea we discussed before...
-    del line  #@
-    del line2  #@
-    del line3  #@
-    second line:
-      del line4  #@
-      del line5  #@
-    third line
-
-    some indented code: #@
-      with more... #@
-    """
-    srclines = src.splitlines(True)
-
-    clean = """
-    first line
-    # Hint: remember that
-    # idea we discussed before...
-    raise NotImplementedError('insert missing code here')
-    second line:
-      raise NotImplementedError('insert missing code here')
-    third line
-
-    raise NotImplementedError('insert missing code here')
-    """
-    cleaned = src2skel(srclines)
-    yield str_match,cleaned,clean
-
-    clean = """
-    first line
-    del line
-    del line2
-    del line3
-    second line:
-      del line4
-      del line5
-    third line
-
-    some indented code:
-      with more...
-    """
-    cleaned = src2soln(srclines)
-    yield str_match,cleaned,clean
-
-
[EMAIL PROTECTED]
-def test():
-    """Simple self-contained test runner."""
-    nose.runmodule(__name__,exit=False,
-                   argv=['--doctests',
-                         #'-s',
-                         #'--pdb-failures',
-                         ])
-
-#-----------------------------------------------------------------------------
-# Execution from the command line.
-
-if __name__ == "__main__":
-    if '--test' in sys.argv:
-        test()
-    else:
-        main(sys.argv)

Modified: trunk/py4science/workbook/bessel.tex
===================================================================
--- trunk/py4science/workbook/bessel.tex        2008-10-19 09:04:27 UTC (rev 
6275)
+++ trunk/py4science/workbook/bessel.tex        2008-10-19 09:11:30 UTC (rev 
6276)
@@ -1,6 +1,10 @@
 \section{Bessel functions}
 \label{sec:bessel}
 
+\textbf{Illustrates}: Special functions library, array manipulations
+to check recursion relation.
+
+
 In this exercise, you will verify a few simple relations involving the Bessel
 functions of the first kind.  The important relations to keep in mind are the
 asymptotic form of $J_n(x)$ for $x>>n$:

Modified: trunk/py4science/workbook/fft_imdenoise.tex
===================================================================
--- trunk/py4science/workbook/fft_imdenoise.tex 2008-10-19 09:04:27 UTC (rev 
6275)
+++ trunk/py4science/workbook/fft_imdenoise.tex 2008-10-19 09:11:30 UTC (rev 
6276)
@@ -1,6 +1,9 @@
 \section{FFT Image Denoising}
 \label{sec:fft_imdenoise}
 
+\textbf{Illustrates}: 2-d image denoising, use of the scipy FFT library,
+array manipulations, image plotting.
+
 Convolution of an input with with a linear filter in the termporal or spatial
 domain is equivalent to multiplication by the fourier transforms of the input
 and the filter in the spectral domain.  This provides a conceptually simple way

Modified: trunk/py4science/workbook/qsort.tex
===================================================================
--- trunk/py4science/workbook/qsort.tex 2008-10-19 09:04:27 UTC (rev 6275)
+++ trunk/py4science/workbook/qsort.tex 2008-10-19 09:11:30 UTC (rev 6276)
@@ -1,6 +1,7 @@
-
 \section{Sorting quickly with QuickSort }
 
+\textbf{Illustrates}: lists, recursion.
+
 Quicksort is one of the best known, and probably the simplest, fast
 algorithm for sorting $n$ items. It is fast in the sense that it
 requires on average $\mathcal{O}(n\log n)$ comparisons instead of

Modified: trunk/py4science/workbook/quad_newton.tex
===================================================================
--- trunk/py4science/workbook/quad_newton.tex   2008-10-19 09:04:27 UTC (rev 
6275)
+++ trunk/py4science/workbook/quad_newton.tex   2008-10-19 09:11:30 UTC (rev 
6276)
@@ -1,6 +1,9 @@
 \section{Newton's method}
 \label{sec:quad_newton}
 
+\textbf{Illustrates:} functions as first class objects, use of the
+scipy libraries.
+
 Consider the problem of solving for $t$ in
 \begin{equation}
   \int_{o}^{t}f(s)ds=u

Modified: trunk/py4science/workbook/trapezoid.tex
===================================================================
--- trunk/py4science/workbook/trapezoid.tex     2008-10-19 09:04:27 UTC (rev 
6275)
+++ trunk/py4science/workbook/trapezoid.tex     2008-10-19 09:11:30 UTC (rev 
6276)
@@ -1,6 +1,9 @@
 \section{Trapezoidal rule}
 \label{sec:trapezoid}
 
+\textbf{Illustrates}: basic array slicing, functions as first class
+objects.
+
 In this exercise, you are tasked with implementing the simple trapezoid
 rule formula for numerical integration. If we want to compute the
 definite integral \begin{equation}

Modified: trunk/py4science/workbook/update_problems.py
===================================================================
--- trunk/py4science/workbook/update_problems.py        2008-10-19 09:04:27 UTC 
(rev 6275)
+++ trunk/py4science/workbook/update_problems.py        2008-10-19 09:11:30 UTC 
(rev 6276)
@@ -10,10 +10,11 @@
 from IPython.genutils import target_outdated
 
 # Constants
-SRC_DIR = '../examples'
-UPDATE = './mkskel.py'
-PROBLEMS_DIR = 'problems'
 
+SRC_DIR = '../examples'  # source dir for the examples
+UPDATE = './mkprob.py'  # executable to make problems (in SRC_DIR)
+PROBLEMS_DIR = 'problems'  # directory for problem output (in this dir)
+
 if __name__ == '__main__':
     
     problems = [f for f in os.listdir(PROBLEMS_DIR) if f.endswith('.py')]

Modified: trunk/py4science/workbook/wallis_pi.tex
===================================================================
--- trunk/py4science/workbook/wallis_pi.tex     2008-10-19 09:04:27 UTC (rev 
6275)
+++ trunk/py4science/workbook/wallis_pi.tex     2008-10-19 09:11:30 UTC (rev 
6276)
@@ -1,6 +1,7 @@
-
 \section{Wallis' slow road to $\pi$}
 
+\textbf{Illustrates}: arbitrary size integers, simple function definitions.
+
 Wallis' formula is an infinite product that converges (slowly) to
 $\pi$:\begin{equation}
 \pi=\prod_{i=1}^{\infty}\frac{4i^{2}}{4i^{2}-1}.\end{equation}


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Matplotlib-checkins mailing list
Matplotlib-checkins@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to