mirror of https://github.com/astral-sh/ruff
Avoid applying `PYI055` to runtime-evaluated annotations (#6457)
## Summary
The use of `|` as a union operator is not always safe, if a type
annotation is evaluated in a runtime context. For example, this code
errors at runtime:
```python
import httpretty
import requests_mock
item: type[requests_mock.Mocker | httpretty] = requests_mock.Mocker
```
However, it's fine in a `.pyi` file, with `__future__` annotations`, or
if the annotation is in a non-evaluated context, like:
```python
def func():
item: type[requests_mock.Mocker | httpretty] = requests_mock.Mocker
```
This PR modifies the rule to avoid enforcing in those invalid,
runtime-evaluated contexts.
Closes https://github.com/astral-sh/ruff/issues/6455.
This commit is contained in:
parent
395bb31247
commit
627f475b91
|
|
@ -1,7 +1,6 @@
|
||||||
import builtins
|
import builtins
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
||||||
x: type[int] | type[str] | type[float]
|
x: type[int] | type[str] | type[float]
|
||||||
y: builtins.type[int] | type[str] | builtins.type[complex]
|
y: builtins.type[int] | type[str] | builtins.type[complex]
|
||||||
|
|
@ -9,7 +8,9 @@ z: Union[type[float], type[complex]]
|
||||||
z: Union[type[float, int], type[complex]]
|
z: Union[type[float, int], type[complex]]
|
||||||
|
|
||||||
|
|
||||||
def func(arg: type[int] | str | type[float]) -> None: ...
|
def func(arg: type[int] | str | type[float]) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
# OK
|
# OK
|
||||||
x: type[int, str, float]
|
x: type[int, str, float]
|
||||||
|
|
@ -17,4 +18,14 @@ y: builtins.type[int, str, complex]
|
||||||
z: Union[float, complex]
|
z: Union[float, complex]
|
||||||
|
|
||||||
|
|
||||||
def func(arg: type[int, float] | str) -> None: ...
|
def func(arg: type[int, float] | str) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||||
|
|
||||||
|
|
||||||
|
def func():
|
||||||
|
# PYI055
|
||||||
|
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
import builtins
|
import builtins
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
||||||
x: type[int] | type[str] | type[float]
|
x: type[int] | type[str] | type[float]
|
||||||
y: builtins.type[int] | type[str] | builtins.type[complex]
|
y: builtins.type[int] | type[str] | builtins.type[complex]
|
||||||
z: Union[type[float], type[complex]]
|
z: Union[type[float], type[complex]]
|
||||||
z: Union[type[float, int], type[complex]]
|
z: Union[type[float, int], type[complex]]
|
||||||
|
|
||||||
|
|
||||||
def func(arg: type[int] | str | type[float]) -> None: ...
|
def func(arg: type[int] | str | type[float]) -> None: ...
|
||||||
|
|
||||||
# OK
|
# OK
|
||||||
|
|
@ -16,5 +14,11 @@ x: type[int, str, float]
|
||||||
y: builtins.type[int, str, complex]
|
y: builtins.type[int, str, complex]
|
||||||
z: Union[float, complex]
|
z: Union[float, complex]
|
||||||
|
|
||||||
|
|
||||||
def func(arg: type[int, float] | str) -> None: ...
|
def func(arg: type[int, float] | str) -> None: ...
|
||||||
|
|
||||||
|
# OK
|
||||||
|
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||||
|
|
||||||
|
def func():
|
||||||
|
# PYI055
|
||||||
|
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,11 @@ impl Violation for UnnecessaryTypeUnion {
|
||||||
|
|
||||||
/// PYI055
|
/// PYI055
|
||||||
pub(crate) fn unnecessary_type_union<'a>(checker: &mut Checker, union: &'a Expr) {
|
pub(crate) fn unnecessary_type_union<'a>(checker: &mut Checker, union: &'a Expr) {
|
||||||
|
// The `|` operator isn't always safe to allow to runtime-evaluated annotations.
|
||||||
|
if checker.semantic().execution_context().is_runtime() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mut type_exprs = Vec::new();
|
let mut type_exprs = Vec::new();
|
||||||
|
|
||||||
// Check if `union` is a PEP604 union (e.g. `float | int`) or a `typing.Union[float, int]`
|
// Check if `union` is a PEP604 union (e.g. `float | int`) or a `typing.Union[float, int]`
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,12 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||||
---
|
---
|
||||||
PYI055.py:5:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
|
PYI055.py:31:11: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty]`.
|
||||||
|
|
|
||||||
5 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
|
||||||
6 | x: type[int] | type[str] | type[float]
|
|
||||||
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
|
||||||
|
|
|
||||||
|
|
||||||
PYI055.py:6:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | float]`.
|
|
||||||
|
|
|
||||||
5 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
|
||||||
6 | x: type[int] | type[str] | type[float]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
|
||||||
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
|
||||||
8 | z: Union[type[float], type[complex]]
|
|
||||||
|
|
|
||||||
|
|
||||||
PYI055.py:7:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
|
|
||||||
|
|
|
||||||
5 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
|
||||||
6 | x: type[int] | type[str] | type[float]
|
|
||||||
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
|
||||||
8 | z: Union[type[float], type[complex]]
|
|
||||||
9 | z: Union[type[float, int], type[complex]]
|
|
||||||
|
|
|
||||||
|
|
||||||
PYI055.py:8:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, complex]]`.
|
|
||||||
|
|
|
||||||
6 | x: type[int] | type[str] | type[float]
|
|
||||||
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
|
||||||
8 | z: Union[type[float], type[complex]]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
|
||||||
9 | z: Union[type[float, int], type[complex]]
|
|
||||||
|
|
|
||||||
|
|
||||||
PYI055.py:9:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, int, complex]]`.
|
|
||||||
|
|
|
||||||
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
|
||||||
8 | z: Union[type[float], type[complex]]
|
|
||||||
9 | z: Union[type[float, int], type[complex]]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
|
||||||
|
|
|
||||||
|
|
||||||
PYI055.py:12:15: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | float]`.
|
|
||||||
|
|
|
|
||||||
12 | def func(arg: type[int] | str | type[float]) -> None: ...
|
29 | def func():
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
30 | # PYI055
|
||||||
13 |
|
31 | item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||||
14 | # OK
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,79 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||||
---
|
---
|
||||||
PYI055.pyi:5:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
|
PYI055.pyi:4:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
|
||||||
|
|
|
|
||||||
5 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
2 | from typing import Union
|
||||||
|
3 |
|
||||||
|
4 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
||||||
6 | x: type[int] | type[str] | type[float]
|
5 | x: type[int] | type[str] | type[float]
|
||||||
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
6 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
||||||
|
|
|
|
||||||
|
|
||||||
PYI055.pyi:6:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | float]`.
|
PYI055.pyi:5:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | float]`.
|
||||||
|
|
|
|
||||||
5 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
4 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
||||||
6 | x: type[int] | type[str] | type[float]
|
5 | x: type[int] | type[str] | type[float]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
||||||
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
6 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
||||||
8 | z: Union[type[float], type[complex]]
|
7 | z: Union[type[float], type[complex]]
|
||||||
|
|
|
|
||||||
|
|
||||||
PYI055.pyi:7:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
|
PYI055.pyi:6:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
|
||||||
|
|
|
|
||||||
5 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
4 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
||||||
6 | x: type[int] | type[str] | type[float]
|
5 | x: type[int] | type[str] | type[float]
|
||||||
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
6 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
||||||
8 | z: Union[type[float], type[complex]]
|
7 | z: Union[type[float], type[complex]]
|
||||||
9 | z: Union[type[float, int], type[complex]]
|
8 | z: Union[type[float, int], type[complex]]
|
||||||
|
|
|
|
||||||
|
|
||||||
PYI055.pyi:8:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, complex]]`.
|
PYI055.pyi:7:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, complex]]`.
|
||||||
|
|
|
|
||||||
6 | x: type[int] | type[str] | type[float]
|
5 | x: type[int] | type[str] | type[float]
|
||||||
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
6 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
||||||
8 | z: Union[type[float], type[complex]]
|
7 | z: Union[type[float], type[complex]]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
||||||
9 | z: Union[type[float, int], type[complex]]
|
8 | z: Union[type[float, int], type[complex]]
|
||||||
|
|
|
|
||||||
|
|
||||||
PYI055.pyi:9:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, int, complex]]`.
|
PYI055.pyi:8:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, int, complex]]`.
|
||||||
|
|
|
||||||
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
|
||||||
8 | z: Union[type[float], type[complex]]
|
|
||||||
9 | z: Union[type[float, int], type[complex]]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
|
||||||
|
|
|
||||||
|
|
||||||
PYI055.pyi:12:15: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | float]`.
|
|
||||||
|
|
|
|
||||||
12 | def func(arg: type[int] | str | type[float]) -> None: ...
|
6 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
||||||
|
7 | z: Union[type[float], type[complex]]
|
||||||
|
8 | z: Union[type[float, int], type[complex]]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
||||||
|
9 |
|
||||||
|
10 | def func(arg: type[int] | str | type[float]) -> None: ...
|
||||||
|
|
|
||||||
|
|
||||||
|
PYI055.pyi:10:15: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | float]`.
|
||||||
|
|
|
||||||
|
8 | z: Union[type[float, int], type[complex]]
|
||||||
|
9 |
|
||||||
|
10 | def func(arg: type[int] | str | type[float]) -> None: ...
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
||||||
13 |
|
11 |
|
||||||
14 | # OK
|
12 | # OK
|
||||||
|
|
|
||||||
|
|
||||||
|
PYI055.pyi:20:7: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty]`.
|
||||||
|
|
|
||||||
|
19 | # OK
|
||||||
|
20 | item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
||||||
|
21 |
|
||||||
|
22 | def func():
|
||||||
|
|
|
||||||
|
|
||||||
|
PYI055.pyi:24:11: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty]`.
|
||||||
|
|
|
||||||
|
22 | def func():
|
||||||
|
23 | # PYI055
|
||||||
|
24 | item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue