[GRASS-SVN] r46843 - in grass/trunk: gui/wxpython/gui_modules gui/wxpython/support lib/python

svn_grass at osgeo.org svn_grass at osgeo.org
Wed Jun 29 08:53:42 EDT 2011


Author: martinl
Date: 2011-06-29 05:53:42 -0700 (Wed, 29 Jun 2011)
New Revision: 46843

Added:
   grass/trunk/lib/python/task.py
Modified:
   grass/trunk/gui/wxpython/gui_modules/gdialogs.py
   grass/trunk/gui/wxpython/gui_modules/ghelp.py
   grass/trunk/gui/wxpython/gui_modules/gmodeler.py
   grass/trunk/gui/wxpython/gui_modules/goutput.py
   grass/trunk/gui/wxpython/gui_modules/menuform.py
   grass/trunk/gui/wxpython/gui_modules/prompt.py
   grass/trunk/gui/wxpython/support/update_menudata.py
   grass/trunk/lib/python/Makefile
   grass/trunk/lib/python/core.py
Log:
pythonlib: rewrite command_info() using grassTask and processTask (moved from wxGUI)


Modified: grass/trunk/gui/wxpython/gui_modules/gdialogs.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/gdialogs.py	2011-06-29 10:52:43 UTC (rev 46842)
+++ grass/trunk/gui/wxpython/gui_modules/gdialogs.py	2011-06-29 12:53:42 UTC (rev 46843)
@@ -39,6 +39,7 @@
 import wx.lib.mixins.listctrl as listmix
 
 from grass.script import core as grass
+from grass.script import task as gtask
 
 import gcmd
 import globalvar
@@ -960,7 +961,7 @@
                                       label="%s" % _("Options"))
         
         cmd = self._getCommand()
-        task = menuform.GUI().ParseInterface(cmd = [cmd])
+        task = gtask.parse_interface(cmd)
         for f in task.get_options()['flags']:
             name = f.get('name', '')
             desc = f.get('label', '')

Modified: grass/trunk/gui/wxpython/gui_modules/ghelp.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/ghelp.py	2011-06-29 10:52:43 UTC (rev 46842)
+++ grass/trunk/gui/wxpython/gui_modules/ghelp.py	2011-06-29 12:53:42 UTC (rev 46843)
@@ -34,6 +34,8 @@
 import wx.lib.flatnotebook as FN
 import  wx.lib.scrolledpanel as scrolled
 
+from grass.script import task as gtask
+
 import menudata
 import gcmd
 import globalvar
@@ -812,7 +814,7 @@
         
         self.optionBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
                                       label = " %s " % _("Options"))
-        task = menuform.GUI().ParseInterface(cmd = ['g.extension'])
+        task = gtask.parse_interface('g.extension')
         for f in task.get_options()['flags']:
             name = f.get('name', '')
             desc = f.get('label', '')

Modified: grass/trunk/gui/wxpython/gui_modules/gmodeler.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/gmodeler.py	2011-06-29 10:52:43 UTC (rev 46842)
+++ grass/trunk/gui/wxpython/gui_modules/gmodeler.py	2011-06-29 12:53:42 UTC (rev 46843)
@@ -77,6 +77,7 @@
 from ghelp        import SearchModuleWindow
 
 from grass.script import core as grass
+from grass.script import task as gtask
 
 class Model(object):
     """!Class representing the model"""
@@ -3458,9 +3459,9 @@
     def _createPage(self, name, params):
         """!Define notebook page"""
         if name in globalvar.grassCmd['all']:
-            task = menuform.grassTask(name)
+            task = gtask.grassTask(name)
         else:
-            task = menuform.grassTask()
+            task = gtask.grassTask()
         task.flags  = params['flags']
         task.params = params['params']
         

Modified: grass/trunk/gui/wxpython/gui_modules/goutput.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/goutput.py	2011-06-29 10:52:43 UTC (rev 46842)
+++ grass/trunk/gui/wxpython/gui_modules/goutput.py	2011-06-29 12:53:42 UTC (rev 46843)
@@ -32,6 +32,7 @@
 from wx.lib.newevent import NewEvent
 
 import grass.script as grass
+from   grass.script import task as gtask
 
 import globalvar
 import gcmd
@@ -526,7 +527,7 @@
                 
                 if len(command) == 1:
                     import menuform
-                    task = menuform.GUI().ParseInterface(command)
+                    task = gtask.parse_interface(command[0])
                     # if not task.has_required():
                     # task = None # run command
                 else:
@@ -552,7 +553,7 @@
             if len(command) == 1:
                 import menuform
                 try:
-                    task = menuform.GUI().ParseInterface(command)
+                    task = gtask.parse_interface(command[0])
                 except:
                     task = None
             else:

Modified: grass/trunk/gui/wxpython/gui_modules/menuform.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/menuform.py	2011-06-29 10:52:43 UTC (rev 46842)
+++ grass/trunk/gui/wxpython/gui_modules/menuform.py	2011-06-29 12:53:42 UTC (rev 46843)
@@ -4,8 +4,6 @@
 description.
 
 Classes:
- - grassTask
- - processTask
  - helpPanel
  - mainFrame
  - cmdPanel
@@ -95,6 +93,7 @@
 sys.path.append(wxbase)
 
 from grass.script import core as grass
+from grass.script import task as gtask
 
 import gselect
 import gcmd
@@ -131,8 +130,11 @@
     rgb2str[ r ] = s
 
 """!Hide some options in the GUI"""
-_blackList = { }
-_ignoreBlackList = True
+#_blackList = { 'enabled' : False,
+#               'items'   : { 'r.buffer' : {'params' : ['input', 'output'],
+#                                           'flags' : ['z', 'overwrite']}}}
+_blackList = { 'enabled' : False,
+               'items'   : {} }
 
 def color_resolve(color):
     if len(color) > 0 and color[0] in "0123456789":
@@ -372,337 +374,7 @@
             if self.request:
                 event = wxUpdateDialog(data = self.request.data)
                 wx.PostEvent(self.parent, event)
-        
-class grassTask:
-    """!This class holds the structures needed for both filling by the
-    parser and use by the interface constructor.
 
-    Use as either grassTask() for empty definition or
-    grassTask('grass.command') for parsed filling.
-    """
-    def __init__(self, grassModule = None):
-        self.path = grassModule
-        self.name = _('unknown')
-        self.params = list()
-        self.description = ''
-        self.label = ''
-        self.flags = list()
-        self.keywords = list()
-        self.errorMsg = ''
-        self.firstParam = None
-        
-        if grassModule is not None:
-            try:
-                processTask(tree = etree.fromstring(getInterfaceDescription(grassModule)),
-                            task = self)
-            except gcmd.GException, e:
-                self.errorMsg = e.value
-                
-            self.define_first()
-        
-    def define_first(self):
-        """!Define first parameter"""
-        if len(self.params) > 0:
-            self.firstParam = self.params[0]['name']
-        
-    def get_error_msg(self):
-        """!Get error message ('' for no error)"""
-        return self.errorMsg
-    
-    def get_name(self):
-        """!Get task name"""
-        return self.name
-    
-    def get_list_params(self, element = 'name'):
-        """!Get list of parameters"""
-        params = []
-        for p in self.params:
-            params.append(p['name'])
-        
-        return params
-
-    def get_list_flags(self, element = 'name'):
-        """!Get list of parameters"""
-        flags = []
-        for p in self.flags:
-            flags.append(p['name'])
-        
-        return flags
-    
-    def get_param(self, value, element = 'name', raiseError = True):
-        """!Find and return a param by name."""
-        try:
-            for p in self.params:
-                val = p[element]
-                if val is None:
-                    continue
-                if type(val) in (types.ListType, types.TupleType):
-                    if value in val:
-                        return p
-                elif type(val) ==  types.StringType:
-                    if p[element][:len(value)] ==  value:
-                        return p
-                else:
-                    if p[element] ==  value:
-                        return p
-        except KeyError:
-            pass
-        
-        if raiseError:
-            raise ValueError, _("Parameter element '%(element)s' not found: '%(value)s'") % \
-                { 'element' : element, 'value' : value }
-        else:
-            return None
-
-    def set_param(self, aParam, aValue, element = 'value'):
-        """!Set param value/values.
-        """
-        try:
-            param = self.get_param(aParam)
-        except ValueError:
-            return
-        
-        param[element] = aValue
-            
-    def get_flag(self, aFlag):
-        """!Find and return a flag by name.
-        """
-        for f in self.flags:
-            if f['name'] ==  aFlag:
-                return f
-        raise ValueError, _("Flag not found: %s") % aFlag
-
-    def set_flag(self, aFlag, aValue, element = 'value'):
-        """!Enable / disable flag.
-        """
-        try:
-            param = self.get_flag(aFlag)
-        except ValueError:
-            return
-        
-        param[element] = aValue
-        
-    def getCmdError(self):
-        """!Get error string produced by getCmd(ignoreErrors = False)
-        
-        @return list of errors
-        """
-        errorList = list()
-        # determine if suppress_required flag is given
-        for f in self.flags:
-            if f['value'] and f['suppress_required']:
-                return errorList
-        
-        for p in self.params:
-            if not p.get('value', '') and p.get('required', False):
-                if not p.get('default', ''):
-                    desc = p.get('label', '')
-                    if not desc:
-                        desc = p['description']
-                    errorList.append(_("Parameter '%(name)s' (%(desc)s) is missing.") % \
-                                         {'name' : p['name'], 'desc' : desc })
-        
-        return errorList
-    
-    def getCmd(self, ignoreErrors = False):
-        """!Produce an array of command name and arguments for feeding
-        into some execve-like command processor.
-
-        If ignoreErrors =  = True then it will return whatever has been
-        built so far, even though it would not be a correct command
-        for GRASS.
-        """
-        cmd = [self.name]
-        
-        suppress_required = False
-        for flag in self.flags:
-            if flag['value']:
-                if len(flag['name']) > 1: # e.g. overwrite
-                    cmd +=  [ '--' + flag['name'] ]
-                else:
-                    cmd +=  [ '-' + flag['name'] ]
-                if flag['suppress_required']:
-                    suppress_required = True
-        for p in self.params:
-            if p.get('value','') ==  '' and p.get('required', False):
-                if p.get('default', '') !=  '':
-                    cmd +=  [ '%s=%s' % (p['name'], p['default']) ]
-                elif ignoreErrors is True and not suppress_required:
-                    cmd +=  [ '%s=%s' % (p['name'], _('<required>')) ]
-            elif p.get('value','') !=  '' and p['value'] !=  p.get('default','') :
-                # Output only values that have been set, and different from defaults
-                cmd +=  [ '%s=%s' % (p['name'], p['value']) ]
-        
-        errList = self.getCmdError()
-        if ignoreErrors is False and errList:
-            raise ValueError, '\n'.join(errList)
-        
-        return cmd
-
-    def set_options(self, opts):
-        """!Set flags and parameters
-
-        @param opts list of flags and parameters"""
-        for opt in opts:
-            if opt[0] ==  '-': # flag
-                self.set_flag(opt.lstrip('-'), True)
-            else: # parameter
-                key, value = opt.split('=', 1)
-                self.set_param(key, value)
-        
-    def get_options(self):
-        """!Get options"""
-        return { 'flags'  : self.flags,
-                 'params' : self.params }
-    
-    def has_required(self):
-        """!Check if command has at least one required paramater"""
-        for p in self.params:
-            if p.get('required', False):
-                return True
-        
-        return False
-
-class processTask:
-    """!A ElementTree handler for the --interface-description output,
-    as defined in grass-interface.dtd. Extend or modify this and the
-    DTD if the XML output of GRASS' parser is extended or modified.
-
-    @param tree root tree node
-    @param task grassTask instance or None
-    @return grassTask instance
-    """
-    def __init__(self, tree, task = None):
-        if task:
-            self.task = task
-        else:
-            self.task = grassTask()
-        
-        self.root = tree
-        
-        self.__processModule()
-        self.__processParams()
-        self.__processFlags()
-        self.task.define_first()
-        
-    def __processModule(self):
-        """!Process module description"""
-        self.task.name = self.root.get('name', default = 'unknown')
-        
-        # keywords
-        for keyword in self._getNodeText(self.root, 'keywords').split(','):
-            self.task.keywords.append(keyword.strip())
-        
-        self.task.label       = self._getNodeText(self.root, 'label')
-        self.task.description = self._getNodeText(self.root, 'description')
-        
-    def __processParams(self):
-        """!Process parameters"""
-        for p in self.root.findall('parameter'):
-            # gisprompt
-            node_gisprompt = p.find('gisprompt')
-            gisprompt = False
-            age = element = prompt = None
-            if node_gisprompt is not None:
-                gisprompt = True
-                age     = node_gisprompt.get('age', '')
-                element = node_gisprompt.get('element', '')
-                prompt  = node_gisprompt.get('prompt', '')
-            
-            # value(s)
-            values = []
-            values_desc = []
-            node_values = p.find('values')
-            if node_values is not None:
-                for pv in node_values.findall('value'):
-                    values.append(self._getNodeText(pv, 'name'))
-                    desc = self._getNodeText(pv, 'description')
-                    if desc:
-                        values_desc.append(desc)
-            
-            # keydesc
-            key_desc = []
-            node_key_desc = p.find('keydesc')
-            if node_key_desc is not None:
-                for ki in node_key_desc.findall('item'):
-                    key_desc.append(ki.text)
-            
-            if p.get('multiple', 'no') ==  'yes':
-                multiple = True
-            else:
-                multiple = False
-            if p.get('required', 'no') ==  'yes':
-                required = True
-            else:
-                required = False
-
-            if not _ignoreBlackList and \
-                    self.task.name in _blackList and \
-                    p.get('name') in _blackList[self.task.name]['params']:
-                hidden = True
-            else:
-                hidden = False
-            
-            self.task.params.append( {
-                "name"           : p.get('name'),
-                "type"           : p.get('type'),
-                "required"       : required,
-                "multiple"       : multiple,
-                "label"          : self._getNodeText(p, 'label'),
-                "description"    : self._getNodeText(p, 'description'),
-                'gisprompt'      : gisprompt,
-                'age'            : age,
-                'element'        : element,
-                'prompt'         : prompt,
-                "guisection"     : self._getNodeText(p, 'guisection'),
-                "guidependency"  : self._getNodeText(p, 'guidependency'),
-                "default"        : self._getNodeText(p, 'default'),
-                "values"         : values,
-                "values_desc"    : values_desc,
-                "value"          : '',
-                "key_desc"       : key_desc,
-                "hidden"         : hidden
-                })
-            
-    def __processFlags(self):
-        """!Process flags"""
-        global _ignoreBlackList
-        for p in self.root.findall('flag'):
-            if not _ignoreBlackList and \
-                    self.task.name in _blackList and \
-                    p.get('name') in _blackList[self.task.name]['flags']:
-                hidden = True
-            else:
-                hidden = False
-            
-            if p.find('suppress_required') is not None:
-                suppress_required = True
-            else:
-                suppress_required = False
-            
-            self.task.flags.append( {
-                    "name"              : p.get('name'),
-                    "label"             : self._getNodeText(p, 'label'),
-                    "description"       : self._getNodeText(p, 'description'),
-                    "guisection"        : self._getNodeText(p, 'guisection'),
-                    "suppress_required" : suppress_required,
-                    "value"             : False,
-                    "hidden"            : hidden
-                    } )
-        
-    def _getNodeText(self, node, tag, default = ''):
-        """!Get node text"""
-        p = node.find(tag)
-        if p is not None:
-            return utils.normalize_whitespace(p.text)
-        
-        return default
-    
-    def GetTask(self):
-        """!Get grassTask instance"""
-        return self.task
-    
-    
 class mainFrame(wx.Frame):
     """!This is the Frame containing the dialog for options input.
 
@@ -2135,26 +1807,6 @@
             
         event.Skip()
         
-def getInterfaceDescription(cmd):
-    """!Returns the XML description for the GRASS cmd.
-
-    The DTD must be located in $GISBASE/etc/grass-interface.dtd,
-    otherwise the parser will not succeed.
-
-    @param cmd command (name of GRASS module)
-    """
-    try:
-        cmdout, cmderr = grass.Popen([cmd, '--interface-description'], stdout = grass.PIPE,
-                                     stderr = grass.PIPE).communicate()
-    except OSError, e:
-        raise gcmd.GException, _("Unable to fetch interface description for command '%s'.") % cmd + \
-            _("Details:") +  "%s" % e
-    if cmderr and cmderr[:7] != 'WARNING':
-        raise gcmd.GException, _("Unable to fetch interface description for command '%s'.") % cmd + \
-            _("Details:") + " %s" % cmderr
-    
-    return cmdout.replace('grass-interface.dtd', os.path.join(globalvar.ETCDIR, 'grass-interface.dtd'))
-    
 class GrassGUIApp(wx.App):
     """!Stand-alone GRASS command GUI
     """
@@ -2190,29 +1842,16 @@
         self.grass_task = None
         self.cmd = list()
         
-        global _ignoreBlackList
+        global _blackList
         if self.parent:
-                _ignoreBlackList = False
+            _blackList['enabled'] = True
         else:
-                _ignoreBlackList = True
-
+            _blackList['enabled'] = False
+        
     def GetCmd(self):
         """!Get validated command"""
         return self.cmd
     
-    def ParseInterface(self, cmd, parser = processTask):
-        """!Parse interface
-
-        @param cmd command to be parsed (given as list)
-        """
-        # enc = locale.getdefaultlocale()[1]
-        # if enc and enc.lower() not in ("utf8", "utf-8"):
-        #     tree = etree.fromstring(getInterfaceDescription(cmd[0]).decode(enc).encode("utf-8"))
-        # else:
-        tree = etree.fromstring(getInterfaceDescription(cmd[0]))
-        
-        return processTask(tree).GetTask()
-    
     def ParseCommand(self, cmd, gmpath = None, completed = None):
         """!Parse command
         
@@ -2235,7 +1874,13 @@
                 dcmd_params.update(completed[2])
         
         # parse the interface decription
-        self.grass_task = self.ParseInterface(cmd)
+        try:
+            global _blackList
+            self.grass_task = gtask.parse_interface(cmd[0],
+                                                    blackList = _blackList)
+        except ValueError, e: #grass.ScriptError, e:
+            gcmd.GError(e.value)
+            return
         
         # if layer parameters previously set, re-insert them into dialog
         if completed is not None:
@@ -2283,7 +1928,7 @@
                             curr_mapset = grass.gisenv()['MAPSET']
                             if mapset and mapset !=  curr_mapset:
                                 value = value + '@' + mapset
-                        
+                    
                     self.grass_task.set_param(key, value)
                     cmd_validated.append(key + '=' + value)
                     i += 1
@@ -2332,8 +1977,8 @@
         """
         # parse the interface decription
         if not self.grass_task:
-            tree = etree.fromstring(getInterfaceDescription(cmd))
-            self.grass_task = processTask(tree).GetTask()
+            tree = etree.fromstring(gtask.get_interface_description(cmd))
+            self.grass_task = gtask.processTask(tree).GetTask()
             
             for p in self.grass_task.params:
                 if p.get('name', '') in ('input', 'map'):
@@ -2442,14 +2087,14 @@
     if sys.argv[1] !=  'test':
         q = wx.LogNull()
         cmd = utils.split(sys.argv[1])
-        task = grassTask(cmd[0])
+        task = gtask.grassTask(cmd[0], blackList = _blackList)
         task.set_options(cmd[1:])
         app = GrassGUIApp(task)
         app.MainLoop()
     else: #Test
         # Test grassTask from within a GRASS session
         if os.getenv("GISBASE") is not None:
-            task = grassTask("d.vect")
+            task = gtask.grassTask("d.vect")
             task.get_param('map')['value'] = "map_name"
             task.get_flag('v')['value'] = True
             task.get_param('layer')['value'] = 1
@@ -2457,7 +2102,7 @@
             assert ' '.join(task.getCmd()) ==  "d.vect -v map = map_name layer = 1 bcolor = red"
         # Test interface building with handmade grassTask,
         # possibly outside of a GRASS session.
-        task = grassTask()
+        task = gtask.grassTask()
         task.name = "TestTask"
         task.description = "This is an artificial grassTask() object intended for testing purposes."
         task.keywords = ["grass","test","task"]

Modified: grass/trunk/gui/wxpython/gui_modules/prompt.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/prompt.py	2011-06-29 10:52:43 UTC (rev 46842)
+++ grass/trunk/gui/wxpython/gui_modules/prompt.py	2011-06-29 12:53:42 UTC (rev 46843)
@@ -31,6 +31,7 @@
 import wx.lib.mixins.listctrl as listmix
 
 from grass.script import core as grass
+from grass.script import task as gtask
 
 import globalvar
 import menudata
@@ -176,7 +177,7 @@
         # get module's description
         if name in self._choicesCmd and not self._module:
             try:
-                self._module = menuform.GUI().ParseInterface(cmd = [name])
+                self._module = gtask.parse_interface(name)
             except IOError:
                 self._module = None
              
@@ -495,7 +496,7 @@
         self.autoCompList   = list()
         self.autoCompFilter = None
         
-        # command description (menuform.grassTask)
+        # command description (gtask.grassTask)
         self.cmdDesc = None
         self.cmdbuffer = self._readHistory()
         self.cmdindex = len(self.cmdbuffer)
@@ -783,7 +784,7 @@
                 self.OnCmdErase(None)
             else:
                 try:
-                    self.cmdDesc = menuform.GUI().ParseInterface(cmd = [cmd])
+                    self.cmdDesc = gtask.parse_interface(cmd)
                 except IOError:
                     self.cmdDesc = None
         
@@ -1015,11 +1016,11 @@
             if cmd not in globalvar.grassCmd['all']:
                 return
             
-            usage, description = self.GetCommandUsage(cmd)
-                                        
+            info = gtask.command_info(cmd)
+            
             self.CallTipSetBackground("#f4f4d1")
             self.CallTipSetForeground("BLACK")
-            self.CallTipShow(pos, usage + '\n\n' + description)
+            self.CallTipShow(pos, info['usage'] + '\n\n' + info['description'])
             
             
         elif event.GetKeyCode() in [wx.WXK_UP, wx.WXK_DOWN] and \
@@ -1092,7 +1093,7 @@
                         cmd != 'r.mapcalc' and \
                         (not self.cmdDesc or cmd != self.cmdDesc.get_name()):
                     try:
-                        self.cmdDesc = menuform.GUI().ParseInterface(cmd = [cmd])
+                        self.cmdDesc = gtask.parse_interface(cmd)
                     except IOError:
                         self.cmdDesc = None
             event.Skip()
@@ -1117,49 +1118,7 @@
         self.SetCurrentPos(pos)
         
         return entry
-
-    def GetCommandUsage(self, command):
-        """!Returns command syntax by running command help"""
-        usage = ''
-        description = ''
-
-        ret, out  = gcmd.RunCommand(command, 'help', getErrorMsg = True)
-               
-        if ret == 0:
-            cmdhelp = out.splitlines()
-            addline = False
-            helplist = []
-            description = ''
-            for line in cmdhelp:
-                if "Usage:" in line:
-                    addline = True
-                    continue
-                elif "Flags:" in line:
-                    addline = False
-                    break
-                elif addline == True:
-                    line = line.strip()
-                    helplist.append(line)
-
-            for line in cmdhelp:
-                if "Description:" in line:
-                    addline = True
-                    continue
-                elif "Keywords:" in line:
-                    addline = False
-                    break
-                elif addline == True:
-                    description += (line + ' ')
-                
-            description = description.strip()
-
-            for line in helplist:
-                usage += line + '\n'
-
-            return usage.strip(), description
-        else:
-            return ''   
-        
+    
     def OnDestroy(self, event):
         """!The clipboard contents can be preserved after
         the app has exited"""

Modified: grass/trunk/gui/wxpython/support/update_menudata.py
===================================================================
--- grass/trunk/gui/wxpython/support/update_menudata.py	2011-06-29 10:52:43 UTC (rev 46842)
+++ grass/trunk/gui/wxpython/support/update_menudata.py	2011-06-29 12:53:42 UTC (rev 46843)
@@ -28,6 +28,7 @@
     import elementtree.ElementTree as etree # Python <= 2.4
 
 from grass.script import core as grass
+from grass.script import task as gtask
 
 sys.path.append('gui_modules')
 import menudata
@@ -51,7 +52,7 @@
         if module in ignore:
             continue
         try:
-            interface = menuform.GUI().ParseInterface(cmd = [module])
+            interface = gtask.parse_interface(module)
         except IOError, e:
             grass.error(e)
             continue

Modified: grass/trunk/lib/python/Makefile
===================================================================
--- grass/trunk/lib/python/Makefile	2011-06-29 10:52:43 UTC (rev 46842)
+++ grass/trunk/lib/python/Makefile	2011-06-29 12:53:42 UTC (rev 46843)
@@ -8,7 +8,7 @@
 GDIR = $(PYDIR)/grass
 DSTDIR = $(GDIR)/script
 
-MODULES = core db raster vector array setup
+MODULES = core db raster vector array setup task
 
 PYFILES := $(patsubst %,$(DSTDIR)/%.py,$(MODULES) __init__)
 PYCFILES := $(patsubst %,$(DSTDIR)/%.pyc,$(MODULES) __init__)

Modified: grass/trunk/lib/python/core.py
===================================================================
--- grass/trunk/lib/python/core.py	2011-06-29 10:52:43 UTC (rev 46842)
+++ grass/trunk/lib/python/core.py	2011-06-29 12:53:42 UTC (rev 46843)
@@ -861,71 +861,6 @@
     """
     return sum(float(x) / 60 ** n for (n, x) in enumerate(s.split(':')))
 
-def command_info(cmd):
-    """!Returns 'help' information for any command as dictionary with entries
-    for description, keywords, usage, flags, and parameters"""
-    
-    cmdinfo = {}
-    s = start_command(cmd, 'help', stdout = subprocess.PIPE, stderr = subprocess.PIPE)
-    out, err = s.communicate()
-    
-    sections = err.split('\n\n')
-
-    #Description
-    first, desc = sections[0].split(':\n', 1)
-    desclines = desc.splitlines()
-    for line in desclines:
-        line = line.strip()+' '
-    
-    # Keywords
-    first, keywords = sections[1].split(':\n', 1)
-    keylines = keywords.splitlines()
-    list = []
-    list = keywords.strip().split(',')
-    cmdinfo['keywords'] = list
-        
-    cmdinfo['description'] = ''.join(desclines).strip()
-
-    # Usage
-    first, usage = sections[2].split(':\n', 1)
-    usagelines = usage.splitlines()
-    list = []
-    for line in usagelines:        
-        line = line.strip()
-        if line == '': continue
-        line = line+' '
-        list.append(line)
-        
-    cmdinfo['usage'] = ''.join(list).strip()
-
-    # Flags
-    first, flags = sections[3].split(':\n', 1)
-    flaglines = flags.splitlines()
-    dict = {}
-    for line in flaglines:
-        line = line.strip()
-        if line == '': continue
-        item = line.split(' ',1)[0].strip()
-        val = line.split(' ',1)[1].strip()
-        dict[item] = val
-        
-    cmdinfo['flags'] = dict
-    
-    # Parameters
-    first, params = err.rsplit(':\n', 1)
-    paramlines = params.splitlines()
-    dict = {}
-    for line in paramlines:
-        line = line.strip()
-        if line == '': continue
-        item = line.split(' ',1)[0].strip()
-        val = line.split(' ',1)[1].strip()
-        dict[item] = val
-         
-    cmdinfo['parameters'] = dict
-                
-    return cmdinfo
-
 # interface to g.mapsets
 
 def mapsets(accessible = True):

Added: grass/trunk/lib/python/task.py
===================================================================
--- grass/trunk/lib/python/task.py	                        (rev 0)
+++ grass/trunk/lib/python/task.py	2011-06-29 12:53:42 UTC (rev 46843)
@@ -0,0 +1,519 @@
+"""!@package grass.script.task
+
+ at brief GRASS Python scripting module (task)
+
+Get interface description of GRASS commands
+
+Based on gui/wxpython/gui_modules/menuform.py
+
+Usage:
+
+ at code
+from grass.script import task as gtask
+
+gtask.command_info('r.info')
+...
+ at endcode
+
+(C) 2011 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS
+for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import types
+import string
+try:
+    import xml.etree.ElementTree as etree
+except ImportError:
+    import elementtree.ElementTree as etree # Python <= 2.4
+
+from core import *
+
+class grassTask:
+    """!This class holds the structures needed for filling by the
+    parser
+
+    Parameter blackList is a dictionary with fixed structure, eg.
+
+    @code
+    blackList = {'items' : {'d.legend' : { 'flags' : ['m'],
+                                           'params' : [] }},
+                 'enabled': True}
+    @endcode
+    
+    @param path full path
+    @param blackList hide some options in the GUI (dictionary)
+    """
+    def __init__(self, path = None, blackList = None):
+        self.path = path
+        self.name = _('unknown')
+        self.params = list()
+        self.description = ''
+        self.label = ''
+        self.flags = list()
+        self.keywords = list()
+        self.errorMsg = ''
+        self.firstParam = None
+        if blackList:
+            self.blackList = blackList
+        else:
+            self.blackList = { 'enabled' : False, 'items' : {} }
+        
+        if path is not None:
+            try:
+                processTask(tree = etree.fromstring(get_interface_description(path)),
+                            task = self)
+            except ScriptError, e:
+                self.errorMsg = e.value
+            
+            self.define_first()
+        
+    def define_first(self):
+        """!Define first parameter
+        """
+        if len(self.params) > 0:
+            self.firstParam = self.params[0]['name']
+        
+    def get_error_msg(self):
+        """!Get error message ('' for no error)
+        """
+        return self.errorMsg
+    
+    def get_name(self):
+        """!Get task name
+        """
+        return self.name
+
+    def get_description(self, full = True):
+        """!Get module's description
+
+        @param full True for label + desc
+        """
+        if self.label:
+            if full:
+                return self.label + ' ' + self.description
+            else:
+                return self.label
+        else:
+            return self.description
+
+    def get_keywords(self):
+        """!Get module's keywords
+        """
+        return self.keywords
+    
+    def get_list_params(self, element = 'name'):
+        """!Get list of parameters
+
+        @param element element name
+        """
+        params = []
+        for p in self.params:
+            params.append(p[element])
+        
+        return params
+
+    def get_list_flags(self, element = 'name'):
+        """!Get list of flags
+
+        @param element element name
+        """
+        flags = []
+        for p in self.flags:
+            flags.append(p[element])
+        
+        return flags
+    
+    def get_param(self, value, element = 'name', raiseError = True):
+        """!Find and return a param by name
+
+        @param value param's value
+        @param element element name
+        @param raiseError True for raise on error
+        """
+        try:
+            for p in self.params:
+                val = p[element]
+                if val is None:
+                    continue
+                if type(val) in (types.ListType, types.TupleType):
+                    if value in val:
+                        return p
+                elif type(val) ==  types.StringType:
+                    if p[element][:len(value)] ==  value:
+                        return p
+                else:
+                    if p[element] ==  value:
+                        return p
+        except KeyError:
+            pass
+        
+        if raiseError:
+            raise ValueError, _("Parameter element '%(element)s' not found: '%(value)s'") % \
+                { 'element' : element, 'value' : value }
+        else:
+            return None
+
+    def get_flag(self, aFlag):
+        """!Find and return a flag by name
+
+        Raises ValueError when the flag is not found.
+
+        @param aFlag name of the flag
+        """
+        for f in self.flags:
+            if f['name'] ==  aFlag:
+                return f
+        raise ValueError, _("Flag not found: %s") % aFlag
+
+    def getCmdError(self):
+        """!Get error string produced by getCmd(ignoreErrors = False)
+        
+        @return list of errors
+        """
+        errorList = list()
+        # determine if suppress_required flag is given
+        for f in self.flags:
+            if f['value'] and f['suppress_required']:
+                return errorList
+        
+        for p in self.params:
+            if not p.get('value', '') and p.get('required', False):
+                if not p.get('default', ''):
+                    desc = p.get('label', '')
+                    if not desc:
+                        desc = p['description']
+                    errorList.append(_("Parameter '%(name)s' (%(desc)s) is missing.") % \
+                                         {'name' : p['name'], 'desc' : desc })
+        
+        return errorList
+    
+    def getCmd(self, ignoreErrors = False):
+        """!Produce an array of command name and arguments for feeding
+        into some execve-like command processor.
+
+        @param ignoreErrors True to return whatever has been built so
+        far, even though it would not be a correct command for GRASS
+        """
+        cmd = [self.name]
+        
+        suppress_required = False
+        for flag in self.flags:
+            if flag['value']:
+                if len(flag['name']) > 1: # e.g. overwrite
+                    cmd +=  [ '--' + flag['name'] ]
+                else:
+                    cmd +=  [ '-' + flag['name'] ]
+                if flag['suppress_required']:
+                    suppress_required = True
+        for p in self.params:
+            if p.get('value','') ==  '' and p.get('required', False):
+                if p.get('default', '') !=  '':
+                    cmd +=  [ '%s=%s' % (p['name'], p['default']) ]
+                elif ignoreErrors is True and not suppress_required:
+                    cmd +=  [ '%s=%s' % (p['name'], _('<required>')) ]
+            elif p.get('value','') !=  '' and p['value'] !=  p.get('default','') :
+                # Output only values that have been set, and different from defaults
+                cmd +=  [ '%s=%s' % (p['name'], p['value']) ]
+        
+        errList = self.getCmdError()
+        if ignoreErrors is False and errList:
+            raise ValueError, '\n'.join(errList)
+        
+        return cmd
+
+    def get_options(self):
+        """!Get options
+        """
+        return { 'flags'  : self.flags,
+                 'params' : self.params }
+    
+    def has_required(self):
+        """!Check if command has at least one required paramater
+        """
+        for p in self.params:
+            if p.get('required', False):
+                return True
+        
+        return False
+    
+    def set_param(self, aParam, aValue, element = 'value'):
+        """!Set param value/values.
+        """
+        try:
+            param = self.get_param(aParam)
+        except ValueError:
+            return
+        
+        param[element] = aValue
+
+    def set_flag(self, aFlag, aValue, element = 'value'):
+        """!Enable / disable flag.
+        """
+        try:
+            param = self.get_flag(aFlag)
+        except ValueError:
+            return
+        
+        param[element] = aValue
+
+    def set_options(self, opts):
+        """!Set flags and parameters
+
+        @param opts list of flags and parameters"""
+        for opt in opts:
+            if opt[0] ==  '-': # flag
+                self.set_flag(opt.lstrip('-'), True)
+            else: # parameter
+                key, value = opt.split('=', 1)
+                self.set_param(key, value)
+        
+class processTask:
+    """!A ElementTree handler for the --interface-description output,
+    as defined in grass-interface.dtd. Extend or modify this and the
+    DTD if the XML output of GRASS' parser is extended or modified.
+
+    @param tree root tree node
+    @param task grassTask instance or None
+    @param blackList list of flags/params to hide
+    
+    @return grassTask instance
+    """
+    def __init__(self, tree, task = None, blackList = None):
+        if task:
+            self.task = task
+        else:
+            self.task = grassTask()
+        if blackList:
+            self.task.blackList = blackList
+        
+        self.root = tree
+        
+        self._process_module()
+        self._process_params()
+        self._process_flags()
+        self.task.define_first()
+        
+    def _process_module(self):
+        """!Process module description
+        """
+        self.task.name = self.root.get('name', default = 'unknown')
+        
+        # keywords
+        for keyword in self._get_node_text(self.root, 'keywords').split(','):
+            self.task.keywords.append(keyword.strip())
+        
+        self.task.label       = self._get_node_text(self.root, 'label')
+        self.task.description = self._get_node_text(self.root, 'description')
+        
+    def _process_params(self):
+        """!Process parameters
+        """
+        for p in self.root.findall('parameter'):
+            # gisprompt
+            node_gisprompt = p.find('gisprompt')
+            gisprompt = False
+            age = element = prompt = None
+            if node_gisprompt is not None:
+                gisprompt = True
+                age     = node_gisprompt.get('age', '')
+                element = node_gisprompt.get('element', '')
+                prompt  = node_gisprompt.get('prompt', '')
+            
+            # value(s)
+            values = []
+            values_desc = []
+            node_values = p.find('values')
+            if node_values is not None:
+                for pv in node_values.findall('value'):
+                    values.append(self._get_node_text(pv, 'name'))
+                    desc = self._get_node_text(pv, 'description')
+                    if desc:
+                        values_desc.append(desc)
+            
+            # keydesc
+            key_desc = []
+            node_key_desc = p.find('keydesc')
+            if node_key_desc is not None:
+                for ki in node_key_desc.findall('item'):
+                    key_desc.append(ki.text)
+            
+            if p.get('multiple', 'no') ==  'yes':
+                multiple = True
+            else:
+                multiple = False
+            if p.get('required', 'no') ==  'yes':
+                required = True
+            else:
+                required = False
+            
+            if self.task.blackList['enabled'] and \
+                    self.task.name in self.task.blackList['items'] and \
+                    p.get('name') in self.task.blackList['items'][self.task.name].get('params', []):
+                hidden = True
+            else:
+                hidden = False
+            
+            self.task.params.append( {
+                "name"           : p.get('name'),
+                "type"           : p.get('type'),
+                "required"       : required,
+                "multiple"       : multiple,
+                "label"          : self._get_node_text(p, 'label'),
+                "description"    : self._get_node_text(p, 'description'),
+                'gisprompt'      : gisprompt,
+                'age'            : age,
+                'element'        : element,
+                'prompt'         : prompt,
+                "guisection"     : self._get_node_text(p, 'guisection'),
+                "guidependency"  : self._get_node_text(p, 'guidependency'),
+                "default"        : self._get_node_text(p, 'default'),
+                "values"         : values,
+                "values_desc"    : values_desc,
+                "value"          : '',
+                "key_desc"       : key_desc,
+                "hidden"         : hidden
+                })
+            
+    def _process_flags(self):
+        """!Process flags
+        """
+        for p in self.root.findall('flag'):
+            if self.task.blackList['enabled'] and \
+                    self.task.name in self.task.blackList['items'] and \
+                    p.get('name') in self.task.blackList['items'][self.task.name].get('flags', []):
+                hidden = True
+            else:
+                hidden = False
+            
+            if p.find('suppress_required') is not None:
+                suppress_required = True
+            else:
+                suppress_required = False
+            
+            self.task.flags.append( {
+                    "name"              : p.get('name'),
+                    "label"             : self._get_node_text(p, 'label'),
+                    "description"       : self._get_node_text(p, 'description'),
+                    "guisection"        : self._get_node_text(p, 'guisection'),
+                    "suppress_required" : suppress_required,
+                    "value"             : False,
+                    "hidden"            : hidden
+                    } )
+        
+    def _get_node_text(self, node, tag, default = ''):
+        """!Get node text"""
+        p = node.find(tag)
+        if p is not None:
+            return string.join(string.split(p.text), ' ')
+        
+        return default
+    
+    def get_task(self):
+        """!Get grassTask instance"""
+        return self.task
+
+def get_interface_description(cmd):
+    """!Returns the XML description for the GRASS cmd.
+
+    The DTD must be located in $GISBASE/etc/grass-interface.dtd,
+    otherwise the parser will not succeed.
+
+    @param cmd command (name of GRASS module)
+    """
+    try:
+        cmdout, cmderr = Popen([cmd, '--interface-description'], stdout = PIPE,
+                                     stderr = PIPE).communicate()
+    except OSError, e:
+        raise ScriptError, _("Unable to fetch interface description for command '%s'.") % cmd + \
+            _("Details:") +  "%s" % e
+    if cmderr and cmderr[:7] != 'WARNING':
+        raise ScriptError, _("Unable to fetch interface description for command '%s'.") % cmd + \
+            _("Details:") + " %s" % cmderr
+    
+    return cmdout.replace('grass-interface.dtd', os.path.join(os.getenv('GISBASE'), 'etc', 'grass-interface.dtd'))
+
+def parse_interface(name, parser = processTask, blackList = None):
+        """!Parse interface of given GRASS module
+        
+        @param name name of GRASS module to be parsed
+        """
+        # enc = locale.getdefaultlocale()[1]
+        # if enc and enc.lower() not in ("utf8", "utf-8"):
+        #     tree = etree.fromstring(getInterfaceDescription(cmd[0]).decode(enc).encode("utf-8"))
+        # else:
+        tree = etree.fromstring(get_interface_description(name))
+        
+        return parser(tree, blackList = blackList).get_task()
+
+def command_info(cmd):
+    """!Returns meta information for any GRASS command as dictionary
+    with entries for description, keywords, usage, flags, and
+    parameters, e.g.
+    
+    @code
+    >>> gtask.command_info('g.tempfile')
+    
+    {'keywords': ['general', 'map management'],
+     'params': [{'gisprompt': False, 'multiple': False, 'name': 'pid', 'guidependency': '',
+                'default': '', 'age': None, 'required': True, 'value': '',
+                'label': '', 'guisection': '', 'key_desc': [], 'values': [], 'values_desc': [],
+                'prompt': None, 'hidden': False, 'element': None, 'type': 'integer',
+                'description': 'Process id to use when naming the tempfile'}],
+     'flags': [{'description': 'Verbose module output', 'value': False, 'label': '', 'guisection': '',
+                'suppress_required': False, 'hidden': False, 'name': 'verbose'}, {'description': 'Quiet module output',
+                'value': False, 'label': '', 'guisection': '', 'suppress_required': False, 'hidden': False, 'name': 'quiet'}],
+     'description': 'Creates a temporary file and prints the file name.',
+     'usage': 'g.tempfile pid=integer [--verbose] [--quiet]'
+    }
+
+    >>> gtask.command_info('v.buffer')['keywords']
+    
+    ['vector', 'geometry', 'buffer']
+    @endcode
+    """
+    task = parse_interface(cmd)
+    cmdinfo = {}
+    
+    cmdinfo['description'] = task.get_description()
+    cmdinfo['keywords']    = task.get_keywords()
+    cmdinfo['flags']       = flags = task.get_options()['flags']
+    cmdinfo['params']      = params = task.get_options()['params']
+    
+    usage = task.get_name()
+    flags_short = list()
+    flags_long  = list()
+    for f in flags:
+        fname = f.get('name', 'unknown')
+        if len(fname) > 1:
+            flags_long.append(fname)
+        else:
+            flags_short.append(fname)
+    
+    if len(flags_short) > 1:
+        usage += ' [-' + ''.join(flags_short) + ']'
+    
+    for p in params:
+        ptype = ','.join(p.get('key_desc', []))
+        if not ptype:
+            ptype = p.get('type', '')
+        req =  p.get('required', False)
+        if not req:
+           usage += ' ['
+        else:
+            usage += ' '
+        usage += p['name'] + '=' + ptype
+        if p.get('multiple', False):
+            usage += '[,' + ptype + ',...]'
+        if not req:
+            usage += ']'
+        
+    for key in flags_long:
+        usage += ' [--' + key + ']'
+    
+    cmdinfo['usage'] = usage
+    
+    return cmdinfo



More information about the grass-commit mailing list