Improve type checking in PYSON

- convert long type to int
- convert str and unicode to basestring
- use isinstance when possible

issue4405
review11881002
This commit is contained in:
Cédric Krier
2015-02-14 17:52:59 +01:00
parent 2e4d1441ea
commit 08b4dc2dd0
2 changed files with 116 additions and 96 deletions
+39 -20
View File
@@ -6,7 +6,23 @@ except ImportError:
import json
import datetime
from dateutil.relativedelta import relativedelta
from functools import reduce
from functools import reduce, wraps
def reduced_type(types):
types = types.copy()
for k, r in [(long, int), (str, basestring), (unicode, basestring)]:
if k in types:
types.remove(k)
types.add(r)
return types
def reduce_type(func):
@wraps(func)
def wrapper(*args, **kwargs):
return reduced_type(func(*args, **kwargs))
return wrapper
class PYSON(object):
@@ -123,6 +139,7 @@ class Eval(PYSON):
'd': self._default,
}
@reduce_type
def types(self):
if isinstance(self._default, PYSON):
return self._default.types()
@@ -225,11 +242,11 @@ class Equal(PYSON):
if isinstance(statement1, PYSON):
types1 = statement1.types()
else:
types1 = set([type(statement1)])
types1 = reduced_type(set([type(statement1)]))
if isinstance(statement2, PYSON):
types2 = statement2.types()
else:
types2 = set([type(statement2)])
types2 = reduced_type(set([type(statement2)]))
assert types1 == types2, 'statements must have the same type'
self._statement1 = statement1
self._statement2 = statement2
@@ -324,13 +341,13 @@ class If(PYSON):
if isinstance(then_statement, PYSON):
then_types = then_statement.types()
else:
then_types = set([type(then_statement)])
then_types = reduced_type(set([type(then_statement)]))
if isinstance(else_statement, PYSON):
assert then_types == else_statement.types(), \
'then and else statements must be the same type'
else_types = else_statement.types()
else:
assert then_types == set([type(else_statement)]), \
'then and else statements must be the same type'
else_types = reduced_type(set([type(else_statement)]))
assert then_types == else_types, \
'then and else statements must be the same type'
self._condition = condition
self._then_statement = then_statement
self._else_statement = else_statement
@@ -343,6 +360,7 @@ class If(PYSON):
'e': self._else_statement,
}
@reduce_type
def types(self):
if isinstance(self._then_statement, PYSON):
return self._then_statement.types()
@@ -367,9 +385,9 @@ class Get(PYSON):
assert isinstance(obj, dict), 'obj must be a dict'
self._obj = obj
if isinstance(key, PYSON):
assert key.types() == set([str, unicode]), 'key must be a string'
assert key.types() == set([basestring]), 'key must be a string'
else:
assert type(key) in [str, unicode], 'key must be a string'
assert isinstance(key, basestring), 'key must be a string'
self._key = key
self._default = default
@@ -381,6 +399,7 @@ class Get(PYSON):
'd': self._default,
}
@reduce_type
def types(self):
if isinstance(self._default, PYSON):
return self._default.types()
@@ -397,20 +416,20 @@ class In(PYSON):
def __init__(self, key, obj):
super(In, self).__init__()
if isinstance(key, PYSON):
assert key.types().issubset(set([str, unicode, int, long])), \
assert key.types().issubset(set([basestring, int])), \
'key must be a string or an integer or a long'
else:
assert type(key) in [str, unicode, int, long], \
assert isinstance(key, (basestring, int, long)), \
'key must be a string or an integer or a long'
if isinstance(obj, PYSON):
assert obj.types().issubset(set([dict, list])), \
'obj must be a dict or a list'
if obj.types() == set([dict]):
assert type(key) in [str, unicode], 'key must be a string'
assert isinstance(key, basestring), 'key must be a string'
else:
assert type(obj) in [dict, list]
if type(obj) == dict:
assert type(key) in [str, unicode], 'key must be a string'
assert isinstance(obj, (dict, list))
if isinstance(obj, dict):
assert isinstance(key, basestring), 'key must be a string'
self._key = key
self._obj = obj
@@ -487,7 +506,7 @@ class DateTime(Date):
for i in (hour, minute, second, microsecond,
delta_hours, delta_minutes, delta_seconds, delta_microseconds):
if isinstance(i, PYSON):
assert i.types() == set([int, long, type(None)]), \
assert i.types() == set([int, type(None)]), \
'%s must be an integer or None' % (i,)
else:
assert isinstance(i, (int, long, type(None))), \
@@ -542,10 +561,10 @@ class Len(PYSON):
def __init__(self, value):
super(Len, self).__init__()
if isinstance(value, PYSON):
assert value.types().issubset(set([dict, list, str, unicode])), \
assert value.types().issubset(set([dict, list, basestring])), \
'value must be a dict or a list or a string'
else:
assert type(value) in [dict, list, str, unicode], \
assert isinstance(value, (dict, list, basestring)), \
'value must be a dict or list or a string'
self._value = value
@@ -556,7 +575,7 @@ class Len(PYSON):
}
def types(self):
return set([int, long])
return set([int])
@staticmethod
def eval(dct, context):
+77 -76
View File
@@ -12,34 +12,34 @@ class PYSONTestCase(unittest.TestCase):
def test0010Eval(self):
'Test pyson.Eval'
self.assert_(pyson.Eval('test').pyson() == {
self.assertEqual(pyson.Eval('test').pyson(), {
'__class__': 'Eval',
'v': 'test',
'd': '',
})
self.assert_(pyson.Eval('test', 'foo').pyson() == {
self.assertEqual(pyson.Eval('test', 'foo').pyson(), {
'__class__': 'Eval',
'v': 'test',
'd': 'foo',
})
self.assert_(pyson.Eval('test', 'foo').types() == set([type('foo')]))
self.assert_(pyson.Eval('test', 1).types() == set([type(1)]))
self.assertEqual(pyson.Eval('test', 'foo').types(), set([basestring]))
self.assertEqual(pyson.Eval('test', 1).types(), set([int]))
eval = pyson.PYSONEncoder().encode(pyson.Eval('test', 0))
self.assert_(pyson.PYSONDecoder({'test': 1}).decode(eval) == 1)
self.assert_(pyson.PYSONDecoder().decode(eval) == 0)
self.assertEqual(pyson.PYSONDecoder({'test': 1}).decode(eval), 1)
self.assertEqual(pyson.PYSONDecoder().decode(eval), 0)
def test0020Not(self):
'Test pyson.Not'
self.assert_(pyson.Not(True).pyson() == {
self.assertEqual(pyson.Not(True).pyson(), {
'__class__': 'Not',
'v': True,
})
self.assertRaises(AssertionError, pyson.Not, 'foo')
self.assert_(pyson.Not(True).types() == set([bool]))
self.assertEqual(pyson.Not(True).types(), set([bool]))
eval = pyson.PYSONEncoder().encode(pyson.Not(True))
self.assertFalse(pyson.PYSONDecoder().decode(eval))
@@ -49,12 +49,12 @@ class PYSONTestCase(unittest.TestCase):
def test0030Bool(self):
'Test pyson.Bool'
self.assert_(pyson.Bool('test').pyson() == {
self.assertEqual(pyson.Bool('test').pyson(), {
'__class__': 'Bool',
'v': 'test',
})
self.assert_(pyson.Bool('test').types() == set([bool]))
self.assertEqual(pyson.Bool('test').types(), set([bool]))
eval = pyson.PYSONEncoder().encode(pyson.Bool(True))
self.assertTrue(pyson.PYSONDecoder().decode(eval))
@@ -88,7 +88,7 @@ class PYSONTestCase(unittest.TestCase):
def test0040And(self):
'Test pyson.And'
self.assert_(pyson.And(True, False).pyson() == {
self.assertEqual(pyson.And(True, False).pyson(), {
'__class__': 'And',
's': [True, False],
})
@@ -99,7 +99,7 @@ class PYSONTestCase(unittest.TestCase):
self.assertRaises(AssertionError, pyson.And, True)
self.assertRaises(AssertionError, pyson.And)
self.assert_(pyson.And(True, False).types() == set([bool]))
self.assertEqual(pyson.And(True, False).types(), set([bool]))
eval = pyson.PYSONEncoder().encode(pyson.And(True, True))
self.assertTrue(pyson.PYSONDecoder().decode(eval))
@@ -130,7 +130,7 @@ class PYSONTestCase(unittest.TestCase):
def test0050Or(self):
'Test pyson.Or'
self.assert_(pyson.Or(True, False).pyson() == {
self.assertEqual(pyson.Or(True, False).pyson(), {
'__class__': 'Or',
's': [True, False],
})
@@ -141,7 +141,7 @@ class PYSONTestCase(unittest.TestCase):
self.assertRaises(AssertionError, pyson.Or, True)
self.assertRaises(AssertionError, pyson.Or)
self.assert_(pyson.Or(True, False).types() == set([bool]))
self.assertEqual(pyson.Or(True, False).types(), set([bool]))
eval = pyson.PYSONEncoder().encode(pyson.Or(True, True))
self.assertTrue(pyson.PYSONDecoder().decode(eval))
@@ -172,7 +172,7 @@ class PYSONTestCase(unittest.TestCase):
def test0060Equal(self):
'Test pyson.Equal'
self.assert_(pyson.Equal('test', 'test').pyson() == {
self.assertEqual(pyson.Equal('test', 'test').pyson(), {
'__class__': 'Equal',
's1': 'test',
's2': 'test',
@@ -180,7 +180,7 @@ class PYSONTestCase(unittest.TestCase):
self.assertRaises(AssertionError, pyson.Equal, 'test', True)
self.assert_(pyson.Equal('test', 'test').types() == set([bool]))
self.assertEqual(pyson.Equal('test', 'test').types(), set([bool]))
eval = pyson.PYSONEncoder().encode(pyson.Equal('test', 'test'))
self.assertTrue(pyson.PYSONDecoder().decode(eval))
@@ -190,7 +190,7 @@ class PYSONTestCase(unittest.TestCase):
def test0070Greater(self):
'Test pyson.Greater'
self.assert_(pyson.Greater(1, 0).pyson() == {
self.assertEqual(pyson.Greater(1, 0).pyson(), {
'__class__': 'Greater',
's1': 1,
's2': 0,
@@ -201,7 +201,7 @@ class PYSONTestCase(unittest.TestCase):
self.assertRaises(AssertionError, pyson.Greater, 1, 'test')
self.assertRaises(AssertionError, pyson.Greater, 1, 0, 'test')
self.assert_(pyson.Greater(1, 0).types() == set([bool]))
self.assertEqual(pyson.Greater(1, 0).types(), set([bool]))
eval = pyson.PYSONEncoder().encode(pyson.Greater(1, 0))
self.assertTrue(pyson.PYSONDecoder().decode(eval))
@@ -223,7 +223,7 @@ class PYSONTestCase(unittest.TestCase):
def test0080Less(self):
'Test pyson.Less'
self.assert_(pyson.Less(0, 1).pyson() == {
self.assertEqual(pyson.Less(0, 1).pyson(), {
'__class__': 'Less',
's1': 0,
's2': 1,
@@ -234,7 +234,7 @@ class PYSONTestCase(unittest.TestCase):
self.assertRaises(AssertionError, pyson.Less, 0, 'test')
self.assertRaises(AssertionError, pyson.Less, 0, 1, 'test')
self.assert_(pyson.Less(0, 1).types() == set([bool]))
self.assertEqual(pyson.Less(0, 1).types(), set([bool]))
eval = pyson.PYSONEncoder().encode(pyson.Less(0, 1))
self.assertTrue(pyson.PYSONDecoder().decode(eval))
@@ -256,7 +256,7 @@ class PYSONTestCase(unittest.TestCase):
def test0090If(self):
'Test pyson.If'
self.assert_(pyson.If(True, 'foo', 'bar').pyson() == {
self.assertEqual(pyson.If(True, 'foo', 'bar').pyson(), {
'__class__': 'If',
'c': True,
't': 'foo',
@@ -267,18 +267,18 @@ class PYSONTestCase(unittest.TestCase):
self.assertRaises(AssertionError, pyson.If, True, 'foo', False)
self.assertEqual(pyson.If(True, 'foo', 'bar').types(),
set([type('foo')]))
self.assert_(pyson.If(True, False, True).types() == set([bool]))
set([basestring]))
self.assertEqual(pyson.If(True, False, True).types(), set([bool]))
eval = pyson.PYSONEncoder().encode(pyson.If(True, 'foo', 'bar'))
self.assert_(pyson.PYSONDecoder().decode(eval) == 'foo')
self.assertEqual(pyson.PYSONDecoder().decode(eval), 'foo')
eval = pyson.PYSONEncoder().encode(pyson.If(False, 'foo', 'bar'))
self.assert_(pyson.PYSONDecoder().decode(eval) == 'bar')
self.assertEqual(pyson.PYSONDecoder().decode(eval), 'bar')
def test0100Get(self):
'Test pyson.Get'
self.assert_(pyson.Get({'foo': 'bar'}, 'foo', 'default').pyson() == {
self.assertEqual(pyson.Get({'foo': 'bar'}, 'foo', 'default').pyson(), {
'__class__': 'Get',
'v': {'foo': 'bar'},
'k': 'foo',
@@ -288,24 +288,25 @@ class PYSONTestCase(unittest.TestCase):
self.assertRaises(AssertionError, pyson.Get, 'test', 'foo', 'default')
self.assertRaises(AssertionError, pyson.Get, {}, 1, 'default')
self.assert_(pyson.Get({}, 'foo', 'default').types() == set([str]))
self.assert_(pyson.Get({}, 'foo', True).types() == set([bool]))
self.assertEqual(pyson.Get({}, 'foo', 'default').types(),
set([basestring]))
self.assertEqual(pyson.Get({}, 'foo', True).types(), set([bool]))
eval = pyson.PYSONEncoder().encode(pyson.Get(
{'foo': 'bar'}, 'foo', 'default'))
self.assert_(pyson.PYSONDecoder().decode(eval) == 'bar')
self.assertEqual(pyson.PYSONDecoder().decode(eval), 'bar')
eval = pyson.PYSONEncoder().encode(pyson.Get(
{'foo': 'bar'}, 'test', 'default'))
self.assert_(pyson.PYSONDecoder().decode(eval) == 'default')
self.assertEqual(pyson.PYSONDecoder().decode(eval), 'default')
eval = pyson.PYSONEncoder().encode(pyson.Get(
{}, 'foo', 'default'))
self.assert_(pyson.PYSONDecoder().decode(eval) == 'default')
self.assertEqual(pyson.PYSONDecoder().decode(eval), 'default')
def test0110In(self):
'Test pyson.In'
self.assert_(pyson.In('foo', {'foo': 'bar'}).pyson() == {
self.assertEqual(pyson.In('foo', {'foo': 'bar'}).pyson(), {
'__class__': 'In',
'k': 'foo',
'v': {'foo': 'bar'},
@@ -314,7 +315,7 @@ class PYSONTestCase(unittest.TestCase):
self.assertRaises(AssertionError, pyson.In, object(), {})
self.assertRaises(AssertionError, pyson.In, 'test', 'foo')
self.assert_(pyson.In('foo', {}).types() == set([bool]))
self.assertEqual(pyson.In('foo', {}).types(), set([bool]))
eval = pyson.PYSONEncoder().encode(pyson.In('foo', {'foo': 'bar'}))
self.assertTrue(pyson.PYSONDecoder().decode(eval))
@@ -348,7 +349,7 @@ class PYSONTestCase(unittest.TestCase):
def test0120Date(self):
'Test pyson.Date'
self.assert_(pyson.Date(2010, 1, 12, -1, 12, -7).pyson() == {
self.assertEqual(pyson.Date(2010, 1, 12, -1, 12, -7).pyson(), {
'__class__': 'Date',
'y': 2010,
'M': 1,
@@ -371,37 +372,37 @@ class PYSONTestCase(unittest.TestCase):
self.assertRaises(AssertionError, pyson.Date, 2010, 1, 12, -1, 12,
'test')
self.assert_(pyson.Date(2010, 1, 12, -1, 12, -7).types()
== set([datetime.date]))
self.assertEqual(pyson.Date(2010, 1, 12, -1, 12, -7).types(),
set([datetime.date]))
eval = pyson.PYSONEncoder().encode(pyson.Date())
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.date.today())
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.date.today())
eval = pyson.PYSONEncoder().encode(pyson.Date(2010, 1, 12))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.date(2010, 1, 12))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.date(2010, 1, 12))
eval = pyson.PYSONEncoder().encode(pyson.Date(2010, 1, 12, -1))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.date(2009, 1, 12))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.date(2009, 1, 12))
eval = pyson.PYSONEncoder().encode(pyson.Date(2010, 1, 12, 0, 12))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.date(2011, 1, 12))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.date(2011, 1, 12))
eval = pyson.PYSONEncoder().encode(pyson.Date(2010, 1, 12, 0, 0, -7))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.date(2010, 1, 5))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.date(2010, 1, 5))
eval = pyson.PYSONEncoder().encode(datetime.date(2010, 2, 22))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.date(2010, 2, 22))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.date(2010, 2, 22))
def test0130DateTime(self):
'Test pyson.DateTime'
self.assert_(pyson.DateTime(2010, 1, 12, 10, 30, 20, 0,
-1, 12, -7, 2, 15, 30, 1).pyson() == {
self.assertEqual(pyson.DateTime(2010, 1, 12, 10, 30, 20, 0,
-1, 12, -7, 2, 15, 30, 1).pyson(), {
'__class__': 'DateTime',
'y': 2010,
'M': 1,
@@ -448,64 +449,64 @@ class PYSONTestCase(unittest.TestCase):
self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 12, 10, 30,
20, 0, -1, 12, -7, 2, 15, 30, 'test')
self.assert_(pyson.DateTime(2010, 1, 12, 10, 30, 20, 0,
-1, 12, -7, 2, 15, 30, 1).types() == set([datetime.datetime]))
self.assertEqual(pyson.DateTime(2010, 1, 12, 10, 30, 20, 0,
-1, 12, -7, 2, 15, 30, 1).types(), set([datetime.datetime]))
eval = pyson.PYSONEncoder().encode(pyson.DateTime(2010, 1, 12,
10, 30, 20, 0))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.datetime(2010, 1, 12, 10, 30, 20, 0))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.datetime(2010, 1, 12, 10, 30, 20, 0))
eval = pyson.PYSONEncoder().encode(pyson.DateTime(2010, 1, 12,
10, 30, 20, 0, -1))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.datetime(2009, 1, 12, 10, 30, 20, 0))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.datetime(2009, 1, 12, 10, 30, 20, 0))
eval = pyson.PYSONEncoder().encode(pyson.DateTime(2010, 1, 12,
10, 30, 20, 0, 0, 12))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.datetime(2011, 1, 12, 10, 30, 20, 0))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.datetime(2011, 1, 12, 10, 30, 20, 0))
eval = pyson.PYSONEncoder().encode(pyson.DateTime(2010, 1, 12,
10, 30, 20, 0, 0, 0, -7))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.datetime(2010, 1, 5, 10, 30, 20, 0))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.datetime(2010, 1, 5, 10, 30, 20, 0))
eval = pyson.PYSONEncoder().encode(pyson.DateTime(2010, 1, 12,
10, 30, 20, 0, 0, 0, 0, 12))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.datetime(2010, 1, 12, 22, 30, 20, 0))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.datetime(2010, 1, 12, 22, 30, 20, 0))
eval = pyson.PYSONEncoder().encode(pyson.DateTime(2010, 1, 12,
10, 30, 20, 0, 0, 0, 0, 0, -30))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.datetime(2010, 1, 12, 10, 0, 20, 0))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.datetime(2010, 1, 12, 10, 0, 20, 0))
eval = pyson.PYSONEncoder().encode(pyson.DateTime(2010, 1, 12,
10, 30, 20, 0, 0, 0, 0, 0, 0, 30))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.datetime(2010, 1, 12, 10, 30, 50, 0))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.datetime(2010, 1, 12, 10, 30, 50, 0))
eval = pyson.PYSONEncoder().encode(pyson.DateTime(2010, 1, 12,
10, 30, 20, 0, 0, 0, 0, 0, 0, 0, 200))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.datetime(2010, 1, 12, 10, 30, 20, 200))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.datetime(2010, 1, 12, 10, 30, 20, 200))
eval = pyson.PYSONEncoder().encode(datetime.datetime(
2010, 2, 22, 10, 30, 20, 200))
self.assert_(pyson.PYSONDecoder().decode(eval)
== datetime.datetime(2010, 2, 22, 10, 30, 20, 200))
self.assertEqual(pyson.PYSONDecoder().decode(eval),
datetime.datetime(2010, 2, 22, 10, 30, 20, 200))
def test0140Len(self):
'Test pyson.Len'
self.assert_(pyson.Len([1, 2, 3]).pyson() == {
self.assertEqual(pyson.Len([1, 2, 3]).pyson(), {
'__class__': 'Len',
'v': [1, 2, 3],
})
self.assertRaises(AssertionError, pyson.Len, object())
self.assert_(pyson.Len([1, 2, 3]).types() == set([int, long]))
self.assertEqual(pyson.Len([1, 2, 3]).types(), set([int]))
eval = pyson.PYSONEncoder().encode(pyson.Len([1, 2, 3]))
self.assertEqual(pyson.PYSONDecoder().decode(eval), 3)
@@ -521,10 +522,10 @@ class PYSONTestCase(unittest.TestCase):
eval = pyson.PYSONEncoder().encode(['id', pyson.If(pyson.Not(
pyson.In('company', pyson.Eval('context', {}))), '=', '!='),
pyson.Get(pyson.Eval('context', {}), 'company', -1)])
self.assert_(pyson.PYSONDecoder({'context': {'company': 1}}
).decode(eval) == ['id', '!=', 1])
self.assert_(pyson.PYSONDecoder({'context': {}}
).decode(eval) == ['id', '=', -1])
self.assertEqual(pyson.PYSONDecoder({'context': {'company': 1}}
).decode(eval), ['id', '!=', 1])
self.assertEqual(pyson.PYSONDecoder({'context': {}}
).decode(eval), ['id', '=', -1])
def suite():