Implement support for TypeVar inference for __new__

This commit is contained in:
Dave Halter
2026-05-01 22:51:00 +02:00
parent 55e5f0cb92
commit 8520a9958b
6 changed files with 35 additions and 20 deletions
+3 -1
View File
@@ -10,7 +10,9 @@ Unreleased
- Removed support for Python 3.8 and 3.9
- Upgraded Typeshed
- Better support for Final/ClassVar
- ``__new__`` is now also recognized as a signature
- ``__new__`` is now also recognized as a signature and TypeVar inference
- Support for ``Self``
- Support for ``TypeAlias``, generics for ``type[...]`` and ``tuple[...]``
0.19.2 (2024-11-10)
+++++++++++++++++++
+2 -2
View File
@@ -17,7 +17,7 @@ from jedi.inference.arguments import ValuesArguments, TreeArgumentsWrapper
from jedi.inference.value.function import \
FunctionValue, FunctionMixin, OverloadedFunctionValue, \
BaseFunctionExecutionContext, FunctionExecutionContext, FunctionNameInClass
from jedi.inference.value.klass import ClassFilter
from jedi.inference.value.klass import ClassFilter, init_or_new_func
from jedi.inference.value.dynamic_arrays import get_dynamic_array_instance
from jedi.parser_utils import function_is_staticmethod, function_is_classmethod
@@ -327,7 +327,7 @@ class TreeInstance(_BaseTreeInstance):
infer_type_vars_for_execution
args = InstanceArguments(self, self._arguments)
for signature in self.class_value.py__getattribute__('__init__').get_signatures():
for signature in init_or_new_func(self.class_value).get_signatures():
# Just take the first result, it should always be one, because we
# control the typeshed code.
funcdef = signature.value.tree_node
+19 -13
View File
@@ -376,19 +376,8 @@ class ClassMixin:
if sigs:
return sigs
args = ValuesArguments([])
init_funcs = self.py__call__(args).py__getattribute__('__init__')
if len(init_funcs) == 1:
init = next(iter(init_funcs))
try:
class_context = init.class_context
except AttributeError:
pass
else:
# In the case where we are on object.__init__, we try to use
# __new__.
if class_context.get_root_context().is_builtins_module() \
and init.class_context.name.string_name == "object":
init_funcs = self.py__call__(args).py__getattribute__('__new__')
instance = self.py__call__(args)
init_funcs = init_or_new_func(instance)
dataclass_sigs = self._get_dataclass_transform_signatures()
if dataclass_sigs:
@@ -481,6 +470,23 @@ class ClassMixin:
return ValueSet({self})
def init_or_new_func(value):
init_funcs = value.py__getattribute__('__init__')
if len(init_funcs) == 1:
init = next(iter(init_funcs))
try:
class_context = init.class_context
except AttributeError:
pass
else:
# In the case where we are on object.__init__, we try to use
# __new__.
if class_context.get_root_context().is_builtins_module() \
and init.class_context.name.string_name == "object":
return value.py__getattribute__('__new__')
return init_funcs
class DataclassParamName(BaseTreeParamName):
"""
Represent a field declaration on a class with dataclass semantics.
+2 -2
View File
@@ -435,9 +435,9 @@ list(set(list(set(a))))[1]
list(set(set(a)))[1]
# frozenset
##? int() str()
#? int() str()
list(frozenset(a))[1]
##? int() str()
#? int() str()
list(set(frozenset(a)))[1]
# iter
@@ -11,6 +11,7 @@ from typing import (
TypeVar,
Union,
Sequence,
Self,
)
K = TypeVar('K')
@@ -395,3 +396,11 @@ def generic_func2(arg: T) -> Union[int, str, T]: pass
generic_func1(b"hello")
#? int() str() bytes()
generic_func2(b"hello")
class CustomGeneric2(Generic[T_co]):
val: T_co
def __init__(cls, val: T_co) -> Self:
raise NotImplementedError
#? int()
CustomGeneric2(1).val
-2
View File
@@ -8,8 +8,6 @@
#! 12 type-error-not-iterable
[a for a in 1]
# TODO wrong?
#! 10 type-error-too-many-arguments
tuple(str(a) for a in [1])
#! 8 type-error-operation