as_string.py 24 KB

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