Implement Self, fixes #2023, fixes #2068

This commit is contained in:
Dave Halter
2026-04-29 17:51:01 +02:00
parent c30732eb04
commit aa72381ed1
16 changed files with 92 additions and 47 deletions
+1 -1
View File
@@ -59,7 +59,7 @@ class HelperValueMixin:
arguments = ValuesArguments([ValueSet([value]) for value in value_list])
return self.inference_state.execute(self, arguments)
def execute_annotation(self):
def execute_annotation(self, context):
return self.execute_with_values()
def gather_annotation_classes(self):
+8 -6
View File
@@ -54,7 +54,7 @@ class CompiledValue(Value):
return create_from_access_path(
self.inference_state,
return_annotation
).execute_annotation()
).execute_annotation(arguments.context)
try:
self.access_handle.getattr_paths('__call__')
@@ -241,7 +241,7 @@ class CompiledValue(Value):
except TypeError:
return NO_VALUES
def execute_annotation(self):
def execute_annotation(self, context):
if self.access_handle.get_repr() == 'None':
# None as an annotation doesn't need to be executed.
return ValueSet([self])
@@ -252,7 +252,9 @@ class CompiledValue(Value):
for path in args
]
if name == 'Union':
return ValueSet.from_sets(arg.execute_annotation() for arg in arguments)
return ValueSet.from_sets(
arg.execute_annotation(context)
for arg in arguments)
elif name:
# While with_generics only exists on very specific objects, we
# should probably be fine, because we control all the typing
@@ -260,8 +262,8 @@ class CompiledValue(Value):
return ValueSet([
v.with_generics(arguments)
for v in self.inference_state.typing_module.py__getattribute__(name)
]).execute_annotation()
return super().execute_annotation()
]).execute_annotation(context)
return super().execute_annotation(context)
def negate(self):
return create_from_access_path(self.inference_state, self.access_handle.negate())
@@ -459,7 +461,7 @@ class CompiledValueFilter(AbstractFilter):
values = create_from_access_path(
self._inference_state,
property_return_annotation
).execute_annotation()
).execute_annotation(None)
if values:
return [CompiledValueName(v, name) for v in values]
+1 -1
View File
@@ -246,7 +246,7 @@ def _execute_array_values(inference_state, array):
cls = FakeTuple if array.array_type == 'tuple' else FakeList
return {cls(inference_state, values)}
else:
return array.execute_annotation()
return array.execute_annotation(None)
@inference_state_method_cache()
+6 -6
View File
@@ -249,12 +249,12 @@ def infer_return_types(function, arguments):
return _infer_annotation_string(
context,
match.group(1).strip()
).execute_annotation()
).execute_annotation(context)
unknown_type_vars = find_unknown_type_vars(context, annotation)
annotation_values = infer_annotation(context, annotation)
if not unknown_type_vars:
return annotation_values.execute_annotation()
return annotation_values.execute_annotation(context)
type_var_dict = infer_type_vars_for_execution(function, arguments, all_annotations)
@@ -262,7 +262,7 @@ def infer_return_types(function, arguments):
ann.define_generics(type_var_dict)
if isinstance(ann, (DefineGenericBaseClass, TypeVar)) else ValueSet({ann})
for ann in annotation_values
).execute_annotation()
).execute_annotation(context)
def infer_type_vars_for_execution(function, arguments, annotation_dict):
@@ -315,7 +315,7 @@ def infer_return_for_callable(arguments, param_values, result_values):
if isinstance(v, (DefineGenericBaseClass, TypeVar))
else ValueSet({v})
for v in result_values
).execute_annotation()
).execute_annotation(arguments.context)
def _infer_type_vars_for_callable(arguments, lazy_params):
@@ -391,7 +391,7 @@ def merge_pairwise_generics(annotation_value, annotated_argument_class):
for annotation_generics_set, actual_generic_set in zip(annotation_generics, actual_generics):
merge_type_var_dicts(
type_var_dict,
annotation_generics_set.infer_type_vars(actual_generic_set.execute_annotation()),
annotation_generics_set.infer_type_vars(actual_generic_set.execute_annotation(None)),
)
return type_var_dict
@@ -438,7 +438,7 @@ def _find_type_from_comment_hint(context, node, varlist, name):
return []
return _infer_annotation_string(
context, match.group(1).strip(), index
).execute_annotation()
).execute_annotation(context)
def find_unknown_type_vars(context, node):
+2 -2
View File
@@ -306,7 +306,7 @@ class _GenericInstanceWrapper(ValueWrapper):
if cls.py__name__() == 'Generator':
generics = cls.get_generics()
try:
return generics[2].execute_annotation()
return generics[2].execute_annotation(None)
except IndexError:
pass
elif cls.py__name__() == 'Iterator':
@@ -427,7 +427,7 @@ class BaseTypingInstance(LazyValueWrapper):
return ValueName(self, self._tree_name)
def _get_wrapped_value(self):
object_, = builtin_from_name(self.inference_state, 'object').execute_annotation()
object_, = builtin_from_name(self.inference_state, 'object').execute_annotation(None)
return object_
def __repr__(self):
+1 -1
View File
@@ -35,7 +35,7 @@ class _AbstractGenericManager:
def get_index_and_execute(self, index):
try:
return self[index].execute_annotation()
return self[index].execute_annotation(None)
except IndexError:
debug.warning('No param #%s found for annotation %s', index, self)
return NO_VALUES
+3 -3
View File
@@ -100,8 +100,8 @@ class TypeVar(BaseTypingValue):
return found
return ValueSet({self})
def execute_annotation(self):
return self._get_classes().execute_annotation()
def execute_annotation(self, context):
return self._get_classes().execute_annotation(context)
def infer_type_vars(self, value_set):
def iterate():
@@ -123,5 +123,5 @@ class TypeWrapper(ValueWrapper):
super().__init__(wrapped_value)
self._original_value = original_value
def execute_annotation(self):
def execute_annotation(self, context):
return ValueSet({self._original_value})
+25 -13
View File
@@ -83,6 +83,9 @@ class TypingModuleName(NameWrapper):
elif name == 'cast':
cast_fn, = self._wrapped_name.infer()
yield CastFunction.create_cached(inference_state, cast_fn)
elif name == 'Self':
yield SelfClass.create_cached(
inference_state, self.parent_context, self.tree_name)
elif name == 'TypedDict':
# TODO doesn't even exist in typeshed/typing.py, yet. But will be
# added soon.
@@ -100,24 +103,24 @@ class TypingModuleFilterWrapper(FilterWrapper):
class ProxyWithGenerics(BaseTypingClassWithGenerics):
def execute_annotation(self):
def execute_annotation(self, context):
string_name = self._tree_name.value
if string_name == 'Union':
# This is kind of a special case, because we have Unions (in Jedi
# ValueSets).
return self.gather_annotation_classes().execute_annotation()
return self.gather_annotation_classes().execute_annotation(context)
elif string_name == 'Optional':
# Optional is basically just saying it's either None or the actual
# type.
return self.gather_annotation_classes().execute_annotation() \
return self.gather_annotation_classes().execute_annotation(context) \
| ValueSet([builtin_from_name(self.inference_state, 'None')])
elif string_name == 'Type':
# The type is actually already given in the index_value
return self._generics_manager[0]
elif string_name in IGNORE_ANNOTATION_PARTS:
# For now don't do anything here, ClassVars are always used.
return self._generics_manager[0].execute_annotation()
return self._generics_manager[0].execute_annotation(context)
mapped = {
'Tuple': Tuple,
@@ -217,17 +220,17 @@ class TypingClassWithGenerics(ProxyWithGenerics, _TypingClassMixin):
# This is basically a trick to avoid extra code: We execute the
# incoming classes to be able to use the normal code for type
# var inference.
value_set.execute_annotation(),
value_set.execute_annotation(None),
)
elif annotation_name == 'Callable':
if len(annotation_generics) == 2:
return annotation_generics[1].infer_type_vars(
value_set.execute_annotation(),
value_set.execute_annotation(None),
)
elif annotation_name == 'Tuple':
tuple_annotation, = self.execute_annotation()
tuple_annotation, = self.execute_annotation(None)
return tuple_annotation.infer_type_vars(value_set)
return type_var_dict
@@ -323,7 +326,7 @@ class Tuple(BaseTypingInstance):
yield LazyKnownValues(self._generics_manager.get_index_and_execute(0))
else:
for v in self._generics_manager.to_tuple():
yield LazyKnownValues(v.execute_annotation())
yield LazyKnownValues(v.execute_annotation(None))
def py__getitem__(self, index_value_set, contextualized_node):
if self._is_homogenous():
@@ -331,11 +334,11 @@ class Tuple(BaseTypingInstance):
return ValueSet.from_sets(
self._generics_manager.to_tuple()
).execute_annotation()
).execute_annotation(None)
def _get_wrapped_value(self):
tuple_, = self.inference_state.builtins_module \
.py__getattribute__('tuple').execute_annotation()
.py__getattribute__('tuple').execute_annotation(None)
return tuple_
@property
@@ -392,11 +395,20 @@ class Protocol(BaseTypingInstance):
class AnyClass(BaseTypingValue):
def execute_annotation(self):
def execute_annotation(self, context):
debug.warning('Used Any - returned no results')
return NO_VALUES
class SelfClass(BaseTypingValue):
def execute_annotation(self, context):
debug.warning('Used Self')
if context is not None:
# Execute the class of Self
return context.get_value().execute_annotation(None)
return NO_VALUES
class OverloadFunction(BaseTypingValue):
@repack_with_argument_clinic('func, /')
def py__call__(self, func_value_set):
@@ -431,7 +443,7 @@ class NewType(Value):
return c
def py__call__(self, arguments):
return self._type_value_set.execute_annotation()
return self._type_value_set.execute_annotation(arguments.context)
@property
def name(self):
@@ -445,7 +457,7 @@ class NewType(Value):
class CastFunction(ValueWrapper):
@repack_with_argument_clinic('type, object, /')
def py__call__(self, type_value_set, object_value_set):
return type_value_set.execute_annotation()
return type_value_set.execute_annotation(None)
class TypedDictClass(BaseTypingValue):
+1 -1
View File
@@ -466,7 +466,7 @@ class _ActualTreeParamName(BaseTreeParamName):
self.function_value, self._get_param_node(),
ignore_stars=ignore_stars)
if execute_annotation:
values = values.execute_annotation()
values = values.execute_annotation(self.function_value.get_default_param_context())
return values
def infer_default(self):
+6 -4
View File
@@ -239,7 +239,7 @@ def _infer_node(context, element):
return context.infer_node(element.children[0])
elif typ == 'annassign':
return annotation.infer_annotation(context, element.children[1]) \
.execute_annotation()
.execute_annotation(context)
elif typ == 'yield_expr':
if len(element.children) and element.children[1].type == 'yield_arg':
# Implies that it's a yield from.
@@ -497,7 +497,7 @@ def infer_factor(value_set, operator):
b = value.py__bool__()
if b is None: # Uncertainty.
yield list(value.inference_state.builtins_module.py__getattribute__('bool')
.execute_annotation()).pop()
.execute_annotation(None)).pop()
else:
yield compiled.create_simple_object(value.inference_state, not b)
else:
@@ -650,7 +650,9 @@ def _infer_comparison_part(inference_state, context, left, operator, right):
_bool_to_value(inference_state, False)
])
elif str_operator in ('in', 'not in'):
return inference_state.builtins_module.py__getattribute__('bool').execute_annotation()
return inference_state.builtins_module.py__getattribute__('bool').execute_annotation(
context
)
def check(obj):
"""Checks if a Jedi object is either a float or an int."""
@@ -719,7 +721,7 @@ def tree_name_to_values(inference_state, context, tree_name):
and first.name.string_name in IGNORE_ANNOTATION_PARTS
)
found_annotation = set_found_annotation
value_set |= found.execute_annotation()
value_set |= found.execute_annotation(context)
if found_annotation:
return value_set
+2 -2
View File
@@ -338,7 +338,7 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
return ValueSet(
GenericClass(c, TupleGenericManager(generics))
for c in async_generator_classes
).execute_annotation()
).execute_annotation(None)
else:
async_classes = inference_state.typing_module.py__getattribute__('Coroutine')
return_values = self.get_return_values()
@@ -346,7 +346,7 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
generics = (return_values.py__class__(), NO_VALUES, NO_VALUES)
return ValueSet(
GenericClass(c, TupleGenericManager(generics)) for c in async_classes
).execute_annotation()
).execute_annotation(None)
else:
# If there are annotations, prefer them over anything else.
if self.is_generator() and not self.infer_annotations():
+1 -1
View File
@@ -506,7 +506,7 @@ class SelfName(TreeNameDefinition):
from jedi.inference.gradual.annotation import infer_annotation
values = infer_annotation(
self.parent_context, stmt.children[1].children[1]
).execute_annotation()
).execute_annotation(None)
if values:
return values
return super().infer()
+2 -2
View File
@@ -44,7 +44,7 @@ class GeneratorBase(LazyAttributeOverwrite, IterableMixin):
array_type = None
def _get_wrapped_value(self):
instance, = self._get_cls().execute_annotation()
instance, = self._get_cls().execute_annotation(None)
return instance
def _get_cls(self):
@@ -214,7 +214,7 @@ class Sequence(LazyAttributeOverwrite, IterableMixin):
c, = GenericClass(
klass,
TupleGenericManager(self._cached_generics())
).execute_annotation()
).execute_annotation(None)
return c
def py__bool__(self):
+2 -2
View File
@@ -46,7 +46,7 @@ _FILTER_LIKE_METHODS = ('create', 'filter', 'exclude', 'update', 'get',
def _get_deferred_attributes(inference_state):
return inference_state.import_module(
('django', 'db', 'models', 'query_utils')
).py__getattribute__('DeferredAttribute').execute_annotation()
).py__getattribute__('DeferredAttribute').execute_annotation(None)
def _infer_scalar_field(inference_state, field_name, field_tree_instance, is_instance):
@@ -130,7 +130,7 @@ def _create_manager_for(cls, manager_cls='BaseManager'):
for m in managers:
if m.is_class_mixin():
generics_manager = TupleGenericManager((ValueSet([cls]),))
for c in GenericClass(m, generics_manager).execute_annotation():
for c in GenericClass(m, generics_manager).execute_annotation(None):
return c
return None
+1 -1
View File
@@ -37,7 +37,7 @@ def infer_anonymous_param(func):
== ('typing', 'Generator')
for v in result):
return ValueSet.from_sets(
v.py__getattribute__('__next__').execute_annotation()
v.py__getattribute__('__next__').execute_annotation(None)
for v in result
)
return result
+30 -1
View File
@@ -3,7 +3,7 @@ Test the typing library, with docstrings and annotations
"""
import typing
from typing import Sequence, MutableSequence, List, Iterable, Iterator, \
AbstractSet, Tuple, Mapping, Dict, Union, Optional, Final
AbstractSet, Tuple, Mapping, Dict, Union, Optional, Final, Self
class B:
pass
@@ -555,3 +555,32 @@ def typed_dict_test_foo(arg: Bar):
arg['an_int']
#? int()
arg['another_variable']
# -----------------
# Self
# -----------------
# From #2023, #2068
class Builder:
def __init__(self):
self.x = 0
self.y = 0
def add_x(self: Self, x: int) -> Self:
self.x = x
return self
def add_y(self: Self, y: int) -> Self:
self.y = y
return self
def add_not_implemented(self: Self, y: int) -> Self:
raise NotImplementedError
b = Builder()
#? Builder()
b.add_x(2)
#? Builder()
b.add_x(2).add_y(5)
#? Builder()
b.add_x(2).add_not_implemented(5)