PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/tasks/clonedigger/logilab/astng/__init__.py

https://github.com/pkcphilip/code-worker
Python | 294 lines | 271 code | 3 blank | 20 comment | 6 complexity | 3a6530995dd1ae6e48d44e9d4acfeb28 MD5 | raw file
  1. # This program is free software; you can redistribute it and/or modify it under
  2. # the terms of the GNU General Public License as published by the Free Software
  3. # Foundation; either version 2 of the License, or (at your option) any later
  4. # version.
  5. #
  6. # This program is distributed in the hope that it will be useful, but WITHOUT
  7. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. #
  10. # You should have received a copy of the GNU General Public License along with
  11. # this program; if not, write to the Free Software Foundation, Inc.,
  12. # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  13. """Python Abstract Syntax Tree New Generation
  14. The aim of this module is to provide a common base representation of
  15. python source code for projects such as pychecker, pyreverse,
  16. pylint... Well, actually the development of this library is essentialy
  17. governed by pylint's needs.
  18. It extends class defined in the compiler.ast [1] module with some
  19. additional methods and attributes. Instance attributes are added by a
  20. builder object, which can either generate extended ast (let's call
  21. them astng ;) by visiting an existant ast tree or by inspecting living
  22. object. Methods are added by monkey patching ast classes.
  23. Main modules are:
  24. * nodes and scoped_nodes for more information about methods and
  25. attributes added to different node classes
  26. * the manager contains a high level object to get astng trees from
  27. source files and living objects. It maintains a cache of previously
  28. constructed tree for quick access
  29. * builder contains the class responsible to build astng trees
  30. :author: Sylvain Thenault
  31. :copyright: 2003-2007 LOGILAB S.A. (Paris, FRANCE)
  32. :contact: http://www.logilab.fr/ -- mailto:python-projects@logilab.org
  33. :copyright: 2003-2007 Sylvain Thenault
  34. :contact: mailto:thenault@gmail.com
  35. """
  36. from __future__ import generators
  37. __doctype__ = "restructuredtext en"
  38. from clonedigger.logilab.common.compat import chain, imap
  39. # WARNING: internal imports order matters !
  40. from clonedigger.logilab.astng._exceptions import *
  41. class InferenceContext(object):
  42. __slots__ = ('startingfrom', 'path', 'lookupname', 'callcontext', 'boundnode')
  43. def __init__(self, node=None, path=None):
  44. self.startingfrom = node # XXX useful ?
  45. if path is None:
  46. self.path = []
  47. else:
  48. self.path = path
  49. self.lookupname = None
  50. self.callcontext = None
  51. self.boundnode = None
  52. def push(self, node):
  53. name = self.lookupname
  54. if (node, name) in self.path:
  55. raise StopIteration()
  56. self.path.append( (node, name) )
  57. def pop(self):
  58. return self.path.pop()
  59. def clone(self):
  60. # XXX copy lookupname/callcontext ?
  61. clone = InferenceContext(self.startingfrom, self.path)
  62. clone.callcontext = self.callcontext
  63. clone.boundnode = self.boundnode
  64. return clone
  65. def unpack_infer(stmt, context=None):
  66. """return an iterator on nodes infered by the given statement
  67. if the infered value is a list or a tuple, recurse on it to
  68. get values infered by its content
  69. """
  70. if isinstance(stmt, (List, Tuple)):
  71. # XXX loosing context
  72. return chain(*imap(unpack_infer, stmt.nodes))
  73. infered = stmt.infer(context).next()
  74. if infered is stmt:
  75. return iter( (stmt,) )
  76. return chain(*imap(unpack_infer, stmt.infer(context)))
  77. def copy_context(context):
  78. if context is not None:
  79. return context.clone()
  80. else:
  81. return InferenceContext()
  82. def _infer_stmts(stmts, context, frame=None):
  83. """return an iterator on statements infered by each statement in <stmts>
  84. """
  85. stmt = None
  86. infered = False
  87. if context is not None:
  88. name = context.lookupname
  89. context = context.clone()
  90. else:
  91. name = None
  92. context = InferenceContext()
  93. for stmt in stmts:
  94. if stmt is YES:
  95. yield stmt
  96. infered = True
  97. continue
  98. context.lookupname = stmt._infer_name(frame, name)
  99. try:
  100. for infered in stmt.infer(context):
  101. yield infered
  102. infered = True
  103. except UnresolvableName:
  104. continue
  105. except InferenceError:
  106. yield YES
  107. infered = True
  108. if not infered:
  109. raise InferenceError(str(stmt))
  110. # special inference objects ###################################################
  111. class Yes(object):
  112. """a yes object"""
  113. def __repr__(self):
  114. return 'YES'
  115. def __getattribute__(self, name):
  116. return self
  117. def __call__(self, *args, **kwargs):
  118. return self
  119. YES = Yes()
  120. class Proxy:
  121. """a simple proxy object"""
  122. def __init__(self, proxied):
  123. self._proxied = proxied
  124. def __getattr__(self, name):
  125. return getattr(self._proxied, name)
  126. def infer(self, context=None):
  127. yield self
  128. class InstanceMethod(Proxy):
  129. """a special node representing a function bound to an instance"""
  130. def __repr__(self):
  131. instance = self._proxied.parent.frame()
  132. return 'Bound method %s of %s.%s' % (self._proxied.name,
  133. instance.root().name,
  134. instance.name)
  135. __str__ = __repr__
  136. def is_bound(self):
  137. return True
  138. class Instance(Proxy):
  139. """a special node representing a class instance"""
  140. def getattr(self, name, context=None, lookupclass=True):
  141. try:
  142. return self._proxied.instance_attr(name, context)
  143. except NotFoundError:
  144. if name == '__class__':
  145. return [self._proxied]
  146. if name == '__name__':
  147. # access to __name__ gives undefined member on class
  148. # instances but not on class objects
  149. raise NotFoundError(name)
  150. if lookupclass:
  151. return self._proxied.getattr(name, context)
  152. raise NotFoundError(name)
  153. def igetattr(self, name, context=None):
  154. """infered getattr"""
  155. try:
  156. # XXX frame should be self._proxied, or not ?
  157. return _infer_stmts(
  158. self._wrap_attr(self.getattr(name, context, lookupclass=False)),
  159. context, frame=self)
  160. except NotFoundError:
  161. try:
  162. # fallback to class'igetattr since it has some logic to handle
  163. # descriptors
  164. return self._wrap_attr(self._proxied.igetattr(name, context))
  165. except NotFoundError:
  166. raise InferenceError(name)
  167. def _wrap_attr(self, attrs):
  168. """wrap bound methods of attrs in a InstanceMethod proxies"""
  169. # Guess which attrs are used in inference.
  170. def wrap(attr):
  171. if isinstance(attr, Function) and attr.type == 'method':
  172. return InstanceMethod(attr)
  173. else:
  174. return attr
  175. return imap(wrap, attrs)
  176. def infer_call_result(self, caller, context=None):
  177. """infer what's a class instance is returning when called"""
  178. infered = False
  179. for node in self._proxied.igetattr('__call__', context):
  180. for res in node.infer_call_result(caller, context):
  181. infered = True
  182. yield res
  183. if not infered:
  184. raise InferenceError()
  185. def __repr__(self):
  186. return 'Instance of %s.%s' % (self._proxied.root().name,
  187. self._proxied.name)
  188. __str__ = __repr__
  189. def callable(self):
  190. try:
  191. self._proxied.getattr('__call__')
  192. return True
  193. except NotFoundError:
  194. return False
  195. def pytype(self):
  196. return self._proxied.qname()
  197. class Generator(Proxy):
  198. """a special node representing a generator"""
  199. def callable(self):
  200. return True
  201. def pytype(self):
  202. return '__builtin__.generator'
  203. # imports #####################################################################
  204. from clonedigger.logilab.astng.manager import ASTNGManager, Project, Package
  205. MANAGER = ASTNGManager()
  206. from clonedigger.logilab.astng.nodes import *
  207. from clonedigger.logilab.astng import nodes
  208. from clonedigger.logilab.astng.scoped_nodes import *
  209. from clonedigger.logilab.astng import inference
  210. from clonedigger.logilab.astng import lookup
  211. lookup._decorate(nodes)
  212. List._proxied = MANAGER.astng_from_class(list)
  213. List.__bases__ += (inference.Instance,)
  214. List.pytype = lambda x: '__builtin__.list'
  215. Tuple._proxied = MANAGER.astng_from_class(tuple)
  216. Tuple.__bases__ += (inference.Instance,)
  217. Tuple.pytype = lambda x: '__builtin__.tuple'
  218. Dict.__bases__ += (inference.Instance,)
  219. Dict._proxied = MANAGER.astng_from_class(dict)
  220. Dict.pytype = lambda x: '__builtin__.dict'
  221. builtin_astng = Dict._proxied.root()
  222. Const.__bases__ += (inference.Instance,)
  223. Const._proxied = None
  224. def Const___getattr__(self, name):
  225. if self.value is None:
  226. raise AttributeError(name)
  227. if self._proxied is None:
  228. self._proxied = MANAGER.astng_from_class(self.value.__class__)
  229. return getattr(self._proxied, name)
  230. Const.__getattr__ = Const___getattr__
  231. def Const_getattr(self, name, context=None, lookupclass=None):
  232. if self.value is None:
  233. raise NotFoundError(name)
  234. if self._proxied is None:
  235. self._proxied = MANAGER.astng_from_class(self.value.__class__)
  236. return self._proxied.getattr(name, context)
  237. Const.getattr = Const_getattr
  238. Const.has_dynamic_getattr = lambda x: False
  239. def Const_pytype(self):
  240. if self.value is None:
  241. return '__builtin__.NoneType'
  242. if self._proxied is None:
  243. self._proxied = MANAGER.astng_from_class(self.value.__class__)
  244. return self._proxied.qname()
  245. Const.pytype = Const_pytype