mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Added support for recent addendum to typing spec that provides a special case for dataclasses so ClassVar
and Final
can be used together to distinguish between a regular Final
instance variable and a ClassVar
that is also Final
. This addresses #7959. (#7962)
This commit is contained in:
parent
24787dad22
commit
7fb7b4158d
@ -3922,6 +3922,13 @@ export class Binder extends ParseTreeWalker {
|
||||
let finalTypeNode: ExpressionNode | undefined;
|
||||
|
||||
if (typeAnnotation) {
|
||||
// Allow Final to be enclosed in ClassVar. Normally, Final implies
|
||||
// ClassVar, but this combination is required in the case of dataclasses.
|
||||
const classVarInfo = this._isAnnotationClassVar(typeAnnotation);
|
||||
if (classVarInfo?.classVarTypeNode) {
|
||||
typeAnnotation = classVarInfo.classVarTypeNode;
|
||||
}
|
||||
|
||||
if (this._isTypingAnnotation(typeAnnotation, 'Final')) {
|
||||
isFinal = true;
|
||||
} else if (typeAnnotation.nodeType === ParseNodeType.Index && typeAnnotation.items.length === 1) {
|
||||
|
@ -7596,8 +7596,26 @@ export function createTypeEvaluator(
|
||||
const typeArgs: TypeResultWithNode[] = [];
|
||||
let adjFlags = flags;
|
||||
|
||||
if (options?.isFinalAnnotation || options?.isClassVarAnnotation) {
|
||||
if (options?.isFinalAnnotation) {
|
||||
adjFlags |= EvaluatorFlags.DisallowClassVar | EvaluatorFlags.DisallowFinal;
|
||||
} else if (options?.isClassVarAnnotation) {
|
||||
adjFlags |= EvaluatorFlags.DisallowClassVar;
|
||||
|
||||
// If the annotation is a variable within the body of a dataclass, a
|
||||
// Final is allowed within the ClassVar annotation. In all other cases,
|
||||
// it's disallowed.
|
||||
let disallowFinal = true;
|
||||
const enclosingClassNode = ParseTreeUtils.getEnclosingClass(node, /* stopeAtFunction */ true);
|
||||
if (enclosingClassNode) {
|
||||
const classTypeInfo = getTypeOfClass(enclosingClassNode);
|
||||
if (classTypeInfo && ClassType.isDataClass(classTypeInfo.classType)) {
|
||||
disallowFinal = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (disallowFinal) {
|
||||
adjFlags |= EvaluatorFlags.DisallowFinal;
|
||||
}
|
||||
} else {
|
||||
adjFlags &= ~(
|
||||
EvaluatorFlags.DoNotSpecialize |
|
||||
|
@ -30,6 +30,15 @@ class Foo(Generic[T]):
|
||||
# be used in a ClassVar.
|
||||
illegal2: ClassVar[T]
|
||||
|
||||
# This should generate an error because Final cannot be
|
||||
# used with a ClassVar.
|
||||
illegal3: ClassVar[Final] = 0
|
||||
|
||||
# This should generate an error because Final cannot be
|
||||
# used with a ClassVar. A second error is generated because
|
||||
# Final[int] is not interpreted as a valid type.
|
||||
illegal4: ClassVar[Final[int]] = 0
|
||||
|
||||
ok1: ClassVar[list]
|
||||
ok2: ClassVar[list[Any]]
|
||||
ok3: Annotated[ClassVar[list[Self]], ""]
|
||||
|
31
packages/pyright-internal/src/tests/samples/dataclass17.py
Normal file
31
packages/pyright-internal/src/tests/samples/dataclass17.py
Normal file
@ -0,0 +1,31 @@
|
||||
# This sample tests the case where a dataclass uses a ClassVar that
|
||||
# is also Final.
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import ClassVar, Final
|
||||
|
||||
|
||||
@dataclass
|
||||
class A:
|
||||
a: Final[int]
|
||||
b: Final[str] = ""
|
||||
c: ClassVar[Final[int]] = 0
|
||||
d: ClassVar[Final] = 0
|
||||
|
||||
|
||||
a = A(1)
|
||||
|
||||
# This should generate an error.
|
||||
a.a = 0
|
||||
|
||||
# This should generate an error.
|
||||
a.b = ""
|
||||
|
||||
# This should generate an error.
|
||||
a.c = 0
|
||||
|
||||
# This should generate an error.
|
||||
A.c = 0
|
||||
|
||||
# This should generate an error.
|
||||
A.d = 0
|
@ -344,6 +344,12 @@ test('DataClass16', () => {
|
||||
TestUtils.validateResults(analysisResults, 1);
|
||||
});
|
||||
|
||||
test('DataClass17', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['dataclass17.py']);
|
||||
|
||||
TestUtils.validateResults(analysisResults, 5);
|
||||
});
|
||||
|
||||
test('DataClassFrozen1', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['dataclassFrozen1.py']);
|
||||
|
||||
|
@ -754,7 +754,7 @@ test('ClassVar2', () => {
|
||||
test('ClassVar3', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['classVar3.py']);
|
||||
|
||||
TestUtils.validateResults(analysisResults, 10);
|
||||
TestUtils.validateResults(analysisResults, 13);
|
||||
});
|
||||
|
||||
test('ClassVar4', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user