Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jedi/inference/gradual/typing.py: 48%
251 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
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.
6This file deals with all the typing.py cases.
7"""
8import itertools
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
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()
38class TypingModuleName(NameWrapper):
39 def infer(self):
40 return ValueSet(self._remap())
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
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()
96class TypingModuleFilterWrapper(FilterWrapper):
97 name_wrapper_class = TypingModuleName
100class ProxyWithGenerics(BaseTypingClassWithGenerics):
101 def execute_annotation(self):
102 string_name = self._tree_name.value
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()
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 )])
134 def gather_annotation_classes(self):
135 return ValueSet.from_sets(self._generics_manager.to_tuple())
137 def _create_instance_with_generics(self, generics_manager):
138 return ProxyWithGenerics(
139 self.parent_context,
140 self._tree_name,
141 generics_manager
142 )
144 def infer_type_vars(self, value_set):
145 annotation_generics = self.get_generics()
147 if not annotation_generics:
148 return {}
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 )
160 return {}
163class ProxyTypingValue(BaseTypingValue):
164 index_class = ProxyWithGenerics
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 )
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 )
188class _TypingClassMixin(ClassMixin):
189 def py__bases__(self):
190 return [LazyKnownValues(
191 self.inference_state.builtins_module.py__getattribute__('object')
192 )]
194 def get_metaclasses(self):
195 return []
197 @property
198 def name(self):
199 return ValueName(self, self._tree_name)
202class TypingClassWithGenerics(ProxyWithGenerics, _TypingClassMixin):
203 def infer_type_vars(self, value_set):
204 type_var_dict = {}
205 annotation_generics = self.get_generics()
207 if not annotation_generics:
208 return type_var_dict
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 )
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 )
225 elif annotation_name == 'Tuple':
226 tuple_annotation, = self.execute_annotation()
227 return tuple_annotation.infer_type_vars(value_set)
229 return type_var_dict
231 def _create_instance_with_generics(self, generics_manager):
232 return TypingClassWithGenerics(
233 self.parent_context,
234 self._tree_name,
235 generics_manager
236 )
239class ProxyTypingClassValue(ProxyTypingValue, _TypingClassMixin):
240 index_class = TypingClassWithGenerics
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
250 @property
251 def name(self):
252 return ValueName(self, self._origin_tree_name)
254 def py__name__(self):
255 return self.name.string_name
257 def __repr__(self):
258 return '<%s: %s>' % (self.__class__.__name__, self._actual)
260 def _get_wrapped_value(self):
261 module_name, class_name = self._actual.split('.')
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
274 def gather_annotation_classes(self):
275 return ValueSet([self._get_wrapped_value()])
277 def get_signatures(self):
278 return []
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)
297 def py__get__(self, instance, class_value):
298 return ValueSet([self])
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()
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)
314 debug.dbg('The getitem type on Tuple was %s' % index)
315 return NO_VALUES
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())
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)
328 return ValueSet.from_sets(
329 self._generics_manager.to_tuple()
330 ).execute_annotation()
332 def _get_wrapped_value(self):
333 tuple_, = self.inference_state.builtins_module \
334 .py__getattribute__('tuple').execute_annotation()
335 return tuple_
337 @property
338 def name(self):
339 return self._wrapped_value.name
341 def infer_type_vars(self, value_set):
342 # Circular
343 from jedi.inference.gradual.annotation import merge_pairwise_generics, merge_type_var_dicts
345 value_set = value_set.filter(
346 lambda x: x.py__name__().lower() == 'tuple',
347 )
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 )
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.
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
373 py_class = method()
374 merge_type_var_dicts(
375 type_var_dict,
376 merge_pairwise_generics(self._class_value, py_class),
377 )
379 return type_var_dict
382class Generic(BaseTypingInstance):
383 pass
386class Protocol(BaseTypingInstance):
387 pass
390class AnyClass(BaseTypingValue):
391 def execute_annotation(self):
392 debug.warning('Used Any - returned no results')
393 return NO_VALUES
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
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())
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
425 def py__class__(self):
426 c, = self._type_value_set.py__class__()
427 return c
429 def py__call__(self, arguments):
430 return self._type_value_set.execute_annotation()
432 @property
433 def name(self):
434 from jedi.inference.compiled.value import CompiledValueName
435 return CompiledValueName(self, 'NewType')
437 def __repr__(self) -> str:
438 return '<NewType: %s>%s' % (self.tree_node, self._type_value_set)
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()
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 """
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
462 @property
463 def name(self):
464 return ValueName(self, self.tree_node.name)
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
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 })
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