1""" 2Tests of various import related things that could not be tested with "Black Box 3Tests". 4""" 5 6import os 7from pathlib import Path 8 9import pytest 10 11import jedi 12from jedi.file_io import FileIO 13from jedi.inference import compiled 14from jedi.inference import imports 15from jedi.api.project import Project 16from jedi.inference.gradual.conversion import _stub_to_python_value_set 17from jedi.inference.references import get_module_contexts_containing_name 18from ..helpers import get_example_dir, test_dir, test_dir_project, root_dir 19from jedi.inference.compiled.subprocess.functions import _find_module_py33, _find_module 20 21THIS_DIR = os.path.dirname(__file__) 22 23 24def test_find_module_basic(): 25 """Needs to work like the old find_module.""" 26 assert _find_module_py33('_io') == (None, False) 27 with pytest.raises(ImportError): 28 assert _find_module_py33('_DOESNTEXIST_') == (None, None) 29 30 31def test_find_module_package(): 32 file_io, is_package = _find_module('json') 33 assert file_io.path.parts[-2:] == ('json', '__init__.py') 34 assert is_package is True 35 36 37def test_find_module_not_package(): 38 file_io, is_package = _find_module('io') 39 assert file_io.path.name == 'io.py' 40 assert is_package is False 41 42 43pkg_zip_path = Path(get_example_dir('zipped_imports', 'pkg.zip')) 44 45 46def test_find_module_package_zipped(Script, inference_state, environment): 47 sys_path = environment.get_sys_path() + [str(pkg_zip_path)] 48 49 project = Project('.', sys_path=sys_path) 50 script = Script('import pkg; pkg.mod', project=project) 51 assert len(script.complete()) == 1 52 53 file_io, is_package = inference_state.compiled_subprocess.get_module_info( 54 sys_path=sys_path, 55 string='pkg', 56 full_name='pkg' 57 ) 58 assert file_io is not None 59 assert file_io.path.parts[-3:] == ('pkg.zip', 'pkg', '__init__.py') 60 assert file_io._zip_path.name == 'pkg.zip' 61 assert is_package is True 62 63 64@pytest.mark.parametrize( 65 'code, file, package, path', [ 66 ('import pkg', '__init__.py', 'pkg', 'pkg'), 67 ('import pkg', '__init__.py', 'pkg', 'pkg'), 68 69 ('from pkg import module', 'module.py', 'pkg', None), 70 ('from pkg.module', 'module.py', 'pkg', None), 71 72 ('from pkg import nested', os.path.join('nested', '__init__.py'), 73 'pkg.nested', os.path.join('pkg', 'nested')), 74 ('from pkg.nested', os.path.join('nested', '__init__.py'), 75 'pkg.nested', os.path.join('pkg', 'nested')), 76 77 ('from pkg.nested import nested_module', 78 os.path.join('nested', 'nested_module.py'), 'pkg.nested', None), 79 ('from pkg.nested.nested_module', 80 os.path.join('nested', 'nested_module.py'), 'pkg.nested', None), 81 82 ('from pkg.namespace import namespace_module', 83 os.path.join('namespace', 'namespace_module.py'), 'pkg.namespace', None), 84 ('from pkg.namespace.namespace_module', 85 os.path.join('namespace', 'namespace_module.py'), 'pkg.namespace', None), 86 ] 87 88) 89def test_correct_zip_package_behavior(Script, inference_state, environment, code, 90 file, package, path): 91 sys_path = environment.get_sys_path() + [str(pkg_zip_path)] 92 pkg, = Script(code, project=Project('.', sys_path=sys_path)).infer() 93 value, = pkg._name.infer() 94 assert value.py__file__() == pkg_zip_path.joinpath('pkg', file) 95 assert '.'.join(value.py__package__()) == package 96 assert value.is_package() is (path is not None) 97 if path is not None: 98 assert value.py__path__() == [str(pkg_zip_path.joinpath(path))] 99 100 value.string_names = None 101 assert value.py__package__() == [] 102 103 104def test_find_module_not_package_zipped(Script, inference_state, environment): 105 path = get_example_dir('zipped_imports', 'not_pkg.zip') 106 sys_path = environment.get_sys_path() + [path] 107 script = Script('import not_pkg; not_pkg.val', project=Project('.', sys_path=sys_path)) 108 assert len(script.complete()) == 1 109 110 file_io, is_package = inference_state.compiled_subprocess.get_module_info( 111 sys_path=map(str, sys_path), 112 string='not_pkg', 113 full_name='not_pkg' 114 ) 115 assert file_io.path.parts[-2:] == ('not_pkg.zip', 'not_pkg.py') 116 assert is_package is False 117 118 119def test_import_not_in_sys_path(Script, environment): 120 """ 121 non-direct imports (not in sys.path) 122 123 This is in the end just a fallback. 124 """ 125 path = get_example_dir() 126 module_path = os.path.join(path, 'not_in_sys_path', 'pkg', 'module.py') 127 # This project tests the smart path option of Project. The sys_path is 128 # explicitly given to make sure that the path is just dumb and only 129 # includes non-folder dependencies. 130 project = Project(path, sys_path=environment.get_sys_path()) 131 a = Script(path=module_path, project=project).infer(line=5) 132 assert a[0].name == 'int' 133 134 a = Script(path=module_path, project=project).infer(line=6) 135 assert a[0].name == 'str' 136 a = Script(path=module_path, project=project).infer(line=7) 137 assert a[0].name == 'str' 138 139 140@pytest.mark.parametrize("code,name", [ 141 ("from flask.ext import foo; foo.", "Foo"), # flask_foo.py 142 ("from flask.ext import bar; bar.", "Bar"), # flaskext/bar.py 143 ("from flask.ext import baz; baz.", "Baz"), # flask_baz/__init__.py 144 ("from flask.ext import moo; moo.", "Moo"), # flaskext/moo/__init__.py 145 ("from flask.ext.", "foo"), 146 ("from flask.ext.", "bar"), 147 ("from flask.ext.", "baz"), 148 ("from flask.ext.", "moo"), 149 pytest.param("import flask.ext.foo; flask.ext.foo.", "Foo", marks=pytest.mark.xfail), 150 pytest.param("import flask.ext.bar; flask.ext.bar.", "Foo", marks=pytest.mark.xfail), 151 pytest.param("import flask.ext.baz; flask.ext.baz.", "Foo", marks=pytest.mark.xfail), 152 pytest.param("import flask.ext.moo; flask.ext.moo.", "Foo", marks=pytest.mark.xfail), 153]) 154def test_flask_ext(Script, code, name): 155 """flask.ext.foo is really imported from flaskext.foo or flask_foo. 156 """ 157 path = get_example_dir('flask-site-packages') 158 completions = Script(code, project=Project('.', sys_path=[path])).complete() 159 assert name in [c.name for c in completions] 160 161 162def test_not_importable_file(Script): 163 src = 'import not_importable_file as x; x.' 164 assert not Script(src, path='example.py', project=test_dir_project).complete() 165 166 167def test_import_unique(Script): 168 src = "import os; os.path" 169 defs = Script(src, path='example.py').infer() 170 parent_contexts = [d._name._value for d in defs] 171 assert len(parent_contexts) == len(set(parent_contexts)) 172 173 174def test_cache_works_with_sys_path_param(Script, tmpdir): 175 foo_path = tmpdir.join('foo') 176 bar_path = tmpdir.join('bar') 177 foo_path.join('module.py').write('foo = 123', ensure=True) 178 bar_path.join('module.py').write('bar = 123', ensure=True) 179 foo_completions = Script( 180 'import module; module.', 181 project=Project('.', sys_path=[foo_path.strpath]), 182 ).complete() 183 bar_completions = Script( 184 'import module; module.', 185 project=Project('.', sys_path=[bar_path.strpath]), 186 ).complete() 187 assert 'foo' in [c.name for c in foo_completions] 188 assert 'bar' not in [c.name for c in foo_completions] 189 190 assert 'bar' in [c.name for c in bar_completions] 191 assert 'foo' not in [c.name for c in bar_completions] 192 193 194def test_import_completion_docstring(Script): 195 import abc 196 s = Script('"""test"""\nimport ab') 197 abc_completions = [c for c in s.complete() if c.name == 'abc'] 198 assert len(abc_completions) == 1 199 assert abc_completions[0].docstring(fast=False) == abc.__doc__ 200 201 # However for performance reasons not all modules are loaded and the 202 # docstring is empty in this case. 203 assert abc_completions[0].docstring() == '' 204 205 206def test_goto_definition_on_import(Script): 207 assert Script("import sys_blabla").infer(1, 8) == [] 208 assert len(Script("import sys").infer(1, 8)) == 1 209 210 211def test_complete_on_empty_import(ScriptWithProject): 212 path = os.path.join(test_dir, 'whatever.py') 213 assert ScriptWithProject("from datetime import").complete()[0].name == 'import' 214 # should just list the files in the directory 215 assert 10 < len(ScriptWithProject("from .", path=path).complete()) < 30 216 217 # Global import 218 assert len(ScriptWithProject("from . import", path=path).complete(1, 5)) > 30 219 # relative import 220 assert 10 < len(ScriptWithProject("from . import", path=path).complete(1, 6)) < 30 221 222 # Global import 223 assert len(ScriptWithProject("from . import classes", path=path).complete(1, 5)) > 30 224 # relative import 225 assert 10 < len(ScriptWithProject("from . import classes", path=path).complete(1, 6)) < 30 226 227 wanted = {'ImportError', 'import', 'ImportWarning'} 228 assert {c.name for c in ScriptWithProject("import").complete()} == wanted 229 assert len(ScriptWithProject("import import", path=path).complete()) > 0 230 231 # 111 232 assert ScriptWithProject("from datetime import").complete()[0].name == 'import' 233 assert ScriptWithProject("from datetime import ").complete() 234 235 236def test_imports_on_global_namespace_without_path(Script): 237 """If the path is None, there shouldn't be any import problem""" 238 completions = Script("import operator").complete() 239 assert [c.name for c in completions] == ['operator'] 240 completions = Script("import operator", path='example.py').complete() 241 assert [c.name for c in completions] == ['operator'] 242 243 # the first one has a path the second doesn't 244 completions = Script("import keyword", path='example.py').complete() 245 assert [c.name for c in completions] == ['keyword'] 246 completions = Script("import keyword").complete() 247 assert [c.name for c in completions] == ['keyword'] 248 249 250def test_named_import(Script): 251 """named import - jedi-vim issue #8""" 252 s = "import time as dt" 253 assert len(Script(s, path='/').infer(1, 15)) == 1 254 assert len(Script(s, path='/').infer(1, 10)) == 1 255 256 257def test_nested_import(Script): 258 s = "import multiprocessing.dummy; multiprocessing.dummy" 259 g = Script(s).goto() 260 assert len(g) == 1 261 assert (g[0].line, g[0].column) != (0, 0) 262 263 264def test_goto(Script): 265 sys, = Script("import sys").goto(follow_imports=True) 266 assert sys.type == 'module' 267 268 269def test_os_after_from(Script): 270 def check(source, result, column=None): 271 completions = Script(source).complete(column=column) 272 assert [c.name for c in completions] == result 273 274 check('\nfrom os. ', ['path']) 275 check('\nfrom os ', ['import']) 276 check('from os ', ['import']) 277 check('\nfrom os import whatever', ['import'], len('from os im')) 278 279 check('from os\\\n', ['import']) 280 check('from os \\\n', ['import']) 281 282 283def test_os_issues(Script): 284 def import_names(*args, **kwargs): 285 return [d.name for d in Script(*args).complete(**kwargs)] 286 287 # Github issue #759 288 s = 'import os, s' 289 assert 'sys' in import_names(s) 290 assert 'path' not in import_names(s, column=len(s) - 1) 291 assert 'os' in import_names(s, column=len(s) - 3) 292 293 # Some more checks 294 s = 'from os import path, e' 295 assert 'environ' in import_names(s) 296 assert 'json' not in import_names(s, column=len(s) - 1) 297 assert 'environ' in import_names(s, column=len(s) - 1) 298 assert 'path' in import_names(s, column=len(s) - 3) 299 300 301def test_path_issues(Script): 302 """ 303 See pull request #684 for details. 304 """ 305 source = '''from datetime import ''' 306 assert Script(source).complete() 307 308 309def test_compiled_import_none(monkeypatch, Script): 310 """ 311 Related to #1079. An import might somehow fail and return None. 312 """ 313 script = Script('import sys') 314 monkeypatch.setattr(compiled, 'load_module', lambda *args, **kwargs: None) 315 def_, = script.infer() 316 assert def_.type == 'module' 317 value, = def_._name.infer() 318 assert not _stub_to_python_value_set(value) 319 320 321@pytest.mark.parametrize( 322 ('path', 'is_package', 'goal'), [ 323 # Both of these tests used to return relative paths to the module 324 # context that was initially given, but now we just work with the file 325 # system. 326 (os.path.join(THIS_DIR, 'test_docstring.py'), False, 327 ('test', 'test_inference', 'test_imports')), 328 (os.path.join(THIS_DIR, '__init__.py'), True, 329 ('test', 'test_inference', 'test_imports')), 330 ] 331) 332def test_get_modules_containing_name(inference_state, path, goal, is_package): 333 module = imports._load_python_module( 334 inference_state, 335 FileIO(path), 336 import_names=('ok', 'lala', 'x'), 337 is_package=is_package, 338 ) 339 assert module 340 module_context = module.as_context() 341 input_module, found_module = get_module_contexts_containing_name( 342 inference_state, 343 [module_context], 344 'string_that_only_exists_here' 345 ) 346 assert input_module is module_context 347 assert found_module.string_names == goal 348 349 350@pytest.mark.parametrize( 351 'path', ('api/whatever/test_this.py', 'api/whatever/file')) 352@pytest.mark.parametrize('empty_sys_path', (False, True)) 353def test_relative_imports_with_multiple_similar_directories(Script, path, empty_sys_path): 354 dir = get_example_dir('issue1209') 355 if empty_sys_path: 356 project = Project(dir, sys_path=(), smart_sys_path=False) 357 else: 358 project = Project(dir) 359 script = Script( 360 "from . ", 361 path=os.path.join(dir, path), 362 project=project, 363 ) 364 name, import_ = script.complete() 365 assert import_.name == 'import' 366 assert name.name == 'api_test1' 367 368 369def test_relative_imports_with_outside_paths(Script): 370 dir = get_example_dir('issue1209') 371 project = Project(dir, sys_path=[], smart_sys_path=False) 372 script = Script( 373 "from ...", 374 path=os.path.join(dir, 'api/whatever/test_this.py'), 375 project=project, 376 ) 377 assert [c.name for c in script.complete()] == ['api', 'whatever'] 378 379 script = Script( 380 "from " + '.' * 100, 381 path=os.path.join(dir, 'api/whatever/test_this.py'), 382 project=project, 383 ) 384 assert not script.complete() 385 386 387def test_relative_imports_without_path(Script): 388 path = get_example_dir('issue1209', 'api', 'whatever') 389 project = Project(path, sys_path=[], smart_sys_path=False) 390 script = Script("from . ", project=project) 391 assert [c.name for c in script.complete()] == ['api_test1', 'import'] 392 393 script = Script("from .. ", project=project) 394 assert [c.name for c in script.complete()] == ['import', 'whatever'] 395 396 script = Script("from ... ", project=project) 397 assert [c.name for c in script.complete()] == ['api', 'import', 'whatever'] 398 399 400def test_relative_import_out_of_file_system(Script): 401 code = "from " + '.' * 100 402 assert not Script(code).complete() 403 script = Script(code + ' ') 404 import_, = script.complete() 405 assert import_.name == 'import' 406 407 script = Script("from " + '.' * 100 + 'abc import ABCMeta') 408 assert not script.infer() 409 assert not script.complete() 410 411 412@pytest.mark.parametrize( 413 'level, directory, project_path, result', [ 414 (1, '/a/b/c', '/a', (['b', 'c'], '/a')), 415 (2, '/a/b/c', '/a', (['b'], '/a')), 416 (3, '/a/b/c', '/a', ([], '/a')), 417 (4, '/a/b/c', '/a', (None, '/')), 418 (5, '/a/b/c', '/a', (None, None)), 419 (1, '/', '/', ([], '/')), 420 (2, '/', '/', (None, None)), 421 (1, '/a/b', '/a/b/c', (None, '/a/b')), 422 (2, '/a/b', '/a/b/c', (None, '/a')), 423 (3, '/a/b', '/a/b/c', (None, '/')), 424 ] 425) 426def test_level_to_import_path(level, directory, project_path, result): 427 assert imports._level_to_base_import_path(project_path, directory, level) == result 428 429 430def test_import_name_calculation(Script): 431 s = Script(path=os.path.join(test_dir, 'completion', 'isinstance.py')) 432 m = s._get_module_context() 433 assert m.string_names == ('test', 'completion', 'isinstance') 434 435 436@pytest.mark.parametrize('name', ('builtins', 'typing')) 437def test_pre_defined_imports_module(Script, environment, name): 438 path = os.path.join(root_dir, name + '.py') 439 module = Script('', path=path)._get_module_context() 440 assert module.string_names == (name,) 441 442 assert str(module.inference_state.builtins_module.py__file__()) != path 443 assert str(module.inference_state.typing_module.py__file__()) != path 444 445 446@pytest.mark.parametrize('name', ('builtins', 'typing')) 447def test_import_needed_modules_by_jedi(Script, environment, tmpdir, name): 448 module_path = tmpdir.join(name + '.py') 449 module_path.write('int = ...') 450 script = Script( 451 'import ' + name, 452 path=tmpdir.join('something.py').strpath, 453 project=Project('.', sys_path=[tmpdir.strpath] + environment.get_sys_path()), 454 ) 455 module, = script.infer() 456 assert str(module._inference_state.builtins_module.py__file__()) != module_path 457 assert str(module._inference_state.typing_module.py__file__()) != module_path 458 459 460def test_import_with_semicolon(Script): 461 names = [c.name for c in Script('xzy; from abc import ').complete()] 462 assert 'ABCMeta' in names 463 assert 'abc' not in names 464 465 466def test_relative_import_star(Script): 467 # Coming from github #1235 468 source = """ 469 from . import * 470 furl.c 471 """ 472 script = Script(source, path='export.py') 473 474 assert script.complete(3, len("furl.c")) 475 476 477@pytest.mark.parametrize('with_init', [False, True]) 478def test_relative_imports_without_path_and_setup_py( 479 Script, inference_state, environment, tmpdir, with_init): 480 # Contrary to other tests here we create a temporary folder that is not 481 # part of a folder with a setup.py that signifies 482 tmpdir.join('file1.py').write('do_foo = 1') 483 other_path = tmpdir.join('other_files') 484 other_path.join('file2.py').write('def do_nothing():\n pass', ensure=True) 485 if with_init: 486 other_path.join('__init__.py').write('') 487 488 for name, code in [('file2', 'from . import file2'), 489 ('file1', 'from .. import file1')]: 490 for func in (jedi.Script.goto, jedi.Script.infer): 491 n, = func(Script(code, path=other_path.join('test1.py').strpath)) 492 assert n.name == name 493 assert n.type == 'module' 494 assert n.line == 1 495 496 497def test_import_recursion(Script): 498 path = get_example_dir('import-recursion', "cq_example.py") 499 for c in Script(path=path).complete(3, 3): 500 c.docstring() 501