[GRASS-SVN] r53780 - in grass/trunk: gui/wxpython/core lib/python

svn_grass at osgeo.org svn_grass at osgeo.org
Mon Nov 12 10:13:54 PST 2012


Author: zarch
Date: 2012-11-12 10:13:53 -0800 (Mon, 12 Nov 2012)
New Revision: 53780

Modified:
   grass/trunk/gui/wxpython/core/globalvar.py
   grass/trunk/lib/python/core.py
Log:
Move gui.wxpython.core.globalvar.GetGRASSCommands to lib.python.core.get_commands

Modified: grass/trunk/gui/wxpython/core/globalvar.py
===================================================================
--- grass/trunk/gui/wxpython/core/globalvar.py	2012-11-12 16:58:11 UTC (rev 53779)
+++ grass/trunk/gui/wxpython/core/globalvar.py	2012-11-12 18:13:53 UTC (rev 53780)
@@ -28,21 +28,22 @@
 from core.debug import Debug
 
 sys.path.append(os.path.join(ETCDIR, "python"))
-import grass.script as grass
+from grass.script.core import get_commands
 
+
 def CheckWxVersion(version = [2, 8, 11, 0]):
     """!Check wx version"""
     ver = wx.version().split(' ')[0]
     if map(int, ver.split('.')) < version:
         return False
-    
+
     return True
 
 def CheckForWx():
     """!Try to import wx module and check its version"""
     if 'wx' in sys.modules.keys():
         return
-    
+
     minVersion = [2, 8, 1, 1]
     try:
         try:
@@ -53,7 +54,7 @@
         wxversion.ensureMinimal(str(minVersion[0]) + '.' + str(minVersion[1]))
         import wx
         version = wx.version().split(' ')[0]
-        
+
         if map(int, version.split('.')) < minVersion:
             raise ValueError('Your wxPython version is %s.%s.%s.%s' % tuple(version.split('.')))
 
@@ -67,7 +68,7 @@
     except locale.Error, e:
         print >> sys.stderr, "Unable to set locale:", e
         os.environ['LC_ALL'] = ''
-    
+
 if not os.getenv("GRASS_WXBUNDLED"):
     CheckForWx()
 import wx
@@ -84,7 +85,7 @@
 FNPageStyle = FN.FNB_VC8 | \
     FN.FNB_BACKGROUND_GRADIENT | \
     FN.FNB_NODRAG | \
-    FN.FNB_TABS_BORDER_SIMPLE 
+    FN.FNB_TABS_BORDER_SIMPLE
 
 FNPageDStyle = FN.FNB_FANCY_TABS | \
     FN.FNB_BOTTOM | \
@@ -110,50 +111,7 @@
 else:
     BIN_EXT = SCT_EXT = ''
 
-def GetGRASSCommands():
-    """!Create list of available GRASS commands to use when parsing
-    string from the command line
 
-    @return list of commands (set) and directory of scripts (collected
-    by extension - MS Windows only)
-    """
-    gisbase = os.environ['GISBASE']
-    cmd = list()
-    if sys.platform == 'win32':
-        scripts = { SCT_EXT : list() }
-    else:
-        scripts = {}
-    
-    # scan bin/
-    if os.path.exists(os.path.join(gisbase, 'bin')):
-        for fname in os.listdir(os.path.join(gisbase, 'bin')):
-            if scripts: # win32
-                name, ext = os.path.splitext(fname)
-                if ext != '.manifest':
-                    cmd.append(name)
-                if ext in scripts.keys():
-                    scripts[ext].append(name)
-            else:
-                cmd.append(fname)
-    
-    # scan scripts/
-    if os.path.exists(os.path.join(gisbase, 'scripts')):
-        for fname in os.listdir(os.path.join(gisbase, 'scripts')):
-            if scripts: # win32
-                name, ext = os.path.splitext(fname)
-                if ext in scripts.keys():
-                    scripts[ext].append(name)
-                cmd.append(name)
-            else:
-                cmd.append(fname)
-    
-    # scan gui/scripts/
-    if os.path.exists(os.path.join(gisbase, 'etc', 'gui', 'scripts')):
-        os.environ["PATH"] = os.getenv("PATH") + os.pathsep + os.path.join(gisbase, 'etc', 'gui', 'scripts')
-        cmd = cmd + os.listdir(os.path.join(gisbase, 'etc', 'gui', 'scripts'))
-    
-    return set(cmd), scripts
-
 def UpdateGRASSAddOnCommands(eList = None):
     """!Update list of available GRASS AddOns commands to use when
     parsing string from the command line
@@ -161,14 +119,14 @@
     @param eList list of AddOns commands to remove
     """
     global grassCmd, grassScripts
-    
+
     # scan addons (path)
     addonPath = os.getenv('GRASS_ADDON_PATH', '')
     addonBase = os.getenv('GRASS_ADDON_BASE')
     if addonBase:
         addonPath += os.pathsep + os.path.join(addonBase, 'bin') + os.pathsep + \
             os.path.join(addonBase, 'scripts')
-        
+
     # remove commands first
     if eList:
         for ext in eList:
@@ -201,11 +159,11 @@
                     grassCmd.add(fname)
                     Debug.msg(3, "AddOn commands: %s", fname)
                     nCmd += 1
-                    
+
     Debug.msg(1, "Number of new AddOn commands: %d", nCmd)
 
 """@brief Collected GRASS-relared binaries/scripts"""
-grassCmd, grassScripts = GetGRASSCommands()
+grassCmd, grassScripts = get_commands()
 Debug.msg(1, "Number of GRASS commands: %d", len(grassCmd))
 UpdateGRASSAddOnCommands()
 

Modified: grass/trunk/lib/python/core.py
===================================================================
--- grass/trunk/lib/python/core.py	2012-11-12 16:58:11 UTC (rev 53779)
+++ grass/trunk/lib/python/core.py	2012-11-12 18:13:53 UTC (rev 53780)
@@ -54,17 +54,17 @@
                                   preexec_fn, close_fds, shell,
                                   cwd, env, universal_newlines,
                                   startupinfo, creationflags)
-        
+
 PIPE = subprocess.PIPE
 STDOUT = subprocess.STDOUT
 
 class ScriptError(Exception):
     def __init__(self, msg):
         self.value = msg
-        
+
     def __str__(self):
         return self.value
-        
+
 raise_on_error = False # raise exception instead of calling fatal()
 
 def call(*args, **kwargs):
@@ -80,7 +80,7 @@
     enc = locale.getdefaultlocale()[1]
     if enc:
         return string.decode(enc)
-    
+
     return string
 
 def _make_val(val):
@@ -93,6 +93,50 @@
 	return _make_val(list(val))
     return str(val)
 
+def get_commands():
+    """!Create list of available GRASS commands to use when parsing
+    string from the command line
+
+    @return list of commands (set) and directory of scripts (collected
+    by extension - MS Windows only)
+
+    @code
+    >>> cmds = list(get_commands()[0])
+    >>> cmds.sort()
+    >>> cmds[:5]
+    ['d.barscale', 'd.colorlist', 'd.colortable', 'd.erase', 'd.font']
+
+    @endcode
+    """
+    gisbase = os.environ['GISBASE']
+    cmd = list()
+    scripts = {'.py': list()} if sys.platform == 'win32' else {}
+
+    def scan(gisbase, directory):
+        dir_path = os.path.join(gisbase, directory)
+        if os.path.exists(dir_path):
+            for fname in os.listdir(os.path.join(gisbase, 'bin')):
+                if scripts:  # win32
+                    name, ext = os.path.splitext(fname)
+                    if ext != '.manifest':
+                        cmd.append(name)
+                    if ext in scripts.keys():
+                        scripts[ext].append(name)
+                else:
+                    cmd.append(fname)
+
+    for directory in ('bin', 'scripts'):
+        scan(gisbase, directory)
+
+    # scan gui/scripts/
+    gui_path = os.path.join(gisbase, 'etc', 'gui', 'scripts')
+    if os.path.exists(gui_path):
+        os.environ["PATH"] = os.getenv("PATH") + os.pathsep + gui_path
+        cmd = cmd + os.listdir(gui_path)
+
+    return set(cmd), scripts
+
+
 def make_command(prog, flags = "", overwrite = False, quiet = False, verbose = False, **options):
     """!Return a list of strings suitable for use as the args parameter to
     Popen() or call(). Example:
@@ -146,14 +190,14 @@
     GUI='text';
     MONITOR='x0';
     \endcode
-    
+
     @param prog GRASS module
     @param flags flags to be used (given as a string)
     @param overwrite True to enable overwriting the output (<tt>--o</tt>)
     @param quiet True to run quietly (<tt>--q</tt>)
     @param verbose True to run verbosely (<tt>--v</tt>)
     @param kwargs module's parameters
-    
+
     @return Popen object
     """
     options = {}
@@ -165,11 +209,11 @@
 	    options[opt] = val
 
     args = make_command(prog, flags, overwrite, quiet, verbose, **options)
-    
+
     if debug_level() > 0:
         sys.stderr.write("D1/%d: %s.start_command(): %s\n" % (debug_level(), __name__, ' '.join(args)))
         sys.stderr.flush()
-    
+
     return Popen(args, **popts)
 
 def run_command(*args, **kwargs):
@@ -201,7 +245,7 @@
     GUI='text';
     MONITOR='x0';
     \endcode
-    
+
     @param args list of unnamed arguments (see start_command() for details)
     @param kwargs list of named arguments (see start_command() for details)
 
@@ -216,7 +260,7 @@
 
     @param args list of unnamed arguments (see start_command() for details)
     @param kwargs list of named arguments (see start_command() for details)
-    
+
     @return Popen object
     """
     kwargs['stdin'] = PIPE
@@ -228,7 +272,7 @@
 
     @param args list of unnamed arguments (see start_command() for details)
     @param kwargs list of named arguments (see start_command() for details)
-    
+
     @return stdout
     """
     ps = pipe_command(*args, **kwargs)
@@ -253,7 +297,7 @@
 
     @param args list of unnamed arguments (see start_command() for details)
     @param kwargs list of named arguments (see start_command() for details)
-    
+
     @return parsed module output
     """
     parse = None
@@ -263,14 +307,14 @@
             parse = kwargs['parse'][0]
             parse_args = kwargs['parse'][1]
         del kwargs['parse']
-    
+
     if 'delimiter' in kwargs:
         parse_args = { 'sep' : kwargs['delimiter'] }
         del kwargs['delimiter']
-    
+
     if not parse:
         parse = parse_key_val # use default fn
-        
+
     res = read_command(*args, **kwargs)
 
     return parse(res, **parse_args)
@@ -324,10 +368,10 @@
     @param debug debug level (0-5)
     """
     run_command("g.message", flags = 'd', message = msg, debug = debug)
-    
+
 def verbose(msg):
     """!Display a verbose message using `g.message -v`
-    
+
     @param msg verbose message to be displayed
     """
     message(msg, flag = 'v')
@@ -341,7 +385,7 @@
 
 def percent(i, n, s):
     """!Display a progress info message using `g.message -p`
-    
+
     @code
     message(_("Percent complete..."))
     n = 100
@@ -349,7 +393,7 @@
         percent(i, n, 1)
     percent(1, 1, 1)
     @endcode
-    
+
     @param i current item
     @param n total number of items
     @param s increment size
@@ -365,31 +409,31 @@
 
 def error(msg):
     """!Display an error message using `g.message -e`
-    
+
     @param msg error message to be displayed
     """
     message(msg, flag = 'e')
 
 def fatal(msg):
     """!Display an error message using `g.message -e`, then abort
-    
+
     Raise exception when raise_on_error is 'True'.
-    
+
     @param msg error message to be displayed
     """
     global raise_on_error
     if raise_on_error:
         raise ScriptError(msg)
-    
+
     error(msg)
     sys.exit(1)
-    
+
 def set_raise_on_error(raise_exp = True):
     """!Define behaviour on fatal error (fatal() called)
 
     @param raise_exp True to raise ScriptError instead of calling
     sys.exit(1) in fatal()
-    
+
     @return current status
     """
     global raise_on_error
@@ -439,11 +483,11 @@
     if not os.getenv("GISBASE"):
         print >> sys.stderr, "You must be in GRASS GIS to run this program."
         sys.exit(1)
-    
+
     cmdline = [basename(sys.argv[0])]
     cmdline += ['"' + arg + '"' for arg in sys.argv[1:]]
     os.environ['CMDLINE'] = ' '.join(cmdline)
-    
+
     argv = sys.argv[:]
     name = argv[0]
     if not os.path.isabs(name):
@@ -451,15 +495,15 @@
 	    argv[0] = os.path.abspath(name)
 	else:
 	    argv[0] = os.path.join(sys.path[0], name)
-    
+
     p = Popen(['g.parser', '-s'] + argv, stdout = PIPE)
     s = p.communicate()[0]
     lines = s.splitlines()
-    
+
     if not lines or lines[0].rstrip('\r\n') != "@ARGS_PARSED@":
         sys.stdout.write(s)
         sys.exit(p.returncode)
-    
+
     return _parse_opts(lines[1:])
 
 # interface to g.tempfile
@@ -467,22 +511,22 @@
 def tempfile(create = True):
     """!Returns the name of a temporary file, created with
     g.tempfile.
-    
+
     @param create True to create a file
-    
+
     @return path to a tmp file
     """
     flags = ''
     if not create:
         flags += 'd'
-    
+
     return read_command("g.tempfile", flags = flags, pid = os.getpid()).strip()
 
 def tempdir():
     """!Returns the name of a temporary dir, created with g.tempfile."""
     tmp = tempfile(create = False)
     os.mkdir(tmp)
-    
+
     return tmp
 
 class KeyValue(dict):
@@ -524,7 +568,7 @@
 
     if not s:
         return result
-    
+
     if vsep:
         lines = s.split(vsep)
         try:
@@ -533,7 +577,7 @@
             pass
     else:
         lines = s.splitlines()
-    
+
     for line in lines:
 	kv = line.split(sep, 1)
 	k = kv[0].strip()
@@ -541,26 +585,26 @@
 	    v = kv[1].strip()
 	else:
 	    v = dflt
-        
+
         if val_type:
             result[k] = val_type(v)
         else:
             result[k] = v
-    
+
     return result
 
 def _text_to_key_value_dict(filename, sep=":", val_sep=","):
     """
         !Convert a key-value text file, where entries are separated
-        by newlines and the key and value are separated by `sep', 
-        into a key-value dictionary and discover/use the correct 
+        by newlines and the key and value are separated by `sep',
+        into a key-value dictionary and discover/use the correct
         data types (float, int or string) for values.
-        
+
         @param filename The name or name and path of the text file to convert
         @param sep The character that separates the keys and values, default is ":"
         @param val_sep The character that separates the values of a single key, default is ","
-        @return The dictionary 
-        
+        @return The dictionary
+
         A text file with this content:
         \code
         a: Hello
@@ -568,16 +612,16 @@
         c: 1,2,3,4,5
         d : hello,8,0.1
         \endcode
-        
+
         Will be represented as this dictionary:
         \code
         {'a': ['Hello'], 'c': [1, 2, 3, 4, 5], 'b': [1.0], 'd': ['hello', 8, 0.1]}
         \endcode
-        
+
     """
     text = open(filename, "r").readlines()
     kvdict = KeyValue()
-    
+
     for line in text:
         if line.find(sep) >= 0:
             key, value = line.split(sep)
@@ -588,11 +632,11 @@
             continue
         values = value.split(val_sep)
         value_list = []
-        
+
         for value in values:
             not_float = False
             not_int = False
-            
+
             # Convert values into correct types
             # We first try integer then float
             try:
@@ -604,33 +648,33 @@
                     value_converted = float(value)
                 except:
                     not_float = True
-                
+
             if not_int and not_float:
                 value_converted = value.strip()
-            
+
             value_list.append(value_converted)
-            
+
         kvdict[key] = value_list
     return kvdict
 
-def compare_key_value_text_files(filename_a, filename_b, sep=":", 
+def compare_key_value_text_files(filename_a, filename_b, sep=":",
                                  val_sep=",", precision=0.000001):
     """
         !Compare two key-value text files that may contain projection parameter
-        
-        @param filename_a The name of the first key-value text file 
+
+        @param filename_a The name of the first key-value text file
         @param filenmae_b The name of the second key-value text file
         @param sep The character that separates the keys and values, default is ":"
         @param val_sep The character that separates the values of a single key, default is ","
         @param precision The precision with which the floating point values are compares
            if abs(a - b) > precision : return False
         @return True if full or almost identical, False if different
-           
+
         This method will print a warning in case keys that are present in the first file
         are not present in the second one.
         The comparison method tries to convert the values into there native format (float, int or string)
         to allow correct comparison.
-        
+
         An example key-value text file may have this content:
         \code
         a: Hello
@@ -641,9 +685,9 @@
     """
     dict_a = _text_to_key_value_dict(filename_a, sep)
     dict_b = _text_to_key_value_dict(filename_b, sep)
-    
+
     missing_keys = 0
-    
+
     # We compare matching keys
     for key in dict_a.keys():
         if dict_b.has_key(key):
@@ -664,7 +708,7 @@
         warning(_("Several keys (%i out of %i) are missing "
                   "in the target file")%(missing_keys, len(dict_a)))
     return True
-    
+
 # interface to g.gisenv
 
 def gisenv():
@@ -702,7 +746,7 @@
     dictionary. Example:
 
     \param region3d True to get 3D region
-    
+
     \code
     >>> region = grass.region()
     >>> [region[key] for key in "nsew"]
@@ -718,7 +762,7 @@
         flgs += '3'
     if complete:
         flgs += 'cep'
-    
+
     s = read_command("g.region", flags = flgs)
     reg = parse_key_val(s, val_type = float)
     for k in ['rows',  'cols',  'cells',
@@ -726,7 +770,7 @@
         if k not in reg:
             continue
 	reg[k] = int(reg[k])
-    
+
     return reg
 
 def region_env(region3d = False,
@@ -765,22 +809,22 @@
                 key in ('top', 'bottom', 'cols3', 'rows3',
                         'depths', 'e-w resol3', 'n-s resol3', 't-b resol'):
             continue
-        
+
         grass_region += '%s: %s;' % (key, value)
-    
+
     if not kwargs: # return current region
         return grass_region
-    
+
     # read other values from `g.region -g`
     flgs = 'ug'
     if region3d:
         flgs += '3'
-        
+
     s = read_command('g.region', flags = flgs, **kwargs)
     if not s:
         return ''
     reg = parse_key_val(s)
-    
+
     kwdata = [('north',     'n'),
               ('south',     's'),
               ('east',      'e'),
@@ -798,12 +842,12 @@
                    ('e-w resol3', 'ewres3'),
                    ('n-s resol3', 'nsres3'),
                    ('t-b resol',  'tbres')]
-    
+
     for wkey, rkey in kwdata:
         grass_region += '%s: %s;' % (wkey, reg[rkey])
-    
+
     return grass_region
-    
+
 def use_temp_region():
     """!Copies the current region to a temporary region with "g.region save=",
     then sets WIND_OVERRIDE to refer to that region. Installs an atexit
@@ -835,7 +879,7 @@
     >>> print result['file']
     /opt/grass-data/spearfish60/PERMANENT/vector/fields
     \endcode
-    
+
     @param name file name
     @param element element type (default 'cell')
     @param mapset mapset name (default all mapsets in search path)
@@ -861,7 +905,7 @@
     >>> grass.list_grouped('rast')['PERMANENT']
     ['aspect', 'erosion1', 'quads', 'soils', 'strm.dist', ...
     @endcode
-    
+
     @param type element type (rast, vect, rast3d, region, ...)
     @param check_search_path True to add mapsets for the search path with no found elements
 
@@ -876,7 +920,7 @@
     if check_search_path:
         for mapset in mapsets(search_path = True):
             result[mapset] = []
-    
+
     mapset = None
     for line in read_command("g.list", type = type).splitlines():
 	if line == "":
@@ -891,7 +935,7 @@
 	    continue
         if mapset:
             result[mapset].extend(line.split())
-    
+
     return result
 
 def _concat(xs):
@@ -910,9 +954,9 @@
     >>> grass.list_pairs('rast')
     [('aspect', 'PERMANENT'), ('erosion1', 'PERMANENT'), ('quads', 'PERMANENT'), ...
     @endcode
-    
+
     @param type element type (rast, vect, rast3d, region, ...)
-    
+
     @return list of tuples (map, mapset)
     """
     return _concat([[(map, mapset) for map in maps]
@@ -930,7 +974,7 @@
     @endcode
 
     @param type element type
-    
+
     @return list of strings ('map@@mapset')
     """
     return ["%s@%s" % pair for pair in list_pairs(type)]
@@ -990,7 +1034,7 @@
     >>> grass.mlist_grouped('rast', pattern='r*')['PERMANENT']
     ['railroads', 'roads', 'rstrct.areas', 'rushmore']
     @endcode
-    
+
     @param type element type (rast, vect, rast3d, region, ...)
     @param pattern pattern string
     @param check_search_path True to add mapsets for the search path with no found elements
@@ -1005,7 +1049,7 @@
     if check_search_path:
         for mapset in mapsets(search_path = True):
             result[mapset] = []
-    
+
     mapset = None
     for line in read_command("g.mlist", quiet = True, flags = "m" + flag,
                              type = type, pattern = pattern).splitlines():
@@ -1014,12 +1058,12 @@
         except ValueError:
             warning(_("Invalid element '%s'") % line)
             continue
-        
+
         if mapset in result:
-            result[mapset].append(name) 
-        else:  	  	 
-            result[mapset] = [name, ] 
-    
+            result[mapset].append(name)
+        else:
+            result[mapset] = [name, ]
+
     return result
 
 # color parsing
@@ -1108,7 +1152,7 @@
 # find a program (replacement for "which")
 
 def find_program(pgm, args = []):
-    """!Attempt to run a program, with optional arguments. 
+    """!Attempt to run a program, with optional arguments.
 
     @param pgm program name
     @param args list of arguments
@@ -1126,7 +1170,7 @@
     except:
 	found = False
     nuldev.close()
-    
+
     return found
 
 # try to remove a file, without complaints
@@ -1170,7 +1214,7 @@
     """!List available mapsets
 
     @param search_path True to list mapsets only in search path
-    
+
     @return list of mapsets
     """
     if search_path:
@@ -1183,7 +1227,7 @@
                            quiet = True)
     if not mapsets:
         fatal(_("Unable to list mapsets"))
-    
+
     return mapsets.splitlines()
 
 # interface to `g.proj -c`
@@ -1194,7 +1238,7 @@
     """!Create new location
 
     Raise ScriptError on error.
-    
+
     @param dbase path to GRASS database
     @param location location name to create
     @param epsg if given create new location based on EPSG code
@@ -1212,13 +1256,13 @@
                     set = 'GISDBASE=%s' % dbase)
     if not os.path.exists(dbase):
             os.mkdir(dbase)
-    
+
     kwargs = dict()
     if datum:
         kwargs['datum'] = datum
     if datum_trans:
         kwargs['datum_trans'] = datum_trans
-    
+
     if epsg:
         ps = pipe_command('g.proj',
                           quiet = True,
@@ -1247,12 +1291,12 @@
                           stderr = PIPE)
     else:
         _create_location_xy(dbase, location)
-    
+
     if epsg or proj4 or filename or wkt:
         error = ps.communicate()[1]
         run_command('g.gisenv',
                     set = 'GISDBASE=%s' % gisdbase)
-        
+
         if ps.returncode != 0 and error:
             raise ScriptError(repr(error))
 
@@ -1267,12 +1311,12 @@
         fd.close()
     except OSError, e:
         raise ScriptError(repr(e))
-        
+
 def _create_location_xy(database, location):
     """!Create unprojected location
 
     Raise ScriptError on error.
-    
+
     @param database GRASS database where to create new location
     @param location location name
     """
@@ -1281,7 +1325,7 @@
         os.chdir(database)
         os.mkdir(location)
         os.mkdir(os.path.join(location, 'PERMANENT'))
-        
+
         # create DEFAULT_WIND and WIND files
         regioninfo = ['proj:       0',
                       'zone:       0',
@@ -1301,16 +1345,16 @@
                       'e-w resol3: 1',
                       'n-s resol3: 1',
                       't-b resol:  1']
-        
+
         defwind = open(os.path.join(location,
                                     "PERMANENT", "DEFAULT_WIND"), 'w')
         for param in regioninfo:
             defwind.write(param + '%s' % os.linesep)
         defwind.close()
-            
+
         shutil.copy(os.path.join(location, "PERMANENT", "DEFAULT_WIND"),
                     os.path.join(location, "PERMANENT", "WIND"))
-        
+
         os.chdir(cur_dir)
     except OSError, e:
         raise ScriptError(repr(e))
@@ -1332,7 +1376,7 @@
                          flags = 'rge')
     for k, v in data.iteritems():
         data[k.strip()] = v.replace('"', '').strip()
-    
+
     return data
 
 # get debug_level



More information about the grass-commit mailing list