mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Changed behavior of conversion from class constructor to callable to conform with the typing spec in the case where the __new__
method of the class returns a value that indicates the __init__
method should be ignored. This addresses #7686. (#7698)
This commit is contained in:
parent
74880b87d3
commit
43fe9a7e2c
@ -787,11 +787,33 @@ export function createFunctionFromConstructor(
|
||||
return fromMetaclassCall;
|
||||
}
|
||||
|
||||
const fromInit = createFunctionFromInitMethod(evaluator, classType, selfType, recursionCount);
|
||||
const fromNew = createFunctionFromNewMethod(evaluator, classType, selfType, recursionCount);
|
||||
|
||||
if (fromInit) {
|
||||
return fromNew ? combineTypes([fromInit, fromNew]) : fromInit;
|
||||
if (fromNew) {
|
||||
let skipInitMethod = false;
|
||||
|
||||
doForEachSignature(fromNew, (signature) => {
|
||||
const newMethodReturnType = FunctionType.getSpecializedReturnType(signature);
|
||||
if (newMethodReturnType && shouldSkipInitEvaluation(evaluator, classType, newMethodReturnType)) {
|
||||
skipInitMethod = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (skipInitMethod) {
|
||||
return fromNew;
|
||||
}
|
||||
}
|
||||
|
||||
const fromInit = createFunctionFromInitMethod(evaluator, classType, selfType, recursionCount);
|
||||
|
||||
// If there is both a __new__ and __init__ method, return a union
|
||||
// comprised of both resulting function types.
|
||||
if (fromNew && fromInit) {
|
||||
return combineTypes([fromInit, fromNew]);
|
||||
}
|
||||
|
||||
if (fromNew || fromInit) {
|
||||
return fromNew ?? fromInit;
|
||||
}
|
||||
|
||||
return fromNew ?? createFunctionFromObjectNewMethod(classType);
|
||||
@ -1061,7 +1083,7 @@ function shouldSkipInitEvaluation(evaluator: TypeEvaluator, classType: ClassType
|
||||
|
||||
let skipInitCheck = false;
|
||||
doForEachSubtype(returnType, (subtype) => {
|
||||
if (isAnyOrUnknown(subtype)) {
|
||||
if (isUnknown(subtype)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,16 @@
|
||||
# a callable.
|
||||
|
||||
|
||||
from typing import Any, Callable, NoReturn, ParamSpec, Self, TypeVar, reveal_type
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Generic,
|
||||
NoReturn,
|
||||
ParamSpec,
|
||||
Self,
|
||||
TypeVar,
|
||||
reveal_type,
|
||||
)
|
||||
|
||||
P = ParamSpec("P")
|
||||
R = TypeVar("R")
|
||||
@ -43,8 +52,6 @@ reveal_type(r3(3), expected_text="Class3")
|
||||
|
||||
|
||||
class Class4:
|
||||
"""__new__ but no __init__"""
|
||||
|
||||
def __new__(cls, x: int) -> int: ...
|
||||
|
||||
|
||||
@ -59,12 +66,38 @@ class Meta1(type):
|
||||
|
||||
|
||||
class Class5(metaclass=Meta1):
|
||||
"""Custom metaclass that overrides type.__call__"""
|
||||
|
||||
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
|
||||
"""This __new__ is ignored for purposes of conversion"""
|
||||
return super().__new__(cls)
|
||||
|
||||
|
||||
r5 = accepts_callable(Class5)
|
||||
reveal_type(r5, expected_text="(*args: Any, **kwargs: Any) -> NoReturn")
|
||||
|
||||
|
||||
class Class6Proxy: ...
|
||||
|
||||
|
||||
class Class6:
|
||||
def __new__(cls) -> Class6Proxy:
|
||||
return Class6Proxy.__new__(cls)
|
||||
|
||||
def __init__(self, x: int) -> None:
|
||||
pass
|
||||
|
||||
|
||||
r6 = accepts_callable(Class6)
|
||||
reveal_type(r6, expected_text="() -> Class6Proxy")
|
||||
reveal_type(r6(), expected_text="Class6Proxy")
|
||||
|
||||
|
||||
class Class6_2:
|
||||
def __new__(cls) -> Any:
|
||||
return super().__new__(cls)
|
||||
|
||||
def __init__(self, x: int) -> None:
|
||||
pass
|
||||
|
||||
|
||||
r6_2 = accepts_callable(Class6_2)
|
||||
reveal_type(r6_2, expected_text="() -> Any")
|
||||
reveal_type(r6_2(), expected_text="Any")
|
||||
|
Loading…
Reference in New Issue
Block a user