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

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

339 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 Tuple, 

19 Type, 

20 TypeVar, 

21 Union, 

22 cast, 

23 overload, 

24) 

25 

26from astroid import util 

27from astroid.context import InferenceContext 

28from astroid.exceptions import ( 

29 AstroidError, 

30 InferenceError, 

31 ParentMissingError, 

32 StatementMissing, 

33 UseInferenceDefault, 

34) 

35from astroid.manager import AstroidManager 

36from astroid.nodes.as_string import AsStringVisitor 

37from astroid.nodes.const import OP_PRECEDENCE 

38from astroid.nodes.utils import Position 

39from astroid.typing import InferenceErrorInfo, InferenceResult, InferFn 

40 

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

42 from typing import Self 

43else: 

44 from typing_extensions import Self 

45 

46 

47if TYPE_CHECKING: 

48 from astroid import nodes 

49 from astroid.nodes import _base_nodes 

50 

51 

52# Types for 'NodeNG.nodes_of_class()' 

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

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

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

56SkipKlassT = Union[None, Type["NodeNG"], Tuple[Type["NodeNG"], ...]] 

57 

58 

59class NodeNG: 

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

61 

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

63 """ 

64 

65 is_statement: ClassVar[bool] = False 

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

67 optional_assign: ClassVar[bool] = ( 

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

69 ) 

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

71 

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

73 assignment if the loop has no iterations. 

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

75 """ 

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

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

78 is_lambda: ClassVar[bool] = False 

79 

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

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

82 """Node attributes that contain child nodes. 

83 

84 This is redefined in most concrete classes. 

85 """ 

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

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

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

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

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

91 _explicit_inference: InferFn[Self] | None = None 

92 

93 def __init__( 

94 self, 

95 lineno: int | None, 

96 col_offset: int | None, 

97 parent: NodeNG | None, 

98 *, 

99 end_lineno: int | None, 

100 end_col_offset: int | None, 

101 ) -> None: 

102 self.lineno = lineno 

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

104 

105 self.col_offset = col_offset 

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

107 

108 self.parent = parent 

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

110 

111 self.end_lineno = end_lineno 

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

113 

114 self.end_col_offset = end_col_offset 

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

116 

117 Note: This is after the last symbol. 

118 """ 

119 

120 self.position: Position | None = None 

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

122 

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

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

125 """ 

126 

127 def infer( 

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

129 ) -> Generator[InferenceResult, None, None]: 

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

131 

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

133 

134 .. seealso:: :ref:`inference` 

135 

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

137 called instead of the default interface. 

138 

139 :returns: The inferred values. 

140 :rtype: iterable 

141 """ 

142 if context is None: 

143 context = InferenceContext() 

144 else: 

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

146 if self._explicit_inference is not None: 

147 # explicit_inference is not bound, give it self explicitly 

148 try: 

149 for result in self._explicit_inference( 

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

151 context, 

152 **kwargs, 

153 ): 

154 context.nodes_inferred += 1 

155 yield result 

156 return 

157 except UseInferenceDefault: 

158 pass 

159 

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

161 if key in context.inferred: 

162 yield from context.inferred[key] 

163 return 

164 

165 results = [] 

166 

167 # Limit inference amount to help with performance issues with 

168 # exponentially exploding possible results. 

169 limit = AstroidManager().max_inferable_values 

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

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

172 results.append(util.Uninferable) 

173 yield util.Uninferable 

174 break 

175 results.append(result) 

176 yield result 

177 context.nodes_inferred += 1 

178 

179 # Cache generated results for subsequent inferences of the 

180 # same node using the same context 

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

182 return 

183 

184 def repr_name(self) -> str: 

185 """Get a name for nice representation. 

186 

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

188 """ 

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

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

191 return "" 

192 

193 def __str__(self) -> str: 

194 rname = self.repr_name() 

195 cname = type(self).__name__ 

196 if rname: 

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

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

199 else: 

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

201 alignment = len(cname) + 1 

202 result = [] 

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

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

205 width = 80 - len(field) - alignment 

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

207 

208 inner = [lines[0]] 

209 for line in lines[1:]: 

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

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

212 

213 return string % { 

214 "cname": cname, 

215 "rname": rname, 

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

217 } 

218 

219 def __repr__(self) -> str: 

220 rname = self.repr_name() 

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

222 try: 

223 lineno = self.fromlineno 

224 except AttributeError: 

225 lineno = 0 

226 if rname: 

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

228 else: 

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

230 return string % { 

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

232 "rname": rname, 

233 "lineno": lineno, 

234 "id": id(self), 

235 } 

236 

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

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

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

240 return func(self) 

241 

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

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

244 for field in self._astroid_fields: 

245 attr = getattr(self, field) 

246 if attr is None: 

247 continue 

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

249 yield from attr 

250 else: 

251 yield attr 

252 yield from () 

253 

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

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

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

257 attr = getattr(self, field) 

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

259 continue 

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

261 return attr[-1] 

262 return attr 

263 return None 

264 

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

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

267 parent = self.parent 

268 while parent is not None: 

269 yield parent 

270 parent = parent.parent 

271 

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

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

274 

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

276 :type node: NodeNG 

277 

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

279 """ 

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

281 

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

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

284 

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

286 """ 

287 if future is not None: 

288 warnings.warn( 

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

290 DeprecationWarning, 

291 stacklevel=2, 

292 ) 

293 if self.is_statement: 

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

295 if not self.parent: 

296 raise StatementMissing(target=self) 

297 return self.parent.statement() 

298 

299 def frame( 

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

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

302 """The first parent frame node. 

303 

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

305 :class:`ClassDef` or :class:`Lambda`. 

306 

307 :returns: The first parent frame node. 

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

309 """ 

310 if future is not None: 

311 warnings.warn( 

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

313 DeprecationWarning, 

314 stacklevel=2, 

315 ) 

316 if self.parent is None: 

317 raise ParentMissingError(target=self) 

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

319 

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

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

322 

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

324 

325 :returns: The first parent scope node. 

326 """ 

327 if not self.parent: 

328 raise ParentMissingError(target=self) 

329 return self.parent.scope() 

330 

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

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

333 

334 :returns: The root node. 

335 """ 

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

337 return self # type: ignore[return-value] # Only 'Module' does not have a parent node. 

338 

339 while parent.parent: 

340 parent = parent.parent 

341 return parent # type: ignore[return-value] # Only 'Module' does not have a parent node. 

342 

343 def child_sequence(self, child): 

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

345 

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

347 :type child: NodeNG 

348 

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

350 :rtype: iterable(NodeNG) 

351 

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

353 the given child. 

354 """ 

355 for field in self._astroid_fields: 

356 node_or_sequence = getattr(self, field) 

357 if node_or_sequence is child: 

358 return [node_or_sequence] 

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

360 if ( 

361 isinstance(node_or_sequence, (tuple, list)) 

362 and child in node_or_sequence 

363 ): 

364 return node_or_sequence 

365 

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

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

368 

369 def locate_child(self, child): 

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

371 

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

373 :type child: NodeNG 

374 

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

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

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

378 

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

380 the given child. 

381 """ 

382 for field in self._astroid_fields: 

383 node_or_sequence = getattr(self, field) 

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

385 if child is node_or_sequence: 

386 return field, child 

387 if ( 

388 isinstance(node_or_sequence, (tuple, list)) 

389 and child in node_or_sequence 

390 ): 

391 return field, node_or_sequence 

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

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

394 

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

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

397 

398 def next_sibling(self): 

399 """The next sibling statement node. 

400 

401 :returns: The next sibling statement node. 

402 :rtype: NodeNG or None 

403 """ 

404 return self.parent.next_sibling() 

405 

406 def previous_sibling(self): 

407 """The previous sibling statement. 

408 

409 :returns: The previous sibling statement node. 

410 :rtype: NodeNG or None 

411 """ 

412 return self.parent.previous_sibling() 

413 

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

415 # single node, and they rarely get looked at 

416 

417 @cached_property 

418 def fromlineno(self) -> int: 

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

420 

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

422 """ 

423 if self.lineno is None: 

424 return self._fixed_source_line() 

425 return self.lineno 

426 

427 @cached_property 

428 def tolineno(self) -> int: 

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

430 

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

432 """ 

433 if self.end_lineno is not None: 

434 return self.end_lineno 

435 if not self._astroid_fields: 

436 # can't have children 

437 last_child = None 

438 else: 

439 last_child = self.last_child() 

440 if last_child is None: 

441 return self.fromlineno 

442 return last_child.tolineno 

443 

444 def _fixed_source_line(self) -> int: 

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

446 

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

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

449 """ 

450 line = self.lineno 

451 _node = self 

452 try: 

453 while line is None: 

454 _node = next(_node.get_children()) 

455 line = _node.lineno 

456 except StopIteration: 

457 parent = self.parent 

458 while parent and line is None: 

459 line = parent.lineno 

460 parent = parent.parent 

461 return line or 0 

462 

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

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

465 

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

467 

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

469 starting at the given line number. 

470 """ 

471 return lineno, self.tolineno 

472 

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

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

475 

476 This definition is stored on the parent scope node. 

477 

478 .. seealso:: :meth:`scope` 

479 

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

481 

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

483 """ 

484 assert self.parent 

485 self.parent.set_local(name, stmt) 

486 

487 @overload 

488 def nodes_of_class( 

489 self, 

490 klass: type[_NodesT], 

491 skip_klass: SkipKlassT = ..., 

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

493 

494 @overload 

495 def nodes_of_class( 

496 self, 

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

498 skip_klass: SkipKlassT = ..., 

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

500 

501 @overload 

502 def nodes_of_class( 

503 self, 

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

505 skip_klass: SkipKlassT = ..., 

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

507 

508 @overload 

509 def nodes_of_class( 

510 self, 

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

512 skip_klass: SkipKlassT = ..., 

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

514 

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

516 self, 

517 klass: ( 

518 type[_NodesT] 

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

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

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

522 ), 

523 skip_klass: SkipKlassT = None, 

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

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

526 

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

528 

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

530 subclasses of :attr:`klass`. 

531 

532 :returns: The node of the given types. 

533 """ 

534 if isinstance(self, klass): 

535 yield self 

536 

537 if skip_klass is None: 

538 for child_node in self.get_children(): 

539 yield from child_node.nodes_of_class(klass, skip_klass) 

540 

541 return 

542 

543 for child_node in self.get_children(): 

544 if isinstance(child_node, skip_klass): 

545 continue 

546 yield from child_node.nodes_of_class(klass, skip_klass) 

547 

548 @cached_property 

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

550 return [] 

551 

552 def _get_name_nodes(self): 

553 for child_node in self.get_children(): 

554 yield from child_node._get_name_nodes() 

555 

556 def _get_return_nodes_skip_functions(self): 

557 yield from () 

558 

559 def _get_yield_nodes_skip_functions(self): 

560 yield from () 

561 

562 def _get_yield_nodes_skip_lambdas(self): 

563 yield from () 

564 

565 def _infer_name(self, frame, name): 

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

567 pass 

568 

569 def _infer( 

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

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

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

573 # this method is overridden by most concrete classes 

574 raise InferenceError( 

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

576 ) 

577 

578 def inferred(self): 

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

580 

581 .. seealso:: :ref:`inference` 

582 

583 :returns: The inferred values. 

584 :rtype: list 

585 """ 

586 return list(self.infer()) 

587 

588 def instantiate_class(self): 

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

590 

591 .. note:: 

592 

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

594 

595 :returns: An instance of the defined class. 

596 :rtype: object 

597 """ 

598 return self 

599 

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

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

602 

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

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

605 :type node: NodeNG 

606 """ 

607 return False 

608 

609 def callable(self) -> bool: 

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

611 

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

613 """ 

614 return False 

615 

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

617 return False 

618 

619 def as_string(self) -> str: 

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

621 return AsStringVisitor()(self) 

622 

623 def repr_tree( 

624 self, 

625 ids=False, 

626 include_linenos=False, 

627 ast_state=False, 

628 indent=" ", 

629 max_depth=0, 

630 max_width=80, 

631 ) -> str: 

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

633 

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

635 :type ids: bool 

636 

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

638 column offsets. 

639 :type include_linenos: bool 

640 

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

642 the whole AST like local and global variables. 

643 :type ast_state: bool 

644 

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

646 :type indent: str 

647 

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

649 nodes deeper than max_depth in the string. 

650 :type max_depth: int 

651 

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

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

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

655 :type max_width: int 

656 

657 :returns: The string representation of the AST. 

658 :rtype: str 

659 """ 

660 

661 @_singledispatch 

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

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

664 contained within an AST, including strings. 

665 """ 

666 lines = pprint.pformat( 

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

668 ).splitlines(True) 

669 result.append(lines[0]) 

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

671 return len(lines) != 1 

672 

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

674 @_repr_tree.register(tuple) 

675 @_repr_tree.register(list) 

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

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

678 AST. 

679 """ 

680 cur_indent += indent 

681 result.append("[") 

682 if not node: 

683 broken = False 

684 elif len(node) == 1: 

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

686 elif len(node) == 2: 

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

688 if not broken: 

689 result.append(", ") 

690 else: 

691 result.append(",\n") 

692 result.append(cur_indent) 

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

694 else: 

695 result.append("\n") 

696 result.append(cur_indent) 

697 for child in node[:-1]: 

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

699 result.append(",\n") 

700 result.append(cur_indent) 

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

702 broken = True 

703 result.append("]") 

704 return broken 

705 

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

707 @_repr_tree.register(NodeNG) 

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

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

710 if node in done: 

711 result.append( 

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

713 ) 

714 return False 

715 done.add(node) 

716 

717 if max_depth and depth > max_depth: 

718 result.append("...") 

719 return False 

720 depth += 1 

721 cur_indent += indent 

722 if ids: 

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

724 else: 

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

726 fields = [] 

727 if include_linenos: 

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

729 fields.extend(node._other_fields) 

730 fields.extend(node._astroid_fields) 

731 if ast_state: 

732 fields.extend(node._other_other_fields) 

733 if not fields: 

734 broken = False 

735 elif len(fields) == 1: 

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

737 broken = _repr_tree( 

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

739 ) 

740 else: 

741 result.append("\n") 

742 result.append(cur_indent) 

743 for field in fields[:-1]: 

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

745 if field == "doc": 

746 continue 

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

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

749 result.append(",\n") 

750 result.append(cur_indent) 

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

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

753 broken = True 

754 result.append(")") 

755 return broken 

756 

757 result: list[str] = [] 

758 _repr_tree(self, result, set()) 

759 return "".join(result) 

760 

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

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

763 

764 The boolean value of a node can have three 

765 possible values: 

766 

767 * False: For instance, empty data structures, 

768 False, empty strings, instances which return 

769 explicitly False from the __nonzero__ / __bool__ 

770 method. 

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

772 classes, functions, modules etc 

773 * Uninferable: The inference engine is uncertain of the 

774 node's value. 

775 

776 :returns: The boolean value of this node. 

777 :rtype: bool or Uninferable 

778 """ 

779 return util.Uninferable 

780 

781 def op_precedence(self): 

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

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

784 

785 def op_left_associative(self) -> bool: 

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

787 return True