Coverage for stricto/generic.py: 100%
193 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-29 10:26 +0100
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-29 10:26 +0100
1"""
2Module providing the Generic() Class
3This class must not be used directly
4"""
5import copy
6from .error import Error, ErrorType
9class GenericType: # pylint: disable=too-many-instance-attributes
10 """
11 A generic type (class for int, string, etc)
12 """
14 def __init__(self, **kwargs):
15 """
16 available arguments
18 description : A tring to describe the object
19 default : The default value
20 notNone : (boolean) must be required or not
22 """
23 self.root = None
24 self.parent = None
25 self.attribute_name = ""
26 self._value = None
27 self._old_value = None
28 self._descrition = kwargs.pop("description", None)
29 self._not_none = kwargs.pop("notNone", kwargs.pop("required", False))
30 self._union = kwargs.pop("union", kwargs.pop("in", None))
31 self.currently_doing_autoset = False
32 constraint = kwargs.pop("constraint", kwargs.pop("constraints", []))
33 self._constraints = constraint if isinstance(constraint, list) else [constraint]
36 self._default = kwargs.pop("default", None)
37 self.check(self._default)
38 self._value = self._default
39 self._old_value = self._value
41 # transformation of the value before setting
42 self._transform = kwargs.pop("transform", None)
43 # the value is computed
44 self._auto_set = kwargs.pop("set", kwargs.pop("compute", None))
45 # on change trigger
46 self._on_change = kwargs.pop("onChange", kwargs.pop("onchange", None))
47 # this object exist
48 self._exists = kwargs.pop("exists", kwargs.pop("existsIf", True))
51 def set_hierachy_attributs(self, root, parent, name):
52 """
53 set the attribute root, parent and name for the current objecte related to the all structure
54 """
55 self.root = root
56 self.parent = parent
57 self.attribute_name = name
59 def am_i_root(self):
60 """
61 Check if this object is the root object
62 """
63 if self.root == self:
64 return True
65 return False
67 def exists(self):
68 """
69 Return True if the object Exist, othewise False.
70 exist can be a function to make this field dependant from the value of another
71 """
72 return self.get_args_or_execute_them(self._exists, self._value)
74 def path_name(self):
75 """
76 return a string with the name of the object
77 """
78 p = []
79 parent = self
80 while parent is not None:
81 p.insert(0, parent.attribute_name)
82 parent = parent.parent
83 return ".".join(p)
85 def get_other_value(self, other):
86 """
87 return the value of the other object if GenericType
88 """
89 if isinstance(other, GenericType):
90 return other.get_value()
91 return other
93 def __add__(self, other):
94 """
95 add two objects
96 """
97 b = self._value + self.get_other_value(other)
98 r = self.__copy__()
100 r.set(b)
101 return r
103 def __sub__(self, other):
104 """
105 sub two objects
106 """
107 b = self._value - self.get_other_value(other)
108 r = self.__copy__()
109 r.set(b)
110 return r
112 def __mul__(self, other):
113 """
114 mul two objects
115 """
116 b = self._value * self.get_other_value(other)
117 r = self.__copy__()
118 r.set(b)
119 return r
121 def __truediv__(self, other):
122 """
123 div two objects
124 """
125 b = self._value / self.get_other_value(other)
126 r = self.__copy__()
127 r.set(b)
128 return r
130 def __floordiv__(self, other):
131 """
132 floordiv two objects
133 """
134 b = self._value // self.get_other_value(other)
135 r = self.__copy__()
136 r.set(b)
137 return r
139 def __pow__(self, other):
140 """
141 pow two objects
142 """
143 b = self._value ** self.get_other_value(other)
144 r = self.__copy__()
145 r.set(b)
146 return r
148 def __mod__(self, other):
149 """
150 mod two objects
151 """
152 b = self._value % self.get_other_value(other)
153 r = self.__copy__()
154 r.set(b)
155 return r
157 def __rshift__(self, other):
158 """
159 __rshift__ two objects
160 """
161 b = self._value >> self.get_other_value(other)
162 r = self.__copy__()
163 r.set(b)
164 return r
166 def __lshift__(self, other):
167 """
168 __lshift__ two objects
169 """
170 b = self._value << self.get_other_value(other)
171 r = self.__copy__()
172 r.set(b)
173 return r
175 def __and__(self, other):
176 """
177 __and__ two objects
178 """
179 b = self._value & self.get_other_value(other)
180 r = self.__copy__()
181 r.set(b)
182 return r
184 def __or__(self, other):
185 """
186 __or__ two objects
187 """
188 b = self._value | self.get_other_value(other)
189 r = self.__copy__()
190 r.set(b)
191 return r
193 def __xor__(self, other):
194 """
195 __xor__ two objects
196 """
197 b = self._value ^ self.get_other_value(other)
198 r = self.__copy__()
199 r.set(b)
200 return r
202 def __eq__(self, other):
203 """
204 equality test two objects
205 """
206 return self._value == self.get_other_value(other)
208 def __ne__(self, other):
209 """
210 ne test two objects
211 """
212 return self._value != self.get_other_value(other)
214 def __lt__(self, other):
216 """
217 lt test two objects
218 """
219 return self._value < self.get_other_value(other)
221 def __le__(self, other):
223 """
224 le test two objects
225 """
226 return self._value <= self.get_other_value(other)
228 def __gt__(self, other):
230 """
231 gt test two objects
232 """
233 return self._value > self.get_other_value(other)
235 def __ge__(self, other):
237 """
238 ge test two objects
239 """
240 return self._value >= self.get_other_value(other)
242 def __copy__(self):
243 cls = self.__class__
244 result = cls.__new__(cls)
245 result.__dict__.update(self.__dict__)
246 return result
248 def copy(self):
249 """
250 call copy
251 """
252 return copy.copy(self)
254 def set(self, value):
255 """
256 Fill with a value or raise an Error if not valid
257 """
259 if self.exists() is False:
260 raise Error(ErrorType.NOTALIST, "locked", self.path_name())
262 if self._auto_set is not None:
263 if self.root.currently_doing_autoset is False:
264 raise Error(ErrorType.READONLY, "Cannot modify value", self.path_name())
266 corrected_value = (
267 value.get_value() if type(value) == type(self) else value # pylint: disable=unidiomatic-typecheck
268 )
269 if callable(self._transform):
270 corrected_value = self._transform(corrected_value, self.root)
272 self.check(corrected_value)
273 return self.set_value_without_checks(corrected_value)
275 def set_value_without_checks(self, value):
276 """
277 return True if some changement, otherwise False
278 """
280 if self.exists() is False:
281 raise Error(ErrorType.NOTALIST, "locked", self.path_name())
283 if self._auto_set is not None:
284 if self.root.currently_doing_autoset is False:
285 raise Error(ErrorType.READONLY, "Cannot modify value", self.path_name())
287 # transform the value before the check
288 corrected_value = (
289 value.get_value() if type(value) == type(self) else value # pylint: disable=unidiomatic-typecheck
290 )
292 self._old_value = self._value
293 self._value = self._default if corrected_value is None else corrected_value
295 if self._old_value == self._value:
296 return False
298 if callable(self._on_change):
299 self._on_change(self._old_value, value, self.root)
300 if self.root is not None:
301 self.root.auto_set()
302 return True
304 def auto_set(self):
305 """
306 compute automatically a value because another value as changed somewhere.
307 (related to set=flag)
308 """
309 if callable(self._auto_set) is False:
310 return
311 new_value = self._auto_set(self.root)
312 self.set_value_without_checks(new_value)
314 def get_value(self):
315 """
316 get the value
317 """
318 return self._value
320 def __repr__(self):
321 return self._value.__repr__()
323 def check(self, value):
324 """
325 check if complain to model or return an Error
326 """
328 # transform the value before the check
329 corrected_value = value
330 if callable(self._transform):
331 corrected_value = self._transform(value, self.root)
333 # handle the None value
334 if corrected_value is None:
335 if self._not_none is True:
336 raise Error(ErrorType.NULL, "Cannot be empty", self.path_name())
337 return True
339 # Check correct type or raise an Error
340 self.check_type(corrected_value)
342 # check constraints or raise an Error
343 self.check_constraints(corrected_value)
345 return True
347 def __getattr__(self, k):
348 """
349 replicate all atributes from value, but prefere self attribute first.
350 """
351 #if k in self.__dict__:
352 # return self.__dict__[k]
353 if hasattr(self._value, k):
354 return getattr(self._value, k)
355 return None
357 def check_type(self, value): # pylint: disable=unused-argument
358 """
359 Check if the type is correct.
360 must be overwritten
361 """
362 # return True
364 def check_constraints(self, value):
365 """
366 Check all constraints
367 """
368 # Union constraint
369 if self._union:
370 l = self.get_args_or_execute_them(self._union, value)
371 if not isinstance(l, list):
372 raise Error(
373 ErrorType.UNION, "Union constraint not list", self.path_name()
374 )
375 if value not in l:
376 raise Error(ErrorType.UNION, "not in list", self.path_name())
378 # ---- constraints as functions
379 for constraint in self._constraints:
380 if callable(constraint) is not True:
381 raise Error(
382 ErrorType.NOTCALLABLE, "constraint not callable", self.path_name()
383 )
384 r = constraint(value, self.root)
385 if r is False:
386 raise Error(
387 ErrorType.CONSTRAINT, "constraint not validated", self.path_name()
388 )
389 return True
391 def get_args_or_execute_them(self, arg, value):
392 """
393 get element from an argument, or if it is callable
394 execute the arg as a function to retreive the information
395 example :
396 min = 12 -> return 12
397 min = computeMin -> return computeMin( value )
398 """
399 if callable(arg):
400 return arg(value, self.root)
401 return arg