Adding some very minor documentation
[ton] / ton / tags.py
1 import collections
2
3 VOID = 0x00
4 TRUE = 0x01
5 FALSE = 0x02
6 BOOL = (TRUE, FALSE)
7 INT8 = 0x10
8 INT16 = 0x11
9 INT32 = 0x12
10 INT64 = 0x13
11 # These are to be supported in the future
12 # FLOAT = 0x20
13 # DOUBLE = 0x21
14 BINARY = 0x30
15 UTF8 = 0x31
16 UTF16 = 0x32
17 UTF32 = 0x33
18 LIST = 0x40
19 DICTIONARY = 0x41
20
21 STRING_TAGS = set([UTF8, UTF16, UTF32])
22
23 DEFAULT_INTEGER_ENCODING = INT32
24 DEFAULT_STRING_ENCODING = UTF8
25
26 TaggedObject = collections.namedtuple('TaggedObject', ['tag', 'value'])
27
28 _TYPES_TO_TAGS = {
29     int: DEFAULT_INTEGER_ENCODING,
30     bytes: BINARY,
31     str: DEFAULT_STRING_ENCODING,
32     list: LIST,
33     dict: DICTIONARY,
34     collections.OrderedDict: DICTIONARY,
35 }
36
37 def _tag(o):
38     if isinstance(o, TaggedObject):
39         return o
40
41     if o is None:
42         return TaggedObject(tag = VOID, value = o)
43
44     if o is True:
45         return TaggedObject(tag = TRUE, value = o)
46
47     if o is False:
48         return TaggedObject(tag = FALSE, value = o)
49
50     return TaggedObject(tag = _TYPES_TO_TAGS[type(o)], value = o)
51
52 _NONE = TaggedObject(tag = VOID, value = None)
53 _TRUE = TaggedObject(tag = TRUE, value = True)
54 _FALSE = TaggedObject(tag = FALSE, value = False)
55
56 _TAGS_TO_IN_RANGE_PREDICATES = collections.OrderedDict([
57     (INT8,  lambda i: -128 <= i and i <= 127),
58     (INT16, lambda i: -32768 <= i and i <= 32767),
59     (INT32, lambda i: -2147483648 <= i and i <= 2147483647),
60     (INT64, lambda i: -9223372036854775808 <= i and i <= 9223372036854775807),
61 ])
62
63 class TooWideError(Exception):
64     pass
65
66 SMALLEST = object()
67
68 def autotag(o, **kwargs):
69     preferred_integer_tag = kwargs.pop('preferred_integer_tag', DEFAULT_INTEGER_ENCODING)
70     preferred_string_tag = kwargs.pop('preferred_string_tag', DEFAULT_STRING_ENCODING)
71
72     if kwargs:
73         raise TypeError("autotag() got an unexpected keyword argument '{}'".format(
74             list(kwargs.keys())[0],
75         ))
76
77     if isinstance(o, TaggedObject):
78         return o
79
80     if o is None:
81         return _NONE
82
83     if o is True:
84         return _TRUE
85
86     if o is False:
87         return _FALSE
88
89     if isinstance(o, int):
90         if preferred_integer_tag is not SMALLEST and _TAGS_TO_IN_RANGE_PREDICATES[preferred_integer_tag](o):
91             return TaggedObject(tag = preferred_integer_tag, value = o)
92
93         else:
94             for tag, in_range_predicate in _TAGS_TO_IN_RANGE_PREDICATES.items():
95                 if in_range_predicate(o):
96                     return TaggedObject(tag = tag, value = o)
97
98             raise TooWideError("Integer {} is too wide to be serialized")
99
100     if isinstance(o, str):
101         # TODO Support SMALLEST for preferred string tag
102         return TaggedObject(tag = preferred_string_tag, value = o)
103
104     if isinstance(o, bytes):
105         return TaggedObject(tag = BINARY, value = o)
106
107     if isinstance(o, list):
108         return TaggedObject(
109             tag = LIST,
110             value = [
111                 autotag(
112                     i,
113                     preferred_integer_tag = preferred_integer_tag,
114                     preferred_string_tag = preferred_string_tag,
115                 ) for i in o
116             ],
117         )
118
119     if isinstance(o, dict):
120         return TaggedObject(
121             tag = DICTIONARY,
122             value = collections.OrderedDict([
123                 (
124                     autotag(
125                         key,
126                         preferred_integer_tag = preferred_integer_tag,
127                         preferred_string_tag = preferred_string_tag,
128                     ),
129                     autotag(
130                         value,
131                         preferred_integer_tag = preferred_integer_tag,
132                         preferred_string_tag = preferred_string_tag,
133                     ),
134                 ) for key, value in o.items()
135             ]),
136         )
137
138     raise Exception('Unsupported type {}'.format(type(o)))