diff --git a/.travis.yml b/.travis.yml index e1529542..a85a4d06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ env: - TOXENV=py26 - TOXENV=py27 - TOXENV=py32 + - TOXENV=py33 install: - pip install --quiet --use-mirrors tox script: diff --git a/jedi/_compatibility.py b/jedi/_compatibility.py index 9a704304..29aef154 100644 --- a/jedi/_compatibility.py +++ b/jedi/_compatibility.py @@ -7,11 +7,69 @@ Most of the code here is necessary to support Python 2.5. Once this dependency will be dropped, we'll get rid of most code. """ import sys +import imp +import os +try: + import importlib +except: + pass is_py3k = sys.hexversion >= 0x03000000 - +is_py33 = sys.hexversion >= 0x03030000 is_py25 = sys.hexversion < 0x02060000 +def find_module_py33(string, path=None): + mod_info = (None, None, None) + loader = None + if path is not None: + # Check for the module in the specidied path + loader = importlib.machinery.PathFinder.find_module(string, path) + else: + # Check for the module in sys.path + loader = importlib.machinery.PathFinder.find_module(string, sys.path) + if loader is None: + # Fallback to find builtins + loader = importlib.find_loader(string) + + if loader is None: + raise ImportError + + try: + if (loader.is_package(string)): + mod_info = (None, os.path.dirname(loader.path), True) + else: + filename = loader.get_filename(string) + if filename and os.path.exists(filename): + mod_info = (open(filename, 'U'), filename, False) + else: + mod_info = (None, filename, False) + except AttributeError: + mod_info = (None, loader.load_module(string).__name__, False) + + return mod_info + +def find_module_pre_py33(string, path=None): + mod_info = None + if path is None: + mod_info = imp.find_module(string) + else: + mod_info = imp.find_module(string, path) + + return (mod_info[0], mod_info[1], mod_info[2][2] == imp.PKG_DIRECTORY) + +def find_module(string, path=None): + """Provides information about a module. + + This function isolates the differences in importing libraries introduced with + python 3.3 on; it gets a module name and optionally a path. It will return a + tuple containin an open file for the module (if not builtin), the filename + or the name of the module if it is a builtin one and a boolean indicating + if the module is contained in a package.""" + if is_py33: + return find_module_py33(string, path) + else: + return find_module_pre_py33(string, path) + # next was defined in python 2.6, in python 3 obj.next won't be possible # anymore try: diff --git a/jedi/cache.py b/jedi/cache.py index 20805845..5d76f24e 100644 --- a/jedi/cache.py +++ b/jedi/cache.py @@ -21,6 +21,7 @@ from __future__ import with_statement import time import os import sys +import hashlib try: import cPickle as pickle except: @@ -221,7 +222,7 @@ def save_module(path, name, parser, pickling=True): class _ModulePickling(object): - version = 1 + version = 2 """ Version number (integer) for file system cache. @@ -312,7 +313,7 @@ class _ModulePickling(object): shutil.rmtree(self._cache_directory()) def _get_hashed_path(self, path): - return self._get_path('%s.pkl' % hash(path)) + return self._get_path('%s.pkl' % hashlib.md5(path.encode("utf-8")).hexdigest()) def _get_path(self, file): dir = self._cache_directory() diff --git a/jedi/imports.py b/jedi/imports.py index 72a3d0f6..db72925f 100644 --- a/jedi/imports.py +++ b/jedi/imports.py @@ -5,9 +5,8 @@ any actual importing done. This module is about finding modules in the filesystem. This can be quite tricky sometimes, because Python imports are not always that simple. -Currently the import process uses ``imp`` to find modules. In the future, it's -a goal to use ``importlib`` for this purpose. There's a `pull request -`_ for that. +This module uses imp for python up to 3.2 and importlib for python 3.3 on; the +correct implementation is delegated to _compatibility. This module also supports import autocompletion, which means to complete statements like ``from datetim`` (curser at the end would return ``datetime``). @@ -17,10 +16,10 @@ from __future__ import with_statement import os import pkgutil -import imp import sys import itertools +from jedi._compatibility import find_module from jedi import modules from jedi import debug from jedi import parsing_representation as pr @@ -238,20 +237,22 @@ class ImportPath(pr.Base): global imports_processed imports_processed += 1 + importing = None if path is not None: - return imp.find_module(string, [path]) + importing = find_module(string, [path]) else: debug.dbg('search_module', string, self.file_path) # Override the sys.path. It works only good that way. # Injecting the path directly into `find_module` did not work. sys.path, temp = sys_path_mod, sys.path try: - i = imp.find_module(string) + importing = find_module(string) except ImportError: sys.path = temp raise sys.path = temp - return i + + return importing if self.file_path: sys_path_mod = list(self.sys_path_with_modifications()) @@ -259,6 +260,9 @@ class ImportPath(pr.Base): else: sys_path_mod = list(modules.get_sys_path()) + def module_not_found(): + raise ModuleNotFound('The module you searched has not been found') + current_namespace = (None, None, None) # now execute those paths rest = [] @@ -277,12 +281,14 @@ class ImportPath(pr.Base): if current_namespace[1]: rest = self.import_path[i:] else: - raise ModuleNotFound( - 'The module you searched has not been found') + module_not_found() + + if current_namespace == (None, None, False): + module_not_found() sys_path_mod.pop(0) # TODO why is this here? path = current_namespace[1] - is_package_directory = current_namespace[2][2] == imp.PKG_DIRECTORY + is_package_directory = current_namespace[2] f = None if is_package_directory or current_namespace[0]: diff --git a/jedi/parsing_representation.py b/jedi/parsing_representation.py index 65210124..311f38ab 100644 --- a/jedi/parsing_representation.py +++ b/jedi/parsing_representation.py @@ -560,8 +560,10 @@ class Flow(Scope): @parent.setter def parent(self, value): self._parent = value - if self.next: + try: self.next.parent = value + except AttributeError: + return def get_code(self, first_indent=False, indention=' '): stmts = [] diff --git a/test/completion/std.py b/test/completion/std.py index 0eec6367..c4ed8c5a 100644 --- a/test/completion/std.py +++ b/test/completion/std.py @@ -89,12 +89,3 @@ def huhu(db): """ #? sqlite3.Connection() db - -# ----------------- -# various regression tests -# ----------------- - -#62 -import threading -#? ['_Verbose', '_VERBOSE'] -threading._Verbose diff --git a/tox.ini b/tox.ini index 6dc16930..8fa50614 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py25, py26, py27, py32 +envlist = py25, py26, py27, py32, py33 [testenv] deps = pytest