transit-python 0.8.174-__py2-none-any.whl → 0.8.240__py2-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.
- scratch/__init__.py +1 -0
- scratch/bools.py +15 -0
- scratch/cache-inconsistent.py +10 -0
- scratch/dt.py +7 -0
- scratch/emptyset.py +19 -0
- scratch/frozendict.py +14 -0
- scratch/int_issue.py +10 -0
- scratch/issue.py +44 -0
- scratch/mp_test.py +22 -0
- scratch/msgpack_issue.py +19 -0
- scratch/newline.py +14 -0
- scratch/streamit.py +56 -0
- scratch/testmprt.py +15 -0
- scratch/unicodeme.py +21 -0
- tests/exemplars_test.py +16 -11
- tests/regression.py +80 -0
- transit/class_hash.py +2 -2
- transit/decoder.py +33 -20
- transit/read_handlers.py +13 -3
- transit/reader.py +34 -10
- transit/rolling_cache.py +3 -6
- transit/sosjson.py +74 -0
- transit/transit_types.py +33 -7
- transit/write_handlers.py +18 -11
- transit/writer.py +83 -60
- {transit_python-0.8.174_.dist-info → transit_python-0.8.240_.dist-info}/METADATA +1 -1
- transit_python-0.8.240_.dist-info/RECORD +37 -0
- {transit_python-0.8.174_.dist-info → transit_python-0.8.240_.dist-info}/metadata.json +1 -1
- {transit_python-0.8.174_.dist-info → transit_python-0.8.240_.dist-info}/top_level.txt +1 -0
- tests/transit_types_test.py +0 -24
- transit_python-0.8.174_.dist-info/RECORD +0 -22
- {transit_python-0.8.174_.dist-info → transit_python-0.8.240_.dist-info}/DESCRIPTION.rst +0 -0
- {transit_python-0.8.174_.dist-info → transit_python-0.8.240_.dist-info}/WHEEL +0 -0
scratch/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# empty
|
scratch/bools.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from StringIO import StringIO
|
|
2
|
+
from transit.writer import Writer
|
|
3
|
+
from transit.reader import Reader
|
|
4
|
+
from transit.transit_types import Symbol
|
|
5
|
+
r = Reader('json')
|
|
6
|
+
io = StringIO()
|
|
7
|
+
w = Writer(io,'json')
|
|
8
|
+
w.write([{"Problem?":True},Symbol("Here"),Symbol("Here")])
|
|
9
|
+
print(io.getvalue())
|
|
10
|
+
print(r.read(StringIO(io.getvalue())))
|
scratch/dt.py
ADDED
scratch/emptyset.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from transit.writer import Writer
|
|
3
|
+
from transit.reader import Reader
|
|
4
|
+
from transit.transit_types import URI
|
|
5
|
+
from StringIO import StringIO
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
io = StringIO()
|
|
9
|
+
|
|
10
|
+
x = set()
|
|
11
|
+
print(x)
|
|
12
|
+
|
|
13
|
+
writer = Writer(io, protocol="json")
|
|
14
|
+
writer.write(x)
|
|
15
|
+
reader = Reader(protocol="json")
|
|
16
|
+
print io.getvalue()
|
|
17
|
+
io.seek(0)
|
|
18
|
+
outcome = reader.read(io)
|
|
19
|
+
print(outcome)
|
scratch/frozendict.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from transit.writer import Writer
|
|
2
|
+
from transit.reader import Reader
|
|
3
|
+
import StringIO
|
|
4
|
+
|
|
5
|
+
a={"a":1}
|
|
6
|
+
b={"a":1,"b":2}
|
|
7
|
+
|
|
8
|
+
io = StringIO.StringIO()
|
|
9
|
+
w = Writer(io, protocol="msgpack")
|
|
10
|
+
|
|
11
|
+
w.write([a,b])
|
|
12
|
+
r = Reader(protocol="msgpack")
|
|
13
|
+
result = r.read(StringIO.StringIO(io.getvalue()))
|
|
14
|
+
print result
|
scratch/int_issue.py
ADDED
scratch/issue.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from transit.reader import Reader
|
|
2
|
+
from transit.writer import Writer
|
|
3
|
+
from StringIO import StringIO
|
|
4
|
+
import datetime
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DateHandler(object):
|
|
8
|
+
@staticmethod
|
|
9
|
+
def tag(d):
|
|
10
|
+
return 'p'
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def rep(d):
|
|
14
|
+
return str(d)
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def string_rep(d):
|
|
18
|
+
return str(d)
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def from_rep(d):
|
|
22
|
+
return datetime.datetime.strptime(d, '%Y-%m-%d').date()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
my_data = {
|
|
26
|
+
'start_date': datetime.date(2014, 9, 9),
|
|
27
|
+
'end_date': datetime.date(2014, 10, 10)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
io = StringIO()
|
|
31
|
+
writer = Writer(io, "json")
|
|
32
|
+
writer.register(datetime.date, DateHandler)
|
|
33
|
+
writer.write(my_data)
|
|
34
|
+
output = io.getvalue()
|
|
35
|
+
io.close()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
reader = Reader()
|
|
39
|
+
reader.register('p', DateHandler)
|
|
40
|
+
parsed = reader.read(StringIO(output))
|
|
41
|
+
|
|
42
|
+
assert parsed['start_date'] == my_data['start_date'] # Should be fine
|
|
43
|
+
assert parsed['end_date'] == my_data['end_date'] # Error here - actual value of parsed['end_date'] is (u'start_date', u'2014-10-10')
|
|
44
|
+
print(parsed)
|
scratch/mp_test.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import transit.writer as twriter
|
|
2
|
+
import transit.reader as treader
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
from StringIO import StringIO
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
import dateutil.tz
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
b = BytesIO()
|
|
10
|
+
#b = StringIO()
|
|
11
|
+
r = treader.Reader(protocol="msgpack")
|
|
12
|
+
w = twriter.Writer(b, protocol="msgpack")
|
|
13
|
+
|
|
14
|
+
UUIDS = (UUID('5a2cbea3-e8c6-428b-b525-21239370dd55'),
|
|
15
|
+
UUID('d1dc64fa-da79-444b-9fa4-d4412f427289'),
|
|
16
|
+
UUID('501a978e-3a3e-4060-b3be-1cf2bd4b1a38'),
|
|
17
|
+
UUID('b3ba141a-a776-48e4-9fae-a28ea8571f58'))
|
|
18
|
+
|
|
19
|
+
w.write(UUIDS)
|
|
20
|
+
print(b.getvalue())
|
|
21
|
+
b.seek(0)
|
|
22
|
+
print r.read(b)
|
scratch/msgpack_issue.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from transit.reader import Reader
|
|
2
|
+
from transit.writer import Writer
|
|
3
|
+
from StringIO import StringIO
|
|
4
|
+
|
|
5
|
+
#json = "{\"~#set\":[{\"~#set\":[1,3,2]},{\"~#set\":[null,0,2.0,\"~~eight\",1,true,\"five\",false,\"~$seven\",\"~:six\"]}]}\n"
|
|
6
|
+
#mpcode = "94a37e6360a37e637ea37e635ea37e6323".decode("hex")
|
|
7
|
+
|
|
8
|
+
json = "{\"~#set\":[1,true]}"
|
|
9
|
+
io = StringIO()
|
|
10
|
+
io.write(json)
|
|
11
|
+
io.seek(0)
|
|
12
|
+
r = Reader(protocol="json")
|
|
13
|
+
correct = r.read(io)
|
|
14
|
+
out = StringIO()
|
|
15
|
+
w = Writer(out, protocol="json")
|
|
16
|
+
w.write(correct)
|
|
17
|
+
print(json)
|
|
18
|
+
print(out.getvalue())
|
|
19
|
+
|
scratch/newline.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from transit.reader import Reader
|
|
2
|
+
from transit.writer import Writer
|
|
3
|
+
from StringIO import StringIO
|
|
4
|
+
|
|
5
|
+
s = 'a\nb'
|
|
6
|
+
|
|
7
|
+
io = StringIO()
|
|
8
|
+
writer = Writer(io, 'json')
|
|
9
|
+
writer.write(s)
|
|
10
|
+
output = io.getvalue()
|
|
11
|
+
io.close()
|
|
12
|
+
|
|
13
|
+
reader = Reader()
|
|
14
|
+
assert s == reader.read(StringIO(output))
|
scratch/streamit.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from StringIO import StringIO
|
|
2
|
+
from copy import copy
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
DELIMS = {"{" : "}",
|
|
6
|
+
"[" : "]",
|
|
7
|
+
"\"": "\""}
|
|
8
|
+
|
|
9
|
+
SKIP = [" ", "\n", "\t"]
|
|
10
|
+
|
|
11
|
+
ESCAPE = "\\"
|
|
12
|
+
|
|
13
|
+
def read_chunk(stream):
|
|
14
|
+
"""Ignore whitespace outside of strings.
|
|
15
|
+
"""
|
|
16
|
+
chunk = stream.read(1)
|
|
17
|
+
while chunk in SKIP:
|
|
18
|
+
chunk = stream.read(1)
|
|
19
|
+
if chunk == "\"":
|
|
20
|
+
chunk += stream.read(1)
|
|
21
|
+
while not chunk.endswith("\""):
|
|
22
|
+
if chunk[-1] == ESCAPE:
|
|
23
|
+
chunk += stream.read(2)
|
|
24
|
+
else:
|
|
25
|
+
chunk += stream.read(1)
|
|
26
|
+
return chunk
|
|
27
|
+
|
|
28
|
+
def yield_json(stream):
|
|
29
|
+
buff = u""
|
|
30
|
+
arr_count = 0
|
|
31
|
+
obj_count = 0
|
|
32
|
+
while True:
|
|
33
|
+
buff += read_chunk(stream)
|
|
34
|
+
|
|
35
|
+
if buff.endswith('{'):
|
|
36
|
+
obj_count += 1
|
|
37
|
+
if buff.endswith('['):
|
|
38
|
+
arr_count += 1
|
|
39
|
+
if buff.endswith(']'):
|
|
40
|
+
arr_count -= 1
|
|
41
|
+
if obj_count == arr_count == 0:
|
|
42
|
+
json_item = copy(buff)
|
|
43
|
+
buff = u""
|
|
44
|
+
yield json_item
|
|
45
|
+
if buff.endswith('}'):
|
|
46
|
+
obj_count -= 1
|
|
47
|
+
if obj_count == arr_count == 0:
|
|
48
|
+
json_item = copy(buff)
|
|
49
|
+
buff = u""
|
|
50
|
+
yield json_item
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
x = StringIO("""[\"^ \",\"~:key0000\",0,\"~:key0001\",1,\"~:key0002\",2,\"~:key0003\",3,\"~:key0004\",4,\"~:key0005\",5,\"~:key0006\",6,\"~:key0007\",7,\"~:key0008\",8,\"~:key0009\",9]\n""")
|
|
54
|
+
# x = StringIO("""{\"~#'\":false}\n""")
|
|
55
|
+
for a in yield_json(x):
|
|
56
|
+
print(a)
|
scratch/testmprt.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from transit import reader, writer
|
|
2
|
+
from StringIO import StringIO
|
|
3
|
+
|
|
4
|
+
for fmt in ("msgpack", "json"):
|
|
5
|
+
io = StringIO()
|
|
6
|
+
w = writer.Writer(io, fmt)
|
|
7
|
+
|
|
8
|
+
for item in [0, 1, 1902382]:
|
|
9
|
+
w.write(item)
|
|
10
|
+
|
|
11
|
+
io.seek(0)
|
|
12
|
+
r = reader.Reader(fmt, io)
|
|
13
|
+
|
|
14
|
+
for item in r.read():
|
|
15
|
+
print(item)
|
scratch/unicodeme.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from transit.writer import Writer
|
|
3
|
+
from transit.reader import Reader
|
|
4
|
+
from transit.transit_types import URI
|
|
5
|
+
from StringIO import StringIO
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
io = StringIO()
|
|
9
|
+
|
|
10
|
+
x = URI(u'http://www.詹姆斯.com/')
|
|
11
|
+
print(x)
|
|
12
|
+
|
|
13
|
+
writer = Writer(io, protocol="json-verbose")
|
|
14
|
+
writer.write(x)
|
|
15
|
+
reader = Reader(protocol="json-verbose")
|
|
16
|
+
print io.getvalue()
|
|
17
|
+
io.seek(0)
|
|
18
|
+
outcome = reader.read(io)
|
|
19
|
+
print(outcome)
|
|
20
|
+
|
|
21
|
+
|
tests/exemplars_test.py
CHANGED
|
@@ -14,26 +14,24 @@
|
|
|
14
14
|
## limitations under the License.
|
|
15
15
|
import unittest
|
|
16
16
|
|
|
17
|
-
# get parent directory into python path
|
|
18
|
-
import sys, os
|
|
19
|
-
sys.path.append(os.path.abspath(os.path.dirname(__file__) + os.path.sep + os.path.pardir))
|
|
20
|
-
|
|
21
17
|
# then import transit stuff
|
|
22
18
|
from transit.reader import Reader, JsonUnmarshaler, MsgPackUnmarshaler
|
|
23
19
|
from transit.writer import Writer
|
|
24
|
-
from transit.transit_types import Keyword, Symbol, URI, frozendict, TaggedValue, Link
|
|
20
|
+
from transit.transit_types import Keyword, Symbol, URI, frozendict, TaggedValue, Link, true, false
|
|
25
21
|
from StringIO import StringIO
|
|
26
22
|
from transit.helpers import mapcat
|
|
27
23
|
from helpers import ints_centered_on, hash_of_size, array_of_symbools
|
|
28
24
|
from uuid import UUID
|
|
29
25
|
from datetime import datetime
|
|
30
26
|
import dateutil.tz
|
|
27
|
+
from math import isnan
|
|
31
28
|
|
|
32
29
|
class ExemplarBaseTest(unittest.TestCase):
|
|
33
30
|
pass
|
|
34
31
|
|
|
35
32
|
def exemplar(name, val):
|
|
36
33
|
class ExemplarTest(ExemplarBaseTest):
|
|
34
|
+
|
|
37
35
|
def test_json(self):
|
|
38
36
|
with open("../transit-format/examples/0.8/simple/" + name + ".json", 'r') as stream:
|
|
39
37
|
data = Reader(protocol="json").read(stream)
|
|
@@ -87,13 +85,18 @@ def exemplar(name, val):
|
|
|
87
85
|
try:
|
|
88
86
|
return unittest.TestCase.assertEqual(self, val, data)
|
|
89
87
|
except AssertionError as e:
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
if not False in [isnan(v) and isnan(d) or isnan(v) == isnan(d)
|
|
89
|
+
for v, d in zip (val, data)]:
|
|
90
|
+
return unittest.TestCase.assertEqual(self, filter(lambda x: not isnan(x), val),
|
|
91
|
+
filter(lambda x: not isnan(x), data))
|
|
92
|
+
else:
|
|
93
|
+
e.args += (name, "failed")
|
|
94
|
+
raise
|
|
92
95
|
|
|
93
96
|
globals()["test_" + name + "_json"] = ExemplarTest
|
|
94
97
|
|
|
95
98
|
ARRAY_SIMPLE = (1, 2, 3)
|
|
96
|
-
ARRAY_MIXED = (0, 1, 2.0,
|
|
99
|
+
ARRAY_MIXED = (0, 1, 2.0, true, false, 'five', Keyword("six"), Symbol("seven"), '~eight', None)
|
|
97
100
|
ARRAY_NESTED = (ARRAY_SIMPLE, ARRAY_MIXED)
|
|
98
101
|
SMALL_STRINGS = ("", "a", "ab", "abc", "abcd", "abcde", "abcdef")
|
|
99
102
|
POWERS_OF_TWO = tuple(map(lambda x: pow(2, x), range(66)))
|
|
@@ -128,14 +131,14 @@ MAP_SIMPLE = frozendict({Keyword("a"): 1,
|
|
|
128
131
|
|
|
129
132
|
MAP_MIXED = frozendict({Keyword("a"): 1,
|
|
130
133
|
Keyword("b"): u"a string",
|
|
131
|
-
Keyword("c"):
|
|
134
|
+
Keyword("c"): true})
|
|
132
135
|
|
|
133
136
|
MAP_NESTED = frozendict({Keyword("simple"): MAP_SIMPLE,
|
|
134
137
|
Keyword("mixed"): MAP_MIXED})
|
|
135
138
|
|
|
136
139
|
exemplar("nil", None)
|
|
137
|
-
exemplar("true",
|
|
138
|
-
exemplar("false",
|
|
140
|
+
exemplar("true", true)
|
|
141
|
+
exemplar("false", false)
|
|
139
142
|
exemplar("zero", 0)
|
|
140
143
|
exemplar("one", 1)
|
|
141
144
|
exemplar("one_string", "hello")
|
|
@@ -215,6 +218,8 @@ exemplar("maps_four_char_string_keys", ({"aaaa": 1, "bbbb": 2},
|
|
|
215
218
|
exemplar("maps_unrecognized_keys", (TaggedValue("abcde", Keyword("anything")),
|
|
216
219
|
TaggedValue("fghij", Keyword("anything-else")),))
|
|
217
220
|
|
|
221
|
+
exemplar("vector_special_numbers", (float("nan"), float("inf"), float("-inf")))
|
|
222
|
+
|
|
218
223
|
# Doesn't exist in simple examples but gave me tests to verify Link.
|
|
219
224
|
#exemplar("link", Link("http://www.blah.com", "test", "test", "link", "test"))
|
|
220
225
|
|
tests/regression.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
## Copyright 2014 Cognitect. All Rights Reserved.
|
|
2
|
+
##
|
|
3
|
+
## Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
## you may not use this file except in compliance with the License.
|
|
5
|
+
## You may obtain a copy of the License at
|
|
6
|
+
##
|
|
7
|
+
## http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
##
|
|
9
|
+
## Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
## distributed under the License is distributed on an "AS-IS" BASIS,
|
|
11
|
+
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
## See the License for the specific language governing permissions and
|
|
13
|
+
## limitations under the License.
|
|
14
|
+
|
|
15
|
+
# This test suite verifies that issues corrected remain corrected.
|
|
16
|
+
import unittest
|
|
17
|
+
|
|
18
|
+
from transit.reader import Reader
|
|
19
|
+
from transit.writer import Writer
|
|
20
|
+
from transit.transit_types import Symbol, frozendict, true, false
|
|
21
|
+
from StringIO import StringIO
|
|
22
|
+
|
|
23
|
+
class RegressionBaseTest(unittest.TestCase):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
def regression(name, value):
|
|
27
|
+
class RegressionTest(RegressionBaseTest):
|
|
28
|
+
|
|
29
|
+
def test_roundtrip(self):
|
|
30
|
+
in_data = value
|
|
31
|
+
io = StringIO()
|
|
32
|
+
w = Writer(io, "json")
|
|
33
|
+
w.write(in_data)
|
|
34
|
+
r = Reader("json")
|
|
35
|
+
out_data = r.read(StringIO(io.getvalue()))
|
|
36
|
+
self.assertEqual(in_data, out_data)
|
|
37
|
+
|
|
38
|
+
globals()["test_" + name + "_json"] = RegressionTest
|
|
39
|
+
|
|
40
|
+
regression("cache_consistency", ({"Problem?":true},
|
|
41
|
+
Symbol("Here"),
|
|
42
|
+
Symbol("Here")))
|
|
43
|
+
regression("one_pair_frozendict", frozendict({"a":1}))
|
|
44
|
+
regression("json_int_max", (2**53+100, 2**63+100))
|
|
45
|
+
regression("newline_in_string", "a\nb")
|
|
46
|
+
|
|
47
|
+
class BooleanTest(unittest.TestCase):
|
|
48
|
+
"""Even though we're roundtripping transit_types.true and
|
|
49
|
+
transit_types.false now, make sure we can still write Python bools.
|
|
50
|
+
|
|
51
|
+
Additionally, make sure we can still do basic logical evaluation on transit
|
|
52
|
+
Boolean values.
|
|
53
|
+
"""
|
|
54
|
+
def test_write_bool(self):
|
|
55
|
+
for protocol in ("json", "json-verbose", "msgpack"):
|
|
56
|
+
io = StringIO()
|
|
57
|
+
w = Writer(io, protocol)
|
|
58
|
+
w.write((True, False))
|
|
59
|
+
r = Reader(protocol)
|
|
60
|
+
io.seek(0)
|
|
61
|
+
out_data = r.read(io)
|
|
62
|
+
assert out_data[0] == true
|
|
63
|
+
assert out_data[1] == false
|
|
64
|
+
|
|
65
|
+
def test_basic_eval(self):
|
|
66
|
+
assert true
|
|
67
|
+
assert not false
|
|
68
|
+
|
|
69
|
+
def test_or(self):
|
|
70
|
+
assert true or false
|
|
71
|
+
assert not (false or false)
|
|
72
|
+
assert true or true
|
|
73
|
+
|
|
74
|
+
def test_and(self):
|
|
75
|
+
assert not (true and false)
|
|
76
|
+
assert true and true
|
|
77
|
+
assert not (false and false)
|
|
78
|
+
|
|
79
|
+
if __name__ == '__main__':
|
|
80
|
+
unittest.main()
|
transit/class_hash.py
CHANGED
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
import collections
|
|
17
17
|
|
|
18
18
|
class ClassDict(collections.MutableMapping):
|
|
19
|
-
"""
|
|
19
|
+
"""A dictionary that looks up class/type keys with inheritance."""
|
|
20
20
|
|
|
21
21
|
def __init__(self, *args, **kwargs):
|
|
22
22
|
self.store = dict()
|
|
23
23
|
self.update(dict(*args, **kwargs))
|
|
24
24
|
|
|
25
25
|
def __getitem__(self, key):
|
|
26
|
-
key = isinstance(key, type)
|
|
26
|
+
key = key if isinstance(key, type) else type(key)
|
|
27
27
|
if key in self.store:
|
|
28
28
|
return self.store[key]
|
|
29
29
|
else:
|
transit/decoder.py
CHANGED
|
@@ -18,6 +18,7 @@ from collections import OrderedDict
|
|
|
18
18
|
from helpers import pairs
|
|
19
19
|
import read_handlers as rh
|
|
20
20
|
from rolling_cache import RollingCache, is_cacheable, is_cache_key
|
|
21
|
+
from transit_types import true, false
|
|
21
22
|
|
|
22
23
|
class Tag(object):
|
|
23
24
|
def __init__(self, tag):
|
|
@@ -34,6 +35,7 @@ default_options = {"decoders": {"_": rh.NoneHandler,
|
|
|
34
35
|
"t": rh.DateHandler,
|
|
35
36
|
"m": rh.DateHandler,
|
|
36
37
|
"n": rh.BigIntegerHandler,
|
|
38
|
+
"z": rh.SpecialNumbersHandler,
|
|
37
39
|
"link": rh.LinkHandler,
|
|
38
40
|
"list": rh.ListHandler,
|
|
39
41
|
"set": rh.SetHandler,
|
|
@@ -47,7 +49,7 @@ ground_decoders = {"_": rh.NoneHandler,
|
|
|
47
49
|
"'": rh.IdentityHandler}
|
|
48
50
|
|
|
49
51
|
class Decoder(object):
|
|
50
|
-
"""
|
|
52
|
+
"""The Decoder is the lowest level entry point for parsing, decoding, and
|
|
51
53
|
fully converting Transit data into Python objects.
|
|
52
54
|
|
|
53
55
|
During the creation of a Decoder object, you can specify custom options
|
|
@@ -55,7 +57,8 @@ class Decoder(object):
|
|
|
55
57
|
can specify your own decoders and override many of the built in decoders,
|
|
56
58
|
some decoders are silently enforced and cannot be overriden. These are
|
|
57
59
|
known as Ground Decoders, and are needed to maintain bottom-tier
|
|
58
|
-
compatibility.
|
|
60
|
+
compatibility.
|
|
61
|
+
"""
|
|
59
62
|
|
|
60
63
|
def __init__(self, options={}):
|
|
61
64
|
self.options = default_options.copy()
|
|
@@ -66,11 +69,12 @@ class Decoder(object):
|
|
|
66
69
|
self.decoders.update(ground_decoders)
|
|
67
70
|
|
|
68
71
|
def decode(self, node, cache=None, as_map_key=False):
|
|
69
|
-
"""
|
|
72
|
+
"""Given a node of data (any supported decodeable obj - string, dict,
|
|
70
73
|
list), return the decoded object. Optionally set the current decode
|
|
71
74
|
cache [None]. If None, a new RollingCache is instantiated and used.
|
|
72
75
|
You may also hit to the decoder that this node is to be treated as a
|
|
73
|
-
map key [False]. This is used internally.
|
|
76
|
+
map key [False]. This is used internally.
|
|
77
|
+
"""
|
|
74
78
|
if not cache:
|
|
75
79
|
cache = RollingCache()
|
|
76
80
|
return self._decode(node, cache, as_map_key)
|
|
@@ -85,18 +89,19 @@ class Decoder(object):
|
|
|
85
89
|
return self.decode_list(node, cache, as_map_key)
|
|
86
90
|
elif tp is str:
|
|
87
91
|
return self.decode_string(unicode(node, "utf-8"), cache, as_map_key)
|
|
88
|
-
|
|
89
|
-
return node
|
|
92
|
+
elif tp is bool:
|
|
93
|
+
return true if node else false
|
|
94
|
+
return node
|
|
90
95
|
|
|
91
96
|
def decode_list(self, node, cache, as_map_key):
|
|
92
|
-
"""
|
|
97
|
+
"""Special case decodes map-as-array.
|
|
93
98
|
Otherwise lists are treated as Python lists.
|
|
94
99
|
|
|
95
100
|
Arguments follow the same convention as the top-level 'decode'
|
|
96
|
-
function
|
|
101
|
+
function.
|
|
102
|
+
"""
|
|
97
103
|
if node:
|
|
98
|
-
|
|
99
|
-
if decoded == MAP_AS_ARR:
|
|
104
|
+
if node[0] == MAP_AS_ARR:
|
|
100
105
|
# key must be decoded before value for caching to work.
|
|
101
106
|
returned_dict = {}
|
|
102
107
|
for k,v in pairs(node[1:]):
|
|
@@ -104,16 +109,16 @@ class Decoder(object):
|
|
|
104
109
|
val = self._decode(v, cache, as_map_key)
|
|
105
110
|
returned_dict[key] = val
|
|
106
111
|
return returned_dict
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
elif isinstance(decoded, Tag):
|
|
112
|
+
|
|
113
|
+
decoded = self._decode(node[0], cache, as_map_key)
|
|
114
|
+
if isinstance(decoded, Tag):
|
|
111
115
|
return self.decode_tag(decoded.tag, self._decode(node[1], cache, as_map_key))
|
|
112
116
|
return tuple(self._decode(x, cache, as_map_key) for x in node)
|
|
113
117
|
|
|
114
118
|
def decode_string(self, string, cache, as_map_key):
|
|
115
|
-
"""
|
|
116
|
-
top-level 'decode' function
|
|
119
|
+
"""Decode a string - arguments follow the same convention as the
|
|
120
|
+
top-level 'decode' function.
|
|
121
|
+
"""
|
|
117
122
|
if is_cache_key(string):
|
|
118
123
|
return self.parse_string(cache.decode(string, as_map_key), cache, as_map_key)
|
|
119
124
|
if is_cacheable(string, as_map_key):
|
|
@@ -131,7 +136,14 @@ class Decoder(object):
|
|
|
131
136
|
if len(hash) != 1:
|
|
132
137
|
h = {}
|
|
133
138
|
for k, v in hash.items():
|
|
134
|
-
|
|
139
|
+
# crude/verbose implementation, but this is only version that
|
|
140
|
+
# plays nice w/cache for both msgpack and json thus far.
|
|
141
|
+
# -- e.g., we have to specify encode/decode order for key/val
|
|
142
|
+
# -- explicitly, all implicit ordering has broken in corner
|
|
143
|
+
# -- cases, thus these extraneous seeming assignments
|
|
144
|
+
key = self._decode(k, cache, True)
|
|
145
|
+
val = self._decode(v, cache, False)
|
|
146
|
+
h[key] = val
|
|
135
147
|
return transit_types.frozendict(h)
|
|
136
148
|
else:
|
|
137
149
|
key,value = hash.items()[0]
|
|
@@ -139,7 +151,7 @@ class Decoder(object):
|
|
|
139
151
|
if isinstance(key, Tag):
|
|
140
152
|
return self.decode_tag(key.tag, self._decode(value, cache, as_map_key))
|
|
141
153
|
else:
|
|
142
|
-
return {key: self._decode(value, cache, False)}
|
|
154
|
+
return transit_types.frozendict({key: self._decode(value, cache, False)})
|
|
143
155
|
|
|
144
156
|
def parse_string(self, string, cache, as_map_key):
|
|
145
157
|
if string.startswith(ESC):
|
|
@@ -155,10 +167,11 @@ class Decoder(object):
|
|
|
155
167
|
return string
|
|
156
168
|
|
|
157
169
|
def register(self, key_or_tag, obj):
|
|
158
|
-
"""
|
|
170
|
+
"""Register a custom Transit tag and new parsing function with the
|
|
159
171
|
decoder. Also, you can optionally set the 'default_decoder' with
|
|
160
172
|
this function. Your new tag and parse/decode function will be added
|
|
161
|
-
to the interal dictionary of decoders for this Decoder object
|
|
173
|
+
to the interal dictionary of decoders for this Decoder object.
|
|
174
|
+
"""
|
|
162
175
|
if key_or_tag == "default_decoder":
|
|
163
176
|
self.options["default_decoder"] = obj
|
|
164
177
|
else:
|
transit/read_handlers.py
CHANGED
|
@@ -47,7 +47,7 @@ class SymbolHandler(object):
|
|
|
47
47
|
class BooleanHandler(object):
|
|
48
48
|
@staticmethod
|
|
49
49
|
def from_rep(x):
|
|
50
|
-
return x == "t"
|
|
50
|
+
return transit_types.true if x == "t" else transit_types.false
|
|
51
51
|
|
|
52
52
|
class IntHandler(object):
|
|
53
53
|
@staticmethod
|
|
@@ -62,7 +62,7 @@ class FloatHandler(object):
|
|
|
62
62
|
class UuidHandler(object):
|
|
63
63
|
@staticmethod
|
|
64
64
|
def from_rep(u):
|
|
65
|
-
"""
|
|
65
|
+
"""Given a string, return a UUID object."""
|
|
66
66
|
if isinstance(u, basestring):
|
|
67
67
|
return uuid.UUID(u)
|
|
68
68
|
|
|
@@ -87,7 +87,7 @@ class DateHandler(object):
|
|
|
87
87
|
return DateHandler._convert_timestamp(long(d))
|
|
88
88
|
@staticmethod
|
|
89
89
|
def _convert_timestamp(ms):
|
|
90
|
-
"""
|
|
90
|
+
"""Given a timestamp in ms, return a DateTime object."""
|
|
91
91
|
return datetime.datetime.fromtimestamp(ms/1000.0, dateutil.tz.tzutc())
|
|
92
92
|
|
|
93
93
|
class BigIntegerHandler(object):
|
|
@@ -120,3 +120,13 @@ class IdentityHandler(object):
|
|
|
120
120
|
def from_rep(i):
|
|
121
121
|
return i
|
|
122
122
|
|
|
123
|
+
class SpecialNumbersHandler(object):
|
|
124
|
+
@staticmethod
|
|
125
|
+
def from_rep(z):
|
|
126
|
+
if z == 'NaN':
|
|
127
|
+
return float('Nan')
|
|
128
|
+
if z == 'INF':
|
|
129
|
+
return float('Inf')
|
|
130
|
+
if z == '-INF':
|
|
131
|
+
return float('-Inf')
|
|
132
|
+
raise ValueError("Don't know how to handle: " + str(z) + " as \"z\"")
|