Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

inference.py 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. # Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
  2. # Copyright (c) 2013-2014 Google, Inc.
  3. # Copyright (c) 2014-2016 Claudiu Popa <pcmanticore@gmail.com>
  4. # Copyright (c) 2015-2016 Cara Vinson <ceridwenv@gmail.com>
  5. # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
  6. # For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
  7. """this module contains a set of functions to handle inference on astroid trees
  8. """
  9. import functools
  10. import itertools
  11. import operator
  12. from astroid import bases
  13. from astroid import context as contextmod
  14. from astroid import exceptions
  15. from astroid import decorators
  16. from astroid import helpers
  17. from astroid import manager
  18. from astroid import nodes
  19. from astroid.interpreter import dunder_lookup
  20. from astroid import protocols
  21. from astroid import util
  22. MANAGER = manager.AstroidManager()
  23. # .infer method ###############################################################
  24. def infer_end(self, context=None):
  25. """inference's end for node such as Module, ClassDef, FunctionDef,
  26. Const...
  27. """
  28. yield self
  29. nodes.Module._infer = infer_end
  30. nodes.ClassDef._infer = infer_end
  31. nodes.FunctionDef._infer = infer_end
  32. nodes.Lambda._infer = infer_end
  33. nodes.Const._infer = infer_end
  34. nodes.Slice._infer = infer_end
  35. def infer_seq(self, context=None):
  36. if not any(isinstance(e, nodes.Starred) for e in self.elts):
  37. yield self
  38. else:
  39. values = _infer_seq(self, context)
  40. new_seq = type(self)(self.lineno, self.col_offset, self.parent)
  41. new_seq.postinit(values)
  42. yield new_seq
  43. def _infer_seq(node, context=None):
  44. """Infer all values based on _BaseContainer.elts"""
  45. values = []
  46. for elt in node.elts:
  47. if isinstance(elt, nodes.Starred):
  48. starred = helpers.safe_infer(elt.value, context)
  49. if starred in (None, util.Uninferable):
  50. raise exceptions.InferenceError(node=node,
  51. context=context)
  52. if not hasattr(starred, 'elts'):
  53. raise exceptions.InferenceError(node=node,
  54. context=context)
  55. values.extend(_infer_seq(starred))
  56. else:
  57. values.append(elt)
  58. return values
  59. nodes.List._infer = infer_seq
  60. nodes.Tuple._infer = infer_seq
  61. nodes.Set._infer = infer_seq
  62. def infer_map(self, context=None):
  63. if not any(isinstance(k, nodes.DictUnpack) for k, _ in self.items):
  64. yield self
  65. else:
  66. items = _infer_map(self, context)
  67. new_seq = type(self)(self.lineno, self.col_offset, self.parent)
  68. new_seq.postinit(list(items.items()))
  69. yield new_seq
  70. def _infer_map(node, context):
  71. """Infer all values based on Dict.items"""
  72. values = {}
  73. for name, value in node.items:
  74. if isinstance(name, nodes.DictUnpack):
  75. double_starred = helpers.safe_infer(value, context)
  76. if double_starred in (None, util.Uninferable):
  77. raise exceptions.InferenceError
  78. if not isinstance(double_starred, nodes.Dict):
  79. raise exceptions.InferenceError(node=node,
  80. context=context)
  81. values.update(_infer_map(double_starred, context))
  82. else:
  83. key = helpers.safe_infer(name, context=context)
  84. value = helpers.safe_infer(value, context=context)
  85. if any(elem in (None, util.Uninferable) for elem in (key, value)):
  86. raise exceptions.InferenceError(node=node,
  87. context=context)
  88. values[key] = value
  89. return values
  90. nodes.Dict._infer = infer_map
  91. def _higher_function_scope(node):
  92. """ Search for the first function which encloses the given
  93. scope. This can be used for looking up in that function's
  94. scope, in case looking up in a lower scope for a particular
  95. name fails.
  96. :param node: A scope node.
  97. :returns:
  98. ``None``, if no parent function scope was found,
  99. otherwise an instance of :class:`astroid.scoped_nodes.Function`,
  100. which encloses the given node.
  101. """
  102. current = node
  103. while current.parent and not isinstance(current.parent, nodes.FunctionDef):
  104. current = current.parent
  105. if current and current.parent:
  106. return current.parent
  107. return None
  108. def infer_name(self, context=None):
  109. """infer a Name: use name lookup rules"""
  110. frame, stmts = self.lookup(self.name)
  111. if not stmts:
  112. # Try to see if the name is enclosed in a nested function
  113. # and use the higher (first function) scope for searching.
  114. # TODO: should this be promoted to other nodes as well?
  115. parent_function = _higher_function_scope(self.scope())
  116. if parent_function:
  117. _, stmts = parent_function.lookup(self.name)
  118. if not stmts:
  119. raise exceptions.NameInferenceError(name=self.name,
  120. scope=self.scope(),
  121. context=context)
  122. context = context.clone()
  123. context.lookupname = self.name
  124. return bases._infer_stmts(stmts, context, frame)
  125. nodes.Name._infer = decorators.path_wrapper(infer_name)
  126. nodes.AssignName.infer_lhs = infer_name # won't work with a path wrapper
  127. @decorators.raise_if_nothing_inferred
  128. @decorators.path_wrapper
  129. def infer_call(self, context=None):
  130. """infer a Call node by trying to guess what the function returns"""
  131. callcontext = context.clone()
  132. callcontext.callcontext = contextmod.CallContext(args=self.args,
  133. keywords=self.keywords)
  134. callcontext.boundnode = None
  135. for callee in self.func.infer(context):
  136. if callee is util.Uninferable:
  137. yield callee
  138. continue
  139. try:
  140. if hasattr(callee, 'infer_call_result'):
  141. for inferred in callee.infer_call_result(self, callcontext):
  142. yield inferred
  143. except exceptions.InferenceError:
  144. ## XXX log error ?
  145. continue
  146. # Explicit StopIteration to return error information, see comment
  147. # in raise_if_nothing_inferred.
  148. raise StopIteration(dict(node=self, context=context))
  149. nodes.Call._infer = infer_call
  150. @decorators.path_wrapper
  151. def infer_import(self, context=None, asname=True):
  152. """infer an Import node: return the imported module/object"""
  153. name = context.lookupname
  154. if name is None:
  155. raise exceptions.InferenceError(node=self, context=context)
  156. try:
  157. if asname:
  158. yield self.do_import_module(self.real_name(name))
  159. else:
  160. yield self.do_import_module(name)
  161. except exceptions.AstroidBuildingError as exc:
  162. util.reraise(exceptions.InferenceError(node=self, error=exc,
  163. context=context))
  164. nodes.Import._infer = infer_import
  165. def infer_name_module(self, name):
  166. context = contextmod.InferenceContext()
  167. context.lookupname = name
  168. return self.infer(context, asname=False)
  169. nodes.Import.infer_name_module = infer_name_module
  170. @decorators.path_wrapper
  171. def infer_import_from(self, context=None, asname=True):
  172. """infer a ImportFrom node: return the imported module/object"""
  173. name = context.lookupname
  174. if name is None:
  175. raise exceptions.InferenceError(node=self, context=context)
  176. if asname:
  177. name = self.real_name(name)
  178. try:
  179. module = self.do_import_module()
  180. except exceptions.AstroidBuildingError as exc:
  181. util.reraise(exceptions.InferenceError(node=self, error=exc,
  182. context=context))
  183. try:
  184. context = contextmod.copy_context(context)
  185. context.lookupname = name
  186. stmts = module.getattr(name, ignore_locals=module is self.root())
  187. return bases._infer_stmts(stmts, context)
  188. except exceptions.AttributeInferenceError as error:
  189. util.reraise(exceptions.InferenceError(
  190. error.message, target=self, attribute=name, context=context))
  191. nodes.ImportFrom._infer = infer_import_from
  192. @decorators.raise_if_nothing_inferred
  193. def infer_attribute(self, context=None):
  194. """infer an Attribute node by using getattr on the associated object"""
  195. for owner in self.expr.infer(context):
  196. if owner is util.Uninferable:
  197. yield owner
  198. continue
  199. if context and context.boundnode:
  200. # This handles the situation where the attribute is accessed through a subclass
  201. # of a base class and the attribute is defined at the base class's level,
  202. # by taking in consideration a redefinition in the subclass.
  203. if (isinstance(owner, bases.Instance)
  204. and isinstance(context.boundnode, bases.Instance)):
  205. try:
  206. if helpers.is_subtype(helpers.object_type(context.boundnode),
  207. helpers.object_type(owner)):
  208. owner = context.boundnode
  209. except exceptions._NonDeducibleTypeHierarchy:
  210. # Can't determine anything useful.
  211. pass
  212. try:
  213. context.boundnode = owner
  214. for obj in owner.igetattr(self.attrname, context):
  215. yield obj
  216. context.boundnode = None
  217. except (exceptions.AttributeInferenceError, exceptions.InferenceError):
  218. context.boundnode = None
  219. except AttributeError:
  220. # XXX method / function
  221. context.boundnode = None
  222. # Explicit StopIteration to return error information, see comment
  223. # in raise_if_nothing_inferred.
  224. raise StopIteration(dict(node=self, context=context))
  225. nodes.Attribute._infer = decorators.path_wrapper(infer_attribute)
  226. nodes.AssignAttr.infer_lhs = infer_attribute # # won't work with a path wrapper
  227. @decorators.path_wrapper
  228. def infer_global(self, context=None):
  229. if context.lookupname is None:
  230. raise exceptions.InferenceError(node=self, context=context)
  231. try:
  232. return bases._infer_stmts(self.root().getattr(context.lookupname),
  233. context)
  234. except exceptions.AttributeInferenceError as error:
  235. util.reraise(exceptions.InferenceError(
  236. error.message, target=self, attribute=context.lookupname,
  237. context=context))
  238. nodes.Global._infer = infer_global
  239. _SUBSCRIPT_SENTINEL = object()
  240. @decorators.raise_if_nothing_inferred
  241. def infer_subscript(self, context=None):
  242. """Inference for subscripts
  243. We're understanding if the index is a Const
  244. or a slice, passing the result of inference
  245. to the value's `getitem` method, which should
  246. handle each supported index type accordingly.
  247. """
  248. value = next(self.value.infer(context))
  249. if value is util.Uninferable:
  250. yield util.Uninferable
  251. return
  252. index = next(self.slice.infer(context))
  253. if index is util.Uninferable:
  254. yield util.Uninferable
  255. return
  256. # Try to deduce the index value.
  257. index_value = _SUBSCRIPT_SENTINEL
  258. if value.__class__ == bases.Instance:
  259. index_value = index
  260. else:
  261. if index.__class__ == bases.Instance:
  262. instance_as_index = helpers.class_instance_as_index(index)
  263. if instance_as_index:
  264. index_value = instance_as_index
  265. else:
  266. index_value = index
  267. if index_value is _SUBSCRIPT_SENTINEL:
  268. raise exceptions.InferenceError(node=self, context=context)
  269. try:
  270. assigned = value.getitem(index_value, context)
  271. except (exceptions.AstroidTypeError,
  272. exceptions.AstroidIndexError,
  273. exceptions.AttributeInferenceError,
  274. AttributeError) as exc:
  275. util.reraise(exceptions.InferenceError(node=self, error=exc,
  276. context=context))
  277. # Prevent inferring if the inferred subscript
  278. # is the same as the original subscripted object.
  279. if self is assigned or assigned is util.Uninferable:
  280. yield util.Uninferable
  281. return
  282. for inferred in assigned.infer(context):
  283. yield inferred
  284. # Explicit StopIteration to return error information, see comment
  285. # in raise_if_nothing_inferred.
  286. raise StopIteration(dict(node=self, context=context))
  287. nodes.Subscript._infer = decorators.path_wrapper(infer_subscript)
  288. nodes.Subscript.infer_lhs = infer_subscript
  289. @decorators.raise_if_nothing_inferred
  290. @decorators.path_wrapper
  291. def _infer_boolop(self, context=None):
  292. """Infer a boolean operation (and / or / not).
  293. The function will calculate the boolean operation
  294. for all pairs generated through inference for each component
  295. node.
  296. """
  297. values = self.values
  298. if self.op == 'or':
  299. predicate = operator.truth
  300. else:
  301. predicate = operator.not_
  302. try:
  303. values = [value.infer(context=context) for value in values]
  304. except exceptions.InferenceError:
  305. yield util.Uninferable
  306. return
  307. for pair in itertools.product(*values):
  308. if any(item is util.Uninferable for item in pair):
  309. # Can't infer the final result, just yield Uninferable.
  310. yield util.Uninferable
  311. continue
  312. bool_values = [item.bool_value() for item in pair]
  313. if any(item is util.Uninferable for item in bool_values):
  314. # Can't infer the final result, just yield Uninferable.
  315. yield util.Uninferable
  316. continue
  317. # Since the boolean operations are short circuited operations,
  318. # this code yields the first value for which the predicate is True
  319. # and if no value respected the predicate, then the last value will
  320. # be returned (or Uninferable if there was no last value).
  321. # This is conforming to the semantics of `and` and `or`:
  322. # 1 and 0 -> 1
  323. # 0 and 1 -> 0
  324. # 1 or 0 -> 1
  325. # 0 or 1 -> 1
  326. value = util.Uninferable
  327. for value, bool_value in zip(pair, bool_values):
  328. if predicate(bool_value):
  329. yield value
  330. break
  331. else:
  332. yield value
  333. # Explicit StopIteration to return error information, see comment
  334. # in raise_if_nothing_inferred.
  335. raise StopIteration(dict(node=self, context=context))
  336. nodes.BoolOp._infer = _infer_boolop
  337. # UnaryOp, BinOp and AugAssign inferences
  338. def _filter_operation_errors(self, infer_callable, context, error):
  339. for result in infer_callable(self, context):
  340. if isinstance(result, error):
  341. # For the sake of .infer(), we don't care about operation
  342. # errors, which is the job of pylint. So return something
  343. # which shows that we can't infer the result.
  344. yield util.Uninferable
  345. else:
  346. yield result
  347. def _infer_unaryop(self, context=None):
  348. """Infer what an UnaryOp should return when evaluated."""
  349. for operand in self.operand.infer(context):
  350. try:
  351. yield operand.infer_unary_op(self.op)
  352. except TypeError as exc:
  353. # The operand doesn't support this operation.
  354. yield util.BadUnaryOperationMessage(operand, self.op, exc)
  355. except AttributeError as exc:
  356. meth = protocols.UNARY_OP_METHOD[self.op]
  357. if meth is None:
  358. # `not node`. Determine node's boolean
  359. # value and negate its result, unless it is
  360. # Uninferable, which will be returned as is.
  361. bool_value = operand.bool_value()
  362. if bool_value is not util.Uninferable:
  363. yield nodes.const_factory(not bool_value)
  364. else:
  365. yield util.Uninferable
  366. else:
  367. if not isinstance(operand, (bases.Instance, nodes.ClassDef)):
  368. # The operation was used on something which
  369. # doesn't support it.
  370. yield util.BadUnaryOperationMessage(operand, self.op, exc)
  371. continue
  372. try:
  373. try:
  374. methods = dunder_lookup.lookup(operand, meth)
  375. except exceptions.AttributeInferenceError:
  376. yield util.BadUnaryOperationMessage(operand, self.op, exc)
  377. continue
  378. meth = methods[0]
  379. inferred = next(meth.infer(context=context))
  380. if inferred is util.Uninferable or not inferred.callable():
  381. continue
  382. context = contextmod.copy_context(context)
  383. context.callcontext = contextmod.CallContext(args=[operand])
  384. call_results = inferred.infer_call_result(self, context=context)
  385. result = next(call_results, None)
  386. if result is None:
  387. # Failed to infer, return the same type.
  388. yield operand
  389. else:
  390. yield result
  391. except exceptions.AttributeInferenceError as exc:
  392. # The unary operation special method was not found.
  393. yield util.BadUnaryOperationMessage(operand, self.op, exc)
  394. except exceptions.InferenceError:
  395. yield util.Uninferable
  396. @decorators.raise_if_nothing_inferred
  397. @decorators.path_wrapper
  398. def infer_unaryop(self, context=None):
  399. """Infer what an UnaryOp should return when evaluated."""
  400. for inferred in _filter_operation_errors(self, _infer_unaryop, context,
  401. util.BadUnaryOperationMessage):
  402. yield inferred
  403. # Explicit StopIteration to return error information, see comment
  404. # in raise_if_nothing_inferred.
  405. raise StopIteration(dict(node=self, context=context))
  406. nodes.UnaryOp._infer_unaryop = _infer_unaryop
  407. nodes.UnaryOp._infer = infer_unaryop
  408. def _is_not_implemented(const):
  409. """Check if the given const node is NotImplemented."""
  410. return isinstance(const, nodes.Const) and const.value is NotImplemented
  411. def _invoke_binop_inference(instance, opnode, op, other, context, method_name):
  412. """Invoke binary operation inference on the given instance."""
  413. methods = dunder_lookup.lookup(instance, method_name)
  414. method = methods[0]
  415. inferred = next(method.infer(context=context))
  416. if inferred is util.Uninferable:
  417. raise exceptions.InferenceError
  418. return instance.infer_binary_op(opnode, op, other, context, inferred)
  419. def _aug_op(instance, opnode, op, other, context, reverse=False):
  420. """Get an inference callable for an augmented binary operation."""
  421. method_name = protocols.AUGMENTED_OP_METHOD[op]
  422. return functools.partial(_invoke_binop_inference,
  423. instance=instance,
  424. op=op, opnode=opnode, other=other,
  425. context=context,
  426. method_name=method_name)
  427. def _bin_op(instance, opnode, op, other, context, reverse=False):
  428. """Get an inference callable for a normal binary operation.
  429. If *reverse* is True, then the reflected method will be used instead.
  430. """
  431. if reverse:
  432. method_name = protocols.REFLECTED_BIN_OP_METHOD[op]
  433. else:
  434. method_name = protocols.BIN_OP_METHOD[op]
  435. return functools.partial(_invoke_binop_inference,
  436. instance=instance,
  437. op=op, opnode=opnode, other=other,
  438. context=context,
  439. method_name=method_name)
  440. def _get_binop_contexts(context, left, right):
  441. """Get contexts for binary operations.
  442. This will return two inferrence contexts, the first one
  443. for x.__op__(y), the other one for y.__rop__(x), where
  444. only the arguments are inversed.
  445. """
  446. # The order is important, since the first one should be
  447. # left.__op__(right).
  448. for arg in (right, left):
  449. new_context = context.clone()
  450. new_context.callcontext = contextmod.CallContext(args=[arg])
  451. new_context.boundnode = None
  452. yield new_context
  453. def _same_type(type1, type2):
  454. """Check if type1 is the same as type2."""
  455. return type1.qname() == type2.qname()
  456. def _get_binop_flow(left, left_type, binary_opnode, right, right_type,
  457. context, reverse_context):
  458. """Get the flow for binary operations.
  459. The rules are a bit messy:
  460. * if left and right have the same type, then only one
  461. method will be called, left.__op__(right)
  462. * if left and right are unrelated typewise, then first
  463. left.__op__(right) is tried and if this does not exist
  464. or returns NotImplemented, then right.__rop__(left) is tried.
  465. * if left is a subtype of right, then only left.__op__(right)
  466. is tried.
  467. * if left is a supertype of right, then right.__rop__(left)
  468. is first tried and then left.__op__(right)
  469. """
  470. op = binary_opnode.op
  471. if _same_type(left_type, right_type):
  472. methods = [_bin_op(left, binary_opnode, op, right, context)]
  473. elif helpers.is_subtype(left_type, right_type):
  474. methods = [_bin_op(left, binary_opnode, op, right, context)]
  475. elif helpers.is_supertype(left_type, right_type):
  476. methods = [_bin_op(right, binary_opnode, op, left, reverse_context, reverse=True),
  477. _bin_op(left, binary_opnode, op, right, context)]
  478. else:
  479. methods = [_bin_op(left, binary_opnode, op, right, context),
  480. _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True)]
  481. return methods
  482. def _get_aug_flow(left, left_type, aug_opnode, right, right_type,
  483. context, reverse_context):
  484. """Get the flow for augmented binary operations.
  485. The rules are a bit messy:
  486. * if left and right have the same type, then left.__augop__(right)
  487. is first tried and then left.__op__(right).
  488. * if left and right are unrelated typewise, then
  489. left.__augop__(right) is tried, then left.__op__(right)
  490. is tried and then right.__rop__(left) is tried.
  491. * if left is a subtype of right, then left.__augop__(right)
  492. is tried and then left.__op__(right).
  493. * if left is a supertype of right, then left.__augop__(right)
  494. is tried, then right.__rop__(left) and then
  495. left.__op__(right)
  496. """
  497. bin_op = aug_opnode.op.strip("=")
  498. aug_op = aug_opnode.op
  499. if _same_type(left_type, right_type):
  500. methods = [_aug_op(left, aug_opnode, aug_op, right, context),
  501. _bin_op(left, aug_opnode, bin_op, right, context)]
  502. elif helpers.is_subtype(left_type, right_type):
  503. methods = [_aug_op(left, aug_opnode, aug_op, right, context),
  504. _bin_op(left, aug_opnode, bin_op, right, context)]
  505. elif helpers.is_supertype(left_type, right_type):
  506. methods = [_aug_op(left, aug_opnode, aug_op, right, context),
  507. _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True),
  508. _bin_op(left, aug_opnode, bin_op, right, context)]
  509. else:
  510. methods = [_aug_op(left, aug_opnode, aug_op, right, context),
  511. _bin_op(left, aug_opnode, bin_op, right, context),
  512. _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True)]
  513. return methods
  514. def _infer_binary_operation(left, right, binary_opnode, context, flow_factory):
  515. """Infer a binary operation between a left operand and a right operand
  516. This is used by both normal binary operations and augmented binary
  517. operations, the only difference is the flow factory used.
  518. """
  519. context, reverse_context = _get_binop_contexts(context, left, right)
  520. left_type = helpers.object_type(left)
  521. right_type = helpers.object_type(right)
  522. methods = flow_factory(left, left_type, binary_opnode, right, right_type,
  523. context, reverse_context)
  524. for method in methods:
  525. try:
  526. results = list(method())
  527. except AttributeError:
  528. continue
  529. except exceptions.AttributeInferenceError:
  530. continue
  531. except exceptions.InferenceError:
  532. yield util.Uninferable
  533. return
  534. else:
  535. if any(result is util.Uninferable for result in results):
  536. yield util.Uninferable
  537. return
  538. # TODO(cpopa): since the inference engine might return
  539. # more values than are actually possible, we decide
  540. # to return util.Uninferable if we have union types.
  541. if all(map(_is_not_implemented, results)):
  542. continue
  543. not_implemented = sum(1 for result in results
  544. if _is_not_implemented(result))
  545. if not_implemented and not_implemented != len(results):
  546. # Can't decide yet what this is, not yet though.
  547. yield util.Uninferable
  548. return
  549. for result in results:
  550. yield result
  551. return
  552. # TODO(cpopa): yield a BadBinaryOperationMessage here,
  553. # since the operation is not supported
  554. yield util.BadBinaryOperationMessage(left_type, binary_opnode.op, right_type)
  555. def _infer_binop(self, context):
  556. """Binary operation inferrence logic."""
  557. if context is None:
  558. context = contextmod.InferenceContext()
  559. left = self.left
  560. right = self.right
  561. # we use two separate contexts for evaluating lhs and rhs because
  562. # 1. evaluating lhs may leave some undesired entries in context.path
  563. # which may not let us infer right value of rhs
  564. lhs_context = context.clone()
  565. rhs_context = context.clone()
  566. for lhs in left.infer(context=lhs_context):
  567. if lhs is util.Uninferable:
  568. # Don't know how to process this.
  569. yield util.Uninferable
  570. return
  571. for rhs in right.infer(context=rhs_context):
  572. if rhs is util.Uninferable:
  573. # Don't know how to process this.
  574. yield util.Uninferable
  575. return
  576. try:
  577. for result in _infer_binary_operation(lhs, rhs, self,
  578. context, _get_binop_flow):
  579. yield result
  580. except exceptions._NonDeducibleTypeHierarchy:
  581. yield util.Uninferable
  582. @decorators.yes_if_nothing_inferred
  583. @decorators.path_wrapper
  584. def infer_binop(self, context=None):
  585. return _filter_operation_errors(self, _infer_binop, context,
  586. util.BadBinaryOperationMessage)
  587. nodes.BinOp._infer_binop = _infer_binop
  588. nodes.BinOp._infer = infer_binop
  589. def _infer_augassign(self, context=None):
  590. """Inference logic for augmented binary operations."""
  591. if context is None:
  592. context = contextmod.InferenceContext()
  593. for lhs in self.target.infer_lhs(context=context):
  594. if lhs is util.Uninferable:
  595. # Don't know how to process this.
  596. yield util.Uninferable
  597. return
  598. rhs_context = context.clone()
  599. for rhs in self.value.infer(context=rhs_context):
  600. if rhs is util.Uninferable:
  601. # Don't know how to process this.
  602. yield util.Uninferable
  603. return
  604. try:
  605. for result in _infer_binary_operation(lhs, rhs, self, context, _get_aug_flow):
  606. yield result
  607. except exceptions._NonDeducibleTypeHierarchy:
  608. yield util.Uninferable
  609. @decorators.path_wrapper
  610. def infer_augassign(self, context=None):
  611. return _filter_operation_errors(self, _infer_augassign, context,
  612. util.BadBinaryOperationMessage)
  613. nodes.AugAssign._infer_augassign = _infer_augassign
  614. nodes.AugAssign._infer = infer_augassign
  615. # End of binary operation inference.
  616. def infer_arguments(self, context=None):
  617. name = context.lookupname
  618. if name is None:
  619. raise exceptions.InferenceError(node=self, context=context)
  620. return protocols._arguments_infer_argname(self, name, context)
  621. nodes.Arguments._infer = infer_arguments
  622. @decorators.path_wrapper
  623. def infer_assign(self, context=None):
  624. """infer a AssignName/AssignAttr: need to inspect the RHS part of the
  625. assign node
  626. """
  627. stmt = self.statement()
  628. if isinstance(stmt, nodes.AugAssign):
  629. return stmt.infer(context)
  630. stmts = list(self.assigned_stmts(context=context))
  631. return bases._infer_stmts(stmts, context)
  632. nodes.AssignName._infer = infer_assign
  633. nodes.AssignAttr._infer = infer_assign
  634. # no infer method on DelName and DelAttr (expected InferenceError)
  635. @decorators.path_wrapper
  636. def infer_empty_node(self, context=None):
  637. if not self.has_underlying_object():
  638. yield util.Uninferable
  639. else:
  640. try:
  641. for inferred in MANAGER.infer_ast_from_something(self.object,
  642. context=context):
  643. yield inferred
  644. except exceptions.AstroidError:
  645. yield util.Uninferable
  646. nodes.EmptyNode._infer = infer_empty_node
  647. def infer_index(self, context=None):
  648. return self.value.infer(context)
  649. nodes.Index._infer = infer_index
  650. # TODO: move directly into bases.Instance when the dependency hell
  651. # will be solved.
  652. def instance_getitem(self, index, context=None):
  653. # Rewrap index to Const for this case
  654. if context:
  655. new_context = context.clone()
  656. else:
  657. context = new_context = contextmod.InferenceContext()
  658. # Create a new callcontext for providing index as an argument.
  659. new_context.callcontext = contextmod.CallContext(args=[index])
  660. new_context.boundnode = self
  661. method = next(self.igetattr('__getitem__', context=context))
  662. if not isinstance(method, bases.BoundMethod):
  663. raise exceptions.InferenceError(
  664. 'Could not find __getitem__ for {node!r}.',
  665. node=self, context=context)
  666. try:
  667. return next(method.infer_call_result(self, new_context))
  668. except StopIteration:
  669. util.reraise(exceptions.InferenceError(
  670. message='Inference for {node!r}[{index!s}] failed.',
  671. node=self, index=index, context=context))
  672. bases.Instance.getitem = instance_getitem