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

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

1254 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""" 

6This module contains the classes for "scoped" node, i.e. which are opening a 

7new local scope in the language definition : Module, ClassDef, FunctionDef (and 

8Lambda, GeneratorExp, DictComp and SetComp to some extent). 

9""" 

10 

11from __future__ import annotations 

12 

13import io 

14import itertools 

15import os 

16import warnings 

17from collections.abc import Generator, Iterable, Iterator, Sequence 

18from functools import cached_property, lru_cache 

19from typing import TYPE_CHECKING, Any, ClassVar, Literal, NoReturn, TypeVar 

20 

21from astroid import bases, protocols, util 

22from astroid.const import IS_PYPY, PY38, PY39_PLUS, PYPY_7_3_11_PLUS 

23from astroid.context import ( 

24 CallContext, 

25 InferenceContext, 

26 bind_context_to_node, 

27 copy_context, 

28) 

29from astroid.exceptions import ( 

30 AstroidBuildingError, 

31 AstroidTypeError, 

32 AttributeInferenceError, 

33 DuplicateBasesError, 

34 InconsistentMroError, 

35 InferenceError, 

36 MroError, 

37 ParentMissingError, 

38 StatementMissing, 

39 TooManyLevelsError, 

40) 

41from astroid.interpreter.dunder_lookup import lookup 

42from astroid.interpreter.objectmodel import ClassModel, FunctionModel, ModuleModel 

43from astroid.manager import AstroidManager 

44from astroid.nodes import ( 

45 Arguments, 

46 Const, 

47 NodeNG, 

48 Unknown, 

49 _base_nodes, 

50 const_factory, 

51 node_classes, 

52) 

53from astroid.nodes.scoped_nodes.mixin import ComprehensionScope, LocalsDictNodeNG 

54from astroid.nodes.scoped_nodes.utils import builtin_lookup 

55from astroid.nodes.utils import Position 

56from astroid.typing import ( 

57 InferBinaryOp, 

58 InferenceErrorInfo, 

59 InferenceResult, 

60 SuccessfulInferenceResult, 

61) 

62 

63if TYPE_CHECKING: 

64 from astroid import nodes, objects 

65 from astroid.nodes._base_nodes import LookupMixIn 

66 

67 

68ITER_METHODS = ("__iter__", "__getitem__") 

69EXCEPTION_BASE_CLASSES = frozenset({"Exception", "BaseException"}) 

70BUILTIN_DESCRIPTORS = frozenset( 

71 {"classmethod", "staticmethod", "builtins.classmethod", "builtins.staticmethod"} 

72) 

73 

74_T = TypeVar("_T") 

75 

76 

77def _c3_merge(sequences, cls, context): 

78 """Merges MROs in *sequences* to a single MRO using the C3 algorithm. 

79 

80 Adapted from http://www.python.org/download/releases/2.3/mro/. 

81 

82 """ 

83 result = [] 

84 while True: 

85 sequences = [s for s in sequences if s] # purge empty sequences 

86 if not sequences: 

87 return result 

88 for s1 in sequences: # find merge candidates among seq heads 

89 candidate = s1[0] 

90 for s2 in sequences: 

91 if candidate in s2[1:]: 

92 candidate = None 

93 break # reject the current head, it appears later 

94 else: 

95 break 

96 if not candidate: 

97 # Show all the remaining bases, which were considered as 

98 # candidates for the next mro sequence. 

99 raise InconsistentMroError( 

100 message="Cannot create a consistent method resolution order " 

101 "for MROs {mros} of class {cls!r}.", 

102 mros=sequences, 

103 cls=cls, 

104 context=context, 

105 ) 

106 

107 result.append(candidate) 

108 # remove the chosen candidate 

109 for seq in sequences: 

110 if seq[0] == candidate: 

111 del seq[0] 

112 return None 

113 

114 

115def clean_typing_generic_mro(sequences: list[list[ClassDef]]) -> None: 

116 """A class can inherit from typing.Generic directly, as base, 

117 and as base of bases. The merged MRO must however only contain the last entry. 

118 To prepare for _c3_merge, remove some typing.Generic entries from 

119 sequences if multiple are present. 

120 

121 This method will check if Generic is in inferred_bases and also 

122 part of bases_mro. If true, remove it from inferred_bases 

123 as well as its entry the bases_mro. 

124 

125 Format sequences: [[self]] + bases_mro + [inferred_bases] 

126 """ 

127 bases_mro = sequences[1:-1] 

128 inferred_bases = sequences[-1] 

129 # Check if Generic is part of inferred_bases 

130 for i, base in enumerate(inferred_bases): 

131 if base.qname() == "typing.Generic": 

132 position_in_inferred_bases = i 

133 break 

134 else: 

135 return 

136 # Check if also part of bases_mro 

137 # Ignore entry for typing.Generic 

138 for i, seq in enumerate(bases_mro): 

139 if i == position_in_inferred_bases: 

140 continue 

141 if any(base.qname() == "typing.Generic" for base in seq): 

142 break 

143 else: 

144 return 

145 # Found multiple Generics in mro, remove entry from inferred_bases 

146 # and the corresponding one from bases_mro 

147 inferred_bases.pop(position_in_inferred_bases) 

148 bases_mro.pop(position_in_inferred_bases) 

149 

150 

151def clean_duplicates_mro( 

152 sequences: list[list[ClassDef]], 

153 cls: ClassDef, 

154 context: InferenceContext | None, 

155) -> list[list[ClassDef]]: 

156 for sequence in sequences: 

157 seen = set() 

158 for node in sequence: 

159 lineno_and_qname = (node.lineno, node.qname()) 

160 if lineno_and_qname in seen: 

161 raise DuplicateBasesError( 

162 message="Duplicates found in MROs {mros} for {cls!r}.", 

163 mros=sequences, 

164 cls=cls, 

165 context=context, 

166 ) 

167 seen.add(lineno_and_qname) 

168 return sequences 

169 

170 

171def function_to_method(n, klass): 

172 if isinstance(n, FunctionDef): 

173 if n.type == "classmethod": 

174 return bases.BoundMethod(n, klass) 

175 if n.type == "property": 

176 return n 

177 if n.type != "staticmethod": 

178 return bases.UnboundMethod(n) 

179 return n 

180 

181 

182class Module(LocalsDictNodeNG): 

183 """Class representing an :class:`ast.Module` node. 

184 

185 >>> import astroid 

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

187 >>> node 

188 <Import l.1 at 0x7f23b2e4e5c0> 

189 >>> node.parent 

190 <Module l.0 at 0x7f23b2e4eda0> 

191 """ 

192 

193 _astroid_fields = ("doc_node", "body") 

194 

195 doc_node: Const | None 

196 """The doc node associated with this node.""" 

197 

198 # attributes below are set by the builder module or by raw factories 

199 

200 file_bytes: str | bytes | None = None 

201 """The string/bytes that this ast was built from.""" 

202 

203 file_encoding: str | None = None 

204 """The encoding of the source file. 

205 

206 This is used to get unicode out of a source file. 

207 Python 2 only. 

208 """ 

209 

210 special_attributes = ModuleModel() 

211 """The names of special attributes that this module has.""" 

212 

213 # names of module attributes available through the global scope 

214 scope_attrs: ClassVar[set[str]] = { 

215 "__name__", 

216 "__doc__", 

217 "__file__", 

218 "__path__", 

219 "__package__", 

220 } 

221 """The names of module attributes available through the global scope.""" 

222 

223 _other_fields = ( 

224 "name", 

225 "file", 

226 "path", 

227 "package", 

228 "pure_python", 

229 "future_imports", 

230 ) 

231 _other_other_fields = ("locals", "globals") 

232 

233 def __init__( 

234 self, 

235 name: str, 

236 file: str | None = None, 

237 path: Sequence[str] | None = None, 

238 package: bool = False, 

239 pure_python: bool = True, 

240 ) -> None: 

241 self.name = name 

242 """The name of the module.""" 

243 

244 self.file = file 

245 """The path to the file that this ast has been extracted from. 

246 

247 This will be ``None`` when the representation has been built from a 

248 built-in module. 

249 """ 

250 

251 self.path = path 

252 

253 self.package = package 

254 """Whether the node represents a package or a module.""" 

255 

256 self.pure_python = pure_python 

257 """Whether the ast was built from source.""" 

258 

259 self.globals: dict[str, list[InferenceResult]] 

260 """A map of the name of a global variable to the node defining the global.""" 

261 

262 self.locals = self.globals = {} 

263 """A map of the name of a local variable to the node defining the local.""" 

264 

265 self.body: list[node_classes.NodeNG] = [] 

266 """The contents of the module.""" 

267 

268 self.future_imports: set[str] = set() 

269 """The imports from ``__future__``.""" 

270 

271 super().__init__( 

272 lineno=0, parent=None, col_offset=0, end_lineno=None, end_col_offset=None 

273 ) 

274 

275 # pylint: enable=redefined-builtin 

276 

277 def postinit( 

278 self, body: list[node_classes.NodeNG], *, doc_node: Const | None = None 

279 ): 

280 self.body = body 

281 self.doc_node = doc_node 

282 

283 def _get_stream(self): 

284 if self.file_bytes is not None: 

285 return io.BytesIO(self.file_bytes) 

286 if self.file is not None: 

287 # pylint: disable=consider-using-with 

288 stream = open(self.file, "rb") 

289 return stream 

290 return None 

291 

292 def stream(self): 

293 """Get a stream to the underlying file or bytes. 

294 

295 :type: file or io.BytesIO or None 

296 """ 

297 return self._get_stream() 

298 

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

300 """Get a range from where this node starts to where this node ends. 

301 

302 :param lineno: Unused. 

303 

304 :returns: The range of line numbers that this node belongs to. 

305 """ 

306 return self.fromlineno, self.tolineno 

307 

308 def scope_lookup( 

309 self, node: LookupMixIn, name: str, offset: int = 0 

310 ) -> tuple[LocalsDictNodeNG, list[node_classes.NodeNG]]: 

311 """Lookup where the given variable is assigned. 

312 

313 :param node: The node to look for assignments up to. 

314 Any assignments after the given node are ignored. 

315 

316 :param name: The name of the variable to find assignments for. 

317 

318 :param offset: The line offset to filter statements up to. 

319 

320 :returns: This scope node and the list of assignments associated to the 

321 given name according to the scope where it has been found (locals, 

322 globals or builtin). 

323 """ 

324 if name in self.scope_attrs and name not in self.locals: 

325 try: 

326 return self, self.getattr(name) 

327 except AttributeInferenceError: 

328 return self, [] 

329 return self._scope_lookup(node, name, offset) 

330 

331 def pytype(self) -> Literal["builtins.module"]: 

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

333 

334 :returns: The name of the type. 

335 """ 

336 return "builtins.module" 

337 

338 def display_type(self) -> str: 

339 """A human readable type of this node. 

340 

341 :returns: The type of this node. 

342 :rtype: str 

343 """ 

344 return "Module" 

345 

346 def getattr( 

347 self, name, context: InferenceContext | None = None, ignore_locals=False 

348 ): 

349 if not name: 

350 raise AttributeInferenceError(target=self, attribute=name, context=context) 

351 

352 result = [] 

353 name_in_locals = name in self.locals 

354 

355 if name in self.special_attributes and not ignore_locals and not name_in_locals: 

356 result = [self.special_attributes.lookup(name)] 

357 if name == "__name__": 

358 result.append(const_factory("__main__")) 

359 elif not ignore_locals and name_in_locals: 

360 result = self.locals[name] 

361 elif self.package: 

362 try: 

363 result = [self.import_module(name, relative_only=True)] 

364 except (AstroidBuildingError, SyntaxError) as exc: 

365 raise AttributeInferenceError( 

366 target=self, attribute=name, context=context 

367 ) from exc 

368 result = [n for n in result if not isinstance(n, node_classes.DelName)] 

369 if result: 

370 return result 

371 raise AttributeInferenceError(target=self, attribute=name, context=context) 

372 

373 def igetattr( 

374 self, name: str, context: InferenceContext | None = None 

375 ) -> Iterator[InferenceResult]: 

376 """Infer the possible values of the given variable. 

377 

378 :param name: The name of the variable to infer. 

379 

380 :returns: The inferred possible values. 

381 """ 

382 # set lookup name since this is necessary to infer on import nodes for 

383 # instance 

384 context = copy_context(context) 

385 context.lookupname = name 

386 try: 

387 return bases._infer_stmts(self.getattr(name, context), context, frame=self) 

388 except AttributeInferenceError as error: 

389 raise InferenceError( 

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

391 ) from error 

392 

393 def fully_defined(self) -> bool: 

394 """Check if this module has been build from a .py file. 

395 

396 If so, the module contains a complete representation, 

397 including the code. 

398 

399 :returns: Whether the module has been built from a .py file. 

400 """ 

401 return self.file is not None and self.file.endswith(".py") 

402 

403 def statement(self, *, future: Literal[None, True] = None) -> NoReturn: 

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

405 

406 When called on a :class:`Module` this raises a StatementMissing. 

407 """ 

408 if future is not None: 

409 warnings.warn( 

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

411 DeprecationWarning, 

412 stacklevel=2, 

413 ) 

414 raise StatementMissing(target=self) 

415 

416 def previous_sibling(self): 

417 """The previous sibling statement. 

418 

419 :returns: The previous sibling statement node. 

420 :rtype: NodeNG or None 

421 """ 

422 

423 def next_sibling(self): 

424 """The next sibling statement node. 

425 

426 :returns: The next sibling statement node. 

427 :rtype: NodeNG or None 

428 """ 

429 

430 _absolute_import_activated = True 

431 

432 def absolute_import_activated(self) -> bool: 

433 """Whether :pep:`328` absolute import behaviour has been enabled. 

434 

435 :returns: Whether :pep:`328` has been enabled. 

436 """ 

437 return self._absolute_import_activated 

438 

439 def import_module( 

440 self, 

441 modname: str, 

442 relative_only: bool = False, 

443 level: int | None = None, 

444 use_cache: bool = True, 

445 ) -> Module: 

446 """Get the ast for a given module as if imported from this module. 

447 

448 :param modname: The name of the module to "import". 

449 

450 :param relative_only: Whether to only consider relative imports. 

451 

452 :param level: The level of relative import. 

453 

454 :param use_cache: Whether to use the astroid_cache of modules. 

455 

456 :returns: The imported module ast. 

457 """ 

458 if relative_only and level is None: 

459 level = 0 

460 absmodname = self.relative_to_absolute_name(modname, level) 

461 

462 try: 

463 return AstroidManager().ast_from_module_name( 

464 absmodname, use_cache=use_cache 

465 ) 

466 except AstroidBuildingError: 

467 # we only want to import a sub module or package of this module, 

468 # skip here 

469 if relative_only: 

470 raise 

471 # Don't repeat the same operation, e.g. for missing modules 

472 # like "_winapi" or "nt" on POSIX systems. 

473 if modname == absmodname: 

474 raise 

475 return AstroidManager().ast_from_module_name(modname, use_cache=use_cache) 

476 

477 def relative_to_absolute_name(self, modname: str, level: int | None) -> str: 

478 """Get the absolute module name for a relative import. 

479 

480 The relative import can be implicit or explicit. 

481 

482 :param modname: The module name to convert. 

483 

484 :param level: The level of relative import. 

485 

486 :returns: The absolute module name. 

487 

488 :raises TooManyLevelsError: When the relative import refers to a 

489 module too far above this one. 

490 """ 

491 # XXX this returns non sens when called on an absolute import 

492 # like 'pylint.checkers.astroid.utils' 

493 # XXX doesn't return absolute name if self.name isn't absolute name 

494 if self.absolute_import_activated() and level is None: 

495 return modname 

496 if level: 

497 if self.package: 

498 level = level - 1 

499 package_name = self.name.rsplit(".", level)[0] 

500 elif ( 

501 self.path 

502 and not os.path.exists(os.path.dirname(self.path[0]) + "/__init__.py") 

503 and os.path.exists( 

504 os.path.dirname(self.path[0]) + "/" + modname.split(".")[0] 

505 ) 

506 ): 

507 level = level - 1 

508 package_name = "" 

509 else: 

510 package_name = self.name.rsplit(".", level)[0] 

511 if level and self.name.count(".") < level: 

512 raise TooManyLevelsError(level=level, name=self.name) 

513 

514 elif self.package: 

515 package_name = self.name 

516 else: 

517 package_name = self.name.rsplit(".", 1)[0] 

518 

519 if package_name: 

520 if not modname: 

521 return package_name 

522 return f"{package_name}.{modname}" 

523 return modname 

524 

525 def wildcard_import_names(self): 

526 """The list of imported names when this module is 'wildcard imported'. 

527 

528 It doesn't include the '__builtins__' name which is added by the 

529 current CPython implementation of wildcard imports. 

530 

531 :returns: The list of imported names. 

532 :rtype: list(str) 

533 """ 

534 # We separate the different steps of lookup in try/excepts 

535 # to avoid catching too many Exceptions 

536 default = [name for name in self.keys() if not name.startswith("_")] 

537 try: 

538 all_values = self["__all__"] 

539 except KeyError: 

540 return default 

541 

542 try: 

543 explicit = next(all_values.assigned_stmts()) 

544 except (InferenceError, StopIteration): 

545 return default 

546 except AttributeError: 

547 # not an assignment node 

548 # XXX infer? 

549 return default 

550 

551 # Try our best to detect the exported name. 

552 inferred = [] 

553 try: 

554 explicit = next(explicit.infer()) 

555 except (InferenceError, StopIteration): 

556 return default 

557 if not isinstance(explicit, (node_classes.Tuple, node_classes.List)): 

558 return default 

559 

560 def str_const(node) -> bool: 

561 return isinstance(node, node_classes.Const) and isinstance(node.value, str) 

562 

563 for node in explicit.elts: 

564 if str_const(node): 

565 inferred.append(node.value) 

566 else: 

567 try: 

568 inferred_node = next(node.infer()) 

569 except (InferenceError, StopIteration): 

570 continue 

571 if str_const(inferred_node): 

572 inferred.append(inferred_node.value) 

573 return inferred 

574 

575 def public_names(self): 

576 """The list of the names that are publicly available in this module. 

577 

578 :returns: The list of public names. 

579 :rtype: list(str) 

580 """ 

581 return [name for name in self.keys() if not name.startswith("_")] 

582 

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

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

585 

586 :returns: The boolean value of this node. 

587 For a :class:`Module` this is always ``True``. 

588 """ 

589 return True 

590 

591 def get_children(self): 

592 yield from self.body 

593 

594 def frame(self: _T, *, future: Literal[None, True] = None) -> _T: 

595 """The node's frame node. 

596 

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

598 :class:`ClassDef` or :class:`Lambda`. 

599 

600 :returns: The node itself. 

601 """ 

602 return self 

603 

604 def _infer( 

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

606 ) -> Generator[Module, None, None]: 

607 yield self 

608 

609 

610class GeneratorExp(ComprehensionScope): 

611 """Class representing an :class:`ast.GeneratorExp` node. 

612 

613 >>> import astroid 

614 >>> node = astroid.extract_node('(thing for thing in things if thing)') 

615 >>> node 

616 <GeneratorExp l.1 at 0x7f23b2e4e400> 

617 """ 

618 

619 _astroid_fields = ("elt", "generators") 

620 _other_other_fields = ("locals",) 

621 elt: NodeNG 

622 """The element that forms the output of the expression.""" 

623 

624 def __init__( 

625 self, 

626 lineno: int, 

627 col_offset: int, 

628 parent: NodeNG, 

629 *, 

630 end_lineno: int | None, 

631 end_col_offset: int | None, 

632 ) -> None: 

633 self.locals = {} 

634 """A map of the name of a local variable to the node defining the local.""" 

635 

636 self.generators: list[nodes.Comprehension] = [] 

637 """The generators that are looped through.""" 

638 

639 super().__init__( 

640 lineno=lineno, 

641 col_offset=col_offset, 

642 end_lineno=end_lineno, 

643 end_col_offset=end_col_offset, 

644 parent=parent, 

645 ) 

646 

647 def postinit(self, elt: NodeNG, generators: list[nodes.Comprehension]) -> None: 

648 self.elt = elt 

649 self.generators = generators 

650 

651 def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: 

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

653 

654 :returns: The boolean value of this node. 

655 For a :class:`GeneratorExp` this is always ``True``. 

656 """ 

657 return True 

658 

659 def get_children(self): 

660 yield self.elt 

661 

662 yield from self.generators 

663 

664 

665class DictComp(ComprehensionScope): 

666 """Class representing an :class:`ast.DictComp` node. 

667 

668 >>> import astroid 

669 >>> node = astroid.extract_node('{k:v for k, v in things if k > v}') 

670 >>> node 

671 <DictComp l.1 at 0x7f23b2e41d68> 

672 """ 

673 

674 _astroid_fields = ("key", "value", "generators") 

675 _other_other_fields = ("locals",) 

676 key: NodeNG 

677 """What produces the keys.""" 

678 

679 value: NodeNG 

680 """What produces the values.""" 

681 

682 def __init__( 

683 self, 

684 lineno: int, 

685 col_offset: int, 

686 parent: NodeNG, 

687 *, 

688 end_lineno: int | None, 

689 end_col_offset: int | None, 

690 ) -> None: 

691 self.locals = {} 

692 """A map of the name of a local variable to the node defining the local.""" 

693 

694 super().__init__( 

695 lineno=lineno, 

696 col_offset=col_offset, 

697 end_lineno=end_lineno, 

698 end_col_offset=end_col_offset, 

699 parent=parent, 

700 ) 

701 

702 def postinit( 

703 self, key: NodeNG, value: NodeNG, generators: list[nodes.Comprehension] 

704 ) -> None: 

705 self.key = key 

706 self.value = value 

707 self.generators = generators 

708 

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

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

711 

712 :returns: The boolean value of this node. 

713 For a :class:`DictComp` this is always :class:`Uninferable`. 

714 :rtype: Uninferable 

715 """ 

716 return util.Uninferable 

717 

718 def get_children(self): 

719 yield self.key 

720 yield self.value 

721 

722 yield from self.generators 

723 

724 

725class SetComp(ComprehensionScope): 

726 """Class representing an :class:`ast.SetComp` node. 

727 

728 >>> import astroid 

729 >>> node = astroid.extract_node('{thing for thing in things if thing}') 

730 >>> node 

731 <SetComp l.1 at 0x7f23b2e41898> 

732 """ 

733 

734 _astroid_fields = ("elt", "generators") 

735 _other_other_fields = ("locals",) 

736 elt: NodeNG 

737 """The element that forms the output of the expression.""" 

738 

739 def __init__( 

740 self, 

741 lineno: int, 

742 col_offset: int, 

743 parent: NodeNG, 

744 *, 

745 end_lineno: int | None, 

746 end_col_offset: int | None, 

747 ) -> None: 

748 self.locals = {} 

749 """A map of the name of a local variable to the node defining the local.""" 

750 

751 self.generators: list[nodes.Comprehension] = [] 

752 """The generators that are looped through.""" 

753 

754 super().__init__( 

755 lineno=lineno, 

756 col_offset=col_offset, 

757 end_lineno=end_lineno, 

758 end_col_offset=end_col_offset, 

759 parent=parent, 

760 ) 

761 

762 def postinit(self, elt: NodeNG, generators: list[nodes.Comprehension]) -> None: 

763 self.elt = elt 

764 self.generators = generators 

765 

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

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

768 

769 :returns: The boolean value of this node. 

770 For a :class:`SetComp` this is always :class:`Uninferable`. 

771 :rtype: Uninferable 

772 """ 

773 return util.Uninferable 

774 

775 def get_children(self): 

776 yield self.elt 

777 

778 yield from self.generators 

779 

780 

781class ListComp(ComprehensionScope): 

782 """Class representing an :class:`ast.ListComp` node. 

783 

784 >>> import astroid 

785 >>> node = astroid.extract_node('[thing for thing in things if thing]') 

786 >>> node 

787 <ListComp l.1 at 0x7f23b2e418d0> 

788 """ 

789 

790 _astroid_fields = ("elt", "generators") 

791 _other_other_fields = ("locals",) 

792 

793 elt: NodeNG 

794 """The element that forms the output of the expression.""" 

795 

796 def __init__( 

797 self, 

798 lineno: int, 

799 col_offset: int, 

800 parent: NodeNG, 

801 *, 

802 end_lineno: int | None, 

803 end_col_offset: int | None, 

804 ) -> None: 

805 self.locals = {} 

806 """A map of the name of a local variable to the node defining it.""" 

807 

808 self.generators: list[nodes.Comprehension] = [] 

809 """The generators that are looped through.""" 

810 

811 super().__init__( 

812 lineno=lineno, 

813 col_offset=col_offset, 

814 end_lineno=end_lineno, 

815 end_col_offset=end_col_offset, 

816 parent=parent, 

817 ) 

818 

819 def postinit(self, elt: NodeNG, generators: list[nodes.Comprehension]): 

820 self.elt = elt 

821 self.generators = generators 

822 

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

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

825 

826 :returns: The boolean value of this node. 

827 For a :class:`ListComp` this is always :class:`Uninferable`. 

828 :rtype: Uninferable 

829 """ 

830 return util.Uninferable 

831 

832 def get_children(self): 

833 yield self.elt 

834 

835 yield from self.generators 

836 

837 

838def _infer_decorator_callchain(node): 

839 """Detect decorator call chaining and see if the end result is a 

840 static or a classmethod. 

841 """ 

842 if not isinstance(node, FunctionDef): 

843 return None 

844 if not node.parent: 

845 return None 

846 try: 

847 result = next(node.infer_call_result(node.parent), None) 

848 except InferenceError: 

849 return None 

850 if isinstance(result, bases.Instance): 

851 result = result._proxied 

852 if isinstance(result, ClassDef): 

853 if result.is_subtype_of("builtins.classmethod"): 

854 return "classmethod" 

855 if result.is_subtype_of("builtins.staticmethod"): 

856 return "staticmethod" 

857 if isinstance(result, FunctionDef): 

858 if not result.decorators: 

859 return None 

860 # Determine if this function is decorated with one of the builtin descriptors we want. 

861 for decorator in result.decorators.nodes: 

862 if isinstance(decorator, node_classes.Name): 

863 if decorator.name in BUILTIN_DESCRIPTORS: 

864 return decorator.name 

865 if ( 

866 isinstance(decorator, node_classes.Attribute) 

867 and isinstance(decorator.expr, node_classes.Name) 

868 and decorator.expr.name == "builtins" 

869 and decorator.attrname in BUILTIN_DESCRIPTORS 

870 ): 

871 return decorator.attrname 

872 return None 

873 

874 

875class Lambda(_base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG): 

876 """Class representing an :class:`ast.Lambda` node. 

877 

878 >>> import astroid 

879 >>> node = astroid.extract_node('lambda arg: arg + 1') 

880 >>> node 

881 <Lambda.<lambda> l.1 at 0x7f23b2e41518> 

882 """ 

883 

884 _astroid_fields: ClassVar[tuple[str, ...]] = ("args", "body") 

885 _other_other_fields: ClassVar[tuple[str, ...]] = ("locals",) 

886 name = "<lambda>" 

887 is_lambda = True 

888 special_attributes = FunctionModel() 

889 """The names of special attributes that this function has.""" 

890 

891 args: Arguments 

892 """The arguments that the function takes.""" 

893 

894 body: NodeNG 

895 """The contents of the function body.""" 

896 

897 def implicit_parameters(self) -> Literal[0]: 

898 return 0 

899 

900 @property 

901 def type(self) -> Literal["method", "function"]: 

902 """Whether this is a method or function. 

903 

904 :returns: 'method' if this is a method, 'function' otherwise. 

905 """ 

906 if self.args.arguments and self.args.arguments[0].name == "self": 

907 if self.parent and isinstance(self.parent.scope(), ClassDef): 

908 return "method" 

909 return "function" 

910 

911 def __init__( 

912 self, 

913 lineno: int, 

914 col_offset: int, 

915 parent: NodeNG, 

916 *, 

917 end_lineno: int | None, 

918 end_col_offset: int | None, 

919 ): 

920 self.locals = {} 

921 """A map of the name of a local variable to the node defining it.""" 

922 

923 self.instance_attrs: dict[str, list[NodeNG]] = {} 

924 

925 super().__init__( 

926 lineno=lineno, 

927 col_offset=col_offset, 

928 end_lineno=end_lineno, 

929 end_col_offset=end_col_offset, 

930 parent=parent, 

931 ) 

932 

933 def postinit(self, args: Arguments, body: NodeNG) -> None: 

934 self.args = args 

935 self.body = body 

936 

937 def pytype(self) -> Literal["builtins.instancemethod", "builtins.function"]: 

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

939 

940 :returns: The name of the type. 

941 """ 

942 if "method" in self.type: 

943 return "builtins.instancemethod" 

944 return "builtins.function" 

945 

946 def display_type(self) -> str: 

947 """A human readable type of this node. 

948 

949 :returns: The type of this node. 

950 :rtype: str 

951 """ 

952 if "method" in self.type: 

953 return "Method" 

954 return "Function" 

955 

956 def callable(self) -> Literal[True]: 

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

958 

959 :returns: Whether this defines something that is callable 

960 For a :class:`Lambda` this is always ``True``. 

961 """ 

962 return True 

963 

964 def argnames(self) -> list[str]: 

965 """Get the names of each of the arguments, including that 

966 of the collections of variable-length arguments ("args", "kwargs", 

967 etc.), as well as positional-only and keyword-only arguments. 

968 

969 :returns: The names of the arguments. 

970 :rtype: list(str) 

971 """ 

972 if self.args.arguments: # maybe None with builtin functions 

973 names = [elt.name for elt in self.args.arguments] 

974 else: 

975 names = [] 

976 

977 return names 

978 

979 def infer_call_result( 

980 self, 

981 caller: SuccessfulInferenceResult | None, 

982 context: InferenceContext | None = None, 

983 ) -> Iterator[InferenceResult]: 

984 """Infer what the function returns when called.""" 

985 return self.body.infer(context) 

986 

987 def scope_lookup( 

988 self, node: LookupMixIn, name: str, offset: int = 0 

989 ) -> tuple[LocalsDictNodeNG, list[NodeNG]]: 

990 """Lookup where the given names is assigned. 

991 

992 :param node: The node to look for assignments up to. 

993 Any assignments after the given node are ignored. 

994 

995 :param name: The name to find assignments for. 

996 

997 :param offset: The line offset to filter statements up to. 

998 

999 :returns: This scope node and the list of assignments associated to the 

1000 given name according to the scope where it has been found (locals, 

1001 globals or builtin). 

1002 """ 

1003 if (self.args.defaults and node in self.args.defaults) or ( 

1004 self.args.kw_defaults and node in self.args.kw_defaults 

1005 ): 

1006 if not self.parent: 

1007 raise ParentMissingError(target=self) 

1008 frame = self.parent.frame() 

1009 # line offset to avoid that def func(f=func) resolve the default 

1010 # value to the defined function 

1011 offset = -1 

1012 else: 

1013 # check this is not used in function decorators 

1014 frame = self 

1015 return frame._scope_lookup(node, name, offset) 

1016 

1017 def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: 

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

1019 

1020 :returns: The boolean value of this node. 

1021 For a :class:`Lambda` this is always ``True``. 

1022 """ 

1023 return True 

1024 

1025 def get_children(self): 

1026 yield self.args 

1027 yield self.body 

1028 

1029 def frame(self: _T, *, future: Literal[None, True] = None) -> _T: 

1030 """The node's frame node. 

1031 

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

1033 :class:`ClassDef` or :class:`Lambda`. 

1034 

1035 :returns: The node itself. 

1036 """ 

1037 return self 

1038 

1039 def getattr( 

1040 self, name: str, context: InferenceContext | None = None 

1041 ) -> list[NodeNG]: 

1042 if not name: 

1043 raise AttributeInferenceError(target=self, attribute=name, context=context) 

1044 

1045 found_attrs = [] 

1046 if name in self.instance_attrs: 

1047 found_attrs = self.instance_attrs[name] 

1048 if name in self.special_attributes: 

1049 found_attrs.append(self.special_attributes.lookup(name)) 

1050 if found_attrs: 

1051 return found_attrs 

1052 raise AttributeInferenceError(target=self, attribute=name) 

1053 

1054 def _infer( 

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

1056 ) -> Generator[Lambda, None, None]: 

1057 yield self 

1058 

1059 def _get_yield_nodes_skip_functions(self): 

1060 """A Lambda node can contain a Yield node in the body.""" 

1061 yield from self.body._get_yield_nodes_skip_functions() 

1062 

1063 

1064class FunctionDef( 

1065 _base_nodes.MultiLineBlockNode, 

1066 _base_nodes.FilterStmtsBaseNode, 

1067 _base_nodes.Statement, 

1068 LocalsDictNodeNG, 

1069): 

1070 """Class representing an :class:`ast.FunctionDef`. 

1071 

1072 >>> import astroid 

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

1074 ... def my_func(arg): 

1075 ... return arg + 1 

1076 ... ''') 

1077 >>> node 

1078 <FunctionDef.my_func l.2 at 0x7f23b2e71e10> 

1079 """ 

1080 

1081 _astroid_fields = ( 

1082 "decorators", 

1083 "args", 

1084 "returns", 

1085 "type_params", 

1086 "doc_node", 

1087 "body", 

1088 ) 

1089 _multi_line_block_fields = ("body",) 

1090 returns = None 

1091 

1092 decorators: node_classes.Decorators | None 

1093 """The decorators that are applied to this method or function.""" 

1094 

1095 doc_node: Const | None 

1096 """The doc node associated with this node.""" 

1097 

1098 args: Arguments 

1099 """The arguments that the function takes.""" 

1100 

1101 is_function = True 

1102 """Whether this node indicates a function. 

1103 

1104 For a :class:`FunctionDef` this is always ``True``. 

1105 

1106 :type: bool 

1107 """ 

1108 type_annotation = None 

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

1110 

1111 :type: NodeNG or None 

1112 """ 

1113 type_comment_args = None 

1114 """ 

1115 If present, this will contain the type annotation for arguments 

1116 passed by a type comment 

1117 """ 

1118 type_comment_returns = None 

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

1120 # attributes below are set by the builder module or by raw factories 

1121 _other_fields = ("name", "position") 

1122 _other_other_fields = ( 

1123 "locals", 

1124 "_type", 

1125 "type_comment_returns", 

1126 "type_comment_args", 

1127 ) 

1128 _type = None 

1129 

1130 name = "<functiondef>" 

1131 

1132 special_attributes = FunctionModel() 

1133 """The names of special attributes that this function has.""" 

1134 

1135 def __init__( 

1136 self, 

1137 name: str, 

1138 lineno: int, 

1139 col_offset: int, 

1140 parent: NodeNG, 

1141 *, 

1142 end_lineno: int | None, 

1143 end_col_offset: int | None, 

1144 ) -> None: 

1145 self.name = name 

1146 """The name of the function.""" 

1147 

1148 self.locals = {} 

1149 """A map of the name of a local variable to the node defining it.""" 

1150 

1151 self.body: list[NodeNG] = [] 

1152 """The contents of the function body.""" 

1153 

1154 self.type_params: list[nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple] = ( 

1155 [] 

1156 ) 

1157 """PEP 695 (Python 3.12+) type params, e.g. first 'T' in def func[T]() -> T: ...""" 

1158 

1159 self.instance_attrs: dict[str, list[NodeNG]] = {} 

1160 

1161 super().__init__( 

1162 lineno=lineno, 

1163 col_offset=col_offset, 

1164 end_lineno=end_lineno, 

1165 end_col_offset=end_col_offset, 

1166 parent=parent, 

1167 ) 

1168 if parent and not isinstance(parent, Unknown): 

1169 frame = parent.frame() 

1170 frame.set_local(name, self) 

1171 

1172 def postinit( 

1173 self, 

1174 args: Arguments, 

1175 body: list[NodeNG], 

1176 decorators: node_classes.Decorators | None = None, 

1177 returns=None, 

1178 type_comment_returns=None, 

1179 type_comment_args=None, 

1180 *, 

1181 position: Position | None = None, 

1182 doc_node: Const | None = None, 

1183 type_params: ( 

1184 list[nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple] | None 

1185 ) = None, 

1186 ): 

1187 """Do some setup after initialisation. 

1188 

1189 :param args: The arguments that the function takes. 

1190 

1191 :param body: The contents of the function body. 

1192 

1193 :param decorators: The decorators that are applied to this 

1194 method or function. 

1195 :params type_comment_returns: 

1196 The return type annotation passed via a type comment. 

1197 :params type_comment_args: 

1198 The args type annotation passed via a type comment. 

1199 :params position: 

1200 Position of function keyword(s) and name. 

1201 :param doc_node: 

1202 The doc node associated with this node. 

1203 :param type_params: 

1204 The type_params associated with this node. 

1205 """ 

1206 self.args = args 

1207 self.body = body 

1208 self.decorators = decorators 

1209 self.returns = returns 

1210 self.type_comment_returns = type_comment_returns 

1211 self.type_comment_args = type_comment_args 

1212 self.position = position 

1213 self.doc_node = doc_node 

1214 self.type_params = type_params or [] 

1215 

1216 @cached_property 

1217 def extra_decorators(self) -> list[node_classes.Call]: 

1218 """The extra decorators that this function can have. 

1219 

1220 Additional decorators are considered when they are used as 

1221 assignments, as in ``method = staticmethod(method)``. 

1222 The property will return all the callables that are used for 

1223 decoration. 

1224 """ 

1225 if not self.parent or not isinstance(frame := self.parent.frame(), ClassDef): 

1226 return [] 

1227 

1228 decorators: list[node_classes.Call] = [] 

1229 for assign in frame._assign_nodes_in_scope: 

1230 if isinstance(assign.value, node_classes.Call) and isinstance( 

1231 assign.value.func, node_classes.Name 

1232 ): 

1233 for assign_node in assign.targets: 

1234 if not isinstance(assign_node, node_classes.AssignName): 

1235 # Support only `name = callable(name)` 

1236 continue 

1237 

1238 if assign_node.name != self.name: 

1239 # Interested only in the assignment nodes that 

1240 # decorates the current method. 

1241 continue 

1242 try: 

1243 meth = frame[self.name] 

1244 except KeyError: 

1245 continue 

1246 else: 

1247 # Must be a function and in the same frame as the 

1248 # original method. 

1249 if ( 

1250 isinstance(meth, FunctionDef) 

1251 and assign_node.frame() == frame 

1252 ): 

1253 decorators.append(assign.value) 

1254 return decorators 

1255 

1256 def pytype(self) -> Literal["builtins.instancemethod", "builtins.function"]: 

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

1258 

1259 :returns: The name of the type. 

1260 """ 

1261 if "method" in self.type: 

1262 return "builtins.instancemethod" 

1263 return "builtins.function" 

1264 

1265 def display_type(self) -> str: 

1266 """A human readable type of this node. 

1267 

1268 :returns: The type of this node. 

1269 :rtype: str 

1270 """ 

1271 if "method" in self.type: 

1272 return "Method" 

1273 return "Function" 

1274 

1275 def callable(self) -> Literal[True]: 

1276 return True 

1277 

1278 def argnames(self) -> list[str]: 

1279 """Get the names of each of the arguments, including that 

1280 of the collections of variable-length arguments ("args", "kwargs", 

1281 etc.), as well as positional-only and keyword-only arguments. 

1282 

1283 :returns: The names of the arguments. 

1284 :rtype: list(str) 

1285 """ 

1286 if self.args.arguments: # maybe None with builtin functions 

1287 names = [elt.name for elt in self.args.arguments] 

1288 else: 

1289 names = [] 

1290 

1291 return names 

1292 

1293 def getattr( 

1294 self, name: str, context: InferenceContext | None = None 

1295 ) -> list[NodeNG]: 

1296 if not name: 

1297 raise AttributeInferenceError(target=self, attribute=name, context=context) 

1298 

1299 found_attrs = [] 

1300 if name in self.instance_attrs: 

1301 found_attrs = self.instance_attrs[name] 

1302 if name in self.special_attributes: 

1303 found_attrs.append(self.special_attributes.lookup(name)) 

1304 if found_attrs: 

1305 return found_attrs 

1306 raise AttributeInferenceError(target=self, attribute=name) 

1307 

1308 @cached_property 

1309 def type(self) -> str: # pylint: disable=too-many-return-statements # noqa: C901 

1310 """The function type for this node. 

1311 

1312 Possible values are: method, function, staticmethod, classmethod. 

1313 """ 

1314 for decorator in self.extra_decorators: 

1315 if decorator.func.name in BUILTIN_DESCRIPTORS: 

1316 return decorator.func.name 

1317 

1318 if not self.parent: 

1319 raise ParentMissingError(target=self) 

1320 

1321 frame = self.parent.frame() 

1322 type_name = "function" 

1323 if isinstance(frame, ClassDef): 

1324 if self.name == "__new__": 

1325 return "classmethod" 

1326 if self.name == "__init_subclass__": 

1327 return "classmethod" 

1328 if self.name == "__class_getitem__": 

1329 return "classmethod" 

1330 

1331 type_name = "method" 

1332 

1333 if not self.decorators: 

1334 return type_name 

1335 

1336 for node in self.decorators.nodes: 

1337 if isinstance(node, node_classes.Name): 

1338 if node.name in BUILTIN_DESCRIPTORS: 

1339 return node.name 

1340 if ( 

1341 isinstance(node, node_classes.Attribute) 

1342 and isinstance(node.expr, node_classes.Name) 

1343 and node.expr.name == "builtins" 

1344 and node.attrname in BUILTIN_DESCRIPTORS 

1345 ): 

1346 return node.attrname 

1347 

1348 if isinstance(node, node_classes.Call): 

1349 # Handle the following case: 

1350 # @some_decorator(arg1, arg2) 

1351 # def func(...) 

1352 # 

1353 try: 

1354 current = next(node.func.infer()) 

1355 except (InferenceError, StopIteration): 

1356 continue 

1357 _type = _infer_decorator_callchain(current) 

1358 if _type is not None: 

1359 return _type 

1360 

1361 try: 

1362 for inferred in node.infer(): 

1363 # Check to see if this returns a static or a class method. 

1364 _type = _infer_decorator_callchain(inferred) 

1365 if _type is not None: 

1366 return _type 

1367 

1368 if not isinstance(inferred, ClassDef): 

1369 continue 

1370 for ancestor in inferred.ancestors(): 

1371 if not isinstance(ancestor, ClassDef): 

1372 continue 

1373 if ancestor.is_subtype_of("builtins.classmethod"): 

1374 return "classmethod" 

1375 if ancestor.is_subtype_of("builtins.staticmethod"): 

1376 return "staticmethod" 

1377 except InferenceError: 

1378 pass 

1379 return type_name 

1380 

1381 @cached_property 

1382 def fromlineno(self) -> int: 

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

1384 

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

1386 """ 

1387 # lineno is the line number of the first decorator, we want the def 

1388 # statement lineno. Similar to 'ClassDef.fromlineno' 

1389 lineno = self.lineno or 0 

1390 if self.decorators is not None: 

1391 lineno += sum( 

1392 node.tolineno - (node.lineno or 0) + 1 for node in self.decorators.nodes 

1393 ) 

1394 

1395 return lineno or 0 

1396 

1397 @cached_property 

1398 def blockstart_tolineno(self): 

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

1400 

1401 :type: int 

1402 """ 

1403 return self.args.tolineno 

1404 

1405 def implicit_parameters(self) -> Literal[0, 1]: 

1406 return 1 if self.is_bound() else 0 

1407 

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

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

1410 

1411 :param lineno: Unused. 

1412 

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

1414 """ 

1415 return self.fromlineno, self.tolineno 

1416 

1417 def igetattr( 

1418 self, name: str, context: InferenceContext | None = None 

1419 ) -> Iterator[InferenceResult]: 

1420 """Inferred getattr, which returns an iterator of inferred statements.""" 

1421 try: 

1422 return bases._infer_stmts(self.getattr(name, context), context, frame=self) 

1423 except AttributeInferenceError as error: 

1424 raise InferenceError( 

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

1426 ) from error 

1427 

1428 def is_method(self) -> bool: 

1429 """Check if this function node represents a method. 

1430 

1431 :returns: Whether this is a method. 

1432 """ 

1433 # check we are defined in a ClassDef, because this is usually expected 

1434 # (e.g. pylint...) when is_method() return True 

1435 return ( 

1436 self.type != "function" 

1437 and self.parent is not None 

1438 and isinstance(self.parent.frame(), ClassDef) 

1439 ) 

1440 

1441 def decoratornames(self, context: InferenceContext | None = None) -> set[str]: 

1442 """Get the qualified names of each of the decorators on this function. 

1443 

1444 :param context: 

1445 An inference context that can be passed to inference functions 

1446 :returns: The names of the decorators. 

1447 """ 

1448 result = set() 

1449 decoratornodes = [] 

1450 if self.decorators is not None: 

1451 decoratornodes += self.decorators.nodes 

1452 decoratornodes += self.extra_decorators 

1453 for decnode in decoratornodes: 

1454 try: 

1455 for infnode in decnode.infer(context=context): 

1456 result.add(infnode.qname()) 

1457 except InferenceError: 

1458 continue 

1459 return result 

1460 

1461 def is_bound(self) -> bool: 

1462 """Check if the function is bound to an instance or class. 

1463 

1464 :returns: Whether the function is bound to an instance or class. 

1465 """ 

1466 return self.type in {"method", "classmethod"} 

1467 

1468 def is_abstract(self, pass_is_abstract=True, any_raise_is_abstract=False) -> bool: 

1469 """Check if the method is abstract. 

1470 

1471 A method is considered abstract if any of the following is true: 

1472 * The only statement is 'raise NotImplementedError' 

1473 * The only statement is 'raise <SomeException>' and any_raise_is_abstract is True 

1474 * The only statement is 'pass' and pass_is_abstract is True 

1475 * The method is annotated with abc.astractproperty/abc.abstractmethod 

1476 

1477 :returns: Whether the method is abstract. 

1478 """ 

1479 if self.decorators: 

1480 for node in self.decorators.nodes: 

1481 try: 

1482 inferred = next(node.infer()) 

1483 except (InferenceError, StopIteration): 

1484 continue 

1485 if inferred and inferred.qname() in { 

1486 "abc.abstractproperty", 

1487 "abc.abstractmethod", 

1488 }: 

1489 return True 

1490 

1491 for child_node in self.body: 

1492 if isinstance(child_node, node_classes.Raise): 

1493 if any_raise_is_abstract: 

1494 return True 

1495 if child_node.raises_not_implemented(): 

1496 return True 

1497 return pass_is_abstract and isinstance(child_node, node_classes.Pass) 

1498 # empty function is the same as function with a single "pass" statement 

1499 if pass_is_abstract: 

1500 return True 

1501 

1502 return False 

1503 

1504 def is_generator(self) -> bool: 

1505 """Check if this is a generator function. 

1506 

1507 :returns: Whether this is a generator function. 

1508 """ 

1509 yields_without_lambdas = set(self._get_yield_nodes_skip_lambdas()) 

1510 yields_without_functions = set(self._get_yield_nodes_skip_functions()) 

1511 # Want an intersecting member that is neither in a lambda nor a function 

1512 return bool(yields_without_lambdas & yields_without_functions) 

1513 

1514 def _infer( 

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

1516 ) -> Generator[objects.Property | FunctionDef, None, InferenceErrorInfo]: 

1517 from astroid import objects # pylint: disable=import-outside-toplevel 

1518 

1519 if not self.decorators or not bases._is_property(self): 

1520 yield self 

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

1522 

1523 # When inferring a property, we instantiate a new `objects.Property` object, 

1524 # which in turn, because it inherits from `FunctionDef`, sets itself in the locals 

1525 # of the wrapping frame. This means that every time we infer a property, the locals 

1526 # are mutated with a new instance of the property. To avoid this, we detect this 

1527 # scenario and avoid passing the `parent` argument to the constructor. 

1528 if not self.parent: 

1529 raise ParentMissingError(target=self) 

1530 parent_frame = self.parent.frame() 

1531 property_already_in_parent_locals = self.name in parent_frame.locals and any( 

1532 isinstance(val, objects.Property) for val in parent_frame.locals[self.name] 

1533 ) 

1534 # We also don't want to pass parent if the definition is within a Try node 

1535 if isinstance( 

1536 self.parent, 

1537 (node_classes.Try, node_classes.If), 

1538 ): 

1539 property_already_in_parent_locals = True 

1540 

1541 prop_func = objects.Property( 

1542 function=self, 

1543 name=self.name, 

1544 lineno=self.lineno, 

1545 parent=self.parent if not property_already_in_parent_locals else None, 

1546 col_offset=self.col_offset, 

1547 ) 

1548 if property_already_in_parent_locals: 

1549 prop_func.parent = self.parent 

1550 prop_func.postinit(body=[], args=self.args, doc_node=self.doc_node) 

1551 yield prop_func 

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

1553 

1554 def infer_yield_result(self, context: InferenceContext | None = None): 

1555 """Infer what the function yields when called 

1556 

1557 :returns: What the function yields 

1558 :rtype: iterable(NodeNG or Uninferable) or None 

1559 """ 

1560 for yield_ in self.nodes_of_class(node_classes.Yield): 

1561 if yield_.value is None: 

1562 const = node_classes.Const(None) 

1563 const.parent = yield_ 

1564 const.lineno = yield_.lineno 

1565 yield const 

1566 elif yield_.scope() == self: 

1567 yield from yield_.value.infer(context=context) 

1568 

1569 def infer_call_result( 

1570 self, 

1571 caller: SuccessfulInferenceResult | None, 

1572 context: InferenceContext | None = None, 

1573 ) -> Iterator[InferenceResult]: 

1574 """Infer what the function returns when called.""" 

1575 if self.is_generator(): 

1576 if isinstance(self, AsyncFunctionDef): 

1577 generator_cls: type[bases.Generator] = bases.AsyncGenerator 

1578 else: 

1579 generator_cls = bases.Generator 

1580 result = generator_cls(self, generator_initial_context=context) 

1581 yield result 

1582 return 

1583 # This is really a gigantic hack to work around metaclass generators 

1584 # that return transient class-generating functions. Pylint's AST structure 

1585 # cannot handle a base class object that is only used for calling __new__, 

1586 # but does not contribute to the inheritance structure itself. We inject 

1587 # a fake class into the hierarchy here for several well-known metaclass 

1588 # generators, and filter it out later. 

1589 if ( 

1590 self.name == "with_metaclass" 

1591 and caller is not None 

1592 and self.args.args 

1593 and len(self.args.args) == 1 

1594 and self.args.vararg is not None 

1595 ): 

1596 if isinstance(caller.args, Arguments): 

1597 assert caller.args.args is not None 

1598 metaclass = next(caller.args.args[0].infer(context), None) 

1599 elif isinstance(caller.args, list): 

1600 metaclass = next(caller.args[0].infer(context), None) 

1601 else: 

1602 raise TypeError( # pragma: no cover 

1603 f"caller.args was neither Arguments nor list; got {type(caller.args)}" 

1604 ) 

1605 if isinstance(metaclass, ClassDef): 

1606 try: 

1607 class_bases = [ 

1608 # Find the first non-None inferred base value 

1609 next( 

1610 b 

1611 for b in arg.infer( 

1612 context=context.clone() if context else context 

1613 ) 

1614 if not (isinstance(b, Const) and b.value is None) 

1615 ) 

1616 for arg in caller.args[1:] 

1617 ] 

1618 except StopIteration as e: 

1619 raise InferenceError(node=caller.args[1:], context=context) from e 

1620 new_class = ClassDef( 

1621 name="temporary_class", 

1622 lineno=0, 

1623 col_offset=0, 

1624 end_lineno=0, 

1625 end_col_offset=0, 

1626 parent=self, 

1627 ) 

1628 new_class.hide = True 

1629 new_class.postinit( 

1630 bases=[ 

1631 base 

1632 for base in class_bases 

1633 if not isinstance(base, util.UninferableBase) 

1634 ], 

1635 body=[], 

1636 decorators=None, 

1637 metaclass=metaclass, 

1638 ) 

1639 yield new_class 

1640 return 

1641 returns = self._get_return_nodes_skip_functions() 

1642 

1643 first_return = next(returns, None) 

1644 if not first_return: 

1645 if self.body: 

1646 if self.is_abstract(pass_is_abstract=True, any_raise_is_abstract=True): 

1647 yield util.Uninferable 

1648 else: 

1649 yield node_classes.Const(None) 

1650 return 

1651 

1652 raise InferenceError("The function does not have any return statements") 

1653 

1654 for returnnode in itertools.chain((first_return,), returns): 

1655 if returnnode.value is None: 

1656 yield node_classes.Const(None) 

1657 else: 

1658 try: 

1659 yield from returnnode.value.infer(context) 

1660 except InferenceError: 

1661 yield util.Uninferable 

1662 

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

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

1665 

1666 :returns: The boolean value of this node. 

1667 For a :class:`FunctionDef` this is always ``True``. 

1668 """ 

1669 return True 

1670 

1671 def get_children(self): 

1672 if self.decorators is not None: 

1673 yield self.decorators 

1674 

1675 yield self.args 

1676 

1677 if self.returns is not None: 

1678 yield self.returns 

1679 yield from self.type_params 

1680 

1681 yield from self.body 

1682 

1683 def scope_lookup( 

1684 self, node: LookupMixIn, name: str, offset: int = 0 

1685 ) -> tuple[LocalsDictNodeNG, list[nodes.NodeNG]]: 

1686 """Lookup where the given name is assigned.""" 

1687 if name == "__class__": 

1688 # __class__ is an implicit closure reference created by the compiler 

1689 # if any methods in a class body refer to either __class__ or super. 

1690 # In our case, we want to be able to look it up in the current scope 

1691 # when `__class__` is being used. 

1692 if self.parent and isinstance(frame := self.parent.frame(), ClassDef): 

1693 return self, [frame] 

1694 

1695 if (self.args.defaults and node in self.args.defaults) or ( 

1696 self.args.kw_defaults and node in self.args.kw_defaults 

1697 ): 

1698 if not self.parent: 

1699 raise ParentMissingError(target=self) 

1700 frame = self.parent.frame() 

1701 # line offset to avoid that def func(f=func) resolve the default 

1702 # value to the defined function 

1703 offset = -1 

1704 else: 

1705 # check this is not used in function decorators 

1706 frame = self 

1707 return frame._scope_lookup(node, name, offset) 

1708 

1709 def frame(self: _T, *, future: Literal[None, True] = None) -> _T: 

1710 """The node's frame node. 

1711 

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

1713 :class:`ClassDef` or :class:`Lambda`. 

1714 

1715 :returns: The node itself. 

1716 """ 

1717 return self 

1718 

1719 

1720class AsyncFunctionDef(FunctionDef): 

1721 """Class representing an :class:`ast.FunctionDef` node. 

1722 

1723 A :class:`AsyncFunctionDef` is an asynchronous function 

1724 created with the `async` keyword. 

1725 

1726 >>> import astroid 

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

1728 async def func(things): 

1729 async for thing in things: 

1730 print(thing) 

1731 ''') 

1732 >>> node 

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

1734 >>> node.body[0] 

1735 <AsyncFor l.3 at 0x7f23b2e417b8> 

1736 """ 

1737 

1738 

1739def _is_metaclass(klass, seen=None, context: InferenceContext | None = None) -> bool: 

1740 """Return if the given class can be 

1741 used as a metaclass. 

1742 """ 

1743 if klass.name == "type": 

1744 return True 

1745 if seen is None: 

1746 seen = set() 

1747 for base in klass.bases: 

1748 try: 

1749 for baseobj in base.infer(context=context): 

1750 baseobj_name = baseobj.qname() 

1751 if baseobj_name in seen: 

1752 continue 

1753 

1754 seen.add(baseobj_name) 

1755 if isinstance(baseobj, bases.Instance): 

1756 # not abstract 

1757 return False 

1758 if baseobj is klass: 

1759 continue 

1760 if not isinstance(baseobj, ClassDef): 

1761 continue 

1762 if baseobj._type == "metaclass": 

1763 return True 

1764 if _is_metaclass(baseobj, seen, context=context): 

1765 return True 

1766 except InferenceError: 

1767 continue 

1768 return False 

1769 

1770 

1771def _class_type(klass, ancestors=None, context: InferenceContext | None = None): 

1772 """return a ClassDef node type to differ metaclass and exception 

1773 from 'regular' classes 

1774 """ 

1775 # XXX we have to store ancestors in case we have an ancestor loop 

1776 if klass._type is not None: 

1777 return klass._type 

1778 if _is_metaclass(klass, context=context): 

1779 klass._type = "metaclass" 

1780 elif klass.name.endswith("Exception"): 

1781 klass._type = "exception" 

1782 else: 

1783 if ancestors is None: 

1784 ancestors = set() 

1785 klass_name = klass.qname() 

1786 if klass_name in ancestors: 

1787 # XXX we are in loop ancestors, and have found no type 

1788 klass._type = "class" 

1789 return "class" 

1790 ancestors.add(klass_name) 

1791 for base in klass.ancestors(recurs=False): 

1792 name = _class_type(base, ancestors) 

1793 if name != "class": 

1794 if name == "metaclass" and not _is_metaclass(klass): 

1795 # don't propagate it if the current class 

1796 # can't be a metaclass 

1797 continue 

1798 klass._type = base.type 

1799 break 

1800 if klass._type is None: 

1801 klass._type = "class" 

1802 return klass._type 

1803 

1804 

1805def get_wrapping_class(node): 

1806 """Get the class that wraps the given node. 

1807 

1808 We consider that a class wraps a node if the class 

1809 is a parent for the said node. 

1810 

1811 :returns: The class that wraps the given node 

1812 :rtype: ClassDef or None 

1813 """ 

1814 

1815 klass = node.frame() 

1816 while klass is not None and not isinstance(klass, ClassDef): 

1817 if klass.parent is None: 

1818 klass = None 

1819 else: 

1820 klass = klass.parent.frame() 

1821 return klass 

1822 

1823 

1824class ClassDef( # pylint: disable=too-many-instance-attributes 

1825 _base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG, _base_nodes.Statement 

1826): 

1827 """Class representing an :class:`ast.ClassDef` node. 

1828 

1829 >>> import astroid 

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

1831 class Thing: 

1832 def my_meth(self, arg): 

1833 return arg + self.offset 

1834 ''') 

1835 >>> node 

1836 <ClassDef.Thing l.2 at 0x7f23b2e9e748> 

1837 """ 

1838 

1839 # some of the attributes below are set by the builder module or 

1840 # by a raw factories 

1841 

1842 # a dictionary of class instances attributes 

1843 _astroid_fields = ( 

1844 "decorators", 

1845 "bases", 

1846 "keywords", 

1847 "doc_node", 

1848 "body", 

1849 "type_params", 

1850 ) # name 

1851 

1852 decorators = None 

1853 """The decorators that are applied to this class. 

1854 

1855 :type: Decorators or None 

1856 """ 

1857 special_attributes = ClassModel() 

1858 """The names of special attributes that this class has. 

1859 

1860 :type: objectmodel.ClassModel 

1861 """ 

1862 

1863 _type = None 

1864 _metaclass: NodeNG | None = None 

1865 _metaclass_hack = False 

1866 hide = False 

1867 type = property( 

1868 _class_type, 

1869 doc=( 

1870 "The class type for this node.\n\n" 

1871 "Possible values are: class, metaclass, exception.\n\n" 

1872 ":type: str" 

1873 ), 

1874 ) 

1875 _other_fields = ("name", "is_dataclass", "position") 

1876 _other_other_fields = ("locals", "_newstyle") 

1877 _newstyle: bool | None = None 

1878 

1879 def __init__( 

1880 self, 

1881 name: str, 

1882 lineno: int, 

1883 col_offset: int, 

1884 parent: NodeNG, 

1885 *, 

1886 end_lineno: int | None, 

1887 end_col_offset: int | None, 

1888 ) -> None: 

1889 self.instance_attrs: dict[str, NodeNG] = {} 

1890 self.locals = {} 

1891 """A map of the name of a local variable to the node defining it.""" 

1892 

1893 self.keywords: list[node_classes.Keyword] = [] 

1894 """The keywords given to the class definition. 

1895 

1896 This is usually for :pep:`3115` style metaclass declaration. 

1897 """ 

1898 

1899 self.bases: list[SuccessfulInferenceResult] = [] 

1900 """What the class inherits from.""" 

1901 

1902 self.body: list[NodeNG] = [] 

1903 """The contents of the class body.""" 

1904 

1905 self.name = name 

1906 """The name of the class.""" 

1907 

1908 self.decorators = None 

1909 """The decorators that are applied to this class.""" 

1910 

1911 self.doc_node: Const | None = None 

1912 """The doc node associated with this node.""" 

1913 

1914 self.is_dataclass: bool = False 

1915 """Whether this class is a dataclass.""" 

1916 

1917 self.type_params: list[nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple] = ( 

1918 [] 

1919 ) 

1920 """PEP 695 (Python 3.12+) type params, e.g. class MyClass[T]: ...""" 

1921 

1922 super().__init__( 

1923 lineno=lineno, 

1924 col_offset=col_offset, 

1925 end_lineno=end_lineno, 

1926 end_col_offset=end_col_offset, 

1927 parent=parent, 

1928 ) 

1929 if parent and not isinstance(parent, Unknown): 

1930 parent.frame().set_local(name, self) 

1931 

1932 for local_name, node in self.implicit_locals(): 

1933 self.add_local_node(node, local_name) 

1934 

1935 infer_binary_op: ClassVar[InferBinaryOp[ClassDef]] = ( 

1936 protocols.instance_class_infer_binary_op 

1937 ) 

1938 

1939 def implicit_parameters(self) -> Literal[1]: 

1940 return 1 

1941 

1942 def implicit_locals(self): 

1943 """Get implicitly defined class definition locals. 

1944 

1945 :returns: the the name and Const pair for each local 

1946 :rtype: tuple(tuple(str, node_classes.Const), ...) 

1947 """ 

1948 locals_ = (("__module__", self.special_attributes.attr___module__),) 

1949 # __qualname__ is defined in PEP3155 

1950 locals_ += (("__qualname__", self.special_attributes.attr___qualname__),) 

1951 return locals_ 

1952 

1953 # pylint: disable=redefined-outer-name 

1954 def postinit( 

1955 self, 

1956 bases: list[SuccessfulInferenceResult], 

1957 body: list[NodeNG], 

1958 decorators: node_classes.Decorators | None, 

1959 newstyle: bool | None = None, 

1960 metaclass: NodeNG | None = None, 

1961 keywords: list[node_classes.Keyword] | None = None, 

1962 *, 

1963 position: Position | None = None, 

1964 doc_node: Const | None = None, 

1965 type_params: ( 

1966 list[nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple] | None 

1967 ) = None, 

1968 ) -> None: 

1969 if keywords is not None: 

1970 self.keywords = keywords 

1971 self.bases = bases 

1972 self.body = body 

1973 self.decorators = decorators 

1974 self._newstyle = newstyle 

1975 self._metaclass = metaclass 

1976 self.position = position 

1977 self.doc_node = doc_node 

1978 self.type_params = type_params or [] 

1979 

1980 def _newstyle_impl(self, context: InferenceContext | None = None): 

1981 if context is None: 

1982 context = InferenceContext() 

1983 if self._newstyle is not None: 

1984 return self._newstyle 

1985 for base in self.ancestors(recurs=False, context=context): 

1986 if base._newstyle_impl(context): 

1987 self._newstyle = True 

1988 break 

1989 klass = self.declared_metaclass() 

1990 # could be any callable, we'd need to infer the result of klass(name, 

1991 # bases, dict). punt if it's not a class node. 

1992 if klass is not None and isinstance(klass, ClassDef): 

1993 self._newstyle = klass._newstyle_impl(context) 

1994 if self._newstyle is None: 

1995 self._newstyle = False 

1996 return self._newstyle 

1997 

1998 _newstyle = None 

1999 newstyle = property( 

2000 _newstyle_impl, 

2001 doc=("Whether this is a new style class or not\n\n" ":type: bool or None"), 

2002 ) 

2003 

2004 @cached_property 

2005 def fromlineno(self) -> int: 

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

2007 

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

2009 """ 

2010 if IS_PYPY and PY38 and not PYPY_7_3_11_PLUS: 

2011 # For Python < 3.8 the lineno is the line number of the first decorator. 

2012 # We want the class statement lineno. Similar to 'FunctionDef.fromlineno' 

2013 # PyPy (3.8): Fixed with version v7.3.11 

2014 lineno = self.lineno or 0 

2015 if self.decorators is not None: 

2016 lineno += sum( 

2017 node.tolineno - (node.lineno or 0) + 1 

2018 for node in self.decorators.nodes 

2019 ) 

2020 

2021 return lineno or 0 

2022 return super().fromlineno 

2023 

2024 @cached_property 

2025 def blockstart_tolineno(self): 

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

2027 

2028 :type: int 

2029 """ 

2030 if self.bases: 

2031 return self.bases[-1].tolineno 

2032 

2033 return self.fromlineno 

2034 

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

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

2037 

2038 :param lineno: Unused. 

2039 

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

2041 """ 

2042 return self.fromlineno, self.tolineno 

2043 

2044 def pytype(self) -> Literal["builtins.type", "builtins.classobj"]: 

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

2046 

2047 :returns: The name of the type. 

2048 """ 

2049 if self.newstyle: 

2050 return "builtins.type" 

2051 return "builtins.classobj" 

2052 

2053 def display_type(self) -> str: 

2054 """A human readable type of this node. 

2055 

2056 :returns: The type of this node. 

2057 :rtype: str 

2058 """ 

2059 return "Class" 

2060 

2061 def callable(self) -> bool: 

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

2063 

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

2065 For a :class:`ClassDef` this is always ``True``. 

2066 """ 

2067 return True 

2068 

2069 def is_subtype_of(self, type_name, context: InferenceContext | None = None) -> bool: 

2070 """Whether this class is a subtype of the given type. 

2071 

2072 :param type_name: The name of the type of check against. 

2073 :type type_name: str 

2074 

2075 :returns: Whether this class is a subtype of the given type. 

2076 """ 

2077 if self.qname() == type_name: 

2078 return True 

2079 

2080 return any(anc.qname() == type_name for anc in self.ancestors(context=context)) 

2081 

2082 def _infer_type_call(self, caller, context): 

2083 try: 

2084 name_node = next(caller.args[0].infer(context)) 

2085 except StopIteration as e: 

2086 raise InferenceError(node=caller.args[0], context=context) from e 

2087 if isinstance(name_node, node_classes.Const) and isinstance( 

2088 name_node.value, str 

2089 ): 

2090 name = name_node.value 

2091 else: 

2092 return util.Uninferable 

2093 

2094 result = ClassDef( 

2095 name, 

2096 lineno=0, 

2097 col_offset=0, 

2098 end_lineno=0, 

2099 end_col_offset=0, 

2100 parent=Unknown(), 

2101 ) 

2102 

2103 # Get the bases of the class. 

2104 try: 

2105 class_bases = next(caller.args[1].infer(context)) 

2106 except StopIteration as e: 

2107 raise InferenceError(node=caller.args[1], context=context) from e 

2108 if isinstance(class_bases, (node_classes.Tuple, node_classes.List)): 

2109 bases = [] 

2110 for base in class_bases.itered(): 

2111 inferred = next(base.infer(context=context), None) 

2112 if inferred: 

2113 bases.append( 

2114 node_classes.EvaluatedObject(original=base, value=inferred) 

2115 ) 

2116 result.bases = bases 

2117 else: 

2118 # There is currently no AST node that can represent an 'unknown' 

2119 # node (Uninferable is not an AST node), therefore we simply return Uninferable here 

2120 # although we know at least the name of the class. 

2121 return util.Uninferable 

2122 

2123 # Get the members of the class 

2124 try: 

2125 members = next(caller.args[2].infer(context)) 

2126 except (InferenceError, StopIteration): 

2127 members = None 

2128 

2129 if members and isinstance(members, node_classes.Dict): 

2130 for attr, value in members.items: 

2131 if isinstance(attr, node_classes.Const) and isinstance(attr.value, str): 

2132 result.locals[attr.value] = [value] 

2133 

2134 result.parent = caller.parent 

2135 return result 

2136 

2137 def infer_call_result( 

2138 self, 

2139 caller: SuccessfulInferenceResult | None, 

2140 context: InferenceContext | None = None, 

2141 ) -> Iterator[InferenceResult]: 

2142 """infer what a class is returning when called""" 

2143 if self.is_subtype_of("builtins.type", context) and len(caller.args) == 3: 

2144 result = self._infer_type_call(caller, context) 

2145 yield result 

2146 return 

2147 

2148 dunder_call = None 

2149 try: 

2150 metaclass = self.metaclass(context=context) 

2151 if metaclass is not None: 

2152 # Only get __call__ if it's defined locally for the metaclass. 

2153 # Otherwise we will find ObjectModel.__call__ which will 

2154 # return an instance of the metaclass. Instantiating the class is 

2155 # handled later. 

2156 if "__call__" in metaclass.locals: 

2157 dunder_call = next(metaclass.igetattr("__call__", context)) 

2158 except (AttributeInferenceError, StopIteration): 

2159 pass 

2160 

2161 if dunder_call and dunder_call.qname() != "builtins.type.__call__": 

2162 # Call type.__call__ if not set metaclass 

2163 # (since type is the default metaclass) 

2164 context = bind_context_to_node(context, self) 

2165 context.callcontext.callee = dunder_call 

2166 yield from dunder_call.infer_call_result(caller, context) 

2167 else: 

2168 yield self.instantiate_class() 

2169 

2170 def scope_lookup( 

2171 self, node: LookupMixIn, name: str, offset: int = 0 

2172 ) -> tuple[LocalsDictNodeNG, list[nodes.NodeNG]]: 

2173 """Lookup where the given name is assigned. 

2174 

2175 :param node: The node to look for assignments up to. 

2176 Any assignments after the given node are ignored. 

2177 

2178 :param name: The name to find assignments for. 

2179 

2180 :param offset: The line offset to filter statements up to. 

2181 

2182 :returns: This scope node and the list of assignments associated to the 

2183 given name according to the scope where it has been found (locals, 

2184 globals or builtin). 

2185 """ 

2186 # If the name looks like a builtin name, just try to look 

2187 # into the upper scope of this class. We might have a 

2188 # decorator that it's poorly named after a builtin object 

2189 # inside this class. 

2190 lookup_upper_frame = ( 

2191 isinstance(node.parent, node_classes.Decorators) 

2192 and name in AstroidManager().builtins_module 

2193 ) 

2194 if ( 

2195 any( 

2196 node == base or base.parent_of(node) and not self.type_params 

2197 for base in self.bases 

2198 ) 

2199 or lookup_upper_frame 

2200 ): 

2201 # Handle the case where we have either a name 

2202 # in the bases of a class, which exists before 

2203 # the actual definition or the case where we have 

2204 # a Getattr node, with that name. 

2205 # 

2206 # name = ... 

2207 # class A(name): 

2208 # def name(self): ... 

2209 # 

2210 # import name 

2211 # class A(name.Name): 

2212 # def name(self): ... 

2213 if not self.parent: 

2214 raise ParentMissingError(target=self) 

2215 frame = self.parent.frame() 

2216 # line offset to avoid that class A(A) resolve the ancestor to 

2217 # the defined class 

2218 offset = -1 

2219 else: 

2220 frame = self 

2221 return frame._scope_lookup(node, name, offset) 

2222 

2223 @property 

2224 def basenames(self): 

2225 """The names of the parent classes 

2226 

2227 Names are given in the order they appear in the class definition. 

2228 

2229 :type: list(str) 

2230 """ 

2231 return [bnode.as_string() for bnode in self.bases] 

2232 

2233 def ancestors( 

2234 self, recurs: bool = True, context: InferenceContext | None = None 

2235 ) -> Generator[ClassDef, None, None]: 

2236 """Iterate over the base classes in prefixed depth first order. 

2237 

2238 :param recurs: Whether to recurse or return direct ancestors only. 

2239 

2240 :returns: The base classes 

2241 """ 

2242 # FIXME: should be possible to choose the resolution order 

2243 # FIXME: inference make infinite loops possible here 

2244 yielded = {self} 

2245 if context is None: 

2246 context = InferenceContext() 

2247 if not self.bases and self.qname() != "builtins.object": 

2248 # This should always be a ClassDef (which we don't assert for) 

2249 yield builtin_lookup("object")[1][0] # type: ignore[misc] 

2250 return 

2251 

2252 for stmt in self.bases: 

2253 with context.restore_path(): 

2254 try: 

2255 for baseobj in stmt.infer(context): 

2256 if not isinstance(baseobj, ClassDef): 

2257 if isinstance(baseobj, bases.Instance): 

2258 baseobj = baseobj._proxied 

2259 else: 

2260 continue 

2261 if not baseobj.hide: 

2262 if baseobj in yielded: 

2263 continue 

2264 yielded.add(baseobj) 

2265 yield baseobj 

2266 if not recurs: 

2267 continue 

2268 for grandpa in baseobj.ancestors(recurs=True, context=context): 

2269 if grandpa is self: 

2270 # This class is the ancestor of itself. 

2271 break 

2272 if grandpa in yielded: 

2273 continue 

2274 yielded.add(grandpa) 

2275 yield grandpa 

2276 except InferenceError: 

2277 continue 

2278 

2279 def local_attr_ancestors(self, name, context: InferenceContext | None = None): 

2280 """Iterate over the parents that define the given name. 

2281 

2282 :param name: The name to find definitions for. 

2283 :type name: str 

2284 

2285 :returns: The parents that define the given name. 

2286 :rtype: iterable(NodeNG) 

2287 """ 

2288 # Look up in the mro if we can. This will result in the 

2289 # attribute being looked up just as Python does it. 

2290 try: 

2291 ancestors: Iterable[ClassDef] = self.mro(context)[1:] 

2292 except MroError: 

2293 # Fallback to use ancestors, we can't determine 

2294 # a sane MRO. 

2295 ancestors = self.ancestors(context=context) 

2296 for astroid in ancestors: 

2297 if name in astroid: 

2298 yield astroid 

2299 

2300 def instance_attr_ancestors(self, name, context: InferenceContext | None = None): 

2301 """Iterate over the parents that define the given name as an attribute. 

2302 

2303 :param name: The name to find definitions for. 

2304 :type name: str 

2305 

2306 :returns: The parents that define the given name as 

2307 an instance attribute. 

2308 :rtype: iterable(NodeNG) 

2309 """ 

2310 for astroid in self.ancestors(context=context): 

2311 if name in astroid.instance_attrs: 

2312 yield astroid 

2313 

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

2315 """Whether this class directly inherits from the given node. 

2316 

2317 :param node: The node to check for. 

2318 :type node: NodeNG 

2319 

2320 :returns: Whether this class directly inherits from the given node. 

2321 """ 

2322 return node in self.bases 

2323 

2324 def local_attr(self, name, context: InferenceContext | None = None): 

2325 """Get the list of assign nodes associated to the given name. 

2326 

2327 Assignments are looked for in both this class and in parents. 

2328 

2329 :returns: The list of assignments to the given name. 

2330 :rtype: list(NodeNG) 

2331 

2332 :raises AttributeInferenceError: If no attribute with this name 

2333 can be found in this class or parent classes. 

2334 """ 

2335 result = [] 

2336 if name in self.locals: 

2337 result = self.locals[name] 

2338 else: 

2339 class_node = next(self.local_attr_ancestors(name, context), None) 

2340 if class_node: 

2341 result = class_node.locals[name] 

2342 result = [n for n in result if not isinstance(n, node_classes.DelAttr)] 

2343 if result: 

2344 return result 

2345 raise AttributeInferenceError(target=self, attribute=name, context=context) 

2346 

2347 def instance_attr(self, name, context: InferenceContext | None = None): 

2348 """Get the list of nodes associated to the given attribute name. 

2349 

2350 Assignments are looked for in both this class and in parents. 

2351 

2352 :returns: The list of assignments to the given name. 

2353 :rtype: list(NodeNG) 

2354 

2355 :raises AttributeInferenceError: If no attribute with this name 

2356 can be found in this class or parent classes. 

2357 """ 

2358 # Return a copy, so we don't modify self.instance_attrs, 

2359 # which could lead to infinite loop. 

2360 values = list(self.instance_attrs.get(name, [])) 

2361 # get all values from parents 

2362 for class_node in self.instance_attr_ancestors(name, context): 

2363 values += class_node.instance_attrs[name] 

2364 values = [n for n in values if not isinstance(n, node_classes.DelAttr)] 

2365 if values: 

2366 return values 

2367 raise AttributeInferenceError(target=self, attribute=name, context=context) 

2368 

2369 def instantiate_class(self) -> bases.Instance: 

2370 """Get an :class:`Instance` of the :class:`ClassDef` node. 

2371 

2372 :returns: An :class:`Instance` of the :class:`ClassDef` node 

2373 """ 

2374 from astroid import objects # pylint: disable=import-outside-toplevel 

2375 

2376 try: 

2377 if any(cls.name in EXCEPTION_BASE_CLASSES for cls in self.mro()): 

2378 # Subclasses of exceptions can be exception instances 

2379 return objects.ExceptionInstance(self) 

2380 except MroError: 

2381 pass 

2382 return bases.Instance(self) 

2383 

2384 def getattr( 

2385 self, 

2386 name: str, 

2387 context: InferenceContext | None = None, 

2388 class_context: bool = True, 

2389 ) -> list[InferenceResult]: 

2390 """Get an attribute from this class, using Python's attribute semantic. 

2391 

2392 This method doesn't look in the :attr:`instance_attrs` dictionary 

2393 since it is done by an :class:`Instance` proxy at inference time. 

2394 It may return an :class:`Uninferable` object if 

2395 the attribute has not been 

2396 found, but a ``__getattr__`` or ``__getattribute__`` method is defined. 

2397 If ``class_context`` is given, then it is considered that the 

2398 attribute is accessed from a class context, 

2399 e.g. ClassDef.attribute, otherwise it might have been accessed 

2400 from an instance as well. If ``class_context`` is used in that 

2401 case, then a lookup in the implicit metaclass and the explicit 

2402 metaclass will be done. 

2403 

2404 :param name: The attribute to look for. 

2405 

2406 :param class_context: Whether the attribute can be accessed statically. 

2407 

2408 :returns: The attribute. 

2409 

2410 :raises AttributeInferenceError: If the attribute cannot be inferred. 

2411 """ 

2412 if not name: 

2413 raise AttributeInferenceError(target=self, attribute=name, context=context) 

2414 

2415 # don't modify the list in self.locals! 

2416 values: list[InferenceResult] = list(self.locals.get(name, [])) 

2417 for classnode in self.ancestors(recurs=True, context=context): 

2418 values += classnode.locals.get(name, []) 

2419 

2420 if name in self.special_attributes and class_context and not values: 

2421 result = [self.special_attributes.lookup(name)] 

2422 if name == "__bases__": 

2423 # Need special treatment, since they are mutable 

2424 # and we need to return all the values. 

2425 result += values 

2426 return result 

2427 

2428 if class_context: 

2429 values += self._metaclass_lookup_attribute(name, context) 

2430 

2431 # Remove AnnAssigns without value, which are not attributes in the purest sense. 

2432 for value in values.copy(): 

2433 if isinstance(value, node_classes.AssignName): 

2434 stmt = value.statement() 

2435 if isinstance(stmt, node_classes.AnnAssign) and stmt.value is None: 

2436 values.pop(values.index(value)) 

2437 

2438 if not values: 

2439 raise AttributeInferenceError(target=self, attribute=name, context=context) 

2440 

2441 return values 

2442 

2443 @lru_cache(maxsize=1024) # noqa 

2444 def _metaclass_lookup_attribute(self, name, context): 

2445 """Search the given name in the implicit and the explicit metaclass.""" 

2446 attrs = set() 

2447 implicit_meta = self.implicit_metaclass() 

2448 context = copy_context(context) 

2449 metaclass = self.metaclass(context=context) 

2450 for cls in (implicit_meta, metaclass): 

2451 if cls and cls != self and isinstance(cls, ClassDef): 

2452 cls_attributes = self._get_attribute_from_metaclass(cls, name, context) 

2453 attrs.update(set(cls_attributes)) 

2454 return attrs 

2455 

2456 def _get_attribute_from_metaclass(self, cls, name, context): 

2457 from astroid import objects # pylint: disable=import-outside-toplevel 

2458 

2459 try: 

2460 attrs = cls.getattr(name, context=context, class_context=True) 

2461 except AttributeInferenceError: 

2462 return 

2463 

2464 for attr in bases._infer_stmts(attrs, context, frame=cls): 

2465 if not isinstance(attr, FunctionDef): 

2466 yield attr 

2467 continue 

2468 

2469 if isinstance(attr, objects.Property): 

2470 yield attr 

2471 continue 

2472 if attr.type == "classmethod": 

2473 # If the method is a classmethod, then it will 

2474 # be bound to the metaclass, not to the class 

2475 # from where the attribute is retrieved. 

2476 # get_wrapping_class could return None, so just 

2477 # default to the current class. 

2478 frame = get_wrapping_class(attr) or self 

2479 yield bases.BoundMethod(attr, frame) 

2480 elif attr.type == "staticmethod": 

2481 yield attr 

2482 else: 

2483 yield bases.BoundMethod(attr, self) 

2484 

2485 def igetattr( 

2486 self, 

2487 name: str, 

2488 context: InferenceContext | None = None, 

2489 class_context: bool = True, 

2490 ) -> Iterator[InferenceResult]: 

2491 """Infer the possible values of the given variable. 

2492 

2493 :param name: The name of the variable to infer. 

2494 

2495 :returns: The inferred possible values. 

2496 """ 

2497 from astroid import objects # pylint: disable=import-outside-toplevel 

2498 

2499 # set lookup name since this is necessary to infer on import nodes for 

2500 # instance 

2501 context = copy_context(context) 

2502 context.lookupname = name 

2503 

2504 metaclass = self.metaclass(context=context) 

2505 try: 

2506 attributes = self.getattr(name, context, class_context=class_context) 

2507 # If we have more than one attribute, make sure that those starting from 

2508 # the second one are from the same scope. This is to account for modifications 

2509 # to the attribute happening *after* the attribute's definition (e.g. AugAssigns on lists) 

2510 if len(attributes) > 1: 

2511 first_attr, attributes = attributes[0], attributes[1:] 

2512 first_scope = first_attr.parent.scope() 

2513 attributes = [first_attr] + [ 

2514 attr 

2515 for attr in attributes 

2516 if attr.parent and attr.parent.scope() == first_scope 

2517 ] 

2518 functions = [attr for attr in attributes if isinstance(attr, FunctionDef)] 

2519 if functions: 

2520 # Prefer only the last function, unless a property is involved. 

2521 last_function = functions[-1] 

2522 attributes = [ 

2523 a 

2524 for a in attributes 

2525 if a not in functions or a is last_function or bases._is_property(a) 

2526 ] 

2527 

2528 for inferred in bases._infer_stmts(attributes, context, frame=self): 

2529 # yield Uninferable object instead of descriptors when necessary 

2530 if not isinstance(inferred, node_classes.Const) and isinstance( 

2531 inferred, bases.Instance 

2532 ): 

2533 try: 

2534 inferred._proxied.getattr("__get__", context) 

2535 except AttributeInferenceError: 

2536 yield inferred 

2537 else: 

2538 yield util.Uninferable 

2539 elif isinstance(inferred, objects.Property): 

2540 function = inferred.function 

2541 if not class_context: 

2542 if not context.callcontext: 

2543 context.callcontext = CallContext( 

2544 args=function.args.arguments, callee=function 

2545 ) 

2546 # Through an instance so we can solve the property 

2547 yield from function.infer_call_result( 

2548 caller=self, context=context 

2549 ) 

2550 # If we're in a class context, we need to determine if the property 

2551 # was defined in the metaclass (a derived class must be a subclass of 

2552 # the metaclass of all its bases), in which case we can resolve the 

2553 # property. If not, i.e. the property is defined in some base class 

2554 # instead, then we return the property object 

2555 elif metaclass and function.parent.scope() is metaclass: 

2556 # Resolve a property as long as it is not accessed through 

2557 # the class itself. 

2558 yield from function.infer_call_result( 

2559 caller=self, context=context 

2560 ) 

2561 else: 

2562 yield inferred 

2563 else: 

2564 yield function_to_method(inferred, self) 

2565 except AttributeInferenceError as error: 

2566 if not name.startswith("__") and self.has_dynamic_getattr(context): 

2567 # class handle some dynamic attributes, return a Uninferable object 

2568 yield util.Uninferable 

2569 else: 

2570 raise InferenceError( 

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

2572 ) from error 

2573 

2574 def has_dynamic_getattr(self, context: InferenceContext | None = None) -> bool: 

2575 """Check if the class has a custom __getattr__ or __getattribute__. 

2576 

2577 If any such method is found and it is not from 

2578 builtins, nor from an extension module, then the function 

2579 will return True. 

2580 

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

2582 """ 

2583 

2584 def _valid_getattr(node): 

2585 root = node.root() 

2586 return root.name != "builtins" and getattr(root, "pure_python", None) 

2587 

2588 try: 

2589 return _valid_getattr(self.getattr("__getattr__", context)[0]) 

2590 except AttributeInferenceError: 

2591 # if self.newstyle: XXX cause an infinite recursion error 

2592 try: 

2593 getattribute = self.getattr("__getattribute__", context)[0] 

2594 return _valid_getattr(getattribute) 

2595 except AttributeInferenceError: 

2596 pass 

2597 return False 

2598 

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

2600 """Return the inference of a subscript. 

2601 

2602 This is basically looking up the method in the metaclass and calling it. 

2603 

2604 :returns: The inferred value of a subscript to this class. 

2605 :rtype: NodeNG 

2606 

2607 :raises AstroidTypeError: If this class does not define a 

2608 ``__getitem__`` method. 

2609 """ 

2610 try: 

2611 methods = lookup(self, "__getitem__", context=context) 

2612 except AttributeInferenceError as exc: 

2613 if isinstance(self, ClassDef): 

2614 # subscripting a class definition may be 

2615 # achieved thanks to __class_getitem__ method 

2616 # which is a classmethod defined in the class 

2617 # that supports subscript and not in the metaclass 

2618 try: 

2619 methods = self.getattr("__class_getitem__") 

2620 # Here it is assumed that the __class_getitem__ node is 

2621 # a FunctionDef. One possible improvement would be to deal 

2622 # with more generic inference. 

2623 except AttributeInferenceError: 

2624 raise AstroidTypeError(node=self, context=context) from exc 

2625 else: 

2626 raise AstroidTypeError(node=self, context=context) from exc 

2627 

2628 method = methods[0] 

2629 

2630 # Create a new callcontext for providing index as an argument. 

2631 new_context = bind_context_to_node(context, self) 

2632 new_context.callcontext = CallContext(args=[index], callee=method) 

2633 

2634 try: 

2635 return next(method.infer_call_result(self, new_context), util.Uninferable) 

2636 except AttributeError: 

2637 # Starting with python3.9, builtin types list, dict etc... 

2638 # are subscriptable thanks to __class_getitem___ classmethod. 

2639 # However in such case the method is bound to an EmptyNode and 

2640 # EmptyNode doesn't have infer_call_result method yielding to 

2641 # AttributeError 

2642 if ( 

2643 isinstance(method, node_classes.EmptyNode) 

2644 and self.pytype() == "builtins.type" 

2645 and PY39_PLUS 

2646 ): 

2647 return self 

2648 raise 

2649 except InferenceError: 

2650 return util.Uninferable 

2651 

2652 def methods(self): 

2653 """Iterate over all of the method defined in this class and its parents. 

2654 

2655 :returns: The methods defined on the class. 

2656 :rtype: iterable(FunctionDef) 

2657 """ 

2658 done = {} 

2659 for astroid in itertools.chain(iter((self,)), self.ancestors()): 

2660 for meth in astroid.mymethods(): 

2661 if meth.name in done: 

2662 continue 

2663 done[meth.name] = None 

2664 yield meth 

2665 

2666 def mymethods(self): 

2667 """Iterate over all of the method defined in this class only. 

2668 

2669 :returns: The methods defined on the class. 

2670 :rtype: iterable(FunctionDef) 

2671 """ 

2672 for member in self.values(): 

2673 if isinstance(member, FunctionDef): 

2674 yield member 

2675 

2676 def implicit_metaclass(self): 

2677 """Get the implicit metaclass of the current class. 

2678 

2679 For newstyle classes, this will return an instance of builtins.type. 

2680 For oldstyle classes, it will simply return None, since there's 

2681 no implicit metaclass there. 

2682 

2683 :returns: The metaclass. 

2684 :rtype: builtins.type or None 

2685 """ 

2686 if self.newstyle: 

2687 return builtin_lookup("type")[1][0] 

2688 return None 

2689 

2690 def declared_metaclass( 

2691 self, context: InferenceContext | None = None 

2692 ) -> SuccessfulInferenceResult | None: 

2693 """Return the explicit declared metaclass for the current class. 

2694 

2695 An explicit declared metaclass is defined 

2696 either by passing the ``metaclass`` keyword argument 

2697 in the class definition line (Python 3) or (Python 2) by 

2698 having a ``__metaclass__`` class attribute, or if there are 

2699 no explicit bases but there is a global ``__metaclass__`` variable. 

2700 

2701 :returns: The metaclass of this class, 

2702 or None if one could not be found. 

2703 """ 

2704 for base in self.bases: 

2705 try: 

2706 for baseobj in base.infer(context=context): 

2707 if isinstance(baseobj, ClassDef) and baseobj.hide: 

2708 self._metaclass = baseobj._metaclass 

2709 self._metaclass_hack = True 

2710 break 

2711 except InferenceError: 

2712 pass 

2713 

2714 if self._metaclass: 

2715 # Expects this from Py3k TreeRebuilder 

2716 try: 

2717 return next( 

2718 node 

2719 for node in self._metaclass.infer(context=context) 

2720 if not isinstance(node, util.UninferableBase) 

2721 ) 

2722 except (InferenceError, StopIteration): 

2723 return None 

2724 

2725 return None 

2726 

2727 def _find_metaclass( 

2728 self, seen: set[ClassDef] | None = None, context: InferenceContext | None = None 

2729 ) -> SuccessfulInferenceResult | None: 

2730 if seen is None: 

2731 seen = set() 

2732 seen.add(self) 

2733 

2734 klass = self.declared_metaclass(context=context) 

2735 if klass is None: 

2736 for parent in self.ancestors(context=context): 

2737 if parent not in seen: 

2738 klass = parent._find_metaclass(seen) 

2739 if klass is not None: 

2740 break 

2741 return klass 

2742 

2743 def metaclass( 

2744 self, context: InferenceContext | None = None 

2745 ) -> SuccessfulInferenceResult | None: 

2746 """Get the metaclass of this class. 

2747 

2748 If this class does not define explicitly a metaclass, 

2749 then the first defined metaclass in ancestors will be used 

2750 instead. 

2751 

2752 :returns: The metaclass of this class. 

2753 """ 

2754 return self._find_metaclass(context=context) 

2755 

2756 def has_metaclass_hack(self): 

2757 return self._metaclass_hack 

2758 

2759 def _islots(self): 

2760 """Return an iterator with the inferred slots.""" 

2761 if "__slots__" not in self.locals: 

2762 return None 

2763 for slots in self.igetattr("__slots__"): 

2764 # check if __slots__ is a valid type 

2765 for meth in ITER_METHODS: 

2766 try: 

2767 slots.getattr(meth) 

2768 break 

2769 except AttributeInferenceError: 

2770 continue 

2771 else: 

2772 continue 

2773 

2774 if isinstance(slots, node_classes.Const): 

2775 # a string. Ignore the following checks, 

2776 # but yield the node, only if it has a value 

2777 if slots.value: 

2778 yield slots 

2779 continue 

2780 if not hasattr(slots, "itered"): 

2781 # we can't obtain the values, maybe a .deque? 

2782 continue 

2783 

2784 if isinstance(slots, node_classes.Dict): 

2785 values = [item[0] for item in slots.items] 

2786 else: 

2787 values = slots.itered() 

2788 if isinstance(values, util.UninferableBase): 

2789 continue 

2790 if not values: 

2791 # Stop the iteration, because the class 

2792 # has an empty list of slots. 

2793 return values 

2794 

2795 for elt in values: 

2796 try: 

2797 for inferred in elt.infer(): 

2798 if not isinstance( 

2799 inferred, node_classes.Const 

2800 ) or not isinstance(inferred.value, str): 

2801 continue 

2802 if not inferred.value: 

2803 continue 

2804 yield inferred 

2805 except InferenceError: 

2806 continue 

2807 

2808 return None 

2809 

2810 def _slots(self): 

2811 if not self.newstyle: 

2812 raise NotImplementedError( 

2813 "The concept of slots is undefined for old-style classes." 

2814 ) 

2815 

2816 slots = self._islots() 

2817 try: 

2818 first = next(slots) 

2819 except StopIteration as exc: 

2820 # The class doesn't have a __slots__ definition or empty slots. 

2821 if exc.args and exc.args[0] not in ("", None): 

2822 return exc.args[0] 

2823 return None 

2824 return [first, *slots] 

2825 

2826 # Cached, because inferring them all the time is expensive 

2827 @cached_property 

2828 def _all_slots(self): 

2829 """Get all the slots for this node. 

2830 

2831 :returns: The names of slots for this class. 

2832 If the class doesn't define any slot, through the ``__slots__`` 

2833 variable, then this function will return a None. 

2834 Also, it will return None in the case the slots were not inferred. 

2835 :rtype: list(str) or None 

2836 """ 

2837 

2838 def grouped_slots( 

2839 mro: list[ClassDef], 

2840 ) -> Iterator[node_classes.NodeNG | None]: 

2841 for cls in mro: 

2842 # Not interested in object, since it can't have slots. 

2843 if cls.qname() == "builtins.object": 

2844 continue 

2845 try: 

2846 cls_slots = cls._slots() 

2847 except NotImplementedError: 

2848 continue 

2849 if cls_slots is not None: 

2850 yield from cls_slots 

2851 else: 

2852 yield None 

2853 

2854 if not self.newstyle: 

2855 raise NotImplementedError( 

2856 "The concept of slots is undefined for old-style classes." 

2857 ) 

2858 

2859 try: 

2860 mro = self.mro() 

2861 except MroError as e: 

2862 raise NotImplementedError( 

2863 "Cannot get slots while parsing mro fails." 

2864 ) from e 

2865 

2866 slots = list(grouped_slots(mro)) 

2867 if not all(slot is not None for slot in slots): 

2868 return None 

2869 

2870 return sorted(set(slots), key=lambda item: item.value) 

2871 

2872 def slots(self): 

2873 return self._all_slots 

2874 

2875 def _inferred_bases(self, context: InferenceContext | None = None): 

2876 # Similar with .ancestors, but the difference is when one base is inferred, 

2877 # only the first object is wanted. That's because 

2878 # we aren't interested in superclasses, as in the following 

2879 # example: 

2880 # 

2881 # class SomeSuperClass(object): pass 

2882 # class SomeClass(SomeSuperClass): pass 

2883 # class Test(SomeClass): pass 

2884 # 

2885 # Inferring SomeClass from the Test's bases will give 

2886 # us both SomeClass and SomeSuperClass, but we are interested 

2887 # only in SomeClass. 

2888 

2889 if context is None: 

2890 context = InferenceContext() 

2891 if not self.bases and self.qname() != "builtins.object": 

2892 yield builtin_lookup("object")[1][0] 

2893 return 

2894 

2895 for stmt in self.bases: 

2896 try: 

2897 # Find the first non-None inferred base value 

2898 baseobj = next( 

2899 b 

2900 for b in stmt.infer(context=context.clone()) 

2901 if not (isinstance(b, Const) and b.value is None) 

2902 ) 

2903 except (InferenceError, StopIteration): 

2904 continue 

2905 if isinstance(baseobj, bases.Instance): 

2906 baseobj = baseobj._proxied 

2907 if not isinstance(baseobj, ClassDef): 

2908 continue 

2909 if not baseobj.hide: 

2910 yield baseobj 

2911 else: 

2912 yield from baseobj.bases 

2913 

2914 def _compute_mro(self, context: InferenceContext | None = None): 

2915 if self.qname() == "builtins.object": 

2916 return [self] 

2917 

2918 inferred_bases = list(self._inferred_bases(context=context)) 

2919 bases_mro = [] 

2920 for base in inferred_bases: 

2921 if base is self: 

2922 continue 

2923 

2924 try: 

2925 mro = base._compute_mro(context=context) 

2926 bases_mro.append(mro) 

2927 except NotImplementedError: 

2928 # Some classes have in their ancestors both newstyle and 

2929 # old style classes. For these we can't retrieve the .mro, 

2930 # although in Python it's possible, since the class we are 

2931 # currently working is in fact new style. 

2932 # So, we fallback to ancestors here. 

2933 ancestors = list(base.ancestors(context=context)) 

2934 bases_mro.append(ancestors) 

2935 

2936 unmerged_mro: list[list[ClassDef]] = [[self], *bases_mro, inferred_bases] 

2937 unmerged_mro = clean_duplicates_mro(unmerged_mro, self, context) 

2938 clean_typing_generic_mro(unmerged_mro) 

2939 return _c3_merge(unmerged_mro, self, context) 

2940 

2941 def mro(self, context: InferenceContext | None = None) -> list[ClassDef]: 

2942 """Get the method resolution order, using C3 linearization. 

2943 

2944 :returns: The list of ancestors, sorted by the mro. 

2945 :rtype: list(NodeNG) 

2946 :raises DuplicateBasesError: Duplicate bases in the same class base 

2947 :raises InconsistentMroError: A class' MRO is inconsistent 

2948 """ 

2949 return self._compute_mro(context=context) 

2950 

2951 def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: 

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

2953 

2954 :returns: The boolean value of this node. 

2955 For a :class:`ClassDef` this is always ``True``. 

2956 """ 

2957 return True 

2958 

2959 def get_children(self): 

2960 if self.decorators is not None: 

2961 yield self.decorators 

2962 

2963 yield from self.bases 

2964 if self.keywords is not None: 

2965 yield from self.keywords 

2966 yield from self.type_params 

2967 

2968 yield from self.body 

2969 

2970 @cached_property 

2971 def _assign_nodes_in_scope(self): 

2972 children_assign_nodes = ( 

2973 child_node._assign_nodes_in_scope for child_node in self.body 

2974 ) 

2975 return list(itertools.chain.from_iterable(children_assign_nodes)) 

2976 

2977 def frame(self: _T, *, future: Literal[None, True] = None) -> _T: 

2978 """The node's frame node. 

2979 

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

2981 :class:`ClassDef` or :class:`Lambda`. 

2982 

2983 :returns: The node itself. 

2984 """ 

2985 return self 

2986 

2987 def _infer( 

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

2989 ) -> Generator[ClassDef, None, None]: 

2990 yield self