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

463 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 07:16 +0000

1from abc import abstractmethod 

2from inspect import Parameter 

3from typing import Optional, Tuple 

4 

5from parso.tree import search_ancestor 

6 

7from jedi.parser_utils import find_statement_documentation, clean_scope_docstring 

8from jedi.inference.utils import unite 

9from jedi.inference.base_value import ValueSet, NO_VALUES 

10from jedi.inference.cache import inference_state_method_cache 

11from jedi.inference import docstrings 

12from jedi.cache import memoize_method 

13from jedi.inference.helpers import deep_ast_copy, infer_call_of_leaf 

14from jedi.plugins import plugin_manager 

15 

16 

17def _merge_name_docs(names): 

18 doc = '' 

19 for name in names: 

20 if doc: 

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

22 # separated by a few dashes. 

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

24 doc += name.py__doc__() 

25 return doc 

26 

27 

28class AbstractNameDefinition: 

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

30 string_name: str 

31 parent_context = None 

32 tree_name = None 

33 is_value_name = True 

34 """ 

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

36 """ 

37 

38 @abstractmethod 

39 def infer(self): 

40 raise NotImplementedError 

41 

42 @abstractmethod 

43 def goto(self): 

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

45 # name will always result on itself. 

46 return {self} 

47 

48 def get_qualified_names(self, include_module_names=False): 

49 qualified_names = self._get_qualified_names() 

50 if qualified_names is None or not include_module_names: 

51 return qualified_names 

52 

53 module_names = self.get_root_context().string_names 

54 if module_names is None: 

55 return None 

56 return module_names + qualified_names 

57 

58 def _get_qualified_names(self): 

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

60 return None 

61 

62 def get_root_context(self): 

63 return self.parent_context.get_root_context() 

64 

65 def get_public_name(self): 

66 return self.string_name 

67 

68 def __repr__(self): 

69 if self.start_pos is None: 

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

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

72 self.string_name, self.start_pos) 

73 

74 def is_import(self): 

75 return False 

76 

77 def py__doc__(self): 

78 return '' 

79 

80 @property 

81 def api_type(self): 

82 return self.parent_context.api_type 

83 

84 def get_defining_qualified_value(self): 

85 """ 

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

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

88 """ 

89 return None 

90 

91 

92class AbstractArbitraryName(AbstractNameDefinition): 

93 """ 

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

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

96 concept of Name for completions as well. 

97 """ 

98 is_value_name = False 

99 

100 def __init__(self, inference_state, string): 

101 self.inference_state = inference_state 

102 self.string_name = string 

103 self.parent_context = inference_state.builtins_module 

104 

105 def infer(self): 

106 return NO_VALUES 

107 

108 

109class AbstractTreeName(AbstractNameDefinition): 

110 def __init__(self, parent_context, tree_name): 

111 self.parent_context = parent_context 

112 self.tree_name = tree_name 

113 

114 def get_qualified_names(self, include_module_names=False): 

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

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

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

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

119 # lookup. 

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

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

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

123 if include_module_names and not import_node.level: 

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

125 else: 

126 return None 

127 

128 return super().get_qualified_names(include_module_names) 

129 

130 def _get_qualified_names(self): 

131 parent_names = self.parent_context.get_qualified_names() 

132 if parent_names is None: 

133 return None 

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

135 

136 def get_defining_qualified_value(self): 

137 if self.is_import(): 

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

139 elif self.parent_context: 

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

141 return None 

142 

143 def goto(self): 

144 context = self.parent_context 

145 name = self.tree_name 

146 definition = name.get_definition(import_name_always=True) 

147 if definition is not None: 

148 type_ = definition.type 

149 if type_ == 'expr_stmt': 

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

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

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

153 if is_simple_name: 

154 return [self] 

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

156 from jedi.inference.imports import goto_import 

157 module_names = goto_import(context, name) 

158 return module_names 

159 else: 

160 return [self] 

161 else: 

162 from jedi.inference.imports import follow_error_node_imports_if_possible 

163 values = follow_error_node_imports_if_possible(context, name) 

164 if values is not None: 

165 return [value.name for value in values] 

166 

167 par = name.parent 

168 node_type = par.type 

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

170 # Named param goto. 

171 trailer = par.parent 

172 if trailer.type == 'arglist': 

173 trailer = trailer.parent 

174 if trailer.type != 'classdef': 

175 if trailer.type == 'decorator': 

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

177 else: 

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

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

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

181 to_infer.pop(0) 

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

183 from jedi.inference.syntax_tree import infer_trailer 

184 for trailer in to_infer[1:]: 

185 value_set = infer_trailer(context, value_set, trailer) 

186 param_names = [] 

187 for value in value_set: 

188 for signature in value.get_signatures(): 

189 for param_name in signature.get_param_names(): 

190 if param_name.string_name == name.value: 

191 param_names.append(param_name) 

192 return param_names 

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

194 index = par.children.index(name) 

195 if index > 0: 

196 new_dotted = deep_ast_copy(par) 

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

198 values = context.infer_node(new_dotted) 

199 return unite( 

200 value.goto(name, name_context=context) 

201 for value in values 

202 ) 

203 

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

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

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

207 else: 

208 stmt = search_ancestor( 

209 name, 'expr_stmt', 'lambdef' 

210 ) 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 = search_ancestor(self.tree_name, '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 def infer(self): 

230 return ValueSet([self._value]) 

231 

232 def py__doc__(self): 

233 doc = self._value.py__doc__() 

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

235 from jedi.inference.gradual.conversion import convert_names 

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

237 if self not in names: 

238 return _merge_name_docs(names) 

239 return doc 

240 

241 def _get_qualified_names(self): 

242 return self._value.get_qualified_names() 

243 

244 def get_root_context(self): 

245 if self.parent_context is None: # A module 

246 return self._value.as_context() 

247 return super().get_root_context() 

248 

249 def get_defining_qualified_value(self): 

250 context = self.parent_context 

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

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

253 return None 

254 

255 @property 

256 def api_type(self): 

257 return self._value.api_type 

258 

259 

260class ValueName(ValueNameMixin, AbstractTreeName): 

261 def __init__(self, value, tree_name): 

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

263 self._value = value 

264 

265 def goto(self): 

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

267 

268 

269class TreeNameDefinition(AbstractTreeName): 

270 _API_TYPES = dict( 

271 import_name='module', 

272 import_from='module', 

273 funcdef='function', 

274 param='param', 

275 classdef='class', 

276 ) 

277 

278 def infer(self): 

279 # Refactor this, should probably be here. 

280 from jedi.inference.syntax_tree import tree_name_to_values 

281 return tree_name_to_values( 

282 self.parent_context.inference_state, 

283 self.parent_context, 

284 self.tree_name 

285 ) 

286 

287 @property 

288 def api_type(self): 

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

290 if definition is None: 

291 return 'statement' 

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

293 

294 def assignment_indexes(self): 

295 """ 

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

297 tuple assignments. 

298 

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

300 

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

302 

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

304 

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

306 

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

308 """ 

309 indexes = [] 

310 is_star_expr = False 

311 node = self.tree_name.parent 

312 compare = self.tree_name 

313 while node is not None: 

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

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

316 if child == compare: 

317 index = int(i / 2) 

318 if is_star_expr: 

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

320 index = slice(index, -from_end) 

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

322 break 

323 else: 

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

325 is_star_expr = False 

326 elif node.type == 'star_expr': 

327 is_star_expr = True 

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

329 break 

330 

331 compare = node 

332 node = node.parent 

333 return indexes 

334 

335 @property 

336 def inference_state(self): 

337 # Used by the cache function below 

338 return self.parent_context.inference_state 

339 

340 @inference_state_method_cache(default='') 

341 def py__doc__(self): 

342 api_type = self.api_type 

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

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

345 from jedi.inference.gradual.conversion import convert_names 

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

347 if self not in names: 

348 return _merge_name_docs(names) 

349 

350 # Make sure the names are not TreeNameDefinitions anymore. 

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

352 

353 if api_type == 'module': 

354 names = self.goto() 

355 if self not in names: 

356 return _merge_name_docs(names) 

357 

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

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

360 return '' 

361 

362 

363class _ParamMixin: 

364 def maybe_positional_argument(self, include_star=True): 

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

366 if include_star: 

367 options.append(Parameter.VAR_POSITIONAL) 

368 return self.get_kind() in options 

369 

370 def maybe_keyword_argument(self, include_stars=True): 

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

372 if include_stars: 

373 options.append(Parameter.VAR_KEYWORD) 

374 return self.get_kind() in options 

375 

376 def _kind_string(self): 

377 kind = self.get_kind() 

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

379 return '*' 

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

381 return '**' 

382 return '' 

383 

384 def get_qualified_names(self, include_module_names=False): 

385 return None 

386 

387 

388class ParamNameInterface(_ParamMixin): 

389 api_type = 'param' 

390 

391 def get_kind(self): 

392 raise NotImplementedError 

393 

394 def to_string(self): 

395 raise NotImplementedError 

396 

397 def get_executed_param_name(self): 

398 """ 

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

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

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

402 

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

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

405 """ 

406 return None 

407 

408 @property 

409 def star_count(self): 

410 kind = self.get_kind() 

411 if kind == Parameter.VAR_POSITIONAL: 

412 return 1 

413 if kind == Parameter.VAR_KEYWORD: 

414 return 2 

415 return 0 

416 

417 def infer_default(self): 

418 return NO_VALUES 

419 

420 

421class BaseTreeParamName(ParamNameInterface, AbstractTreeName): 

422 annotation_node = None 

423 default_node = None 

424 

425 def to_string(self): 

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

427 annotation = self.annotation_node 

428 default = self.default_node 

429 if annotation is not None: 

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

431 if default is not None: 

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

433 return output 

434 

435 def get_public_name(self): 

436 name = self.string_name 

437 if name.startswith('__'): 

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

439 # variables in typeshed. 

440 name = name[2:] 

441 return name 

442 

443 def goto(self, **kwargs): 

444 return [self] 

445 

446 

447class _ActualTreeParamName(BaseTreeParamName): 

448 def __init__(self, function_value, tree_name): 

449 super().__init__( 

450 function_value.get_default_param_context(), tree_name) 

451 self.function_value = function_value 

452 

453 def _get_param_node(self): 

454 return search_ancestor(self.tree_name, 'param') 

455 

456 @property 

457 def annotation_node(self): 

458 return self._get_param_node().annotation 

459 

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

461 from jedi.inference.gradual.annotation import infer_param 

462 values = infer_param( 

463 self.function_value, self._get_param_node(), 

464 ignore_stars=ignore_stars) 

465 if execute_annotation: 

466 values = values.execute_annotation() 

467 return values 

468 

469 def infer_default(self): 

470 node = self.default_node 

471 if node is None: 

472 return NO_VALUES 

473 return self.parent_context.infer_node(node) 

474 

475 @property 

476 def default_node(self): 

477 return self._get_param_node().default 

478 

479 def get_kind(self): 

480 tree_param = self._get_param_node() 

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

482 return Parameter.VAR_POSITIONAL 

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

484 return Parameter.VAR_KEYWORD 

485 

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

487 # variables in typeshed. 

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

489 return Parameter.POSITIONAL_ONLY 

490 

491 parent = tree_param.parent 

492 param_appeared = False 

493 for p in parent.children: 

494 if param_appeared: 

495 if p == '/': 

496 return Parameter.POSITIONAL_ONLY 

497 else: 

498 if p == '*': 

499 return Parameter.KEYWORD_ONLY 

500 if p.type == 'param': 

501 if p.star_count: 

502 return Parameter.KEYWORD_ONLY 

503 if p == tree_param: 

504 param_appeared = True 

505 return Parameter.POSITIONAL_OR_KEYWORD 

506 

507 def infer(self): 

508 values = self.infer_annotation() 

509 if values: 

510 return values 

511 

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

513 return doc_params 

514 

515 

516class AnonymousParamName(_ActualTreeParamName): 

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

518 def goto(self): 

519 return super().goto() 

520 

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

522 def infer(self): 

523 values = super().infer() 

524 if values: 

525 return values 

526 from jedi.inference.dynamic_params import dynamic_param_lookup 

527 param = self._get_param_node() 

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

529 if values: 

530 return values 

531 

532 if param.star_count == 1: 

533 from jedi.inference.value.iterable import FakeTuple 

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

535 elif param.star_count == 2: 

536 from jedi.inference.value.iterable import FakeDict 

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

538 elif param.default is None: 

539 return NO_VALUES 

540 else: 

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

542 return ValueSet({value}) 

543 

544 

545class ParamName(_ActualTreeParamName): 

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

547 super().__init__(function_value, tree_name) 

548 self.arguments = arguments 

549 

550 def infer(self): 

551 values = super().infer() 

552 if values: 

553 return values 

554 

555 return self.get_executed_param_name().infer() 

556 

557 def get_executed_param_name(self): 

558 from jedi.inference.param import get_executed_param_names 

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

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

561 

562 

563class ParamNameWrapper(_ParamMixin): 

564 def __init__(self, param_name): 

565 self._wrapped_param_name = param_name 

566 

567 def __getattr__(self, name): 

568 return getattr(self._wrapped_param_name, name) 

569 

570 def __repr__(self): 

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

572 

573 

574class ImportName(AbstractNameDefinition): 

575 start_pos = (1, 0) 

576 _level = 0 

577 

578 def __init__(self, parent_context, string_name): 

579 self._from_module_context = parent_context 

580 self.string_name = string_name 

581 

582 def get_qualified_names(self, include_module_names=False): 

583 if include_module_names: 

584 if self._level: 

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

586 module_names = self._from_module_context.string_names 

587 if module_names is None: 

588 return module_names 

589 return module_names + (self.string_name,) 

590 return (self.string_name,) 

591 return () 

592 

593 @property 

594 def parent_context(self): 

595 m = self._from_module_context 

596 import_values = self.infer() 

597 if not import_values: 

598 return m 

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

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

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

602 

603 @memoize_method 

604 def infer(self): 

605 from jedi.inference.imports import Importer 

606 m = self._from_module_context 

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

608 

609 def goto(self): 

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

611 

612 @property 

613 def api_type(self): 

614 return 'module' 

615 

616 def py__doc__(self): 

617 return _merge_name_docs(self.goto()) 

618 

619 

620class SubModuleName(ImportName): 

621 _level = 1 

622 

623 

624class NameWrapper: 

625 def __init__(self, wrapped_name): 

626 self._wrapped_name = wrapped_name 

627 

628 def __getattr__(self, name): 

629 return getattr(self._wrapped_name, name) 

630 

631 def __repr__(self): 

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

633 

634 

635class StubNameMixin: 

636 def py__doc__(self): 

637 from jedi.inference.gradual.conversion import convert_names 

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

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

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

641 names = [self] 

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

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

644 

645 names = convert_names(names, prefer_stub_to_compiled=False) 

646 if self in names: 

647 return super().py__doc__() 

648 else: 

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

650 # from the implementation. 

651 return _merge_name_docs(names) 

652 

653 

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

655class StubName(StubNameMixin, TreeNameDefinition): 

656 def infer(self): 

657 inferred = super().infer() 

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

659 from jedi.inference.gradual.stub_value import VersionInfo 

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

661 return inferred 

662 

663 

664class ModuleName(ValueNameMixin, AbstractNameDefinition): 

665 start_pos = 1, 0 

666 

667 def __init__(self, value, name): 

668 self._value = value 

669 self._name = name 

670 

671 @property 

672 def string_name(self): 

673 return self._name 

674 

675 

676class StubModuleName(StubNameMixin, ModuleName): 

677 pass