GRASS Programmer's Manual  6.5.svn(2014)-r66266
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
task.py
Go to the documentation of this file.
1 """!@package grass.script.task
2 
3 @brief GRASS Python scripting module (task)
4 
5 Get interface description of GRASS commands
6 
7 Based on gui/wxpython/gui_modules/menuform.py
8 
9 Usage:
10 
11 @code
12 from grass.script import task as gtask
13 
14 gtask.command_info('r.info')
15 ...
16 @endcode
17 
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
21 for details.
22 
23 @author Martin Landa <landa.martin gmail.com>
24 """
25 
26 import types
27 import string
28 try:
29  import xml.etree.ElementTree as etree
30 except ImportError:
31  import elementtree.ElementTree as etree # Python <= 2.4
32 
33 from core import *
34 
35 class grassTask:
36  """!This class holds the structures needed for filling by the
37  parser
38 
39  Parameter blackList is a dictionary with fixed structure, eg.
40 
41  @code
42  blackList = {'items' : {'d.legend' : { 'flags' : ['m'],
43  'params' : [] }},
44  'enabled': True}
45  @endcode
46 
47  @param path full path
48  @param blackList hide some options in the GUI (dictionary)
49  """
50  def __init__(self, path = None, blackList = None):
51  self.path = path
52  self.name = _('unknown')
53  self.params = list()
54  self.description = ''
55  self.label = ''
56  self.flags = list()
57  self.keywords = list()
58  self.errorMsg = ''
59  self.firstParam = None
60  if blackList:
61  self.blackList = blackList
62  else:
63  self.blackList = { 'enabled' : False, 'items' : {} }
64 
65  if path is not None:
66  try:
67  processTask(tree = etree.fromstring(get_interface_description(path)),
68  task = self)
69  except ScriptError, e:
70  self.errorMsg = e.value
71 
72  self.define_first()
73 
74  def define_first(self):
75  """!Define first parameter
76 
77  @return name of first parameter
78  """
79  if len(self.params) > 0:
80  self.firstParam = self.params[0]['name']
81 
82  return self.firstParam
83 
84  def get_error_msg(self):
85  """!Get error message ('' for no error)
86  """
87  return self.errorMsg
88 
89  def get_name(self):
90  """!Get task name
91  """
92  return self.name
93 
94  def get_description(self, full = True):
95  """!Get module's description
96 
97  @param full True for label + desc
98  """
99  if self.label:
100  if full:
101  return self.label + ' ' + self.description
102  else:
103  return self.label
104  else:
105  return self.description
106 
107  def get_keywords(self):
108  """!Get module's keywords
109  """
110  return self.keywords
111 
112  def get_list_params(self, element = 'name'):
113  """!Get list of parameters
114 
115  @param element element name
116  """
117  params = []
118  for p in self.params:
119  params.append(p[element])
120 
121  return params
122 
123  def get_list_flags(self, element = 'name'):
124  """!Get list of flags
125 
126  @param element element name
127  """
128  flags = []
129  for p in self.flags:
130  flags.append(p[element])
131 
132  return flags
133 
134  def get_param(self, value, element = 'name', raiseError = True):
135  """!Find and return a param by name
136 
137  @param value param's value
138  @param element element name
139  @param raiseError True for raise on error
140  """
141  try:
142  for p in self.params:
143  val = p[element]
144  if val is None:
145  continue
146  if type(val) in (types.ListType, types.TupleType):
147  if value in val:
148  return p
149  elif type(val) == types.StringType:
150  if p[element][:len(value)] == value:
151  return p
152  else:
153  if p[element] == value:
154  return p
155  except KeyError:
156  pass
157 
158  if raiseError:
159  raise ValueError, _("Parameter element '%(element)s' not found: '%(value)s'") % \
160  { 'element' : element, 'value' : value }
161  else:
162  return None
163 
164  def get_flag(self, aFlag):
165  """!Find and return a flag by name
166 
167  Raises ValueError when the flag is not found.
168 
169  @param aFlag name of the flag
170  """
171  for f in self.flags:
172  if f['name'] == aFlag:
173  return f
174  raise ValueError, _("Flag not found: %s") % aFlag
175 
176  def get_cmd_error(self):
177  """!Get error string produced by get_cmd(ignoreErrors = False)
178 
179  @return list of errors
180  """
181  errorList = list()
182  # determine if suppress_required flag is given
183  for f in self.flags:
184  if f['value'] and f['suppress_required']:
185  return errorList
186 
187  for p in self.params:
188  if not p.get('value', '') and p.get('required', False):
189  if not p.get('default', ''):
190  desc = p.get('label', '')
191  if not desc:
192  desc = p['description']
193  errorList.append(_("Parameter '%(name)s' (%(desc)s) is missing.") % \
194  {'name' : p['name'], 'desc' : desc })
195 
196  return errorList
197 
198  def get_cmd(self, ignoreErrors = False, ignoreRequired = False, ignoreDefault = True):
199  """!Produce an array of command name and arguments for feeding
200  into some execve-like command processor.
201 
202  @param ignoreErrors True to return whatever has been built so
203  far, even though it would not be a correct command for GRASS
204  @param ignoreRequired True to ignore required flags, otherwise
205  '<required>' is shown
206  @param ignoreDefault True to ignore parameters with default values
207  """
208  cmd = [self.name]
209 
210  suppress_required = False
211  for flag in self.flags:
212  if flag['value']:
213  if len(flag['name']) > 1: # e.g. overwrite
214  cmd += [ '--' + flag['name'] ]
215  else:
216  cmd += [ '-' + flag['name'] ]
217  if flag['suppress_required']:
218  suppress_required = True
219  for p in self.params:
220  if p.get('value', '') == '' and p.get('required', False):
221  if p.get('default', '') != '':
222  cmd += [ '%s=%s' % (p['name'], p['default']) ]
223  elif ignoreErrors and not suppress_required and not ignoreRequired:
224  cmd += [ '%s=%s' % (p['name'], _('<required>')) ]
225  elif p.get('value', '') == '' and p.get('default', '') != '' and not ignoreDefault:
226  cmd += [ '%s=%s' % (p['name'], p['default']) ]
227  elif p.get('value', '') != '' and \
228  (p['value'] != p.get('default', '') or not ignoreDefault):
229  # output only values that have been set, and different from defaults
230  cmd += [ '%s=%s' % (p['name'], p['value']) ]
231 
232  errList = self.get_cmd_error()
233  if ignoreErrors is False and errList:
234  raise ValueError, '\n'.join(errList)
235 
236  return cmd
237 
238  def get_options(self):
239  """!Get options
240  """
241  return { 'flags' : self.flags,
242  'params' : self.params }
243 
244  def has_required(self):
245  """!Check if command has at least one required paramater
246  """
247  for p in self.params:
248  if p.get('required', False):
249  return True
250 
251  return False
252 
253  def set_param(self, aParam, aValue, element = 'value'):
254  """!Set param value/values.
255  """
256  try:
257  param = self.get_param(aParam)
258  except ValueError:
259  return
260 
261  param[element] = aValue
262 
263  def set_flag(self, aFlag, aValue, element = 'value'):
264  """!Enable / disable flag.
265  """
266  try:
267  param = self.get_flag(aFlag)
268  except ValueError:
269  return
270 
271  param[element] = aValue
272 
273  def set_options(self, opts):
274  """!Set flags and parameters
275 
276  @param opts list of flags and parameters"""
277  for opt in opts:
278  if opt[0] == '-': # flag
279  self.set_flag(opt.lstrip('-'), True)
280  else: # parameter
281  key, value = opt.split('=', 1)
282  self.set_param(key, value)
283 
285  """!A ElementTree handler for the --interface-description output,
286  as defined in grass-interface.dtd. Extend or modify this and the
287  DTD if the XML output of GRASS' parser is extended or modified.
288 
289  @param tree root tree node
290  @param task grassTask instance or None
291  @param blackList list of flags/params to hide
292 
293  @return grassTask instance
294  """
295  def __init__(self, tree, task = None, blackList = None):
296  if task:
297  self.task = task
298  else:
299  self.task = grassTask()
300  if blackList:
301  self.task.blackList = blackList
302 
303  self.root = tree
304 
305  self._process_module()
306  self._process_params()
307  self._process_flags()
308  self.task.define_first()
309 
310  def _process_module(self):
311  """!Process module description
312  """
313  self.task.name = self.root.get('name', default = 'unknown')
314 
315  # keywords
316  for keyword in self._get_node_text(self.root, 'keywords').split(','):
317  self.task.keywords.append(keyword.strip())
318 
319  self.task.label = self._get_node_text(self.root, 'label')
320  self.task.description = self._get_node_text(self.root, 'description')
321 
322  def _process_params(self):
323  """!Process parameters
324  """
325  for p in self.root.findall('parameter'):
326  # gisprompt
327  node_gisprompt = p.find('gisprompt')
328  gisprompt = False
329  age = element = prompt = None
330  if node_gisprompt is not None:
331  gisprompt = True
332  age = node_gisprompt.get('age', '')
333  element = node_gisprompt.get('element', '')
334  prompt = node_gisprompt.get('prompt', '')
335 
336  # value(s)
337  values = []
338  values_desc = []
339  node_values = p.find('values')
340  if node_values is not None:
341  for pv in node_values.findall('value'):
342  values.append(self._get_node_text(pv, 'name'))
343  desc = self._get_node_text(pv, 'description')
344  if desc:
345  values_desc.append(desc)
346 
347  # keydesc
348  key_desc = []
349  node_key_desc = p.find('keydesc')
350  if node_key_desc is not None:
351  for ki in node_key_desc.findall('item'):
352  key_desc.append(ki.text)
353 
354  if p.get('multiple', 'no') == 'yes':
355  multiple = True
356  else:
357  multiple = False
358  if p.get('required', 'no') == 'yes':
359  required = True
360  else:
361  required = False
362 
363  if self.task.blackList['enabled'] and \
364  self.task.name in self.task.blackList['items'] and \
365  p.get('name') in self.task.blackList['items'][self.task.name].get('params', []):
366  hidden = True
367  else:
368  hidden = False
369 
370  self.task.params.append( {
371  "name" : p.get('name'),
372  "type" : p.get('type'),
373  "required" : required,
374  "multiple" : multiple,
375  "label" : self._get_node_text(p, 'label'),
376  "description" : self._get_node_text(p, 'description'),
377  'gisprompt' : gisprompt,
378  'age' : age,
379  'element' : element,
380  'prompt' : prompt,
381  "guisection" : self._get_node_text(p, 'guisection'),
382  "guidependency" : self._get_node_text(p, 'guidependency'),
383  "default" : self._get_node_text(p, 'default'),
384  "values" : values,
385  "values_desc" : values_desc,
386  "value" : '',
387  "key_desc" : key_desc,
388  "hidden" : hidden
389  })
390 
391  def _process_flags(self):
392  """!Process flags
393  """
394  for p in self.root.findall('flag'):
395  if self.task.blackList['enabled'] and \
396  self.task.name in self.task.blackList['items'] and \
397  p.get('name') in self.task.blackList['items'][self.task.name].get('flags', []):
398  hidden = True
399  else:
400  hidden = False
401 
402  if p.find('suppress_required') is not None:
403  suppress_required = True
404  else:
405  suppress_required = False
406 
407  self.task.flags.append( {
408  "name" : p.get('name'),
409  "label" : self._get_node_text(p, 'label'),
410  "description" : self._get_node_text(p, 'description'),
411  "guisection" : self._get_node_text(p, 'guisection'),
412  "suppress_required" : suppress_required,
413  "value" : False,
414  "hidden" : hidden
415  } )
416 
417  def _get_node_text(self, node, tag, default = ''):
418  """!Get node text"""
419  p = node.find(tag)
420  if p is not None:
421  return string.join(string.split(p.text), ' ')
422 
423  return default
424 
425  def get_task(self):
426  """!Get grassTask instance"""
427  return self.task
428 
430  """!Returns the XML description for the GRASS cmd.
431 
432  The DTD must be located in $GISBASE/etc/grass-interface.dtd,
433  otherwise the parser will not succeed.
434 
435  @param cmd command (name of GRASS module)
436  """
437  try:
438  if sys.platform == 'win32' and os.path.splitext(cmd)[1] == '.py':
439  os.chdir(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'scripts'))
440  args = [sys.executable, cmd, '--interface-description']
441  else:
442  args = [cmd, '--interface-description']
443 
444  p = Popen(args, stdout = PIPE, stderr = PIPE)
445  cmdout, cmderr = p.communicate()
446  if p.returncode != 0:
447  raise ScriptError, _("Unable to fetch interface description for command '%(cmd)s'."
448  "\n\nDetails: %(det)s") % { 'cmd' : cmd, 'det' : decode(cmderr) }
449  except OSError, e:
450  raise ScriptError, _("Unable to fetch interface description for command '%(cmd)s'."
451  "\n\nDetails: %(det)s") % { 'cmd' : cmd, 'det' : e }
452 
453  # if cmderr and cmderr[:7] != 'WARNING':
454  # raise ScriptError, _("Unable to fetch interface description for command '%(cmd)s'."
455  # "\n\nDetails: %(det)s") % { 'cmd': cmd, 'det' : decode(cmderr) }
456 
457  return cmdout.replace('grass-interface.dtd', os.path.join(os.getenv('GISBASE'), 'etc', 'grass-interface.dtd'))
458 
459 def parse_interface(name, parser = processTask, blackList = None):
460  """!Parse interface of given GRASS module
461 
462  @param name name of GRASS module to be parsed
463  """
464  enc = locale.getdefaultlocale()[1]
465  if enc and enc.lower() == "cp932":
466  p = re.compile('encoding="' + enc + '"', re.IGNORECASE)
467  tree = etree.fromstring(p.sub('encoding="utf-8"',
468  get_interface_description(name).decode(enc).encode("utf-8")))
469  else:
470  tree = etree.fromstring(get_interface_description(name))
471 
472  return parser(tree, blackList = blackList).get_task()
473 
474 def command_info(cmd):
475  """!Returns meta information for any GRASS command as dictionary
476  with entries for description, keywords, usage, flags, and
477  parameters, e.g.
478 
479  @code
480  >>> gtask.command_info('g.tempfile')
481 
482  {'keywords': ['general', 'map management'],
483  'params': [{'gisprompt': False, 'multiple': False, 'name': 'pid', 'guidependency': '',
484  'default': '', 'age': None, 'required': True, 'value': '',
485  'label': '', 'guisection': '', 'key_desc': [], 'values': [], 'values_desc': [],
486  'prompt': None, 'hidden': False, 'element': None, 'type': 'integer',
487  'description': 'Process id to use when naming the tempfile'}],
488  'flags': [{'description': 'Verbose module output', 'value': False, 'label': '', 'guisection': '',
489  'suppress_required': False, 'hidden': False, 'name': 'verbose'}, {'description': 'Quiet module output',
490  'value': False, 'label': '', 'guisection': '', 'suppress_required': False, 'hidden': False, 'name': 'quiet'}],
491  'description': 'Creates a temporary file and prints the file name.',
492  'usage': 'g.tempfile pid=integer [--verbose] [--quiet]'
493  }
494 
495  >>> gtask.command_info('v.buffer')['keywords']
496 
497  ['vector', 'geometry', 'buffer']
498  @endcode
499  """
500  task = parse_interface(cmd)
501  cmdinfo = {}
502 
503  cmdinfo['description'] = task.get_description()
504  cmdinfo['keywords'] = task.get_keywords()
505  cmdinfo['flags'] = flags = task.get_options()['flags']
506  cmdinfo['params'] = params = task.get_options()['params']
507 
508  usage = task.get_name()
509  flags_short = list()
510  flags_long = list()
511  for f in flags:
512  fname = f.get('name', 'unknown')
513  if len(fname) > 1:
514  flags_long.append(fname)
515  else:
516  flags_short.append(fname)
517 
518  if len(flags_short) > 1:
519  usage += ' [-' + ''.join(flags_short) + ']'
520 
521  for p in params:
522  ptype = ','.join(p.get('key_desc', []))
523  if not ptype:
524  ptype = p.get('type', '')
525  req = p.get('required', False)
526  if not req:
527  usage += ' ['
528  else:
529  usage += ' '
530  usage += p['name'] + '=' + ptype
531  if p.get('multiple', False):
532  usage += '[,' + ptype + ',...]'
533  if not req:
534  usage += ']'
535 
536  for key in flags_long:
537  usage += ' [--' + key + ']'
538 
539  cmdinfo['usage'] = usage
540 
541  return cmdinfo
def decode
Definition: core.py:80
def define_first
Define first parameter.
Definition: task.py:74
def get_param
Find and return a param by name.
Definition: task.py:134
def _process_flags
Process flags.
Definition: task.py:391
def get_options
Get options.
Definition: task.py:238
def set_options
Set flags and parameters.
Definition: task.py:273
def get_description
Get module&#39;s description.
Definition: task.py:94
A ElementTree handler for the –interface-description output, as defined in grass-interface.dtd.
Definition: task.py:284
def get_cmd
Produce an array of command name and arguments for feeding into some execve-like command processor...
Definition: task.py:198
def get_cmd_error
Get error string produced by get_cmd(ignoreErrors = False)
Definition: task.py:176
def split
Platform spefic shlex.split.
Definition: core/utils.py:37
def command_info
Returns meta information for any GRASS command as dictionary with entries for description, keywords, usage, flags, and parameters, e.g.
Definition: task.py:474
def _process_module
Process module description.
Definition: task.py:310
This class holds the structures needed for filling by the parser.
Definition: task.py:35
def get_interface_description
Returns the XML description for the GRASS cmd.
Definition: task.py:429
def get_task
Get grassTask instance.
Definition: task.py:425
def get_name
Get task name.
Definition: task.py:89
def get_keywords
Get module&#39;s keywords.
Definition: task.py:107
def _process_params
Process parameters.
Definition: task.py:322
def get_list_flags
Get list of flags.
Definition: task.py:123
def has_required
Check if command has at least one required paramater.
Definition: task.py:244
def parse_interface
Parse interface of given GRASS module.
Definition: task.py:459
def set_flag
Enable / disable flag.
Definition: task.py:263
def get_error_msg
Get error message (&#39;&#39; for no error)
Definition: task.py:84
def get_flag
Find and return a flag by name.
Definition: task.py:164
def parser
Interface to g.parser, intended to be run from the top-level, e.g.
Definition: core.py:428
def set_param
Set param value/values.
Definition: task.py:253
def _get_node_text
Get node text.
Definition: task.py:417
def get_list_params
Get list of parameters.
Definition: task.py:112