proteus 7.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- proteus/__init__.py +1421 -0
- proteus/config.py +423 -0
- proteus/pyson.py +771 -0
- proteus/tests/__init__.py +9 -0
- proteus/tests/common.py +17 -0
- proteus/tests/test_action.py +49 -0
- proteus/tests/test_config.py +40 -0
- proteus/tests/test_context.py +16 -0
- proteus/tests/test_model.py +406 -0
- proteus/tests/test_readme.py +23 -0
- proteus/tests/test_report.py +29 -0
- proteus/tests/test_wizard.py +54 -0
- proteus-7.8.0.dist-info/METADATA +223 -0
- proteus-7.8.0.dist-info/RECORD +18 -0
- proteus-7.8.0.dist-info/WHEEL +5 -0
- proteus-7.8.0.dist-info/licenses/LICENSE +841 -0
- proteus-7.8.0.dist-info/top_level.txt +1 -0
- proteus-7.8.0.dist-info/zip-safe +1 -0
proteus/pyson.py
ADDED
|
@@ -0,0 +1,771 @@
|
|
|
1
|
+
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
2
|
+
# this repository contains the full copyright notices and license terms.
|
|
3
|
+
import datetime
|
|
4
|
+
import json
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from functools import reduce
|
|
7
|
+
|
|
8
|
+
from dateutil.relativedelta import relativedelta
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PYSON(object):
|
|
12
|
+
|
|
13
|
+
def pyson(self):
|
|
14
|
+
raise NotImplementedError
|
|
15
|
+
|
|
16
|
+
def types(self):
|
|
17
|
+
raise NotImplementedError
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def eval(dct, context):
|
|
21
|
+
raise NotImplementedError
|
|
22
|
+
|
|
23
|
+
def __invert__(self):
|
|
24
|
+
if self.types() != {bool}:
|
|
25
|
+
return Not(Bool(self))
|
|
26
|
+
else:
|
|
27
|
+
return Not(self)
|
|
28
|
+
|
|
29
|
+
def __and__(self, other):
|
|
30
|
+
if (isinstance(other, PYSON)
|
|
31
|
+
and other.types() != {bool}):
|
|
32
|
+
other = Bool(other)
|
|
33
|
+
if (isinstance(self, And)
|
|
34
|
+
and not isinstance(self, Or)):
|
|
35
|
+
return And(*self._statements, other)
|
|
36
|
+
if self.types() != {bool}:
|
|
37
|
+
return And(Bool(self), other)
|
|
38
|
+
else:
|
|
39
|
+
return And(self, other)
|
|
40
|
+
|
|
41
|
+
__rand__ = __and__
|
|
42
|
+
|
|
43
|
+
def __or__(self, other):
|
|
44
|
+
if (isinstance(other, PYSON)
|
|
45
|
+
and other.types() != {bool}):
|
|
46
|
+
other = Bool(other)
|
|
47
|
+
if isinstance(self, Or):
|
|
48
|
+
return Or(*self._statements, other)
|
|
49
|
+
if self.types() != {bool}:
|
|
50
|
+
return Or(Bool(self), other)
|
|
51
|
+
else:
|
|
52
|
+
return Or(self, other)
|
|
53
|
+
|
|
54
|
+
__ror__ = __or__
|
|
55
|
+
|
|
56
|
+
def __eq__(self, other):
|
|
57
|
+
return Equal(self, other)
|
|
58
|
+
|
|
59
|
+
def __ne__(self, other):
|
|
60
|
+
return Not(Equal(self, other))
|
|
61
|
+
|
|
62
|
+
def __gt__(self, other):
|
|
63
|
+
return Greater(self, other)
|
|
64
|
+
|
|
65
|
+
def __ge__(self, other):
|
|
66
|
+
return Greater(self, other, True)
|
|
67
|
+
|
|
68
|
+
def __lt__(self, other):
|
|
69
|
+
return Less(self, other)
|
|
70
|
+
|
|
71
|
+
def __le__(self, other):
|
|
72
|
+
return Less(self, other, True)
|
|
73
|
+
|
|
74
|
+
def get(self, k, d=''):
|
|
75
|
+
return Get(self, k, d)
|
|
76
|
+
|
|
77
|
+
def in_(self, obj):
|
|
78
|
+
return In(self, obj)
|
|
79
|
+
|
|
80
|
+
def contains(self, k):
|
|
81
|
+
return In(k, self)
|
|
82
|
+
|
|
83
|
+
def __repr__(self):
|
|
84
|
+
klass = self.__class__.__name__
|
|
85
|
+
return '%s(%s)' % (klass, ', '.join(map(repr, self.__repr_params__)))
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def __repr_params__(self):
|
|
89
|
+
return NotImplementedError
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class PYSONEncoder(json.JSONEncoder):
|
|
93
|
+
|
|
94
|
+
def default(self, obj):
|
|
95
|
+
if isinstance(obj, PYSON):
|
|
96
|
+
return obj.pyson()
|
|
97
|
+
elif isinstance(obj, datetime.date):
|
|
98
|
+
if isinstance(obj, datetime.datetime):
|
|
99
|
+
return DateTime(obj.year, obj.month, obj.day,
|
|
100
|
+
obj.hour, obj.minute, obj.second, obj.microsecond
|
|
101
|
+
).pyson()
|
|
102
|
+
else:
|
|
103
|
+
return Date(obj.year, obj.month, obj.day).pyson()
|
|
104
|
+
elif isinstance(obj, datetime.timedelta):
|
|
105
|
+
return TimeDelta(obj.days, obj.seconds, obj.microseconds)
|
|
106
|
+
elif isinstance(obj, Decimal):
|
|
107
|
+
return float(obj)
|
|
108
|
+
return super().default(obj)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class PYSONDecoder(json.JSONDecoder):
|
|
112
|
+
|
|
113
|
+
def __init__(self, context=None, noeval=False):
|
|
114
|
+
self.__context = context or {}
|
|
115
|
+
self.noeval = noeval
|
|
116
|
+
super().__init__(object_hook=self._object_hook)
|
|
117
|
+
|
|
118
|
+
def _object_hook(self, dct):
|
|
119
|
+
if '__class__' in dct:
|
|
120
|
+
klass = CONTEXT.get(dct['__class__'])
|
|
121
|
+
if klass:
|
|
122
|
+
if not self.noeval:
|
|
123
|
+
return klass.eval(dct, self.__context)
|
|
124
|
+
else:
|
|
125
|
+
dct = dct.copy()
|
|
126
|
+
del dct['__class__']
|
|
127
|
+
return klass(**dct)
|
|
128
|
+
return dct
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class Eval(PYSON):
|
|
132
|
+
|
|
133
|
+
def __init__(self, v, d=''):
|
|
134
|
+
super().__init__()
|
|
135
|
+
self._value = v
|
|
136
|
+
self._default = d
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def __repr_params__(self):
|
|
140
|
+
return self._value, self._default
|
|
141
|
+
|
|
142
|
+
def pyson(self):
|
|
143
|
+
return {
|
|
144
|
+
'__class__': 'Eval',
|
|
145
|
+
'v': self._value,
|
|
146
|
+
'd': self._default,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
def types(self):
|
|
150
|
+
if isinstance(self._default, PYSON):
|
|
151
|
+
return self._default.types()
|
|
152
|
+
else:
|
|
153
|
+
return {type(self._default)}
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
def eval(dct, context):
|
|
157
|
+
if '.' in dct['v'] and dct['v'] not in context:
|
|
158
|
+
base, name = dct['v'].split('.', 1)
|
|
159
|
+
return Eval.eval({
|
|
160
|
+
'v': name,
|
|
161
|
+
'd': dct['d'],
|
|
162
|
+
}, context.get(base) or {})
|
|
163
|
+
return context.get(dct['v'], dct['d'])
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def basename(self):
|
|
167
|
+
name = self._value
|
|
168
|
+
if name.startswith('_parent_'):
|
|
169
|
+
name = name[len('_parent_'):]
|
|
170
|
+
if '.' in name:
|
|
171
|
+
name = name.split('.', 1)[0]
|
|
172
|
+
return name
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class Not(PYSON):
|
|
176
|
+
|
|
177
|
+
def __init__(self, v):
|
|
178
|
+
super().__init__()
|
|
179
|
+
if isinstance(v, PYSON):
|
|
180
|
+
if v.types() != {bool}:
|
|
181
|
+
v = Bool(v)
|
|
182
|
+
elif not isinstance(v, bool):
|
|
183
|
+
v = bool(v)
|
|
184
|
+
self._value = v
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def __repr_params__(self):
|
|
188
|
+
return (self._value,)
|
|
189
|
+
|
|
190
|
+
def pyson(self):
|
|
191
|
+
return {
|
|
192
|
+
'__class__': 'Not',
|
|
193
|
+
'v': self._value,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
def types(self):
|
|
197
|
+
return {bool}
|
|
198
|
+
|
|
199
|
+
@staticmethod
|
|
200
|
+
def eval(dct, context):
|
|
201
|
+
return not dct['v']
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class Bool(PYSON):
|
|
205
|
+
|
|
206
|
+
def __init__(self, v):
|
|
207
|
+
super().__init__()
|
|
208
|
+
self._value = v
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def __repr_params__(self):
|
|
212
|
+
return (self._value,)
|
|
213
|
+
|
|
214
|
+
def pyson(self):
|
|
215
|
+
return {
|
|
216
|
+
'__class__': 'Bool',
|
|
217
|
+
'v': self._value,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
def types(self):
|
|
221
|
+
return {bool}
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def eval(dct, context):
|
|
225
|
+
return bool(dct['v'])
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class And(PYSON):
|
|
229
|
+
|
|
230
|
+
def __init__(self, *statements, **kwargs):
|
|
231
|
+
super().__init__()
|
|
232
|
+
statements = list(statements) + kwargs.get('s', [])
|
|
233
|
+
for i, statement in enumerate(list(statements)):
|
|
234
|
+
if isinstance(statement, PYSON):
|
|
235
|
+
if statement.types() != {bool}:
|
|
236
|
+
statements[i] = Bool(statement)
|
|
237
|
+
elif not isinstance(statement, bool):
|
|
238
|
+
statements[i] = bool(statement)
|
|
239
|
+
assert len(statements) >= 2, 'must have at least 2 statements'
|
|
240
|
+
self._statements = statements
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def __repr_params__(self):
|
|
244
|
+
return tuple(self._statements)
|
|
245
|
+
|
|
246
|
+
def pyson(self):
|
|
247
|
+
return {
|
|
248
|
+
'__class__': 'And',
|
|
249
|
+
's': self._statements,
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
def types(self):
|
|
253
|
+
return {bool}
|
|
254
|
+
|
|
255
|
+
@staticmethod
|
|
256
|
+
def eval(dct, context):
|
|
257
|
+
return bool(reduce(lambda x, y: x and y, dct['s']))
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class Or(And):
|
|
261
|
+
|
|
262
|
+
def pyson(self):
|
|
263
|
+
res = super().pyson()
|
|
264
|
+
res['__class__'] = 'Or'
|
|
265
|
+
return res
|
|
266
|
+
|
|
267
|
+
@staticmethod
|
|
268
|
+
def eval(dct, context):
|
|
269
|
+
return bool(reduce(lambda x, y: x or y, dct['s']))
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class Equal(PYSON):
|
|
273
|
+
|
|
274
|
+
def __init__(self, s1, s2):
|
|
275
|
+
statement1, statement2 = s1, s2
|
|
276
|
+
super().__init__()
|
|
277
|
+
if isinstance(statement1, PYSON):
|
|
278
|
+
types1 = statement1.types()
|
|
279
|
+
else:
|
|
280
|
+
types1 = {type(s1)}
|
|
281
|
+
if isinstance(statement2, PYSON):
|
|
282
|
+
types2 = statement2.types()
|
|
283
|
+
else:
|
|
284
|
+
types2 = {type(s2)}
|
|
285
|
+
assert types1 == types2, 'statements must have the same type'
|
|
286
|
+
self._statement1 = statement1
|
|
287
|
+
self._statement2 = statement2
|
|
288
|
+
|
|
289
|
+
@property
|
|
290
|
+
def __repr_params__(self):
|
|
291
|
+
return (self._statement1, self._statement2)
|
|
292
|
+
|
|
293
|
+
def pyson(self):
|
|
294
|
+
return {
|
|
295
|
+
'__class__': 'Equal',
|
|
296
|
+
's1': self._statement1,
|
|
297
|
+
's2': self._statement2,
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
def types(self):
|
|
301
|
+
return {bool}
|
|
302
|
+
|
|
303
|
+
@staticmethod
|
|
304
|
+
def eval(dct, context):
|
|
305
|
+
return dct['s1'] == dct['s2']
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class Greater(PYSON):
|
|
309
|
+
|
|
310
|
+
def __init__(self, s1, s2, e=False):
|
|
311
|
+
statement1, statement2, equal = s1, s2, e
|
|
312
|
+
super().__init__()
|
|
313
|
+
for i in (statement1, statement2):
|
|
314
|
+
if isinstance(i, PYSON):
|
|
315
|
+
assert i.types().issubset({
|
|
316
|
+
int, float, type(None),
|
|
317
|
+
datetime.datetime, datetime.date,
|
|
318
|
+
datetime.timedelta}), \
|
|
319
|
+
'statement must be an integer, float, date or datetime'
|
|
320
|
+
else:
|
|
321
|
+
assert isinstance(i, (
|
|
322
|
+
int, float, type(None),
|
|
323
|
+
datetime.datetime, datetime.date,
|
|
324
|
+
datetime.timedelta)), \
|
|
325
|
+
'statement must be an integer, float, date or datetime'
|
|
326
|
+
if isinstance(equal, PYSON):
|
|
327
|
+
if equal.types() != {bool}:
|
|
328
|
+
equal = Bool(equal)
|
|
329
|
+
elif not isinstance(equal, bool):
|
|
330
|
+
equal = bool(equal)
|
|
331
|
+
self._statement1 = statement1
|
|
332
|
+
self._statement2 = statement2
|
|
333
|
+
self._equal = equal
|
|
334
|
+
|
|
335
|
+
@property
|
|
336
|
+
def __repr_params__(self):
|
|
337
|
+
return (self._statement1, self._statement2, self._equal)
|
|
338
|
+
|
|
339
|
+
def pyson(self):
|
|
340
|
+
return {
|
|
341
|
+
'__class__': 'Greater',
|
|
342
|
+
's1': self._statement1,
|
|
343
|
+
's2': self._statement2,
|
|
344
|
+
'e': self._equal,
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
def types(self):
|
|
348
|
+
return {bool}
|
|
349
|
+
|
|
350
|
+
@staticmethod
|
|
351
|
+
def _convert(dct):
|
|
352
|
+
for i in ('s1', 's2'):
|
|
353
|
+
if dct[i] is None:
|
|
354
|
+
dct[i] = 0.0
|
|
355
|
+
if not isinstance(dct[i], (int, float)):
|
|
356
|
+
dct = dct.copy()
|
|
357
|
+
stmt = dct[i]
|
|
358
|
+
if isinstance(stmt, datetime.datetime):
|
|
359
|
+
stmt = stmt.timestamp()
|
|
360
|
+
elif isinstance(stmt, datetime.date):
|
|
361
|
+
time = datetime.time(0, 0)
|
|
362
|
+
stmt = datetime.datetime.combine(stmt, time).timestamp()
|
|
363
|
+
elif isinstance(stmt, datetime.timedelta):
|
|
364
|
+
stmt = stmt.total_seconds()
|
|
365
|
+
dct[i] = float(stmt)
|
|
366
|
+
return dct
|
|
367
|
+
|
|
368
|
+
@staticmethod
|
|
369
|
+
def eval(dct, context):
|
|
370
|
+
if dct['s1'] is None or dct['s2'] is None:
|
|
371
|
+
return False
|
|
372
|
+
dct = Greater._convert(dct)
|
|
373
|
+
if dct['e']:
|
|
374
|
+
return dct['s1'] >= dct['s2']
|
|
375
|
+
else:
|
|
376
|
+
return dct['s1'] > dct['s2']
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
class Less(Greater):
|
|
380
|
+
|
|
381
|
+
def pyson(self):
|
|
382
|
+
res = super().pyson()
|
|
383
|
+
res['__class__'] = 'Less'
|
|
384
|
+
return res
|
|
385
|
+
|
|
386
|
+
@staticmethod
|
|
387
|
+
def eval(dct, context):
|
|
388
|
+
if dct['s1'] is None or dct['s2'] is None:
|
|
389
|
+
return False
|
|
390
|
+
dct = Less._convert(dct)
|
|
391
|
+
if dct['e']:
|
|
392
|
+
return dct['s1'] <= dct['s2']
|
|
393
|
+
else:
|
|
394
|
+
return dct['s1'] < dct['s2']
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
class If(PYSON):
|
|
398
|
+
|
|
399
|
+
def __init__(self, c, t, e=None):
|
|
400
|
+
condition, then_statement, else_statement = c, t, e
|
|
401
|
+
super().__init__()
|
|
402
|
+
if isinstance(condition, PYSON):
|
|
403
|
+
if condition.types() != {bool}:
|
|
404
|
+
condition = Bool(condition)
|
|
405
|
+
elif not isinstance(condition, bool):
|
|
406
|
+
condition = bool(condition)
|
|
407
|
+
self._condition = condition
|
|
408
|
+
self._then_statement = then_statement
|
|
409
|
+
self._else_statement = else_statement
|
|
410
|
+
|
|
411
|
+
@property
|
|
412
|
+
def __repr_params__(self):
|
|
413
|
+
return (self._condition, self._then_statement, self._else_statement)
|
|
414
|
+
|
|
415
|
+
def pyson(self):
|
|
416
|
+
return {
|
|
417
|
+
'__class__': 'If',
|
|
418
|
+
'c': self._condition,
|
|
419
|
+
't': self._then_statement,
|
|
420
|
+
'e': self._else_statement,
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
def types(self):
|
|
424
|
+
if isinstance(self._then_statement, PYSON):
|
|
425
|
+
types = self._then_statement.types()
|
|
426
|
+
else:
|
|
427
|
+
types = {type(self._then_statement)}
|
|
428
|
+
if isinstance(self._else_statement, PYSON):
|
|
429
|
+
types |= self._else_statement.types()
|
|
430
|
+
else:
|
|
431
|
+
types |= {type(self._else_statement)}
|
|
432
|
+
return types
|
|
433
|
+
|
|
434
|
+
@staticmethod
|
|
435
|
+
def eval(dct, context):
|
|
436
|
+
if dct['c']:
|
|
437
|
+
return dct['t']
|
|
438
|
+
else:
|
|
439
|
+
return dct['e']
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
class Get(PYSON):
|
|
443
|
+
|
|
444
|
+
def __init__(self, v, k, d=''):
|
|
445
|
+
obj, key, default = v, k, d
|
|
446
|
+
super().__init__()
|
|
447
|
+
if isinstance(obj, PYSON):
|
|
448
|
+
assert obj.types() == {dict}, 'obj must be a dict'
|
|
449
|
+
else:
|
|
450
|
+
assert isinstance(obj, dict), 'obj must be a dict'
|
|
451
|
+
self._obj = obj
|
|
452
|
+
if isinstance(key, PYSON):
|
|
453
|
+
assert key.types() == {str}, 'key must be a string'
|
|
454
|
+
else:
|
|
455
|
+
assert isinstance(key, str), 'key must be a string'
|
|
456
|
+
self._key = key
|
|
457
|
+
self._default = default
|
|
458
|
+
|
|
459
|
+
@property
|
|
460
|
+
def __repr_params__(self):
|
|
461
|
+
return (self._obj, self._key, self._default)
|
|
462
|
+
|
|
463
|
+
def pyson(self):
|
|
464
|
+
return {
|
|
465
|
+
'__class__': 'Get',
|
|
466
|
+
'v': self._obj,
|
|
467
|
+
'k': self._key,
|
|
468
|
+
'd': self._default,
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
def types(self):
|
|
472
|
+
if isinstance(self._default, PYSON):
|
|
473
|
+
return self._default.types()
|
|
474
|
+
else:
|
|
475
|
+
return {type(self._default)}
|
|
476
|
+
|
|
477
|
+
@staticmethod
|
|
478
|
+
def eval(dct, context):
|
|
479
|
+
return dct['v'].get(dct['k'], dct['d'])
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
class In(PYSON):
|
|
483
|
+
|
|
484
|
+
def __init__(self, k, v):
|
|
485
|
+
key, obj = k, v
|
|
486
|
+
super().__init__()
|
|
487
|
+
if isinstance(key, PYSON):
|
|
488
|
+
assert key.types().issubset({str, int}), \
|
|
489
|
+
'key must be a string or an integer or a long'
|
|
490
|
+
else:
|
|
491
|
+
assert isinstance(key, (str, int)), \
|
|
492
|
+
'key must be a string or an integer or a long'
|
|
493
|
+
if isinstance(obj, PYSON):
|
|
494
|
+
assert obj.types().issubset({dict, list}), \
|
|
495
|
+
'obj must be a dict or a list'
|
|
496
|
+
if obj.types() == {dict}:
|
|
497
|
+
if isinstance(key, PYSON):
|
|
498
|
+
assert key.types() == {str}, 'key must be a string'
|
|
499
|
+
else:
|
|
500
|
+
assert isinstance(key, str), 'key must be a string'
|
|
501
|
+
else:
|
|
502
|
+
assert isinstance(obj, (dict, list))
|
|
503
|
+
if isinstance(obj, dict):
|
|
504
|
+
if isinstance(key, PYSON):
|
|
505
|
+
assert key.types() == {str}, 'key must be a string'
|
|
506
|
+
else:
|
|
507
|
+
assert isinstance(key, str), 'key must be a string'
|
|
508
|
+
self._key = key
|
|
509
|
+
self._obj = obj
|
|
510
|
+
|
|
511
|
+
@property
|
|
512
|
+
def __repr_params__(self):
|
|
513
|
+
return (self._key, self._obj)
|
|
514
|
+
|
|
515
|
+
def pyson(self):
|
|
516
|
+
return {
|
|
517
|
+
'__class__': 'In',
|
|
518
|
+
'k': self._key,
|
|
519
|
+
'v': self._obj,
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
def types(self):
|
|
523
|
+
return {bool}
|
|
524
|
+
|
|
525
|
+
@staticmethod
|
|
526
|
+
def eval(dct, context):
|
|
527
|
+
if dct['v']:
|
|
528
|
+
return dct['k'] in dct['v']
|
|
529
|
+
else:
|
|
530
|
+
return False
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
class Date(PYSON):
|
|
534
|
+
|
|
535
|
+
def __init__(self, year=None, month=None, day=None,
|
|
536
|
+
delta_years=0, delta_months=0, delta_days=0, start=None, **kwargs):
|
|
537
|
+
year = kwargs.get('y', year)
|
|
538
|
+
month = kwargs.get('M', month)
|
|
539
|
+
day = kwargs.get('d', day)
|
|
540
|
+
delta_years = kwargs.get('dy', delta_years)
|
|
541
|
+
delta_months = kwargs.get('dM', delta_months)
|
|
542
|
+
delta_days = kwargs.get('dd', delta_days)
|
|
543
|
+
super().__init__()
|
|
544
|
+
for i in (year, month, day, delta_years, delta_months, delta_days):
|
|
545
|
+
if isinstance(i, PYSON):
|
|
546
|
+
assert i.types().issubset({int, type(None)}), \
|
|
547
|
+
'%s must be an integer or None' % (i,)
|
|
548
|
+
else:
|
|
549
|
+
assert isinstance(i, (int, type(None))), \
|
|
550
|
+
'%s must be an integer or None' % (i,)
|
|
551
|
+
self._year = year
|
|
552
|
+
self._month = month
|
|
553
|
+
self._day = day
|
|
554
|
+
self._delta_years = delta_years
|
|
555
|
+
self._delta_months = delta_months
|
|
556
|
+
self._delta_days = delta_days
|
|
557
|
+
self._start = start
|
|
558
|
+
|
|
559
|
+
@property
|
|
560
|
+
def __repr_params__(self):
|
|
561
|
+
return (self._year, self._month, self._day,
|
|
562
|
+
self._delta_years, self._delta_months, self._delta_days,
|
|
563
|
+
self._start)
|
|
564
|
+
|
|
565
|
+
def pyson(self):
|
|
566
|
+
return {
|
|
567
|
+
'__class__': 'Date',
|
|
568
|
+
'y': self._year,
|
|
569
|
+
'M': self._month,
|
|
570
|
+
'd': self._day,
|
|
571
|
+
'dy': self._delta_years,
|
|
572
|
+
'dM': self._delta_months,
|
|
573
|
+
'dd': self._delta_days,
|
|
574
|
+
'start': self._start,
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
def types(self):
|
|
578
|
+
return {datetime.date}
|
|
579
|
+
|
|
580
|
+
@staticmethod
|
|
581
|
+
def eval(dct, context):
|
|
582
|
+
today = dct.get('start')
|
|
583
|
+
if isinstance(today, datetime.datetime):
|
|
584
|
+
today = today.date()
|
|
585
|
+
if not isinstance(today, datetime.date):
|
|
586
|
+
today = datetime.date.today()
|
|
587
|
+
return today + relativedelta(
|
|
588
|
+
year=dct['y'],
|
|
589
|
+
month=dct['M'],
|
|
590
|
+
day=dct['d'],
|
|
591
|
+
years=dct['dy'],
|
|
592
|
+
months=dct['dM'],
|
|
593
|
+
days=dct['dd'],
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
class DateTime(Date):
|
|
598
|
+
|
|
599
|
+
def __init__(self, year=None, month=None, day=None,
|
|
600
|
+
hour=None, minute=None, second=None, microsecond=None,
|
|
601
|
+
delta_years=0, delta_months=0, delta_days=0,
|
|
602
|
+
delta_hours=0, delta_minutes=0, delta_seconds=0,
|
|
603
|
+
delta_microseconds=0, start=None, **kwargs):
|
|
604
|
+
hour = kwargs.get('h', hour)
|
|
605
|
+
minute = kwargs.get('m', minute)
|
|
606
|
+
second = kwargs.get('s', second)
|
|
607
|
+
microsecond = kwargs.get('ms', microsecond)
|
|
608
|
+
delta_hours = kwargs.get('dh', delta_hours)
|
|
609
|
+
delta_minutes = kwargs.get('dm', delta_minutes)
|
|
610
|
+
delta_seconds = kwargs.get('ds', delta_seconds)
|
|
611
|
+
delta_microseconds = kwargs.get('dms', delta_microseconds)
|
|
612
|
+
super().__init__(year=year, month=month, day=day,
|
|
613
|
+
delta_years=delta_years, delta_months=delta_months,
|
|
614
|
+
delta_days=delta_days, start=start, **kwargs)
|
|
615
|
+
for i in (hour, minute, second, microsecond,
|
|
616
|
+
delta_hours, delta_minutes, delta_seconds, delta_microseconds):
|
|
617
|
+
if isinstance(i, PYSON):
|
|
618
|
+
assert i.types() == {int, type(None)}, \
|
|
619
|
+
'%s must be an integer or None' % (i,)
|
|
620
|
+
else:
|
|
621
|
+
assert isinstance(i, (int, type(None))), \
|
|
622
|
+
'%s must be an integer or None' % (i,)
|
|
623
|
+
self._hour = hour
|
|
624
|
+
self._minute = minute
|
|
625
|
+
self._second = second
|
|
626
|
+
self._microsecond = microsecond
|
|
627
|
+
self._delta_hours = delta_hours
|
|
628
|
+
self._delta_minutes = delta_minutes
|
|
629
|
+
self._delta_seconds = delta_seconds
|
|
630
|
+
self._delta_microseconds = delta_microseconds
|
|
631
|
+
|
|
632
|
+
@property
|
|
633
|
+
def __repr_params__(self):
|
|
634
|
+
date_params = super().__repr_params__
|
|
635
|
+
return (date_params[:3]
|
|
636
|
+
+ (self._hour, self._minute, self._second, self._microsecond)
|
|
637
|
+
+ date_params[3:-1]
|
|
638
|
+
+ (self._delta_hours, self._delta_minutes, self._delta_seconds,
|
|
639
|
+
self._delta_microseconds)
|
|
640
|
+
+ date_params[-1:])
|
|
641
|
+
|
|
642
|
+
def pyson(self):
|
|
643
|
+
res = super().pyson()
|
|
644
|
+
res['__class__'] = 'DateTime'
|
|
645
|
+
res['h'] = self._hour
|
|
646
|
+
res['m'] = self._minute
|
|
647
|
+
res['s'] = self._second
|
|
648
|
+
res['ms'] = self._microsecond
|
|
649
|
+
res['dh'] = self._delta_hours
|
|
650
|
+
res['dm'] = self._delta_minutes
|
|
651
|
+
res['ds'] = self._delta_seconds
|
|
652
|
+
res['dms'] = self._delta_microseconds
|
|
653
|
+
return res
|
|
654
|
+
|
|
655
|
+
def types(self):
|
|
656
|
+
return {datetime.datetime}
|
|
657
|
+
|
|
658
|
+
@staticmethod
|
|
659
|
+
def eval(dct, context):
|
|
660
|
+
now = dct.get('start')
|
|
661
|
+
if (isinstance(now, datetime.date)
|
|
662
|
+
and not isinstance(now, datetime.datetime)):
|
|
663
|
+
now = datetime.datetime.combine(now, datetime.time())
|
|
664
|
+
if not isinstance(now, datetime.datetime):
|
|
665
|
+
now = datetime.datetime.now(
|
|
666
|
+
datetime.timezone.utc).replace(tzinfo=None)
|
|
667
|
+
return now + relativedelta(
|
|
668
|
+
year=dct['y'],
|
|
669
|
+
month=dct['M'],
|
|
670
|
+
day=dct['d'],
|
|
671
|
+
hour=dct['h'],
|
|
672
|
+
minute=dct['m'],
|
|
673
|
+
second=dct['s'],
|
|
674
|
+
microsecond=dct['ms'],
|
|
675
|
+
years=dct['dy'],
|
|
676
|
+
months=dct['dM'],
|
|
677
|
+
days=dct['dd'],
|
|
678
|
+
hours=dct['dh'],
|
|
679
|
+
minutes=dct['dm'],
|
|
680
|
+
seconds=dct['ds'],
|
|
681
|
+
microseconds=dct['dms'],
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
class TimeDelta(PYSON):
|
|
686
|
+
|
|
687
|
+
def __init__(self, days=0, seconds=0, microseconds=0):
|
|
688
|
+
for i in [days, seconds, microseconds]:
|
|
689
|
+
if isinstance(i, PYSON):
|
|
690
|
+
assert i.types().issubset({int, float}), \
|
|
691
|
+
'%s must be an integer' % (i,)
|
|
692
|
+
else:
|
|
693
|
+
assert isinstance(i, (int, float)), \
|
|
694
|
+
'%s must be an integer' % (i,)
|
|
695
|
+
self._days = days
|
|
696
|
+
self._seconds = seconds
|
|
697
|
+
self._microseconds = microseconds
|
|
698
|
+
|
|
699
|
+
@property
|
|
700
|
+
def __repr_params__(self):
|
|
701
|
+
return self._days, self._seconds, self._microseconds
|
|
702
|
+
|
|
703
|
+
def pyson(self):
|
|
704
|
+
return {
|
|
705
|
+
'__class__': 'TimeDelta',
|
|
706
|
+
'd': self._days,
|
|
707
|
+
's': self._seconds,
|
|
708
|
+
'm': self._microseconds,
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
def types(self):
|
|
712
|
+
return {datetime.timedelta}
|
|
713
|
+
|
|
714
|
+
@staticmethod
|
|
715
|
+
def eval(dct, context):
|
|
716
|
+
return datetime.timedelta(
|
|
717
|
+
days=dct['d'],
|
|
718
|
+
seconds=dct['s'],
|
|
719
|
+
microseconds=dct['m'],
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
class Len(PYSON):
|
|
724
|
+
|
|
725
|
+
def __init__(self, v):
|
|
726
|
+
super().__init__()
|
|
727
|
+
if isinstance(v, PYSON):
|
|
728
|
+
assert v.types().issubset({dict, list, str}), \
|
|
729
|
+
'value must be a dict or a list or a string'
|
|
730
|
+
else:
|
|
731
|
+
assert isinstance(v, (dict, list, str)), \
|
|
732
|
+
'value must be a dict or list or a string'
|
|
733
|
+
self._value = v
|
|
734
|
+
|
|
735
|
+
@property
|
|
736
|
+
def __repr_params__(self):
|
|
737
|
+
return (self._value,)
|
|
738
|
+
|
|
739
|
+
def pyson(self):
|
|
740
|
+
return {
|
|
741
|
+
'__class__': 'Len',
|
|
742
|
+
'v': self._value,
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
def types(self):
|
|
746
|
+
return {int}
|
|
747
|
+
|
|
748
|
+
@staticmethod
|
|
749
|
+
def eval(dct, context):
|
|
750
|
+
return len(dct['v'])
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
CONTEXT = {
|
|
754
|
+
'Eval': Eval,
|
|
755
|
+
'Not': Not,
|
|
756
|
+
'Bool': Bool,
|
|
757
|
+
'And': And,
|
|
758
|
+
'Or': Or,
|
|
759
|
+
'Equal': Equal,
|
|
760
|
+
'Greater': Greater,
|
|
761
|
+
'Less': Less,
|
|
762
|
+
'If': If,
|
|
763
|
+
'Get': Get,
|
|
764
|
+
'In': In,
|
|
765
|
+
'Date': Date,
|
|
766
|
+
'DateTime': DateTime,
|
|
767
|
+
'TimeDelta': TimeDelta,
|
|
768
|
+
'Len': Len,
|
|
769
|
+
'true': True,
|
|
770
|
+
'false': False,
|
|
771
|
+
}
|