inference.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  1. # Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
  2. # Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
  3. # Copyright (c) 2013-2014 Google, Inc.
  4. # Copyright (c) 2014-2020 Claudiu Popa <pcmanticore@gmail.com>
  5. # Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
  6. # Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
  7. # Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
  8. # Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
  9. # Copyright (c) 2017 Michał Masłowski <m.maslowski@clearcode.cc>
  10. # Copyright (c) 2017 Calen Pennington <cale@edx.org>
  11. # Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
  12. # Copyright (c) 2018-2019 Nick Drozd <nicholasdrozd@gmail.com>
  13. # Copyright (c) 2018 Daniel Martin <daniel.martin@crowdstrike.com>
  14. # Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi>
  15. # Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
  16. # Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
  17. # Copyright (c) 2018 HoverHell <hoverhell@gmail.com>
  18. # Copyright (c) 2020 Leandro T. C. Melo <ltcmelo@gmail.com>
  19. # Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
  20. # Copyright (c) 2021 Kian Meng, Ang <kianmeng.ang@gmail.com>
  21. # Copyright (c) 2021 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
  22. # Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com>
  23. # Copyright (c) 2021 Andrew Haigh <hello@nelf.in>
  24. # Copyright (c) 2021 David Liu <david@cs.toronto.edu>
  25. # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
  26. # For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
  27. """this module contains a set of functions to handle inference on astroid trees
  28. """
  29. import ast
  30. import functools
  31. import itertools
  32. import operator
  33. from typing import Any, Callable, Dict, Iterable, Optional
  34. import wrapt
  35. from astroid import bases, decorators, helpers, nodes, protocols, util
  36. from astroid.context import (
  37. CallContext,
  38. InferenceContext,
  39. bind_context_to_node,
  40. copy_context,
  41. )
  42. from astroid.exceptions import (
  43. AstroidBuildingError,
  44. AstroidError,
  45. AstroidIndexError,
  46. AstroidTypeError,
  47. AttributeInferenceError,
  48. InferenceError,
  49. NameInferenceError,
  50. _NonDeducibleTypeHierarchy,
  51. )
  52. from astroid.interpreter import dunder_lookup
  53. from astroid.manager import AstroidManager
  54. # Prevents circular imports
  55. objects = util.lazy_import("objects")
  56. # .infer method ###############################################################
  57. def infer_end(self, context=None):
  58. """Inference's end for nodes that yield themselves on inference
  59. These are objects for which inference does not have any semantic,
  60. such as Module or Consts.
  61. """
  62. yield self
  63. # We add ignores to all these assignments in this file
  64. # See https://github.com/python/mypy/issues/2427
  65. nodes.Module._infer = infer_end # type: ignore[assignment]
  66. nodes.ClassDef._infer = infer_end # type: ignore[assignment]
  67. nodes.Lambda._infer = infer_end # type: ignore[assignment]
  68. nodes.Const._infer = infer_end # type: ignore[assignment]
  69. nodes.Slice._infer = infer_end # type: ignore[assignment]
  70. def _infer_sequence_helper(node, context=None):
  71. """Infer all values based on _BaseContainer.elts"""
  72. values = []
  73. for elt in node.elts:
  74. if isinstance(elt, nodes.Starred):
  75. starred = helpers.safe_infer(elt.value, context)
  76. if not starred:
  77. raise InferenceError(node=node, context=context)
  78. if not hasattr(starred, "elts"):
  79. raise InferenceError(node=node, context=context)
  80. values.extend(_infer_sequence_helper(starred))
  81. elif isinstance(elt, nodes.NamedExpr):
  82. value = helpers.safe_infer(elt.value, context)
  83. if not value:
  84. raise InferenceError(node=node, context=context)
  85. values.append(value)
  86. else:
  87. values.append(elt)
  88. return values
  89. @decorators.raise_if_nothing_inferred
  90. def infer_sequence(self, context=None):
  91. has_starred_named_expr = any(
  92. isinstance(e, (nodes.Starred, nodes.NamedExpr)) for e in self.elts
  93. )
  94. if has_starred_named_expr:
  95. values = _infer_sequence_helper(self, context)
  96. new_seq = type(self)(
  97. lineno=self.lineno, col_offset=self.col_offset, parent=self.parent
  98. )
  99. new_seq.postinit(values)
  100. yield new_seq
  101. else:
  102. yield self
  103. nodes.List._infer = infer_sequence # type: ignore[assignment]
  104. nodes.Tuple._infer = infer_sequence # type: ignore[assignment]
  105. nodes.Set._infer = infer_sequence # type: ignore[assignment]
  106. def infer_map(self, context=None):
  107. if not any(isinstance(k, nodes.DictUnpack) for k, _ in self.items):
  108. yield self
  109. else:
  110. items = _infer_map(self, context)
  111. new_seq = type(self)(self.lineno, self.col_offset, self.parent)
  112. new_seq.postinit(list(items.items()))
  113. yield new_seq
  114. def _update_with_replacement(lhs_dict, rhs_dict):
  115. """Delete nodes that equate to duplicate keys
  116. Since an astroid node doesn't 'equal' another node with the same value,
  117. this function uses the as_string method to make sure duplicate keys
  118. don't get through
  119. Note that both the key and the value are astroid nodes
  120. Fixes issue with DictUnpack causing duplicte keys
  121. in inferred Dict items
  122. :param dict(nodes.NodeNG, nodes.NodeNG) lhs_dict: Dictionary to 'merge' nodes into
  123. :param dict(nodes.NodeNG, nodes.NodeNG) rhs_dict: Dictionary with nodes to pull from
  124. :return dict(nodes.NodeNG, nodes.NodeNG): merged dictionary of nodes
  125. """
  126. combined_dict = itertools.chain(lhs_dict.items(), rhs_dict.items())
  127. # Overwrite keys which have the same string values
  128. string_map = {key.as_string(): (key, value) for key, value in combined_dict}
  129. # Return to dictionary
  130. return dict(string_map.values())
  131. def _infer_map(node, context):
  132. """Infer all values based on Dict.items"""
  133. values = {}
  134. for name, value in node.items:
  135. if isinstance(name, nodes.DictUnpack):
  136. double_starred = helpers.safe_infer(value, context)
  137. if not double_starred:
  138. raise InferenceError
  139. if not isinstance(double_starred, nodes.Dict):
  140. raise InferenceError(node=node, context=context)
  141. unpack_items = _infer_map(double_starred, context)
  142. values = _update_with_replacement(values, unpack_items)
  143. else:
  144. key = helpers.safe_infer(name, context=context)
  145. value = helpers.safe_infer(value, context=context)
  146. if any(not elem for elem in (key, value)):
  147. raise InferenceError(node=node, context=context)
  148. values = _update_with_replacement(values, {key: value})
  149. return values
  150. nodes.Dict._infer = infer_map # type: ignore[assignment]
  151. def _higher_function_scope(node):
  152. """Search for the first function which encloses the given
  153. scope. This can be used for looking up in that function's
  154. scope, in case looking up in a lower scope for a particular
  155. name fails.
  156. :param node: A scope node.
  157. :returns:
  158. ``None``, if no parent function scope was found,
  159. otherwise an instance of :class:`astroid.nodes.scoped_nodes.Function`,
  160. which encloses the given node.
  161. """
  162. current = node
  163. while current.parent and not isinstance(current.parent, nodes.FunctionDef):
  164. current = current.parent
  165. if current and current.parent:
  166. return current.parent
  167. return None
  168. def infer_name(self, context=None):
  169. """infer a Name: use name lookup rules"""
  170. frame, stmts = self.lookup(self.name)
  171. if not stmts:
  172. # Try to see if the name is enclosed in a nested function
  173. # and use the higher (first function) scope for searching.
  174. parent_function = _higher_function_scope(self.scope())
  175. if parent_function:
  176. _, stmts = parent_function.lookup(self.name)
  177. if not stmts:
  178. raise NameInferenceError(
  179. name=self.name, scope=self.scope(), context=context
  180. )
  181. context = copy_context(context)
  182. context.lookupname = self.name
  183. return bases._infer_stmts(stmts, context, frame)
  184. # pylint: disable=no-value-for-parameter
  185. nodes.Name._infer = decorators.raise_if_nothing_inferred(
  186. decorators.path_wrapper(infer_name)
  187. )
  188. nodes.AssignName.infer_lhs = infer_name # won't work with a path wrapper
  189. @decorators.raise_if_nothing_inferred
  190. @decorators.path_wrapper
  191. def infer_call(self, context=None):
  192. """infer a Call node by trying to guess what the function returns"""
  193. callcontext = copy_context(context)
  194. callcontext.boundnode = None
  195. if context is not None:
  196. callcontext.extra_context = _populate_context_lookup(self, context.clone())
  197. for callee in self.func.infer(context):
  198. if callee is util.Uninferable:
  199. yield callee
  200. continue
  201. try:
  202. if hasattr(callee, "infer_call_result"):
  203. callcontext.callcontext = CallContext(
  204. args=self.args, keywords=self.keywords, callee=callee
  205. )
  206. yield from callee.infer_call_result(caller=self, context=callcontext)
  207. except InferenceError:
  208. continue
  209. return dict(node=self, context=context)
  210. nodes.Call._infer = infer_call # type: ignore[assignment]
  211. @decorators.raise_if_nothing_inferred
  212. @decorators.path_wrapper
  213. def infer_import(self, context=None, asname=True):
  214. """infer an Import node: return the imported module/object"""
  215. name = context.lookupname
  216. if name is None:
  217. raise InferenceError(node=self, context=context)
  218. try:
  219. if asname:
  220. yield self.do_import_module(self.real_name(name))
  221. else:
  222. yield self.do_import_module(name)
  223. except AstroidBuildingError as exc:
  224. raise InferenceError(node=self, context=context) from exc
  225. nodes.Import._infer = infer_import
  226. @decorators.raise_if_nothing_inferred
  227. @decorators.path_wrapper
  228. def infer_import_from(self, context=None, asname=True):
  229. """infer a ImportFrom node: return the imported module/object"""
  230. name = context.lookupname
  231. if name is None:
  232. raise InferenceError(node=self, context=context)
  233. if asname:
  234. try:
  235. name = self.real_name(name)
  236. except AttributeInferenceError as exc:
  237. # See https://github.com/PyCQA/pylint/issues/4692
  238. raise InferenceError(node=self, context=context) from exc
  239. try:
  240. module = self.do_import_module()
  241. except AstroidBuildingError as exc:
  242. raise InferenceError(node=self, context=context) from exc
  243. try:
  244. context = copy_context(context)
  245. context.lookupname = name
  246. stmts = module.getattr(name, ignore_locals=module is self.root())
  247. return bases._infer_stmts(stmts, context)
  248. except AttributeInferenceError as error:
  249. raise InferenceError(
  250. str(error), target=self, attribute=name, context=context
  251. ) from error
  252. nodes.ImportFrom._infer = infer_import_from # type: ignore[assignment]
  253. def infer_attribute(self, context=None):
  254. """infer an Attribute node by using getattr on the associated object"""
  255. for owner in self.expr.infer(context):
  256. if owner is util.Uninferable:
  257. yield owner
  258. continue
  259. if not context:
  260. context = InferenceContext()
  261. old_boundnode = context.boundnode
  262. try:
  263. context.boundnode = owner
  264. yield from owner.igetattr(self.attrname, context)
  265. except (
  266. AttributeInferenceError,
  267. InferenceError,
  268. AttributeError,
  269. ):
  270. pass
  271. finally:
  272. context.boundnode = old_boundnode
  273. return dict(node=self, context=context)
  274. nodes.Attribute._infer = decorators.raise_if_nothing_inferred(
  275. decorators.path_wrapper(infer_attribute)
  276. )
  277. # won't work with a path wrapper
  278. nodes.AssignAttr.infer_lhs = decorators.raise_if_nothing_inferred(infer_attribute)
  279. @decorators.raise_if_nothing_inferred
  280. @decorators.path_wrapper
  281. def infer_global(self, context=None):
  282. if context.lookupname is None:
  283. raise InferenceError(node=self, context=context)
  284. try:
  285. return bases._infer_stmts(self.root().getattr(context.lookupname), context)
  286. except AttributeInferenceError as error:
  287. raise InferenceError(
  288. str(error), target=self, attribute=context.lookupname, context=context
  289. ) from error
  290. nodes.Global._infer = infer_global # type: ignore[assignment]
  291. _SUBSCRIPT_SENTINEL = object()
  292. def infer_subscript(self, context=None):
  293. """Inference for subscripts
  294. We're understanding if the index is a Const
  295. or a slice, passing the result of inference
  296. to the value's `getitem` method, which should
  297. handle each supported index type accordingly.
  298. """
  299. found_one = False
  300. for value in self.value.infer(context):
  301. if value is util.Uninferable:
  302. yield util.Uninferable
  303. return None
  304. for index in self.slice.infer(context):
  305. if index is util.Uninferable:
  306. yield util.Uninferable
  307. return None
  308. # Try to deduce the index value.
  309. index_value = _SUBSCRIPT_SENTINEL
  310. if value.__class__ == bases.Instance:
  311. index_value = index
  312. elif index.__class__ == bases.Instance:
  313. instance_as_index = helpers.class_instance_as_index(index)
  314. if instance_as_index:
  315. index_value = instance_as_index
  316. else:
  317. index_value = index
  318. if index_value is _SUBSCRIPT_SENTINEL:
  319. raise InferenceError(node=self, context=context)
  320. try:
  321. assigned = value.getitem(index_value, context)
  322. except (
  323. AstroidTypeError,
  324. AstroidIndexError,
  325. AttributeInferenceError,
  326. AttributeError,
  327. ) as exc:
  328. raise InferenceError(node=self, context=context) from exc
  329. # Prevent inferring if the inferred subscript
  330. # is the same as the original subscripted object.
  331. if self is assigned or assigned is util.Uninferable:
  332. yield util.Uninferable
  333. return None
  334. yield from assigned.infer(context)
  335. found_one = True
  336. if found_one:
  337. return dict(node=self, context=context)
  338. return None
  339. nodes.Subscript._infer = decorators.raise_if_nothing_inferred( # type: ignore[assignment]
  340. decorators.path_wrapper(infer_subscript)
  341. )
  342. nodes.Subscript.infer_lhs = decorators.raise_if_nothing_inferred(infer_subscript)
  343. @decorators.raise_if_nothing_inferred
  344. @decorators.path_wrapper
  345. def _infer_boolop(self, context=None):
  346. """Infer a boolean operation (and / or / not).
  347. The function will calculate the boolean operation
  348. for all pairs generated through inference for each component
  349. node.
  350. """
  351. values = self.values
  352. if self.op == "or":
  353. predicate = operator.truth
  354. else:
  355. predicate = operator.not_
  356. try:
  357. values = [value.infer(context=context) for value in values]
  358. except InferenceError:
  359. yield util.Uninferable
  360. return None
  361. for pair in itertools.product(*values):
  362. if any(item is util.Uninferable for item in pair):
  363. # Can't infer the final result, just yield Uninferable.
  364. yield util.Uninferable
  365. continue
  366. bool_values = [item.bool_value() for item in pair]
  367. if any(item is util.Uninferable for item in bool_values):
  368. # Can't infer the final result, just yield Uninferable.
  369. yield util.Uninferable
  370. continue
  371. # Since the boolean operations are short circuited operations,
  372. # this code yields the first value for which the predicate is True
  373. # and if no value respected the predicate, then the last value will
  374. # be returned (or Uninferable if there was no last value).
  375. # This is conforming to the semantics of `and` and `or`:
  376. # 1 and 0 -> 1
  377. # 0 and 1 -> 0
  378. # 1 or 0 -> 1
  379. # 0 or 1 -> 1
  380. value = util.Uninferable
  381. for value, bool_value in zip(pair, bool_values):
  382. if predicate(bool_value):
  383. yield value
  384. break
  385. else:
  386. yield value
  387. return dict(node=self, context=context)
  388. nodes.BoolOp._infer = _infer_boolop
  389. # UnaryOp, BinOp and AugAssign inferences
  390. def _filter_operation_errors(self, infer_callable, context, error):
  391. for result in infer_callable(self, context):
  392. if isinstance(result, error):
  393. # For the sake of .infer(), we don't care about operation
  394. # errors, which is the job of pylint. So return something
  395. # which shows that we can't infer the result.
  396. yield util.Uninferable
  397. else:
  398. yield result
  399. def _infer_unaryop(self, context=None):
  400. """Infer what an UnaryOp should return when evaluated."""
  401. for operand in self.operand.infer(context):
  402. try:
  403. yield operand.infer_unary_op(self.op)
  404. except TypeError as exc:
  405. # The operand doesn't support this operation.
  406. yield util.BadUnaryOperationMessage(operand, self.op, exc)
  407. except AttributeError as exc:
  408. meth = protocols.UNARY_OP_METHOD[self.op]
  409. if meth is None:
  410. # `not node`. Determine node's boolean
  411. # value and negate its result, unless it is
  412. # Uninferable, which will be returned as is.
  413. bool_value = operand.bool_value()
  414. if bool_value is not util.Uninferable:
  415. yield nodes.const_factory(not bool_value)
  416. else:
  417. yield util.Uninferable
  418. else:
  419. if not isinstance(operand, (bases.Instance, nodes.ClassDef)):
  420. # The operation was used on something which
  421. # doesn't support it.
  422. yield util.BadUnaryOperationMessage(operand, self.op, exc)
  423. continue
  424. try:
  425. try:
  426. methods = dunder_lookup.lookup(operand, meth)
  427. except AttributeInferenceError:
  428. yield util.BadUnaryOperationMessage(operand, self.op, exc)
  429. continue
  430. meth = methods[0]
  431. inferred = next(meth.infer(context=context), None)
  432. if inferred is util.Uninferable or not inferred.callable():
  433. continue
  434. context = copy_context(context)
  435. context.boundnode = operand
  436. context.callcontext = CallContext(args=[], callee=inferred)
  437. call_results = inferred.infer_call_result(self, context=context)
  438. result = next(call_results, None)
  439. if result is None:
  440. # Failed to infer, return the same type.
  441. yield operand
  442. else:
  443. yield result
  444. except AttributeInferenceError as exc:
  445. # The unary operation special method was not found.
  446. yield util.BadUnaryOperationMessage(operand, self.op, exc)
  447. except InferenceError:
  448. yield util.Uninferable
  449. @decorators.raise_if_nothing_inferred
  450. @decorators.path_wrapper
  451. def infer_unaryop(self, context=None):
  452. """Infer what an UnaryOp should return when evaluated."""
  453. yield from _filter_operation_errors(
  454. self, _infer_unaryop, context, util.BadUnaryOperationMessage
  455. )
  456. return dict(node=self, context=context)
  457. nodes.UnaryOp._infer_unaryop = _infer_unaryop
  458. nodes.UnaryOp._infer = infer_unaryop
  459. def _is_not_implemented(const):
  460. """Check if the given const node is NotImplemented."""
  461. return isinstance(const, nodes.Const) and const.value is NotImplemented
  462. def _invoke_binop_inference(instance, opnode, op, other, context, method_name):
  463. """Invoke binary operation inference on the given instance."""
  464. methods = dunder_lookup.lookup(instance, method_name)
  465. context = bind_context_to_node(context, instance)
  466. method = methods[0]
  467. context.callcontext.callee = method
  468. try:
  469. inferred = next(method.infer(context=context))
  470. except StopIteration as e:
  471. raise InferenceError(node=method, context=context) from e
  472. if inferred is util.Uninferable:
  473. raise InferenceError
  474. return instance.infer_binary_op(opnode, op, other, context, inferred)
  475. def _aug_op(instance, opnode, op, other, context, reverse=False):
  476. """Get an inference callable for an augmented binary operation."""
  477. method_name = protocols.AUGMENTED_OP_METHOD[op]
  478. return functools.partial(
  479. _invoke_binop_inference,
  480. instance=instance,
  481. op=op,
  482. opnode=opnode,
  483. other=other,
  484. context=context,
  485. method_name=method_name,
  486. )
  487. def _bin_op(instance, opnode, op, other, context, reverse=False):
  488. """Get an inference callable for a normal binary operation.
  489. If *reverse* is True, then the reflected method will be used instead.
  490. """
  491. if reverse:
  492. method_name = protocols.REFLECTED_BIN_OP_METHOD[op]
  493. else:
  494. method_name = protocols.BIN_OP_METHOD[op]
  495. return functools.partial(
  496. _invoke_binop_inference,
  497. instance=instance,
  498. op=op,
  499. opnode=opnode,
  500. other=other,
  501. context=context,
  502. method_name=method_name,
  503. )
  504. def _get_binop_contexts(context, left, right):
  505. """Get contexts for binary operations.
  506. This will return two inference contexts, the first one
  507. for x.__op__(y), the other one for y.__rop__(x), where
  508. only the arguments are inversed.
  509. """
  510. # The order is important, since the first one should be
  511. # left.__op__(right).
  512. for arg in (right, left):
  513. new_context = context.clone()
  514. new_context.callcontext = CallContext(args=[arg])
  515. new_context.boundnode = None
  516. yield new_context
  517. def _same_type(type1, type2):
  518. """Check if type1 is the same as type2."""
  519. return type1.qname() == type2.qname()
  520. def _get_binop_flow(
  521. left, left_type, binary_opnode, right, right_type, context, reverse_context
  522. ):
  523. """Get the flow for binary operations.
  524. The rules are a bit messy:
  525. * if left and right have the same type, then only one
  526. method will be called, left.__op__(right)
  527. * if left and right are unrelated typewise, then first
  528. left.__op__(right) is tried and if this does not exist
  529. or returns NotImplemented, then right.__rop__(left) is tried.
  530. * if left is a subtype of right, then only left.__op__(right)
  531. is tried.
  532. * if left is a supertype of right, then right.__rop__(left)
  533. is first tried and then left.__op__(right)
  534. """
  535. op = binary_opnode.op
  536. if _same_type(left_type, right_type):
  537. methods = [_bin_op(left, binary_opnode, op, right, context)]
  538. elif helpers.is_subtype(left_type, right_type):
  539. methods = [_bin_op(left, binary_opnode, op, right, context)]
  540. elif helpers.is_supertype(left_type, right_type):
  541. methods = [
  542. _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True),
  543. _bin_op(left, binary_opnode, op, right, context),
  544. ]
  545. else:
  546. methods = [
  547. _bin_op(left, binary_opnode, op, right, context),
  548. _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True),
  549. ]
  550. return methods
  551. def _get_aug_flow(
  552. left, left_type, aug_opnode, right, right_type, context, reverse_context
  553. ):
  554. """Get the flow for augmented binary operations.
  555. The rules are a bit messy:
  556. * if left and right have the same type, then left.__augop__(right)
  557. is first tried and then left.__op__(right).
  558. * if left and right are unrelated typewise, then
  559. left.__augop__(right) is tried, then left.__op__(right)
  560. is tried and then right.__rop__(left) is tried.
  561. * if left is a subtype of right, then left.__augop__(right)
  562. is tried and then left.__op__(right).
  563. * if left is a supertype of right, then left.__augop__(right)
  564. is tried, then right.__rop__(left) and then
  565. left.__op__(right)
  566. """
  567. bin_op = aug_opnode.op.strip("=")
  568. aug_op = aug_opnode.op
  569. if _same_type(left_type, right_type):
  570. methods = [
  571. _aug_op(left, aug_opnode, aug_op, right, context),
  572. _bin_op(left, aug_opnode, bin_op, right, context),
  573. ]
  574. elif helpers.is_subtype(left_type, right_type):
  575. methods = [
  576. _aug_op(left, aug_opnode, aug_op, right, context),
  577. _bin_op(left, aug_opnode, bin_op, right, context),
  578. ]
  579. elif helpers.is_supertype(left_type, right_type):
  580. methods = [
  581. _aug_op(left, aug_opnode, aug_op, right, context),
  582. _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True),
  583. _bin_op(left, aug_opnode, bin_op, right, context),
  584. ]
  585. else:
  586. methods = [
  587. _aug_op(left, aug_opnode, aug_op, right, context),
  588. _bin_op(left, aug_opnode, bin_op, right, context),
  589. _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True),
  590. ]
  591. return methods
  592. def _infer_binary_operation(left, right, binary_opnode, context, flow_factory):
  593. """Infer a binary operation between a left operand and a right operand
  594. This is used by both normal binary operations and augmented binary
  595. operations, the only difference is the flow factory used.
  596. """
  597. context, reverse_context = _get_binop_contexts(context, left, right)
  598. left_type = helpers.object_type(left)
  599. right_type = helpers.object_type(right)
  600. methods = flow_factory(
  601. left, left_type, binary_opnode, right, right_type, context, reverse_context
  602. )
  603. for method in methods:
  604. try:
  605. results = list(method())
  606. except AttributeError:
  607. continue
  608. except AttributeInferenceError:
  609. continue
  610. except InferenceError:
  611. yield util.Uninferable
  612. return
  613. else:
  614. if any(result is util.Uninferable for result in results):
  615. yield util.Uninferable
  616. return
  617. if all(map(_is_not_implemented, results)):
  618. continue
  619. not_implemented = sum(
  620. 1 for result in results if _is_not_implemented(result)
  621. )
  622. if not_implemented and not_implemented != len(results):
  623. # Can't infer yet what this is.
  624. yield util.Uninferable
  625. return
  626. yield from results
  627. return
  628. # The operation doesn't seem to be supported so let the caller know about it
  629. yield util.BadBinaryOperationMessage(left_type, binary_opnode.op, right_type)
  630. def _infer_binop(self, context):
  631. """Binary operation inference logic."""
  632. left = self.left
  633. right = self.right
  634. # we use two separate contexts for evaluating lhs and rhs because
  635. # 1. evaluating lhs may leave some undesired entries in context.path
  636. # which may not let us infer right value of rhs
  637. context = context or InferenceContext()
  638. lhs_context = copy_context(context)
  639. rhs_context = copy_context(context)
  640. lhs_iter = left.infer(context=lhs_context)
  641. rhs_iter = right.infer(context=rhs_context)
  642. for lhs, rhs in itertools.product(lhs_iter, rhs_iter):
  643. if any(value is util.Uninferable for value in (rhs, lhs)):
  644. # Don't know how to process this.
  645. yield util.Uninferable
  646. return
  647. try:
  648. yield from _infer_binary_operation(lhs, rhs, self, context, _get_binop_flow)
  649. except _NonDeducibleTypeHierarchy:
  650. yield util.Uninferable
  651. @decorators.yes_if_nothing_inferred
  652. @decorators.path_wrapper
  653. def infer_binop(self, context=None):
  654. return _filter_operation_errors(
  655. self, _infer_binop, context, util.BadBinaryOperationMessage
  656. )
  657. nodes.BinOp._infer_binop = _infer_binop
  658. nodes.BinOp._infer = infer_binop
  659. COMPARE_OPS: Dict[str, Callable[[Any, Any], bool]] = {
  660. "==": operator.eq,
  661. "!=": operator.ne,
  662. "<": operator.lt,
  663. "<=": operator.le,
  664. ">": operator.gt,
  665. ">=": operator.ge,
  666. "in": lambda a, b: a in b,
  667. "not in": lambda a, b: a not in b,
  668. }
  669. UNINFERABLE_OPS = {
  670. "is",
  671. "is not",
  672. }
  673. def _to_literal(node: nodes.NodeNG) -> Any:
  674. # Can raise SyntaxError or ValueError from ast.literal_eval
  675. # Can raise AttributeError from node.as_string() as not all nodes have a visitor
  676. # Is this the stupidest idea or the simplest idea?
  677. return ast.literal_eval(node.as_string())
  678. def _do_compare(
  679. left_iter: Iterable[nodes.NodeNG], op: str, right_iter: Iterable[nodes.NodeNG]
  680. ) -> "bool | type[util.Uninferable]":
  681. """
  682. If all possible combinations are either True or False, return that:
  683. >>> _do_compare([1, 2], '<=', [3, 4])
  684. True
  685. >>> _do_compare([1, 2], '==', [3, 4])
  686. False
  687. If any item is uninferable, or if some combinations are True and some
  688. are False, return Uninferable:
  689. >>> _do_compare([1, 3], '<=', [2, 4])
  690. util.Uninferable
  691. """
  692. retval = None
  693. if op in UNINFERABLE_OPS:
  694. return util.Uninferable
  695. op_func = COMPARE_OPS[op]
  696. for left, right in itertools.product(left_iter, right_iter):
  697. if left is util.Uninferable or right is util.Uninferable:
  698. return util.Uninferable
  699. try:
  700. left, right = _to_literal(left), _to_literal(right)
  701. except (SyntaxError, ValueError, AttributeError):
  702. return util.Uninferable
  703. try:
  704. expr = op_func(left, right)
  705. except TypeError as exc:
  706. raise AstroidTypeError from exc
  707. if retval is None:
  708. retval = expr
  709. elif retval != expr:
  710. return util.Uninferable
  711. # (or both, but "True | False" is basically the same)
  712. return retval # it was all the same value
  713. def _infer_compare(
  714. self: nodes.Compare, context: Optional[InferenceContext] = None
  715. ) -> Any:
  716. """Chained comparison inference logic."""
  717. retval = True
  718. ops = self.ops
  719. left_node = self.left
  720. lhs = list(left_node.infer(context=context))
  721. # should we break early if first element is uninferable?
  722. for op, right_node in ops:
  723. # eagerly evaluate rhs so that values can be re-used as lhs
  724. rhs = list(right_node.infer(context=context))
  725. try:
  726. retval = _do_compare(lhs, op, rhs)
  727. except AstroidTypeError:
  728. retval = util.Uninferable
  729. break
  730. if retval is not True:
  731. break # short-circuit
  732. lhs = rhs # continue
  733. if retval is util.Uninferable:
  734. yield retval
  735. else:
  736. yield nodes.Const(retval)
  737. nodes.Compare._infer = _infer_compare # type: ignore[assignment]
  738. def _infer_augassign(self, context=None):
  739. """Inference logic for augmented binary operations."""
  740. if context is None:
  741. context = InferenceContext()
  742. rhs_context = context.clone()
  743. lhs_iter = self.target.infer_lhs(context=context)
  744. rhs_iter = self.value.infer(context=rhs_context)
  745. for lhs, rhs in itertools.product(lhs_iter, rhs_iter):
  746. if any(value is util.Uninferable for value in (rhs, lhs)):
  747. # Don't know how to process this.
  748. yield util.Uninferable
  749. return
  750. try:
  751. yield from _infer_binary_operation(
  752. left=lhs,
  753. right=rhs,
  754. binary_opnode=self,
  755. context=context,
  756. flow_factory=_get_aug_flow,
  757. )
  758. except _NonDeducibleTypeHierarchy:
  759. yield util.Uninferable
  760. @decorators.raise_if_nothing_inferred
  761. @decorators.path_wrapper
  762. def infer_augassign(self, context=None):
  763. return _filter_operation_errors(
  764. self, _infer_augassign, context, util.BadBinaryOperationMessage
  765. )
  766. nodes.AugAssign._infer_augassign = _infer_augassign
  767. nodes.AugAssign._infer = infer_augassign
  768. # End of binary operation inference.
  769. @decorators.raise_if_nothing_inferred
  770. def infer_arguments(self, context=None):
  771. name = context.lookupname
  772. if name is None:
  773. raise InferenceError(node=self, context=context)
  774. return protocols._arguments_infer_argname(self, name, context)
  775. nodes.Arguments._infer = infer_arguments # type: ignore[assignment]
  776. @decorators.raise_if_nothing_inferred
  777. @decorators.path_wrapper
  778. def infer_assign(self, context=None):
  779. """infer a AssignName/AssignAttr: need to inspect the RHS part of the
  780. assign node
  781. """
  782. if isinstance(self.parent, nodes.AugAssign):
  783. return self.parent.infer(context)
  784. stmts = list(self.assigned_stmts(context=context))
  785. return bases._infer_stmts(stmts, context)
  786. nodes.AssignName._infer = infer_assign
  787. nodes.AssignAttr._infer = infer_assign
  788. @decorators.raise_if_nothing_inferred
  789. @decorators.path_wrapper
  790. def infer_empty_node(self, context=None):
  791. if not self.has_underlying_object():
  792. yield util.Uninferable
  793. else:
  794. try:
  795. yield from AstroidManager().infer_ast_from_something(
  796. self.object, context=context
  797. )
  798. except AstroidError:
  799. yield util.Uninferable
  800. nodes.EmptyNode._infer = infer_empty_node # type: ignore[assignment]
  801. @decorators.raise_if_nothing_inferred
  802. def infer_index(self, context=None):
  803. return self.value.infer(context)
  804. nodes.Index._infer = infer_index # type: ignore[assignment]
  805. def _populate_context_lookup(call, context):
  806. # Allows context to be saved for later
  807. # for inference inside a function
  808. context_lookup = {}
  809. if context is None:
  810. return context_lookup
  811. for arg in call.args:
  812. if isinstance(arg, nodes.Starred):
  813. context_lookup[arg.value] = context
  814. else:
  815. context_lookup[arg] = context
  816. keywords = call.keywords if call.keywords is not None else []
  817. for keyword in keywords:
  818. context_lookup[keyword.value] = context
  819. return context_lookup
  820. @decorators.raise_if_nothing_inferred
  821. def infer_ifexp(self, context=None):
  822. """Support IfExp inference
  823. If we can't infer the truthiness of the condition, we default
  824. to inferring both branches. Otherwise, we infer either branch
  825. depending on the condition.
  826. """
  827. both_branches = False
  828. # We use two separate contexts for evaluating lhs and rhs because
  829. # evaluating lhs may leave some undesired entries in context.path
  830. # which may not let us infer right value of rhs.
  831. context = context or InferenceContext()
  832. lhs_context = copy_context(context)
  833. rhs_context = copy_context(context)
  834. try:
  835. test = next(self.test.infer(context=context.clone()))
  836. except (InferenceError, StopIteration):
  837. both_branches = True
  838. else:
  839. if test is not util.Uninferable:
  840. if test.bool_value():
  841. yield from self.body.infer(context=lhs_context)
  842. else:
  843. yield from self.orelse.infer(context=rhs_context)
  844. else:
  845. both_branches = True
  846. if both_branches:
  847. yield from self.body.infer(context=lhs_context)
  848. yield from self.orelse.infer(context=rhs_context)
  849. nodes.IfExp._infer = infer_ifexp # type: ignore[assignment]
  850. # pylint: disable=dangerous-default-value
  851. @wrapt.decorator
  852. def _cached_generator(func, instance, args, kwargs, _cache={}): # noqa: B006
  853. node = args[0]
  854. try:
  855. return iter(_cache[func, id(node)])
  856. except KeyError:
  857. result = func(*args, **kwargs)
  858. # Need to keep an iterator around
  859. original, copy = itertools.tee(result)
  860. _cache[func, id(node)] = list(copy)
  861. return original
  862. # When inferring a property, we instantiate a new `objects.Property` object,
  863. # which in turn, because it inherits from `FunctionDef`, sets itself in the locals
  864. # of the wrapping frame. This means that every time we infer a property, the locals
  865. # are mutated with a new instance of the property. This is why we cache the result
  866. # of the function's inference.
  867. @_cached_generator
  868. def infer_functiondef(self, context=None):
  869. if not self.decorators or not bases._is_property(self):
  870. yield self
  871. return dict(node=self, context=context)
  872. prop_func = objects.Property(
  873. function=self,
  874. name=self.name,
  875. doc=self.doc,
  876. lineno=self.lineno,
  877. parent=self.parent,
  878. col_offset=self.col_offset,
  879. )
  880. prop_func.postinit(body=[], args=self.args)
  881. yield prop_func
  882. return dict(node=self, context=context)
  883. nodes.FunctionDef._infer = infer_functiondef # type: ignore[assignment]