parameter.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Tue Apr 2 18:31:47 2013
  4. @author: pietro
  5. """
  6. from __future__ import (
  7. nested_scopes,
  8. generators,
  9. division,
  10. absolute_import,
  11. with_statement,
  12. print_function,
  13. unicode_literals,
  14. )
  15. import re
  16. from grass.pygrass.modules.interface.docstring import docstring_property
  17. from grass.pygrass.modules.interface.read import GETTYPE, element2dict, DOC
  18. def _check_value(param, value):
  19. """Function to check the correctness of a value and
  20. return the checked value and the original.
  21. """
  22. must_val = "The Parameter <%s>, must be one of the following values: %r"
  23. req = "The Parameter <%s>, require: %s, get: %s instead: %r\n%s"
  24. string = (type(b""), type(""))
  25. def raiseexcpet(exc, param, ptype, value):
  26. """Function to modifa the error message"""
  27. msg = req % (param.name, param.typedesc, ptype, value, str(exc))
  28. if isinstance(exc, ValueError):
  29. raise ValueError(msg)
  30. elif isinstance(exc, TypeError):
  31. raise TypeError(msg)
  32. else:
  33. exc.message = msg
  34. raise exc
  35. def check_string(value):
  36. """Function to check that a string parameter is already a string"""
  37. if param.type in string:
  38. if type(value) in (int, float):
  39. value = str(value)
  40. if type(value) not in string:
  41. msg = (
  42. "The Parameter <%s> require a string," " %s instead is provided: %r"
  43. )
  44. raise ValueError(msg % (param.name, type(value), value))
  45. return value
  46. # return None if None
  47. if value is None:
  48. return param.default, param.default
  49. # find errors with multiple parmeters
  50. if isinstance(value, (list, tuple)):
  51. if param.keydescvalues:
  52. return (
  53. (
  54. [
  55. value,
  56. ],
  57. value,
  58. )
  59. if isinstance(value, tuple)
  60. else (value, value)
  61. )
  62. if param.multiple:
  63. # everything looks fine, so check each value
  64. try:
  65. return [param.type(check_string(val)) for val in value], value
  66. except Exception as exc:
  67. raiseexcpet(exc, param, param.type, value)
  68. else:
  69. msg = "The Parameter <%s> does not accept multiple inputs"
  70. raise TypeError(msg % param.name)
  71. if param.keydescvalues:
  72. msg = "The Parameter <%s> require multiple inputs in the form: %s"
  73. raise TypeError(msg % (param.name, param.keydescvalues))
  74. if param.typedesc == "all":
  75. return value, value
  76. # check string before trying to convert value to the correct type
  77. check_string(value)
  78. # the value is a scalar
  79. try:
  80. newvalue = param.type(value)
  81. except Exception as exc:
  82. raiseexcpet(exc, param, type(value), value)
  83. # check values
  84. if hasattr(param, "values"):
  85. if param.type in (float, int):
  86. # check for value in range
  87. if (param.min is not None and newvalue < param.min) or (
  88. param.max is not None and newvalue > param.max
  89. ):
  90. err_str = (
  91. "The Parameter <%s>, must be between: "
  92. "%g<=value<=%g, %r is outside."
  93. )
  94. raise ValueError(err_str % (param.name, param.min, param.max, newvalue))
  95. # check if value is in the list of valid values
  96. if param.values is not None and newvalue not in param.values:
  97. raise ValueError(must_val % (param.name, param.values))
  98. return (
  99. (
  100. [
  101. newvalue,
  102. ]
  103. if (param.multiple or param.keydescvalues)
  104. else newvalue
  105. ),
  106. value,
  107. )
  108. # TODO add documentation
  109. class Parameter(object):
  110. """The Parameter object store all information about a parameter of a
  111. GRASS GIS module. ::
  112. >>> param = Parameter(diz=dict(name='int_number', required='yes',
  113. ... multiple='no', type='integer',
  114. ... values=[2, 4, 6, 8]))
  115. >>> param.value = 2
  116. >>> param.value
  117. 2
  118. >>> param.value = 3
  119. Traceback (most recent call last):
  120. ...
  121. ValueError: The Parameter <int_number>, must be one of the following values: [2, 4, 6, 8]
  122. ...
  123. """
  124. def __init__(self, xparameter=None, diz=None):
  125. self._value = None
  126. self._rawvalue = None
  127. self.min = None
  128. self.max = None
  129. diz = element2dict(xparameter) if xparameter is not None else diz
  130. if diz is None:
  131. raise TypeError("Xparameter or diz are required")
  132. self.name = diz["name"]
  133. self.required = True if diz["required"] == "yes" else False
  134. self.multiple = True if diz["multiple"] == "yes" else False
  135. # check the type
  136. if diz["type"] in GETTYPE:
  137. self.type = GETTYPE[diz["type"]]
  138. self.typedesc = diz["type"]
  139. else:
  140. raise TypeError("New type: %s, ignored" % diz["type"])
  141. self.description = diz.get("description", None)
  142. self.keydesc, self.keydescvalues = diz.get("keydesc", (None, None))
  143. #
  144. # values
  145. #
  146. if "values" in diz:
  147. try:
  148. # Check for integer ranges: "3-30" or float ranges: "0.0-1.0"
  149. isrange = re.match(
  150. "(?P<min>-*\d+.*\d*)*-(?P<max>\d+.*\d*)*", diz["values"][0]
  151. )
  152. if isrange:
  153. mn, mx = isrange.groups()
  154. self.min = None if mn is None else float(mn)
  155. self.max = None if mx is None else float(mx)
  156. self.values = None
  157. self.isrange = diz["values"][0]
  158. # No range was found
  159. else:
  160. self.values = [self.type(i) for i in diz["values"]]
  161. self.isrange = False
  162. except TypeError:
  163. self.values = [self.type(i) for i in diz["values"]]
  164. self.isrange = False
  165. #
  166. # default
  167. #
  168. if "default" in diz and diz["default"]:
  169. if self.multiple or self.keydescvalues:
  170. self.default = [self.type(v) for v in diz["default"].split(",")]
  171. else:
  172. self.default = self.type(diz["default"])
  173. else:
  174. self.default = None
  175. self._value, self._rawvalue = self.default, self.default
  176. self.guisection = diz.get("guisection", None)
  177. #
  178. # gisprompt
  179. #
  180. if "gisprompt" in diz and diz["gisprompt"]:
  181. self.typedesc = diz["gisprompt"].get("prompt", "")
  182. self.input = False if diz["gisprompt"]["age"] == "new" else True
  183. else:
  184. self.input = True
  185. def _get_value(self):
  186. return self._value
  187. def _set_value(self, value):
  188. self._value, self._rawvalue = _check_value(self, value)
  189. # here the property function is used to transform value in an attribute
  190. # in this case we define which function must be use to get/set the value
  191. value = property(
  192. fget=_get_value,
  193. fset=_set_value,
  194. doc="Parameter value transformed and validated.",
  195. )
  196. @property
  197. def rawvalue(self):
  198. """Parameter value as insert by user without transformation"""
  199. return self._rawvalue
  200. def get_bash(self):
  201. """Return the BASH representation of the parameter. ::
  202. >>> param = Parameter(diz=dict(name='int_number', required='yes',
  203. ... multiple='no', type='integer',
  204. ... values=[2, 4, 6, 8], default=8))
  205. >>> param.get_bash()
  206. 'int_number=8'
  207. ..
  208. """
  209. sep = ","
  210. if isinstance(self.rawvalue, (list, tuple)):
  211. value = sep.join(
  212. [
  213. sep.join([str(v) for v in val])
  214. if isinstance(val, tuple)
  215. else str(val)
  216. for val in self.rawvalue
  217. ]
  218. )
  219. else:
  220. value = str(self.rawvalue)
  221. return "%s=%s" % (self.name, value)
  222. def get_python(self):
  223. """Return a string with the Python representation of the parameter. ::
  224. >>> param = Parameter(diz=dict(name='int_number', required='yes',
  225. ... multiple='no', type='integer',
  226. ... values=[2, 4, 6, 8], default=8))
  227. >>> param.get_python()
  228. 'int_number=8'
  229. ..
  230. """
  231. if self.value is None:
  232. return ""
  233. return """%s=%r""" % (self.name, self.value)
  234. def __str__(self):
  235. """Return the BASH representation of the GRASS module parameter."""
  236. return self.get_bash()
  237. def __repr__(self):
  238. """Return the python representation of the GRASS module parameter."""
  239. str_repr = "Parameter <%s> (required:%s, type:%s, multiple:%s)"
  240. mtype = ("raster", "vector") # map type
  241. return str_repr % (
  242. self.name,
  243. "yes" if self.required else "no",
  244. self.type if self.type in mtype else self.typedesc,
  245. "yes" if self.multiple else "no",
  246. )
  247. @docstring_property(__doc__)
  248. def __doc__(self):
  249. """Return the docstring of the parameter
  250. {name}: {default}{required}{multi}{ptype}
  251. {description}{values}"",
  252. ::
  253. >>> param = Parameter(diz=dict(name='int_number',
  254. ... description="Set an number",
  255. ... required='yes',
  256. ... multiple='no', type='integer',
  257. ... values=[2, 4, 6, 8], default=8))
  258. >>> print(param.__doc__)
  259. int_number: 8, required, integer
  260. Set an number
  261. Values: 2, 4, 6, 8
  262. ..
  263. """
  264. if hasattr(self, "values"):
  265. if self.isrange:
  266. vals = self.isrange
  267. else:
  268. vals = ", ".join([repr(val) for val in self.values])
  269. else:
  270. vals = False
  271. if self.keydescvalues:
  272. keydescvals = "\n (%s)" % ", ".join(self.keydescvalues)
  273. return DOC["param"].format(
  274. name=self.name,
  275. default=repr(self.default) + ", " if self.default else "",
  276. required="required, " if self.required else "optional, ",
  277. multi="multi" if self.multiple else "",
  278. ptype=self.typedesc,
  279. description=self.description,
  280. values="\n Values: {0}".format(vals) if vals else "",
  281. keydescvalues=keydescvals if self.keydescvalues else "",
  282. )