mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 06:11:21 +00:00
Mark quotes as unnecessary for non-evaluated annotations (#11485)
## Summary Similar to #11414, this PR extends `UP037` to flag quoted annotations that are located in positions that won't be evaluated at runtime. For example, the quotes on `Tuple` are unnecessary in: ```python from typing import TYPE_CHECKING if TYPE_CHECKING: from typing import Tuple def foo(): x: "Tuple[int, int]" = (0, 0) foo() ```
This commit is contained in:
parent
573facd2ba
commit
519a65007f
8 changed files with 88 additions and 34 deletions
|
@ -1414,7 +1414,7 @@ fn check_input_from_argfile() -> Result<()> {
|
|||
fs::write(&file_a_path, b"import os")?;
|
||||
fs::write(&file_b_path, b"print('hello, world!')")?;
|
||||
|
||||
// Create a the input file for argfile to expand
|
||||
// Create the input file for argfile to expand
|
||||
let input_file_path = tempdir.path().join("file_paths.txt");
|
||||
fs::write(
|
||||
&input_file_path,
|
||||
|
|
14
crates/ruff_linter/resources/test/fixtures/pyupgrade/UP037_1.py
vendored
Normal file
14
crates/ruff_linter/resources/test/fixtures/pyupgrade/UP037_1.py
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
def foo():
|
||||
# UP037
|
||||
x: "Tuple[int, int]" = (0, 0)
|
||||
print(x)
|
||||
|
||||
|
||||
# OK
|
||||
X: "Tuple[int, int]" = (0, 0)
|
|
@ -2152,7 +2152,7 @@ impl<'a> Checker<'a> {
|
|||
|
||||
self.semantic.restore(snapshot);
|
||||
|
||||
if self.semantic.in_annotation() && self.semantic.future_annotations_or_stub() {
|
||||
if self.semantic.in_annotation() && self.semantic.in_typing_only_annotation() {
|
||||
if self.enabled(Rule::QuotedAnnotation) {
|
||||
pyupgrade::rules::quoted_annotation(self, value, range);
|
||||
}
|
||||
|
|
|
@ -55,7 +55,8 @@ mod tests {
|
|||
#[test_case(Rule::OutdatedVersionBlock, Path::new("UP036_5.py"))]
|
||||
#[test_case(Rule::PrintfStringFormatting, Path::new("UP031_0.py"))]
|
||||
#[test_case(Rule::PrintfStringFormatting, Path::new("UP031_1.py"))]
|
||||
#[test_case(Rule::QuotedAnnotation, Path::new("UP037.py"))]
|
||||
#[test_case(Rule::QuotedAnnotation, Path::new("UP037_0.py"))]
|
||||
#[test_case(Rule::QuotedAnnotation, Path::new("UP037_1.py"))]
|
||||
#[test_case(Rule::RedundantOpenModes, Path::new("UP015.py"))]
|
||||
#[test_case(Rule::ReplaceStdoutStderr, Path::new("UP022.py"))]
|
||||
#[test_case(Rule::ReplaceUniversalNewlines, Path::new("UP021.py"))]
|
||||
|
|
|
@ -10,11 +10,18 @@ use crate::checkers::ast::Checker;
|
|||
///
|
||||
/// ## Why is this bad?
|
||||
/// In Python, type annotations can be quoted to avoid forward references.
|
||||
///
|
||||
/// However, if `from __future__ import annotations` is present, Python
|
||||
/// will always evaluate type annotations in a deferred manner, making
|
||||
/// the quotes unnecessary.
|
||||
///
|
||||
/// Similarly, if the annotation is located in a typing-only context and
|
||||
/// won't be evaluated by Python at runtime, the quotes will also be
|
||||
/// considered unnecessary. For example, Python does not evaluate type
|
||||
/// annotations on assignments in function bodies.
|
||||
///
|
||||
/// ## Example
|
||||
/// Given:
|
||||
/// ```python
|
||||
/// from __future__ import annotations
|
||||
///
|
||||
|
@ -32,6 +39,18 @@ use crate::checkers::ast::Checker;
|
|||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Given:
|
||||
/// ```python
|
||||
/// def foo() -> None:
|
||||
/// bar: "Bar"
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def foo() -> None:
|
||||
/// bar: Bar
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 563](https://peps.python.org/pep-0563/)
|
||||
/// - [Python documentation: `__future__`](https://docs.python.org/3/library/__future__.html#module-__future__)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
UP037.py:18:14: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:18:14: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
18 | def foo(var: "MyClass") -> "MyClass":
|
||||
| ^^^^^^^^^ UP037
|
||||
|
@ -19,7 +19,7 @@ UP037.py:18:14: UP037 [*] Remove quotes from type annotation
|
|||
20 20 |
|
||||
21 21 |
|
||||
|
||||
UP037.py:18:28: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:18:28: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
18 | def foo(var: "MyClass") -> "MyClass":
|
||||
| ^^^^^^^^^ UP037
|
||||
|
@ -37,7 +37,7 @@ UP037.py:18:28: UP037 [*] Remove quotes from type annotation
|
|||
20 20 |
|
||||
21 21 |
|
||||
|
||||
UP037.py:19:8: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:19:8: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
18 | def foo(var: "MyClass") -> "MyClass":
|
||||
19 | x: "MyClass"
|
||||
|
@ -55,7 +55,7 @@ UP037.py:19:8: UP037 [*] Remove quotes from type annotation
|
|||
21 21 |
|
||||
22 22 | def foo(*, inplace: "bool"):
|
||||
|
||||
UP037.py:22:21: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:22:21: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
22 | def foo(*, inplace: "bool"):
|
||||
| ^^^^^^ UP037
|
||||
|
@ -73,7 +73,7 @@ UP037.py:22:21: UP037 [*] Remove quotes from type annotation
|
|||
24 24 |
|
||||
25 25 |
|
||||
|
||||
UP037.py:26:16: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:26:16: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
26 | def foo(*args: "str", **kwargs: "int"):
|
||||
| ^^^^^ UP037
|
||||
|
@ -91,7 +91,7 @@ UP037.py:26:16: UP037 [*] Remove quotes from type annotation
|
|||
28 28 |
|
||||
29 29 |
|
||||
|
||||
UP037.py:26:33: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:26:33: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
26 | def foo(*args: "str", **kwargs: "int"):
|
||||
| ^^^^^ UP037
|
||||
|
@ -109,7 +109,7 @@ UP037.py:26:33: UP037 [*] Remove quotes from type annotation
|
|||
28 28 |
|
||||
29 29 |
|
||||
|
||||
UP037.py:30:10: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:30:10: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
30 | x: Tuple["MyClass"]
|
||||
| ^^^^^^^^^ UP037
|
||||
|
@ -128,7 +128,7 @@ UP037.py:30:10: UP037 [*] Remove quotes from type annotation
|
|||
32 32 | x: Callable[["MyClass"], None]
|
||||
33 33 |
|
||||
|
||||
UP037.py:32:14: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:32:14: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
30 | x: Tuple["MyClass"]
|
||||
31 |
|
||||
|
@ -147,7 +147,7 @@ UP037.py:32:14: UP037 [*] Remove quotes from type annotation
|
|||
34 34 |
|
||||
35 35 | class Foo(NamedTuple):
|
||||
|
||||
UP037.py:36:8: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:36:8: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
35 | class Foo(NamedTuple):
|
||||
36 | x: "MyClass"
|
||||
|
@ -165,7 +165,7 @@ UP037.py:36:8: UP037 [*] Remove quotes from type annotation
|
|||
38 38 |
|
||||
39 39 | class D(TypedDict):
|
||||
|
||||
UP037.py:40:27: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:40:27: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
39 | class D(TypedDict):
|
||||
40 | E: TypedDict("E", foo="int", total=False)
|
||||
|
@ -183,7 +183,7 @@ UP037.py:40:27: UP037 [*] Remove quotes from type annotation
|
|||
42 42 |
|
||||
43 43 | class D(TypedDict):
|
||||
|
||||
UP037.py:44:31: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:44:31: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
43 | class D(TypedDict):
|
||||
44 | E: TypedDict("E", {"foo": "int"})
|
||||
|
@ -201,7 +201,7 @@ UP037.py:44:31: UP037 [*] Remove quotes from type annotation
|
|||
46 46 |
|
||||
47 47 | x: Annotated["str", "metadata"]
|
||||
|
||||
UP037.py:47:14: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:47:14: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
47 | x: Annotated["str", "metadata"]
|
||||
| ^^^^^ UP037
|
||||
|
@ -220,7 +220,7 @@ UP037.py:47:14: UP037 [*] Remove quotes from type annotation
|
|||
49 49 | x: Arg("str", "name")
|
||||
50 50 |
|
||||
|
||||
UP037.py:49:8: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:49:8: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
47 | x: Annotated["str", "metadata"]
|
||||
48 |
|
||||
|
@ -241,7 +241,7 @@ UP037.py:49:8: UP037 [*] Remove quotes from type annotation
|
|||
51 51 | x: DefaultArg("str", "name")
|
||||
52 52 |
|
||||
|
||||
UP037.py:51:15: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:51:15: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
49 | x: Arg("str", "name")
|
||||
50 |
|
||||
|
@ -262,7 +262,7 @@ UP037.py:51:15: UP037 [*] Remove quotes from type annotation
|
|||
53 53 | x: NamedArg("str", "name")
|
||||
54 54 |
|
||||
|
||||
UP037.py:53:13: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:53:13: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
51 | x: DefaultArg("str", "name")
|
||||
52 |
|
||||
|
@ -283,7 +283,7 @@ UP037.py:53:13: UP037 [*] Remove quotes from type annotation
|
|||
55 55 | x: DefaultNamedArg("str", "name")
|
||||
56 56 |
|
||||
|
||||
UP037.py:55:20: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:55:20: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
53 | x: NamedArg("str", "name")
|
||||
54 |
|
||||
|
@ -304,7 +304,7 @@ UP037.py:55:20: UP037 [*] Remove quotes from type annotation
|
|||
57 57 | x: DefaultNamedArg("str", name="name")
|
||||
58 58 |
|
||||
|
||||
UP037.py:57:20: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:57:20: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
55 | x: DefaultNamedArg("str", "name")
|
||||
56 |
|
||||
|
@ -325,7 +325,7 @@ UP037.py:57:20: UP037 [*] Remove quotes from type annotation
|
|||
59 59 | x: VarArg("str")
|
||||
60 60 |
|
||||
|
||||
UP037.py:59:11: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:59:11: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
57 | x: DefaultNamedArg("str", name="name")
|
||||
58 |
|
||||
|
@ -346,7 +346,7 @@ UP037.py:59:11: UP037 [*] Remove quotes from type annotation
|
|||
61 61 | x: List[List[List["MyClass"]]]
|
||||
62 62 |
|
||||
|
||||
UP037.py:61:19: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:61:19: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
59 | x: VarArg("str")
|
||||
60 |
|
||||
|
@ -367,7 +367,7 @@ UP037.py:61:19: UP037 [*] Remove quotes from type annotation
|
|||
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 64 |
|
||||
|
||||
UP037.py:63:29: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:63:29: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
61 | x: List[List[List["MyClass"]]]
|
||||
62 |
|
||||
|
@ -388,7 +388,7 @@ UP037.py:63:29: UP037 [*] Remove quotes from type annotation
|
|||
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
|
||||
UP037.py:63:45: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:63:45: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
61 | x: List[List[List["MyClass"]]]
|
||||
62 |
|
||||
|
@ -409,7 +409,7 @@ UP037.py:63:45: UP037 [*] Remove quotes from type annotation
|
|||
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
|
||||
UP037.py:65:29: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:65:29: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 |
|
||||
|
@ -430,7 +430,7 @@ UP037.py:65:29: UP037 [*] Remove quotes from type annotation
|
|||
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
68 68 |
|
||||
|
||||
UP037.py:65:36: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:65:36: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 |
|
||||
|
@ -451,7 +451,7 @@ UP037.py:65:36: UP037 [*] Remove quotes from type annotation
|
|||
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
68 68 |
|
||||
|
||||
UP037.py:65:45: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:65:45: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 |
|
||||
|
@ -472,7 +472,7 @@ UP037.py:65:45: UP037 [*] Remove quotes from type annotation
|
|||
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
68 68 |
|
||||
|
||||
UP037.py:65:52: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:65:52: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 |
|
||||
|
@ -493,7 +493,7 @@ UP037.py:65:52: UP037 [*] Remove quotes from type annotation
|
|||
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
68 68 |
|
||||
|
||||
UP037.py:67:24: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:67:24: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 |
|
||||
|
@ -514,7 +514,7 @@ UP037.py:67:24: UP037 [*] Remove quotes from type annotation
|
|||
69 69 | X: MyCallable("X")
|
||||
70 70 |
|
||||
|
||||
UP037.py:67:38: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:67:38: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 |
|
||||
|
@ -535,7 +535,7 @@ UP037.py:67:38: UP037 [*] Remove quotes from type annotation
|
|||
69 69 | X: MyCallable("X")
|
||||
70 70 |
|
||||
|
||||
UP037.py:67:45: UP037 [*] Remove quotes from type annotation
|
||||
UP037_0.py:67:45: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 |
|
||||
|
@ -554,6 +554,4 @@ UP037.py:67:45: UP037 [*] Remove quotes from type annotation
|
|||
67 |+x: NamedTuple(typename="X", fields=[("foo", int)])
|
||||
68 68 |
|
||||
69 69 | X: MyCallable("X")
|
||||
70 70 |
|
||||
|
||||
|
||||
70 70 |
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
UP037_1.py:9:8: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
7 | def foo():
|
||||
8 | # UP037
|
||||
9 | x: "Tuple[int, int]" = (0, 0)
|
||||
| ^^^^^^^^^^^^^^^^^ UP037
|
||||
10 | print(x)
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Safe fix
|
||||
6 6 |
|
||||
7 7 | def foo():
|
||||
8 8 | # UP037
|
||||
9 |- x: "Tuple[int, int]" = (0, 0)
|
||||
9 |+ x: Tuple[int, int] = (0, 0)
|
||||
10 10 | print(x)
|
||||
11 11 |
|
||||
12 12 |
|
Loading…
Add table
Add a link
Reference in a new issue