mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-19 04:07:36 +03:00
Added support for PEP 612 (ParameterSpecification).
This commit is contained in:
parent
6f30005de1
commit
5bb39c8a71
@ -36,6 +36,12 @@ if sys.version_info >= (3, 8):
|
||||
if sys.version_info < (3, 7):
|
||||
class GenericMeta(type): ...
|
||||
|
||||
class ParameterSpecification:
|
||||
args: Any = ...
|
||||
kwargs: Any = ...
|
||||
|
||||
def __init__(self, name: str): ...
|
||||
|
||||
# Return type that indicates a function does not return.
|
||||
# This type is equivalent to the None type, but the no-op Union is necessary to
|
||||
# distinguish the None type from the None value.
|
||||
|
@ -136,7 +136,6 @@ import {
|
||||
UnknownType,
|
||||
} from './types';
|
||||
import {
|
||||
addDefaultFunctionParameters,
|
||||
addTypeVarsToListIfUnique,
|
||||
areTypesSame,
|
||||
buildTypeVarMapFromSpecializedClass,
|
||||
@ -160,6 +159,7 @@ import {
|
||||
isEllipsisType,
|
||||
isNoReturnType,
|
||||
isOptionalType,
|
||||
isParameterSpecificationType,
|
||||
isProperty,
|
||||
lookUpClassMember,
|
||||
lookUpObjectMember,
|
||||
@ -246,6 +246,9 @@ export const enum EvaluatorFlags {
|
||||
|
||||
// 'Final' is not allowed in this context.
|
||||
FinalDisallowed = 1 << 6,
|
||||
|
||||
// A ParameterSpecification isn't allowed
|
||||
ParameterSpecificationDisallowed = 1 << 7,
|
||||
}
|
||||
|
||||
interface EvaluatorUsage {
|
||||
@ -877,7 +880,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
typeResult = getTypeOfExpression(
|
||||
node.typeAnnotation,
|
||||
undefined,
|
||||
EvaluatorFlags.EvaluateStringLiteralAsType
|
||||
EvaluatorFlags.EvaluateStringLiteralAsType | EvaluatorFlags.ParameterSpecificationDisallowed
|
||||
);
|
||||
break;
|
||||
}
|
||||
@ -919,7 +922,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
}
|
||||
|
||||
let evaluatorFlags = EvaluatorFlags.ConvertEllipsisToAny | EvaluatorFlags.EvaluateStringLiteralAsType;
|
||||
let evaluatorFlags =
|
||||
EvaluatorFlags.ConvertEllipsisToAny |
|
||||
EvaluatorFlags.EvaluateStringLiteralAsType |
|
||||
EvaluatorFlags.ParameterSpecificationDisallowed;
|
||||
|
||||
const isAnnotationEvaluationPostponed =
|
||||
fileInfo.futureImports.get('annotations') !== undefined || fileInfo.isStubFile;
|
||||
@ -932,7 +938,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
evaluatorFlags |= EvaluatorFlags.FinalDisallowed;
|
||||
}
|
||||
|
||||
return convertClassToObject(getTypeOfExpression(node, undefined, evaluatorFlags).type);
|
||||
const classType = getTypeOfExpression(node, undefined, evaluatorFlags).type;
|
||||
|
||||
return convertClassToObject(classType);
|
||||
}
|
||||
|
||||
function getTypeFromDecorator(node: DecoratorNode, functionOrClassType: Type): Type {
|
||||
@ -1541,7 +1549,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
name: 'cls',
|
||||
type: classType,
|
||||
});
|
||||
addDefaultFunctionParameters(newType);
|
||||
FunctionType.addDefaultParameters(newType);
|
||||
newType.details.declaredReturnType = ObjectType.create(classType);
|
||||
|
||||
FunctionType.addParameter(initType, {
|
||||
@ -1686,7 +1694,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
name: 'cls',
|
||||
type: classType,
|
||||
});
|
||||
addDefaultFunctionParameters(newType);
|
||||
FunctionType.addDefaultParameters(newType);
|
||||
newType.details.declaredReturnType = ObjectType.create(classType);
|
||||
|
||||
// Synthesize an __init__ method.
|
||||
@ -2610,6 +2618,12 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
type = UnknownType.create();
|
||||
}
|
||||
|
||||
if (type.category === TypeCategory.TypeVar && type.isParameterSpec) {
|
||||
if (flags & EvaluatorFlags.ParameterSpecificationDisallowed) {
|
||||
addError('ParameterSpecification not allowed in this context', node);
|
||||
}
|
||||
}
|
||||
|
||||
return { type, node };
|
||||
}
|
||||
|
||||
@ -2660,6 +2674,20 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
|
||||
case TypeCategory.TypeVar: {
|
||||
if (baseType.isParameterSpec) {
|
||||
if (memberName === 'args' || memberName === 'kwargs') {
|
||||
return { type: AnyType.create(), node };
|
||||
}
|
||||
const fileInfo = getFileInfo(node);
|
||||
addDiagnostic(
|
||||
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
`"${memberName}" is not a known member of ParameterSpecification`,
|
||||
node.memberName
|
||||
);
|
||||
return { type: UnknownType.create(), node };
|
||||
}
|
||||
|
||||
return getTypeFromMemberAccessWithBaseType(
|
||||
node,
|
||||
{
|
||||
@ -3433,9 +3461,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
|
||||
function getTypeArgs(node: IndexItemsNode, flags: EvaluatorFlags): TypeResult[] {
|
||||
const typeArgs: TypeResult[] = [];
|
||||
const adjFlags = flags & ~EvaluatorFlags.ParameterSpecificationDisallowed;
|
||||
|
||||
node.items.forEach((expr) => {
|
||||
typeArgs.push(getTypeArg(expr, flags));
|
||||
typeArgs.push(getTypeArg(expr, adjFlags));
|
||||
});
|
||||
|
||||
return typeArgs;
|
||||
@ -3714,7 +3743,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
type = AnyType.create();
|
||||
}
|
||||
} else if (className === 'TypeVar') {
|
||||
type = createTypeVarType(errorNode, argList);
|
||||
type = createTypeVarType(errorNode, argList, /* isParamSpec */ false);
|
||||
} else if (className === 'ParameterSpecification') {
|
||||
type = createTypeVarType(errorNode, argList, /* isParamSpec */ true);
|
||||
} else if (className === 'NamedTuple') {
|
||||
type = createNamedTupleType(errorNode, argList, true);
|
||||
} else if (
|
||||
@ -4786,10 +4817,23 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
return true;
|
||||
}
|
||||
|
||||
function createTypeVarType(errorNode: ExpressionNode, argList: FunctionArgument[]): Type | undefined {
|
||||
function createTypeVarType(
|
||||
errorNode: ExpressionNode,
|
||||
argList: FunctionArgument[],
|
||||
isParamSpec: boolean
|
||||
): Type | undefined {
|
||||
let typeVarName = '';
|
||||
|
||||
if (isParamSpec) {
|
||||
const fileInfo = getFileInfo(errorNode);
|
||||
if (!fileInfo.isStubFile && fileInfo.executionEnvironment.pythonVersion < PythonVersion.V39) {
|
||||
addError('ParameterSpecification requires Python 3.9 or newer', errorNode);
|
||||
}
|
||||
}
|
||||
|
||||
const typeVarTypeName = isParamSpec ? 'ParameterSpecification' : 'TypeVar';
|
||||
if (argList.length === 0) {
|
||||
addError('Expected name of TypeVar as first parameter', errorNode);
|
||||
addError(`Expected name of ${typeVarTypeName} as first parameter`, errorNode);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -4797,10 +4841,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
if (firstArg.valueExpression && firstArg.valueExpression.nodeType === ParseNodeType.StringList) {
|
||||
typeVarName = firstArg.valueExpression.strings.map((s) => s.value).join('');
|
||||
} else {
|
||||
addError('Expected name of TypeVar as first parameter', firstArg.valueExpression || errorNode);
|
||||
addError(`Expected name of ${typeVarTypeName} as first parameter`, firstArg.valueExpression || errorNode);
|
||||
}
|
||||
|
||||
const typeVar = TypeVarType.create(typeVarName);
|
||||
const typeVar = TypeVarType.create(typeVarName, isParamSpec);
|
||||
|
||||
// Parse the remaining parameters.
|
||||
for (let i = 1; i < argList.length; i++) {
|
||||
@ -4816,7 +4860,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
);
|
||||
}
|
||||
|
||||
if (paramName === 'bound') {
|
||||
if (paramName === 'bound' && !isParamSpec) {
|
||||
if (typeVar.constraints.length > 0) {
|
||||
addError(
|
||||
`A TypeVar cannot be both bound and constrained`,
|
||||
@ -4829,7 +4873,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
typeVar.boundType = convertClassToObject(argType);
|
||||
}
|
||||
} else if (paramName === 'covariant') {
|
||||
} else if (paramName === 'covariant' && !isParamSpec) {
|
||||
if (argList[i].valueExpression && getBooleanValue(argList[i].valueExpression!)) {
|
||||
if (typeVar.isContravariant) {
|
||||
addError(
|
||||
@ -4840,7 +4884,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
typeVar.isCovariant = true;
|
||||
}
|
||||
}
|
||||
} else if (paramName === 'contravariant') {
|
||||
} else if (paramName === 'contravariant' && !isParamSpec) {
|
||||
if (argList[i].valueExpression && getBooleanValue(argList[i].valueExpression!)) {
|
||||
if (typeVar.isContravariant) {
|
||||
addError(
|
||||
@ -4852,11 +4896,14 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addError(`"${paramName}" is unknown parameter to TypeVar`, argList[i].valueExpression || errorNode);
|
||||
addError(
|
||||
`"${paramName}" is unknown parameter to ${typeVarTypeName}`,
|
||||
argList[i].valueExpression || errorNode
|
||||
);
|
||||
}
|
||||
|
||||
paramNameMap.set(paramName, paramName);
|
||||
} else {
|
||||
} else if (!isParamSpec) {
|
||||
if (typeVar.boundType) {
|
||||
addError(`A TypeVar cannot be both bound and constrained`, argList[i].valueExpression || errorNode);
|
||||
} else {
|
||||
@ -4869,6 +4916,12 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
TypeVarType.addConstraint(typeVar, convertClassToObject(argType));
|
||||
}
|
||||
} else {
|
||||
addError(
|
||||
`A ParameterSpec declaration does not support more than one argument`,
|
||||
argList[i].valueExpression || errorNode
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5275,7 +5328,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
const entryTypeInfo = getTypeOfExpression(
|
||||
entryTypeNode,
|
||||
undefined,
|
||||
EvaluatorFlags.EvaluateStringLiteralAsType
|
||||
EvaluatorFlags.EvaluateStringLiteralAsType |
|
||||
EvaluatorFlags.ParameterSpecificationDisallowed
|
||||
);
|
||||
if (entryTypeInfo) {
|
||||
entryType = convertClassToObject(entryTypeInfo.type);
|
||||
@ -5347,7 +5401,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
|
||||
if (addGenericGetAttribute) {
|
||||
addDefaultFunctionParameters(constructorType);
|
||||
FunctionType.addDefaultParameters(constructorType);
|
||||
}
|
||||
|
||||
// Always use generic parameters for __init__. The __new__ method
|
||||
@ -5359,7 +5413,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
FunctionTypeFlags.SynthesizedMethod | FunctionTypeFlags.SkipConstructorCheck
|
||||
);
|
||||
FunctionType.addParameter(initType, selfParameter);
|
||||
addDefaultFunctionParameters(initType);
|
||||
FunctionType.addDefaultParameters(initType);
|
||||
initType.details.declaredReturnType = NoneType.create();
|
||||
|
||||
classFields.set('__new__', Symbol.createWithType(SymbolFlags.ClassMember, constructorType));
|
||||
@ -6335,6 +6389,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
addError(`"..." not allowed in this context`, entry.node);
|
||||
} else if (entry.type.category === TypeCategory.Module) {
|
||||
addError(`Module not allowed in this context`, entry.node);
|
||||
} else if (isParameterSpecificationType(entry.type)) {
|
||||
addError(`ParameterSpecification not allowed in this context`, entry.node);
|
||||
}
|
||||
|
||||
FunctionType.addParameter(functionType, {
|
||||
@ -6346,12 +6402,15 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
});
|
||||
});
|
||||
} else if (isEllipsisType(typeArgs[0].type)) {
|
||||
addDefaultFunctionParameters(functionType);
|
||||
FunctionType.addDefaultParameters(functionType);
|
||||
} else if (isParameterSpecificationType(typeArgs[0].type)) {
|
||||
FunctionType.addDefaultParameters(functionType);
|
||||
functionType.details.parameterSpecification = typeArgs[0].type as TypeVarType;
|
||||
} else {
|
||||
addError(`Expected parameter type list or "..."`, typeArgs[0].node);
|
||||
}
|
||||
} else {
|
||||
addDefaultFunctionParameters(functionType, /* useUnknown */ true);
|
||||
FunctionType.addDefaultParameters(functionType, /* useUnknown */ true);
|
||||
}
|
||||
|
||||
if (typeArgs && typeArgs.length > 1) {
|
||||
@ -6359,6 +6418,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
addError(`"..." not allowed in this context`, typeArgs[1].node);
|
||||
} else if (typeArgs[1].type.category === TypeCategory.Module) {
|
||||
addError(`Module not allowed in this context`, typeArgs[1].node);
|
||||
} else if (isParameterSpecificationType(typeArgs[1].type)) {
|
||||
addError(`ParameterSpecification not allowed in this context`, typeArgs[1].node);
|
||||
}
|
||||
functionType.details.declaredReturnType = convertClassToObject(typeArgs[1].type);
|
||||
} else {
|
||||
@ -6383,6 +6444,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
addError(`"..." not allowed in this context`, typeArgs[0].node);
|
||||
} else if (typeArgs[0].type.category === TypeCategory.Module) {
|
||||
addError(`Module not allowed in this context`, typeArgs[0].node);
|
||||
} else if (isParameterSpecificationType(typeArgs[0].type)) {
|
||||
addError(`ParameterSpecification not allowed in this context`, typeArgs[1].node);
|
||||
}
|
||||
|
||||
return combineTypes([typeArgs[0].type, NoneType.create()]);
|
||||
@ -6520,9 +6583,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
} else if (typeArgs!.length !== 2 || index !== 1) {
|
||||
addError(`"..." allowed only as the second of two arguments`, typeArg.node);
|
||||
}
|
||||
if (typeArg.type.category === TypeCategory.Module) {
|
||||
addError(`Module not allowed in this context`, typeArg.node);
|
||||
}
|
||||
} else if (typeArg.type.category === TypeCategory.Module) {
|
||||
addError(`Module not allowed in this context`, typeArg.node);
|
||||
} else if (isParameterSpecificationType(typeArg.type)) {
|
||||
addError(`ParameterSpecification not allowed in this context`, typeArg.node);
|
||||
}
|
||||
});
|
||||
|
||||
@ -6586,6 +6650,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
addError(`"..." not allowed in this context`, typeArg.node);
|
||||
} else if (typeArg.type.category === TypeCategory.Module) {
|
||||
addError(`Module not allowed in this context`, typeArg.node);
|
||||
} else if (isParameterSpecificationType(typeArg.type)) {
|
||||
addError(`ParameterSpecification not allowed in this context`, typeArg.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7116,7 +7182,11 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
// Create a type parameter for each simple, named parameter
|
||||
// in the __init__ method.
|
||||
classType.details.typeParameters = genericParams.map((param) =>
|
||||
TypeVarType.create(`__type_of_${param.name!.value}`, true)
|
||||
TypeVarType.create(
|
||||
`__type_of_${param.name!.value}`,
|
||||
/* isParameterSpec */ false,
|
||||
/* isSynthesized */ true
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -9347,7 +9417,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
const arg1Type = getTypeOfExpression(
|
||||
arg1Expr,
|
||||
undefined,
|
||||
EvaluatorFlags.EvaluateStringLiteralAsType
|
||||
EvaluatorFlags.EvaluateStringLiteralAsType | EvaluatorFlags.ParameterSpecificationDisallowed
|
||||
).type;
|
||||
const classTypeList = getIsInstanceClassTypes(arg1Type);
|
||||
if (classTypeList) {
|
||||
@ -9718,6 +9788,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
addError(`"..." not allowed in this context`, typeArg.node);
|
||||
} else if (typeArg.type.category === TypeCategory.Module) {
|
||||
addError(`Module not allowed in this context`, typeArg.node);
|
||||
} else if (isParameterSpecificationType(typeArg.type)) {
|
||||
addError(`ParameterSpecification not allowed in this context`, typeArg.node);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -9761,7 +9833,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
return getTypeOfExpression(
|
||||
arg.valueExpression!,
|
||||
undefined,
|
||||
expectingType ? EvaluatorFlags.EvaluateStringLiteralAsType : EvaluatorFlags.None
|
||||
expectingType
|
||||
? EvaluatorFlags.EvaluateStringLiteralAsType | EvaluatorFlags.ParameterSpecificationDisallowed
|
||||
: EvaluatorFlags.None
|
||||
).type;
|
||||
}
|
||||
|
||||
@ -11032,7 +11106,14 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
flags = CanAssignFlags.Default,
|
||||
recursionCount = 0
|
||||
): boolean {
|
||||
const curTypeVarMapping = typeVarMap.get(destType.name);
|
||||
const curTypeVarMapping = typeVarMap.getTypeVar(destType.name);
|
||||
|
||||
if (destType.isParameterSpec) {
|
||||
diag.addMessage(
|
||||
`Type "${printType(srcType)}" is not compatible with ParameterSpecification "${destType.name}"`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle the constrained case.
|
||||
if (destType.constraints.length > 0) {
|
||||
@ -11062,7 +11143,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
} else {
|
||||
// Assign the type to the type var.
|
||||
if (!typeVarMap.isLocked()) {
|
||||
typeVarMap.set(destType.name, constrainedType, false);
|
||||
typeVarMap.setTypeVar(destType.name, constrainedType, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -11163,7 +11244,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
|
||||
if (!typeVarMap.isLocked()) {
|
||||
typeVarMap.set(destType.name, updatedType, updatedTypeIsNarrowable);
|
||||
typeVarMap.setTypeVar(destType.name, updatedType, updatedTypeIsNarrowable);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -11460,7 +11541,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
// manually set to the corresponding enum object type.
|
||||
if (typeVarMap && ClassType.isBuiltIn(metaclass, 'EnumMeta')) {
|
||||
if (!typeVarMap.isLocked()) {
|
||||
typeVarMap.set('_T', ObjectType.create(srcType), false);
|
||||
typeVarMap.setTypeVar('_T', ObjectType.create(srcType), false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -11536,7 +11617,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addDefaultFunctionParameters(constructorFunction);
|
||||
FunctionType.addDefaultParameters(constructorFunction);
|
||||
}
|
||||
|
||||
srcFunction = constructorFunction;
|
||||
@ -11773,6 +11854,11 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
}
|
||||
|
||||
// Are we assigning to a function with a ParameterSpecification?
|
||||
if (destType.details.parameterSpecification && typeVarMap && !typeVarMap.isLocked()) {
|
||||
typeVarMap.setParameterSpecification(destType.details.parameterSpecification.name, srcType);
|
||||
}
|
||||
|
||||
return canAssign;
|
||||
}
|
||||
|
||||
@ -12288,7 +12374,12 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
}
|
||||
|
||||
case TypeCategory.Function: {
|
||||
// If it's a Callable with a ParameterSpecification, use the
|
||||
// Callable notation.
|
||||
const parts = printFunctionParts(type, recursionCount);
|
||||
if (type.details.parameterSpecification) {
|
||||
return `Callable[${type.details.parameterSpecification.name}, ${parts[1]}]`;
|
||||
}
|
||||
return `(${parts[0].join(', ')}) -> ${parts[1]}`;
|
||||
}
|
||||
|
||||
@ -12359,6 +12450,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
|
||||
|
||||
const typeName = type.name;
|
||||
|
||||
if (type.isParameterSpec) {
|
||||
return `ParameterSpecification["${typeName}"]`;
|
||||
}
|
||||
|
||||
// Print the name in a simplified form if it's embedded
|
||||
// inside another type string.
|
||||
if (recursionCount > 0) {
|
||||
|
@ -342,6 +342,14 @@ export function isNoReturnType(type: Type): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isParameterSpecificationType(type: Type): boolean {
|
||||
if (type.category !== TypeCategory.TypeVar) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return type.isParameterSpec;
|
||||
}
|
||||
|
||||
export function isProperty(type: Type): boolean {
|
||||
return type.category === TypeCategory.Object && ClassType.isPropertyClass(type.classType);
|
||||
}
|
||||
@ -382,7 +390,7 @@ export function specializeType(
|
||||
}
|
||||
|
||||
// Shortcut if there are no type variables defined.
|
||||
if (typeVarMap && !makeConcrete && typeVarMap.size() === 0) {
|
||||
if (typeVarMap && !makeConcrete && typeVarMap.typeVarCount() === 0) {
|
||||
return type;
|
||||
}
|
||||
|
||||
@ -396,7 +404,7 @@ export function specializeType(
|
||||
|
||||
if (type.category === TypeCategory.TypeVar) {
|
||||
if (typeVarMap) {
|
||||
const replacementType = typeVarMap.get(type.name);
|
||||
const replacementType = typeVarMap.getTypeVar(type.name);
|
||||
if (replacementType) {
|
||||
return replacementType;
|
||||
}
|
||||
@ -430,7 +438,7 @@ export function specializeType(
|
||||
return specializeType(firstTypeArg.classType, typeVarMap, makeConcrete, recursionLevel + 1);
|
||||
} else if (firstTypeArg.category === TypeCategory.TypeVar) {
|
||||
if (typeVarMap) {
|
||||
const replacementType = typeVarMap.get(firstTypeArg.name);
|
||||
const replacementType = typeVarMap.getTypeVar(firstTypeArg.name);
|
||||
if (replacementType && replacementType.category === TypeCategory.Object) {
|
||||
return replacementType.classType;
|
||||
}
|
||||
@ -589,19 +597,6 @@ export function lookUpClassMember(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function addDefaultFunctionParameters(functionType: FunctionType, useUnknown = false) {
|
||||
FunctionType.addParameter(functionType, {
|
||||
category: ParameterCategory.VarArgList,
|
||||
name: 'args',
|
||||
type: useUnknown ? UnknownType.create() : AnyType.create(),
|
||||
});
|
||||
FunctionType.addParameter(functionType, {
|
||||
category: ParameterCategory.VarArgDictionary,
|
||||
name: 'kwargs',
|
||||
type: useUnknown ? UnknownType.create() : AnyType.create(),
|
||||
});
|
||||
}
|
||||
|
||||
export function getMetaclass(type: ClassType, recursionCount = 0): ClassType | UnknownType | undefined {
|
||||
if (recursionCount > maxTypeRecursionCount) {
|
||||
return undefined;
|
||||
@ -764,8 +759,8 @@ export function setTypeArgumentsRecursive(destType: Type, srcType: Type, typeVar
|
||||
break;
|
||||
|
||||
case TypeCategory.TypeVar:
|
||||
if (!typeVarMap.has(destType.name)) {
|
||||
typeVarMap.set(destType.name, srcType, typeVarMap.isNarrowable(destType.name));
|
||||
if (!typeVarMap.hasTypeVar(destType.name)) {
|
||||
typeVarMap.setTypeVar(destType.name, srcType, typeVarMap.isNarrowable(destType.name));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -817,7 +812,7 @@ export function buildTypeVarMap(typeParameters: TypeVarType[], typeArgs: Type[]
|
||||
typeArgType = getConcreteTypeFromTypeVar(typeParam);
|
||||
}
|
||||
|
||||
typeVarMap.set(typeVarName, typeArgType, false);
|
||||
typeVarMap.setTypeVar(typeVarName, typeArgType, false);
|
||||
});
|
||||
|
||||
return typeVarMap;
|
||||
@ -1096,10 +1091,10 @@ function _specializeClassType(
|
||||
ClassType.getTypeParameters(classType).forEach((typeParam) => {
|
||||
let typeArgType: Type;
|
||||
|
||||
if (typeVarMap && typeVarMap.get(typeParam.name)) {
|
||||
if (typeVarMap && typeVarMap.getTypeVar(typeParam.name)) {
|
||||
// If the type var map already contains this type var, use
|
||||
// the existing type.
|
||||
typeArgType = typeVarMap.get(typeParam.name)!;
|
||||
typeArgType = typeVarMap.getTypeVar(typeParam.name)!;
|
||||
specializationNeeded = true;
|
||||
} else {
|
||||
// If the type var map wasn't provided or doesn't contain this
|
||||
@ -1157,11 +1152,19 @@ function _specializeOverloadedFunctionType(
|
||||
}
|
||||
|
||||
function _specializeFunctionType(
|
||||
functionType: FunctionType,
|
||||
sourceType: FunctionType,
|
||||
typeVarMap: TypeVarMap | undefined,
|
||||
makeConcrete: boolean,
|
||||
recursionLevel: number
|
||||
): FunctionType {
|
||||
let functionType = sourceType;
|
||||
|
||||
// Handle functions with a parameter specification in a special manner.
|
||||
if (functionType.details.parameterSpecification) {
|
||||
const paramSpec = typeVarMap?.getParameterSpecification(functionType.details.parameterSpecification.name);
|
||||
functionType = FunctionType.cloneForParameterSpecification(functionType, paramSpec);
|
||||
}
|
||||
|
||||
const declaredReturnType =
|
||||
functionType.specializedTypes && functionType.specializedTypes.returnType
|
||||
? functionType.specializedTypes.returnType
|
||||
|
@ -10,23 +10,29 @@
|
||||
*/
|
||||
|
||||
import { assert } from '../common/debug';
|
||||
import { Type } from './types';
|
||||
import { FunctionType, Type } from './types';
|
||||
|
||||
export class TypeVarMap {
|
||||
private _typeMap: Map<string, Type>;
|
||||
private _typeVarMap: Map<string, Type>;
|
||||
private _parameterSpecificationMap: Map<string, FunctionType>;
|
||||
private _isNarrowableMap: Map<string, boolean>;
|
||||
private _isLocked = false;
|
||||
|
||||
constructor() {
|
||||
this._typeMap = new Map<string, Type>();
|
||||
this._typeVarMap = new Map<string, Type>();
|
||||
this._parameterSpecificationMap = new Map<string, FunctionType>();
|
||||
this._isNarrowableMap = new Map<string, boolean>();
|
||||
}
|
||||
|
||||
clone() {
|
||||
const newTypeVarMap = new TypeVarMap();
|
||||
|
||||
this._typeMap.forEach((value, name) => {
|
||||
newTypeVarMap.set(name, value, this.isNarrowable(name));
|
||||
this._typeVarMap.forEach((value, name) => {
|
||||
newTypeVarMap.setTypeVar(name, value, this.isNarrowable(name));
|
||||
});
|
||||
|
||||
this._parameterSpecificationMap.forEach((value, name) => {
|
||||
newTypeVarMap.setParameterSpecification(name, value);
|
||||
});
|
||||
|
||||
newTypeVarMap._isLocked = this._isLocked;
|
||||
@ -34,26 +40,35 @@ export class TypeVarMap {
|
||||
return newTypeVarMap;
|
||||
}
|
||||
|
||||
has(name: string): boolean {
|
||||
return this._typeMap.has(name);
|
||||
hasTypeVar(name: string): boolean {
|
||||
return this._typeVarMap.has(name);
|
||||
}
|
||||
|
||||
get(name: string): Type | undefined {
|
||||
return this._typeMap.get(name);
|
||||
getTypeVar(name: string): Type | undefined {
|
||||
return this._typeVarMap.get(name);
|
||||
}
|
||||
|
||||
forEach(callback: (value: Type, key: string) => void) {
|
||||
return this._typeMap.forEach(callback);
|
||||
}
|
||||
|
||||
set(name: string, type: Type, isNarrowable: boolean) {
|
||||
setTypeVar(name: string, type: Type, isNarrowable: boolean) {
|
||||
assert(!this._isLocked);
|
||||
this._typeMap.set(name, type);
|
||||
this._typeVarMap.set(name, type);
|
||||
this._isNarrowableMap.set(name, isNarrowable);
|
||||
}
|
||||
|
||||
size() {
|
||||
return this._typeMap.size;
|
||||
hasParameterSpecification(name: string): boolean {
|
||||
return this._parameterSpecificationMap.has(name);
|
||||
}
|
||||
|
||||
getParameterSpecification(name: string): FunctionType | undefined {
|
||||
return this._parameterSpecificationMap.get(name);
|
||||
}
|
||||
|
||||
setParameterSpecification(name: string, type: FunctionType) {
|
||||
assert(!this._isLocked);
|
||||
this._parameterSpecificationMap.set(name, type);
|
||||
}
|
||||
|
||||
typeVarCount() {
|
||||
return this._typeVarMap.size;
|
||||
}
|
||||
|
||||
isNarrowable(name: string): boolean {
|
||||
|
@ -642,6 +642,10 @@ interface FunctionDetails {
|
||||
declaration?: FunctionDeclaration;
|
||||
builtInName?: string;
|
||||
docString?: string;
|
||||
|
||||
// Parameter specification used only for Callable types created
|
||||
// with a ParameterSpecification representing the parameters.
|
||||
parameterSpecification?: TypeVarType;
|
||||
}
|
||||
|
||||
export interface SpecializedFunctionTypes {
|
||||
@ -732,6 +736,40 @@ export namespace FunctionType {
|
||||
return newFunction;
|
||||
}
|
||||
|
||||
// Creates a new function based on the parameters of another function. If
|
||||
// paramTemplate is undefined, use default (generic) parameters.
|
||||
export function cloneForParameterSpecification(type: FunctionType, paramTemplate: FunctionType | undefined) {
|
||||
const newFunction = create(type.details.name, type.details.flags, type.details.docString);
|
||||
|
||||
// Make a shallow clone of the details.
|
||||
newFunction.details = { ...type.details };
|
||||
|
||||
// The clone should no longer have a parameter specification
|
||||
// since we're replacing it.
|
||||
delete newFunction.details.parameterSpecification;
|
||||
|
||||
if (paramTemplate) {
|
||||
newFunction.details.parameters = paramTemplate.details.parameters;
|
||||
} else {
|
||||
FunctionType.addDefaultParameters(newFunction);
|
||||
}
|
||||
|
||||
return newFunction;
|
||||
}
|
||||
|
||||
export function addDefaultParameters(functionType: FunctionType, useUnknown = false) {
|
||||
FunctionType.addParameter(functionType, {
|
||||
category: ParameterCategory.VarArgList,
|
||||
name: 'args',
|
||||
type: useUnknown ? UnknownType.create() : AnyType.create(),
|
||||
});
|
||||
FunctionType.addParameter(functionType, {
|
||||
category: ParameterCategory.VarArgDictionary,
|
||||
name: 'kwargs',
|
||||
type: useUnknown ? UnknownType.create() : AnyType.create(),
|
||||
});
|
||||
}
|
||||
|
||||
export function isInstanceMethod(type: FunctionType): boolean {
|
||||
return (
|
||||
(type.details.flags &
|
||||
@ -921,19 +959,21 @@ export interface TypeVarType extends TypeBase {
|
||||
boundType?: Type;
|
||||
isCovariant: boolean;
|
||||
isContravariant: boolean;
|
||||
isParameterSpec: boolean;
|
||||
|
||||
// Internally created (e.g. for pseudo-generic classes)
|
||||
isSynthesized: boolean;
|
||||
}
|
||||
|
||||
export namespace TypeVarType {
|
||||
export function create(name: string, isSynthesized = false) {
|
||||
export function create(name: string, isParameterSpec: boolean, isSynthesized = false) {
|
||||
const newTypeVarType: TypeVarType = {
|
||||
category: TypeCategory.TypeVar,
|
||||
name,
|
||||
constraints: [],
|
||||
isCovariant: false,
|
||||
isContravariant: false,
|
||||
isParameterSpec,
|
||||
isSynthesized,
|
||||
};
|
||||
return newTypeVarType;
|
||||
|
@ -1467,3 +1467,23 @@ test('Unions1', () => {
|
||||
const analysisResults39 = TestUtils.typeAnalyzeSampleFiles(['unions1.py'], configOptions);
|
||||
validateResults(analysisResults39, 0);
|
||||
});
|
||||
|
||||
test('ParamSpec1', () => {
|
||||
const configOptions = new ConfigOptions('.');
|
||||
|
||||
configOptions.defaultPythonVersion = PythonVersion.V39;
|
||||
const results = TestUtils.typeAnalyzeSampleFiles(['paramSpec1.py'], configOptions);
|
||||
validateResults(results, 7);
|
||||
});
|
||||
|
||||
test('ParamSpec2', () => {
|
||||
const configOptions = new ConfigOptions('.');
|
||||
|
||||
configOptions.defaultPythonVersion = PythonVersion.V38;
|
||||
const analysisResults38 = TestUtils.typeAnalyzeSampleFiles(['paramSpec2.py'], configOptions);
|
||||
validateResults(analysisResults38, 1);
|
||||
|
||||
configOptions.defaultPythonVersion = PythonVersion.V39;
|
||||
const analysisResults39 = TestUtils.typeAnalyzeSampleFiles(['paramSpec2.py'], configOptions);
|
||||
validateResults(analysisResults39, 0);
|
||||
});
|
||||
|
34
server/src/tests/samples/paramSpec1.py
Normal file
34
server/src/tests/samples/paramSpec1.py
Normal file
@ -0,0 +1,34 @@
|
||||
# This sample tests error conditions for ParameterSpecification (PEP 612).
|
||||
|
||||
from typing import Callable, List, ParameterSpecification, Tuple, cast
|
||||
|
||||
|
||||
TParams = ParameterSpecification("TParams")
|
||||
|
||||
# This should generate an error because ParameterSpecifications
|
||||
# can't be used as a type annotation.
|
||||
def foo(a: TParams) -> int:
|
||||
return 1
|
||||
|
||||
a = 3
|
||||
|
||||
# This should generate an error.
|
||||
b = cast(TParams, a)
|
||||
|
||||
# This should generate an error.
|
||||
foo(1)
|
||||
|
||||
# This should generate an error.
|
||||
c: List[TParams] = []
|
||||
|
||||
d: Callable[TParams, int]
|
||||
|
||||
# This should generate an error.
|
||||
e: Callable[TParams, TParams]
|
||||
|
||||
# This should generate an error.
|
||||
f: Callable[[TParams], int]
|
||||
|
||||
# This should generate an error.
|
||||
g: Tuple[TParams]
|
||||
|
27
server/src/tests/samples/paramSpec2.py
Normal file
27
server/src/tests/samples/paramSpec2.py
Normal file
@ -0,0 +1,27 @@
|
||||
# This sample tests ParameterSpecification (PEP 612) behavior.
|
||||
|
||||
from asyncio import Future
|
||||
from typing import Awaitable, Callable, ParameterSpecification, TypeVar
|
||||
|
||||
TParams = ParameterSpecification("TParams")
|
||||
TReturn = TypeVar("TReturn")
|
||||
|
||||
|
||||
def awaitable_wrapper(
|
||||
a: Callable[TParams, TReturn]
|
||||
) -> Callable[TParams, Awaitable[TReturn]]:
|
||||
def foo_internal(args: TParams.args, kwargs: TParams.kwargs) -> Awaitable[TReturn]:
|
||||
ft: Future[TReturn] = Future()
|
||||
ft.set_result(a(*args, **kwargs))
|
||||
return ft
|
||||
|
||||
return foo_internal
|
||||
|
||||
|
||||
@awaitable_wrapper
|
||||
def bar(a: int, b: str) -> float:
|
||||
return 2.3
|
||||
|
||||
|
||||
async def bbb() -> float:
|
||||
return await bar(2, "3")
|
Loading…
Reference in New Issue
Block a user