-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpiqi.py
214 lines (156 loc) · 4.99 KB
/
piqi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
import sys
import wrappers
import piqi_of_piq
import piqi_of_json
import piqi_to_json
# parse state
#
# TODO: make it less hacky
_parse_piqi_module = None
class ObjectProxy(wrappers.ObjectProxy):
def __init__(self, wrapped, piqi_type, loc):
super(ObjectProxy, self).__init__(wrapped)
self._self_loc = loc
self._self_piqi_type = piqi_type
self._self_piqi_module = _parse_piqi_module
@property
def __loc__(self):
return self._self_loc
@property
def __piqi_type__(self):
return self._self_piqi_type
@property
def __piqi_module__(self):
return self._self_piqi_module
def __repr__(self):
return repr(self.__wrapped__)
def unwrap_object(x):
if isinstance(x, ObjectProxy):
return unwrap_object(x.__wrapped__)
else:
return x
def make_record(fields, piqi_type, loc=None):
obj = Record(fields)
return ObjectProxy(obj, piqi_type, loc)
def make_list(items, piqi_type, loc=None):
obj = List(items)
return ObjectProxy(obj, piqi_type, loc)
def make_variant(tag, value, piqi_type, loc=None):
obj = Variant((Tag(tag), value))
return ObjectProxy(obj, piqi_type, loc)
def make_enum(tag, piqi_type, loc=None):
obj = Enum(tag)
return ObjectProxy(obj, piqi_type, loc)
def make_scalar(value, loc=None):
# XXX: why piqi_type would be None?
return ObjectProxy(value, None, loc)
def make_any(loc=None, **kwargs):
obj = Any(**kwargs)
return ObjectProxy(obj, 'piqi-any', loc)
# representation of a generic piqi object
#
# as e.g. returned by piqi_of_piq.parse()
class Record(object):
def __init__(self, fields):
for name, value in fields:
self.__setattr__(name, value)
def __repr__(self):
return repr(vars(self))
class List(list):
pass
class Variant(tuple):
pass
# TODO, XXX: what about aliases? we should probably support them as well
# XXX: anything else? int code?
class Tag(str):
# overriding str "constructor", for details see https://stackoverflow.com/questions/7255655/how-to-subclass-str-in-python/33272874#33272874
def __new__(cls, x):
return super(Tag, cls).__new__(cls, make_name(x))
class Enum(Tag):
pass
def make_name(x):
return str(x).replace('-', '_')
def make_field_name(field_spec):
piqi_name = name_of_field(field_spec)
name = make_name(piqi_name)
if field_spec['mode'] == 'repeated':
name = name + '_list'
return name
class Any(object):
def __init__(self, typename=None, piq_ast=None, json_ast=None):
self.typename = typename
self.piq_ast = piq_ast
self.json_ast = json_ast
self.obj = None
def __repr__(self):
return repr(vars(self))
#class ParseError(Exception):
class ParseError(RuntimeError):
def __init__(self, loc, error):
line = str(loc.line) if loc else 'unknown'
RuntimeError.__init__(self, line + ': ' + error)
self.error = error
self.loc = loc
def __repr__(self):
return error
# return one of built-in types, or None for user-defined types
#
# TODO: don't hardcode built-in piqi types, grab them from piqi self-spec as
# others piqic do
def get_piqi_type(typename):
piqi_type = None
if typename == 'bool':
piqi_type = 'bool'
elif typename == 'string':
piqi_type = 'string'
elif typename == 'binary':
piqi_type = 'binary'
elif typename == 'piqi-any':
piqi_type = 'any'
elif typename in ('int', 'uint',
'int32', 'uint32',
'int64', 'uint64',
'int32-fixed', 'uint32-fixed',
'int64-fixed', 'uint64-fixed',
'protobuf-int32', 'protobuf-int64'):
piqi_type = 'int'
elif typename in ('float32', 'float64', 'float'):
piqi_type = 'float'
return piqi_type
def is_piqi_type(typename):
return (get_piqi_type(typename) is not None)
# TODO: fix code duplication with piqi_of_piq
def name_of_field(t):
return t.get('name', t.get('type'))
def name_of_option(t):
return t.get('name', t.get('type'))
def unalias(typename):
piqi_type = get_piqi_type(typename)
if piqi_type:
return piqi_type, None
else:
type_tag, typedef = res = resolve_type(typename)
if type_tag == 'alias':
return unalias(typedef['type'])
else:
return res
# resolve user-defined type
def resolve_type(typename, piqi_module=None):
if piqi_module is None:
piqi_module = _parse_piqi_module
return piqi_module.typedef_index[typename]
def parse(x, module_name, typename, format='piq'):
# init parsing state
global _parse_piqi_module
_parse_piqi_module = sys.modules[module_name]
if format == 'piq':
return piqi_of_piq.parse(typename, x)
elif format == 'json':
return piqi_of_json.parse(typename, x)
else:
assert False
def gen(x, format='json'):
if format == 'json':
return piqi_to_json.gen(x)
else:
assert False