Fixed recent regression that affected unannotated __call__ methods in a metaclass. This change aligns pyright's behavior to the typing spec. It addresses #7717. (#7746)

This commit is contained in:
Eric Traut 2024-04-22 17:50:11 -07:00 committed by GitHub
parent 9ca7cbd48c
commit 419a8f41c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 41 additions and 21 deletions

View File

@ -674,28 +674,31 @@ function validateMetaclassCall(
): CallResult | undefined { ): CallResult | undefined {
const metaclassCallMethodInfo = getBoundCallMethod(evaluator, errorNode, type); const metaclassCallMethodInfo = getBoundCallMethod(evaluator, errorNode, type);
if (metaclassCallMethodInfo) { if (!metaclassCallMethodInfo) {
const callResult = evaluator.validateCallArguments( return undefined;
errorNode,
argList,
metaclassCallMethodInfo,
/* typeVarContext */ undefined,
skipUnknownArgCheck,
inferenceContext
);
if (!callResult.returnType || isUnknown(callResult.returnType)) {
// The return result isn't known. We'll assume in this case that
// the metaclass __call__ method allocated a new instance of the
// requested class.
const typeVarContext = new TypeVarContext(getTypeVarScopeId(type));
callResult.returnType = applyExpectedTypeForConstructor(evaluator, type, inferenceContext, typeVarContext);
}
return callResult;
} }
return undefined; const callResult = evaluator.validateCallArguments(
errorNode,
argList,
metaclassCallMethodInfo,
/* typeVarContext */ undefined,
skipUnknownArgCheck,
inferenceContext
);
// If the return type is unannotated, don't use the inferred return type.
const callType = metaclassCallMethodInfo.type;
if (isFunction(callType) && !callType.details.declaredReturnType) {
return undefined;
}
// If the return type is unknown, ignore it.
if (callResult.returnType && isUnknown(callResult.returnType)) {
return undefined;
}
return callResult;
} }
function applyExpectedSubtypeForConstructor( function applyExpectedSubtypeForConstructor(

View File

@ -5,6 +5,9 @@
# pyright: reportIncompatibleMethodOverride=false # pyright: reportIncompatibleMethodOverride=false
from typing import Any, Self
class MetaClass1(type): class MetaClass1(type):
def __call__(cls, **kwargs): def __call__(cls, **kwargs):
return object.__new__(**kwargs) return object.__new__(**kwargs)
@ -33,7 +36,7 @@ reveal_type(v2, expected_text="NoReturn")
class MetaClass3(type): class MetaClass3(type):
def __call__(cls, *args, **kwargs): def __call__(cls, *args, **kwargs) -> Any:
return super().__call__(*args, **kwargs) return super().__call__(*args, **kwargs)
@ -44,3 +47,17 @@ class Class3(metaclass=MetaClass3):
v3 = Class3() v3 = Class3()
reveal_type(v3, expected_text="Any") reveal_type(v3, expected_text="Any")
class MetaClass4(type):
def __call__(cls, *args, **kwargs):
return super().__call__(*args, **kwargs)
class Class4(metaclass=MetaClass4):
def __new__(cls, *args, **kwargs) -> Self:
return super().__new__(cls, *args, **kwargs)
v4 = Class4()
reveal_type(v4, expected_text="Class4")