diff --git a/jedi/_compatibility.py b/jedi/_compatibility.py index 9cce90a5..423c6374 100644 --- a/jedi/_compatibility.py +++ b/jedi/_compatibility.py @@ -31,12 +31,19 @@ def find_module_py33(string, path=None): module_file = None else: module_path = loader.get_filename(string) - module_file = open(module_path) + mode = 'rb' if module_path.endswith(".pyc") else 'r' + module_file = open(module_path, mode) except AttributeError: - # is builtin module - module_path = string - module_file = None - is_package = False + # ExtensionLoader has not attribute get_filename, instead it has a + # path attribute that we can use to retrieve the module path + try: + module_path = loader.path + module_file = open(loader.path, 'rb') + except AttributeError: + module_path = string + module_file = None + finally: + is_package = False return module_file, module_path, is_package diff --git a/pytest.ini b/pytest.ini index fa132557..0d73061d 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,7 +2,7 @@ addopts = --doctest-modules # Ignore broken files in blackbox test directories -norecursedirs = .* docs completion refactor absolute_import namespace_package scripts +norecursedirs = .* docs completion refactor absolute_import namespace_package scripts extensions # Activate `clean_jedi_cache` fixture for all tests. This should be # fine as long as we are using `clean_jedi_cache` as a session scoped diff --git a/test/extensions/README.rst b/test/extensions/README.rst new file mode 100644 index 00000000..5f0b69ff --- /dev/null +++ b/test/extensions/README.rst @@ -0,0 +1,10 @@ +This directory contains pre-compiled extensions modules used to test completions +for compiled modules on Travis-CI (Ubuntu 12.04 64bit). + +To build the extensions modules, run:: + + python setup.py build_ext -i + + +Then move the compiled modules to their testing package ( ./**compiledXX**, where XX is the +python version used to run setup.py). \ No newline at end of file diff --git a/test/extensions/compiled.pyx b/test/extensions/compiled.pyx new file mode 100644 index 00000000..1e7c6907 --- /dev/null +++ b/test/extensions/compiled.pyx @@ -0,0 +1,6 @@ +cdef class Foo: + pass + + +cdef class Bar: + pass diff --git a/test/extensions/compiled27/__init__.py b/test/extensions/compiled27/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/extensions/compiled27/compiled.so b/test/extensions/compiled27/compiled.so new file mode 100755 index 00000000..becbfb56 Binary files /dev/null and b/test/extensions/compiled27/compiled.so differ diff --git a/test/extensions/compiled32/__init__.py b/test/extensions/compiled32/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/extensions/compiled32/compiled.so b/test/extensions/compiled32/compiled.so new file mode 100755 index 00000000..df34cfa0 Binary files /dev/null and b/test/extensions/compiled32/compiled.so differ diff --git a/test/extensions/setup.py b/test/extensions/setup.py new file mode 100644 index 00000000..a13237d2 --- /dev/null +++ b/test/extensions/setup.py @@ -0,0 +1,6 @@ +from distutils.core import setup +from Cython.Build import cythonize + +setup( + ext_modules=cythonize("compiled.pyx") +) diff --git a/test/test_compiled.py b/test/test_compiled.py new file mode 100644 index 00000000..c783a3e2 --- /dev/null +++ b/test/test_compiled.py @@ -0,0 +1,19 @@ +""" +Test compiled module +""" +import os +import platform +import sys +import jedi + +from .helpers import cwd_at + + +@cwd_at('test/extensions') +def test_compiled(): + if platform.architecture()[0] == '64bit': + package_name = "compiled%s%s" % sys.version_info[:2] + sys.path.insert(0, os.getcwd()) + if os.path.exists(package_name): + s = jedi.Script("from %s import compiled; compiled." % package_name) + assert len(s.completions()) >= 2 diff --git a/test/test_pyc.py b/test/test_pyc.py new file mode 100644 index 00000000..610b9e06 --- /dev/null +++ b/test/test_pyc.py @@ -0,0 +1,56 @@ +""" +Test completions from *.pyc files: + + - generate a dummy python module + - compile the dummy module to generate a *.pyc + - delete the pure python dummy module + - try jedi on the generated *.pyc +""" +import compileall +import os +import shutil +import sys + +import jedi + + +SRC = """class Foo: + pass + +class Bar: + pass +""" + + +def generate_pyc(): + os.mkdir("dummy_package") + with open("dummy_package/__init__.py", 'w'): + pass + with open("dummy_package/dummy.py", 'w') as f: + f.write(SRC) + compileall.compile_file("dummy_package/dummy.py") + os.remove("dummy_package/dummy.py") + + if sys.version_info[0] == 3: + # Python3 specific: + # To import pyc modules, we must move them out of the __pycache__ + # directory and rename them to remove ".cpython-%s%d" + # see: http://stackoverflow.com/questions/11648440/python-does-not-detect-pyc-files + for f in os.listdir("dummy_package/__pycache__"): + dst = f.replace('.cpython-%s%s' % sys.version_info[:2], "") + dst = os.path.join("dummy_package", dst) + shutil.copy(os.path.join("dummy_package/__pycache__", f), dst) + + +def test_pyc(): + """ + The list of completion must be greater than 2. + """ + generate_pyc() + s = jedi.Script("from dummy_package import dummy; dummy.") + assert len(s.completions()) >= 2 + shutil.rmtree("dummy_package") + + +if __name__ == "__main__": + test_pyc()