mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-19 04:07:36 +03:00
Fixed bug in synthesized __new__
method for NamedTuple class that caused the constructor of subclasses of the NamedTuple to construct the base class.
This commit is contained in:
parent
8057c63bc1
commit
d07a64e733
@ -49,8 +49,10 @@ import {
|
||||
import {
|
||||
applySolvedTypeVars,
|
||||
buildTypeVarMapFromSpecializedClass,
|
||||
convertToInstance,
|
||||
isLiteralType,
|
||||
specializeTupleClass,
|
||||
synthesizeTypeVarForSelfCls,
|
||||
} from './typeUtils';
|
||||
|
||||
// Validates fields for compatibility with a dataclass and synthesizes
|
||||
@ -64,6 +66,7 @@ export function synthesizeDataClassMethods(
|
||||
) {
|
||||
assert(ClassType.isDataClass(classType));
|
||||
|
||||
const classTypeVar = synthesizeTypeVarForSelfCls(classType, /* isClsParam */ true);
|
||||
const newType = FunctionType.createInstance(
|
||||
'__new__',
|
||||
'',
|
||||
@ -75,16 +78,16 @@ export function synthesizeDataClassMethods(
|
||||
FunctionType.addParameter(newType, {
|
||||
category: ParameterCategory.Simple,
|
||||
name: 'cls',
|
||||
type: classType,
|
||||
type: classTypeVar,
|
||||
hasDeclaredType: true,
|
||||
});
|
||||
FunctionType.addDefaultParameters(newType);
|
||||
newType.details.declaredReturnType = ClassType.cloneAsInstance(classType);
|
||||
newType.details.declaredReturnType = convertToInstance(classTypeVar);
|
||||
|
||||
const selfParam: FunctionParameter = {
|
||||
category: ParameterCategory.Simple,
|
||||
name: 'self',
|
||||
type: ClassType.cloneAsInstance(classType),
|
||||
type: synthesizeTypeVarForSelfCls(classType, /* isClsParam */ false),
|
||||
hasDeclaredType: true,
|
||||
};
|
||||
FunctionType.addParameter(initType, selfParam);
|
||||
|
@ -42,6 +42,7 @@ import {
|
||||
isOpenEndedTupleClass,
|
||||
isTupleClass,
|
||||
specializeTupleClass,
|
||||
synthesizeTypeVarForSelfCls,
|
||||
} from './typeUtils';
|
||||
|
||||
// Creates a new custom tuple factory class with named values.
|
||||
@ -98,6 +99,7 @@ export function createNamedTupleType(
|
||||
isInstantiableClass(namedTupleType) ? namedTupleType.details.effectiveMetaclass : UnknownType.create()
|
||||
);
|
||||
classType.details.baseClasses.push(namedTupleType);
|
||||
classType.details.typeVarScopeId = evaluator.getScopeIdForNode(errorNode);
|
||||
|
||||
const classFields = classType.details.fields;
|
||||
classFields.set(
|
||||
@ -105,20 +107,21 @@ export function createNamedTupleType(
|
||||
Symbol.createWithType(SymbolFlags.ClassMember | SymbolFlags.IgnoredForProtocolMatch, classType)
|
||||
);
|
||||
|
||||
const classTypeVar = synthesizeTypeVarForSelfCls(classType, /* isClsParam */ true);
|
||||
const constructorType = FunctionType.createInstance(
|
||||
'__new__',
|
||||
'',
|
||||
'',
|
||||
FunctionTypeFlags.ConstructorMethod | FunctionTypeFlags.SynthesizedMethod
|
||||
);
|
||||
constructorType.details.declaredReturnType = ClassType.cloneAsInstance(classType);
|
||||
constructorType.details.declaredReturnType = convertToInstance(classTypeVar);
|
||||
if (ParseTreeUtils.isAssignmentToDefaultsFollowingNamedTuple(errorNode)) {
|
||||
constructorType.details.flags |= FunctionTypeFlags.DisableDefaultChecks;
|
||||
}
|
||||
FunctionType.addParameter(constructorType, {
|
||||
category: ParameterCategory.Simple,
|
||||
name: 'cls',
|
||||
type: classType,
|
||||
type: classTypeVar,
|
||||
hasDeclaredType: true,
|
||||
});
|
||||
|
||||
@ -127,7 +130,7 @@ export function createNamedTupleType(
|
||||
const selfParameter: FunctionParameter = {
|
||||
category: ParameterCategory.Simple,
|
||||
name: 'self',
|
||||
type: ClassType.cloneAsInstance(classType),
|
||||
type: synthesizeTypeVarForSelfCls(classType, /* isClsParam */ false),
|
||||
hasDeclaredType: true,
|
||||
};
|
||||
|
||||
|
@ -258,12 +258,12 @@ import {
|
||||
removeTruthinessFromType,
|
||||
requiresSpecialization,
|
||||
requiresTypeArguments,
|
||||
selfSpecializeClassType,
|
||||
setTypeArgumentsRecursive,
|
||||
specializeClassType,
|
||||
specializeForBaseClass,
|
||||
specializeTupleClass,
|
||||
stripLiteralValue,
|
||||
synthesizeTypeVarForSelfCls,
|
||||
transformExpectedTypeForConstructor,
|
||||
transformPossibleRecursiveTypeAlias,
|
||||
} from './typeUtils';
|
||||
@ -12569,7 +12569,6 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
const inferredParamType = inferFirstParamType(
|
||||
functionType.details.flags,
|
||||
containingClassType,
|
||||
node
|
||||
);
|
||||
if (inferredParamType) {
|
||||
functionType.details.parameters[0].type = inferredParamType;
|
||||
@ -12680,26 +12679,12 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
function inferFirstParamType(
|
||||
flags: FunctionTypeFlags,
|
||||
containingClassType: ClassType,
|
||||
functionNode: FunctionNode
|
||||
): Type | undefined {
|
||||
if ((flags & FunctionTypeFlags.StaticMethod) === 0) {
|
||||
if (containingClassType) {
|
||||
const hasClsParam = flags & (FunctionTypeFlags.ClassMethod | FunctionTypeFlags.ConstructorMethod);
|
||||
|
||||
const selfType = TypeVarType.createInstance(`__type_of_self_${containingClassType.details.name}`);
|
||||
const scopeId = getScopeIdForNode(functionNode);
|
||||
selfType.details.isSynthesized = true;
|
||||
selfType.details.isSynthesizedSelfCls = true;
|
||||
selfType.nameWithScope = TypeVarType.makeNameWithScope(selfType.details.name, scopeId);
|
||||
selfType.scopeId = scopeId;
|
||||
|
||||
// The self/cls parameter is allowed to skip the abstract class test
|
||||
// because the caller is possibly passing in a non-abstract subclass.
|
||||
selfType.details.boundType = ClassType.cloneAsInstance(
|
||||
selfSpecializeClassType(containingClassType, /* includeSubclasses */ true)
|
||||
);
|
||||
|
||||
return hasClsParam ? convertToInstantiable(selfType) : selfType;
|
||||
const hasClsParam =
|
||||
(flags & (FunctionTypeFlags.ClassMethod | FunctionTypeFlags.ConstructorMethod)) !== 0;
|
||||
return synthesizeTypeVarForSelfCls(containingClassType, hasClsParam);
|
||||
}
|
||||
}
|
||||
|
||||
@ -14324,7 +14309,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
const functionFlags = getFunctionFlagsFromDecorators(functionNode, /* isInClass */ true);
|
||||
// If the first parameter doesn't have an explicit type annotation,
|
||||
// provide a type if it's an instance, class or constructor method.
|
||||
const inferredParamType = inferFirstParamType(functionFlags, classInfo.classType, functionNode);
|
||||
const inferredParamType = inferFirstParamType(functionFlags, classInfo.classType);
|
||||
writeTypeCache(node.name!, inferredParamType || UnknownType.create(), /* isIncomplete */ false);
|
||||
return;
|
||||
}
|
||||
|
@ -1303,6 +1303,23 @@ export function removeTruthinessFromType(type: Type): Type {
|
||||
});
|
||||
}
|
||||
|
||||
export function synthesizeTypeVarForSelfCls(classType: ClassType, isClsParam: boolean) {
|
||||
const selfType = TypeVarType.createInstance(`__type_of_${isClsParam ? 'cls' : 'self'}_${classType.details.name}`);
|
||||
const scopeId = getTypeVarScopeId(classType) ?? '';
|
||||
selfType.details.isSynthesized = true;
|
||||
selfType.details.isSynthesizedSelfCls = true;
|
||||
selfType.nameWithScope = TypeVarType.makeNameWithScope(selfType.details.name, scopeId);
|
||||
selfType.scopeId = scopeId;
|
||||
|
||||
// The self/cls parameter is allowed to skip the abstract class test
|
||||
// because the caller is possibly passing in a non-abstract subclass.
|
||||
selfType.details.boundType = ClassType.cloneAsInstance(
|
||||
selfSpecializeClassType(classType, /* includeSubclasses */ true)
|
||||
);
|
||||
|
||||
return isClsParam ? convertToInstantiable(selfType) : selfType;
|
||||
}
|
||||
|
||||
// Returns the declared yield type if provided, or undefined otherwise.
|
||||
export function getDeclaredGeneratorYieldType(functionType: FunctionType): Type | undefined {
|
||||
const returnType = FunctionType.getSpecializedReturnType(functionType);
|
||||
|
24
packages/pyright-internal/src/tests/samples/namedTuples4.py
Normal file
24
packages/pyright-internal/src/tests/samples/namedTuples4.py
Normal file
@ -0,0 +1,24 @@
|
||||
# This sample tests the case where a class derives from a named tuple.
|
||||
# The synthesized __new__ method should be able to handle this.
|
||||
|
||||
from collections import namedtuple
|
||||
from typing import Literal, NamedTuple
|
||||
|
||||
|
||||
Class1 = namedtuple("Class1", "name")
|
||||
|
||||
|
||||
class Class2(Class1):
|
||||
some_class_member = 1
|
||||
|
||||
|
||||
t1: Literal["Class2"] = reveal_type(Class2(name="a"))
|
||||
|
||||
Class3 = NamedTuple("Class3", [("name", str)])
|
||||
|
||||
|
||||
class Class4(Class3):
|
||||
some_class_member = 1
|
||||
|
||||
|
||||
t2: Literal["Class4"] = reveal_type(Class4(name="a"))
|
@ -980,6 +980,12 @@ test('NamedTuples3', () => {
|
||||
TestUtils.validateResults(analysisResults, 2);
|
||||
});
|
||||
|
||||
test('NamedTuples4', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['namedTuples4.py']);
|
||||
|
||||
TestUtils.validateResults(analysisResults, 0);
|
||||
});
|
||||
|
||||
test('Slots1', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['slots1.py']);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user