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

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

2113 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"""Module for some node classes. More nodes in scoped_nodes.py""" 

6 

7from __future__ import annotations 

8 

9import abc 

10import ast 

11import itertools 

12import operator 

13import sys 

14import typing 

15import warnings 

16from collections.abc import Callable, Generator, Iterable, Iterator, Mapping 

17from functools import cached_property 

18from typing import TYPE_CHECKING, Any, ClassVar, Literal, Union 

19 

20from astroid import decorators, protocols, util 

21from astroid.bases import Instance, _infer_stmts 

22from astroid.const import _EMPTY_OBJECT_MARKER, PY314_PLUS, Context 

23from astroid.context import CallContext, InferenceContext, copy_context 

24from astroid.exceptions import ( 

25 AstroidBuildingError, 

26 AstroidError, 

27 AstroidIndexError, 

28 AstroidTypeError, 

29 AstroidValueError, 

30 AttributeInferenceError, 

31 InferenceError, 

32 NameInferenceError, 

33 NoDefault, 

34 ParentMissingError, 

35 _NonDeducibleTypeHierarchy, 

36) 

37from astroid.interpreter import dunder_lookup 

38from astroid.manager import AstroidManager 

39from astroid.nodes import _base_nodes 

40from astroid.nodes.const import OP_PRECEDENCE 

41from astroid.nodes.node_ng import NodeNG 

42from astroid.nodes.scoped_nodes import SYNTHETIC_ROOT 

43from astroid.typing import ( 

44 ConstFactoryResult, 

45 InferenceErrorInfo, 

46 InferenceResult, 

47 SuccessfulInferenceResult, 

48) 

49 

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

51 from typing import Self 

52else: 

53 from typing_extensions import Self 

54 

55if TYPE_CHECKING: 

56 from astroid import nodes 

57 from astroid.nodes import LocalsDictNodeNG 

58 

59 

60def _is_const(value) -> bool: 

61 return isinstance(value, tuple(CONST_CLS)) 

62 

63 

64_NodesT = typing.TypeVar("_NodesT", bound=NodeNG) 

65_BadOpMessageT = typing.TypeVar("_BadOpMessageT", bound=util.BadOperationMessage) 

66 

67# pylint: disable-next=consider-alternative-union-syntax 

68AssignedStmtsPossibleNode = Union["List", "Tuple", "AssignName", "AssignAttr", None] 

69AssignedStmtsCall = Callable[ 

70 [ 

71 _NodesT, 

72 AssignedStmtsPossibleNode, 

73 InferenceContext | None, 

74 list[int] | None, 

75 ], 

76 Any, 

77] 

78InferBinaryOperation = Callable[ 

79 [_NodesT, InferenceContext | None], 

80 Generator[InferenceResult | _BadOpMessageT], 

81] 

82InferLHS = Callable[ 

83 [_NodesT, InferenceContext | None], 

84 Generator[InferenceResult, None, InferenceErrorInfo | None], 

85] 

86InferUnaryOp = Callable[[_NodesT, str], ConstFactoryResult] 

87 

88 

89@decorators.raise_if_nothing_inferred 

90def unpack_infer(stmt, context: InferenceContext | None = None): 

91 """recursively generate nodes inferred by the given statement. 

92 If the inferred value is a list or a tuple, recurse on the elements 

93 """ 

94 if isinstance(stmt, (List, Tuple)): 

95 for elt in stmt.elts: 

96 if elt is util.Uninferable: 

97 yield elt 

98 continue 

99 yield from unpack_infer(elt, context) 

100 return {"node": stmt, "context": context} 

101 # if inferred is a final node, return it and stop 

102 inferred = next(stmt.infer(context), util.Uninferable) 

103 if inferred is stmt: 

104 yield inferred 

105 return {"node": stmt, "context": context} 

106 # else, infer recursively, except Uninferable object that should be returned as is 

107 for inferred in stmt.infer(context): 

108 if isinstance(inferred, util.UninferableBase): 

109 yield inferred 

110 else: 

111 yield from unpack_infer(inferred, context) 

112 

113 return {"node": stmt, "context": context} 

114 

115 

116def are_exclusive(stmt1, stmt2, exceptions: list[str] | None = None) -> bool: 

117 """return true if the two given statements are mutually exclusive 

118 

119 `exceptions` may be a list of exception names. If specified, discard If 

120 branches and check one of the statement is in an exception handler catching 

121 one of the given exceptions. 

122 

123 algorithm : 

124 1) index stmt1's parents 

125 2) climb among stmt2's parents until we find a common parent 

126 3) if the common parent is a If or Try statement, look if nodes are 

127 in exclusive branches 

128 """ 

129 # index stmt1's parents 

130 stmt1_parents = {} 

131 children = {} 

132 previous = stmt1 

133 for node in stmt1.node_ancestors(): 

134 stmt1_parents[node] = 1 

135 children[node] = previous 

136 previous = node 

137 # climb among stmt2's parents until we find a common parent 

138 previous = stmt2 

139 for node in stmt2.node_ancestors(): 

140 if node in stmt1_parents: 

141 # if the common parent is a If or Try statement, look if 

142 # nodes are in exclusive branches 

143 if isinstance(node, If) and exceptions is None: 

144 c2attr, c2node = node.locate_child(previous) 

145 c1attr, c1node = node.locate_child(children[node]) 

146 if "test" in (c1attr, c2attr): 

147 # If any node is `If.test`, then it must be inclusive with 

148 # the other node (`If.body` and `If.orelse`) 

149 return False 

150 if c1attr != c2attr: 

151 # different `If` branches (`If.body` and `If.orelse`) 

152 return True 

153 elif isinstance(node, Try): 

154 c2attr, c2node = node.locate_child(previous) 

155 c1attr, c1node = node.locate_child(children[node]) 

156 if c1node is not c2node: 

157 first_in_body_caught_by_handlers = ( 

158 c2attr == "handlers" 

159 and c1attr == "body" 

160 and previous.catch(exceptions) 

161 ) 

162 second_in_body_caught_by_handlers = ( 

163 c2attr == "body" 

164 and c1attr == "handlers" 

165 and children[node].catch(exceptions) 

166 ) 

167 first_in_else_other_in_handlers = ( 

168 c2attr == "handlers" and c1attr == "orelse" 

169 ) 

170 second_in_else_other_in_handlers = ( 

171 c2attr == "orelse" and c1attr == "handlers" 

172 ) 

173 if any( 

174 ( 

175 first_in_body_caught_by_handlers, 

176 second_in_body_caught_by_handlers, 

177 first_in_else_other_in_handlers, 

178 second_in_else_other_in_handlers, 

179 ) 

180 ): 

181 return True 

182 elif c2attr == "handlers" and c1attr == "handlers": 

183 return previous is not children[node] 

184 return False 

185 previous = node 

186 return False 

187 

188 

189# getitem() helpers. 

190 

191_SLICE_SENTINEL = object() 

192 

193 

194def _slice_value(index, context: InferenceContext | None = None): 

195 """Get the value of the given slice index.""" 

196 

197 if isinstance(index, Const): 

198 if isinstance(index.value, (int, type(None))): 

199 return index.value 

200 elif index is None: 

201 return None 

202 else: 

203 # Try to infer what the index actually is. 

204 # Since we can't return all the possible values, 

205 # we'll stop at the first possible value. 

206 try: 

207 inferred = next(index.infer(context=context)) 

208 except (InferenceError, StopIteration): 

209 pass 

210 else: 

211 if isinstance(inferred, Const): 

212 if isinstance(inferred.value, (int, type(None))): 

213 return inferred.value 

214 

215 # Use a sentinel, because None can be a valid 

216 # value that this function can return, 

217 # as it is the case for unspecified bounds. 

218 return _SLICE_SENTINEL 

219 

220 

221def _infer_slice(node, context: InferenceContext | None = None): 

222 lower = _slice_value(node.lower, context) 

223 upper = _slice_value(node.upper, context) 

224 step = _slice_value(node.step, context) 

225 if all(elem is not _SLICE_SENTINEL for elem in (lower, upper, step)): 

226 return slice(lower, upper, step) 

227 

228 raise AstroidTypeError( 

229 message="Could not infer slice used in subscript", 

230 node=node, 

231 index=node.parent, 

232 context=context, 

233 ) 

234 

235 

236def _container_getitem(instance, elts, index, context: InferenceContext | None = None): 

237 """Get a slice or an item, using the given *index*, for the given sequence.""" 

238 try: 

239 if isinstance(index, Slice): 

240 index_slice = _infer_slice(index, context=context) 

241 new_cls = instance.__class__() 

242 new_cls.elts = elts[index_slice] 

243 new_cls.parent = instance.parent 

244 return new_cls 

245 if isinstance(index, Const): 

246 return elts[index.value] 

247 except ValueError as exc: 

248 raise AstroidValueError( 

249 message="Slice {index!r} cannot index container", 

250 node=instance, 

251 index=index, 

252 context=context, 

253 ) from exc 

254 except IndexError as exc: 

255 raise AstroidIndexError( 

256 message="Index {index!s} out of range", 

257 node=instance, 

258 index=index, 

259 context=context, 

260 ) from exc 

261 except TypeError as exc: 

262 raise AstroidTypeError( 

263 message="Type error {error!r}", node=instance, index=index, context=context 

264 ) from exc 

265 

266 raise AstroidTypeError(f"Could not use {index} as subscript index") 

267 

268 

269class BaseContainer(_base_nodes.ParentAssignNode, Instance, metaclass=abc.ABCMeta): 

270 """Base class for Set, FrozenSet, Tuple and List.""" 

271 

272 _astroid_fields = ("elts",) 

273 

274 def __init__( 

275 self, 

276 lineno: int | None, 

277 col_offset: int | None, 

278 parent: NodeNG | None, 

279 *, 

280 end_lineno: int | None, 

281 end_col_offset: int | None, 

282 ) -> None: 

283 self.elts: list[SuccessfulInferenceResult] = [] 

284 """The elements in the node.""" 

285 

286 super().__init__( 

287 lineno=lineno, 

288 col_offset=col_offset, 

289 end_lineno=end_lineno, 

290 end_col_offset=end_col_offset, 

291 parent=parent, 

292 ) 

293 

294 def postinit(self, elts: list[SuccessfulInferenceResult]) -> None: 

295 self.elts = elts 

296 

297 @classmethod 

298 def from_elements(cls, elts: Iterable[Any]) -> Self: 

299 """Create a node of this type from the given list of elements. 

300 

301 :param elts: The list of elements that the node should contain. 

302 

303 :returns: A new node containing the given elements. 

304 """ 

305 node = cls( 

306 lineno=None, 

307 col_offset=None, 

308 parent=None, 

309 end_lineno=None, 

310 end_col_offset=None, 

311 ) 

312 node.elts = [const_factory(e) if _is_const(e) else e for e in elts] 

313 return node 

314 

315 def itered(self): 

316 """An iterator over the elements this node contains. 

317 

318 :returns: The contents of this node. 

319 :rtype: iterable(NodeNG) 

320 """ 

321 return self.elts 

322 

323 def bool_value(self, context: InferenceContext | None = None) -> bool: 

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

325 

326 :returns: The boolean value of this node. 

327 """ 

328 return bool(self.elts) 

329 

330 @abc.abstractmethod 

331 def pytype(self) -> str: 

332 """Get the name of the type that this node represents. 

333 

334 :returns: The name of the type. 

335 """ 

336 

337 def get_children(self): 

338 yield from self.elts 

339 

340 @decorators.raise_if_nothing_inferred 

341 def _infer( 

342 self, 

343 context: InferenceContext | None = None, 

344 **kwargs: Any, 

345 ) -> Iterator[Self]: 

346 has_starred_named_expr = any( 

347 isinstance(e, (Starred, NamedExpr)) for e in self.elts 

348 ) 

349 if has_starred_named_expr: 

350 values = self._infer_sequence_helper(context) 

351 new_seq = type(self)( 

352 lineno=self.lineno, 

353 col_offset=self.col_offset, 

354 parent=self.parent, 

355 end_lineno=self.end_lineno, 

356 end_col_offset=self.end_col_offset, 

357 ) 

358 new_seq.postinit(values) 

359 

360 yield new_seq 

361 else: 

362 yield self 

363 

364 def _infer_sequence_helper( 

365 self, context: InferenceContext | None = None 

366 ) -> list[SuccessfulInferenceResult]: 

367 """Infer all values based on BaseContainer.elts.""" 

368 values = [] 

369 

370 for elt in self.elts: 

371 if isinstance(elt, Starred): 

372 starred = util.safe_infer(elt.value, context) 

373 if not starred: 

374 raise InferenceError(node=self, context=context) 

375 if not hasattr(starred, "elts"): 

376 raise InferenceError(node=self, context=context) 

377 # TODO: fresh context? 

378 values.extend(starred._infer_sequence_helper(context)) 

379 elif isinstance(elt, NamedExpr): 

380 value = util.safe_infer(elt.value, context) 

381 if not value: 

382 raise InferenceError(node=self, context=context) 

383 values.append(value) 

384 else: 

385 values.append(elt) 

386 return values 

387 

388 

389# Name classes 

390 

391 

392class AssignName( 

393 _base_nodes.NoChildrenNode, 

394 _base_nodes.LookupMixIn, 

395 _base_nodes.ParentAssignNode, 

396): 

397 """Variation of :class:`ast.Assign` representing assignment to a name. 

398 

399 An :class:`AssignName` is the name of something that is assigned to. 

400 This includes variables defined in a function signature or in a loop. 

401 

402 >>> import astroid 

403 >>> node = astroid.extract_node('variable = range(10)') 

404 >>> node 

405 <Assign l.1 at 0x7effe1db8550> 

406 >>> list(node.get_children()) 

407 [<AssignName.variable l.1 at 0x7effe1db8748>, <Call l.1 at 0x7effe1db8630>] 

408 >>> list(node.get_children())[0].as_string() 

409 'variable' 

410 """ 

411 

412 _other_fields = ("name",) 

413 

414 def __init__( 

415 self, 

416 name: str, 

417 lineno: int, 

418 col_offset: int, 

419 parent: NodeNG, 

420 *, 

421 end_lineno: int | None, 

422 end_col_offset: int | None, 

423 ) -> None: 

424 self.name = name 

425 """The name that is assigned to.""" 

426 

427 super().__init__( 

428 lineno=lineno, 

429 col_offset=col_offset, 

430 end_lineno=end_lineno, 

431 end_col_offset=end_col_offset, 

432 parent=parent, 

433 ) 

434 

435 assigned_stmts = protocols.assend_assigned_stmts 

436 """Returns the assigned statement (non inferred) according to the assignment type. 

437 See astroid/protocols.py for actual implementation. 

438 """ 

439 

440 @decorators.raise_if_nothing_inferred 

441 @decorators.path_wrapper 

442 def _infer( 

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

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

445 """Infer an AssignName: need to inspect the RHS part of the 

446 assign node. 

447 """ 

448 if isinstance(self.parent, AugAssign): 

449 return self.parent.infer(context) 

450 

451 stmts = list(self.assigned_stmts(context=context)) 

452 return _infer_stmts(stmts, context) 

453 

454 @decorators.raise_if_nothing_inferred 

455 def infer_lhs( 

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

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

458 """Infer a Name: use name lookup rules. 

459 

460 Same implementation as Name._infer.""" 

461 # pylint: disable=import-outside-toplevel 

462 from astroid.constraint import get_constraints 

463 from astroid.helpers import _higher_function_scope 

464 

465 frame, stmts = self.lookup(self.name) 

466 if not stmts: 

467 # Try to see if the name is enclosed in a nested function 

468 # and use the higher (first function) scope for searching. 

469 parent_function = _higher_function_scope(self.scope()) 

470 if parent_function: 

471 _, stmts = parent_function.lookup(self.name) 

472 

473 if not stmts: 

474 raise NameInferenceError( 

475 name=self.name, scope=self.scope(), context=context 

476 ) 

477 context = copy_context(context) 

478 context.lookupname = self.name 

479 context.constraints[self.name] = get_constraints(self, frame) 

480 

481 return _infer_stmts(stmts, context, frame) 

482 

483 

484class DelName( 

485 _base_nodes.NoChildrenNode, _base_nodes.LookupMixIn, _base_nodes.ParentAssignNode 

486): 

487 """Variation of :class:`ast.Delete` representing deletion of a name. 

488 

489 A :class:`DelName` is the name of something that is deleted. 

490 

491 >>> import astroid 

492 >>> node = astroid.extract_node("del variable #@") 

493 >>> list(node.get_children()) 

494 [<DelName.variable l.1 at 0x7effe1da4d30>] 

495 >>> list(node.get_children())[0].as_string() 

496 'variable' 

497 """ 

498 

499 _other_fields = ("name",) 

500 

501 def __init__( 

502 self, 

503 name: str, 

504 lineno: int, 

505 col_offset: int, 

506 parent: NodeNG, 

507 *, 

508 end_lineno: int | None, 

509 end_col_offset: int | None, 

510 ) -> None: 

511 self.name = name 

512 """The name that is being deleted.""" 

513 

514 super().__init__( 

515 lineno=lineno, 

516 col_offset=col_offset, 

517 end_lineno=end_lineno, 

518 end_col_offset=end_col_offset, 

519 parent=parent, 

520 ) 

521 

522 

523class Name(_base_nodes.LookupMixIn, _base_nodes.NoChildrenNode): 

524 """Class representing an :class:`ast.Name` node. 

525 

526 A :class:`Name` node is something that is named, but not covered by 

527 :class:`AssignName` or :class:`DelName`. 

528 

529 >>> import astroid 

530 >>> node = astroid.extract_node('range(10)') 

531 >>> node 

532 <Call l.1 at 0x7effe1db8710> 

533 >>> list(node.get_children()) 

534 [<Name.range l.1 at 0x7effe1db86a0>, <Const.int l.1 at 0x7effe1db8518>] 

535 >>> list(node.get_children())[0].as_string() 

536 'range' 

537 """ 

538 

539 _other_fields = ("name",) 

540 

541 def __init__( 

542 self, 

543 name: str, 

544 lineno: int, 

545 col_offset: int, 

546 parent: NodeNG, 

547 *, 

548 end_lineno: int | None, 

549 end_col_offset: int | None, 

550 ) -> None: 

551 self.name = name 

552 """The name that this node refers to.""" 

553 

554 super().__init__( 

555 lineno=lineno, 

556 col_offset=col_offset, 

557 end_lineno=end_lineno, 

558 end_col_offset=end_col_offset, 

559 parent=parent, 

560 ) 

561 

562 def _get_name_nodes(self): 

563 yield self 

564 

565 for child_node in self.get_children(): 

566 yield from child_node._get_name_nodes() 

567 

568 @decorators.raise_if_nothing_inferred 

569 @decorators.path_wrapper 

570 def _infer( 

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

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

573 """Infer a Name: use name lookup rules 

574 

575 Same implementation as AssignName._infer_lhs.""" 

576 # pylint: disable=import-outside-toplevel 

577 from astroid.constraint import get_constraints 

578 from astroid.helpers import _higher_function_scope 

579 

580 frame, stmts = self.lookup(self.name) 

581 if not stmts: 

582 # Try to see if the name is enclosed in a nested function 

583 # and use the higher (first function) scope for searching. 

584 parent_function = _higher_function_scope(self.scope()) 

585 if parent_function: 

586 _, stmts = parent_function.lookup(self.name) 

587 

588 if not stmts: 

589 raise NameInferenceError( 

590 name=self.name, scope=self.scope(), context=context 

591 ) 

592 context = copy_context(context) 

593 context.lookupname = self.name 

594 context.constraints[self.name] = get_constraints(self, frame) 

595 

596 return _infer_stmts(stmts, context, frame) 

597 

598 

599DEPRECATED_ARGUMENT_DEFAULT = "DEPRECATED_ARGUMENT_DEFAULT" 

600 

601 

602class Arguments( 

603 _base_nodes.AssignTypeNode 

604): # pylint: disable=too-many-instance-attributes 

605 """Class representing an :class:`ast.arguments` node. 

606 

607 An :class:`Arguments` node represents that arguments in a 

608 function definition. 

609 

610 >>> import astroid 

611 >>> node = astroid.extract_node('def foo(bar): pass') 

612 >>> node 

613 <FunctionDef.foo l.1 at 0x7effe1db8198> 

614 >>> node.args 

615 <Arguments l.1 at 0x7effe1db82e8> 

616 """ 

617 

618 # Python 3.4+ uses a different approach regarding annotations, 

619 # each argument is a new class, _ast.arg, which exposes an 

620 # 'annotation' attribute. In astroid though, arguments are exposed 

621 # as is in the Arguments node and the only way to expose annotations 

622 # is by using something similar with Python 3.3: 

623 # - we expose 'varargannotation' and 'kwargannotation' of annotations 

624 # of varargs and kwargs. 

625 # - we expose 'annotation', a list with annotations for 

626 # for each normal argument. If an argument doesn't have an 

627 # annotation, its value will be None. 

628 _astroid_fields = ( 

629 "args", 

630 "defaults", 

631 "kwonlyargs", 

632 "posonlyargs", 

633 "posonlyargs_annotations", 

634 "kw_defaults", 

635 "annotations", 

636 "varargannotation", 

637 "kwargannotation", 

638 "kwonlyargs_annotations", 

639 "type_comment_args", 

640 "type_comment_kwonlyargs", 

641 "type_comment_posonlyargs", 

642 ) 

643 

644 _other_fields = ("vararg", "kwarg") 

645 

646 args: list[AssignName] | None 

647 """The names of the required arguments. 

648 

649 Can be None if the associated function does not have a retrievable 

650 signature and the arguments are therefore unknown. 

651 This can happen with (builtin) functions implemented in C that have 

652 incomplete signature information. 

653 """ 

654 

655 defaults: list[NodeNG] | None 

656 """The default values for arguments that can be passed positionally.""" 

657 

658 kwonlyargs: list[AssignName] 

659 """The keyword arguments that cannot be passed positionally.""" 

660 

661 posonlyargs: list[AssignName] 

662 """The arguments that can only be passed positionally.""" 

663 

664 kw_defaults: list[NodeNG | None] | None 

665 """The default values for keyword arguments that cannot be passed positionally.""" 

666 

667 annotations: list[NodeNG | None] 

668 """The type annotations of arguments that can be passed positionally.""" 

669 

670 posonlyargs_annotations: list[NodeNG | None] 

671 """The type annotations of arguments that can only be passed positionally.""" 

672 

673 kwonlyargs_annotations: list[NodeNG | None] 

674 """The type annotations of arguments that cannot be passed positionally.""" 

675 

676 type_comment_args: list[NodeNG | None] 

677 """The type annotation, passed by a type comment, of each argument. 

678 

679 If an argument does not have a type comment, 

680 the value for that argument will be None. 

681 """ 

682 

683 type_comment_kwonlyargs: list[NodeNG | None] 

684 """The type annotation, passed by a type comment, of each keyword only argument. 

685 

686 If an argument does not have a type comment, 

687 the value for that argument will be None. 

688 """ 

689 

690 type_comment_posonlyargs: list[NodeNG | None] 

691 """The type annotation, passed by a type comment, of each positional argument. 

692 

693 If an argument does not have a type comment, 

694 the value for that argument will be None. 

695 """ 

696 

697 varargannotation: NodeNG | None 

698 """The type annotation for the variable length arguments.""" 

699 

700 kwargannotation: NodeNG | None 

701 """The type annotation for the variable length keyword arguments.""" 

702 

703 vararg_node: AssignName | None 

704 """The node for variable length arguments""" 

705 

706 kwarg_node: AssignName | None 

707 """The node for variable keyword arguments""" 

708 

709 def __init__( 

710 self, 

711 vararg: str | None, 

712 kwarg: str | None, 

713 parent: NodeNG, 

714 vararg_node: AssignName | None = None, 

715 kwarg_node: AssignName | None = None, 

716 ) -> None: 

717 """Almost all attributes can be None for living objects where introspection failed.""" 

718 super().__init__( 

719 parent=parent, 

720 lineno=None, 

721 col_offset=None, 

722 end_lineno=None, 

723 end_col_offset=None, 

724 ) 

725 

726 self.vararg = vararg 

727 """The name of the variable length arguments.""" 

728 

729 self.kwarg = kwarg 

730 """The name of the variable length keyword arguments.""" 

731 

732 self.vararg_node = vararg_node 

733 self.kwarg_node = kwarg_node 

734 

735 # pylint: disable=too-many-arguments, too-many-positional-arguments 

736 def postinit( 

737 self, 

738 args: list[AssignName] | None, 

739 defaults: list[NodeNG] | None, 

740 kwonlyargs: list[AssignName], 

741 kw_defaults: list[NodeNG | None] | None, 

742 annotations: list[NodeNG | None], 

743 posonlyargs: list[AssignName], 

744 kwonlyargs_annotations: list[NodeNG | None], 

745 posonlyargs_annotations: list[NodeNG | None], 

746 varargannotation: NodeNG | None = None, 

747 kwargannotation: NodeNG | None = None, 

748 type_comment_args: list[NodeNG | None] | None = None, 

749 type_comment_kwonlyargs: list[NodeNG | None] | None = None, 

750 type_comment_posonlyargs: list[NodeNG | None] | None = None, 

751 ) -> None: 

752 self.args = args 

753 self.defaults = defaults 

754 self.kwonlyargs = kwonlyargs 

755 self.posonlyargs = posonlyargs 

756 self.kw_defaults = kw_defaults 

757 self.annotations = annotations 

758 self.kwonlyargs_annotations = kwonlyargs_annotations 

759 self.posonlyargs_annotations = posonlyargs_annotations 

760 

761 # Parameters that got added later and need a default 

762 self.varargannotation = varargannotation 

763 self.kwargannotation = kwargannotation 

764 if type_comment_args is None: 

765 type_comment_args = [] 

766 self.type_comment_args = type_comment_args 

767 if type_comment_kwonlyargs is None: 

768 type_comment_kwonlyargs = [] 

769 self.type_comment_kwonlyargs = type_comment_kwonlyargs 

770 if type_comment_posonlyargs is None: 

771 type_comment_posonlyargs = [] 

772 self.type_comment_posonlyargs = type_comment_posonlyargs 

773 

774 assigned_stmts = protocols.arguments_assigned_stmts 

775 """Returns the assigned statement (non inferred) according to the assignment type. 

776 See astroid/protocols.py for actual implementation. 

777 """ 

778 

779 def _infer_name(self, frame, name): 

780 if self.parent is frame: 

781 return name 

782 return None 

783 

784 @cached_property 

785 def fromlineno(self) -> int: 

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

787 

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

789 """ 

790 lineno = super().fromlineno 

791 return max(lineno, self.parent.fromlineno or 0) 

792 

793 @cached_property 

794 def arguments(self): 

795 """Get all the arguments for this node. This includes: 

796 * Positional only arguments 

797 * Positional arguments 

798 * Keyword arguments 

799 * Variable arguments (.e.g *args) 

800 * Variable keyword arguments (e.g **kwargs) 

801 """ 

802 retval = list(itertools.chain((self.posonlyargs or ()), (self.args or ()))) 

803 if self.vararg_node: 

804 retval.append(self.vararg_node) 

805 retval += self.kwonlyargs or () 

806 if self.kwarg_node: 

807 retval.append(self.kwarg_node) 

808 

809 return retval 

810 

811 def format_args(self, *, skippable_names: set[str] | None = None) -> str: 

812 """Get the arguments formatted as string. 

813 

814 :returns: The formatted arguments. 

815 :rtype: str 

816 """ 

817 result = [] 

818 positional_only_defaults = [] 

819 positional_or_keyword_defaults = self.defaults 

820 if self.defaults: 

821 args = self.args or [] 

822 positional_or_keyword_defaults = self.defaults[-len(args) :] 

823 positional_only_defaults = self.defaults[: len(self.defaults) - len(args)] 

824 

825 if self.posonlyargs: 

826 result.append( 

827 _format_args( 

828 self.posonlyargs, 

829 positional_only_defaults, 

830 self.posonlyargs_annotations, 

831 skippable_names=skippable_names, 

832 ) 

833 ) 

834 result.append("/") 

835 if self.args: 

836 result.append( 

837 _format_args( 

838 self.args, 

839 positional_or_keyword_defaults, 

840 getattr(self, "annotations", None), 

841 skippable_names=skippable_names, 

842 ) 

843 ) 

844 if self.vararg: 

845 result.append(f"*{self.vararg}") 

846 if self.kwonlyargs: 

847 if not self.vararg: 

848 result.append("*") 

849 result.append( 

850 _format_args( 

851 self.kwonlyargs, 

852 self.kw_defaults, 

853 self.kwonlyargs_annotations, 

854 skippable_names=skippable_names, 

855 ) 

856 ) 

857 if self.kwarg: 

858 result.append(f"**{self.kwarg}") 

859 return ", ".join(result) 

860 

861 def _get_arguments_data( 

862 self, 

863 ) -> tuple[ 

864 dict[str, tuple[str | None, str | None]], 

865 dict[str, tuple[str | None, str | None]], 

866 ]: 

867 """Get the arguments as dictionary with information about typing and defaults. 

868 

869 The return tuple contains a dictionary for positional and keyword arguments with their typing 

870 and their default value, if any. 

871 The method follows a similar order as format_args but instead of formatting into a string it 

872 returns the data that is used to do so. 

873 """ 

874 pos_only: dict[str, tuple[str | None, str | None]] = {} 

875 kw_only: dict[str, tuple[str | None, str | None]] = {} 

876 

877 # Setup and match defaults with arguments 

878 positional_only_defaults = [] 

879 positional_or_keyword_defaults = self.defaults 

880 if self.defaults: 

881 args = self.args or [] 

882 positional_or_keyword_defaults = self.defaults[-len(args) :] 

883 positional_only_defaults = self.defaults[: len(self.defaults) - len(args)] 

884 

885 for index, posonly in enumerate(self.posonlyargs): 

886 annotation, default = self.posonlyargs_annotations[index], None 

887 if annotation is not None: 

888 annotation = annotation.as_string() 

889 if positional_only_defaults: 

890 default = positional_only_defaults[index].as_string() 

891 pos_only[posonly.name] = (annotation, default) 

892 

893 for index, arg in enumerate(self.args): 

894 annotation, default = self.annotations[index], None 

895 if annotation is not None: 

896 annotation = annotation.as_string() 

897 if positional_or_keyword_defaults: 

898 defaults_offset = len(self.args) - len(positional_or_keyword_defaults) 

899 default_index = index - defaults_offset 

900 if ( 

901 default_index > -1 

902 and positional_or_keyword_defaults[default_index] is not None 

903 ): 

904 default = positional_or_keyword_defaults[default_index].as_string() 

905 pos_only[arg.name] = (annotation, default) 

906 

907 if self.vararg: 

908 annotation = self.varargannotation 

909 if annotation is not None: 

910 annotation = annotation.as_string() 

911 pos_only[self.vararg] = (annotation, None) 

912 

913 for index, kwarg in enumerate(self.kwonlyargs): 

914 annotation = self.kwonlyargs_annotations[index] 

915 if annotation is not None: 

916 annotation = annotation.as_string() 

917 default = self.kw_defaults[index] 

918 if default is not None: 

919 default = default.as_string() 

920 kw_only[kwarg.name] = (annotation, default) 

921 

922 if self.kwarg: 

923 annotation = self.kwargannotation 

924 if annotation is not None: 

925 annotation = annotation.as_string() 

926 kw_only[self.kwarg] = (annotation, None) 

927 

928 return pos_only, kw_only 

929 

930 def default_value(self, argname): 

931 """Get the default value for an argument. 

932 

933 :param argname: The name of the argument to get the default value for. 

934 :type argname: str 

935 

936 :raises NoDefault: If there is no default value defined for the 

937 given argument. 

938 """ 

939 args = [ 

940 arg for arg in self.arguments if arg.name not in [self.vararg, self.kwarg] 

941 ] 

942 

943 index = _find_arg(argname, self.kwonlyargs)[0] 

944 if (index is not None) and (len(self.kw_defaults) > index): 

945 if self.kw_defaults[index] is not None: 

946 return self.kw_defaults[index] 

947 raise NoDefault(func=self.parent, name=argname) 

948 

949 index = _find_arg(argname, args)[0] 

950 if index is not None: 

951 idx = index - (len(args) - len(self.defaults) - len(self.kw_defaults)) 

952 if idx >= 0: 

953 return self.defaults[idx] 

954 

955 raise NoDefault(func=self.parent, name=argname) 

956 

957 def is_argument(self, name) -> bool: 

958 """Check if the given name is defined in the arguments. 

959 

960 :param name: The name to check for. 

961 :type name: str 

962 

963 :returns: Whether the given name is defined in the arguments, 

964 """ 

965 if name == self.vararg: 

966 return True 

967 if name == self.kwarg: 

968 return True 

969 return self.find_argname(name)[1] is not None 

970 

971 def find_argname(self, argname, rec=DEPRECATED_ARGUMENT_DEFAULT): 

972 """Get the index and :class:`AssignName` node for given name. 

973 

974 :param argname: The name of the argument to search for. 

975 :type argname: str 

976 

977 :returns: The index and node for the argument. 

978 :rtype: tuple(str or None, AssignName or None) 

979 """ 

980 if rec != DEPRECATED_ARGUMENT_DEFAULT: # pragma: no cover 

981 warnings.warn( 

982 "The rec argument will be removed in astroid 3.1.", 

983 DeprecationWarning, 

984 stacklevel=2, 

985 ) 

986 if self.arguments: 

987 index, argument = _find_arg(argname, self.arguments) 

988 if argument: 

989 return index, argument 

990 return None, None 

991 

992 def get_children(self): 

993 yield from self.posonlyargs or () 

994 

995 for elt in self.posonlyargs_annotations: 

996 if elt is not None: 

997 yield elt 

998 

999 yield from self.args or () 

1000 

1001 if self.defaults is not None: 

1002 yield from self.defaults 

1003 yield from self.kwonlyargs 

1004 

1005 for elt in self.kw_defaults or (): 

1006 if elt is not None: 

1007 yield elt 

1008 

1009 for elt in self.annotations: 

1010 if elt is not None: 

1011 yield elt 

1012 

1013 if self.varargannotation is not None: 

1014 yield self.varargannotation 

1015 

1016 if self.kwargannotation is not None: 

1017 yield self.kwargannotation 

1018 

1019 for elt in self.kwonlyargs_annotations: 

1020 if elt is not None: 

1021 yield elt 

1022 

1023 def get_annotations(self) -> Iterator[nodes.NodeNG]: 

1024 """Iterate over all annotations nodes.""" 

1025 for elt in self.posonlyargs_annotations: 

1026 if elt is not None: 

1027 yield elt 

1028 for elt in self.annotations: 

1029 if elt is not None: 

1030 yield elt 

1031 if self.varargannotation is not None: 

1032 yield self.varargannotation 

1033 

1034 for elt in self.kwonlyargs_annotations: 

1035 if elt is not None: 

1036 yield elt 

1037 if self.kwargannotation is not None: 

1038 yield self.kwargannotation 

1039 

1040 @decorators.raise_if_nothing_inferred 

1041 def _infer( 

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

1043 ) -> Generator[InferenceResult]: 

1044 # pylint: disable-next=import-outside-toplevel 

1045 from astroid.protocols import _arguments_infer_argname 

1046 

1047 if context is None or context.lookupname is None: 

1048 raise InferenceError(node=self, context=context) 

1049 return _arguments_infer_argname(self, context.lookupname, context) 

1050 

1051 

1052def _find_arg(argname, args): 

1053 for i, arg in enumerate(args): 

1054 if arg.name == argname: 

1055 return i, arg 

1056 return None, None 

1057 

1058 

1059def _format_args( 

1060 args, defaults=None, annotations=None, skippable_names: set[str] | None = None 

1061) -> str: 

1062 if skippable_names is None: 

1063 skippable_names = set() 

1064 values = [] 

1065 if args is None: 

1066 return "" 

1067 if annotations is None: 

1068 annotations = [] 

1069 if defaults is not None: 

1070 default_offset = len(args) - len(defaults) 

1071 else: 

1072 default_offset = None 

1073 packed = itertools.zip_longest(args, annotations) 

1074 for i, (arg, annotation) in enumerate(packed): 

1075 if arg.name in skippable_names: 

1076 continue 

1077 if isinstance(arg, Tuple): 

1078 values.append(f"({_format_args(arg.elts)})") 

1079 else: 

1080 argname = arg.name 

1081 default_sep = "=" 

1082 if annotation is not None: 

1083 argname += ": " + annotation.as_string() 

1084 default_sep = " = " 

1085 values.append(argname) 

1086 

1087 if default_offset is not None and i >= default_offset: 

1088 if defaults[i - default_offset] is not None: 

1089 values[-1] += default_sep + defaults[i - default_offset].as_string() 

1090 return ", ".join(values) 

1091 

1092 

1093def _infer_attribute( 

1094 node: nodes.AssignAttr | nodes.Attribute, 

1095 context: InferenceContext | None = None, 

1096 **kwargs: Any, 

1097) -> Generator[InferenceResult, None, InferenceErrorInfo]: 

1098 """Infer an AssignAttr/Attribute node by using getattr on the associated object.""" 

1099 # pylint: disable=import-outside-toplevel 

1100 from astroid.constraint import get_constraints 

1101 from astroid.nodes import ClassDef 

1102 

1103 for owner in node.expr.infer(context): 

1104 if isinstance(owner, util.UninferableBase): 

1105 yield owner 

1106 continue 

1107 

1108 context = copy_context(context) 

1109 old_boundnode = context.boundnode 

1110 try: 

1111 context.boundnode = owner 

1112 if isinstance(owner, (ClassDef, Instance)): 

1113 frame = owner if isinstance(owner, ClassDef) else owner._proxied 

1114 context.constraints[node.attrname] = get_constraints(node, frame=frame) 

1115 if node.attrname == "argv" and owner.name == "sys": 

1116 # sys.argv will never be inferable during static analysis 

1117 # It's value would be the args passed to the linter itself 

1118 yield util.Uninferable 

1119 else: 

1120 yield from owner.igetattr(node.attrname, context) 

1121 except ( 

1122 AttributeInferenceError, 

1123 InferenceError, 

1124 AttributeError, 

1125 ): 

1126 pass 

1127 finally: 

1128 context.boundnode = old_boundnode 

1129 return InferenceErrorInfo(node=node, context=context) 

1130 

1131 

1132class AssignAttr(_base_nodes.LookupMixIn, _base_nodes.ParentAssignNode): 

1133 """Variation of :class:`ast.Assign` representing assignment to an attribute. 

1134 

1135 >>> import astroid 

1136 >>> node = astroid.extract_node('self.attribute = range(10)') 

1137 >>> node 

1138 <Assign l.1 at 0x7effe1d521d0> 

1139 >>> list(node.get_children()) 

1140 [<AssignAttr.attribute l.1 at 0x7effe1d52320>, <Call l.1 at 0x7effe1d522e8>] 

1141 >>> list(node.get_children())[0].as_string() 

1142 'self.attribute' 

1143 """ 

1144 

1145 expr: NodeNG 

1146 

1147 _astroid_fields = ("expr",) 

1148 _other_fields = ("attrname",) 

1149 

1150 def __init__( 

1151 self, 

1152 attrname: str, 

1153 lineno: int, 

1154 col_offset: int, 

1155 parent: NodeNG, 

1156 *, 

1157 end_lineno: int | None, 

1158 end_col_offset: int | None, 

1159 ) -> None: 

1160 self.attrname = attrname 

1161 """The name of the attribute being assigned to.""" 

1162 

1163 super().__init__( 

1164 lineno=lineno, 

1165 col_offset=col_offset, 

1166 end_lineno=end_lineno, 

1167 end_col_offset=end_col_offset, 

1168 parent=parent, 

1169 ) 

1170 

1171 def postinit(self, expr: NodeNG) -> None: 

1172 self.expr = expr 

1173 

1174 assigned_stmts = protocols.assend_assigned_stmts 

1175 """Returns the assigned statement (non inferred) according to the assignment type. 

1176 See astroid/protocols.py for actual implementation. 

1177 """ 

1178 

1179 def get_children(self): 

1180 yield self.expr 

1181 

1182 @decorators.raise_if_nothing_inferred 

1183 @decorators.path_wrapper 

1184 def _infer( 

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

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

1187 """Infer an AssignAttr: need to inspect the RHS part of the 

1188 assign node. 

1189 """ 

1190 if isinstance(self.parent, AugAssign): 

1191 return self.parent.infer(context) 

1192 

1193 stmts = list(self.assigned_stmts(context=context)) 

1194 return _infer_stmts(stmts, context) 

1195 

1196 @decorators.raise_if_nothing_inferred 

1197 @decorators.path_wrapper 

1198 def infer_lhs( 

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

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

1201 return _infer_attribute(self, context, **kwargs) 

1202 

1203 

1204class Assert(_base_nodes.Statement): 

1205 """Class representing an :class:`ast.Assert` node. 

1206 

1207 An :class:`Assert` node represents an assert statement. 

1208 

1209 >>> import astroid 

1210 >>> node = astroid.extract_node('assert len(things) == 10, "Not enough things"') 

1211 >>> node 

1212 <Assert l.1 at 0x7effe1d527b8> 

1213 """ 

1214 

1215 _astroid_fields = ("test", "fail") 

1216 

1217 test: NodeNG 

1218 """The test that passes or fails the assertion.""" 

1219 

1220 fail: NodeNG | None 

1221 """The message shown when the assertion fails.""" 

1222 

1223 def postinit(self, test: NodeNG, fail: NodeNG | None) -> None: 

1224 self.fail = fail 

1225 self.test = test 

1226 

1227 def get_children(self): 

1228 yield self.test 

1229 

1230 if self.fail is not None: 

1231 yield self.fail 

1232 

1233 

1234class Assign(_base_nodes.AssignTypeNode, _base_nodes.Statement): 

1235 """Class representing an :class:`ast.Assign` node. 

1236 

1237 An :class:`Assign` is a statement where something is explicitly 

1238 asssigned to. 

1239 

1240 >>> import astroid 

1241 >>> node = astroid.extract_node('variable = range(10)') 

1242 >>> node 

1243 <Assign l.1 at 0x7effe1db8550> 

1244 """ 

1245 

1246 targets: list[NodeNG] 

1247 """What is being assigned to.""" 

1248 

1249 value: NodeNG 

1250 """The value being assigned to the variables.""" 

1251 

1252 type_annotation: NodeNG | None 

1253 """If present, this will contain the type annotation passed by a type comment""" 

1254 

1255 _astroid_fields = ("targets", "value") 

1256 _other_other_fields = ("type_annotation",) 

1257 

1258 def postinit( 

1259 self, 

1260 targets: list[NodeNG], 

1261 value: NodeNG, 

1262 type_annotation: NodeNG | None, 

1263 ) -> None: 

1264 self.targets = targets 

1265 self.value = value 

1266 self.type_annotation = type_annotation 

1267 

1268 assigned_stmts = protocols.assign_assigned_stmts 

1269 """Returns the assigned statement (non inferred) according to the assignment type. 

1270 See astroid/protocols.py for actual implementation. 

1271 """ 

1272 

1273 def get_children(self): 

1274 yield from self.targets 

1275 

1276 yield self.value 

1277 

1278 @cached_property 

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

1280 return [self, *self.value._assign_nodes_in_scope] 

1281 

1282 def _get_yield_nodes_skip_functions(self): 

1283 yield from self.value._get_yield_nodes_skip_functions() 

1284 

1285 def _get_yield_nodes_skip_lambdas(self): 

1286 yield from self.value._get_yield_nodes_skip_lambdas() 

1287 

1288 

1289class AnnAssign(_base_nodes.AssignTypeNode, _base_nodes.Statement): 

1290 """Class representing an :class:`ast.AnnAssign` node. 

1291 

1292 An :class:`AnnAssign` is an assignment with a type annotation. 

1293 

1294 >>> import astroid 

1295 >>> node = astroid.extract_node('variable: List[int] = range(10)') 

1296 >>> node 

1297 <AnnAssign l.1 at 0x7effe1d4c630> 

1298 """ 

1299 

1300 _astroid_fields = ("target", "annotation", "value") 

1301 _other_fields = ("simple",) 

1302 

1303 target: Name | Attribute | Subscript 

1304 """What is being assigned to.""" 

1305 

1306 annotation: NodeNG 

1307 """The type annotation of what is being assigned to.""" 

1308 

1309 value: NodeNG | None 

1310 """The value being assigned to the variables.""" 

1311 

1312 simple: int 

1313 """Whether :attr:`target` is a pure name or a complex statement.""" 

1314 

1315 def postinit( 

1316 self, 

1317 target: Name | Attribute | Subscript, 

1318 annotation: NodeNG, 

1319 simple: int, 

1320 value: NodeNG | None, 

1321 ) -> None: 

1322 self.target = target 

1323 self.annotation = annotation 

1324 self.value = value 

1325 self.simple = simple 

1326 

1327 assigned_stmts = protocols.assign_annassigned_stmts 

1328 """Returns the assigned statement (non inferred) according to the assignment type. 

1329 See astroid/protocols.py for actual implementation. 

1330 """ 

1331 

1332 def get_children(self): 

1333 yield self.target 

1334 yield self.annotation 

1335 

1336 if self.value is not None: 

1337 yield self.value 

1338 

1339 

1340class AugAssign( 

1341 _base_nodes.AssignTypeNode, _base_nodes.OperatorNode, _base_nodes.Statement 

1342): 

1343 """Class representing an :class:`ast.AugAssign` node. 

1344 

1345 An :class:`AugAssign` is an assignment paired with an operator. 

1346 

1347 >>> import astroid 

1348 >>> node = astroid.extract_node('variable += 1') 

1349 >>> node 

1350 <AugAssign l.1 at 0x7effe1db4d68> 

1351 """ 

1352 

1353 _astroid_fields = ("target", "value") 

1354 _other_fields = ("op",) 

1355 

1356 target: Name | Attribute | Subscript 

1357 """What is being assigned to.""" 

1358 

1359 value: NodeNG 

1360 """The value being assigned to the variable.""" 

1361 

1362 def __init__( 

1363 self, 

1364 op: str, 

1365 lineno: int, 

1366 col_offset: int, 

1367 parent: NodeNG, 

1368 *, 

1369 end_lineno: int | None, 

1370 end_col_offset: int | None, 

1371 ) -> None: 

1372 self.op = op 

1373 """The operator that is being combined with the assignment. 

1374 

1375 This includes the equals sign. 

1376 """ 

1377 

1378 super().__init__( 

1379 lineno=lineno, 

1380 col_offset=col_offset, 

1381 end_lineno=end_lineno, 

1382 end_col_offset=end_col_offset, 

1383 parent=parent, 

1384 ) 

1385 

1386 def postinit(self, target: Name | Attribute | Subscript, value: NodeNG) -> None: 

1387 self.target = target 

1388 self.value = value 

1389 

1390 assigned_stmts = protocols.assign_assigned_stmts 

1391 """Returns the assigned statement (non inferred) according to the assignment type. 

1392 See astroid/protocols.py for actual implementation. 

1393 """ 

1394 

1395 def type_errors( 

1396 self, context: InferenceContext | None = None 

1397 ) -> list[util.BadBinaryOperationMessage]: 

1398 """Get a list of type errors which can occur during inference. 

1399 

1400 Each TypeError is represented by a :class:`BadBinaryOperationMessage` , 

1401 which holds the original exception. 

1402 

1403 If any inferred result is uninferable, an empty list is returned. 

1404 """ 

1405 bad = [] 

1406 try: 

1407 for result in self._infer_augassign(context=context): 

1408 if result is util.Uninferable: 

1409 raise InferenceError 

1410 if isinstance(result, util.BadBinaryOperationMessage): 

1411 bad.append(result) 

1412 except InferenceError: 

1413 return [] 

1414 return bad 

1415 

1416 def get_children(self): 

1417 yield self.target 

1418 yield self.value 

1419 

1420 def _get_yield_nodes_skip_functions(self): 

1421 """An AugAssign node can contain a Yield node in the value""" 

1422 yield from self.value._get_yield_nodes_skip_functions() 

1423 yield from super()._get_yield_nodes_skip_functions() 

1424 

1425 def _get_yield_nodes_skip_lambdas(self): 

1426 """An AugAssign node can contain a Yield node in the value""" 

1427 yield from self.value._get_yield_nodes_skip_lambdas() 

1428 yield from super()._get_yield_nodes_skip_lambdas() 

1429 

1430 def _infer_augassign( 

1431 self, context: InferenceContext | None = None 

1432 ) -> Generator[InferenceResult | util.BadBinaryOperationMessage]: 

1433 """Inference logic for augmented binary operations.""" 

1434 context = context or InferenceContext() 

1435 

1436 rhs_context = context.clone() 

1437 

1438 lhs_iter = self.target.infer_lhs(context=context) 

1439 rhs_iter = self.value.infer(context=rhs_context) 

1440 

1441 for lhs, rhs in itertools.product(lhs_iter, rhs_iter): 

1442 if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)): 

1443 # Don't know how to process this. 

1444 yield util.Uninferable 

1445 return 

1446 

1447 try: 

1448 yield from self._infer_binary_operation( 

1449 left=lhs, 

1450 right=rhs, 

1451 binary_opnode=self, 

1452 context=context, 

1453 flow_factory=self._get_aug_flow, 

1454 ) 

1455 except _NonDeducibleTypeHierarchy: 

1456 yield util.Uninferable 

1457 

1458 @decorators.raise_if_nothing_inferred 

1459 @decorators.path_wrapper 

1460 def _infer( 

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

1462 ) -> Generator[InferenceResult]: 

1463 return self._filter_operation_errors( 

1464 self._infer_augassign, context, util.BadBinaryOperationMessage 

1465 ) 

1466 

1467 

1468class BinOp(_base_nodes.OperatorNode): 

1469 """Class representing an :class:`ast.BinOp` node. 

1470 

1471 A :class:`BinOp` node is an application of a binary operator. 

1472 

1473 >>> import astroid 

1474 >>> node = astroid.extract_node('a + b') 

1475 >>> node 

1476 <BinOp l.1 at 0x7f23b2e8cfd0> 

1477 """ 

1478 

1479 _astroid_fields = ("left", "right") 

1480 _other_fields = ("op",) 

1481 

1482 left: NodeNG 

1483 """What is being applied to the operator on the left side.""" 

1484 

1485 right: NodeNG 

1486 """What is being applied to the operator on the right side.""" 

1487 

1488 def __init__( 

1489 self, 

1490 op: str, 

1491 lineno: int, 

1492 col_offset: int, 

1493 parent: NodeNG, 

1494 *, 

1495 end_lineno: int | None, 

1496 end_col_offset: int | None, 

1497 ) -> None: 

1498 self.op = op 

1499 """The operator.""" 

1500 

1501 super().__init__( 

1502 lineno=lineno, 

1503 col_offset=col_offset, 

1504 end_lineno=end_lineno, 

1505 end_col_offset=end_col_offset, 

1506 parent=parent, 

1507 ) 

1508 

1509 def postinit(self, left: NodeNG, right: NodeNG) -> None: 

1510 self.left = left 

1511 self.right = right 

1512 

1513 def type_errors( 

1514 self, context: InferenceContext | None = None 

1515 ) -> list[util.BadBinaryOperationMessage]: 

1516 """Get a list of type errors which can occur during inference. 

1517 

1518 Each TypeError is represented by a :class:`BadBinaryOperationMessage`, 

1519 which holds the original exception. 

1520 

1521 If any inferred result is uninferable, an empty list is returned. 

1522 """ 

1523 bad = [] 

1524 try: 

1525 for result in self._infer_binop(context=context): 

1526 if result is util.Uninferable: 

1527 raise InferenceError 

1528 if isinstance(result, util.BadBinaryOperationMessage): 

1529 bad.append(result) 

1530 except InferenceError: 

1531 return [] 

1532 return bad 

1533 

1534 def get_children(self): 

1535 yield self.left 

1536 yield self.right 

1537 

1538 def op_precedence(self) -> int: 

1539 return OP_PRECEDENCE[self.op] 

1540 

1541 def op_left_associative(self) -> bool: 

1542 # 2**3**4 == 2**(3**4) 

1543 return self.op != "**" 

1544 

1545 def _infer_binop( 

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

1547 ) -> Generator[InferenceResult]: 

1548 """Binary operation inference logic.""" 

1549 left = self.left 

1550 right = self.right 

1551 

1552 # we use two separate contexts for evaluating lhs and rhs because 

1553 # 1. evaluating lhs may leave some undesired entries in context.path 

1554 # which may not let us infer right value of rhs 

1555 context = context or InferenceContext() 

1556 lhs_context = copy_context(context) 

1557 rhs_context = copy_context(context) 

1558 lhs_iter = left.infer(context=lhs_context) 

1559 rhs_iter = right.infer(context=rhs_context) 

1560 for lhs, rhs in itertools.product(lhs_iter, rhs_iter): 

1561 if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)): 

1562 # Don't know how to process this. 

1563 yield util.Uninferable 

1564 return 

1565 

1566 try: 

1567 yield from self._infer_binary_operation( 

1568 lhs, rhs, self, context, self._get_binop_flow 

1569 ) 

1570 except _NonDeducibleTypeHierarchy: 

1571 yield util.Uninferable 

1572 

1573 @decorators.yes_if_nothing_inferred 

1574 @decorators.path_wrapper 

1575 def _infer( 

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

1577 ) -> Generator[InferenceResult]: 

1578 return self._filter_operation_errors( 

1579 self._infer_binop, context, util.BadBinaryOperationMessage 

1580 ) 

1581 

1582 

1583class BoolOp(NodeNG): 

1584 """Class representing an :class:`ast.BoolOp` node. 

1585 

1586 A :class:`BoolOp` is an application of a boolean operator. 

1587 

1588 >>> import astroid 

1589 >>> node = astroid.extract_node('a and b') 

1590 >>> node 

1591 <BinOp l.1 at 0x7f23b2e71c50> 

1592 """ 

1593 

1594 _astroid_fields = ("values",) 

1595 _other_fields = ("op",) 

1596 

1597 def __init__( 

1598 self, 

1599 op: str, 

1600 lineno: int | None = None, 

1601 col_offset: int | None = None, 

1602 parent: NodeNG | None = None, 

1603 *, 

1604 end_lineno: int | None = None, 

1605 end_col_offset: int | None = None, 

1606 ) -> None: 

1607 """ 

1608 :param op: The operator. 

1609 

1610 :param lineno: The line that this node appears on in the source code. 

1611 

1612 :param col_offset: The column that this node appears on in the 

1613 source code. 

1614 

1615 :param parent: The parent node in the syntax tree. 

1616 

1617 :param end_lineno: The last line this node appears on in the source code. 

1618 

1619 :param end_col_offset: The end column this node appears on in the 

1620 source code. Note: This is after the last symbol. 

1621 """ 

1622 self.op: str = op 

1623 """The operator.""" 

1624 

1625 self.values: list[NodeNG] = [] 

1626 """The values being applied to the operator.""" 

1627 

1628 super().__init__( 

1629 lineno=lineno, 

1630 col_offset=col_offset, 

1631 end_lineno=end_lineno, 

1632 end_col_offset=end_col_offset, 

1633 parent=parent, 

1634 ) 

1635 

1636 def postinit(self, values: list[NodeNG] | None = None) -> None: 

1637 """Do some setup after initialisation. 

1638 

1639 :param values: The values being applied to the operator. 

1640 """ 

1641 if values is not None: 

1642 self.values = values 

1643 

1644 def get_children(self): 

1645 yield from self.values 

1646 

1647 def op_precedence(self) -> int: 

1648 return OP_PRECEDENCE[self.op] 

1649 

1650 @decorators.raise_if_nothing_inferred 

1651 @decorators.path_wrapper 

1652 def _infer( 

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

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

1655 """Infer a boolean operation (and / or / not). 

1656 

1657 The function will calculate the boolean operation 

1658 for all pairs generated through inference for each component 

1659 node. 

1660 """ 

1661 values = self.values 

1662 if self.op == "or": 

1663 predicate = operator.truth 

1664 else: 

1665 predicate = operator.not_ 

1666 

1667 try: 

1668 inferred_values = [value.infer(context=context) for value in values] 

1669 except InferenceError: 

1670 yield util.Uninferable 

1671 return None 

1672 

1673 for pair in itertools.product(*inferred_values): 

1674 if any(isinstance(item, util.UninferableBase) for item in pair): 

1675 # Can't infer the final result, just yield Uninferable. 

1676 yield util.Uninferable 

1677 continue 

1678 

1679 bool_values = [item.bool_value() for item in pair] 

1680 if any(isinstance(item, util.UninferableBase) for item in bool_values): 

1681 # Can't infer the final result, just yield Uninferable. 

1682 yield util.Uninferable 

1683 continue 

1684 

1685 # Since the boolean operations are short circuited operations, 

1686 # this code yields the first value for which the predicate is True 

1687 # and if no value respected the predicate, then the last value will 

1688 # be returned (or Uninferable if there was no last value). 

1689 # This is conforming to the semantics of `and` and `or`: 

1690 # 1 and 0 -> 1 

1691 # 0 and 1 -> 0 

1692 # 1 or 0 -> 1 

1693 # 0 or 1 -> 1 

1694 value = util.Uninferable 

1695 for value, bool_value in zip(pair, bool_values): 

1696 if predicate(bool_value): 

1697 yield value 

1698 break 

1699 else: 

1700 yield value 

1701 

1702 return InferenceErrorInfo(node=self, context=context) 

1703 

1704 

1705class Break(_base_nodes.NoChildrenNode, _base_nodes.Statement): 

1706 """Class representing an :class:`ast.Break` node. 

1707 

1708 >>> import astroid 

1709 >>> node = astroid.extract_node('break') 

1710 >>> node 

1711 <Break l.1 at 0x7f23b2e9e5c0> 

1712 """ 

1713 

1714 

1715class Call(NodeNG): 

1716 """Class representing an :class:`ast.Call` node. 

1717 

1718 A :class:`Call` node is a call to a function, method, etc. 

1719 

1720 >>> import astroid 

1721 >>> node = astroid.extract_node('function()') 

1722 >>> node 

1723 <Call l.1 at 0x7f23b2e71eb8> 

1724 """ 

1725 

1726 _astroid_fields = ("func", "args", "keywords") 

1727 

1728 func: NodeNG 

1729 """What is being called.""" 

1730 

1731 args: list[NodeNG] 

1732 """The positional arguments being given to the call.""" 

1733 

1734 keywords: list[Keyword] 

1735 """The keyword arguments being given to the call.""" 

1736 

1737 def postinit( 

1738 self, func: NodeNG, args: list[NodeNG], keywords: list[Keyword] 

1739 ) -> None: 

1740 self.func = func 

1741 self.args = args 

1742 self.keywords = keywords 

1743 

1744 @property 

1745 def starargs(self) -> list[Starred]: 

1746 """The positional arguments that unpack something.""" 

1747 return [arg for arg in self.args if isinstance(arg, Starred)] 

1748 

1749 @property 

1750 def kwargs(self) -> list[Keyword]: 

1751 """The keyword arguments that unpack something.""" 

1752 return [keyword for keyword in self.keywords if keyword.arg is None] 

1753 

1754 def get_children(self): 

1755 yield self.func 

1756 

1757 yield from self.args 

1758 

1759 yield from self.keywords 

1760 

1761 @decorators.raise_if_nothing_inferred 

1762 @decorators.path_wrapper 

1763 def _infer( 

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

1765 ) -> Generator[InferenceResult, None, InferenceErrorInfo]: 

1766 """Infer a Call node by trying to guess what the function returns.""" 

1767 callcontext = copy_context(context) 

1768 callcontext.boundnode = None 

1769 if context is not None: 

1770 callcontext.extra_context = self._populate_context_lookup(context.clone()) 

1771 

1772 for callee in self.func.infer(context): 

1773 if isinstance(callee, util.UninferableBase): 

1774 yield callee 

1775 continue 

1776 try: 

1777 if hasattr(callee, "infer_call_result"): 

1778 callcontext.callcontext = CallContext( 

1779 args=self.args, keywords=self.keywords, callee=callee 

1780 ) 

1781 yield from callee.infer_call_result( 

1782 caller=self, context=callcontext 

1783 ) 

1784 except InferenceError: 

1785 continue 

1786 return InferenceErrorInfo(node=self, context=context) 

1787 

1788 def _populate_context_lookup(self, context: InferenceContext | None): 

1789 """Allows context to be saved for later for inference inside a function.""" 

1790 context_lookup: dict[InferenceResult, InferenceContext] = {} 

1791 if context is None: 

1792 return context_lookup 

1793 for arg in self.args: 

1794 if isinstance(arg, Starred): 

1795 context_lookup[arg.value] = context 

1796 else: 

1797 context_lookup[arg] = context 

1798 keywords = self.keywords if self.keywords is not None else [] 

1799 for keyword in keywords: 

1800 context_lookup[keyword.value] = context 

1801 return context_lookup 

1802 

1803 

1804COMPARE_OPS: dict[str, Callable[[Any, Any], bool]] = { 

1805 "==": operator.eq, 

1806 "!=": operator.ne, 

1807 "<": operator.lt, 

1808 "<=": operator.le, 

1809 ">": operator.gt, 

1810 ">=": operator.ge, 

1811 "in": lambda a, b: a in b, 

1812 "not in": lambda a, b: a not in b, 

1813} 

1814UNINFERABLE_OPS = { 

1815 "is", 

1816 "is not", 

1817} 

1818 

1819 

1820class Compare(NodeNG): 

1821 """Class representing an :class:`ast.Compare` node. 

1822 

1823 A :class:`Compare` node indicates a comparison. 

1824 

1825 >>> import astroid 

1826 >>> node = astroid.extract_node('a <= b <= c') 

1827 >>> node 

1828 <Compare l.1 at 0x7f23b2e9e6d8> 

1829 >>> node.ops 

1830 [('<=', <Name.b l.1 at 0x7f23b2e9e2b0>), ('<=', <Name.c l.1 at 0x7f23b2e9e390>)] 

1831 """ 

1832 

1833 _astroid_fields = ("left", "ops") 

1834 

1835 left: NodeNG 

1836 """The value at the left being applied to a comparison operator.""" 

1837 

1838 ops: list[tuple[str, NodeNG]] 

1839 """The remainder of the operators and their relevant right hand value.""" 

1840 

1841 def postinit(self, left: NodeNG, ops: list[tuple[str, NodeNG]]) -> None: 

1842 self.left = left 

1843 self.ops = ops 

1844 

1845 def get_children(self): 

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

1847 

1848 Overridden to handle the tuple fields and skip returning the operator 

1849 strings. 

1850 

1851 :returns: The children. 

1852 :rtype: iterable(NodeNG) 

1853 """ 

1854 yield self.left 

1855 for _, comparator in self.ops: 

1856 yield comparator # we don't want the 'op' 

1857 

1858 def last_child(self): 

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

1860 

1861 :returns: The last child. 

1862 :rtype: NodeNG 

1863 """ 

1864 # XXX maybe if self.ops: 

1865 return self.ops[-1][1] 

1866 # return self.left 

1867 

1868 # TODO: move to util? 

1869 @staticmethod 

1870 def _to_literal(node: SuccessfulInferenceResult) -> Any: 

1871 # Can raise SyntaxError, ValueError, or TypeError from ast.literal_eval 

1872 # Can raise AttributeError from node.as_string() as not all nodes have a visitor 

1873 # Is this the stupidest idea or the simplest idea? 

1874 return ast.literal_eval(node.as_string()) 

1875 

1876 def _do_compare( 

1877 self, 

1878 left_iter: Iterable[InferenceResult], 

1879 op: str, 

1880 right_iter: Iterable[InferenceResult], 

1881 ) -> bool | util.UninferableBase: 

1882 """ 

1883 If all possible combinations are either True or False, return that: 

1884 >>> _do_compare([1, 2], '<=', [3, 4]) 

1885 True 

1886 >>> _do_compare([1, 2], '==', [3, 4]) 

1887 False 

1888 

1889 If any item is uninferable, or if some combinations are True and some 

1890 are False, return Uninferable: 

1891 >>> _do_compare([1, 3], '<=', [2, 4]) 

1892 util.Uninferable 

1893 """ 

1894 retval: bool | None = None 

1895 if op in UNINFERABLE_OPS: 

1896 return util.Uninferable 

1897 op_func = COMPARE_OPS[op] 

1898 

1899 for left, right in itertools.product(left_iter, right_iter): 

1900 if isinstance(left, util.UninferableBase) or isinstance( 

1901 right, util.UninferableBase 

1902 ): 

1903 return util.Uninferable 

1904 

1905 try: 

1906 left, right = self._to_literal(left), self._to_literal(right) 

1907 except (SyntaxError, ValueError, AttributeError, TypeError): 

1908 return util.Uninferable 

1909 

1910 try: 

1911 expr = op_func(left, right) 

1912 except TypeError as exc: 

1913 raise AstroidTypeError from exc 

1914 

1915 if retval is None: 

1916 retval = expr 

1917 elif retval != expr: 

1918 return util.Uninferable 

1919 # (or both, but "True | False" is basically the same) 

1920 

1921 assert retval is not None 

1922 return retval # it was all the same value 

1923 

1924 def _infer( 

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

1926 ) -> Generator[nodes.Const | util.UninferableBase]: 

1927 """Chained comparison inference logic.""" 

1928 retval: bool | util.UninferableBase = True 

1929 

1930 ops = self.ops 

1931 left_node = self.left 

1932 lhs = list(left_node.infer(context=context)) 

1933 # should we break early if first element is uninferable? 

1934 for op, right_node in ops: 

1935 # eagerly evaluate rhs so that values can be re-used as lhs 

1936 rhs = list(right_node.infer(context=context)) 

1937 try: 

1938 retval = self._do_compare(lhs, op, rhs) 

1939 except AstroidTypeError: 

1940 retval = util.Uninferable 

1941 break 

1942 if retval is not True: 

1943 break # short-circuit 

1944 lhs = rhs # continue 

1945 if retval is util.Uninferable: 

1946 yield retval # type: ignore[misc] 

1947 else: 

1948 yield Const(retval) 

1949 

1950 

1951class Comprehension(NodeNG): 

1952 """Class representing an :class:`ast.comprehension` node. 

1953 

1954 A :class:`Comprehension` indicates the loop inside any type of 

1955 comprehension including generator expressions. 

1956 

1957 >>> import astroid 

1958 >>> node = astroid.extract_node('[x for x in some_values]') 

1959 >>> list(node.get_children()) 

1960 [<Name.x l.1 at 0x7f23b2e352b0>, <Comprehension l.1 at 0x7f23b2e35320>] 

1961 >>> list(node.get_children())[1].as_string() 

1962 'for x in some_values' 

1963 """ 

1964 

1965 _astroid_fields = ("target", "iter", "ifs") 

1966 _other_fields = ("is_async",) 

1967 

1968 optional_assign = True 

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

1970 

1971 target: NodeNG 

1972 """What is assigned to by the comprehension.""" 

1973 

1974 iter: NodeNG 

1975 """What is iterated over by the comprehension.""" 

1976 

1977 ifs: list[NodeNG] 

1978 """The contents of any if statements that filter the comprehension.""" 

1979 

1980 is_async: bool 

1981 """Whether this is an asynchronous comprehension or not.""" 

1982 

1983 def postinit( 

1984 self, 

1985 target: NodeNG, 

1986 iter: NodeNG, # pylint: disable = redefined-builtin 

1987 ifs: list[NodeNG], 

1988 is_async: bool, 

1989 ) -> None: 

1990 self.target = target 

1991 self.iter = iter 

1992 self.ifs = ifs 

1993 self.is_async = is_async 

1994 

1995 assigned_stmts = protocols.for_assigned_stmts 

1996 """Returns the assigned statement (non inferred) according to the assignment type. 

1997 See astroid/protocols.py for actual implementation. 

1998 """ 

1999 

2000 def assign_type(self): 

2001 """The type of assignment that this node performs. 

2002 

2003 :returns: The assignment type. 

2004 :rtype: NodeNG 

2005 """ 

2006 return self 

2007 

2008 def _get_filtered_stmts( 

2009 self, lookup_node, node, stmts, mystmt: _base_nodes.Statement | None 

2010 ): 

2011 """method used in filter_stmts""" 

2012 if self is mystmt: 

2013 if isinstance(lookup_node, (Const, Name)): 

2014 return [lookup_node], True 

2015 

2016 elif self.statement() is mystmt: 

2017 # original node's statement is the assignment, only keeps 

2018 # current node (gen exp, list comp) 

2019 

2020 return [node], True 

2021 

2022 return stmts, False 

2023 

2024 def get_children(self): 

2025 yield self.target 

2026 yield self.iter 

2027 

2028 yield from self.ifs 

2029 

2030 

2031class Const(_base_nodes.NoChildrenNode, Instance): 

2032 """Class representing any constant including num, str, bool, None, bytes. 

2033 

2034 >>> import astroid 

2035 >>> node = astroid.extract_node('(5, "This is a string.", True, None, b"bytes")') 

2036 >>> node 

2037 <Tuple.tuple l.1 at 0x7f23b2e358d0> 

2038 >>> list(node.get_children()) 

2039 [<Const.int l.1 at 0x7f23b2e35940>, 

2040 <Const.str l.1 at 0x7f23b2e35978>, 

2041 <Const.bool l.1 at 0x7f23b2e359b0>, 

2042 <Const.NoneType l.1 at 0x7f23b2e359e8>, 

2043 <Const.bytes l.1 at 0x7f23b2e35a20>] 

2044 """ 

2045 

2046 _other_fields = ("value", "kind") 

2047 

2048 def __init__( 

2049 self, 

2050 value: Any, 

2051 lineno: int | None = None, 

2052 col_offset: int | None = None, 

2053 parent: NodeNG = SYNTHETIC_ROOT, 

2054 kind: str | None = None, 

2055 *, 

2056 end_lineno: int | None = None, 

2057 end_col_offset: int | None = None, 

2058 ) -> None: 

2059 """ 

2060 :param value: The value that the constant represents. 

2061 

2062 :param lineno: The line that this node appears on in the source code. 

2063 

2064 :param col_offset: The column that this node appears on in the 

2065 source code. 

2066 

2067 :param parent: The parent node in the syntax tree. 

2068 

2069 :param kind: The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only. 

2070 

2071 :param end_lineno: The last line this node appears on in the source code. 

2072 

2073 :param end_col_offset: The end column this node appears on in the 

2074 source code. Note: This is after the last symbol. 

2075 """ 

2076 if getattr(value, "__name__", None) == "__doc__": 

2077 warnings.warn( # pragma: no cover 

2078 "You have most likely called a __doc__ field of some object " 

2079 "and it didn't return a string. " 

2080 "That happens to some symbols from the standard library. " 

2081 "Check for isinstance(<X>.__doc__, str).", 

2082 RuntimeWarning, 

2083 stacklevel=0, 

2084 ) 

2085 self.value = value 

2086 """The value that the constant represents.""" 

2087 

2088 self.kind: str | None = kind # can be None 

2089 """"The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only.""" 

2090 

2091 super().__init__( 

2092 lineno=lineno, 

2093 col_offset=col_offset, 

2094 end_lineno=end_lineno, 

2095 end_col_offset=end_col_offset, 

2096 parent=parent, 

2097 ) 

2098 

2099 Instance.__init__(self, None) 

2100 

2101 infer_unary_op = protocols.const_infer_unary_op 

2102 infer_binary_op = protocols.const_infer_binary_op 

2103 

2104 def __getattr__(self, name): 

2105 # This is needed because of Proxy's __getattr__ method. 

2106 # Calling object.__new__ on this class without calling 

2107 # __init__ would result in an infinite loop otherwise 

2108 # since __getattr__ is called when an attribute doesn't 

2109 # exist and self._proxied indirectly calls self.value 

2110 # and Proxy __getattr__ calls self.value 

2111 if name == "value": 

2112 raise AttributeError 

2113 return super().__getattr__(name) 

2114 

2115 def getitem(self, index, context: InferenceContext | None = None): 

2116 """Get an item from this node if subscriptable. 

2117 

2118 :param index: The node to use as a subscript index. 

2119 :type index: Const or Slice 

2120 

2121 :raises AstroidTypeError: When the given index cannot be used as a 

2122 subscript index, or if this node is not subscriptable. 

2123 """ 

2124 if isinstance(index, Const): 

2125 index_value = index.value 

2126 elif isinstance(index, Slice): 

2127 index_value = _infer_slice(index, context=context) 

2128 

2129 else: 

2130 raise AstroidTypeError( 

2131 f"Could not use type {type(index)} as subscript index" 

2132 ) 

2133 

2134 try: 

2135 if isinstance(self.value, (str, bytes)): 

2136 return Const(self.value[index_value]) 

2137 except ValueError as exc: 

2138 raise AstroidValueError( 

2139 f"Could not index {self.value!r} with {index_value!r}" 

2140 ) from exc 

2141 except IndexError as exc: 

2142 raise AstroidIndexError( 

2143 message="Index {index!r} out of range", 

2144 node=self, 

2145 index=index, 

2146 context=context, 

2147 ) from exc 

2148 except TypeError as exc: 

2149 raise AstroidTypeError( 

2150 message="Type error {error!r}", node=self, index=index, context=context 

2151 ) from exc 

2152 

2153 raise AstroidTypeError(f"{self!r} (value={self.value})") 

2154 

2155 def has_dynamic_getattr(self) -> bool: 

2156 """Check if the node has a custom __getattr__ or __getattribute__. 

2157 

2158 :returns: Whether the class has a custom __getattr__ or __getattribute__. 

2159 For a :class:`Const` this is always ``False``. 

2160 """ 

2161 return False 

2162 

2163 def itered(self): 

2164 """An iterator over the elements this node contains. 

2165 

2166 :returns: The contents of this node. 

2167 :rtype: iterable(Const) 

2168 

2169 :raises TypeError: If this node does not represent something that is iterable. 

2170 """ 

2171 if isinstance(self.value, str): 

2172 return [const_factory(elem) for elem in self.value] 

2173 raise TypeError(f"Cannot iterate over type {type(self.value)!r}") 

2174 

2175 def pytype(self) -> str: 

2176 """Get the name of the type that this node represents. 

2177 

2178 :returns: The name of the type. 

2179 """ 

2180 return self._proxied.qname() 

2181 

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

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

2184 

2185 :returns: The boolean value of this node. 

2186 :rtype: bool or Uninferable 

2187 """ 

2188 # bool(NotImplemented) is deprecated; it raises TypeError starting from Python 3.14 

2189 # and returns True for versions under 3.14 

2190 if self.value is NotImplemented: 

2191 return util.Uninferable if PY314_PLUS else True 

2192 return bool(self.value) 

2193 

2194 def _infer( 

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

2196 ) -> Iterator[Const]: 

2197 yield self 

2198 

2199 

2200class Continue(_base_nodes.NoChildrenNode, _base_nodes.Statement): 

2201 """Class representing an :class:`ast.Continue` node. 

2202 

2203 >>> import astroid 

2204 >>> node = astroid.extract_node('continue') 

2205 >>> node 

2206 <Continue l.1 at 0x7f23b2e35588> 

2207 """ 

2208 

2209 

2210class Decorators(NodeNG): 

2211 """A node representing a list of decorators. 

2212 

2213 A :class:`Decorators` is the decorators that are applied to 

2214 a method or function. 

2215 

2216 >>> import astroid 

2217 >>> node = astroid.extract_node(''' 

2218 @property 

2219 def my_property(self): 

2220 return 3 

2221 ''') 

2222 >>> node 

2223 <FunctionDef.my_property l.2 at 0x7f23b2e35d30> 

2224 >>> list(node.get_children())[0] 

2225 <Decorators l.1 at 0x7f23b2e35d68> 

2226 """ 

2227 

2228 _astroid_fields = ("nodes",) 

2229 

2230 nodes: list[NodeNG] 

2231 """The decorators that this node contains.""" 

2232 

2233 def postinit(self, nodes: list[NodeNG]) -> None: 

2234 self.nodes = nodes 

2235 

2236 def scope(self) -> LocalsDictNodeNG: 

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

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

2239 

2240 :returns: The first parent scope node. 

2241 """ 

2242 # skip the function node to go directly to the upper level scope 

2243 if not self.parent: 

2244 raise ParentMissingError(target=self) 

2245 if not self.parent.parent: 

2246 raise ParentMissingError(target=self.parent) 

2247 return self.parent.parent.scope() 

2248 

2249 def get_children(self): 

2250 yield from self.nodes 

2251 

2252 

2253class DelAttr(_base_nodes.ParentAssignNode): 

2254 """Variation of :class:`ast.Delete` representing deletion of an attribute. 

2255 

2256 >>> import astroid 

2257 >>> node = astroid.extract_node('del self.attr') 

2258 >>> node 

2259 <Delete l.1 at 0x7f23b2e35f60> 

2260 >>> list(node.get_children())[0] 

2261 <DelAttr.attr l.1 at 0x7f23b2e411d0> 

2262 """ 

2263 

2264 _astroid_fields = ("expr",) 

2265 _other_fields = ("attrname",) 

2266 

2267 expr: NodeNG 

2268 """The name that this node represents.""" 

2269 

2270 def __init__( 

2271 self, 

2272 attrname: str, 

2273 lineno: int, 

2274 col_offset: int, 

2275 parent: NodeNG, 

2276 *, 

2277 end_lineno: int | None, 

2278 end_col_offset: int | None, 

2279 ) -> None: 

2280 self.attrname = attrname 

2281 """The name of the attribute that is being deleted.""" 

2282 

2283 super().__init__( 

2284 lineno=lineno, 

2285 col_offset=col_offset, 

2286 end_lineno=end_lineno, 

2287 end_col_offset=end_col_offset, 

2288 parent=parent, 

2289 ) 

2290 

2291 def postinit(self, expr: NodeNG) -> None: 

2292 self.expr = expr 

2293 

2294 def get_children(self): 

2295 yield self.expr 

2296 

2297 

2298class Delete(_base_nodes.AssignTypeNode, _base_nodes.Statement): 

2299 """Class representing an :class:`ast.Delete` node. 

2300 

2301 A :class:`Delete` is a ``del`` statement this is deleting something. 

2302 

2303 >>> import astroid 

2304 >>> node = astroid.extract_node('del self.attr') 

2305 >>> node 

2306 <Delete l.1 at 0x7f23b2e35f60> 

2307 """ 

2308 

2309 _astroid_fields = ("targets",) 

2310 

2311 def __init__( 

2312 self, 

2313 lineno: int, 

2314 col_offset: int, 

2315 parent: NodeNG, 

2316 *, 

2317 end_lineno: int | None, 

2318 end_col_offset: int | None, 

2319 ) -> None: 

2320 self.targets: list[NodeNG] = [] 

2321 """What is being deleted.""" 

2322 

2323 super().__init__( 

2324 lineno=lineno, 

2325 col_offset=col_offset, 

2326 end_lineno=end_lineno, 

2327 end_col_offset=end_col_offset, 

2328 parent=parent, 

2329 ) 

2330 

2331 def postinit(self, targets: list[NodeNG]) -> None: 

2332 self.targets = targets 

2333 

2334 def get_children(self): 

2335 yield from self.targets 

2336 

2337 

2338class Dict(NodeNG, Instance): 

2339 """Class representing an :class:`ast.Dict` node. 

2340 

2341 A :class:`Dict` is a dictionary that is created with ``{}`` syntax. 

2342 

2343 >>> import astroid 

2344 >>> node = astroid.extract_node('{1: "1"}') 

2345 >>> node 

2346 <Dict.dict l.1 at 0x7f23b2e35cc0> 

2347 """ 

2348 

2349 _astroid_fields = ("items",) 

2350 

2351 def __init__( 

2352 self, 

2353 lineno: int | None, 

2354 col_offset: int | None, 

2355 parent: NodeNG | None, 

2356 *, 

2357 end_lineno: int | None, 

2358 end_col_offset: int | None, 

2359 ) -> None: 

2360 self.items: list[tuple[InferenceResult, InferenceResult]] = [] 

2361 """The key-value pairs contained in the dictionary.""" 

2362 

2363 super().__init__( 

2364 lineno=lineno, 

2365 col_offset=col_offset, 

2366 end_lineno=end_lineno, 

2367 end_col_offset=end_col_offset, 

2368 parent=parent, 

2369 ) 

2370 

2371 def postinit(self, items: list[tuple[InferenceResult, InferenceResult]]) -> None: 

2372 """Do some setup after initialisation. 

2373 

2374 :param items: The key-value pairs contained in the dictionary. 

2375 """ 

2376 self.items = items 

2377 

2378 infer_unary_op = protocols.dict_infer_unary_op 

2379 

2380 def pytype(self) -> Literal["builtins.dict"]: 

2381 """Get the name of the type that this node represents. 

2382 

2383 :returns: The name of the type. 

2384 """ 

2385 return "builtins.dict" 

2386 

2387 def get_children(self): 

2388 """Get the key and value nodes below this node. 

2389 

2390 Children are returned in the order that they are defined in the source 

2391 code, key first then the value. 

2392 

2393 :returns: The children. 

2394 :rtype: iterable(NodeNG) 

2395 """ 

2396 for key, value in self.items: 

2397 yield key 

2398 yield value 

2399 

2400 def last_child(self): 

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

2402 

2403 :returns: The last child, or None if no children exist. 

2404 :rtype: NodeNG or None 

2405 """ 

2406 if self.items: 

2407 return self.items[-1][1] 

2408 return None 

2409 

2410 def itered(self): 

2411 """An iterator over the keys this node contains. 

2412 

2413 :returns: The keys of this node. 

2414 :rtype: iterable(NodeNG) 

2415 """ 

2416 return [key for (key, _) in self.items] 

2417 

2418 def getitem( 

2419 self, index: Const | Slice, context: InferenceContext | None = None 

2420 ) -> NodeNG: 

2421 """Get an item from this node. 

2422 

2423 :param index: The node to use as a subscript index. 

2424 

2425 :raises AstroidTypeError: When the given index cannot be used as a 

2426 subscript index, or if this node is not subscriptable. 

2427 :raises AstroidIndexError: If the given index does not exist in the 

2428 dictionary. 

2429 """ 

2430 for key, value in self.items: 

2431 # TODO(cpopa): no support for overriding yet, {1:2, **{1: 3}}. 

2432 if isinstance(key, DictUnpack): 

2433 inferred_value = util.safe_infer(value, context) 

2434 if not isinstance(inferred_value, Dict): 

2435 continue 

2436 

2437 try: 

2438 return inferred_value.getitem(index, context) 

2439 except (AstroidTypeError, AstroidIndexError): 

2440 continue 

2441 

2442 for inferredkey in key.infer(context): 

2443 if isinstance(inferredkey, util.UninferableBase): 

2444 continue 

2445 if isinstance(inferredkey, Const) and isinstance(index, Const): 

2446 if inferredkey.value == index.value: 

2447 return value 

2448 

2449 raise AstroidIndexError(index) 

2450 

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

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

2453 

2454 :returns: The boolean value of this node. 

2455 :rtype: bool 

2456 """ 

2457 return bool(self.items) 

2458 

2459 def _infer( 

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

2461 ) -> Iterator[nodes.Dict]: 

2462 if not any(isinstance(k, DictUnpack) for k, _ in self.items): 

2463 yield self 

2464 else: 

2465 items = self._infer_map(context) 

2466 new_seq = type(self)( 

2467 lineno=self.lineno, 

2468 col_offset=self.col_offset, 

2469 parent=self.parent, 

2470 end_lineno=self.end_lineno, 

2471 end_col_offset=self.end_col_offset, 

2472 ) 

2473 new_seq.postinit(list(items.items())) 

2474 yield new_seq 

2475 

2476 @staticmethod 

2477 def _update_with_replacement( 

2478 lhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult], 

2479 rhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult], 

2480 ) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]: 

2481 """Delete nodes that equate to duplicate keys. 

2482 

2483 Since an astroid node doesn't 'equal' another node with the same value, 

2484 this function uses the as_string method to make sure duplicate keys 

2485 don't get through 

2486 

2487 Note that both the key and the value are astroid nodes 

2488 

2489 Fixes issue with DictUnpack causing duplicate keys 

2490 in inferred Dict items 

2491 

2492 :param lhs_dict: Dictionary to 'merge' nodes into 

2493 :param rhs_dict: Dictionary with nodes to pull from 

2494 :return : merged dictionary of nodes 

2495 """ 

2496 combined_dict = itertools.chain(lhs_dict.items(), rhs_dict.items()) 

2497 # Overwrite keys which have the same string values 

2498 string_map = {key.as_string(): (key, value) for key, value in combined_dict} 

2499 # Return to dictionary 

2500 return dict(string_map.values()) 

2501 

2502 def _infer_map( 

2503 self, context: InferenceContext | None 

2504 ) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]: 

2505 """Infer all values based on Dict.items.""" 

2506 values: dict[SuccessfulInferenceResult, SuccessfulInferenceResult] = {} 

2507 for name, value in self.items: 

2508 if isinstance(name, DictUnpack): 

2509 double_starred = util.safe_infer(value, context) 

2510 if not double_starred: 

2511 raise InferenceError 

2512 if not isinstance(double_starred, Dict): 

2513 raise InferenceError(node=self, context=context) 

2514 unpack_items = double_starred._infer_map(context) 

2515 values = self._update_with_replacement(values, unpack_items) 

2516 else: 

2517 key = util.safe_infer(name, context=context) 

2518 safe_value = util.safe_infer(value, context=context) 

2519 if any(not elem for elem in (key, safe_value)): 

2520 raise InferenceError(node=self, context=context) 

2521 # safe_value is SuccessfulInferenceResult as bool(Uninferable) == False 

2522 values = self._update_with_replacement(values, {key: safe_value}) 

2523 return values 

2524 

2525 

2526class Expr(_base_nodes.Statement): 

2527 """Class representing an :class:`ast.Expr` node. 

2528 

2529 An :class:`Expr` is any expression that does not have its value used or 

2530 stored. 

2531 

2532 >>> import astroid 

2533 >>> node = astroid.extract_node('method()') 

2534 >>> node 

2535 <Call l.1 at 0x7f23b2e352b0> 

2536 >>> node.parent 

2537 <Expr l.1 at 0x7f23b2e35278> 

2538 """ 

2539 

2540 _astroid_fields = ("value",) 

2541 

2542 value: NodeNG 

2543 """What the expression does.""" 

2544 

2545 def postinit(self, value: NodeNG) -> None: 

2546 self.value = value 

2547 

2548 def get_children(self): 

2549 yield self.value 

2550 

2551 def _get_yield_nodes_skip_functions(self): 

2552 if not self.value.is_function: 

2553 yield from self.value._get_yield_nodes_skip_functions() 

2554 

2555 def _get_yield_nodes_skip_lambdas(self): 

2556 if not self.value.is_lambda: 

2557 yield from self.value._get_yield_nodes_skip_lambdas() 

2558 

2559 

2560class EmptyNode(_base_nodes.NoChildrenNode): 

2561 """Holds an arbitrary object in the :attr:`LocalsDictNodeNG.locals`.""" 

2562 

2563 object = None 

2564 

2565 def __init__( 

2566 self, 

2567 lineno: None = None, 

2568 col_offset: None = None, 

2569 parent: NodeNG = SYNTHETIC_ROOT, 

2570 *, 

2571 end_lineno: None = None, 

2572 end_col_offset: None = None, 

2573 ) -> None: 

2574 super().__init__( 

2575 lineno=lineno, 

2576 col_offset=col_offset, 

2577 end_lineno=end_lineno, 

2578 end_col_offset=end_col_offset, 

2579 parent=parent, 

2580 ) 

2581 

2582 def has_underlying_object(self) -> bool: 

2583 return self.object is not None and self.object is not _EMPTY_OBJECT_MARKER 

2584 

2585 @decorators.raise_if_nothing_inferred 

2586 @decorators.path_wrapper 

2587 def _infer( 

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

2589 ) -> Generator[InferenceResult]: 

2590 if not self.has_underlying_object(): 

2591 yield util.Uninferable 

2592 else: 

2593 try: 

2594 yield from AstroidManager().infer_ast_from_something( 

2595 self.object, context=context 

2596 ) 

2597 except AstroidError: 

2598 yield util.Uninferable 

2599 

2600 

2601class ExceptHandler( 

2602 _base_nodes.MultiLineBlockNode, _base_nodes.AssignTypeNode, _base_nodes.Statement 

2603): 

2604 """Class representing an :class:`ast.ExceptHandler`. node. 

2605 

2606 An :class:`ExceptHandler` is an ``except`` block on a try-except. 

2607 

2608 >>> import astroid 

2609 >>> node = astroid.extract_node(''' 

2610 try: 

2611 do_something() 

2612 except Exception as error: 

2613 print("Error!") 

2614 ''') 

2615 >>> node 

2616 <Try l.2 at 0x7f23b2e9d908> 

2617 >>> node.handlers 

2618 [<ExceptHandler l.4 at 0x7f23b2e9e860>] 

2619 """ 

2620 

2621 _astroid_fields = ("type", "name", "body") 

2622 _multi_line_block_fields = ("body",) 

2623 

2624 type: NodeNG | None 

2625 """The types that the block handles.""" 

2626 

2627 name: AssignName | None 

2628 """The name that the caught exception is assigned to.""" 

2629 

2630 body: list[NodeNG] 

2631 """The contents of the block.""" 

2632 

2633 assigned_stmts = protocols.excepthandler_assigned_stmts 

2634 """Returns the assigned statement (non inferred) according to the assignment type. 

2635 See astroid/protocols.py for actual implementation. 

2636 """ 

2637 

2638 def postinit( 

2639 self, 

2640 type: NodeNG | None, # pylint: disable = redefined-builtin 

2641 name: AssignName | None, 

2642 body: list[NodeNG], 

2643 ) -> None: 

2644 self.type = type 

2645 self.name = name 

2646 self.body = body 

2647 

2648 def get_children(self): 

2649 if self.type is not None: 

2650 yield self.type 

2651 

2652 if self.name is not None: 

2653 yield self.name 

2654 

2655 yield from self.body 

2656 

2657 @cached_property 

2658 def blockstart_tolineno(self): 

2659 """The line on which the beginning of this block ends. 

2660 

2661 :type: int 

2662 """ 

2663 if self.name: 

2664 return self.name.tolineno 

2665 if self.type: 

2666 return self.type.tolineno 

2667 return self.lineno 

2668 

2669 def catch(self, exceptions: list[str] | None) -> bool: 

2670 """Check if this node handles any of the given 

2671 

2672 :param exceptions: The names of the exceptions to check for. 

2673 """ 

2674 if self.type is None or exceptions is None: 

2675 return True 

2676 return any(node.name in exceptions for node in self.type._get_name_nodes()) 

2677 

2678 

2679class For( 

2680 _base_nodes.MultiLineWithElseBlockNode, 

2681 _base_nodes.AssignTypeNode, 

2682 _base_nodes.Statement, 

2683): 

2684 """Class representing an :class:`ast.For` node. 

2685 

2686 >>> import astroid 

2687 >>> node = astroid.extract_node('for thing in things: print(thing)') 

2688 >>> node 

2689 <For l.1 at 0x7f23b2e8cf28> 

2690 """ 

2691 

2692 _astroid_fields = ("target", "iter", "body", "orelse") 

2693 _other_other_fields = ("type_annotation",) 

2694 _multi_line_block_fields = ("body", "orelse") 

2695 

2696 optional_assign = True 

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

2698 

2699 This is always ``True`` for :class:`For` nodes. 

2700 """ 

2701 

2702 target: NodeNG 

2703 """What the loop assigns to.""" 

2704 

2705 iter: NodeNG 

2706 """What the loop iterates over.""" 

2707 

2708 body: list[NodeNG] 

2709 """The contents of the body of the loop.""" 

2710 

2711 orelse: list[NodeNG] 

2712 """The contents of the ``else`` block of the loop.""" 

2713 

2714 type_annotation: NodeNG | None 

2715 """If present, this will contain the type annotation passed by a type comment""" 

2716 

2717 def postinit( 

2718 self, 

2719 target: NodeNG, 

2720 iter: NodeNG, # pylint: disable = redefined-builtin 

2721 body: list[NodeNG], 

2722 orelse: list[NodeNG], 

2723 type_annotation: NodeNG | None, 

2724 ) -> None: 

2725 self.target = target 

2726 self.iter = iter 

2727 self.body = body 

2728 self.orelse = orelse 

2729 self.type_annotation = type_annotation 

2730 

2731 assigned_stmts = protocols.for_assigned_stmts 

2732 """Returns the assigned statement (non inferred) according to the assignment type. 

2733 See astroid/protocols.py for actual implementation. 

2734 """ 

2735 

2736 @cached_property 

2737 def blockstart_tolineno(self): 

2738 """The line on which the beginning of this block ends. 

2739 

2740 :type: int 

2741 """ 

2742 return self.iter.tolineno 

2743 

2744 def get_children(self): 

2745 yield self.target 

2746 yield self.iter 

2747 

2748 yield from self.body 

2749 yield from self.orelse 

2750 

2751 

2752class AsyncFor(For): 

2753 """Class representing an :class:`ast.AsyncFor` node. 

2754 

2755 An :class:`AsyncFor` is an asynchronous :class:`For` built with 

2756 the ``async`` keyword. 

2757 

2758 >>> import astroid 

2759 >>> node = astroid.extract_node(''' 

2760 async def func(things): 

2761 async for thing in things: 

2762 print(thing) 

2763 ''') 

2764 >>> node 

2765 <AsyncFunctionDef.func l.2 at 0x7f23b2e416d8> 

2766 >>> node.body[0] 

2767 <AsyncFor l.3 at 0x7f23b2e417b8> 

2768 """ 

2769 

2770 

2771class Await(NodeNG): 

2772 """Class representing an :class:`ast.Await` node. 

2773 

2774 An :class:`Await` is the ``await`` keyword. 

2775 

2776 >>> import astroid 

2777 >>> node = astroid.extract_node(''' 

2778 async def func(things): 

2779 await other_func() 

2780 ''') 

2781 >>> node 

2782 <AsyncFunctionDef.func l.2 at 0x7f23b2e41748> 

2783 >>> node.body[0] 

2784 <Expr l.3 at 0x7f23b2e419e8> 

2785 >>> list(node.body[0].get_children())[0] 

2786 <Await l.3 at 0x7f23b2e41a20> 

2787 """ 

2788 

2789 _astroid_fields = ("value",) 

2790 

2791 value: NodeNG 

2792 """What to wait for.""" 

2793 

2794 def postinit(self, value: NodeNG) -> None: 

2795 self.value = value 

2796 

2797 def get_children(self): 

2798 yield self.value 

2799 

2800 

2801class ImportFrom(_base_nodes.ImportNode): 

2802 """Class representing an :class:`ast.ImportFrom` node. 

2803 

2804 >>> import astroid 

2805 >>> node = astroid.extract_node('from my_package import my_module') 

2806 >>> node 

2807 <ImportFrom l.1 at 0x7f23b2e415c0> 

2808 """ 

2809 

2810 _other_fields = ("modname", "names", "level") 

2811 

2812 def __init__( 

2813 self, 

2814 fromname: str | None, 

2815 names: list[tuple[str, str | None]], 

2816 level: int | None = 0, 

2817 lineno: int | None = None, 

2818 col_offset: int | None = None, 

2819 parent: NodeNG | None = None, 

2820 *, 

2821 end_lineno: int | None = None, 

2822 end_col_offset: int | None = None, 

2823 ) -> None: 

2824 """ 

2825 :param fromname: The module that is being imported from. 

2826 

2827 :param names: What is being imported from the module. 

2828 

2829 :param level: The level of relative import. 

2830 

2831 :param lineno: The line that this node appears on in the source code. 

2832 

2833 :param col_offset: The column that this node appears on in the 

2834 source code. 

2835 

2836 :param parent: The parent node in the syntax tree. 

2837 

2838 :param end_lineno: The last line this node appears on in the source code. 

2839 

2840 :param end_col_offset: The end column this node appears on in the 

2841 source code. Note: This is after the last symbol. 

2842 """ 

2843 self.modname: str | None = fromname # can be None 

2844 """The module that is being imported from. 

2845 

2846 This is ``None`` for relative imports. 

2847 """ 

2848 

2849 self.names: list[tuple[str, str | None]] = names 

2850 """What is being imported from the module. 

2851 

2852 Each entry is a :class:`tuple` of the name being imported, 

2853 and the alias that the name is assigned to (if any). 

2854 """ 

2855 

2856 # TODO When is 'level' None? 

2857 self.level: int | None = level # can be None 

2858 """The level of relative import. 

2859 

2860 Essentially this is the number of dots in the import. 

2861 This is always 0 for absolute imports. 

2862 """ 

2863 

2864 super().__init__( 

2865 lineno=lineno, 

2866 col_offset=col_offset, 

2867 end_lineno=end_lineno, 

2868 end_col_offset=end_col_offset, 

2869 parent=parent, 

2870 ) 

2871 

2872 @decorators.raise_if_nothing_inferred 

2873 @decorators.path_wrapper 

2874 def _infer( 

2875 self, 

2876 context: InferenceContext | None = None, 

2877 asname: bool = True, 

2878 **kwargs: Any, 

2879 ) -> Generator[InferenceResult]: 

2880 """Infer a ImportFrom node: return the imported module/object.""" 

2881 context = context or InferenceContext() 

2882 name = context.lookupname 

2883 if name is None: 

2884 raise InferenceError(node=self, context=context) 

2885 if asname: 

2886 try: 

2887 name = self.real_name(name) 

2888 except AttributeInferenceError as exc: 

2889 # See https://github.com/pylint-dev/pylint/issues/4692 

2890 raise InferenceError(node=self, context=context) from exc 

2891 try: 

2892 module = self.do_import_module() 

2893 except AstroidBuildingError as exc: 

2894 raise InferenceError(node=self, context=context) from exc 

2895 

2896 try: 

2897 context = copy_context(context) 

2898 context.lookupname = name 

2899 stmts = module.getattr(name, ignore_locals=module is self.root()) 

2900 return _infer_stmts(stmts, context) 

2901 except AttributeInferenceError as error: 

2902 raise InferenceError( 

2903 str(error), target=self, attribute=name, context=context 

2904 ) from error 

2905 

2906 

2907class Attribute(NodeNG): 

2908 """Class representing an :class:`ast.Attribute` node.""" 

2909 

2910 expr: NodeNG 

2911 

2912 _astroid_fields = ("expr",) 

2913 _other_fields = ("attrname",) 

2914 

2915 def __init__( 

2916 self, 

2917 attrname: str, 

2918 lineno: int, 

2919 col_offset: int, 

2920 parent: NodeNG, 

2921 *, 

2922 end_lineno: int | None, 

2923 end_col_offset: int | None, 

2924 ) -> None: 

2925 self.attrname = attrname 

2926 """The name of the attribute.""" 

2927 

2928 super().__init__( 

2929 lineno=lineno, 

2930 col_offset=col_offset, 

2931 end_lineno=end_lineno, 

2932 end_col_offset=end_col_offset, 

2933 parent=parent, 

2934 ) 

2935 

2936 def postinit(self, expr: NodeNG) -> None: 

2937 self.expr = expr 

2938 

2939 def get_children(self): 

2940 yield self.expr 

2941 

2942 @decorators.raise_if_nothing_inferred 

2943 @decorators.path_wrapper 

2944 def _infer( 

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

2946 ) -> Generator[InferenceResult, None, InferenceErrorInfo]: 

2947 return _infer_attribute(self, context, **kwargs) 

2948 

2949 

2950class Global(_base_nodes.NoChildrenNode, _base_nodes.Statement): 

2951 """Class representing an :class:`ast.Global` node. 

2952 

2953 >>> import astroid 

2954 >>> node = astroid.extract_node('global a_global') 

2955 >>> node 

2956 <Global l.1 at 0x7f23b2e9de10> 

2957 """ 

2958 

2959 _other_fields = ("names",) 

2960 

2961 def __init__( 

2962 self, 

2963 names: list[str], 

2964 lineno: int | None = None, 

2965 col_offset: int | None = None, 

2966 parent: NodeNG | None = None, 

2967 *, 

2968 end_lineno: int | None = None, 

2969 end_col_offset: int | None = None, 

2970 ) -> None: 

2971 """ 

2972 :param names: The names being declared as global. 

2973 

2974 :param lineno: The line that this node appears on in the source code. 

2975 

2976 :param col_offset: The column that this node appears on in the 

2977 source code. 

2978 

2979 :param parent: The parent node in the syntax tree. 

2980 

2981 :param end_lineno: The last line this node appears on in the source code. 

2982 

2983 :param end_col_offset: The end column this node appears on in the 

2984 source code. Note: This is after the last symbol. 

2985 """ 

2986 self.names: list[str] = names 

2987 """The names being declared as global.""" 

2988 

2989 super().__init__( 

2990 lineno=lineno, 

2991 col_offset=col_offset, 

2992 end_lineno=end_lineno, 

2993 end_col_offset=end_col_offset, 

2994 parent=parent, 

2995 ) 

2996 

2997 def _infer_name(self, frame, name): 

2998 return name 

2999 

3000 @decorators.raise_if_nothing_inferred 

3001 @decorators.path_wrapper 

3002 def _infer( 

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

3004 ) -> Generator[InferenceResult]: 

3005 if context is None or context.lookupname is None: 

3006 raise InferenceError(node=self, context=context) 

3007 try: 

3008 # pylint: disable-next=no-member 

3009 return _infer_stmts(self.root().getattr(context.lookupname), context) 

3010 except AttributeInferenceError as error: 

3011 raise InferenceError( 

3012 str(error), target=self, attribute=context.lookupname, context=context 

3013 ) from error 

3014 

3015 

3016class If(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement): 

3017 """Class representing an :class:`ast.If` node. 

3018 

3019 >>> import astroid 

3020 >>> node = astroid.extract_node('if condition: print(True)') 

3021 >>> node 

3022 <If l.1 at 0x7f23b2e9dd30> 

3023 """ 

3024 

3025 _astroid_fields = ("test", "body", "orelse") 

3026 _multi_line_block_fields = ("body", "orelse") 

3027 

3028 test: NodeNG 

3029 """The condition that the statement tests.""" 

3030 

3031 body: list[NodeNG] 

3032 """The contents of the block.""" 

3033 

3034 orelse: list[NodeNG] 

3035 """The contents of the ``else`` block.""" 

3036 

3037 def postinit(self, test: NodeNG, body: list[NodeNG], orelse: list[NodeNG]) -> None: 

3038 self.test = test 

3039 self.body = body 

3040 self.orelse = orelse 

3041 

3042 @cached_property 

3043 def blockstart_tolineno(self): 

3044 """The line on which the beginning of this block ends. 

3045 

3046 :type: int 

3047 """ 

3048 return self.test.tolineno 

3049 

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

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

3052 

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

3054 

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

3056 starting at the given line number. 

3057 """ 

3058 if lineno == self.body[0].fromlineno: 

3059 return lineno, lineno 

3060 if lineno <= self.body[-1].tolineno: 

3061 return lineno, self.body[-1].tolineno 

3062 return self._elsed_block_range(lineno, self.orelse, self.body[0].fromlineno - 1) 

3063 

3064 def get_children(self): 

3065 yield self.test 

3066 

3067 yield from self.body 

3068 yield from self.orelse 

3069 

3070 def has_elif_block(self) -> bool: 

3071 return len(self.orelse) == 1 and isinstance(self.orelse[0], If) 

3072 

3073 def _get_yield_nodes_skip_functions(self): 

3074 """An If node can contain a Yield node in the test""" 

3075 yield from self.test._get_yield_nodes_skip_functions() 

3076 yield from super()._get_yield_nodes_skip_functions() 

3077 

3078 def _get_yield_nodes_skip_lambdas(self): 

3079 """An If node can contain a Yield node in the test""" 

3080 yield from self.test._get_yield_nodes_skip_lambdas() 

3081 yield from super()._get_yield_nodes_skip_lambdas() 

3082 

3083 

3084class IfExp(NodeNG): 

3085 """Class representing an :class:`ast.IfExp` node. 

3086 >>> import astroid 

3087 >>> node = astroid.extract_node('value if condition else other') 

3088 >>> node 

3089 <IfExp l.1 at 0x7f23b2e9dbe0> 

3090 """ 

3091 

3092 _astroid_fields = ("test", "body", "orelse") 

3093 

3094 test: NodeNG 

3095 """The condition that the statement tests.""" 

3096 

3097 body: NodeNG 

3098 """The contents of the block.""" 

3099 

3100 orelse: NodeNG 

3101 """The contents of the ``else`` block.""" 

3102 

3103 def postinit(self, test: NodeNG, body: NodeNG, orelse: NodeNG) -> None: 

3104 self.test = test 

3105 self.body = body 

3106 self.orelse = orelse 

3107 

3108 def get_children(self): 

3109 yield self.test 

3110 yield self.body 

3111 yield self.orelse 

3112 

3113 def op_left_associative(self) -> Literal[False]: 

3114 # `1 if True else 2 if False else 3` is parsed as 

3115 # `1 if True else (2 if False else 3)` 

3116 return False 

3117 

3118 @decorators.raise_if_nothing_inferred 

3119 def _infer( 

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

3121 ) -> Generator[InferenceResult]: 

3122 """Support IfExp inference. 

3123 

3124 If we can't infer the truthiness of the condition, we default 

3125 to inferring both branches. Otherwise, we infer either branch 

3126 depending on the condition. 

3127 """ 

3128 both_branches = False 

3129 # We use two separate contexts for evaluating lhs and rhs because 

3130 # evaluating lhs may leave some undesired entries in context.path 

3131 # which may not let us infer right value of rhs. 

3132 

3133 context = context or InferenceContext() 

3134 lhs_context = copy_context(context) 

3135 rhs_context = copy_context(context) 

3136 try: 

3137 test = next(self.test.infer(context=context.clone())) 

3138 except (InferenceError, StopIteration): 

3139 both_branches = True 

3140 else: 

3141 test_bool_value = test.bool_value() 

3142 if not isinstance(test, util.UninferableBase) and not isinstance( 

3143 test_bool_value, util.UninferableBase 

3144 ): 

3145 if test_bool_value: 

3146 yield from self.body.infer(context=lhs_context) 

3147 else: 

3148 yield from self.orelse.infer(context=rhs_context) 

3149 else: 

3150 both_branches = True 

3151 if both_branches: 

3152 yield from self.body.infer(context=lhs_context) 

3153 yield from self.orelse.infer(context=rhs_context) 

3154 

3155 

3156class Import(_base_nodes.ImportNode): 

3157 """Class representing an :class:`ast.Import` node. 

3158 >>> import astroid 

3159 >>> node = astroid.extract_node('import astroid') 

3160 >>> node 

3161 <Import l.1 at 0x7f23b2e4e5c0> 

3162 """ 

3163 

3164 _other_fields = ("names",) 

3165 

3166 def __init__( 

3167 self, 

3168 names: list[tuple[str, str | None]], 

3169 lineno: int | None = None, 

3170 col_offset: int | None = None, 

3171 parent: NodeNG | None = None, 

3172 *, 

3173 end_lineno: int | None = None, 

3174 end_col_offset: int | None = None, 

3175 ) -> None: 

3176 """ 

3177 :param names: The names being imported. 

3178 

3179 :param lineno: The line that this node appears on in the source code. 

3180 

3181 :param col_offset: The column that this node appears on in the 

3182 source code. 

3183 

3184 :param parent: The parent node in the syntax tree. 

3185 

3186 :param end_lineno: The last line this node appears on in the source code. 

3187 

3188 :param end_col_offset: The end column this node appears on in the 

3189 source code. Note: This is after the last symbol. 

3190 """ 

3191 self.names: list[tuple[str, str | None]] = names 

3192 """The names being imported. 

3193 

3194 Each entry is a :class:`tuple` of the name being imported, 

3195 and the alias that the name is assigned to (if any). 

3196 """ 

3197 

3198 super().__init__( 

3199 lineno=lineno, 

3200 col_offset=col_offset, 

3201 end_lineno=end_lineno, 

3202 end_col_offset=end_col_offset, 

3203 parent=parent, 

3204 ) 

3205 

3206 @decorators.raise_if_nothing_inferred 

3207 @decorators.path_wrapper 

3208 def _infer( 

3209 self, 

3210 context: InferenceContext | None = None, 

3211 asname: bool = True, 

3212 **kwargs: Any, 

3213 ) -> Generator[nodes.Module]: 

3214 """Infer an Import node: return the imported module/object.""" 

3215 context = context or InferenceContext() 

3216 name = context.lookupname 

3217 if name is None: 

3218 raise InferenceError(node=self, context=context) 

3219 

3220 try: 

3221 if asname: 

3222 yield self.do_import_module(self.real_name(name)) 

3223 else: 

3224 yield self.do_import_module(name) 

3225 except AstroidBuildingError as exc: 

3226 raise InferenceError(node=self, context=context) from exc 

3227 

3228 

3229class Keyword(NodeNG): 

3230 """Class representing an :class:`ast.keyword` node. 

3231 

3232 >>> import astroid 

3233 >>> node = astroid.extract_node('function(a_kwarg=True)') 

3234 >>> node 

3235 <Call l.1 at 0x7f23b2e9e320> 

3236 >>> node.keywords 

3237 [<Keyword l.1 at 0x7f23b2e9e9b0>] 

3238 """ 

3239 

3240 _astroid_fields = ("value",) 

3241 _other_fields = ("arg",) 

3242 

3243 value: NodeNG 

3244 """The value being assigned to the keyword argument.""" 

3245 

3246 def __init__( 

3247 self, 

3248 arg: str | None, 

3249 lineno: int | None, 

3250 col_offset: int | None, 

3251 parent: NodeNG, 

3252 *, 

3253 end_lineno: int | None, 

3254 end_col_offset: int | None, 

3255 ) -> None: 

3256 self.arg = arg 

3257 """The argument being assigned to.""" 

3258 

3259 super().__init__( 

3260 lineno=lineno, 

3261 col_offset=col_offset, 

3262 end_lineno=end_lineno, 

3263 end_col_offset=end_col_offset, 

3264 parent=parent, 

3265 ) 

3266 

3267 def postinit(self, value: NodeNG) -> None: 

3268 self.value = value 

3269 

3270 def get_children(self): 

3271 yield self.value 

3272 

3273 

3274class List(BaseContainer): 

3275 """Class representing an :class:`ast.List` node. 

3276 

3277 >>> import astroid 

3278 >>> node = astroid.extract_node('[1, 2, 3]') 

3279 >>> node 

3280 <List.list l.1 at 0x7f23b2e9e128> 

3281 """ 

3282 

3283 _other_fields = ("ctx",) 

3284 

3285 def __init__( 

3286 self, 

3287 ctx: Context | None = None, 

3288 lineno: int | None = None, 

3289 col_offset: int | None = None, 

3290 parent: NodeNG | None = None, 

3291 *, 

3292 end_lineno: int | None = None, 

3293 end_col_offset: int | None = None, 

3294 ) -> None: 

3295 """ 

3296 :param ctx: Whether the list is assigned to or loaded from. 

3297 

3298 :param lineno: The line that this node appears on in the source code. 

3299 

3300 :param col_offset: The column that this node appears on in the 

3301 source code. 

3302 

3303 :param parent: The parent node in the syntax tree. 

3304 

3305 :param end_lineno: The last line this node appears on in the source code. 

3306 

3307 :param end_col_offset: The end column this node appears on in the 

3308 source code. Note: This is after the last symbol. 

3309 """ 

3310 self.ctx: Context | None = ctx 

3311 """Whether the list is assigned to or loaded from.""" 

3312 

3313 super().__init__( 

3314 lineno=lineno, 

3315 col_offset=col_offset, 

3316 end_lineno=end_lineno, 

3317 end_col_offset=end_col_offset, 

3318 parent=parent, 

3319 ) 

3320 

3321 assigned_stmts = protocols.sequence_assigned_stmts 

3322 """Returns the assigned statement (non inferred) according to the assignment type. 

3323 See astroid/protocols.py for actual implementation. 

3324 """ 

3325 

3326 infer_unary_op = protocols.list_infer_unary_op 

3327 infer_binary_op = protocols.tl_infer_binary_op 

3328 

3329 def pytype(self) -> Literal["builtins.list"]: 

3330 """Get the name of the type that this node represents. 

3331 

3332 :returns: The name of the type. 

3333 """ 

3334 return "builtins.list" 

3335 

3336 def getitem(self, index, context: InferenceContext | None = None): 

3337 """Get an item from this node. 

3338 

3339 :param index: The node to use as a subscript index. 

3340 :type index: Const or Slice 

3341 """ 

3342 return _container_getitem(self, self.elts, index, context=context) 

3343 

3344 

3345class Nonlocal(_base_nodes.NoChildrenNode, _base_nodes.Statement): 

3346 """Class representing an :class:`ast.Nonlocal` node. 

3347 

3348 >>> import astroid 

3349 >>> node = astroid.extract_node(''' 

3350 def function(): 

3351 nonlocal var 

3352 ''') 

3353 >>> node 

3354 <FunctionDef.function l.2 at 0x7f23b2e9e208> 

3355 >>> node.body[0] 

3356 <Nonlocal l.3 at 0x7f23b2e9e908> 

3357 """ 

3358 

3359 _other_fields = ("names",) 

3360 

3361 def __init__( 

3362 self, 

3363 names: list[str], 

3364 lineno: int | None = None, 

3365 col_offset: int | None = None, 

3366 parent: NodeNG | None = None, 

3367 *, 

3368 end_lineno: int | None = None, 

3369 end_col_offset: int | None = None, 

3370 ) -> None: 

3371 """ 

3372 :param names: The names being declared as not local. 

3373 

3374 :param lineno: The line that this node appears on in the source code. 

3375 

3376 :param col_offset: The column that this node appears on in the 

3377 source code. 

3378 

3379 :param parent: The parent node in the syntax tree. 

3380 

3381 :param end_lineno: The last line this node appears on in the source code. 

3382 

3383 :param end_col_offset: The end column this node appears on in the 

3384 source code. Note: This is after the last symbol. 

3385 """ 

3386 self.names: list[str] = names 

3387 """The names being declared as not local.""" 

3388 

3389 super().__init__( 

3390 lineno=lineno, 

3391 col_offset=col_offset, 

3392 end_lineno=end_lineno, 

3393 end_col_offset=end_col_offset, 

3394 parent=parent, 

3395 ) 

3396 

3397 def _infer_name(self, frame, name): 

3398 return name 

3399 

3400 

3401class ParamSpec(_base_nodes.AssignTypeNode): 

3402 """Class representing a :class:`ast.ParamSpec` node. 

3403 

3404 >>> import astroid 

3405 >>> node = astroid.extract_node('type Alias[**P] = Callable[P, int]') 

3406 >>> node.type_params[0] 

3407 <ParamSpec l.1 at 0x7f23b2e4e198> 

3408 """ 

3409 

3410 _astroid_fields = ("name", "default_value") 

3411 name: AssignName 

3412 default_value: NodeNG | None 

3413 

3414 def __init__( 

3415 self, 

3416 lineno: int, 

3417 col_offset: int, 

3418 parent: NodeNG, 

3419 *, 

3420 end_lineno: int, 

3421 end_col_offset: int, 

3422 ) -> None: 

3423 super().__init__( 

3424 lineno=lineno, 

3425 col_offset=col_offset, 

3426 end_lineno=end_lineno, 

3427 end_col_offset=end_col_offset, 

3428 parent=parent, 

3429 ) 

3430 

3431 def postinit(self, *, name: AssignName, default_value: NodeNG | None) -> None: 

3432 self.name = name 

3433 self.default_value = default_value 

3434 

3435 def _infer( 

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

3437 ) -> Iterator[ParamSpec]: 

3438 yield self 

3439 

3440 assigned_stmts = protocols.generic_type_assigned_stmts 

3441 """Returns the assigned statement (non inferred) according to the assignment type. 

3442 See astroid/protocols.py for actual implementation. 

3443 """ 

3444 

3445 

3446class Pass(_base_nodes.NoChildrenNode, _base_nodes.Statement): 

3447 """Class representing an :class:`ast.Pass` node. 

3448 

3449 >>> import astroid 

3450 >>> node = astroid.extract_node('pass') 

3451 >>> node 

3452 <Pass l.1 at 0x7f23b2e9e748> 

3453 """ 

3454 

3455 

3456class Raise(_base_nodes.Statement): 

3457 """Class representing an :class:`ast.Raise` node. 

3458 

3459 >>> import astroid 

3460 >>> node = astroid.extract_node('raise RuntimeError("Something bad happened!")') 

3461 >>> node 

3462 <Raise l.1 at 0x7f23b2e9e828> 

3463 """ 

3464 

3465 _astroid_fields = ("exc", "cause") 

3466 

3467 exc: NodeNG | None 

3468 """What is being raised.""" 

3469 

3470 cause: NodeNG | None 

3471 """The exception being used to raise this one.""" 

3472 

3473 def postinit( 

3474 self, 

3475 exc: NodeNG | None, 

3476 cause: NodeNG | None, 

3477 ) -> None: 

3478 self.exc = exc 

3479 self.cause = cause 

3480 

3481 def raises_not_implemented(self) -> bool: 

3482 """Check if this node raises a :class:`NotImplementedError`. 

3483 

3484 :returns: Whether this node raises a :class:`NotImplementedError`. 

3485 """ 

3486 if not self.exc: 

3487 return False 

3488 return any( 

3489 name.name == "NotImplementedError" for name in self.exc._get_name_nodes() 

3490 ) 

3491 

3492 def get_children(self): 

3493 if self.exc is not None: 

3494 yield self.exc 

3495 

3496 if self.cause is not None: 

3497 yield self.cause 

3498 

3499 

3500class Return(_base_nodes.Statement): 

3501 """Class representing an :class:`ast.Return` node. 

3502 

3503 >>> import astroid 

3504 >>> node = astroid.extract_node('return True') 

3505 >>> node 

3506 <Return l.1 at 0x7f23b8211908> 

3507 """ 

3508 

3509 _astroid_fields = ("value",) 

3510 

3511 value: NodeNG | None 

3512 """The value being returned.""" 

3513 

3514 def postinit(self, value: NodeNG | None) -> None: 

3515 self.value = value 

3516 

3517 def get_children(self): 

3518 if self.value is not None: 

3519 yield self.value 

3520 

3521 def is_tuple_return(self) -> bool: 

3522 return isinstance(self.value, Tuple) 

3523 

3524 def _get_return_nodes_skip_functions(self): 

3525 yield self 

3526 

3527 

3528class Set(BaseContainer): 

3529 """Class representing an :class:`ast.Set` node. 

3530 

3531 >>> import astroid 

3532 >>> node = astroid.extract_node('{1, 2, 3}') 

3533 >>> node 

3534 <Set.set l.1 at 0x7f23b2e71d68> 

3535 """ 

3536 

3537 infer_unary_op = protocols.set_infer_unary_op 

3538 

3539 def pytype(self) -> Literal["builtins.set"]: 

3540 """Get the name of the type that this node represents. 

3541 

3542 :returns: The name of the type. 

3543 """ 

3544 return "builtins.set" 

3545 

3546 

3547class Slice(NodeNG): 

3548 """Class representing an :class:`ast.Slice` node. 

3549 

3550 >>> import astroid 

3551 >>> node = astroid.extract_node('things[1:3]') 

3552 >>> node 

3553 <Subscript l.1 at 0x7f23b2e71f60> 

3554 >>> node.slice 

3555 <Slice l.1 at 0x7f23b2e71e80> 

3556 """ 

3557 

3558 _astroid_fields = ("lower", "upper", "step") 

3559 

3560 lower: NodeNG | None 

3561 """The lower index in the slice.""" 

3562 

3563 upper: NodeNG | None 

3564 """The upper index in the slice.""" 

3565 

3566 step: NodeNG | None 

3567 """The step to take between indexes.""" 

3568 

3569 def postinit( 

3570 self, 

3571 lower: NodeNG | None, 

3572 upper: NodeNG | None, 

3573 step: NodeNG | None, 

3574 ) -> None: 

3575 self.lower = lower 

3576 self.upper = upper 

3577 self.step = step 

3578 

3579 def _wrap_attribute(self, attr): 

3580 """Wrap the empty attributes of the Slice in a Const node.""" 

3581 if not attr: 

3582 const = const_factory(attr) 

3583 const.parent = self 

3584 return const 

3585 return attr 

3586 

3587 @cached_property 

3588 def _proxied(self) -> nodes.ClassDef: 

3589 builtins = AstroidManager().builtins_module 

3590 return builtins.getattr("slice")[0] 

3591 

3592 def pytype(self) -> Literal["builtins.slice"]: 

3593 """Get the name of the type that this node represents. 

3594 

3595 :returns: The name of the type. 

3596 """ 

3597 return "builtins.slice" 

3598 

3599 def display_type(self) -> Literal["Slice"]: 

3600 """A human readable type of this node. 

3601 

3602 :returns: The type of this node. 

3603 """ 

3604 return "Slice" 

3605 

3606 def igetattr( 

3607 self, attrname: str, context: InferenceContext | None = None 

3608 ) -> Iterator[SuccessfulInferenceResult]: 

3609 """Infer the possible values of the given attribute on the slice. 

3610 

3611 :param attrname: The name of the attribute to infer. 

3612 

3613 :returns: The inferred possible values. 

3614 """ 

3615 if attrname == "start": 

3616 yield self._wrap_attribute(self.lower) 

3617 elif attrname == "stop": 

3618 yield self._wrap_attribute(self.upper) 

3619 elif attrname == "step": 

3620 yield self._wrap_attribute(self.step) 

3621 else: 

3622 yield from self.getattr(attrname, context=context) 

3623 

3624 def getattr(self, attrname, context: InferenceContext | None = None): 

3625 return self._proxied.getattr(attrname, context) 

3626 

3627 def get_children(self): 

3628 if self.lower is not None: 

3629 yield self.lower 

3630 

3631 if self.upper is not None: 

3632 yield self.upper 

3633 

3634 if self.step is not None: 

3635 yield self.step 

3636 

3637 def _infer( 

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

3639 ) -> Iterator[Slice]: 

3640 yield self 

3641 

3642 

3643class Starred(_base_nodes.ParentAssignNode): 

3644 """Class representing an :class:`ast.Starred` node. 

3645 

3646 >>> import astroid 

3647 >>> node = astroid.extract_node('*args') 

3648 >>> node 

3649 <Starred l.1 at 0x7f23b2e41978> 

3650 """ 

3651 

3652 _astroid_fields = ("value",) 

3653 _other_fields = ("ctx",) 

3654 

3655 value: NodeNG 

3656 """What is being unpacked.""" 

3657 

3658 def __init__( 

3659 self, 

3660 ctx: Context, 

3661 lineno: int, 

3662 col_offset: int, 

3663 parent: NodeNG, 

3664 *, 

3665 end_lineno: int | None, 

3666 end_col_offset: int | None, 

3667 ) -> None: 

3668 self.ctx = ctx 

3669 """Whether the starred item is assigned to or loaded from.""" 

3670 

3671 super().__init__( 

3672 lineno=lineno, 

3673 col_offset=col_offset, 

3674 end_lineno=end_lineno, 

3675 end_col_offset=end_col_offset, 

3676 parent=parent, 

3677 ) 

3678 

3679 def postinit(self, value: NodeNG) -> None: 

3680 self.value = value 

3681 

3682 assigned_stmts = protocols.starred_assigned_stmts 

3683 """Returns the assigned statement (non inferred) according to the assignment type. 

3684 See astroid/protocols.py for actual implementation. 

3685 """ 

3686 

3687 def get_children(self): 

3688 yield self.value 

3689 

3690 

3691class Subscript(NodeNG): 

3692 """Class representing an :class:`ast.Subscript` node. 

3693 

3694 >>> import astroid 

3695 >>> node = astroid.extract_node('things[1:3]') 

3696 >>> node 

3697 <Subscript l.1 at 0x7f23b2e71f60> 

3698 """ 

3699 

3700 _SUBSCRIPT_SENTINEL = object() 

3701 _astroid_fields = ("value", "slice") 

3702 _other_fields = ("ctx",) 

3703 

3704 value: NodeNG 

3705 """What is being indexed.""" 

3706 

3707 slice: NodeNG 

3708 """The slice being used to lookup.""" 

3709 

3710 def __init__( 

3711 self, 

3712 ctx: Context, 

3713 lineno: int, 

3714 col_offset: int, 

3715 parent: NodeNG, 

3716 *, 

3717 end_lineno: int | None, 

3718 end_col_offset: int | None, 

3719 ) -> None: 

3720 self.ctx = ctx 

3721 """Whether the subscripted item is assigned to or loaded from.""" 

3722 

3723 super().__init__( 

3724 lineno=lineno, 

3725 col_offset=col_offset, 

3726 end_lineno=end_lineno, 

3727 end_col_offset=end_col_offset, 

3728 parent=parent, 

3729 ) 

3730 

3731 # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. 

3732 def postinit(self, value: NodeNG, slice: NodeNG) -> None: 

3733 self.value = value 

3734 self.slice = slice 

3735 

3736 def get_children(self): 

3737 yield self.value 

3738 yield self.slice 

3739 

3740 def _infer_subscript( 

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

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

3743 """Inference for subscripts. 

3744 

3745 We're understanding if the index is a Const 

3746 or a slice, passing the result of inference 

3747 to the value's `getitem` method, which should 

3748 handle each supported index type accordingly. 

3749 """ 

3750 from astroid import helpers # pylint: disable=import-outside-toplevel 

3751 

3752 found_one = False 

3753 for value in self.value.infer(context): 

3754 if isinstance(value, util.UninferableBase): 

3755 yield util.Uninferable 

3756 return None 

3757 for index in self.slice.infer(context): 

3758 if isinstance(index, util.UninferableBase): 

3759 yield util.Uninferable 

3760 return None 

3761 

3762 # Try to deduce the index value. 

3763 index_value = self._SUBSCRIPT_SENTINEL 

3764 if value.__class__ == Instance: 

3765 index_value = index 

3766 elif index.__class__ == Instance: 

3767 instance_as_index = helpers.class_instance_as_index(index) 

3768 if instance_as_index: 

3769 index_value = instance_as_index 

3770 else: 

3771 index_value = index 

3772 

3773 if index_value is self._SUBSCRIPT_SENTINEL: 

3774 raise InferenceError(node=self, context=context) 

3775 

3776 try: 

3777 assigned = value.getitem(index_value, context) 

3778 except ( 

3779 AstroidTypeError, 

3780 AstroidIndexError, 

3781 AstroidValueError, 

3782 AttributeInferenceError, 

3783 AttributeError, 

3784 ) as exc: 

3785 raise InferenceError(node=self, context=context) from exc 

3786 

3787 # Prevent inferring if the inferred subscript 

3788 # is the same as the original subscripted object. 

3789 if self is assigned or isinstance(assigned, util.UninferableBase): 

3790 yield util.Uninferable 

3791 return None 

3792 yield from assigned.infer(context) 

3793 found_one = True 

3794 

3795 if found_one: 

3796 return InferenceErrorInfo(node=self, context=context) 

3797 return None 

3798 

3799 @decorators.raise_if_nothing_inferred 

3800 @decorators.path_wrapper 

3801 def _infer(self, context: InferenceContext | None = None, **kwargs: Any): 

3802 return self._infer_subscript(context, **kwargs) 

3803 

3804 @decorators.raise_if_nothing_inferred 

3805 def infer_lhs(self, context: InferenceContext | None = None, **kwargs: Any): 

3806 return self._infer_subscript(context, **kwargs) 

3807 

3808 

3809class Try(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement): 

3810 """Class representing a :class:`ast.Try` node. 

3811 

3812 >>> import astroid 

3813 >>> node = astroid.extract_node(''' 

3814 try: 

3815 do_something() 

3816 except Exception as error: 

3817 print("Error!") 

3818 finally: 

3819 print("Cleanup!") 

3820 ''') 

3821 >>> node 

3822 <Try l.2 at 0x7f23b2e41d68> 

3823 """ 

3824 

3825 _astroid_fields = ("body", "handlers", "orelse", "finalbody") 

3826 _multi_line_block_fields = ("body", "handlers", "orelse", "finalbody") 

3827 

3828 def __init__( 

3829 self, 

3830 *, 

3831 lineno: int, 

3832 col_offset: int, 

3833 end_lineno: int, 

3834 end_col_offset: int, 

3835 parent: NodeNG, 

3836 ) -> None: 

3837 """ 

3838 :param lineno: The line that this node appears on in the source code. 

3839 

3840 :param col_offset: The column that this node appears on in the 

3841 source code. 

3842 

3843 :param parent: The parent node in the syntax tree. 

3844 

3845 :param end_lineno: The last line this node appears on in the source code. 

3846 

3847 :param end_col_offset: The end column this node appears on in the 

3848 source code. Note: This is after the last symbol. 

3849 """ 

3850 self.body: list[NodeNG] = [] 

3851 """The contents of the block to catch exceptions from.""" 

3852 

3853 self.handlers: list[ExceptHandler] = [] 

3854 """The exception handlers.""" 

3855 

3856 self.orelse: list[NodeNG] = [] 

3857 """The contents of the ``else`` block.""" 

3858 

3859 self.finalbody: list[NodeNG] = [] 

3860 """The contents of the ``finally`` block.""" 

3861 

3862 super().__init__( 

3863 lineno=lineno, 

3864 col_offset=col_offset, 

3865 end_lineno=end_lineno, 

3866 end_col_offset=end_col_offset, 

3867 parent=parent, 

3868 ) 

3869 

3870 def postinit( 

3871 self, 

3872 *, 

3873 body: list[NodeNG], 

3874 handlers: list[ExceptHandler], 

3875 orelse: list[NodeNG], 

3876 finalbody: list[NodeNG], 

3877 ) -> None: 

3878 """Do some setup after initialisation. 

3879 

3880 :param body: The contents of the block to catch exceptions from. 

3881 

3882 :param handlers: The exception handlers. 

3883 

3884 :param orelse: The contents of the ``else`` block. 

3885 

3886 :param finalbody: The contents of the ``finally`` block. 

3887 """ 

3888 self.body = body 

3889 self.handlers = handlers 

3890 self.orelse = orelse 

3891 self.finalbody = finalbody 

3892 

3893 def _infer_name(self, frame, name): 

3894 return name 

3895 

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

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

3898 if lineno == self.fromlineno: 

3899 return lineno, lineno 

3900 if self.body and self.body[0].fromlineno <= lineno <= self.body[-1].tolineno: 

3901 # Inside try body - return from lineno till end of try body 

3902 return lineno, self.body[-1].tolineno 

3903 for exhandler in self.handlers: 

3904 if exhandler.type and lineno == exhandler.type.fromlineno: 

3905 return lineno, lineno 

3906 if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno: 

3907 return lineno, exhandler.body[-1].tolineno 

3908 if self.orelse: 

3909 if self.orelse[0].fromlineno - 1 == lineno: 

3910 return lineno, lineno 

3911 if self.orelse[0].fromlineno <= lineno <= self.orelse[-1].tolineno: 

3912 return lineno, self.orelse[-1].tolineno 

3913 if self.finalbody: 

3914 if self.finalbody[0].fromlineno - 1 == lineno: 

3915 return lineno, lineno 

3916 if self.finalbody[0].fromlineno <= lineno <= self.finalbody[-1].tolineno: 

3917 return lineno, self.finalbody[-1].tolineno 

3918 return lineno, self.tolineno 

3919 

3920 def get_children(self): 

3921 yield from self.body 

3922 yield from self.handlers 

3923 yield from self.orelse 

3924 yield from self.finalbody 

3925 

3926 

3927class TryStar(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement): 

3928 """Class representing an :class:`ast.TryStar` node.""" 

3929 

3930 _astroid_fields = ("body", "handlers", "orelse", "finalbody") 

3931 _multi_line_block_fields = ("body", "handlers", "orelse", "finalbody") 

3932 

3933 def __init__( 

3934 self, 

3935 *, 

3936 lineno: int | None = None, 

3937 col_offset: int | None = None, 

3938 end_lineno: int | None = None, 

3939 end_col_offset: int | None = None, 

3940 parent: NodeNG | None = None, 

3941 ) -> None: 

3942 """ 

3943 :param lineno: The line that this node appears on in the source code. 

3944 :param col_offset: The column that this node appears on in the 

3945 source code. 

3946 :param parent: The parent node in the syntax tree. 

3947 :param end_lineno: The last line this node appears on in the source code. 

3948 :param end_col_offset: The end column this node appears on in the 

3949 source code. Note: This is after the last symbol. 

3950 """ 

3951 self.body: list[NodeNG] = [] 

3952 """The contents of the block to catch exceptions from.""" 

3953 

3954 self.handlers: list[ExceptHandler] = [] 

3955 """The exception handlers.""" 

3956 

3957 self.orelse: list[NodeNG] = [] 

3958 """The contents of the ``else`` block.""" 

3959 

3960 self.finalbody: list[NodeNG] = [] 

3961 """The contents of the ``finally`` block.""" 

3962 

3963 super().__init__( 

3964 lineno=lineno, 

3965 col_offset=col_offset, 

3966 end_lineno=end_lineno, 

3967 end_col_offset=end_col_offset, 

3968 parent=parent, 

3969 ) 

3970 

3971 def postinit( 

3972 self, 

3973 *, 

3974 body: list[NodeNG] | None = None, 

3975 handlers: list[ExceptHandler] | None = None, 

3976 orelse: list[NodeNG] | None = None, 

3977 finalbody: list[NodeNG] | None = None, 

3978 ) -> None: 

3979 """Do some setup after initialisation. 

3980 :param body: The contents of the block to catch exceptions from. 

3981 :param handlers: The exception handlers. 

3982 :param orelse: The contents of the ``else`` block. 

3983 :param finalbody: The contents of the ``finally`` block. 

3984 """ 

3985 if body: 

3986 self.body = body 

3987 if handlers: 

3988 self.handlers = handlers 

3989 if orelse: 

3990 self.orelse = orelse 

3991 if finalbody: 

3992 self.finalbody = finalbody 

3993 

3994 def _infer_name(self, frame, name): 

3995 return name 

3996 

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

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

3999 if lineno == self.fromlineno: 

4000 return lineno, lineno 

4001 if self.body and self.body[0].fromlineno <= lineno <= self.body[-1].tolineno: 

4002 # Inside try body - return from lineno till end of try body 

4003 return lineno, self.body[-1].tolineno 

4004 for exhandler in self.handlers: 

4005 if exhandler.type and lineno == exhandler.type.fromlineno: 

4006 return lineno, lineno 

4007 if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno: 

4008 return lineno, exhandler.body[-1].tolineno 

4009 if self.orelse: 

4010 if self.orelse[0].fromlineno - 1 == lineno: 

4011 return lineno, lineno 

4012 if self.orelse[0].fromlineno <= lineno <= self.orelse[-1].tolineno: 

4013 return lineno, self.orelse[-1].tolineno 

4014 if self.finalbody: 

4015 if self.finalbody[0].fromlineno - 1 == lineno: 

4016 return lineno, lineno 

4017 if self.finalbody[0].fromlineno <= lineno <= self.finalbody[-1].tolineno: 

4018 return lineno, self.finalbody[-1].tolineno 

4019 return lineno, self.tolineno 

4020 

4021 def get_children(self): 

4022 yield from self.body 

4023 yield from self.handlers 

4024 yield from self.orelse 

4025 yield from self.finalbody 

4026 

4027 

4028class Tuple(BaseContainer): 

4029 """Class representing an :class:`ast.Tuple` node. 

4030 

4031 >>> import astroid 

4032 >>> node = astroid.extract_node('(1, 2, 3)') 

4033 >>> node 

4034 <Tuple.tuple l.1 at 0x7f23b2e41780> 

4035 """ 

4036 

4037 _other_fields = ("ctx",) 

4038 

4039 def __init__( 

4040 self, 

4041 ctx: Context | None = None, 

4042 lineno: int | None = None, 

4043 col_offset: int | None = None, 

4044 parent: NodeNG | None = None, 

4045 *, 

4046 end_lineno: int | None = None, 

4047 end_col_offset: int | None = None, 

4048 ) -> None: 

4049 """ 

4050 :param ctx: Whether the tuple is assigned to or loaded from. 

4051 

4052 :param lineno: The line that this node appears on in the source code. 

4053 

4054 :param col_offset: The column that this node appears on in the 

4055 source code. 

4056 

4057 :param parent: The parent node in the syntax tree. 

4058 

4059 :param end_lineno: The last line this node appears on in the source code. 

4060 

4061 :param end_col_offset: The end column this node appears on in the 

4062 source code. Note: This is after the last symbol. 

4063 """ 

4064 self.ctx: Context | None = ctx 

4065 """Whether the tuple is assigned to or loaded from.""" 

4066 

4067 super().__init__( 

4068 lineno=lineno, 

4069 col_offset=col_offset, 

4070 end_lineno=end_lineno, 

4071 end_col_offset=end_col_offset, 

4072 parent=parent, 

4073 ) 

4074 

4075 assigned_stmts = protocols.sequence_assigned_stmts 

4076 """Returns the assigned statement (non inferred) according to the assignment type. 

4077 See astroid/protocols.py for actual implementation. 

4078 """ 

4079 

4080 infer_unary_op = protocols.tuple_infer_unary_op 

4081 infer_binary_op = protocols.tl_infer_binary_op 

4082 

4083 def pytype(self) -> Literal["builtins.tuple"]: 

4084 """Get the name of the type that this node represents. 

4085 

4086 :returns: The name of the type. 

4087 """ 

4088 return "builtins.tuple" 

4089 

4090 def getitem(self, index, context: InferenceContext | None = None): 

4091 """Get an item from this node. 

4092 

4093 :param index: The node to use as a subscript index. 

4094 :type index: Const or Slice 

4095 """ 

4096 return _container_getitem(self, self.elts, index, context=context) 

4097 

4098 

4099class TypeAlias(_base_nodes.AssignTypeNode, _base_nodes.Statement): 

4100 """Class representing a :class:`ast.TypeAlias` node. 

4101 

4102 >>> import astroid 

4103 >>> node = astroid.extract_node('type Point = tuple[float, float]') 

4104 >>> node 

4105 <TypeAlias l.1 at 0x7f23b2e4e198> 

4106 """ 

4107 

4108 _astroid_fields = ("name", "type_params", "value") 

4109 

4110 name: AssignName 

4111 type_params: list[TypeVar | ParamSpec | TypeVarTuple] 

4112 value: NodeNG 

4113 

4114 def __init__( 

4115 self, 

4116 lineno: int, 

4117 col_offset: int, 

4118 parent: NodeNG, 

4119 *, 

4120 end_lineno: int, 

4121 end_col_offset: int, 

4122 ) -> None: 

4123 super().__init__( 

4124 lineno=lineno, 

4125 col_offset=col_offset, 

4126 end_lineno=end_lineno, 

4127 end_col_offset=end_col_offset, 

4128 parent=parent, 

4129 ) 

4130 

4131 def postinit( 

4132 self, 

4133 *, 

4134 name: AssignName, 

4135 type_params: list[TypeVar | ParamSpec | TypeVarTuple], 

4136 value: NodeNG, 

4137 ) -> None: 

4138 self.name = name 

4139 self.type_params = type_params 

4140 self.value = value 

4141 

4142 def _infer( 

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

4144 ) -> Iterator[TypeAlias]: 

4145 yield self 

4146 

4147 assigned_stmts: ClassVar[ 

4148 Callable[ 

4149 [ 

4150 TypeAlias, 

4151 AssignName, 

4152 InferenceContext | None, 

4153 None, 

4154 ], 

4155 Generator[NodeNG], 

4156 ] 

4157 ] = protocols.assign_assigned_stmts 

4158 

4159 

4160class TypeVar(_base_nodes.AssignTypeNode): 

4161 """Class representing a :class:`ast.TypeVar` node. 

4162 

4163 >>> import astroid 

4164 >>> node = astroid.extract_node('type Point[T] = tuple[float, float]') 

4165 >>> node.type_params[0] 

4166 <TypeVar l.1 at 0x7f23b2e4e198> 

4167 """ 

4168 

4169 _astroid_fields = ("name", "bound", "default_value") 

4170 name: AssignName 

4171 bound: NodeNG | None 

4172 default_value: NodeNG | None 

4173 

4174 def __init__( 

4175 self, 

4176 lineno: int, 

4177 col_offset: int, 

4178 parent: NodeNG, 

4179 *, 

4180 end_lineno: int, 

4181 end_col_offset: int, 

4182 ) -> None: 

4183 super().__init__( 

4184 lineno=lineno, 

4185 col_offset=col_offset, 

4186 end_lineno=end_lineno, 

4187 end_col_offset=end_col_offset, 

4188 parent=parent, 

4189 ) 

4190 

4191 def postinit( 

4192 self, 

4193 *, 

4194 name: AssignName, 

4195 bound: NodeNG | None, 

4196 default_value: NodeNG | None = None, 

4197 ) -> None: 

4198 self.name = name 

4199 self.bound = bound 

4200 self.default_value = default_value 

4201 

4202 def _infer( 

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

4204 ) -> Iterator[TypeVar]: 

4205 yield self 

4206 

4207 assigned_stmts = protocols.generic_type_assigned_stmts 

4208 """Returns the assigned statement (non inferred) according to the assignment type. 

4209 See astroid/protocols.py for actual implementation. 

4210 """ 

4211 

4212 

4213class TypeVarTuple(_base_nodes.AssignTypeNode): 

4214 """Class representing a :class:`ast.TypeVarTuple` node. 

4215 

4216 >>> import astroid 

4217 >>> node = astroid.extract_node('type Alias[*Ts] = tuple[*Ts]') 

4218 >>> node.type_params[0] 

4219 <TypeVarTuple l.1 at 0x7f23b2e4e198> 

4220 """ 

4221 

4222 _astroid_fields = ("name", "default_value") 

4223 name: AssignName 

4224 default_value: NodeNG | None 

4225 

4226 def __init__( 

4227 self, 

4228 lineno: int, 

4229 col_offset: int, 

4230 parent: NodeNG, 

4231 *, 

4232 end_lineno: int, 

4233 end_col_offset: int, 

4234 ) -> None: 

4235 super().__init__( 

4236 lineno=lineno, 

4237 col_offset=col_offset, 

4238 end_lineno=end_lineno, 

4239 end_col_offset=end_col_offset, 

4240 parent=parent, 

4241 ) 

4242 

4243 def postinit( 

4244 self, *, name: AssignName, default_value: NodeNG | None = None 

4245 ) -> None: 

4246 self.name = name 

4247 self.default_value = default_value 

4248 

4249 def _infer( 

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

4251 ) -> Iterator[TypeVarTuple]: 

4252 yield self 

4253 

4254 assigned_stmts = protocols.generic_type_assigned_stmts 

4255 """Returns the assigned statement (non inferred) according to the assignment type. 

4256 See astroid/protocols.py for actual implementation. 

4257 """ 

4258 

4259 

4260UNARY_OP_METHOD = { 

4261 "+": "__pos__", 

4262 "-": "__neg__", 

4263 "~": "__invert__", 

4264 "not": None, # XXX not '__nonzero__' 

4265} 

4266 

4267 

4268class UnaryOp(_base_nodes.OperatorNode): 

4269 """Class representing an :class:`ast.UnaryOp` node. 

4270 

4271 >>> import astroid 

4272 >>> node = astroid.extract_node('-5') 

4273 >>> node 

4274 <UnaryOp l.1 at 0x7f23b2e4e198> 

4275 """ 

4276 

4277 _astroid_fields = ("operand",) 

4278 _other_fields = ("op",) 

4279 

4280 operand: NodeNG 

4281 """What the unary operator is applied to.""" 

4282 

4283 def __init__( 

4284 self, 

4285 op: str, 

4286 lineno: int, 

4287 col_offset: int, 

4288 parent: NodeNG, 

4289 *, 

4290 end_lineno: int | None, 

4291 end_col_offset: int | None, 

4292 ) -> None: 

4293 self.op = op 

4294 """The operator.""" 

4295 

4296 super().__init__( 

4297 lineno=lineno, 

4298 col_offset=col_offset, 

4299 end_lineno=end_lineno, 

4300 end_col_offset=end_col_offset, 

4301 parent=parent, 

4302 ) 

4303 

4304 def postinit(self, operand: NodeNG) -> None: 

4305 self.operand = operand 

4306 

4307 def type_errors( 

4308 self, context: InferenceContext | None = None 

4309 ) -> list[util.BadUnaryOperationMessage]: 

4310 """Get a list of type errors which can occur during inference. 

4311 

4312 Each TypeError is represented by a :class:`BadUnaryOperationMessage`, 

4313 which holds the original exception. 

4314 

4315 If any inferred result is uninferable, an empty list is returned. 

4316 """ 

4317 bad = [] 

4318 try: 

4319 for result in self._infer_unaryop(context=context): 

4320 if result is util.Uninferable: 

4321 raise InferenceError 

4322 if isinstance(result, util.BadUnaryOperationMessage): 

4323 bad.append(result) 

4324 except InferenceError: 

4325 return [] 

4326 return bad 

4327 

4328 def get_children(self): 

4329 yield self.operand 

4330 

4331 def op_precedence(self) -> int: 

4332 if self.op == "not": 

4333 return OP_PRECEDENCE[self.op] 

4334 

4335 return super().op_precedence() 

4336 

4337 def _infer_unaryop( 

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

4339 ) -> Generator[ 

4340 InferenceResult | util.BadUnaryOperationMessage, None, InferenceErrorInfo 

4341 ]: 

4342 """Infer what an UnaryOp should return when evaluated.""" 

4343 from astroid.nodes import ClassDef # pylint: disable=import-outside-toplevel 

4344 

4345 for operand in self.operand.infer(context): 

4346 try: 

4347 yield operand.infer_unary_op(self.op) 

4348 except TypeError as exc: 

4349 # The operand doesn't support this operation. 

4350 yield util.BadUnaryOperationMessage(operand, self.op, exc) 

4351 except AttributeError as exc: 

4352 meth = UNARY_OP_METHOD[self.op] 

4353 if meth is None: 

4354 # `not node`. Determine node's boolean 

4355 # value and negate its result, unless it is 

4356 # Uninferable, which will be returned as is. 

4357 bool_value = operand.bool_value() 

4358 if not isinstance(bool_value, util.UninferableBase): 

4359 yield const_factory(not bool_value) 

4360 else: 

4361 yield util.Uninferable 

4362 else: 

4363 if not isinstance(operand, (Instance, ClassDef)): 

4364 # The operation was used on something which 

4365 # doesn't support it. 

4366 yield util.BadUnaryOperationMessage(operand, self.op, exc) 

4367 continue 

4368 

4369 try: 

4370 try: 

4371 methods = dunder_lookup.lookup(operand, meth) 

4372 except AttributeInferenceError: 

4373 yield util.BadUnaryOperationMessage(operand, self.op, exc) 

4374 continue 

4375 

4376 meth = methods[0] 

4377 inferred = next(meth.infer(context=context), None) 

4378 if ( 

4379 isinstance(inferred, util.UninferableBase) 

4380 or not inferred.callable() 

4381 ): 

4382 continue 

4383 

4384 context = copy_context(context) 

4385 context.boundnode = operand 

4386 context.callcontext = CallContext(args=[], callee=inferred) 

4387 

4388 call_results = inferred.infer_call_result(self, context=context) 

4389 result = next(call_results, None) 

4390 if result is None: 

4391 # Failed to infer, return the same type. 

4392 yield operand 

4393 else: 

4394 yield result 

4395 except AttributeInferenceError as inner_exc: 

4396 # The unary operation special method was not found. 

4397 yield util.BadUnaryOperationMessage(operand, self.op, inner_exc) 

4398 except InferenceError: 

4399 yield util.Uninferable 

4400 

4401 @decorators.raise_if_nothing_inferred 

4402 @decorators.path_wrapper 

4403 def _infer( 

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

4405 ) -> Generator[InferenceResult, None, InferenceErrorInfo]: 

4406 """Infer what an UnaryOp should return when evaluated.""" 

4407 yield from self._filter_operation_errors( 

4408 self._infer_unaryop, context, util.BadUnaryOperationMessage 

4409 ) 

4410 return InferenceErrorInfo(node=self, context=context) 

4411 

4412 

4413class While(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement): 

4414 """Class representing an :class:`ast.While` node. 

4415 

4416 >>> import astroid 

4417 >>> node = astroid.extract_node(''' 

4418 while condition(): 

4419 print("True") 

4420 ''') 

4421 >>> node 

4422 <While l.2 at 0x7f23b2e4e390> 

4423 """ 

4424 

4425 _astroid_fields = ("test", "body", "orelse") 

4426 _multi_line_block_fields = ("body", "orelse") 

4427 

4428 test: NodeNG 

4429 """The condition that the loop tests.""" 

4430 

4431 body: list[NodeNG] 

4432 """The contents of the loop.""" 

4433 

4434 orelse: list[NodeNG] 

4435 """The contents of the ``else`` block.""" 

4436 

4437 def postinit( 

4438 self, 

4439 test: NodeNG, 

4440 body: list[NodeNG], 

4441 orelse: list[NodeNG], 

4442 ) -> None: 

4443 self.test = test 

4444 self.body = body 

4445 self.orelse = orelse 

4446 

4447 @cached_property 

4448 def blockstart_tolineno(self): 

4449 """The line on which the beginning of this block ends. 

4450 

4451 :type: int 

4452 """ 

4453 return self.test.tolineno 

4454 

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

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

4457 

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

4459 

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

4461 starting at the given line number. 

4462 """ 

4463 return self._elsed_block_range(lineno, self.orelse) 

4464 

4465 def get_children(self): 

4466 yield self.test 

4467 

4468 yield from self.body 

4469 yield from self.orelse 

4470 

4471 def _get_yield_nodes_skip_functions(self): 

4472 """A While node can contain a Yield node in the test""" 

4473 yield from self.test._get_yield_nodes_skip_functions() 

4474 yield from super()._get_yield_nodes_skip_functions() 

4475 

4476 def _get_yield_nodes_skip_lambdas(self): 

4477 """A While node can contain a Yield node in the test""" 

4478 yield from self.test._get_yield_nodes_skip_lambdas() 

4479 yield from super()._get_yield_nodes_skip_lambdas() 

4480 

4481 

4482class With( 

4483 _base_nodes.MultiLineWithElseBlockNode, 

4484 _base_nodes.AssignTypeNode, 

4485 _base_nodes.Statement, 

4486): 

4487 """Class representing an :class:`ast.With` node. 

4488 

4489 >>> import astroid 

4490 >>> node = astroid.extract_node(''' 

4491 with open(file_path) as file_: 

4492 print(file_.read()) 

4493 ''') 

4494 >>> node 

4495 <With l.2 at 0x7f23b2e4e710> 

4496 """ 

4497 

4498 _astroid_fields = ("items", "body") 

4499 _other_other_fields = ("type_annotation",) 

4500 _multi_line_block_fields = ("body",) 

4501 

4502 def __init__( 

4503 self, 

4504 lineno: int | None = None, 

4505 col_offset: int | None = None, 

4506 parent: NodeNG | None = None, 

4507 *, 

4508 end_lineno: int | None = None, 

4509 end_col_offset: int | None = None, 

4510 ) -> None: 

4511 """ 

4512 :param lineno: The line that this node appears on in the source code. 

4513 

4514 :param col_offset: The column that this node appears on in the 

4515 source code. 

4516 

4517 :param parent: The parent node in the syntax tree. 

4518 

4519 :param end_lineno: The last line this node appears on in the source code. 

4520 

4521 :param end_col_offset: The end column this node appears on in the 

4522 source code. Note: This is after the last symbol. 

4523 """ 

4524 self.items: list[tuple[NodeNG, NodeNG | None]] = [] 

4525 """The pairs of context managers and the names they are assigned to.""" 

4526 

4527 self.body: list[NodeNG] = [] 

4528 """The contents of the ``with`` block.""" 

4529 

4530 self.type_annotation: NodeNG | None = None # can be None 

4531 """If present, this will contain the type annotation passed by a type comment""" 

4532 

4533 super().__init__( 

4534 lineno=lineno, 

4535 col_offset=col_offset, 

4536 end_lineno=end_lineno, 

4537 end_col_offset=end_col_offset, 

4538 parent=parent, 

4539 ) 

4540 

4541 def postinit( 

4542 self, 

4543 items: list[tuple[NodeNG, NodeNG | None]] | None = None, 

4544 body: list[NodeNG] | None = None, 

4545 type_annotation: NodeNG | None = None, 

4546 ) -> None: 

4547 """Do some setup after initialisation. 

4548 

4549 :param items: The pairs of context managers and the names 

4550 they are assigned to. 

4551 

4552 :param body: The contents of the ``with`` block. 

4553 """ 

4554 if items is not None: 

4555 self.items = items 

4556 if body is not None: 

4557 self.body = body 

4558 self.type_annotation = type_annotation 

4559 

4560 assigned_stmts = protocols.with_assigned_stmts 

4561 """Returns the assigned statement (non inferred) according to the assignment type. 

4562 See astroid/protocols.py for actual implementation. 

4563 """ 

4564 

4565 @cached_property 

4566 def blockstart_tolineno(self): 

4567 """The line on which the beginning of this block ends. 

4568 

4569 :type: int 

4570 """ 

4571 return self.items[-1][0].tolineno 

4572 

4573 def get_children(self): 

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

4575 

4576 :returns: The children. 

4577 :rtype: iterable(NodeNG) 

4578 """ 

4579 for expr, var in self.items: 

4580 yield expr 

4581 if var: 

4582 yield var 

4583 yield from self.body 

4584 

4585 

4586class AsyncWith(With): 

4587 """Asynchronous ``with`` built with the ``async`` keyword.""" 

4588 

4589 

4590class Yield(NodeNG): 

4591 """Class representing an :class:`ast.Yield` node. 

4592 

4593 >>> import astroid 

4594 >>> node = astroid.extract_node('yield True') 

4595 >>> node 

4596 <Yield l.1 at 0x7f23b2e4e5f8> 

4597 """ 

4598 

4599 _astroid_fields = ("value",) 

4600 

4601 value: NodeNG | None 

4602 """The value to yield.""" 

4603 

4604 def postinit(self, value: NodeNG | None) -> None: 

4605 self.value = value 

4606 

4607 def get_children(self): 

4608 if self.value is not None: 

4609 yield self.value 

4610 

4611 def _get_yield_nodes_skip_functions(self): 

4612 yield self 

4613 

4614 def _get_yield_nodes_skip_lambdas(self): 

4615 yield self 

4616 

4617 

4618class YieldFrom(Yield): # TODO value is required, not optional 

4619 """Class representing an :class:`ast.YieldFrom` node.""" 

4620 

4621 

4622class DictUnpack(_base_nodes.NoChildrenNode): 

4623 """Represents the unpacking of dicts into dicts using :pep:`448`.""" 

4624 

4625 

4626class FormattedValue(NodeNG): 

4627 """Class representing an :class:`ast.FormattedValue` node. 

4628 

4629 Represents a :pep:`498` format string. 

4630 

4631 >>> import astroid 

4632 >>> node = astroid.extract_node('f"Format {type_}"') 

4633 >>> node 

4634 <JoinedStr l.1 at 0x7f23b2e4ed30> 

4635 >>> node.values 

4636 [<Const.str l.1 at 0x7f23b2e4eda0>, <FormattedValue l.1 at 0x7f23b2e4edd8>] 

4637 """ 

4638 

4639 _astroid_fields = ("value", "format_spec") 

4640 _other_fields = ("conversion",) 

4641 

4642 def __init__( 

4643 self, 

4644 lineno: int | None = None, 

4645 col_offset: int | None = None, 

4646 parent: NodeNG | None = None, 

4647 *, 

4648 end_lineno: int | None = None, 

4649 end_col_offset: int | None = None, 

4650 ) -> None: 

4651 """ 

4652 :param lineno: The line that this node appears on in the source code. 

4653 

4654 :param col_offset: The column that this node appears on in the 

4655 source code. 

4656 

4657 :param parent: The parent node in the syntax tree. 

4658 

4659 :param end_lineno: The last line this node appears on in the source code. 

4660 

4661 :param end_col_offset: The end column this node appears on in the 

4662 source code. Note: This is after the last symbol. 

4663 """ 

4664 self.value: NodeNG 

4665 """The value to be formatted into the string.""" 

4666 

4667 self.conversion: int 

4668 """The type of formatting to be applied to the value. 

4669 

4670 .. seealso:: 

4671 :class:`ast.FormattedValue` 

4672 """ 

4673 

4674 self.format_spec: JoinedStr | None = None 

4675 """The formatting to be applied to the value. 

4676 

4677 .. seealso:: 

4678 :class:`ast.FormattedValue` 

4679 """ 

4680 

4681 super().__init__( 

4682 lineno=lineno, 

4683 col_offset=col_offset, 

4684 end_lineno=end_lineno, 

4685 end_col_offset=end_col_offset, 

4686 parent=parent, 

4687 ) 

4688 

4689 def postinit( 

4690 self, 

4691 *, 

4692 value: NodeNG, 

4693 conversion: int, 

4694 format_spec: JoinedStr | None = None, 

4695 ) -> None: 

4696 """Do some setup after initialisation. 

4697 

4698 :param value: The value to be formatted into the string. 

4699 

4700 :param conversion: The type of formatting to be applied to the value. 

4701 

4702 :param format_spec: The formatting to be applied to the value. 

4703 :type format_spec: JoinedStr or None 

4704 """ 

4705 self.value = value 

4706 self.conversion = conversion 

4707 self.format_spec = format_spec 

4708 

4709 def get_children(self): 

4710 yield self.value 

4711 

4712 if self.format_spec is not None: 

4713 yield self.format_spec 

4714 

4715 def _infer( 

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

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

4718 format_specs = Const("") if self.format_spec is None else self.format_spec 

4719 uninferable_already_generated = False 

4720 for format_spec in format_specs.infer(context, **kwargs): 

4721 if not isinstance(format_spec, Const): 

4722 if not uninferable_already_generated: 

4723 yield util.Uninferable 

4724 uninferable_already_generated = True 

4725 continue 

4726 for value in self.value.infer(context, **kwargs): 

4727 value_to_format = value 

4728 if isinstance(value, Const): 

4729 value_to_format = value.value 

4730 try: 

4731 formatted = format(value_to_format, format_spec.value) 

4732 yield Const( 

4733 formatted, 

4734 lineno=self.lineno, 

4735 col_offset=self.col_offset, 

4736 end_lineno=self.end_lineno, 

4737 end_col_offset=self.end_col_offset, 

4738 ) 

4739 continue 

4740 except (ValueError, TypeError): 

4741 # happens when format_spec.value is invalid 

4742 yield util.Uninferable 

4743 uninferable_already_generated = True 

4744 continue 

4745 

4746 

4747UNINFERABLE_VALUE = "{Uninferable}" 

4748 

4749 

4750class JoinedStr(NodeNG): 

4751 """Represents a list of string expressions to be joined. 

4752 

4753 >>> import astroid 

4754 >>> node = astroid.extract_node('f"Format {type_}"') 

4755 >>> node 

4756 <JoinedStr l.1 at 0x7f23b2e4ed30> 

4757 """ 

4758 

4759 _astroid_fields = ("values",) 

4760 

4761 def __init__( 

4762 self, 

4763 lineno: int | None = None, 

4764 col_offset: int | None = None, 

4765 parent: NodeNG | None = None, 

4766 *, 

4767 end_lineno: int | None = None, 

4768 end_col_offset: int | None = None, 

4769 ) -> None: 

4770 """ 

4771 :param lineno: The line that this node appears on in the source code. 

4772 

4773 :param col_offset: The column that this node appears on in the 

4774 source code. 

4775 

4776 :param parent: The parent node in the syntax tree. 

4777 

4778 :param end_lineno: The last line this node appears on in the source code. 

4779 

4780 :param end_col_offset: The end column this node appears on in the 

4781 source code. Note: This is after the last symbol. 

4782 """ 

4783 self.values: list[NodeNG] = [] 

4784 """The string expressions to be joined. 

4785 

4786 :type: list(FormattedValue or Const) 

4787 """ 

4788 

4789 super().__init__( 

4790 lineno=lineno, 

4791 col_offset=col_offset, 

4792 end_lineno=end_lineno, 

4793 end_col_offset=end_col_offset, 

4794 parent=parent, 

4795 ) 

4796 

4797 def postinit(self, values: list[NodeNG] | None = None) -> None: 

4798 """Do some setup after initialisation. 

4799 

4800 :param value: The string expressions to be joined. 

4801 

4802 :type: list(FormattedValue or Const) 

4803 """ 

4804 if values is not None: 

4805 self.values = values 

4806 

4807 def get_children(self): 

4808 yield from self.values 

4809 

4810 def _infer( 

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

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

4813 if self.values: 

4814 yield from self._infer_with_values(context) 

4815 else: 

4816 yield Const("") 

4817 

4818 def _infer_with_values( 

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

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

4821 uninferable_already_generated = False 

4822 for inferred in self._infer_from_values(self.values, context): 

4823 failed = inferred is util.Uninferable or ( 

4824 isinstance(inferred, Const) and UNINFERABLE_VALUE in inferred.value 

4825 ) 

4826 if failed: 

4827 if not uninferable_already_generated: 

4828 uninferable_already_generated = True 

4829 yield util.Uninferable 

4830 continue 

4831 yield inferred 

4832 

4833 @classmethod 

4834 def _infer_from_values( 

4835 cls, nodes: list[NodeNG], context: InferenceContext | None = None, **kwargs: Any 

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

4837 if not nodes: 

4838 return 

4839 if len(nodes) == 1: 

4840 for node in cls._safe_infer_from_node(nodes[0], context, **kwargs): 

4841 if isinstance(node, Const): 

4842 yield node 

4843 continue 

4844 yield Const(UNINFERABLE_VALUE) 

4845 return 

4846 for prefix in cls._safe_infer_from_node(nodes[0], context, **kwargs): 

4847 for suffix in cls._infer_from_values(nodes[1:], context, **kwargs): 

4848 result = "" 

4849 for node in (prefix, suffix): 

4850 if isinstance(node, Const): 

4851 result += str(node.value) 

4852 continue 

4853 result += UNINFERABLE_VALUE 

4854 yield Const(result) 

4855 

4856 @classmethod 

4857 def _safe_infer_from_node( 

4858 cls, node: NodeNG, context: InferenceContext | None = None, **kwargs: Any 

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

4860 try: 

4861 yield from node._infer(context, **kwargs) 

4862 except InferenceError: 

4863 yield util.Uninferable 

4864 

4865 

4866class NamedExpr(_base_nodes.AssignTypeNode): 

4867 """Represents the assignment from the assignment expression 

4868 

4869 >>> import astroid 

4870 >>> module = astroid.parse('if a := 1: pass') 

4871 >>> module.body[0].test 

4872 <NamedExpr l.1 at 0x7f23b2e4ed30> 

4873 """ 

4874 

4875 _astroid_fields = ("target", "value") 

4876 

4877 optional_assign = True 

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

4879 

4880 Since NamedExpr are not always called they do not always assign.""" 

4881 

4882 def __init__( 

4883 self, 

4884 lineno: int | None = None, 

4885 col_offset: int | None = None, 

4886 parent: NodeNG | None = None, 

4887 *, 

4888 end_lineno: int | None = None, 

4889 end_col_offset: int | None = None, 

4890 ) -> None: 

4891 """ 

4892 :param lineno: The line that this node appears on in the source code. 

4893 

4894 :param col_offset: The column that this node appears on in the 

4895 source code. 

4896 

4897 :param parent: The parent node in the syntax tree. 

4898 

4899 :param end_lineno: The last line this node appears on in the source code. 

4900 

4901 :param end_col_offset: The end column this node appears on in the 

4902 source code. Note: This is after the last symbol. 

4903 """ 

4904 self.target: NodeNG 

4905 """The assignment target 

4906 

4907 :type: Name 

4908 """ 

4909 

4910 self.value: NodeNG 

4911 """The value that gets assigned in the expression""" 

4912 

4913 super().__init__( 

4914 lineno=lineno, 

4915 col_offset=col_offset, 

4916 end_lineno=end_lineno, 

4917 end_col_offset=end_col_offset, 

4918 parent=parent, 

4919 ) 

4920 

4921 def postinit(self, target: NodeNG, value: NodeNG) -> None: 

4922 self.target = target 

4923 self.value = value 

4924 

4925 assigned_stmts = protocols.named_expr_assigned_stmts 

4926 """Returns the assigned statement (non inferred) according to the assignment type. 

4927 See astroid/protocols.py for actual implementation. 

4928 """ 

4929 

4930 def frame(self) -> nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda: 

4931 """The first parent frame node. 

4932 

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

4934 or :class:`ClassDef`. 

4935 

4936 :returns: The first parent frame node. 

4937 """ 

4938 if not self.parent: 

4939 raise ParentMissingError(target=self) 

4940 

4941 # For certain parents NamedExpr evaluate to the scope of the parent 

4942 if isinstance(self.parent, (Arguments, Keyword, Comprehension)): 

4943 if not self.parent.parent: 

4944 raise ParentMissingError(target=self.parent) 

4945 if not self.parent.parent.parent: 

4946 raise ParentMissingError(target=self.parent.parent) 

4947 return self.parent.parent.parent.frame() 

4948 

4949 return self.parent.frame() 

4950 

4951 def scope(self) -> LocalsDictNodeNG: 

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

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

4954 

4955 :returns: The first parent scope node. 

4956 """ 

4957 if not self.parent: 

4958 raise ParentMissingError(target=self) 

4959 

4960 # For certain parents NamedExpr evaluate to the scope of the parent 

4961 if isinstance(self.parent, (Arguments, Keyword, Comprehension)): 

4962 if not self.parent.parent: 

4963 raise ParentMissingError(target=self.parent) 

4964 if not self.parent.parent.parent: 

4965 raise ParentMissingError(target=self.parent.parent) 

4966 return self.parent.parent.parent.scope() 

4967 

4968 return self.parent.scope() 

4969 

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

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

4972 NamedExpr's in Arguments, Keyword or Comprehension are evaluated in their 

4973 parent's parent scope. So we add to their frame's locals. 

4974 

4975 .. seealso:: :meth:`scope` 

4976 

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

4978 

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

4980 """ 

4981 self.frame().set_local(name, stmt) 

4982 

4983 

4984class Unknown(_base_nodes.AssignTypeNode): 

4985 """This node represents a node in a constructed AST where 

4986 introspection is not possible. 

4987 

4988 Used in the args attribute of FunctionDef nodes where function signature 

4989 introspection failed, and as a placeholder in ObjectModel. 

4990 """ 

4991 

4992 name = "Unknown" 

4993 

4994 def __init__( 

4995 self, 

4996 parent: NodeNG, 

4997 lineno: None = None, 

4998 col_offset: None = None, 

4999 *, 

5000 end_lineno: None = None, 

5001 end_col_offset: None = None, 

5002 ) -> None: 

5003 super().__init__( 

5004 lineno=lineno, 

5005 col_offset=col_offset, 

5006 end_lineno=end_lineno, 

5007 end_col_offset=end_col_offset, 

5008 parent=parent, 

5009 ) 

5010 

5011 def qname(self) -> Literal["Unknown"]: 

5012 return "Unknown" 

5013 

5014 def _infer(self, context: InferenceContext | None = None, **kwargs): 

5015 """Inference on an Unknown node immediately terminates.""" 

5016 yield util.Uninferable 

5017 

5018 

5019UNATTACHED_UNKNOWN = Unknown(parent=SYNTHETIC_ROOT) 

5020 

5021 

5022class EvaluatedObject(NodeNG): 

5023 """Contains an object that has already been inferred 

5024 

5025 This class is useful to pre-evaluate a particular node, 

5026 with the resulting class acting as the non-evaluated node. 

5027 """ 

5028 

5029 name = "EvaluatedObject" 

5030 _astroid_fields = ("original",) 

5031 _other_fields = ("value",) 

5032 

5033 def __init__( 

5034 self, original: SuccessfulInferenceResult, value: InferenceResult 

5035 ) -> None: 

5036 self.original: SuccessfulInferenceResult = original 

5037 """The original node that has already been evaluated""" 

5038 

5039 self.value: InferenceResult = value 

5040 """The inferred value""" 

5041 

5042 super().__init__( 

5043 lineno=self.original.lineno, 

5044 col_offset=self.original.col_offset, 

5045 parent=self.original.parent, 

5046 end_lineno=self.original.end_lineno, 

5047 end_col_offset=self.original.end_col_offset, 

5048 ) 

5049 

5050 def _infer( 

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

5052 ) -> Generator[NodeNG | util.UninferableBase]: 

5053 yield self.value 

5054 

5055 

5056# Pattern matching ####################################################### 

5057 

5058 

5059class Match(_base_nodes.Statement, _base_nodes.MultiLineBlockNode): 

5060 """Class representing a :class:`ast.Match` node. 

5061 

5062 >>> import astroid 

5063 >>> node = astroid.extract_node(''' 

5064 match x: 

5065 case 200: 

5066 ... 

5067 case _: 

5068 ... 

5069 ''') 

5070 >>> node 

5071 <Match l.2 at 0x10c24e170> 

5072 """ 

5073 

5074 _astroid_fields = ("subject", "cases") 

5075 _multi_line_block_fields = ("cases",) 

5076 

5077 def __init__( 

5078 self, 

5079 lineno: int | None = None, 

5080 col_offset: int | None = None, 

5081 parent: NodeNG | None = None, 

5082 *, 

5083 end_lineno: int | None = None, 

5084 end_col_offset: int | None = None, 

5085 ) -> None: 

5086 self.subject: NodeNG 

5087 self.cases: list[MatchCase] 

5088 super().__init__( 

5089 lineno=lineno, 

5090 col_offset=col_offset, 

5091 end_lineno=end_lineno, 

5092 end_col_offset=end_col_offset, 

5093 parent=parent, 

5094 ) 

5095 

5096 def postinit( 

5097 self, 

5098 *, 

5099 subject: NodeNG, 

5100 cases: list[MatchCase], 

5101 ) -> None: 

5102 self.subject = subject 

5103 self.cases = cases 

5104 

5105 

5106class Pattern(NodeNG): 

5107 """Base class for all Pattern nodes.""" 

5108 

5109 

5110class MatchCase(_base_nodes.MultiLineBlockNode): 

5111 """Class representing a :class:`ast.match_case` node. 

5112 

5113 >>> import astroid 

5114 >>> node = astroid.extract_node(''' 

5115 match x: 

5116 case 200: 

5117 ... 

5118 ''') 

5119 >>> node.cases[0] 

5120 <MatchCase l.3 at 0x10c24e590> 

5121 """ 

5122 

5123 _astroid_fields = ("pattern", "guard", "body") 

5124 _multi_line_block_fields = ("body",) 

5125 

5126 lineno: None 

5127 col_offset: None 

5128 end_lineno: None 

5129 end_col_offset: None 

5130 

5131 def __init__(self, *, parent: NodeNG | None = None) -> None: 

5132 self.pattern: Pattern 

5133 self.guard: NodeNG | None 

5134 self.body: list[NodeNG] 

5135 super().__init__( 

5136 parent=parent, 

5137 lineno=None, 

5138 col_offset=None, 

5139 end_lineno=None, 

5140 end_col_offset=None, 

5141 ) 

5142 

5143 def postinit( 

5144 self, 

5145 *, 

5146 pattern: Pattern, 

5147 guard: NodeNG | None, 

5148 body: list[NodeNG], 

5149 ) -> None: 

5150 self.pattern = pattern 

5151 self.guard = guard 

5152 self.body = body 

5153 

5154 

5155class MatchValue(Pattern): 

5156 """Class representing a :class:`ast.MatchValue` node. 

5157 

5158 >>> import astroid 

5159 >>> node = astroid.extract_node(''' 

5160 match x: 

5161 case 200: 

5162 ... 

5163 ''') 

5164 >>> node.cases[0].pattern 

5165 <MatchValue l.3 at 0x10c24e200> 

5166 """ 

5167 

5168 _astroid_fields = ("value",) 

5169 

5170 def __init__( 

5171 self, 

5172 lineno: int | None = None, 

5173 col_offset: int | None = None, 

5174 parent: NodeNG | None = None, 

5175 *, 

5176 end_lineno: int | None = None, 

5177 end_col_offset: int | None = None, 

5178 ) -> None: 

5179 self.value: NodeNG 

5180 super().__init__( 

5181 lineno=lineno, 

5182 col_offset=col_offset, 

5183 end_lineno=end_lineno, 

5184 end_col_offset=end_col_offset, 

5185 parent=parent, 

5186 ) 

5187 

5188 def postinit(self, *, value: NodeNG) -> None: 

5189 self.value = value 

5190 

5191 

5192class MatchSingleton(Pattern): 

5193 """Class representing a :class:`ast.MatchSingleton` node. 

5194 

5195 >>> import astroid 

5196 >>> node = astroid.extract_node(''' 

5197 match x: 

5198 case True: 

5199 ... 

5200 case False: 

5201 ... 

5202 case None: 

5203 ... 

5204 ''') 

5205 >>> node.cases[0].pattern 

5206 <MatchSingleton l.3 at 0x10c2282e0> 

5207 >>> node.cases[1].pattern 

5208 <MatchSingleton l.5 at 0x10c228af0> 

5209 >>> node.cases[2].pattern 

5210 <MatchSingleton l.7 at 0x10c229f90> 

5211 """ 

5212 

5213 _other_fields = ("value",) 

5214 

5215 def __init__( 

5216 self, 

5217 *, 

5218 value: Literal[True, False, None], 

5219 lineno: int | None = None, 

5220 col_offset: int | None = None, 

5221 end_lineno: int | None = None, 

5222 end_col_offset: int | None = None, 

5223 parent: NodeNG | None = None, 

5224 ) -> None: 

5225 self.value = value 

5226 super().__init__( 

5227 lineno=lineno, 

5228 col_offset=col_offset, 

5229 end_lineno=end_lineno, 

5230 end_col_offset=end_col_offset, 

5231 parent=parent, 

5232 ) 

5233 

5234 

5235class MatchSequence(Pattern): 

5236 """Class representing a :class:`ast.MatchSequence` node. 

5237 

5238 >>> import astroid 

5239 >>> node = astroid.extract_node(''' 

5240 match x: 

5241 case [1, 2]: 

5242 ... 

5243 case (1, 2, *_): 

5244 ... 

5245 ''') 

5246 >>> node.cases[0].pattern 

5247 <MatchSequence l.3 at 0x10ca80d00> 

5248 >>> node.cases[1].pattern 

5249 <MatchSequence l.5 at 0x10ca80b20> 

5250 """ 

5251 

5252 _astroid_fields = ("patterns",) 

5253 

5254 def __init__( 

5255 self, 

5256 lineno: int | None = None, 

5257 col_offset: int | None = None, 

5258 parent: NodeNG | None = None, 

5259 *, 

5260 end_lineno: int | None = None, 

5261 end_col_offset: int | None = None, 

5262 ) -> None: 

5263 self.patterns: list[Pattern] 

5264 super().__init__( 

5265 lineno=lineno, 

5266 col_offset=col_offset, 

5267 end_lineno=end_lineno, 

5268 end_col_offset=end_col_offset, 

5269 parent=parent, 

5270 ) 

5271 

5272 def postinit(self, *, patterns: list[Pattern]) -> None: 

5273 self.patterns = patterns 

5274 

5275 

5276class MatchMapping(_base_nodes.AssignTypeNode, Pattern): 

5277 """Class representing a :class:`ast.MatchMapping` node. 

5278 

5279 >>> import astroid 

5280 >>> node = astroid.extract_node(''' 

5281 match x: 

5282 case {1: "Hello", 2: "World", 3: _, **rest}: 

5283 ... 

5284 ''') 

5285 >>> node.cases[0].pattern 

5286 <MatchMapping l.3 at 0x10c8a8850> 

5287 """ 

5288 

5289 _astroid_fields = ("keys", "patterns", "rest") 

5290 

5291 def __init__( 

5292 self, 

5293 lineno: int | None = None, 

5294 col_offset: int | None = None, 

5295 parent: NodeNG | None = None, 

5296 *, 

5297 end_lineno: int | None = None, 

5298 end_col_offset: int | None = None, 

5299 ) -> None: 

5300 self.keys: list[NodeNG] 

5301 self.patterns: list[Pattern] 

5302 self.rest: AssignName | None 

5303 super().__init__( 

5304 lineno=lineno, 

5305 col_offset=col_offset, 

5306 end_lineno=end_lineno, 

5307 end_col_offset=end_col_offset, 

5308 parent=parent, 

5309 ) 

5310 

5311 def postinit( 

5312 self, 

5313 *, 

5314 keys: list[NodeNG], 

5315 patterns: list[Pattern], 

5316 rest: AssignName | None, 

5317 ) -> None: 

5318 self.keys = keys 

5319 self.patterns = patterns 

5320 self.rest = rest 

5321 

5322 assigned_stmts = protocols.match_mapping_assigned_stmts 

5323 """Returns the assigned statement (non inferred) according to the assignment type. 

5324 See astroid/protocols.py for actual implementation. 

5325 """ 

5326 

5327 

5328class MatchClass(Pattern): 

5329 """Class representing a :class:`ast.MatchClass` node. 

5330 

5331 >>> import astroid 

5332 >>> node = astroid.extract_node(''' 

5333 match x: 

5334 case Point2D(0, 0): 

5335 ... 

5336 case Point3D(x=0, y=0, z=0): 

5337 ... 

5338 ''') 

5339 >>> node.cases[0].pattern 

5340 <MatchClass l.3 at 0x10ca83940> 

5341 >>> node.cases[1].pattern 

5342 <MatchClass l.5 at 0x10ca80880> 

5343 """ 

5344 

5345 _astroid_fields = ("cls", "patterns", "kwd_patterns") 

5346 _other_fields = ("kwd_attrs",) 

5347 

5348 def __init__( 

5349 self, 

5350 lineno: int | None = None, 

5351 col_offset: int | None = None, 

5352 parent: NodeNG | None = None, 

5353 *, 

5354 end_lineno: int | None = None, 

5355 end_col_offset: int | None = None, 

5356 ) -> None: 

5357 self.cls: NodeNG 

5358 self.patterns: list[Pattern] 

5359 self.kwd_attrs: list[str] 

5360 self.kwd_patterns: list[Pattern] 

5361 super().__init__( 

5362 lineno=lineno, 

5363 col_offset=col_offset, 

5364 end_lineno=end_lineno, 

5365 end_col_offset=end_col_offset, 

5366 parent=parent, 

5367 ) 

5368 

5369 def postinit( 

5370 self, 

5371 *, 

5372 cls: NodeNG, 

5373 patterns: list[Pattern], 

5374 kwd_attrs: list[str], 

5375 kwd_patterns: list[Pattern], 

5376 ) -> None: 

5377 self.cls = cls 

5378 self.patterns = patterns 

5379 self.kwd_attrs = kwd_attrs 

5380 self.kwd_patterns = kwd_patterns 

5381 

5382 

5383class MatchStar(_base_nodes.AssignTypeNode, Pattern): 

5384 """Class representing a :class:`ast.MatchStar` node. 

5385 

5386 >>> import astroid 

5387 >>> node = astroid.extract_node(''' 

5388 match x: 

5389 case [1, *_]: 

5390 ... 

5391 ''') 

5392 >>> node.cases[0].pattern.patterns[1] 

5393 <MatchStar l.3 at 0x10ca809a0> 

5394 """ 

5395 

5396 _astroid_fields = ("name",) 

5397 

5398 def __init__( 

5399 self, 

5400 lineno: int | None = None, 

5401 col_offset: int | None = None, 

5402 parent: NodeNG | None = None, 

5403 *, 

5404 end_lineno: int | None = None, 

5405 end_col_offset: int | None = None, 

5406 ) -> None: 

5407 self.name: AssignName | None 

5408 super().__init__( 

5409 lineno=lineno, 

5410 col_offset=col_offset, 

5411 end_lineno=end_lineno, 

5412 end_col_offset=end_col_offset, 

5413 parent=parent, 

5414 ) 

5415 

5416 def postinit(self, *, name: AssignName | None) -> None: 

5417 self.name = name 

5418 

5419 assigned_stmts = protocols.match_star_assigned_stmts 

5420 """Returns the assigned statement (non inferred) according to the assignment type. 

5421 See astroid/protocols.py for actual implementation. 

5422 """ 

5423 

5424 

5425class MatchAs(_base_nodes.AssignTypeNode, Pattern): 

5426 """Class representing a :class:`ast.MatchAs` node. 

5427 

5428 >>> import astroid 

5429 >>> node = astroid.extract_node(''' 

5430 match x: 

5431 case [1, a]: 

5432 ... 

5433 case {'key': b}: 

5434 ... 

5435 case Point2D(0, 0) as c: 

5436 ... 

5437 case d: 

5438 ... 

5439 ''') 

5440 >>> node.cases[0].pattern.patterns[1] 

5441 <MatchAs l.3 at 0x10d0b2da0> 

5442 >>> node.cases[1].pattern.patterns[0] 

5443 <MatchAs l.5 at 0x10d0b2920> 

5444 >>> node.cases[2].pattern 

5445 <MatchAs l.7 at 0x10d0b06a0> 

5446 >>> node.cases[3].pattern 

5447 <MatchAs l.9 at 0x10d09b880> 

5448 """ 

5449 

5450 _astroid_fields = ("pattern", "name") 

5451 

5452 def __init__( 

5453 self, 

5454 lineno: int | None = None, 

5455 col_offset: int | None = None, 

5456 parent: NodeNG | None = None, 

5457 *, 

5458 end_lineno: int | None = None, 

5459 end_col_offset: int | None = None, 

5460 ) -> None: 

5461 self.pattern: Pattern | None 

5462 self.name: AssignName | None 

5463 super().__init__( 

5464 lineno=lineno, 

5465 col_offset=col_offset, 

5466 end_lineno=end_lineno, 

5467 end_col_offset=end_col_offset, 

5468 parent=parent, 

5469 ) 

5470 

5471 def postinit( 

5472 self, 

5473 *, 

5474 pattern: Pattern | None, 

5475 name: AssignName | None, 

5476 ) -> None: 

5477 self.pattern = pattern 

5478 self.name = name 

5479 

5480 assigned_stmts = protocols.match_as_assigned_stmts 

5481 """Returns the assigned statement (non inferred) according to the assignment type. 

5482 See astroid/protocols.py for actual implementation. 

5483 """ 

5484 

5485 

5486class MatchOr(Pattern): 

5487 """Class representing a :class:`ast.MatchOr` node. 

5488 

5489 >>> import astroid 

5490 >>> node = astroid.extract_node(''' 

5491 match x: 

5492 case 400 | 401 | 402: 

5493 ... 

5494 ''') 

5495 >>> node.cases[0].pattern 

5496 <MatchOr l.3 at 0x10d0b0b50> 

5497 """ 

5498 

5499 _astroid_fields = ("patterns",) 

5500 

5501 def __init__( 

5502 self, 

5503 lineno: int | None = None, 

5504 col_offset: int | None = None, 

5505 parent: NodeNG | None = None, 

5506 *, 

5507 end_lineno: int | None = None, 

5508 end_col_offset: int | None = None, 

5509 ) -> None: 

5510 self.patterns: list[Pattern] 

5511 super().__init__( 

5512 lineno=lineno, 

5513 col_offset=col_offset, 

5514 end_lineno=end_lineno, 

5515 end_col_offset=end_col_offset, 

5516 parent=parent, 

5517 ) 

5518 

5519 def postinit(self, *, patterns: list[Pattern]) -> None: 

5520 self.patterns = patterns 

5521 

5522 

5523class TemplateStr(NodeNG): 

5524 """Class representing an :class:`ast.TemplateStr` node. 

5525 

5526 >>> import astroid 

5527 >>> node = astroid.extract_node('t"{name} finished {place!s}"') 

5528 >>> node 

5529 <TemplateStr l.1 at 0x103b7aa50> 

5530 """ 

5531 

5532 _astroid_fields = ("values",) 

5533 

5534 def __init__( 

5535 self, 

5536 lineno: int | None = None, 

5537 col_offset: int | None = None, 

5538 parent: NodeNG | None = None, 

5539 *, 

5540 end_lineno: int | None = None, 

5541 end_col_offset: int | None = None, 

5542 ) -> None: 

5543 self.values: list[NodeNG] 

5544 super().__init__( 

5545 lineno=lineno, 

5546 col_offset=col_offset, 

5547 end_lineno=end_lineno, 

5548 end_col_offset=end_col_offset, 

5549 parent=parent, 

5550 ) 

5551 

5552 def postinit(self, *, values: list[NodeNG]) -> None: 

5553 self.values = values 

5554 

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

5556 yield from self.values 

5557 

5558 

5559class Interpolation(NodeNG): 

5560 """Class representing an :class:`ast.Interpolation` node. 

5561 

5562 >>> import astroid 

5563 >>> node = astroid.extract_node('t"{name} finished {place!s}"') 

5564 >>> node 

5565 <TemplateStr l.1 at 0x103b7aa50> 

5566 >>> node.values[0] 

5567 <Interpolation l.1 at 0x103b7acf0> 

5568 >>> node.values[2] 

5569 <Interpolation l.1 at 0x10411e5d0> 

5570 """ 

5571 

5572 _astroid_fields = ("value", "format_spec") 

5573 _other_fields = ("str", "conversion") 

5574 

5575 def __init__( 

5576 self, 

5577 lineno: int | None = None, 

5578 col_offset: int | None = None, 

5579 parent: NodeNG | None = None, 

5580 *, 

5581 end_lineno: int | None = None, 

5582 end_col_offset: int | None = None, 

5583 ) -> None: 

5584 self.value: NodeNG 

5585 """Any expression node.""" 

5586 

5587 self.str: str 

5588 """Text of the interpolation expression.""" 

5589 

5590 self.conversion: int 

5591 """The type of formatting to be applied to the value. 

5592 

5593 .. seealso:: 

5594 :class:`ast.Interpolation` 

5595 """ 

5596 

5597 self.format_spec: JoinedStr | None = None 

5598 """The formatting to be applied to the value. 

5599 

5600 .. seealso:: 

5601 :class:`ast.Interpolation` 

5602 """ 

5603 

5604 super().__init__( 

5605 lineno=lineno, 

5606 col_offset=col_offset, 

5607 end_lineno=end_lineno, 

5608 end_col_offset=end_col_offset, 

5609 parent=parent, 

5610 ) 

5611 

5612 def postinit( 

5613 self, 

5614 *, 

5615 value: NodeNG, 

5616 str: str, # pylint: disable=redefined-builtin 

5617 conversion: int = -1, 

5618 format_spec: JoinedStr | None = None, 

5619 ) -> None: 

5620 self.value = value 

5621 self.str = str 

5622 self.conversion = conversion 

5623 self.format_spec = format_spec 

5624 

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

5626 yield self.value 

5627 if self.format_spec: 

5628 yield self.format_spec 

5629 

5630 

5631# constants ############################################################## 

5632 

5633# The _proxied attribute of all container types (List, Tuple, etc.) 

5634# are set during bootstrapping by _astroid_bootstrapping(). 

5635CONST_CLS: dict[type, type[NodeNG]] = { 

5636 list: List, 

5637 tuple: Tuple, 

5638 dict: Dict, 

5639 set: Set, 

5640 type(None): Const, 

5641 type(NotImplemented): Const, 

5642 type(...): Const, 

5643 bool: Const, 

5644 int: Const, 

5645 float: Const, 

5646 complex: Const, 

5647 str: Const, 

5648 bytes: Const, 

5649} 

5650 

5651 

5652def _create_basic_elements( 

5653 value: Iterable[Any], node: List | Set | Tuple 

5654) -> list[NodeNG]: 

5655 """Create a list of nodes to function as the elements of a new node.""" 

5656 elements: list[NodeNG] = [] 

5657 for element in value: 

5658 # NOTE: avoid accessing any attributes of element in the loop. 

5659 element_node = const_factory(element) 

5660 element_node.parent = node 

5661 elements.append(element_node) 

5662 return elements 

5663 

5664 

5665def _create_dict_items( 

5666 values: Mapping[Any, Any], node: Dict 

5667) -> list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]]: 

5668 """Create a list of node pairs to function as the items of a new dict node.""" 

5669 elements: list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]] = [] 

5670 for key, value in values.items(): 

5671 # NOTE: avoid accessing any attributes of both key and value in the loop. 

5672 key_node = const_factory(key) 

5673 key_node.parent = node 

5674 value_node = const_factory(value) 

5675 value_node.parent = node 

5676 elements.append((key_node, value_node)) 

5677 return elements 

5678 

5679 

5680def const_factory(value: Any) -> ConstFactoryResult: 

5681 """Return an astroid node for a python value.""" 

5682 # NOTE: avoid accessing any attributes of value until it is known that value 

5683 # is of a const type, to avoid possibly triggering code for a live object. 

5684 # Accesses include value.__class__ and isinstance(value, ...), but not type(value). 

5685 # See: https://github.com/pylint-dev/astroid/issues/2686 

5686 value_type = type(value) 

5687 assert not issubclass(value_type, NodeNG) 

5688 

5689 # This only handles instances of the CONST types. Any 

5690 # subclasses get inferred as EmptyNode. 

5691 # TODO: See if we should revisit these with the normal builder. 

5692 if value_type not in CONST_CLS: 

5693 node = EmptyNode() 

5694 node.object = value 

5695 return node 

5696 

5697 instance: List | Set | Tuple | Dict 

5698 initializer_cls = CONST_CLS[value_type] 

5699 if issubclass(initializer_cls, (List, Set, Tuple)): 

5700 instance = initializer_cls( 

5701 lineno=None, 

5702 col_offset=None, 

5703 parent=SYNTHETIC_ROOT, 

5704 end_lineno=None, 

5705 end_col_offset=None, 

5706 ) 

5707 instance.postinit(_create_basic_elements(value, instance)) 

5708 return instance 

5709 if issubclass(initializer_cls, Dict): 

5710 instance = initializer_cls( 

5711 lineno=None, 

5712 col_offset=None, 

5713 parent=SYNTHETIC_ROOT, 

5714 end_lineno=None, 

5715 end_col_offset=None, 

5716 ) 

5717 instance.postinit(_create_dict_items(value, instance)) 

5718 return instance 

5719 return Const(value)