Skip to content

Commit

Permalink
Allow Final and ClassVar to be combined in both directions within…
Browse files Browse the repository at this point in the history
… a dataclass. Previously, the `Final` qualifier needed to be the outermost. This addresses #8676. (#8678)
  • Loading branch information
erictraut authored Aug 6, 2024
1 parent 08e02e4 commit c285825
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 10 deletions.
24 changes: 15 additions & 9 deletions packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7607,24 +7607,30 @@ export function createTypeEvaluator(
const typeArgs: TypeResultWithNode[] = [];
let adjFlags = flags | EvalFlags.NoConvertSpecialForm;

if (options?.isFinalAnnotation) {
adjFlags |= EvalFlags.NoClassVar | EvalFlags.NoFinal;
} else if (options?.isClassVarAnnotation) {
adjFlags |= EvalFlags.NoClassVar;

const allowFinalClassVar = () => {
// If the annotation is a variable within the body of a dataclass, a
// Final is allowed within the ClassVar annotation. In all other cases,
// Final is allowed with a 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;
return true;
}
}
return false;
};

if (options?.isFinalAnnotation) {
adjFlags |= EvalFlags.NoFinal;

if (!allowFinalClassVar()) {
adjFlags |= EvalFlags.NoClassVar;
}
} else if (options?.isClassVarAnnotation) {
adjFlags |= EvalFlags.NoClassVar;

if (disallowFinal) {
if (!allowFinalClassVar()) {
adjFlags |= EvalFlags.NoFinal;
}
} else {
Expand Down
4 changes: 4 additions & 0 deletions packages/pyright-internal/src/tests/samples/dataclass17.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class A:
b: Final[str] = ""
c: ClassVar[Final[int]] = 0
d: ClassVar[Final] = 0
e: Final[ClassVar[int]] = 0


a = A(1)
Expand All @@ -29,3 +30,6 @@ class A:

# This should generate an error.
A.d = 0

# This should generate an error.
A.e = 0
2 changes: 1 addition & 1 deletion packages/pyright-internal/src/tests/typeEvaluator4.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ test('DataClass16', () => {
test('DataClass17', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['dataclass17.py']);

TestUtils.validateResults(analysisResults, 5);
TestUtils.validateResults(analysisResults, 6);
});

test('DataClassReplace1', () => {
Expand Down

0 comments on commit c285825

Please sign in to comment.