forked from PyCQA/redbaron
/
redbaron.py
327 lines (247 loc) · 10.9 KB
/
redbaron.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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
import sys
from types import ModuleType
from UserList import UserList
import baron
def indent(line, indentation):
return "\n".join(map(lambda x: indentation + x, line.split("\n")))
def to_node(node):
class_name = "".join(map(lambda x: x.capitalize(), node["type"].split("_"))) + "Node"
if class_name in globals():
return globals()[class_name](node)
else:
return type(class_name, (Node,), {})(node)
class NodeList(UserList):
def find(self, identifier, recursive=True, **kwargs):
for i in self.data:
candidate = i.find(identifier, recursive=recursive, **kwargs)
if candidate is not None:
return candidate
def __getattr__(self, key):
return self.find(key)
def find_all(self, identifier, recursive=True, **kwargs):
to_return = NodeList([])
for i in self.data:
to_return += i.find_all(identifier, recursive=recursive, **kwargs)
return to_return
findAll = find_all
__call__ = find_all
def fst(self):
return [x.fst() for x in self.data]
def dumps(self):
return baron.dumps(self.fst())
def __repr__(self):
to_return = ""
for number, value in enumerate(self.data):
to_return += ("%-3s " % number) + "\n ".join(value.__repr__().split("\n"))
to_return += "\n"
return to_return
return "%s" % [x.__repr__() for x in self.data]
def help(self):
for num, i in enumerate(self.data):
print num, "-----------------------------------------------------"
print i.__help__()
def __help__(self):
return [x.__help__() for x in self.data]
def copy(self):
# XXX not very optimised but at least very simple
return RedBaron(self.dumps())
class Node(object):
def __init__(self, node):
self.init = True
self._str_keys = []
self._list_keys = []
self._dict_keys = []
for key, value in node.items():
if isinstance(value, dict):
if value:
setattr(self, key, to_node(value))
else:
setattr(self, key, None)
self._dict_keys.append(key)
elif isinstance(value, list):
setattr(self, key, NodeList(map(to_node, value)))
self._list_keys.append(key)
else:
setattr(self, key, value)
self._str_keys.append(key)
self.init = False
def find(self, identifier, recursive=True, **kwargs):
all_my_keys = self._str_keys + self._list_keys + self._dict_keys
if identifier.lower() in self._generate_identifiers():
for key in kwargs:
if key not in all_my_keys:
break
if getattr(self, key) != kwargs[key]:
break
else: # else it match so the else clause will be used
# (for once that this else stuff is usefull)
return self
if not recursive:
return None
for i in self._dict_keys:
i = getattr(self, i)
if not i:
continue
found = i.find(identifier, recursive, **kwargs)
if found:
return found
for key in self._list_keys:
for i in getattr(self, key):
found = i.find(identifier, recursive, **kwargs)
if found:
return found
def __getattr__(self, key):
return self.find(key)
def find_all(self, identifier, recursive=True, **kwargs):
to_return = NodeList([])
all_my_keys = self._str_keys + self._list_keys + self._dict_keys
if identifier.lower() in self._generate_identifiers():
for key in kwargs:
if key not in all_my_keys:
break
if getattr(self, key) != kwargs[key]:
break
else: # else it match so the else clause will be used
# (for once that this else stuff is usefull)
to_return.append(self)
if not recursive:
return to_return
for i in self._dict_keys:
i = getattr(self, i)
if not i:
continue
to_return += i.find_all(identifier, recursive, **kwargs)
for key in self._list_keys:
for i in getattr(self, key):
to_return += i.find_all(identifier, recursive, **kwargs)
return to_return
findAll = find_all
__call__ = find_all
def _generate_identifiers(self):
return map(lambda x: x.lower(), [self.type, self.__class__.__name__, self.__class__.__name__.replace("Node", ""), self.type + "_"])
def fst(self):
to_return = {}
for key in self._str_keys:
to_return[key] = getattr(self, key)
for key in self._list_keys:
to_return[key] = [node.fst() for node in getattr(self, key)]
for key in self._dict_keys:
if getattr(self, key):
to_return[key] = getattr(self, key).fst()
else:
to_return[key] = {}
return to_return
def dumps(self):
return baron.dumps(self.fst())
def help(self, with_formatting=False):
print self.__help__(with_formatting)
def __help__(self, with_formatting=False):
to_join = ["%s()" % self.__class__.__name__]
to_join += ["%s=%s" % (key, repr(getattr(self, key))) for key in self._str_keys if key != "type" and "formatting" not in key]
to_join += ["%s ->\n %s" % (key, indent(getattr(self, key).__help__(), " ").lstrip() if getattr(self, key) else getattr(self, key)) for key in self._dict_keys if "formatting" not in key]
# need to do this otherwise I end up with stacked quoted list
# example: value=[\'DottedAsNameNode(target=\\\'None\\\', as=\\\'False\\\', value=DottedNameNode(value=["NameNode(value=\\\'pouet\\\')"])]
for key in filter(lambda x: "formatting" not in x, self._list_keys):
to_join.append(("%s ->" % key))
for i in getattr(self, key):
to_join.append(" * " + indent(i.__help__(), " ").lstrip())
if with_formatting:
to_join += ["%s=%s" % (key, repr(getattr(self, key))) for key in self._str_keys if key != "type" and "formatting" in key]
to_join += ["%s=%s" % (key, getattr(self, key).__help__() if getattr(self, key) else getattr(self, key)) for key in self._dict_keys if "formatting" in key]
for key in filter(lambda x: "formatting" in x, self._list_keys):
to_join.append(("%s ->" % key))
for i in getattr(self, key):
to_join.append(" * " + indent(i.__help__(), " ").lstrip())
return "\n ".join(to_join)
def __repr__(self):
return baron.dumps([self.fst()])
def copy(self):
# XXX not very optimised but at least very simple
return RedBaron(self.dumps())[0]
def __setattr__(self, name, value):
if name == "init" or self.init:
return super(Node, self).__setattr__(name, value)
if name in self._str_keys and not isinstance(value, (basestring, int)):
value = str(value)
elif name in self._dict_keys:
if isinstance(value, basestring):
value = RedBaron(value)[0]
if isinstance(value, dict): # assuming that we got some fst
value = to_node(value)
# TODO check attribution to raise error/warning?
elif name in self._list_keys:
if isinstance(value, basestring):
value = RedBaron(value)
elif isinstance(value, dict): # assuming that we got some fst
# also assuming the user do strange things
value = [to_node(value)]
elif isinstance(value, list) and not isinstance(value, NodeList):
# assume the user can pass a list of random stuff
new_value = []
for i in value:
if isinstance(i, basestring):
new_value.append(RedBaron(i)[0])
elif isinstance(i, dict): # assuming that we got some fst
new_value.append(to_node(i))
else:
new_value.append(i)
value = new_value
return super(Node, self).__setattr__(name, value)
class IntNode(Node):
def __init__(self, node):
super(IntNode, self).__init__(node)
self.value = int(self.value)
def fst(self):
return {
"type": "int",
"value": str(self.value),
"section": "number",
}
class EndlNode(Node):
def __repr__(self):
return repr(baron.dumps([self.fst()]))
class ImportNode(Node):
def modules(self):
return [x.value.dumps()for x in self('dotted_as_name')]
def names(self):
return [x.target if x.target else x.value.dumps() for x in self('dotted_as_name')]
class RedBaron(NodeList):
def __init__(self, source_code):
self.data = map(to_node, baron.parse(source_code))
# enter the black magic realm, beware of what you might find
# (in fact that's pretty simple appart from the strange stuff needed)
# this basically allows to write code like:
# from redbaron.nodes import WatheverNode
# and a new class with Node has the parent will be created on the fly
# if this class doesn't already exist (like IntNode for example)
# while this is horribly black magic, this allows quite some cool stuff
class MissingNodesBuilder(dict):
def __init__(self, globals, baked_args={}):
self.globals = globals
self.baked_args = baked_args
def __getitem__(self, key):
if key in self.globals:
return self.globals[key]
if key.endswith("Node"):
new_node_class = type(key, (Node,), {})
self.globals[key] = new_node_class
return new_node_class
class BlackMagicImportHook(ModuleType):
def __init__(self, self_module, baked_args={}):
# this code is directly inspired by amoffat/sh
# see https://github.com/amoffat/sh/blob/80af5726d8aa42017ced548abbd39b489068922a/sh.py#L1695
for attr in ["__builtins__", "__doc__", "__name__", "__package__"]:
setattr(self, attr, getattr(self_module, attr))
# python 3.2 (2.7 and 3.3 work fine) breaks on osx (not ubuntu)
# if we set this to None. and 3.3 needs a value for __path__
self.__path__ = []
self.self_module = self_module
self._env = MissingNodesBuilder(globals(), baked_args)
def __getattr__(self, name):
return self._env[name]
def __setattr__(self, name, value):
if hasattr(self, "_env"):
self._env[name] = value
ModuleType.__setattr__(self, name, value)
self = sys.modules[__name__]
sys.modules[__name__] = BlackMagicImportHook(self)