1
0
forked from VimPlug/jedi

Move load_module a bit around

This commit is contained in:
Dave Halter
2017-12-04 19:18:30 +01:00
parent 617b11c92b
commit 542644ad19
4 changed files with 84 additions and 78 deletions

View File

@@ -1,20 +1,9 @@
import types import types
import sys
import os
import re
from jedi._compatibility import builtins as _builtins from jedi._compatibility import builtins as _builtins
from jedi.evaluate.compiled.context import CompiledObject, CompiledName, \ from jedi.evaluate.compiled.context import CompiledObject, CompiledName, \
CompiledObjectFilter, CompiledContextName, create_from_access_path CompiledObjectFilter, CompiledContextName, create_from_access_path
from jedi.evaluate.compiled.access import create_access_path from jedi.evaluate.compiled import access
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
def builtin_from_name(evaluator, string): def builtin_from_name(evaluator, string):
@@ -33,7 +22,7 @@ def create_simple_object(evaluator, obj):
def create(evaluator, obj): def create(evaluator, obj):
return create_from_access_path( 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): def load_module(evaluator, path=None, name=None):
sys_path = list(evaluator.project.sys_path) return create_from_access_path(
if path is not None: evaluator,
dotted_path = dotted_from_fs_path(path, sys_path=sys_path) access.load_module(evaluator, path=path, name=name)
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('/', '.')

View File

@@ -4,8 +4,10 @@ import sys
import operator as op import operator as op
from collections import namedtuple from collections import namedtuple
from jedi import debug
from jedi._compatibility import unicode, is_py3, is_py34, builtins, py_version from jedi._compatibility import unicode, is_py3, is_py34, builtins, py_version
from jedi.evaluate.compiled.getattr_static import getattr_static from jedi.evaluate.compiled.getattr_static import getattr_static
from jedi.evaluate.utils import dotted_from_fs_path
MethodDescriptorType = type(str.replace) MethodDescriptorType = type(str.replace)
@@ -110,6 +112,36 @@ def create_access(evaluator, obj):
return DirectObjectAccess(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): class AccessPath(object):
def __init__(self, accesses): def __init__(self, accesses):
self.accesses = accesses self.accesses = accesses

View File

@@ -28,7 +28,7 @@ from jedi.evaluate import sys_path
from jedi.evaluate import helpers from jedi.evaluate import helpers
from jedi.evaluate import compiled from jedi.evaluate import compiled
from jedi.evaluate import analysis 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.cache import evaluator_method_cache
from jedi.evaluate.filters import AbstractNameDefinition from jedi.evaluate.filters import AbstractNameDefinition
from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS 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: if sys_path is None:
sys_path = evaluator.project.sys_path 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')) \ if path is not None and path.endswith(('.py', '.zip', '.egg')) \
and dotted_path not in settings.auto_import_modules: and dotted_path not in settings.auto_import_modules:

View File

@@ -2,10 +2,19 @@
import sys import sys
import contextlib import contextlib
import functools import functools
import re
import os
from jedi._compatibility import reraise 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 to_list(func):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
return list(func(*args, **kwargs)) return list(func(*args, **kwargs))
@@ -108,3 +117,38 @@ def indent_block(text, indention=' '):
text = text[:-1] text = text[:-1]
lines = text.split('\n') lines = text.split('\n')
return '\n'.join(map(lambda s: indention + s, lines)) + temp 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('/', '.')