From e985252f2d7d258a265a6a62fb8f8e56f5f8925f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 10 Mar 2015 23:38:33 +0100 Subject: [PATCH] Add __repr__ to PYSON issue4607 review4151002 --- CHANGELOG | 1 + trytond/pyson.py | 66 +++++++++++++++++++++++++++++++++++++ trytond/tests/test_pyson.py | 46 ++++++++++++++++++++++++-- 3 files changed, 111 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e140ace81d..42ef362aef 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add __repr__ to PYSON * Remove safe_eval * Add ModelView.view_attributes * Add pyson attribute on data field tag diff --git a/trytond/pyson.py b/trytond/pyson.py index d1c5c1d163..72f565a6e7 100644 --- a/trytond/pyson.py +++ b/trytond/pyson.py @@ -95,6 +95,14 @@ class PYSON(object): def contains(self, k): return In(k, self) + def __repr__(self): + klass = self.__class__.__name__ + return '%s(%s)' % (klass, ', '.join(map(repr, self.__repr_params__))) + + @property + def __repr_params__(self): + return NotImplementedError + class PYSONEncoder(json.JSONEncoder): @@ -132,6 +140,10 @@ class Eval(PYSON): self._value = value self._default = default + @property + def __repr_params__(self): + return self._value, self._default + def pyson(self): return { '__class__': 'Eval', @@ -161,6 +173,10 @@ class Not(PYSON): assert isinstance(value, bool), 'value must be boolean' self._value = value + @property + def __repr_params__(self): + return (self._value,) + def pyson(self): return { '__class__': 'Not', @@ -181,6 +197,10 @@ class Bool(PYSON): super(Bool, self).__init__() self._value = value + @property + def __repr_params__(self): + return (self._value,) + def pyson(self): return { '__class__': 'Bool', @@ -209,6 +229,10 @@ class And(PYSON): assert len(statements) >= 2, 'must have at least 2 statements' self._statements = list(statements) + @property + def __repr_params__(self): + return tuple(self._statements) + def pyson(self): return { '__class__': 'And', @@ -251,6 +275,10 @@ class Equal(PYSON): self._statement1 = statement1 self._statement2 = statement2 + @property + def __repr_params__(self): + return (self._statement1, self._statement2) + def pyson(self): return { '__class__': 'Equal', @@ -285,6 +313,10 @@ class Greater(PYSON): self._statement2 = statement2 self._equal = equal + @property + def __repr_params__(self): + return (self._statement1, self._statement2, self._equal) + def pyson(self): return { '__class__': 'Greater', @@ -352,6 +384,10 @@ class If(PYSON): self._then_statement = then_statement self._else_statement = else_statement + @property + def __repr_params__(self): + return (self._condition, self._then_statement, self._else_statement) + def pyson(self): return { '__class__': 'If', @@ -391,6 +427,10 @@ class Get(PYSON): self._key = key self._default = default + @property + def __repr_params__(self): + return (self._obj, self._key, self._default) + def pyson(self): return { '__class__': 'Get', @@ -433,6 +473,10 @@ class In(PYSON): self._key = key self._obj = obj + @property + def __repr_params__(self): + return (self._key, self._obj) + def pyson(self): return { '__class__': 'In', @@ -467,6 +511,11 @@ class Date(PYSON): self._delta_months = delta_months self._delta_days = delta_days + @property + def __repr_params__(self): + return (self._year, self._month, self._day, + self._delta_years, self._delta_months, self._delta_days) + def pyson(self): return { '__class__': 'Date', @@ -520,6 +569,15 @@ class DateTime(Date): self._delta_seconds = delta_seconds self._delta_microseconds = delta_microseconds + @property + def __repr_params__(self): + date_params = super(DateTime, self).__repr_params__ + return (date_params[:3] + + (self._hour, self._minute, self._second, self._microsecond) + + date_params[3:] + + (self._delta_hours, self._delta_minutes, self._delta_seconds, + self._delta_microseconds)) + def pyson(self): res = super(DateTime, self).pyson() res['__class__'] = 'DateTime' @@ -568,6 +626,10 @@ class Len(PYSON): 'value must be a dict or list or a string' self._value = value + @property + def __repr_params__(self): + return (self._value,) + def pyson(self): return { '__class__': 'Len', @@ -590,6 +652,10 @@ class Id(PYSON): self._module = module self._fs_id = fs_id + @property + def __repr_params__(self): + return (self._module, self._fs_id) + def pyson(self): from trytond.pool import Pool ModelData = Pool().get('ir.model.data') diff --git a/trytond/tests/test_pyson.py b/trytond/tests/test_pyson.py index de0e411921..53ce057102 100644 --- a/trytond/tests/test_pyson.py +++ b/trytond/tests/test_pyson.py @@ -30,6 +30,9 @@ class PYSONTestCase(unittest.TestCase): self.assertEqual(pyson.PYSONDecoder({'test': 1}).decode(eval), 1) self.assertEqual(pyson.PYSONDecoder().decode(eval), 0) + self.assertEqual(repr(pyson.Eval('test', 'foo')), + "Eval('test', 'foo')") + def test0020Not(self): 'Test pyson.Not' self.assertEqual(pyson.Not(True).pyson(), { @@ -47,6 +50,8 @@ class PYSONTestCase(unittest.TestCase): eval = pyson.PYSONEncoder().encode(pyson.Not(False)) self.assertTrue(pyson.PYSONDecoder().decode(eval)) + self.assertEqual(repr(pyson.Not(True)), 'Not(True)') + def test0030Bool(self): 'Test pyson.Bool' self.assertEqual(pyson.Bool('test').pyson(), { @@ -86,6 +91,8 @@ class PYSONTestCase(unittest.TestCase): eval = pyson.PYSONEncoder().encode(pyson.Bool({})) self.assertFalse(pyson.PYSONDecoder().decode(eval)) + self.assertEqual(repr(pyson.Bool('test')), "Bool('test')") + def test0040And(self): 'Test pyson.And' self.assertEqual(pyson.And(True, False).pyson(), { @@ -128,6 +135,9 @@ class PYSONTestCase(unittest.TestCase): eval = pyson.PYSONEncoder().encode(pyson.And(False, False, True)) self.assertFalse(pyson.PYSONDecoder().decode(eval)) + self.assertEqual(repr(pyson.And(False, True, True)), + 'And(False, True, True)') + def test0050Or(self): 'Test pyson.Or' self.assertEqual(pyson.Or(True, False).pyson(), { @@ -170,6 +180,9 @@ class PYSONTestCase(unittest.TestCase): eval = pyson.PYSONEncoder().encode(pyson.Or(False, False, True)) self.assertTrue(pyson.PYSONDecoder().decode(eval)) + self.assertEqual(repr(pyson.Or(False, True, True)), + 'Or(False, True, True)') + def test0060Equal(self): 'Test pyson.Equal' self.assertEqual(pyson.Equal('test', 'test').pyson(), { @@ -188,6 +201,9 @@ class PYSONTestCase(unittest.TestCase): eval = pyson.PYSONEncoder().encode(pyson.Equal('foo', 'bar')) self.assertFalse(pyson.PYSONDecoder().decode(eval)) + self.assertEqual(repr(pyson.Equal('foo', 'bar')), + "Equal('foo', 'bar')") + def test0070Greater(self): 'Test pyson.Greater' self.assertEqual(pyson.Greater(1, 0).pyson(), { @@ -221,6 +237,8 @@ class PYSONTestCase(unittest.TestCase): eval = pyson.PYSONEncoder().encode(pyson.Greater(1, 1, True)) self.assertTrue(pyson.PYSONDecoder().decode(eval)) + self.assertEqual(repr(pyson.Greater(1, 0)), 'Greater(1, 0, False)') + def test0080Less(self): 'Test pyson.Less' self.assertEqual(pyson.Less(0, 1).pyson(), { @@ -254,6 +272,8 @@ class PYSONTestCase(unittest.TestCase): eval = pyson.PYSONEncoder().encode(pyson.Less(1, 1, True)) self.assertTrue(pyson.PYSONDecoder().decode(eval)) + self.assertEqual(repr(pyson.Less(0, 1)), 'Less(0, 1, False)') + def test0090If(self): 'Test pyson.If' self.assertEqual(pyson.If(True, 'foo', 'bar').pyson(), { @@ -276,6 +296,9 @@ class PYSONTestCase(unittest.TestCase): eval = pyson.PYSONEncoder().encode(pyson.If(False, 'foo', 'bar')) self.assertEqual(pyson.PYSONDecoder().decode(eval), 'bar') + self.assertEqual(repr(pyson.If(True, 'foo', 'bar')), + "If(True, 'foo', 'bar')") + def test0100Get(self): 'Test pyson.Get' self.assertEqual(pyson.Get({'foo': 'bar'}, 'foo', 'default').pyson(), { @@ -304,6 +327,9 @@ class PYSONTestCase(unittest.TestCase): {}, 'foo', 'default')) self.assertEqual(pyson.PYSONDecoder().decode(eval), 'default') + self.assertEqual(repr(pyson.Get({'foo': 'bar'}, 'foo', 'default')), + "Get({'foo': 'bar'}, 'foo', 'default')") + def test0110In(self): 'Test pyson.In' self.assertEqual(pyson.In('foo', {'foo': 'bar'}).pyson(), { @@ -347,6 +373,9 @@ class PYSONTestCase(unittest.TestCase): eval = pyson.PYSONEncoder().encode(pyson.In('test', [])) self.assertFalse(pyson.PYSONDecoder().decode(eval)) + self.assertEqual(repr(pyson.In('foo', ['foo', 'bar'])), + "In('foo', ['foo', 'bar'])") + def test0120Date(self): 'Test pyson.Date' self.assertEqual(pyson.Date(2010, 1, 12, -1, 12, -7).pyson(), { @@ -399,6 +428,9 @@ class PYSONTestCase(unittest.TestCase): self.assertEqual(pyson.PYSONDecoder().decode(eval), datetime.date(2010, 2, 22)) + self.assertEqual(repr(pyson.Date(2010, 1, 12, -1, 12, -7)), + 'Date(2010, 1, 12, -1, 12, -7)') + def test0130DateTime(self): 'Test pyson.DateTime' self.assertEqual(pyson.DateTime(2010, 1, 12, 10, 30, 20, 0, @@ -497,6 +529,10 @@ class PYSONTestCase(unittest.TestCase): self.assertEqual(pyson.PYSONDecoder().decode(eval), datetime.datetime(2010, 2, 22, 10, 30, 20, 200)) + self.assertEqual(repr(pyson.DateTime(2010, 1, 12, 10, 30, 20, 0, + -1, 12, -7, 2, 15, 30, 1)), + 'DateTime(2010, 1, 12, 10, 30, 20, 0, -1, 12, -7, 2, 15, 30, 1)') + def test0140Len(self): 'Test pyson.Len' self.assertEqual(pyson.Len([1, 2, 3]).pyson(), { @@ -517,16 +553,22 @@ class PYSONTestCase(unittest.TestCase): eval = pyson.PYSONEncoder().encode(pyson.Len('foo bar')) self.assertEqual(pyson.PYSONDecoder().decode(eval), 7) + self.assertEqual(repr(pyson.Len([1, 2, 3])), 'Len([1, 2, 3])') + def test0900Composite(self): 'Test Composite' - eval = pyson.PYSONEncoder().encode(['id', pyson.If(pyson.Not( - pyson.In('company', pyson.Eval('context', {}))), '=', '!='), + expr = pyson.If(pyson.Not( + pyson.In('company', pyson.Eval('context', {}))), '=', '!=') + eval = pyson.PYSONEncoder().encode(['id', expr, pyson.Get(pyson.Eval('context', {}), 'company', -1)]) self.assertEqual(pyson.PYSONDecoder({'context': {'company': 1}} ).decode(eval), ['id', '!=', 1]) self.assertEqual(pyson.PYSONDecoder({'context': {}} ).decode(eval), ['id', '=', -1]) + self.assertEqual(repr(expr), + "If(Not(In('company', Eval('context', {}))), '=', '!=')") + def suite(): return unittest.TestLoader().loadTestsFromTestCase(PYSONTestCase)