bases.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  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. """This module contains base classes and functions for the nodes and some
  5. inference utils.
  6. """
  7. from __future__ import annotations
  8. import collections
  9. import collections.abc
  10. from collections.abc import Iterable, Iterator
  11. from typing import TYPE_CHECKING, Any, Literal
  12. from astroid import decorators, nodes
  13. from astroid.const import PY311_PLUS
  14. from astroid.context import (
  15. CallContext,
  16. InferenceContext,
  17. bind_context_to_node,
  18. copy_context,
  19. )
  20. from astroid.exceptions import (
  21. AstroidTypeError,
  22. AttributeInferenceError,
  23. InferenceError,
  24. NameInferenceError,
  25. )
  26. from astroid.interpreter import objectmodel
  27. from astroid.typing import (
  28. InferenceErrorInfo,
  29. InferenceResult,
  30. SuccessfulInferenceResult,
  31. )
  32. from astroid.util import Uninferable, UninferableBase, safe_infer
  33. if TYPE_CHECKING:
  34. from astroid.constraint import Constraint
  35. PROPERTIES = {"builtins.property", "abc.abstractproperty", "functools.cached_property"}
  36. # enum.property was added in Python 3.11
  37. if PY311_PLUS:
  38. PROPERTIES.add("enum.property")
  39. # List of possible property names. We use this list in order
  40. # to see if a method is a property or not. This should be
  41. # pretty reliable and fast, the alternative being to check each
  42. # decorator to see if its a real property-like descriptor, which
  43. # can be too complicated.
  44. # Also, these aren't qualified, because each project can
  45. # define them, we shouldn't expect to know every possible
  46. # property-like decorator!
  47. POSSIBLE_PROPERTIES = {
  48. "cached_property",
  49. "cachedproperty",
  50. "lazyproperty",
  51. "lazy_property",
  52. "reify",
  53. "lazyattribute",
  54. "lazy_attribute",
  55. "LazyProperty",
  56. "lazy",
  57. "cache_readonly",
  58. "DynamicClassAttribute",
  59. }
  60. def _is_property(
  61. meth: nodes.FunctionDef | UnboundMethod, context: InferenceContext | None = None
  62. ) -> bool:
  63. decoratornames = meth.decoratornames(context=context)
  64. if PROPERTIES.intersection(decoratornames):
  65. return True
  66. stripped = {
  67. name.split(".")[-1]
  68. for name in decoratornames
  69. if not isinstance(name, UninferableBase)
  70. }
  71. if any(name in stripped for name in POSSIBLE_PROPERTIES):
  72. return True
  73. if not meth.decorators:
  74. return False
  75. # Lookup for subclasses of *property*
  76. for decorator in meth.decorators.nodes or ():
  77. inferred = safe_infer(decorator, context=context)
  78. if inferred is None or isinstance(inferred, UninferableBase):
  79. continue
  80. if isinstance(inferred, nodes.ClassDef):
  81. # Check for a class which inherits from a standard property type
  82. if any(inferred.is_subtype_of(pclass) for pclass in PROPERTIES):
  83. return True
  84. for base_class in inferred.bases:
  85. # Check for a class which inherits from functools.cached_property
  86. # and includes a subscripted type annotation
  87. if isinstance(base_class, nodes.Subscript):
  88. value = safe_infer(base_class.value, context=context)
  89. if not isinstance(value, nodes.ClassDef):
  90. continue
  91. if value.name != "cached_property":
  92. continue
  93. module, _ = value.lookup(value.name)
  94. if isinstance(module, nodes.Module) and module.name == "functools":
  95. return True
  96. continue
  97. return False
  98. class Proxy:
  99. """A simple proxy object.
  100. Note:
  101. Subclasses of this object will need a custom __getattr__
  102. if new instance attributes are created. See the Const class
  103. """
  104. _proxied: nodes.ClassDef | nodes.FunctionDef | nodes.Lambda | UnboundMethod
  105. def __init__(
  106. self,
  107. proxied: (
  108. nodes.ClassDef | nodes.FunctionDef | nodes.Lambda | UnboundMethod | None
  109. ) = None,
  110. ) -> None:
  111. if proxied is None:
  112. # This is a hack to allow calling this __init__ during bootstrapping of
  113. # builtin classes and their docstrings.
  114. # For Const, Generator, and UnionType nodes the _proxied attribute
  115. # is set during bootstrapping
  116. # as we first need to build the ClassDef that they can proxy.
  117. # Thus, if proxied is None self should be a Const or Generator
  118. # as that is the only way _proxied will be correctly set as a ClassDef.
  119. assert isinstance(self, (nodes.Const, Generator, UnionType))
  120. else:
  121. self._proxied = proxied
  122. def __getattr__(self, name: str) -> Any:
  123. if name == "_proxied":
  124. return self.__class__._proxied
  125. if name in self.__dict__:
  126. return self.__dict__[name]
  127. return getattr(self._proxied, name)
  128. def infer( # type: ignore[return]
  129. self, context: InferenceContext | None = None, **kwargs: Any
  130. ) -> collections.abc.Generator[InferenceResult, None, InferenceErrorInfo | None]:
  131. yield self
  132. def _infer_stmts(
  133. stmts: Iterable[InferenceResult],
  134. context: InferenceContext | None,
  135. frame: nodes.NodeNG | BaseInstance | None = None,
  136. ) -> collections.abc.Generator[InferenceResult]:
  137. """Return an iterator on statements inferred by each statement in *stmts*."""
  138. inferred = False
  139. constraint_failed = False
  140. if context is not None:
  141. name = context.lookupname
  142. context = context.clone()
  143. if name is not None:
  144. constraints = context.constraints.get(name, {})
  145. else:
  146. constraints = {}
  147. else:
  148. name = None
  149. constraints = {}
  150. context = InferenceContext()
  151. for stmt in stmts:
  152. if isinstance(stmt, UninferableBase):
  153. yield stmt
  154. inferred = True
  155. continue
  156. context.lookupname = stmt._infer_name(frame, name)
  157. try:
  158. stmt_constraints: set[Constraint] = set()
  159. for constraint_stmt, potential_constraints in constraints.items():
  160. if not constraint_stmt.parent_of(stmt):
  161. stmt_constraints.update(potential_constraints)
  162. for inf in stmt.infer(context=context):
  163. if all(constraint.satisfied_by(inf) for constraint in stmt_constraints):
  164. yield inf
  165. inferred = True
  166. else:
  167. constraint_failed = True
  168. except NameInferenceError:
  169. continue
  170. except InferenceError:
  171. yield Uninferable
  172. inferred = True
  173. if not inferred and constraint_failed:
  174. yield Uninferable
  175. elif not inferred:
  176. raise InferenceError(
  177. "Inference failed for all members of {stmts!r}.",
  178. stmts=stmts,
  179. frame=frame,
  180. context=context,
  181. )
  182. def _infer_method_result_truth(
  183. instance: Instance, method_name: str, context: InferenceContext
  184. ) -> bool | UninferableBase:
  185. # Get the method from the instance and try to infer
  186. # its return's truth value.
  187. meth = next(instance.igetattr(method_name, context=context), None)
  188. if meth and hasattr(meth, "infer_call_result"):
  189. if not meth.callable():
  190. return Uninferable
  191. try:
  192. context.callcontext = CallContext(args=[], callee=meth)
  193. for value in meth.infer_call_result(instance, context=context):
  194. if isinstance(value, UninferableBase):
  195. return value
  196. try:
  197. inferred = next(value.infer(context=context))
  198. except StopIteration as e:
  199. raise InferenceError(context=context) from e
  200. return inferred.bool_value()
  201. except InferenceError:
  202. pass
  203. return Uninferable
  204. class BaseInstance(Proxy):
  205. """An instance base class, which provides lookup methods for potential
  206. instances.
  207. """
  208. _proxied: nodes.ClassDef
  209. special_attributes: objectmodel.ObjectModel
  210. def display_type(self) -> str:
  211. return "Instance of"
  212. def getattr(
  213. self,
  214. name: str,
  215. context: InferenceContext | None = None,
  216. lookupclass: bool = True,
  217. ) -> list[InferenceResult]:
  218. try:
  219. values = self._proxied.instance_attr(name, context)
  220. except AttributeInferenceError as exc:
  221. if self.special_attributes and name in self.special_attributes:
  222. return [self.special_attributes.lookup(name)]
  223. if lookupclass:
  224. # Class attributes not available through the instance
  225. # unless they are explicitly defined.
  226. return self._proxied.getattr(name, context, class_context=False)
  227. raise AttributeInferenceError(
  228. target=self, attribute=name, context=context
  229. ) from exc
  230. # since we've no context information, return matching class members as
  231. # well
  232. if lookupclass:
  233. try:
  234. return values + self._proxied.getattr(
  235. name, context, class_context=False
  236. )
  237. except AttributeInferenceError:
  238. pass
  239. return values
  240. def igetattr(
  241. self, name: str, context: InferenceContext | None = None
  242. ) -> Iterator[InferenceResult]:
  243. """Inferred getattr."""
  244. if not context:
  245. context = InferenceContext()
  246. try:
  247. context.lookupname = name
  248. # XXX frame should be self._proxied, or not ?
  249. get_attr = self.getattr(name, context, lookupclass=False)
  250. yield from _infer_stmts(
  251. self._wrap_attr(get_attr, context), context, frame=self
  252. )
  253. except AttributeInferenceError:
  254. try:
  255. # fallback to class.igetattr since it has some logic to handle
  256. # descriptors
  257. # But only if the _proxied is the Class.
  258. if self._proxied.__class__.__name__ != "ClassDef":
  259. raise
  260. attrs = self._proxied.igetattr(name, context, class_context=False)
  261. yield from self._wrap_attr(attrs, context)
  262. except AttributeInferenceError as error:
  263. raise InferenceError(**vars(error)) from error
  264. def _wrap_attr(
  265. self, attrs: Iterable[InferenceResult], context: InferenceContext | None = None
  266. ) -> Iterator[InferenceResult]:
  267. """Wrap bound methods of attrs in a InstanceMethod proxies."""
  268. for attr in attrs:
  269. if isinstance(attr, UnboundMethod):
  270. if _is_property(attr):
  271. yield from attr.infer_call_result(self, context)
  272. else:
  273. yield BoundMethod(attr, self)
  274. elif isinstance(attr, nodes.Lambda):
  275. if attr.args.arguments and attr.args.arguments[0].name == "self":
  276. yield BoundMethod(attr, self)
  277. continue
  278. yield attr
  279. else:
  280. yield attr
  281. def infer_call_result(
  282. self,
  283. caller: SuccessfulInferenceResult | None,
  284. context: InferenceContext | None = None,
  285. ) -> Iterator[InferenceResult]:
  286. """Infer what a class instance is returning when called."""
  287. context = bind_context_to_node(context, self)
  288. inferred = False
  289. # If the call is an attribute on the instance, we infer the attribute itself
  290. if isinstance(caller, nodes.Call) and isinstance(caller.func, nodes.Attribute):
  291. for res in self.igetattr(caller.func.attrname, context):
  292. inferred = True
  293. yield res
  294. # Otherwise we infer the call to the __call__ dunder normally
  295. for node in self._proxied.igetattr("__call__", context):
  296. if isinstance(node, UninferableBase) or not node.callable():
  297. continue
  298. if isinstance(node, BaseInstance) and node._proxied is self._proxied:
  299. inferred = True
  300. yield node
  301. # Prevent recursion.
  302. continue
  303. for res in node.infer_call_result(caller, context):
  304. inferred = True
  305. yield res
  306. if not inferred:
  307. raise InferenceError(node=self, caller=caller, context=context)
  308. class Instance(BaseInstance):
  309. """A special node representing a class instance."""
  310. special_attributes = objectmodel.InstanceModel()
  311. def __init__(self, proxied: nodes.ClassDef | None) -> None:
  312. super().__init__(proxied)
  313. @decorators.yes_if_nothing_inferred
  314. def infer_binary_op(
  315. self,
  316. opnode: nodes.AugAssign | nodes.BinOp,
  317. operator: str,
  318. other: InferenceResult,
  319. context: InferenceContext,
  320. method: SuccessfulInferenceResult,
  321. ) -> Generator[InferenceResult]:
  322. return method.infer_call_result(self, context)
  323. def __repr__(self) -> str:
  324. return "<Instance of {}.{} at 0x{}>".format(
  325. self._proxied.root().name, self._proxied.name, id(self)
  326. )
  327. def __str__(self) -> str:
  328. return f"Instance of {self._proxied.root().name}.{self._proxied.name}"
  329. def callable(self) -> bool:
  330. try:
  331. self._proxied.getattr("__call__", class_context=False)
  332. return True
  333. except AttributeInferenceError:
  334. return False
  335. def pytype(self) -> str:
  336. return self._proxied.qname()
  337. def display_type(self) -> str:
  338. return "Instance of"
  339. def bool_value(
  340. self, context: InferenceContext | None = None
  341. ) -> bool | UninferableBase:
  342. """Infer the truth value for an Instance.
  343. The truth value of an instance is determined by these conditions:
  344. * if it implements __bool__ on Python 3 or __nonzero__
  345. on Python 2, then its bool value will be determined by
  346. calling this special method and checking its result.
  347. * when this method is not defined, __len__() is called, if it
  348. is defined, and the object is considered true if its result is
  349. nonzero. If a class defines neither __len__() nor __bool__(),
  350. all its instances are considered true.
  351. """
  352. context = context or InferenceContext()
  353. context.boundnode = self
  354. try:
  355. result = _infer_method_result_truth(self, "__bool__", context)
  356. except (InferenceError, AttributeInferenceError):
  357. # Fallback to __len__.
  358. try:
  359. result = _infer_method_result_truth(self, "__len__", context)
  360. except (AttributeInferenceError, InferenceError):
  361. return True
  362. return result
  363. def getitem(
  364. self, index: nodes.Const, context: InferenceContext | None = None
  365. ) -> InferenceResult | None:
  366. new_context = bind_context_to_node(context, self)
  367. if not context:
  368. context = new_context
  369. method = next(self.igetattr("__getitem__", context=context), None)
  370. # Create a new CallContext for providing index as an argument.
  371. new_context.callcontext = CallContext(args=[index], callee=method)
  372. if not isinstance(method, BoundMethod):
  373. raise InferenceError(
  374. "Could not find __getitem__ for {node!r}.", node=self, context=context
  375. )
  376. if len(method.args.arguments) != 2: # (self, index)
  377. raise AstroidTypeError(
  378. "__getitem__ for {node!r} does not have correct signature",
  379. node=self,
  380. context=context,
  381. )
  382. return next(method.infer_call_result(self, new_context), None)
  383. class UnboundMethod(Proxy):
  384. """A special node representing a method not bound to an instance."""
  385. _proxied: nodes.FunctionDef | UnboundMethod
  386. special_attributes: (
  387. objectmodel.BoundMethodModel | objectmodel.UnboundMethodModel
  388. ) = objectmodel.UnboundMethodModel()
  389. def __repr__(self) -> str:
  390. assert self._proxied.parent, "Expected a parent node"
  391. frame = self._proxied.parent.frame()
  392. return "<{} {} of {} at 0x{}".format(
  393. self.__class__.__name__, self._proxied.name, frame.qname(), id(self)
  394. )
  395. def implicit_parameters(self) -> Literal[0, 1]:
  396. return 0
  397. def is_bound(self) -> bool:
  398. return False
  399. def getattr(self, name: str, context: InferenceContext | None = None):
  400. if name in self.special_attributes:
  401. return [self.special_attributes.lookup(name)]
  402. return self._proxied.getattr(name, context)
  403. def igetattr(
  404. self, name: str, context: InferenceContext | None = None
  405. ) -> Iterator[InferenceResult]:
  406. if name in self.special_attributes:
  407. return iter((self.special_attributes.lookup(name),))
  408. return self._proxied.igetattr(name, context)
  409. def infer_call_result(
  410. self,
  411. caller: SuccessfulInferenceResult | None,
  412. context: InferenceContext | None = None,
  413. ) -> Iterator[InferenceResult]:
  414. """
  415. The boundnode of the regular context with a function called
  416. on ``object.__new__`` will be of type ``object``,
  417. which is incorrect for the argument in general.
  418. If no context is given the ``object.__new__`` call argument will
  419. be correctly inferred except when inside a call that requires
  420. the additional context (such as a classmethod) of the boundnode
  421. to determine which class the method was called from
  422. """
  423. # If we're unbound method __new__ of a builtin, the result is an
  424. # instance of the class given as first argument.
  425. if self._proxied.name == "__new__":
  426. assert self._proxied.parent, "Expected a parent node"
  427. qname = self._proxied.parent.frame().qname()
  428. # Avoid checking builtins.type: _infer_type_new_call() does more validation
  429. if qname.startswith("builtins.") and qname != "builtins.type":
  430. return self._infer_builtin_new(caller, context or InferenceContext())
  431. return self._proxied.infer_call_result(caller, context)
  432. def _infer_builtin_new(
  433. self,
  434. caller: SuccessfulInferenceResult | None,
  435. context: InferenceContext,
  436. ) -> collections.abc.Generator[nodes.Const | Instance | UninferableBase]:
  437. if not isinstance(caller, nodes.Call):
  438. return
  439. if not caller.args:
  440. return
  441. # Attempt to create a constant
  442. if len(caller.args) > 1:
  443. value = None
  444. if isinstance(caller.args[1], nodes.Const):
  445. value = caller.args[1].value
  446. else:
  447. inferred_arg = next(caller.args[1].infer(), None)
  448. if isinstance(inferred_arg, nodes.Const):
  449. value = inferred_arg.value
  450. if value is not None:
  451. const = nodes.const_factory(value)
  452. assert not isinstance(const, nodes.EmptyNode)
  453. yield const
  454. return
  455. node_context = context.extra_context.get(caller.args[0])
  456. for inferred in caller.args[0].infer(context=node_context):
  457. if isinstance(inferred, UninferableBase):
  458. yield inferred
  459. if isinstance(inferred, nodes.ClassDef):
  460. yield Instance(inferred)
  461. raise InferenceError
  462. def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
  463. return True
  464. class BoundMethod(UnboundMethod):
  465. """A special node representing a method bound to an instance."""
  466. special_attributes = objectmodel.BoundMethodModel()
  467. def __init__(
  468. self,
  469. proxy: nodes.FunctionDef | nodes.Lambda | UnboundMethod,
  470. bound: SuccessfulInferenceResult,
  471. ) -> None:
  472. super().__init__(proxy)
  473. self.bound = bound
  474. def implicit_parameters(self) -> Literal[0, 1]:
  475. if self.name == "__new__":
  476. # __new__ acts as a classmethod but the class argument is not implicit.
  477. return 0
  478. return 1
  479. def is_bound(self) -> Literal[True]:
  480. return True
  481. def _infer_type_new_call(
  482. self, caller: nodes.Call, context: InferenceContext
  483. ) -> nodes.ClassDef | None: # noqa: C901
  484. """Try to infer what type.__new__(mcs, name, bases, attrs) returns.
  485. In order for such call to be valid, the metaclass needs to be
  486. a subtype of ``type``, the name needs to be a string, the bases
  487. needs to be a tuple of classes
  488. """
  489. # pylint: disable=import-outside-toplevel; circular import
  490. from astroid.nodes import Pass
  491. # Verify the metaclass
  492. try:
  493. mcs = next(caller.args[0].infer(context=context))
  494. except StopIteration as e:
  495. raise InferenceError(context=context) from e
  496. if not isinstance(mcs, nodes.ClassDef):
  497. # Not a valid first argument.
  498. return None
  499. if not mcs.is_subtype_of("builtins.type"):
  500. # Not a valid metaclass.
  501. return None
  502. # Verify the name
  503. try:
  504. name = next(caller.args[1].infer(context=context))
  505. except StopIteration as e:
  506. raise InferenceError(context=context) from e
  507. if not isinstance(name, nodes.Const):
  508. # Not a valid name, needs to be a const.
  509. return None
  510. if not isinstance(name.value, str):
  511. # Needs to be a string.
  512. return None
  513. # Verify the bases
  514. try:
  515. bases = next(caller.args[2].infer(context=context))
  516. except StopIteration as e:
  517. raise InferenceError(context=context) from e
  518. if not isinstance(bases, nodes.Tuple):
  519. # Needs to be a tuple.
  520. return None
  521. try:
  522. inferred_bases = [next(elt.infer(context=context)) for elt in bases.elts]
  523. except StopIteration as e:
  524. raise InferenceError(context=context) from e
  525. if any(not isinstance(base, nodes.ClassDef) for base in inferred_bases):
  526. # All the bases needs to be Classes
  527. return None
  528. # Verify the attributes.
  529. try:
  530. attrs = next(caller.args[3].infer(context=context))
  531. except StopIteration as e:
  532. raise InferenceError(context=context) from e
  533. if not isinstance(attrs, nodes.Dict):
  534. # Needs to be a dictionary.
  535. return None
  536. cls_locals: dict[str, list[InferenceResult]] = collections.defaultdict(list)
  537. for key, value in attrs.items:
  538. try:
  539. key = next(key.infer(context=context))
  540. except StopIteration as e:
  541. raise InferenceError(context=context) from e
  542. try:
  543. value = next(value.infer(context=context))
  544. except StopIteration as e:
  545. raise InferenceError(context=context) from e
  546. # Ignore non string keys
  547. if isinstance(key, nodes.Const) and isinstance(key.value, str):
  548. cls_locals[key.value].append(value)
  549. # Build the class from now.
  550. cls = mcs.__class__(
  551. name=name.value,
  552. lineno=caller.lineno or 0,
  553. col_offset=caller.col_offset or 0,
  554. parent=caller,
  555. end_lineno=caller.end_lineno,
  556. end_col_offset=caller.end_col_offset,
  557. )
  558. empty = Pass(
  559. parent=cls,
  560. lineno=caller.lineno,
  561. col_offset=caller.col_offset,
  562. end_lineno=caller.end_lineno,
  563. end_col_offset=caller.end_col_offset,
  564. )
  565. cls.postinit(
  566. bases=bases.elts,
  567. body=[empty],
  568. decorators=None,
  569. newstyle=True,
  570. metaclass=mcs,
  571. keywords=[],
  572. )
  573. cls.locals = cls_locals
  574. return cls
  575. def infer_call_result(
  576. self,
  577. caller: SuccessfulInferenceResult | None,
  578. context: InferenceContext | None = None,
  579. ) -> Iterator[InferenceResult]:
  580. context = bind_context_to_node(context, self.bound)
  581. if (
  582. isinstance(self.bound, nodes.ClassDef)
  583. and self.bound.name == "type"
  584. and self.name == "__new__"
  585. and isinstance(caller, nodes.Call)
  586. and len(caller.args) == 4
  587. ):
  588. # Check if we have a ``type.__new__(mcs, name, bases, attrs)`` call.
  589. new_cls = self._infer_type_new_call(caller, context)
  590. if new_cls:
  591. return iter((new_cls,))
  592. return super().infer_call_result(caller, context)
  593. def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
  594. return True
  595. class Generator(BaseInstance):
  596. """A special node representing a generator.
  597. Proxied class is set once for all in raw_building.
  598. """
  599. # We defer initialization of special_attributes to the __init__ method since the constructor
  600. # of GeneratorModel requires the raw_building to be complete
  601. # TODO: This should probably be refactored.
  602. special_attributes: objectmodel.GeneratorBaseModel
  603. def __init__(
  604. self,
  605. parent: nodes.FunctionDef,
  606. generator_initial_context: InferenceContext | None = None,
  607. ) -> None:
  608. super().__init__()
  609. self.parent = parent
  610. self._call_context = copy_context(generator_initial_context)
  611. # See comment above: this is a deferred initialization.
  612. Generator.special_attributes = objectmodel.GeneratorModel()
  613. def infer_yield_types(self) -> Iterator[InferenceResult]:
  614. yield from self.parent.infer_yield_result(self._call_context)
  615. def callable(self) -> Literal[False]:
  616. return False
  617. def pytype(self) -> str:
  618. return "builtins.generator"
  619. def display_type(self) -> str:
  620. return "Generator"
  621. def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
  622. return True
  623. def __repr__(self) -> str:
  624. return f"<Generator({self._proxied.name}) l.{self.lineno} at 0x{id(self)}>"
  625. def __str__(self) -> str:
  626. return f"Generator({self._proxied.name})"
  627. class AsyncGenerator(Generator):
  628. """Special node representing an async generator."""
  629. def __init__(self, *args, **kwargs):
  630. super().__init__(*args, **kwargs)
  631. AsyncGenerator.special_attributes = objectmodel.AsyncGeneratorModel()
  632. def pytype(self) -> Literal["builtins.async_generator"]:
  633. return "builtins.async_generator"
  634. def display_type(self) -> str:
  635. return "AsyncGenerator"
  636. def __repr__(self) -> str:
  637. return f"<AsyncGenerator({self._proxied.name}) l.{self.lineno} at 0x{id(self)}>"
  638. def __str__(self) -> str:
  639. return f"AsyncGenerator({self._proxied.name})"
  640. class UnionType(BaseInstance):
  641. """Special node representing new style typing unions.
  642. Proxied class is set once for all in raw_building.
  643. """
  644. def __init__(
  645. self,
  646. left: UnionType | nodes.ClassDef | nodes.Const,
  647. right: UnionType | nodes.ClassDef | nodes.Const,
  648. parent: nodes.NodeNG | None = None,
  649. ) -> None:
  650. super().__init__()
  651. self.parent = parent
  652. self.left = left
  653. self.right = right
  654. def callable(self) -> Literal[False]:
  655. return False
  656. def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
  657. return True
  658. def pytype(self) -> Literal["types.UnionType"]:
  659. return "types.UnionType"
  660. def display_type(self) -> str:
  661. return "UnionType"
  662. def __repr__(self) -> str:
  663. return f"<UnionType({self._proxied.name}) l.{self.lineno} at 0x{id(self)}>"
  664. def __str__(self) -> str:
  665. return f"UnionType({self._proxied.name})"