]>
Commit | Line | Data |
---|---|---|
53e6db90 DC |
1 | from jedi.inference.cache import inference_state_method_cache |
2 | from jedi.inference.base_value import ValueSet, NO_VALUES, Value, \ | |
3 | iterator_to_value_set, LazyValueWrapper, ValueWrapper | |
4 | from jedi.inference.compiled import builtin_from_name | |
5 | from jedi.inference.value.klass import ClassFilter | |
6 | from jedi.inference.value.klass import ClassMixin | |
7 | from jedi.inference.utils import to_list | |
8 | from jedi.inference.names import AbstractNameDefinition, ValueName | |
9 | from jedi.inference.context import ClassContext | |
10 | from jedi.inference.gradual.generics import TupleGenericManager | |
11 | ||
12 | ||
13 | class _BoundTypeVarName(AbstractNameDefinition): | |
14 | """ | |
15 | This type var was bound to a certain type, e.g. int. | |
16 | """ | |
17 | def __init__(self, type_var, value_set): | |
18 | self._type_var = type_var | |
19 | self.parent_context = type_var.parent_context | |
20 | self._value_set = value_set | |
21 | ||
22 | def infer(self): | |
23 | def iter_(): | |
24 | for value in self._value_set: | |
25 | # Replace any with the constraints if they are there. | |
26 | from jedi.inference.gradual.typing import AnyClass | |
27 | if isinstance(value, AnyClass): | |
28 | yield from self._type_var.constraints | |
29 | else: | |
30 | yield value | |
31 | return ValueSet(iter_()) | |
32 | ||
33 | def py__name__(self): | |
34 | return self._type_var.py__name__() | |
35 | ||
36 | def __repr__(self): | |
37 | return '<%s %s -> %s>' % (self.__class__.__name__, self.py__name__(), self._value_set) | |
38 | ||
39 | ||
40 | class _TypeVarFilter: | |
41 | """ | |
42 | A filter for all given variables in a class. | |
43 | ||
44 | A = TypeVar('A') | |
45 | B = TypeVar('B') | |
46 | class Foo(Mapping[A, B]): | |
47 | ... | |
48 | ||
49 | In this example we would have two type vars given: A and B | |
50 | """ | |
51 | def __init__(self, generics, type_vars): | |
52 | self._generics = generics | |
53 | self._type_vars = type_vars | |
54 | ||
55 | def get(self, name): | |
56 | for i, type_var in enumerate(self._type_vars): | |
57 | if type_var.py__name__() == name: | |
58 | try: | |
59 | return [_BoundTypeVarName(type_var, self._generics[i])] | |
60 | except IndexError: | |
61 | return [type_var.name] | |
62 | return [] | |
63 | ||
64 | def values(self): | |
65 | # The values are not relevant. If it's not searched exactly, the type | |
66 | # vars are just global and should be looked up as that. | |
67 | return [] | |
68 | ||
69 | ||
70 | class _AnnotatedClassContext(ClassContext): | |
71 | def get_filters(self, *args, **kwargs): | |
72 | filters = super().get_filters( | |
73 | *args, **kwargs | |
74 | ) | |
75 | yield from filters | |
76 | ||
77 | # The type vars can only be looked up if it's a global search and | |
78 | # not a direct lookup on the class. | |
79 | yield self._value.get_type_var_filter() | |
80 | ||
81 | ||
82 | class DefineGenericBaseClass(LazyValueWrapper): | |
83 | def __init__(self, generics_manager): | |
84 | self._generics_manager = generics_manager | |
85 | ||
86 | def _create_instance_with_generics(self, generics_manager): | |
87 | raise NotImplementedError | |
88 | ||
89 | @inference_state_method_cache() | |
90 | def get_generics(self): | |
91 | return self._generics_manager.to_tuple() | |
92 | ||
93 | def define_generics(self, type_var_dict): | |
94 | from jedi.inference.gradual.type_var import TypeVar | |
95 | changed = False | |
96 | new_generics = [] | |
97 | for generic_set in self.get_generics(): | |
98 | values = NO_VALUES | |
99 | for generic in generic_set: | |
100 | if isinstance(generic, (DefineGenericBaseClass, TypeVar)): | |
101 | result = generic.define_generics(type_var_dict) | |
102 | values |= result | |
103 | if result != ValueSet({generic}): | |
104 | changed = True | |
105 | else: | |
106 | values |= ValueSet([generic]) | |
107 | new_generics.append(values) | |
108 | ||
109 | if not changed: | |
110 | # There might not be any type vars that change. In that case just | |
111 | # return itself, because it does not make sense to potentially lose | |
112 | # cached results. | |
113 | return ValueSet([self]) | |
114 | ||
115 | return ValueSet([self._create_instance_with_generics( | |
116 | TupleGenericManager(tuple(new_generics)) | |
117 | )]) | |
118 | ||
119 | def is_same_class(self, other): | |
120 | if not isinstance(other, DefineGenericBaseClass): | |
121 | return False | |
122 | ||
123 | if self.tree_node != other.tree_node: | |
124 | # TODO not sure if this is nice. | |
125 | return False | |
126 | given_params1 = self.get_generics() | |
127 | given_params2 = other.get_generics() | |
128 | ||
129 | if len(given_params1) != len(given_params2): | |
130 | # If the amount of type vars doesn't match, the class doesn't | |
131 | # match. | |
132 | return False | |
133 | ||
134 | # Now compare generics | |
135 | return all( | |
136 | any( | |
137 | # TODO why is this ordering the correct one? | |
138 | cls2.is_same_class(cls1) | |
139 | # TODO I'm still not sure gather_annotation_classes is a good | |
140 | # idea. They are essentially here to avoid comparing Tuple <=> | |
141 | # tuple and instead compare tuple <=> tuple, but at the moment | |
142 | # the whole `is_same_class` and `is_sub_class` matching is just | |
143 | # not in the best shape. | |
144 | for cls1 in class_set1.gather_annotation_classes() | |
145 | for cls2 in class_set2.gather_annotation_classes() | |
146 | ) for class_set1, class_set2 in zip(given_params1, given_params2) | |
147 | ) | |
148 | ||
149 | def get_signatures(self): | |
150 | return [] | |
151 | ||
152 | def __repr__(self): | |
153 | return '<%s: %s%s>' % ( | |
154 | self.__class__.__name__, | |
155 | self._wrapped_value, | |
156 | list(self.get_generics()), | |
157 | ) | |
158 | ||
159 | ||
160 | class GenericClass(DefineGenericBaseClass, ClassMixin): | |
161 | """ | |
162 | A class that is defined with generics, might be something simple like: | |
163 | ||
164 | class Foo(Generic[T]): ... | |
165 | my_foo_int_cls = Foo[int] | |
166 | """ | |
167 | def __init__(self, class_value, generics_manager): | |
168 | super().__init__(generics_manager) | |
169 | self._class_value = class_value | |
170 | ||
171 | def _get_wrapped_value(self): | |
172 | return self._class_value | |
173 | ||
174 | def get_type_hint(self, add_class_info=True): | |
175 | n = self.py__name__() | |
176 | # Not sure if this is the best way to do this, but all of these types | |
177 | # are a bit special in that they have type aliases and other ways to | |
178 | # become lower case. It's probably better to make them upper case, | |
179 | # because that's what you can use in annotations. | |
180 | n = dict(list="List", dict="Dict", set="Set", tuple="Tuple").get(n, n) | |
181 | s = n + self._generics_manager.get_type_hint() | |
182 | if add_class_info: | |
183 | return 'Type[%s]' % s | |
184 | return s | |
185 | ||
186 | def get_type_var_filter(self): | |
187 | return _TypeVarFilter(self.get_generics(), self.list_type_vars()) | |
188 | ||
189 | def py__call__(self, arguments): | |
190 | instance, = super().py__call__(arguments) | |
191 | return ValueSet([_GenericInstanceWrapper(instance)]) | |
192 | ||
193 | def _as_context(self): | |
194 | return _AnnotatedClassContext(self) | |
195 | ||
196 | @to_list | |
197 | def py__bases__(self): | |
198 | for base in self._wrapped_value.py__bases__(): | |
199 | yield _LazyGenericBaseClass(self, base, self._generics_manager) | |
200 | ||
201 | def _create_instance_with_generics(self, generics_manager): | |
202 | return GenericClass(self._class_value, generics_manager) | |
203 | ||
204 | def is_sub_class_of(self, class_value): | |
205 | if super().is_sub_class_of(class_value): | |
206 | return True | |
207 | return self._class_value.is_sub_class_of(class_value) | |
208 | ||
209 | def with_generics(self, generics_tuple): | |
210 | return self._class_value.with_generics(generics_tuple) | |
211 | ||
212 | def infer_type_vars(self, value_set): | |
213 | # Circular | |
214 | from jedi.inference.gradual.annotation import merge_pairwise_generics, merge_type_var_dicts | |
215 | ||
216 | annotation_name = self.py__name__() | |
217 | type_var_dict = {} | |
218 | if annotation_name == 'Iterable': | |
219 | annotation_generics = self.get_generics() | |
220 | if annotation_generics: | |
221 | return annotation_generics[0].infer_type_vars( | |
222 | value_set.merge_types_of_iterate(), | |
223 | ) | |
224 | else: | |
225 | # Note: we need to handle the MRO _in order_, so we need to extract | |
226 | # the elements from the set first, then handle them, even if we put | |
227 | # them back in a set afterwards. | |
228 | for py_class in value_set: | |
229 | if py_class.is_instance() and not py_class.is_compiled(): | |
230 | py_class = py_class.get_annotated_class_object() | |
231 | else: | |
232 | continue | |
233 | ||
234 | if py_class.api_type != 'class': | |
235 | # Functions & modules don't have an MRO and we're not | |
236 | # expecting a Callable (those are handled separately within | |
237 | # TypingClassValueWithIndex). | |
238 | continue | |
239 | ||
240 | for parent_class in py_class.py__mro__(): | |
241 | class_name = parent_class.py__name__() | |
242 | if annotation_name == class_name: | |
243 | merge_type_var_dicts( | |
244 | type_var_dict, | |
245 | merge_pairwise_generics(self, parent_class), | |
246 | ) | |
247 | break | |
248 | ||
249 | return type_var_dict | |
250 | ||
251 | ||
252 | class _LazyGenericBaseClass: | |
253 | def __init__(self, class_value, lazy_base_class, generics_manager): | |
254 | self._class_value = class_value | |
255 | self._lazy_base_class = lazy_base_class | |
256 | self._generics_manager = generics_manager | |
257 | ||
258 | @iterator_to_value_set | |
259 | def infer(self): | |
260 | for base in self._lazy_base_class.infer(): | |
261 | if isinstance(base, GenericClass): | |
262 | # Here we have to recalculate the given types. | |
263 | yield GenericClass.create_cached( | |
264 | base.inference_state, | |
265 | base._wrapped_value, | |
266 | TupleGenericManager(tuple(self._remap_type_vars(base))), | |
267 | ) | |
268 | else: | |
269 | if base.is_class_mixin(): | |
270 | # This case basically allows classes like `class Foo(List)` | |
271 | # to be used like `Foo[int]`. The generics are not | |
272 | # necessary and can be used later. | |
273 | yield GenericClass.create_cached( | |
274 | base.inference_state, | |
275 | base, | |
276 | self._generics_manager, | |
277 | ) | |
278 | else: | |
279 | yield base | |
280 | ||
281 | def _remap_type_vars(self, base): | |
282 | from jedi.inference.gradual.type_var import TypeVar | |
283 | filter = self._class_value.get_type_var_filter() | |
284 | for type_var_set in base.get_generics(): | |
285 | new = NO_VALUES | |
286 | for type_var in type_var_set: | |
287 | if isinstance(type_var, TypeVar): | |
288 | names = filter.get(type_var.py__name__()) | |
289 | new |= ValueSet.from_sets( | |
290 | name.infer() for name in names | |
291 | ) | |
292 | else: | |
293 | # Mostly will be type vars, except if in some cases | |
294 | # a concrete type will already be there. In that | |
295 | # case just add it to the value set. | |
296 | new |= ValueSet([type_var]) | |
297 | yield new | |
298 | ||
299 | def __repr__(self): | |
300 | return '<%s: %s>' % (self.__class__.__name__, self._lazy_base_class) | |
301 | ||
302 | ||
303 | class _GenericInstanceWrapper(ValueWrapper): | |
304 | def py__stop_iteration_returns(self): | |
305 | for cls in self._wrapped_value.class_value.py__mro__(): | |
306 | if cls.py__name__() == 'Generator': | |
307 | generics = cls.get_generics() | |
308 | try: | |
309 | return generics[2].execute_annotation() | |
310 | except IndexError: | |
311 | pass | |
312 | elif cls.py__name__() == 'Iterator': | |
313 | return ValueSet([builtin_from_name(self.inference_state, 'None')]) | |
314 | return self._wrapped_value.py__stop_iteration_returns() | |
315 | ||
316 | def get_type_hint(self, add_class_info=True): | |
317 | return self._wrapped_value.class_value.get_type_hint(add_class_info=False) | |
318 | ||
319 | ||
320 | class _PseudoTreeNameClass(Value): | |
321 | """ | |
322 | In typeshed, some classes are defined like this: | |
323 | ||
324 | Tuple: _SpecialForm = ... | |
325 | ||
326 | Now this is not a real class, therefore we have to do some workarounds like | |
327 | this class. Essentially this class makes it possible to goto that `Tuple` | |
328 | name, without affecting anything else negatively. | |
329 | """ | |
330 | api_type = 'class' | |
331 | ||
332 | def __init__(self, parent_context, tree_name): | |
333 | super().__init__( | |
334 | parent_context.inference_state, | |
335 | parent_context | |
336 | ) | |
337 | self._tree_name = tree_name | |
338 | ||
339 | @property | |
340 | def tree_node(self): | |
341 | return self._tree_name | |
342 | ||
343 | def get_filters(self, *args, **kwargs): | |
344 | # TODO this is obviously wrong. Is it though? | |
345 | class EmptyFilter(ClassFilter): | |
346 | def __init__(self): | |
347 | pass | |
348 | ||
349 | def get(self, name, **kwargs): | |
350 | return [] | |
351 | ||
352 | def values(self, **kwargs): | |
353 | return [] | |
354 | ||
355 | yield EmptyFilter() | |
356 | ||
357 | def py__class__(self): | |
358 | # This might not be 100% correct, but it is good enough. The details of | |
359 | # the typing library are not really an issue for Jedi. | |
360 | return builtin_from_name(self.inference_state, 'type') | |
361 | ||
362 | @property | |
363 | def name(self): | |
364 | return ValueName(self, self._tree_name) | |
365 | ||
366 | def get_qualified_names(self): | |
367 | return (self._tree_name.value,) | |
368 | ||
369 | def __repr__(self): | |
370 | return '%s(%s)' % (self.__class__.__name__, self._tree_name.value) | |
371 | ||
372 | ||
373 | class BaseTypingValue(LazyValueWrapper): | |
374 | def __init__(self, parent_context, tree_name): | |
375 | self.inference_state = parent_context.inference_state | |
376 | self.parent_context = parent_context | |
377 | self._tree_name = tree_name | |
378 | ||
379 | @property | |
380 | def name(self): | |
381 | return ValueName(self, self._tree_name) | |
382 | ||
383 | def _get_wrapped_value(self): | |
384 | return _PseudoTreeNameClass(self.parent_context, self._tree_name) | |
385 | ||
386 | def get_signatures(self): | |
387 | return self._wrapped_value.get_signatures() | |
388 | ||
389 | def __repr__(self): | |
390 | return '%s(%s)' % (self.__class__.__name__, self._tree_name.value) | |
391 | ||
392 | ||
393 | class BaseTypingClassWithGenerics(DefineGenericBaseClass): | |
394 | def __init__(self, parent_context, tree_name, generics_manager): | |
395 | super().__init__(generics_manager) | |
396 | self.inference_state = parent_context.inference_state | |
397 | self.parent_context = parent_context | |
398 | self._tree_name = tree_name | |
399 | ||
400 | def _get_wrapped_value(self): | |
401 | return _PseudoTreeNameClass(self.parent_context, self._tree_name) | |
402 | ||
403 | def __repr__(self): | |
404 | return '%s(%s%s)' % (self.__class__.__name__, self._tree_name.value, | |
405 | self._generics_manager) | |
406 | ||
407 | ||
408 | class BaseTypingInstance(LazyValueWrapper): | |
409 | def __init__(self, parent_context, class_value, tree_name, generics_manager): | |
410 | self.inference_state = class_value.inference_state | |
411 | self.parent_context = parent_context | |
412 | self._class_value = class_value | |
413 | self._tree_name = tree_name | |
414 | self._generics_manager = generics_manager | |
415 | ||
416 | def py__class__(self): | |
417 | return self._class_value | |
418 | ||
419 | def get_annotated_class_object(self): | |
420 | return self._class_value | |
421 | ||
422 | def get_qualified_names(self): | |
423 | return (self.py__name__(),) | |
424 | ||
425 | @property | |
426 | def name(self): | |
427 | return ValueName(self, self._tree_name) | |
428 | ||
429 | def _get_wrapped_value(self): | |
430 | object_, = builtin_from_name(self.inference_state, 'object').execute_annotation() | |
431 | return object_ | |
432 | ||
433 | def __repr__(self): | |
434 | return '<%s: %s>' % (self.__class__.__name__, self._generics_manager) |