bases.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. # Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
  2. # Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
  3. # Copyright (c) 2014-2020 Claudiu Popa <pcmanticore@gmail.com>
  4. # Copyright (c) 2014 Google, Inc.
  5. # Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
  6. # Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
  7. # Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
  8. # Copyright (c) 2016-2017 Derek Gustafson <degustaf@gmail.com>
  9. # Copyright (c) 2017 Calen Pennington <calen.pennington@gmail.com>
  10. # Copyright (c) 2018-2019 Nick Drozd <nicholasdrozd@gmail.com>
  11. # Copyright (c) 2018-2019 hippo91 <guillaume.peillex@gmail.com>
  12. # Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi>
  13. # Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
  14. # Copyright (c) 2018 Daniel Colascione <dancol@dancol.org>
  15. # Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
  16. # Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
  17. # Copyright (c) 2021 Tushar Sadhwani <86737547+tushar-deepsource@users.noreply.github.com>
  18. # Copyright (c) 2021 pre-commit-ci[bot] <bot@noreply.github.com>
  19. # Copyright (c) 2021 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
  20. # Copyright (c) 2021 David Liu <david@cs.toronto.edu>
  21. # Copyright (c) 2021 doranid <ddandd@gmail.com>
  22. # Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com>
  23. # Copyright (c) 2021 Andrew Haigh <hello@nelf.in>
  24. # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
  25. # For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
  26. """This module contains base classes and functions for the nodes and some
  27. inference utils.
  28. """
  29. import collections
  30. from astroid import decorators
  31. from astroid.const import PY310_PLUS
  32. from astroid.context import (
  33. CallContext,
  34. InferenceContext,
  35. bind_context_to_node,
  36. copy_context,
  37. )
  38. from astroid.exceptions import (
  39. AstroidTypeError,
  40. AttributeInferenceError,
  41. InferenceError,
  42. NameInferenceError,
  43. )
  44. from astroid.util import Uninferable, lazy_descriptor, lazy_import
  45. objectmodel = lazy_import("interpreter.objectmodel")
  46. helpers = lazy_import("helpers")
  47. manager = lazy_import("manager")
  48. # TODO: check if needs special treatment
  49. BOOL_SPECIAL_METHOD = "__bool__"
  50. BUILTINS = "builtins" # TODO Remove in 2.8
  51. PROPERTIES = {"builtins.property", "abc.abstractproperty"}
  52. if PY310_PLUS:
  53. PROPERTIES.add("enum.property")
  54. # List of possible property names. We use this list in order
  55. # to see if a method is a property or not. This should be
  56. # pretty reliable and fast, the alternative being to check each
  57. # decorator to see if its a real property-like descriptor, which
  58. # can be too complicated.
  59. # Also, these aren't qualified, because each project can
  60. # define them, we shouldn't expect to know every possible
  61. # property-like decorator!
  62. POSSIBLE_PROPERTIES = {
  63. "cached_property",
  64. "cachedproperty",
  65. "lazyproperty",
  66. "lazy_property",
  67. "reify",
  68. "lazyattribute",
  69. "lazy_attribute",
  70. "LazyProperty",
  71. "lazy",
  72. "cache_readonly",
  73. "DynamicClassAttribute",
  74. }
  75. def _is_property(meth, context=None):
  76. decoratornames = meth.decoratornames(context=context)
  77. if PROPERTIES.intersection(decoratornames):
  78. return True
  79. stripped = {
  80. name.split(".")[-1] for name in decoratornames if name is not Uninferable
  81. }
  82. if any(name in stripped for name in POSSIBLE_PROPERTIES):
  83. return True
  84. # Lookup for subclasses of *property*
  85. if not meth.decorators:
  86. return False
  87. for decorator in meth.decorators.nodes or ():
  88. inferred = helpers.safe_infer(decorator, context=context)
  89. if inferred is None or inferred is Uninferable:
  90. continue
  91. if inferred.__class__.__name__ == "ClassDef":
  92. for base_class in inferred.bases:
  93. if base_class.__class__.__name__ != "Name":
  94. continue
  95. module, _ = base_class.lookup(base_class.name)
  96. if module.name == "builtins" and base_class.name == "property":
  97. return True
  98. return False
  99. class Proxy:
  100. """a simple proxy object
  101. Note:
  102. Subclasses of this object will need a custom __getattr__
  103. if new instance attributes are created. See the Const class
  104. """
  105. _proxied = None # proxied object may be set by class or by instance
  106. def __init__(self, proxied=None):
  107. if proxied is not None:
  108. self._proxied = proxied
  109. def __getattr__(self, name):
  110. if name == "_proxied":
  111. return self.__class__._proxied
  112. if name in self.__dict__:
  113. return self.__dict__[name]
  114. return getattr(self._proxied, name)
  115. def infer(self, context=None):
  116. yield self
  117. def _infer_stmts(stmts, context, frame=None):
  118. """Return an iterator on statements inferred by each statement in *stmts*."""
  119. inferred = False
  120. if context is not None:
  121. name = context.lookupname
  122. context = context.clone()
  123. else:
  124. name = None
  125. context = InferenceContext()
  126. for stmt in stmts:
  127. if stmt is Uninferable:
  128. yield stmt
  129. inferred = True
  130. continue
  131. context.lookupname = stmt._infer_name(frame, name)
  132. try:
  133. for inf in stmt.infer(context=context):
  134. yield inf
  135. inferred = True
  136. except NameInferenceError:
  137. continue
  138. except InferenceError:
  139. yield Uninferable
  140. inferred = True
  141. if not inferred:
  142. raise InferenceError(
  143. "Inference failed for all members of {stmts!r}.",
  144. stmts=stmts,
  145. frame=frame,
  146. context=context,
  147. )
  148. def _infer_method_result_truth(instance, method_name, context):
  149. # Get the method from the instance and try to infer
  150. # its return's truth value.
  151. meth = next(instance.igetattr(method_name, context=context), None)
  152. if meth and hasattr(meth, "infer_call_result"):
  153. if not meth.callable():
  154. return Uninferable
  155. try:
  156. context.callcontext = CallContext(args=[], callee=meth)
  157. for value in meth.infer_call_result(instance, context=context):
  158. if value is Uninferable:
  159. return value
  160. try:
  161. inferred = next(value.infer(context=context))
  162. except StopIteration as e:
  163. raise InferenceError(context=context) from e
  164. return inferred.bool_value()
  165. except InferenceError:
  166. pass
  167. return Uninferable
  168. class BaseInstance(Proxy):
  169. """An instance base class, which provides lookup methods for potential instances."""
  170. special_attributes = None
  171. def display_type(self):
  172. return "Instance of"
  173. def getattr(self, name, context=None, lookupclass=True):
  174. try:
  175. values = self._proxied.instance_attr(name, context)
  176. except AttributeInferenceError as exc:
  177. if self.special_attributes and name in self.special_attributes:
  178. return [self.special_attributes.lookup(name)]
  179. if lookupclass:
  180. # Class attributes not available through the instance
  181. # unless they are explicitly defined.
  182. return self._proxied.getattr(name, context, class_context=False)
  183. raise AttributeInferenceError(
  184. target=self, attribute=name, context=context
  185. ) from exc
  186. # since we've no context information, return matching class members as
  187. # well
  188. if lookupclass:
  189. try:
  190. return values + self._proxied.getattr(
  191. name, context, class_context=False
  192. )
  193. except AttributeInferenceError:
  194. pass
  195. return values
  196. def igetattr(self, name, context=None):
  197. """inferred getattr"""
  198. if not context:
  199. context = InferenceContext()
  200. try:
  201. context.lookupname = name
  202. # avoid recursively inferring the same attr on the same class
  203. if context.push(self._proxied):
  204. raise InferenceError(
  205. message="Cannot infer the same attribute again",
  206. node=self,
  207. context=context,
  208. )
  209. # XXX frame should be self._proxied, or not ?
  210. get_attr = self.getattr(name, context, lookupclass=False)
  211. yield from _infer_stmts(
  212. self._wrap_attr(get_attr, context), context, frame=self
  213. )
  214. except AttributeInferenceError:
  215. try:
  216. # fallback to class.igetattr since it has some logic to handle
  217. # descriptors
  218. # But only if the _proxied is the Class.
  219. if self._proxied.__class__.__name__ != "ClassDef":
  220. raise
  221. attrs = self._proxied.igetattr(name, context, class_context=False)
  222. yield from self._wrap_attr(attrs, context)
  223. except AttributeInferenceError as error:
  224. raise InferenceError(**vars(error)) from error
  225. def _wrap_attr(self, attrs, context=None):
  226. """wrap bound methods of attrs in a InstanceMethod proxies"""
  227. for attr in attrs:
  228. if isinstance(attr, UnboundMethod):
  229. if _is_property(attr):
  230. yield from attr.infer_call_result(self, context)
  231. else:
  232. yield BoundMethod(attr, self)
  233. elif hasattr(attr, "name") and attr.name == "<lambda>":
  234. if attr.args.arguments and attr.args.arguments[0].name == "self":
  235. yield BoundMethod(attr, self)
  236. continue
  237. yield attr
  238. else:
  239. yield attr
  240. def infer_call_result(self, caller, context=None):
  241. """infer what a class instance is returning when called"""
  242. context = bind_context_to_node(context, self)
  243. inferred = False
  244. for node in self._proxied.igetattr("__call__", context):
  245. if node is Uninferable or not node.callable():
  246. continue
  247. for res in node.infer_call_result(caller, context):
  248. inferred = True
  249. yield res
  250. if not inferred:
  251. raise InferenceError(node=self, caller=caller, context=context)
  252. class Instance(BaseInstance):
  253. """A special node representing a class instance."""
  254. # pylint: disable=unnecessary-lambda
  255. special_attributes = lazy_descriptor(lambda: objectmodel.InstanceModel())
  256. def __repr__(self):
  257. return "<Instance of {}.{} at 0x{}>".format(
  258. self._proxied.root().name, self._proxied.name, id(self)
  259. )
  260. def __str__(self):
  261. return f"Instance of {self._proxied.root().name}.{self._proxied.name}"
  262. def callable(self):
  263. try:
  264. self._proxied.getattr("__call__", class_context=False)
  265. return True
  266. except AttributeInferenceError:
  267. return False
  268. def pytype(self):
  269. return self._proxied.qname()
  270. def display_type(self):
  271. return "Instance of"
  272. def bool_value(self, context=None):
  273. """Infer the truth value for an Instance
  274. The truth value of an instance is determined by these conditions:
  275. * if it implements __bool__ on Python 3 or __nonzero__
  276. on Python 2, then its bool value will be determined by
  277. calling this special method and checking its result.
  278. * when this method is not defined, __len__() is called, if it
  279. is defined, and the object is considered true if its result is
  280. nonzero. If a class defines neither __len__() nor __bool__(),
  281. all its instances are considered true.
  282. """
  283. context = context or InferenceContext()
  284. context.boundnode = self
  285. try:
  286. result = _infer_method_result_truth(self, BOOL_SPECIAL_METHOD, context)
  287. except (InferenceError, AttributeInferenceError):
  288. # Fallback to __len__.
  289. try:
  290. result = _infer_method_result_truth(self, "__len__", context)
  291. except (AttributeInferenceError, InferenceError):
  292. return True
  293. return result
  294. def getitem(self, index, context=None):
  295. # TODO: Rewrap index to Const for this case
  296. new_context = bind_context_to_node(context, self)
  297. if not context:
  298. context = new_context
  299. method = next(self.igetattr("__getitem__", context=context), None)
  300. # Create a new CallContext for providing index as an argument.
  301. new_context.callcontext = CallContext(args=[index], callee=method)
  302. if not isinstance(method, BoundMethod):
  303. raise InferenceError(
  304. "Could not find __getitem__ for {node!r}.", node=self, context=context
  305. )
  306. if len(method.args.arguments) != 2: # (self, index)
  307. raise AstroidTypeError(
  308. "__getitem__ for {node!r} does not have correct signature",
  309. node=self,
  310. context=context,
  311. )
  312. return next(method.infer_call_result(self, new_context), None)
  313. class UnboundMethod(Proxy):
  314. """a special node representing a method not bound to an instance"""
  315. # pylint: disable=unnecessary-lambda
  316. special_attributes = lazy_descriptor(lambda: objectmodel.UnboundMethodModel())
  317. def __repr__(self):
  318. frame = self._proxied.parent.frame(future=True)
  319. return "<{} {} of {} at 0x{}".format(
  320. self.__class__.__name__, self._proxied.name, frame.qname(), id(self)
  321. )
  322. def implicit_parameters(self):
  323. return 0
  324. def is_bound(self):
  325. return False
  326. def getattr(self, name, context=None):
  327. if name in self.special_attributes:
  328. return [self.special_attributes.lookup(name)]
  329. return self._proxied.getattr(name, context)
  330. def igetattr(self, name, context=None):
  331. if name in self.special_attributes:
  332. return iter((self.special_attributes.lookup(name),))
  333. return self._proxied.igetattr(name, context)
  334. def infer_call_result(self, caller, context):
  335. """
  336. The boundnode of the regular context with a function called
  337. on ``object.__new__`` will be of type ``object``,
  338. which is incorrect for the argument in general.
  339. If no context is given the ``object.__new__`` call argument will
  340. correctly inferred except when inside a call that requires
  341. the additional context (such as a classmethod) of the boundnode
  342. to determine which class the method was called from
  343. """
  344. # If we're unbound method __new__ of builtin object, the result is an
  345. # instance of the class given as first argument.
  346. if (
  347. self._proxied.name == "__new__"
  348. and self._proxied.parent.frame(future=True).qname() == "builtins.object"
  349. ):
  350. if caller.args:
  351. node_context = context.extra_context.get(caller.args[0])
  352. infer = caller.args[0].infer(context=node_context)
  353. else:
  354. infer = []
  355. return (Instance(x) if x is not Uninferable else x for x in infer)
  356. return self._proxied.infer_call_result(caller, context)
  357. def bool_value(self, context=None):
  358. return True
  359. class BoundMethod(UnboundMethod):
  360. """a special node representing a method bound to an instance"""
  361. # pylint: disable=unnecessary-lambda
  362. special_attributes = lazy_descriptor(lambda: objectmodel.BoundMethodModel())
  363. def __init__(self, proxy, bound):
  364. super().__init__(proxy)
  365. self.bound = bound
  366. def implicit_parameters(self):
  367. if self.name == "__new__":
  368. # __new__ acts as a classmethod but the class argument is not implicit.
  369. return 0
  370. return 1
  371. def is_bound(self):
  372. return True
  373. def _infer_type_new_call(self, caller, context):
  374. """Try to infer what type.__new__(mcs, name, bases, attrs) returns.
  375. In order for such call to be valid, the metaclass needs to be
  376. a subtype of ``type``, the name needs to be a string, the bases
  377. needs to be a tuple of classes
  378. """
  379. # pylint: disable=import-outside-toplevel; circular import
  380. from astroid.nodes import Pass
  381. # Verify the metaclass
  382. try:
  383. mcs = next(caller.args[0].infer(context=context))
  384. except StopIteration as e:
  385. raise InferenceError(context=context) from e
  386. if mcs.__class__.__name__ != "ClassDef":
  387. # Not a valid first argument.
  388. return None
  389. if not mcs.is_subtype_of("builtins.type"):
  390. # Not a valid metaclass.
  391. return None
  392. # Verify the name
  393. try:
  394. name = next(caller.args[1].infer(context=context))
  395. except StopIteration as e:
  396. raise InferenceError(context=context) from e
  397. if name.__class__.__name__ != "Const":
  398. # Not a valid name, needs to be a const.
  399. return None
  400. if not isinstance(name.value, str):
  401. # Needs to be a string.
  402. return None
  403. # Verify the bases
  404. try:
  405. bases = next(caller.args[2].infer(context=context))
  406. except StopIteration as e:
  407. raise InferenceError(context=context) from e
  408. if bases.__class__.__name__ != "Tuple":
  409. # Needs to be a tuple.
  410. return None
  411. try:
  412. inferred_bases = [next(elt.infer(context=context)) for elt in bases.elts]
  413. except StopIteration as e:
  414. raise InferenceError(context=context) from e
  415. if any(base.__class__.__name__ != "ClassDef" for base in inferred_bases):
  416. # All the bases needs to be Classes
  417. return None
  418. # Verify the attributes.
  419. try:
  420. attrs = next(caller.args[3].infer(context=context))
  421. except StopIteration as e:
  422. raise InferenceError(context=context) from e
  423. if attrs.__class__.__name__ != "Dict":
  424. # Needs to be a dictionary.
  425. return None
  426. cls_locals = collections.defaultdict(list)
  427. for key, value in attrs.items:
  428. try:
  429. key = next(key.infer(context=context))
  430. except StopIteration as e:
  431. raise InferenceError(context=context) from e
  432. try:
  433. value = next(value.infer(context=context))
  434. except StopIteration as e:
  435. raise InferenceError(context=context) from e
  436. # Ignore non string keys
  437. if key.__class__.__name__ == "Const" and isinstance(key.value, str):
  438. cls_locals[key.value].append(value)
  439. # Build the class from now.
  440. cls = mcs.__class__(
  441. name=name.value,
  442. lineno=caller.lineno,
  443. col_offset=caller.col_offset,
  444. parent=caller,
  445. )
  446. empty = Pass()
  447. cls.postinit(
  448. bases=bases.elts,
  449. body=[empty],
  450. decorators=[],
  451. newstyle=True,
  452. metaclass=mcs,
  453. keywords=[],
  454. )
  455. cls.locals = cls_locals
  456. return cls
  457. def infer_call_result(self, caller, context=None):
  458. context = bind_context_to_node(context, self.bound)
  459. if (
  460. self.bound.__class__.__name__ == "ClassDef"
  461. and self.bound.name == "type"
  462. and self.name == "__new__"
  463. and len(caller.args) == 4
  464. ):
  465. # Check if we have a ``type.__new__(mcs, name, bases, attrs)`` call.
  466. new_cls = self._infer_type_new_call(caller, context)
  467. if new_cls:
  468. return iter((new_cls,))
  469. return super().infer_call_result(caller, context)
  470. def bool_value(self, context=None):
  471. return True
  472. class Generator(BaseInstance):
  473. """a special node representing a generator.
  474. Proxied class is set once for all in raw_building.
  475. """
  476. special_attributes = lazy_descriptor(objectmodel.GeneratorModel)
  477. def __init__(self, parent=None, generator_initial_context=None):
  478. super().__init__()
  479. self.parent = parent
  480. self._call_context = copy_context(generator_initial_context)
  481. @decorators.cached
  482. def infer_yield_types(self):
  483. yield from self.parent.infer_yield_result(self._call_context)
  484. def callable(self):
  485. return False
  486. def pytype(self):
  487. return "builtins.generator"
  488. def display_type(self):
  489. return "Generator"
  490. def bool_value(self, context=None):
  491. return True
  492. def __repr__(self):
  493. return f"<Generator({self._proxied.name}) l.{self.lineno} at 0x{id(self)}>"
  494. def __str__(self):
  495. return f"Generator({self._proxied.name})"
  496. class AsyncGenerator(Generator):
  497. """Special node representing an async generator"""
  498. def pytype(self):
  499. return "builtins.async_generator"
  500. def display_type(self):
  501. return "AsyncGenerator"
  502. def __repr__(self):
  503. return f"<AsyncGenerator({self._proxied.name}) l.{self.lineno} at 0x{id(self)}>"
  504. def __str__(self):
  505. return f"AsyncGenerator({self._proxied.name})"