diff --git a/CHANGELOG b/CHANGELOG index 42ef362aef..c53e831cd7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add noeval on PYSONDecoder * Add __repr__ to PYSON * Remove safe_eval * Add ModelView.view_attributes diff --git a/doc/ref/pyson.rst b/doc/ref/pyson.rst index fd1640be50..0f3b3a2ca0 100644 --- a/doc/ref/pyson.rst +++ b/doc/ref/pyson.rst @@ -50,15 +50,15 @@ Instance method: Returns a string representation of a given PYSON statement. ``object`` contains a PYSON statement. -.. class:: PYSONDecoder() +.. class:: PYSONDecoder([context[, noeval]]) -Decoder for string into PYSON statement representation. +Decoder for string into the evaluated or not PYSON statement. Instance method: .. method:: PYSONDecoder.decode(object) - Returns a PYSON statement representation of a given string. + Returns a PYSON statement evaluated or not of a given string. ``object`` contains a string. Statements diff --git a/trytond/pyson.py b/trytond/pyson.py index 72f565a6e7..c455588eaf 100644 --- a/trytond/pyson.py +++ b/trytond/pyson.py @@ -121,24 +121,30 @@ class PYSONEncoder(json.JSONEncoder): class PYSONDecoder(json.JSONDecoder): - def __init__(self, context=None): + def __init__(self, context=None, noeval=False): self.__context = context or {} + self.noeval = noeval super(PYSONDecoder, self).__init__(object_hook=self._object_hook) def _object_hook(self, dct): if '__class__' in dct: - klass = globals().get(dct['__class__']) - if klass and hasattr(klass, 'eval'): - return klass.eval(dct, self.__context) + klass = CONTEXT.get(dct['__class__']) + if klass: + if not self.noeval: + return klass.eval(dct, self.__context) + else: + dct = dct.copy() + del dct['__class__'] + return klass(**dct) return dct class Eval(PYSON): - def __init__(self, value, default=''): + def __init__(self, v, d=''): super(Eval, self).__init__() - self._value = value - self._default = default + self._value = v + self._default = d @property def __repr_params__(self): @@ -165,13 +171,13 @@ class Eval(PYSON): class Not(PYSON): - def __init__(self, value): + def __init__(self, v): super(Not, self).__init__() - if isinstance(value, PYSON): - assert value.types() == set([bool]), 'value must be boolean' + if isinstance(v, PYSON): + assert v.types() == set([bool]), 'value must be boolean' else: - assert isinstance(value, bool), 'value must be boolean' - self._value = value + assert isinstance(v, bool), 'value must be boolean' + self._value = v @property def __repr_params__(self): @@ -193,9 +199,9 @@ class Not(PYSON): class Bool(PYSON): - def __init__(self, value): + def __init__(self, v): super(Bool, self).__init__() - self._value = value + self._value = v @property def __repr_params__(self): @@ -217,8 +223,9 @@ class Bool(PYSON): class And(PYSON): - def __init__(self, *statements): + def __init__(self, *statements, **kwargs): super(And, self).__init__() + statements = list(statements) + kwargs.get('s', []) for statement in statements: if isinstance(statement, PYSON): assert statement.types() == set([bool]), \ @@ -227,7 +234,7 @@ class And(PYSON): assert isinstance(statement, bool), \ 'statement must be boolean' assert len(statements) >= 2, 'must have at least 2 statements' - self._statements = list(statements) + self._statements = statements @property def __repr_params__(self): @@ -261,16 +268,17 @@ class Or(And): class Equal(PYSON): - def __init__(self, statement1, statement2): + def __init__(self, s1, s2): + statement1, statement2 = s1, s2 super(Equal, self).__init__() if isinstance(statement1, PYSON): types1 = statement1.types() else: - types1 = reduced_type(set([type(statement1)])) + types1 = reduced_type(set([type(s1)])) if isinstance(statement2, PYSON): types2 = statement2.types() else: - types2 = reduced_type(set([type(statement2)])) + types2 = reduced_type(set([type(s2)])) assert types1 == types2, 'statements must have the same type' self._statement1 = statement1 self._statement2 = statement2 @@ -296,7 +304,8 @@ class Equal(PYSON): class Greater(PYSON): - def __init__(self, statement1, statement2, equal=False): + def __init__(self, s1, s2, e=False): + statement1, statement2, equal = s1, s2, e super(Greater, self).__init__() for i in (statement1, statement2): if isinstance(i, PYSON): @@ -363,7 +372,8 @@ class Less(Greater): class If(PYSON): - def __init__(self, condition, then_statement, else_statement=None): + def __init__(self, c, t, e=None): + condition, then_statement, else_statement = c, t, e super(If, self).__init__() if isinstance(condition, PYSON): assert condition.types() == set([bool]), \ @@ -413,7 +423,8 @@ class If(PYSON): class Get(PYSON): - def __init__(self, obj, key, default=''): + def __init__(self, v, k, d=''): + obj, key, default = v, k, d super(Get, self).__init__() if isinstance(obj, PYSON): assert obj.types() == set([dict]), 'obj must be a dict' @@ -453,7 +464,8 @@ class Get(PYSON): class In(PYSON): - def __init__(self, key, obj): + def __init__(self, k, v): + key, obj = k, v super(In, self).__init__() if isinstance(key, PYSON): assert key.types().issubset(set([basestring, int])), \ @@ -495,7 +507,13 @@ class In(PYSON): class Date(PYSON): def __init__(self, year=None, month=None, day=None, - delta_years=0, delta_months=0, delta_days=0): + delta_years=0, delta_months=0, delta_days=0, **kwargs): + year = kwargs.get('y', year) + month = kwargs.get('M', month) + day = kwargs.get('d', day) + delta_years = kwargs.get('dy', delta_years) + delta_months = kwargs.get('dM', delta_months) + delta_days = kwargs.get('dd', delta_days) super(Date, self).__init__() for i in (year, month, day, delta_years, delta_months, delta_days): if isinstance(i, PYSON): @@ -548,10 +566,18 @@ class DateTime(Date): hour=None, minute=None, second=None, microsecond=None, delta_years=0, delta_months=0, delta_days=0, delta_hours=0, delta_minutes=0, delta_seconds=0, - delta_microseconds=0): + delta_microseconds=0, **kwargs): + hour = kwargs.get('h', hour) + minute = kwargs.get('m', minute) + second = kwargs.get('s', second) + microsecond = kwargs.get('ms', microsecond) + delta_hours = kwargs.get('dh', delta_hours) + delta_minutes = kwargs.get('dm', delta_minutes) + delta_seconds = kwargs.get('ds', delta_seconds) + delta_microseconds = kwargs.get('dms', delta_microseconds) super(DateTime, self).__init__(year=year, month=month, day=day, delta_years=delta_years, delta_months=delta_months, - delta_days=delta_days) + delta_days=delta_days, **kwargs) for i in (hour, minute, second, microsecond, delta_hours, delta_minutes, delta_seconds, delta_microseconds): if isinstance(i, PYSON): @@ -616,15 +642,15 @@ class DateTime(Date): class Len(PYSON): - def __init__(self, value): + def __init__(self, v): super(Len, self).__init__() - if isinstance(value, PYSON): - assert value.types().issubset(set([dict, list, basestring])), \ + if isinstance(v, PYSON): + assert v.types().issubset(set([dict, list, basestring])), \ 'value must be a dict or a list or a string' else: - assert isinstance(value, (dict, list, basestring)), \ + assert isinstance(v, (dict, list, basestring)), \ 'value must be a dict or list or a string' - self._value = value + self._value = v @property def __repr_params__(self): diff --git a/trytond/tests/test_pyson.py b/trytond/tests/test_pyson.py index 53ce057102..5cf65c2a35 100644 --- a/trytond/tests/test_pyson.py +++ b/trytond/tests/test_pyson.py @@ -569,6 +569,29 @@ class PYSONTestCase(unittest.TestCase): self.assertEqual(repr(expr), "If(Not(In('company', Eval('context', {}))), '=', '!=')") + def test_noeval(self): + decoder = pyson.PYSONDecoder(noeval=True) + encoder = pyson.PYSONEncoder() + + for instance in [ + pyson.Eval('test', 0), + pyson.Not(True), + pyson.Bool('test'), + pyson.And(True, False, True), + pyson.Or(False, True, True), + pyson.Equal('foo', 'bar'), + pyson.Greater(1, 0), + pyson.Less(0, 1), + pyson.If(True, 'foo', 'bar'), + pyson.Get({'foo': 'bar'}, 'foo', 'default'), + pyson.In('foo', ['foo', 'bar']), + pyson.Date(), + pyson.DateTime(), + pyson.Len([1, 2, 3]), + ]: + self.assertEqual(decoder.decode(encoder.encode(instance)).pyson(), + instance.pyson()) + def suite(): return unittest.TestLoader().loadTestsFromTestCase(PYSONTestCase)