mirror of
https://github.com/microsoft/pyright.git
synced 2024-11-03 21:30:08 +03:00
Added support for deferred annotation evaluation for Annotated
type arguments beyond the first one. This addresses https://github.com/microsoft/pylance-release/issues/4565. (#5472)
Co-authored-by: Eric Traut <erictr@microsoft.com>
This commit is contained in:
parent
f9796dd5cd
commit
8b38b5dbf9
@ -229,6 +229,9 @@ export class Binder extends ParseTreeWalker {
|
||||
// Are we currently binding code located within an except block?
|
||||
private _isInExceptSuite = false;
|
||||
|
||||
// Are we currently walking the type arguments to an Annotated type annotation?
|
||||
private _isInAnnotatedAnnotation = false;
|
||||
|
||||
// A list of names assigned to __slots__ within a class.
|
||||
private _dunderSlotsEntries: StringListNode[] | undefined;
|
||||
|
||||
@ -651,7 +654,12 @@ export class Binder extends ParseTreeWalker {
|
||||
// and this can lead to a performance issue when walking the control
|
||||
// flow graph if we need to evaluate every decorator.
|
||||
if (!ParseTreeUtils.isNodeContainedWithinNodeType(node, ParseNodeType.Decorator)) {
|
||||
this._createCallFlowNode(node);
|
||||
// Skip if we're in an 'Annotated' annotation because this creates
|
||||
// problems for "No Return" return type analysis when annotation
|
||||
// evaluation is deferred.
|
||||
if (!this._isInAnnotatedAnnotation) {
|
||||
this._createCallFlowNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Is this an manipulation of dunder all?
|
||||
@ -1269,7 +1277,22 @@ export class Binder extends ParseTreeWalker {
|
||||
|
||||
override visitIndex(node: IndexNode): boolean {
|
||||
AnalyzerNodeInfo.setFlowNode(node, this._currentFlowNode!);
|
||||
return true;
|
||||
|
||||
this.walk(node.baseExpression);
|
||||
|
||||
// If we're within an 'Annotated' type annotation, set the flag.
|
||||
const wasInAnnotatedAnnotation = this._isInAnnotatedAnnotation;
|
||||
if (this._isTypingAnnotation(node.baseExpression, 'Annotated')) {
|
||||
this._isInAnnotatedAnnotation = true;
|
||||
}
|
||||
|
||||
node.items.forEach((argNode) => {
|
||||
this.walk(argNode);
|
||||
});
|
||||
|
||||
this._isInAnnotatedAnnotation = wasInAnnotatedAnnotation;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
override visitIf(node: IfNode): boolean {
|
||||
|
@ -7171,17 +7171,31 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
let typeResult: TypeResultWithNode;
|
||||
|
||||
// If it's a custom __class_getitem__, none of the arguments should be
|
||||
// treated as types. If it's an Annotated[a, b, c], only the first index
|
||||
// should be treated as a type. The others can be regular (non-type) objects.
|
||||
if (options?.hasCustomClassGetItem || (options?.isAnnotatedClass && argIndex > 0)) {
|
||||
// treated as types.
|
||||
if (options?.hasCustomClassGetItem) {
|
||||
adjFlags =
|
||||
EvaluatorFlags.DisallowParamSpec |
|
||||
EvaluatorFlags.DisallowTypeVarTuple |
|
||||
EvaluatorFlags.DoNotSpecialize |
|
||||
EvaluatorFlags.DisallowClassVar;
|
||||
typeResult = {
|
||||
...getTypeOfExpression(
|
||||
expr,
|
||||
EvaluatorFlags.DisallowParamSpec |
|
||||
EvaluatorFlags.DisallowTypeVarTuple |
|
||||
EvaluatorFlags.DoNotSpecialize |
|
||||
EvaluatorFlags.DisallowClassVar
|
||||
),
|
||||
...getTypeOfExpression(expr, adjFlags),
|
||||
node: expr,
|
||||
};
|
||||
} else if (options?.isAnnotatedClass && argIndex > 0) {
|
||||
// If it's an Annotated[a, b, c], only the first index should be
|
||||
// treated as a type.The others can be regular(non - type) objects.
|
||||
adjFlags =
|
||||
EvaluatorFlags.DisallowParamSpec |
|
||||
EvaluatorFlags.DisallowTypeVarTuple |
|
||||
EvaluatorFlags.DoNotSpecialize |
|
||||
EvaluatorFlags.DisallowClassVar;
|
||||
if (isAnnotationEvaluationPostponed(AnalyzerNodeInfo.getFileInfo(node))) {
|
||||
adjFlags |= EvaluatorFlags.AllowForwardReferences;
|
||||
}
|
||||
|
||||
typeResult = {
|
||||
...getTypeOfExpression(expr, adjFlags),
|
||||
node: expr,
|
||||
};
|
||||
} else {
|
||||
@ -7564,7 +7578,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
if (node.leftExpression.nodeType === ParseNodeType.Lambda) {
|
||||
baseTypeResult = getTypeOfLambdaForCall(node, inferenceContext);
|
||||
} else {
|
||||
baseTypeResult = getTypeOfExpression(node.leftExpression, EvaluatorFlags.DoNotSpecialize);
|
||||
baseTypeResult = getTypeOfExpression(
|
||||
node.leftExpression,
|
||||
EvaluatorFlags.DoNotSpecialize | (flags & EvaluatorFlags.AllowForwardReferences)
|
||||
);
|
||||
}
|
||||
|
||||
const argList = node.arguments.map((arg) => {
|
||||
|
18
packages/pyright-internal/src/tests/samples/annotated2.py
Normal file
18
packages/pyright-internal/src/tests/samples/annotated2.py
Normal file
@ -0,0 +1,18 @@
|
||||
# This sample tests the case where Annotated is used with deferred
|
||||
# annotation evaluation.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Annotated
|
||||
|
||||
|
||||
v1: Annotated[str, ClassA, func1(), v2[0]] = ""
|
||||
|
||||
v2 = [1, 2, 3]
|
||||
|
||||
|
||||
class ClassA:
|
||||
...
|
||||
|
||||
|
||||
def func1():
|
||||
...
|
@ -1146,6 +1146,12 @@ test('Annotated1', () => {
|
||||
TestUtils.validateResults(analysisResults39, 3);
|
||||
});
|
||||
|
||||
test('Annotated2', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['annotated2.py']);
|
||||
|
||||
TestUtils.validateResults(analysisResults, 0);
|
||||
});
|
||||
|
||||
test('Circular1', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['circular1.py']);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user