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