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

395 statements  

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

1""" 

2Contains all classes and functions to deal with lists, dicts, generators and 

3iterators in general. 

4""" 

5from jedi.inference import compiled 

6from jedi.inference import analysis 

7from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues, \ 

8 LazyTreeValue 

9from jedi.inference.helpers import get_int_or_none, is_string, \ 

10 reraise_getitem_errors, SimpleGetItemNotFound 

11from jedi.inference.utils import safe_property, to_list 

12from jedi.inference.cache import inference_state_method_cache 

13from jedi.inference.filters import LazyAttributeOverwrite, publish_method 

14from jedi.inference.base_value import ValueSet, Value, NO_VALUES, \ 

15 ContextualizedNode, iterate_values, sentinel, \ 

16 LazyValueWrapper 

17from jedi.parser_utils import get_sync_comp_fors 

18from jedi.inference.context import CompForContext 

19from jedi.inference.value.dynamic_arrays import check_array_additions 

20 

21 

22class IterableMixin: 

23 def py__next__(self, contextualized_node=None): 

24 return self.py__iter__(contextualized_node) 

25 

26 def py__stop_iteration_returns(self): 

27 return ValueSet([compiled.builtin_from_name(self.inference_state, 'None')]) 

28 

29 # At the moment, safe values are simple values like "foo", 1 and not 

30 # lists/dicts. Therefore as a small speed optimization we can just do the 

31 # default instead of resolving the lazy wrapped values, that are just 

32 # doing this in the end as well. 

33 # This mostly speeds up patterns like `sys.version_info >= (3, 0)` in 

34 # typeshed. 

35 get_safe_value = Value.get_safe_value 

36 

37 

38class GeneratorBase(LazyAttributeOverwrite, IterableMixin): 

39 array_type = None 

40 

41 def _get_wrapped_value(self): 

42 instance, = self._get_cls().execute_annotation() 

43 return instance 

44 

45 def _get_cls(self): 

46 generator, = self.inference_state.typing_module.py__getattribute__('Generator') 

47 return generator 

48 

49 def py__bool__(self): 

50 return True 

51 

52 @publish_method('__iter__') 

53 def _iter(self, arguments): 

54 return ValueSet([self]) 

55 

56 @publish_method('send') 

57 @publish_method('__next__') 

58 def _next(self, arguments): 

59 return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__()) 

60 

61 def py__stop_iteration_returns(self): 

62 return ValueSet([compiled.builtin_from_name(self.inference_state, 'None')]) 

63 

64 @property 

65 def name(self): 

66 return compiled.CompiledValueName(self, 'Generator') 

67 

68 def get_annotated_class_object(self): 

69 from jedi.inference.gradual.generics import TupleGenericManager 

70 gen_values = self.merge_types_of_iterate().py__class__() 

71 gm = TupleGenericManager((gen_values, NO_VALUES, NO_VALUES)) 

72 return self._get_cls().with_generics(gm) 

73 

74 

75class Generator(GeneratorBase): 

76 """Handling of `yield` functions.""" 

77 def __init__(self, inference_state, func_execution_context): 

78 super().__init__(inference_state) 

79 self._func_execution_context = func_execution_context 

80 

81 def py__iter__(self, contextualized_node=None): 

82 iterators = self._func_execution_context.infer_annotations() 

83 if iterators: 

84 return iterators.iterate(contextualized_node) 

85 return self._func_execution_context.get_yield_lazy_values() 

86 

87 def py__stop_iteration_returns(self): 

88 return self._func_execution_context.get_return_values() 

89 

90 def __repr__(self): 

91 return "<%s of %s>" % (type(self).__name__, self._func_execution_context) 

92 

93 

94def comprehension_from_atom(inference_state, value, atom): 

95 bracket = atom.children[0] 

96 test_list_comp = atom.children[1] 

97 

98 if bracket == '{': 

99 if atom.children[1].children[1] == ':': 

100 sync_comp_for = test_list_comp.children[3] 

101 if sync_comp_for.type == 'comp_for': 

102 sync_comp_for = sync_comp_for.children[1] 

103 

104 return DictComprehension( 

105 inference_state, 

106 value, 

107 sync_comp_for_node=sync_comp_for, 

108 key_node=test_list_comp.children[0], 

109 value_node=test_list_comp.children[2], 

110 ) 

111 else: 

112 cls = SetComprehension 

113 elif bracket == '(': 

114 cls = GeneratorComprehension 

115 elif bracket == '[': 

116 cls = ListComprehension 

117 

118 sync_comp_for = test_list_comp.children[1] 

119 if sync_comp_for.type == 'comp_for': 

120 sync_comp_for = sync_comp_for.children[1] 

121 

122 return cls( 

123 inference_state, 

124 defining_context=value, 

125 sync_comp_for_node=sync_comp_for, 

126 entry_node=test_list_comp.children[0], 

127 ) 

128 

129 

130class ComprehensionMixin: 

131 @inference_state_method_cache() 

132 def _get_comp_for_context(self, parent_context, comp_for): 

133 return CompForContext(parent_context, comp_for) 

134 

135 def _nested(self, comp_fors, parent_context=None): 

136 comp_for = comp_fors[0] 

137 

138 is_async = comp_for.parent.type == 'comp_for' 

139 

140 input_node = comp_for.children[3] 

141 parent_context = parent_context or self._defining_context 

142 input_types = parent_context.infer_node(input_node) 

143 

144 cn = ContextualizedNode(parent_context, input_node) 

145 iterated = input_types.iterate(cn, is_async=is_async) 

146 exprlist = comp_for.children[1] 

147 for i, lazy_value in enumerate(iterated): 

148 types = lazy_value.infer() 

149 dct = unpack_tuple_to_dict(parent_context, types, exprlist) 

150 context = self._get_comp_for_context( 

151 parent_context, 

152 comp_for, 

153 ) 

154 with context.predefine_names(comp_for, dct): 

155 try: 

156 yield from self._nested(comp_fors[1:], context) 

157 except IndexError: 

158 iterated = context.infer_node(self._entry_node) 

159 if self.array_type == 'dict': 

160 yield iterated, context.infer_node(self._value_node) 

161 else: 

162 yield iterated 

163 

164 @inference_state_method_cache(default=[]) 

165 @to_list 

166 def _iterate(self): 

167 comp_fors = tuple(get_sync_comp_fors(self._sync_comp_for_node)) 

168 yield from self._nested(comp_fors) 

169 

170 def py__iter__(self, contextualized_node=None): 

171 for set_ in self._iterate(): 

172 yield LazyKnownValues(set_) 

173 

174 def __repr__(self): 

175 return "<%s of %s>" % (type(self).__name__, self._sync_comp_for_node) 

176 

177 

178class _DictMixin: 

179 def _get_generics(self): 

180 return tuple(c_set.py__class__() for c_set in self.get_mapping_item_values()) 

181 

182 

183class Sequence(LazyAttributeOverwrite, IterableMixin): 

184 api_type = 'instance' 

185 

186 @property 

187 def name(self): 

188 return compiled.CompiledValueName(self, self.array_type) 

189 

190 def _get_generics(self): 

191 return (self.merge_types_of_iterate().py__class__(),) 

192 

193 @inference_state_method_cache(default=()) 

194 def _cached_generics(self): 

195 return self._get_generics() 

196 

197 def _get_wrapped_value(self): 

198 from jedi.inference.gradual.base import GenericClass 

199 from jedi.inference.gradual.generics import TupleGenericManager 

200 klass = compiled.builtin_from_name(self.inference_state, self.array_type) 

201 c, = GenericClass( 

202 klass, 

203 TupleGenericManager(self._cached_generics()) 

204 ).execute_annotation() 

205 return c 

206 

207 def py__bool__(self): 

208 return None # We don't know the length, because of appends. 

209 

210 @safe_property 

211 def parent(self): 

212 return self.inference_state.builtins_module 

213 

214 def py__getitem__(self, index_value_set, contextualized_node): 

215 if self.array_type == 'dict': 

216 return self._dict_values() 

217 return iterate_values(ValueSet([self])) 

218 

219 

220class _BaseComprehension(ComprehensionMixin): 

221 def __init__(self, inference_state, defining_context, sync_comp_for_node, entry_node): 

222 assert sync_comp_for_node.type == 'sync_comp_for' 

223 super().__init__(inference_state) 

224 self._defining_context = defining_context 

225 self._sync_comp_for_node = sync_comp_for_node 

226 self._entry_node = entry_node 

227 

228 

229class ListComprehension(_BaseComprehension, Sequence): 

230 array_type = 'list' 

231 

232 def py__simple_getitem__(self, index): 

233 if isinstance(index, slice): 

234 return ValueSet([self]) 

235 

236 all_types = list(self.py__iter__()) 

237 with reraise_getitem_errors(IndexError, TypeError): 

238 lazy_value = all_types[index] 

239 return lazy_value.infer() 

240 

241 

242class SetComprehension(_BaseComprehension, Sequence): 

243 array_type = 'set' 

244 

245 

246class GeneratorComprehension(_BaseComprehension, GeneratorBase): 

247 pass 

248 

249 

250class _DictKeyMixin: 

251 # TODO merge with _DictMixin? 

252 def get_mapping_item_values(self): 

253 return self._dict_keys(), self._dict_values() 

254 

255 def get_key_values(self): 

256 # TODO merge with _dict_keys? 

257 return self._dict_keys() 

258 

259 

260class DictComprehension(ComprehensionMixin, Sequence, _DictKeyMixin): 

261 array_type = 'dict' 

262 

263 def __init__(self, inference_state, defining_context, sync_comp_for_node, key_node, value_node): 

264 assert sync_comp_for_node.type == 'sync_comp_for' 

265 super().__init__(inference_state) 

266 self._defining_context = defining_context 

267 self._sync_comp_for_node = sync_comp_for_node 

268 self._entry_node = key_node 

269 self._value_node = value_node 

270 

271 def py__iter__(self, contextualized_node=None): 

272 for keys, values in self._iterate(): 

273 yield LazyKnownValues(keys) 

274 

275 def py__simple_getitem__(self, index): 

276 for keys, values in self._iterate(): 

277 for k in keys: 

278 # Be careful in the future if refactoring, index could be a 

279 # slice object. 

280 if k.get_safe_value(default=object()) == index: 

281 return values 

282 raise SimpleGetItemNotFound() 

283 

284 def _dict_keys(self): 

285 return ValueSet.from_sets(keys for keys, values in self._iterate()) 

286 

287 def _dict_values(self): 

288 return ValueSet.from_sets(values for keys, values in self._iterate()) 

289 

290 @publish_method('values') 

291 def _imitate_values(self, arguments): 

292 lazy_value = LazyKnownValues(self._dict_values()) 

293 return ValueSet([FakeList(self.inference_state, [lazy_value])]) 

294 

295 @publish_method('items') 

296 def _imitate_items(self, arguments): 

297 lazy_values = [ 

298 LazyKnownValue( 

299 FakeTuple( 

300 self.inference_state, 

301 [LazyKnownValues(key), 

302 LazyKnownValues(value)] 

303 ) 

304 ) 

305 for key, value in self._iterate() 

306 ] 

307 

308 return ValueSet([FakeList(self.inference_state, lazy_values)]) 

309 

310 def exact_key_items(self): 

311 # NOTE: A smarter thing can probably done here to achieve better 

312 # completions, but at least like this jedi doesn't crash 

313 return [] 

314 

315 

316class SequenceLiteralValue(Sequence): 

317 _TUPLE_LIKE = 'testlist_star_expr', 'testlist', 'subscriptlist' 

318 mapping = {'(': 'tuple', 

319 '[': 'list', 

320 '{': 'set'} 

321 

322 def __init__(self, inference_state, defining_context, atom): 

323 super().__init__(inference_state) 

324 self.atom = atom 

325 self._defining_context = defining_context 

326 

327 if self.atom.type in self._TUPLE_LIKE: 

328 self.array_type = 'tuple' 

329 else: 

330 self.array_type = SequenceLiteralValue.mapping[atom.children[0]] 

331 """The builtin name of the array (list, set, tuple or dict).""" 

332 

333 def _get_generics(self): 

334 if self.array_type == 'tuple': 

335 return tuple(x.infer().py__class__() for x in self.py__iter__()) 

336 return super()._get_generics() 

337 

338 def py__simple_getitem__(self, index): 

339 """Here the index is an int/str. Raises IndexError/KeyError.""" 

340 if isinstance(index, slice): 

341 return ValueSet([self]) 

342 else: 

343 with reraise_getitem_errors(TypeError, KeyError, IndexError): 

344 node = self.get_tree_entries()[index] 

345 if node == ':' or node.type == 'subscript': 

346 return NO_VALUES 

347 return self._defining_context.infer_node(node) 

348 

349 def py__iter__(self, contextualized_node=None): 

350 """ 

351 While values returns the possible values for any array field, this 

352 function returns the value for a certain index. 

353 """ 

354 for node in self.get_tree_entries(): 

355 if node == ':' or node.type == 'subscript': 

356 # TODO this should probably use at least part of the code 

357 # of infer_subscript_list. 

358 yield LazyKnownValue(Slice(self._defining_context, None, None, None)) 

359 else: 

360 yield LazyTreeValue(self._defining_context, node) 

361 yield from check_array_additions(self._defining_context, self) 

362 

363 def py__len__(self): 

364 # This function is not really used often. It's more of a try. 

365 return len(self.get_tree_entries()) 

366 

367 def get_tree_entries(self): 

368 c = self.atom.children 

369 

370 if self.atom.type in self._TUPLE_LIKE: 

371 return c[::2] 

372 

373 array_node = c[1] 

374 if array_node in (']', '}', ')'): 

375 return [] # Direct closing bracket, doesn't contain items. 

376 

377 if array_node.type == 'testlist_comp': 

378 # filter out (for now) pep 448 single-star unpacking 

379 return [value for value in array_node.children[::2] 

380 if value.type != "star_expr"] 

381 elif array_node.type == 'dictorsetmaker': 

382 kv = [] 

383 iterator = iter(array_node.children) 

384 for key in iterator: 

385 if key == "**": 

386 # dict with pep 448 double-star unpacking 

387 # for now ignoring the values imported by ** 

388 next(iterator) 

389 next(iterator, None) # Possible comma. 

390 else: 

391 op = next(iterator, None) 

392 if op is None or op == ',': 

393 if key.type == "star_expr": 

394 # pep 448 single-star unpacking 

395 # for now ignoring values imported by * 

396 pass 

397 else: 

398 kv.append(key) # A set. 

399 else: 

400 assert op == ':' # A dict. 

401 kv.append((key, next(iterator))) 

402 next(iterator, None) # Possible comma. 

403 return kv 

404 else: 

405 if array_node.type == "star_expr": 

406 # pep 448 single-star unpacking 

407 # for now ignoring values imported by * 

408 return [] 

409 else: 

410 return [array_node] 

411 

412 def __repr__(self): 

413 return "<%s of %s>" % (self.__class__.__name__, self.atom) 

414 

415 

416class DictLiteralValue(_DictMixin, SequenceLiteralValue, _DictKeyMixin): 

417 array_type = 'dict' 

418 

419 def __init__(self, inference_state, defining_context, atom): 

420 # Intentionally don't call the super class. This is definitely a sign 

421 # that the architecture is bad and we should refactor. 

422 Sequence.__init__(self, inference_state) 

423 self._defining_context = defining_context 

424 self.atom = atom 

425 

426 def py__simple_getitem__(self, index): 

427 """Here the index is an int/str. Raises IndexError/KeyError.""" 

428 compiled_value_index = compiled.create_simple_object(self.inference_state, index) 

429 for key, value in self.get_tree_entries(): 

430 for k in self._defining_context.infer_node(key): 

431 for key_v in k.execute_operation(compiled_value_index, '=='): 

432 if key_v.get_safe_value(): 

433 return self._defining_context.infer_node(value) 

434 raise SimpleGetItemNotFound('No key found in dictionary %s.' % self) 

435 

436 def py__iter__(self, contextualized_node=None): 

437 """ 

438 While values returns the possible values for any array field, this 

439 function returns the value for a certain index. 

440 """ 

441 # Get keys. 

442 types = NO_VALUES 

443 for k, _ in self.get_tree_entries(): 

444 types |= self._defining_context.infer_node(k) 

445 # We don't know which dict index comes first, therefore always 

446 # yield all the types. 

447 for _ in types: 

448 yield LazyKnownValues(types) 

449 

450 @publish_method('values') 

451 def _imitate_values(self, arguments): 

452 lazy_value = LazyKnownValues(self._dict_values()) 

453 return ValueSet([FakeList(self.inference_state, [lazy_value])]) 

454 

455 @publish_method('items') 

456 def _imitate_items(self, arguments): 

457 lazy_values = [ 

458 LazyKnownValue(FakeTuple( 

459 self.inference_state, 

460 (LazyTreeValue(self._defining_context, key_node), 

461 LazyTreeValue(self._defining_context, value_node)) 

462 )) for key_node, value_node in self.get_tree_entries() 

463 ] 

464 

465 return ValueSet([FakeList(self.inference_state, lazy_values)]) 

466 

467 def exact_key_items(self): 

468 """ 

469 Returns a generator of tuples like dict.items(), where the key is 

470 resolved (as a string) and the values are still lazy values. 

471 """ 

472 for key_node, value in self.get_tree_entries(): 

473 for key in self._defining_context.infer_node(key_node): 

474 if is_string(key): 

475 yield key.get_safe_value(), LazyTreeValue(self._defining_context, value) 

476 

477 def _dict_values(self): 

478 return ValueSet.from_sets( 

479 self._defining_context.infer_node(v) 

480 for k, v in self.get_tree_entries() 

481 ) 

482 

483 def _dict_keys(self): 

484 return ValueSet.from_sets( 

485 self._defining_context.infer_node(k) 

486 for k, v in self.get_tree_entries() 

487 ) 

488 

489 

490class _FakeSequence(Sequence): 

491 def __init__(self, inference_state, lazy_value_list): 

492 """ 

493 type should be one of "tuple", "list" 

494 """ 

495 super().__init__(inference_state) 

496 self._lazy_value_list = lazy_value_list 

497 

498 def py__simple_getitem__(self, index): 

499 if isinstance(index, slice): 

500 return ValueSet([self]) 

501 

502 with reraise_getitem_errors(IndexError, TypeError): 

503 lazy_value = self._lazy_value_list[index] 

504 return lazy_value.infer() 

505 

506 def py__iter__(self, contextualized_node=None): 

507 return self._lazy_value_list 

508 

509 def py__bool__(self): 

510 return bool(len(self._lazy_value_list)) 

511 

512 def __repr__(self): 

513 return "<%s of %s>" % (type(self).__name__, self._lazy_value_list) 

514 

515 

516class FakeTuple(_FakeSequence): 

517 array_type = 'tuple' 

518 

519 

520class FakeList(_FakeSequence): 

521 array_type = 'tuple' 

522 

523 

524class FakeDict(_DictMixin, Sequence, _DictKeyMixin): 

525 array_type = 'dict' 

526 

527 def __init__(self, inference_state, dct): 

528 super().__init__(inference_state) 

529 self._dct = dct 

530 

531 def py__iter__(self, contextualized_node=None): 

532 for key in self._dct: 

533 yield LazyKnownValue(compiled.create_simple_object(self.inference_state, key)) 

534 

535 def py__simple_getitem__(self, index): 

536 with reraise_getitem_errors(KeyError, TypeError): 

537 lazy_value = self._dct[index] 

538 return lazy_value.infer() 

539 

540 @publish_method('values') 

541 def _values(self, arguments): 

542 return ValueSet([FakeTuple( 

543 self.inference_state, 

544 [LazyKnownValues(self._dict_values())] 

545 )]) 

546 

547 def _dict_values(self): 

548 return ValueSet.from_sets(lazy_value.infer() for lazy_value in self._dct.values()) 

549 

550 def _dict_keys(self): 

551 return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__()) 

552 

553 def exact_key_items(self): 

554 return self._dct.items() 

555 

556 def __repr__(self): 

557 return '<%s: %s>' % (self.__class__.__name__, self._dct) 

558 

559 

560class MergedArray(Sequence): 

561 def __init__(self, inference_state, arrays): 

562 super().__init__(inference_state) 

563 self.array_type = arrays[-1].array_type 

564 self._arrays = arrays 

565 

566 def py__iter__(self, contextualized_node=None): 

567 for array in self._arrays: 

568 yield from array.py__iter__() 

569 

570 def py__simple_getitem__(self, index): 

571 return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__()) 

572 

573 

574def unpack_tuple_to_dict(context, types, exprlist): 

575 """ 

576 Unpacking tuple assignments in for statements and expr_stmts. 

577 """ 

578 if exprlist.type == 'name': 

579 return {exprlist.value: types} 

580 elif exprlist.type == 'atom' and exprlist.children[0] in ('(', '['): 

581 return unpack_tuple_to_dict(context, types, exprlist.children[1]) 

582 elif exprlist.type in ('testlist', 'testlist_comp', 'exprlist', 

583 'testlist_star_expr'): 

584 dct = {} 

585 parts = iter(exprlist.children[::2]) 

586 n = 0 

587 for lazy_value in types.iterate(ContextualizedNode(context, exprlist)): 

588 n += 1 

589 try: 

590 part = next(parts) 

591 except StopIteration: 

592 analysis.add(context, 'value-error-too-many-values', part, 

593 message="ValueError: too many values to unpack (expected %s)" % n) 

594 else: 

595 dct.update(unpack_tuple_to_dict(context, lazy_value.infer(), part)) 

596 has_parts = next(parts, None) 

597 if types and has_parts is not None: 

598 analysis.add(context, 'value-error-too-few-values', has_parts, 

599 message="ValueError: need more than %s values to unpack" % n) 

600 return dct 

601 elif exprlist.type == 'power' or exprlist.type == 'atom_expr': 

602 # Something like ``arr[x], var = ...``. 

603 # This is something that is not yet supported, would also be difficult 

604 # to write into a dict. 

605 return {} 

606 elif exprlist.type == 'star_expr': # `a, *b, c = x` type unpackings 

607 # Currently we're not supporting them. 

608 return {} 

609 raise NotImplementedError 

610 

611 

612class Slice(LazyValueWrapper): 

613 def __init__(self, python_context, start, stop, step): 

614 self.inference_state = python_context.inference_state 

615 self._context = python_context 

616 # All of them are either a Precedence or None. 

617 self._start = start 

618 self._stop = stop 

619 self._step = step 

620 

621 def _get_wrapped_value(self): 

622 value = compiled.builtin_from_name(self._context.inference_state, 'slice') 

623 slice_value, = value.execute_with_values() 

624 return slice_value 

625 

626 def get_safe_value(self, default=sentinel): 

627 """ 

628 Imitate CompiledValue.obj behavior and return a ``builtin.slice()`` 

629 object. 

630 """ 

631 def get(element): 

632 if element is None: 

633 return None 

634 

635 result = self._context.infer_node(element) 

636 if len(result) != 1: 

637 # For simplicity, we want slices to be clear defined with just 

638 # one type. Otherwise we will return an empty slice object. 

639 raise IndexError 

640 

641 value, = result 

642 return get_int_or_none(value) 

643 

644 try: 

645 return slice(get(self._start), get(self._stop), get(self._step)) 

646 except IndexError: 

647 return slice(None, None, None)