OLD | NEW |
(Empty) | |
| 1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
| 2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| 3 # copyright 2003-2010 Sylvain Thenault, all rights reserved. |
| 4 # contact mailto:thenault@gmail.com |
| 5 # |
| 6 # This file is part of logilab-astng. |
| 7 # |
| 8 # logilab-astng is free software: you can redistribute it and/or modify it |
| 9 # under the terms of the GNU Lesser General Public License as published by the |
| 10 # Free Software Foundation, either version 2.1 of the License, or (at your |
| 11 # option) any later version. |
| 12 # |
| 13 # logilab-astng is distributed in the hope that it will be useful, but |
| 14 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 15 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License |
| 16 # for more details. |
| 17 # |
| 18 # You should have received a copy of the GNU Lesser General Public License along |
| 19 # with logilab-astng. If not, see <http://www.gnu.org/licenses/>. |
| 20 """This module renders ASTNG nodes to string representation. |
| 21 |
| 22 It will probably not work on bare _ast trees. |
| 23 """ |
| 24 import sys |
| 25 |
| 26 |
| 27 INDENT = ' ' # 4 spaces ; keep indentation variable |
| 28 |
| 29 |
| 30 def _import_string(names): |
| 31 """return a list of (name, asname) formatted as a string""" |
| 32 _names = [] |
| 33 for name, asname in names: |
| 34 if asname is not None: |
| 35 _names.append('%s as %s' % (name, asname)) |
| 36 else: |
| 37 _names.append(name) |
| 38 return ', '.join(_names) |
| 39 |
| 40 |
| 41 class AsStringVisitor(object): |
| 42 """Visitor to render an ASTNG node as string """ |
| 43 |
| 44 def __call__(self, node): |
| 45 """Makes this visitor behave as a simple function""" |
| 46 return node.accept(self) |
| 47 |
| 48 def _stmt_list(self, stmts): |
| 49 """return a list of nodes to string""" |
| 50 stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if n
str]) |
| 51 return INDENT + stmts.replace('\n', '\n'+INDENT) |
| 52 |
| 53 |
| 54 ## visit_<node> methods ########################################### |
| 55 |
| 56 def visit_arguments(self, node): |
| 57 """return an astng.Function node as string""" |
| 58 return node.format_args() |
| 59 |
| 60 def visit_assattr(self, node): |
| 61 """return an astng.AssAttr node as string""" |
| 62 return self.visit_getattr(node) |
| 63 |
| 64 def visit_assert(self, node): |
| 65 """return an astng.Assert node as string""" |
| 66 if node.fail: |
| 67 return 'assert %s, %s' % (node.test.accept(self), |
| 68 node.fail.accept(self)) |
| 69 return 'assert %s' % node.test.accept(self) |
| 70 |
| 71 def visit_assname(self, node): |
| 72 """return an astng.AssName node as string""" |
| 73 return node.name |
| 74 |
| 75 def visit_assign(self, node): |
| 76 """return an astng.Assign node as string""" |
| 77 lhs = ' = '.join([n.accept(self) for n in node.targets]) |
| 78 return '%s = %s' % (lhs, node.value.accept(self)) |
| 79 |
| 80 def visit_augassign(self, node): |
| 81 """return an astng.AugAssign node as string""" |
| 82 return '%s %s %s' % (node.target.accept(self), node.op, node.value.accep
t(self)) |
| 83 |
| 84 def visit_backquote(self, node): |
| 85 """return an astng.Backquote node as string""" |
| 86 return '`%s`' % node.value.accept(self) |
| 87 |
| 88 def visit_binop(self, node): |
| 89 """return an astng.BinOp node as string""" |
| 90 return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.acc
ept(self)) |
| 91 |
| 92 def visit_boolop(self, node): |
| 93 """return an astng.BoolOp node as string""" |
| 94 return (' %s ' % node.op).join(['(%s)' % n.accept(self) |
| 95 for n in node.values]) |
| 96 |
| 97 def visit_break(self, node): |
| 98 """return an astng.Break node as string""" |
| 99 return 'break' |
| 100 |
| 101 def visit_callfunc(self, node): |
| 102 """return an astng.CallFunc node as string""" |
| 103 expr_str = node.func.accept(self) |
| 104 args = [arg.accept(self) for arg in node.args] |
| 105 if node.starargs: |
| 106 args.append( '*' + node.starargs.accept(self)) |
| 107 if node.kwargs: |
| 108 args.append( '**' + node.kwargs.accept(self)) |
| 109 return '%s(%s)' % (expr_str, ', '.join(args)) |
| 110 |
| 111 def visit_class(self, node): |
| 112 """return an astng.Class node as string""" |
| 113 decorate = node.decorators and node.decorators.accept(self) or '' |
| 114 bases = ', '.join([n.accept(self) for n in node.bases]) |
| 115 bases = bases and '(%s)' % bases or '' |
| 116 docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' |
| 117 return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs, |
| 118 self._stmt_list( node.body)) |
| 119 |
| 120 def visit_compare(self, node): |
| 121 """return an astng.Compare node as string""" |
| 122 rhs_str = ' '.join(['%s %s' % (op, expr.accept(self)) |
| 123 for op, expr in node.ops]) |
| 124 return '%s %s' % (node.left.accept(self), rhs_str) |
| 125 |
| 126 def visit_comprehension(self, node): |
| 127 """return an astng.Comprehension node as string""" |
| 128 ifs = ''.join([ ' if %s' % n.accept(self) for n in node.ifs]) |
| 129 return 'for %s in %s%s' % (node.target.accept(self), |
| 130 node.iter.accept(self), ifs ) |
| 131 |
| 132 def visit_const(self, node): |
| 133 """return an astng.Const node as string""" |
| 134 return repr(node.value) |
| 135 |
| 136 def visit_continue(self, node): |
| 137 """return an astng.Continue node as string""" |
| 138 return 'continue' |
| 139 |
| 140 def visit_delete(self, node): # XXX check if correct |
| 141 """return an astng.Delete node as string""" |
| 142 return 'del %s' % ', '.join([child.accept(self) |
| 143 for child in node.targets]) |
| 144 |
| 145 def visit_delattr(self, node): |
| 146 """return an astng.DelAttr node as string""" |
| 147 return self.visit_getattr(node) |
| 148 |
| 149 def visit_delname(self, node): |
| 150 """return an astng.DelName node as string""" |
| 151 return node.name |
| 152 |
| 153 def visit_decorators(self, node): |
| 154 """return an astng.Decorators node as string""" |
| 155 return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes]) |
| 156 |
| 157 def visit_dict(self, node): |
| 158 """return an astng.Dict node as string""" |
| 159 return '{%s}' % ', '.join(['%s: %s' % (key.accept(self), |
| 160 value.accept(self)) for key, value in node.items]) |
| 161 |
| 162 def visit_dictcomp(self, node): |
| 163 """return an astng.DictComp node as string""" |
| 164 return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self), |
| 165 ' '.join([n.accept(self) for n in node.generators])) |
| 166 |
| 167 def visit_discard(self, node): |
| 168 """return an astng.Discard node as string""" |
| 169 return node.value.accept(self) |
| 170 |
| 171 def visit_emptynode(self, node): |
| 172 """dummy method for visiting an Empty node""" |
| 173 return '' |
| 174 |
| 175 def visit_excepthandler(self, node): |
| 176 if node.type: |
| 177 if node.name: |
| 178 excs = 'except %s, %s' % (node.type.accept(self), |
| 179 node.name.accept(self)) |
| 180 else: |
| 181 excs = 'except %s' % node.type.accept(self) |
| 182 else: |
| 183 excs = 'except' |
| 184 return '%s:\n%s' % (excs, self._stmt_list(node.body)) |
| 185 |
| 186 def visit_ellipsis(self, node): |
| 187 """return an astng.Ellipsis node as string""" |
| 188 return '...' |
| 189 |
| 190 def visit_empty(self, node): |
| 191 """return an Empty node as string""" |
| 192 return '' |
| 193 |
| 194 def visit_exec(self, node): |
| 195 """return an astng.Exec node as string""" |
| 196 if node.locals: |
| 197 return 'exec %s in %s, %s' % (node.expr.accept(self), |
| 198 node.locals.accept(self), |
| 199 node.globals.accept(self)) |
| 200 if node.globals: |
| 201 return 'exec %s in %s' % (node.expr.accept(self), |
| 202 node.globals.accept(self)) |
| 203 return 'exec %s' % node.expr.accept(self) |
| 204 |
| 205 def visit_extslice(self, node): |
| 206 """return an astng.ExtSlice node as string""" |
| 207 return ','.join( [dim.accept(self) for dim in node.dims] ) |
| 208 |
| 209 def visit_for(self, node): |
| 210 """return an astng.For node as string""" |
| 211 fors = 'for %s in %s:\n%s' % (node.target.accept(self), |
| 212 node.iter.accept(self), |
| 213 self._stmt_list( node.body)) |
| 214 if node.orelse: |
| 215 fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse)) |
| 216 return fors |
| 217 |
| 218 def visit_from(self, node): |
| 219 """return an astng.From node as string""" |
| 220 return 'from %s import %s' % ('.' * (node.level or 0) + node.modname, |
| 221 _import_string(node.names)) |
| 222 |
| 223 def visit_function(self, node): |
| 224 """return an astng.Function node as string""" |
| 225 decorate = node.decorators and node.decorators.accept(self) or '' |
| 226 docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' |
| 227 return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(
self), |
| 228 docs, self._stmt_list(node.body)) |
| 229 |
| 230 def visit_genexpr(self, node): |
| 231 """return an astng.GenExpr node as string""" |
| 232 return '(%s %s)' % (node.elt.accept(self), ' '.join([n.accept(self) |
| 233 for n in node.generators])) |
| 234 |
| 235 def visit_getattr(self, node): |
| 236 """return an astng.Getattr node as string""" |
| 237 return '%s.%s' % (node.expr.accept(self), node.attrname) |
| 238 |
| 239 def visit_global(self, node): |
| 240 """return an astng.Global node as string""" |
| 241 return 'global %s' % ', '.join(node.names) |
| 242 |
| 243 def visit_if(self, node): |
| 244 """return an astng.If node as string""" |
| 245 ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body
))] |
| 246 if node.orelse:# XXX use elif ??? |
| 247 ifs.append('else:\n%s' % self._stmt_list(node.orelse)) |
| 248 return '\n'.join(ifs) |
| 249 |
| 250 def visit_ifexp(self, node): |
| 251 """return an astng.IfExp node as string""" |
| 252 return '%s if %s else %s' % (node.body.accept(self), |
| 253 node.test.accept(self), node.orelse.accept(self)) |
| 254 |
| 255 def visit_import(self, node): |
| 256 """return an astng.Import node as string""" |
| 257 return 'import %s' % _import_string(node.names) |
| 258 |
| 259 def visit_keyword(self, node): |
| 260 """return an astng.Keyword node as string""" |
| 261 return '%s=%s' % (node.arg, node.value.accept(self)) |
| 262 |
| 263 def visit_lambda(self, node): |
| 264 """return an astng.Lambda node as string""" |
| 265 return 'lambda %s: %s' % (node.args.accept(self), node.body.accept(self)
) |
| 266 |
| 267 def visit_list(self, node): |
| 268 """return an astng.List node as string""" |
| 269 return '[%s]' % ', '.join([child.accept(self) for child in node.elts]) |
| 270 |
| 271 def visit_listcomp(self, node): |
| 272 """return an astng.ListComp node as string""" |
| 273 return '[%s %s]' % (node.elt.accept(self), ' '.join([n.accept(self) |
| 274 for n in node.generators])) |
| 275 |
| 276 def visit_module(self, node): |
| 277 """return an astng.Module node as string""" |
| 278 docs = node.doc and '"""%s"""\n\n' % node.doc or '' |
| 279 return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n' |
| 280 |
| 281 def visit_name(self, node): |
| 282 """return an astng.Name node as string""" |
| 283 return node.name |
| 284 |
| 285 def visit_pass(self, node): |
| 286 """return an astng.Pass node as string""" |
| 287 return 'pass' |
| 288 |
| 289 def visit_print(self, node): |
| 290 """return an astng.Print node as string""" |
| 291 nodes = ', '.join([n.accept(self) for n in node.values]) |
| 292 if not node.nl: |
| 293 nodes = '%s,' % nodes |
| 294 if node.dest: |
| 295 return 'print >> %s, %s' % (node.dest.accept(self), nodes) |
| 296 return 'print %s' % nodes |
| 297 |
| 298 def visit_raise(self, node): |
| 299 """return an astng.Raise node as string""" |
| 300 if node.exc: |
| 301 if node.inst: |
| 302 if node.tback: |
| 303 return 'raise %s, %s, %s' % (node.exc.accept(self), |
| 304 node.inst.accept(self), |
| 305 node.tback.accept(self)) |
| 306 return 'raise %s, %s' % (node.exc.accept(self), |
| 307 node.inst.accept(self)) |
| 308 return 'raise %s' % node.exc.accept(self) |
| 309 return 'raise' |
| 310 |
| 311 def visit_return(self, node): |
| 312 """return an astng.Return node as string""" |
| 313 if node.value: |
| 314 return 'return %s' % node.value.accept(self) |
| 315 else: |
| 316 return 'return' |
| 317 |
| 318 def visit_index(self, node): |
| 319 """return a astng.Index node as string""" |
| 320 return node.value.accept(self) |
| 321 |
| 322 def visit_set(self, node): |
| 323 """return an astng.Set node as string""" |
| 324 return '{%s}' % ', '.join([child.accept(self) for child in node.elts]) |
| 325 |
| 326 def visit_setcomp(self, node): |
| 327 """return an astng.SetComp node as string""" |
| 328 return '{%s %s}' % (node.elt.accept(self), ' '.join([n.accept(self) |
| 329 for n in node.generators])) |
| 330 |
| 331 def visit_slice(self, node): |
| 332 """return a astng.Slice node as string""" |
| 333 lower = node.lower and node.lower.accept(self) or '' |
| 334 upper = node.upper and node.upper.accept(self) or '' |
| 335 step = node.step and node.step.accept(self) or '' |
| 336 if step: |
| 337 return '%s:%s:%s' % (lower, upper, step) |
| 338 return '%s:%s' % (lower, upper) |
| 339 |
| 340 def visit_subscript(self, node): |
| 341 """return an astng.Subscript node as string""" |
| 342 return '%s[%s]' % (node.value.accept(self), node.slice.accept(self)) |
| 343 |
| 344 def visit_tryexcept(self, node): |
| 345 """return an astng.TryExcept node as string""" |
| 346 trys = ['try:\n%s' % self._stmt_list( node.body)] |
| 347 for handler in node.handlers: |
| 348 trys.append(handler.accept(self)) |
| 349 if node.orelse: |
| 350 trys.append('else:\n%s' % self._stmt_list(node.orelse)) |
| 351 return '\n'.join(trys) |
| 352 |
| 353 def visit_tryfinally(self, node): |
| 354 """return an astng.TryFinally node as string""" |
| 355 return 'try:\n%s\nfinally:\n%s' % (self._stmt_list( node.body), |
| 356 self._stmt_list(node.finalbody)) |
| 357 |
| 358 def visit_tuple(self, node): |
| 359 """return an astng.Tuple node as string""" |
| 360 return '(%s)' % ', '.join([child.accept(self) for child in node.elts]) |
| 361 |
| 362 def visit_unaryop(self, node): |
| 363 """return an astng.UnaryOp node as string""" |
| 364 if node.op == 'not': |
| 365 operator = 'not ' |
| 366 else: |
| 367 operator = node.op |
| 368 return '%s%s' % (operator, node.operand.accept(self)) |
| 369 |
| 370 def visit_while(self, node): |
| 371 """return an astng.While node as string""" |
| 372 whiles = 'while %s:\n%s' % (node.test.accept(self), |
| 373 self._stmt_list(node.body)) |
| 374 if node.orelse: |
| 375 whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse)) |
| 376 return whiles |
| 377 |
| 378 def visit_with(self, node): # 'with' without 'as' is possible |
| 379 """return an astng.With node as string""" |
| 380 as_var = node.vars and " as (%s)" % (node.vars.accept(self)) or "" |
| 381 withs = 'with (%s)%s:\n%s' % (node.expr.accept(self), as_var, |
| 382 self._stmt_list( node.body)) |
| 383 return withs |
| 384 |
| 385 def visit_yield(self, node): |
| 386 """yield an ast.Yield node as string""" |
| 387 yi_val = node.value and (" " + node.value.accept(self)) or "" |
| 388 return 'yield' + yi_val |
| 389 |
| 390 |
| 391 class AsStringVisitor3k(AsStringVisitor): |
| 392 """AsStringVisitor3k overwrites some AsStringVisitor methods""" |
| 393 |
| 394 def visit_excepthandler(self, node): |
| 395 if node.type: |
| 396 if node.name: |
| 397 excs = 'except %s as %s' % (node.type.accept(self), |
| 398 node.name.accept(self)) |
| 399 else: |
| 400 excs = 'except %s' % node.type.accept(self) |
| 401 else: |
| 402 excs = 'except' |
| 403 return '%s:\n%s' % (excs, self._stmt_list(node.body)) |
| 404 |
| 405 def visit_nonlocal(self, node): |
| 406 """return an astng.Nonlocal node as string""" |
| 407 return 'nonlocal %s' % ', '.join(node.names) |
| 408 |
| 409 def visit_raise(self, node): |
| 410 """return an astng.Raise node as string""" |
| 411 if node.exc: |
| 412 if node.cause: |
| 413 return 'raise %s from %s' % (node.exc.accept(self), |
| 414 node.cause.accept(self)) |
| 415 return 'raise %s' % node.exc.accept(self) |
| 416 return 'raise' |
| 417 |
| 418 def visit_starred(self, node): |
| 419 """return Starred node as string""" |
| 420 return "*" + node.value.accept(self) |
| 421 |
| 422 if sys.version_info >= (3, 0): |
| 423 AsStringVisitor = AsStringVisitor3k |
| 424 |
| 425 # this visitor is stateless, thus it can be reused |
| 426 as_string = AsStringVisitor() |
| 427 |
OLD | NEW |