mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ty] Conditionally defined dataclass fields (#19197)
## Summary Fixes a bug where conditionally defined dataclass fields were previously ignored. Thanks to @lipefree for reporting this. ## Test Plan New Markdown tests
This commit is contained in:
parent
d78d10dd94
commit
ce2bdb9357
3 changed files with 91 additions and 1 deletions
|
@ -558,6 +558,50 @@ class C(Base):
|
|||
reveal_type(C.__init__) # revealed: (self: C, x: int = Literal[15], y: int = Literal[0], z: int = Literal[10]) -> None
|
||||
```
|
||||
|
||||
## Conditionally defined fields
|
||||
|
||||
### Statically known conditions
|
||||
|
||||
Fields that are defined in always-reachable branches are always present in the synthesized
|
||||
`__init__` method. Fields that are defined in never-reachable branches are not present:
|
||||
|
||||
```py
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class C:
|
||||
normal: int
|
||||
|
||||
if 1 + 2 == 3:
|
||||
always_present: str
|
||||
|
||||
if 1 + 2 == 4:
|
||||
never_present: bool
|
||||
|
||||
reveal_type(C.__init__) # revealed: (self: C, normal: int, always_present: str) -> None
|
||||
```
|
||||
|
||||
### Dynamic conditions
|
||||
|
||||
If a field is conditionally defined, we currently assume that it is always present. A more complex
|
||||
alternative here would be to synthesized a union of all possible `__init__` signatures:
|
||||
|
||||
```py
|
||||
from dataclasses import dataclass
|
||||
|
||||
def flag() -> bool:
|
||||
return True
|
||||
|
||||
@dataclass
|
||||
class C:
|
||||
normal: int
|
||||
|
||||
if flag():
|
||||
conditionally_present: str
|
||||
|
||||
reveal_type(C.__init__) # revealed: (self: C, normal: int, conditionally_present: str) -> None
|
||||
```
|
||||
|
||||
## Generic dataclasses
|
||||
|
||||
```toml
|
||||
|
@ -789,6 +833,23 @@ class Fails: # error: [duplicate-kw-only]
|
|||
reveal_type(Fails.__init__) # revealed: (self: Fails, a: int, *, c: str, e: bytes) -> None
|
||||
```
|
||||
|
||||
This also works if `KW_ONLY` is used in a conditional branch:
|
||||
|
||||
```py
|
||||
def flag() -> bool:
|
||||
return True
|
||||
|
||||
@dataclass
|
||||
class D: # error: [duplicate-kw-only]
|
||||
x: int
|
||||
_1: KW_ONLY
|
||||
|
||||
if flag():
|
||||
y: str
|
||||
_2: KW_ONLY
|
||||
z: float
|
||||
```
|
||||
|
||||
## Other special cases
|
||||
|
||||
### `dataclasses.dataclass`
|
||||
|
|
|
@ -37,6 +37,18 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/dataclasses/dataclasses.
|
|||
23 | e: bytes
|
||||
24 |
|
||||
25 | reveal_type(Fails.__init__) # revealed: (self: Fails, a: int, *, c: str, e: bytes) -> None
|
||||
26 | def flag() -> bool:
|
||||
27 | return True
|
||||
28 |
|
||||
29 | @dataclass
|
||||
30 | class D: # error: [duplicate-kw-only]
|
||||
31 | x: int
|
||||
32 | _1: KW_ONLY
|
||||
33 |
|
||||
34 | if flag():
|
||||
35 | y: str
|
||||
36 | _2: KW_ONLY
|
||||
37 | z: float
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
@ -109,6 +121,23 @@ info[revealed-type]: Revealed type
|
|||
24 |
|
||||
25 | reveal_type(Fails.__init__) # revealed: (self: Fails, a: int, *, c: str, e: bytes) -> None
|
||||
| ^^^^^^^^^^^^^^ `(self: Fails, a: int, *, c: str, e: bytes) -> None`
|
||||
26 | def flag() -> bool:
|
||||
27 | return True
|
||||
|
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[duplicate-kw-only]: Dataclass has more than one field annotated with `KW_ONLY`
|
||||
--> src/mdtest_snippet.py:30:7
|
||||
|
|
||||
29 | @dataclass
|
||||
30 | class D: # error: [duplicate-kw-only]
|
||||
| ^
|
||||
31 | x: int
|
||||
32 | _1: KW_ONLY
|
||||
|
|
||||
info: `KW_ONLY` fields: `_1`, `_2`
|
||||
info: rule `duplicate-kw-only` is enabled by default
|
||||
|
||||
```
|
||||
|
|
|
@ -1650,7 +1650,7 @@ impl<'db> ClassLiteral<'db> {
|
|||
if !declarations
|
||||
.clone()
|
||||
.all(|DeclarationWithConstraint { declaration, .. }| {
|
||||
declaration.is_defined_and(|declaration| {
|
||||
declaration.is_undefined_or(|declaration| {
|
||||
matches!(
|
||||
declaration.kind(db),
|
||||
DefinitionKind::AnnotatedAssignment(..)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue