1
# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
4
# This file is part of logilab-astng.
6
# logilab-astng is free software: you can redistribute it and/or modify it
7
# under the terms of the GNU Lesser General Public License as published by the
8
# Free Software Foundation, either version 2.1 of the License, or (at your
9
# option) any later version.
11
# logilab-astng is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16
# You should have received a copy of the GNU Lesser General Public License along
17
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
18
"""This module renders ASTNG nodes as string:
20
* :func:`to_code` function return equivalent (hopefuly valid) python string
22
* :func:`dump` function return an internal representation of nodes found
23
in the tree, useful for debugging or understanding the tree structure
28
INDENT = ' ' # 4 spaces ; keep indentation variable
31
def dump(node, ids=False):
32
"""print a nice astng tree representation.
34
:param ids: if true, we also print the ids (usefull for debugging)
37
_repr_tree(node, result, ids=ids)
38
return "\n".join(result)
40
def _repr_tree(node, result, indent='', _done=None, ids=False):
41
"""built a tree representation of a node as a list of lines"""
44
if not hasattr(node, '_astng_fields'): # not a astng node
47
result.append( indent + 'loop in tree: %s' % node )
52
node_str += ' . \t%x' % id(node)
53
result.append( indent + node_str )
55
for field in node._astng_fields:
56
value = getattr(node, field)
57
if isinstance(value, (list, tuple) ):
58
result.append( indent + field + " = [" )
60
if isinstance(child, (list, tuple) ):
61
# special case for Dict # FIXME
62
_repr_tree(child[0], result, indent, _done, ids)
63
_repr_tree(child[1], result, indent, _done, ids)
64
result.append(indent + ',')
66
_repr_tree(child, result, indent, _done, ids)
67
result.append( indent + "]" )
69
result.append( indent + field + " = " )
70
_repr_tree(value, result, indent, _done, ids)
73
class AsStringVisitor(object):
74
"""Visitor to render an ASTNG node as a valid python code string"""
76
def __call__(self, node):
77
"""Makes this visitor behave as a simple function"""
78
return node.accept(self)
80
def _stmt_list(self, stmts):
81
"""return a list of nodes to string"""
82
stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr])
83
return INDENT + stmts.replace('\n', '\n'+INDENT)
86
## visit_<node> methods ###########################################
88
def visit_arguments(self, node):
89
"""return an astng.Function node as string"""
90
return node.format_args()
92
def visit_assattr(self, node):
93
"""return an astng.AssAttr node as string"""
94
return self.visit_getattr(node)
96
def visit_assert(self, node):
97
"""return an astng.Assert node as string"""
99
return 'assert %s, %s' % (node.test.accept(self),
100
node.fail.accept(self))
101
return 'assert %s' % node.test.accept(self)
103
def visit_assname(self, node):
104
"""return an astng.AssName node as string"""
107
def visit_assign(self, node):
108
"""return an astng.Assign node as string"""
109
lhs = ' = '.join([n.accept(self) for n in node.targets])
110
return '%s = %s' % (lhs, node.value.accept(self))
112
def visit_augassign(self, node):
113
"""return an astng.AugAssign node as string"""
114
return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self))
116
def visit_backquote(self, node):
117
"""return an astng.Backquote node as string"""
118
return '`%s`' % node.value.accept(self)
120
def visit_binop(self, node):
121
"""return an astng.BinOp node as string"""
122
return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self))
124
def visit_boolop(self, node):
125
"""return an astng.BoolOp node as string"""
126
return (' %s ' % node.op).join(['(%s)' % n.accept(self)
127
for n in node.values])
129
def visit_break(self, node):
130
"""return an astng.Break node as string"""
133
def visit_callfunc(self, node):
134
"""return an astng.CallFunc node as string"""
135
expr_str = node.func.accept(self)
136
args = [arg.accept(self) for arg in node.args]
138
args.append( '*' + node.starargs.accept(self))
140
args.append( '**' + node.kwargs.accept(self))
141
return '%s(%s)' % (expr_str, ', '.join(args))
143
def visit_class(self, node):
144
"""return an astng.Class node as string"""
145
decorate = node.decorators and node.decorators.accept(self) or ''
146
bases = ', '.join([n.accept(self) for n in node.bases])
147
bases = bases and '(%s)' % bases or ''
148
docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or ''
149
return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs,
150
self._stmt_list( node.body))
152
def visit_compare(self, node):
153
"""return an astng.Compare node as string"""
154
rhs_str = ' '.join(['%s %s' % (op, expr.accept(self))
155
for op, expr in node.ops])
156
return '%s %s' % (node.left.accept(self), rhs_str)
158
def visit_comprehension(self, node):
159
"""return an astng.Comprehension node as string"""
160
ifs = ''.join([ ' if %s' % n.accept(self) for n in node.ifs])
161
return 'for %s in %s%s' % (node.target.accept(self),
162
node.iter.accept(self), ifs )
164
def visit_const(self, node):
165
"""return an astng.Const node as string"""
166
return repr(node.value)
168
def visit_continue(self, node):
169
"""return an astng.Continue node as string"""
172
def visit_delete(self, node): # XXX check if correct
173
"""return an astng.Delete node as string"""
174
return 'del %s' % ', '.join([child.accept(self)
175
for child in node.targets])
177
def visit_delattr(self, node):
178
"""return an astng.DelAttr node as string"""
179
return self.visit_getattr(node)
181
def visit_delname(self, node):
182
"""return an astng.DelName node as string"""
185
def visit_decorators(self, node):
186
"""return an astng.Decorators node as string"""
187
return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes])
189
def visit_dict(self, node):
190
"""return an astng.Dict node as string"""
191
return '{%s}' % ', '.join(['%s: %s' % (key.accept(self),
192
value.accept(self)) for key, value in node.items])
194
def visit_dictcomp(self, node):
195
"""return an astng.DictComp node as string"""
196
return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self),
197
' '.join([n.accept(self) for n in node.generators]))
199
def visit_discard(self, node):
200
"""return an astng.Discard node as string"""
201
return node.value.accept(self)
203
def visit_emptynode(self, node):
204
"""dummy method for visiting an Empty node"""
207
def visit_excepthandler(self, node):
210
excs = 'except %s, %s' % (node.type.accept(self),
211
node.name.accept(self))
213
excs = 'except %s' % node.type.accept(self)
216
return '%s:\n%s' % (excs, self._stmt_list(node.body))
218
def visit_ellipsis(self, node):
219
"""return an astng.Ellipsis node as string"""
222
def visit_empty(self, node):
223
"""return an Empty node as string"""
226
def visit_exec(self, node):
227
"""return an astng.Exec node as string"""
229
return 'exec %s in %s, %s' % (node.expr.accept(self),
230
node.locals.accept(self),
231
node.globals.accept(self))
233
return 'exec %s in %s' % (node.expr.accept(self),
234
node.globals.accept(self))
235
return 'exec %s' % node.expr.accept(self)
237
def visit_extslice(self, node):
238
"""return an astng.ExtSlice node as string"""
239
return ','.join( [dim.accept(self) for dim in node.dims] )
241
def visit_for(self, node):
242
"""return an astng.For node as string"""
243
fors = 'for %s in %s:\n%s' % (node.target.accept(self),
244
node.iter.accept(self),
245
self._stmt_list( node.body))
247
fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse))
250
def visit_from(self, node):
251
"""return an astng.From node as string"""
252
return 'from %s import %s' % ('.' * (node.level or 0) + node.modname,
253
_import_string(node.names))
255
def visit_function(self, node):
256
"""return an astng.Function node as string"""
257
decorate = node.decorators and node.decorators.accept(self) or ''
258
docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or ''
259
return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(self),
260
docs, self._stmt_list(node.body))
262
def visit_genexpr(self, node):
263
"""return an astng.GenExpr node as string"""
264
return '(%s %s)' % (node.elt.accept(self), ' '.join([n.accept(self)
265
for n in node.generators]))
267
def visit_getattr(self, node):
268
"""return an astng.Getattr node as string"""
269
return '%s.%s' % (node.expr.accept(self), node.attrname)
271
def visit_global(self, node):
272
"""return an astng.Global node as string"""
273
return 'global %s' % ', '.join(node.names)
275
def visit_if(self, node):
276
"""return an astng.If node as string"""
277
ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))]
278
if node.orelse:# XXX use elif ???
279
ifs.append('else:\n%s' % self._stmt_list(node.orelse))
280
return '\n'.join(ifs)
282
def visit_ifexp(self, node):
283
"""return an astng.IfExp node as string"""
284
return '%s if %s else %s' % (node.body.accept(self),
285
node.test.accept(self), node.orelse.accept(self))
287
def visit_import(self, node):
288
"""return an astng.Import node as string"""
289
return 'import %s' % _import_string(node.names)
291
def visit_keyword(self, node):
292
"""return an astng.Keyword node as string"""
293
return '%s=%s' % (node.arg, node.value.accept(self))
295
def visit_lambda(self, node):
296
"""return an astng.Lambda node as string"""
297
return 'lambda %s: %s' % (node.args.accept(self), node.body.accept(self))
299
def visit_list(self, node):
300
"""return an astng.List node as string"""
301
return '[%s]' % ', '.join([child.accept(self) for child in node.elts])
303
def visit_listcomp(self, node):
304
"""return an astng.ListComp node as string"""
305
return '[%s %s]' % (node.elt.accept(self), ' '.join([n.accept(self)
306
for n in node.generators]))
308
def visit_module(self, node):
309
"""return an astng.Module node as string"""
310
docs = node.doc and '"""%s"""\n\n' % node.doc or ''
311
return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n'
313
def visit_name(self, node):
314
"""return an astng.Name node as string"""
317
def visit_pass(self, node):
318
"""return an astng.Pass node as string"""
321
def visit_print(self, node):
322
"""return an astng.Print node as string"""
323
nodes = ', '.join([n.accept(self) for n in node.values])
325
nodes = '%s,' % nodes
327
return 'print >> %s, %s' % (node.dest.accept(self), nodes)
328
return 'print %s' % nodes
330
def visit_raise(self, node):
331
"""return an astng.Raise node as string"""
335
return 'raise %s, %s, %s' % (node.exc.accept(self),
336
node.inst.accept(self),
337
node.tback.accept(self))
338
return 'raise %s, %s' % (node.exc.accept(self),
339
node.inst.accept(self))
340
return 'raise %s' % node.exc.accept(self)
343
def visit_return(self, node):
344
"""return an astng.Return node as string"""
346
return 'return %s' % node.value.accept(self)
350
def visit_index(self, node):
351
"""return a astng.Index node as string"""
352
return node.value.accept(self)
354
def visit_set(self, node):
355
"""return an astng.Set node as string"""
356
return '{%s}' % ', '.join([child.accept(self) for child in node.elts])
358
def visit_setcomp(self, node):
359
"""return an astng.SetComp node as string"""
360
return '{%s %s}' % (node.elt.accept(self), ' '.join([n.accept(self)
361
for n in node.generators]))
363
def visit_slice(self, node):
364
"""return a astng.Slice node as string"""
365
lower = node.lower and node.lower.accept(self) or ''
366
upper = node.upper and node.upper.accept(self) or ''
367
step = node.step and node.step.accept(self) or ''
369
return '%s:%s:%s' % (lower, upper, step)
370
return '%s:%s' % (lower, upper)
372
def visit_subscript(self, node):
373
"""return an astng.Subscript node as string"""
374
return '%s[%s]' % (node.value.accept(self), node.slice.accept(self))
376
def visit_tryexcept(self, node):
377
"""return an astng.TryExcept node as string"""
378
trys = ['try:\n%s' % self._stmt_list( node.body)]
379
for handler in node.handlers:
380
trys.append(handler.accept(self))
382
trys.append('else:\n%s' % self._stmt_list(node.orelse))
383
return '\n'.join(trys)
385
def visit_tryfinally(self, node):
386
"""return an astng.TryFinally node as string"""
387
return 'try:\n%s\nfinally:\n%s' % (self._stmt_list( node.body),
388
self._stmt_list(node.finalbody))
390
def visit_tuple(self, node):
391
"""return an astng.Tuple node as string"""
392
return '(%s)' % ', '.join([child.accept(self) for child in node.elts])
394
def visit_unaryop(self, node):
395
"""return an astng.UnaryOp node as string"""
400
return '%s%s' % (operator, node.operand.accept(self))
402
def visit_while(self, node):
403
"""return an astng.While node as string"""
404
whiles = 'while %s:\n%s' % (node.test.accept(self),
405
self._stmt_list(node.body))
407
whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse))
410
def visit_with(self, node): # 'with' without 'as' is possible
411
"""return an astng.With node as string"""
412
as_var = node.vars and " as (%s)" % (node.vars.accept(self)) or ""
413
withs = 'with (%s)%s:\n%s' % (node.expr.accept(self), as_var,
414
self._stmt_list( node.body))
417
def visit_yield(self, node):
418
"""yield an ast.Yield node as string"""
419
yi_val = node.value and (" " + node.value.accept(self)) or ""
420
expr = 'yield' + yi_val
421
if node.parent.is_statement:
424
return "(%s)" % (expr,)
427
class AsStringVisitor3k(AsStringVisitor):
428
"""AsStringVisitor3k overwrites some AsStringVisitor methods"""
430
def visit_excepthandler(self, node):
433
excs = 'except %s as %s' % (node.type.accept(self),
434
node.name.accept(self))
436
excs = 'except %s' % node.type.accept(self)
439
return '%s:\n%s' % (excs, self._stmt_list(node.body))
441
def visit_nonlocal(self, node):
442
"""return an astng.Nonlocal node as string"""
443
return 'nonlocal %s' % ', '.join(node.names)
445
def visit_raise(self, node):
446
"""return an astng.Raise node as string"""
449
return 'raise %s from %s' % (node.exc.accept(self),
450
node.cause.accept(self))
451
return 'raise %s' % node.exc.accept(self)
454
def visit_starred(self, node):
455
"""return Starred node as string"""
456
return "*" + node.value.accept(self)
459
def _import_string(names):
460
"""return a list of (name, asname) formatted as a string"""
462
for name, asname in names:
463
if asname is not None:
464
_names.append('%s as %s' % (name, asname))
467
return ', '.join(_names)
470
if sys.version_info >= (3, 0):
471
AsStringVisitor = AsStringVisitor3k
473
# this visitor is stateless, thus it can be reused
474
to_code = AsStringVisitor()