PageRenderTime 71ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/jedi/evaluate/compiled/__init__.py

https://gitlab.com/18runt88/jedi
Python | 531 lines | 466 code | 30 blank | 35 comment | 35 complexity | f0a1390f6d6773c31378d1c1cf69b880 MD5 | raw file
  1. """
  2. Imitate the parser representation.
  3. """
  4. import inspect
  5. import re
  6. import sys
  7. import os
  8. from functools import partial
  9. from jedi._compatibility import builtins as _builtins, unicode
  10. from jedi import debug
  11. from jedi.cache import underscore_memoization, memoize_method
  12. from jedi.evaluate.sys_path import get_sys_path
  13. from jedi.parser.tree import Param, Base, Operator, zero_position_modifier
  14. from jedi.evaluate.helpers import FakeName
  15. from . import fake
  16. _sep = os.path.sep
  17. if os.path.altsep is not None:
  18. _sep += os.path.altsep
  19. _path_re = re.compile('(?:\.[^{0}]+|[{0}]__init__\.py)$'.format(re.escape(_sep)))
  20. del _sep
  21. class CheckAttribute(object):
  22. """Raises an AttributeError if the attribute X isn't available."""
  23. def __init__(self, func):
  24. self.func = func
  25. # Remove the py in front of e.g. py__call__.
  26. self.check_name = func.__name__[2:]
  27. def __get__(self, instance, owner):
  28. # This might raise an AttributeError. That's wanted.
  29. getattr(instance.obj, self.check_name)
  30. return partial(self.func, instance)
  31. class CompiledObject(Base):
  32. # comply with the parser
  33. start_pos = 0, 0
  34. path = None # modules have this attribute - set it to None.
  35. used_names = {} # To be consistent with modules.
  36. def __init__(self, obj, parent=None):
  37. self.obj = obj
  38. self.parent = parent
  39. @property
  40. def py__call__(self):
  41. def actual(evaluator, params):
  42. if inspect.isclass(self.obj):
  43. from jedi.evaluate.representation import Instance
  44. return [Instance(evaluator, self, params)]
  45. else:
  46. return list(self._execute_function(evaluator, params))
  47. # Might raise an AttributeError, which is intentional.
  48. self.obj.__call__
  49. return actual
  50. @CheckAttribute
  51. def py__class__(self, evaluator):
  52. return CompiledObject(self.obj.__class__, parent=self.parent)
  53. @CheckAttribute
  54. def py__mro__(self, evaluator):
  55. return tuple(create(evaluator, cls, self.parent) for cls in self.obj.__mro__)
  56. @CheckAttribute
  57. def py__bases__(self, evaluator):
  58. return tuple(create(evaluator, cls) for cls in self.obj.__bases__)
  59. def py__bool__(self):
  60. return bool(self.obj)
  61. def py__file__(self):
  62. return self.obj.__file__
  63. def is_class(self):
  64. return inspect.isclass(self.obj)
  65. @property
  66. def doc(self):
  67. return inspect.getdoc(self.obj) or ''
  68. @property
  69. def params(self):
  70. params_str, ret = self._parse_function_doc()
  71. tokens = params_str.split(',')
  72. if inspect.ismethoddescriptor(self._cls().obj):
  73. tokens.insert(0, 'self')
  74. params = []
  75. for p in tokens:
  76. parts = [FakeName(part) for part in p.strip().split('=')]
  77. if len(parts) > 1:
  78. parts.insert(1, Operator(zero_position_modifier, '=', (0, 0)))
  79. params.append(Param(parts, self))
  80. return params
  81. def __repr__(self):
  82. return '<%s: %s>' % (type(self).__name__, repr(self.obj))
  83. @underscore_memoization
  84. def _parse_function_doc(self):
  85. if self.doc is None:
  86. return '', ''
  87. return _parse_function_doc(self.doc)
  88. def api_type(self):
  89. if fake.is_class_instance(self.obj):
  90. return 'instance'
  91. cls = self._cls().obj
  92. if inspect.isclass(cls):
  93. return 'class'
  94. elif inspect.ismodule(cls):
  95. return 'module'
  96. elif inspect.isbuiltin(cls) or inspect.ismethod(cls) \
  97. or inspect.ismethoddescriptor(cls):
  98. return 'function'
  99. @property
  100. def type(self):
  101. """Imitate the tree.Node.type values."""
  102. cls = self._cls().obj
  103. if inspect.isclass(cls):
  104. return 'classdef'
  105. elif inspect.ismodule(cls):
  106. return 'file_input'
  107. elif inspect.isbuiltin(cls) or inspect.ismethod(cls) \
  108. or inspect.ismethoddescriptor(cls):
  109. return 'funcdef'
  110. @underscore_memoization
  111. def _cls(self):
  112. # Ensures that a CompiledObject is returned that is not an instance (like list)
  113. if fake.is_class_instance(self.obj):
  114. try:
  115. c = self.obj.__class__
  116. except AttributeError:
  117. # happens with numpy.core.umath._UFUNC_API (you get it
  118. # automatically by doing `import numpy`.
  119. c = type(None)
  120. return CompiledObject(c, self.parent)
  121. return self
  122. @property
  123. def names_dict(self):
  124. # For compatibility with `representation.Class`.
  125. return self.names_dicts(False)[0]
  126. def names_dicts(self, search_global, is_instance=False):
  127. return self._names_dict_ensure_one_dict(is_instance)
  128. @memoize_method
  129. def _names_dict_ensure_one_dict(self, is_instance):
  130. """
  131. search_global shouldn't change the fact that there's one dict, this way
  132. there's only one `object`.
  133. """
  134. return [LazyNamesDict(self._cls(), is_instance)]
  135. def get_subscope_by_name(self, name):
  136. if name in dir(self._cls().obj):
  137. return CompiledName(self._cls(), name).parent
  138. else:
  139. raise KeyError("CompiledObject doesn't have an attribute '%s'." % name)
  140. def get_index_types(self, evaluator, index_array=()):
  141. # If the object doesn't have `__getitem__`, just raise the
  142. # AttributeError.
  143. if not hasattr(self.obj, '__getitem__'):
  144. debug.warning('Tried to call __getitem__ on non-iterable.')
  145. return []
  146. if type(self.obj) not in (str, list, tuple, unicode, bytes, bytearray, dict):
  147. # Get rid of side effects, we won't call custom `__getitem__`s.
  148. return []
  149. result = []
  150. from jedi.evaluate.iterable import create_indexes_or_slices
  151. for typ in create_indexes_or_slices(evaluator, index_array):
  152. index = None
  153. try:
  154. index = typ.obj
  155. new = self.obj[index]
  156. except (KeyError, IndexError, TypeError, AttributeError):
  157. # Just try, we don't care if it fails, except for slices.
  158. if isinstance(index, slice):
  159. result.append(self)
  160. else:
  161. result.append(CompiledObject(new))
  162. if not result:
  163. try:
  164. for obj in self.obj:
  165. result.append(CompiledObject(obj))
  166. except TypeError:
  167. pass # self.obj maynot have an __iter__ method.
  168. return result
  169. @property
  170. def name(self):
  171. # might not exist sometimes (raises AttributeError)
  172. return FakeName(self._cls().obj.__name__, self)
  173. def _execute_function(self, evaluator, params):
  174. if self.type != 'funcdef':
  175. return
  176. for name in self._parse_function_doc()[1].split():
  177. try:
  178. bltn_obj = _create_from_name(builtin, builtin, name)
  179. except AttributeError:
  180. continue
  181. else:
  182. if isinstance(bltn_obj, CompiledObject) and bltn_obj.obj is None:
  183. # We want everything except None.
  184. continue
  185. for result in evaluator.execute(bltn_obj, params):
  186. yield result
  187. @property
  188. @underscore_memoization
  189. def subscopes(self):
  190. """
  191. Returns only the faked scopes - the other ones are not important for
  192. internal analysis.
  193. """
  194. module = self.get_parent_until()
  195. faked_subscopes = []
  196. for name in dir(self._cls().obj):
  197. f = fake.get_faked(module.obj, self.obj, name)
  198. if f:
  199. f.parent = self
  200. faked_subscopes.append(f)
  201. return faked_subscopes
  202. def is_scope(self):
  203. return True
  204. def get_self_attributes(self):
  205. return [] # Instance compatibility
  206. def get_imports(self):
  207. return [] # Builtins don't have imports
  208. class LazyNamesDict(object):
  209. """
  210. A names_dict instance for compiled objects, resembles the parser.tree.
  211. """
  212. def __init__(self, compiled_obj, is_instance):
  213. self._compiled_obj = compiled_obj
  214. self._is_instance = is_instance
  215. def __iter__(self):
  216. return (v[0].value for v in self.values())
  217. @memoize_method
  218. def __getitem__(self, name):
  219. try:
  220. getattr(self._compiled_obj.obj, name)
  221. except AttributeError:
  222. raise KeyError('%s in %s not found.' % (name, self._compiled_obj))
  223. return [CompiledName(self._compiled_obj, name)]
  224. def values(self):
  225. obj = self._compiled_obj.obj
  226. values = []
  227. for name in dir(obj):
  228. try:
  229. values.append(self[name])
  230. except KeyError:
  231. # The dir function can be wrong.
  232. pass
  233. # dir doesn't include the type names.
  234. if not inspect.ismodule(obj) and obj != type and not self._is_instance:
  235. values += _type_names_dict.values()
  236. return values
  237. class CompiledName(FakeName):
  238. def __init__(self, obj, name):
  239. super(CompiledName, self).__init__(name)
  240. self._obj = obj
  241. self.name = name
  242. def __repr__(self):
  243. try:
  244. name = self._obj.name # __name__ is not defined all the time
  245. except AttributeError:
  246. name = None
  247. return '<%s: (%s).%s>' % (type(self).__name__, name, self.name)
  248. def is_definition(self):
  249. return True
  250. @property
  251. @underscore_memoization
  252. def parent(self):
  253. module = self._obj.get_parent_until()
  254. return _create_from_name(module, self._obj, self.name)
  255. @parent.setter
  256. def parent(self, value):
  257. pass # Just ignore this, FakeName tries to overwrite the parent attribute.
  258. def dotted_from_fs_path(fs_path, sys_path=None):
  259. """
  260. Changes `/usr/lib/python3.4/email/utils.py` to `email.utils`. I.e.
  261. compares the path with sys.path and then returns the dotted_path. If the
  262. path is not in the sys.path, just returns None.
  263. """
  264. if sys_path is None:
  265. sys_path = get_sys_path()
  266. if os.path.basename(fs_path).startswith('__init__.'):
  267. # We are calculating the path. __init__ files are not interesting.
  268. fs_path = os.path.dirname(fs_path)
  269. # prefer
  270. # - UNIX
  271. # /path/to/pythonX.Y/lib-dynload
  272. # /path/to/pythonX.Y/site-packages
  273. # - Windows
  274. # C:\path\to\DLLs
  275. # C:\path\to\Lib\site-packages
  276. # over
  277. # - UNIX
  278. # /path/to/pythonX.Y
  279. # - Windows
  280. # C:\path\to\Lib
  281. path = ''
  282. for s in sys_path:
  283. if (fs_path.startswith(s) and len(path) < len(s)):
  284. path = s
  285. return _path_re.sub('', fs_path[len(path):].lstrip(os.path.sep)).replace(os.path.sep, '.')
  286. def load_module(path=None, name=None):
  287. if path is not None:
  288. dotted_path = dotted_from_fs_path(path)
  289. else:
  290. dotted_path = name
  291. sys_path = get_sys_path()
  292. if dotted_path is None:
  293. p, _, dotted_path = path.partition(os.path.sep)
  294. sys_path.insert(0, p)
  295. temp, sys.path = sys.path, sys_path
  296. try:
  297. __import__(dotted_path)
  298. except RuntimeError:
  299. if 'PySide' in dotted_path or 'PyQt' in dotted_path:
  300. # RuntimeError: the PyQt4.QtCore and PyQt5.QtCore modules both wrap
  301. # the QObject class.
  302. # See https://github.com/davidhalter/jedi/pull/483
  303. return None
  304. raise
  305. except ImportError:
  306. # If a module is "corrupt" or not really a Python module or whatever.
  307. debug.warning('Module %s not importable.', path)
  308. return None
  309. finally:
  310. sys.path = temp
  311. # Just access the cache after import, because of #59 as well as the very
  312. # complicated import structure of Python.
  313. module = sys.modules[dotted_path]
  314. return CompiledObject(module)
  315. docstr_defaults = {
  316. 'floating point number': 'float',
  317. 'character': 'str',
  318. 'integer': 'int',
  319. 'dictionary': 'dict',
  320. 'string': 'str',
  321. }
  322. def _parse_function_doc(doc):
  323. """
  324. Takes a function and returns the params and return value as a tuple.
  325. This is nothing more than a docstring parser.
  326. TODO docstrings like utime(path, (atime, mtime)) and a(b [, b]) -> None
  327. TODO docstrings like 'tuple of integers'
  328. """
  329. # parse round parentheses: def func(a, (b,c))
  330. try:
  331. count = 0
  332. start = doc.index('(')
  333. for i, s in enumerate(doc[start:]):
  334. if s == '(':
  335. count += 1
  336. elif s == ')':
  337. count -= 1
  338. if count == 0:
  339. end = start + i
  340. break
  341. param_str = doc[start + 1:end]
  342. except (ValueError, UnboundLocalError):
  343. # ValueError for doc.index
  344. # UnboundLocalError for undefined end in last line
  345. debug.dbg('no brackets found - no param')
  346. end = 0
  347. param_str = ''
  348. else:
  349. # remove square brackets, that show an optional param ( = None)
  350. def change_options(m):
  351. args = m.group(1).split(',')
  352. for i, a in enumerate(args):
  353. if a and '=' not in a:
  354. args[i] += '=None'
  355. return ','.join(args)
  356. while True:
  357. param_str, changes = re.subn(r' ?\[([^\[\]]+)\]',
  358. change_options, param_str)
  359. if changes == 0:
  360. break
  361. param_str = param_str.replace('-', '_') # see: isinstance.__doc__
  362. # parse return value
  363. r = re.search('-[>-]* ', doc[end:end + 7])
  364. if r is None:
  365. ret = ''
  366. else:
  367. index = end + r.end()
  368. # get result type, which can contain newlines
  369. pattern = re.compile(r'(,\n|[^\n-])+')
  370. ret_str = pattern.match(doc, index).group(0).strip()
  371. # New object -> object()
  372. ret_str = re.sub(r'[nN]ew (.*)', r'\1()', ret_str)
  373. ret = docstr_defaults.get(ret_str, ret_str)
  374. return param_str, ret
  375. class Builtin(CompiledObject):
  376. @memoize_method
  377. def get_by_name(self, name):
  378. return self.names_dict[name][0].parent
  379. def _a_generator(foo):
  380. """Used to have an object to return for generators."""
  381. yield 42
  382. yield foo
  383. def _create_from_name(module, parent, name):
  384. faked = fake.get_faked(module.obj, parent.obj, name)
  385. # only functions are necessary.
  386. if faked is not None:
  387. faked.parent = parent
  388. return faked
  389. try:
  390. obj = getattr(parent.obj, name)
  391. except AttributeError:
  392. # happens e.g. in properties of
  393. # PyQt4.QtGui.QStyleOptionComboBox.currentText
  394. # -> just set it to None
  395. obj = None
  396. return CompiledObject(obj, parent)
  397. builtin = Builtin(_builtins)
  398. magic_function_class = CompiledObject(type(load_module), parent=builtin)
  399. generator_obj = CompiledObject(_a_generator(1.0))
  400. _type_names_dict = builtin.get_by_name('type').names_dict
  401. none_obj = builtin.get_by_name('None')
  402. false_obj = builtin.get_by_name('False')
  403. true_obj = builtin.get_by_name('True')
  404. object_obj = builtin.get_by_name('object')
  405. def keyword_from_value(obj):
  406. if obj is None:
  407. return none_obj
  408. elif obj is False:
  409. return false_obj
  410. elif obj is True:
  411. return true_obj
  412. else:
  413. raise NotImplementedError
  414. def compiled_objects_cache(func):
  415. def wrapper(evaluator, obj, parent=builtin, module=None):
  416. # Do a very cheap form of caching here.
  417. key = id(obj), id(parent), id(module)
  418. try:
  419. return evaluator.compiled_cache[key][0]
  420. except KeyError:
  421. result = func(evaluator, obj, parent, module)
  422. # Need to cache all of them, otherwise the id could be overwritten.
  423. evaluator.compiled_cache[key] = result, obj, parent, module
  424. return result
  425. return wrapper
  426. @compiled_objects_cache
  427. def create(evaluator, obj, parent=builtin, module=None):
  428. """
  429. A very weird interface class to this module. The more options provided the
  430. more acurate loading compiled objects is.
  431. """
  432. if not inspect.ismodule(obj):
  433. faked = fake.get_faked(module and module.obj, obj)
  434. if faked is not None:
  435. faked.parent = parent
  436. return faked
  437. try:
  438. if parent == builtin and obj.__module__ in ('builtins', '__builtin__'):
  439. return builtin.get_by_name(obj.__name__)
  440. except AttributeError:
  441. pass
  442. return CompiledObject(obj, parent)