From 542644ad19e1e084d079fa1be8fd36d743412818 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Mon, 4 Dec 2017 19:18:30 +0100 Subject: [PATCH] Move load_module a bit around --- jedi/evaluate/compiled/__init__.py | 82 +++--------------------------- jedi/evaluate/compiled/access.py | 32 ++++++++++++ jedi/evaluate/imports.py | 4 +- jedi/evaluate/utils.py | 44 ++++++++++++++++ 4 files changed, 84 insertions(+), 78 deletions(-) diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index 057a6b0a..0ed539e0 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -1,20 +1,9 @@ import types -import sys -import os -import re from jedi._compatibility import builtins as _builtins from jedi.evaluate.compiled.context import CompiledObject, CompiledName, \ CompiledObjectFilter, CompiledContextName, create_from_access_path -from jedi.evaluate.compiled.access import create_access_path -from jedi import debug - - -_sep = os.path.sep -if os.path.altsep is not None: - _sep += os.path.altsep -_path_re = re.compile('(?:\.[^{0}]+|[{0}]__init__\.py)$'.format(re.escape(_sep))) -del _sep +from jedi.evaluate.compiled import access def builtin_from_name(evaluator, string): @@ -33,7 +22,7 @@ def create_simple_object(evaluator, obj): def create(evaluator, obj): return create_from_access_path( - evaluator, create_access_path(evaluator, obj) + evaluator, access.create_access_path(evaluator, obj) ) @@ -58,66 +47,7 @@ def get_special_object(evaluator, identifier): def load_module(evaluator, path=None, name=None): - sys_path = list(evaluator.project.sys_path) - if path is not None: - dotted_path = dotted_from_fs_path(path, sys_path=sys_path) - else: - dotted_path = name - - temp, sys.path = sys.path, sys_path - try: - __import__(dotted_path) - except RuntimeError: - if 'PySide' in dotted_path or 'PyQt' in dotted_path: - # RuntimeError: the PyQt4.QtCore and PyQt5.QtCore modules both wrap - # the QObject class. - # See https://github.com/davidhalter/jedi/pull/483 - return None - raise - except ImportError: - # If a module is "corrupt" or not really a Python module or whatever. - debug.warning('Module %s not importable in path %s.', dotted_path, path) - return None - finally: - sys.path = temp - - # Just access the cache after import, because of #59 as well as the very - # complicated import structure of Python. - module = sys.modules[dotted_path] - - return create(evaluator, module) - - -def dotted_from_fs_path(fs_path, sys_path): - """ - Changes `/usr/lib/python3.4/email/utils.py` to `email.utils`. I.e. - compares the path with sys.path and then returns the dotted_path. If the - path is not in the sys.path, just returns None. - """ - if os.path.basename(fs_path).startswith('__init__.'): - # We are calculating the path. __init__ files are not interesting. - fs_path = os.path.dirname(fs_path) - - # prefer - # - UNIX - # /path/to/pythonX.Y/lib-dynload - # /path/to/pythonX.Y/site-packages - # - Windows - # C:\path\to\DLLs - # C:\path\to\Lib\site-packages - # over - # - UNIX - # /path/to/pythonX.Y - # - Windows - # C:\path\to\Lib - path = '' - for s in sys_path: - if (fs_path.startswith(s) and len(path) < len(s)): - path = s - - # - Window - # X:\path\to\lib-dynload/datetime.pyd => datetime - module_path = fs_path[len(path):].lstrip(os.path.sep).lstrip('/') - # - Window - # Replace like X:\path\to\something/foo/bar.py - return _path_re.sub('', module_path).replace(os.path.sep, '.').replace('/', '.') + return create_from_access_path( + evaluator, + access.load_module(evaluator, path=path, name=name) + ) diff --git a/jedi/evaluate/compiled/access.py b/jedi/evaluate/compiled/access.py index 48fcf9d6..c6459087 100644 --- a/jedi/evaluate/compiled/access.py +++ b/jedi/evaluate/compiled/access.py @@ -4,8 +4,10 @@ import sys import operator as op from collections import namedtuple +from jedi import debug from jedi._compatibility import unicode, is_py3, is_py34, builtins, py_version from jedi.evaluate.compiled.getattr_static import getattr_static +from jedi.evaluate.utils import dotted_from_fs_path MethodDescriptorType = type(str.replace) @@ -110,6 +112,36 @@ def create_access(evaluator, obj): return DirectObjectAccess(evaluator, obj) +def load_module(evaluator, path=None, name=None): + sys_path = list(evaluator.project.sys_path) + if path is not None: + dotted_path = dotted_from_fs_path(path, sys_path=sys_path) + else: + dotted_path = name + + temp, sys.path = sys.path, sys_path + try: + __import__(dotted_path) + except RuntimeError: + if 'PySide' in dotted_path or 'PyQt' in dotted_path: + # RuntimeError: the PyQt4.QtCore and PyQt5.QtCore modules both wrap + # the QObject class. + # See https://github.com/davidhalter/jedi/pull/483 + return None + raise + except ImportError: + # If a module is "corrupt" or not really a Python module or whatever. + debug.warning('Module %s not importable in path %s.', dotted_path, path) + return None + finally: + sys.path = temp + + # Just access the cache after import, because of #59 as well as the very + # complicated import structure of Python. + module = sys.modules[dotted_path] + return create_access_path(evaluator, module) + + class AccessPath(object): def __init__(self, accesses): self.accesses = accesses diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 6d2865d3..3a812ff5 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -28,7 +28,7 @@ from jedi.evaluate import sys_path from jedi.evaluate import helpers from jedi.evaluate import compiled from jedi.evaluate import analysis -from jedi.evaluate.utils import unite +from jedi.evaluate.utils import unite, dotted_from_fs_path from jedi.evaluate.cache import evaluator_method_cache from jedi.evaluate.filters import AbstractNameDefinition from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS @@ -481,7 +481,7 @@ def _load_module(evaluator, path=None, code=None, sys_path=None, parent_module=N if sys_path is None: sys_path = evaluator.project.sys_path - dotted_path = path and compiled.dotted_from_fs_path(path, sys_path) + dotted_path = path and dotted_from_fs_path(path, sys_path) if path is not None and path.endswith(('.py', '.zip', '.egg')) \ and dotted_path not in settings.auto_import_modules: diff --git a/jedi/evaluate/utils.py b/jedi/evaluate/utils.py index 7fc1c246..e00e4774 100644 --- a/jedi/evaluate/utils.py +++ b/jedi/evaluate/utils.py @@ -2,10 +2,19 @@ import sys import contextlib import functools +import re +import os from jedi._compatibility import reraise +_sep = os.path.sep +if os.path.altsep is not None: + _sep += os.path.altsep +_path_re = re.compile('(?:\.[^{0}]+|[{0}]__init__\.py)$'.format(re.escape(_sep))) +del _sep + + def to_list(func): def wrapper(*args, **kwargs): return list(func(*args, **kwargs)) @@ -108,3 +117,38 @@ def indent_block(text, indention=' '): text = text[:-1] lines = text.split('\n') return '\n'.join(map(lambda s: indention + s, lines)) + temp + + +def dotted_from_fs_path(fs_path, sys_path): + """ + Changes `/usr/lib/python3.4/email/utils.py` to `email.utils`. I.e. + compares the path with sys.path and then returns the dotted_path. If the + path is not in the sys.path, just returns None. + """ + if os.path.basename(fs_path).startswith('__init__.'): + # We are calculating the path. __init__ files are not interesting. + fs_path = os.path.dirname(fs_path) + + # prefer + # - UNIX + # /path/to/pythonX.Y/lib-dynload + # /path/to/pythonX.Y/site-packages + # - Windows + # C:\path\to\DLLs + # C:\path\to\Lib\site-packages + # over + # - UNIX + # /path/to/pythonX.Y + # - Windows + # C:\path\to\Lib + path = '' + for s in sys_path: + if (fs_path.startswith(s) and len(path) < len(s)): + path = s + + # - Window + # X:\path\to\lib-dynload/datetime.pyd => datetime + module_path = fs_path[len(path):].lstrip(os.path.sep).lstrip('/') + # - Window + # Replace like X:\path\to\something/foo/bar.py + return _path_re.sub('', module_path).replace(os.path.sep, '.').replace('/', '.')