source: grass/trunk/lib/python/pygrass/modules/interface/parameter.py

Last change on this file was 73229, checked in by annakrat, 6 years ago

experimental GSoC 2018 Python 3 support by Sanjeet Bhatti

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