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.

protocols.py 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. # Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
  2. # Copyright (c) 2014-2016 Claudiu Popa <pcmanticore@gmail.com>
  3. # Copyright (c) 2014 Google, Inc.
  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 python protocols for nodes
  8. where it makes sense.
  9. """
  10. import collections
  11. import operator as operator_mod
  12. import sys
  13. import six
  14. from astroid import arguments
  15. from astroid import bases
  16. from astroid import context as contextmod
  17. from astroid import exceptions
  18. from astroid import decorators
  19. from astroid import node_classes
  20. from astroid import helpers
  21. from astroid import nodes
  22. from astroid import util
  23. raw_building = util.lazy_import('raw_building')
  24. objects = util.lazy_import('objects')
  25. def _reflected_name(name):
  26. return "__r" + name[2:]
  27. def _augmented_name(name):
  28. return "__i" + name[2:]
  29. _CONTEXTLIB_MGR = 'contextlib.contextmanager'
  30. BIN_OP_METHOD = {'+': '__add__',
  31. '-': '__sub__',
  32. '/': '__div__' if six.PY2 else '__truediv__',
  33. '//': '__floordiv__',
  34. '*': '__mul__',
  35. '**': '__pow__',
  36. '%': '__mod__',
  37. '&': '__and__',
  38. '|': '__or__',
  39. '^': '__xor__',
  40. '<<': '__lshift__',
  41. '>>': '__rshift__',
  42. '@': '__matmul__'
  43. }
  44. REFLECTED_BIN_OP_METHOD = {
  45. key: _reflected_name(value)
  46. for (key, value) in BIN_OP_METHOD.items()
  47. }
  48. AUGMENTED_OP_METHOD = {
  49. key + "=": _augmented_name(value)
  50. for (key, value) in BIN_OP_METHOD.items()
  51. }
  52. UNARY_OP_METHOD = {'+': '__pos__',
  53. '-': '__neg__',
  54. '~': '__invert__',
  55. 'not': None, # XXX not '__nonzero__'
  56. }
  57. _UNARY_OPERATORS = {
  58. '+': operator_mod.pos,
  59. '-': operator_mod.neg,
  60. '~': operator_mod.invert,
  61. 'not': operator_mod.not_,
  62. }
  63. def _infer_unary_op(obj, op):
  64. func = _UNARY_OPERATORS[op]
  65. value = func(obj)
  66. return nodes.const_factory(value)
  67. nodes.Tuple.infer_unary_op = lambda self, op: _infer_unary_op(tuple(self.elts), op)
  68. nodes.List.infer_unary_op = lambda self, op: _infer_unary_op(self.elts, op)
  69. nodes.Set.infer_unary_op = lambda self, op: _infer_unary_op(set(self.elts), op)
  70. nodes.Const.infer_unary_op = lambda self, op: _infer_unary_op(self.value, op)
  71. nodes.Dict.infer_unary_op = lambda self, op: _infer_unary_op(dict(self.items), op)
  72. # Binary operations
  73. BIN_OP_IMPL = {'+': lambda a, b: a + b,
  74. '-': lambda a, b: a - b,
  75. '/': lambda a, b: a / b,
  76. '//': lambda a, b: a // b,
  77. '*': lambda a, b: a * b,
  78. '**': lambda a, b: a ** b,
  79. '%': lambda a, b: a % b,
  80. '&': lambda a, b: a & b,
  81. '|': lambda a, b: a | b,
  82. '^': lambda a, b: a ^ b,
  83. '<<': lambda a, b: a << b,
  84. '>>': lambda a, b: a >> b,
  85. }
  86. if sys.version_info >= (3, 5):
  87. # MatMult is available since Python 3.5+.
  88. BIN_OP_IMPL['@'] = operator_mod.matmul
  89. for _KEY, _IMPL in list(BIN_OP_IMPL.items()):
  90. BIN_OP_IMPL[_KEY + '='] = _IMPL
  91. @decorators.yes_if_nothing_inferred
  92. def const_infer_binary_op(self, opnode, operator, other, context, _):
  93. not_implemented = nodes.Const(NotImplemented)
  94. if isinstance(other, nodes.Const):
  95. try:
  96. impl = BIN_OP_IMPL[operator]
  97. try:
  98. yield nodes.const_factory(impl(self.value, other.value))
  99. except TypeError:
  100. # ArithmeticError is not enough: float >> float is a TypeError
  101. yield not_implemented
  102. except Exception: # pylint: disable=broad-except
  103. yield util.Uninferable
  104. except TypeError:
  105. yield not_implemented
  106. elif isinstance(self.value, six.string_types) and operator == '%':
  107. # TODO(cpopa): implement string interpolation later on.
  108. yield util.Uninferable
  109. else:
  110. yield not_implemented
  111. nodes.Const.infer_binary_op = const_infer_binary_op
  112. def _multiply_seq_by_int(self, opnode, other, context):
  113. node = self.__class__(parent=opnode)
  114. elts = []
  115. filtered_elts = (elt for elt in self.elts if elt is not util.Uninferable)
  116. for elt in filtered_elts:
  117. infered = helpers.safe_infer(elt, context)
  118. if infered is None:
  119. infered = util.Uninferable
  120. elts.append(infered)
  121. node.elts = elts * other.value
  122. return node
  123. def _filter_uninferable_nodes(elts, context):
  124. for elt in elts:
  125. if elt is util.Uninferable:
  126. yield nodes.Unknown()
  127. else:
  128. for inferred in elt.infer(context):
  129. if inferred is not util.Uninferable:
  130. yield inferred
  131. else:
  132. yield nodes.Unknown()
  133. @decorators.yes_if_nothing_inferred
  134. def tl_infer_binary_op(self, opnode, operator, other, context, method):
  135. not_implemented = nodes.Const(NotImplemented)
  136. if isinstance(other, self.__class__) and operator == '+':
  137. node = self.__class__(parent=opnode)
  138. elts = list(_filter_uninferable_nodes(self.elts, context))
  139. elts += list(_filter_uninferable_nodes(other.elts, context))
  140. node.elts = elts
  141. yield node
  142. elif isinstance(other, nodes.Const) and operator == '*':
  143. if not isinstance(other.value, int):
  144. yield not_implemented
  145. return
  146. yield _multiply_seq_by_int(self, opnode, other, context)
  147. elif isinstance(other, bases.Instance) and operator == '*':
  148. # Verify if the instance supports __index__.
  149. as_index = helpers.class_instance_as_index(other)
  150. if not as_index:
  151. yield util.Uninferable
  152. else:
  153. yield _multiply_seq_by_int(self, opnode, as_index, context)
  154. else:
  155. yield not_implemented
  156. nodes.Tuple.infer_binary_op = tl_infer_binary_op
  157. nodes.List.infer_binary_op = tl_infer_binary_op
  158. @decorators.yes_if_nothing_inferred
  159. def instance_class_infer_binary_op(self, opnode, operator, other, context, method):
  160. return method.infer_call_result(self, context)
  161. bases.Instance.infer_binary_op = instance_class_infer_binary_op
  162. nodes.ClassDef.infer_binary_op = instance_class_infer_binary_op
  163. # assignment ##################################################################
  164. """the assigned_stmts method is responsible to return the assigned statement
  165. (e.g. not inferred) according to the assignment type.
  166. The `asspath` argument is used to record the lhs path of the original node.
  167. For instance if we want assigned statements for 'c' in 'a, (b,c)', asspath
  168. will be [1, 1] once arrived to the Assign node.
  169. The `context` argument is the current inference context which should be given
  170. to any intermediary inference necessary.
  171. """
  172. def _resolve_looppart(parts, asspath, context):
  173. """recursive function to resolve multiple assignments on loops"""
  174. asspath = asspath[:]
  175. index = asspath.pop(0)
  176. for part in parts:
  177. if part is util.Uninferable:
  178. continue
  179. # XXX handle __iter__ and log potentially detected errors
  180. if not hasattr(part, 'itered'):
  181. continue
  182. try:
  183. itered = part.itered()
  184. except TypeError:
  185. continue # XXX log error
  186. for stmt in itered:
  187. index_node = nodes.Const(index)
  188. try:
  189. assigned = stmt.getitem(index_node, context)
  190. except (AttributeError,
  191. exceptions.AstroidTypeError,
  192. exceptions.AstroidIndexError):
  193. continue
  194. if not asspath:
  195. # we achieved to resolved the assignment path,
  196. # don't infer the last part
  197. yield assigned
  198. elif assigned is util.Uninferable:
  199. break
  200. else:
  201. # we are not yet on the last part of the path
  202. # search on each possibly inferred value
  203. try:
  204. for inferred in _resolve_looppart(assigned.infer(context),
  205. asspath, context):
  206. yield inferred
  207. except exceptions.InferenceError:
  208. break
  209. @decorators.raise_if_nothing_inferred
  210. def for_assigned_stmts(self, node=None, context=None, asspath=None):
  211. if isinstance(self, nodes.AsyncFor) or getattr(self, 'is_async', False):
  212. # Skip inferring of async code for now
  213. raise StopIteration(dict(node=self, unknown=node,
  214. assign_path=asspath, context=context))
  215. if asspath is None:
  216. for lst in self.iter.infer(context):
  217. if isinstance(lst, (nodes.Tuple, nodes.List)):
  218. for item in lst.elts:
  219. yield item
  220. else:
  221. for inferred in _resolve_looppart(self.iter.infer(context),
  222. asspath, context):
  223. yield inferred
  224. # Explicit StopIteration to return error information, see comment
  225. # in raise_if_nothing_inferred.
  226. raise StopIteration(dict(node=self, unknown=node,
  227. assign_path=asspath, context=context))
  228. nodes.For.assigned_stmts = for_assigned_stmts
  229. nodes.Comprehension.assigned_stmts = for_assigned_stmts
  230. def sequence_assigned_stmts(self, node=None, context=None, asspath=None):
  231. if asspath is None:
  232. asspath = []
  233. try:
  234. index = self.elts.index(node)
  235. except ValueError:
  236. util.reraise(exceptions.InferenceError(
  237. 'Tried to retrieve a node {node!r} which does not exist',
  238. node=self, assign_path=asspath, context=context))
  239. asspath.insert(0, index)
  240. return self.parent.assigned_stmts(node=self, context=context, asspath=asspath)
  241. nodes.Tuple.assigned_stmts = sequence_assigned_stmts
  242. nodes.List.assigned_stmts = sequence_assigned_stmts
  243. def assend_assigned_stmts(self, node=None, context=None, asspath=None):
  244. return self.parent.assigned_stmts(node=self, context=context)
  245. nodes.AssignName.assigned_stmts = assend_assigned_stmts
  246. nodes.AssignAttr.assigned_stmts = assend_assigned_stmts
  247. def _arguments_infer_argname(self, name, context):
  248. # arguments information may be missing, in which case we can't do anything
  249. # more
  250. if not (self.args or self.vararg or self.kwarg):
  251. yield util.Uninferable
  252. return
  253. # first argument of instance/class method
  254. if self.args and getattr(self.args[0], 'name', None) == name:
  255. functype = self.parent.type
  256. cls = self.parent.parent.scope()
  257. is_metaclass = isinstance(cls, nodes.ClassDef) and cls.type == 'metaclass'
  258. # If this is a metaclass, then the first argument will always
  259. # be the class, not an instance.
  260. if is_metaclass or functype == 'classmethod':
  261. yield cls
  262. return
  263. if functype == 'method':
  264. yield bases.Instance(self.parent.parent.frame())
  265. return
  266. if context and context.callcontext:
  267. call_site = arguments.CallSite(context.callcontext)
  268. for value in call_site.infer_argument(self.parent, name, context):
  269. yield value
  270. return
  271. # TODO: just provide the type here, no need to have an empty Dict.
  272. if name == self.vararg:
  273. vararg = nodes.const_factory(())
  274. vararg.parent = self
  275. yield vararg
  276. return
  277. if name == self.kwarg:
  278. kwarg = nodes.const_factory({})
  279. kwarg.parent = self
  280. yield kwarg
  281. return
  282. # if there is a default value, yield it. And then yield Uninferable to reflect
  283. # we can't guess given argument value
  284. try:
  285. context = contextmod.copy_context(context)
  286. for inferred in self.default_value(name).infer(context):
  287. yield inferred
  288. yield util.Uninferable
  289. except exceptions.NoDefault:
  290. yield util.Uninferable
  291. def arguments_assigned_stmts(self, node=None, context=None, asspath=None):
  292. if context.callcontext:
  293. # reset call context/name
  294. callcontext = context.callcontext
  295. context = contextmod.copy_context(context)
  296. context.callcontext = None
  297. args = arguments.CallSite(callcontext)
  298. return args.infer_argument(self.parent, node.name, context)
  299. return _arguments_infer_argname(self, node.name, context)
  300. nodes.Arguments.assigned_stmts = arguments_assigned_stmts
  301. @decorators.raise_if_nothing_inferred
  302. def assign_assigned_stmts(self, node=None, context=None, asspath=None):
  303. if not asspath:
  304. yield self.value
  305. return
  306. for inferred in _resolve_asspart(self.value.infer(context), asspath, context):
  307. yield inferred
  308. # Explicit StopIteration to return error information, see comment
  309. # in raise_if_nothing_inferred.
  310. raise StopIteration(dict(node=self, unknown=node,
  311. assign_path=asspath, context=context))
  312. def assign_annassigned_stmts(self, node=None, context=None, asspath=None):
  313. for inferred in assign_assigned_stmts(self, node, context, asspath):
  314. if inferred is None:
  315. yield util.Uninferable
  316. else:
  317. yield inferred
  318. nodes.Assign.assigned_stmts = assign_assigned_stmts
  319. nodes.AnnAssign.assigned_stmts = assign_annassigned_stmts
  320. nodes.AugAssign.assigned_stmts = assign_assigned_stmts
  321. def _resolve_asspart(parts, asspath, context):
  322. """recursive function to resolve multiple assignments"""
  323. asspath = asspath[:]
  324. index = asspath.pop(0)
  325. for part in parts:
  326. if hasattr(part, 'getitem'):
  327. index_node = nodes.Const(index)
  328. try:
  329. assigned = part.getitem(index_node, context)
  330. # XXX raise a specific exception to avoid potential hiding of
  331. # unexpected exception ?
  332. except (exceptions.AstroidTypeError, exceptions.AstroidIndexError):
  333. return
  334. if not asspath:
  335. # we achieved to resolved the assignment path, don't infer the
  336. # last part
  337. yield assigned
  338. elif assigned is util.Uninferable:
  339. return
  340. else:
  341. # we are not yet on the last part of the path search on each
  342. # possibly inferred value
  343. try:
  344. for inferred in _resolve_asspart(assigned.infer(context),
  345. asspath, context):
  346. yield inferred
  347. except exceptions.InferenceError:
  348. return
  349. @decorators.raise_if_nothing_inferred
  350. def excepthandler_assigned_stmts(self, node=None, context=None, asspath=None):
  351. for assigned in node_classes.unpack_infer(self.type):
  352. if isinstance(assigned, nodes.ClassDef):
  353. assigned = objects.ExceptionInstance(assigned)
  354. yield assigned
  355. # Explicit StopIteration to return error information, see comment
  356. # in raise_if_nothing_inferred.
  357. raise StopIteration(dict(node=self, unknown=node,
  358. assign_path=asspath, context=context))
  359. nodes.ExceptHandler.assigned_stmts = excepthandler_assigned_stmts
  360. def _infer_context_manager(self, mgr, context):
  361. try:
  362. inferred = next(mgr.infer(context=context))
  363. except exceptions.InferenceError:
  364. return
  365. if isinstance(inferred, bases.Generator):
  366. # Check if it is decorated with contextlib.contextmanager.
  367. func = inferred.parent
  368. if not func.decorators:
  369. return
  370. for decorator_node in func.decorators.nodes:
  371. decorator = next(decorator_node.infer(context))
  372. if isinstance(decorator, nodes.FunctionDef):
  373. if decorator.qname() == _CONTEXTLIB_MGR:
  374. break
  375. else:
  376. # It doesn't interest us.
  377. return
  378. # Get the first yield point. If it has multiple yields,
  379. # then a RuntimeError will be raised.
  380. # TODO(cpopa): Handle flows.
  381. possible_yield_points = func.nodes_of_class(nodes.Yield)
  382. # Ignore yields in nested functions
  383. yield_point = next((node for node in possible_yield_points
  384. if node.scope() == func), None)
  385. if yield_point:
  386. if not yield_point.value:
  387. # TODO(cpopa): an empty yield. Should be wrapped to Const.
  388. const = nodes.Const(None)
  389. const.parent = yield_point
  390. const.lineno = yield_point.lineno
  391. yield const
  392. else:
  393. for inferred in yield_point.value.infer(context=context):
  394. yield inferred
  395. elif isinstance(inferred, bases.Instance):
  396. try:
  397. enter = next(inferred.igetattr('__enter__', context=context))
  398. except (exceptions.InferenceError, exceptions.AttributeInferenceError):
  399. return
  400. if not isinstance(enter, bases.BoundMethod):
  401. return
  402. if not context.callcontext:
  403. context.callcontext = contextmod.CallContext(args=[inferred])
  404. for result in enter.infer_call_result(self, context):
  405. yield result
  406. @decorators.raise_if_nothing_inferred
  407. def with_assigned_stmts(self, node=None, context=None, asspath=None):
  408. """Infer names and other nodes from a *with* statement.
  409. This enables only inference for name binding in a *with* statement.
  410. For instance, in the following code, inferring `func` will return
  411. the `ContextManager` class, not whatever ``__enter__`` returns.
  412. We are doing this intentionally, because we consider that the context
  413. manager result is whatever __enter__ returns and what it is binded
  414. using the ``as`` keyword.
  415. class ContextManager(object):
  416. def __enter__(self):
  417. return 42
  418. with ContextManager() as f:
  419. pass
  420. # ContextManager().infer() will return ContextManager
  421. # f.infer() will return 42.
  422. Arguments:
  423. self: nodes.With
  424. node: The target of the assignment, `as (a, b)` in `with foo as (a, b)`.
  425. context: TODO
  426. asspath: TODO
  427. """
  428. mgr = next(mgr for (mgr, vars) in self.items if vars == node)
  429. if asspath is None:
  430. for result in _infer_context_manager(self, mgr, context):
  431. yield result
  432. else:
  433. for result in _infer_context_manager(self, mgr, context):
  434. # Walk the asspath and get the item at the final index.
  435. obj = result
  436. for index in asspath:
  437. if not hasattr(obj, 'elts'):
  438. raise exceptions.InferenceError(
  439. 'Wrong type ({targets!r}) for {node!r} assignment',
  440. node=self, targets=node, assign_path=asspath,
  441. context=context)
  442. try:
  443. obj = obj.elts[index]
  444. except IndexError:
  445. util.reraise(exceptions.InferenceError(
  446. 'Tried to infer a nonexistent target with index {index} '
  447. 'in {node!r}.', node=self, targets=node,
  448. assign_path=asspath, context=context))
  449. except TypeError:
  450. util.reraise(exceptions.InferenceError(
  451. 'Tried to unpack an non-iterable value '
  452. 'in {node!r}.', node=self, targets=node,
  453. assign_path=asspath, context=context))
  454. yield obj
  455. # Explicit StopIteration to return error information, see comment
  456. # in raise_if_nothing_inferred.
  457. raise StopIteration(dict(node=self, unknown=node,
  458. assign_path=asspath, context=context))
  459. nodes.With.assigned_stmts = with_assigned_stmts
  460. @decorators.yes_if_nothing_inferred
  461. def starred_assigned_stmts(self, node=None, context=None, asspath=None):
  462. """
  463. Arguments:
  464. self: nodes.Starred
  465. node: TODO
  466. context: TODO
  467. asspath: TODO
  468. """
  469. stmt = self.statement()
  470. if not isinstance(stmt, (nodes.Assign, nodes.For)):
  471. raise exceptions.InferenceError('Statement {stmt!r} enclosing {node!r} '
  472. 'must be an Assign or For node.',
  473. node=self, stmt=stmt, unknown=node,
  474. context=context)
  475. if isinstance(stmt, nodes.Assign):
  476. value = stmt.value
  477. lhs = stmt.targets[0]
  478. if sum(1 for node in lhs.nodes_of_class(nodes.Starred)) > 1:
  479. raise exceptions.InferenceError('Too many starred arguments in the '
  480. ' assignment targets {lhs!r}.',
  481. node=self, targets=lhs,
  482. unknown=node, context=context)
  483. if context is None:
  484. context = contextmod.InferenceContext()
  485. try:
  486. rhs = next(value.infer(context))
  487. except exceptions.InferenceError:
  488. yield util.Uninferable
  489. return
  490. if rhs is util.Uninferable or not hasattr(rhs, 'elts'):
  491. # Not interested in inferred values without elts.
  492. yield util.Uninferable
  493. return
  494. elts = collections.deque(rhs.elts[:])
  495. if len(lhs.elts) > len(rhs.elts):
  496. raise exceptions.InferenceError('More targets, {targets!r}, than '
  497. 'values to unpack, {values!r}.',
  498. node=self, targets=lhs,
  499. values=rhs, unknown=node,
  500. context=context)
  501. # Unpack iteratively the values from the rhs of the assignment,
  502. # until the find the starred node. What will remain will
  503. # be the list of values which the Starred node will represent
  504. # This is done in two steps, from left to right to remove
  505. # anything before the starred node and from right to left
  506. # to remove anything after the starred node.
  507. for index, left_node in enumerate(lhs.elts):
  508. if not isinstance(left_node, nodes.Starred):
  509. elts.popleft()
  510. continue
  511. lhs_elts = collections.deque(reversed(lhs.elts[index:]))
  512. for right_node in lhs_elts:
  513. if not isinstance(right_node, nodes.Starred):
  514. elts.pop()
  515. continue
  516. # We're done
  517. packed = nodes.List()
  518. packed.elts = elts
  519. packed.parent = self
  520. yield packed
  521. break
  522. nodes.Starred.assigned_stmts = starred_assigned_stmts