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.

raw_building.py 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. # Copyright (c) 2006-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 create astroid trees from scratch
  8. (build_* functions) or from living object (object_build_* functions)
  9. """
  10. import inspect
  11. import logging
  12. import os
  13. import sys
  14. import types
  15. import six
  16. from astroid import bases
  17. from astroid import manager
  18. from astroid import node_classes
  19. from astroid import nodes
  20. MANAGER = manager.AstroidManager()
  21. # the keys of CONST_CLS eg python builtin types
  22. _CONSTANTS = tuple(node_classes.CONST_CLS)
  23. _JYTHON = os.name == 'java'
  24. _BUILTINS = vars(six.moves.builtins)
  25. _LOG = logging.getLogger(__name__)
  26. def _io_discrepancy(member):
  27. # _io module names itself `io`: http://bugs.python.org/issue18602
  28. member_self = getattr(member, '__self__', None)
  29. return (member_self and
  30. inspect.ismodule(member_self) and
  31. member_self.__name__ == '_io' and
  32. member.__module__ == 'io')
  33. def _attach_local_node(parent, node, name):
  34. node.name = name # needed by add_local_node
  35. parent.add_local_node(node)
  36. def _add_dunder_class(func, member):
  37. """Add a __class__ member to the given func node, if we can determine it."""
  38. python_cls = member.__class__
  39. cls_name = getattr(python_cls, '__name__', None)
  40. if not cls_name:
  41. return
  42. cls_bases = [ancestor.__name__ for ancestor in python_cls.__bases__]
  43. ast_klass = build_class(cls_name, cls_bases, python_cls.__doc__)
  44. func.instance_attrs['__class__'] = [ast_klass]
  45. _marker = object()
  46. def attach_dummy_node(node, name, runtime_object=_marker):
  47. """create a dummy node and register it in the locals of the given
  48. node with the specified name
  49. """
  50. enode = nodes.EmptyNode()
  51. enode.object = runtime_object
  52. _attach_local_node(node, enode, name)
  53. def _has_underlying_object(self):
  54. return self.object is not None and self.object is not _marker
  55. nodes.EmptyNode.has_underlying_object = _has_underlying_object
  56. def attach_const_node(node, name, value):
  57. """create a Const node and register it in the locals of the given
  58. node with the specified name
  59. """
  60. if name not in node.special_attributes:
  61. _attach_local_node(node, nodes.const_factory(value), name)
  62. def attach_import_node(node, modname, membername):
  63. """create a ImportFrom node and register it in the locals of the given
  64. node with the specified name
  65. """
  66. from_node = nodes.ImportFrom(modname, [(membername, None)])
  67. _attach_local_node(node, from_node, membername)
  68. def build_module(name, doc=None):
  69. """create and initialize a astroid Module node"""
  70. node = nodes.Module(name, doc, pure_python=False)
  71. node.package = False
  72. node.parent = None
  73. return node
  74. def build_class(name, basenames=(), doc=None):
  75. """create and initialize a astroid ClassDef node"""
  76. node = nodes.ClassDef(name, doc)
  77. for base in basenames:
  78. basenode = nodes.Name()
  79. basenode.name = base
  80. node.bases.append(basenode)
  81. basenode.parent = node
  82. return node
  83. def build_function(name, args=None, defaults=None, doc=None):
  84. """create and initialize a astroid FunctionDef node"""
  85. args, defaults = args or [], defaults or []
  86. # first argument is now a list of decorators
  87. func = nodes.FunctionDef(name, doc)
  88. func.args = argsnode = nodes.Arguments()
  89. argsnode.args = []
  90. for arg in args:
  91. argsnode.args.append(nodes.Name())
  92. argsnode.args[-1].name = arg
  93. argsnode.args[-1].parent = argsnode
  94. argsnode.defaults = []
  95. for default in defaults:
  96. argsnode.defaults.append(nodes.const_factory(default))
  97. argsnode.defaults[-1].parent = argsnode
  98. argsnode.kwarg = None
  99. argsnode.vararg = None
  100. argsnode.parent = func
  101. if args:
  102. register_arguments(func)
  103. return func
  104. def build_from_import(fromname, names):
  105. """create and initialize an astroid ImportFrom import statement"""
  106. return nodes.ImportFrom(fromname, [(name, None) for name in names])
  107. def register_arguments(func, args=None):
  108. """add given arguments to local
  109. args is a list that may contains nested lists
  110. (i.e. def func(a, (b, c, d)): ...)
  111. """
  112. if args is None:
  113. args = func.args.args
  114. if func.args.vararg:
  115. func.set_local(func.args.vararg, func.args)
  116. if func.args.kwarg:
  117. func.set_local(func.args.kwarg, func.args)
  118. for arg in args:
  119. if isinstance(arg, nodes.Name):
  120. func.set_local(arg.name, arg)
  121. else:
  122. register_arguments(func, arg.elts)
  123. def object_build_class(node, member, localname):
  124. """create astroid for a living class object"""
  125. basenames = [base.__name__ for base in member.__bases__]
  126. return _base_class_object_build(node, member, basenames,
  127. localname=localname)
  128. def object_build_function(node, member, localname):
  129. """create astroid for a living function object"""
  130. # pylint: disable=deprecated-method; completely removed in 2.0
  131. args, varargs, varkw, defaults = inspect.getargspec(member)
  132. if varargs is not None:
  133. args.append(varargs)
  134. if varkw is not None:
  135. args.append(varkw)
  136. func = build_function(getattr(member, '__name__', None) or localname, args,
  137. defaults, member.__doc__)
  138. node.add_local_node(func, localname)
  139. def object_build_datadescriptor(node, member, name):
  140. """create astroid for a living data descriptor object"""
  141. return _base_class_object_build(node, member, [], name)
  142. def object_build_methoddescriptor(node, member, localname):
  143. """create astroid for a living method descriptor object"""
  144. # FIXME get arguments ?
  145. func = build_function(getattr(member, '__name__', None) or localname,
  146. doc=member.__doc__)
  147. # set node's arguments to None to notice that we have no information, not
  148. # and empty argument list
  149. func.args.args = None
  150. node.add_local_node(func, localname)
  151. _add_dunder_class(func, member)
  152. def _base_class_object_build(node, member, basenames, name=None, localname=None):
  153. """create astroid for a living class object, with a given set of base names
  154. (e.g. ancestors)
  155. """
  156. klass = build_class(name or getattr(member, '__name__', None) or localname,
  157. basenames, member.__doc__)
  158. klass._newstyle = isinstance(member, type)
  159. node.add_local_node(klass, localname)
  160. try:
  161. # limit the instantiation trick since it's too dangerous
  162. # (such as infinite test execution...)
  163. # this at least resolves common case such as Exception.args,
  164. # OSError.errno
  165. if issubclass(member, Exception):
  166. instdict = member().__dict__
  167. else:
  168. raise TypeError
  169. except: # pylint: disable=bare-except
  170. pass
  171. else:
  172. for item_name, obj in instdict.items():
  173. valnode = nodes.EmptyNode()
  174. valnode.object = obj
  175. valnode.parent = klass
  176. valnode.lineno = 1
  177. klass.instance_attrs[item_name] = [valnode]
  178. return klass
  179. def _build_from_function(node, name, member, module):
  180. # verify this is not an imported function
  181. try:
  182. code = six.get_function_code(member)
  183. except AttributeError:
  184. # Some implementations don't provide the code object,
  185. # such as Jython.
  186. code = None
  187. filename = getattr(code, 'co_filename', None)
  188. if filename is None:
  189. assert isinstance(member, object)
  190. object_build_methoddescriptor(node, member, name)
  191. elif filename != getattr(module, '__file__', None):
  192. attach_dummy_node(node, name, member)
  193. else:
  194. object_build_function(node, member, name)
  195. class InspectBuilder(object):
  196. """class for building nodes from living object
  197. this is actually a really minimal representation, including only Module,
  198. FunctionDef and ClassDef nodes and some others as guessed.
  199. """
  200. # astroid from living objects ###############################################
  201. def __init__(self):
  202. self._done = {}
  203. self._module = None
  204. def inspect_build(self, module, modname=None, path=None):
  205. """build astroid from a living module (i.e. using inspect)
  206. this is used when there is no python source code available (either
  207. because it's a built-in module or because the .py is not available)
  208. """
  209. self._module = module
  210. if modname is None:
  211. modname = module.__name__
  212. try:
  213. node = build_module(modname, module.__doc__)
  214. except AttributeError:
  215. # in jython, java modules have no __doc__ (see #109562)
  216. node = build_module(modname)
  217. node.file = node.path = os.path.abspath(path) if path else path
  218. node.name = modname
  219. MANAGER.cache_module(node)
  220. node.package = hasattr(module, '__path__')
  221. self._done = {}
  222. self.object_build(node, module)
  223. return node
  224. def object_build(self, node, obj):
  225. """recursive method which create a partial ast from real objects
  226. (only function, class, and method are handled)
  227. """
  228. if obj in self._done:
  229. return self._done[obj]
  230. self._done[obj] = node
  231. for name in dir(obj):
  232. try:
  233. member = getattr(obj, name)
  234. except AttributeError:
  235. # damned ExtensionClass.Base, I know you're there !
  236. attach_dummy_node(node, name)
  237. continue
  238. if inspect.ismethod(member):
  239. member = six.get_method_function(member)
  240. if inspect.isfunction(member):
  241. _build_from_function(node, name, member, self._module)
  242. elif inspect.isbuiltin(member):
  243. if (not _io_discrepancy(member) and
  244. self.imported_member(node, member, name)):
  245. continue
  246. object_build_methoddescriptor(node, member, name)
  247. elif inspect.isclass(member):
  248. if self.imported_member(node, member, name):
  249. continue
  250. if member in self._done:
  251. class_node = self._done[member]
  252. if class_node not in node.locals.get(name, ()):
  253. node.add_local_node(class_node, name)
  254. else:
  255. class_node = object_build_class(node, member, name)
  256. # recursion
  257. self.object_build(class_node, member)
  258. if name == '__class__' and class_node.parent is None:
  259. class_node.parent = self._done[self._module]
  260. elif inspect.ismethoddescriptor(member):
  261. assert isinstance(member, object)
  262. object_build_methoddescriptor(node, member, name)
  263. elif inspect.isdatadescriptor(member):
  264. assert isinstance(member, object)
  265. object_build_datadescriptor(node, member, name)
  266. elif isinstance(member, _CONSTANTS):
  267. attach_const_node(node, name, member)
  268. elif inspect.isroutine(member):
  269. # This should be called for Jython, where some builtin
  270. # methods aren't caught by isbuiltin branch.
  271. _build_from_function(node, name, member, self._module)
  272. else:
  273. # create an empty node so that the name is actually defined
  274. attach_dummy_node(node, name, member)
  275. return None
  276. def imported_member(self, node, member, name):
  277. """verify this is not an imported class or handle it"""
  278. # /!\ some classes like ExtensionClass doesn't have a __module__
  279. # attribute ! Also, this may trigger an exception on badly built module
  280. # (see http://www.logilab.org/ticket/57299 for instance)
  281. try:
  282. modname = getattr(member, '__module__', None)
  283. except: # pylint: disable=bare-except
  284. _LOG.exception('unexpected error while building '
  285. 'astroid from living object')
  286. modname = None
  287. if modname is None:
  288. if (name in ('__new__', '__subclasshook__')
  289. or (name in _BUILTINS and _JYTHON)):
  290. # Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14)
  291. # >>> print object.__new__.__module__
  292. # None
  293. modname = six.moves.builtins.__name__
  294. else:
  295. attach_dummy_node(node, name, member)
  296. return True
  297. real_name = {
  298. 'gtk': 'gtk_gtk',
  299. '_io': 'io',
  300. }.get(modname, modname)
  301. if real_name != self._module.__name__:
  302. # check if it sounds valid and then add an import node, else use a
  303. # dummy node
  304. try:
  305. getattr(sys.modules[modname], name)
  306. except (KeyError, AttributeError):
  307. attach_dummy_node(node, name, member)
  308. else:
  309. attach_import_node(node, modname, name)
  310. return True
  311. return False
  312. ### astroid bootstrapping ######################################################
  313. Astroid_BUILDER = InspectBuilder()
  314. _CONST_PROXY = {}
  315. def _astroid_bootstrapping(astroid_builtin=None):
  316. """astroid boot strapping the builtins module"""
  317. # this boot strapping is necessary since we need the Const nodes to
  318. # inspect_build builtins, and then we can proxy Const
  319. if astroid_builtin is None:
  320. from six.moves import builtins
  321. astroid_builtin = Astroid_BUILDER.inspect_build(builtins)
  322. # pylint: disable=redefined-outer-name
  323. for cls, node_cls in node_classes.CONST_CLS.items():
  324. if cls is type(None):
  325. proxy = build_class('NoneType')
  326. proxy.parent = astroid_builtin
  327. elif cls is type(NotImplemented):
  328. proxy = build_class('NotImplementedType')
  329. proxy.parent = astroid_builtin
  330. else:
  331. proxy = astroid_builtin.getattr(cls.__name__)[0]
  332. if cls in (dict, list, set, tuple):
  333. node_cls._proxied = proxy
  334. else:
  335. _CONST_PROXY[cls] = proxy
  336. _astroid_bootstrapping()
  337. # TODO : find a nicer way to handle this situation;
  338. # However __proxied introduced an
  339. # infinite recursion (see https://bugs.launchpad.net/pylint/+bug/456870)
  340. def _set_proxied(const):
  341. return _CONST_PROXY[const.value.__class__]
  342. nodes.Const._proxied = property(_set_proxied)
  343. _GeneratorType = nodes.ClassDef(types.GeneratorType.__name__, types.GeneratorType.__doc__)
  344. _GeneratorType.parent = MANAGER.astroid_cache[six.moves.builtins.__name__]
  345. bases.Generator._proxied = _GeneratorType
  346. Astroid_BUILDER.object_build(bases.Generator._proxied, types.GeneratorType)
  347. _builtins = MANAGER.astroid_cache[six.moves.builtins.__name__]
  348. BUILTIN_TYPES = (types.GetSetDescriptorType, types.GeneratorType,
  349. types.MemberDescriptorType, type(None), type(NotImplemented),
  350. types.FunctionType, types.MethodType,
  351. types.BuiltinFunctionType, types.ModuleType, types.TracebackType)
  352. for _type in BUILTIN_TYPES:
  353. if _type.__name__ not in _builtins:
  354. cls = nodes.ClassDef(_type.__name__, _type.__doc__)
  355. cls.parent = MANAGER.astroid_cache[six.moves.builtins.__name__]
  356. Astroid_BUILDER.object_build(cls, _type)
  357. _builtins[_type.__name__] = cls