Skip to content

Commit

Permalink
Avoid treating dataclasses.KW_ONLY as typing-only (#12863)
Browse files Browse the repository at this point in the history
## Summary

Closes #12859.
  • Loading branch information
charliermarsh authored Aug 13, 2024
1 parent d0ac38f commit e05953a
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""Test: avoid marking a `KW_ONLY` annotation as typing-only."""

from __future__ import annotations

from dataclasses import KW_ONLY, dataclass, Field


@dataclass
class Test1:
a: int
_: KW_ONLY
b: str


@dataclass
class Test2:
a: int
b: Field
8 changes: 5 additions & 3 deletions crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,14 @@ pub(crate) fn is_dataclass_meta_annotation(annotation: &Expr, semantic: &Semanti
matches!(qualified_name.segments(), ["dataclasses", "dataclass"])
})
}) {
// Determine whether the annotation is `typing.ClassVar` or `dataclasses.InitVar`.
// Determine whether the annotation is `typing.ClassVar`, `dataclasses.InitVar`, or `dataclasses.KW_ONLY`.
return semantic
.resolve_qualified_name(map_subscript(annotation))
.is_some_and(|qualified_name| {
matches!(qualified_name.segments(), ["dataclasses", "InitVar"])
|| semantic.match_typing_qualified_name(&qualified_name, "ClassVar")
matches!(
qualified_name.segments(),
["dataclasses", "InitVar" | "KW_ONLY"]
) || semantic.match_typing_qualified_name(&qualified_name, "ClassVar")
});
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ mod tests {
#[test_case(Rule::TypingOnlyFirstPartyImport, Path::new("TCH001.py"))]
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("TCH003.py"))]
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("init_var.py"))]
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("kw_only.py"))]
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("snapshot.py"))]
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("TCH002.py"))]
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("quote.py"))]
Expand Down Expand Up @@ -77,6 +78,7 @@ mod tests {

#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("strict.py"))]
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("init_var.py"))]
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("kw_only.py"))]
fn strict(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("strict_{}_{}", rule_code.as_ref(), path.to_string_lossy());
let diagnostics = test_path(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
---
kw_only.py:5:45: TCH003 [*] Move standard library import `dataclasses.Field` into a type-checking block
|
3 | from __future__ import annotations
4 |
5 | from dataclasses import KW_ONLY, dataclass, Field
| ^^^^^ TCH003
|
= help: Move into type-checking block

Unsafe fix
2 2 |
3 3 | from __future__ import annotations
4 4 |
5 |-from dataclasses import KW_ONLY, dataclass, Field
5 |+from dataclasses import KW_ONLY, dataclass
6 |+from typing import TYPE_CHECKING
7 |+
8 |+if TYPE_CHECKING:
9 |+ from dataclasses import Field
6 10 |
7 11 |
8 12 | @dataclass
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
---

0 comments on commit e05953a

Please sign in to comment.