mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-02 21:03:11 +00:00
[ty] Implicit instance attributes declared Final (#19462)
## Summary
Adds proper type inference for implicit instance attributes that are
declared with a "bare" `Final` and adds `invalid-assignment` diagnostics
for all implicit instance attributes that are declared `Final` or
`Final[…]`.
## Test Plan
New and updated MD tests.
## Ecosystem analysis
```diff
pytest (https://github.com/pytest-dev/pytest)
+ error[invalid-return-type] src/_pytest/fixtures.py:1662:24: Return type does not match returned value: expected `Scope`, found `Scope | (Unknown & ~None & ~((...) -> object) & ~str) | (((str, Config, /) -> Unknown) & ~((...) -> object) & ~str) | (Unknown & ~str)
```
The definition of the `scope` attribute is [here](
5f99385635/src/_pytest/fixtures.py (L1020-L1028)).
Looks like this is a new false positive due to missing `TypeAlias`
support that is surfaced here because we now infer a more precise type
for `FixtureDef._scope`.
This commit is contained in:
parent
dc66019fbc
commit
b8dec79182
4 changed files with 111 additions and 66 deletions
|
|
@ -19,6 +19,10 @@ FINAL_A: Final[int] = 1
|
|||
FINAL_B: Annotated[Final[int], "the annotation for FINAL_B"] = 1
|
||||
FINAL_C: Final[Annotated[int, "the annotation for FINAL_C"]] = 1
|
||||
FINAL_D: "Final[int]" = 1
|
||||
# Note: Some type checkers do not support a separate declaration and
|
||||
# assignment for `Final` symbols, but it's possible to support this in
|
||||
# ty, and is useful for code that declares symbols `Final` inside
|
||||
# `if TYPE_CHECKING` blocks.
|
||||
FINAL_F: Final[int]
|
||||
FINAL_F = 1
|
||||
|
||||
|
|
@ -87,6 +91,8 @@ class C:
|
|||
def __init__(self):
|
||||
self.FINAL_C: Final[int] = 1
|
||||
self.FINAL_D: Final = 1
|
||||
self.FINAL_E: Final
|
||||
self.FINAL_E = 1
|
||||
|
||||
reveal_type(C.FINAL_A) # revealed: int
|
||||
reveal_type(C.FINAL_B) # revealed: Literal[1]
|
||||
|
|
@ -94,8 +100,8 @@ reveal_type(C.FINAL_B) # revealed: Literal[1]
|
|||
reveal_type(C().FINAL_A) # revealed: int
|
||||
reveal_type(C().FINAL_B) # revealed: Literal[1]
|
||||
reveal_type(C().FINAL_C) # revealed: int
|
||||
# TODO: this should be `Literal[1]`
|
||||
reveal_type(C().FINAL_D) # revealed: Unknown
|
||||
reveal_type(C().FINAL_D) # revealed: Literal[1]
|
||||
reveal_type(C().FINAL_E) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
## Not modifiable
|
||||
|
|
@ -181,6 +187,8 @@ class C(metaclass=Meta):
|
|||
def __init__(self):
|
||||
self.INSTANCE_FINAL_A: Final[int] = 1
|
||||
self.INSTANCE_FINAL_B: Final = 1
|
||||
self.INSTANCE_FINAL_C: Final[int]
|
||||
self.INSTANCE_FINAL_C = 1
|
||||
|
||||
# error: [invalid-assignment] "Cannot assign to final attribute `META_FINAL_A` on type `<class 'C'>`"
|
||||
C.META_FINAL_A = 2
|
||||
|
|
@ -197,10 +205,12 @@ c = C()
|
|||
c.CLASS_FINAL_A = 2
|
||||
# error: [invalid-assignment] "Cannot assign to final attribute `CLASS_FINAL_B` on type `C`"
|
||||
c.CLASS_FINAL_B = 2
|
||||
# TODO: this should be an error
|
||||
# error: [invalid-assignment] "Cannot assign to final attribute `INSTANCE_FINAL_A` on type `C`"
|
||||
c.INSTANCE_FINAL_A = 2
|
||||
# TODO: this should be an error
|
||||
# error: [invalid-assignment] "Cannot assign to final attribute `INSTANCE_FINAL_B` on type `C`"
|
||||
c.INSTANCE_FINAL_B = 2
|
||||
# error: [invalid-assignment] "Cannot assign to final attribute `INSTANCE_FINAL_C` on type `C`"
|
||||
c.INSTANCE_FINAL_C = 2
|
||||
```
|
||||
|
||||
## Mutability
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue