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

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

463 statements  

1from abc import abstractmethod 

2from inspect import Parameter 

3from typing import Optional, Tuple 

4 

5from jedi.parser_utils import find_statement_documentation, clean_scope_docstring 

6from jedi.inference.utils import unite 

7from jedi.inference.base_value import ValueSet, NO_VALUES 

8from jedi.inference.cache import inference_state_method_cache 

9from jedi.inference import docstrings 

10from jedi.cache import memoize_method 

11from jedi.inference.helpers import deep_ast_copy, infer_call_of_leaf 

12from jedi.plugins import plugin_manager 

13 

14 

15def _merge_name_docs(names): 

16 doc = '' 

17 for name in names: 

18 if doc: 

19 # In case we have multiple values, just return all of them 

20 # separated by a few dashes. 

21 doc += '\n' + '-' * 30 + '\n' 

22 doc += name.py__doc__() 

23 return doc 

24 

25 

26class AbstractNameDefinition: 

27 start_pos: Optional[Tuple[int, int]] = None 

28 string_name: str 

29 parent_context = None 

30 tree_name = None 

31 is_value_name = True 

32 """ 

33 Used for the Jedi API to know if it's a keyword or an actual name. 

34 """ 

35 

36 @abstractmethod 

37 def infer(self): 

38 raise NotImplementedError 

39 

40 @abstractmethod 

41 def goto(self): 

42 # Typically names are already definitions and therefore a goto on that 

43 # name will always result on itself. 

44 return {self} 

45 

46 def get_qualified_names(self, include_module_names=False): 

47 qualified_names = self._get_qualified_names() 

48 if qualified_names is None or not include_module_names: 

49 return qualified_names 

50 

51 module_names = self.get_root_context().string_names 

52 if module_names is None: 

53 return None 

54 return module_names + qualified_names 

55 

56 def _get_qualified_names(self): 

57 # By default, a name has no qualified names. 

58 return None 

59 

60 def get_root_context(self): 

61 return self.parent_context.get_root_context() 

62 

63 def get_public_name(self): 

64 return self.string_name 

65 

66 def __repr__(self): 

67 if self.start_pos is None: 

68 return '<%s: string_name=%s>' % (self.__class__.__name__, self.string_name) 

69 return '<%s: string_name=%s start_pos=%s>' % (self.__class__.__name__, 

70 self.string_name, self.start_pos) 

71 

72 def is_import(self): 

73 return False 

74 

75 def py__doc__(self): 

76 return '' 

77 

78 @property 

79 def api_type(self): 

80 return self.parent_context.api_type 

81 

82 def get_defining_qualified_value(self): 

83 """ 

84 Returns either None or the value that is public and qualified. Won't 

85 return a function, because a name in a function is never public. 

86 """ 

87 return None 

88 

89 

90class AbstractArbitraryName(AbstractNameDefinition): 

91 """ 

92 When you e.g. want to complete dicts keys, you probably want to complete 

93 string literals, which is not really a name, but for Jedi we use this 

94 concept of Name for completions as well. 

95 """ 

96 is_value_name = False 

97 

98 def __init__(self, inference_state, string): 

99 self.inference_state = inference_state 

100 self.string_name = string 

101 self.parent_context = inference_state.builtins_module 

102 

103 def infer(self): 

104 return NO_VALUES 

105 

106 

107class AbstractTreeName(AbstractNameDefinition): 

108 def __init__(self, parent_context, tree_name): 

109 self.parent_context = parent_context 

110 self.tree_name = tree_name 

111 

112 def get_qualified_names(self, include_module_names=False): 

113 import_node = self.tree_name.search_ancestor('import_name', 'import_from') 

114 # For import nodes we cannot just have names, because it's very unclear 

115 # how they would look like. For now we just ignore them in most cases. 

116 # In case of level == 1, it works always, because it's like a submodule 

117 # lookup. 

118 if import_node is not None and not (import_node.level == 1 

119 and self.get_root_context().get_value().is_package()): 

120 # TODO improve the situation for when level is present. 

121 if include_module_names and not import_node.level: 

122 return tuple(n.value for n in import_node.get_path_for_name(self.tree_name)) 

123 else: 

124 return None 

125 

126 return super().get_qualified_names(include_module_names) 

127 

128 def _get_qualified_names(self): 

129 parent_names = self.parent_context.get_qualified_names() 

130 if parent_names is None: 

131 return None 

132 return parent_names + (self.tree_name.value,) 

133 

134 def get_defining_qualified_value(self): 

135 if self.is_import(): 

136 raise NotImplementedError("Shouldn't really happen, please report") 

137 elif self.parent_context: 

138 return self.parent_context.get_value() # Might be None 

139 return None 

140 

141 def goto(self): 

142 context = self.parent_context 

143 name = self.tree_name 

144 definition = name.get_definition(import_name_always=True) 

145 if definition is not None: 

146 type_ = definition.type 

147 if type_ == 'expr_stmt': 

148 # Only take the parent, because if it's more complicated than just 

149 # a name it's something you can "goto" again. 

150 is_simple_name = name.parent.type not in ('power', 'trailer') 

151 if is_simple_name: 

152 return [self] 

153 elif type_ in ('import_from', 'import_name'): 

154 from jedi.inference.imports import goto_import 

155 module_names = goto_import(context, name) 

156 return module_names 

157 else: 

158 return [self] 

159 else: 

160 from jedi.inference.imports import follow_error_node_imports_if_possible 

161 values = follow_error_node_imports_if_possible(context, name) 

162 if values is not None: 

163 return [value.name for value in values] 

164 

165 par = name.parent 

166 node_type = par.type 

167 if node_type == 'argument' and par.children[1] == '=' and par.children[0] == name: 

168 # Named param goto. 

169 trailer = par.parent 

170 if trailer.type == 'arglist': 

171 trailer = trailer.parent 

172 if trailer.type != 'classdef': 

173 if trailer.type == 'decorator': 

174 value_set = context.infer_node(trailer.children[1]) 

175 else: 

176 i = trailer.parent.children.index(trailer) 

177 to_infer = trailer.parent.children[:i] 

178 if to_infer[0] == 'await': 

179 to_infer.pop(0) 

180 value_set = context.infer_node(to_infer[0]) 

181 from jedi.inference.syntax_tree import infer_trailer 

182 for trailer in to_infer[1:]: 

183 value_set = infer_trailer(context, value_set, trailer) 

184 param_names = [] 

185 for value in value_set: 

186 for signature in value.get_signatures(): 

187 for param_name in signature.get_param_names(): 

188 if param_name.string_name == name.value: 

189 param_names.append(param_name) 

190 return param_names 

191 elif node_type == 'dotted_name': # Is a decorator. 

192 index = par.children.index(name) 

193 if index > 0: 

194 new_dotted = deep_ast_copy(par) 

195 new_dotted.children[index - 1:] = [] 

196 values = context.infer_node(new_dotted) 

197 return unite( 

198 value.goto(name, name_context=context) 

199 for value in values 

200 ) 

201 

202 if node_type == 'trailer' and par.children[0] == '.': 

203 values = infer_call_of_leaf(context, name, cut_own_trailer=True) 

204 return values.goto(name, name_context=context) 

205 else: 

206 stmt = name.search_ancestor('expr_stmt', 'lambdef') or name 

207 if stmt.type == 'lambdef': 

208 stmt = name 

209 return context.goto(name, position=stmt.start_pos) 

210 

211 def is_import(self): 

212 imp = self.tree_name.search_ancestor('import_from', 'import_name') 

213 return imp is not None 

214 

215 @property 

216 def string_name(self): 

217 return self.tree_name.value 

218 

219 @property 

220 def start_pos(self): 

221 return self.tree_name.start_pos 

222 

223 

224class ValueNameMixin: 

225 def infer(self): 

226 return ValueSet([self._value]) 

227 

228 def py__doc__(self): 

229 doc = self._value.py__doc__() 

230 if not doc and self._value.is_stub(): 

231 from jedi.inference.gradual.conversion import convert_names 

232 names = convert_names([self], prefer_stub_to_compiled=False) 

233 if self not in names: 

234 return _merge_name_docs(names) 

235 return doc 

236 

237 def _get_qualified_names(self): 

238 return self._value.get_qualified_names() 

239 

240 def get_root_context(self): 

241 if self.parent_context is None: # A module 

242 return self._value.as_context() 

243 return super().get_root_context() 

244 

245 def get_defining_qualified_value(self): 

246 context = self.parent_context 

247 if context is not None and (context.is_module() or context.is_class()): 

248 return self.parent_context.get_value() # Might be None 

249 return None 

250 

251 @property 

252 def api_type(self): 

253 return self._value.api_type 

254 

255 

256class ValueName(ValueNameMixin, AbstractTreeName): 

257 def __init__(self, value, tree_name): 

258 super().__init__(value.parent_context, tree_name) 

259 self._value = value 

260 

261 def goto(self): 

262 return ValueSet([self._value.name]) 

263 

264 

265class TreeNameDefinition(AbstractTreeName): 

266 _API_TYPES = dict( 

267 import_name='module', 

268 import_from='module', 

269 funcdef='function', 

270 param='param', 

271 classdef='class', 

272 ) 

273 

274 def infer(self): 

275 # Refactor this, should probably be here. 

276 from jedi.inference.syntax_tree import tree_name_to_values 

277 return tree_name_to_values( 

278 self.parent_context.inference_state, 

279 self.parent_context, 

280 self.tree_name 

281 ) 

282 

283 @property 

284 def api_type(self): 

285 definition = self.tree_name.get_definition(import_name_always=True) 

286 if definition is None: 

287 return 'statement' 

288 return self._API_TYPES.get(definition.type, 'statement') 

289 

290 def assignment_indexes(self): 

291 """ 

292 Returns an array of tuple(int, node) of the indexes that are used in 

293 tuple assignments. 

294 

295 For example if the name is ``y`` in the following code:: 

296 

297 x, (y, z) = 2, '' 

298 

299 would result in ``[(1, xyz_node), (0, yz_node)]``. 

300 

301 When searching for b in the case ``a, *b, c = [...]`` it will return:: 

302 

303 [(slice(1, -1), abc_node)] 

304 """ 

305 indexes = [] 

306 is_star_expr = False 

307 node = self.tree_name.parent 

308 compare = self.tree_name 

309 while node is not None: 

310 if node.type in ('testlist', 'testlist_comp', 'testlist_star_expr', 'exprlist'): 

311 for i, child in enumerate(node.children): 

312 if child == compare: 

313 index = int(i / 2) 

314 if is_star_expr: 

315 from_end = int((len(node.children) - i) / 2) 

316 index = slice(index, -from_end) 

317 indexes.insert(0, (index, node)) 

318 break 

319 else: 

320 raise LookupError("Couldn't find the assignment.") 

321 is_star_expr = False 

322 elif node.type == 'star_expr': 

323 is_star_expr = True 

324 elif node.type in ('expr_stmt', 'sync_comp_for'): 

325 break 

326 

327 compare = node 

328 node = node.parent 

329 return indexes 

330 

331 @property 

332 def inference_state(self): 

333 # Used by the cache function below 

334 return self.parent_context.inference_state 

335 

336 @inference_state_method_cache(default='') 

337 def py__doc__(self): 

338 api_type = self.api_type 

339 if api_type in ('function', 'class', 'property'): 

340 if self.parent_context.get_root_context().is_stub(): 

341 from jedi.inference.gradual.conversion import convert_names 

342 names = convert_names([self], prefer_stub_to_compiled=False) 

343 if self not in names: 

344 return _merge_name_docs(names) 

345 

346 # Make sure the names are not TreeNameDefinitions anymore. 

347 return clean_scope_docstring(self.tree_name.get_definition()) 

348 

349 if api_type == 'module': 

350 names = self.goto() 

351 if self not in names: 

352 return _merge_name_docs(names) 

353 

354 if api_type == 'statement' and self.tree_name.is_definition(): 

355 return find_statement_documentation(self.tree_name.get_definition()) 

356 return '' 

357 

358 

359class _ParamMixin: 

360 def maybe_positional_argument(self, include_star=True): 

361 options = [Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD] 

362 if include_star: 

363 options.append(Parameter.VAR_POSITIONAL) 

364 return self.get_kind() in options 

365 

366 def maybe_keyword_argument(self, include_stars=True): 

367 options = [Parameter.KEYWORD_ONLY, Parameter.POSITIONAL_OR_KEYWORD] 

368 if include_stars: 

369 options.append(Parameter.VAR_KEYWORD) 

370 return self.get_kind() in options 

371 

372 def _kind_string(self): 

373 kind = self.get_kind() 

374 if kind == Parameter.VAR_POSITIONAL: # *args 

375 return '*' 

376 if kind == Parameter.VAR_KEYWORD: # **kwargs 

377 return '**' 

378 return '' 

379 

380 def get_qualified_names(self, include_module_names=False): 

381 return None 

382 

383 

384class ParamNameInterface(_ParamMixin): 

385 api_type = 'param' 

386 

387 def get_kind(self): 

388 raise NotImplementedError 

389 

390 def to_string(self): 

391 raise NotImplementedError 

392 

393 def get_executed_param_name(self): 

394 """ 

395 For dealing with type inference and working around the graph, we 

396 sometimes want to have the param name of the execution. This feels a 

397 bit strange and we might have to refactor at some point. 

398 

399 For now however it exists to avoid infering params when we don't really 

400 need them (e.g. when we can just instead use annotations. 

401 """ 

402 return None 

403 

404 @property 

405 def star_count(self): 

406 kind = self.get_kind() 

407 if kind == Parameter.VAR_POSITIONAL: 

408 return 1 

409 if kind == Parameter.VAR_KEYWORD: 

410 return 2 

411 return 0 

412 

413 def infer_default(self): 

414 return NO_VALUES 

415 

416 

417class BaseTreeParamName(ParamNameInterface, AbstractTreeName): 

418 annotation_node = None 

419 default_node = None 

420 

421 def to_string(self): 

422 output = self._kind_string() + self.get_public_name() 

423 annotation = self.annotation_node 

424 default = self.default_node 

425 if annotation is not None: 

426 output += ': ' + annotation.get_code(include_prefix=False) 

427 if default is not None: 

428 output += '=' + default.get_code(include_prefix=False) 

429 return output 

430 

431 def get_public_name(self): 

432 name = self.string_name 

433 if name.startswith('__'): 

434 # Params starting with __ are an equivalent to positional only 

435 # variables in typeshed. 

436 name = name[2:] 

437 return name 

438 

439 def goto(self, **kwargs): 

440 return [self] 

441 

442 

443class _ActualTreeParamName(BaseTreeParamName): 

444 def __init__(self, function_value, tree_name): 

445 super().__init__( 

446 function_value.get_default_param_context(), tree_name) 

447 self.function_value = function_value 

448 

449 def _get_param_node(self): 

450 return self.tree_name.search_ancestor('param') 

451 

452 @property 

453 def annotation_node(self): 

454 return self._get_param_node().annotation 

455 

456 def infer_annotation(self, execute_annotation=True, ignore_stars=False): 

457 from jedi.inference.gradual.annotation import infer_param 

458 values = infer_param( 

459 self.function_value, self._get_param_node(), 

460 ignore_stars=ignore_stars) 

461 if execute_annotation: 

462 values = values.execute_annotation() 

463 return values 

464 

465 def infer_default(self): 

466 node = self.default_node 

467 if node is None: 

468 return NO_VALUES 

469 return self.parent_context.infer_node(node) 

470 

471 @property 

472 def default_node(self): 

473 return self._get_param_node().default 

474 

475 def get_kind(self): 

476 tree_param = self._get_param_node() 

477 if tree_param.star_count == 1: # *args 

478 return Parameter.VAR_POSITIONAL 

479 if tree_param.star_count == 2: # **kwargs 

480 return Parameter.VAR_KEYWORD 

481 

482 # Params starting with __ are an equivalent to positional only 

483 # variables in typeshed. 

484 if tree_param.name.value.startswith('__'): 

485 return Parameter.POSITIONAL_ONLY 

486 

487 parent = tree_param.parent 

488 param_appeared = False 

489 for p in parent.children: 

490 if param_appeared: 

491 if p == '/': 

492 return Parameter.POSITIONAL_ONLY 

493 else: 

494 if p == '*': 

495 return Parameter.KEYWORD_ONLY 

496 if p.type == 'param': 

497 if p.star_count: 

498 return Parameter.KEYWORD_ONLY 

499 if p == tree_param: 

500 param_appeared = True 

501 return Parameter.POSITIONAL_OR_KEYWORD 

502 

503 def infer(self): 

504 values = self.infer_annotation() 

505 if values: 

506 return values 

507 

508 doc_params = docstrings.infer_param(self.function_value, self._get_param_node()) 

509 return doc_params 

510 

511 

512class AnonymousParamName(_ActualTreeParamName): 

513 @plugin_manager.decorate(name='goto_anonymous_param') 

514 def goto(self): 

515 return super().goto() 

516 

517 @plugin_manager.decorate(name='infer_anonymous_param') 

518 def infer(self): 

519 values = super().infer() 

520 if values: 

521 return values 

522 from jedi.inference.dynamic_params import dynamic_param_lookup 

523 param = self._get_param_node() 

524 values = dynamic_param_lookup(self.function_value, param.position_index) 

525 if values: 

526 return values 

527 

528 if param.star_count == 1: 

529 from jedi.inference.value.iterable import FakeTuple 

530 value = FakeTuple(self.function_value.inference_state, []) 

531 elif param.star_count == 2: 

532 from jedi.inference.value.iterable import FakeDict 

533 value = FakeDict(self.function_value.inference_state, {}) 

534 elif param.default is None: 

535 return NO_VALUES 

536 else: 

537 return self.function_value.parent_context.infer_node(param.default) 

538 return ValueSet({value}) 

539 

540 

541class ParamName(_ActualTreeParamName): 

542 def __init__(self, function_value, tree_name, arguments): 

543 super().__init__(function_value, tree_name) 

544 self.arguments = arguments 

545 

546 def infer(self): 

547 values = super().infer() 

548 if values: 

549 return values 

550 

551 return self.get_executed_param_name().infer() 

552 

553 def get_executed_param_name(self): 

554 from jedi.inference.param import get_executed_param_names 

555 params_names = get_executed_param_names(self.function_value, self.arguments) 

556 return params_names[self._get_param_node().position_index] 

557 

558 

559class ParamNameWrapper(_ParamMixin): 

560 def __init__(self, param_name): 

561 self._wrapped_param_name = param_name 

562 

563 def __getattr__(self, name): 

564 return getattr(self._wrapped_param_name, name) 

565 

566 def __repr__(self): 

567 return '<%s: %s>' % (self.__class__.__name__, self._wrapped_param_name) 

568 

569 

570class ImportName(AbstractNameDefinition): 

571 start_pos = (1, 0) 

572 _level = 0 

573 

574 def __init__(self, parent_context, string_name): 

575 self._from_module_context = parent_context 

576 self.string_name = string_name 

577 

578 def get_qualified_names(self, include_module_names=False): 

579 if include_module_names: 

580 if self._level: 

581 assert self._level == 1, "Everything else is not supported for now" 

582 module_names = self._from_module_context.string_names 

583 if module_names is None: 

584 return module_names 

585 return module_names + (self.string_name,) 

586 return (self.string_name,) 

587 return () 

588 

589 @property 

590 def parent_context(self): 

591 m = self._from_module_context 

592 import_values = self.infer() 

593 if not import_values: 

594 return m 

595 # It's almost always possible to find the import or to not find it. The 

596 # importing returns only one value, pretty much always. 

597 return next(iter(import_values)).as_context() 

598 

599 @memoize_method 

600 def infer(self): 

601 from jedi.inference.imports import Importer 

602 m = self._from_module_context 

603 return Importer(m.inference_state, [self.string_name], m, level=self._level).follow() 

604 

605 def goto(self): 

606 return [m.name for m in self.infer()] 

607 

608 @property 

609 def api_type(self): 

610 return 'module' 

611 

612 def py__doc__(self): 

613 return _merge_name_docs(self.goto()) 

614 

615 

616class SubModuleName(ImportName): 

617 _level = 1 

618 

619 

620class NameWrapper: 

621 def __init__(self, wrapped_name): 

622 self._wrapped_name = wrapped_name 

623 

624 def __getattr__(self, name): 

625 return getattr(self._wrapped_name, name) 

626 

627 def __repr__(self): 

628 return '%s(%s)' % (self.__class__.__name__, self._wrapped_name) 

629 

630 

631class StubNameMixin: 

632 def py__doc__(self): 

633 from jedi.inference.gradual.conversion import convert_names 

634 # Stubs are not complicated and we can just follow simple statements 

635 # that have an equals in them, because they typically make something 

636 # else public. See e.g. stubs for `requests`. 

637 names = [self] 

638 if self.api_type == 'statement' and '=' in self.tree_name.get_definition().children: 

639 names = [v.name for v in self.infer()] 

640 

641 names = convert_names(names, prefer_stub_to_compiled=False) 

642 if self in names: 

643 return super().py__doc__() 

644 else: 

645 # We have signatures ourselves in stubs, so don't use signatures 

646 # from the implementation. 

647 return _merge_name_docs(names) 

648 

649 

650# From here on down we make looking up the sys.version_info fast. 

651class StubName(StubNameMixin, TreeNameDefinition): 

652 def infer(self): 

653 inferred = super().infer() 

654 if self.string_name == 'version_info' and self.get_root_context().py__name__() == 'sys': 

655 from jedi.inference.gradual.stub_value import VersionInfo 

656 return ValueSet(VersionInfo(c) for c in inferred) 

657 return inferred 

658 

659 

660class ModuleName(ValueNameMixin, AbstractNameDefinition): 

661 start_pos = 1, 0 

662 

663 def __init__(self, value, name): 

664 self._value = value 

665 self._name = name 

666 

667 @property 

668 def string_name(self): 

669 return self._name 

670 

671 

672class StubModuleName(StubNameMixin, ModuleName): 

673 pass