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

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

351 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 

5from __future__ import annotations 

6 

7import pprint 

8import sys 

9import warnings 

10from collections.abc import Generator, Iterator 

11from functools import cached_property 

12from functools import singledispatch as _singledispatch 

13from typing import ( 

14 TYPE_CHECKING, 

15 Any, 

16 ClassVar, 

17 Literal, 

18 TypeVar, 

19 Union, 

20 cast, 

21 overload, 

22) 

23 

24from astroid import nodes, util 

25from astroid.context import InferenceContext 

26from astroid.exceptions import ( 

27 AstroidError, 

28 InferenceError, 

29 ParentMissingError, 

30 StatementMissing, 

31 UseInferenceDefault, 

32) 

33from astroid.manager import AstroidManager 

34from astroid.nodes.as_string import AsStringVisitor 

35from astroid.nodes.const import OP_PRECEDENCE 

36from astroid.nodes.utils import Position 

37from astroid.typing import InferenceErrorInfo, InferenceResult, InferFn 

38 

39if sys.version_info >= (3, 11): 

40 from typing import Self 

41else: 

42 from typing_extensions import Self 

43 

44 

45if TYPE_CHECKING: 

46 from astroid.nodes import _base_nodes 

47 

48 

49# Types for 'NodeNG.nodes_of_class()' 

50_NodesT = TypeVar("_NodesT", bound="NodeNG") 

51_NodesT2 = TypeVar("_NodesT2", bound="NodeNG") 

52_NodesT3 = TypeVar("_NodesT3", bound="NodeNG") 

53SkipKlassT = Union[None, type["NodeNG"], tuple[type["NodeNG"], ...]] 

54 

55 

56class NodeNG: 

57 """A node of the new Abstract Syntax Tree (AST). 

58 

59 This is the base class for all Astroid node classes. 

60 """ 

61 

62 is_statement: ClassVar[bool] = False 

63 """Whether this node indicates a statement.""" 

64 optional_assign: ClassVar[bool] = ( 

65 False # True for For (and for Comprehension if py <3.0) 

66 ) 

67 """Whether this node optionally assigns a variable. 

68 

69 This is for loop assignments because loop won't necessarily perform an 

70 assignment if the loop has no iterations. 

71 This is also the case from comprehensions in Python 2. 

72 """ 

73 is_function: ClassVar[bool] = False # True for FunctionDef nodes 

74 """Whether this node indicates a function.""" 

75 is_lambda: ClassVar[bool] = False 

76 

77 # Attributes below are set by the builder module or by raw factories 

78 _astroid_fields: ClassVar[tuple[str, ...]] = () 

79 """Node attributes that contain child nodes. 

80 

81 This is redefined in most concrete classes. 

82 """ 

83 _other_fields: ClassVar[tuple[str, ...]] = () 

84 """Node attributes that do not contain child nodes.""" 

85 _other_other_fields: ClassVar[tuple[str, ...]] = () 

86 """Attributes that contain AST-dependent fields.""" 

87 # instance specific inference function infer(node, context) 

88 _explicit_inference: InferFn[Self] | None = None 

89 

90 def __init__( 

91 self, 

92 lineno: int | None, 

93 col_offset: int | None, 

94 parent: NodeNG | None, 

95 *, 

96 end_lineno: int | None, 

97 end_col_offset: int | None, 

98 ) -> None: 

99 self.lineno = lineno 

100 """The line that this node appears on in the source code.""" 

101 

102 self.col_offset = col_offset 

103 """The column that this node appears on in the source code.""" 

104 

105 self.parent = parent 

106 """The parent node in the syntax tree.""" 

107 

108 self.end_lineno = end_lineno 

109 """The last line this node appears on in the source code.""" 

110 

111 self.end_col_offset = end_col_offset 

112 """The end column this node appears on in the source code. 

113 

114 Note: This is after the last symbol. 

115 """ 

116 

117 self.position: Position | None = None 

118 """Position of keyword(s) and name. 

119 

120 Used as fallback for block nodes which might not provide good 

121 enough positional information. E.g. ClassDef, FunctionDef. 

122 """ 

123 

124 def infer( 

125 self, context: InferenceContext | None = None, **kwargs: Any 

126 ) -> Generator[InferenceResult]: 

127 """Get a generator of the inferred values. 

128 

129 This is the main entry point to the inference system. 

130 

131 .. seealso:: :ref:`inference` 

132 

133 If the instance has some explicit inference function set, it will be 

134 called instead of the default interface. 

135 

136 :returns: The inferred values. 

137 :rtype: iterable 

138 """ 

139 if context is None: 

140 context = InferenceContext() 

141 else: 

142 context = context.extra_context.get(self, context) 

143 if self._explicit_inference is not None: 

144 # explicit_inference is not bound, give it self explicitly 

145 try: 

146 for result in self._explicit_inference( 

147 self, # type: ignore[arg-type] 

148 context, 

149 **kwargs, 

150 ): 

151 context.nodes_inferred += 1 

152 yield result 

153 return 

154 except UseInferenceDefault: 

155 pass 

156 

157 key = (self, context.lookupname, context.callcontext, context.boundnode) 

158 if key in context.inferred: 

159 yield from context.inferred[key] 

160 return 

161 

162 results = [] 

163 

164 # Limit inference amount to help with performance issues with 

165 # exponentially exploding possible results. 

166 limit = AstroidManager().max_inferable_values 

167 for i, result in enumerate(self._infer(context=context, **kwargs)): 

168 if i >= limit or (context.nodes_inferred > context.max_inferred): 

169 results.append(util.Uninferable) 

170 yield util.Uninferable 

171 break 

172 results.append(result) 

173 yield result 

174 context.nodes_inferred += 1 

175 

176 # Cache generated results for subsequent inferences of the 

177 # same node using the same context 

178 context.inferred[key] = tuple(results) 

179 return 

180 

181 def repr_name(self) -> str: 

182 """Get a name for nice representation. 

183 

184 This is either :attr:`name`, :attr:`attrname`, or the empty string. 

185 """ 

186 if all(name not in self._astroid_fields for name in ("name", "attrname")): 

187 return getattr(self, "name", "") or getattr(self, "attrname", "") 

188 return "" 

189 

190 def __str__(self) -> str: 

191 rname = self.repr_name() 

192 cname = type(self).__name__ 

193 if rname: 

194 string = "%(cname)s.%(rname)s(%(fields)s)" 

195 alignment = len(cname) + len(rname) + 2 

196 else: 

197 string = "%(cname)s(%(fields)s)" 

198 alignment = len(cname) + 1 

199 result = [] 

200 for field in self._other_fields + self._astroid_fields: 

201 value = getattr(self, field, "Unknown") 

202 width = 80 - len(field) - alignment 

203 lines = pprint.pformat(value, indent=2, width=width).splitlines(True) 

204 

205 inner = [lines[0]] 

206 for line in lines[1:]: 

207 inner.append(" " * alignment + line) 

208 result.append(f"{field}={''.join(inner)}") 

209 

210 return string % { 

211 "cname": cname, 

212 "rname": rname, 

213 "fields": (",\n" + " " * alignment).join(result), 

214 } 

215 

216 def __repr__(self) -> str: 

217 rname = self.repr_name() 

218 # The dependencies used to calculate fromlineno (if not cached) may not exist at the time 

219 try: 

220 lineno = self.fromlineno 

221 except AttributeError: 

222 lineno = 0 

223 if rname: 

224 string = "<%(cname)s.%(rname)s l.%(lineno)s at 0x%(id)x>" 

225 else: 

226 string = "<%(cname)s l.%(lineno)s at 0x%(id)x>" 

227 return string % { 

228 "cname": type(self).__name__, 

229 "rname": rname, 

230 "lineno": lineno, 

231 "id": id(self), 

232 } 

233 

234 def accept(self, visitor: AsStringVisitor) -> str: 

235 """Visit this node using the given visitor.""" 

236 func = getattr(visitor, "visit_" + self.__class__.__name__.lower()) 

237 return func(self) 

238 

239 def get_children(self) -> Iterator[NodeNG]: 

240 """Get the child nodes below this node.""" 

241 for field in self._astroid_fields: 

242 attr = getattr(self, field) 

243 if attr is None: 

244 continue 

245 if isinstance(attr, (list, tuple)): 

246 yield from attr 

247 else: 

248 yield attr 

249 yield from () 

250 

251 def last_child(self) -> NodeNG | None: 

252 """An optimized version of list(get_children())[-1].""" 

253 for field in self._astroid_fields[::-1]: 

254 attr = getattr(self, field) 

255 if not attr: # None or empty list / tuple 

256 continue 

257 if isinstance(attr, (list, tuple)): 

258 return attr[-1] 

259 return attr 

260 return None 

261 

262 def node_ancestors(self) -> Iterator[NodeNG]: 

263 """Yield parent, grandparent, etc until there are no more.""" 

264 parent = self.parent 

265 while parent is not None: 

266 yield parent 

267 parent = parent.parent 

268 

269 def parent_of(self, node) -> bool: 

270 """Check if this node is the parent of the given node. 

271 

272 :param node: The node to check if it is the child. 

273 :type node: NodeNG 

274 

275 :returns: Whether this node is the parent of the given node. 

276 """ 

277 return any(self is parent for parent in node.node_ancestors()) 

278 

279 def statement(self, *, future: Literal[None, True] = None) -> _base_nodes.Statement: 

280 """The first parent node, including self, marked as statement node. 

281 

282 :raises StatementMissing: If self has no parent attribute. 

283 """ 

284 if future is not None: 

285 warnings.warn( 

286 "The future arg will be removed in astroid 4.0.", 

287 DeprecationWarning, 

288 stacklevel=2, 

289 ) 

290 if self.is_statement: 

291 return cast("_base_nodes.Statement", self) 

292 if not self.parent: 

293 raise StatementMissing(target=self) 

294 return self.parent.statement() 

295 

296 def frame( 

297 self, *, future: Literal[None, True] = None 

298 ) -> nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda: 

299 """The first parent frame node. 

300 

301 A frame node is a :class:`Module`, :class:`FunctionDef`, 

302 :class:`ClassDef` or :class:`Lambda`. 

303 

304 :returns: The first parent frame node. 

305 :raises ParentMissingError: If self has no parent attribute. 

306 """ 

307 if future is not None: 

308 warnings.warn( 

309 "The future arg will be removed in astroid 4.0.", 

310 DeprecationWarning, 

311 stacklevel=2, 

312 ) 

313 if self.parent is None: 

314 raise ParentMissingError(target=self) 

315 return self.parent.frame(future=future) 

316 

317 def scope(self) -> nodes.LocalsDictNodeNG: 

318 """The first parent node defining a new scope. 

319 

320 These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes. 

321 

322 :returns: The first parent scope node. 

323 """ 

324 if not self.parent: 

325 raise ParentMissingError(target=self) 

326 return self.parent.scope() 

327 

328 def root(self) -> nodes.Module: 

329 """Return the root node of the syntax tree. 

330 

331 :returns: The root node. 

332 """ 

333 if not (parent := self.parent): 

334 assert isinstance(self, nodes.Module) 

335 return self 

336 

337 while parent.parent: 

338 parent = parent.parent 

339 assert isinstance(parent, nodes.Module) 

340 return parent 

341 

342 def child_sequence(self, child): 

343 """Search for the sequence that contains this child. 

344 

345 :param child: The child node to search sequences for. 

346 :type child: NodeNG 

347 

348 :returns: The sequence containing the given child node. 

349 :rtype: iterable(NodeNG) 

350 

351 :raises AstroidError: If no sequence could be found that contains 

352 the given child. 

353 """ 

354 for field in self._astroid_fields: 

355 node_or_sequence = getattr(self, field) 

356 if node_or_sequence is child: 

357 return [node_or_sequence] 

358 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes 

359 if ( 

360 isinstance(node_or_sequence, (tuple, list)) 

361 and child in node_or_sequence 

362 ): 

363 return node_or_sequence 

364 

365 msg = "Could not find %s in %s's children" 

366 raise AstroidError(msg % (repr(child), repr(self))) 

367 

368 def locate_child(self, child): 

369 """Find the field of this node that contains the given child. 

370 

371 :param child: The child node to search fields for. 

372 :type child: NodeNG 

373 

374 :returns: A tuple of the name of the field that contains the child, 

375 and the sequence or node that contains the child node. 

376 :rtype: tuple(str, iterable(NodeNG) or NodeNG) 

377 

378 :raises AstroidError: If no field could be found that contains 

379 the given child. 

380 """ 

381 for field in self._astroid_fields: 

382 node_or_sequence = getattr(self, field) 

383 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes 

384 if child is node_or_sequence: 

385 return field, child 

386 if ( 

387 isinstance(node_or_sequence, (tuple, list)) 

388 and child in node_or_sequence 

389 ): 

390 return field, node_or_sequence 

391 msg = "Could not find %s in %s's children" 

392 raise AstroidError(msg % (repr(child), repr(self))) 

393 

394 # FIXME : should we merge child_sequence and locate_child ? locate_child 

395 # is only used in are_exclusive, child_sequence one time in pylint. 

396 

397 def next_sibling(self): 

398 """The next sibling statement node. 

399 

400 :returns: The next sibling statement node. 

401 :rtype: NodeNG or None 

402 """ 

403 return self.parent.next_sibling() 

404 

405 def previous_sibling(self): 

406 """The previous sibling statement. 

407 

408 :returns: The previous sibling statement node. 

409 :rtype: NodeNG or None 

410 """ 

411 return self.parent.previous_sibling() 

412 

413 # these are lazy because they're relatively expensive to compute for every 

414 # single node, and they rarely get looked at 

415 

416 @cached_property 

417 def fromlineno(self) -> int: 

418 """The first line that this node appears on in the source code. 

419 

420 Can also return 0 if the line can not be determined. 

421 """ 

422 if self.lineno is None: 

423 return self._fixed_source_line() 

424 return self.lineno 

425 

426 @cached_property 

427 def tolineno(self) -> int: 

428 """The last line that this node appears on in the source code. 

429 

430 Can also return 0 if the line can not be determined. 

431 """ 

432 if self.end_lineno is not None: 

433 return self.end_lineno 

434 if not self._astroid_fields: 

435 # can't have children 

436 last_child = None 

437 else: 

438 last_child = self.last_child() 

439 if last_child is None: 

440 return self.fromlineno 

441 return last_child.tolineno 

442 

443 def _fixed_source_line(self) -> int: 

444 """Attempt to find the line that this node appears on. 

445 

446 We need this method since not all nodes have :attr:`lineno` set. 

447 Will return 0 if the line number can not be determined. 

448 """ 

449 line = self.lineno 

450 _node = self 

451 try: 

452 while line is None: 

453 _node = next(_node.get_children()) 

454 line = _node.lineno 

455 except StopIteration: 

456 parent = self.parent 

457 while parent and line is None: 

458 line = parent.lineno 

459 parent = parent.parent 

460 return line or 0 

461 

462 def block_range(self, lineno: int) -> tuple[int, int]: 

463 """Get a range from the given line number to where this node ends. 

464 

465 :param lineno: The line number to start the range at. 

466 

467 :returns: The range of line numbers that this node belongs to, 

468 starting at the given line number. 

469 """ 

470 return lineno, self.tolineno 

471 

472 def set_local(self, name: str, stmt: NodeNG) -> None: 

473 """Define that the given name is declared in the given statement node. 

474 

475 This definition is stored on the parent scope node. 

476 

477 .. seealso:: :meth:`scope` 

478 

479 :param name: The name that is being defined. 

480 

481 :param stmt: The statement that defines the given name. 

482 """ 

483 assert self.parent 

484 self.parent.set_local(name, stmt) 

485 

486 @overload 

487 def nodes_of_class( 

488 self, 

489 klass: type[_NodesT], 

490 skip_klass: SkipKlassT = ..., 

491 ) -> Iterator[_NodesT]: ... 

492 

493 @overload 

494 def nodes_of_class( 

495 self, 

496 klass: tuple[type[_NodesT], type[_NodesT2]], 

497 skip_klass: SkipKlassT = ..., 

498 ) -> Iterator[_NodesT] | Iterator[_NodesT2]: ... 

499 

500 @overload 

501 def nodes_of_class( 

502 self, 

503 klass: tuple[type[_NodesT], type[_NodesT2], type[_NodesT3]], 

504 skip_klass: SkipKlassT = ..., 

505 ) -> Iterator[_NodesT] | Iterator[_NodesT2] | Iterator[_NodesT3]: ... 

506 

507 @overload 

508 def nodes_of_class( 

509 self, 

510 klass: tuple[type[_NodesT], ...], 

511 skip_klass: SkipKlassT = ..., 

512 ) -> Iterator[_NodesT]: ... 

513 

514 def nodes_of_class( # type: ignore[misc] # mypy doesn't correctly recognize the overloads 

515 self, 

516 klass: ( 

517 type[_NodesT] 

518 | tuple[type[_NodesT], type[_NodesT2]] 

519 | tuple[type[_NodesT], type[_NodesT2], type[_NodesT3]] 

520 | tuple[type[_NodesT], ...] 

521 ), 

522 skip_klass: SkipKlassT = None, 

523 ) -> Iterator[_NodesT] | Iterator[_NodesT2] | Iterator[_NodesT3]: 

524 """Get the nodes (including this one or below) of the given types. 

525 

526 :param klass: The types of node to search for. 

527 

528 :param skip_klass: The types of node to ignore. This is useful to ignore 

529 subclasses of :attr:`klass`. 

530 

531 :returns: The node of the given types. 

532 """ 

533 if isinstance(self, klass): 

534 yield self 

535 

536 if skip_klass is None: 

537 for child_node in self.get_children(): 

538 yield from child_node.nodes_of_class(klass, skip_klass) 

539 

540 return 

541 

542 for child_node in self.get_children(): 

543 if isinstance(child_node, skip_klass): 

544 continue 

545 yield from child_node.nodes_of_class(klass, skip_klass) 

546 

547 @cached_property 

548 def _assign_nodes_in_scope(self) -> list[nodes.Assign]: 

549 return [] 

550 

551 def _get_name_nodes(self): 

552 for child_node in self.get_children(): 

553 yield from child_node._get_name_nodes() 

554 

555 def _get_return_nodes_skip_functions(self): 

556 yield from () 

557 

558 def _get_yield_nodes_skip_functions(self): 

559 yield from () 

560 

561 def _get_yield_nodes_skip_lambdas(self): 

562 yield from () 

563 

564 def _infer_name(self, frame, name): 

565 # overridden for ImportFrom, Import, Global, Try, TryStar and Arguments 

566 pass 

567 

568 def _infer( 

569 self, context: InferenceContext | None = None, **kwargs: Any 

570 ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: 

571 """We don't know how to resolve a statement by default.""" 

572 # this method is overridden by most concrete classes 

573 raise InferenceError( 

574 "No inference function for {node!r}.", node=self, context=context 

575 ) 

576 

577 def inferred(self): 

578 """Get a list of the inferred values. 

579 

580 .. seealso:: :ref:`inference` 

581 

582 :returns: The inferred values. 

583 :rtype: list 

584 """ 

585 return list(self.infer()) 

586 

587 def instantiate_class(self): 

588 """Instantiate an instance of the defined class. 

589 

590 .. note:: 

591 

592 On anything other than a :class:`ClassDef` this will return self. 

593 

594 :returns: An instance of the defined class. 

595 :rtype: object 

596 """ 

597 return self 

598 

599 def has_base(self, node) -> bool: 

600 """Check if this node inherits from the given type. 

601 

602 :param node: The node defining the base to look for. 

603 Usually this is a :class:`Name` node. 

604 :type node: NodeNG 

605 """ 

606 return False 

607 

608 def callable(self) -> bool: 

609 """Whether this node defines something that is callable. 

610 

611 :returns: Whether this defines something that is callable. 

612 """ 

613 return False 

614 

615 def eq(self, value) -> bool: 

616 return False 

617 

618 def as_string(self) -> str: 

619 """Get the source code that this node represents.""" 

620 return AsStringVisitor()(self) 

621 

622 def repr_tree( 

623 self, 

624 ids=False, 

625 include_linenos=False, 

626 ast_state=False, 

627 indent=" ", 

628 max_depth=0, 

629 max_width=80, 

630 ) -> str: 

631 """Get a string representation of the AST from this node. 

632 

633 :param ids: If true, includes the ids with the node type names. 

634 :type ids: bool 

635 

636 :param include_linenos: If true, includes the line numbers and 

637 column offsets. 

638 :type include_linenos: bool 

639 

640 :param ast_state: If true, includes information derived from 

641 the whole AST like local and global variables. 

642 :type ast_state: bool 

643 

644 :param indent: A string to use to indent the output string. 

645 :type indent: str 

646 

647 :param max_depth: If set to a positive integer, won't return 

648 nodes deeper than max_depth in the string. 

649 :type max_depth: int 

650 

651 :param max_width: Attempt to format the output string to stay 

652 within this number of characters, but can exceed it under some 

653 circumstances. Only positive integer values are valid, the default is 80. 

654 :type max_width: int 

655 

656 :returns: The string representation of the AST. 

657 :rtype: str 

658 """ 

659 

660 # pylint: disable = too-many-statements 

661 

662 @_singledispatch 

663 def _repr_tree(node, result, done, cur_indent="", depth=1): 

664 """Outputs a representation of a non-tuple/list, non-node that's 

665 contained within an AST, including strings. 

666 """ 

667 lines = pprint.pformat( 

668 node, width=max(max_width - len(cur_indent), 1) 

669 ).splitlines(True) 

670 result.append(lines[0]) 

671 result.extend([cur_indent + line for line in lines[1:]]) 

672 return len(lines) != 1 

673 

674 # pylint: disable=unused-variable,useless-suppression; doesn't understand singledispatch 

675 @_repr_tree.register(tuple) 

676 @_repr_tree.register(list) 

677 def _repr_seq(node, result, done, cur_indent="", depth=1): 

678 """Outputs a representation of a sequence that's contained within an 

679 AST. 

680 """ 

681 cur_indent += indent 

682 result.append("[") 

683 if not node: 

684 broken = False 

685 elif len(node) == 1: 

686 broken = _repr_tree(node[0], result, done, cur_indent, depth) 

687 elif len(node) == 2: 

688 broken = _repr_tree(node[0], result, done, cur_indent, depth) 

689 if not broken: 

690 result.append(", ") 

691 else: 

692 result.append(",\n") 

693 result.append(cur_indent) 

694 broken = _repr_tree(node[1], result, done, cur_indent, depth) or broken 

695 else: 

696 result.append("\n") 

697 result.append(cur_indent) 

698 for child in node[:-1]: 

699 _repr_tree(child, result, done, cur_indent, depth) 

700 result.append(",\n") 

701 result.append(cur_indent) 

702 _repr_tree(node[-1], result, done, cur_indent, depth) 

703 broken = True 

704 result.append("]") 

705 return broken 

706 

707 # pylint: disable=unused-variable,useless-suppression; doesn't understand singledispatch 

708 @_repr_tree.register(NodeNG) 

709 def _repr_node(node, result, done, cur_indent="", depth=1): 

710 """Outputs a strings representation of an astroid node.""" 

711 if node in done: 

712 result.append( 

713 indent + f"<Recursion on {type(node).__name__} with id={id(node)}" 

714 ) 

715 return False 

716 done.add(node) 

717 

718 if max_depth and depth > max_depth: 

719 result.append("...") 

720 return False 

721 depth += 1 

722 cur_indent += indent 

723 if ids: 

724 result.append(f"{type(node).__name__}<0x{id(node):x}>(\n") 

725 else: 

726 result.append(f"{type(node).__name__}(") 

727 fields = [] 

728 if include_linenos: 

729 fields.extend(("lineno", "col_offset")) 

730 fields.extend(node._other_fields) 

731 fields.extend(node._astroid_fields) 

732 if ast_state: 

733 fields.extend(node._other_other_fields) 

734 if not fields: 

735 broken = False 

736 elif len(fields) == 1: 

737 result.append(f"{fields[0]}=") 

738 broken = _repr_tree( 

739 getattr(node, fields[0]), result, done, cur_indent, depth 

740 ) 

741 else: 

742 result.append("\n") 

743 result.append(cur_indent) 

744 for field in fields[:-1]: 

745 # TODO: Remove this after removal of the 'doc' attribute 

746 if field == "doc": 

747 continue 

748 result.append(f"{field}=") 

749 _repr_tree(getattr(node, field), result, done, cur_indent, depth) 

750 result.append(",\n") 

751 result.append(cur_indent) 

752 result.append(f"{fields[-1]}=") 

753 _repr_tree(getattr(node, fields[-1]), result, done, cur_indent, depth) 

754 broken = True 

755 result.append(")") 

756 return broken 

757 

758 result: list[str] = [] 

759 _repr_tree(self, result, set()) 

760 return "".join(result) 

761 

762 def bool_value(self, context: InferenceContext | None = None): 

763 """Determine the boolean value of this node. 

764 

765 The boolean value of a node can have three 

766 possible values: 

767 

768 * False: For instance, empty data structures, 

769 False, empty strings, instances which return 

770 explicitly False from the __nonzero__ / __bool__ 

771 method. 

772 * True: Most of constructs are True by default: 

773 classes, functions, modules etc 

774 * Uninferable: The inference engine is uncertain of the 

775 node's value. 

776 

777 :returns: The boolean value of this node. 

778 :rtype: bool or Uninferable 

779 """ 

780 return util.Uninferable 

781 

782 def op_precedence(self) -> int: 

783 # Look up by class name or default to highest precedence 

784 return OP_PRECEDENCE.get(self.__class__.__name__, len(OP_PRECEDENCE)) 

785 

786 def op_left_associative(self) -> bool: 

787 # Everything is left associative except `**` and IfExp 

788 return True