diff --git a/jedi/evaluate.py b/jedi/evaluate.py index 551852d7..c76bbb27 100644 --- a/jedi/evaluate.py +++ b/jedi/evaluate.py @@ -68,11 +68,11 @@ backtracking algorithm. .. todo:: nonlocal statement, needed or can be ignored? (py3k) """ -from _compatibility import next, hasattr, is_py3k, unicode, reraise import sys import itertools +from jedi._compatibility import next, hasattr, is_py3k, unicode, reraise from jedi import common from jedi import cache from jedi import parsing_representation as pr diff --git a/jedi/evaluate_representation.py b/jedi/evaluate_representation.py index 99fded99..f5c6e991 100644 --- a/jedi/evaluate_representation.py +++ b/jedi/evaluate_representation.py @@ -12,7 +12,7 @@ they change classes in Python 3. import copy import itertools -from _compatibility import property, use_metaclass, next, hasattr +from jedi._compatibility import property, use_metaclass, next, hasattr from jedi import parsing_representation as pr from jedi import cache from jedi import helpers diff --git a/jedi/keywords.py b/jedi/keywords.py index 07461bb7..5495ae32 100644 --- a/jedi/keywords.py +++ b/jedi/keywords.py @@ -2,7 +2,7 @@ import pydoc import keyword from jedi._compatibility import is_py3k -from jedi import builtin +import builtin try: from pydoc_data import topics as pydoc_topics diff --git a/jedi/modules.py b/jedi/modules.py index b6c8f1e8..9277e742 100644 --- a/jedi/modules.py +++ b/jedi/modules.py @@ -15,13 +15,12 @@ Apart from those classes there's a ``sys.path`` fetching function, as well as """ from __future__ import with_statement -from _compatibility import exec_function, unicode, is_py25, literal_eval - import re import tokenize import sys import os +from jedi._compatibility import exec_function, unicode, is_py25, literal_eval from jedi import cache from jedi import parsing from jedi import parsing_representation as pr diff --git a/jedi/parsing.py b/jedi/parsing.py index 17ca7d49..69607fec 100644 --- a/jedi/parsing.py +++ b/jedi/parsing.py @@ -15,11 +15,11 @@ within the statement. This lowers memory usage and cpu time and reduces the complexity of the ``Parser`` (there's another parser sitting inside ``Statement``, which produces ``Array`` and ``Call``). """ -from _compatibility import next, StringIO import tokenize import keyword +from jedi._compatibility import next, StringIO from jedi import debug from jedi import common from jedi import parsing_representation as pr diff --git a/test/test_regression.py b/test/test_regression.py index 6ed6a9f9..3952e6b7 100755 --- a/test/test_regression.py +++ b/test/test_regression.py @@ -16,7 +16,7 @@ from .base import TestBase, unittest, cwd_at import jedi from jedi._compatibility import is_py25, utf8, unicode from jedi import api -from jedi import api_classes +api_classes = api.api_classes #jedi.set_debug_function(jedi.debug.print_to_stdout) @@ -534,5 +534,45 @@ def test_settings_module(): assert cache.settings is settings +def test_no_duplicate_modules(): + """ + Make sure that import hack works as expected. + + Jedi does an import hack (see: jedi/__init__.py) to have submodules + with circular dependencies. The modules in this circular dependency + "loop" must be imported by ``import `` rather than normal + ``from jedi import `` (or ``from . jedi ...``). This test + make sure that this is satisfied. + + See also: + + - `#160 `_ + - `#161 `_ + """ + import sys + jedipath = os.path.dirname(os.path.abspath(jedi.__file__)) + + def is_submodule(m): + try: + filepath = m.__file__ + except AttributeError: + return False + return os.path.abspath(filepath).startswith(jedipath) + + modules = list(filter(is_submodule, sys.modules.values())) + top_modules = [m for m in modules if not m.__name__.startswith('jedi.')] + for m in modules: + if m is jedi: + # py.test automatically improts `jedi.*` when --doctest-modules + # is given. So this test cannot succeeds. + continue + for tm in top_modules: + try: + imported = getattr(m, tm.__name__) + except AttributeError: + continue + assert imported is tm + + if __name__ == '__main__': unittest.main()