Compare commits
400 Commits
v0.0.282
...
main-backu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e87032d57d | ||
|
|
0c2abf8316 | ||
|
|
6e85e0010f | ||
|
|
2ea0a0b28a | ||
|
|
10abc8fd92 | ||
|
|
61195bc7b1 | ||
|
|
9040554be5 | ||
|
|
06ba1cf0ad | ||
|
|
7a63a8f45a | ||
|
|
c1bc0dc83d | ||
|
|
90573975f3 | ||
|
|
802df97d55 | ||
|
|
6f4ee32807 | ||
|
|
7ec979d022 | ||
|
|
fab9cc5294 | ||
|
|
ff7bab589e | ||
|
|
593b46be5e | ||
|
|
13196fc500 | ||
|
|
5ef4ccd632 | ||
|
|
e363fb860e | ||
|
|
4888d800fb | ||
|
|
4d03b9b5b2 | ||
|
|
fb365c642b | ||
|
|
db04fd4157 | ||
|
|
ff3e8ead36 | ||
|
|
1bbec2b967 | ||
|
|
510be51cfa | ||
|
|
3b4c8fffe5 | ||
|
|
126652b684 | ||
|
|
57e8712d76 | ||
|
|
78c6ede1c9 | ||
|
|
a843a00f6b | ||
|
|
f846f1ea42 | ||
|
|
1d4b7a395f | ||
|
|
ce3ce0734b | ||
|
|
b0e119f049 | ||
|
|
1f5e707829 | ||
|
|
ed7acfe477 | ||
|
|
c31b58eb39 | ||
|
|
c0a3a20c63 | ||
|
|
05ae26b935 | ||
|
|
b996b21ffc | ||
|
|
2d1f69cbb9 | ||
|
|
0f2e295f3a | ||
|
|
c174bbf1f2 | ||
|
|
8078663b6c | ||
|
|
f60e204b73 | ||
|
|
08ebbe40d7 | ||
|
|
ed3b4eb72b | ||
|
|
f0d200c8a1 | ||
|
|
8d74eee750 | ||
|
|
21aa0b8d84 | ||
|
|
9cb00518e5 | ||
|
|
6f65c5cba7 | ||
|
|
8a415fa61e | ||
|
|
5054cbe84f | ||
|
|
69d27d924c | ||
|
|
5270020423 | ||
|
|
edcfcb4a74 | ||
|
|
40a603208f | ||
|
|
b2f95e2848 | ||
|
|
a2e3209c42 | ||
|
|
5e9e8a7589 | ||
|
|
fdec727f80 | ||
|
|
3fbf4f6804 | ||
|
|
fe25708d89 | ||
|
|
342cd19f50 | ||
|
|
4a2c4aad0b | ||
|
|
ae3a477c97 | ||
|
|
531aeb3511 | ||
|
|
4de0cb1827 | ||
|
|
5493c9f4e3 | ||
|
|
d23611db65 | ||
|
|
7d384d88d0 | ||
|
|
b81273e9bc | ||
|
|
335780aeea | ||
|
|
e1f02fced7 | ||
|
|
d4084fb17a | ||
|
|
41a0ef8740 | ||
|
|
25cc1da319 | ||
|
|
33a3c407a9 | ||
|
|
fac0c25343 | ||
|
|
4ff779c298 | ||
|
|
726884b287 | ||
|
|
851f23668f | ||
|
|
3654cf0bdf | ||
|
|
6c5c311bab | ||
|
|
531e41ae2c | ||
|
|
b48834fe2d | ||
|
|
205ee80033 | ||
|
|
fc301ab1b0 | ||
|
|
e820928f11 | ||
|
|
ff17f6e178 | ||
|
|
3bdf8a940a | ||
|
|
9d47d3d212 | ||
|
|
611dcc2e9b | ||
|
|
53de75efc3 | ||
|
|
0c7d16b61a | ||
|
|
735c06d5f4 | ||
|
|
02f13abf50 | ||
|
|
10dda125ff | ||
|
|
27e3873dc2 | ||
|
|
dd4cc25227 | ||
|
|
b78001c953 | ||
|
|
718354673e | ||
|
|
192379cede | ||
|
|
a983f4383f | ||
|
|
947fb53d0b | ||
|
|
2baad9ead6 | ||
|
|
2af9805662 | ||
|
|
5b2af304a2 | ||
|
|
cbe4e8c5f3 | ||
|
|
aabc96dde9 | ||
|
|
99e108dd53 | ||
|
|
d6b6df5d1c | ||
|
|
822cac5aa0 | ||
|
|
aa101e4f26 | ||
|
|
cbc4cb286b | ||
|
|
6fa3d0f90a | ||
|
|
455bcc01a0 | ||
|
|
d495cd9129 | ||
|
|
d8822d1091 | ||
|
|
75f6ce1ae5 | ||
|
|
41e9e7280a | ||
|
|
17c8abcec1 | ||
|
|
e000b1c304 | ||
|
|
243ca16b34 | ||
|
|
4de9580b92 | ||
|
|
5cf85f0b9d | ||
|
|
4dc030ba9d | ||
|
|
1d366d52ab | ||
|
|
a3d9d8cb14 | ||
|
|
09a6afdd04 | ||
|
|
a14e43e03a | ||
|
|
f47dfca4e3 | ||
|
|
58c35ab458 | ||
|
|
7b8844bd3e | ||
|
|
6d7358090b | ||
|
|
9f1a538eba | ||
|
|
48920a034e | ||
|
|
13d6e275ef | ||
|
|
96eb80f5cf | ||
|
|
a73bac9ed1 | ||
|
|
6b60f85cc4 | ||
|
|
39b2dbe04d | ||
|
|
e1f70100ac | ||
|
|
d66d935879 | ||
|
|
e28f333f23 | ||
|
|
0adcdd995d | ||
|
|
e6eb49ffb0 | ||
|
|
80109b1fe0 | ||
|
|
bd64603950 | ||
|
|
5b0e92d725 | ||
|
|
ae9d3c3193 | ||
|
|
3873414b30 | ||
|
|
a9f4b59f40 | ||
|
|
6d6155413b | ||
|
|
f9b5469642 | ||
|
|
ef38eb6b1a | ||
|
|
0ea53825db | ||
|
|
9d6ae774f8 | ||
|
|
55fc0e83f3 | ||
|
|
713dd2b91e | ||
|
|
969ea23d67 | ||
|
|
b80bbec8e6 | ||
|
|
8f425e9ce2 | ||
|
|
d7e2e7361e | ||
|
|
c37e0c7f03 | ||
|
|
0d7b94817d | ||
|
|
f43e5b72e2 | ||
|
|
a19f294b0c | ||
|
|
7ebef61c47 | ||
|
|
b61f4d7b69 | ||
|
|
e26369a34e | ||
|
|
97a08ee77b | ||
|
|
cb8c6fb78d | ||
|
|
8580e4ebb5 | ||
|
|
39fc23cf92 | ||
|
|
66e3080173 | ||
|
|
1511b6631b | ||
|
|
2a8aa6f308 | ||
|
|
dc628cab8f | ||
|
|
c137bc9d77 | ||
|
|
60180fd54c | ||
|
|
c7ed645cc6 | ||
|
|
8caa28f0f8 | ||
|
|
8aa3bc93f3 | ||
|
|
f39ffef370 | ||
|
|
e093d2bee6 | ||
|
|
ca5b474d45 | ||
|
|
2b43d45bd5 | ||
|
|
4bdc2d47c1 | ||
|
|
09b82e41d5 | ||
|
|
cc6d8a1c58 | ||
|
|
07918f0a9a | ||
|
|
a0786ea872 | ||
|
|
4713b2b3ab | ||
|
|
e7f14ab9b8 | ||
|
|
56c73cc63d | ||
|
|
659f4dd8bf | ||
|
|
bd158089e0 | ||
|
|
a73bee7aae | ||
|
|
1468fe46ab | ||
|
|
c9364718b4 | ||
|
|
38cf933bcb | ||
|
|
838990ae15 | ||
|
|
aa0290bbfc | ||
|
|
f74e44d1e8 | ||
|
|
d9df131720 | ||
|
|
f2ffe12f8c | ||
|
|
6dba8430be | ||
|
|
b26365b215 | ||
|
|
2d019930e9 | ||
|
|
d5fc7c4c87 | ||
|
|
393869c47c | ||
|
|
4edd2bf78a | ||
|
|
d3c4551629 | ||
|
|
581f6e176c | ||
|
|
ac4d3c076c | ||
|
|
e5fe037e38 | ||
|
|
872b9d4765 | ||
|
|
674eeec29c | ||
|
|
4f1e7c6291 | ||
|
|
884a7bdb15 | ||
|
|
7885344bcf | ||
|
|
e8fef39861 | ||
|
|
509cf7ed0d | ||
|
|
a4a5366504 | ||
|
|
2dfd053bed | ||
|
|
7f552e4594 | ||
|
|
9efa872023 | ||
|
|
fd8468c5eb | ||
|
|
958c7e33ad | ||
|
|
0f311cd5e5 | ||
|
|
0365752bf3 | ||
|
|
6d140426c1 | ||
|
|
33a62789f7 | ||
|
|
84dff79ddc | ||
|
|
300710f7db | ||
|
|
515dceb07b | ||
|
|
2858c315bf | ||
|
|
3b0fd61b3b | ||
|
|
9030679193 | ||
|
|
71ba4226c1 | ||
|
|
3d03439618 | ||
|
|
39f410c909 | ||
|
|
4222b13e6c | ||
|
|
d691527ead | ||
|
|
40e2150f7e | ||
|
|
92bf96608c | ||
|
|
fac6a857f6 | ||
|
|
68586f8e3c | ||
|
|
f8787a9377 | ||
|
|
530a20cc96 | ||
|
|
6cb57b2075 | ||
|
|
92b2574d52 | ||
|
|
95fb938bd6 | ||
|
|
9683314264 | ||
|
|
d9bbeeb9b3 | ||
|
|
68116d5c11 | ||
|
|
e164a41723 | ||
|
|
aa32a73c5b | ||
|
|
9d7d629cef | ||
|
|
439298e735 | ||
|
|
16173bf581 | ||
|
|
7e7b2eadee | ||
|
|
80116c768d | ||
|
|
200390c1ab | ||
|
|
24d2ab8b0a | ||
|
|
41f21a7b5d | ||
|
|
63e4a36e27 | ||
|
|
15ab44384c | ||
|
|
5380b85579 | ||
|
|
b707f53f23 | ||
|
|
6439867f78 | ||
|
|
1904d095f9 | ||
|
|
7e8f683808 | ||
|
|
37b1894834 | ||
|
|
201d08583a | ||
|
|
107b2e11ae | ||
|
|
661b210391 | ||
|
|
9aed9143fe | ||
|
|
c21d0d9283 | ||
|
|
313fd7d28c | ||
|
|
3aa0096212 | ||
|
|
53dec88029 | ||
|
|
bd0c15d34e | ||
|
|
ce0be73841 | ||
|
|
b2ac4f60f1 | ||
|
|
1fdfa5fe1b | ||
|
|
c0f390ebc6 | ||
|
|
c387b5d523 | ||
|
|
ea95e1a715 | ||
|
|
c16e08d59b | ||
|
|
ff00460ff4 | ||
|
|
f4672e4256 | ||
|
|
6e89b3ab1a | ||
|
|
6a174dae45 | ||
|
|
8936ab2f8d | ||
|
|
165b979733 | ||
|
|
06c92bb899 | ||
|
|
1a657731dd | ||
|
|
d8cfc7e84f | ||
|
|
de2e88656e | ||
|
|
052dee72b8 | ||
|
|
751f9d304f | ||
|
|
d6f9dd0763 | ||
|
|
5fc8c4d0b1 | ||
|
|
1b7a272b77 | ||
|
|
3abdc87076 | ||
|
|
99b02be35a | ||
|
|
f99167d4ed | ||
|
|
816e1e711c | ||
|
|
e2b28d07c8 | ||
|
|
28785784b2 | ||
|
|
6f6b7b2312 | ||
|
|
93cb05ebda | ||
|
|
e8200ab674 | ||
|
|
492f09298f | ||
|
|
35eea0b8ec | ||
|
|
1eeddb521e | ||
|
|
96a50810a6 | ||
|
|
51b7dbb89c | ||
|
|
28a8c3a062 | ||
|
|
4e5626dfd5 | ||
|
|
f979d8dbc3 | ||
|
|
cc084b4fec | ||
|
|
bbeec36fdb | ||
|
|
b6c230f3ca | ||
|
|
a5b59f3c9d | ||
|
|
d5a208ca9d | ||
|
|
fa41a1e2f6 | ||
|
|
952d70b9d1 | ||
|
|
02953b9fe6 | ||
|
|
8adc74fe26 | ||
|
|
48c0cb5599 | ||
|
|
2e33a3d0e9 | ||
|
|
1cc342e4ed | ||
|
|
519718e65d | ||
|
|
5da5490b19 | ||
|
|
518cf728c3 | ||
|
|
3397737a76 | ||
|
|
6211a3a3a8 | ||
|
|
e64d7d196d | ||
|
|
f8b45e48e1 | ||
|
|
f99e4789ed | ||
|
|
86b847204e | ||
|
|
26b529f9dc | ||
|
|
b21ed24025 | ||
|
|
a414677892 | ||
|
|
bfe4795b6c | ||
|
|
40690b9761 | ||
|
|
1fd898c14c | ||
|
|
42b95a9a95 | ||
|
|
e8230efe1a | ||
|
|
7fcc18daea | ||
|
|
2b7bf79d29 | ||
|
|
904fc477f1 | ||
|
|
bfac0355dc | ||
|
|
c16e650071 | ||
|
|
acde8bb625 | ||
|
|
7f99404618 | ||
|
|
3742f9117b | ||
|
|
a66902406f | ||
|
|
51b6571ee1 | ||
|
|
2345bc895d | ||
|
|
53c48bf6b9 | ||
|
|
ffacac05bb | ||
|
|
3bae0823f7 | ||
|
|
29644a30d7 | ||
|
|
07712eda58 | ||
|
|
76728bb69d | ||
|
|
721c2709c8 | ||
|
|
7865cace06 | ||
|
|
a9330765fa | ||
|
|
9941eef853 | ||
|
|
e494175cd2 | ||
|
|
dd7560c494 | ||
|
|
6603b4a124 | ||
|
|
dab7e20a33 | ||
|
|
13f3ec5b2b | ||
|
|
452cd308d5 | ||
|
|
3c469eaa26 | ||
|
|
0c5bf8e470 | ||
|
|
3f52fdd50a | ||
|
|
f0c0897f23 | ||
|
|
a35d4dc22a | ||
|
|
5fe1bd3900 | ||
|
|
3737af157f | ||
|
|
ed01cd63ee | ||
|
|
1dd57971a9 | ||
|
|
d1ae5a7448 | ||
|
|
e89d6febea | ||
|
|
4dcad05aa0 | ||
|
|
b0d270b881 | ||
|
|
e3ccef2504 | ||
|
|
dcdd964e0c | ||
|
|
107442cc06 | ||
|
|
10957879db | ||
|
|
f0148f46d0 |
6
.github/workflows/ci.yaml
vendored
6
.github/workflows/ci.yaml
vendored
@@ -50,10 +50,6 @@ jobs:
|
||||
- crates/ruff_formatter/**
|
||||
- crates/ruff_python_trivia/**
|
||||
- crates/ruff_python_ast/**
|
||||
- crates/ruff_source_file/**
|
||||
- crates/ruff_python_index/**
|
||||
- crates/ruff_text_size/**
|
||||
- crates/ruff_python_parser/**
|
||||
|
||||
|
||||
cargo-fmt:
|
||||
@@ -335,7 +331,7 @@ jobs:
|
||||
- name: "Cache rust"
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: "Formatter progress"
|
||||
run: scripts/formatter_ecosystem_checks.sh
|
||||
run: scripts/formatter_progress.sh
|
||||
- name: "Github step summary"
|
||||
run: grep "similarity index" target/progress_projects_report.txt | sort > $GITHUB_STEP_SUMMARY
|
||||
# CPython is not black formatted, so we run only the stability check
|
||||
|
||||
63
Cargo.lock
generated
63
Cargo.lock
generated
@@ -133,12 +133,6 @@ dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
|
||||
[[package]]
|
||||
name = "ascii-canvas"
|
||||
version = "3.0.0"
|
||||
@@ -800,7 +794,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.282"
|
||||
version = "0.0.280"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1033,7 +1027,6 @@ dependencies = [
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-width",
|
||||
"vt100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2042,7 +2035,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.282"
|
||||
version = "0.0.280"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2141,7 +2134,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.282"
|
||||
version = "0.0.280"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2201,6 +2194,7 @@ dependencies = [
|
||||
"indoc",
|
||||
"itertools",
|
||||
"libcst",
|
||||
"log",
|
||||
"once_cell",
|
||||
"pretty_assertions",
|
||||
"rayon",
|
||||
@@ -2224,9 +2218,6 @@ dependencies = [
|
||||
"strum_macros",
|
||||
"tempfile",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-indicatif",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2291,7 +2282,6 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3125,18 +3115,6 @@ dependencies = [
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-indicatif"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b38ed3722d27705c3bd7ca0ccf29acc3d8e1c717b4cd87f97891a2c1834ea1af"
|
||||
dependencies = [
|
||||
"indicatif",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.1.3"
|
||||
@@ -3335,39 +3313,6 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vt100"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"log",
|
||||
"unicode-width",
|
||||
"vte",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"utf8parse",
|
||||
"vte_generate_state_changes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte_generate_state_changes"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.0"
|
||||
|
||||
@@ -46,9 +46,6 @@ syn = { version = "2.0.15" }
|
||||
test-case = { version = "3.0.0" }
|
||||
thiserror = { version = "1.0.43" }
|
||||
toml = { version = "0.7.2" }
|
||||
tracing = "0.1.37"
|
||||
tracing-indicatif = "0.3.4"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
# v1.0.1
|
||||
|
||||
@@ -140,7 +140,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.0.282
|
||||
rev: v0.0.280
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.282"
|
||||
version = "0.0.280"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.282"
|
||||
version = "0.0.280"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import typing
|
||||
import sys
|
||||
from typing import TypeAlias
|
||||
|
||||
|
||||
_UnusedPrivateTypeAlias: TypeAlias = int | None
|
||||
_T: typing.TypeAlias = str
|
||||
|
||||
# OK
|
||||
_UsedPrivateTypeAlias: TypeAlias = int | None
|
||||
|
||||
def func(arg: _UsedPrivateTypeAlias) -> _UsedPrivateTypeAlias:
|
||||
...
|
||||
|
||||
|
||||
if sys.version_info > (3, 9):
|
||||
_PrivateTypeAlias: TypeAlias = str | None
|
||||
else:
|
||||
_PrivateTypeAlias: TypeAlias = float | None
|
||||
|
||||
|
||||
def func2(arg: _PrivateTypeAlias) -> None: ...
|
||||
@@ -1,22 +0,0 @@
|
||||
import typing
|
||||
import sys
|
||||
from typing import TypeAlias
|
||||
|
||||
|
||||
_UnusedPrivateTypeAlias: TypeAlias = int | None
|
||||
_T: typing.TypeAlias = str
|
||||
|
||||
# OK
|
||||
_UsedPrivateTypeAlias: TypeAlias = int | None
|
||||
|
||||
def func(arg: _UsedPrivateTypeAlias) -> _UsedPrivateTypeAlias:
|
||||
...
|
||||
|
||||
|
||||
if sys.version_info > (3, 9):
|
||||
_PrivateTypeAlias: TypeAlias = str | None
|
||||
else:
|
||||
_PrivateTypeAlias: TypeAlias = float | None
|
||||
|
||||
|
||||
def func2(arg: _PrivateTypeAlias) -> None: ...
|
||||
@@ -1,18 +0,0 @@
|
||||
import typing
|
||||
from typing import TypedDict
|
||||
|
||||
|
||||
class _UnusedTypedDict(TypedDict):
|
||||
foo: str
|
||||
|
||||
|
||||
class _UnusedTypedDict2(typing.TypedDict):
|
||||
bar: int
|
||||
|
||||
|
||||
class _UsedTypedDict(TypedDict):
|
||||
foo: bytes
|
||||
|
||||
|
||||
class _CustomClass(_UsedTypedDict):
|
||||
bar: list[int]
|
||||
@@ -1,32 +0,0 @@
|
||||
import sys
|
||||
import typing
|
||||
from typing import TypedDict
|
||||
|
||||
|
||||
class _UnusedTypedDict(TypedDict):
|
||||
foo: str
|
||||
|
||||
|
||||
class _UnusedTypedDict2(typing.TypedDict):
|
||||
bar: int
|
||||
|
||||
|
||||
# OK
|
||||
class _UsedTypedDict(TypedDict):
|
||||
foo: bytes
|
||||
|
||||
|
||||
class _CustomClass(_UsedTypedDict):
|
||||
bar: list[int]
|
||||
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
class _UsedTypedDict2(TypedDict):
|
||||
foo: int
|
||||
else:
|
||||
class _UsedTypedDict2(TypedDict):
|
||||
foo: float
|
||||
|
||||
|
||||
class _CustomClass2(_UsedTypedDict2):
|
||||
bar: list[int]
|
||||
@@ -1,15 +1,7 @@
|
||||
import contextlib
|
||||
import pathlib
|
||||
import pathlib as pl
|
||||
from pathlib import Path
|
||||
from pathlib import Path as P
|
||||
|
||||
# SIM115
|
||||
f = open("foo.txt")
|
||||
f = Path("foo.txt").open()
|
||||
f = pathlib.Path("foo.txt").open()
|
||||
f = pl.Path("foo.txt").open()
|
||||
f = P("foo.txt").open()
|
||||
data = f.read()
|
||||
f.close()
|
||||
|
||||
|
||||
@@ -30,13 +30,3 @@ for key in list(obj.keys()):
|
||||
(k for k in obj.keys()) # SIM118
|
||||
|
||||
key in (obj or {}).keys() # SIM118
|
||||
|
||||
from typing import KeysView
|
||||
|
||||
|
||||
class Foo:
|
||||
def keys(self) -> KeysView[object]:
|
||||
...
|
||||
|
||||
def __contains__(self, key: object) -> bool:
|
||||
return key in self.keys() # OK
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
"""Regression test: ensure that we don't treat the export entry as a typing-only reference."""
|
||||
from __future__ import annotations
|
||||
|
||||
from logging import getLogger
|
||||
|
||||
__all__ = ("getLogger",)
|
||||
|
||||
|
||||
def foo() -> None:
|
||||
pass
|
||||
@@ -1,2 +0,0 @@
|
||||
import bar
|
||||
import foo
|
||||
@@ -1,2 +0,0 @@
|
||||
import foo
|
||||
import bar
|
||||
@@ -1,23 +1,28 @@
|
||||
# PERF203
|
||||
for i in range(10):
|
||||
try:
|
||||
try: # PERF203
|
||||
print(f"{i}")
|
||||
except:
|
||||
print("error")
|
||||
|
||||
# OK
|
||||
try:
|
||||
for i in range(10):
|
||||
print(f"{i}")
|
||||
except:
|
||||
print("error")
|
||||
|
||||
# OK
|
||||
i = 0
|
||||
while i < 10:
|
||||
while i < 10: # PERF203
|
||||
try:
|
||||
print(f"{i}")
|
||||
except:
|
||||
print("error")
|
||||
|
||||
i += 1
|
||||
|
||||
try:
|
||||
i = 0
|
||||
while i < 10:
|
||||
print(f"{i}")
|
||||
i += 1
|
||||
except:
|
||||
print("error")
|
||||
|
||||
@@ -45,18 +45,3 @@ def f():
|
||||
for i in items:
|
||||
if i not in result:
|
||||
result.append(i) # OK
|
||||
|
||||
|
||||
def f():
|
||||
fibonacci = [0, 1]
|
||||
for i in range(20):
|
||||
fibonacci.append(sum(fibonacci[-2:])) # OK
|
||||
print(fibonacci)
|
||||
|
||||
|
||||
def f():
|
||||
foo = object()
|
||||
foo.fibonacci = [0, 1]
|
||||
for i in range(20):
|
||||
foo.fibonacci.append(sum(foo.fibonacci[-2:])) # OK
|
||||
print(foo.fibonacci)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#: E241
|
||||
a = (1, 2)
|
||||
#: Okay
|
||||
b = (1, 20)
|
||||
#: E242
|
||||
a = (1, 2) # tab before 2
|
||||
#: Okay
|
||||
b = (1, 20) # space before 20
|
||||
#: E241 E241 E241
|
||||
# issue 135
|
||||
more_spaces = [a, b,
|
||||
ef, +h,
|
||||
c, -d]
|
||||
@@ -66,6 +66,3 @@ while 1:
|
||||
#: E703:2:1
|
||||
0\
|
||||
;
|
||||
#: E701:2:3
|
||||
a = \
|
||||
5;
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
"""Test type parameters and aliases"""
|
||||
|
||||
# Type parameters in type alias statements
|
||||
|
||||
from some_module import Bar
|
||||
|
||||
type Foo[T] = T # OK
|
||||
type Foo[T] = list[T] # OK
|
||||
type Foo[T: ForwardA] = T # OK
|
||||
type Foo[*Ts] = Bar[Ts] # OK
|
||||
type Foo[**P] = Bar[P] # OK
|
||||
class ForwardA: ...
|
||||
|
||||
# Types used in aliased assignment must exist
|
||||
|
||||
type Foo = DoesNotExist # F821: Undefined name `DoesNotExist`
|
||||
type Foo = list[DoesNotExist] # F821: Undefined name `DoesNotExist`
|
||||
|
||||
# Type parameters do not escape alias scopes
|
||||
|
||||
type Foo[T] = T
|
||||
T # F821: Undefined name `T` - not accessible afterward alias scope
|
||||
|
||||
# Type parameters in functions
|
||||
|
||||
def foo[T](t: T) -> T: return t # OK
|
||||
async def afoo[T](t: T) -> T: return t # OK
|
||||
def with_forward_ref[T: ForwardB](t: T) -> T: return t # OK
|
||||
def can_access_inside[T](t: T) -> T: # OK
|
||||
print(T) # OK
|
||||
return t # OK
|
||||
class ForwardB: ...
|
||||
|
||||
|
||||
# Type parameters do not escape function scopes
|
||||
|
||||
from some_library import some_decorator
|
||||
|
||||
@some_decorator(T) # F821: Undefined name `T` - not accessible in decorators
|
||||
|
||||
def foo[T](t: T) -> None: ...
|
||||
T # F821: Undefined name `T` - not accessible afterward function scope
|
||||
|
||||
|
||||
# Type parameters in classes
|
||||
|
||||
class Foo[T](list[T]): ... # OK
|
||||
class UsesForward[T: ForwardC](list[T]): ... # OK
|
||||
class ForwardC: ...
|
||||
class WithinBody[T](list[T]): # OK
|
||||
t = T # OK
|
||||
x: T # OK
|
||||
|
||||
def foo(self, x: T) -> T: # OK
|
||||
return x
|
||||
|
||||
def foo(self):
|
||||
T # OK
|
||||
|
||||
|
||||
# Type parameters do not escape class scopes
|
||||
|
||||
from some_library import some_decorator
|
||||
@some_decorator(T) # F821: Undefined name `T` - not accessible in decorators
|
||||
|
||||
class Foo[T](list[T]): ...
|
||||
T # F821: Undefined name `T` - not accessible after class scope
|
||||
|
||||
# Types specified in bounds should exist
|
||||
|
||||
type Foo[T: DoesNotExist] = T # F821: Undefined name `DoesNotExist`
|
||||
def foo[T: DoesNotExist](t: T) -> T: return t # F821: Undefined name `DoesNotExist`
|
||||
class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
|
||||
|
||||
type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
|
||||
# Type parameters in nested classes
|
||||
|
||||
class Parent[T]:
|
||||
t = T # OK
|
||||
|
||||
def can_use_class_variable(self, x: t) -> t: # OK
|
||||
return x
|
||||
|
||||
class Child:
|
||||
def can_access_parent_type_parameter(self, x: T) -> T: # OK
|
||||
T # OK
|
||||
return x
|
||||
|
||||
def cannot_access_parent_variable(self, x: t) -> t: # F821: Undefined name `T`
|
||||
t # F821: Undefined name `t`
|
||||
return x
|
||||
|
||||
# Type parameters in nested functions
|
||||
|
||||
def can_access_inside_nested[T](t: T) -> T: # OK
|
||||
def bar(x: T) -> T: # OK
|
||||
T # OK
|
||||
return x
|
||||
|
||||
bar(t)
|
||||
@@ -63,10 +63,3 @@ def main():
|
||||
|
||||
for sys in range(5):
|
||||
pass
|
||||
|
||||
|
||||
import requests_mock as rm
|
||||
|
||||
|
||||
def requests_mock(requests_mock: rm.Mocker):
|
||||
print(rm.ANY)
|
||||
|
||||
@@ -147,10 +147,3 @@ def f() -> None:
|
||||
global CONSTANT
|
||||
CONSTANT = 1
|
||||
CONSTANT = 2
|
||||
|
||||
|
||||
def f() -> None:
|
||||
try:
|
||||
print("hello")
|
||||
except A as e :
|
||||
print("oh no!")
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
class Person:
|
||||
def __init__(self):
|
||||
self.name = "monty"
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Person) and other.name == self.name
|
||||
|
||||
class Language:
|
||||
def __init__(self):
|
||||
self.name = "python"
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Language) and other.name == self.name
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
@@ -1,3 +1,5 @@
|
||||
"""A mirror of UP037_1.py, with `from __future__ import annotations`."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import (
|
||||
108
crates/ruff/resources/test/fixtures/pyupgrade/UP037_1.py
vendored
Normal file
108
crates/ruff/resources/test/fixtures/pyupgrade/UP037_1.py
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
"""A mirror of UP037_0.py, without `from __future__ import annotations`."""
|
||||
|
||||
from typing import (
|
||||
Annotated,
|
||||
Callable,
|
||||
List,
|
||||
Literal,
|
||||
NamedTuple,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
TypedDict,
|
||||
cast,
|
||||
)
|
||||
|
||||
from mypy_extensions import Arg, DefaultArg, DefaultNamedArg, NamedArg, VarArg
|
||||
|
||||
|
||||
def foo(var: "MyClass") -> "MyClass":
|
||||
x: "MyClass"
|
||||
|
||||
|
||||
def foo(*, inplace: "bool"):
|
||||
pass
|
||||
|
||||
|
||||
def foo(*args: "str", **kwargs: "int"):
|
||||
pass
|
||||
|
||||
|
||||
x: Tuple["MyClass"]
|
||||
|
||||
x: Callable[["MyClass"], None]
|
||||
|
||||
|
||||
class Foo(NamedTuple):
|
||||
x: "MyClass"
|
||||
|
||||
|
||||
class D(TypedDict):
|
||||
E: TypedDict("E", foo="int", total=False)
|
||||
|
||||
|
||||
class D(TypedDict):
|
||||
E: TypedDict("E", {"foo": "int"})
|
||||
|
||||
|
||||
x: Annotated["str", "metadata"]
|
||||
|
||||
x: Arg("str", "name")
|
||||
|
||||
x: DefaultArg("str", "name")
|
||||
|
||||
x: NamedArg("str", "name")
|
||||
|
||||
x: DefaultNamedArg("str", "name")
|
||||
|
||||
x: DefaultNamedArg("str", name="name")
|
||||
|
||||
x: VarArg("str")
|
||||
|
||||
x: List[List[List["MyClass"]]]
|
||||
|
||||
x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
|
||||
x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
|
||||
x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
|
||||
X: MyCallable("X")
|
||||
|
||||
|
||||
# OK
|
||||
class D(TypedDict):
|
||||
E: TypedDict("E")
|
||||
|
||||
|
||||
x: Annotated[()]
|
||||
|
||||
x: DefaultNamedArg(name="name", quox="str")
|
||||
|
||||
x: DefaultNamedArg(name="name")
|
||||
|
||||
x: NamedTuple("X", [("foo",), ("bar",)])
|
||||
|
||||
x: NamedTuple("X", ["foo", "bar"])
|
||||
|
||||
x: NamedTuple()
|
||||
|
||||
x: Literal["foo", "bar"]
|
||||
|
||||
x = cast(x, "str")
|
||||
|
||||
|
||||
def foo(x, *args, **kwargs):
|
||||
...
|
||||
|
||||
|
||||
def foo(*, inplace):
|
||||
...
|
||||
|
||||
|
||||
x: Annotated[1:2] = ...
|
||||
|
||||
x = TypeVar("x", "str", "int")
|
||||
|
||||
x = cast("str", x)
|
||||
|
||||
X = List["MyClass"]
|
||||
@@ -2,9 +2,21 @@ x = range(10)
|
||||
|
||||
# RUF015
|
||||
list(x)[0]
|
||||
list(x)[:1]
|
||||
list(x)[:1:1]
|
||||
list(x)[:1:2]
|
||||
tuple(x)[0]
|
||||
tuple(x)[:1]
|
||||
tuple(x)[:1:1]
|
||||
tuple(x)[:1:2]
|
||||
list(i for i in x)[0]
|
||||
list(i for i in x)[:1]
|
||||
list(i for i in x)[:1:1]
|
||||
list(i for i in x)[:1:2]
|
||||
[i for i in x][0]
|
||||
[i for i in x][:1]
|
||||
[i for i in x][:1:1]
|
||||
[i for i in x][:1:2]
|
||||
|
||||
# OK (not indexing (solely) the first element)
|
||||
list(x)
|
||||
@@ -17,9 +29,6 @@ list(x)[::]
|
||||
[i for i in x]
|
||||
[i for i in x][1]
|
||||
[i for i in x][-1]
|
||||
[i for i in x][:1]
|
||||
[i for i in x][:1:1]
|
||||
[i for i in x][:1:2]
|
||||
[i for i in x][1:]
|
||||
[i for i in x][:3:2]
|
||||
[i for i in x][::2]
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import os # ruff: noqa: F401
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
@@ -1,6 +1,6 @@
|
||||
use itertools::Itertools;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use itertools::Itertools;
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
@@ -59,30 +59,35 @@ fn apply_fixes<'a>(
|
||||
})
|
||||
.sorted_by(|(rule1, fix1), (rule2, fix2)| cmp_fix(*rule1, *rule2, fix1, fix2))
|
||||
{
|
||||
let mut edits = fix
|
||||
.edits()
|
||||
.iter()
|
||||
.filter(|edit| !applied.contains(edit))
|
||||
.peekable();
|
||||
// If we already applied an identical fix as part of another correction, skip
|
||||
// any re-application.
|
||||
if fix.edits().iter().all(|edit| applied.contains(edit)) {
|
||||
*fixed.entry(rule).or_default() += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the fix contains at least one new edit, enforce isolation and positional requirements.
|
||||
if let Some(first) = edits.peek() {
|
||||
// If this fix requires isolation, and we've already applied another fix in the
|
||||
// same isolation group, skip it.
|
||||
if let IsolationLevel::Group(id) = fix.isolation() {
|
||||
if !isolated.insert(id) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Best-effort approach: if this fix overlaps with a fix we've already applied,
|
||||
// skip it.
|
||||
if last_pos.map_or(false, |last_pos| {
|
||||
fix.min_start()
|
||||
.map_or(false, |fix_location| last_pos >= fix_location)
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this fix overlaps with a fix we've already applied, skip it.
|
||||
if last_pos.map_or(false, |last_pos| last_pos >= first.start()) {
|
||||
// If this fix requires isolation, and we've already applied another fix in the
|
||||
// same isolation group, skip it.
|
||||
if let IsolationLevel::Group(id) = fix.isolation() {
|
||||
if !isolated.insert(id) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut applied_edits = Vec::with_capacity(fix.edits().len());
|
||||
for edit in edits {
|
||||
for edit in fix
|
||||
.edits()
|
||||
.iter()
|
||||
.sorted_unstable_by_key(|edit| edit.start())
|
||||
{
|
||||
// Add all contents from `last_pos` to `fix.location`.
|
||||
let slice = locator.slice(TextRange::new(last_pos.unwrap_or_default(), edit.start()));
|
||||
output.push_str(slice);
|
||||
@@ -98,10 +103,9 @@ fn apply_fixes<'a>(
|
||||
|
||||
// Track that the edit was applied.
|
||||
last_pos = Some(edit.end());
|
||||
applied_edits.push(edit);
|
||||
applied.insert(edit);
|
||||
}
|
||||
|
||||
applied.extend(applied_edits.drain(..));
|
||||
*fixed.entry(rule).or_default() += 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use ruff_diagnostics::{Diagnostic, Fix};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
use crate::rules::{flake8_import_conventions, flake8_pyi, pyflakes, pylint};
|
||||
use ruff_diagnostics::{Diagnostic, Fix};
|
||||
|
||||
/// Run lint rules over the [`Binding`]s.
|
||||
pub(crate) fn bindings(checker: &mut Checker) {
|
||||
@@ -11,7 +10,9 @@ pub(crate) fn bindings(checker: &mut Checker) {
|
||||
Rule::InvalidAllObject,
|
||||
Rule::UnaliasedCollectionsAbcSetImport,
|
||||
Rule::UnconventionalImportAlias,
|
||||
Rule::UnusedPrivateTypeVar,
|
||||
Rule::UnusedVariable,
|
||||
Rule::UnusedPrivateProtocol,
|
||||
]) {
|
||||
return;
|
||||
}
|
||||
@@ -64,6 +65,20 @@ pub(crate) fn bindings(checker: &mut Checker) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateTypeVar) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_pyi::rules::unused_private_type_var(checker, binding)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateProtocol) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_pyi::rules::unused_private_protocol(checker, binding)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruff_python_semantic::{Binding, BindingKind, ScopeKind};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
use crate::rules::{flake8_pyi, flake8_type_checking, flake8_unused_arguments, pyflakes, pylint};
|
||||
use crate::rules::{flake8_type_checking, flake8_unused_arguments, pyflakes, pylint};
|
||||
|
||||
/// Run lint rules over all deferred scopes in the [`SemanticModel`].
|
||||
pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
@@ -24,10 +24,6 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
Rule::UnusedImport,
|
||||
Rule::UnusedLambdaArgument,
|
||||
Rule::UnusedMethodArgument,
|
||||
Rule::UnusedPrivateProtocol,
|
||||
Rule::UnusedPrivateTypeAlias,
|
||||
Rule::UnusedPrivateTypeVar,
|
||||
Rule::UnusedPrivateTypedDict,
|
||||
Rule::UnusedStaticMethodArgument,
|
||||
Rule::UnusedVariable,
|
||||
]) {
|
||||
@@ -218,21 +214,6 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
|
||||
if checker.is_stub {
|
||||
if checker.enabled(Rule::UnusedPrivateTypeVar) {
|
||||
flake8_pyi::rules::unused_private_type_var(checker, scope, &mut diagnostics);
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateProtocol) {
|
||||
flake8_pyi::rules::unused_private_protocol(checker, scope, &mut diagnostics);
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateTypeAlias) {
|
||||
flake8_pyi::rules::unused_private_type_alias(checker, scope, &mut diagnostics);
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateTypedDict) {
|
||||
flake8_pyi::rules::unused_private_typed_dict(checker, scope, &mut diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(
|
||||
scope.kind,
|
||||
ScopeKind::Function(_) | ScopeKind::AsyncFunction(_) | ScopeKind::Lambda(_)
|
||||
|
||||
@@ -396,9 +396,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
flake8_django::rules::model_without_dunder_str(checker, class_def);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::EqWithoutHash) {
|
||||
pylint::rules::object_without_hash_method(checker, class_def);
|
||||
}
|
||||
if checker.enabled(Rule::GlobalStatement) {
|
||||
pylint::rules::global_statement(checker, name);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use ruff_python_ast::{Expr, TypeParam};
|
||||
use ruff_python_semantic::{ScopeId, Snapshot};
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use ruff_python_semantic::{ScopeId, Snapshot};
|
||||
|
||||
/// A collection of AST nodes that are deferred for later analysis.
|
||||
/// Used to, e.g., store functions, whose bodies shouldn't be analyzed until all
|
||||
/// module-level definitions have been analyzed.
|
||||
@@ -10,7 +11,6 @@ pub(crate) struct Deferred<'a> {
|
||||
pub(crate) scopes: Vec<ScopeId>,
|
||||
pub(crate) string_type_definitions: Vec<(TextRange, &'a str, Snapshot)>,
|
||||
pub(crate) future_type_definitions: Vec<(&'a Expr, Snapshot)>,
|
||||
pub(crate) type_param_definitions: Vec<(&'a TypeParam, Snapshot)>,
|
||||
pub(crate) functions: Vec<Snapshot>,
|
||||
pub(crate) lambdas: Vec<(&'a Expr, Snapshot)>,
|
||||
pub(crate) for_loops: Vec<Snapshot>,
|
||||
|
||||
@@ -453,14 +453,12 @@ where
|
||||
args,
|
||||
decorator_list,
|
||||
returns,
|
||||
type_params,
|
||||
..
|
||||
})
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef {
|
||||
body,
|
||||
args,
|
||||
decorator_list,
|
||||
type_params,
|
||||
returns,
|
||||
..
|
||||
}) => {
|
||||
@@ -474,12 +472,6 @@ where
|
||||
// are enabled.
|
||||
let runtime_annotation = !self.semantic.future_annotations();
|
||||
|
||||
self.semantic.push_scope(ScopeKind::Type);
|
||||
|
||||
for type_param in type_params {
|
||||
self.visit_type_param(type_param);
|
||||
}
|
||||
|
||||
for arg_with_default in args
|
||||
.posonlyargs
|
||||
.iter()
|
||||
@@ -550,25 +542,18 @@ where
|
||||
bases,
|
||||
keywords,
|
||||
decorator_list,
|
||||
type_params,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
for decorator in decorator_list {
|
||||
self.visit_decorator(decorator);
|
||||
}
|
||||
|
||||
self.semantic.push_scope(ScopeKind::Type);
|
||||
|
||||
for type_param in type_params {
|
||||
self.visit_type_param(type_param);
|
||||
}
|
||||
for expr in bases {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
for keyword in keywords {
|
||||
self.visit_keyword(keyword);
|
||||
}
|
||||
for decorator in decorator_list {
|
||||
self.visit_decorator(decorator);
|
||||
}
|
||||
|
||||
let definition = docstrings::extraction::extract_definition(
|
||||
ExtractionTarget::Class,
|
||||
@@ -577,6 +562,7 @@ where
|
||||
&self.semantic.definitions,
|
||||
);
|
||||
self.semantic.push_definition(definition);
|
||||
|
||||
self.semantic.push_scope(ScopeKind::Class(class_def));
|
||||
|
||||
// Extract any global bindings from the class body.
|
||||
@@ -586,20 +572,6 @@ where
|
||||
|
||||
self.visit_body(body);
|
||||
}
|
||||
Stmt::TypeAlias(ast::StmtTypeAlias {
|
||||
range: _range,
|
||||
name,
|
||||
type_params,
|
||||
value,
|
||||
}) => {
|
||||
self.semantic.push_scope(ScopeKind::Type);
|
||||
for type_param in type_params {
|
||||
self.visit_type_param(type_param);
|
||||
}
|
||||
self.visit_expr(value);
|
||||
self.semantic.pop_scope();
|
||||
self.visit_expr(name);
|
||||
}
|
||||
Stmt::Try(ast::StmtTry {
|
||||
body,
|
||||
handlers,
|
||||
@@ -743,9 +715,8 @@ where
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { name, .. }) => {
|
||||
let scope_id = self.semantic.scope_id;
|
||||
self.deferred.scopes.push(scope_id);
|
||||
self.semantic.pop_scope(); // Function scope
|
||||
self.semantic.pop_scope();
|
||||
self.semantic.pop_definition();
|
||||
self.semantic.pop_scope(); // Type parameter scope
|
||||
self.add_binding(
|
||||
name,
|
||||
stmt.identifier(),
|
||||
@@ -756,9 +727,8 @@ where
|
||||
Stmt::ClassDef(ast::StmtClassDef { name, .. }) => {
|
||||
let scope_id = self.semantic.scope_id;
|
||||
self.deferred.scopes.push(scope_id);
|
||||
self.semantic.pop_scope(); // Class scope
|
||||
self.semantic.pop_scope();
|
||||
self.semantic.pop_definition();
|
||||
self.semantic.pop_scope(); // Type parameter scope
|
||||
self.add_binding(
|
||||
name,
|
||||
stmt.identifier(),
|
||||
@@ -1361,26 +1331,6 @@ where
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_type_param(&mut self, type_param: &'b ast::TypeParam) {
|
||||
// Step 1: Binding
|
||||
match type_param {
|
||||
ast::TypeParam::TypeVar(ast::TypeParamTypeVar { name, range, .. })
|
||||
| ast::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple { name, range })
|
||||
| ast::TypeParam::ParamSpec(ast::TypeParamParamSpec { name, range }) => {
|
||||
self.add_binding(
|
||||
name.as_str(),
|
||||
*range,
|
||||
BindingKind::TypeParam,
|
||||
BindingFlags::empty(),
|
||||
);
|
||||
}
|
||||
}
|
||||
// Step 2: Traversal
|
||||
self.deferred
|
||||
.type_param_definitions
|
||||
.push((type_param, self.semantic.snapshot()));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Checker<'a> {
|
||||
@@ -1597,10 +1547,10 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
fn handle_node_load(&mut self, expr: &Expr) {
|
||||
let Expr::Name(expr) = expr else {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = expr else {
|
||||
return;
|
||||
};
|
||||
self.semantic.resolve_load(expr);
|
||||
self.semantic.resolve_load(id, expr.range());
|
||||
}
|
||||
|
||||
fn handle_node_store(&mut self, id: &'a str, expr: &Expr) {
|
||||
@@ -1731,7 +1681,6 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
fn visit_deferred_future_type_definitions(&mut self) {
|
||||
let snapshot = self.semantic.snapshot();
|
||||
while !self.deferred.future_type_definitions.is_empty() {
|
||||
let type_definitions = std::mem::take(&mut self.deferred.future_type_definitions);
|
||||
for (expr, snapshot) in type_definitions {
|
||||
@@ -1742,29 +1691,9 @@ impl<'a> Checker<'a> {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
self.semantic.restore(snapshot);
|
||||
}
|
||||
|
||||
fn visit_deferred_type_param_definitions(&mut self) {
|
||||
let snapshot = self.semantic.snapshot();
|
||||
while !self.deferred.type_param_definitions.is_empty() {
|
||||
let type_params = std::mem::take(&mut self.deferred.type_param_definitions);
|
||||
for (type_param, snapshot) in type_params {
|
||||
self.semantic.restore(snapshot);
|
||||
|
||||
if let ast::TypeParam::TypeVar(ast::TypeParamTypeVar {
|
||||
bound: Some(bound), ..
|
||||
}) = type_param
|
||||
{
|
||||
self.visit_expr(bound);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.semantic.restore(snapshot);
|
||||
}
|
||||
|
||||
fn visit_deferred_string_type_definitions(&mut self, allocator: &'a typed_arena::Arena<Expr>) {
|
||||
let snapshot = self.semantic.snapshot();
|
||||
while !self.deferred.string_type_definitions.is_empty() {
|
||||
let type_definitions = std::mem::take(&mut self.deferred.string_type_definitions);
|
||||
for (range, value, snapshot) in type_definitions {
|
||||
@@ -1775,7 +1704,7 @@ impl<'a> Checker<'a> {
|
||||
|
||||
self.semantic.restore(snapshot);
|
||||
|
||||
if self.semantic.in_annotation() && self.semantic.future_annotations() {
|
||||
if self.semantic.in_typing_only_annotation() {
|
||||
if self.enabled(Rule::QuotedAnnotation) {
|
||||
pyupgrade::rules::quoted_annotation(self, value, range);
|
||||
}
|
||||
@@ -1808,11 +1737,9 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.semantic.restore(snapshot);
|
||||
}
|
||||
|
||||
fn visit_deferred_functions(&mut self) {
|
||||
let snapshot = self.semantic.snapshot();
|
||||
while !self.deferred.functions.is_empty() {
|
||||
let deferred_functions = std::mem::take(&mut self.deferred.functions);
|
||||
for snapshot in deferred_functions {
|
||||
@@ -1830,11 +1757,9 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.semantic.restore(snapshot);
|
||||
}
|
||||
|
||||
fn visit_deferred_lambdas(&mut self) {
|
||||
let snapshot = self.semantic.snapshot();
|
||||
while !self.deferred.lambdas.is_empty() {
|
||||
let lambdas = std::mem::take(&mut self.deferred.lambdas);
|
||||
for (expr, snapshot) in lambdas {
|
||||
@@ -1853,13 +1778,10 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.semantic.restore(snapshot);
|
||||
}
|
||||
|
||||
/// Run any lint rules that operate over the module exports (i.e., members of `__all__`).
|
||||
fn visit_exports(&mut self) {
|
||||
let snapshot = self.semantic.snapshot();
|
||||
|
||||
let exports: Vec<(&str, TextRange)> = self
|
||||
.semantic
|
||||
.global_scope()
|
||||
@@ -1902,8 +1824,6 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.semantic.restore(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1962,7 +1882,6 @@ pub(crate) fn check_ast(
|
||||
checker.visit_deferred_functions();
|
||||
checker.visit_deferred_lambdas();
|
||||
checker.visit_deferred_future_type_definitions();
|
||||
checker.visit_deferred_type_param_definitions();
|
||||
let allocator = typed_arena::Arena::new();
|
||||
checker.visit_deferred_string_type_definitions(&allocator);
|
||||
checker.visit_exports();
|
||||
|
||||
@@ -9,9 +9,9 @@ use ruff_source_file::Locator;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::rules::pycodestyle::rules::logical_lines::{
|
||||
extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword,
|
||||
missing_whitespace_around_operator, space_after_comma, space_around_operator,
|
||||
whitespace_around_keywords, whitespace_around_named_parameter_equals,
|
||||
whitespace_before_comment, whitespace_before_parameters, LogicalLines, TokenFlags,
|
||||
missing_whitespace_around_operator, space_around_operator, whitespace_around_keywords,
|
||||
whitespace_around_named_parameter_equals, whitespace_before_comment,
|
||||
whitespace_before_parameters, LogicalLines, TokenFlags,
|
||||
};
|
||||
use crate::settings::Settings;
|
||||
|
||||
@@ -61,9 +61,6 @@ pub(crate) fn check_logical_lines(
|
||||
missing_whitespace_around_operator(&line, &mut context);
|
||||
missing_whitespace(&line, should_fix_missing_whitespace, &mut context);
|
||||
}
|
||||
if line.flags().contains(TokenFlags::PUNCTUATION) {
|
||||
space_after_comma(&line, &mut context);
|
||||
}
|
||||
|
||||
if line
|
||||
.flags()
|
||||
|
||||
@@ -84,8 +84,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pycodestyle, "E227") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundBitwiseOrShiftOperator),
|
||||
(Pycodestyle, "E228") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundModuloOperator),
|
||||
(Pycodestyle, "E231") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespace),
|
||||
(Pycodestyle, "E241") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MultipleSpacesAfterComma),
|
||||
(Pycodestyle, "E242") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::TabAfterComma),
|
||||
(Pycodestyle, "E251") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::UnexpectedSpacesAroundKeywordParameterEquals),
|
||||
(Pycodestyle, "E252") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundParameterEquals),
|
||||
(Pycodestyle, "E261") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::TooFewSpacesBeforeInlineComment),
|
||||
@@ -225,7 +223,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "W0711") => (RuleGroup::Unspecified, rules::pylint::rules::BinaryOpException),
|
||||
(Pylint, "W1508") => (RuleGroup::Unspecified, rules::pylint::rules::InvalidEnvvarDefault),
|
||||
(Pylint, "W1509") => (RuleGroup::Unspecified, rules::pylint::rules::SubprocessPopenPreexecFn),
|
||||
(Pylint, "W1641") => (RuleGroup::Nursery, rules::pylint::rules::EqWithoutHash),
|
||||
(Pylint, "W2901") => (RuleGroup::Unspecified, rules::pylint::rules::RedefinedLoopName),
|
||||
(Pylint, "W3301") => (RuleGroup::Unspecified, rules::pylint::rules::NestedMinMax),
|
||||
|
||||
@@ -654,9 +651,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Pyi, "044") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::FutureAnnotationsInStub),
|
||||
(Flake8Pyi, "045") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::IterMethodReturnIterable),
|
||||
(Flake8Pyi, "046") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnusedPrivateProtocol),
|
||||
(Flake8Pyi, "047") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnusedPrivateTypeAlias),
|
||||
(Flake8Pyi, "048") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::StubBodyMultipleStatements),
|
||||
(Flake8Pyi, "049") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnusedPrivateTypedDict),
|
||||
(Flake8Pyi, "050") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::NoReturnArgumentAnnotationInStub),
|
||||
(Flake8Pyi, "052") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnannotatedAssignmentInStub),
|
||||
(Flake8Pyi, "054") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::NumericLiteralTooLong),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::Display;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write};
|
||||
use std::iter;
|
||||
@@ -42,20 +41,6 @@ pub fn round_trip(path: &Path) -> anyhow::Result<String> {
|
||||
Ok(String::from_utf8(writer)?)
|
||||
}
|
||||
|
||||
impl Display for SourceValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SourceValue::String(string) => f.write_str(string),
|
||||
SourceValue::StringArray(string_array) => {
|
||||
for string in string_array {
|
||||
f.write_str(string)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
/// Return the [`SourceValue`] of the cell.
|
||||
fn source(&self) -> &SourceValue {
|
||||
@@ -422,11 +407,6 @@ impl Notebook {
|
||||
self.content = transformed.to_string();
|
||||
}
|
||||
|
||||
/// Return a slice of [`Cell`] in the Jupyter notebook.
|
||||
pub fn cells(&self) -> &[Cell] {
|
||||
&self.raw.cells
|
||||
}
|
||||
|
||||
/// Return `true` if the notebook is a Python notebook, `false` otherwise.
|
||||
pub fn is_python_notebook(&self) -> bool {
|
||||
self.raw
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::fmt::{Display, Formatter};
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
use colored::{Color, ColoredString, Colorize, Styles};
|
||||
|
||||
use itertools::Itertools;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
use similar::{ChangeTag, TextDiff};
|
||||
|
||||
@@ -38,7 +38,12 @@ impl Display for Diff<'_> {
|
||||
let mut output = String::with_capacity(self.source_code.source_text().len());
|
||||
let mut last_end = TextSize::default();
|
||||
|
||||
for edit in self.fix.edits() {
|
||||
for edit in self
|
||||
.fix
|
||||
.edits()
|
||||
.iter()
|
||||
.sorted_unstable_by_key(|edit| edit.start())
|
||||
{
|
||||
output.push_str(
|
||||
self.source_code
|
||||
.slice(TextRange::new(last_end, edit.start())),
|
||||
|
||||
@@ -12,7 +12,6 @@ use ruff_python_ast::Ranged;
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_trivia::indentation_at_offset;
|
||||
use ruff_source_file::{LineEnding, Locator};
|
||||
|
||||
use crate::codes::NoqaCode;
|
||||
@@ -249,34 +248,22 @@ impl FileExemption {
|
||||
let path_display = relativize_path(path);
|
||||
warn!("Invalid `# ruff: noqa` directive at {path_display}:{line}: {err}");
|
||||
}
|
||||
Ok(Some(exemption)) => {
|
||||
if indentation_at_offset(range.start(), locator).is_none() {
|
||||
#[allow(deprecated)]
|
||||
let line = locator.compute_line_index(range.start());
|
||||
let path_display = relativize_path(path);
|
||||
warn!("Unexpected `# ruff: noqa` directive at {path_display}:{line}. File-level suppression comments must appear on their own line.");
|
||||
continue;
|
||||
}
|
||||
|
||||
match exemption {
|
||||
ParsedFileExemption::All => {
|
||||
return Some(Self::All);
|
||||
Ok(Some(ParsedFileExemption::All)) => {
|
||||
return Some(Self::All);
|
||||
}
|
||||
Ok(Some(ParsedFileExemption::Codes(codes))) => {
|
||||
exempt_codes.extend(codes.into_iter().filter_map(|code| {
|
||||
if let Ok(rule) = Rule::from_code(get_redirect_target(code).unwrap_or(code))
|
||||
{
|
||||
Some(rule.noqa_code())
|
||||
} else {
|
||||
#[allow(deprecated)]
|
||||
let line = locator.compute_line_index(range.start());
|
||||
let path_display = relativize_path(path);
|
||||
warn!("Invalid code provided to `# ruff: noqa` at {path_display}:{line}: {code}");
|
||||
None
|
||||
}
|
||||
ParsedFileExemption::Codes(codes) => {
|
||||
exempt_codes.extend(codes.into_iter().filter_map(|code| {
|
||||
if let Ok(rule) = Rule::from_code(get_redirect_target(code).unwrap_or(code))
|
||||
{
|
||||
Some(rule.noqa_code())
|
||||
} else {
|
||||
#[allow(deprecated)]
|
||||
let line = locator.compute_line_index(range.start());
|
||||
let path_display = relativize_path(path);
|
||||
warn!("Invalid rule code provided to `# ruff: noqa` at {path_display}:{line}: {code}");
|
||||
None
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
Ok(None) => {}
|
||||
}
|
||||
|
||||
@@ -303,7 +303,6 @@ impl Rule {
|
||||
| Rule::MissingWhitespaceAroundOperator
|
||||
| Rule::MissingWhitespaceAroundParameterEquals
|
||||
| Rule::MultipleLeadingHashesForBlockComment
|
||||
| Rule::MultipleSpacesAfterComma
|
||||
| Rule::MultipleSpacesAfterKeyword
|
||||
| Rule::MultipleSpacesAfterOperator
|
||||
| Rule::MultipleSpacesBeforeKeyword
|
||||
@@ -313,7 +312,6 @@ impl Rule {
|
||||
| Rule::NoSpaceAfterBlockComment
|
||||
| Rule::NoSpaceAfterInlineComment
|
||||
| Rule::OverIndented
|
||||
| Rule::TabAfterComma
|
||||
| Rule::TabAfterKeyword
|
||||
| Rule::TabAfterOperator
|
||||
| Rule::TabBeforeKeyword
|
||||
|
||||
@@ -242,7 +242,6 @@ impl Renamer {
|
||||
// By default, replace the binding's name with the target name.
|
||||
BindingKind::Annotation
|
||||
| BindingKind::Argument
|
||||
| BindingKind::TypeParam
|
||||
| BindingKind::NamedExprAssignment
|
||||
| BindingKind::UnpackedAssignment
|
||||
| BindingKind::Assignment
|
||||
|
||||
@@ -6,35 +6,6 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `jinja2` templates that use `autoescape=False`.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// `jinja2` templates that use `autoescape=False` are vulnerable to cross-site
|
||||
/// scripting (XSS) attacks that allow attackers to execute arbitrary
|
||||
/// JavaScript.
|
||||
///
|
||||
/// By default, `jinja2` sets `autoescape` to `False`, so it is important to
|
||||
/// set `autoescape=True` or use the `select_autoescape` function to mitigate
|
||||
/// XSS vulnerabilities.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// import jinja2
|
||||
///
|
||||
/// jinja2.Environment(loader=jinja2.FileSystemLoader("."))
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// import jinja2
|
||||
///
|
||||
/// jinja2.Environment(loader=jinja2.FileSystemLoader("."), autoescape=True)
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Jinja documentation: API](https://jinja.palletsprojects.com/en/latest/api/#autoescaping)
|
||||
/// - [Common Weakness Enumeration: CWE-94](https://cwe.mitre.org/data/definitions/94.html)
|
||||
#[violation]
|
||||
pub struct Jinja2AutoescapeFalse {
|
||||
value: bool,
|
||||
|
||||
@@ -6,24 +6,6 @@ use ruff_python_ast::helpers::find_keyword;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for insecure `logging.config.listen` calls.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// `logging.config.listen` starts a server that listens for logging
|
||||
/// configuration requests. This is insecure as parts of the configuration are
|
||||
/// passed to the built-in `eval` function, which can be used to execute
|
||||
/// arbitrary code.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// import logging
|
||||
///
|
||||
/// logging.config.listen(9999)
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `logging.config.listen()`](https://docs.python.org/3/library/logging.config.html#logging.config.listen)
|
||||
#[violation]
|
||||
pub struct LoggingConfigInsecureListen;
|
||||
|
||||
|
||||
@@ -5,25 +5,6 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `paramiko` calls.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// `paramiko` calls allow users to execute arbitrary shell commands on a
|
||||
/// remote machine. If the inputs to these calls are not properly sanitized,
|
||||
/// they can be vulnerable to shell injection attacks.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// import paramiko
|
||||
///
|
||||
/// client = paramiko.SSHClient()
|
||||
/// client.exec_command("echo $HOME")
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Common Weakness Enumeration: CWE-78](https://cwe.mitre.org/data/definitions/78.html)
|
||||
/// - [Paramiko documentation: `SSHClient.exec_command()`](https://docs.paramiko.org/en/stable/api/client.html#paramiko.client.SSHClient.exec_command)
|
||||
#[violation]
|
||||
pub struct ParamikoCall;
|
||||
|
||||
|
||||
@@ -75,31 +75,6 @@ impl Violation for StartProcessWithNoShell {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the starting of a process with a partial executable path.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Starting a process with a partial executable path can allow attackers to
|
||||
/// execute arbitrary executable by adjusting the `PATH` environment variable.
|
||||
/// Consider using a full path to the executable instead.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// import subprocess
|
||||
///
|
||||
/// subprocess.Popen(["ruff", "check", "file.py"])
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// import subprocess
|
||||
///
|
||||
/// subprocess.Popen(["/usr/bin/ruff", "check", "file.py"])
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `subprocess.Popen()`](https://docs.python.org/3/library/subprocess.html#subprocess.Popen)
|
||||
/// - [Common Weakness Enumeration: CWE-78](https://cwe.mitre.org/data/definitions/78.html)
|
||||
#[violation]
|
||||
pub struct StartProcessWithPartialPath;
|
||||
|
||||
@@ -110,29 +85,6 @@ impl Violation for StartProcessWithPartialPath {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for possible wildcard injections in calls to `subprocess.Popen()`.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Wildcard injections can lead to unexpected behavior if unintended files are
|
||||
/// matched by the wildcard. Consider using a more specific path instead.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// import subprocess
|
||||
///
|
||||
/// subprocess.Popen(["chmod", "777", "*.py"])
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// import subprocess
|
||||
///
|
||||
/// subprocess.Popen(["chmod", "777", "main.py"])
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Common Weakness Enumeration: CWE-78](https://cwe.mitre.org/data/definitions/78.html)
|
||||
#[violation]
|
||||
pub struct UnixCommandWildcardInjection;
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ fn assignment(obj: &Expr, name: &str, value: &Expr, generator: Generator) -> Str
|
||||
range: TextRange::default(),
|
||||
})],
|
||||
value: Box::new(value.clone()),
|
||||
type_comment: None,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
generator.stmt(&stmt)
|
||||
|
||||
@@ -8,40 +8,6 @@ use crate::checkers::ast::Checker;
|
||||
|
||||
use super::helpers;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for usage of `datetime.datetime.today()`.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// `datetime` objects are "naive" by default, in that they do not include
|
||||
/// timezone information. "Naive" objects are easy to understand, but ignore
|
||||
/// some aspects of reality, which can lead to subtle bugs. Timezone-aware
|
||||
/// `datetime` objects are preferred, as they represent a specific moment in
|
||||
/// time, unlike "naive" objects.
|
||||
///
|
||||
/// `datetime.datetime.today()` crates a "naive" object; instead, use
|
||||
/// instead, use `datetime.datetime.now(tz=)` to create a timezone-aware
|
||||
/// object.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// import datetime
|
||||
///
|
||||
/// datetime.datetime.today()
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// import datetime
|
||||
///
|
||||
/// datetime.datetime.now(tz=datetime.timezone.utc)
|
||||
/// ```
|
||||
///
|
||||
/// Or, for Python 3.11 and later:
|
||||
/// ```python
|
||||
/// import datetime
|
||||
///
|
||||
/// datetime.datetime.now(tz=datetime.UTC)
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct CallDatetimeToday;
|
||||
|
||||
@@ -55,6 +21,12 @@ impl Violation for CallDatetimeToday {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks for `datetime.datetime.today()`. (DTZ002)
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
///
|
||||
/// It uses the system local timezone.
|
||||
/// Use `datetime.datetime.now(tz=)` instead.
|
||||
pub(crate) fn call_datetime_today(checker: &mut Checker, func: &Expr, location: TextRange) {
|
||||
if !checker
|
||||
.semantic()
|
||||
|
||||
@@ -14,12 +14,9 @@ use super::helpers;
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// `datetime` objects are "naive" by default, in that they do not include
|
||||
/// timezone information. "Naive" objects are easy to understand, but ignore
|
||||
/// some aspects of reality, which can lead to subtle bugs. Timezone-aware
|
||||
/// `datetime` objects are preferred, as they represent a specific moment in
|
||||
/// time, unlike "naive" objects.
|
||||
///
|
||||
/// By providing a `tzinfo` value, a `datetime` can be made timezone-aware.
|
||||
/// timezone information. By providing a `tzinfo`, a `datetime` can be made
|
||||
/// timezone "aware". "Naive" objects are easy to understand, but ignore some
|
||||
/// aspects of reality, which can lead to subtle bugs.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
|
||||
@@ -286,6 +286,7 @@ fn generate_fix(
|
||||
range: TextRange::default(),
|
||||
})],
|
||||
value: Box::new(exc_arg.clone()),
|
||||
type_comment: None,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
|
||||
|
||||
@@ -97,10 +97,6 @@ mod tests {
|
||||
#[test_case(Rule::UnusedPrivateTypeVar, Path::new("PYI018.pyi"))]
|
||||
#[test_case(Rule::UnusedPrivateProtocol, Path::new("PYI046.py"))]
|
||||
#[test_case(Rule::UnusedPrivateProtocol, Path::new("PYI046.pyi"))]
|
||||
#[test_case(Rule::UnusedPrivateTypeAlias, Path::new("PYI047.py"))]
|
||||
#[test_case(Rule::UnusedPrivateTypeAlias, Path::new("PYI047.pyi"))]
|
||||
#[test_case(Rule::UnusedPrivateTypedDict, Path::new("PYI049.py"))]
|
||||
#[test_case(Rule::UnusedPrivateTypedDict, Path::new("PYI049.pyi"))]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use ruff_python_ast::{self as ast, Expr, Ranged};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Expr, Ranged};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::rules::flake8_pyi::helpers::traverse_union;
|
||||
@@ -42,7 +44,7 @@ impl Violation for UnnecessaryLiteralUnion {
|
||||
|
||||
/// PYI030
|
||||
pub(crate) fn unnecessary_literal_union<'a>(checker: &mut Checker, expr: &'a Expr) {
|
||||
let mut literal_exprs = Vec::new();
|
||||
let mut literal_exprs = SmallVec::<[&Box<Expr>; 1]>::new();
|
||||
|
||||
// Adds a member to `literal_exprs` if it is a `Literal` annotation
|
||||
let mut collect_literal_expr = |expr: &'a Expr, _| {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||
use ruff_python_semantic::Scope;
|
||||
use ruff_python_semantic::Binding;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
@@ -73,255 +73,68 @@ impl Violation for UnusedPrivateProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the presence of unused private `typing.TypeAlias` definitions.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// A private `typing.TypeAlias` that is defined but not used is likely a
|
||||
/// mistake, and should either be used, made public, or removed to avoid
|
||||
/// confusion.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// import typing
|
||||
///
|
||||
/// _UnusedTypeAlias: typing.TypeAlias = int
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// import typing
|
||||
///
|
||||
/// _UsedTypeAlias: typing.TypeAlias = int
|
||||
///
|
||||
///
|
||||
/// def func(arg: _UsedTypeAlias) -> _UsedTypeAlias:
|
||||
/// ...
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct UnusedPrivateTypeAlias {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Violation for UnusedPrivateTypeAlias {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnusedPrivateTypeAlias { name } = self;
|
||||
format!("Private TypeAlias `{name}` is never used")
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the presence of unused private `typing.TypedDict` definitions.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// A private `typing.TypedDict` that is defined but not used is likely a
|
||||
/// mistake, and should either be used, made public, or removed to avoid
|
||||
/// confusion.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// import typing
|
||||
///
|
||||
///
|
||||
/// class _UnusedPrivateTypedDict(typing.TypedDict):
|
||||
/// foo: list[int]
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// import typing
|
||||
///
|
||||
///
|
||||
/// class _UsedPrivateTypedDict(typing.TypedDict):
|
||||
/// foo: set[str]
|
||||
///
|
||||
///
|
||||
/// def func(arg: _UsedPrivateTypedDict) -> _UsedPrivateTypedDict:
|
||||
/// ...
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct UnusedPrivateTypedDict {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Violation for UnusedPrivateTypedDict {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnusedPrivateTypedDict { name } = self;
|
||||
format!("Private TypedDict `{name}` is never used")
|
||||
}
|
||||
}
|
||||
|
||||
/// PYI018
|
||||
pub(crate) fn unused_private_type_var(
|
||||
checker: &Checker,
|
||||
scope: &Scope,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
) {
|
||||
for binding in scope
|
||||
.binding_ids()
|
||||
.map(|binding_id| checker.semantic().binding(binding_id))
|
||||
{
|
||||
if !(binding.kind.is_assignment() && binding.is_private_declaration()) {
|
||||
continue;
|
||||
}
|
||||
if binding.is_used() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(source) = binding.source else {
|
||||
continue;
|
||||
};
|
||||
let Stmt::Assign(ast::StmtAssign { targets, value, .. }) = checker.semantic().stmts[source]
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let [Expr::Name(ast::ExprName { id, .. })] = &targets[..] else {
|
||||
continue;
|
||||
};
|
||||
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
if !checker.semantic().match_typing_expr(func, "TypeVar") {
|
||||
continue;
|
||||
}
|
||||
|
||||
diagnostics.push(Diagnostic::new(
|
||||
UnusedPrivateTypeVar {
|
||||
name: id.to_string(),
|
||||
},
|
||||
binding.range,
|
||||
));
|
||||
pub(crate) fn unused_private_type_var(checker: &Checker, binding: &Binding) -> Option<Diagnostic> {
|
||||
if !(binding.kind.is_assignment() && binding.is_private_declaration()) {
|
||||
return None;
|
||||
}
|
||||
if binding.is_used() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Some(source) = binding.source else {
|
||||
return None;
|
||||
};
|
||||
let Stmt::Assign(ast::StmtAssign { targets, value, .. }) = checker.semantic().stmts[source]
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
let [Expr::Name(ast::ExprName { id, .. })] = &targets[..] else {
|
||||
return None;
|
||||
};
|
||||
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else {
|
||||
return None;
|
||||
};
|
||||
if !checker.semantic().match_typing_expr(func, "TypeVar") {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Diagnostic::new(
|
||||
UnusedPrivateTypeVar {
|
||||
name: id.to_string(),
|
||||
},
|
||||
binding.range,
|
||||
))
|
||||
}
|
||||
|
||||
/// PYI046
|
||||
pub(crate) fn unused_private_protocol(
|
||||
checker: &Checker,
|
||||
scope: &Scope,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
) {
|
||||
for binding in scope
|
||||
.binding_ids()
|
||||
.map(|binding_id| checker.semantic().binding(binding_id))
|
||||
{
|
||||
if !(binding.kind.is_class_definition() && binding.is_private_declaration()) {
|
||||
continue;
|
||||
}
|
||||
if binding.is_used() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(source) = binding.source else {
|
||||
continue;
|
||||
};
|
||||
let Stmt::ClassDef(ast::StmtClassDef { name, bases, .. }) =
|
||||
checker.semantic().stmts[source]
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if !bases
|
||||
.iter()
|
||||
.any(|base| checker.semantic().match_typing_expr(base, "Protocol"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
diagnostics.push(Diagnostic::new(
|
||||
UnusedPrivateProtocol {
|
||||
name: name.to_string(),
|
||||
},
|
||||
binding.range,
|
||||
));
|
||||
pub(crate) fn unused_private_protocol(checker: &Checker, binding: &Binding) -> Option<Diagnostic> {
|
||||
if !(binding.kind.is_class_definition() && binding.is_private_declaration()) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
/// PYI047
|
||||
pub(crate) fn unused_private_type_alias(
|
||||
checker: &Checker,
|
||||
scope: &Scope,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
) {
|
||||
for binding in scope
|
||||
.binding_ids()
|
||||
.map(|binding_id| checker.semantic().binding(binding_id))
|
||||
{
|
||||
if !(binding.kind.is_assignment() && binding.is_private_declaration()) {
|
||||
continue;
|
||||
}
|
||||
if binding.is_used() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(source) = binding.source else {
|
||||
continue;
|
||||
};
|
||||
let Stmt::AnnAssign(ast::StmtAnnAssign {
|
||||
target, annotation, ..
|
||||
}) = checker.semantic().stmts[source]
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let Some(ast::ExprName { id, .. }) = target.as_name_expr() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if !checker
|
||||
.semantic()
|
||||
.match_typing_expr(annotation, "TypeAlias")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
diagnostics.push(Diagnostic::new(
|
||||
UnusedPrivateTypeAlias {
|
||||
name: id.to_string(),
|
||||
},
|
||||
binding.range,
|
||||
));
|
||||
if binding.is_used() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
/// PYI049
|
||||
pub(crate) fn unused_private_typed_dict(
|
||||
checker: &Checker,
|
||||
scope: &Scope,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
) {
|
||||
for binding in scope
|
||||
.binding_ids()
|
||||
.map(|binding_id| checker.semantic().binding(binding_id))
|
||||
let Some(source) = binding.source else {
|
||||
return None;
|
||||
};
|
||||
let Stmt::ClassDef(ast::StmtClassDef { name, bases, .. }) = checker.semantic().stmts[source]
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if !bases
|
||||
.iter()
|
||||
.any(|base| checker.semantic().match_typing_expr(base, "Protocol"))
|
||||
{
|
||||
if !(binding.kind.is_class_definition() && binding.is_private_declaration()) {
|
||||
continue;
|
||||
}
|
||||
if binding.is_used() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(source) = binding.source else {
|
||||
continue;
|
||||
};
|
||||
let Stmt::ClassDef(ast::StmtClassDef { name, bases, .. }) =
|
||||
checker.semantic().stmts[source]
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if !bases
|
||||
.iter()
|
||||
.any(|base| checker.semantic().match_typing_expr(base, "TypedDict"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
diagnostics.push(Diagnostic::new(
|
||||
UnusedPrivateTypedDict {
|
||||
name: name.to_string(),
|
||||
},
|
||||
binding.range,
|
||||
));
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Diagnostic::new(
|
||||
UnusedPrivateProtocol {
|
||||
name: name.to_string(),
|
||||
},
|
||||
binding.range,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
PYI047.pyi:6:1: PYI047 Private TypeAlias `_UnusedPrivateTypeAlias` is never used
|
||||
|
|
||||
6 | _UnusedPrivateTypeAlias: TypeAlias = int | None
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ PYI047
|
||||
7 | _T: typing.TypeAlias = str
|
||||
|
|
||||
|
||||
PYI047.pyi:7:1: PYI047 Private TypeAlias `_T` is never used
|
||||
|
|
||||
6 | _UnusedPrivateTypeAlias: TypeAlias = int | None
|
||||
7 | _T: typing.TypeAlias = str
|
||||
| ^^ PYI047
|
||||
8 |
|
||||
9 | # OK
|
||||
|
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
PYI049.pyi:6:7: PYI049 Private TypedDict `_UnusedTypedDict` is never used
|
||||
|
|
||||
6 | class _UnusedTypedDict(TypedDict):
|
||||
| ^^^^^^^^^^^^^^^^ PYI049
|
||||
7 | foo: str
|
||||
|
|
||||
|
||||
PYI049.pyi:10:7: PYI049 Private TypedDict `_UnusedTypedDict2` is never used
|
||||
|
|
||||
10 | class _UnusedTypedDict2(typing.TypedDict):
|
||||
| ^^^^^^^^^^^^^^^^^ PYI049
|
||||
11 | bar: int
|
||||
|
|
||||
|
||||
|
||||
@@ -69,34 +69,6 @@ impl Violation for PytestCompositeAssertion {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `assert` statements in `except` clauses.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// When testing for exceptions, `pytest.raises()` should be used instead of
|
||||
/// `assert` statements in `except` clauses, as it's more explicit and
|
||||
/// idiomatic. Further, `pytest.raises()` will fail if the exception is _not_
|
||||
/// raised, unlike the `assert` statement.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// def test_foo():
|
||||
/// try:
|
||||
/// 1 / 0
|
||||
/// except ZeroDivisionError as e:
|
||||
/// assert e.args
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def test_foo():
|
||||
/// with pytest.raises(ZeroDivisionError) as exc_info:
|
||||
/// 1 / 0
|
||||
/// assert exc_info.value.args
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: `pytest.raises`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-raises)
|
||||
#[violation]
|
||||
pub struct PytestAssertInExcept {
|
||||
name: String,
|
||||
|
||||
@@ -45,7 +45,7 @@ use super::helpers::{is_empty_or_null_string, is_pytest_fail};
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: `pytest.fail`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-fail)
|
||||
/// - [API Reference: `pytest.fail`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-fail)
|
||||
#[violation]
|
||||
pub struct PytestFailWithoutMessage;
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ use super::helpers::{
|
||||
/// - `flake8-pytest-style.fixture-parentheses`
|
||||
///
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: API Reference: Fixtures](https://docs.pytest.org/en/latest/reference/reference.html#fixtures-api)
|
||||
/// - [API Reference: Fixtures](https://docs.pytest.org/en/latest/reference/reference.html#fixtures-api)
|
||||
#[violation]
|
||||
pub struct PytestFixtureIncorrectParenthesesStyle {
|
||||
expected: Parentheses,
|
||||
@@ -129,47 +129,6 @@ impl Violation for PytestIncorrectFixtureNameUnderscore {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `pytest` test functions that should be decorated with
|
||||
/// `@pytest.mark.usefixtures`.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// In `pytest`, fixture injection is used to activate fixtures in a test
|
||||
/// function.
|
||||
///
|
||||
/// Fixtures can be injected either by passing them as parameters to the test
|
||||
/// function, or by using the `@pytest.mark.usefixtures` decorator.
|
||||
///
|
||||
/// If the test function depends on the fixture being activated, but does not
|
||||
/// use it in the test body or otherwise rely on its return value, prefer
|
||||
/// the `@pytest.mark.usefixtures` decorator, to make the dependency explicit
|
||||
/// and avoid the confusion caused by unused arguments.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// @pytest.fixture
|
||||
/// def _patch_something():
|
||||
/// ...
|
||||
///
|
||||
///
|
||||
/// def test_foo(_patch_something):
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// @pytest.fixture
|
||||
/// def _patch_something():
|
||||
/// ...
|
||||
///
|
||||
///
|
||||
/// @pytest.mark.usefixtures("_patch_something")
|
||||
/// def test_foo():
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: `pytest.mark.usefixtures`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-mark-usefixtures)
|
||||
#[violation]
|
||||
pub struct PytestFixtureParamWithoutValue {
|
||||
name: String,
|
||||
@@ -217,7 +176,7 @@ impl Violation for PytestFixtureParamWithoutValue {
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: `yield_fixture` functions](https://docs.pytest.org/en/latest/yieldfixture.html)
|
||||
/// - [`yield_fixture` functions](https://docs.pytest.org/en/latest/yieldfixture.html)
|
||||
#[violation]
|
||||
pub struct PytestDeprecatedYieldFixture;
|
||||
|
||||
@@ -228,49 +187,6 @@ impl Violation for PytestDeprecatedYieldFixture {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for unnecessary `request.addfinalizer` usages in `pytest` fixtures.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// `pytest` offers two ways to perform cleanup in fixture code. The first is
|
||||
/// sequential (via the `yield` statement), the second callback-based (via
|
||||
/// `request.addfinalizer`).
|
||||
///
|
||||
/// The sequential approach is more readable and should be preferred, unless
|
||||
/// the fixture uses the "factory as fixture" pattern.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// @pytest.fixture()
|
||||
/// def my_fixture(request):
|
||||
/// resource = acquire_resource()
|
||||
/// request.addfinalizer(resource.release)
|
||||
/// return resource
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// @pytest.fixture()
|
||||
/// def my_fixture():
|
||||
/// resource = acquire_resource()
|
||||
/// yield resource
|
||||
/// resource.release()
|
||||
///
|
||||
///
|
||||
/// # "factory-as-fixture" pattern
|
||||
/// @pytest.fixture()
|
||||
/// def my_factory(request):
|
||||
/// def create_resource(arg):
|
||||
/// resource = acquire_resource(arg)
|
||||
/// request.addfinalizer(resource.release)
|
||||
/// return resource
|
||||
///
|
||||
/// return create_resource
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: Adding finalizers directly](https://docs.pytest.org/en/latest/how-to/fixtures.html#adding-finalizers-directly)
|
||||
/// - [`pytest` documentation: Factories as fixtures](https://docs.pytest.org/en/latest/how-to/fixtures.html#factories-as-fixtures)
|
||||
#[violation]
|
||||
pub struct PytestFixtureFinalizerCallback;
|
||||
|
||||
@@ -280,39 +196,7 @@ impl Violation for PytestFixtureFinalizerCallback {
|
||||
format!("Use `yield` instead of `request.addfinalizer`")
|
||||
}
|
||||
}
|
||||
/// ## What it does
|
||||
/// Checks for unnecessary `yield` expressions in `pytest` fixtures.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// In `pytest` fixtures, the `yield` expression should only be used for fixtures
|
||||
/// that include teardown code, to clean up the fixture after the test function
|
||||
/// has finished executing.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// @pytest.fixture()
|
||||
/// def my_fixture():
|
||||
/// resource = acquire_resource()
|
||||
/// yield resource
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// @pytest.fixture()
|
||||
/// def my_fixture_with_teardown():
|
||||
/// resource = acquire_resource()
|
||||
/// yield resource
|
||||
/// resource.release()
|
||||
///
|
||||
///
|
||||
/// @pytest.fixture()
|
||||
/// def my_fixture_without_teardown():
|
||||
/// resource = acquire_resource()
|
||||
/// return resource
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: Teardown/Cleanup](https://docs.pytest.org/en/latest/how-to/fixtures.html#teardown-cleanup-aka-fixture-finalization)
|
||||
|
||||
#[violation]
|
||||
pub struct PytestUselessYieldFixture {
|
||||
name: String,
|
||||
|
||||
@@ -9,37 +9,6 @@ use crate::registry::{AsRule, Rule};
|
||||
|
||||
use super::helpers::get_mark_decorators;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for parameter-free `@pytest.mark.<marker>()` decorators with or
|
||||
/// without parentheses, depending on the `flake8-pytest-style.mark-parentheses`
|
||||
/// setting.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// If a `@pytest.mark.<marker>()` doesn't take any arguments, the parentheses are
|
||||
/// optional.
|
||||
///
|
||||
/// Either removing those unnecessary parentheses _or_ requiring them for all
|
||||
/// fixtures is fine, but it's best to be consistent.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// @pytest.mark.foo
|
||||
/// def test_something():
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// @pytest.mark.foo()
|
||||
/// def test_something():
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// ## Options
|
||||
/// - `flake8-pytest-style.mark-parentheses`
|
||||
///
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: Marks](https://docs.pytest.org/en/latest/reference/reference.html#marks)
|
||||
#[violation]
|
||||
pub struct PytestIncorrectMarkParenthesesStyle {
|
||||
mark_name: String,
|
||||
|
||||
@@ -66,7 +66,7 @@ impl Violation for PytestRaisesTooBroad {
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: `pytest.raises`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-raises)
|
||||
/// - [API Reference: `pytest.raises`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-raises)
|
||||
#[violation]
|
||||
pub struct PytestRaisesWithoutException;
|
||||
|
||||
|
||||
@@ -566,6 +566,7 @@ fn ternary(target_var: &Expr, body_value: &Expr, test: &Expr, orelse_value: &Exp
|
||||
let node1 = ast::StmtAssign {
|
||||
targets: vec![target_var.clone()],
|
||||
value: Box::new(node.into()),
|
||||
type_comment: None,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
node1.into()
|
||||
@@ -956,6 +957,7 @@ pub(crate) fn use_dict_get_with_default(checker: &mut Checker, stmt_if: &StmtIf)
|
||||
let node5 = ast::StmtAssign {
|
||||
targets: vec![node4],
|
||||
value: Box::new(node3.into()),
|
||||
type_comment: None,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let contents = checker.generator().stmt(&node5.into());
|
||||
|
||||
@@ -82,25 +82,13 @@ fn key_in_dict(
|
||||
return;
|
||||
}
|
||||
|
||||
let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func.as_ref() else {
|
||||
let Expr::Attribute(ast::ExprAttribute { attr, .. }) = func.as_ref() else {
|
||||
return;
|
||||
};
|
||||
if attr != "keys" {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore `self.keys()`, which will almost certainly be intentional, as in:
|
||||
// ```python
|
||||
// def __contains__(self, key: object) -> bool:
|
||||
// return key in self.keys()
|
||||
// ```
|
||||
if value
|
||||
.as_name_expr()
|
||||
.map_or(false, |name| matches!(name.id.as_str(), "self"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Slice exact content to preserve formatting.
|
||||
let left_content = checker.locator().slice(left.range());
|
||||
let Ok(value_content) =
|
||||
|
||||
@@ -105,32 +105,13 @@ fn match_exit_stack(semantic: &SemanticModel) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Return `true` if `func` is the builtin `open` or `pathlib.Path(...).open`.
|
||||
fn is_open(checker: &mut Checker, func: &Expr) -> bool {
|
||||
match func {
|
||||
// pathlib.Path(...).open()
|
||||
Expr::Attribute(ast::ExprAttribute { attr, value, .. }) if attr.as_str() == "open" => {
|
||||
match value.as_ref() {
|
||||
Expr::Call(ast::ExprCall { func, .. }) => checker
|
||||
.semantic()
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| {
|
||||
matches!(call_path.as_slice(), ["pathlib", "Path"])
|
||||
}),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
// open(...)
|
||||
Expr::Name(ast::ExprName { id, .. }) => {
|
||||
id.as_str() == "open" && checker.semantic().is_builtin("open")
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// SIM115
|
||||
pub(crate) fn open_file_with_context_handler(checker: &mut Checker, func: &Expr) {
|
||||
if !is_open(checker, func) {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = func else {
|
||||
return;
|
||||
};
|
||||
|
||||
if id.as_str() != "open" {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -139,6 +120,10 @@ pub(crate) fn open_file_with_context_handler(checker: &mut Checker, func: &Expr)
|
||||
return;
|
||||
}
|
||||
|
||||
if !checker.semantic().is_builtin("open") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ex) `with contextlib.ExitStack() as exit_stack: ...`
|
||||
if match_exit_stack(checker.semantic()) {
|
||||
return;
|
||||
|
||||
@@ -1,63 +1,23 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_simplify/mod.rs
|
||||
---
|
||||
SIM115.py:8:5: SIM115 Use context handler for opening files
|
||||
|
|
||||
7 | # SIM115
|
||||
8 | f = open("foo.txt")
|
||||
| ^^^^ SIM115
|
||||
9 | f = Path("foo.txt").open()
|
||||
10 | f = pathlib.Path("foo.txt").open()
|
||||
|
|
||||
SIM115.py:4:5: SIM115 Use context handler for opening files
|
||||
|
|
||||
3 | # SIM115
|
||||
4 | f = open("foo.txt")
|
||||
| ^^^^ SIM115
|
||||
5 | data = f.read()
|
||||
6 | f.close()
|
||||
|
|
||||
|
||||
SIM115.py:9:5: SIM115 Use context handler for opening files
|
||||
SIM115.py:31:9: SIM115 Use context handler for opening files
|
||||
|
|
||||
7 | # SIM115
|
||||
8 | f = open("foo.txt")
|
||||
9 | f = Path("foo.txt").open()
|
||||
| ^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
10 | f = pathlib.Path("foo.txt").open()
|
||||
11 | f = pl.Path("foo.txt").open()
|
||||
|
|
||||
|
||||
SIM115.py:10:5: SIM115 Use context handler for opening files
|
||||
|
|
||||
8 | f = open("foo.txt")
|
||||
9 | f = Path("foo.txt").open()
|
||||
10 | f = pathlib.Path("foo.txt").open()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
11 | f = pl.Path("foo.txt").open()
|
||||
12 | f = P("foo.txt").open()
|
||||
|
|
||||
|
||||
SIM115.py:11:5: SIM115 Use context handler for opening files
|
||||
|
|
||||
9 | f = Path("foo.txt").open()
|
||||
10 | f = pathlib.Path("foo.txt").open()
|
||||
11 | f = pl.Path("foo.txt").open()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
12 | f = P("foo.txt").open()
|
||||
13 | data = f.read()
|
||||
|
|
||||
|
||||
SIM115.py:12:5: SIM115 Use context handler for opening files
|
||||
|
|
||||
10 | f = pathlib.Path("foo.txt").open()
|
||||
11 | f = pl.Path("foo.txt").open()
|
||||
12 | f = P("foo.txt").open()
|
||||
| ^^^^^^^^^^^^^^^^^ SIM115
|
||||
13 | data = f.read()
|
||||
14 | f.close()
|
||||
|
|
||||
|
||||
SIM115.py:39:9: SIM115 Use context handler for opening files
|
||||
|
|
||||
37 | # SIM115
|
||||
38 | with contextlib.ExitStack():
|
||||
39 | f = open("filename")
|
||||
29 | # SIM115
|
||||
30 | with contextlib.ExitStack():
|
||||
31 | f = open("filename")
|
||||
| ^^^^ SIM115
|
||||
40 |
|
||||
41 | # OK
|
||||
32 |
|
||||
33 | # OK
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -265,7 +265,6 @@ SIM118.py:30:8: SIM118 [*] Use `k in obj` instead of `k in obj.keys()`
|
||||
30 |+(k for k in obj) # SIM118
|
||||
31 31 |
|
||||
32 32 | key in (obj or {}).keys() # SIM118
|
||||
33 33 |
|
||||
|
||||
SIM118.py:32:1: SIM118 [*] Use `key in (obj or {})` instead of `key in (obj or {}).keys()`
|
||||
|
|
||||
@@ -273,8 +272,6 @@ SIM118.py:32:1: SIM118 [*] Use `key in (obj or {})` instead of `key in (obj or {
|
||||
31 |
|
||||
32 | key in (obj or {}).keys() # SIM118
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118
|
||||
33 |
|
||||
34 | from typing import KeysView
|
||||
|
|
||||
= help: Convert to `key in (obj or {})`
|
||||
|
||||
@@ -284,8 +281,5 @@ SIM118.py:32:1: SIM118 [*] Use `key in (obj or {})` instead of `key in (obj or {
|
||||
31 31 |
|
||||
32 |-key in (obj or {}).keys() # SIM118
|
||||
32 |+key in (obj or {}) # SIM118
|
||||
33 33 |
|
||||
34 34 | from typing import KeysView
|
||||
35 35 |
|
||||
|
||||
|
||||
|
||||
@@ -15,13 +15,10 @@ mod tests {
|
||||
use crate::test::{test_path, test_snippet};
|
||||
use crate::{assert_messages, settings};
|
||||
|
||||
#[test_case(Rule::EmptyTypeCheckingBlock, Path::new("TCH005.py"))]
|
||||
#[test_case(Rule::TypingOnlyFirstPartyImport, Path::new("TCH001.py"))]
|
||||
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("TCH002.py"))]
|
||||
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("TCH003.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_1.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_10.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_11.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_12.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_13.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_14.pyi"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_2.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_3.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_4.py"))]
|
||||
@@ -30,10 +27,12 @@ mod tests {
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_7.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_8.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_9.py"))]
|
||||
#[test_case(Rule::TypingOnlyFirstPartyImport, Path::new("TCH001.py"))]
|
||||
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("TCH003.py"))]
|
||||
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("snapshot.py"))]
|
||||
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("TCH002.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_10.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_11.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_12.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_13.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_14.pyi"))]
|
||||
#[test_case(Rule::EmptyTypeCheckingBlock, Path::new("TCH005.py"))]
|
||||
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("strict.py"))]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
|
||||
---
|
||||
|
||||
@@ -317,7 +317,10 @@ pub(crate) fn unused_arguments(
|
||||
scope: &Scope,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
) {
|
||||
let Some(parent) = &checker.semantic().first_non_type_parent_scope(scope) else {
|
||||
let Some(parent) = scope
|
||||
.parent
|
||||
.map(|scope_id| &checker.semantic().scopes[scope_id])
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ use ruff_text_size::TextRange;
|
||||
fn to_formatted_value_expr(inner: &Expr) -> Expr {
|
||||
let node = ast::ExprFormattedValue {
|
||||
value: Box::new(inner.clone()),
|
||||
debug_text: None,
|
||||
conversion: ConversionFlag::None,
|
||||
format_spec: None,
|
||||
range: TextRange::default(),
|
||||
|
||||
@@ -9,24 +9,6 @@ use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
use crate::rules::flynt::helpers;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `str#join` calls that can be replaced with f-strings.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// f-strings are more readable and generally preferred over `str#join` calls.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// " ".join((foo, bar))
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// f"{foo} {bar}"
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: f-strings](https://docs.python.org/3/reference/lexical_analysis.html#f-strings)
|
||||
#[violation]
|
||||
pub struct StaticJoinToFString {
|
||||
expr: String,
|
||||
|
||||
@@ -314,8 +314,6 @@ mod tests {
|
||||
|
||||
#[test_case(Path::new("add_newline_before_comments.py"))]
|
||||
#[test_case(Path::new("as_imports_comments.py"))]
|
||||
#[test_case(Path::new("bom_sorted.py"))]
|
||||
#[test_case(Path::new("bom_unsorted.py"))]
|
||||
#[test_case(Path::new("combine_as_imports.py"))]
|
||||
#[test_case(Path::new("combine_import_from.py"))]
|
||||
#[test_case(Path::new("comments.py"))]
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/isort/mod.rs
|
||||
---
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/isort/mod.rs
|
||||
---
|
||||
bom_unsorted.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
||||
|
|
||||
1 | import foo
|
||||
| _^
|
||||
2 | | import bar
|
||||
|
|
||||
= help: Organize imports
|
||||
|
||||
ℹ Fix
|
||||
1 |-import foo
|
||||
2 |-import bar
|
||||
1 |+import bar
|
||||
2 |+import foo
|
||||
|
||||
|
||||
@@ -9,6 +9,21 @@ use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions};
|
||||
|
||||
use crate::settings::types::IdentifierPattern;
|
||||
|
||||
const IGNORE_NAMES: [&str; 12] = [
|
||||
"setUp",
|
||||
"tearDown",
|
||||
"setUpClass",
|
||||
"tearDownClass",
|
||||
"setUpModule",
|
||||
"tearDownModule",
|
||||
"asyncSetUp",
|
||||
"asyncTearDown",
|
||||
"setUpTestData",
|
||||
"failureException",
|
||||
"longMessage",
|
||||
"maxDiff",
|
||||
];
|
||||
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions,
|
||||
)]
|
||||
@@ -28,14 +43,6 @@ pub struct Options {
|
||||
)]
|
||||
/// A list of names (or patterns) to ignore when considering `pep8-naming` violations.
|
||||
pub ignore_names: Option<Vec<String>>,
|
||||
#[option(
|
||||
default = r#"[]"#,
|
||||
value_type = "list[str]",
|
||||
example = r#"extend-ignore-names = ["callMethod"]"#
|
||||
)]
|
||||
/// Additional names (or patterns) to ignore when considering `pep8-naming` violations,
|
||||
/// in addition to those included in `ignore-names`.
|
||||
pub extend_ignore_names: Option<Vec<String>>,
|
||||
#[option(
|
||||
default = r#"[]"#,
|
||||
value_type = "list[str]",
|
||||
@@ -75,29 +82,12 @@ pub struct Settings {
|
||||
pub staticmethod_decorators: Vec<String>,
|
||||
}
|
||||
|
||||
fn default_ignore_names() -> Vec<String> {
|
||||
vec![
|
||||
"setUp".to_string(),
|
||||
"tearDown".to_string(),
|
||||
"setUpClass".to_string(),
|
||||
"tearDownClass".to_string(),
|
||||
"setUpModule".to_string(),
|
||||
"tearDownModule".to_string(),
|
||||
"asyncSetUp".to_string(),
|
||||
"asyncTearDown".to_string(),
|
||||
"setUpTestData".to_string(),
|
||||
"failureException".to_string(),
|
||||
"longMessage".to_string(),
|
||||
"maxDiff".to_string(),
|
||||
]
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ignore_names: default_ignore_names()
|
||||
.into_iter()
|
||||
.map(|name| IdentifierPattern::new(&name).unwrap())
|
||||
ignore_names: IGNORE_NAMES
|
||||
.iter()
|
||||
.map(|name| IdentifierPattern::new(name).unwrap())
|
||||
.collect(),
|
||||
classmethod_decorators: Vec::new(),
|
||||
staticmethod_decorators: Vec::new(),
|
||||
@@ -110,13 +100,18 @@ impl TryFrom<Options> for Settings {
|
||||
|
||||
fn try_from(options: Options) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
ignore_names: options
|
||||
.ignore_names
|
||||
.unwrap_or_else(default_ignore_names)
|
||||
.into_iter()
|
||||
.chain(options.extend_ignore_names.unwrap_or_default().into_iter())
|
||||
.map(|name| IdentifierPattern::new(&name).map_err(SettingsError::InvalidIgnoreName))
|
||||
.collect::<Result<Vec<_>, Self::Error>>()?,
|
||||
ignore_names: match options.ignore_names {
|
||||
Some(names) => names
|
||||
.into_iter()
|
||||
.map(|name| {
|
||||
IdentifierPattern::new(&name).map_err(SettingsError::InvalidIgnoreName)
|
||||
})
|
||||
.collect::<Result<Vec<_>, Self::Error>>()?,
|
||||
None => IGNORE_NAMES
|
||||
.into_iter()
|
||||
.map(|name| IdentifierPattern::new(name).unwrap())
|
||||
.collect(),
|
||||
},
|
||||
classmethod_decorators: options.classmethod_decorators.unwrap_or_default(),
|
||||
staticmethod_decorators: options.staticmethod_decorators.unwrap_or_default(),
|
||||
})
|
||||
@@ -157,7 +152,6 @@ impl From<Settings> for Options {
|
||||
.map(|pattern| pattern.as_str().to_owned())
|
||||
.collect(),
|
||||
),
|
||||
extend_ignore_names: None,
|
||||
classmethod_decorators: Some(settings.classmethod_decorators),
|
||||
staticmethod_decorators: Some(settings.staticmethod_decorators),
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::comparable::ComparableExpr;
|
||||
use ruff_python_ast::helpers::any_over_expr;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -130,13 +129,6 @@ pub(crate) fn manual_list_comprehension(checker: &mut Checker, target: &Expr, bo
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid, e.g., `for x in y: filtered.append(filtered[-1] * 2)`.
|
||||
if any_over_expr(arg, &|expr| {
|
||||
ComparableExpr::from(expr) == ComparableExpr::from(value)
|
||||
}) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid if the value is used in the conditional test, e.g.,
|
||||
//
|
||||
// ```python
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::any_over_expr;
|
||||
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
@@ -88,7 +89,7 @@ pub(crate) fn manual_list_copy(checker: &mut Checker, target: &Expr, body: &[Stm
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid, e.g., `for x in y: filtered[x].append(x)`.
|
||||
// Avoid, e.g., `for x in y: filtered[x].append(x * x)`.
|
||||
if any_over_expr(value, &|expr| {
|
||||
expr.as_name_expr().map_or(false, |expr| expr.id == *id)
|
||||
}) {
|
||||
|
||||
@@ -67,15 +67,14 @@ pub(crate) fn try_except_in_loop(checker: &mut Checker, body: &[Stmt]) {
|
||||
return;
|
||||
}
|
||||
|
||||
let [Stmt::Try(ast::StmtTry { handlers, .. })] = body else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(handler) = handlers.first() else {
|
||||
return;
|
||||
};
|
||||
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(TryExceptInLoop, handler.range()));
|
||||
checker.diagnostics.extend(body.iter().filter_map(|stmt| {
|
||||
if let Stmt::Try(ast::StmtTry { handlers, .. }) = stmt {
|
||||
handlers
|
||||
.iter()
|
||||
.next()
|
||||
.map(|handler| Diagnostic::new(TryExceptInLoop, handler.range()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/perflint/mod.rs
|
||||
---
|
||||
PERF203.py:5:5: PERF203 `try`-`except` within a loop incurs performance overhead
|
||||
PERF203.py:4:5: PERF203 `try`-`except` within a loop incurs performance overhead
|
||||
|
|
||||
3 | try:
|
||||
4 | print(f"{i}")
|
||||
5 | except:
|
||||
2 | try: # PERF203
|
||||
3 | print(f"{i}")
|
||||
4 | except:
|
||||
| _____^
|
||||
6 | | print("error")
|
||||
5 | | print("error")
|
||||
| |______________________^ PERF203
|
||||
7 |
|
||||
8 | # OK
|
||||
6 |
|
||||
7 | try:
|
||||
|
|
||||
|
||||
PERF203.py:17:5: PERF203 `try`-`except` within a loop incurs performance overhead
|
||||
|
|
||||
15 | try:
|
||||
16 | print(f"{i}")
|
||||
17 | except:
|
||||
| _____^
|
||||
18 | | print("error")
|
||||
| |______________________^ PERF203
|
||||
19 |
|
||||
20 | i += 1
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -70,7 +70,6 @@ mod tests {
|
||||
#[test_case(Rule::IndentationWithInvalidMultiple, Path::new("E11.py"))]
|
||||
#[test_case(Rule::IndentationWithInvalidMultipleComment, Path::new("E11.py"))]
|
||||
#[test_case(Rule::MultipleLeadingHashesForBlockComment, Path::new("E26.py"))]
|
||||
#[test_case(Rule::MultipleSpacesAfterComma, Path::new("E24.py"))]
|
||||
#[test_case(Rule::MultipleSpacesAfterKeyword, Path::new("E27.py"))]
|
||||
#[test_case(Rule::MultipleSpacesAfterOperator, Path::new("E22.py"))]
|
||||
#[test_case(Rule::MultipleSpacesBeforeKeyword, Path::new("E27.py"))]
|
||||
@@ -81,7 +80,6 @@ mod tests {
|
||||
#[test_case(Rule::NoSpaceAfterBlockComment, Path::new("E26.py"))]
|
||||
#[test_case(Rule::NoSpaceAfterInlineComment, Path::new("E26.py"))]
|
||||
#[test_case(Rule::OverIndented, Path::new("E11.py"))]
|
||||
#[test_case(Rule::TabAfterComma, Path::new("E24.py"))]
|
||||
#[test_case(Rule::TabAfterKeyword, Path::new("E27.py"))]
|
||||
#[test_case(Rule::TabAfterOperator, Path::new("E22.py"))]
|
||||
#[test_case(Rule::TabBeforeKeyword, Path::new("E27.py"))]
|
||||
|
||||
@@ -229,6 +229,7 @@ fn function(
|
||||
decorator_list: vec![],
|
||||
returns: Some(Box::new(return_type)),
|
||||
type_params: vec![],
|
||||
type_comment: None,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
return generator.stmt(&func);
|
||||
@@ -241,6 +242,7 @@ fn function(
|
||||
decorator_list: vec![],
|
||||
returns: None,
|
||||
type_params: vec![],
|
||||
type_comment: None,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
generator.stmt(&func)
|
||||
|
||||
@@ -5,26 +5,6 @@ use ruff_python_parser::TokenKind;
|
||||
use crate::checkers::logical_lines::LogicalLinesContext;
|
||||
use crate::rules::pycodestyle::rules::logical_lines::LogicalLine;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for missing whitespace around all operators.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// According to [PEP 8], there should be one space before and after all
|
||||
/// operators.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// if number==42:
|
||||
/// print('you have found the meaning of life')
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// if number == 42:
|
||||
/// print('you have found the meaning of life')
|
||||
/// ```
|
||||
///
|
||||
/// [PEP 8]: https://peps.python.org/pep-0008/#pet-peeves
|
||||
// E225
|
||||
#[violation]
|
||||
pub struct MissingWhitespaceAroundOperator;
|
||||
@@ -36,24 +16,6 @@ impl Violation for MissingWhitespaceAroundOperator {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for missing whitespace arithmetic operators.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// According to [PEP 8], there should be one space before and after an
|
||||
/// arithmetic operator (+, -, /, and *).
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// number = 40+2
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// number = 40 + 2
|
||||
/// ```
|
||||
///
|
||||
/// [PEP 8]: https://peps.python.org/pep-0008/#pet-peeves
|
||||
// E226
|
||||
#[violation]
|
||||
pub struct MissingWhitespaceAroundArithmeticOperator;
|
||||
@@ -65,24 +27,6 @@ impl Violation for MissingWhitespaceAroundArithmeticOperator {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for missing whitespace around bitwise and shift operators.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// According to [PEP 8], there should be one space before and after bitwise and
|
||||
/// shift operators (<<, >>, &, |, ^).
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// x = 128<<1
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// x = 128 << 1
|
||||
/// ```
|
||||
///
|
||||
/// [PEP 8]: https://peps.python.org/pep-0008/#pet-peeves
|
||||
// E227
|
||||
#[violation]
|
||||
pub struct MissingWhitespaceAroundBitwiseOrShiftOperator;
|
||||
@@ -94,24 +38,6 @@ impl Violation for MissingWhitespaceAroundBitwiseOrShiftOperator {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for missing whitespace around the modulo operator.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// According to [PEP 8], the modulo operator (%) should have whitespace on
|
||||
/// either side of it.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// remainder = 10%2
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// remainder = 10 % 2
|
||||
/// ```
|
||||
///
|
||||
/// [PEP 8]: https://peps.python.org/pep-0008/#other-recommendations
|
||||
// E228
|
||||
#[violation]
|
||||
pub struct MissingWhitespaceAroundModuloOperator;
|
||||
|
||||
@@ -120,57 +120,6 @@ impl Violation for MultipleSpacesAfterOperator {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for extraneous tabs after a comma.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Commas should be followed by one space, never tabs.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// a = 4,\t5
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// a = 4, 3
|
||||
/// ```
|
||||
///
|
||||
#[violation]
|
||||
pub struct TabAfterComma;
|
||||
|
||||
impl Violation for TabAfterComma {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Tab after comma")
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for extraneous whitespace after a comma.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// According to the `black` code style, commas should be followed by a single space.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// a = 4, 5
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// a = 4, 5
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct MultipleSpacesAfterComma;
|
||||
|
||||
impl Violation for MultipleSpacesAfterComma {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Multiple spaces after comma")
|
||||
}
|
||||
}
|
||||
|
||||
/// E221, E222, E223, E224
|
||||
pub(crate) fn space_around_operator(line: &LogicalLine, context: &mut LogicalLinesContext) {
|
||||
let mut after_operator = false;
|
||||
@@ -212,23 +161,6 @@ pub(crate) fn space_around_operator(line: &LogicalLine, context: &mut LogicalLin
|
||||
}
|
||||
}
|
||||
|
||||
/// E241, E242
|
||||
pub(crate) fn space_after_comma(line: &LogicalLine, context: &mut LogicalLinesContext) {
|
||||
for token in line.tokens() {
|
||||
if matches!(token.kind(), TokenKind::Comma) {
|
||||
match line.trailing_whitespace(token) {
|
||||
(Whitespace::Tab, len) => {
|
||||
context.push(TabAfterComma, TextRange::at(token.end(), len));
|
||||
}
|
||||
(Whitespace::Many, len) => {
|
||||
context.push(MultipleSpacesAfterComma, TextRange::at(token.end(), len));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fn is_operator_token(token: TokenKind) -> bool {
|
||||
matches!(
|
||||
token,
|
||||
|
||||
@@ -8,11 +8,11 @@ use crate::checkers::logical_lines::LogicalLinesContext;
|
||||
use crate::rules::pycodestyle::rules::logical_lines::LogicalLine;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for extraneous whitespace immediately preceding an open parenthesis
|
||||
/// Checks for extraneous whitespace immediately after an open parenthesis
|
||||
/// or bracket.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// According to [PEP 8], open parentheses and brackets should not be preceded
|
||||
/// According to [PEP 8], open parentheses and brackets should not be followed
|
||||
/// by any trailing whitespace.
|
||||
///
|
||||
/// ## Example
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pycodestyle/mod.rs
|
||||
---
|
||||
E24.py:2:8: E241 Multiple spaces after comma
|
||||
|
|
||||
1 | #: E241
|
||||
2 | a = (1, 2)
|
||||
| ^^ E241
|
||||
3 | #: Okay
|
||||
4 | b = (1, 20)
|
||||
|
|
||||
|
||||
E24.py:11:18: E241 Multiple spaces after comma
|
||||
|
|
||||
9 | #: E241 E241 E241
|
||||
10 | # issue 135
|
||||
11 | more_spaces = [a, b,
|
||||
| ^^^^ E241
|
||||
12 | ef, +h,
|
||||
13 | c, -d]
|
||||
|
|
||||
|
||||
E24.py:12:19: E241 Multiple spaces after comma
|
||||
|
|
||||
10 | # issue 135
|
||||
11 | more_spaces = [a, b,
|
||||
12 | ef, +h,
|
||||
| ^^ E241
|
||||
13 | c, -d]
|
||||
|
|
||||
|
||||
E24.py:13:18: E241 Multiple spaces after comma
|
||||
|
|
||||
11 | more_spaces = [a, b,
|
||||
12 | ef, +h,
|
||||
13 | c, -d]
|
||||
| ^^^ E241
|
||||
|
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pycodestyle/mod.rs
|
||||
---
|
||||
E24.py:6:8: E242 Tab after comma
|
||||
|
|
||||
4 | b = (1, 20)
|
||||
5 | #: E242
|
||||
6 | a = (1, 2) # tab before 2
|
||||
| ^ E242
|
||||
7 | #: Okay
|
||||
8 | b = (1, 20) # space before 20
|
||||
|
|
||||
|
||||
|
||||
@@ -70,8 +70,6 @@ E70.py:68:1: E703 [*] Statement ends with an unnecessary semicolon
|
||||
67 | 0\
|
||||
68 | ;
|
||||
| ^ E703
|
||||
69 | #: E701:2:3
|
||||
70 | a = \
|
||||
|
|
||||
= help: Remove unnecessary semicolon
|
||||
|
||||
@@ -82,24 +80,5 @@ E70.py:68:1: E703 [*] Statement ends with an unnecessary semicolon
|
||||
67 |-0\
|
||||
68 |-;
|
||||
67 |+0
|
||||
69 68 | #: E701:2:3
|
||||
70 69 | a = \
|
||||
71 70 | 5;
|
||||
|
||||
E70.py:71:4: E703 [*] Statement ends with an unnecessary semicolon
|
||||
|
|
||||
69 | #: E701:2:3
|
||||
70 | a = \
|
||||
71 | 5;
|
||||
| ^ E703
|
||||
|
|
||||
= help: Remove unnecessary semicolon
|
||||
|
||||
ℹ Fix
|
||||
68 68 | ;
|
||||
69 69 | #: E701:2:3
|
||||
70 70 | a = \
|
||||
71 |- 5;
|
||||
71 |+ 5
|
||||
|
||||
|
||||
|
||||
@@ -115,7 +115,6 @@ pub(crate) fn remove_exception_handler_assignment(
|
||||
|
||||
// Lex forwards, to the `:` token.
|
||||
let following = SimpleTokenizer::starts_at(bound_exception.range.end(), locator.contents())
|
||||
.skip_trivia()
|
||||
.next()
|
||||
.context("expected the exception name to be followed by a colon")?;
|
||||
debug_assert!(matches!(following.kind, SimpleTokenKind::Colon));
|
||||
|
||||
@@ -128,7 +128,6 @@ mod tests {
|
||||
#[test_case(Rule::UndefinedName, Path::new("F821_14.py"))]
|
||||
#[test_case(Rule::UndefinedName, Path::new("F821_15.py"))]
|
||||
#[test_case(Rule::UndefinedName, Path::new("F821_16.py"))]
|
||||
#[test_case(Rule::UndefinedName, Path::new("F821_17.py"))]
|
||||
#[test_case(Rule::UndefinedExport, Path::new("F822_0.py"))]
|
||||
#[test_case(Rule::UndefinedExport, Path::new("F822_1.py"))]
|
||||
#[test_case(Rule::UndefinedExport, Path::new("F822_2.py"))]
|
||||
@@ -2415,45 +2414,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliased_submodule_import() {
|
||||
flakes(
|
||||
r#"
|
||||
import fu.bar as baz
|
||||
import fu.bar as baz
|
||||
baz
|
||||
"#,
|
||||
&[Rule::RedefinedWhileUnused],
|
||||
);
|
||||
|
||||
flakes(
|
||||
r#"
|
||||
import fu.bar as baz
|
||||
import baz
|
||||
baz
|
||||
"#,
|
||||
&[Rule::RedefinedWhileUnused],
|
||||
);
|
||||
|
||||
flakes(
|
||||
r#"
|
||||
import fu.bar as baz
|
||||
import fu.bar as bop
|
||||
baz, bop
|
||||
"#,
|
||||
&[],
|
||||
);
|
||||
|
||||
flakes(
|
||||
r#"
|
||||
import foo.baz
|
||||
import foo.baz as foo
|
||||
foo
|
||||
"#,
|
||||
&[Rule::RedefinedWhileUnused],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn used_package_with_submodule_import() {
|
||||
// Usage of package marks submodule imports as used.
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
F821_17.py:16:12: F821 Undefined name `DoesNotExist`
|
||||
|
|
||||
14 | # Types used in aliased assignment must exist
|
||||
15 |
|
||||
16 | type Foo = DoesNotExist # F821: Undefined name `DoesNotExist`
|
||||
| ^^^^^^^^^^^^ F821
|
||||
17 | type Foo = list[DoesNotExist] # F821: Undefined name `DoesNotExist`
|
||||
|
|
||||
|
||||
F821_17.py:17:17: F821 Undefined name `DoesNotExist`
|
||||
|
|
||||
16 | type Foo = DoesNotExist # F821: Undefined name `DoesNotExist`
|
||||
17 | type Foo = list[DoesNotExist] # F821: Undefined name `DoesNotExist`
|
||||
| ^^^^^^^^^^^^ F821
|
||||
18 |
|
||||
19 | # Type parameters do not escape alias scopes
|
||||
|
|
||||
|
||||
F821_17.py:22:1: F821 Undefined name `T`
|
||||
|
|
||||
21 | type Foo[T] = T
|
||||
22 | T # F821: Undefined name `T` - not accessible afterward alias scope
|
||||
| ^ F821
|
||||
23 |
|
||||
24 | # Type parameters in functions
|
||||
|
|
||||
|
||||
F821_17.py:39:17: F821 Undefined name `T`
|
||||
|
|
||||
37 | from some_library import some_decorator
|
||||
38 |
|
||||
39 | @some_decorator(T) # F821: Undefined name `T` - not accessible in decorators
|
||||
| ^ F821
|
||||
40 |
|
||||
41 | def foo[T](t: T) -> None: ...
|
||||
|
|
||||
|
||||
F821_17.py:42:1: F821 Undefined name `T`
|
||||
|
|
||||
41 | def foo[T](t: T) -> None: ...
|
||||
42 | T # F821: Undefined name `T` - not accessible afterward function scope
|
||||
| ^ F821
|
||||
|
|
||||
|
||||
F821_17.py:64:17: F821 Undefined name `T`
|
||||
|
|
||||
63 | from some_library import some_decorator
|
||||
64 | @some_decorator(T) # F821: Undefined name `T` - not accessible in decorators
|
||||
| ^ F821
|
||||
65 |
|
||||
66 | class Foo[T](list[T]): ...
|
||||
|
|
||||
|
||||
F821_17.py:67:1: F821 Undefined name `T`
|
||||
|
|
||||
66 | class Foo[T](list[T]): ...
|
||||
67 | T # F821: Undefined name `T` - not accessible after class scope
|
||||
| ^ F821
|
||||
68 |
|
||||
69 | # Types specified in bounds should exist
|
||||
|
|
||||
|
||||
F821_17.py:71:13: F821 Undefined name `DoesNotExist`
|
||||
|
|
||||
69 | # Types specified in bounds should exist
|
||||
70 |
|
||||
71 | type Foo[T: DoesNotExist] = T # F821: Undefined name `DoesNotExist`
|
||||
| ^^^^^^^^^^^^ F821
|
||||
72 | def foo[T: DoesNotExist](t: T) -> T: return t # F821: Undefined name `DoesNotExist`
|
||||
73 | class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
|
||||
|
|
||||
|
||||
F821_17.py:72:12: F821 Undefined name `DoesNotExist`
|
||||
|
|
||||
71 | type Foo[T: DoesNotExist] = T # F821: Undefined name `DoesNotExist`
|
||||
72 | def foo[T: DoesNotExist](t: T) -> T: return t # F821: Undefined name `DoesNotExist`
|
||||
| ^^^^^^^^^^^^ F821
|
||||
73 | class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
|
||||
|
|
||||
|
||||
F821_17.py:73:14: F821 Undefined name `DoesNotExist`
|
||||
|
|
||||
71 | type Foo[T: DoesNotExist] = T # F821: Undefined name `DoesNotExist`
|
||||
72 | def foo[T: DoesNotExist](t: T) -> T: return t # F821: Undefined name `DoesNotExist`
|
||||
73 | class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
|
||||
| ^^^^^^^^^^^^ F821
|
||||
74 |
|
||||
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
|
|
||||
|
||||
F821_17.py:75:14: F821 Undefined name `DoesNotExist1`
|
||||
|
|
||||
73 | class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
|
||||
74 |
|
||||
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
| ^^^^^^^^^^^^^ F821
|
||||
76 | def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
77 | class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
|
|
||||
|
||||
F821_17.py:75:29: F821 Undefined name `DoesNotExist2`
|
||||
|
|
||||
73 | class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
|
||||
74 |
|
||||
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
| ^^^^^^^^^^^^^ F821
|
||||
76 | def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
77 | class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
|
|
||||
|
||||
F821_17.py:76:13: F821 Undefined name `DoesNotExist1`
|
||||
|
|
||||
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
76 | def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
| ^^^^^^^^^^^^^ F821
|
||||
77 | class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
|
|
||||
|
||||
F821_17.py:76:28: F821 Undefined name `DoesNotExist2`
|
||||
|
|
||||
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
76 | def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
| ^^^^^^^^^^^^^ F821
|
||||
77 | class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
|
|
||||
|
||||
F821_17.py:77:15: F821 Undefined name `DoesNotExist1`
|
||||
|
|
||||
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
76 | def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
77 | class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
| ^^^^^^^^^^^^^ F821
|
||||
78 |
|
||||
79 | # Type parameters in nested classes
|
||||
|
|
||||
|
||||
F821_17.py:77:30: F821 Undefined name `DoesNotExist2`
|
||||
|
|
||||
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
76 | def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
77 | class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
| ^^^^^^^^^^^^^ F821
|
||||
78 |
|
||||
79 | # Type parameters in nested classes
|
||||
|
|
||||
|
||||
F821_17.py:92:52: F821 Undefined name `t`
|
||||
|
|
||||
90 | return x
|
||||
91 |
|
||||
92 | def cannot_access_parent_variable(self, x: t) -> t: # F821: Undefined name `T`
|
||||
| ^ F821
|
||||
93 | t # F821: Undefined name `t`
|
||||
94 | return x
|
||||
|
|
||||
|
||||
F821_17.py:92:58: F821 Undefined name `t`
|
||||
|
|
||||
90 | return x
|
||||
91 |
|
||||
92 | def cannot_access_parent_variable(self, x: t) -> t: # F821: Undefined name `T`
|
||||
| ^ F821
|
||||
93 | t # F821: Undefined name `t`
|
||||
94 | return x
|
||||
|
|
||||
|
||||
F821_17.py:93:17: F821 Undefined name `t`
|
||||
|
|
||||
92 | def cannot_access_parent_variable(self, x: t) -> t: # F821: Undefined name `T`
|
||||
93 | t # F821: Undefined name `t`
|
||||
| ^ F821
|
||||
94 | return x
|
||||
|
|
||||
|
||||
|
||||
@@ -584,22 +584,4 @@ F841_3.py:139:17: F841 Local variable `x` is assigned to but never used
|
||||
|
|
||||
= help: Remove assignment to unused variable `x`
|
||||
|
||||
F841_3.py:155:17: F841 [*] Local variable `e` is assigned to but never used
|
||||
|
|
||||
153 | try:
|
||||
154 | print("hello")
|
||||
155 | except A as e :
|
||||
| ^ F841
|
||||
156 | print("oh no!")
|
||||
|
|
||||
= help: Remove assignment to unused variable `e`
|
||||
|
||||
ℹ Fix
|
||||
152 152 | def f() -> None:
|
||||
153 153 | try:
|
||||
154 154 | print("hello")
|
||||
155 |- except A as e :
|
||||
155 |+ except A:
|
||||
156 156 | print("oh no!")
|
||||
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ pub(super) fn in_dunder_init(semantic: &SemanticModel, settings: &Settings) -> b
|
||||
if name != "__init__" {
|
||||
return false;
|
||||
}
|
||||
let Some(parent) = semantic.first_non_type_parent_scope(scope) else {
|
||||
let Some(parent) = scope.parent.map(|scope_id| &semantic.scopes[scope_id]) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ mod tests {
|
||||
Path::new("repeated_isinstance_calls.py")
|
||||
)]
|
||||
#[test_case(Rule::ComparisonWithItself, Path::new("comparison_with_itself.py"))]
|
||||
#[test_case(Rule::EqWithoutHash, Path::new("eq_without_hash.py"))]
|
||||
#[test_case(Rule::ManualFromImport, Path::new("import_aliasing.py"))]
|
||||
#[test_case(Rule::SingleStringSlots, Path::new("single_string_slots.py"))]
|
||||
#[test_case(Rule::SysExitAlias, Path::new("sys_exit_alias_0.py"))]
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
use ruff_python_ast::{self as ast, Ranged, Stmt};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for classes that implement `__eq__` but not `__hash__`.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// A class that implements `__eq__` but not `__hash__` will have its hash
|
||||
/// method implicitly set to `None`. This will cause the class to be
|
||||
/// unhashable, will in turn cause issues when using the class as a key in a
|
||||
/// dictionary or a member of a set.
|
||||
///
|
||||
/// ## Known problems
|
||||
/// Does not check for `__hash__` implementations in superclasses.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// class Person:
|
||||
/// def __init__(self):
|
||||
/// self.name = "monty"
|
||||
///
|
||||
/// def __eq__(self, other):
|
||||
/// return isinstance(other, Person) and other.name == self.name
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// class Person:
|
||||
/// def __init__(self):
|
||||
/// self.name = "monty"
|
||||
///
|
||||
/// def __eq__(self, other):
|
||||
/// return isinstance(other, Person) and other.name == self.name
|
||||
///
|
||||
/// def __hash__(self):
|
||||
/// return hash(self.name)
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct EqWithoutHash;
|
||||
|
||||
impl Violation for EqWithoutHash {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Object does not implement `__hash__` method")
|
||||
}
|
||||
}
|
||||
|
||||
/// W1641
|
||||
pub(crate) fn object_without_hash_method(
|
||||
checker: &mut Checker,
|
||||
ast::StmtClassDef { name, body, .. }: &ast::StmtClassDef,
|
||||
) {
|
||||
if has_eq_without_hash(body) {
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(EqWithoutHash, name.range()));
|
||||
}
|
||||
}
|
||||
|
||||
fn has_eq_without_hash(body: &[Stmt]) -> bool {
|
||||
let mut has_hash = false;
|
||||
let mut has_eq = false;
|
||||
for statement in body {
|
||||
let Stmt::FunctionDef(ast::StmtFunctionDef { name, .. }) = statement else {
|
||||
continue;
|
||||
};
|
||||
match name.as_str() {
|
||||
"__hash__" => has_hash = true,
|
||||
"__eq__" => has_eq = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
has_eq && !has_hash
|
||||
}
|
||||
@@ -10,7 +10,6 @@ pub(crate) use comparison_of_constant::*;
|
||||
pub(crate) use comparison_with_itself::*;
|
||||
pub(crate) use continue_in_finally::*;
|
||||
pub(crate) use duplicate_bases::*;
|
||||
pub(crate) use eq_without_hash::*;
|
||||
pub(crate) use global_statement::*;
|
||||
pub(crate) use global_variable_not_assigned::*;
|
||||
pub(crate) use import_self::*;
|
||||
@@ -64,7 +63,6 @@ mod comparison_of_constant;
|
||||
mod comparison_with_itself;
|
||||
mod continue_in_finally;
|
||||
mod duplicate_bases;
|
||||
mod eq_without_hash;
|
||||
mod global_statement;
|
||||
mod global_variable_not_assigned;
|
||||
mod import_self;
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pylint/mod.rs
|
||||
---
|
||||
eq_without_hash.py:1:7: PLW1641 Object does not implement `__hash__` method
|
||||
|
|
||||
1 | class Person:
|
||||
| ^^^^^^ PLW1641
|
||||
2 | def __init__(self):
|
||||
3 | self.name = "monty"
|
||||
|
|
||||
|
||||
|
||||
@@ -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"))]
|
||||
|
||||
@@ -15,6 +15,11 @@ use crate::registry::Rule;
|
||||
/// will always evaluate type annotations in a deferred manner, making
|
||||
/// the quotes unnecessary.
|
||||
///
|
||||
/// Type annotations can also be unquoted in some other contexts, even
|
||||
/// without `from __future__ import annotations`. For example, annotated
|
||||
/// assignments within function bodies are not evaluated at runtime, and so can
|
||||
/// be unquoted.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// from __future__ import annotations
|
||||
|
||||
@@ -1,559 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
UP037.py:18:14: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
18 | def foo(var: "MyClass") -> "MyClass":
|
||||
| ^^^^^^^^^ UP037
|
||||
19 | x: "MyClass"
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
15 15 | from mypy_extensions import Arg, DefaultArg, DefaultNamedArg, NamedArg, VarArg
|
||||
16 16 |
|
||||
17 17 |
|
||||
18 |-def foo(var: "MyClass") -> "MyClass":
|
||||
18 |+def foo(var: MyClass) -> "MyClass":
|
||||
19 19 | x: "MyClass"
|
||||
20 20 |
|
||||
21 21 |
|
||||
|
||||
UP037.py:18:28: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
18 | def foo(var: "MyClass") -> "MyClass":
|
||||
| ^^^^^^^^^ UP037
|
||||
19 | x: "MyClass"
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
15 15 | from mypy_extensions import Arg, DefaultArg, DefaultNamedArg, NamedArg, VarArg
|
||||
16 16 |
|
||||
17 17 |
|
||||
18 |-def foo(var: "MyClass") -> "MyClass":
|
||||
18 |+def foo(var: "MyClass") -> MyClass:
|
||||
19 19 | x: "MyClass"
|
||||
20 20 |
|
||||
21 21 |
|
||||
|
||||
UP037.py:19:8: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
18 | def foo(var: "MyClass") -> "MyClass":
|
||||
19 | x: "MyClass"
|
||||
| ^^^^^^^^^ UP037
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
16 16 |
|
||||
17 17 |
|
||||
18 18 | def foo(var: "MyClass") -> "MyClass":
|
||||
19 |- x: "MyClass"
|
||||
19 |+ x: MyClass
|
||||
20 20 |
|
||||
21 21 |
|
||||
22 22 | def foo(*, inplace: "bool"):
|
||||
|
||||
UP037.py:22:21: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
22 | def foo(*, inplace: "bool"):
|
||||
| ^^^^^^ UP037
|
||||
23 | pass
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
19 19 | x: "MyClass"
|
||||
20 20 |
|
||||
21 21 |
|
||||
22 |-def foo(*, inplace: "bool"):
|
||||
22 |+def foo(*, inplace: bool):
|
||||
23 23 | pass
|
||||
24 24 |
|
||||
25 25 |
|
||||
|
||||
UP037.py:26:16: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
26 | def foo(*args: "str", **kwargs: "int"):
|
||||
| ^^^^^ UP037
|
||||
27 | pass
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
23 23 | pass
|
||||
24 24 |
|
||||
25 25 |
|
||||
26 |-def foo(*args: "str", **kwargs: "int"):
|
||||
26 |+def foo(*args: str, **kwargs: "int"):
|
||||
27 27 | pass
|
||||
28 28 |
|
||||
29 29 |
|
||||
|
||||
UP037.py:26:33: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
26 | def foo(*args: "str", **kwargs: "int"):
|
||||
| ^^^^^ UP037
|
||||
27 | pass
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
23 23 | pass
|
||||
24 24 |
|
||||
25 25 |
|
||||
26 |-def foo(*args: "str", **kwargs: "int"):
|
||||
26 |+def foo(*args: "str", **kwargs: int):
|
||||
27 27 | pass
|
||||
28 28 |
|
||||
29 29 |
|
||||
|
||||
UP037.py:30:10: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
30 | x: Tuple["MyClass"]
|
||||
| ^^^^^^^^^ UP037
|
||||
31 |
|
||||
32 | x: Callable[["MyClass"], None]
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
27 27 | pass
|
||||
28 28 |
|
||||
29 29 |
|
||||
30 |-x: Tuple["MyClass"]
|
||||
30 |+x: Tuple[MyClass]
|
||||
31 31 |
|
||||
32 32 | x: Callable[["MyClass"], None]
|
||||
33 33 |
|
||||
|
||||
UP037.py:32:14: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
30 | x: Tuple["MyClass"]
|
||||
31 |
|
||||
32 | x: Callable[["MyClass"], None]
|
||||
| ^^^^^^^^^ UP037
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
29 29 |
|
||||
30 30 | x: Tuple["MyClass"]
|
||||
31 31 |
|
||||
32 |-x: Callable[["MyClass"], None]
|
||||
32 |+x: Callable[[MyClass], None]
|
||||
33 33 |
|
||||
34 34 |
|
||||
35 35 | class Foo(NamedTuple):
|
||||
|
||||
UP037.py:36:8: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
35 | class Foo(NamedTuple):
|
||||
36 | x: "MyClass"
|
||||
| ^^^^^^^^^ UP037
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
33 33 |
|
||||
34 34 |
|
||||
35 35 | class Foo(NamedTuple):
|
||||
36 |- x: "MyClass"
|
||||
36 |+ x: MyClass
|
||||
37 37 |
|
||||
38 38 |
|
||||
39 39 | class D(TypedDict):
|
||||
|
||||
UP037.py:40:27: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
39 | class D(TypedDict):
|
||||
40 | E: TypedDict("E", foo="int", total=False)
|
||||
| ^^^^^ UP037
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
37 37 |
|
||||
38 38 |
|
||||
39 39 | class D(TypedDict):
|
||||
40 |- E: TypedDict("E", foo="int", total=False)
|
||||
40 |+ E: TypedDict("E", foo=int, total=False)
|
||||
41 41 |
|
||||
42 42 |
|
||||
43 43 | class D(TypedDict):
|
||||
|
||||
UP037.py:44:31: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
43 | class D(TypedDict):
|
||||
44 | E: TypedDict("E", {"foo": "int"})
|
||||
| ^^^^^ UP037
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
41 41 |
|
||||
42 42 |
|
||||
43 43 | class D(TypedDict):
|
||||
44 |- E: TypedDict("E", {"foo": "int"})
|
||||
44 |+ E: TypedDict("E", {"foo": int})
|
||||
45 45 |
|
||||
46 46 |
|
||||
47 47 | x: Annotated["str", "metadata"]
|
||||
|
||||
UP037.py:47:14: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
47 | x: Annotated["str", "metadata"]
|
||||
| ^^^^^ UP037
|
||||
48 |
|
||||
49 | x: Arg("str", "name")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
44 44 | E: TypedDict("E", {"foo": "int"})
|
||||
45 45 |
|
||||
46 46 |
|
||||
47 |-x: Annotated["str", "metadata"]
|
||||
47 |+x: Annotated[str, "metadata"]
|
||||
48 48 |
|
||||
49 49 | x: Arg("str", "name")
|
||||
50 50 |
|
||||
|
||||
UP037.py:49:8: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
47 | x: Annotated["str", "metadata"]
|
||||
48 |
|
||||
49 | x: Arg("str", "name")
|
||||
| ^^^^^ UP037
|
||||
50 |
|
||||
51 | x: DefaultArg("str", "name")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
46 46 |
|
||||
47 47 | x: Annotated["str", "metadata"]
|
||||
48 48 |
|
||||
49 |-x: Arg("str", "name")
|
||||
49 |+x: Arg(str, "name")
|
||||
50 50 |
|
||||
51 51 | x: DefaultArg("str", "name")
|
||||
52 52 |
|
||||
|
||||
UP037.py:51:15: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
49 | x: Arg("str", "name")
|
||||
50 |
|
||||
51 | x: DefaultArg("str", "name")
|
||||
| ^^^^^ UP037
|
||||
52 |
|
||||
53 | x: NamedArg("str", "name")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
48 48 |
|
||||
49 49 | x: Arg("str", "name")
|
||||
50 50 |
|
||||
51 |-x: DefaultArg("str", "name")
|
||||
51 |+x: DefaultArg(str, "name")
|
||||
52 52 |
|
||||
53 53 | x: NamedArg("str", "name")
|
||||
54 54 |
|
||||
|
||||
UP037.py:53:13: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
51 | x: DefaultArg("str", "name")
|
||||
52 |
|
||||
53 | x: NamedArg("str", "name")
|
||||
| ^^^^^ UP037
|
||||
54 |
|
||||
55 | x: DefaultNamedArg("str", "name")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
50 50 |
|
||||
51 51 | x: DefaultArg("str", "name")
|
||||
52 52 |
|
||||
53 |-x: NamedArg("str", "name")
|
||||
53 |+x: NamedArg(str, "name")
|
||||
54 54 |
|
||||
55 55 | x: DefaultNamedArg("str", "name")
|
||||
56 56 |
|
||||
|
||||
UP037.py:55:20: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
53 | x: NamedArg("str", "name")
|
||||
54 |
|
||||
55 | x: DefaultNamedArg("str", "name")
|
||||
| ^^^^^ UP037
|
||||
56 |
|
||||
57 | x: DefaultNamedArg("str", name="name")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
52 52 |
|
||||
53 53 | x: NamedArg("str", "name")
|
||||
54 54 |
|
||||
55 |-x: DefaultNamedArg("str", "name")
|
||||
55 |+x: DefaultNamedArg(str, "name")
|
||||
56 56 |
|
||||
57 57 | x: DefaultNamedArg("str", name="name")
|
||||
58 58 |
|
||||
|
||||
UP037.py:57:20: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
55 | x: DefaultNamedArg("str", "name")
|
||||
56 |
|
||||
57 | x: DefaultNamedArg("str", name="name")
|
||||
| ^^^^^ UP037
|
||||
58 |
|
||||
59 | x: VarArg("str")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
54 54 |
|
||||
55 55 | x: DefaultNamedArg("str", "name")
|
||||
56 56 |
|
||||
57 |-x: DefaultNamedArg("str", name="name")
|
||||
57 |+x: DefaultNamedArg(str, name="name")
|
||||
58 58 |
|
||||
59 59 | x: VarArg("str")
|
||||
60 60 |
|
||||
|
||||
UP037.py:59:11: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
57 | x: DefaultNamedArg("str", name="name")
|
||||
58 |
|
||||
59 | x: VarArg("str")
|
||||
| ^^^^^ UP037
|
||||
60 |
|
||||
61 | x: List[List[List["MyClass"]]]
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
56 56 |
|
||||
57 57 | x: DefaultNamedArg("str", name="name")
|
||||
58 58 |
|
||||
59 |-x: VarArg("str")
|
||||
59 |+x: VarArg(str)
|
||||
60 60 |
|
||||
61 61 | x: List[List[List["MyClass"]]]
|
||||
62 62 |
|
||||
|
||||
UP037.py:61:19: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
59 | x: VarArg("str")
|
||||
60 |
|
||||
61 | x: List[List[List["MyClass"]]]
|
||||
| ^^^^^^^^^ UP037
|
||||
62 |
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
58 58 |
|
||||
59 59 | x: VarArg("str")
|
||||
60 60 |
|
||||
61 |-x: List[List[List["MyClass"]]]
|
||||
61 |+x: List[List[List[MyClass]]]
|
||||
62 62 |
|
||||
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 64 |
|
||||
|
||||
UP037.py:63:29: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
61 | x: List[List[List["MyClass"]]]
|
||||
62 |
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
| ^^^^^ UP037
|
||||
64 |
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
60 60 |
|
||||
61 61 | x: List[List[List["MyClass"]]]
|
||||
62 62 |
|
||||
63 |-x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
63 |+x: NamedTuple("X", [("foo", int), ("bar", "str")])
|
||||
64 64 |
|
||||
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
|
||||
UP037.py:63:45: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
61 | x: List[List[List["MyClass"]]]
|
||||
62 |
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
| ^^^^^ UP037
|
||||
64 |
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
60 60 |
|
||||
61 61 | x: List[List[List["MyClass"]]]
|
||||
62 62 |
|
||||
63 |-x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
63 |+x: NamedTuple("X", [("foo", "int"), ("bar", str)])
|
||||
64 64 |
|
||||
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
|
||||
UP037.py:65:29: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 |
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
| ^^^^^ UP037
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
62 62 |
|
||||
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 64 |
|
||||
65 |-x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
65 |+x: NamedTuple("X", fields=[(foo, "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
68 68 |
|
||||
|
||||
UP037.py:65:36: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 |
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
| ^^^^^ UP037
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
62 62 |
|
||||
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 64 |
|
||||
65 |-x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
65 |+x: NamedTuple("X", fields=[("foo", int), ("bar", "str")])
|
||||
66 66 |
|
||||
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
68 68 |
|
||||
|
||||
UP037.py:65:45: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 |
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
| ^^^^^ UP037
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
62 62 |
|
||||
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 64 |
|
||||
65 |-x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
65 |+x: NamedTuple("X", fields=[("foo", "int"), (bar, "str")])
|
||||
66 66 |
|
||||
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
68 68 |
|
||||
|
||||
UP037.py:65:52: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 |
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
| ^^^^^ UP037
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
62 62 |
|
||||
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 64 |
|
||||
65 |-x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
65 |+x: NamedTuple("X", fields=[("foo", "int"), ("bar", str)])
|
||||
66 66 |
|
||||
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
68 68 |
|
||||
|
||||
UP037.py:67:24: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
| ^^^ UP037
|
||||
68 |
|
||||
69 | X: MyCallable("X")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
64 64 |
|
||||
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
67 |-x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
67 |+x: NamedTuple(typename=X, fields=[("foo", "int")])
|
||||
68 68 |
|
||||
69 69 | X: MyCallable("X")
|
||||
70 70 |
|
||||
|
||||
UP037.py:67:38: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
| ^^^^^ UP037
|
||||
68 |
|
||||
69 | X: MyCallable("X")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
64 64 |
|
||||
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
67 |-x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
67 |+x: NamedTuple(typename="X", fields=[(foo, "int")])
|
||||
68 68 |
|
||||
69 69 | X: MyCallable("X")
|
||||
70 70 |
|
||||
|
||||
UP037.py:67:45: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
| ^^^^^ UP037
|
||||
68 |
|
||||
69 | X: MyCallable("X")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
64 64 |
|
||||
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
67 |-x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
67 |+x: NamedTuple(typename="X", fields=[("foo", int)])
|
||||
68 68 |
|
||||
69 69 | X: MyCallable("X")
|
||||
70 70 |
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user