1 """!@package grass.script.task
3 @brief GRASS Python scripting module (task)
5 Get interface description of GRASS commands
7 Based on gui/wxpython/gui_modules/menuform.py
12 from grass.script import task as gtask
14 gtask.command_info('r.info')
18 (C) 2011 by the GRASS Development Team
19 This program is free software under the GNU General Public
20 License (>=v2). Read the file COPYING that comes with GRASS
23 @author Martin Landa <landa.martin gmail.com>
29 import xml.etree.ElementTree
as etree
31 import elementtree.ElementTree
as etree
36 """!This class holds the structures needed for filling by the
39 Parameter blackList is a dictionary with fixed structure, eg.
42 blackList = {'items' : {'d.legend' : { 'flags' : ['m'],
48 @param blackList hide some options in the GUI (dictionary)
50 def __init__(self, path = None, blackList = None):
63 self.
blackList = {
'enabled' :
False,
'items' : {} }
69 except ScriptError, e:
75 """!Define first parameter
81 """!Get error message ('' for no error)
91 """!Get module's description
93 @param full True for label + desc
104 """!Get module's keywords
109 """!Get list of parameters
111 @param element element name
115 params.append(p[element])
120 """!Get list of flags
122 @param element element name
126 flags.append(p[element])
130 def get_param(self, value, element = 'name', raiseError = True):
131 """!Find and return a param by name
133 @param value param's value
134 @param element element name
135 @param raiseError True for raise on error
142 if type(val)
in (types.ListType, types.TupleType):
145 elif type(val) == types.StringType:
146 if p[element][:len(value)] == value:
149 if p[element] == value:
155 raise ValueError, _(
"Parameter element '%(element)s' not found: '%(value)s'") % \
156 {
'element' : element,
'value' : value }
161 """!Find and return a flag by name
163 Raises ValueError when the flag is not found.
165 @param aFlag name of the flag
168 if f[
'name'] == aFlag:
170 raise ValueError, _(
"Flag not found: %s") % aFlag
173 """!Get error string produced by getCmd(ignoreErrors = False)
175 @return list of errors
180 if f[
'value']
and f[
'suppress_required']:
184 if not p.get(
'value',
'')
and p.get(
'required',
False):
185 if not p.get(
'default',
''):
186 desc = p.get(
'label',
'')
188 desc = p[
'description']
189 errorList.append(_(
"Parameter '%(name)s' (%(desc)s) is missing.") % \
190 {
'name' : p[
'name'],
'desc' : desc })
194 def getCmd(self, ignoreErrors = False, ignoreRequired = False):
195 """!Produce an array of command name and arguments for feeding
196 into some execve-like command processor.
198 @param ignoreErrors True to return whatever has been built so
199 far, even though it would not be a correct command for GRASS
200 @param ignoreRequired True to ignore required flags, otherwise
201 '<required>' is shown
205 suppress_required =
False
206 for flag
in self.
flags:
208 if len(flag[
'name']) > 1:
209 cmd += [
'--' + flag[
'name'] ]
211 cmd += [
'-' + flag[
'name'] ]
212 if flag[
'suppress_required']:
213 suppress_required =
True
215 if p.get(
'value',
'') ==
'' and p.get(
'required',
False):
216 if p.get(
'default',
'') !=
'':
217 cmd += [
'%s=%s' % (p[
'name'], p[
'default']) ]
218 elif ignoreErrors
and not suppress_required
and not ignoreRequired:
219 cmd += [
'%s=%s' % (p[
'name'], _(
'<required>')) ]
220 elif p.get(
'value',
'') !=
'' and p[
'value'] != p.get(
'default',
'') :
222 cmd += [
'%s=%s' % (p[
'name'], p[
'value']) ]
225 if ignoreErrors
is False and errList:
226 raise ValueError,
'\n'.join(errList)
233 return {
'flags' : self.
flags,
237 """!Check if command has at least one required paramater
240 if p.get(
'required',
False):
246 """!Set param value/values.
253 param[element] = aValue
255 def set_flag(self, aFlag, aValue, element = 'value'):
256 """!Enable / disable flag.
263 param[element] = aValue
266 """!Set flags and parameters
268 @param opts list of flags and parameters"""
271 self.
set_flag(opt.lstrip(
'-'),
True)
273 key, value = opt.split(
'=', 1)
277 """!A ElementTree handler for the --interface-description output,
278 as defined in grass-interface.dtd. Extend or modify this and the
279 DTD if the XML output of GRASS' parser is extended or modified.
281 @param tree root tree node
282 @param task grassTask instance or None
283 @param blackList list of flags/params to hide
285 @return grassTask instance
287 def __init__(self, tree, task = None, blackList = None):
293 self.task.blackList = blackList
300 self.task.define_first()
302 def _process_module(self):
303 """!Process module description
305 self.task.name = self.root.get(
'name', default =
'unknown')
309 self.task.keywords.append(keyword.strip())
314 def _process_params(self):
315 """!Process parameters
317 for p
in self.root.findall(
'parameter'):
319 node_gisprompt = p.find(
'gisprompt')
321 age = element = prompt =
None
322 if node_gisprompt
is not None:
324 age = node_gisprompt.get(
'age',
'')
325 element = node_gisprompt.get(
'element',
'')
326 prompt = node_gisprompt.get(
'prompt',
'')
331 node_values = p.find(
'values')
332 if node_values
is not None:
333 for pv
in node_values.findall(
'value'):
337 values_desc.append(desc)
341 node_key_desc = p.find(
'keydesc')
342 if node_key_desc
is not None:
343 for ki
in node_key_desc.findall(
'item'):
344 key_desc.append(ki.text)
346 if p.get(
'multiple',
'no') ==
'yes':
350 if p.get(
'required',
'no') ==
'yes':
355 if self.task.blackList[
'enabled']
and \
356 self.task.name
in self.task.blackList[
'items']
and \
357 p.get(
'name')
in self.task.blackList[
'items'][self.task.name].get(
'params', []):
362 self.task.params.append( {
363 "name" : p.get(
'name'),
364 "type" : p.get(
'type'),
365 "required" : required,
366 "multiple" : multiple,
369 'gisprompt' : gisprompt,
377 "values_desc" : values_desc,
379 "key_desc" : key_desc,
383 def _process_flags(self):
386 for p
in self.root.findall(
'flag'):
387 if self.task.blackList[
'enabled']
and \
388 self.task.name
in self.task.blackList[
'items']
and \
389 p.get(
'name')
in self.task.blackList[
'items'][self.task.name].get(
'flags', []):
394 if p.find(
'suppress_required')
is not None:
395 suppress_required =
True
397 suppress_required =
False
399 self.task.flags.append( {
400 "name" : p.get(
'name'),
404 "suppress_required" : suppress_required,
409 def _get_node_text(self, node, tag, default = ''):
413 return string.join(string.split(p.text),
' ')
418 """!Get grassTask instance"""
422 """!Returns the XML description for the GRASS cmd.
424 The DTD must be located in $GISBASE/etc/grass-interface.dtd,
425 otherwise the parser will not succeed.
427 @param cmd command (name of GRASS module)
430 if sys.platform ==
'win32' and os.path.splitext(cmd)[1] ==
'.py':
431 os.chdir(os.path.join(os.getenv(
'GISBASE'),
'etc',
'gui',
'scripts'))
432 args = [sys.executable, cmd,
'--interface-description']
434 args = [cmd,
'--interface-description']
436 cmdout, cmderr =
Popen(args, stdout = PIPE,
437 stderr = PIPE).communicate()
440 raise ScriptError, _(
"Unable to fetch interface description for command '%(cmd)s'."
441 "\n\nDetails: %(det)s") % {
'cmd' : cmd,
'det' : e }
447 return cmdout.replace(
'grass-interface.dtd', os.path.join(os.getenv(
'GISBASE'),
'etc',
'grass-interface.dtd'))
450 """!Parse interface of given GRASS module
452 @param name name of GRASS module to be parsed
454 enc = locale.getdefaultlocale()[1]
455 if enc
and enc.lower() ==
"cp932":
456 p = re.compile(
'encoding="' + enc +
'"', re.IGNORECASE)
457 tree = etree.fromstring(p.sub(
'encoding="utf-8"',
462 return parser(tree, blackList = blackList).get_task()
465 """!Returns meta information for any GRASS command as dictionary
466 with entries for description, keywords, usage, flags, and
470 >>> gtask.command_info('g.tempfile')
472 {'keywords': ['general', 'map management'],
473 'params': [{'gisprompt': False, 'multiple': False, 'name': 'pid', 'guidependency': '',
474 'default': '', 'age': None, 'required': True, 'value': '',
475 'label': '', 'guisection': '', 'key_desc': [], 'values': [], 'values_desc': [],
476 'prompt': None, 'hidden': False, 'element': None, 'type': 'integer',
477 'description': 'Process id to use when naming the tempfile'}],
478 'flags': [{'description': 'Verbose module output', 'value': False, 'label': '', 'guisection': '',
479 'suppress_required': False, 'hidden': False, 'name': 'verbose'}, {'description': 'Quiet module output',
480 'value': False, 'label': '', 'guisection': '', 'suppress_required': False, 'hidden': False, 'name': 'quiet'}],
481 'description': 'Creates a temporary file and prints the file name.',
482 'usage': 'g.tempfile pid=integer [--verbose] [--quiet]'
485 >>> gtask.command_info('v.buffer')['keywords']
487 ['vector', 'geometry', 'buffer']
493 cmdinfo[
'description'] = task.get_description()
494 cmdinfo[
'keywords'] = task.get_keywords()
495 cmdinfo[
'flags'] = flags = task.get_options()[
'flags']
496 cmdinfo[
'params'] = params = task.get_options()[
'params']
498 usage = task.get_name()
502 fname = f.get(
'name',
'unknown')
504 flags_long.append(fname)
506 flags_short.append(fname)
508 if len(flags_short) > 1:
509 usage +=
' [-' +
''.join(flags_short) +
']'
512 ptype =
','.join(p.get(
'key_desc', []))
514 ptype = p.get(
'type',
'')
515 req = p.get(
'required',
False)
520 usage += p[
'name'] +
'=' + ptype
521 if p.get(
'multiple',
False):
522 usage +=
'[,' + ptype +
',...]'
526 for key
in flags_long:
527 usage +=
' [--' + key +
']'
529 cmdinfo[
'usage'] = usage