mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Added support for PEP 585. Standard collection types defined in builtins can now be used like their typing counterparts. This includes "tuple", which needs special-case handling because its class definition in builtins.pyi indicates that it has a single type parameter, but it actually supports variadic parameters.
This commit is contained in:
parent
d9e588c7af
commit
7bed1e1aaa
@ -14,6 +14,7 @@ Pyright supports [configuration files](/docs/configuration.md) that provide gran
|
||||
* [PEP 544](https://www.python.org/dev/peps/pep-0544/) structural subtyping
|
||||
* [PEP 561](https://www.python.org/dev/peps/pep-0561/) distributing and packaging type information
|
||||
* [PEP 563](https://www.python.org/dev/peps/pep-0563/) postponed evaluation of annotations
|
||||
* [PEP 585](https://www.python.org/dev/peps/pep-0585/) type hinting generics in standard collections
|
||||
* [PEP 589](https://www.python.org/dev/peps/pep-0589/) typed dictionaries
|
||||
* [PEP 591](https://www.python.org/dev/peps/pep-0591/) final qualifier
|
||||
* [PEP 593](https://www.python.org/dev/peps/pep-0593/) flexible variable annotations
|
||||
|
@ -108,6 +108,7 @@ import {
|
||||
isNoReturnType,
|
||||
isPartlyUnknown,
|
||||
isProperty,
|
||||
isTupleClass,
|
||||
lookUpClassMember,
|
||||
makeTypeVarsConcrete,
|
||||
partiallySpecializeType,
|
||||
@ -597,7 +598,7 @@ export class Checker extends ParseTreeWalker {
|
||||
// assert (x > 3, "bad value x")
|
||||
const type = this._evaluator.getType(node.testExpression);
|
||||
if (type && isObject(type)) {
|
||||
if (ClassType.isBuiltIn(type.classType, 'Tuple') && type.classType.typeArguments) {
|
||||
if (isTupleClass(type.classType) && type.classType.typeArguments) {
|
||||
if (type.classType.typeArguments.length > 0) {
|
||||
const lastTypeArg = type.classType.typeArguments[type.classType.typeArguments.length - 1];
|
||||
if (!isEllipsisType(lastTypeArg)) {
|
||||
@ -1460,11 +1461,7 @@ export class Checker extends ParseTreeWalker {
|
||||
};
|
||||
|
||||
let isValidType = true;
|
||||
if (
|
||||
isObject(arg1Type) &&
|
||||
ClassType.isBuiltIn(arg1Type.classType, 'Tuple') &&
|
||||
arg1Type.classType.typeArguments
|
||||
) {
|
||||
if (isObject(arg1Type) && isTupleClass(arg1Type.classType) && arg1Type.classType.typeArguments) {
|
||||
isValidType = !arg1Type.classType.typeArguments.some((typeArg) => !isSupportedTypeForIsInstance(typeArg));
|
||||
} else {
|
||||
isValidType = isSupportedTypeForIsInstance(arg1Type);
|
||||
@ -1513,7 +1510,7 @@ export class Checker extends ParseTreeWalker {
|
||||
// The isinstance and issubclass call supports a variation where the second
|
||||
// parameter is a tuple of classes.
|
||||
const objClass = arg1Type.classType;
|
||||
if (ClassType.isBuiltIn(objClass, 'Tuple') && objClass.typeArguments) {
|
||||
if (isTupleClass(objClass) && objClass.typeArguments) {
|
||||
objClass.typeArguments.forEach((typeArg) => {
|
||||
if (isClass(typeArg)) {
|
||||
classTypeList.push(typeArg);
|
||||
|
@ -182,6 +182,7 @@ import {
|
||||
isParamSpecType,
|
||||
isPartlyUnknown,
|
||||
isProperty,
|
||||
isTupleClass,
|
||||
isTypeAliasRecursive,
|
||||
lookUpClassMember,
|
||||
lookUpObjectMember,
|
||||
@ -426,6 +427,7 @@ const nonSubscriptableBuiltinTypes: { [builtinName: string]: PythonVersion } = {
|
||||
'builtins.list': PythonVersion.V3_9,
|
||||
'builtins._PathLike': PythonVersion.V3_9,
|
||||
'builtins.set': PythonVersion.V3_9,
|
||||
'builtins.tuple': PythonVersion.V3_9,
|
||||
'collections.ChainMap': PythonVersion.V3_9,
|
||||
'collections.Counter': PythonVersion.V3_9,
|
||||
'collections.DefaultDict': PythonVersion.V3_9,
|
||||
@ -1013,7 +1015,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
if (!TypeBase.isInstantiable(resultType)) {
|
||||
const isEmptyTuple =
|
||||
isObject(resultType) &&
|
||||
ClassType.isBuiltIn(resultType.classType, 'Tuple') &&
|
||||
isTupleClass(resultType.classType) &&
|
||||
resultType.classType.typeArguments?.length === 0;
|
||||
|
||||
if ((flags & EvaluatorFlags.AllowEmptyTupleAsType) === 0 || !isEmptyTuple) {
|
||||
@ -4029,7 +4031,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
// perform special processing of the type args in this case to permit
|
||||
// empty tuples.
|
||||
let adjustedFlags = flags;
|
||||
if (isClass(subtype) && ClassType.isBuiltIn(subtype, 'Tuple')) {
|
||||
if (isClass(subtype) && isTupleClass(subtype)) {
|
||||
adjustedFlags |= EvaluatorFlags.AllowEmptyTupleAsType;
|
||||
}
|
||||
|
||||
@ -4329,7 +4331,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
if (expectedType && isObject(expectedType)) {
|
||||
const tupleClass = expectedType.classType;
|
||||
|
||||
if (ClassType.isBuiltIn(tupleClass, 'Tuple') && tupleClass.typeArguments) {
|
||||
if (isTupleClass(tupleClass) && tupleClass.typeArguments) {
|
||||
// Is this a homogeneous tuple of indeterminate length? If so,
|
||||
// match the number of expected types to the number of entries
|
||||
// in the tuple expression.
|
||||
@ -4360,10 +4362,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
// unpacked entries onto the new tuple. If it's not an upacked tuple
|
||||
// but some other iterator (e.g. a List), we won't know the number of
|
||||
// items, so we'll need to leave the Tuple open-ended.
|
||||
if (
|
||||
isObject(typeResult.unpackedType) &&
|
||||
ClassType.isBuiltIn(typeResult.unpackedType.classType, 'Tuple')
|
||||
) {
|
||||
if (isObject(typeResult.unpackedType) && isTupleClass(typeResult.unpackedType.classType)) {
|
||||
const typeArgs = typeResult.unpackedType.classType.typeArguments;
|
||||
|
||||
// If the Tuple wasn't specialized or has a "..." type parameter, we can't
|
||||
@ -4393,20 +4392,12 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
return { type, node };
|
||||
}
|
||||
|
||||
// Classes of type Tuple and tuple require special handling because they
|
||||
// support variadic type parameters (including a form that represents homogenous
|
||||
// arbitrary-length tuples).
|
||||
function cloneTupleForSpecialization(tupleClass: ClassType, typeArgs: Type[], isTypeArgumentExplicit: boolean) {
|
||||
// Verify our assumptions in case typing.pyi changes its definition of Tuple.
|
||||
if (
|
||||
tupleClass.details.mro.length < 2 ||
|
||||
!isClass(tupleClass.details.mro[0]) ||
|
||||
!ClassType.isBuiltIn(tupleClass.details.mro[0], 'Tuple') ||
|
||||
!isClass(tupleClass.details.mro[1]) ||
|
||||
!ClassType.isBuiltIn(tupleClass.details.mro[1], 'tuple')
|
||||
) {
|
||||
return tupleClass;
|
||||
}
|
||||
|
||||
// Create a copy of the Tuple class that overrides the normal MRO
|
||||
// entries with a version of Tuple and tuple that are specialized
|
||||
// entries with a version of Tuple and/or tuple that are specialized
|
||||
// appropriately.
|
||||
let combinedTupleType: Type = AnyType.create(false);
|
||||
if (typeArgs.length === 2 && isEllipsisType(typeArgs[1])) {
|
||||
@ -4415,17 +4406,51 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
combinedTupleType = combineTypes(typeArgs);
|
||||
}
|
||||
|
||||
const specializedTuple = ClassType.cloneForSpecialization(tupleClass, typeArgs, isTypeArgumentExplicit);
|
||||
const effectiveTypeArguments = [combinedTupleType];
|
||||
const specializedTuple = ClassType.cloneForSpecialization(
|
||||
tupleClass,
|
||||
typeArgs,
|
||||
isTypeArgumentExplicit,
|
||||
/* skipAbstractClassTest */ undefined,
|
||||
effectiveTypeArguments
|
||||
);
|
||||
specializedTuple.details = { ...specializedTuple.details };
|
||||
specializedTuple.details.mro = [...specializedTuple.details.mro];
|
||||
specializedTuple.details.mro[0] = specializedTuple;
|
||||
specializedTuple.details.mro[1] = ClassType.cloneForSpecialization(
|
||||
specializedTuple.details.mro[1] as ClassType,
|
||||
[combinedTupleType],
|
||||
isTypeArgumentExplicit
|
||||
);
|
||||
|
||||
return specializedTuple;
|
||||
// Handle the specialization of "Tuple" which inherits from "tuple".
|
||||
if (ClassType.isBuiltIn(tupleClass, 'Tuple')) {
|
||||
assert(
|
||||
tupleClass.details.mro.length >= 2 &&
|
||||
isClass(tupleClass.details.mro[0]) &&
|
||||
ClassType.isBuiltIn(tupleClass.details.mro[0], 'Tuple') &&
|
||||
isClass(tupleClass.details.mro[1]) &&
|
||||
ClassType.isBuiltIn(tupleClass.details.mro[1], 'tuple')
|
||||
);
|
||||
|
||||
specializedTuple.details.mro[1] = ClassType.cloneForSpecialization(
|
||||
specializedTuple.details.mro[1] as ClassType,
|
||||
[combinedTupleType],
|
||||
isTypeArgumentExplicit,
|
||||
/* skipAbstractClassTest */ undefined,
|
||||
effectiveTypeArguments
|
||||
);
|
||||
|
||||
return specializedTuple;
|
||||
}
|
||||
|
||||
// Handle the specialization of "tuple" directly.
|
||||
if (ClassType.isBuiltIn(tupleClass, 'tuple')) {
|
||||
assert(
|
||||
tupleClass.details.mro.length >= 1 &&
|
||||
isClass(tupleClass.details.mro[0]) &&
|
||||
ClassType.isBuiltIn(tupleClass.details.mro[0], 'tuple')
|
||||
);
|
||||
|
||||
return specializedTuple;
|
||||
}
|
||||
|
||||
return tupleClass;
|
||||
}
|
||||
|
||||
function updateNamedTupleBaseClass(classType: ClassType, typeArgs: Type[], isTypeArgumentExplicit: boolean) {
|
||||
@ -5236,7 +5261,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
|
||||
// If the expected type is generic (not specialized), we can't proceed.
|
||||
const expectedTypeArgs = expectedTypeWithoutNone.classType.typeArguments;
|
||||
const expectedTypeArgs =
|
||||
expectedTypeWithoutNone.classType.effectiveTypeArguments || expectedTypeWithoutNone.classType.typeArguments;
|
||||
if (expectedTypeArgs === undefined) {
|
||||
return;
|
||||
}
|
||||
@ -5619,7 +5645,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
// type which will be a union of all element types.
|
||||
if (
|
||||
isObject(argType) &&
|
||||
ClassType.isBuiltIn(argType.classType, 'Tuple') &&
|
||||
isTupleClass(argType.classType) &&
|
||||
argType.classType.typeArguments &&
|
||||
argType.classType.typeArguments.length > 0 &&
|
||||
!isEllipsisType(argType.classType.typeArguments[argType.classType.typeArguments.length - 1])
|
||||
@ -6362,7 +6388,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
const arg1Type = getTypeForArgument(argList[1]);
|
||||
if (
|
||||
!isObject(arg1Type) ||
|
||||
!ClassType.isBuiltIn(arg1Type.classType, 'Tuple') ||
|
||||
!isTupleClass(arg1Type.classType) ||
|
||||
arg1Type.classType.typeArguments === undefined
|
||||
) {
|
||||
return undefined;
|
||||
@ -7494,7 +7520,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
// The result should be a Tuple
|
||||
if (isObject(dictEntryType)) {
|
||||
const classType = dictEntryType.classType;
|
||||
if (ClassType.isBuiltIn(classType, 'Tuple')) {
|
||||
if (isTupleClass(classType)) {
|
||||
const typeArgs = classType.typeArguments;
|
||||
if (typeArgs && typeArgs.length === 2) {
|
||||
keyTypes.push(typeArgs[0]);
|
||||
@ -8209,7 +8235,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
paramLimit?: number,
|
||||
allowParamSpec = false
|
||||
): Type {
|
||||
const isTuple = ClassType.isBuiltIn(classType, 'Tuple');
|
||||
const isTuple = isTupleClass(classType);
|
||||
|
||||
if (typeArgs) {
|
||||
// Verify that we didn't receive any inappropriate ellipses or modules.
|
||||
@ -8228,11 +8254,11 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
});
|
||||
|
||||
// Handle Tuple[()] as a special case, as defined in PEP 483.
|
||||
if (ClassType.isBuiltIn(classType, 'Tuple')) {
|
||||
if (isTuple) {
|
||||
if (
|
||||
typeArgs.length === 1 &&
|
||||
isObject(typeArgs[0].type) &&
|
||||
ClassType.isBuiltIn(typeArgs[0].type.classType, 'Tuple') &&
|
||||
isTupleClass(typeArgs[0].type.classType) &&
|
||||
typeArgs[0].type.classType.typeArguments &&
|
||||
typeArgs[0].type.classType.typeArguments.length === 0
|
||||
) {
|
||||
@ -11812,7 +11838,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
|
||||
if (isObject(argType)) {
|
||||
const objClass = argType.classType;
|
||||
if (ClassType.isBuiltIn(objClass, 'Tuple') && objClass.typeArguments) {
|
||||
if (isTupleClass(objClass) && objClass.typeArguments) {
|
||||
let foundNonClassType = false;
|
||||
const classTypeList: ClassType[] = [];
|
||||
objClass.typeArguments.forEach((typeArg) => {
|
||||
@ -12184,6 +12210,12 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
}
|
||||
|
||||
// Handle "tuple" specially, since it needs to act like "Tuple"
|
||||
// in Python 3.9 and newer.
|
||||
if (ClassType.isBuiltIn(classType, 'tuple')) {
|
||||
return createSpecialType(classType, typeArgs, undefined);
|
||||
}
|
||||
|
||||
let typeArgCount = typeArgs ? typeArgs.length : 0;
|
||||
|
||||
// Make sure the argument list count is correct.
|
||||
@ -13545,7 +13577,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
if (ancestorIndex === 0 && ClassType.isSpecialBuiltIn(destType)) {
|
||||
// Handle built-in types that support arbitrary numbers
|
||||
// of type parameters like Tuple.
|
||||
if (destType.details.name === 'Tuple') {
|
||||
if (isTupleClass(destType)) {
|
||||
if (destType.typeArguments && curSrcType.typeArguments) {
|
||||
const destTypeArgs = destType.typeArguments;
|
||||
let destArgCount = destTypeArgs.length;
|
||||
@ -13607,8 +13639,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
|
||||
// If the dest type isn't specialized, there are no type args to validate.
|
||||
const ancestorTypeArgs = ancestorType.typeArguments;
|
||||
if (!ancestorTypeArgs) {
|
||||
if (!ancestorType.typeArguments) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -13634,9 +13665,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
!typeVarMap.isLocked()
|
||||
) {
|
||||
// Populate the typeVar map with type arguments of the source.
|
||||
const srcTypeArgs = curSrcType.effectiveTypeArguments || curSrcType.typeArguments;
|
||||
for (let i = 0; i < destType.details.typeParameters.length; i++) {
|
||||
const typeArgType =
|
||||
i < curSrcType.typeArguments.length ? curSrcType.typeArguments[i] : UnknownType.create();
|
||||
i < srcTypeArgs.length ? srcTypeArgs[i] : UnknownType.create();
|
||||
typeVarMap.setTypeVar(destType.details.typeParameters[i], typeArgType, /* isNarrowable */ true);
|
||||
}
|
||||
}
|
||||
@ -13672,9 +13704,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
assert(ClassType.isSameGenericClass(destType, srcType));
|
||||
|
||||
const destTypeParams = ClassType.getTypeParameters(destType);
|
||||
const destTypeArgs = destType.typeArguments!;
|
||||
const destTypeArgs = destType.effectiveTypeArguments || destType.typeArguments!;
|
||||
assert(destTypeArgs !== undefined);
|
||||
const srcTypeArgs = srcType.typeArguments;
|
||||
const srcTypeArgs = srcType.effectiveTypeArguments || srcType.typeArguments;
|
||||
|
||||
if (srcTypeArgs) {
|
||||
if (ClassType.isSpecialBuiltIn(srcType) || srcTypeArgs.length === destTypeParams.length) {
|
||||
@ -15442,7 +15474,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
']';
|
||||
}
|
||||
} else {
|
||||
if (ClassType.isBuiltIn(type, 'Tuple')) {
|
||||
if (isTupleClass(type)) {
|
||||
objName += '[()]';
|
||||
}
|
||||
}
|
||||
|
@ -400,7 +400,7 @@ export function canBeFalsy(type: Type, recursionLevel = 0): boolean {
|
||||
|
||||
case TypeCategory.Object: {
|
||||
// Handle tuples specially.
|
||||
if (ClassType.isBuiltIn(type.classType, 'Tuple') && type.classType.typeArguments) {
|
||||
if (isTupleClass(type.classType) && type.classType.typeArguments) {
|
||||
if (type.classType.typeArguments.length === 0) {
|
||||
return true;
|
||||
}
|
||||
@ -463,7 +463,7 @@ export function canBeTruthy(type: Type, recursionLevel = 0): boolean {
|
||||
|
||||
case TypeCategory.Object: {
|
||||
// Check for Tuple[()] (an empty tuple).
|
||||
if (ClassType.isBuiltIn(type.classType, 'Tuple')) {
|
||||
if (isTupleClass(type.classType)) {
|
||||
if (type.classType.typeArguments && type.classType.typeArguments.length === 0) {
|
||||
return false;
|
||||
}
|
||||
@ -495,11 +495,9 @@ export function getSpecializedTupleType(type: Type): ClassType | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// See if this class derives from Tuple. If it does, we'll assume that it
|
||||
// See if this class derives from Tuple or tuple. If it does, we'll assume that it
|
||||
// hasn't been overridden in a way that changes the behavior of the tuple class.
|
||||
const tupleClass = classType.details.mro.find(
|
||||
(mroClass) => isClass(mroClass) && ClassType.isBuiltIn(mroClass, 'Tuple')
|
||||
);
|
||||
const tupleClass = classType.details.mro.find((mroClass) => isClass(mroClass) && isTupleClass(mroClass));
|
||||
if (!tupleClass || !isClass(tupleClass)) {
|
||||
return undefined;
|
||||
}
|
||||
@ -556,6 +554,10 @@ export function isProperty(type: Type): type is ObjectType {
|
||||
return isObject(type) && ClassType.isPropertyClass(type.classType);
|
||||
}
|
||||
|
||||
export function isTupleClass(type: ClassType) {
|
||||
return ClassType.isBuiltIn(type) && (type.details.name === 'Tuple' || type.details.name === 'tuple');
|
||||
}
|
||||
|
||||
// Partially specializes a type within the context of a specified
|
||||
// (presumably specialized) class.
|
||||
export function partiallySpecializeType(type: Type, contextClassType: ClassType): Type {
|
||||
@ -955,6 +957,11 @@ export function setTypeArgumentsRecursive(destType: Type, srcType: Type, typeVar
|
||||
setTypeArgumentsRecursive(typeArg, srcType, typeVarMap, recursionCount + 1);
|
||||
});
|
||||
}
|
||||
if (destType.effectiveTypeArguments) {
|
||||
destType.effectiveTypeArguments.forEach((typeArg) => {
|
||||
setTypeArgumentsRecursive(typeArg, srcType, typeVarMap, recursionCount + 1);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCategory.Object:
|
||||
@ -1403,6 +1410,7 @@ function _specializeClassType(
|
||||
}
|
||||
|
||||
let newTypeArgs: Type[] = [];
|
||||
let newEffectiveTypeArgs: Type[] | undefined;
|
||||
let specializationNeeded = false;
|
||||
|
||||
// If type args were previously provided, specialize them.
|
||||
@ -1414,6 +1422,16 @@ function _specializeClassType(
|
||||
}
|
||||
return newTypeArgType;
|
||||
});
|
||||
|
||||
if (classType.effectiveTypeArguments) {
|
||||
newEffectiveTypeArgs = classType.effectiveTypeArguments.map((oldTypeArgType) => {
|
||||
const newTypeArgType = specializeType(oldTypeArgType, typeVarMap, makeConcrete, recursionLevel + 1);
|
||||
if (newTypeArgType !== oldTypeArgType) {
|
||||
specializationNeeded = true;
|
||||
}
|
||||
return newTypeArgType;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
ClassType.getTypeParameters(classType).forEach((typeParam) => {
|
||||
let typeArgType: Type;
|
||||
@ -1441,7 +1459,13 @@ function _specializeClassType(
|
||||
return classType;
|
||||
}
|
||||
|
||||
return ClassType.cloneForSpecialization(classType, newTypeArgs, /* isTypeArgumentExplicit */ false);
|
||||
return ClassType.cloneForSpecialization(
|
||||
classType,
|
||||
newTypeArgs,
|
||||
/* isTypeArgumentExplicit */ false,
|
||||
/* skipAbstractClassTest */ undefined,
|
||||
newEffectiveTypeArgs
|
||||
);
|
||||
}
|
||||
|
||||
// Converts a type var type into the most specific type
|
||||
|
@ -310,6 +310,13 @@ export interface ClassType extends TypeBase {
|
||||
// some or all of the type parameters.
|
||||
typeArguments?: Type[];
|
||||
|
||||
// For a few classes (e.g., Tuple and tuple), the class definition
|
||||
// calls for a single type parameter but the spec allows the programmer
|
||||
// to provide variadic type arguments. To make these compatible, we need
|
||||
// to derive a single effective type argument from the provided type
|
||||
// arguments.
|
||||
effectiveTypeArguments?: Type[];
|
||||
|
||||
// If type arguments are present, were they explicit (i.e.
|
||||
// provided explicitly in the code)?
|
||||
isTypeArgumentExplicit?: boolean;
|
||||
@ -359,7 +366,8 @@ export namespace ClassType {
|
||||
classType: ClassType,
|
||||
typeArguments: Type[] | undefined,
|
||||
isTypeArgumentExplicit: boolean,
|
||||
skipAbstractClassTest = false
|
||||
skipAbstractClassTest = false,
|
||||
effectiveTypeArguments?: Type[]
|
||||
): ClassType {
|
||||
const newClassType = create(
|
||||
classType.details.name,
|
||||
@ -372,8 +380,13 @@ export namespace ClassType {
|
||||
);
|
||||
|
||||
newClassType.details = classType.details;
|
||||
newClassType.typeArguments = typeArguments;
|
||||
if (typeArguments) {
|
||||
newClassType.typeArguments = typeArguments;
|
||||
}
|
||||
newClassType.isTypeArgumentExplicit = isTypeArgumentExplicit;
|
||||
if (effectiveTypeArguments) {
|
||||
newClassType.effectiveTypeArguments = effectiveTypeArguments;
|
||||
}
|
||||
|
||||
if (classType.literalValue !== undefined) {
|
||||
newClassType.literalValue = classType.literalValue;
|
||||
|
@ -762,6 +762,12 @@ test('Tuples7', () => {
|
||||
validateResults(analysisResults, 7);
|
||||
});
|
||||
|
||||
test('Tuples8', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tuples8.py']);
|
||||
|
||||
validateResults(analysisResults, 7);
|
||||
});
|
||||
|
||||
test('NamedTuples1', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['namedTuples1.py']);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This sample tests handling of tuples and tracking
|
||||
# of specific types within a tuple.
|
||||
|
||||
from typing import NamedTuple, Tuple, TypeVar
|
||||
from typing import Tuple, TypeVar
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
@ -38,6 +38,7 @@ for aaa in objA:
|
||||
class ClassB(Tuple[_T, ...]):
|
||||
pass
|
||||
|
||||
|
||||
objB = ClassB[complex]()
|
||||
|
||||
(x, y, z) = objB
|
||||
@ -56,4 +57,3 @@ xx3: int = x
|
||||
yy3: int = y
|
||||
zz3: int = z
|
||||
|
||||
|
||||
|
60
packages/pyright-internal/src/tests/samples/tuples8.py
Normal file
60
packages/pyright-internal/src/tests/samples/tuples8.py
Normal file
@ -0,0 +1,60 @@
|
||||
# This sample verifies that the "tuple" type is treated
|
||||
# analogously to "Tuple" type.
|
||||
|
||||
from typing import TypeVar
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
class ClassA(tuple[int, str, int, _T]):
|
||||
pass
|
||||
|
||||
|
||||
objA = ClassA[complex]()
|
||||
|
||||
(a, b, c, d) = objA
|
||||
|
||||
aa1: int = a
|
||||
bb1: str = b
|
||||
cc1: int = c
|
||||
dd1: complex = d
|
||||
|
||||
aa2: int = objA[0]
|
||||
bb2: str = objA[1]
|
||||
cc2: int = objA[2]
|
||||
dd2: complex = objA[3]
|
||||
|
||||
# These should generate errors because
|
||||
# these are not the correct types.
|
||||
aa3: str = a
|
||||
bb3: complex = b
|
||||
cc3: str = c
|
||||
dd3: int = d
|
||||
|
||||
for aaa in objA:
|
||||
print(aaa)
|
||||
|
||||
|
||||
class ClassB(tuple[_T, ...]):
|
||||
pass
|
||||
|
||||
|
||||
objB = ClassB[complex]()
|
||||
|
||||
(x, y, z) = objB
|
||||
|
||||
xx1: complex = x
|
||||
yy1: complex = y
|
||||
zz1: complex = z
|
||||
|
||||
xx2: complex = objB[0]
|
||||
yy2: complex = objB[1]
|
||||
zz2: complex = objB[2]
|
||||
|
||||
# These should generate errors because
|
||||
# these are not the correct types.
|
||||
xx3: int = x
|
||||
yy3: int = y
|
||||
zz3: int = z
|
||||
|
@ -15,6 +15,7 @@ Pyright supports [configuration files](/docs/configuration.md) that provide gran
|
||||
* [PEP 544](https://www.python.org/dev/peps/pep-0544/) structural subtyping
|
||||
* [PEP 561](https://www.python.org/dev/peps/pep-0561/) distributing and packaging type information
|
||||
* [PEP 563](https://www.python.org/dev/peps/pep-0563/) postponed evaluation of annotations
|
||||
* [PEP 585](https://www.python.org/dev/peps/pep-0585/) type hinting generics in standard collections
|
||||
* [PEP 589](https://www.python.org/dev/peps/pep-0589/) typed dictionaries
|
||||
* [PEP 591](https://www.python.org/dev/peps/pep-0591/) final qualifier
|
||||
* [PEP 593](https://www.python.org/dev/peps/pep-0593/) flexible variable annotations
|
||||
|
Loading…
Reference in New Issue
Block a user