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

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

352 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 sys 

8from collections.abc import Generator, Iterator 

9from functools import cached_property 

10from functools import singledispatch as _singledispatch 

11from typing import ( 

12 TYPE_CHECKING, 

13 Any, 

14 ClassVar, 

15 TypeVar, 

16 cast, 

17 overload, 

18) 

19 

20from astroid import nodes, util 

21from astroid.context import InferenceContext 

22from astroid.exceptions import ( 

23 AstroidError, 

24 InferenceError, 

25 ParentMissingError, 

26 StatementMissing, 

27 UseInferenceDefault, 

28) 

29from astroid.manager import AstroidManager 

30from astroid.nodes.as_string import AsStringVisitor 

31from astroid.nodes.const import OP_PRECEDENCE 

32from astroid.nodes.utils import Position 

33from astroid.typing import InferenceErrorInfo, InferenceResult, InferFn 

34 

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

36 from typing import Self 

37else: 

38 from typing_extensions import Self 

39 

40 

41if TYPE_CHECKING: 

42 from astroid.nodes import _base_nodes 

43 

44 FrameType = nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda 

45 

46 

47# Types for 'NodeNG.nodes_of_class()' 

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

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

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

51SkipKlassT = None | type["NodeNG"] | tuple[type["NodeNG"], ...] 

52 

53 

54class NodeNG: 

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

56 

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

58 """ 

59 

60 is_statement: ClassVar[bool] = False 

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

62 optional_assign: ClassVar[bool] = ( 

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

64 ) 

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

66 

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

68 assignment if the loop has no iterations. 

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

70 """ 

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

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

73 is_lambda: ClassVar[bool] = False 

74 

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

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

77 """Node attributes that contain child nodes. 

78 

79 This is redefined in most concrete classes. 

80 """ 

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

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

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

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

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

86 _explicit_inference: InferFn[Self] | None = None 

87 

88 def __init__( 

89 self, 

90 lineno: int | None, 

91 col_offset: int | None, 

92 parent: NodeNG | None, 

93 *, 

94 end_lineno: int | None, 

95 end_col_offset: int | None, 

96 ) -> None: 

97 self.lineno = lineno 

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

99 

100 self.col_offset = col_offset 

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

102 

103 self.parent = parent 

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

105 

106 self.end_lineno = end_lineno 

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

108 

109 self.end_col_offset = end_col_offset 

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

111 

112 Note: This is after the last symbol. 

113 """ 

114 

115 self.position: Position | None = None 

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

117 

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

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

120 """ 

121 

122 def infer( 

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

124 ) -> Generator[InferenceResult]: 

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

126 

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

128 

129 .. seealso:: :ref:`inference` 

130 

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

132 called instead of the default interface. 

133 

134 :returns: The inferred values. 

135 :rtype: iterable 

136 """ 

137 if context is None: 

138 context = InferenceContext() 

139 else: 

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

141 if self._explicit_inference is not None: 

142 # explicit_inference is not bound, give it self explicitly 

143 try: 

144 for result in self._explicit_inference( 

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

146 context, 

147 **kwargs, 

148 ): 

149 context.nodes_inferred += 1 

150 yield result 

151 return 

152 except UseInferenceDefault: 

153 pass 

154 

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

156 if key in context.inferred: 

157 yield from context.inferred[key] 

158 return 

159 

160 results = [] 

161 

162 # Limit inference amount to help with performance issues with 

163 # exponentially exploding possible results. 

164 limit = AstroidManager().max_inferable_values 

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

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

167 results.append(util.Uninferable) 

168 yield util.Uninferable 

169 break 

170 results.append(result) 

171 yield result 

172 context.nodes_inferred += 1 

173 

174 # Cache generated results for subsequent inferences of the 

175 # same node using the same context 

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

177 return 

178 

179 def repr_name(self) -> str: 

180 """Get a name for nice representation. 

181 

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

183 """ 

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

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

186 return "" 

187 

188 def __str__(self) -> str: 

189 import pprint # pylint: disable=import-outside-toplevel 

190 

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 = max(80 - len(field) - alignment, 1) 

203 try: 

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

205 except ValueError: 

206 lines = [f"<{type(value).__name__}>"] 

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) -> _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 self.is_statement: 

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

289 if not self.parent: 

290 raise StatementMissing(target=self) 

291 return self.parent.statement() 

292 

293 def frame(self) -> FrameType: 

294 """The first parent frame node. 

295 

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

297 :class:`ClassDef` or :class:`Lambda`. 

298 

299 :returns: The first parent frame node. 

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

301 """ 

302 if self.parent is None: 

303 raise ParentMissingError(target=self) 

304 return self.parent.frame() 

305 

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

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

308 

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

310 

311 :returns: The first parent scope node. 

312 """ 

313 if not self.parent: 

314 raise ParentMissingError(target=self) 

315 return self.parent.scope() 

316 

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

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

319 

320 :returns: The root node. 

321 """ 

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

323 assert isinstance(self, nodes.Module) 

324 return self 

325 

326 while parent.parent: 

327 parent = parent.parent 

328 assert isinstance(parent, nodes.Module) 

329 return parent 

330 

331 def child_sequence(self, child): 

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

333 

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

335 :type child: NodeNG 

336 

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

338 :rtype: iterable(NodeNG) 

339 

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

341 the given child. 

342 """ 

343 for field in self._astroid_fields: 

344 node_or_sequence = getattr(self, field) 

345 if node_or_sequence is child: 

346 return [node_or_sequence] 

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

348 if ( 

349 isinstance(node_or_sequence, (tuple, list)) 

350 and child in node_or_sequence 

351 ): 

352 return node_or_sequence 

353 

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

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

356 

357 def locate_child(self, child): 

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

359 

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

361 :type child: NodeNG 

362 

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

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

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

366 

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

368 the given child. 

369 """ 

370 for field in self._astroid_fields: 

371 node_or_sequence = getattr(self, field) 

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

373 if child is node_or_sequence: 

374 return field, child 

375 if ( 

376 isinstance(node_or_sequence, (tuple, list)) 

377 and child in node_or_sequence 

378 ): 

379 return field, node_or_sequence 

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

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

382 

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

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

385 

386 def next_sibling(self): 

387 """The next sibling statement node. 

388 

389 :returns: The next sibling statement node. 

390 :rtype: NodeNG or None 

391 """ 

392 return self.parent.next_sibling() 

393 

394 def previous_sibling(self): 

395 """The previous sibling statement. 

396 

397 :returns: The previous sibling statement node. 

398 :rtype: NodeNG or None 

399 """ 

400 return self.parent.previous_sibling() 

401 

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

403 # single node, and they rarely get looked at 

404 

405 @cached_property 

406 def fromlineno(self) -> int: 

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

408 

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

410 """ 

411 if self.lineno is None: 

412 return self._fixed_source_line() 

413 return self.lineno 

414 

415 @cached_property 

416 def tolineno(self) -> int: 

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

418 

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

420 """ 

421 if self.end_lineno is not None: 

422 return self.end_lineno 

423 if not self._astroid_fields: 

424 # can't have children 

425 last_child = None 

426 else: 

427 last_child = self.last_child() 

428 if last_child is None: 

429 return self.fromlineno 

430 return last_child.tolineno 

431 

432 def _fixed_source_line(self) -> int: 

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

434 

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

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

437 """ 

438 line = self.lineno 

439 _node = self 

440 try: 

441 while line is None: 

442 _node = next(_node.get_children()) 

443 line = _node.lineno 

444 except StopIteration: 

445 parent = self.parent 

446 while parent and line is None: 

447 line = parent.lineno 

448 parent = parent.parent 

449 return line or 0 

450 

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

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

453 

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

455 

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

457 starting at the given line number. 

458 """ 

459 return lineno, self.tolineno 

460 

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

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

463 

464 This definition is stored on the parent scope node. 

465 

466 .. seealso:: :meth:`scope` 

467 

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

469 

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

471 """ 

472 assert self.parent 

473 self.parent.set_local(name, stmt) 

474 

475 @overload 

476 def nodes_of_class( 

477 self, 

478 klass: type[_NodesT], 

479 skip_klass: SkipKlassT = ..., 

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

481 

482 @overload 

483 def nodes_of_class( 

484 self, 

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

486 skip_klass: SkipKlassT = ..., 

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

488 

489 @overload 

490 def nodes_of_class( 

491 self, 

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

493 skip_klass: SkipKlassT = ..., 

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

495 

496 @overload 

497 def nodes_of_class( 

498 self, 

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

500 skip_klass: SkipKlassT = ..., 

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

502 

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

504 self, 

505 klass: ( 

506 type[_NodesT] 

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

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

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

510 ), 

511 skip_klass: SkipKlassT = None, 

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

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

514 

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

516 

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

518 subclasses of :attr:`klass`. 

519 

520 :returns: The node of the given types. 

521 """ 

522 if isinstance(self, klass): 

523 yield self 

524 

525 if skip_klass is None: 

526 for child_node in self.get_children(): 

527 yield from child_node.nodes_of_class(klass, skip_klass) 

528 

529 return 

530 

531 for child_node in self.get_children(): 

532 if isinstance(child_node, skip_klass): 

533 continue 

534 yield from child_node.nodes_of_class(klass, skip_klass) 

535 

536 @cached_property 

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

538 return [] 

539 

540 def _get_name_nodes(self): 

541 for child_node in self.get_children(): 

542 yield from child_node._get_name_nodes() 

543 

544 def _get_return_nodes_skip_functions(self): 

545 yield from () 

546 

547 def _get_yield_nodes_skip_functions(self): 

548 yield from () 

549 

550 def _get_yield_nodes_skip_lambdas(self): 

551 yield from () 

552 

553 def _infer_name(self, frame, name): 

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

555 pass 

556 

557 def _infer( 

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

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

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

561 # this method is overridden by most concrete classes 

562 raise InferenceError( 

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

564 ) 

565 

566 def inferred(self): 

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

568 

569 .. seealso:: :ref:`inference` 

570 

571 :returns: The inferred values. 

572 :rtype: list 

573 """ 

574 return list(self.infer()) 

575 

576 def instantiate_class(self): 

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

578 

579 .. note:: 

580 

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

582 

583 :returns: An instance of the defined class. 

584 :rtype: object 

585 """ 

586 return self 

587 

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

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

590 

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

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

593 :type node: NodeNG 

594 """ 

595 return False 

596 

597 def callable(self) -> bool: 

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

599 

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

601 """ 

602 return False 

603 

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

605 return False 

606 

607 def as_string(self) -> str: 

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

609 return AsStringVisitor()(self) 

610 

611 def repr_tree( 

612 self, 

613 ids=False, 

614 include_linenos=False, 

615 ast_state=False, 

616 indent=" ", 

617 max_depth=0, 

618 max_width=80, 

619 ) -> str: 

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

621 

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

623 :type ids: bool 

624 

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

626 column offsets. 

627 :type include_linenos: bool 

628 

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

630 the whole AST like local and global variables. 

631 :type ast_state: bool 

632 

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

634 :type indent: str 

635 

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

637 nodes deeper than max_depth in the string. 

638 :type max_depth: int 

639 

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

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

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

643 :type max_width: int 

644 

645 :returns: The string representation of the AST. 

646 :rtype: str 

647 """ 

648 

649 # pylint: disable = too-many-statements 

650 import pprint # pylint: disable=import-outside-toplevel 

651 

652 @_singledispatch 

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

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

655 contained within an AST, including strings. 

656 """ 

657 lines = pprint.pformat( 

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

659 ).splitlines(True) 

660 result.append(lines[0]) 

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

662 return len(lines) != 1 

663 

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

665 @_repr_tree.register(tuple) 

666 @_repr_tree.register(list) 

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

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

669 AST. 

670 """ 

671 cur_indent += indent 

672 result.append("[") 

673 if not node: 

674 broken = False 

675 elif len(node) == 1: 

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

677 elif len(node) == 2: 

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

679 if not broken: 

680 result.append(", ") 

681 else: 

682 result.append(",\n") 

683 result.append(cur_indent) 

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

685 else: 

686 result.append("\n") 

687 result.append(cur_indent) 

688 for child in node[:-1]: 

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

690 result.append(",\n") 

691 result.append(cur_indent) 

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

693 broken = True 

694 result.append("]") 

695 return broken 

696 

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

698 @_repr_tree.register(NodeNG) 

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

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

701 if node in done: 

702 result.append( 

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

704 ) 

705 return False 

706 done.add(node) 

707 

708 if max_depth and depth > max_depth: 

709 result.append("...") 

710 return False 

711 depth += 1 

712 cur_indent += indent 

713 if ids: 

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

715 else: 

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

717 fields = [] 

718 if include_linenos: 

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

720 fields.extend(node._other_fields) 

721 fields.extend(node._astroid_fields) 

722 if ast_state: 

723 fields.extend(node._other_other_fields) 

724 if not fields: 

725 broken = False 

726 elif len(fields) == 1: 

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

728 broken = _repr_tree( 

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

730 ) 

731 else: 

732 result.append("\n") 

733 result.append(cur_indent) 

734 for field in fields[:-1]: 

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

736 if field == "doc": 

737 continue 

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

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

740 result.append(",\n") 

741 result.append(cur_indent) 

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

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

744 broken = True 

745 result.append(")") 

746 return broken 

747 

748 result: list[str] = [] 

749 _repr_tree(self, result, set()) 

750 return "".join(result) 

751 

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

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

754 

755 The boolean value of a node can have three 

756 possible values: 

757 

758 * False: For instance, empty data structures, 

759 False, empty strings, instances which return 

760 explicitly False from the __nonzero__ / __bool__ 

761 method. 

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

763 classes, functions, modules etc 

764 * Uninferable: The inference engine is uncertain of the 

765 node's value. 

766 

767 :returns: The boolean value of this node. 

768 :rtype: bool or Uninferable 

769 """ 

770 return util.Uninferable 

771 

772 def op_precedence(self) -> int: 

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

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

775 

776 def op_left_associative(self) -> bool: 

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

778 return True