PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/logilab/astng/inference.py

https://gitlab.com/holtscomm/gae-purchase-order-system
Python | 382 lines | 321 code | 12 blank | 49 comment | 34 complexity | 4754814394e22e695839d521670b970b MD5 | raw file
  1. # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
  2. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
  3. # copyright 2003-2010 Sylvain Thenault, all rights reserved.
  4. # contact mailto:thenault@gmail.com
  5. #
  6. # This file is part of logilab-astng.
  7. #
  8. # logilab-astng is free software: you can redistribute it and/or modify it
  9. # under the terms of the GNU Lesser General Public License as published by the
  10. # Free Software Foundation, either version 2.1 of the License, or (at your
  11. # option) any later version.
  12. #
  13. # logilab-astng is distributed in the hope that it will be useful, but
  14. # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15. # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  16. # for more details.
  17. #
  18. # You should have received a copy of the GNU Lesser General Public License along
  19. # with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
  20. """this module contains a set of functions to handle inference on astng trees
  21. """
  22. __doctype__ = "restructuredtext en"
  23. from itertools import chain
  24. import sys
  25. from logilab.astng import nodes
  26. from logilab.astng.manager import ASTNGManager
  27. from logilab.astng.exceptions import (ASTNGBuildingException, ASTNGError,
  28. InferenceError, NoDefault, NotFoundError, UnresolvableName)
  29. from logilab.astng.bases import YES, Instance, InferenceContext, Generator, \
  30. _infer_stmts, copy_context, path_wrapper, raise_if_nothing_infered
  31. from logilab.astng.protocols import _arguments_infer_argname
  32. MANAGER = ASTNGManager()
  33. class CallContext:
  34. """when inferring a function call, this class is used to remember values
  35. given as argument
  36. """
  37. def __init__(self, args, starargs, dstarargs):
  38. self.args = []
  39. self.nargs = {}
  40. for arg in args:
  41. if isinstance(arg, nodes.Keyword):
  42. self.nargs[arg.arg] = arg.value
  43. else:
  44. self.args.append(arg)
  45. self.starargs = starargs
  46. self.dstarargs = dstarargs
  47. def infer_argument(self, funcnode, name, context):
  48. """infer a function argument value according to the call context"""
  49. # 1. search in named keywords
  50. try:
  51. return self.nargs[name].infer(context)
  52. except KeyError:
  53. # Function.args.args can be None in astng (means that we don't have
  54. # information on argnames)
  55. argindex = funcnode.args.find_argname(name)[0]
  56. if argindex is not None:
  57. # 2. first argument of instance/class method
  58. if argindex == 0 and funcnode.type in ('method', 'classmethod'):
  59. if context.boundnode is not None:
  60. boundnode = context.boundnode
  61. else:
  62. # XXX can do better ?
  63. boundnode = funcnode.parent.frame()
  64. if funcnode.type == 'method':
  65. if not isinstance(boundnode, Instance):
  66. boundnode = Instance(boundnode)
  67. return iter((boundnode,))
  68. if funcnode.type == 'classmethod':
  69. return iter((boundnode,))
  70. # 2. search arg index
  71. try:
  72. return self.args[argindex].infer(context)
  73. except IndexError:
  74. pass
  75. # 3. search in *args (.starargs)
  76. if self.starargs is not None:
  77. its = []
  78. for infered in self.starargs.infer(context):
  79. if infered is YES:
  80. its.append((YES,))
  81. continue
  82. try:
  83. its.append(infered.getitem(argindex, context).infer(context))
  84. except (InferenceError, AttributeError):
  85. its.append((YES,))
  86. except (IndexError, TypeError):
  87. continue
  88. if its:
  89. return chain(*its)
  90. # 4. XXX search in **kwargs (.dstarargs)
  91. if self.dstarargs is not None:
  92. its = []
  93. for infered in self.dstarargs.infer(context):
  94. if infered is YES:
  95. its.append((YES,))
  96. continue
  97. try:
  98. its.append(infered.getitem(name, context).infer(context))
  99. except (InferenceError, AttributeError):
  100. its.append((YES,))
  101. except (IndexError, TypeError):
  102. continue
  103. if its:
  104. return chain(*its)
  105. # 5. */** argument, (Tuple or Dict)
  106. if name == funcnode.args.vararg:
  107. return iter((nodes.const_factory(())))
  108. if name == funcnode.args.kwarg:
  109. return iter((nodes.const_factory({})))
  110. # 6. return default value if any
  111. try:
  112. return funcnode.args.default_value(name).infer(context)
  113. except NoDefault:
  114. raise InferenceError(name)
  115. # .infer method ###############################################################
  116. def infer_end(self, context=None):
  117. """inference's end for node such as Module, Class, Function, Const...
  118. """
  119. yield self
  120. nodes.Module.infer = infer_end
  121. nodes.Class.infer = infer_end
  122. nodes.Function.infer = infer_end
  123. nodes.Lambda.infer = infer_end
  124. nodes.Const.infer = infer_end
  125. nodes.List.infer = infer_end
  126. nodes.Tuple.infer = infer_end
  127. nodes.Dict.infer = infer_end
  128. def infer_name(self, context=None):
  129. """infer a Name: use name lookup rules"""
  130. frame, stmts = self.lookup(self.name)
  131. if not stmts:
  132. raise UnresolvableName(self.name)
  133. context = context.clone()
  134. context.lookupname = self.name
  135. return _infer_stmts(stmts, context, frame)
  136. nodes.Name.infer = path_wrapper(infer_name)
  137. nodes.AssName.infer_lhs = infer_name # won't work with a path wrapper
  138. def infer_callfunc(self, context=None):
  139. """infer a CallFunc node by trying to guess what the function returns"""
  140. callcontext = context.clone()
  141. callcontext.callcontext = CallContext(self.args, self.starargs, self.kwargs)
  142. callcontext.boundnode = None
  143. for callee in self.func.infer(context):
  144. if callee is YES:
  145. yield callee
  146. continue
  147. try:
  148. if hasattr(callee, 'infer_call_result'):
  149. for infered in callee.infer_call_result(self, callcontext):
  150. yield infered
  151. except InferenceError:
  152. ## XXX log error ?
  153. continue
  154. nodes.CallFunc.infer = path_wrapper(raise_if_nothing_infered(infer_callfunc))
  155. def infer_import(self, context=None, asname=True):
  156. """infer an Import node: return the imported module/object"""
  157. name = context.lookupname
  158. if name is None:
  159. raise InferenceError()
  160. if asname:
  161. yield self.do_import_module(self.real_name(name))
  162. else:
  163. yield self.do_import_module(name)
  164. nodes.Import.infer = path_wrapper(infer_import)
  165. def infer_name_module(self, name):
  166. context = InferenceContext()
  167. context.lookupname = name
  168. return self.infer(context, asname=False)
  169. nodes.Import.infer_name_module = infer_name_module
  170. def infer_from(self, context=None, asname=True):
  171. """infer a From nodes: return the imported module/object"""
  172. name = context.lookupname
  173. if name is None:
  174. raise InferenceError()
  175. if asname:
  176. name = self.real_name(name)
  177. module = self.do_import_module(self.modname)
  178. try:
  179. context = copy_context(context)
  180. context.lookupname = name
  181. return _infer_stmts(module.getattr(name, ignore_locals=module is self.root()), context)
  182. except NotFoundError:
  183. raise InferenceError(name)
  184. nodes.From.infer = path_wrapper(infer_from)
  185. def infer_getattr(self, context=None):
  186. """infer a Getattr node by using getattr on the associated object"""
  187. #context = context.clone()
  188. for owner in self.expr.infer(context):
  189. if owner is YES:
  190. yield owner
  191. continue
  192. try:
  193. context.boundnode = owner
  194. for obj in owner.igetattr(self.attrname, context):
  195. yield obj
  196. context.boundnode = None
  197. except (NotFoundError, InferenceError):
  198. context.boundnode = None
  199. except AttributeError:
  200. # XXX method / function
  201. context.boundnode = None
  202. nodes.Getattr.infer = path_wrapper(raise_if_nothing_infered(infer_getattr))
  203. nodes.AssAttr.infer_lhs = raise_if_nothing_infered(infer_getattr) # # won't work with a path wrapper
  204. def infer_global(self, context=None):
  205. if context.lookupname is None:
  206. raise InferenceError()
  207. try:
  208. return _infer_stmts(self.root().getattr(context.lookupname), context)
  209. except NotFoundError:
  210. raise InferenceError()
  211. nodes.Global.infer = path_wrapper(infer_global)
  212. def infer_subscript(self, context=None):
  213. """infer simple subscription such as [1,2,3][0] or (1,2,3)[-1]"""
  214. if isinstance(self.slice, nodes.Index):
  215. index = self.slice.value.infer(context).next()
  216. if index is YES:
  217. yield YES
  218. return
  219. try:
  220. # suppose it's a Tuple/List node (attribute error else)
  221. assigned = self.value.getitem(index.value, context)
  222. except AttributeError:
  223. raise InferenceError()
  224. except (IndexError, TypeError):
  225. yield YES
  226. return
  227. for infered in assigned.infer(context):
  228. yield infered
  229. else:
  230. raise InferenceError()
  231. nodes.Subscript.infer = path_wrapper(infer_subscript)
  232. nodes.Subscript.infer_lhs = raise_if_nothing_infered(infer_subscript)
  233. UNARY_OP_METHOD = {'+': '__pos__',
  234. '-': '__neg__',
  235. '~': '__invert__',
  236. 'not': None, # XXX not '__nonzero__'
  237. }
  238. def infer_unaryop(self, context=None):
  239. for operand in self.operand.infer(context):
  240. try:
  241. yield operand.infer_unary_op(self.op)
  242. except TypeError:
  243. continue
  244. except AttributeError:
  245. meth = UNARY_OP_METHOD[self.op]
  246. if meth is None:
  247. yield YES
  248. else:
  249. try:
  250. # XXX just suppose if the type implement meth, returned type
  251. # will be the same
  252. operand.getattr(meth)
  253. yield operand
  254. except GeneratorExit:
  255. raise
  256. except:
  257. yield YES
  258. nodes.UnaryOp.infer = path_wrapper(infer_unaryop)
  259. BIN_OP_METHOD = {'+': '__add__',
  260. '-': '__sub__',
  261. '/': '__div__',
  262. '//': '__floordiv__',
  263. '*': '__mul__',
  264. '**': '__power__',
  265. '%': '__mod__',
  266. '&': '__and__',
  267. '|': '__or__',
  268. '^': '__xor__',
  269. '<<': '__lshift__',
  270. '>>': '__rshift__',
  271. }
  272. def _infer_binop(operator, operand1, operand2, context, failures=None):
  273. if operand1 is YES:
  274. yield operand1
  275. return
  276. try:
  277. for valnode in operand1.infer_binary_op(operator, operand2, context):
  278. yield valnode
  279. except AttributeError:
  280. try:
  281. # XXX just suppose if the type implement meth, returned type
  282. # will be the same
  283. operand1.getattr(BIN_OP_METHOD[operator])
  284. yield operand1
  285. except:
  286. if failures is None:
  287. yield YES
  288. else:
  289. failures.append(operand1)
  290. def infer_binop(self, context=None):
  291. failures = []
  292. for lhs in self.left.infer(context):
  293. for val in _infer_binop(self.op, lhs, self.right, context, failures):
  294. yield val
  295. for lhs in failures:
  296. for rhs in self.right.infer(context):
  297. for val in _infer_binop(self.op, rhs, lhs, context):
  298. yield val
  299. nodes.BinOp.infer = path_wrapper(infer_binop)
  300. def infer_arguments(self, context=None):
  301. name = context.lookupname
  302. if name is None:
  303. raise InferenceError()
  304. return _arguments_infer_argname(self, name, context)
  305. nodes.Arguments.infer = infer_arguments
  306. def infer_ass(self, context=None):
  307. """infer a AssName/AssAttr: need to inspect the RHS part of the
  308. assign node
  309. """
  310. stmt = self.statement()
  311. if isinstance(stmt, nodes.AugAssign):
  312. return stmt.infer(context)
  313. stmts = list(self.assigned_stmts(context=context))
  314. return _infer_stmts(stmts, context)
  315. nodes.AssName.infer = path_wrapper(infer_ass)
  316. nodes.AssAttr.infer = path_wrapper(infer_ass)
  317. def infer_augassign(self, context=None):
  318. failures = []
  319. for lhs in self.target.infer_lhs(context):
  320. for val in _infer_binop(self.op, lhs, self.value, context, failures):
  321. yield val
  322. for lhs in failures:
  323. for rhs in self.value.infer(context):
  324. for val in _infer_binop(self.op, rhs, lhs, context):
  325. yield val
  326. nodes.AugAssign.infer = path_wrapper(infer_augassign)
  327. # no infer method on DelName and DelAttr (expected InferenceError)
  328. def infer_empty_node(self, context=None):
  329. if not self.has_underlying_object():
  330. yield YES
  331. else:
  332. try:
  333. for infered in MANAGER.infer_astng_from_something(self.object,
  334. context=context):
  335. yield infered
  336. except ASTNGError:
  337. yield YES
  338. nodes.EmptyNode.infer = path_wrapper(infer_empty_node)