as_string.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
  2. # For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
  3. # Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
  4. """This module renders Astroid nodes as string"""
  5. from __future__ import annotations
  6. from collections.abc import Iterator
  7. from typing import TYPE_CHECKING
  8. from astroid import nodes
  9. if TYPE_CHECKING:
  10. from astroid.nodes import Const
  11. from astroid.nodes.node_classes import (
  12. Match,
  13. MatchAs,
  14. MatchCase,
  15. MatchClass,
  16. MatchMapping,
  17. MatchOr,
  18. MatchSequence,
  19. MatchSingleton,
  20. MatchStar,
  21. MatchValue,
  22. Unknown,
  23. )
  24. # pylint: disable=unused-argument
  25. DOC_NEWLINE = "\0"
  26. # Visitor pattern require argument all the time and is not better with staticmethod
  27. # noinspection PyUnusedLocal,PyMethodMayBeStatic
  28. class AsStringVisitor:
  29. """Visitor to render an Astroid node as a valid python code string"""
  30. def __init__(self, indent: str = " "):
  31. self.indent: str = indent
  32. def __call__(self, node) -> str:
  33. """Makes this visitor behave as a simple function"""
  34. return node.accept(self).replace(DOC_NEWLINE, "\n")
  35. def _docs_dedent(self, doc_node: Const | None) -> str:
  36. """Stop newlines in docs being indented by self._stmt_list"""
  37. if not doc_node:
  38. return ""
  39. return '\n{}"""{}"""'.format(
  40. self.indent, doc_node.value.replace("\n", DOC_NEWLINE)
  41. )
  42. def _stmt_list(self, stmts: list, indent: bool = True) -> str:
  43. """return a list of nodes to string"""
  44. stmts_str: str = "\n".join(
  45. nstr for nstr in [n.accept(self) for n in stmts] if nstr
  46. )
  47. if not indent:
  48. return stmts_str
  49. return self.indent + stmts_str.replace("\n", "\n" + self.indent)
  50. def _precedence_parens(self, node, child, is_left: bool = True) -> str:
  51. """Wrap child in parens only if required to keep same semantics"""
  52. if self._should_wrap(node, child, is_left):
  53. return f"({child.accept(self)})"
  54. return child.accept(self)
  55. def _should_wrap(self, node, child, is_left: bool) -> bool:
  56. """Wrap child if:
  57. - it has lower precedence
  58. - same precedence with position opposite to associativity direction
  59. """
  60. node_precedence = node.op_precedence()
  61. child_precedence = child.op_precedence()
  62. if node_precedence > child_precedence:
  63. # 3 * (4 + 5)
  64. return True
  65. if (
  66. node_precedence == child_precedence
  67. and is_left != node.op_left_associative()
  68. ):
  69. # 3 - (4 - 5)
  70. # (2**3)**4
  71. return True
  72. return False
  73. # visit_<node> methods ###########################################
  74. def visit_await(self, node) -> str:
  75. return f"await {node.value.accept(self)}"
  76. def visit_asyncwith(self, node) -> str:
  77. return f"async {self.visit_with(node)}"
  78. def visit_asyncfor(self, node) -> str:
  79. return f"async {self.visit_for(node)}"
  80. def visit_arguments(self, node) -> str:
  81. """return an astroid.Function node as string"""
  82. return node.format_args()
  83. def visit_assignattr(self, node) -> str:
  84. """return an astroid.AssAttr node as string"""
  85. return self.visit_attribute(node)
  86. def visit_assert(self, node) -> str:
  87. """return an astroid.Assert node as string"""
  88. if node.fail:
  89. return f"assert {node.test.accept(self)}, {node.fail.accept(self)}"
  90. return f"assert {node.test.accept(self)}"
  91. def visit_assignname(self, node) -> str:
  92. """return an astroid.AssName node as string"""
  93. return node.name
  94. def visit_assign(self, node) -> str:
  95. """return an astroid.Assign node as string"""
  96. lhs = " = ".join(n.accept(self) for n in node.targets)
  97. return f"{lhs} = {node.value.accept(self)}"
  98. def visit_augassign(self, node) -> str:
  99. """return an astroid.AugAssign node as string"""
  100. return f"{node.target.accept(self)} {node.op} {node.value.accept(self)}"
  101. def visit_annassign(self, node) -> str:
  102. """Return an astroid.AugAssign node as string"""
  103. target = node.target.accept(self)
  104. annotation = node.annotation.accept(self)
  105. if node.value is None:
  106. return f"{target}: {annotation}"
  107. return f"{target}: {annotation} = {node.value.accept(self)}"
  108. def visit_binop(self, node) -> str:
  109. """return an astroid.BinOp node as string"""
  110. left = self._precedence_parens(node, node.left)
  111. right = self._precedence_parens(node, node.right, is_left=False)
  112. if node.op == "**":
  113. return f"{left}{node.op}{right}"
  114. return f"{left} {node.op} {right}"
  115. def visit_boolop(self, node) -> str:
  116. """return an astroid.BoolOp node as string"""
  117. values = [f"{self._precedence_parens(node, n)}" for n in node.values]
  118. return (f" {node.op} ").join(values)
  119. def visit_break(self, node) -> str:
  120. """return an astroid.Break node as string"""
  121. return "break"
  122. def visit_call(self, node) -> str:
  123. """return an astroid.Call node as string"""
  124. expr_str = self._precedence_parens(node, node.func)
  125. args = [arg.accept(self) for arg in node.args]
  126. if node.keywords:
  127. keywords = [kwarg.accept(self) for kwarg in node.keywords]
  128. else:
  129. keywords = []
  130. args.extend(keywords)
  131. return f"{expr_str}({', '.join(args)})"
  132. def visit_classdef(self, node) -> str:
  133. """return an astroid.ClassDef node as string"""
  134. decorate = node.decorators.accept(self) if node.decorators else ""
  135. args = [n.accept(self) for n in node.bases]
  136. if node._metaclass and not node.has_metaclass_hack():
  137. args.append("metaclass=" + node._metaclass.accept(self))
  138. args += [n.accept(self) for n in node.keywords]
  139. args_str = f"({', '.join(args)})" if args else ""
  140. docs = self._docs_dedent(node.doc_node)
  141. return "\n\n{}class {}{}:{}\n{}\n".format(
  142. decorate, node.name, args_str, docs, self._stmt_list(node.body)
  143. )
  144. def visit_compare(self, node) -> str:
  145. """return an astroid.Compare node as string"""
  146. rhs_str = " ".join(
  147. f"{op} {self._precedence_parens(node, expr, is_left=False)}"
  148. for op, expr in node.ops
  149. )
  150. return f"{self._precedence_parens(node, node.left)} {rhs_str}"
  151. def visit_comprehension(self, node) -> str:
  152. """return an astroid.Comprehension node as string"""
  153. ifs = "".join(f" if {n.accept(self)}" for n in node.ifs)
  154. generated = f"for {node.target.accept(self)} in {node.iter.accept(self)}{ifs}"
  155. return f"{'async ' if node.is_async else ''}{generated}"
  156. def visit_const(self, node) -> str:
  157. """return an astroid.Const node as string"""
  158. if node.value is Ellipsis:
  159. return "..."
  160. return repr(node.value)
  161. def visit_continue(self, node) -> str:
  162. """return an astroid.Continue node as string"""
  163. return "continue"
  164. def visit_delete(self, node) -> str: # XXX check if correct
  165. """return an astroid.Delete node as string"""
  166. return f"del {', '.join(child.accept(self) for child in node.targets)}"
  167. def visit_delattr(self, node) -> str:
  168. """return an astroid.DelAttr node as string"""
  169. return self.visit_attribute(node)
  170. def visit_delname(self, node) -> str:
  171. """return an astroid.DelName node as string"""
  172. return node.name
  173. def visit_decorators(self, node) -> str:
  174. """return an astroid.Decorators node as string"""
  175. return "@%s\n" % "\n@".join(item.accept(self) for item in node.nodes)
  176. def visit_dict(self, node) -> str:
  177. """return an astroid.Dict node as string"""
  178. return "{%s}" % ", ".join(self._visit_dict(node))
  179. def _visit_dict(self, node) -> Iterator[str]:
  180. for key, value in node.items:
  181. key = key.accept(self)
  182. value = value.accept(self)
  183. if key == "**":
  184. # It can only be a DictUnpack node.
  185. yield key + value
  186. else:
  187. yield f"{key}: {value}"
  188. def visit_dictunpack(self, node) -> str:
  189. return "**"
  190. def visit_dictcomp(self, node) -> str:
  191. """return an astroid.DictComp node as string"""
  192. return "{{{}: {} {}}}".format(
  193. node.key.accept(self),
  194. node.value.accept(self),
  195. " ".join(n.accept(self) for n in node.generators),
  196. )
  197. def visit_expr(self, node) -> str:
  198. """return an astroid.Discard node as string"""
  199. return node.value.accept(self)
  200. def visit_emptynode(self, node) -> str:
  201. """dummy method for visiting an Empty node"""
  202. return ""
  203. def visit_excepthandler(self, node) -> str:
  204. n = "except"
  205. if isinstance(getattr(node, "parent", None), nodes.TryStar):
  206. n = "except*"
  207. if node.type:
  208. if node.name:
  209. excs = f"{n} {node.type.accept(self)} as {node.name.accept(self)}"
  210. else:
  211. excs = f"{n} {node.type.accept(self)}"
  212. else:
  213. excs = f"{n}"
  214. return f"{excs}:\n{self._stmt_list(node.body)}"
  215. def visit_empty(self, node) -> str:
  216. """return an Empty node as string"""
  217. return ""
  218. def visit_for(self, node) -> str:
  219. """return an astroid.For node as string"""
  220. fors = "for {} in {}:\n{}".format(
  221. node.target.accept(self), node.iter.accept(self), self._stmt_list(node.body)
  222. )
  223. if node.orelse:
  224. fors = f"{fors}\nelse:\n{self._stmt_list(node.orelse)}"
  225. return fors
  226. def visit_importfrom(self, node) -> str:
  227. """return an astroid.ImportFrom node as string"""
  228. return "from {} import {}".format(
  229. "." * (node.level or 0) + node.modname, _import_string(node.names)
  230. )
  231. def visit_joinedstr(self, node) -> str:
  232. string = "".join(
  233. # Use repr on the string literal parts
  234. # to get proper escapes, e.g. \n, \\, \"
  235. # But strip the quotes off the ends
  236. # (they will always be one character: ' or ")
  237. repr(value.value)[1:-1]
  238. # Literal braces must be doubled to escape them
  239. .replace("{", "{{").replace("}", "}}")
  240. # Each value in values is either a string literal (Const)
  241. # or a FormattedValue
  242. if type(value).__name__ == "Const" else value.accept(self)
  243. for value in node.values
  244. )
  245. # Try to find surrounding quotes that don't appear at all in the string.
  246. # Because the formatted values inside {} can't contain backslash (\)
  247. # using a triple quote is sometimes necessary
  248. for quote in ("'", '"', '"""', "'''"):
  249. if quote not in string:
  250. break
  251. return "f" + quote + string + quote
  252. def visit_formattedvalue(self, node) -> str:
  253. result = node.value.accept(self)
  254. if node.conversion and node.conversion >= 0:
  255. # e.g. if node.conversion == 114: result += "!r"
  256. result += "!" + chr(node.conversion)
  257. if node.format_spec:
  258. # The format spec is itself a JoinedString, i.e. an f-string
  259. # We strip the f and quotes of the ends
  260. result += ":" + node.format_spec.accept(self)[2:-1]
  261. return "{%s}" % result
  262. def handle_functiondef(self, node, keyword) -> str:
  263. """return a (possibly async) function definition node as string"""
  264. decorate = node.decorators.accept(self) if node.decorators else ""
  265. docs = self._docs_dedent(node.doc_node)
  266. trailer = ":"
  267. if node.returns:
  268. return_annotation = " -> " + node.returns.as_string()
  269. trailer = return_annotation + ":"
  270. def_format = "\n%s%s %s(%s)%s%s\n%s"
  271. return def_format % (
  272. decorate,
  273. keyword,
  274. node.name,
  275. node.args.accept(self),
  276. trailer,
  277. docs,
  278. self._stmt_list(node.body),
  279. )
  280. def visit_functiondef(self, node) -> str:
  281. """return an astroid.FunctionDef node as string"""
  282. return self.handle_functiondef(node, "def")
  283. def visit_asyncfunctiondef(self, node) -> str:
  284. """return an astroid.AsyncFunction node as string"""
  285. return self.handle_functiondef(node, "async def")
  286. def visit_generatorexp(self, node) -> str:
  287. """return an astroid.GeneratorExp node as string"""
  288. return "({} {})".format(
  289. node.elt.accept(self), " ".join(n.accept(self) for n in node.generators)
  290. )
  291. def visit_attribute(self, node) -> str:
  292. """return an astroid.Getattr node as string"""
  293. left = self._precedence_parens(node, node.expr)
  294. if left.isdigit():
  295. left = f"({left})"
  296. return f"{left}.{node.attrname}"
  297. def visit_global(self, node) -> str:
  298. """return an astroid.Global node as string"""
  299. return f"global {', '.join(node.names)}"
  300. def visit_if(self, node) -> str:
  301. """return an astroid.If node as string"""
  302. ifs = [f"if {node.test.accept(self)}:\n{self._stmt_list(node.body)}"]
  303. if node.has_elif_block():
  304. ifs.append(f"el{self._stmt_list(node.orelse, indent=False)}")
  305. elif node.orelse:
  306. ifs.append(f"else:\n{self._stmt_list(node.orelse)}")
  307. return "\n".join(ifs)
  308. def visit_ifexp(self, node) -> str:
  309. """return an astroid.IfExp node as string"""
  310. return "{} if {} else {}".format(
  311. self._precedence_parens(node, node.body, is_left=True),
  312. self._precedence_parens(node, node.test, is_left=True),
  313. self._precedence_parens(node, node.orelse, is_left=False),
  314. )
  315. def visit_import(self, node) -> str:
  316. """return an astroid.Import node as string"""
  317. return f"import {_import_string(node.names)}"
  318. def visit_keyword(self, node) -> str:
  319. """return an astroid.Keyword node as string"""
  320. if node.arg is None:
  321. return f"**{node.value.accept(self)}"
  322. return f"{node.arg}={node.value.accept(self)}"
  323. def visit_lambda(self, node) -> str:
  324. """return an astroid.Lambda node as string"""
  325. args = node.args.accept(self)
  326. body = node.body.accept(self)
  327. if args:
  328. return f"lambda {args}: {body}"
  329. return f"lambda: {body}"
  330. def visit_list(self, node) -> str:
  331. """return an astroid.List node as string"""
  332. return f"[{', '.join(child.accept(self) for child in node.elts)}]"
  333. def visit_listcomp(self, node) -> str:
  334. """return an astroid.ListComp node as string"""
  335. return "[{} {}]".format(
  336. node.elt.accept(self), " ".join(n.accept(self) for n in node.generators)
  337. )
  338. def visit_module(self, node) -> str:
  339. """return an astroid.Module node as string"""
  340. docs = f'"""{node.doc_node.value}"""\n\n' if node.doc_node else ""
  341. return docs + "\n".join(n.accept(self) for n in node.body) + "\n\n"
  342. def visit_name(self, node) -> str:
  343. """return an astroid.Name node as string"""
  344. return node.name
  345. def visit_namedexpr(self, node) -> str:
  346. """Return an assignment expression node as string"""
  347. target = node.target.accept(self)
  348. value = node.value.accept(self)
  349. return f"{target} := {value}"
  350. def visit_nonlocal(self, node) -> str:
  351. """return an astroid.Nonlocal node as string"""
  352. return f"nonlocal {', '.join(node.names)}"
  353. def visit_pass(self, node) -> str:
  354. """return an astroid.Pass node as string"""
  355. return "pass"
  356. def visit_raise(self, node) -> str:
  357. """return an astroid.Raise node as string"""
  358. if node.exc:
  359. if node.cause:
  360. return f"raise {node.exc.accept(self)} from {node.cause.accept(self)}"
  361. return f"raise {node.exc.accept(self)}"
  362. return "raise"
  363. def visit_return(self, node) -> str:
  364. """return an astroid.Return node as string"""
  365. if node.is_tuple_return() and len(node.value.elts) > 1:
  366. elts = [child.accept(self) for child in node.value.elts]
  367. return f"return {', '.join(elts)}"
  368. if node.value:
  369. return f"return {node.value.accept(self)}"
  370. return "return"
  371. def visit_set(self, node) -> str:
  372. """return an astroid.Set node as string"""
  373. return "{%s}" % ", ".join(child.accept(self) for child in node.elts)
  374. def visit_setcomp(self, node) -> str:
  375. """return an astroid.SetComp node as string"""
  376. return "{{{} {}}}".format(
  377. node.elt.accept(self), " ".join(n.accept(self) for n in node.generators)
  378. )
  379. def visit_slice(self, node) -> str:
  380. """return an astroid.Slice node as string"""
  381. lower = node.lower.accept(self) if node.lower else ""
  382. upper = node.upper.accept(self) if node.upper else ""
  383. step = node.step.accept(self) if node.step else ""
  384. if step:
  385. return f"{lower}:{upper}:{step}"
  386. return f"{lower}:{upper}"
  387. def visit_subscript(self, node) -> str:
  388. """return an astroid.Subscript node as string"""
  389. idx = node.slice
  390. if idx.__class__.__name__.lower() == "index":
  391. idx = idx.value
  392. idxstr = idx.accept(self)
  393. if idx.__class__.__name__.lower() == "tuple" and idx.elts:
  394. # Remove parenthesis in tuple and extended slice.
  395. # a[(::1, 1:)] is not valid syntax.
  396. idxstr = idxstr[1:-1]
  397. return f"{self._precedence_parens(node, node.value)}[{idxstr}]"
  398. def visit_tryexcept(self, node) -> str:
  399. """return an astroid.TryExcept node as string"""
  400. trys = [f"try:\n{self._stmt_list(node.body)}"]
  401. for handler in node.handlers:
  402. trys.append(handler.accept(self))
  403. if node.orelse:
  404. trys.append(f"else:\n{self._stmt_list(node.orelse)}")
  405. return "\n".join(trys)
  406. def visit_tryfinally(self, node) -> str:
  407. """return an astroid.TryFinally node as string"""
  408. return "try:\n{}\nfinally:\n{}".format(
  409. self._stmt_list(node.body), self._stmt_list(node.finalbody)
  410. )
  411. def visit_trystar(self, node) -> str:
  412. """return an astroid.TryStar node as string"""
  413. trys = [f"try:\n{self._stmt_list(node.body)}"]
  414. for handler in node.handlers:
  415. trys.append(handler.accept(self))
  416. if node.orelse:
  417. trys.append(f"else:\n{self._stmt_list(node.orelse)}")
  418. if node.finalbody:
  419. trys.append(f"finally:\n{self._stmt_list(node.finalbody)}")
  420. return "\n".join(trys)
  421. def visit_tuple(self, node) -> str:
  422. """return an astroid.Tuple node as string"""
  423. if len(node.elts) == 1:
  424. return f"({node.elts[0].accept(self)}, )"
  425. return f"({', '.join(child.accept(self) for child in node.elts)})"
  426. def visit_unaryop(self, node) -> str:
  427. """return an astroid.UnaryOp node as string"""
  428. if node.op == "not":
  429. operator = "not "
  430. else:
  431. operator = node.op
  432. return f"{operator}{self._precedence_parens(node, node.operand)}"
  433. def visit_while(self, node) -> str:
  434. """return an astroid.While node as string"""
  435. whiles = f"while {node.test.accept(self)}:\n{self._stmt_list(node.body)}"
  436. if node.orelse:
  437. whiles = f"{whiles}\nelse:\n{self._stmt_list(node.orelse)}"
  438. return whiles
  439. def visit_with(self, node) -> str: # 'with' without 'as' is possible
  440. """return an astroid.With node as string"""
  441. items = ", ".join(
  442. f"{expr.accept(self)}" + (v and f" as {v.accept(self)}" or "")
  443. for expr, v in node.items
  444. )
  445. return f"with {items}:\n{self._stmt_list(node.body)}"
  446. def visit_yield(self, node) -> str:
  447. """yield an ast.Yield node as string"""
  448. yi_val = (" " + node.value.accept(self)) if node.value else ""
  449. expr = "yield" + yi_val
  450. if node.parent.is_statement:
  451. return expr
  452. return f"({expr})"
  453. def visit_yieldfrom(self, node) -> str:
  454. """Return an astroid.YieldFrom node as string."""
  455. yi_val = (" " + node.value.accept(self)) if node.value else ""
  456. expr = "yield from" + yi_val
  457. if node.parent.is_statement:
  458. return expr
  459. return f"({expr})"
  460. def visit_starred(self, node) -> str:
  461. """return Starred node as string"""
  462. return "*" + node.value.accept(self)
  463. def visit_match(self, node: Match) -> str:
  464. """Return an astroid.Match node as string."""
  465. return f"match {node.subject.accept(self)}:\n{self._stmt_list(node.cases)}"
  466. def visit_matchcase(self, node: MatchCase) -> str:
  467. """Return an astroid.MatchCase node as string."""
  468. guard_str = f" if {node.guard.accept(self)}" if node.guard else ""
  469. return (
  470. f"case {node.pattern.accept(self)}{guard_str}:\n"
  471. f"{self._stmt_list(node.body)}"
  472. )
  473. def visit_matchvalue(self, node: MatchValue) -> str:
  474. """Return an astroid.MatchValue node as string."""
  475. return node.value.accept(self)
  476. @staticmethod
  477. def visit_matchsingleton(node: MatchSingleton) -> str:
  478. """Return an astroid.MatchSingleton node as string."""
  479. return str(node.value)
  480. def visit_matchsequence(self, node: MatchSequence) -> str:
  481. """Return an astroid.MatchSequence node as string."""
  482. if node.patterns is None:
  483. return "[]"
  484. return f"[{', '.join(p.accept(self) for p in node.patterns)}]"
  485. def visit_matchmapping(self, node: MatchMapping) -> str:
  486. """Return an astroid.MatchMapping node as string."""
  487. mapping_strings: list[str] = []
  488. if node.keys and node.patterns:
  489. mapping_strings.extend(
  490. f"{key.accept(self)}: {p.accept(self)}"
  491. for key, p in zip(node.keys, node.patterns)
  492. )
  493. if node.rest:
  494. mapping_strings.append(f"**{node.rest.accept(self)}")
  495. return f"{'{'}{', '.join(mapping_strings)}{'}'}"
  496. def visit_matchclass(self, node: MatchClass) -> str:
  497. """Return an astroid.MatchClass node as string."""
  498. if node.cls is None:
  499. raise AssertionError(f"{node} does not have a 'cls' node")
  500. class_strings: list[str] = []
  501. if node.patterns:
  502. class_strings.extend(p.accept(self) for p in node.patterns)
  503. if node.kwd_attrs and node.kwd_patterns:
  504. for attr, pattern in zip(node.kwd_attrs, node.kwd_patterns):
  505. class_strings.append(f"{attr}={pattern.accept(self)}")
  506. return f"{node.cls.accept(self)}({', '.join(class_strings)})"
  507. def visit_matchstar(self, node: MatchStar) -> str:
  508. """Return an astroid.MatchStar node as string."""
  509. return f"*{node.name.accept(self) if node.name else '_'}"
  510. def visit_matchas(self, node: MatchAs) -> str:
  511. """Return an astroid.MatchAs node as string."""
  512. # pylint: disable=import-outside-toplevel
  513. # Prevent circular dependency
  514. from astroid.nodes.node_classes import MatchClass, MatchMapping, MatchSequence
  515. if isinstance(node.parent, (MatchSequence, MatchMapping, MatchClass)):
  516. return node.name.accept(self) if node.name else "_"
  517. return (
  518. f"{node.pattern.accept(self) if node.pattern else '_'}"
  519. f"{f' as {node.name.accept(self)}' if node.name else ''}"
  520. )
  521. def visit_matchor(self, node: MatchOr) -> str:
  522. """Return an astroid.MatchOr node as string."""
  523. if node.patterns is None:
  524. raise AssertionError(f"{node} does not have pattern nodes")
  525. return " | ".join(p.accept(self) for p in node.patterns)
  526. # These aren't for real AST nodes, but for inference objects.
  527. def visit_frozenset(self, node):
  528. return node.parent.accept(self)
  529. def visit_super(self, node):
  530. return node.parent.accept(self)
  531. def visit_uninferable(self, node):
  532. return str(node)
  533. def visit_property(self, node):
  534. return node.function.accept(self)
  535. def visit_evaluatedobject(self, node):
  536. return node.original.accept(self)
  537. def visit_unknown(self, node: Unknown) -> str:
  538. return str(node)
  539. def _import_string(names) -> str:
  540. """return a list of (name, asname) formatted as a string"""
  541. _names = []
  542. for name, asname in names:
  543. if asname is not None:
  544. _names.append(f"{name} as {asname}")
  545. else:
  546. _names.append(name)
  547. return ", ".join(_names)
  548. # This sets the default indent to 4 spaces.
  549. to_code = AsStringVisitor(" ")