diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index f051583f..e8a9a016 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -208,7 +208,31 @@ class CompiledName(FakeName): pass # Just ignore this, FakeName tries to overwrite the parent attribute. +def dotted_from_fs_path(fs_path, sys_path=None): + """ + 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. + """ + sep = os.path.sep + shortest = None + if sys_path is None: + sys_path = get_sys_path() + for s in sys_path: + if fs_path.startswith(s): + path = fs_path[len(s):].strip(sep) + path = re.sub('\.[^%s]*|%s__init__.py$' % (sep, sep), '', path) + dotted = path.split(sep) + dotted = '.'.join(dotted) + # At this point dotted could be both `lib-dynload.datetime` and + # `datetime`. The shorter one is typically the module we want. + if shortest is None or len(shortest) > len(dotted): + shortest = dotted + return shortest + + def load_module(path, name): + """ if not name: name = os.path.basename(path) name = name.rpartition('.')[0] # cut file type (normally .so) @@ -230,17 +254,22 @@ def load_module(path, name): else: path = os.path.dirname(path) + """ + if path is not None: + dotted_path = dotted_from_fs_path(path) + else: + dotted_path = name + sys_path = get_sys_path() - if path: - sys_path.insert(0, path) + if dotted_path is None: + p, _, dotted_path = path.partition(os.path.sep) + sys_path.insert(0, p) temp, sys.path = sys.path, sys_path - try: - module = __import__(name, {}, {}, dot_path[:-1]) - except AttributeError: - # use sys.modules, because you cannot access some modules - # directly. -> github issue #59 - module = sys.modules[name] + __import__(dotted_path) + # Just access the cache after import, because of #59 as well as the very + # complicated import structure of Python. + module = sys.modules[dotted_path] sys.path = temp return CompiledObject(module) diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 5592403e..633a7a44 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -12,7 +12,6 @@ This module also supports import autocompletion, which means to complete statements like ``from datetim`` (curser at the end would return ``datetime``). """ import os -import re import pkgutil import sys from itertools import chain @@ -511,7 +510,7 @@ def remove_star_imports(evaluator, scope, ignored_modules=()): def _load_module(path=None, source=None, name=None, sys_path=None): def load(source): - dotted_path = path and _reverse_fs_path(path, sys_path) + dotted_path = path and compiled.dotted_from_fs_path(path, sys_path) if path is not None and path.endswith('.py') \ and not dotted_path in settings.auto_import_modules: if source is None: @@ -528,26 +527,6 @@ def _load_module(path=None, source=None, name=None, sys_path=None): return load(source) if cached is None else cached.module -def _reverse_fs_path(fs_path, sys_path=None): - """ - 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. - """ - sep = os.path.sep - shortest = None - sys_path = get_sys_path() - for s in sys_path: - if fs_path.startswith(s): - path = fs_path[len(s):].strip(sep) - path = re.sub('\.[^%s]*|%s__init__.py$' % (sep, sep), '', path) - dotted = '.'.join(path.split(sep)) - # At this point dotted could be both `lib-dynload.datetime` and - # `datetime`. The shorter one is typically the module we want. - if shortest is None or len(shortest) > len(dotted): - shortest = dotted - return shortest - def get_modules_containing_name(mods, name): """ Search a name in the directories of modules. diff --git a/jedi/settings.py b/jedi/settings.py index a0839e99..0daa3fad 100644 --- a/jedi/settings.py +++ b/jedi/settings.py @@ -180,7 +180,9 @@ dynamic_flow_information = True Check for `isinstance` and other information to infer a type. """ -auto_import_modules = ['hashlib'] +auto_import_modules = [ + 'hashlib', # setattr +] """ Modules that are not analyzed but imported, although they contain Python code. This improves autocompletion for libraries that use ``setattr`` or diff --git a/test/completion/std.py b/test/completion/std.py index 9372f481..f7ee7d14 100644 --- a/test/completion/std.py +++ b/test/completion/std.py @@ -114,3 +114,12 @@ def huhu(db): """ #? sqlite3.Connection() db + +# ----------------- +# hashlib +# ----------------- + +import hashlib + +#? ['md5'] +hashlib.md5