Added support for parameter type inference for lambdas whose parameters include default arguments. This is analogous to the logic used for unannotated function parameters with default arguments. This addresses #6558. (#6563)

This commit is contained in:
Eric Traut 2023-11-27 17:51:31 -08:00 committed by GitHub
parent f7e247986f
commit 5b3523ff53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 43 deletions

View File

@ -225,6 +225,13 @@ def func(a, b=0, c=None):
reveal_type(func) # (a: Unknown, b: int, c: Unknown | None) -> None
```
This inference technique also applies to lambdas whose input parameters include default arguments.
```python
cb = lambda x = "": x
reveal_type(cb) # (x: str = "" -> str)
```
#### Literals
Python 3.8 introduced support for _literal types_. This allows a type checker like Pyright to track specific literal values of str, bytes, int, bool, and enum values. As with other types, literal types can be declared.

View File

@ -13835,6 +13835,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// its type from the default value expression.
paramType = getTypeOfExpression(param.defaultValue, undefined, inferenceContext).type;
}
} else if (param.defaultValue) {
// If there is no inference context but we have a default value,
// use the default value to infer the parameter's type.
paramType = inferParameterTypeFromDefaultValue(param.defaultValue);
}
if (param.name) {
@ -17638,6 +17642,13 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// type from this information.
const paramValueExpr = functionNode.parameters[paramIndex].defaultValue;
if (paramValueExpr) {
return inferParameterTypeFromDefaultValue(paramValueExpr);
}
return undefined;
}
function inferParameterTypeFromDefaultValue(paramValueExpr: ExpressionNode) {
const defaultValueType = getTypeOfExpression(paramValueExpr, EvaluatorFlags.ConvertEllipsisToAny).type;
let inferredParamType: Type | undefined;
@ -17674,7 +17685,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
if (inferredParamType) {
const fileInfo = AnalyzerNodeInfo.getFileInfo(functionNode);
const fileInfo = AnalyzerNodeInfo.getFileInfo(paramValueExpr);
if (fileInfo.isInPyTypedPackage && !fileInfo.isStubFile) {
inferredParamType = TypeBase.cloneForAmbiguousType(inferredParamType);
}
@ -17683,9 +17694,6 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
return inferredParamType;
}
return undefined;
}
// Transforms the parameter type based on its category. If it's a simple parameter,
// no transform is applied. If it's a var-arg or keyword-arg parameter, the type
// is wrapped in a List or Dict.

View File

@ -0,0 +1,9 @@
# This sample tests type inference for a lambda that has no inference
# context but has a default argument value.
lambda1 = lambda x="": x
reveal_type(lambda1, expected_text='(x: str = "") -> str')
lambda2 = lambda x=None: x
reveal_type(lambda2, expected_text="(x: Unknown | None = None) -> (Unknown | None)")

View File

@ -741,6 +741,12 @@ test('Lambda13', () => {
TestUtils.validateResults(analysisResults, 0);
});
test('Lambda14', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['lambda14.py']);
TestUtils.validateResults(analysisResults, 0);
});
test('Call1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['call1.py']);