Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/jedi-0.19.2-py3.11.egg/jedi/inference/imports.py: 17%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

289 statements  

1""" 

2:mod:`jedi.inference.imports` is here to resolve import statements and return 

3the modules/classes/functions/whatever, which they stand for. However there's 

4not any actual importing done. This module is about finding modules in the 

5filesystem. This can be quite tricky sometimes, because Python imports are not 

6always that simple. 

7 

8This module also supports import autocompletion, which means to complete 

9statements like ``from datetim`` (cursor at the end would return ``datetime``). 

10""" 

11import os 

12from pathlib import Path 

13 

14from parso.python import tree 

15 

16from jedi import debug 

17from jedi import settings 

18from jedi.file_io import FolderIO 

19from jedi.parser_utils import get_cached_code_lines 

20from jedi.inference import sys_path 

21from jedi.inference import helpers 

22from jedi.inference import compiled 

23from jedi.inference import analysis 

24from jedi.inference.utils import unite 

25from jedi.inference.cache import inference_state_method_cache 

26from jedi.inference.names import ImportName, SubModuleName 

27from jedi.inference.base_value import ValueSet, NO_VALUES 

28from jedi.inference.gradual.typeshed import import_module_decorator, \ 

29 create_stub_module, parse_stub_module 

30from jedi.inference.compiled.subprocess.functions import ImplicitNSInfo 

31from jedi.plugins import plugin_manager 

32 

33 

34class ModuleCache: 

35 def __init__(self): 

36 self._name_cache = {} 

37 

38 def add(self, string_names, value_set): 

39 if string_names is not None: 

40 self._name_cache[string_names] = value_set 

41 

42 def get(self, string_names): 

43 return self._name_cache.get(string_names) 

44 

45 

46# This memoization is needed, because otherwise we will infinitely loop on 

47# certain imports. 

48@inference_state_method_cache(default=NO_VALUES) 

49def infer_import(context, tree_name): 

50 module_context = context.get_root_context() 

51 from_import_name, import_path, level, values = \ 

52 _prepare_infer_import(module_context, tree_name) 

53 if values: 

54 

55 if from_import_name is not None: 

56 values = values.py__getattribute__( 

57 from_import_name, 

58 name_context=context, 

59 analysis_errors=False 

60 ) 

61 

62 if not values: 

63 path = import_path + (from_import_name,) 

64 importer = Importer(context.inference_state, path, module_context, level) 

65 values = importer.follow() 

66 debug.dbg('after import: %s', values) 

67 return values 

68 

69 

70@inference_state_method_cache(default=[]) 

71def goto_import(context, tree_name): 

72 module_context = context.get_root_context() 

73 from_import_name, import_path, level, values = \ 

74 _prepare_infer_import(module_context, tree_name) 

75 if not values: 

76 return [] 

77 

78 if from_import_name is not None: 

79 names = unite([ 

80 c.goto( 

81 from_import_name, 

82 name_context=context, 

83 analysis_errors=False 

84 ) for c in values 

85 ]) 

86 # Avoid recursion on the same names. 

87 if names and not any(n.tree_name is tree_name for n in names): 

88 return names 

89 

90 path = import_path + (from_import_name,) 

91 importer = Importer(context.inference_state, path, module_context, level) 

92 values = importer.follow() 

93 return set(s.name for s in values) 

94 

95 

96def _prepare_infer_import(module_context, tree_name): 

97 import_node = tree_name.search_ancestor('import_name', 'import_from') 

98 import_path = import_node.get_path_for_name(tree_name) 

99 from_import_name = None 

100 try: 

101 from_names = import_node.get_from_names() 

102 except AttributeError: 

103 # Is an import_name 

104 pass 

105 else: 

106 if len(from_names) + 1 == len(import_path): 

107 # We have to fetch the from_names part first and then check 

108 # if from_names exists in the modules. 

109 from_import_name = import_path[-1] 

110 import_path = from_names 

111 

112 importer = Importer(module_context.inference_state, tuple(import_path), 

113 module_context, import_node.level) 

114 

115 return from_import_name, tuple(import_path), import_node.level, importer.follow() 

116 

117 

118def _add_error(value, name, message): 

119 if hasattr(name, 'parent') and value is not None: 

120 analysis.add(value, 'import-error', name, message) 

121 else: 

122 debug.warning('ImportError without origin: ' + message) 

123 

124 

125def _level_to_base_import_path(project_path, directory, level): 

126 """ 

127 In case the level is outside of the currently known package (something like 

128 import .....foo), we can still try our best to help the user for 

129 completions. 

130 """ 

131 for i in range(level - 1): 

132 old = directory 

133 directory = os.path.dirname(directory) 

134 if old == directory: 

135 return None, None 

136 

137 d = directory 

138 level_import_paths = [] 

139 # Now that we are on the level that the user wants to be, calculate the 

140 # import path for it. 

141 while True: 

142 if d == project_path: 

143 return level_import_paths, d 

144 dir_name = os.path.basename(d) 

145 if dir_name: 

146 level_import_paths.insert(0, dir_name) 

147 d = os.path.dirname(d) 

148 else: 

149 return None, directory 

150 

151 

152class Importer: 

153 def __init__(self, inference_state, import_path, module_context, level=0): 

154 """ 

155 An implementation similar to ``__import__``. Use `follow` 

156 to actually follow the imports. 

157 

158 *level* specifies whether to use absolute or relative imports. 0 (the 

159 default) means only perform absolute imports. Positive values for level 

160 indicate the number of parent directories to search relative to the 

161 directory of the module calling ``__import__()`` (see PEP 328 for the 

162 details). 

163 

164 :param import_path: List of namespaces (strings or Names). 

165 """ 

166 debug.speed('import %s %s' % (import_path, module_context)) 

167 self._inference_state = inference_state 

168 self.level = level 

169 self._module_context = module_context 

170 

171 self._fixed_sys_path = None 

172 self._infer_possible = True 

173 if level: 

174 base = module_context.get_value().py__package__() 

175 # We need to care for two cases, the first one is if it's a valid 

176 # Python import. This import has a properly defined module name 

177 # chain like `foo.bar.baz` and an import in baz is made for 

178 # `..lala.` It can then resolve to `foo.bar.lala`. 

179 # The else here is a heuristic for all other cases, if for example 

180 # in `foo` you search for `...bar`, it's obviously out of scope. 

181 # However since Jedi tries to just do it's best, we help the user 

182 # here, because he might have specified something wrong in his 

183 # project. 

184 if level <= len(base): 

185 # Here we basically rewrite the level to 0. 

186 base = tuple(base) 

187 if level > 1: 

188 base = base[:-level + 1] 

189 import_path = base + tuple(import_path) 

190 else: 

191 path = module_context.py__file__() 

192 project_path = self._inference_state.project.path 

193 import_path = list(import_path) 

194 if path is None: 

195 # If no path is defined, our best guess is that the current 

196 # file is edited by a user on the current working 

197 # directory. We need to add an initial path, because it 

198 # will get removed as the name of the current file. 

199 directory = project_path 

200 else: 

201 directory = os.path.dirname(path) 

202 

203 base_import_path, base_directory = _level_to_base_import_path( 

204 project_path, directory, level, 

205 ) 

206 if base_directory is None: 

207 # Everything is lost, the relative import does point 

208 # somewhere out of the filesystem. 

209 self._infer_possible = False 

210 else: 

211 self._fixed_sys_path = [base_directory] 

212 

213 if base_import_path is None: 

214 if import_path: 

215 _add_error( 

216 module_context, import_path[0], 

217 message='Attempted relative import beyond top-level package.' 

218 ) 

219 else: 

220 import_path = base_import_path + import_path 

221 self.import_path = import_path 

222 

223 @property 

224 def _str_import_path(self): 

225 """Returns the import path as pure strings instead of `Name`.""" 

226 return tuple( 

227 name.value if isinstance(name, tree.Name) else name 

228 for name in self.import_path 

229 ) 

230 

231 def _sys_path_with_modifications(self, is_completion): 

232 if self._fixed_sys_path is not None: 

233 return self._fixed_sys_path 

234 

235 return ( 

236 # For import completions we don't want to see init paths, but for 

237 # inference we want to show the user as much as possible. 

238 # See GH #1446. 

239 self._inference_state.get_sys_path(add_init_paths=not is_completion) 

240 + [ 

241 str(p) for p 

242 in sys_path.check_sys_path_modifications(self._module_context) 

243 ] 

244 ) 

245 

246 def follow(self): 

247 if not self.import_path: 

248 if self._fixed_sys_path: 

249 # This is a bit of a special case, that maybe should be 

250 # revisited. If the project path is wrong or the user uses 

251 # relative imports the wrong way, we might end up here, where 

252 # the `fixed_sys_path == project.path` in that case we kind of 

253 # use the project.path.parent directory as our path. This is 

254 # usually not a problem, except if imports in other places are 

255 # using the same names. Example: 

256 # 

257 # foo/ < #1 

258 # - setup.py 

259 # - foo/ < #2 

260 # - __init__.py 

261 # - foo.py < #3 

262 # 

263 # If the top foo is our project folder and somebody uses 

264 # `from . import foo` in `setup.py`, it will resolve to foo #2, 

265 # which means that the import for foo.foo is cached as 

266 # `__init__.py` (#2) and not as `foo.py` (#3). This is usually 

267 # not an issue, because this case is probably pretty rare, but 

268 # might be an issue for some people. 

269 # 

270 # However for most normal cases where we work with different 

271 # file names, this code path hits where we basically change the 

272 # project path to an ancestor of project path. 

273 from jedi.inference.value.namespace import ImplicitNamespaceValue 

274 import_path = (os.path.basename(self._fixed_sys_path[0]),) 

275 ns = ImplicitNamespaceValue( 

276 self._inference_state, 

277 string_names=import_path, 

278 paths=self._fixed_sys_path, 

279 ) 

280 return ValueSet({ns}) 

281 return NO_VALUES 

282 if not self._infer_possible: 

283 return NO_VALUES 

284 

285 # Check caches first 

286 from_cache = self._inference_state.stub_module_cache.get(self._str_import_path) 

287 if from_cache is not None: 

288 return ValueSet({from_cache}) 

289 from_cache = self._inference_state.module_cache.get(self._str_import_path) 

290 if from_cache is not None: 

291 return from_cache 

292 

293 sys_path = self._sys_path_with_modifications(is_completion=False) 

294 

295 return import_module_by_names( 

296 self._inference_state, self.import_path, sys_path, self._module_context 

297 ) 

298 

299 def _get_module_names(self, search_path=None, in_module=None): 

300 """ 

301 Get the names of all modules in the search_path. This means file names 

302 and not names defined in the files. 

303 """ 

304 if search_path is None: 

305 sys_path = self._sys_path_with_modifications(is_completion=True) 

306 else: 

307 sys_path = search_path 

308 return list(iter_module_names( 

309 self._inference_state, self._module_context, sys_path, 

310 module_cls=ImportName if in_module is None else SubModuleName, 

311 add_builtin_modules=search_path is None and in_module is None, 

312 )) 

313 

314 def completion_names(self, inference_state, only_modules=False): 

315 """ 

316 :param only_modules: Indicates wheter it's possible to import a 

317 definition that is not defined in a module. 

318 """ 

319 if not self._infer_possible: 

320 return [] 

321 

322 names = [] 

323 if self.import_path: 

324 # flask 

325 if self._str_import_path == ('flask', 'ext'): 

326 # List Flask extensions like ``flask_foo`` 

327 for mod in self._get_module_names(): 

328 modname = mod.string_name 

329 if modname.startswith('flask_'): 

330 extname = modname[len('flask_'):] 

331 names.append(ImportName(self._module_context, extname)) 

332 # Now the old style: ``flaskext.foo`` 

333 for dir in self._sys_path_with_modifications(is_completion=True): 

334 flaskext = os.path.join(dir, 'flaskext') 

335 if os.path.isdir(flaskext): 

336 names += self._get_module_names([flaskext]) 

337 

338 values = self.follow() 

339 for value in values: 

340 # Non-modules are not completable. 

341 if value.api_type not in ('module', 'namespace'): # not a module 

342 continue 

343 if not value.is_compiled(): 

344 # sub_modules_dict is not implemented for compiled modules. 

345 names += value.sub_modules_dict().values() 

346 

347 if not only_modules: 

348 from jedi.inference.gradual.conversion import convert_values 

349 

350 both_values = values | convert_values(values) 

351 for c in both_values: 

352 for filter in c.get_filters(): 

353 names += filter.values() 

354 else: 

355 if self.level: 

356 # We only get here if the level cannot be properly calculated. 

357 names += self._get_module_names(self._fixed_sys_path) 

358 else: 

359 # This is just the list of global imports. 

360 names += self._get_module_names() 

361 return names 

362 

363 

364def import_module_by_names(inference_state, import_names, sys_path=None, 

365 module_context=None, prefer_stubs=True): 

366 if sys_path is None: 

367 sys_path = inference_state.get_sys_path() 

368 

369 str_import_names = tuple( 

370 i.value if isinstance(i, tree.Name) else i 

371 for i in import_names 

372 ) 

373 value_set = [None] 

374 for i, name in enumerate(import_names): 

375 value_set = ValueSet.from_sets([ 

376 import_module( 

377 inference_state, 

378 str_import_names[:i+1], 

379 parent_module_value, 

380 sys_path, 

381 prefer_stubs=prefer_stubs, 

382 ) for parent_module_value in value_set 

383 ]) 

384 if not value_set: 

385 message = 'No module named ' + '.'.join(str_import_names) 

386 if module_context is not None: 

387 _add_error(module_context, name, message) 

388 else: 

389 debug.warning(message) 

390 return NO_VALUES 

391 return value_set 

392 

393 

394@plugin_manager.decorate() 

395@import_module_decorator 

396def import_module(inference_state, import_names, parent_module_value, sys_path): 

397 """ 

398 This method is very similar to importlib's `_gcd_import`. 

399 """ 

400 if import_names[0] in settings.auto_import_modules: 

401 module = _load_builtin_module(inference_state, import_names, sys_path) 

402 if module is None: 

403 return NO_VALUES 

404 return ValueSet([module]) 

405 

406 module_name = '.'.join(import_names) 

407 if parent_module_value is None: 

408 # Override the sys.path. It works only good that way. 

409 # Injecting the path directly into `find_module` did not work. 

410 file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info( 

411 string=import_names[-1], 

412 full_name=module_name, 

413 sys_path=sys_path, 

414 is_global_search=True, 

415 ) 

416 if is_pkg is None: 

417 return NO_VALUES 

418 else: 

419 paths = parent_module_value.py__path__() 

420 if paths is None: 

421 # The module might not be a package. 

422 return NO_VALUES 

423 

424 file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info( 

425 string=import_names[-1], 

426 path=paths, 

427 full_name=module_name, 

428 is_global_search=False, 

429 ) 

430 if is_pkg is None: 

431 return NO_VALUES 

432 

433 if isinstance(file_io_or_ns, ImplicitNSInfo): 

434 from jedi.inference.value.namespace import ImplicitNamespaceValue 

435 module = ImplicitNamespaceValue( 

436 inference_state, 

437 string_names=tuple(file_io_or_ns.name.split('.')), 

438 paths=file_io_or_ns.paths, 

439 ) 

440 elif file_io_or_ns is None: 

441 module = _load_builtin_module(inference_state, import_names, sys_path) 

442 if module is None: 

443 return NO_VALUES 

444 else: 

445 module = _load_python_module( 

446 inference_state, file_io_or_ns, 

447 import_names=import_names, 

448 is_package=is_pkg, 

449 ) 

450 

451 if parent_module_value is None: 

452 debug.dbg('global search_module %s: %s', import_names[-1], module) 

453 else: 

454 debug.dbg('search_module %s in paths %s: %s', module_name, paths, module) 

455 return ValueSet([module]) 

456 

457 

458def _load_python_module(inference_state, file_io, 

459 import_names=None, is_package=False): 

460 module_node = inference_state.parse( 

461 file_io=file_io, 

462 cache=True, 

463 diff_cache=settings.fast_parser, 

464 cache_path=settings.cache_directory, 

465 ) 

466 

467 from jedi.inference.value import ModuleValue 

468 return ModuleValue( 

469 inference_state, module_node, 

470 file_io=file_io, 

471 string_names=import_names, 

472 code_lines=get_cached_code_lines(inference_state.grammar, file_io.path), 

473 is_package=is_package, 

474 ) 

475 

476 

477def _load_builtin_module(inference_state, import_names=None, sys_path=None): 

478 project = inference_state.project 

479 if sys_path is None: 

480 sys_path = inference_state.get_sys_path() 

481 if not project._load_unsafe_extensions: 

482 safe_paths = set(project._get_base_sys_path(inference_state)) 

483 sys_path = [p for p in sys_path if p in safe_paths] 

484 

485 dotted_name = '.'.join(import_names) 

486 assert dotted_name is not None 

487 module = compiled.load_module(inference_state, dotted_name=dotted_name, sys_path=sys_path) 

488 if module is None: 

489 # The file might raise an ImportError e.g. and therefore not be 

490 # importable. 

491 return None 

492 return module 

493 

494 

495def load_module_from_path(inference_state, file_io, import_names=None, is_package=None): 

496 """ 

497 This should pretty much only be used for get_modules_containing_name. It's 

498 here to ensure that a random path is still properly loaded into the Jedi 

499 module structure. 

500 """ 

501 path = Path(file_io.path) 

502 if import_names is None: 

503 e_sys_path = inference_state.get_sys_path() 

504 import_names, is_package = sys_path.transform_path_to_dotted(e_sys_path, path) 

505 else: 

506 assert isinstance(is_package, bool) 

507 

508 is_stub = path.suffix == '.pyi' 

509 if is_stub: 

510 folder_io = file_io.get_parent_folder() 

511 if folder_io.path.endswith('-stubs'): 

512 folder_io = FolderIO(folder_io.path[:-6]) 

513 if path.name == '__init__.pyi': 

514 python_file_io = folder_io.get_file_io('__init__.py') 

515 else: 

516 python_file_io = folder_io.get_file_io(import_names[-1] + '.py') 

517 

518 try: 

519 v = load_module_from_path( 

520 inference_state, python_file_io, 

521 import_names, is_package=is_package 

522 ) 

523 values = ValueSet([v]) 

524 except FileNotFoundError: 

525 values = NO_VALUES 

526 

527 return create_stub_module( 

528 inference_state, inference_state.latest_grammar, values, 

529 parse_stub_module(inference_state, file_io), file_io, import_names 

530 ) 

531 else: 

532 module = _load_python_module( 

533 inference_state, file_io, 

534 import_names=import_names, 

535 is_package=is_package, 

536 ) 

537 inference_state.module_cache.add(import_names, ValueSet([module])) 

538 return module 

539 

540 

541def load_namespace_from_path(inference_state, folder_io): 

542 import_names, is_package = sys_path.transform_path_to_dotted( 

543 inference_state.get_sys_path(), 

544 Path(folder_io.path) 

545 ) 

546 from jedi.inference.value.namespace import ImplicitNamespaceValue 

547 return ImplicitNamespaceValue(inference_state, import_names, [folder_io.path]) 

548 

549 

550def follow_error_node_imports_if_possible(context, name): 

551 error_node = name.search_ancestor('error_node') 

552 if error_node is not None: 

553 # Get the first command start of a started simple_stmt. The error 

554 # node is sometimes a small_stmt and sometimes a simple_stmt. Check 

555 # for ; leaves that start a new statements. 

556 start_index = 0 

557 for index, n in enumerate(error_node.children): 

558 if n.start_pos > name.start_pos: 

559 break 

560 if n == ';': 

561 start_index = index + 1 

562 nodes = error_node.children[start_index:] 

563 first_name = nodes[0].get_first_leaf().value 

564 

565 # Make it possible to infer stuff like `import foo.` or 

566 # `from foo.bar`. 

567 if first_name in ('from', 'import'): 

568 is_import_from = first_name == 'from' 

569 level, names = helpers.parse_dotted_names( 

570 nodes, 

571 is_import_from=is_import_from, 

572 until_node=name, 

573 ) 

574 return Importer( 

575 context.inference_state, names, context.get_root_context(), level).follow() 

576 return None 

577 

578 

579def iter_module_names(inference_state, module_context, search_path, 

580 module_cls=ImportName, add_builtin_modules=True): 

581 """ 

582 Get the names of all modules in the search_path. This means file names 

583 and not names defined in the files. 

584 """ 

585 # add builtin module names 

586 if add_builtin_modules: 

587 for name in inference_state.compiled_subprocess.get_builtin_module_names(): 

588 yield module_cls(module_context, name) 

589 

590 for name in inference_state.compiled_subprocess.iter_module_names(search_path): 

591 yield module_cls(module_context, name)