raw_building.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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 create astng trees from scratch
  21. (build_* functions) or from living object (object_build_* functions)
  22. """
  23. __docformat__ = "restructuredtext en"
  24. import sys
  25. from os.path import abspath
  26. from inspect import (getargspec, isdatadescriptor, isfunction, ismethod,
  27. ismethoddescriptor, isclass, isbuiltin)
  28. from logilab.astng import BUILTINS_MODULE
  29. from logilab.astng.node_classes import CONST_CLS
  30. from logilab.astng.nodes import (Module, Class, Const, const_factory, From,
  31. Function, EmptyNode, Name, Arguments, Dict, List, Set, Tuple)
  32. from logilab.astng.bases import Generator
  33. from logilab.astng.manager import ASTNGManager
  34. MANAGER = ASTNGManager()
  35. _CONSTANTS = tuple(CONST_CLS) # the keys of CONST_CLS eg python builtin types
  36. def _attach_local_node(parent, node, name):
  37. node.name = name # needed by add_local_node
  38. parent.add_local_node(node)
  39. _marker = object()
  40. def attach_dummy_node(node, name, object=_marker):
  41. """create a dummy node and register it in the locals of the given
  42. node with the specified name
  43. """
  44. enode = EmptyNode()
  45. enode.object = object
  46. _attach_local_node(node, enode, name)
  47. EmptyNode.has_underlying_object = lambda self: self.object is not _marker
  48. def attach_const_node(node, name, value):
  49. """create a Const node and register it in the locals of the given
  50. node with the specified name
  51. """
  52. if not name in node.special_attributes:
  53. _attach_local_node(node, const_factory(value), name)
  54. def attach_import_node(node, modname, membername):
  55. """create a From node and register it in the locals of the given
  56. node with the specified name
  57. """
  58. from_node = From(modname, [(membername, None)])
  59. _attach_local_node(node, from_node, membername)
  60. def build_module(name, doc=None):
  61. """create and initialize a astng Module node"""
  62. node = Module(name, doc, pure_python=False)
  63. node.package = False
  64. node.parent = None
  65. return node
  66. def build_class(name, basenames=(), doc=None):
  67. """create and initialize a astng Class node"""
  68. node = Class(name, doc)
  69. for base in basenames:
  70. basenode = Name()
  71. basenode.name = base
  72. node.bases.append(basenode)
  73. basenode.parent = node
  74. return node
  75. def build_function(name, args=None, defaults=None, flag=0, doc=None):
  76. """create and initialize a astng Function node"""
  77. args, defaults = args or [], defaults or []
  78. # first argument is now a list of decorators
  79. func = Function(name, doc)
  80. func.args = argsnode = Arguments()
  81. argsnode.args = []
  82. for arg in args:
  83. argsnode.args.append(Name())
  84. argsnode.args[-1].name = arg
  85. argsnode.args[-1].parent = argsnode
  86. argsnode.defaults = []
  87. for default in defaults:
  88. argsnode.defaults.append(const_factory(default))
  89. argsnode.defaults[-1].parent = argsnode
  90. argsnode.kwarg = None
  91. argsnode.vararg = None
  92. argsnode.parent = func
  93. if args:
  94. register_arguments(func)
  95. return func
  96. def build_from_import(fromname, names):
  97. """create and initialize an astng From import statement"""
  98. return From(fromname, [(name, None) for name in names])
  99. def register_arguments(func, args=None):
  100. """add given arguments to local
  101. args is a list that may contains nested lists
  102. (i.e. def func(a, (b, c, d)): ...)
  103. """
  104. if args is None:
  105. args = func.args.args
  106. if func.args.vararg:
  107. func.set_local(func.args.vararg, func.args)
  108. if func.args.kwarg:
  109. func.set_local(func.args.kwarg, func.args)
  110. for arg in args:
  111. if isinstance(arg, Name):
  112. func.set_local(arg.name, arg)
  113. else:
  114. register_arguments(func, arg.elts)
  115. def object_build_class(node, member, localname):
  116. """create astng for a living class object"""
  117. basenames = [base.__name__ for base in member.__bases__]
  118. return _base_class_object_build(node, member, basenames,
  119. localname=localname)
  120. def object_build_function(node, member, localname):
  121. """create astng for a living function object"""
  122. args, varargs, varkw, defaults = getargspec(member)
  123. if varargs is not None:
  124. args.append(varargs)
  125. if varkw is not None:
  126. args.append(varkw)
  127. func = build_function(getattr(member, '__name__', None) or localname, args,
  128. defaults, member.func_code.co_flags, member.__doc__)
  129. node.add_local_node(func, localname)
  130. def object_build_datadescriptor(node, member, name):
  131. """create astng for a living data descriptor object"""
  132. return _base_class_object_build(node, member, [], name)
  133. def object_build_methoddescriptor(node, member, localname):
  134. """create astng for a living method descriptor object"""
  135. # FIXME get arguments ?
  136. func = build_function(getattr(member, '__name__', None) or localname,
  137. doc=member.__doc__)
  138. # set node's arguments to None to notice that we have no information, not
  139. # and empty argument list
  140. func.args.args = None
  141. node.add_local_node(func, localname)
  142. def _base_class_object_build(node, member, basenames, name=None, localname=None):
  143. """create astng for a living class object, with a given set of base names
  144. (e.g. ancestors)
  145. """
  146. klass = build_class(name or getattr(member, '__name__', None) or localname,
  147. basenames, member.__doc__)
  148. klass._newstyle = isinstance(member, type)
  149. node.add_local_node(klass, localname)
  150. try:
  151. # limit the instantiation trick since it's too dangerous
  152. # (such as infinite test execution...)
  153. # this at least resolves common case such as Exception.args,
  154. # OSError.errno
  155. if issubclass(member, Exception):
  156. instdict = member().__dict__
  157. else:
  158. raise TypeError
  159. except:
  160. pass
  161. else:
  162. for name, obj in instdict.items():
  163. valnode = EmptyNode()
  164. valnode.object = obj
  165. valnode.parent = klass
  166. valnode.lineno = 1
  167. klass.instance_attrs[name] = [valnode]
  168. return klass
  169. class InspectBuilder(object):
  170. """class for building nodes from living object
  171. this is actually a really minimal representation, including only Module,
  172. Function and Class nodes and some others as guessed.
  173. """
  174. # astng from living objects ###############################################
  175. def __init__(self):
  176. self._done = {}
  177. self._module = None
  178. def inspect_build(self, module, modname=None, path=None):
  179. """build astng from a living module (i.e. using inspect)
  180. this is used when there is no python source code available (either
  181. because it's a built-in module or because the .py is not available)
  182. """
  183. self._module = module
  184. if modname is None:
  185. modname = module.__name__
  186. node = build_module(modname, module.__doc__)
  187. node.file = node.path = path and abspath(path) or path
  188. MANAGER.astng_cache[modname] = node
  189. node.package = hasattr(module, '__path__')
  190. self._done = {}
  191. self.object_build(node, module)
  192. return node
  193. def object_build(self, node, obj):
  194. """recursive method which create a partial ast from real objects
  195. (only function, class, and method are handled)
  196. """
  197. if obj in self._done:
  198. return self._done[obj]
  199. self._done[obj] = node
  200. for name in dir(obj):
  201. try:
  202. member = getattr(obj, name)
  203. except AttributeError:
  204. # damned ExtensionClass.Base, I know you're there !
  205. attach_dummy_node(node, name)
  206. continue
  207. if ismethod(member):
  208. member = member.im_func
  209. if isfunction(member):
  210. # verify this is not an imported function
  211. filename = getattr(member.func_code, 'co_filename', None)
  212. if filename is None:
  213. assert isinstance(member, object)
  214. object_build_methoddescriptor(node, member, name)
  215. elif filename != getattr(self._module, '__file__', None):
  216. attach_dummy_node(node, name, member)
  217. else:
  218. object_build_function(node, member, name)
  219. elif isbuiltin(member):
  220. if self.imported_member(node, member, name):
  221. #if obj is object:
  222. # print 'skippp', obj, name, member
  223. continue
  224. object_build_methoddescriptor(node, member, name)
  225. elif isclass(member):
  226. if self.imported_member(node, member, name):
  227. continue
  228. if member in self._done:
  229. class_node = self._done[member]
  230. if not class_node in node.locals.get(name, ()):
  231. node.add_local_node(class_node, name)
  232. else:
  233. class_node = object_build_class(node, member, name)
  234. # recursion
  235. self.object_build(class_node, member)
  236. if name == '__class__' and class_node.parent is None:
  237. class_node.parent = self._done[self._module]
  238. elif ismethoddescriptor(member):
  239. assert isinstance(member, object)
  240. object_build_methoddescriptor(node, member, name)
  241. elif isdatadescriptor(member):
  242. assert isinstance(member, object)
  243. object_build_datadescriptor(node, member, name)
  244. elif isinstance(member, _CONSTANTS):
  245. attach_const_node(node, name, member)
  246. else:
  247. # create an empty node so that the name is actually defined
  248. attach_dummy_node(node, name, member)
  249. def imported_member(self, node, member, name):
  250. """verify this is not an imported class or handle it"""
  251. # /!\ some classes like ExtensionClass doesn't have a __module__
  252. # attribute ! Also, this may trigger an exception on badly built module
  253. # (see http://www.logilab.org/ticket/57299 for instance)
  254. try:
  255. modname = getattr(member, '__module__', None)
  256. except:
  257. # XXX use logging
  258. print 'unexpected error while building astng from living object'
  259. import traceback
  260. traceback.print_exc()
  261. modname = None
  262. if modname is None:
  263. if name in ('__new__', '__subclasshook__'):
  264. # Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14)
  265. # >>> print object.__new__.__module__
  266. # None
  267. modname = BUILTINS_MODULE
  268. else:
  269. attach_dummy_node(node, name, member)
  270. return True
  271. if {'gtk': 'gtk._gtk'}.get(modname, modname) != self._module.__name__:
  272. # check if it sounds valid and then add an import node, else use a
  273. # dummy node
  274. try:
  275. getattr(sys.modules[modname], name)
  276. except (KeyError, AttributeError):
  277. attach_dummy_node(node, name, member)
  278. else:
  279. attach_import_node(node, modname, name)
  280. return True
  281. return False
  282. ### astng boot strapping ################################################### ###
  283. _CONST_PROXY = {}
  284. def astng_boot_strapping():
  285. """astng boot strapping the builtins module"""
  286. # this boot strapping is necessary since we need the Const nodes to
  287. # inspect_build builtins, and then we can proxy Const
  288. builder = InspectBuilder()
  289. from logilab.common.compat import builtins
  290. astng_builtin = builder.inspect_build(builtins)
  291. for cls, node_cls in CONST_CLS.items():
  292. if cls is type(None):
  293. proxy = build_class('NoneType')
  294. proxy.parent = astng_builtin
  295. else:
  296. proxy = astng_builtin.getattr(cls.__name__)[0] # XXX
  297. if cls in (dict, list, set, tuple):
  298. node_cls._proxied = proxy
  299. else:
  300. _CONST_PROXY[cls] = proxy
  301. astng_boot_strapping()
  302. # TODO : find a nicer way to handle this situation;
  303. # However __proxied introduced an
  304. # infinite recursion (see https://bugs.launchpad.net/pylint/+bug/456870)
  305. def _set_proxied(const):
  306. return _CONST_PROXY[const.value.__class__]
  307. Const._proxied = property(_set_proxied)
  308. # FIXME : is it alright that Generator._proxied is not a astng node?
  309. Generator._proxied = MANAGER.infer_astng_from_something(type(a for a in ()))