as_string.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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. It will probably not work on bare _ast trees.
  22. """
  23. import sys
  24. INDENT = ' ' # 4 spaces ; keep indentation variable
  25. def _import_string(names):
  26. """return a list of (name, asname) formatted as a string"""
  27. _names = []
  28. for name, asname in names:
  29. if asname is not None:
  30. _names.append('%s as %s' % (name, asname))
  31. else:
  32. _names.append(name)
  33. return ', '.join(_names)
  34. class AsStringVisitor(object):
  35. """Visitor to render an ASTNG node as string """
  36. def __call__(self, node):
  37. """Makes this visitor behave as a simple function"""
  38. return node.accept(self)
  39. def _stmt_list(self, stmts):
  40. """return a list of nodes to string"""
  41. stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr])
  42. return INDENT + stmts.replace('\n', '\n'+INDENT)
  43. ## visit_<node> methods ###########################################
  44. def visit_arguments(self, node):
  45. """return an astng.Function node as string"""
  46. return node.format_args()
  47. def visit_assattr(self, node):
  48. """return an astng.AssAttr node as string"""
  49. return self.visit_getattr(node)
  50. def visit_assert(self, node):
  51. """return an astng.Assert node as string"""
  52. if node.fail:
  53. return 'assert %s, %s' % (node.test.accept(self),
  54. node.fail.accept(self))
  55. return 'assert %s' % node.test.accept(self)
  56. def visit_assname(self, node):
  57. """return an astng.AssName node as string"""
  58. return node.name
  59. def visit_assign(self, node):
  60. """return an astng.Assign node as string"""
  61. lhs = ' = '.join([n.accept(self) for n in node.targets])
  62. return '%s = %s' % (lhs, node.value.accept(self))
  63. def visit_augassign(self, node):
  64. """return an astng.AugAssign node as string"""
  65. return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self))
  66. def visit_backquote(self, node):
  67. """return an astng.Backquote node as string"""
  68. return '`%s`' % node.value.accept(self)
  69. def visit_binop(self, node):
  70. """return an astng.BinOp node as string"""
  71. return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self))
  72. def visit_boolop(self, node):
  73. """return an astng.BoolOp node as string"""
  74. return (' %s ' % node.op).join(['(%s)' % n.accept(self)
  75. for n in node.values])
  76. def visit_break(self, node):
  77. """return an astng.Break node as string"""
  78. return 'break'
  79. def visit_callfunc(self, node):
  80. """return an astng.CallFunc node as string"""
  81. expr_str = node.func.accept(self)
  82. args = [arg.accept(self) for arg in node.args]
  83. if node.starargs:
  84. args.append( '*' + node.starargs.accept(self))
  85. if node.kwargs:
  86. args.append( '**' + node.kwargs.accept(self))
  87. return '%s(%s)' % (expr_str, ', '.join(args))
  88. def visit_class(self, node):
  89. """return an astng.Class node as string"""
  90. decorate = node.decorators and node.decorators.accept(self) or ''
  91. bases = ', '.join([n.accept(self) for n in node.bases])
  92. bases = bases and '(%s)' % bases or ''
  93. docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or ''
  94. return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs,
  95. self._stmt_list( node.body))
  96. def visit_compare(self, node):
  97. """return an astng.Compare node as string"""
  98. rhs_str = ' '.join(['%s %s' % (op, expr.accept(self))
  99. for op, expr in node.ops])
  100. return '%s %s' % (node.left.accept(self), rhs_str)
  101. def visit_comprehension(self, node):
  102. """return an astng.Comprehension node as string"""
  103. ifs = ''.join([ ' if %s' % n.accept(self) for n in node.ifs])
  104. return 'for %s in %s%s' % (node.target.accept(self),
  105. node.iter.accept(self), ifs )
  106. def visit_const(self, node):
  107. """return an astng.Const node as string"""
  108. return repr(node.value)
  109. def visit_continue(self, node):
  110. """return an astng.Continue node as string"""
  111. return 'continue'
  112. def visit_delete(self, node): # XXX check if correct
  113. """return an astng.Delete node as string"""
  114. return 'del %s' % ', '.join([child.accept(self)
  115. for child in node.targets])
  116. def visit_delattr(self, node):
  117. """return an astng.DelAttr node as string"""
  118. return self.visit_getattr(node)
  119. def visit_delname(self, node):
  120. """return an astng.DelName node as string"""
  121. return node.name
  122. def visit_decorators(self, node):
  123. """return an astng.Decorators node as string"""
  124. return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes])
  125. def visit_dict(self, node):
  126. """return an astng.Dict node as string"""
  127. return '{%s}' % ', '.join(['%s: %s' % (key.accept(self),
  128. value.accept(self)) for key, value in node.items])
  129. def visit_dictcomp(self, node):
  130. """return an astng.DictComp node as string"""
  131. return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self),
  132. ' '.join([n.accept(self) for n in node.generators]))
  133. def visit_discard(self, node):
  134. """return an astng.Discard node as string"""
  135. return node.value.accept(self)
  136. def visit_emptynode(self, node):
  137. """dummy method for visiting an Empty node"""
  138. return ''
  139. def visit_excepthandler(self, node):
  140. if node.type:
  141. if node.name:
  142. excs = 'except %s, %s' % (node.type.accept(self),
  143. node.name.accept(self))
  144. else:
  145. excs = 'except %s' % node.type.accept(self)
  146. else:
  147. excs = 'except'
  148. return '%s:\n%s' % (excs, self._stmt_list(node.body))
  149. def visit_ellipsis(self, node):
  150. """return an astng.Ellipsis node as string"""
  151. return '...'
  152. def visit_empty(self, node):
  153. """return an Empty node as string"""
  154. return ''
  155. def visit_exec(self, node):
  156. """return an astng.Exec node as string"""
  157. if node.locals:
  158. return 'exec %s in %s, %s' % (node.expr.accept(self),
  159. node.locals.accept(self),
  160. node.globals.accept(self))
  161. if node.globals:
  162. return 'exec %s in %s' % (node.expr.accept(self),
  163. node.globals.accept(self))
  164. return 'exec %s' % node.expr.accept(self)
  165. def visit_extslice(self, node):
  166. """return an astng.ExtSlice node as string"""
  167. return ','.join( [dim.accept(self) for dim in node.dims] )
  168. def visit_for(self, node):
  169. """return an astng.For node as string"""
  170. fors = 'for %s in %s:\n%s' % (node.target.accept(self),
  171. node.iter.accept(self),
  172. self._stmt_list( node.body))
  173. if node.orelse:
  174. fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse))
  175. return fors
  176. def visit_from(self, node):
  177. """return an astng.From node as string"""
  178. return 'from %s import %s' % ('.' * (node.level or 0) + node.modname,
  179. _import_string(node.names))
  180. def visit_function(self, node):
  181. """return an astng.Function node as string"""
  182. decorate = node.decorators and node.decorators.accept(self) or ''
  183. docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or ''
  184. return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(self),
  185. docs, self._stmt_list(node.body))
  186. def visit_genexpr(self, node):
  187. """return an astng.GenExpr node as string"""
  188. return '(%s %s)' % (node.elt.accept(self), ' '.join([n.accept(self)
  189. for n in node.generators]))
  190. def visit_getattr(self, node):
  191. """return an astng.Getattr node as string"""
  192. return '%s.%s' % (node.expr.accept(self), node.attrname)
  193. def visit_global(self, node):
  194. """return an astng.Global node as string"""
  195. return 'global %s' % ', '.join(node.names)
  196. def visit_if(self, node):
  197. """return an astng.If node as string"""
  198. ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))]
  199. if node.orelse:# XXX use elif ???
  200. ifs.append('else:\n%s' % self._stmt_list(node.orelse))
  201. return '\n'.join(ifs)
  202. def visit_ifexp(self, node):
  203. """return an astng.IfExp node as string"""
  204. return '%s if %s else %s' % (node.body.accept(self),
  205. node.test.accept(self), node.orelse.accept(self))
  206. def visit_import(self, node):
  207. """return an astng.Import node as string"""
  208. return 'import %s' % _import_string(node.names)
  209. def visit_keyword(self, node):
  210. """return an astng.Keyword node as string"""
  211. return '%s=%s' % (node.arg, node.value.accept(self))
  212. def visit_lambda(self, node):
  213. """return an astng.Lambda node as string"""
  214. return 'lambda %s: %s' % (node.args.accept(self), node.body.accept(self))
  215. def visit_list(self, node):
  216. """return an astng.List node as string"""
  217. return '[%s]' % ', '.join([child.accept(self) for child in node.elts])
  218. def visit_listcomp(self, node):
  219. """return an astng.ListComp node as string"""
  220. return '[%s %s]' % (node.elt.accept(self), ' '.join([n.accept(self)
  221. for n in node.generators]))
  222. def visit_module(self, node):
  223. """return an astng.Module node as string"""
  224. docs = node.doc and '"""%s"""\n\n' % node.doc or ''
  225. return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n'
  226. def visit_name(self, node):
  227. """return an astng.Name node as string"""
  228. return node.name
  229. def visit_pass(self, node):
  230. """return an astng.Pass node as string"""
  231. return 'pass'
  232. def visit_print(self, node):
  233. """return an astng.Print node as string"""
  234. nodes = ', '.join([n.accept(self) for n in node.values])
  235. if not node.nl:
  236. nodes = '%s,' % nodes
  237. if node.dest:
  238. return 'print >> %s, %s' % (node.dest.accept(self), nodes)
  239. return 'print %s' % nodes
  240. def visit_raise(self, node):
  241. """return an astng.Raise node as string"""
  242. if node.exc:
  243. if node.inst:
  244. if node.tback:
  245. return 'raise %s, %s, %s' % (node.exc.accept(self),
  246. node.inst.accept(self),
  247. node.tback.accept(self))
  248. return 'raise %s, %s' % (node.exc.accept(self),
  249. node.inst.accept(self))
  250. return 'raise %s' % node.exc.accept(self)
  251. return 'raise'
  252. def visit_return(self, node):
  253. """return an astng.Return node as string"""
  254. if node.value:
  255. return 'return %s' % node.value.accept(self)
  256. else:
  257. return 'return'
  258. def visit_index(self, node):
  259. """return a astng.Index node as string"""
  260. return node.value.accept(self)
  261. def visit_set(self, node):
  262. """return an astng.Set node as string"""
  263. return '{%s}' % ', '.join([child.accept(self) for child in node.elts])
  264. def visit_setcomp(self, node):
  265. """return an astng.SetComp node as string"""
  266. return '{%s %s}' % (node.elt.accept(self), ' '.join([n.accept(self)
  267. for n in node.generators]))
  268. def visit_slice(self, node):
  269. """return a astng.Slice node as string"""
  270. lower = node.lower and node.lower.accept(self) or ''
  271. upper = node.upper and node.upper.accept(self) or ''
  272. step = node.step and node.step.accept(self) or ''
  273. if step:
  274. return '%s:%s:%s' % (lower, upper, step)
  275. return '%s:%s' % (lower, upper)
  276. def visit_subscript(self, node):
  277. """return an astng.Subscript node as string"""
  278. return '%s[%s]' % (node.value.accept(self), node.slice.accept(self))
  279. def visit_tryexcept(self, node):
  280. """return an astng.TryExcept node as string"""
  281. trys = ['try:\n%s' % self._stmt_list( node.body)]
  282. for handler in node.handlers:
  283. trys.append(handler.accept(self))
  284. if node.orelse:
  285. trys.append('else:\n%s' % self._stmt_list(node.orelse))
  286. return '\n'.join(trys)
  287. def visit_tryfinally(self, node):
  288. """return an astng.TryFinally node as string"""
  289. return 'try:\n%s\nfinally:\n%s' % (self._stmt_list( node.body),
  290. self._stmt_list(node.finalbody))
  291. def visit_tuple(self, node):
  292. """return an astng.Tuple node as string"""
  293. return '(%s)' % ', '.join([child.accept(self) for child in node.elts])
  294. def visit_unaryop(self, node):
  295. """return an astng.UnaryOp node as string"""
  296. if node.op == 'not':
  297. operator = 'not '
  298. else:
  299. operator = node.op
  300. return '%s%s' % (operator, node.operand.accept(self))
  301. def visit_while(self, node):
  302. """return an astng.While node as string"""
  303. whiles = 'while %s:\n%s' % (node.test.accept(self),
  304. self._stmt_list(node.body))
  305. if node.orelse:
  306. whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse))
  307. return whiles
  308. def visit_with(self, node): # 'with' without 'as' is possible
  309. """return an astng.With node as string"""
  310. as_var = node.vars and " as (%s)" % (node.vars.accept(self)) or ""
  311. withs = 'with (%s)%s:\n%s' % (node.expr.accept(self), as_var,
  312. self._stmt_list( node.body))
  313. return withs
  314. def visit_yield(self, node):
  315. """yield an ast.Yield node as string"""
  316. yi_val = node.value and (" " + node.value.accept(self)) or ""
  317. return 'yield' + yi_val
  318. class AsStringVisitor3k(AsStringVisitor):
  319. """AsStringVisitor3k overwrites some AsStringVisitor methods"""
  320. def visit_excepthandler(self, node):
  321. if node.type:
  322. if node.name:
  323. excs = 'except %s as %s' % (node.type.accept(self),
  324. node.name.accept(self))
  325. else:
  326. excs = 'except %s' % node.type.accept(self)
  327. else:
  328. excs = 'except'
  329. return '%s:\n%s' % (excs, self._stmt_list(node.body))
  330. def visit_nonlocal(self, node):
  331. """return an astng.Nonlocal node as string"""
  332. return 'nonlocal %s' % ', '.join(node.names)
  333. def visit_raise(self, node):
  334. """return an astng.Raise node as string"""
  335. if node.exc:
  336. if node.cause:
  337. return 'raise %s from %s' % (node.exc.accept(self),
  338. node.cause.accept(self))
  339. return 'raise %s' % node.exc.accept(self)
  340. return 'raise'
  341. def visit_starred(self, node):
  342. """return Starred node as string"""
  343. return "*" + node.value.accept(self)
  344. if sys.version_info >= (3, 0):
  345. AsStringVisitor = AsStringVisitor3k
  346. # this visitor is stateless, thus it can be reused
  347. as_string = AsStringVisitor()