Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/astroid/nodes/as_string.py: 53%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

380 statements  

1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html 

2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE 

3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt 

4 

5"""This module renders Astroid nodes as string""" 

6 

7from __future__ import annotations 

8 

9import warnings 

10from collections.abc import Iterator 

11from typing import TYPE_CHECKING 

12 

13from astroid import nodes 

14 

15if TYPE_CHECKING: 

16 from astroid import objects 

17 from astroid.nodes import Const 

18 from astroid.nodes.node_classes import ( 

19 Match, 

20 MatchAs, 

21 MatchCase, 

22 MatchClass, 

23 MatchMapping, 

24 MatchOr, 

25 MatchSequence, 

26 MatchSingleton, 

27 MatchStar, 

28 MatchValue, 

29 Unknown, 

30 ) 

31 from astroid.nodes.node_ng import NodeNG 

32 

33# pylint: disable=unused-argument 

34 

35DOC_NEWLINE = "\0" 

36 

37 

38# Visitor pattern require argument all the time and is not better with staticmethod 

39# noinspection PyUnusedLocal,PyMethodMayBeStatic 

40class AsStringVisitor: 

41 """Visitor to render an Astroid node as a valid python code string""" 

42 

43 def __init__(self, indent: str = " "): 

44 self.indent: str = indent 

45 

46 def __call__(self, node: NodeNG) -> str: 

47 """Makes this visitor behave as a simple function""" 

48 return node.accept(self).replace(DOC_NEWLINE, "\n") 

49 

50 def _docs_dedent(self, doc_node: Const | None) -> str: 

51 """Stop newlines in docs being indented by self._stmt_list""" 

52 if not doc_node: 

53 return "" 

54 

55 return '\n{}"""{}"""'.format( 

56 self.indent, doc_node.value.replace("\n", DOC_NEWLINE) 

57 ) 

58 

59 def _stmt_list(self, stmts: list, indent: bool = True) -> str: 

60 """return a list of nodes to string""" 

61 stmts_str: str = "\n".join( 

62 nstr for nstr in [n.accept(self) for n in stmts] if nstr 

63 ) 

64 if not indent: 

65 return stmts_str 

66 

67 return self.indent + stmts_str.replace("\n", "\n" + self.indent) 

68 

69 def _precedence_parens( 

70 self, node: NodeNG, child: NodeNG, is_left: bool = True 

71 ) -> str: 

72 """Wrap child in parens only if required to keep same semantics""" 

73 if self._should_wrap(node, child, is_left): 

74 return f"({child.accept(self)})" 

75 

76 return child.accept(self) 

77 

78 def _should_wrap(self, node: NodeNG, child: NodeNG, is_left: bool) -> bool: 

79 """Wrap child if: 

80 - it has lower precedence 

81 - same precedence with position opposite to associativity direction 

82 """ 

83 node_precedence = node.op_precedence() 

84 child_precedence = child.op_precedence() 

85 

86 if node_precedence > child_precedence: 

87 # 3 * (4 + 5) 

88 return True 

89 

90 if ( 

91 node_precedence == child_precedence 

92 and is_left != node.op_left_associative() 

93 ): 

94 # 3 - (4 - 5) 

95 # (2**3)**4 

96 return True 

97 

98 return False 

99 

100 # visit_<node> methods ########################################### 

101 

102 def visit_await(self, node: nodes.Await) -> str: 

103 return f"await {node.value.accept(self)}" 

104 

105 def visit_asyncwith(self, node: nodes.AsyncWith) -> str: 

106 return f"async {self.visit_with(node)}" 

107 

108 def visit_asyncfor(self, node: nodes.AsyncFor) -> str: 

109 return f"async {self.visit_for(node)}" 

110 

111 def visit_arguments(self, node: nodes.Arguments) -> str: 

112 """return an astroid.Arguments node as string""" 

113 return node.format_args() 

114 

115 def visit_assignattr(self, node: nodes.AssignAttr) -> str: 

116 """return an astroid.AssignAttr node as string""" 

117 return self.visit_attribute(node) 

118 

119 def visit_assert(self, node: nodes.Assert) -> str: 

120 """return an astroid.Assert node as string""" 

121 if node.fail: 

122 return f"assert {node.test.accept(self)}, {node.fail.accept(self)}" 

123 return f"assert {node.test.accept(self)}" 

124 

125 def visit_assignname(self, node: nodes.AssignName) -> str: 

126 """return an astroid.AssignName node as string""" 

127 return node.name 

128 

129 def visit_assign(self, node: nodes.Assign) -> str: 

130 """return an astroid.Assign node as string""" 

131 lhs = " = ".join(n.accept(self) for n in node.targets) 

132 return f"{lhs} = {node.value.accept(self)}" 

133 

134 def visit_augassign(self, node: nodes.AugAssign) -> str: 

135 """return an astroid.AugAssign node as string""" 

136 return f"{node.target.accept(self)} {node.op} {node.value.accept(self)}" 

137 

138 def visit_annassign(self, node: nodes.AnnAssign) -> str: 

139 """Return an astroid.AnnAssign node as string""" 

140 

141 target = node.target.accept(self) 

142 annotation = node.annotation.accept(self) 

143 if node.value is None: 

144 return f"{target}: {annotation}" 

145 return f"{target}: {annotation} = {node.value.accept(self)}" 

146 

147 def visit_binop(self, node: nodes.BinOp) -> str: 

148 """return an astroid.BinOp node as string""" 

149 left = self._precedence_parens(node, node.left) 

150 right = self._precedence_parens(node, node.right, is_left=False) 

151 if node.op == "**": 

152 return f"{left}{node.op}{right}" 

153 

154 return f"{left} {node.op} {right}" 

155 

156 def visit_boolop(self, node: nodes.BoolOp) -> str: 

157 """return an astroid.BoolOp node as string""" 

158 values = [f"{self._precedence_parens(node, n)}" for n in node.values] 

159 return (f" {node.op} ").join(values) 

160 

161 def visit_break(self, node: nodes.Break) -> str: 

162 """return an astroid.Break node as string""" 

163 return "break" 

164 

165 def visit_call(self, node: nodes.Call) -> str: 

166 """return an astroid.Call node as string""" 

167 expr_str = self._precedence_parens(node, node.func) 

168 args = [arg.accept(self) for arg in node.args] 

169 if node.keywords: 

170 keywords = [kwarg.accept(self) for kwarg in node.keywords] 

171 else: 

172 keywords = [] 

173 

174 args.extend(keywords) 

175 return f"{expr_str}({', '.join(args)})" 

176 

177 def visit_classdef(self, node: nodes.ClassDef) -> str: 

178 """return an astroid.ClassDef node as string""" 

179 decorate = node.decorators.accept(self) if node.decorators else "" 

180 args = [n.accept(self) for n in node.bases] 

181 if node._metaclass and not node.has_metaclass_hack(): 

182 args.append("metaclass=" + node._metaclass.accept(self)) 

183 args += [n.accept(self) for n in node.keywords] 

184 args_str = f"({', '.join(args)})" if args else "" 

185 docs = self._docs_dedent(node.doc_node) 

186 # TODO: handle type_params 

187 return "\n\n{}class {}{}:{}\n{}\n".format( 

188 decorate, node.name, args_str, docs, self._stmt_list(node.body) 

189 ) 

190 

191 def visit_compare(self, node: nodes.Compare) -> str: 

192 """return an astroid.Compare node as string""" 

193 rhs_str = " ".join( 

194 f"{op} {self._precedence_parens(node, expr, is_left=False)}" 

195 for op, expr in node.ops 

196 ) 

197 return f"{self._precedence_parens(node, node.left)} {rhs_str}" 

198 

199 def visit_comprehension(self, node: nodes.Comprehension) -> str: 

200 """return an astroid.Comprehension node as string""" 

201 ifs = "".join(f" if {n.accept(self)}" for n in node.ifs) 

202 generated = f"for {node.target.accept(self)} in {node.iter.accept(self)}{ifs}" 

203 return f"{'async ' if node.is_async else ''}{generated}" 

204 

205 def visit_const(self, node: nodes.Const) -> str: 

206 """return an astroid.Const node as string""" 

207 if node.value is Ellipsis: 

208 return "..." 

209 return repr(node.value) 

210 

211 def visit_continue(self, node: nodes.Continue) -> str: 

212 """return an astroid.Continue node as string""" 

213 return "continue" 

214 

215 def visit_delete(self, node: nodes.Delete) -> str: 

216 """return an astroid.Delete node as string""" 

217 return f"del {', '.join(child.accept(self) for child in node.targets)}" 

218 

219 def visit_delattr(self, node: nodes.DelAttr) -> str: 

220 """return an astroid.DelAttr node as string""" 

221 return self.visit_attribute(node) 

222 

223 def visit_delname(self, node: nodes.DelName) -> str: 

224 """return an astroid.DelName node as string""" 

225 return node.name 

226 

227 def visit_decorators(self, node: nodes.Decorators) -> str: 

228 """return an astroid.Decorators node as string""" 

229 return "@%s\n" % "\n@".join(item.accept(self) for item in node.nodes) 

230 

231 def visit_dict(self, node: nodes.Dict) -> str: 

232 """return an astroid.Dict node as string""" 

233 return "{%s}" % ", ".join(self._visit_dict(node)) 

234 

235 def _visit_dict(self, node: nodes.Dict) -> Iterator[str]: 

236 for key, value in node.items: 

237 key = key.accept(self) 

238 value = value.accept(self) 

239 if key == "**": 

240 # It can only be a DictUnpack node. 

241 yield key + value 

242 else: 

243 yield f"{key}: {value}" 

244 

245 def visit_dictunpack(self, node: nodes.DictUnpack) -> str: 

246 return "**" 

247 

248 def visit_dictcomp(self, node: nodes.DictComp) -> str: 

249 """return an astroid.DictComp node as string""" 

250 return "{{{}: {} {}}}".format( 

251 node.key.accept(self), 

252 node.value.accept(self), 

253 " ".join(n.accept(self) for n in node.generators), 

254 ) 

255 

256 def visit_expr(self, node: nodes.Expr) -> str: 

257 """return an astroid.Expr node as string""" 

258 return node.value.accept(self) 

259 

260 def visit_emptynode(self, node: nodes.EmptyNode) -> str: 

261 """dummy method for visiting an EmptyNode""" 

262 return "" 

263 

264 def visit_excepthandler(self, node: nodes.ExceptHandler) -> str: 

265 n = "except" 

266 if isinstance(getattr(node, "parent", None), nodes.TryStar): 

267 n = "except*" 

268 if node.type: 

269 if node.name: 

270 excs = f"{n} {node.type.accept(self)} as {node.name.accept(self)}" 

271 else: 

272 excs = f"{n} {node.type.accept(self)}" 

273 else: 

274 excs = f"{n}" 

275 return f"{excs}:\n{self._stmt_list(node.body)}" 

276 

277 def visit_empty(self, node: nodes.EmptyNode) -> str: 

278 """return an EmptyNode as string""" 

279 return "" 

280 

281 def visit_for(self, node: nodes.For) -> str: 

282 """return an astroid.For node as string""" 

283 fors = "for {} in {}:\n{}".format( 

284 node.target.accept(self), node.iter.accept(self), self._stmt_list(node.body) 

285 ) 

286 if node.orelse: 

287 fors = f"{fors}\nelse:\n{self._stmt_list(node.orelse)}" 

288 return fors 

289 

290 def visit_importfrom(self, node: nodes.ImportFrom) -> str: 

291 """return an astroid.ImportFrom node as string""" 

292 return "from {} import {}".format( 

293 "." * (node.level or 0) + node.modname, _import_string(node.names) 

294 ) 

295 

296 def visit_joinedstr(self, node: nodes.JoinedStr) -> str: 

297 string = "".join( 

298 # Use repr on the string literal parts 

299 # to get proper escapes, e.g. \n, \\, \" 

300 # But strip the quotes off the ends 

301 # (they will always be one character: ' or ") 

302 ( 

303 repr(value.value)[1:-1] 

304 # Literal braces must be doubled to escape them 

305 .replace("{", "{{").replace("}", "}}") 

306 # Each value in values is either a string literal (Const) 

307 # or a FormattedValue 

308 if type(value).__name__ == "Const" 

309 else value.accept(self) 

310 ) 

311 for value in node.values 

312 ) 

313 

314 # Try to find surrounding quotes that don't appear at all in the string. 

315 # Because the formatted values inside {} can't contain backslash (\) 

316 # using a triple quote is sometimes necessary 

317 for quote in ("'", '"', '"""', "'''"): 

318 if quote not in string: 

319 break 

320 

321 return "f" + quote + string + quote 

322 

323 def visit_formattedvalue(self, node: nodes.FormattedValue) -> str: 

324 result = node.value.accept(self) 

325 if node.conversion and node.conversion >= 0: 

326 # e.g. if node.conversion == 114: result += "!r" 

327 result += "!" + chr(node.conversion) 

328 if node.format_spec: 

329 # The format spec is itself a JoinedString, i.e. an f-string 

330 # We strip the f and quotes of the ends 

331 result += ":" + node.format_spec.accept(self)[2:-1] 

332 return "{%s}" % result 

333 

334 def handle_functiondef(self, node: nodes.FunctionDef, keyword: str) -> str: 

335 """return a (possibly async) function definition node as string""" 

336 decorate = node.decorators.accept(self) if node.decorators else "" 

337 docs = self._docs_dedent(node.doc_node) 

338 trailer = ":" 

339 if node.returns: 

340 return_annotation = " -> " + node.returns.as_string() 

341 trailer = return_annotation + ":" 

342 # TODO: handle type_params 

343 def_format = "\n%s%s %s(%s)%s%s\n%s" 

344 return def_format % ( 

345 decorate, 

346 keyword, 

347 node.name, 

348 node.args.accept(self), 

349 trailer, 

350 docs, 

351 self._stmt_list(node.body), 

352 ) 

353 

354 def visit_functiondef(self, node: nodes.FunctionDef) -> str: 

355 """return an astroid.FunctionDef node as string""" 

356 return self.handle_functiondef(node, "def") 

357 

358 def visit_asyncfunctiondef(self, node: nodes.AsyncFunctionDef) -> str: 

359 """return an astroid.AsyncFunction node as string""" 

360 return self.handle_functiondef(node, "async def") 

361 

362 def visit_generatorexp(self, node: nodes.GeneratorExp) -> str: 

363 """return an astroid.GeneratorExp node as string""" 

364 return "({} {})".format( 

365 node.elt.accept(self), " ".join(n.accept(self) for n in node.generators) 

366 ) 

367 

368 def visit_attribute( 

369 self, node: nodes.Attribute | nodes.AssignAttr | nodes.DelAttr 

370 ) -> str: 

371 """return an astroid.Attribute node as string""" 

372 try: 

373 left = self._precedence_parens(node, node.expr) 

374 except RecursionError: 

375 warnings.warn( 

376 "Recursion limit exhausted; defaulting to adding parentheses.", 

377 UserWarning, 

378 stacklevel=2, 

379 ) 

380 left = f"({node.expr.accept(self)})" 

381 if left.isdigit(): 

382 left = f"({left})" 

383 return f"{left}.{node.attrname}" 

384 

385 def visit_global(self, node: nodes.Global) -> str: 

386 """return an astroid.Global node as string""" 

387 return f"global {', '.join(node.names)}" 

388 

389 def visit_if(self, node: nodes.If) -> str: 

390 """return an astroid.If node as string""" 

391 ifs = [f"if {node.test.accept(self)}:\n{self._stmt_list(node.body)}"] 

392 if node.has_elif_block(): 

393 ifs.append(f"el{self._stmt_list(node.orelse, indent=False)}") 

394 elif node.orelse: 

395 ifs.append(f"else:\n{self._stmt_list(node.orelse)}") 

396 return "\n".join(ifs) 

397 

398 def visit_ifexp(self, node: nodes.IfExp) -> str: 

399 """return an astroid.IfExp node as string""" 

400 return "{} if {} else {}".format( 

401 self._precedence_parens(node, node.body, is_left=True), 

402 self._precedence_parens(node, node.test, is_left=True), 

403 self._precedence_parens(node, node.orelse, is_left=False), 

404 ) 

405 

406 def visit_import(self, node: nodes.Import) -> str: 

407 """return an astroid.Import node as string""" 

408 return f"import {_import_string(node.names)}" 

409 

410 def visit_keyword(self, node: nodes.Keyword) -> str: 

411 """return an astroid.Keyword node as string""" 

412 if node.arg is None: 

413 return f"**{node.value.accept(self)}" 

414 return f"{node.arg}={node.value.accept(self)}" 

415 

416 def visit_lambda(self, node: nodes.Lambda) -> str: 

417 """return an astroid.Lambda node as string""" 

418 args = node.args.accept(self) 

419 body = node.body.accept(self) 

420 if args: 

421 return f"lambda {args}: {body}" 

422 

423 return f"lambda: {body}" 

424 

425 def visit_list(self, node: nodes.List) -> str: 

426 """return an astroid.List node as string""" 

427 return f"[{', '.join(child.accept(self) for child in node.elts)}]" 

428 

429 def visit_listcomp(self, node: nodes.ListComp) -> str: 

430 """return an astroid.ListComp node as string""" 

431 return "[{} {}]".format( 

432 node.elt.accept(self), " ".join(n.accept(self) for n in node.generators) 

433 ) 

434 

435 def visit_module(self, node: nodes.Module) -> str: 

436 """return an astroid.Module node as string""" 

437 docs = f'"""{node.doc_node.value}"""\n\n' if node.doc_node else "" 

438 return docs + "\n".join(n.accept(self) for n in node.body) + "\n\n" 

439 

440 def visit_name(self, node: nodes.Name) -> str: 

441 """return an astroid.Name node as string""" 

442 return node.name 

443 

444 def visit_namedexpr(self, node: nodes.NamedExpr) -> str: 

445 """Return an assignment expression node as string""" 

446 target = node.target.accept(self) 

447 value = node.value.accept(self) 

448 return f"{target} := {value}" 

449 

450 def visit_nonlocal(self, node: nodes.Nonlocal) -> str: 

451 """return an astroid.Nonlocal node as string""" 

452 return f"nonlocal {', '.join(node.names)}" 

453 

454 def visit_paramspec(self, node: nodes.ParamSpec) -> str: 

455 """return an astroid.ParamSpec node as string""" 

456 return node.name.accept(self) 

457 

458 def visit_pass(self, node: nodes.Pass) -> str: 

459 """return an astroid.Pass node as string""" 

460 return "pass" 

461 

462 def visit_partialfunction(self, node: objects.PartialFunction) -> str: 

463 """Return an objects.PartialFunction as string.""" 

464 return self.visit_functiondef(node) 

465 

466 def visit_raise(self, node: nodes.Raise) -> str: 

467 """return an astroid.Raise node as string""" 

468 if node.exc: 

469 if node.cause: 

470 return f"raise {node.exc.accept(self)} from {node.cause.accept(self)}" 

471 return f"raise {node.exc.accept(self)}" 

472 return "raise" 

473 

474 def visit_return(self, node: nodes.Return) -> str: 

475 """return an astroid.Return node as string""" 

476 if node.is_tuple_return() and len(node.value.elts) > 1: 

477 elts = [child.accept(self) for child in node.value.elts] 

478 return f"return {', '.join(elts)}" 

479 

480 if node.value: 

481 return f"return {node.value.accept(self)}" 

482 

483 return "return" 

484 

485 def visit_set(self, node: nodes.Set) -> str: 

486 """return an astroid.Set node as string""" 

487 return "{%s}" % ", ".join(child.accept(self) for child in node.elts) 

488 

489 def visit_setcomp(self, node: nodes.SetComp) -> str: 

490 """return an astroid.SetComp node as string""" 

491 return "{{{} {}}}".format( 

492 node.elt.accept(self), " ".join(n.accept(self) for n in node.generators) 

493 ) 

494 

495 def visit_slice(self, node: nodes.Slice) -> str: 

496 """return an astroid.Slice node as string""" 

497 lower = node.lower.accept(self) if node.lower else "" 

498 upper = node.upper.accept(self) if node.upper else "" 

499 step = node.step.accept(self) if node.step else "" 

500 if step: 

501 return f"{lower}:{upper}:{step}" 

502 return f"{lower}:{upper}" 

503 

504 def visit_subscript(self, node: nodes.Subscript) -> str: 

505 """return an astroid.Subscript node as string""" 

506 idx = node.slice 

507 if idx.__class__.__name__.lower() == "index": 

508 idx = idx.value 

509 idxstr = idx.accept(self) 

510 if idx.__class__.__name__.lower() == "tuple" and idx.elts: 

511 # Remove parenthesis in tuple and extended slice. 

512 # a[(::1, 1:)] is not valid syntax. 

513 idxstr = idxstr[1:-1] 

514 return f"{self._precedence_parens(node, node.value)}[{idxstr}]" 

515 

516 def visit_try(self, node: nodes.Try) -> str: 

517 """return an astroid.Try node as string""" 

518 trys = [f"try:\n{self._stmt_list(node.body)}"] 

519 for handler in node.handlers: 

520 trys.append(handler.accept(self)) 

521 if node.orelse: 

522 trys.append(f"else:\n{self._stmt_list(node.orelse)}") 

523 if node.finalbody: 

524 trys.append(f"finally:\n{self._stmt_list(node.finalbody)}") 

525 return "\n".join(trys) 

526 

527 def visit_trystar(self, node: nodes.TryStar) -> str: 

528 """return an astroid.TryStar node as string""" 

529 trys = [f"try:\n{self._stmt_list(node.body)}"] 

530 for handler in node.handlers: 

531 trys.append(handler.accept(self)) 

532 if node.orelse: 

533 trys.append(f"else:\n{self._stmt_list(node.orelse)}") 

534 if node.finalbody: 

535 trys.append(f"finally:\n{self._stmt_list(node.finalbody)}") 

536 return "\n".join(trys) 

537 

538 def visit_tuple(self, node: nodes.Tuple) -> str: 

539 """return an astroid.Tuple node as string""" 

540 if len(node.elts) == 1: 

541 return f"({node.elts[0].accept(self)}, )" 

542 return f"({', '.join(child.accept(self) for child in node.elts)})" 

543 

544 def visit_typealias(self, node: nodes.TypeAlias) -> str: 

545 """return an astroid.TypeAlias node as string""" 

546 return node.name.accept(self) if node.name else "_" 

547 

548 def visit_typevar(self, node: nodes.TypeVar) -> str: 

549 """return an astroid.TypeVar node as string""" 

550 return node.name.accept(self) if node.name else "_" 

551 

552 def visit_typevartuple(self, node: nodes.TypeVarTuple) -> str: 

553 """return an astroid.TypeVarTuple node as string""" 

554 return "*" + node.name.accept(self) if node.name else "" 

555 

556 def visit_unaryop(self, node: nodes.UnaryOp) -> str: 

557 """return an astroid.UnaryOp node as string""" 

558 if node.op == "not": 

559 operator = "not " 

560 else: 

561 operator = node.op 

562 return f"{operator}{self._precedence_parens(node, node.operand)}" 

563 

564 def visit_while(self, node: nodes.While) -> str: 

565 """return an astroid.While node as string""" 

566 whiles = f"while {node.test.accept(self)}:\n{self._stmt_list(node.body)}" 

567 if node.orelse: 

568 whiles = f"{whiles}\nelse:\n{self._stmt_list(node.orelse)}" 

569 return whiles 

570 

571 def visit_with(self, node: nodes.With) -> str: # 'with' without 'as' is possible 

572 """return an astroid.With node as string""" 

573 items = ", ".join( 

574 f"{expr.accept(self)}" + ((v and f" as {v.accept(self)}") or "") 

575 for expr, v in node.items 

576 ) 

577 return f"with {items}:\n{self._stmt_list(node.body)}" 

578 

579 def visit_yield(self, node: nodes.Yield) -> str: 

580 """yield an ast.Yield node as string""" 

581 yi_val = (" " + node.value.accept(self)) if node.value else "" 

582 expr = "yield" + yi_val 

583 if node.parent.is_statement: 

584 return expr 

585 

586 return f"({expr})" 

587 

588 def visit_yieldfrom(self, node: nodes.YieldFrom) -> str: 

589 """Return an astroid.YieldFrom node as string.""" 

590 yi_val = (" " + node.value.accept(self)) if node.value else "" 

591 expr = "yield from" + yi_val 

592 if node.parent.is_statement: 

593 return expr 

594 

595 return f"({expr})" 

596 

597 def visit_starred(self, node: nodes.Starred) -> str: 

598 """return Starred node as string""" 

599 return "*" + node.value.accept(self) 

600 

601 def visit_match(self, node: Match) -> str: 

602 """Return an astroid.Match node as string.""" 

603 return f"match {node.subject.accept(self)}:\n{self._stmt_list(node.cases)}" 

604 

605 def visit_matchcase(self, node: MatchCase) -> str: 

606 """Return an astroid.MatchCase node as string.""" 

607 guard_str = f" if {node.guard.accept(self)}" if node.guard else "" 

608 return ( 

609 f"case {node.pattern.accept(self)}{guard_str}:\n" 

610 f"{self._stmt_list(node.body)}" 

611 ) 

612 

613 def visit_matchvalue(self, node: MatchValue) -> str: 

614 """Return an astroid.MatchValue node as string.""" 

615 return node.value.accept(self) 

616 

617 @staticmethod 

618 def visit_matchsingleton(node: MatchSingleton) -> str: 

619 """Return an astroid.MatchSingleton node as string.""" 

620 return str(node.value) 

621 

622 def visit_matchsequence(self, node: MatchSequence) -> str: 

623 """Return an astroid.MatchSequence node as string.""" 

624 if node.patterns is None: 

625 return "[]" 

626 return f"[{', '.join(p.accept(self) for p in node.patterns)}]" 

627 

628 def visit_matchmapping(self, node: MatchMapping) -> str: 

629 """Return an astroid.MatchMapping node as string.""" 

630 mapping_strings: list[str] = [] 

631 if node.keys and node.patterns: 

632 mapping_strings.extend( 

633 f"{key.accept(self)}: {p.accept(self)}" 

634 for key, p in zip(node.keys, node.patterns) 

635 ) 

636 if node.rest: 

637 mapping_strings.append(f"**{node.rest.accept(self)}") 

638 return f"{'{'}{', '.join(mapping_strings)}{'}'}" 

639 

640 def visit_matchclass(self, node: MatchClass) -> str: 

641 """Return an astroid.MatchClass node as string.""" 

642 if node.cls is None: 

643 raise AssertionError(f"{node} does not have a 'cls' node") 

644 class_strings: list[str] = [] 

645 if node.patterns: 

646 class_strings.extend(p.accept(self) for p in node.patterns) 

647 if node.kwd_attrs and node.kwd_patterns: 

648 for attr, pattern in zip(node.kwd_attrs, node.kwd_patterns): 

649 class_strings.append(f"{attr}={pattern.accept(self)}") 

650 return f"{node.cls.accept(self)}({', '.join(class_strings)})" 

651 

652 def visit_matchstar(self, node: MatchStar) -> str: 

653 """Return an astroid.MatchStar node as string.""" 

654 return f"*{node.name.accept(self) if node.name else '_'}" 

655 

656 def visit_matchas(self, node: MatchAs) -> str: 

657 """Return an astroid.MatchAs node as string.""" 

658 # pylint: disable=import-outside-toplevel 

659 # Prevent circular dependency 

660 from astroid.nodes.node_classes import MatchClass, MatchMapping, MatchSequence 

661 

662 if isinstance(node.parent, (MatchSequence, MatchMapping, MatchClass)): 

663 return node.name.accept(self) if node.name else "_" 

664 return ( 

665 f"{node.pattern.accept(self) if node.pattern else '_'}" 

666 f"{f' as {node.name.accept(self)}' if node.name else ''}" 

667 ) 

668 

669 def visit_matchor(self, node: MatchOr) -> str: 

670 """Return an astroid.MatchOr node as string.""" 

671 if node.patterns is None: 

672 raise AssertionError(f"{node} does not have pattern nodes") 

673 return " | ".join(p.accept(self) for p in node.patterns) 

674 

675 # These aren't for real AST nodes, but for inference objects. 

676 

677 def visit_frozenset(self, node: objects.FrozenSet) -> str: 

678 return node.parent.accept(self) 

679 

680 def visit_super(self, node: objects.Super) -> str: 

681 return node.parent.accept(self) 

682 

683 def visit_uninferable(self, node) -> str: 

684 return str(node) 

685 

686 def visit_property(self, node: objects.Property) -> str: 

687 return node.function.accept(self) 

688 

689 def visit_evaluatedobject(self, node: nodes.EvaluatedObject) -> str: 

690 return node.original.accept(self) 

691 

692 def visit_unknown(self, node: Unknown) -> str: 

693 return str(node) 

694 

695 

696def _import_string(names: list[tuple[str, str | None]]) -> str: 

697 """return a list of (name, asname) formatted as a string""" 

698 _names = [] 

699 for name, asname in names: 

700 if asname is not None: 

701 _names.append(f"{name} as {asname}") 

702 else: 

703 _names.append(name) 

704 return ", ".join(_names) 

705 

706 

707# This sets the default indent to 4 spaces. 

708to_code = AsStringVisitor(" ")