mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Fixed bug that causes Final
annotation not to be honored when wrapped in an Annotated
annotation. This addresses #8097.
This commit is contained in:
parent
ec229aa925
commit
5a0ec2dda4
@ -3924,6 +3924,12 @@ export class Binder extends ParseTreeWalker {
|
||||
|
||||
if (this._isTypingAnnotation(typeAnnotation, 'Final')) {
|
||||
isFinal = true;
|
||||
} else if (
|
||||
typeAnnotation.nodeType === ParseNodeType.Index &&
|
||||
typeAnnotation.items.length > 0 &&
|
||||
this._isTypingAnnotation(typeAnnotation.baseExpression, 'Annotated')
|
||||
) {
|
||||
return this._isAnnotationFinal(typeAnnotation.items[0].valueExpression);
|
||||
} else if (typeAnnotation.nodeType === ParseNodeType.Index && typeAnnotation.items.length === 1) {
|
||||
// Recursively call to see if the base expression is "Final".
|
||||
const finalInfo = this._isAnnotationFinal(typeAnnotation.baseExpression);
|
||||
|
@ -2,37 +2,37 @@
|
||||
# introduced in Python 3.8.
|
||||
|
||||
import typing
|
||||
from typing import Any, Final, Protocol, TypeVar
|
||||
from typing import Annotated, Any, Final, Protocol, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
foo1: typing.Final = 3
|
||||
v1: typing.Final = 3
|
||||
|
||||
must_be_int: int = foo1
|
||||
must_be_int: int = v1
|
||||
|
||||
# This should generate an error because
|
||||
# reassignment of a Final variable should
|
||||
# not be allowed.
|
||||
foo1 = 4
|
||||
v1 = 4
|
||||
|
||||
# This should generate an error because there
|
||||
# is a previous Final declaration.
|
||||
foo1: Final[int]
|
||||
v1: Final[int]
|
||||
|
||||
# This should generate an error because
|
||||
# the type doesn't match.
|
||||
foo2: Final[str] = 3
|
||||
v2: Final[str] = 3
|
||||
|
||||
# This should generate an error because
|
||||
# we expect only one type argument for Final.
|
||||
foo3: Final[str, int] = "hello"
|
||||
v3: Final[str, int] = "hello"
|
||||
|
||||
|
||||
foo4: Final = 5
|
||||
reveal_type(foo4, expected_text="Literal[5]")
|
||||
v4: Final = 5
|
||||
reveal_type(v4, expected_text="Literal[5]")
|
||||
|
||||
|
||||
class Foo:
|
||||
class ClassA:
|
||||
member1: Final = 4
|
||||
|
||||
# This should generate an error because only
|
||||
@ -85,11 +85,11 @@ class Foo:
|
||||
self.member7: Final = 6
|
||||
|
||||
|
||||
reveal_type(Foo.member1, expected_text="Literal[4]")
|
||||
reveal_type(Foo(True).member1, expected_text="Literal[4]")
|
||||
reveal_type(ClassA.member1, expected_text="Literal[4]")
|
||||
reveal_type(ClassA(True).member1, expected_text="Literal[4]")
|
||||
|
||||
|
||||
class Bar(Foo):
|
||||
class ClassB(ClassA):
|
||||
# This should generate an error because we are overriding
|
||||
# a member that is marked Final in the parent class.
|
||||
member1 = 5
|
||||
@ -118,7 +118,7 @@ def func1(a: Final[int]):
|
||||
b: list[Final[int]] = []
|
||||
|
||||
|
||||
class ClassA:
|
||||
class ClassC:
|
||||
member1: Final = 3
|
||||
member2: Final
|
||||
member4: Final
|
||||
@ -133,10 +133,10 @@ class ClassA:
|
||||
self.member3: Final = "hi"
|
||||
|
||||
# This should generate an error.
|
||||
ClassA.member4 = "hi"
|
||||
ClassC.member4 = "hi"
|
||||
|
||||
# This should generate an error.
|
||||
ClassA.member5 = 3
|
||||
ClassC.member5 = 3
|
||||
|
||||
def other(self):
|
||||
# This should generate an error.
|
||||
@ -149,7 +149,7 @@ class ClassA:
|
||||
self.member3 = "hi"
|
||||
|
||||
|
||||
a = ClassA()
|
||||
a = ClassC()
|
||||
|
||||
# This should generate an error.
|
||||
a.member1 = 4
|
||||
@ -185,7 +185,7 @@ def func2():
|
||||
(a, x) = (1, 2)
|
||||
|
||||
|
||||
class ClassB:
|
||||
class ClassD:
|
||||
def __init__(self):
|
||||
self.x: Final = 1
|
||||
|
||||
@ -194,7 +194,7 @@ class ClassB:
|
||||
self.x += 1
|
||||
|
||||
|
||||
class ClassC(Protocol):
|
||||
class ClassE(Protocol):
|
||||
x: Final[int]
|
||||
|
||||
|
||||
@ -206,11 +206,11 @@ def func3(x: type[T]) -> T:
|
||||
func3(Final[int])
|
||||
|
||||
|
||||
foo5: Final = lambda: None
|
||||
v5: Final = lambda: None
|
||||
|
||||
|
||||
# This should generate an error because foo5 is declared as Final.
|
||||
def foo5() -> None:
|
||||
def v5() -> None:
|
||||
pass
|
||||
|
||||
|
||||
@ -218,3 +218,19 @@ def foo5() -> None:
|
||||
from typing import ClassVar
|
||||
|
||||
ClassVar: Final = 3
|
||||
|
||||
|
||||
v6: Annotated[Final[int], "meta"] = 1
|
||||
|
||||
# This should generate an error
|
||||
v6 = 2
|
||||
|
||||
v7: Annotated[Annotated[Final[int], "meta"], "meta"] = 1
|
||||
|
||||
# This should generate an error
|
||||
v7 = 2
|
||||
|
||||
v8: Annotated[Final, "meta"] = 1
|
||||
|
||||
# This should generate an error
|
||||
v8 = 2
|
||||
|
@ -32,7 +32,7 @@ test('Final2', () => {
|
||||
|
||||
test('Final3', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['final3.py']);
|
||||
TestUtils.validateResults(analysisResults, 35);
|
||||
TestUtils.validateResults(analysisResults, 38);
|
||||
});
|
||||
|
||||
test('Final4', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user