bases.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. # -*- coding: utf-8 -*-
  2. # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
  3. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
  4. # copyright 2003-2010 Sylvain Thenault, all rights reserved.
  5. # contact mailto:thenault@gmail.com
  6. #
  7. # This file is part of logilab-astng.
  8. #
  9. # logilab-astng is free software: you can redistribute it and/or modify it
  10. # under the terms of the GNU Lesser General Public License as published by the
  11. # Free Software Foundation, either version 2.1 of the License, or (at your
  12. # option) any later version.
  13. #
  14. # logilab-astng is distributed in the hope that it will be useful, but
  15. # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  16. # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  17. # for more details.
  18. #
  19. # You should have received a copy of the GNU Lesser General Public License along
  20. # with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
  21. """This module contains base classes and functions for the nodes and some
  22. inference utils.
  23. """
  24. __docformat__ = "restructuredtext en"
  25. from contextlib import contextmanager
  26. from logilab.common.compat import builtins
  27. from logilab.astng import BUILTINS_MODULE
  28. from logilab.astng.exceptions import InferenceError, ASTNGError, \
  29. NotFoundError, UnresolvableName
  30. from logilab.astng.as_string import as_string
  31. BUILTINS_NAME = builtins.__name__
  32. class Proxy(object):
  33. """a simple proxy object"""
  34. _proxied = None
  35. def __init__(self, proxied=None):
  36. if proxied is not None:
  37. self._proxied = proxied
  38. def __getattr__(self, name):
  39. if name == '_proxied':
  40. return getattr(self.__class__, '_proxied')
  41. if name in self.__dict__:
  42. return self.__dict__[name]
  43. return getattr(self._proxied, name)
  44. def infer(self, context=None):
  45. yield self
  46. # Inference ##################################################################
  47. class InferenceContext(object):
  48. __slots__ = ('path', 'lookupname', 'callcontext', 'boundnode')
  49. def __init__(self, path=None):
  50. if path is None:
  51. self.path = set()
  52. else:
  53. self.path = path
  54. self.lookupname = None
  55. self.callcontext = None
  56. self.boundnode = None
  57. def push(self, node):
  58. name = self.lookupname
  59. if (node, name) in self.path:
  60. raise StopIteration()
  61. self.path.add( (node, name) )
  62. def clone(self):
  63. # XXX copy lookupname/callcontext ?
  64. clone = InferenceContext(self.path)
  65. clone.callcontext = self.callcontext
  66. clone.boundnode = self.boundnode
  67. return clone
  68. @contextmanager
  69. def restore_path(self):
  70. path = set(self.path)
  71. yield
  72. self.path = path
  73. def copy_context(context):
  74. if context is not None:
  75. return context.clone()
  76. else:
  77. return InferenceContext()
  78. def _infer_stmts(stmts, context, frame=None):
  79. """return an iterator on statements inferred by each statement in <stmts>
  80. """
  81. stmt = None
  82. infered = False
  83. if context is not None:
  84. name = context.lookupname
  85. context = context.clone()
  86. else:
  87. name = None
  88. context = InferenceContext()
  89. for stmt in stmts:
  90. if stmt is YES:
  91. yield stmt
  92. infered = True
  93. continue
  94. context.lookupname = stmt._infer_name(frame, name)
  95. try:
  96. for infered in stmt.infer(context):
  97. yield infered
  98. infered = True
  99. except UnresolvableName:
  100. continue
  101. except InferenceError:
  102. yield YES
  103. infered = True
  104. if not infered:
  105. raise InferenceError(str(stmt))
  106. # special inference objects (e.g. may be returned as nodes by .infer()) #######
  107. class _Yes(object):
  108. """a yes object"""
  109. def __repr__(self):
  110. return 'YES'
  111. def __getattribute__(self, name):
  112. if name.startswith('__') and name.endswith('__'):
  113. # to avoid inspection pb
  114. return super(_Yes, self).__getattribute__(name)
  115. return self
  116. def __call__(self, *args, **kwargs):
  117. return self
  118. YES = _Yes()
  119. class Instance(Proxy):
  120. """a special node representing a class instance"""
  121. def getattr(self, name, context=None, lookupclass=True):
  122. try:
  123. values = self._proxied.instance_attr(name, context)
  124. except NotFoundError:
  125. if name == '__class__':
  126. return [self._proxied]
  127. if lookupclass:
  128. # class attributes not available through the instance
  129. # unless they are explicitly defined
  130. if name in ('__name__', '__bases__', '__mro__', '__subclasses__'):
  131. return self._proxied.local_attr(name)
  132. return self._proxied.getattr(name, context)
  133. raise NotFoundError(name)
  134. # since we've no context information, return matching class members as
  135. # well
  136. if lookupclass:
  137. try:
  138. return values + self._proxied.getattr(name, context)
  139. except NotFoundError:
  140. pass
  141. return values
  142. def igetattr(self, name, context=None):
  143. """inferred getattr"""
  144. try:
  145. # XXX frame should be self._proxied, or not ?
  146. get_attr = self.getattr(name, context, lookupclass=False)
  147. return _infer_stmts(self._wrap_attr(get_attr, context), context,
  148. frame=self)
  149. except NotFoundError:
  150. try:
  151. # fallback to class'igetattr since it has some logic to handle
  152. # descriptors
  153. return self._wrap_attr(self._proxied.igetattr(name, context),
  154. context)
  155. except NotFoundError:
  156. raise InferenceError(name)
  157. def _wrap_attr(self, attrs, context=None):
  158. """wrap bound methods of attrs in a InstanceMethod proxies"""
  159. for attr in attrs:
  160. if isinstance(attr, UnboundMethod):
  161. if BUILTINS_NAME + '.property' in attr.decoratornames():
  162. for infered in attr.infer_call_result(self, context):
  163. yield infered
  164. else:
  165. yield BoundMethod(attr, self)
  166. else:
  167. yield attr
  168. def infer_call_result(self, caller, context=None):
  169. """infer what a class instance is returning when called"""
  170. infered = False
  171. for node in self._proxied.igetattr('__call__', context):
  172. for res in node.infer_call_result(caller, context):
  173. infered = True
  174. yield res
  175. if not infered:
  176. raise InferenceError()
  177. def __repr__(self):
  178. return '<Instance of %s.%s at 0x%s>' % (self._proxied.root().name,
  179. self._proxied.name,
  180. id(self))
  181. def __str__(self):
  182. return 'Instance of %s.%s' % (self._proxied.root().name,
  183. self._proxied.name)
  184. def callable(self):
  185. try:
  186. self._proxied.getattr('__call__')
  187. return True
  188. except NotFoundError:
  189. return False
  190. def pytype(self):
  191. return self._proxied.qname()
  192. def display_type(self):
  193. return 'Instance of'
  194. class UnboundMethod(Proxy):
  195. """a special node representing a method not bound to an instance"""
  196. def __repr__(self):
  197. frame = self._proxied.parent.frame()
  198. return '<%s %s of %s at 0x%s' % (self.__class__.__name__,
  199. self._proxied.name,
  200. frame.qname(), id(self))
  201. def is_bound(self):
  202. return False
  203. def getattr(self, name, context=None):
  204. if name == 'im_func':
  205. return [self._proxied]
  206. return super(UnboundMethod, self).getattr(name, context)
  207. def igetattr(self, name, context=None):
  208. if name == 'im_func':
  209. return iter((self._proxied,))
  210. return super(UnboundMethod, self).igetattr(name, context)
  211. def infer_call_result(self, caller, context):
  212. # If we're unbound method __new__ of builtin object, the result is an
  213. # instance of the class given as first argument.
  214. if (self._proxied.name == '__new__' and
  215. self._proxied.parent.frame().qname() == '%s.object' % BUILTINS_MODULE):
  216. return (x is YES and x or Instance(x) for x in caller.args[0].infer())
  217. return self._proxied.infer_call_result(caller, context)
  218. class BoundMethod(UnboundMethod):
  219. """a special node representing a method bound to an instance"""
  220. def __init__(self, proxy, bound):
  221. UnboundMethod.__init__(self, proxy)
  222. self.bound = bound
  223. def is_bound(self):
  224. return True
  225. def infer_call_result(self, caller, context):
  226. context = context.clone()
  227. context.boundnode = self.bound
  228. return self._proxied.infer_call_result(caller, context)
  229. class Generator(Instance):
  230. """a special node representing a generator"""
  231. def callable(self):
  232. return True
  233. def pytype(self):
  234. return '%s.generator' % BUILTINS_MODULE
  235. def display_type(self):
  236. return 'Generator'
  237. def __repr__(self):
  238. return '<Generator(%s) l.%s at 0x%s>' % (self._proxied.name, self.lineno, id(self))
  239. def __str__(self):
  240. return 'Generator(%s)' % (self._proxied.name)
  241. # decorators ##################################################################
  242. def path_wrapper(func):
  243. """return the given infer function wrapped to handle the path"""
  244. def wrapped(node, context=None, _func=func, **kwargs):
  245. """wrapper function handling context"""
  246. if context is None:
  247. context = InferenceContext()
  248. context.push(node)
  249. yielded = set()
  250. for res in _func(node, context, **kwargs):
  251. # unproxy only true instance, not const, tuple, dict...
  252. if res.__class__ is Instance:
  253. ares = res._proxied
  254. else:
  255. ares = res
  256. if not ares in yielded:
  257. yield res
  258. yielded.add(ares)
  259. return wrapped
  260. def yes_if_nothing_infered(func):
  261. def wrapper(*args, **kwargs):
  262. infered = False
  263. for node in func(*args, **kwargs):
  264. infered = True
  265. yield node
  266. if not infered:
  267. yield YES
  268. return wrapper
  269. def raise_if_nothing_infered(func):
  270. def wrapper(*args, **kwargs):
  271. infered = False
  272. for node in func(*args, **kwargs):
  273. infered = True
  274. yield node
  275. if not infered:
  276. raise InferenceError()
  277. return wrapper
  278. # Node ######################################################################
  279. class NodeNG(object):
  280. """Base Class for all ASTNG node classes.
  281. It represents a node of the new abstract syntax tree.
  282. """
  283. is_statement = False
  284. optional_assign = False # True for For (and for Comprehension if py <3.0)
  285. is_function = False # True for Function nodes
  286. # attributes below are set by the builder module or by raw factories
  287. lineno = None
  288. fromlineno = None
  289. tolineno = None
  290. col_offset = None
  291. # parent node in the tree
  292. parent = None
  293. # attributes containing child node(s) redefined in most concrete classes:
  294. _astng_fields = ()
  295. def _repr_name(self):
  296. """return self.name or self.attrname or '' for nice representation"""
  297. return getattr(self, 'name', getattr(self, 'attrname', ''))
  298. def __str__(self):
  299. return '%s(%s)' % (self.__class__.__name__, self._repr_name())
  300. def __repr__(self):
  301. return '<%s(%s) l.%s [%s] at Ox%x>' % (self.__class__.__name__,
  302. self._repr_name(),
  303. self.fromlineno,
  304. self.root().name,
  305. id(self))
  306. def accept(self, visitor):
  307. klass = self.__class__.__name__
  308. func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
  309. return func(self)
  310. def get_children(self):
  311. for field in self._astng_fields:
  312. attr = getattr(self, field)
  313. if attr is None:
  314. continue
  315. if isinstance(attr, (list, tuple)):
  316. for elt in attr:
  317. yield elt
  318. else:
  319. yield attr
  320. def last_child(self):
  321. """an optimized version of list(get_children())[-1]"""
  322. for field in self._astng_fields[::-1]:
  323. attr = getattr(self, field)
  324. if not attr: # None or empty listy / tuple
  325. continue
  326. if isinstance(attr, (list, tuple)):
  327. return attr[-1]
  328. else:
  329. return attr
  330. return None
  331. def parent_of(self, node):
  332. """return true if i'm a parent of the given node"""
  333. parent = node.parent
  334. while parent is not None:
  335. if self is parent:
  336. return True
  337. parent = parent.parent
  338. return False
  339. def statement(self):
  340. """return the first parent node marked as statement node"""
  341. if self.is_statement:
  342. return self
  343. return self.parent.statement()
  344. def frame(self):
  345. """return the first parent frame node (i.e. Module, Function or Class)
  346. """
  347. return self.parent.frame()
  348. def scope(self):
  349. """return the first node defining a new scope (i.e. Module, Function,
  350. Class, Lambda but also GenExpr)
  351. """
  352. return self.parent.scope()
  353. def root(self):
  354. """return the root node of the tree, (i.e. a Module)"""
  355. if self.parent:
  356. return self.parent.root()
  357. return self
  358. def child_sequence(self, child):
  359. """search for the right sequence where the child lies in"""
  360. for field in self._astng_fields:
  361. node_or_sequence = getattr(self, field)
  362. if node_or_sequence is child:
  363. return [node_or_sequence]
  364. # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
  365. if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence:
  366. return node_or_sequence
  367. else:
  368. msg = 'Could not found %s in %s\'s children'
  369. raise ASTNGError(msg % (repr(child), repr(self)))
  370. def locate_child(self, child):
  371. """return a 2-uple (child attribute name, sequence or node)"""
  372. for field in self._astng_fields:
  373. node_or_sequence = getattr(self, field)
  374. # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
  375. if child is node_or_sequence:
  376. return field, child
  377. if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence:
  378. return field, node_or_sequence
  379. msg = 'Could not found %s in %s\'s children'
  380. raise ASTNGError(msg % (repr(child), repr(self)))
  381. # FIXME : should we merge child_sequence and locate_child ? locate_child
  382. # is only used in are_exclusive, child_sequence one time in pylint.
  383. def next_sibling(self):
  384. """return the next sibling statement"""
  385. return self.parent.next_sibling()
  386. def previous_sibling(self):
  387. """return the previous sibling statement"""
  388. return self.parent.previous_sibling()
  389. def nearest(self, nodes):
  390. """return the node which is the nearest before this one in the
  391. given list of nodes
  392. """
  393. myroot = self.root()
  394. mylineno = self.fromlineno
  395. nearest = None, 0
  396. for node in nodes:
  397. assert node.root() is myroot, \
  398. 'nodes %s and %s are not from the same module' % (self, node)
  399. lineno = node.fromlineno
  400. if node.fromlineno > mylineno:
  401. break
  402. if lineno > nearest[1]:
  403. nearest = node, lineno
  404. # FIXME: raise an exception if nearest is None ?
  405. return nearest[0]
  406. def set_line_info(self, lastchild):
  407. if self.lineno is None:
  408. self.fromlineno = self._fixed_source_line()
  409. else:
  410. self.fromlineno = self.lineno
  411. if lastchild is None:
  412. self.tolineno = self.fromlineno
  413. else:
  414. self.tolineno = lastchild.tolineno
  415. return
  416. # TODO / FIXME:
  417. assert self.fromlineno is not None, self
  418. assert self.tolineno is not None, self
  419. def _fixed_source_line(self):
  420. """return the line number where the given node appears
  421. we need this method since not all nodes have the lineno attribute
  422. correctly set...
  423. """
  424. line = self.lineno
  425. _node = self
  426. try:
  427. while line is None:
  428. _node = _node.get_children().next()
  429. line = _node.lineno
  430. except StopIteration:
  431. _node = self.parent
  432. while _node and line is None:
  433. line = _node.lineno
  434. _node = _node.parent
  435. return line
  436. def block_range(self, lineno):
  437. """handle block line numbers range for non block opening statements
  438. """
  439. return lineno, self.tolineno
  440. def set_local(self, name, stmt):
  441. """delegate to a scoped parent handling a locals dictionary"""
  442. self.parent.set_local(name, stmt)
  443. def nodes_of_class(self, klass, skip_klass=None):
  444. """return an iterator on nodes which are instance of the given class(es)
  445. klass may be a class object or a tuple of class objects
  446. """
  447. if isinstance(self, klass):
  448. yield self
  449. for child_node in self.get_children():
  450. if skip_klass is not None and isinstance(child_node, skip_klass):
  451. continue
  452. for matching in child_node.nodes_of_class(klass, skip_klass):
  453. yield matching
  454. def _infer_name(self, frame, name):
  455. # overridden for From, Import, Global, TryExcept and Arguments
  456. return None
  457. def infer(self, context=None):
  458. """we don't know how to resolve a statement by default"""
  459. # this method is overridden by most concrete classes
  460. raise InferenceError(self.__class__.__name__)
  461. def infered(self):
  462. '''return list of infered values for a more simple inference usage'''
  463. return list(self.infer())
  464. def instanciate_class(self):
  465. """instanciate a node if it is a Class node, else return self"""
  466. return self
  467. def has_base(self, node):
  468. return False
  469. def callable(self):
  470. return False
  471. def eq(self, value):
  472. return False
  473. def as_string(self):
  474. return as_string(self)
  475. def repr_tree(self, ids=False):
  476. """print a nice astng tree representation.
  477. :param ids: if true, we also print the ids (usefull for debugging)"""
  478. result = []
  479. _repr_tree(self, result, ids=ids)
  480. return "\n".join(result)
  481. class Statement(NodeNG):
  482. """Statement node adding a few attributes"""
  483. is_statement = True
  484. def next_sibling(self):
  485. """return the next sibling statement"""
  486. stmts = self.parent.child_sequence(self)
  487. index = stmts.index(self)
  488. try:
  489. return stmts[index +1]
  490. except IndexError:
  491. pass
  492. def previous_sibling(self):
  493. """return the previous sibling statement"""
  494. stmts = self.parent.child_sequence(self)
  495. index = stmts.index(self)
  496. if index >= 1:
  497. return stmts[index -1]
  498. INDENT = " "
  499. def _repr_tree(node, result, indent='', _done=None, ids=False):
  500. """built a tree representation of a node as a list of lines"""
  501. if _done is None:
  502. _done = set()
  503. if not hasattr(node, '_astng_fields'): # not a astng node
  504. return
  505. if node in _done:
  506. result.append( indent + 'loop in tree: %s' % node )
  507. return
  508. _done.add(node)
  509. node_str = str(node)
  510. if ids:
  511. node_str += ' . \t%x' % id(node)
  512. result.append( indent + node_str )
  513. indent += INDENT
  514. for field in node._astng_fields:
  515. value = getattr(node, field)
  516. if isinstance(value, (list, tuple) ):
  517. result.append( indent + field + " = [" )
  518. for child in value:
  519. if isinstance(child, (list, tuple) ):
  520. # special case for Dict # FIXME
  521. _repr_tree(child[0], result, indent, _done, ids)
  522. _repr_tree(child[1], result, indent, _done, ids)
  523. result.append(indent + ',')
  524. else:
  525. _repr_tree(child, result, indent, _done, ids)
  526. result.append( indent + "]" )
  527. else:
  528. result.append( indent + field + " = " )
  529. _repr_tree(value, result, indent, _done, ids)