Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/jedi-0.20.0-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

471 statements  

1from abc import abstractmethod 

2from inspect import Parameter 

3from typing import Optional, Tuple, Any 

4 

5from jedi.parser_utils import find_statement_documentation, clean_scope_docstring 

6from jedi.inference.base_value import ValueSet, NO_VALUES 

7from jedi.inference.cache import inference_state_method_cache 

8from jedi.inference import docstrings 

9from jedi.cache import memoize_method 

10from jedi.inference.helpers import deep_ast_copy, infer_call_of_leaf 

11from jedi.plugins import plugin_manager 

12 

13 

14def _merge_name_docs(names): 

15 doc = '' 

16 for name in names: 

17 if doc: 

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

19 # separated by a few dashes. 

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

21 doc += name.py__doc__() 

22 return doc 

23 

24 

25class AbstractNameDefinition: 

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

27 string_name: str 

28 parent_context = None 

29 tree_name = None 

30 is_value_name = True 

31 """ 

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

33 """ 

34 

35 @abstractmethod 

36 def infer(self): 

37 raise NotImplementedError 

38 

39 def goto(self): 

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

41 # name will always result on itself. 

42 return {self} 

43 

44 def get_qualified_names(self, include_module_names=False): 

45 qualified_names = self._get_qualified_names() 

46 if qualified_names is None or not include_module_names: 

47 return qualified_names 

48 

49 module_names = self.get_root_context().string_names 

50 if module_names is None: 

51 return None 

52 return module_names + qualified_names 

53 

54 def _get_qualified_names(self): 

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

56 return None 

57 

58 def get_root_context(self): 

59 return self.parent_context.get_root_context() 

60 

61 def get_public_name(self): 

62 return self.string_name 

63 

64 def __repr__(self): 

65 if self.start_pos is None: 

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

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

68 self.string_name, self.start_pos) 

69 

70 def is_import(self): 

71 return False 

72 

73 def py__doc__(self): 

74 return '' 

75 

76 @property 

77 def api_type(self): 

78 return self.parent_context.api_type 

79 

80 def get_defining_qualified_value(self): 

81 """ 

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

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

84 """ 

85 return None 

86 

87 

88class AbstractArbitraryName(AbstractNameDefinition): 

89 """ 

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

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

92 concept of Name for completions as well. 

93 """ 

94 is_value_name = False 

95 

96 def __init__(self, inference_state, string): 

97 self.inference_state = inference_state 

98 self.string_name = string 

99 self.parent_context = inference_state.builtins_module 

100 

101 def infer(self): 

102 return NO_VALUES 

103 

104 

105class AbstractTreeName(AbstractNameDefinition): 

106 tree_name: Any 

107 parent_context: Any 

108 

109 def __init__(self, parent_context, tree_name): 

110 self.parent_context = parent_context 

111 self.tree_name = tree_name 

112 

113 def get_qualified_names(self, include_module_names=False): 

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

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

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

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

118 # lookup. 

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

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

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

122 if include_module_names and not import_node.level: 

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

124 else: 

125 return None 

126 

127 return super().get_qualified_names(include_module_names) 

128 

129 def _get_qualified_names(self): 

130 parent_names = self.parent_context.get_qualified_names() 

131 if parent_names is None: 

132 return None 

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

134 

135 def get_defining_qualified_value(self): 

136 if self.is_import(): 

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

138 elif self.parent_context: 

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

140 return None 

141 

142 def goto(self): 

143 context = self.parent_context 

144 name = self.tree_name 

145 definition = name.get_definition(import_name_always=True) 

146 if definition is not None: 

147 type_ = definition.type 

148 if type_ == 'expr_stmt': 

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

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

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

152 if is_simple_name: 

153 return [self] 

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

155 from jedi.inference.imports import goto_import 

156 module_names = goto_import(context, name) 

157 return module_names 

158 else: 

159 return [self] 

160 else: 

161 from jedi.inference.imports import follow_error_node_imports_if_possible 

162 values = follow_error_node_imports_if_possible(context, name) 

163 if values is not None: 

164 return [value.name for value in values] 

165 

166 par = name.parent 

167 node_type = par.type 

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

169 # Named param goto. 

170 trailer = par.parent 

171 if trailer.type == 'arglist': 

172 trailer = trailer.parent 

173 if trailer.type == 'error_node': 

174 return [] 

175 if trailer.type != 'classdef': 

176 if trailer.type == 'decorator': 

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

178 else: 

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

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

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

182 to_infer.pop(0) 

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

184 from jedi.inference.syntax_tree import infer_trailer 

185 for trailer in to_infer[1:]: 

186 value_set = infer_trailer(context, value_set, trailer) 

187 param_names = [] 

188 for value in value_set: 

189 for signature in value.get_signatures(): 

190 for param_name in signature.get_param_names(): 

191 if param_name.string_name == name.value: 

192 param_names.append(param_name) 

193 return param_names 

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

195 index = par.children.index(name) 

196 if index > 0: 

197 new_dotted = deep_ast_copy(par) 

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

199 values = context.infer_node(new_dotted) 

200 return [ 

201 n 

202 for value in values 

203 for n in value.goto(name, name_context=context) 

204 ] 

205 

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

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

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

209 else: 

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

211 if stmt.type == 'lambdef': 

212 stmt = name 

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

214 

215 def is_import(self): 

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

217 return imp is not None 

218 

219 @property 

220 def string_name(self): 

221 return self.tree_name.value 

222 

223 @property 

224 def start_pos(self): 

225 return self.tree_name.start_pos 

226 

227 

228class ValueNameMixin: 

229 _value: Any 

230 parent_context: Any 

231 

232 def infer(self): 

233 return ValueSet([self._value]) 

234 

235 def py__doc__(self): 

236 doc = self._value.py__doc__() 

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

238 from jedi.inference.gradual.conversion import convert_names 

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

240 if self not in names: 

241 return _merge_name_docs(names) 

242 return doc 

243 

244 def _get_qualified_names(self): 

245 return self._value.get_qualified_names() 

246 

247 def get_root_context(self): 

248 if self.parent_context is None: # A module 

249 return self._value.as_context() 

250 return super().get_root_context() # type: ignore 

251 

252 def get_defining_qualified_value(self): 

253 context = self.parent_context 

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

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

256 return None 

257 

258 @property 

259 def api_type(self): 

260 return self._value.api_type 

261 

262 

263class ValueName(ValueNameMixin, AbstractTreeName): 

264 def __init__(self, value, tree_name): 

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

266 self._value = value 

267 

268 def goto(self): 

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

270 

271 

272class TreeNameDefinition(AbstractTreeName): 

273 _API_TYPES = dict( 

274 import_name='module', 

275 import_from='module', 

276 funcdef='function', 

277 param='param', 

278 classdef='class', 

279 ) 

280 

281 def infer(self): 

282 # Refactor this, should probably be here. 

283 from jedi.inference.syntax_tree import tree_name_to_values 

284 return tree_name_to_values( 

285 self.parent_context.inference_state, 

286 self.parent_context, 

287 self.tree_name 

288 ) 

289 

290 @property 

291 def api_type(self): 

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

293 if definition is None: 

294 return 'statement' 

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

296 

297 def assignment_indexes(self): 

298 """ 

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

300 tuple assignments. 

301 

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

303 

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

305 

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

307 

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

309 

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

311 """ 

312 indexes = [] 

313 is_star_expr = False 

314 node = self.tree_name.parent 

315 compare = self.tree_name 

316 while node is not None: 

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

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

319 if child == compare: 

320 index = int(i / 2) 

321 if is_star_expr: 

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

323 index = slice(index, -from_end) 

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

325 break 

326 else: 

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

328 is_star_expr = False 

329 elif node.type == 'star_expr': 

330 is_star_expr = True 

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

332 break 

333 

334 compare = node 

335 node = node.parent 

336 return indexes 

337 

338 @property 

339 def inference_state(self): 

340 # Used by the cache function below 

341 return self.parent_context.inference_state 

342 

343 @inference_state_method_cache(default='') 

344 def py__doc__(self): 

345 api_type = self.api_type 

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

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

348 from jedi.inference.gradual.conversion import convert_names 

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

350 if self not in names: 

351 return _merge_name_docs(names) 

352 

353 # Make sure the names are not TreeNameDefinitions anymore. 

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

355 

356 if api_type == 'module': 

357 names = self.goto() 

358 if self not in names: 

359 return _merge_name_docs(names) 

360 

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

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

363 return '' 

364 

365 

366class _ParamMixin: 

367 get_kind: Any 

368 

369 def maybe_positional_argument(self, include_star=True): 

370 options: list[int] = [Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD] 

371 if include_star: 

372 options.append(Parameter.VAR_POSITIONAL) 

373 return self.get_kind() in options 

374 

375 def maybe_keyword_argument(self, include_stars=True): 

376 options: list[int] = [Parameter.KEYWORD_ONLY, Parameter.POSITIONAL_OR_KEYWORD] 

377 if include_stars: 

378 options.append(Parameter.VAR_KEYWORD) 

379 return self.get_kind() in options 

380 

381 def _kind_string(self): 

382 kind = self.get_kind() 

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

384 return '*' 

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

386 return '**' 

387 return '' 

388 

389 def get_qualified_names(self, include_module_names=False): 

390 return None 

391 

392 

393class ParamNameInterface(_ParamMixin): 

394 api_type = 'param' 

395 

396 def get_kind(self): 

397 raise NotImplementedError 

398 

399 def to_string(self): 

400 raise NotImplementedError 

401 

402 def get_executed_param_name(self): 

403 """ 

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

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

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

407 

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

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

410 """ 

411 return None 

412 

413 @property 

414 def star_count(self): 

415 kind = self.get_kind() 

416 if kind == Parameter.VAR_POSITIONAL: 

417 return 1 

418 if kind == Parameter.VAR_KEYWORD: 

419 return 2 

420 return 0 

421 

422 def infer_default(self): 

423 return NO_VALUES 

424 

425 

426class BaseTreeParamName(ParamNameInterface, AbstractTreeName): 

427 annotation_node = None 

428 default_node = None 

429 

430 def to_string(self): 

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

432 annotation = self.annotation_node 

433 default = self.default_node 

434 if annotation is not None: 

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

436 if default is not None: 

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

438 return output 

439 

440 def get_public_name(self): 

441 name = self.string_name 

442 if name.startswith('__'): 

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

444 # variables in typeshed. 

445 name = name[2:] 

446 return name 

447 

448 def goto(self, **kwargs): 

449 return [self] 

450 

451 

452class _ActualTreeParamName(BaseTreeParamName): 

453 def __init__(self, function_value, tree_name): 

454 super().__init__( 

455 function_value.get_default_param_context(), tree_name) 

456 self.function_value = function_value 

457 

458 def _get_param_node(self): 

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

460 

461 @property 

462 def annotation_node(self): 

463 return self._get_param_node().annotation 

464 

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

466 from jedi.inference.gradual.annotation import infer_param 

467 values = infer_param( 

468 self.function_value, self._get_param_node(), 

469 ignore_stars=ignore_stars) 

470 if execute_annotation: 

471 values = values.execute_annotation(self.function_value.get_default_param_context()) 

472 return values 

473 

474 def infer_default(self): 

475 node = self.default_node 

476 if node is None: 

477 return NO_VALUES 

478 return self.parent_context.infer_node(node) 

479 

480 @property 

481 def default_node(self): 

482 return self._get_param_node().default 

483 

484 def get_kind(self): 

485 tree_param = self._get_param_node() 

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

487 return Parameter.VAR_POSITIONAL 

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

489 return Parameter.VAR_KEYWORD 

490 

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

492 # variables in typeshed. 

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

494 return Parameter.POSITIONAL_ONLY 

495 

496 parent = tree_param.parent 

497 param_appeared = False 

498 for p in parent.children: 

499 if param_appeared: 

500 if p == '/': 

501 return Parameter.POSITIONAL_ONLY 

502 else: 

503 if p == '*': 

504 return Parameter.KEYWORD_ONLY 

505 if p.type == 'param': 

506 if p.star_count: 

507 return Parameter.KEYWORD_ONLY 

508 if p == tree_param: 

509 param_appeared = True 

510 return Parameter.POSITIONAL_OR_KEYWORD 

511 

512 def infer(self): 

513 values = self.infer_annotation() 

514 if values: 

515 return values 

516 

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

518 return doc_params 

519 

520 

521class AnonymousParamName(_ActualTreeParamName): 

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

523 def goto(self): 

524 return super().goto() 

525 

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

527 def infer(self): 

528 values = super().infer() 

529 if values: 

530 return values 

531 from jedi.inference.dynamic_params import dynamic_param_lookup 

532 param = self._get_param_node() 

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

534 if values: 

535 return values 

536 

537 if param.star_count == 1: 

538 from jedi.inference.value.iterable import FakeTuple 

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

540 elif param.star_count == 2: 

541 from jedi.inference.value.iterable import FakeDict 

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

543 elif param.default is None: 

544 return NO_VALUES 

545 else: 

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

547 return ValueSet({value}) 

548 

549 

550class ParamName(_ActualTreeParamName): 

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

552 super().__init__(function_value, tree_name) 

553 self.arguments = arguments 

554 

555 def infer(self): 

556 values = super().infer() 

557 if values: 

558 return values 

559 

560 return self.get_executed_param_name().infer() 

561 

562 def get_executed_param_name(self): 

563 from jedi.inference.param import get_executed_param_names 

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

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

566 

567 

568class ParamNameWrapper(_ParamMixin): 

569 def __init__(self, param_name): 

570 self._wrapped_param_name = param_name 

571 

572 def __getattr__(self, name): 

573 return getattr(self._wrapped_param_name, name) 

574 

575 def __repr__(self): 

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

577 

578 

579class ImportName(AbstractNameDefinition): 

580 start_pos = (1, 0) 

581 _level = 0 

582 

583 def __init__(self, parent_context, string_name): 

584 self._from_module_context = parent_context 

585 self.string_name = string_name 

586 

587 def get_qualified_names(self, include_module_names=False): 

588 if include_module_names: 

589 if self._level: 

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

591 module_names = self._from_module_context.string_names 

592 if module_names is None: 

593 return module_names 

594 return module_names + (self.string_name,) 

595 return (self.string_name,) 

596 return () 

597 

598 @property 

599 def parent_context(self): 

600 m = self._from_module_context 

601 import_values = self.infer() 

602 if not import_values: 

603 return m 

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

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

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

607 

608 @memoize_method 

609 def infer(self): 

610 from jedi.inference.imports import Importer 

611 m = self._from_module_context 

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

613 

614 def goto(self): 

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

616 

617 @property 

618 def api_type(self): 

619 return 'module' 

620 

621 def py__doc__(self): 

622 return _merge_name_docs(self.goto()) 

623 

624 

625class SubModuleName(ImportName): 

626 _level = 1 

627 

628 

629class NameWrapper: 

630 def __init__(self, wrapped_name): 

631 self._wrapped_name = wrapped_name 

632 

633 def __getattr__(self, name): 

634 return getattr(self._wrapped_name, name) 

635 

636 def __repr__(self): 

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

638 

639 

640class StubNameMixin: 

641 api_type: str 

642 tree_name: Any 

643 infer: Any 

644 

645 def py__doc__(self): 

646 from jedi.inference.gradual.conversion import convert_names 

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

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

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

650 names = [self] 

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

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

653 

654 names = convert_names(names, prefer_stub_to_compiled=False) 

655 if self in names: 

656 return super().py__doc__() # type: ignore 

657 else: 

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

659 # from the implementation. 

660 return _merge_name_docs(names) 

661 

662 

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

664class StubName(StubNameMixin, TreeNameDefinition): 

665 def infer(self): 

666 inferred = super().infer() 

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

668 from jedi.inference.gradual.stub_value import VersionInfo 

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

670 return inferred 

671 

672 

673class ModuleName(ValueNameMixin, AbstractNameDefinition): 

674 start_pos = 1, 0 

675 

676 def __init__(self, value, name): 

677 self._value = value 

678 self._name = name 

679 

680 @property 

681 def string_name(self): 

682 return self._name 

683 

684 

685class StubModuleName(StubNameMixin, ModuleName): 

686 pass