1"""
2We need to somehow work with the typing objects. Since the typing objects are
3pretty bare we need to add all the Jedi customizations to make them work as
4values.
5
6This file deals with all the typing.py cases.
7"""
8import itertools
9from typing import Any
10
11from jedi import debug
12from jedi.inference.compiled import builtin_from_name, create_simple_object
13from jedi.inference.base_value import ValueSet, NO_VALUES, Value, \
14 LazyValueWrapper, ValueWrapper
15from jedi.inference.lazy_value import LazyKnownValues
16from jedi.inference.arguments import repack_with_argument_clinic
17from jedi.inference.filters import FilterWrapper
18from jedi.inference.names import NameWrapper, ValueName
19from jedi.inference.value.klass import ClassMixin
20from jedi.inference.gradual.base import BaseTypingValue, \
21 BaseTypingClassWithGenerics, BaseTypingInstance
22from jedi.inference.gradual.type_var import TypeVarClass
23from jedi.inference.gradual.generics import LazyGenericManager, TupleGenericManager
24
25_PROXY_CLASS_TYPES = 'Tuple Generic Protocol Callable Type'.split()
26_TYPE_ALIAS_TYPES = {
27 'List': 'builtins.list',
28 'Dict': 'builtins.dict',
29 'Set': 'builtins.set',
30 'FrozenSet': 'builtins.frozenset',
31 'ChainMap': 'collections.ChainMap',
32 'Counter': 'collections.Counter',
33 'DefaultDict': 'collections.defaultdict',
34 'Deque': 'collections.deque',
35}
36_PROXY_TYPES = ['Optional', 'Union', 'ClassVar', 'Annotated', 'Final']
37IGNORE_ANNOTATION_PARTS = ['ClassVar', 'Annotated', 'Final']
38
39
40class TypingModuleName(NameWrapper):
41 def infer(self):
42 return ValueSet(self._remap())
43
44 def _remap(self):
45 name = self.string_name
46 inference_state = self.parent_context.inference_state
47 try:
48 actual = _TYPE_ALIAS_TYPES[name]
49 except KeyError:
50 pass
51 else:
52 yield TypeAlias.create_cached(
53 inference_state, self.parent_context, self.tree_name, actual)
54 return
55
56 if name in _PROXY_CLASS_TYPES:
57 yield ProxyTypingClassValue.create_cached(
58 inference_state, self.parent_context, self.tree_name)
59 elif name in _PROXY_TYPES:
60 yield ProxyTypingValue.create_cached(
61 inference_state, self.parent_context, self.tree_name)
62 elif name == 'runtime':
63 # We don't want anything here, not sure what this function is
64 # supposed to do, since it just appears in the stubs and shouldn't
65 # have any effects there (because it's never executed).
66 return
67 elif name == 'TypeVar':
68 cls, = self._wrapped_name.infer()
69 yield TypeVarClass.create_cached(inference_state, cls)
70 elif name == 'Any':
71 yield AnyClass.create_cached(
72 inference_state, self.parent_context, self.tree_name)
73 elif name == 'TYPE_CHECKING':
74 # This is needed for e.g. imports that are only available for type
75 # checking or are in cycles. The user can then check this variable.
76 yield builtin_from_name(inference_state, 'True')
77 elif name == 'overload':
78 yield OverloadFunction.create_cached(
79 inference_state, self.parent_context, self.tree_name)
80 elif name == 'NewType':
81 v, = self._wrapped_name.infer()
82 yield NewTypeFunction.create_cached(inference_state, v)
83 elif name == 'cast':
84 cast_fn, = self._wrapped_name.infer()
85 yield CastFunction.create_cached(inference_state, cast_fn)
86 elif name == 'Self':
87 yield SelfClass.create_cached(
88 inference_state, self.parent_context, self.tree_name)
89 elif name == 'TypedDict':
90 # TODO doesn't even exist in typeshed/typing.py, yet. But will be
91 # added soon.
92 yield TypedDictClass.create_cached(
93 inference_state, self.parent_context, self.tree_name)
94 else:
95 # Not necessary, as long as we are not doing type checking:
96 # no_type_check & no_type_check_decorator
97 # Everything else shouldn't be relevant...
98 yield from self._wrapped_name.infer()
99
100
101class TypingModuleFilterWrapper(FilterWrapper):
102 name_wrapper_class = TypingModuleName
103
104
105class ProxyWithGenerics(BaseTypingClassWithGenerics):
106 def execute_annotation(self, context):
107 string_name = self._tree_name.value
108
109 if string_name == 'Union':
110 # This is kind of a special case, because we have Unions (in Jedi
111 # ValueSets).
112 return self.gather_annotation_classes().execute_annotation(context)
113 elif string_name == 'Optional':
114 # Optional is basically just saying it's either None or the actual
115 # type.
116 return self.gather_annotation_classes().execute_annotation(context) \
117 | ValueSet([builtin_from_name(self.inference_state, 'None')])
118 elif string_name == 'Type':
119 # The type is actually already given in the index_value
120 return self._generics_manager[0]
121 elif string_name in IGNORE_ANNOTATION_PARTS:
122 # For now don't do anything here, ClassVars are always used.
123 return self._generics_manager[0].execute_annotation(context)
124
125 mapped = {
126 'Tuple': Tuple,
127 'Generic': Generic,
128 'Protocol': Protocol,
129 'Callable': Callable,
130 }
131 cls = mapped[string_name]
132 return ValueSet([cls(
133 self.parent_context,
134 self,
135 self._tree_name,
136 generics_manager=self._generics_manager,
137 )])
138
139 def gather_annotation_classes(self):
140 return ValueSet.from_sets(self._generics_manager.to_tuple())
141
142 def _create_instance_with_generics(self, generics_manager):
143 return ProxyWithGenerics(
144 self.parent_context,
145 self._tree_name,
146 generics_manager
147 )
148
149 def infer_type_vars(self, value_set):
150 annotation_generics = self.get_generics()
151
152 if not annotation_generics:
153 return {}
154
155 annotation_name = self.py__name__()
156 if annotation_name == 'Optional':
157 # Optional[T] is equivalent to Union[T, None]. In Jedi unions
158 # are represented by members within a ValueSet, so we extract
159 # the T from the Optional[T] by removing the None value.
160 none = builtin_from_name(self.inference_state, 'None')
161 return annotation_generics[0].infer_type_vars(
162 value_set.filter(lambda x: x != none),
163 )
164
165 return {}
166
167
168class ProxyTypingValue(BaseTypingValue):
169 index_class = ProxyWithGenerics
170
171 def with_generics(self, generics_tuple):
172 return self.index_class.create_cached(
173 self.inference_state,
174 self.parent_context,
175 self._tree_name,
176 generics_manager=TupleGenericManager(generics_tuple)
177 )
178
179 def py__getitem__(self, index_value_set, contextualized_node):
180 return ValueSet(
181 self.index_class.create_cached(
182 self.inference_state,
183 self.parent_context,
184 self._tree_name,
185 generics_manager=LazyGenericManager(
186 context_of_index=contextualized_node.context,
187 index_value=index_value,
188 )
189 ) for index_value in index_value_set
190 )
191
192
193class _TypingClassMixin(ClassMixin):
194 _tree_name: Any
195
196 def py__bases__(self):
197 return [LazyKnownValues(
198 self.inference_state.builtins_module.py__getattribute__('object')
199 )]
200
201 def get_metaclasses(self):
202 return []
203
204 @property
205 def name(self):
206 return ValueName(self, self._tree_name)
207
208
209class TypingClassWithGenerics(ProxyWithGenerics, _TypingClassMixin):
210 def infer_type_vars(self, value_set):
211 type_var_dict = {}
212 annotation_generics = self.get_generics()
213
214 if not annotation_generics:
215 return type_var_dict
216
217 annotation_name = self.py__name__()
218 if annotation_name == 'Type':
219 return annotation_generics[0].infer_type_vars(
220 # This is basically a trick to avoid extra code: We execute the
221 # incoming classes to be able to use the normal code for type
222 # var inference.
223 value_set.execute_annotation(None),
224 )
225
226 elif annotation_name == 'Callable':
227 if len(annotation_generics) == 2:
228 return annotation_generics[1].infer_type_vars(
229 value_set.execute_annotation(None),
230 )
231
232 elif annotation_name == 'Tuple':
233 tuple_annotation, = self.execute_annotation(None)
234 return tuple_annotation.infer_type_vars(value_set)
235
236 return type_var_dict
237
238 def _create_instance_with_generics(self, generics_manager):
239 return TypingClassWithGenerics(
240 self.parent_context,
241 self._tree_name,
242 generics_manager
243 )
244
245
246class ProxyTypingClassValue(ProxyTypingValue, _TypingClassMixin):
247 index_class = TypingClassWithGenerics
248
249
250class TypeAlias(LazyValueWrapper):
251 def __init__(self, parent_context, origin_tree_name, actual):
252 self.inference_state = parent_context.inference_state
253 self.parent_context = parent_context
254 self._origin_tree_name = origin_tree_name
255 self._actual = actual # e.g. builtins.list
256
257 @property
258 def name(self):
259 return ValueName(self, self._origin_tree_name)
260
261 def py__name__(self):
262 return self.name.string_name
263
264 def __repr__(self):
265 return '<%s: %s>' % (self.__class__.__name__, self._actual)
266
267 def _get_wrapped_value(self):
268 module_name, class_name = self._actual.split('.')
269
270 # TODO use inference_state.import_module?
271 from jedi.inference.imports import Importer
272 module, = Importer(
273 self.inference_state, [module_name], self.inference_state.builtins_module
274 ).follow()
275 classes = module.py__getattribute__(class_name)
276 # There should only be one, because it's code that we control.
277 assert len(classes) == 1, classes
278 cls = next(iter(classes))
279 return cls
280
281 def gather_annotation_classes(self):
282 return ValueSet([self._get_wrapped_value()])
283
284 def get_signatures(self):
285 return []
286
287
288class Callable(BaseTypingInstance):
289 def py__call__(self, arguments):
290 """
291 def x() -> Callable[[Callable[..., _T]], _T]: ...
292 """
293 # The 0th index are the arguments.
294 try:
295 param_values = self._generics_manager[0]
296 result_values = self._generics_manager[1]
297 except IndexError:
298 debug.warning('Callable[...] defined without two arguments')
299 return NO_VALUES
300 else:
301 from jedi.inference.gradual.annotation import infer_return_for_callable
302 return infer_return_for_callable(arguments, param_values, result_values)
303
304 def py__get__(self, instance, class_value):
305 return ValueSet([self])
306
307
308class Tuple(BaseTypingInstance):
309 def _is_homogenous(self):
310 # To specify a variable-length tuple of homogeneous type, Tuple[T, ...]
311 # is used.
312 return self._generics_manager.is_homogenous_tuple()
313
314 def py__simple_getitem__(self, index):
315 if self._is_homogenous():
316 return self._generics_manager.get_index_and_execute(0)
317 else:
318 if isinstance(index, int):
319 return self._generics_manager.get_index_and_execute(index)
320
321 debug.dbg('The getitem type on Tuple was %s' % index)
322 return NO_VALUES
323
324 def py__iter__(self, contextualized_node=None):
325 if self._is_homogenous():
326 yield LazyKnownValues(self._generics_manager.get_index_and_execute(0))
327 else:
328 for v in self._generics_manager.to_tuple():
329 yield LazyKnownValues(v.execute_annotation(None))
330
331 def py__getitem__(self, index_value_set, contextualized_node):
332 if self._is_homogenous():
333 return self._generics_manager.get_index_and_execute(0)
334
335 return ValueSet.from_sets(
336 self._generics_manager.to_tuple()
337 ).execute_annotation(None)
338
339 def _get_wrapped_value(self):
340 tuple_, = self.inference_state.builtins_module \
341 .py__getattribute__('tuple').execute_annotation(None)
342 return tuple_
343
344 @property
345 def name(self):
346 return self._wrapped_value.name
347
348 def infer_type_vars(self, value_set):
349 # Circular
350 from jedi.inference.gradual.annotation import merge_pairwise_generics, merge_type_var_dicts
351
352 value_set = value_set.filter(
353 lambda x: x.py__name__().lower() == 'tuple',
354 )
355
356 if self._is_homogenous():
357 # The parameter annotation is of the form `Tuple[T, ...]`,
358 # so we treat the incoming tuple like a iterable sequence
359 # rather than a positional container of elements.
360 return self._class_value.get_generics()[0].infer_type_vars(
361 value_set.merge_types_of_iterate(),
362 )
363
364 else:
365 # The parameter annotation has only explicit type parameters
366 # (e.g: `Tuple[T]`, `Tuple[T, U]`, `Tuple[T, U, V]`, etc.) so we
367 # treat the incoming values as needing to match the annotation
368 # exactly, just as we would for non-tuple annotations.
369
370 type_var_dict = {}
371 for element in value_set:
372 try:
373 method = element.get_annotated_class_object
374 except AttributeError:
375 # This might still happen, because the tuple name matching
376 # above is not 100% correct, so just catch the remaining
377 # cases here.
378 continue
379
380 py_class = method()
381 merge_type_var_dicts(
382 type_var_dict,
383 merge_pairwise_generics(self._class_value, py_class),
384 )
385
386 return type_var_dict
387
388
389class Generic(BaseTypingInstance):
390 pass
391
392
393class Protocol(BaseTypingInstance):
394 pass
395
396
397class AnyClass(BaseTypingValue):
398 def execute_annotation(self, context):
399 debug.warning('Used Any - returned no results')
400 return NO_VALUES
401
402
403class SelfClass(BaseTypingValue):
404 def execute_annotation(self, context):
405 debug.warning('Used Self')
406 if context is not None:
407 # Execute the class of Self
408 return context.get_value().execute_annotation(None)
409 return NO_VALUES
410
411
412class OverloadFunction(BaseTypingValue):
413 @repack_with_argument_clinic('func, /')
414 def py__call__(self, func_value_set):
415 # Just pass arguments through.
416 return func_value_set
417
418
419class NewTypeFunction(ValueWrapper):
420 def py__call__(self, arguments):
421 ordered_args = arguments.unpack()
422 next(ordered_args, (None, None))
423 _, second_arg = next(ordered_args, (None, None))
424 if second_arg is None:
425 return NO_VALUES
426 return ValueSet(
427 NewType(
428 self.inference_state,
429 contextualized_node.context,
430 contextualized_node.node,
431 second_arg.infer(),
432 ) for contextualized_node in arguments.get_calling_nodes())
433
434
435class NewType(Value):
436 def __init__(self, inference_state, parent_context, tree_node, type_value_set):
437 super().__init__(inference_state, parent_context)
438 self._type_value_set = type_value_set
439 self.tree_node = tree_node
440
441 def py__class__(self):
442 c, = self._type_value_set.py__class__()
443 return c
444
445 def py__call__(self, arguments):
446 return self._type_value_set.execute_annotation(arguments.context)
447
448 @property
449 def name(self):
450 from jedi.inference.compiled.value import CompiledValueName
451 return CompiledValueName(self, 'NewType')
452
453 def __repr__(self) -> str:
454 return '<NewType: %s>%s' % (self.tree_node, self._type_value_set)
455
456
457class CastFunction(ValueWrapper):
458 @repack_with_argument_clinic('type, object, /')
459 def py__call__(self, type_value_set, object_value_set):
460 return type_value_set.execute_annotation(None)
461
462
463class TypedDictClass(BaseTypingValue):
464 """
465 This class has no responsibilities and is just here to make sure that typed
466 dicts can be identified.
467 """
468
469
470class TypedDict(LazyValueWrapper):
471 """Represents the instance version of ``TypedDictClass``."""
472 def __init__(self, definition_class):
473 self.inference_state = definition_class.inference_state
474 self.parent_context = definition_class.parent_context
475 self.tree_node = definition_class.tree_node
476 self._definition_class = definition_class
477
478 @property
479 def name(self):
480 return ValueName(self, self.tree_node.name)
481
482 def py__simple_getitem__(self, index):
483 if isinstance(index, str):
484 return ValueSet.from_sets(
485 name.infer()
486 for filter in self._definition_class.get_filters(is_instance=True)
487 for name in filter.get(index)
488 )
489 return NO_VALUES
490
491 def get_key_values(self):
492 filtered_values = itertools.chain.from_iterable((
493 f.values()
494 for f in self._definition_class.get_filters(is_instance=True)
495 ))
496 return ValueSet({
497 create_simple_object(self.inference_state, v.string_name)
498 for v in filtered_values
499 })
500
501 def _get_wrapped_value(self):
502 d, = self.inference_state.builtins_module.py__getattribute__('dict')
503 result, = d.execute_with_values()
504 return result