Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jedi/inference/compiled/value.py: 32%

391 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

1""" 

2Imitate the parser representation. 

3""" 

4import re 

5from functools import partial 

6from inspect import Parameter 

7from pathlib import Path 

8from typing import Optional 

9 

10from jedi import debug 

11from jedi.inference.utils import to_list 

12from jedi.cache import memoize_method 

13from jedi.inference.filters import AbstractFilter 

14from jedi.inference.names import AbstractNameDefinition, ValueNameMixin, \ 

15 ParamNameInterface 

16from jedi.inference.base_value import Value, ValueSet, NO_VALUES 

17from jedi.inference.lazy_value import LazyKnownValue 

18from jedi.inference.compiled.access import _sentinel 

19from jedi.inference.cache import inference_state_function_cache 

20from jedi.inference.helpers import reraise_getitem_errors 

21from jedi.inference.signature import BuiltinSignature 

22from jedi.inference.context import CompiledContext, CompiledModuleContext 

23 

24 

25class CheckAttribute: 

26 """Raises :exc:`AttributeError` if the attribute X is not available.""" 

27 def __init__(self, check_name=None): 

28 # Remove the py in front of e.g. py__call__. 

29 self.check_name = check_name 

30 

31 def __call__(self, func): 

32 self.func = func 

33 if self.check_name is None: 

34 self.check_name = func.__name__[2:] 

35 return self 

36 

37 def __get__(self, instance, owner): 

38 if instance is None: 

39 return self 

40 

41 # This might raise an AttributeError. That's wanted. 

42 instance.access_handle.getattr_paths(self.check_name) 

43 return partial(self.func, instance) 

44 

45 

46class CompiledValue(Value): 

47 def __init__(self, inference_state, access_handle, parent_context=None): 

48 super().__init__(inference_state, parent_context) 

49 self.access_handle = access_handle 

50 

51 def py__call__(self, arguments): 

52 return_annotation = self.access_handle.get_return_annotation() 

53 if return_annotation is not None: 

54 return create_from_access_path( 

55 self.inference_state, 

56 return_annotation 

57 ).execute_annotation() 

58 

59 try: 

60 self.access_handle.getattr_paths('__call__') 

61 except AttributeError: 

62 return super().py__call__(arguments) 

63 else: 

64 if self.access_handle.is_class(): 

65 from jedi.inference.value import CompiledInstance 

66 return ValueSet([ 

67 CompiledInstance(self.inference_state, self.parent_context, self, arguments) 

68 ]) 

69 else: 

70 return ValueSet(self._execute_function(arguments)) 

71 

72 @CheckAttribute() 

73 def py__class__(self): 

74 return create_from_access_path(self.inference_state, self.access_handle.py__class__()) 

75 

76 @CheckAttribute() 

77 def py__mro__(self): 

78 return (self,) + tuple( 

79 create_from_access_path(self.inference_state, access) 

80 for access in self.access_handle.py__mro__accesses() 

81 ) 

82 

83 @CheckAttribute() 

84 def py__bases__(self): 

85 return tuple( 

86 create_from_access_path(self.inference_state, access) 

87 for access in self.access_handle.py__bases__() 

88 ) 

89 

90 def get_qualified_names(self): 

91 return self.access_handle.get_qualified_names() 

92 

93 def py__bool__(self): 

94 return self.access_handle.py__bool__() 

95 

96 def is_class(self): 

97 return self.access_handle.is_class() 

98 

99 def is_function(self): 

100 return self.access_handle.is_function() 

101 

102 def is_module(self): 

103 return self.access_handle.is_module() 

104 

105 def is_compiled(self): 

106 return True 

107 

108 def is_stub(self): 

109 return False 

110 

111 def is_instance(self): 

112 return self.access_handle.is_instance() 

113 

114 def py__doc__(self): 

115 return self.access_handle.py__doc__() 

116 

117 @to_list 

118 def get_param_names(self): 

119 try: 

120 signature_params = self.access_handle.get_signature_params() 

121 except ValueError: # Has no signature 

122 params_str, ret = self._parse_function_doc() 

123 if not params_str: 

124 tokens = [] 

125 else: 

126 tokens = params_str.split(',') 

127 if self.access_handle.ismethoddescriptor(): 

128 tokens.insert(0, 'self') 

129 for p in tokens: 

130 name, _, default = p.strip().partition('=') 

131 yield UnresolvableParamName(self, name, default) 

132 else: 

133 for signature_param in signature_params: 

134 yield SignatureParamName(self, signature_param) 

135 

136 def get_signatures(self): 

137 _, return_string = self._parse_function_doc() 

138 return [BuiltinSignature(self, return_string)] 

139 

140 def __repr__(self): 

141 return '<%s: %s>' % (self.__class__.__name__, self.access_handle.get_repr()) 

142 

143 @memoize_method 

144 def _parse_function_doc(self): 

145 doc = self.py__doc__() 

146 if doc is None: 

147 return '', '' 

148 

149 return _parse_function_doc(doc) 

150 

151 @property 

152 def api_type(self): 

153 return self.access_handle.get_api_type() 

154 

155 def get_filters(self, is_instance=False, origin_scope=None): 

156 yield self._ensure_one_filter(is_instance) 

157 

158 @memoize_method 

159 def _ensure_one_filter(self, is_instance): 

160 return CompiledValueFilter(self.inference_state, self, is_instance) 

161 

162 def py__simple_getitem__(self, index): 

163 with reraise_getitem_errors(IndexError, KeyError, TypeError): 

164 try: 

165 access = self.access_handle.py__simple_getitem__( 

166 index, 

167 safe=not self.inference_state.allow_unsafe_executions 

168 ) 

169 except AttributeError: 

170 return super().py__simple_getitem__(index) 

171 if access is None: 

172 return super().py__simple_getitem__(index) 

173 

174 return ValueSet([create_from_access_path(self.inference_state, access)]) 

175 

176 def py__getitem__(self, index_value_set, contextualized_node): 

177 all_access_paths = self.access_handle.py__getitem__all_values() 

178 if all_access_paths is None: 

179 # This means basically that no __getitem__ has been defined on this 

180 # object. 

181 return super().py__getitem__(index_value_set, contextualized_node) 

182 return ValueSet( 

183 create_from_access_path(self.inference_state, access) 

184 for access in all_access_paths 

185 ) 

186 

187 def py__iter__(self, contextualized_node=None): 

188 if not self.access_handle.has_iter(): 

189 yield from super().py__iter__(contextualized_node) 

190 

191 access_path_list = self.access_handle.py__iter__list() 

192 if access_path_list is None: 

193 # There is no __iter__ method on this object. 

194 return 

195 

196 for access in access_path_list: 

197 yield LazyKnownValue(create_from_access_path(self.inference_state, access)) 

198 

199 def py__name__(self): 

200 return self.access_handle.py__name__() 

201 

202 @property 

203 def name(self): 

204 name = self.py__name__() 

205 if name is None: 

206 name = self.access_handle.get_repr() 

207 return CompiledValueName(self, name) 

208 

209 def _execute_function(self, params): 

210 from jedi.inference import docstrings 

211 from jedi.inference.compiled import builtin_from_name 

212 if self.api_type != 'function': 

213 return 

214 

215 for name in self._parse_function_doc()[1].split(): 

216 try: 

217 # TODO wtf is this? this is exactly the same as the thing 

218 # below. It uses getattr as well. 

219 self.inference_state.builtins_module.access_handle.getattr_paths(name) 

220 except AttributeError: 

221 continue 

222 else: 

223 bltn_obj = builtin_from_name(self.inference_state, name) 

224 yield from self.inference_state.execute(bltn_obj, params) 

225 yield from docstrings.infer_return_types(self) 

226 

227 def get_safe_value(self, default=_sentinel): 

228 try: 

229 return self.access_handle.get_safe_value() 

230 except ValueError: 

231 if default == _sentinel: 

232 raise 

233 return default 

234 

235 def execute_operation(self, other, operator): 

236 try: 

237 return ValueSet([create_from_access_path( 

238 self.inference_state, 

239 self.access_handle.execute_operation(other.access_handle, operator) 

240 )]) 

241 except TypeError: 

242 return NO_VALUES 

243 

244 def execute_annotation(self): 

245 if self.access_handle.get_repr() == 'None': 

246 # None as an annotation doesn't need to be executed. 

247 return ValueSet([self]) 

248 

249 name, args = self.access_handle.get_annotation_name_and_args() 

250 arguments = [ 

251 ValueSet([create_from_access_path(self.inference_state, path)]) 

252 for path in args 

253 ] 

254 if name == 'Union': 

255 return ValueSet.from_sets(arg.execute_annotation() for arg in arguments) 

256 elif name: 

257 # While with_generics only exists on very specific objects, we 

258 # should probably be fine, because we control all the typing 

259 # objects. 

260 return ValueSet([ 

261 v.with_generics(arguments) 

262 for v in self.inference_state.typing_module.py__getattribute__(name) 

263 ]).execute_annotation() 

264 return super().execute_annotation() 

265 

266 def negate(self): 

267 return create_from_access_path(self.inference_state, self.access_handle.negate()) 

268 

269 def get_metaclasses(self): 

270 return NO_VALUES 

271 

272 def _as_context(self): 

273 return CompiledContext(self) 

274 

275 @property 

276 def array_type(self): 

277 return self.access_handle.get_array_type() 

278 

279 def get_key_values(self): 

280 return [ 

281 create_from_access_path(self.inference_state, k) 

282 for k in self.access_handle.get_key_paths() 

283 ] 

284 

285 def get_type_hint(self, add_class_info=True): 

286 if self.access_handle.get_repr() in ('None', "<class 'NoneType'>"): 

287 return 'None' 

288 return None 

289 

290 

291class CompiledModule(CompiledValue): 

292 file_io = None # For modules 

293 

294 def _as_context(self): 

295 return CompiledModuleContext(self) 

296 

297 def py__path__(self): 

298 return self.access_handle.py__path__() 

299 

300 def is_package(self): 

301 return self.py__path__() is not None 

302 

303 @property 

304 def string_names(self): 

305 # For modules 

306 name = self.py__name__() 

307 if name is None: 

308 return () 

309 return tuple(name.split('.')) 

310 

311 def py__file__(self) -> Optional[Path]: 

312 return self.access_handle.py__file__() # type: ignore[no-any-return] 

313 

314 

315class CompiledName(AbstractNameDefinition): 

316 def __init__(self, inference_state, parent_value, name, is_descriptor): 

317 self._inference_state = inference_state 

318 self.parent_context = parent_value.as_context() 

319 self._parent_value = parent_value 

320 self.string_name = name 

321 self.is_descriptor = is_descriptor 

322 

323 def py__doc__(self): 

324 return self.infer_compiled_value().py__doc__() 

325 

326 def _get_qualified_names(self): 

327 parent_qualified_names = self.parent_context.get_qualified_names() 

328 if parent_qualified_names is None: 

329 return None 

330 return parent_qualified_names + (self.string_name,) 

331 

332 def get_defining_qualified_value(self): 

333 context = self.parent_context 

334 if context.is_module() or context.is_class(): 

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

336 

337 return None 

338 

339 def __repr__(self): 

340 try: 

341 name = self.parent_context.name # __name__ is not defined all the time 

342 except AttributeError: 

343 name = None 

344 return '<%s: (%s).%s>' % (self.__class__.__name__, name, self.string_name) 

345 

346 @property 

347 def api_type(self): 

348 if self.is_descriptor: 

349 # In case of properties we want to avoid executions as much as 

350 # possible. Since the api_type can be wrong for other reasons 

351 # anyway, we just return instance here. 

352 return "instance" 

353 return self.infer_compiled_value().api_type 

354 

355 def infer(self): 

356 return ValueSet([self.infer_compiled_value()]) 

357 

358 @memoize_method 

359 def infer_compiled_value(self): 

360 return create_from_name(self._inference_state, self._parent_value, self.string_name) 

361 

362 

363class SignatureParamName(ParamNameInterface, AbstractNameDefinition): 

364 def __init__(self, compiled_value, signature_param): 

365 self.parent_context = compiled_value.parent_context 

366 self._signature_param = signature_param 

367 

368 @property 

369 def string_name(self): 

370 return self._signature_param.name 

371 

372 def to_string(self): 

373 s = self._kind_string() + self.string_name 

374 if self._signature_param.has_annotation: 

375 s += ': ' + self._signature_param.annotation_string 

376 if self._signature_param.has_default: 

377 s += '=' + self._signature_param.default_string 

378 return s 

379 

380 def get_kind(self): 

381 return getattr(Parameter, self._signature_param.kind_name) 

382 

383 def infer(self): 

384 p = self._signature_param 

385 inference_state = self.parent_context.inference_state 

386 values = NO_VALUES 

387 if p.has_default: 

388 values = ValueSet([create_from_access_path(inference_state, p.default)]) 

389 if p.has_annotation: 

390 annotation = create_from_access_path(inference_state, p.annotation) 

391 values |= annotation.execute_with_values() 

392 return values 

393 

394 

395class UnresolvableParamName(ParamNameInterface, AbstractNameDefinition): 

396 def __init__(self, compiled_value, name, default): 

397 self.parent_context = compiled_value.parent_context 

398 self.string_name = name 

399 self._default = default 

400 

401 def get_kind(self): 

402 return Parameter.POSITIONAL_ONLY 

403 

404 def to_string(self): 

405 string = self.string_name 

406 if self._default: 

407 string += '=' + self._default 

408 return string 

409 

410 def infer(self): 

411 return NO_VALUES 

412 

413 

414class CompiledValueName(ValueNameMixin, AbstractNameDefinition): 

415 def __init__(self, value, name): 

416 self.string_name = name 

417 self._value = value 

418 self.parent_context = value.parent_context 

419 

420 

421class EmptyCompiledName(AbstractNameDefinition): 

422 """ 

423 Accessing some names will raise an exception. To avoid not having any 

424 completions, just give Jedi the option to return this object. It infers to 

425 nothing. 

426 """ 

427 def __init__(self, inference_state, name): 

428 self.parent_context = inference_state.builtins_module 

429 self.string_name = name 

430 

431 def infer(self): 

432 return NO_VALUES 

433 

434 

435class CompiledValueFilter(AbstractFilter): 

436 def __init__(self, inference_state, compiled_value, is_instance=False): 

437 self._inference_state = inference_state 

438 self.compiled_value = compiled_value 

439 self.is_instance = is_instance 

440 

441 def get(self, name): 

442 access_handle = self.compiled_value.access_handle 

443 safe = not self._inference_state.allow_unsafe_executions 

444 return self._get( 

445 name, 

446 lambda name: access_handle.is_allowed_getattr(name, safe=safe), 

447 lambda name: name in access_handle.dir(), 

448 check_has_attribute=True 

449 ) 

450 

451 def _get(self, name, allowed_getattr_callback, in_dir_callback, check_has_attribute=False): 

452 """ 

453 To remove quite a few access calls we introduced the callback here. 

454 """ 

455 has_attribute, is_descriptor, property_return_annotation = allowed_getattr_callback( 

456 name, 

457 ) 

458 if property_return_annotation is not None: 

459 values = create_from_access_path( 

460 self._inference_state, 

461 property_return_annotation 

462 ).execute_annotation() 

463 if values: 

464 return [CompiledValueName(v, name) for v in values] 

465 

466 if check_has_attribute and not has_attribute: 

467 return [] 

468 

469 if (is_descriptor or not has_attribute) \ 

470 and not self._inference_state.allow_unsafe_executions: 

471 return [self._get_cached_name(name, is_empty=True)] 

472 

473 if self.is_instance and not in_dir_callback(name): 

474 return [] 

475 return [self._get_cached_name(name, is_descriptor=is_descriptor)] 

476 

477 @memoize_method 

478 def _get_cached_name(self, name, is_empty=False, *, is_descriptor=False): 

479 if is_empty: 

480 return EmptyCompiledName(self._inference_state, name) 

481 else: 

482 return self._create_name(name, is_descriptor=is_descriptor) 

483 

484 def values(self): 

485 from jedi.inference.compiled import builtin_from_name 

486 names = [] 

487 needs_type_completions, dir_infos = self.compiled_value.access_handle.get_dir_infos() 

488 # We could use `safe=False` here as well, especially as a parameter to 

489 # get_dir_infos. But this would lead to a lot of property executions 

490 # that are probably not wanted. The drawback for this is that we 

491 # have a different name for `get` and `values`. For `get` we always 

492 # execute. 

493 for name in dir_infos: 

494 names += self._get( 

495 name, 

496 lambda name: dir_infos[name], 

497 lambda name: name in dir_infos, 

498 ) 

499 

500 # ``dir`` doesn't include the type names. 

501 if not self.is_instance and needs_type_completions: 

502 for filter in builtin_from_name(self._inference_state, 'type').get_filters(): 

503 names += filter.values() 

504 return names 

505 

506 def _create_name(self, name, is_descriptor): 

507 return CompiledName( 

508 self._inference_state, 

509 self.compiled_value, 

510 name, 

511 is_descriptor, 

512 ) 

513 

514 def __repr__(self): 

515 return "<%s: %s>" % (self.__class__.__name__, self.compiled_value) 

516 

517 

518docstr_defaults = { 

519 'floating point number': 'float', 

520 'character': 'str', 

521 'integer': 'int', 

522 'dictionary': 'dict', 

523 'string': 'str', 

524} 

525 

526 

527def _parse_function_doc(doc): 

528 """ 

529 Takes a function and returns the params and return value as a tuple. 

530 This is nothing more than a docstring parser. 

531 

532 TODO docstrings like utime(path, (atime, mtime)) and a(b [, b]) -> None 

533 TODO docstrings like 'tuple of integers' 

534 """ 

535 # parse round parentheses: def func(a, (b,c)) 

536 try: 

537 count = 0 

538 start = doc.index('(') 

539 for i, s in enumerate(doc[start:]): 

540 if s == '(': 

541 count += 1 

542 elif s == ')': 

543 count -= 1 

544 if count == 0: 

545 end = start + i 

546 break 

547 param_str = doc[start + 1:end] 

548 except (ValueError, UnboundLocalError): 

549 # ValueError for doc.index 

550 # UnboundLocalError for undefined end in last line 

551 debug.dbg('no brackets found - no param') 

552 end = 0 

553 param_str = '' 

554 else: 

555 # remove square brackets, that show an optional param ( = None) 

556 def change_options(m): 

557 args = m.group(1).split(',') 

558 for i, a in enumerate(args): 

559 if a and '=' not in a: 

560 args[i] += '=None' 

561 return ','.join(args) 

562 

563 while True: 

564 param_str, changes = re.subn(r' ?\[([^\[\]]+)\]', 

565 change_options, param_str) 

566 if changes == 0: 

567 break 

568 param_str = param_str.replace('-', '_') # see: isinstance.__doc__ 

569 

570 # parse return value 

571 r = re.search('-[>-]* ', doc[end:end + 7]) 

572 if r is None: 

573 ret = '' 

574 else: 

575 index = end + r.end() 

576 # get result type, which can contain newlines 

577 pattern = re.compile(r'(,\n|[^\n-])+') 

578 ret_str = pattern.match(doc, index).group(0).strip() 

579 # New object -> object() 

580 ret_str = re.sub(r'[nN]ew (.*)', r'\1()', ret_str) 

581 

582 ret = docstr_defaults.get(ret_str, ret_str) 

583 

584 return param_str, ret 

585 

586 

587def create_from_name(inference_state, compiled_value, name): 

588 access_paths = compiled_value.access_handle.getattr_paths(name, default=None) 

589 

590 value = None 

591 for access_path in access_paths: 

592 value = create_cached_compiled_value( 

593 inference_state, 

594 access_path, 

595 parent_context=None if value is None else value.as_context(), 

596 ) 

597 return value 

598 

599 

600def _normalize_create_args(func): 

601 """The cache doesn't care about keyword vs. normal args.""" 

602 def wrapper(inference_state, obj, parent_context=None): 

603 return func(inference_state, obj, parent_context) 

604 return wrapper 

605 

606 

607def create_from_access_path(inference_state, access_path): 

608 value = None 

609 for name, access in access_path.accesses: 

610 value = create_cached_compiled_value( 

611 inference_state, 

612 access, 

613 parent_context=None if value is None else value.as_context() 

614 ) 

615 return value 

616 

617 

618@_normalize_create_args 

619@inference_state_function_cache() 

620def create_cached_compiled_value(inference_state, access_handle, parent_context): 

621 assert not isinstance(parent_context, CompiledValue) 

622 if parent_context is None: 

623 cls = CompiledModule 

624 else: 

625 cls = CompiledValue 

626 return cls(inference_state, access_handle, parent_context)