module.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Tue Apr 2 18:41:27 2013
  4. @author: pietro
  5. """
  6. from __future__ import print_function
  7. import subprocess
  8. from itertools import izip_longest
  9. from xml.etree.ElementTree import fromstring
  10. from grass.pygrass.errors import GrassError, ParameterError
  11. from parameter import Parameter
  12. from flag import Flag
  13. from typedict import TypeDict
  14. from read import GETFROMTAG, DOC
  15. class Module(object):
  16. """
  17. Python allow developers to not specify all the arguments and
  18. keyword arguments of a method or function.
  19. ::
  20. def f(*args):
  21. for arg in args:
  22. print arg
  23. therefore if we call the function like: ::
  24. >>> f('grass', 'gis', 'modules')
  25. grass
  26. gis
  27. modules
  28. or we can define a new list: ::
  29. >>> words = ['grass', 'gis', 'modules']
  30. >>> f(*words)
  31. grass
  32. gis
  33. modules
  34. we can do the same with keyword arguments, rewrite the above function: ::
  35. def f(*args, **kargs):
  36. for arg in args:
  37. print arg
  38. for key, value in kargs.items():
  39. print "%s = %r" % (key, value)
  40. now we can use the new function, with: ::
  41. >>> f('grass', 'gis', 'modules', os = 'linux', language = 'python')
  42. grass
  43. gis
  44. modules
  45. os = 'linux'
  46. language = 'python'
  47. or, as before we can, define a dictionary and give the dictionary to
  48. the function, like: ::
  49. >>> keywords = {'os' : 'linux', 'language' : 'python'}
  50. >>> f(*words, **keywords)
  51. grass
  52. gis
  53. modules
  54. os = 'linux'
  55. language = 'python'
  56. In the Module class we heavily use this language feature to pass arguments
  57. and keyword arguments to the grass module.
  58. """
  59. def __init__(self, cmd, *args, **kargs):
  60. self.name = cmd
  61. try:
  62. # call the command with --interface-description
  63. get_cmd_xml = subprocess.Popen([cmd, "--interface-description"],
  64. stdout=subprocess.PIPE)
  65. except OSError:
  66. str_err = "Module %r not found, please check that the module exist"
  67. raise GrassError(str_err % self.name)
  68. # get the xml of the module
  69. self.xml = get_cmd_xml.communicate()[0]
  70. # transform and parse the xml into an Element class:
  71. # http://docs.python.org/library/xml.etree.elementtree.html
  72. tree = fromstring(self.xml)
  73. for e in tree:
  74. if e.tag not in ('parameter', 'flag'):
  75. self.__setattr__(e.tag, GETFROMTAG[e.tag](e))
  76. #
  77. # extract parameters from the xml
  78. #
  79. self.params_list = [Parameter(p) for p in tree.findall("parameter")]
  80. self.inputs = TypeDict(Parameter)
  81. self.outputs = TypeDict(Parameter)
  82. self.required = []
  83. # Insert parameters into input/output and required
  84. for par in self.params_list:
  85. if par.input:
  86. self.inputs[par.name] = par
  87. else:
  88. self.outputs[par.name] = par
  89. if par.required:
  90. self.required.append(par)
  91. #
  92. # extract flags from the xml
  93. #
  94. flags_list = [Flag(f) for f in tree.findall("flag")]
  95. self.flags = TypeDict(Flag)
  96. for flag in flags_list:
  97. self.flags[flag.name] = flag
  98. #
  99. # Add new attributes to the class
  100. #
  101. self.run_ = True
  102. self.finish_ = True
  103. self.env_ = None
  104. self.stdin_ = None
  105. self.stdin = None
  106. self.stdout_ = None
  107. self.stderr_ = None
  108. diz = {'name': 'stdin', 'required': False,
  109. 'multiple': False, 'type': 'all',
  110. 'value': None}
  111. self.inputs['stdin'] = Parameter(diz=diz)
  112. diz['name'] = 'stdout'
  113. self.outputs['stdout'] = Parameter(diz=diz)
  114. diz['name'] = 'stderr'
  115. self.outputs['stderr'] = Parameter(diz=diz)
  116. self.popen = None
  117. if args or kargs:
  118. self.__call__(*args, **kargs)
  119. def __call__(self, *args, **kargs):
  120. if not args and not kargs:
  121. self.run()
  122. return
  123. #
  124. # check for extra kargs, set attribute and remove from dictionary
  125. #
  126. if 'flags' in kargs:
  127. for flg in kargs['flags']:
  128. self.flags[flg].value = True
  129. del(kargs['flags'])
  130. if 'run_' in kargs:
  131. self.run_ = kargs['run_']
  132. del(kargs['run_'])
  133. if 'stdin_' in kargs:
  134. self.inputs['stdin'].value = kargs['stdin_']
  135. del(kargs['stdin_'])
  136. if 'stdout_' in kargs:
  137. self.outputs['stdout'].value = kargs['stdout_']
  138. del(kargs['stdout_'])
  139. if 'stderr_' in kargs:
  140. self.outputs['stderr'].value = kargs['stderr_']
  141. del(kargs['stderr_'])
  142. if 'env_' in kargs:
  143. self.env_ = kargs['env_']
  144. del(kargs['env_'])
  145. if 'finish_' in kargs:
  146. self.finish_ = kargs['finish_']
  147. del(kargs['finish_'])
  148. #
  149. # check args
  150. #
  151. for param, arg in zip(self.params_list, args):
  152. param.value = arg
  153. for key, val in kargs.items():
  154. if key in self.inputs:
  155. self.inputs[key].value = val
  156. elif key in self.outputs:
  157. self.outputs[key].value = val
  158. elif key in self.flags:
  159. # we need to add this, because some parameters (overwrite,
  160. # verbose and quiet) work like parameters
  161. self.flags[key].value = val
  162. else:
  163. raise ParameterError('%s is not a valid parameter.' % key)
  164. #
  165. # check reqire parameters
  166. #
  167. for par in self.required:
  168. if par.value is None:
  169. raise ParameterError(
  170. "Required parameter <%s> not set." % par.name)
  171. #
  172. # check if execute
  173. #
  174. if self.run_:
  175. self.run()
  176. def get_bash(self):
  177. return ' '.join(self.make_cmd())
  178. def get_python(self):
  179. prefix = self.name.split('.')[0]
  180. name = '_'.join(self.name.split('.')[1:])
  181. params = ', '.join([par.get_python() for par in self.params_list
  182. if par.get_python() != ''])
  183. special = ', '.join([flg.get_python()
  184. for flg in self.flags.values()
  185. if flg.special and flg.get_python() != ''])
  186. # pre name par flg special
  187. if self.flags and special:
  188. return "%s.%s(%s, flags=%r, %s)" % (prefix, name, params,
  189. self.flags, special)
  190. elif self.flags:
  191. return "%s.%s(%s, flags=%r)" % (prefix, name, params, self.flags)
  192. elif special:
  193. return "%s.%s(%s, %s)" % (prefix, name, params, special)
  194. else:
  195. return "%s.%s(%s)" % (prefix, name, params)
  196. def __str__(self):
  197. return ' '.join(self.make_cmd())
  198. def __repr__(self):
  199. return "Module(%r)" % self.name
  200. @property
  201. def __doc__(self):
  202. """{cmd_name}({cmd_params})
  203. """
  204. head = DOC['head'].format(cmd_name=self.name,
  205. cmd_params=('\n' + # go to a new line
  206. # give space under the function name
  207. (' ' * (len(self.name) + 1))).join([', '.join(
  208. # transform each parameter in string
  209. [str(param) for param in line if param is not None])
  210. # make a list of parameters with only 3 param per line
  211. for line in izip_longest(*[iter(self.params_list)] * 3)]),)
  212. params = '\n'.join([par.__doc__ for par in self.params_list])
  213. flags = self.flags.__doc__
  214. return '\n'.join([head, params, DOC['flag_head'], flags])
  215. def get_dict(self):
  216. dic = {}
  217. dic['name'] = self.name
  218. dic['inputs'] = [(k, v.value) for k, v in self.inputs.items()
  219. if v.value]
  220. dic['outputs'] = [(k, v.value) for k, v in self.outputs.items()
  221. if v.value]
  222. dic['flags'] = [flg for flg in self.flags if self.flags[flg].value]
  223. return dic
  224. def make_cmd(self):
  225. args = [self.name, ]
  226. for par in self.params_list:
  227. if par.value is not None:
  228. args.append(str(par))
  229. for flg in self.flags:
  230. if self.flags[flg].value:
  231. args.append(str(self.flags[flg]))
  232. return args
  233. def run(self, node=None):
  234. if self.inputs['stdin'].value:
  235. self.stdin = self.inputs['stdin'].value
  236. self.stdin_ = subprocess.PIPE
  237. if self.outputs['stdout'].value:
  238. self.stdout_ = self.outputs['stdout'].value
  239. if self.outputs['stderr'].value:
  240. self.stderr_ = self.outputs['stderr'].value
  241. cmd = self.make_cmd()
  242. self.popen = subprocess.Popen(cmd,
  243. stdin=self.stdin_,
  244. stdout=self.stdout_,
  245. stderr=self.stderr_,
  246. env=self.env_)
  247. if self.finish_:
  248. self.popen.wait()
  249. stdout, stderr = self.popen.communicate(input=self.stdin)
  250. self.outputs['stdout'].value = stdout if stdout else ''
  251. self.outputs['stderr'].value = stderr if stderr else ''