Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
685d9ab848 | ||
|
|
093f9156e1 | ||
|
|
615e62ae24 | ||
|
|
76a0c45773 | ||
|
|
3ec46f0936 | ||
|
|
a6ec2eb044 | ||
|
|
9d3a5530af | ||
|
|
8766e6a666 | ||
|
|
b08367b5a8 | ||
|
|
98a8330124 | ||
|
|
4d52ea87ef | ||
|
|
291239b9f1 | ||
|
|
224334b6d1 | ||
|
|
0cab3f8437 | ||
|
|
5f8810e987 | ||
|
|
f15c562a1c | ||
|
|
4f3b63edd4 | ||
|
|
bab8691132 | ||
|
|
50c85fd192 | ||
|
|
23819ae338 | ||
|
|
4bf2879067 | ||
|
|
b359f3a9ff | ||
|
|
73d0461d55 | ||
|
|
55bb36fb8b | ||
|
|
cebea16fe4 | ||
|
|
f7be192f8b | ||
|
|
e88275280b | ||
|
|
7370a27c09 | ||
|
|
0ad6b8224d | ||
|
|
f3aa409d9a | ||
|
|
adb5c5b150 | ||
|
|
b69b6a7ec8 | ||
|
|
a7d2def9cd | ||
|
|
d9ead4e6df | ||
|
|
b346f74915 | ||
|
|
708295f4c9 | ||
|
|
4190f1045e | ||
|
|
353857e2a5 | ||
|
|
55b43c8ea7 | ||
|
|
6036d1bbe2 |
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.235
|
||||
rev: v0.0.236
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix]
|
||||
|
||||
253
Cargo.lock
generated
253
Cargo.lock
generated
@@ -82,9 +82,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "assert_cmd"
|
||||
version = "2.0.7"
|
||||
version = "2.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa3d466004a8b4cb1bc34044240a2fd29d17607e2e3bd613eb44fd48e8100da3"
|
||||
checksum = "9834fcc22e0874394a010230586367d4a3e9f11b560f469262678547e1d2575e"
|
||||
dependencies = [
|
||||
"bstr 1.1.0",
|
||||
"doc-comment",
|
||||
@@ -181,9 +181,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.1"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||
|
||||
[[package]]
|
||||
name = "cachedir"
|
||||
@@ -236,12 +236,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chunked_transfer"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
|
||||
|
||||
[[package]]
|
||||
name = "ciborium"
|
||||
version = "0.2.0"
|
||||
@@ -283,13 +277,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.0.32"
|
||||
version = "4.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
|
||||
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex 0.3.0",
|
||||
"clap_lex 0.3.1",
|
||||
"is-terminal",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
@@ -298,11 +292,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.0.7"
|
||||
version = "4.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10861370d2ba66b0f5989f83ebf35db6421713fd92351790e7fdd6c36774c56b"
|
||||
checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75"
|
||||
dependencies = [
|
||||
"clap 4.0.32",
|
||||
"clap 4.1.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -311,26 +305,26 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4160b4a4f72ef58bd766bad27c09e6ef1cc9d82a22f6a0f55d152985a4a48e31"
|
||||
dependencies = [
|
||||
"clap 4.0.32",
|
||||
"clap 4.1.4",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete_fig"
|
||||
version = "4.0.2"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46b30e010e669cd021e5004f3be26cff6b7c08d2a8a0d65b48d43a8cc0efd6c3"
|
||||
checksum = "cf0c76d8fcf782a1102ccfcd10ca8246e7fdd609c1cd6deddbb96cb638e9bb5c"
|
||||
dependencies = [
|
||||
"clap 4.0.32",
|
||||
"clap 4.1.4",
|
||||
"clap_complete",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.0.21"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
|
||||
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
@@ -350,9 +344,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
|
||||
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
@@ -399,15 +393,14 @@ checksum = "5458d9d1a587efaf5091602c59d299696a3877a439c8f6d461a2d3cce11df87a"
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.2"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c"
|
||||
checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"terminal_size",
|
||||
"winapi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -551,9 +544,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.85"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd"
|
||||
checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@@ -563,9 +556,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.85"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0"
|
||||
checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -578,15 +571,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.85"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59"
|
||||
checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.85"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6"
|
||||
checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -757,10 +750,10 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.32",
|
||||
"clap 4.1.4",
|
||||
"colored",
|
||||
"configparser",
|
||||
"once_cell",
|
||||
@@ -771,7 +764,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"toml 0.6.0",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -833,18 +826,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.9"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
|
||||
checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr 0.2.17",
|
||||
"bstr 1.1.0",
|
||||
"fnv",
|
||||
"log",
|
||||
"regex",
|
||||
@@ -931,11 +924,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ignore"
|
||||
version = "0.4.18"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
|
||||
checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"globset",
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -989,9 +981,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.23.0"
|
||||
version = "1.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48b08a091dfe5b09a6a9688c468fdd5b4396e92ce09e2eb932f0884b02788a4"
|
||||
checksum = "f6f0f08b46e4379744de2ab67aa8f7de3ffd1da3e275adc41fcc82053ede46ff"
|
||||
dependencies = [
|
||||
"console",
|
||||
"lazy_static",
|
||||
@@ -1014,9 +1006,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
|
||||
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
@@ -1242,9 +1234,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
@@ -1302,9 +1294,9 @@ checksum = "d906846a98739ed9d73d66e62c2641eef8321f1734b7a1156ab045a0248fb2b3"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.26.1"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694"
|
||||
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
@@ -1369,9 +1361,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
|
||||
checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"serde",
|
||||
@@ -1408,18 +1400,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.5.7"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
|
||||
checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.5.7"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
|
||||
checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -1457,9 +1449,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.5"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
|
||||
checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -1527,9 +1519,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.5.3"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4257b4a04d91f7e9e6290be5d3da4804dd5784fafde3a497d73eb2b4a158c30a"
|
||||
checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
@@ -1537,9 +1529,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.5.3"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "241cda393b0cdd65e62e07e12454f1f25d57017dcc514b1514cd3c4645e3a0a6"
|
||||
checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
@@ -1547,9 +1539,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.5.3"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46b53634d8c8196302953c74d5352f33d0c512a9499bd2ce468fc9f4128fa27c"
|
||||
checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
@@ -1560,9 +1552,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.5.3"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ef4f1332a8d4678b41966bb4cc1d0676880e84183a1ecc3f4b69f03e99c7a51"
|
||||
checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
@@ -1703,9 +1695,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "2.1.4"
|
||||
version = "2.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f54fc5dc63ed3bbf19494623db4f3af16842c0d975818e469022d09e53f0aa05"
|
||||
checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd"
|
||||
dependencies = [
|
||||
"difflib",
|
||||
"itertools",
|
||||
@@ -1730,13 +1722,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.2.1"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9"
|
||||
checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
"toml 0.5.11",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1765,9 +1756,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.49"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
|
||||
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1852,9 +1843,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.10.1"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
|
||||
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
@@ -1884,9 +1875,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.0"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1931,13 +1922,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"clap 4.0.32",
|
||||
"clap 4.1.4",
|
||||
"colored",
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
@@ -1979,14 +1970,14 @@ dependencies = [
|
||||
"textwrap",
|
||||
"thiserror",
|
||||
"titlecase",
|
||||
"toml 0.6.0",
|
||||
"toml",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1995,7 +1986,7 @@ dependencies = [
|
||||
"bincode",
|
||||
"cachedir",
|
||||
"chrono",
|
||||
"clap 4.0.32",
|
||||
"clap 4.1.4",
|
||||
"clap_complete_command",
|
||||
"clearscreen",
|
||||
"colored",
|
||||
@@ -2023,10 +2014,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.32",
|
||||
"clap 4.1.4",
|
||||
"itertools",
|
||||
"libcst",
|
||||
"once_cell",
|
||||
@@ -2044,7 +2035,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
@@ -2071,9 +2062,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.5"
|
||||
version = "0.36.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -2085,9 +2076,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.7"
|
||||
version = "0.20.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c"
|
||||
checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
@@ -2253,9 +2244,9 @@ checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.151"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -2273,9 +2264,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.151"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8"
|
||||
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2448,23 +2439,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminfo"
|
||||
version = "0.7.5"
|
||||
@@ -2602,15 +2583,6 @@ dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.6.0"
|
||||
@@ -2733,9 +2705,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.8"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
@@ -2801,12 +2773,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "2.5.0"
|
||||
version = "2.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97acb4c28a254fd7a4aeec976c46a7fa404eac4d7c134b30c75144846d7cb8f"
|
||||
checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"chunked_transfer",
|
||||
"flate2",
|
||||
"log",
|
||||
"once_cell",
|
||||
@@ -3000,9 +2971,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.3.0"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
|
||||
checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
|
||||
dependencies = [
|
||||
"either",
|
||||
"libc",
|
||||
@@ -3063,45 +3034,45 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.0"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.0"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.0"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.0"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.0"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.0"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
|
||||
@@ -8,7 +8,7 @@ default-members = [".", "ruff_cli"]
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
@@ -46,7 +46,7 @@ num-traits = "0.2.15"
|
||||
once_cell = { version = "1.16.0" }
|
||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
||||
regex = { version = "1.6.0" }
|
||||
ruff_macros = { version = "0.0.235", path = "ruff_macros" }
|
||||
ruff_macros = { version = "0.0.236", path = "ruff_macros" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
|
||||
205
LICENSE
205
LICENSE
@@ -800,3 +800,208 @@ are:
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-logging-format, licensed as follows:
|
||||
"""
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
490
README.md
490
README.md
@@ -67,7 +67,9 @@ Ruff is extremely actively developed and used in major open-source projects like
|
||||
- [Home Assistant](https://github.com/home-assistant/core)
|
||||
- [Cryptography (PyCA)](https://github.com/pyca/cryptography)
|
||||
- [cibuildwheel (PyPA)](https://github.com/pypa/cibuildwheel)
|
||||
- [build (PyPA)](https://github.com/pypa/build)
|
||||
- [Babel](https://github.com/python-babel/babel)
|
||||
- [featuretools](https://github.com/alteryx/featuretools)
|
||||
|
||||
Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster).
|
||||
|
||||
@@ -113,9 +115,9 @@ developer of [Zulip](https://github.com/zulip/zulip):
|
||||
1. [pycodestyle (E, W)](#pycodestyle-e-w)
|
||||
1. [mccabe (C90)](#mccabe-c90)
|
||||
1. [isort (I)](#isort-i)
|
||||
1. [pep8-naming (N)](#pep8-naming-n)
|
||||
1. [pydocstyle (D)](#pydocstyle-d)
|
||||
1. [pyupgrade (UP)](#pyupgrade-up)
|
||||
1. [pep8-naming (N)](#pep8-naming-n)
|
||||
1. [flake8-2020 (YTT)](#flake8-2020-ytt)
|
||||
1. [flake8-annotations (ANN)](#flake8-annotations-ann)
|
||||
1. [flake8-bandit (S)](#flake8-bandit-s)
|
||||
@@ -123,30 +125,31 @@ developer of [Zulip](https://github.com/zulip/zulip):
|
||||
1. [flake8-boolean-trap (FBT)](#flake8-boolean-trap-fbt)
|
||||
1. [flake8-bugbear (B)](#flake8-bugbear-b)
|
||||
1. [flake8-builtins (A)](#flake8-builtins-a)
|
||||
1. [flake8-commas (COM)](#flake8-commas-com)
|
||||
1. [flake8-comprehensions (C4)](#flake8-comprehensions-c4)
|
||||
1. [flake8-datetimez (DTZ)](#flake8-datetimez-dtz)
|
||||
1. [flake8-debugger (T10)](#flake8-debugger-t10)
|
||||
1. [flake8-errmsg (EM)](#flake8-errmsg-em)
|
||||
1. [flake8-executable (EXE)](#flake8-executable-exe)
|
||||
1. [flake8-implicit-str-concat (ISC)](#flake8-implicit-str-concat-isc)
|
||||
1. [flake8-import-conventions (ICN)](#flake8-import-conventions-icn)
|
||||
1. [flake8-logging-format (G)](#flake8-logging-format-g)
|
||||
1. [flake8-no-pep420 (INP)](#flake8-no-pep420-inp)
|
||||
1. [flake8-pie (PIE)](#flake8-pie-pie)
|
||||
1. [flake8-print (T20)](#flake8-print-t20)
|
||||
1. [flake8-pytest-style (PT)](#flake8-pytest-style-pt)
|
||||
1. [flake8-quotes (Q)](#flake8-quotes-q)
|
||||
1. [flake8-return (RET)](#flake8-return-ret)
|
||||
1. [flake8-simplify (SIM)](#flake8-simplify-sim)
|
||||
1. [flake8-tidy-imports (TID)](#flake8-tidy-imports-tid)
|
||||
1. [flake8-type-checking (TCH)](#flake8-type-checking-tch)
|
||||
1. [flake8-unused-arguments (ARG)](#flake8-unused-arguments-arg)
|
||||
1. [flake8-datetimez (DTZ)](#flake8-datetimez-dtz)
|
||||
1. [flake8-use-pathlib (PTH)](#flake8-use-pathlib-pth)
|
||||
1. [eradicate (ERA)](#eradicate-era)
|
||||
1. [pandas-vet (PD)](#pandas-vet-pd)
|
||||
1. [pygrep-hooks (PGH)](#pygrep-hooks-pgh)
|
||||
1. [Pylint (PL)](#pylint-pl)
|
||||
1. [flake8-pie (PIE)](#flake8-pie-pie)
|
||||
1. [flake8-commas (COM)](#flake8-commas-com)
|
||||
1. [flake8-no-pep420 (INP)](#flake8-no-pep420-inp)
|
||||
1. [flake8-executable (EXE)](#flake8-executable-exe)
|
||||
1. [flake8-type-checking (TCH)](#flake8-type-checking-tch)
|
||||
1. [tryceratops (TRY)](#tryceratops-try)
|
||||
1. [flake8-use-pathlib (PTH)](#flake8-use-pathlib-pth)
|
||||
1. [Ruff-specific rules (RUF)](#ruff-specific-rules-ruf)<!-- End auto-generated table of contents. -->
|
||||
1. [Editor Integrations](#editor-integrations)
|
||||
1. [FAQ](#faq)
|
||||
@@ -213,7 +216,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.0.235'
|
||||
rev: 'v0.0.236'
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -227,12 +230,14 @@ If left unspecified, the default configuration is equivalent to:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
|
||||
# Enable Pyflakes `E` and `F` codes by default.
|
||||
select = ["E", "F"]
|
||||
ignore = []
|
||||
|
||||
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
||||
fixable = ["A", "B", "C", "D", "E", "F", "..."]
|
||||
unfixable = []
|
||||
|
||||
# Exclude a variety of commonly ignored directories.
|
||||
exclude = [
|
||||
".bzr",
|
||||
@@ -257,6 +262,9 @@ exclude = [
|
||||
]
|
||||
per-file-ignores = {}
|
||||
|
||||
# Same as Black.
|
||||
line-length = 88
|
||||
|
||||
# Allow unused variables when underscore-prefixed.
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
@@ -268,20 +276,21 @@ target-version = "py310"
|
||||
max-complexity = 10
|
||||
```
|
||||
|
||||
As an example, the following would configure Ruff to: (1) avoid enforcing line-length violations
|
||||
(`E501`); (2) never remove unused imports (`F401`); and (3) ignore import-at-top-of-file violations
|
||||
(`E402`) in `__init__.py` files:
|
||||
As an example, the following would configure Ruff to: (1) enforce flake8-bugbear rules, in addition
|
||||
to the defaults; (2) avoid enforcing line-length violations (`E501`); (3) avoid attempting to fix
|
||||
flake8-bugbear (`B`) violations; and (3) ignore import-at-top-of-file violations (`E402`) in
|
||||
`__init__.py` files:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Enable Pyflakes and pycodestyle rules.
|
||||
select = ["E", "F"]
|
||||
# Enable flake8-bugbear (`B`) rules.
|
||||
select = ["E", "F", "B"]
|
||||
|
||||
# Never enforce `E501` (line length violations).
|
||||
ignore = ["E501"]
|
||||
|
||||
# Never try to fix `F401` (unused imports).
|
||||
unfixable = ["F401"]
|
||||
# Avoid trying to fix flake8-bugbear (`B`) violations.
|
||||
unfixable = ["B"]
|
||||
|
||||
# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
|
||||
[tool.ruff.per-file-ignores]
|
||||
@@ -306,10 +315,18 @@ prefix, followed by three digits (e.g., `F401`). The prefix indicates that "sour
|
||||
rules is determined by the `select` and `ignore` options, which support both the full code (e.g.,
|
||||
`F401`) and the prefix (e.g., `F`).
|
||||
|
||||
As a special-case, Ruff also supports the `ALL` code, which enables all rules. Note that some of the
|
||||
`pydocstyle` rules conflict (e.g., `D203` and `D211`) as they represent alternative docstring
|
||||
formats. Enabling `ALL` without further configuration may result in suboptimal behavior, especially
|
||||
for the `pydocstyle` plugin.
|
||||
As a special-case, Ruff also supports the `ALL` code, which enables all rules.
|
||||
|
||||
If you're wondering how to configure Ruff, here are some **recommended guidelines**:
|
||||
|
||||
- Prefer `select` and `ignore` over `extend-select` and `extend-ignore`, to make your rule set
|
||||
explicit.
|
||||
- Use `ALL` with discretion. Enabling `ALL` will implicitly enable new rules whenever you upgrade.
|
||||
- Start with a small set of rules (`select = ["E", "F"]`) and add a category at-a-time. For example,
|
||||
you might consider expanding to `select = ["E", "F", "B"]` to enable the popular `flake8-bugbear`
|
||||
extension.
|
||||
- By default, Ruff's autofix is aggressive. If you find that it's too aggressive for your liking,
|
||||
consider turning off autofix for specific rules or categories (see: [FAQ](#ruff-tried-to-fix-something-but-it-broke-my-code-what-should-i-do)).
|
||||
|
||||
As an alternative to `pyproject.toml`, Ruff will also respect a `ruff.toml` file, which implements
|
||||
an equivalent schema (though the `[tool.ruff]` hierarchy can be omitted). For example, the
|
||||
@@ -352,80 +369,71 @@ Arguments:
|
||||
[FILES]...
|
||||
|
||||
Options:
|
||||
--config <CONFIG>
|
||||
Path to the `pyproject.toml` or `ruff.toml` file to use for configuration
|
||||
-v, --verbose
|
||||
Enable verbose logging
|
||||
-q, --quiet
|
||||
Print lint violations, but nothing else
|
||||
-s, --silent
|
||||
Disable all logging (but still exit with status code "1" upon detecting lint violations)
|
||||
-e, --exit-zero
|
||||
Exit with status code "0", even upon detecting lint violations
|
||||
-w, --watch
|
||||
Run in watch mode by re-running whenever files change
|
||||
--fix
|
||||
Attempt to automatically fix lint violations
|
||||
--fix-only
|
||||
Fix any fixable lint violations, but don't report on leftover violations. Implies `--fix`
|
||||
--diff
|
||||
Avoid writing any fixed files back; instead, output a diff for each changed file to stdout
|
||||
-n, --no-cache
|
||||
Disable cache reads
|
||||
--isolated
|
||||
Ignore all configuration files
|
||||
--fix Attempt to automatically fix lint violations
|
||||
--show-source Show violations with source code
|
||||
--diff Avoid writing any fixed files back; instead, output a diff for each changed file to stdout
|
||||
-w, --watch Run in watch mode by re-running whenever files change
|
||||
--fix-only Fix any fixable lint violations, but don't report on leftover violations. Implies `--fix`
|
||||
--format <FORMAT> Output serialization format for violations [env: RUFF_FORMAT=] [possible values: text, json, junit, grouped, github, gitlab, pylint]
|
||||
--config <CONFIG> Path to the `pyproject.toml` or `ruff.toml` file to use for configuration
|
||||
--add-noqa Enable automatic additions of `noqa` directives to failing lines
|
||||
--show-files See the files Ruff will be run against with the current settings
|
||||
--show-settings See the settings Ruff will use to lint a given Python file
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
Rule selection:
|
||||
--select <RULE_CODE>
|
||||
Comma-separated list of rule codes to enable (or ALL, to enable all rules)
|
||||
--extend-select <RULE_CODE>
|
||||
Like --select, but adds additional rule codes on top of the selected ones
|
||||
--ignore <RULE_CODE>
|
||||
Comma-separated list of rule codes to disable
|
||||
--extend-select <RULE_CODE>
|
||||
Like --select, but adds additional rule codes on top of the selected ones
|
||||
--extend-ignore <RULE_CODE>
|
||||
Like --ignore, but adds additional rule codes on top of the ignored ones
|
||||
--exclude <FILE_PATTERN>
|
||||
List of paths, used to omit files and/or directories from analysis
|
||||
--extend-exclude <FILE_PATTERN>
|
||||
Like --exclude, but adds additional files and directories on top of those already excluded
|
||||
--per-file-ignores <PER_FILE_IGNORES>
|
||||
List of mappings from file pattern to code to exclude
|
||||
--fixable <RULE_CODE>
|
||||
List of rule codes to treat as eligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
|
||||
--unfixable <RULE_CODE>
|
||||
List of rule codes to treat as ineligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
|
||||
--per-file-ignores <PER_FILE_IGNORES>
|
||||
List of mappings from file pattern to code to exclude
|
||||
--format <FORMAT>
|
||||
Output serialization format for violations [env: RUFF_FORMAT=] [possible values: text, json, junit, grouped, github, gitlab, pylint]
|
||||
--stdin-filename <STDIN_FILENAME>
|
||||
The name of the file when passing it through stdin
|
||||
--cache-dir <CACHE_DIR>
|
||||
Path to the cache directory [env: RUFF_CACHE_DIR=]
|
||||
--show-source
|
||||
Show violations with source code
|
||||
--respect-gitignore
|
||||
Respect file exclusions via `.gitignore` and other standard ignore files
|
||||
--force-exclude
|
||||
Enforce exclusions, even for paths passed to Ruff directly on the command-line
|
||||
--update-check
|
||||
Enable or disable automatic update checks
|
||||
--dummy-variable-rgx <DUMMY_VARIABLE_RGX>
|
||||
Regular expression matching the name of dummy variables
|
||||
|
||||
File selection:
|
||||
--exclude <FILE_PATTERN> List of paths, used to omit files and/or directories from analysis
|
||||
--extend-exclude <FILE_PATTERN> Like --exclude, but adds additional files and directories on top of those already excluded
|
||||
--respect-gitignore Respect file exclusions via `.gitignore` and other standard ignore files
|
||||
--force-exclude Enforce exclusions, even for paths passed to Ruff directly on the command-line
|
||||
|
||||
Rule configuration:
|
||||
--target-version <TARGET_VERSION>
|
||||
The minimum Python version that should be supported
|
||||
--line-length <LINE_LENGTH>
|
||||
Set the line-length for length-associated rules and automatic formatting
|
||||
--add-noqa
|
||||
Enable automatic additions of `noqa` directives to failing lines
|
||||
--clean
|
||||
Clear any caches in the current directory or any subdirectories
|
||||
--explain <EXPLAIN>
|
||||
Explain a rule
|
||||
--show-files
|
||||
See the files Ruff will be run against with the current settings
|
||||
--show-settings
|
||||
See the settings Ruff will use to lint a given Python file
|
||||
-h, --help
|
||||
Print help information
|
||||
-V, --version
|
||||
Print version information
|
||||
--dummy-variable-rgx <DUMMY_VARIABLE_RGX>
|
||||
Regular expression matching the name of dummy variables
|
||||
|
||||
Miscellaneous:
|
||||
-n, --no-cache
|
||||
Disable cache reads
|
||||
--isolated
|
||||
Ignore all configuration files
|
||||
--cache-dir <CACHE_DIR>
|
||||
Path to the cache directory [env: RUFF_CACHE_DIR=]
|
||||
--stdin-filename <STDIN_FILENAME>
|
||||
The name of the file when passing it through stdin
|
||||
-e, --exit-zero
|
||||
Exit with status code "0", even upon detecting lint violations
|
||||
--update-check
|
||||
Enable or disable automatic update checks
|
||||
|
||||
Subcommands:
|
||||
--explain <EXPLAIN> Explain a rule
|
||||
--clean Clear any caches in the current directory or any subdirectories
|
||||
|
||||
Log levels:
|
||||
-v, --verbose Enable verbose logging
|
||||
-q, --quiet Print lint violations, but nothing else
|
||||
-s, --silent Disable all logging (but still exit with status code "1" upon detecting lint violations)
|
||||
```
|
||||
<!-- End auto-generated cli help. -->
|
||||
|
||||
@@ -548,7 +556,9 @@ add `noqa` directives to all failing lines, with the appropriate rule codes.
|
||||
|
||||
Regardless of the rule's origin, Ruff re-implements every rule in Rust as a first-party feature.
|
||||
|
||||
By default, Ruff enables all `E` and `F` rule codes, which correspond to those built-in to Flake8.
|
||||
By default, Ruff enables Flake8's `E` and `F` rules. Ruff supports all rules from the `F` category,
|
||||
and a [subset](#error-e) of the `E` category, omitting those stylistic rules made obsolete by the
|
||||
use of an autoformatter, like [Black](https://github.com/psf/black).
|
||||
|
||||
The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` command-line option.
|
||||
|
||||
@@ -652,6 +662,28 @@ For more, see [isort](https://pypi.org/project/isort/) on PyPI.
|
||||
| I001 | unsorted-imports | Import block is un-sorted or un-formatted | 🛠 |
|
||||
| I002 | missing-required-import | Missing required import: `{name}` | 🛠 |
|
||||
|
||||
### pep8-naming (N)
|
||||
|
||||
For more, see [pep8-naming](https://pypi.org/project/pep8-naming/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| N801 | invalid-class-name | Class name `{name}` should use CapWords convention | |
|
||||
| N802 | invalid-function-name | Function name `{name}` should be lowercase | |
|
||||
| N803 | invalid-argument-name | Argument name `{name}` should be lowercase | |
|
||||
| N804 | invalid-first-argument-name-for-class-method | First argument of a class method should be named `cls` | |
|
||||
| N805 | invalid-first-argument-name-for-method | First argument of a method should be named `self` | |
|
||||
| N806 | non-lowercase-variable-in-function | Variable `{name}` in function should be lowercase | |
|
||||
| N807 | dunder-function-name | Function name should not start and end with `__` | |
|
||||
| N811 | constant-imported-as-non-constant | Constant `{name}` imported as non-constant `{asname}` | |
|
||||
| N812 | lowercase-imported-as-non-lowercase | Lowercase `{name}` imported as non-lowercase `{asname}` | |
|
||||
| N813 | camelcase-imported-as-lowercase | Camelcase `{name}` imported as lowercase `{asname}` | |
|
||||
| N814 | camelcase-imported-as-constant | Camelcase `{name}` imported as constant `{asname}` | |
|
||||
| N815 | mixed-case-variable-in-class-scope | Variable `{name}` in class scope should not be mixedCase | |
|
||||
| N816 | mixed-case-variable-in-global-scope | Variable `{name}` in global scope should not be mixedCase | |
|
||||
| N817 | camelcase-imported-as-acronym | Camelcase `{name}` imported as acronym `{asname}` | |
|
||||
| N818 | error-suffix-on-exception-name | Exception name `{name}` should be named with an Error suffix | |
|
||||
|
||||
### pydocstyle (D)
|
||||
|
||||
For more, see [pydocstyle](https://pypi.org/project/pydocstyle/) on PyPI.
|
||||
@@ -745,28 +777,6 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/) on PyPI.
|
||||
| UP033 | functools-cache | Use `@functools.cache` instead of `@functools.lru_cache(maxsize=None)` | 🛠 |
|
||||
| UP034 | extraneous-parentheses | Avoid extraneous parentheses | 🛠 |
|
||||
|
||||
### pep8-naming (N)
|
||||
|
||||
For more, see [pep8-naming](https://pypi.org/project/pep8-naming/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| N801 | invalid-class-name | Class name `{name}` should use CapWords convention | |
|
||||
| N802 | invalid-function-name | Function name `{name}` should be lowercase | |
|
||||
| N803 | invalid-argument-name | Argument name `{name}` should be lowercase | |
|
||||
| N804 | invalid-first-argument-name-for-class-method | First argument of a class method should be named `cls` | |
|
||||
| N805 | invalid-first-argument-name-for-method | First argument of a method should be named `self` | |
|
||||
| N806 | non-lowercase-variable-in-function | Variable `{name}` in function should be lowercase | |
|
||||
| N807 | dunder-function-name | Function name should not start and end with `__` | |
|
||||
| N811 | constant-imported-as-non-constant | Constant `{name}` imported as non-constant `{asname}` | |
|
||||
| N812 | lowercase-imported-as-non-lowercase | Lowercase `{name}` imported as non-lowercase `{asname}` | |
|
||||
| N813 | camelcase-imported-as-lowercase | Camelcase `{name}` imported as lowercase `{asname}` | |
|
||||
| N814 | camelcase-imported-as-constant | Camelcase `{name}` imported as constant `{asname}` | |
|
||||
| N815 | mixed-case-variable-in-class-scope | Variable `{name}` in class scope should not be mixedCase | |
|
||||
| N816 | mixed-case-variable-in-global-scope | Variable `{name}` in global scope should not be mixedCase | |
|
||||
| N817 | camelcase-imported-as-acronym | Camelcase `{name}` imported as acronym `{asname}` | |
|
||||
| N818 | error-suffix-on-exception-name | Exception name `{name}` should be named with an Error suffix | |
|
||||
|
||||
### flake8-2020 (YTT)
|
||||
|
||||
For more, see [flake8-2020](https://pypi.org/project/flake8-2020/) on PyPI.
|
||||
@@ -888,6 +898,16 @@ For more, see [flake8-builtins](https://pypi.org/project/flake8-builtins/) on Py
|
||||
| A002 | builtin-argument-shadowing | Argument `{name}` is shadowing a python builtin | |
|
||||
| A003 | builtin-attribute-shadowing | Class attribute `{name}` is shadowing a python builtin | |
|
||||
|
||||
### flake8-commas (COM)
|
||||
|
||||
For more, see [flake8-commas](https://pypi.org/project/flake8-commas/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| COM812 | trailing-comma-missing | Trailing comma missing | 🛠 |
|
||||
| COM818 | trailing-comma-on-bare-tuple-prohibited | Trailing comma on bare tuple prohibited | |
|
||||
| COM819 | trailing-comma-prohibited | Trailing comma prohibited | 🛠 |
|
||||
|
||||
### flake8-comprehensions (C4)
|
||||
|
||||
For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/) on PyPI.
|
||||
@@ -911,6 +931,22 @@ For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehens
|
||||
| C416 | unnecessary-comprehension | Unnecessary `{obj_type}` comprehension (rewrite using `{obj_type}()`) | 🛠 |
|
||||
| C417 | unnecessary-map | Unnecessary `map` usage (rewrite using a generator expression) | |
|
||||
|
||||
### flake8-datetimez (DTZ)
|
||||
|
||||
For more, see [flake8-datetimez](https://pypi.org/project/flake8-datetimez/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| DTZ001 | call-datetime-without-tzinfo | The use of `datetime.datetime()` without `tzinfo` argument is not allowed | |
|
||||
| DTZ002 | call-datetime-today | The use of `datetime.datetime.today()` is not allowed | |
|
||||
| DTZ003 | call-datetime-utcnow | The use of `datetime.datetime.utcnow()` is not allowed | |
|
||||
| DTZ004 | call-datetime-utcfromtimestamp | The use of `datetime.datetime.utcfromtimestamp()` is not allowed | |
|
||||
| DTZ005 | call-datetime-now-without-tzinfo | The use of `datetime.datetime.now()` without `tz` argument is not allowed | |
|
||||
| DTZ006 | call-datetime-fromtimestamp | The use of `datetime.datetime.fromtimestamp()` without `tz` argument is not allowed | |
|
||||
| DTZ007 | call-datetime-strptime-without-zone | The use of `datetime.datetime.strptime()` without %z must be followed by `.replace(tzinfo=)` or `.astimezone()` | |
|
||||
| DTZ011 | call-date-today | The use of `datetime.date.today()` is not allowed. | |
|
||||
| DTZ012 | call-date-fromtimestamp | The use of `datetime.date.fromtimestamp()` is not allowed | |
|
||||
|
||||
### flake8-debugger (T10)
|
||||
|
||||
For more, see [flake8-debugger](https://pypi.org/project/flake8-debugger/) on PyPI.
|
||||
@@ -929,6 +965,18 @@ For more, see [flake8-errmsg](https://pypi.org/project/flake8-errmsg/) on PyPI.
|
||||
| EM102 | f-string-in-exception | Exception must not use an f-string literal, assign to variable first | |
|
||||
| EM103 | dot-format-in-exception | Exception must not use a `.format()` string directly, assign to variable first | |
|
||||
|
||||
### flake8-executable (EXE)
|
||||
|
||||
For more, see [flake8-executable](https://pypi.org/project/flake8-executable/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| EXE001 | shebang-not-executable | Shebang is present but file is not executable | |
|
||||
| EXE002 | shebang-missing-executable-file | The file is executable but no shebang is present | |
|
||||
| EXE003 | shebang-python | Shebang should contain "python" | |
|
||||
| EXE004 | shebang-whitespace | Avoid whitespace before shebang | 🛠 |
|
||||
| EXE005 | shebang-newline | Shebang should be at the beginning of the file | |
|
||||
|
||||
### flake8-implicit-str-concat (ISC)
|
||||
|
||||
For more, see [flake8-implicit-str-concat](https://pypi.org/project/flake8-implicit-str-concat/) on PyPI.
|
||||
@@ -947,6 +995,42 @@ For more, see [flake8-import-conventions](https://github.com/joaopalmeiro/flake8
|
||||
| ---- | ---- | ------- | --- |
|
||||
| ICN001 | import-alias-is-not-conventional | `{name}` should be imported as `{asname}` | |
|
||||
|
||||
### flake8-logging-format (G)
|
||||
|
||||
For more, see [flake8-logging-format](https://pypi.org/project/flake8-logging-format/0.9.0/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| G001 | logging-string-format | Logging statement uses `string.format()` | |
|
||||
| G002 | logging-percent-format | Logging statement uses `%` | |
|
||||
| G003 | logging-string-concat | Logging statement uses `+` | |
|
||||
| G004 | logging-f-string | Logging statement uses f-string | |
|
||||
| G010 | logging-warn | Logging statement uses `warn` instead of `warning` | 🛠 |
|
||||
| G101 | logging-extra-attr-clash | Logging statement uses an extra field that clashes with a LogRecord field: `{key}` | |
|
||||
| G201 | logging-exc-info | Logging `.exception(...)` should be used instead of `.error(..., exc_info=True)` | |
|
||||
| G202 | logging-redundant-exc-info | Logging statement has redundant `exc_info` | |
|
||||
|
||||
### flake8-no-pep420 (INP)
|
||||
|
||||
For more, see [flake8-no-pep420](https://pypi.org/project/flake8-no-pep420/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| INP001 | implicit-namespace-package | File `{filename}` is part of an implicit namespace package. Add an `__init__.py`. | |
|
||||
|
||||
### flake8-pie (PIE)
|
||||
|
||||
For more, see [flake8-pie](https://pypi.org/project/flake8-pie/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| PIE790 | no-unnecessary-pass | Unnecessary `pass` statement | 🛠 |
|
||||
| PIE794 | dupe-class-field-definitions | Class field `{name}` is defined multiple times | 🛠 |
|
||||
| PIE796 | prefer-unique-enums | Enum contains duplicate value: `{value}` | |
|
||||
| PIE800 | no-unnecessary-spread | Unnecessary spread `**` | |
|
||||
| PIE804 | no-unnecessary-dict-kwargs | Unnecessary `dict` kwargs | |
|
||||
| PIE807 | prefer-list-builtin | Prefer `list` over useless lambda | 🛠 |
|
||||
|
||||
### flake8-print (T20)
|
||||
|
||||
For more, see [flake8-print](https://pypi.org/project/flake8-print/) on PyPI.
|
||||
@@ -1055,6 +1139,18 @@ For more, see [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports
|
||||
| TID251 | banned-api | `{name}` is banned: {message} | |
|
||||
| TID252 | relative-imports | Relative imports from parent modules are banned | |
|
||||
|
||||
### flake8-type-checking (TCH)
|
||||
|
||||
For more, see [flake8-type-checking](https://pypi.org/project/flake8-type-checking/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| TCH001 | typing-only-first-party-import | Move application import `{}` into a type-checking block | |
|
||||
| TCH002 | typing-only-third-party-import | Move third-party import `{}` into a type-checking block | |
|
||||
| TCH003 | typing-only-standard-library-import | Move standard library import `{}` into a type-checking block | |
|
||||
| TCH004 | runtime-import-in-type-checking-block | Move import `{}` out of type-checking block. Import is used for more than type hinting. | |
|
||||
| TCH005 | empty-type-checking-block | Found empty type-checking block | |
|
||||
|
||||
### flake8-unused-arguments (ARG)
|
||||
|
||||
For more, see [flake8-unused-arguments](https://pypi.org/project/flake8-unused-arguments/) on PyPI.
|
||||
@@ -1067,21 +1163,37 @@ For more, see [flake8-unused-arguments](https://pypi.org/project/flake8-unused-a
|
||||
| ARG004 | unused-static-method-argument | Unused static method argument: `{name}` | |
|
||||
| ARG005 | unused-lambda-argument | Unused lambda argument: `{name}` | |
|
||||
|
||||
### flake8-datetimez (DTZ)
|
||||
### flake8-use-pathlib (PTH)
|
||||
|
||||
For more, see [flake8-datetimez](https://pypi.org/project/flake8-datetimez/) on PyPI.
|
||||
For more, see [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| DTZ001 | call-datetime-without-tzinfo | The use of `datetime.datetime()` without `tzinfo` argument is not allowed | |
|
||||
| DTZ002 | call-datetime-today | The use of `datetime.datetime.today()` is not allowed | |
|
||||
| DTZ003 | call-datetime-utcnow | The use of `datetime.datetime.utcnow()` is not allowed | |
|
||||
| DTZ004 | call-datetime-utcfromtimestamp | The use of `datetime.datetime.utcfromtimestamp()` is not allowed | |
|
||||
| DTZ005 | call-datetime-now-without-tzinfo | The use of `datetime.datetime.now()` without `tz` argument is not allowed | |
|
||||
| DTZ006 | call-datetime-fromtimestamp | The use of `datetime.datetime.fromtimestamp()` without `tz` argument is not allowed | |
|
||||
| DTZ007 | call-datetime-strptime-without-zone | The use of `datetime.datetime.strptime()` without %z must be followed by `.replace(tzinfo=)` or `.astimezone()` | |
|
||||
| DTZ011 | call-date-today | The use of `datetime.date.today()` is not allowed. | |
|
||||
| DTZ012 | call-date-fromtimestamp | The use of `datetime.date.fromtimestamp()` is not allowed | |
|
||||
| PTH100 | pathlib-abspath | `os.path.abspath` should be replaced by `.resolve()` | |
|
||||
| PTH101 | pathlib-chmod | `os.chmod` should be replaced by `.chmod()` | |
|
||||
| PTH102 | pathlib-mkdir | `os.mkdir` should be replaced by `.mkdir()` | |
|
||||
| PTH103 | pathlib-makedirs | `os.makedirs` should be replaced by `.mkdir(parents=True)` | |
|
||||
| PTH104 | pathlib-rename | `os.rename` should be replaced by `.rename()` | |
|
||||
| PTH105 | pathlib-replace | `os.replace`should be replaced by `.replace()` | |
|
||||
| PTH106 | pathlib-rmdir | `os.rmdir` should be replaced by `.rmdir()` | |
|
||||
| PTH107 | pathlib-remove | `os.remove` should be replaced by `.unlink()` | |
|
||||
| PTH108 | pathlib-unlink | `os.unlink` should be replaced by `.unlink()` | |
|
||||
| PTH109 | pathlib-getcwd | `os.getcwd()` should be replaced by `Path.cwd()` | |
|
||||
| PTH110 | pathlib-exists | `os.path.exists` should be replaced by `.exists()` | |
|
||||
| PTH111 | pathlib-expanduser | `os.path.expanduser` should be replaced by `.expanduser()` | |
|
||||
| PTH112 | pathlib-is-dir | `os.path.isdir` should be replaced by `.is_dir()` | |
|
||||
| PTH113 | pathlib-is-file | `os.path.isfile` should be replaced by `.is_file()` | |
|
||||
| PTH114 | pathlib-is-link | `os.path.islink` should be replaced by `.is_symlink()` | |
|
||||
| PTH115 | pathlib-readlink | `os.readlink(` should be replaced by `.readlink()` | |
|
||||
| PTH116 | pathlib-stat | `os.stat` should be replaced by `.stat()` or `.owner()` or `.group()` | |
|
||||
| PTH117 | pathlib-is-abs | `os.path.isabs` should be replaced by `.is_absolute()` | |
|
||||
| PTH118 | pathlib-join | `os.path.join` should be replaced by foo_path / "bar" | |
|
||||
| PTH119 | pathlib-basename | `os.path.basename` should be replaced by `.name` | |
|
||||
| PTH120 | pathlib-dirname | `os.path.dirname` should be replaced by `.parent` | |
|
||||
| PTH121 | pathlib-samefile | `os.path.samefile` should be replaced by `.samefile()` | |
|
||||
| PTH122 | pathlib-splitext | `os.path.splitext` should be replaced by `.suffix` | |
|
||||
| PTH123 | pathlib-open | `open("foo")` should be replaced by`Path("foo").open()` | |
|
||||
| PTH124 | pathlib-py-path | `py.path` is in maintenance mode, use `pathlib` instead | |
|
||||
|
||||
### eradicate (ERA)
|
||||
|
||||
@@ -1154,104 +1266,20 @@ For more, see [Pylint](https://pypi.org/project/pylint/) on PyPI.
|
||||
| PLW0120 | useless-else-on-loop | Else clause on loop without a break statement, remove the else and de-indent all the code inside it | |
|
||||
| PLW0602 | global-variable-not-assigned | Using global for `{name}` but no assignment is done | |
|
||||
|
||||
### flake8-pie (PIE)
|
||||
|
||||
For more, see [flake8-pie](https://pypi.org/project/flake8-pie/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| PIE790 | no-unnecessary-pass | Unnecessary `pass` statement | 🛠 |
|
||||
| PIE794 | dupe-class-field-definitions | Class field `{name}` is defined multiple times | 🛠 |
|
||||
| PIE796 | prefer-unique-enums | Enum contains duplicate value: `{value}` | |
|
||||
| PIE800 | no-unnecessary-spread | Unnecessary spread `**` | |
|
||||
| PIE804 | no-unnecessary-dict-kwargs | Unnecessary `dict` kwargs | |
|
||||
| PIE807 | prefer-list-builtin | Prefer `list` over useless lambda | 🛠 |
|
||||
|
||||
### flake8-commas (COM)
|
||||
|
||||
For more, see [flake8-commas](https://pypi.org/project/flake8-commas/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| COM812 | trailing-comma-missing | Trailing comma missing | 🛠 |
|
||||
| COM818 | trailing-comma-on-bare-tuple-prohibited | Trailing comma on bare tuple prohibited | |
|
||||
| COM819 | trailing-comma-prohibited | Trailing comma prohibited | 🛠 |
|
||||
|
||||
### flake8-no-pep420 (INP)
|
||||
|
||||
For more, see [flake8-no-pep420](https://pypi.org/project/flake8-no-pep420/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| INP001 | implicit-namespace-package | File `{filename}` is part of an implicit namespace package. Add an `__init__.py`. | |
|
||||
|
||||
### flake8-executable (EXE)
|
||||
|
||||
For more, see [flake8-executable](https://pypi.org/project/flake8-executable/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| EXE001 | shebang-not-executable | Shebang is present but file is not executable | |
|
||||
| EXE002 | shebang-missing-executable-file | The file is executable but no shebang is present | |
|
||||
| EXE003 | shebang-python | Shebang should contain "python" | |
|
||||
| EXE004 | shebang-whitespace | Avoid whitespace before shebang | 🛠 |
|
||||
| EXE005 | shebang-newline | Shebang should be at the beginning of the file | |
|
||||
|
||||
### flake8-type-checking (TCH)
|
||||
|
||||
For more, see [flake8-type-checking](https://pypi.org/project/flake8-type-checking/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| TCH001 | typing-only-first-party-import | Move application import `{}` into a type-checking block | |
|
||||
| TCH002 | typing-only-third-party-import | Move third-party import `{}` into a type-checking block | |
|
||||
| TCH003 | typing-only-standard-library-import | Move standard library import `{}` into a type-checking block | |
|
||||
| TCH004 | runtime-import-in-type-checking-block | Move import `{}` out of type-checking block. Import is used for more than type hinting. | |
|
||||
| TCH005 | empty-type-checking-block | Found empty type-checking block | |
|
||||
|
||||
### tryceratops (TRY)
|
||||
|
||||
For more, see [tryceratops](https://pypi.org/project/tryceratops/1.1.0/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| TRY002 | raise-vanilla-class | Create your own exception | |
|
||||
| TRY003 | raise-vanilla-args | Avoid specifying long messages outside the exception class | |
|
||||
| TRY004 | prefer-type-error | Prefer `TypeError` exception for invalid type | 🛠 |
|
||||
| TRY200 | reraise-no-cause | Use `raise from` to specify exception cause | |
|
||||
| TRY201 | verbose-raise | Use `raise` without specifying exception name | |
|
||||
| TRY300 | try-consider-else | Consider `else` block | |
|
||||
| TRY300 | try-consider-else | Consider moving this statement to an `else` block | |
|
||||
| TRY301 | raise-within-try | Abstract `raise` to an inner function | |
|
||||
|
||||
### flake8-use-pathlib (PTH)
|
||||
|
||||
For more, see [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| PTH100 | pathlib-abspath | `os.path.abspath` should be replaced by `.resolve()` | |
|
||||
| PTH101 | pathlib-chmod | `os.chmod` should be replaced by `.chmod()` | |
|
||||
| PTH102 | pathlib-mkdir | `os.mkdir` should be replaced by `.mkdir()` | |
|
||||
| PTH103 | pathlib-makedirs | `os.makedirs` should be replaced by `.mkdir(parents=True)` | |
|
||||
| PTH104 | pathlib-rename | `os.rename` should be replaced by `.rename()` | |
|
||||
| PTH105 | pathlib-replace | `os.replace`should be replaced by `.replace()` | |
|
||||
| PTH106 | pathlib-rmdir | `os.rmdir` should be replaced by `.rmdir()` | |
|
||||
| PTH107 | pathlib-remove | `os.remove` should be replaced by `.unlink()` | |
|
||||
| PTH108 | pathlib-unlink | `os.unlink` should be replaced by `.unlink()` | |
|
||||
| PTH109 | pathlib-getcwd | `os.getcwd()` should be replaced by `Path.cwd()` | |
|
||||
| PTH110 | pathlib-exists | `os.path.exists` should be replaced by `.exists()` | |
|
||||
| PTH111 | pathlib-expanduser | `os.path.expanduser` should be replaced by `.expanduser()` | |
|
||||
| PTH112 | pathlib-is-dir | `os.path.isdir` should be replaced by `.is_dir()` | |
|
||||
| PTH113 | pathlib-is-file | `os.path.isfile` should be replaced by `.is_file()` | |
|
||||
| PTH114 | pathlib-is-link | `os.path.islink` should be replaced by `.is_symlink()` | |
|
||||
| PTH115 | pathlib-readlink | `os.readlink(` should be replaced by `.readlink()` | |
|
||||
| PTH116 | pathlib-stat | `os.stat` should be replaced by `.stat()` or `.owner()` or `.group()` | |
|
||||
| PTH117 | pathlib-is-abs | `os.path.isabs` should be replaced by `.is_absolute()` | |
|
||||
| PTH118 | pathlib-join | `os.path.join` should be replaced by foo_path / "bar" | |
|
||||
| PTH119 | pathlib-basename | `os.path.basename` should be replaced by `.name` | |
|
||||
| PTH120 | pathlib-dirname | `os.path.dirname` should be replaced by `.parent` | |
|
||||
| PTH121 | pathlib-samefile | `os.path.samefile` should be replaced by `.samefile()` | |
|
||||
| PTH122 | pathlib-splitext | `os.path.splitext` should be replaced by `.suffix` | |
|
||||
| PTH123 | pathlib-open | `open("foo")` should be replaced by`Path("foo").open()` | |
|
||||
| PTH124 | pathlib-py-path | `py.path` is in maintenance mode, use `pathlib` instead | |
|
||||
| TRY400 | error-instead-of-exception | Use `logging.exception` instead of `logging.error` | |
|
||||
|
||||
### Ruff-specific rules (RUF)
|
||||
|
||||
@@ -1524,7 +1552,9 @@ automatically convert your existing configuration.)
|
||||
Ruff can be used as a drop-in replacement for Flake8 when used (1) without or with a small number of
|
||||
plugins, (2) alongside Black, and (3) on Python 3 code.
|
||||
|
||||
Under those conditions, Ruff implements every rule in Flake8.
|
||||
Under those conditions, Ruff implements every rule in Flake8. In practice, that means Ruff
|
||||
implements all of the `F` rules (which originate from Pyflakes), along with a subset of the `E` and
|
||||
`W` rules (which originate from pycodestyle).
|
||||
|
||||
Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools
|
||||
natively, including:
|
||||
@@ -1548,6 +1578,7 @@ natively, including:
|
||||
- [`flake8-executable`](https://pypi.org/project/flake8-executable/)
|
||||
- [`flake8-implicit-str-concat`](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
- [`flake8-logging-format](https://pypi.org/project/flake8-logging-format/)
|
||||
- [`flake8-no-pep420`](https://pypi.org/project/flake8-no-pep420)
|
||||
- [`flake8-pie`](https://pypi.org/project/flake8-pie/) ([#1543](https://github.com/charliermarsh/ruff/issues/1543))
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
@@ -1557,6 +1588,8 @@ natively, including:
|
||||
- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998))
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/)
|
||||
- [`flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
|
||||
- [`flake8-use-pathlib`](https://pypi.org/project/flake8-use-pathlib/)
|
||||
- [`isort`](https://pypi.org/project/isort/)
|
||||
- [`mccabe`](https://pypi.org/project/mccabe/)
|
||||
- [`pandas-vet`](https://pypi.org/project/pandas-vet/)
|
||||
@@ -1618,6 +1651,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
- [`flake8-executable`](https://pypi.org/project/flake8-executable/)
|
||||
- [`flake8-implicit-str-concat`](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
- [`flake8-logging-format](https://pypi.org/project/flake8-logging-format/)
|
||||
- [`flake8-no-pep420`](https://pypi.org/project/flake8-no-pep420)
|
||||
- [`flake8-pie`](https://pypi.org/project/flake8-pie/) ([#1543](https://github.com/charliermarsh/ruff/issues/1543))
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
@@ -1627,6 +1661,8 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998))
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/)
|
||||
- [`flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
|
||||
- [`flake8-use-pathlib`](https://pypi.org/project/flake8-use-pathlib/)
|
||||
- [`mccabe`](https://pypi.org/project/mccabe/)
|
||||
- [`pandas-vet`](https://pypi.org/project/pandas-vet/)
|
||||
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
|
||||
@@ -1751,7 +1787,6 @@ unfixable = ["B", "SIM", "TRY", "RUF"]
|
||||
|
||||
If you find a case where Ruff's autofix breaks your code, please file an Issue!
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome and hugely appreciated. To get started, check out the
|
||||
@@ -2374,7 +2409,7 @@ Enabled by default.
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
respect_gitignore = false
|
||||
respect-gitignore = false
|
||||
```
|
||||
|
||||
---
|
||||
@@ -2439,7 +2474,7 @@ my_package/
|
||||
bar.py
|
||||
```
|
||||
|
||||
The `src` directory should be included in `source` (e.g., `source =
|
||||
The `src` directory should be included in the `src` option (e.g., `src =
|
||||
["src"]`), such that when resolving imports, `my_package.foo` is
|
||||
considered a first-party import.
|
||||
|
||||
@@ -3076,6 +3111,45 @@ and can be circumvented via `eval` or `importlib`.
|
||||
|
||||
---
|
||||
|
||||
### `flake8-type-checking`
|
||||
|
||||
#### [`exempt-modules`](#exempt-modules)
|
||||
|
||||
Exempt certain modules from needing to be moved into type-checking
|
||||
blocks.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-type-checking]
|
||||
exempt-modules = ["typing_extensions"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`strict`](#strict)
|
||||
|
||||
Enforce TC001, TC002, and TC003 rules even when valid runtime imports
|
||||
are present for the same module.
|
||||
See: https://github.com/snok/flake8-type-checking#strict.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-type-checking]
|
||||
strict = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `flake8-unused-arguments`
|
||||
|
||||
#### [`ignore-variadic-names`](#ignore-variadic-names)
|
||||
|
||||
4
flake8_to_ruff/Cargo.lock
generated
4
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -26,7 +26,7 @@ use ruff::flake8_to_ruff::{self, ExternalConfig};
|
||||
about = "Convert existing Flake8 configuration to Ruff.",
|
||||
long_about = None
|
||||
)]
|
||||
struct Cli {
|
||||
struct Args {
|
||||
/// Path to the Flake8 configuration file (e.g., `setup.cfg`, `tox.ini`, or
|
||||
/// `.flake8`).
|
||||
#[arg(required = true)]
|
||||
@@ -41,15 +41,15 @@ struct Cli {
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
let args = Args::parse();
|
||||
|
||||
// Read the INI file.
|
||||
let mut ini = Ini::new_cs();
|
||||
ini.set_multiline(true);
|
||||
let config = ini.load(cli.file).map_err(|msg| anyhow::anyhow!(msg))?;
|
||||
let config = ini.load(args.file).map_err(|msg| anyhow::anyhow!(msg))?;
|
||||
|
||||
// Read the pyproject.toml file.
|
||||
let pyproject = cli.pyproject.map(flake8_to_ruff::parse).transpose()?;
|
||||
let pyproject = args.pyproject.map(flake8_to_ruff::parse).transpose()?;
|
||||
let external_config = pyproject
|
||||
.as_ref()
|
||||
.and_then(|pyproject| pyproject.tool.as_ref())
|
||||
@@ -60,7 +60,7 @@ fn main() -> Result<()> {
|
||||
.unwrap_or_default();
|
||||
|
||||
// Create Ruff's pyproject.toml section.
|
||||
let pyproject = flake8_to_ruff::convert(&config, &external_config, cli.plugin)?;
|
||||
let pyproject = flake8_to_ruff::convert(&config, &external_config, args.plugin)?;
|
||||
println!("{}", toml::to_string_pretty(&pyproject)?);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "ruff"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
description = "An extremely fast Python linter, written in Rust."
|
||||
authors = [
|
||||
{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" },
|
||||
|
||||
@@ -61,3 +61,34 @@ except Exception as e:
|
||||
raise bad
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
import logging
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
logging.error("...")
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
logging.error("...", exc_info=False)
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
logging.error("...", exc_info=None)
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
logging.exception("...")
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
logging.error("...", exc_info=True)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Should emit:
|
||||
B009 - Line 18, 19, 20, 21, 22
|
||||
B010 - Line 33, 34, 35, 36
|
||||
B009 - Line 19, 20, 21, 22, 23, 24
|
||||
B010 - Line 40, 41, 42, 43, 44, 45
|
||||
"""
|
||||
|
||||
# Valid getattr usage
|
||||
@@ -13,10 +13,12 @@ getattr(foo, bar, None)
|
||||
getattr(foo, "123abc")
|
||||
getattr(foo, r"123\abc")
|
||||
getattr(foo, "except")
|
||||
getattr(foo, "__123abc")
|
||||
|
||||
# Invalid usage
|
||||
getattr(foo, "bar")
|
||||
getattr(foo, "_123abc")
|
||||
getattr(foo, "__123abc__")
|
||||
getattr(foo, "abc123")
|
||||
getattr(foo, r"abc123")
|
||||
_ = lambda x: getattr(x, "bar")
|
||||
@@ -27,6 +29,7 @@ if getattr(x, "bar"):
|
||||
setattr(foo, bar, None)
|
||||
setattr(foo, "bar{foo}".format(foo="a"), None)
|
||||
setattr(foo, "123abc", None)
|
||||
setattr(foo, "__123abc", None)
|
||||
setattr(foo, r"123\abc", None)
|
||||
setattr(foo, "except", None)
|
||||
_ = lambda x: setattr(x, "bar", 1)
|
||||
@@ -36,6 +39,7 @@ if setattr(x, "bar", 1):
|
||||
# Invalid usage
|
||||
setattr(foo, "bar", None)
|
||||
setattr(foo, "_123abc", None)
|
||||
setattr(foo, "__123abc__", None)
|
||||
setattr(foo, "abc123", None)
|
||||
setattr(foo, r"abc123", None)
|
||||
setattr(foo.bar, r"baz", None)
|
||||
|
||||
3
resources/test/fixtures/flake8_logging_format/G001.py
vendored
Normal file
3
resources/test/fixtures/flake8_logging_format/G001.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import logging
|
||||
|
||||
logging.info("Hello {}".format("World!"))
|
||||
3
resources/test/fixtures/flake8_logging_format/G002.py
vendored
Normal file
3
resources/test/fixtures/flake8_logging_format/G002.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import logging
|
||||
|
||||
logging.info("Hello %s" % "World!")
|
||||
3
resources/test/fixtures/flake8_logging_format/G003.py
vendored
Normal file
3
resources/test/fixtures/flake8_logging_format/G003.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import logging
|
||||
|
||||
logging.info("Hello" + " " + "World!")
|
||||
4
resources/test/fixtures/flake8_logging_format/G004.py
vendored
Normal file
4
resources/test/fixtures/flake8_logging_format/G004.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import logging
|
||||
|
||||
name = "world"
|
||||
logging.info(f"Hello {name}")
|
||||
3
resources/test/fixtures/flake8_logging_format/G010.py
vendored
Normal file
3
resources/test/fixtures/flake8_logging_format/G010.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import logging
|
||||
|
||||
logging.warn("Hello World!")
|
||||
8
resources/test/fixtures/flake8_logging_format/G101_1.py
vendored
Normal file
8
resources/test/fixtures/flake8_logging_format/G101_1.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import logging
|
||||
|
||||
logging.info(
|
||||
"Hello world!",
|
||||
extra={
|
||||
"name": "foobar",
|
||||
},
|
||||
)
|
||||
8
resources/test/fixtures/flake8_logging_format/G101_2.py
vendored
Normal file
8
resources/test/fixtures/flake8_logging_format/G101_2.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import logging
|
||||
|
||||
logging.info(
|
||||
"Hello world!",
|
||||
extra=dict(
|
||||
name="foobar",
|
||||
),
|
||||
)
|
||||
3
resources/test/fixtures/flake8_logging_format/G201.py
vendored
Normal file
3
resources/test/fixtures/flake8_logging_format/G201.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import logging
|
||||
|
||||
logging.error('Hello World', exc_info=True)
|
||||
3
resources/test/fixtures/flake8_logging_format/G202.py
vendored
Normal file
3
resources/test/fixtures/flake8_logging_format/G202.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import logging
|
||||
|
||||
logging.exception('Hello World', exc_info=True)
|
||||
7
resources/test/fixtures/flake8_logging_format/G_argparse_parser_error_ok.py
vendored
Normal file
7
resources/test/fixtures/flake8_logging_format/G_argparse_parser_error_ok.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("target_dir", type=Path)
|
||||
args = parser.parse_args()
|
||||
parser.error(f"Target directory {args.target_dir} does not exist")
|
||||
8
resources/test/fixtures/flake8_logging_format/G_extra_ok.py
vendored
Normal file
8
resources/test/fixtures/flake8_logging_format/G_extra_ok.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import logging
|
||||
|
||||
logging.info(
|
||||
"Hello {world}!",
|
||||
extra=dict(
|
||||
world="World",
|
||||
),
|
||||
)
|
||||
8
resources/test/fixtures/flake8_logging_format/G_extra_str_format_ok.py
vendored
Normal file
8
resources/test/fixtures/flake8_logging_format/G_extra_str_format_ok.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import logging
|
||||
|
||||
logging.info(
|
||||
"Hello {world}!",
|
||||
extra=dict(
|
||||
world="{}".format("World"),
|
||||
),
|
||||
)
|
||||
3
resources/test/fixtures/flake8_logging_format/G_simple_ok.py
vendored
Normal file
3
resources/test/fixtures/flake8_logging_format/G_simple_ok.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import logging
|
||||
|
||||
logging.info("Hello World!")
|
||||
3
resources/test/fixtures/flake8_logging_format/G_warnings_ok.py
vendored
Normal file
3
resources/test/fixtures/flake8_logging_format/G_warnings_ok.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import warnings
|
||||
|
||||
warnings.warn("Hello World!")
|
||||
35
resources/test/fixtures/flake8_pie/PIE790.py
vendored
35
resources/test/fixtures/flake8_pie/PIE790.py
vendored
@@ -1,12 +1,12 @@
|
||||
class Foo:
|
||||
"""buzz"""
|
||||
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
|
||||
if foo:
|
||||
"""foo"""
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
|
||||
def multi_statement() -> None:
|
||||
@@ -18,28 +18,28 @@ if foo:
|
||||
pass
|
||||
else:
|
||||
"""bar"""
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
|
||||
while True:
|
||||
pass
|
||||
else:
|
||||
"""bar"""
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
|
||||
for _ in range(10):
|
||||
pass
|
||||
else:
|
||||
"""bar"""
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
|
||||
async for _ in range(10):
|
||||
pass
|
||||
else:
|
||||
"""bar"""
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
|
||||
def foo() -> None:
|
||||
@@ -47,7 +47,7 @@ def foo() -> None:
|
||||
buzz
|
||||
"""
|
||||
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
|
||||
async def foo():
|
||||
@@ -55,14 +55,14 @@ async def foo():
|
||||
buzz
|
||||
"""
|
||||
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
"""
|
||||
buzz
|
||||
"""
|
||||
pass # PIE790
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@@ -71,29 +71,34 @@ try:
|
||||
bar()
|
||||
except ValueError:
|
||||
"""bar"""
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
|
||||
for _ in range(10):
|
||||
"""buzz"""
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
async for _ in range(10):
|
||||
"""buzz"""
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
while cond:
|
||||
"""buzz"""
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
|
||||
with bar:
|
||||
"""buzz"""
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
async with bar:
|
||||
"""buzz"""
|
||||
pass # PIE790
|
||||
pass
|
||||
|
||||
|
||||
def foo() -> None:
|
||||
"""buzz"""
|
||||
pass # bar
|
||||
|
||||
|
||||
class Foo:
|
||||
|
||||
16
resources/test/fixtures/flake8_type_checking/exempt_modules.py
vendored
Normal file
16
resources/test/fixtures/flake8_type_checking/exempt_modules.py
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
def f():
|
||||
import pandas as pd
|
||||
|
||||
x: pd.DataFrame
|
||||
|
||||
|
||||
def f():
|
||||
import pandas.core.frame as pd
|
||||
|
||||
x: pd.DataFrame
|
||||
|
||||
|
||||
def f():
|
||||
import flask
|
||||
|
||||
x: flask
|
||||
54
resources/test/fixtures/flake8_type_checking/strict.py
vendored
Normal file
54
resources/test/fixtures/flake8_type_checking/strict.py
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
def f():
|
||||
# Even in strict mode, this shouldn't rase an error, since `pkg` is used at runtime,
|
||||
# and implicitly imports `pkg.bar`.
|
||||
import pkg
|
||||
import pkg.bar
|
||||
|
||||
def test(value: pkg.bar.A):
|
||||
return pkg.B()
|
||||
|
||||
|
||||
def f():
|
||||
# Even in strict mode, this shouldn't rase an error, since `pkg.bar` is used at
|
||||
# runtime, and implicitly imports `pkg`.
|
||||
import pkg
|
||||
import pkg.bar
|
||||
|
||||
def test(value: pkg.A):
|
||||
return pkg.bar.B()
|
||||
|
||||
|
||||
def f():
|
||||
# In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
|
||||
import pkg
|
||||
from pkg import A
|
||||
|
||||
def test(value: A):
|
||||
return pkg.B()
|
||||
|
||||
|
||||
def f():
|
||||
# In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
|
||||
from pkg import A, B
|
||||
|
||||
def test(value: A):
|
||||
return B()
|
||||
|
||||
|
||||
def f():
|
||||
# Even in strict mode, this shouldn't rase an error, since `pkg.baz` is used at
|
||||
# runtime, and implicitly imports `pkg.bar`.
|
||||
import pkg.bar
|
||||
import pkg.baz
|
||||
|
||||
def test(value: pkg.bar.A):
|
||||
return pkg.baz.B()
|
||||
|
||||
|
||||
def f():
|
||||
# In un-strict mode, this _should_ rase an error, since `pkg` is used at runtime.
|
||||
import pkg
|
||||
from pkg.bar import A
|
||||
|
||||
def test(value: A):
|
||||
return pkg.B()
|
||||
31
resources/test/fixtures/tryceratops/TRY002.py
vendored
Normal file
31
resources/test/fixtures/tryceratops/TRY002.py
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
Violation:
|
||||
|
||||
Raising vanilla exception with custom message means it should be customized.
|
||||
"""
|
||||
|
||||
from somewhere import exceptions
|
||||
|
||||
|
||||
def func():
|
||||
a = 1
|
||||
if a == 1:
|
||||
raise Exception("Custom message")
|
||||
|
||||
b = 1
|
||||
if b == 1:
|
||||
raise Exception
|
||||
|
||||
|
||||
def ignore():
|
||||
try:
|
||||
a = 1
|
||||
except Exception as ex:
|
||||
# This is another violation, but this specific analyzer shouldn't care
|
||||
raise ex
|
||||
|
||||
|
||||
def anotherfunc():
|
||||
a = 1
|
||||
if a == 1:
|
||||
raise exceptions.Exception("Another except") # That's fine
|
||||
54
resources/test/fixtures/tryceratops/TRY003.py
vendored
Normal file
54
resources/test/fixtures/tryceratops/TRY003.py
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
class CustomException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def func():
|
||||
a = 1
|
||||
if a == 1:
|
||||
raise CustomException("Long message")
|
||||
elif a == 2:
|
||||
raise CustomException("Short") # This is acceptable
|
||||
elif a == 3:
|
||||
raise CustomException("its_code_not_message") # This is acceptable
|
||||
|
||||
|
||||
def ignore():
|
||||
try:
|
||||
a = 1
|
||||
except Exception as ex:
|
||||
# This is another violation, but this specific analyzer shouldn't care
|
||||
raise ex
|
||||
|
||||
|
||||
class BadArgCantBeEven(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class GoodArgCantBeEven(Exception):
|
||||
def __init__(self, arg):
|
||||
super().__init__(f"The argument '{arg}' should be even")
|
||||
|
||||
|
||||
def bad(a):
|
||||
if a % 2 == 0:
|
||||
raise BadArgCantBeEven(f"The argument '{a}' should be even")
|
||||
|
||||
|
||||
def another_bad(a):
|
||||
if a % 2 == 0:
|
||||
raise BadArgCantBeEven(f"The argument {a} should not be odd.")
|
||||
|
||||
|
||||
def and_another_bad(a):
|
||||
if a % 2 == 0:
|
||||
raise BadArgCantBeEven("The argument `a` should not be odd.")
|
||||
|
||||
|
||||
def good(a: int):
|
||||
if a % 2 == 0:
|
||||
raise GoodArgCantBeEven(a)
|
||||
|
||||
|
||||
def another_good(a):
|
||||
if a % 2 == 0:
|
||||
raise GoodArgCantBeEven(a)
|
||||
52
resources/test/fixtures/tryceratops/TRY400.py
vendored
Normal file
52
resources/test/fixtures/tryceratops/TRY400.py
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
Violation:
|
||||
Use '.exception' over '.error' inside except blocks
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def bad():
|
||||
try:
|
||||
a = 1
|
||||
except Exception:
|
||||
logging.error("Context message here")
|
||||
|
||||
if True:
|
||||
logging.error("Context message here")
|
||||
|
||||
|
||||
def bad():
|
||||
try:
|
||||
a = 1
|
||||
except Exception:
|
||||
logger.error("Context message here")
|
||||
|
||||
if True:
|
||||
logger.error("Context message here")
|
||||
|
||||
|
||||
def bad():
|
||||
try:
|
||||
a = 1
|
||||
except Exception:
|
||||
self.logger.error("Context message here")
|
||||
|
||||
if True:
|
||||
self.logger.error("Context message here")
|
||||
|
||||
|
||||
def good():
|
||||
try:
|
||||
a = 1
|
||||
except Exception:
|
||||
logger.exception("Context message here")
|
||||
|
||||
|
||||
def good():
|
||||
try:
|
||||
a = 1
|
||||
except Exception:
|
||||
foo.exception("Context message here")
|
||||
@@ -230,6 +230,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"flake8-type-checking": {
|
||||
"description": "Options for the `flake8-type-checking` plugin.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Flake8TypeCheckingOptions"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"flake8-unused-arguments": {
|
||||
"description": "Options for the `flake8-unused-arguments` plugin.",
|
||||
"anyOf": [
|
||||
@@ -421,7 +432,7 @@
|
||||
]
|
||||
},
|
||||
"src": {
|
||||
"description": "The source code paths to consider, e.g., when resolving first- vs. third-party imports.\n\nAs an example: given a Python package structure like:\n\n```text my_package/ pyproject.toml src/ my_package/ __init__.py foo.py bar.py ```\n\nThe `src` directory should be included in `source` (e.g., `source = [\"src\"]`), such that when resolving imports, `my_package.foo` is considered a first-party import.\n\nThis field supports globs. For example, if you have a series of Python packages in a `python_modules` directory, `src = [\"python_modules/*\"]` would expand to incorporate all of the packages in that directory. User home directory and environment variables will also be expanded.",
|
||||
"description": "The source code paths to consider, e.g., when resolving first- vs. third-party imports.\n\nAs an example: given a Python package structure like:\n\n```text my_package/ pyproject.toml src/ my_package/ __init__.py foo.py bar.py ```\n\nThe `src` directory should be included in the `src` option (e.g., `src = [\"src\"]`), such that when resolving imports, `my_package.foo` is considered a first-party import.\n\nThis field supports globs. For example, if you have a series of Python packages in a `python_modules` directory, `src = [\"python_modules/*\"]` would expand to incorporate all of the packages in that directory. User home directory and environment variables will also be expanded.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
@@ -822,6 +833,29 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Flake8TypeCheckingOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"exempt-modules": {
|
||||
"description": "Exempt certain modules from needing to be moved into type-checking blocks.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"strict": {
|
||||
"description": "Enforce TC001, TC002, and TC003 rules even when valid runtime imports are present for the same module. See: https://github.com/snok/flake8-type-checking#strict.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Flake8UnusedArgumentsOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1475,6 +1509,22 @@
|
||||
"FBT001",
|
||||
"FBT002",
|
||||
"FBT003",
|
||||
"G",
|
||||
"G0",
|
||||
"G00",
|
||||
"G001",
|
||||
"G002",
|
||||
"G003",
|
||||
"G004",
|
||||
"G01",
|
||||
"G010",
|
||||
"G1",
|
||||
"G10",
|
||||
"G101",
|
||||
"G2",
|
||||
"G20",
|
||||
"G201",
|
||||
"G202",
|
||||
"I",
|
||||
"I0",
|
||||
"I00",
|
||||
@@ -1776,6 +1826,8 @@
|
||||
"TRY",
|
||||
"TRY0",
|
||||
"TRY00",
|
||||
"TRY002",
|
||||
"TRY003",
|
||||
"TRY004",
|
||||
"TRY2",
|
||||
"TRY20",
|
||||
@@ -1785,6 +1837,9 @@
|
||||
"TRY30",
|
||||
"TRY300",
|
||||
"TRY301",
|
||||
"TRY4",
|
||||
"TRY40",
|
||||
"TRY400",
|
||||
"UP",
|
||||
"UP0",
|
||||
"UP00",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
@@ -19,124 +19,166 @@ use rustc_hash::FxHashMap;
|
||||
)]
|
||||
#[command(version)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Cli {
|
||||
pub struct Args {
|
||||
#[arg(required_unless_present_any = ["clean", "explain", "generate_shell_completion"])]
|
||||
pub files: Vec<PathBuf>,
|
||||
/// Path to the `pyproject.toml` or `ruff.toml` file to use for
|
||||
/// configuration.
|
||||
#[arg(long, conflicts_with = "isolated")]
|
||||
pub config: Option<PathBuf>,
|
||||
/// Enable verbose logging.
|
||||
#[arg(short, long, group = "verbosity")]
|
||||
pub verbose: bool,
|
||||
/// Print lint violations, but nothing else.
|
||||
#[arg(short, long, group = "verbosity")]
|
||||
pub quiet: bool,
|
||||
/// Disable all logging (but still exit with status code "1" upon detecting
|
||||
/// lint violations).
|
||||
#[arg(short, long, group = "verbosity")]
|
||||
pub silent: bool,
|
||||
/// Exit with status code "0", even upon detecting lint violations.
|
||||
#[arg(short, long)]
|
||||
pub exit_zero: bool,
|
||||
/// Run in watch mode by re-running whenever files change.
|
||||
#[arg(short, long)]
|
||||
pub watch: bool,
|
||||
/// Attempt to automatically fix lint violations.
|
||||
#[arg(long, overrides_with("no_fix"))]
|
||||
fix: bool,
|
||||
#[clap(long, overrides_with("fix"), hide = true)]
|
||||
no_fix: bool,
|
||||
/// Show violations with source code.
|
||||
#[arg(long, overrides_with("no_show_source"))]
|
||||
show_source: bool,
|
||||
#[clap(long, overrides_with("show_source"), hide = true)]
|
||||
no_show_source: bool,
|
||||
/// Avoid writing any fixed files back; instead, output a diff for each
|
||||
/// changed file to stdout.
|
||||
#[arg(long)]
|
||||
pub diff: bool,
|
||||
/// Run in watch mode by re-running whenever files change.
|
||||
#[arg(short, long)]
|
||||
pub watch: bool,
|
||||
/// Fix any fixable lint violations, but don't report on leftover
|
||||
/// violations. Implies `--fix`.
|
||||
#[arg(long, overrides_with("no_fix_only"))]
|
||||
fix_only: bool,
|
||||
#[clap(long, overrides_with("fix_only"), hide = true)]
|
||||
no_fix_only: bool,
|
||||
/// Avoid writing any fixed files back; instead, output a diff for each
|
||||
/// changed file to stdout.
|
||||
#[arg(long)]
|
||||
pub diff: bool,
|
||||
/// Disable cache reads.
|
||||
#[arg(short, long)]
|
||||
pub no_cache: bool,
|
||||
/// Ignore all configuration files.
|
||||
#[arg(long, conflicts_with = "config")]
|
||||
pub isolated: bool,
|
||||
/// Comma-separated list of rule codes to enable (or ALL, to enable all
|
||||
/// rules).
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub select: Option<Vec<RuleSelector>>,
|
||||
/// Like --select, but adds additional rule codes on top of the selected
|
||||
/// ones.
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub extend_select: Option<Vec<RuleSelector>>,
|
||||
/// Comma-separated list of rule codes to disable.
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub ignore: Option<Vec<RuleSelector>>,
|
||||
/// Like --ignore, but adds additional rule codes on top of the ignored
|
||||
/// ones.
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub extend_ignore: Option<Vec<RuleSelector>>,
|
||||
/// List of paths, used to omit files and/or directories from analysis.
|
||||
#[arg(long, value_delimiter = ',', value_name = "FILE_PATTERN")]
|
||||
pub exclude: Option<Vec<FilePattern>>,
|
||||
/// Like --exclude, but adds additional files and directories on top of
|
||||
/// those already excluded.
|
||||
#[arg(long, value_delimiter = ',', value_name = "FILE_PATTERN")]
|
||||
pub extend_exclude: Option<Vec<FilePattern>>,
|
||||
/// List of rule codes to treat as eligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub fixable: Option<Vec<RuleSelector>>,
|
||||
/// List of rule codes to treat as ineligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub unfixable: Option<Vec<RuleSelector>>,
|
||||
/// List of mappings from file pattern to code to exclude
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub per_file_ignores: Option<Vec<PatternPrefixPair>>,
|
||||
/// Output serialization format for violations.
|
||||
#[arg(long, value_enum, env = "RUFF_FORMAT")]
|
||||
pub format: Option<SerializationFormat>,
|
||||
/// The name of the file when passing it through stdin.
|
||||
#[arg(long)]
|
||||
pub stdin_filename: Option<PathBuf>,
|
||||
/// Path to the cache directory.
|
||||
#[arg(long, env = "RUFF_CACHE_DIR")]
|
||||
pub cache_dir: Option<PathBuf>,
|
||||
/// Show violations with source code.
|
||||
#[arg(long, overrides_with("no_show_source"))]
|
||||
show_source: bool,
|
||||
#[clap(long, overrides_with("show_source"), hide = true)]
|
||||
no_show_source: bool,
|
||||
/// Path to the `pyproject.toml` or `ruff.toml` file to use for
|
||||
/// configuration.
|
||||
#[arg(long, conflicts_with = "isolated")]
|
||||
pub config: Option<PathBuf>,
|
||||
/// Comma-separated list of rule codes to enable (or ALL, to enable all
|
||||
/// rules).
|
||||
#[arg(
|
||||
long,
|
||||
value_delimiter = ',',
|
||||
value_name = "RULE_CODE",
|
||||
help_heading = "Rule selection"
|
||||
)]
|
||||
pub select: Option<Vec<RuleSelector>>,
|
||||
/// Comma-separated list of rule codes to disable.
|
||||
#[arg(
|
||||
long,
|
||||
value_delimiter = ',',
|
||||
value_name = "RULE_CODE",
|
||||
help_heading = "Rule selection"
|
||||
)]
|
||||
pub ignore: Option<Vec<RuleSelector>>,
|
||||
/// Like --select, but adds additional rule codes on top of the selected
|
||||
/// ones.
|
||||
#[arg(
|
||||
long,
|
||||
value_delimiter = ',',
|
||||
value_name = "RULE_CODE",
|
||||
help_heading = "Rule selection"
|
||||
)]
|
||||
pub extend_select: Option<Vec<RuleSelector>>,
|
||||
/// Like --ignore, but adds additional rule codes on top of the ignored
|
||||
/// ones.
|
||||
#[arg(
|
||||
long,
|
||||
value_delimiter = ',',
|
||||
value_name = "RULE_CODE",
|
||||
help_heading = "Rule selection"
|
||||
)]
|
||||
pub extend_ignore: Option<Vec<RuleSelector>>,
|
||||
/// List of mappings from file pattern to code to exclude
|
||||
#[arg(long, value_delimiter = ',', help_heading = "Rule selection")]
|
||||
pub per_file_ignores: Option<Vec<PatternPrefixPair>>,
|
||||
/// List of paths, used to omit files and/or directories from analysis.
|
||||
#[arg(
|
||||
long,
|
||||
value_delimiter = ',',
|
||||
value_name = "FILE_PATTERN",
|
||||
help_heading = "File selection"
|
||||
)]
|
||||
pub exclude: Option<Vec<FilePattern>>,
|
||||
/// Like --exclude, but adds additional files and directories on top of
|
||||
/// those already excluded.
|
||||
#[arg(
|
||||
long,
|
||||
value_delimiter = ',',
|
||||
value_name = "FILE_PATTERN",
|
||||
help_heading = "File selection"
|
||||
)]
|
||||
pub extend_exclude: Option<Vec<FilePattern>>,
|
||||
/// List of rule codes to treat as eligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(
|
||||
long,
|
||||
value_delimiter = ',',
|
||||
value_name = "RULE_CODE",
|
||||
help_heading = "Rule selection"
|
||||
)]
|
||||
pub fixable: Option<Vec<RuleSelector>>,
|
||||
/// List of rule codes to treat as ineligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(
|
||||
long,
|
||||
value_delimiter = ',',
|
||||
value_name = "RULE_CODE",
|
||||
help_heading = "Rule selection"
|
||||
)]
|
||||
pub unfixable: Option<Vec<RuleSelector>>,
|
||||
/// Respect file exclusions via `.gitignore` and other standard ignore
|
||||
/// files.
|
||||
#[arg(long, overrides_with("no_respect_gitignore"))]
|
||||
#[arg(
|
||||
long,
|
||||
overrides_with("no_respect_gitignore"),
|
||||
help_heading = "File selection"
|
||||
)]
|
||||
respect_gitignore: bool,
|
||||
#[clap(long, overrides_with("respect_gitignore"), hide = true)]
|
||||
no_respect_gitignore: bool,
|
||||
/// Enforce exclusions, even for paths passed to Ruff directly on the
|
||||
/// command-line.
|
||||
#[arg(long, overrides_with("no_force_exclude"))]
|
||||
#[arg(
|
||||
long,
|
||||
overrides_with("no_force_exclude"),
|
||||
help_heading = "File selection"
|
||||
)]
|
||||
force_exclude: bool,
|
||||
#[clap(long, overrides_with("force_exclude"), hide = true)]
|
||||
no_force_exclude: bool,
|
||||
/// Enable or disable automatic update checks.
|
||||
#[arg(long, overrides_with("no_update_check"))]
|
||||
update_check: bool,
|
||||
#[clap(long, overrides_with("update_check"), hide = true)]
|
||||
no_update_check: bool,
|
||||
/// Regular expression matching the name of dummy variables.
|
||||
#[arg(long)]
|
||||
pub dummy_variable_rgx: Option<Regex>,
|
||||
/// The minimum Python version that should be supported.
|
||||
#[arg(long)]
|
||||
#[arg(long, help_heading = "Rule configuration")]
|
||||
pub target_version: Option<PythonVersion>,
|
||||
/// Set the line-length for length-associated rules and automatic
|
||||
/// formatting.
|
||||
#[arg(long)]
|
||||
#[arg(long, help_heading = "Rule configuration")]
|
||||
pub line_length: Option<usize>,
|
||||
/// Regular expression matching the name of dummy variables.
|
||||
#[arg(long, help_heading = "Rule configuration")]
|
||||
pub dummy_variable_rgx: Option<Regex>,
|
||||
/// Disable cache reads.
|
||||
#[arg(short, long, help_heading = "Miscellaneous")]
|
||||
pub no_cache: bool,
|
||||
/// Ignore all configuration files.
|
||||
#[arg(long, conflicts_with = "config", help_heading = "Miscellaneous")]
|
||||
pub isolated: bool,
|
||||
/// Path to the cache directory.
|
||||
#[arg(long, env = "RUFF_CACHE_DIR", help_heading = "Miscellaneous")]
|
||||
pub cache_dir: Option<PathBuf>,
|
||||
/// The name of the file when passing it through stdin.
|
||||
#[arg(long, help_heading = "Miscellaneous")]
|
||||
pub stdin_filename: Option<PathBuf>,
|
||||
/// Exit with status code "0", even upon detecting lint violations.
|
||||
#[arg(short, long, help_heading = "Miscellaneous")]
|
||||
pub exit_zero: bool,
|
||||
/// Enable or disable automatic update checks.
|
||||
#[arg(
|
||||
long,
|
||||
overrides_with("no_update_check"),
|
||||
help_heading = "Miscellaneous"
|
||||
)]
|
||||
update_check: bool,
|
||||
#[clap(long, overrides_with("update_check"), hide = true)]
|
||||
no_update_check: bool,
|
||||
/// Enable automatic additions of `noqa` directives to failing lines.
|
||||
#[arg(
|
||||
long,
|
||||
@@ -151,25 +193,11 @@ pub struct Cli {
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub add_noqa: bool,
|
||||
/// Clear any caches in the current directory or any subdirectories.
|
||||
#[arg(
|
||||
long,
|
||||
// Fake subcommands.
|
||||
conflicts_with = "add_noqa",
|
||||
// conflicts_with = "clean",
|
||||
conflicts_with = "explain",
|
||||
conflicts_with = "generate_shell_completion",
|
||||
conflicts_with = "show_files",
|
||||
conflicts_with = "show_settings",
|
||||
// Unsupported default-command arguments.
|
||||
conflicts_with = "stdin_filename",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub clean: bool,
|
||||
/// Explain a rule.
|
||||
#[arg(
|
||||
long,
|
||||
value_parser=Rule::from_code,
|
||||
help_heading="Subcommands",
|
||||
// Fake subcommands.
|
||||
conflicts_with = "add_noqa",
|
||||
conflicts_with = "clean",
|
||||
@@ -182,6 +210,22 @@ pub struct Cli {
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub explain: Option<&'static Rule>,
|
||||
/// Clear any caches in the current directory or any subdirectories.
|
||||
#[arg(
|
||||
long,
|
||||
help_heading="Subcommands",
|
||||
// Fake subcommands.
|
||||
conflicts_with = "add_noqa",
|
||||
// conflicts_with = "clean",
|
||||
conflicts_with = "explain",
|
||||
conflicts_with = "generate_shell_completion",
|
||||
conflicts_with = "show_files",
|
||||
conflicts_with = "show_settings",
|
||||
// Unsupported default-command arguments.
|
||||
conflicts_with = "stdin_filename",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub clean: bool,
|
||||
/// Generate shell completion
|
||||
#[arg(
|
||||
long,
|
||||
@@ -229,9 +273,40 @@ pub struct Cli {
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub show_settings: bool,
|
||||
#[clap(flatten)]
|
||||
pub log_level_args: LogLevelArgs,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[derive(Debug, clap::Args)]
|
||||
pub struct LogLevelArgs {
|
||||
/// Enable verbose logging.
|
||||
#[arg(short, long, group = "verbosity", help_heading = "Log levels")]
|
||||
pub verbose: bool,
|
||||
/// Print lint violations, but nothing else.
|
||||
#[arg(short, long, group = "verbosity", help_heading = "Log levels")]
|
||||
pub quiet: bool,
|
||||
/// Disable all logging (but still exit with status code "1" upon detecting
|
||||
/// lint violations).
|
||||
#[arg(short, long, group = "verbosity", help_heading = "Log levels")]
|
||||
pub silent: bool,
|
||||
}
|
||||
|
||||
impl From<&LogLevelArgs> for LogLevel {
|
||||
fn from(args: &LogLevelArgs) -> Self {
|
||||
if args.silent {
|
||||
LogLevel::Silent
|
||||
} else if args.quiet {
|
||||
LogLevel::Quiet
|
||||
} else if args.verbose {
|
||||
LogLevel::Verbose
|
||||
} else {
|
||||
LogLevel::Default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Args {
|
||||
/// Partition the CLI into command-line arguments and configuration
|
||||
/// overrides.
|
||||
pub fn partition(self) -> (Arguments, Overrides) {
|
||||
@@ -247,12 +322,9 @@ impl Cli {
|
||||
generate_shell_completion: self.generate_shell_completion,
|
||||
isolated: self.isolated,
|
||||
no_cache: self.no_cache,
|
||||
quiet: self.quiet,
|
||||
show_files: self.show_files,
|
||||
show_settings: self.show_settings,
|
||||
silent: self.silent,
|
||||
stdin_filename: self.stdin_filename,
|
||||
verbose: self.verbose,
|
||||
watch: self.watch,
|
||||
},
|
||||
Overrides {
|
||||
@@ -308,12 +380,9 @@ pub struct Arguments {
|
||||
pub generate_shell_completion: Option<clap_complete_command::Shell>,
|
||||
pub isolated: bool,
|
||||
pub no_cache: bool,
|
||||
pub quiet: bool,
|
||||
pub show_files: bool,
|
||||
pub show_settings: bool,
|
||||
pub silent: bool,
|
||||
pub stdin_filename: Option<PathBuf>,
|
||||
pub verbose: bool,
|
||||
pub watch: bool,
|
||||
}
|
||||
|
||||
@@ -420,19 +489,6 @@ impl ConfigProcessor for &Overrides {
|
||||
}
|
||||
}
|
||||
|
||||
/// Map the CLI settings to a `LogLevel`.
|
||||
pub fn extract_log_level(cli: &Arguments) -> LogLevel {
|
||||
if cli.silent {
|
||||
LogLevel::Silent
|
||||
} else if cli.quiet {
|
||||
LogLevel::Quiet
|
||||
} else if cli.verbose {
|
||||
LogLevel::Verbose
|
||||
} else {
|
||||
LogLevel::Default
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a list of `PatternPrefixPair` structs to `PerFileIgnore`.
|
||||
pub fn collect_per_file_ignores(pairs: Vec<PatternPrefixPair>) -> Vec<PerFileIgnore> {
|
||||
let mut per_file_ignores: FxHashMap<String, Vec<RuleSelector>> = FxHashMap::default();
|
||||
@@ -16,15 +16,15 @@ use ruff::linter::add_noqa_to_path;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::message::{Location, Message};
|
||||
use ruff::registry::{Linter, Rule, RuleNamespace};
|
||||
use ruff::resolver::{FileDiscovery, PyprojectDiscovery};
|
||||
use ruff::resolver::PyprojectDiscovery;
|
||||
use ruff::settings::flags;
|
||||
use ruff::settings::types::SerializationFormat;
|
||||
use ruff::{fix, fs, packaging, resolver, warn_user_once, AutofixAvailability, IOError};
|
||||
use serde::Serialize;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::args::Overrides;
|
||||
use crate::cache;
|
||||
use crate::cli::Overrides;
|
||||
use crate::diagnostics::{lint_path, lint_stdin, Diagnostics};
|
||||
use crate::iterators::par_iter;
|
||||
|
||||
@@ -32,15 +32,13 @@ use crate::iterators::par_iter;
|
||||
pub fn run(
|
||||
files: &[PathBuf],
|
||||
pyproject_strategy: &PyprojectDiscovery,
|
||||
file_strategy: &FileDiscovery,
|
||||
overrides: &Overrides,
|
||||
cache: flags::Cache,
|
||||
autofix: fix::FixMode,
|
||||
) -> Result<Diagnostics> {
|
||||
// Collect all the Python files to check.
|
||||
let start = Instant::now();
|
||||
let (paths, resolver) =
|
||||
resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?;
|
||||
let (paths, resolver) = resolver::python_files_in_path(files, pyproject_strategy, overrides)?;
|
||||
let duration = start.elapsed();
|
||||
debug!("Identified files to lint in: {:?}", duration);
|
||||
|
||||
@@ -49,9 +47,6 @@ pub fn run(
|
||||
return Ok(Diagnostics::default());
|
||||
}
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
resolver.validate(pyproject_strategy)?;
|
||||
|
||||
// Initialize the cache.
|
||||
if matches!(cache, flags::Cache::Enabled) {
|
||||
match &pyproject_strategy {
|
||||
@@ -156,12 +151,11 @@ fn read_from_stdin() -> Result<String> {
|
||||
pub fn run_stdin(
|
||||
filename: Option<&Path>,
|
||||
pyproject_strategy: &PyprojectDiscovery,
|
||||
file_strategy: &FileDiscovery,
|
||||
overrides: &Overrides,
|
||||
autofix: fix::FixMode,
|
||||
) -> Result<Diagnostics> {
|
||||
if let Some(filename) = filename {
|
||||
if !resolver::python_file_at_path(filename, pyproject_strategy, file_strategy, overrides)? {
|
||||
if !resolver::python_file_at_path(filename, pyproject_strategy, overrides)? {
|
||||
return Ok(Diagnostics::default());
|
||||
}
|
||||
}
|
||||
@@ -182,13 +176,11 @@ pub fn run_stdin(
|
||||
pub fn add_noqa(
|
||||
files: &[PathBuf],
|
||||
pyproject_strategy: &PyprojectDiscovery,
|
||||
file_strategy: &FileDiscovery,
|
||||
overrides: &Overrides,
|
||||
) -> Result<usize> {
|
||||
// Collect all the files to check.
|
||||
let start = Instant::now();
|
||||
let (paths, resolver) =
|
||||
resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?;
|
||||
let (paths, resolver) = resolver::python_files_in_path(files, pyproject_strategy, overrides)?;
|
||||
let duration = start.elapsed();
|
||||
debug!("Identified files to lint in: {:?}", duration);
|
||||
|
||||
@@ -197,9 +189,6 @@ pub fn add_noqa(
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
resolver.validate(pyproject_strategy)?;
|
||||
|
||||
let start = Instant::now();
|
||||
let modifications: usize = par_iter(&paths)
|
||||
.flatten()
|
||||
@@ -226,15 +215,10 @@ pub fn add_noqa(
|
||||
pub fn show_settings(
|
||||
files: &[PathBuf],
|
||||
pyproject_strategy: &PyprojectDiscovery,
|
||||
file_strategy: &FileDiscovery,
|
||||
overrides: &Overrides,
|
||||
) -> Result<()> {
|
||||
// Collect all files in the hierarchy.
|
||||
let (paths, resolver) =
|
||||
resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?;
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
resolver.validate(pyproject_strategy)?;
|
||||
let (paths, resolver) = resolver::python_files_in_path(files, pyproject_strategy, overrides)?;
|
||||
|
||||
// Print the list of files.
|
||||
let Some(entry) = paths
|
||||
@@ -255,21 +239,16 @@ pub fn show_settings(
|
||||
pub fn show_files(
|
||||
files: &[PathBuf],
|
||||
pyproject_strategy: &PyprojectDiscovery,
|
||||
file_strategy: &FileDiscovery,
|
||||
overrides: &Overrides,
|
||||
) -> Result<()> {
|
||||
// Collect all files in the hierarchy.
|
||||
let (paths, resolver) =
|
||||
resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?;
|
||||
let (paths, _resolver) = resolver::python_files_in_path(files, pyproject_strategy, overrides)?;
|
||||
|
||||
if paths.is_empty() {
|
||||
warn_user_once!("No Python files found under the given path(s)");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
resolver.validate(pyproject_strategy)?;
|
||||
|
||||
// Print the list of files.
|
||||
for entry in paths
|
||||
.iter()
|
||||
|
||||
@@ -42,9 +42,6 @@ pub fn lint_path(
|
||||
cache: flags::Cache,
|
||||
autofix: fix::FixMode,
|
||||
) -> Result<Diagnostics> {
|
||||
// Validate the `Settings` and return any errors.
|
||||
settings.lib.validate()?;
|
||||
|
||||
// Check the cache.
|
||||
// TODO(charlie): `fixer::Mode::Apply` and `fixer::Mode::Diff` both have
|
||||
// side-effects that aren't captured in the cache. (In practice, it's fine
|
||||
@@ -116,9 +113,6 @@ pub fn lint_stdin(
|
||||
settings: &Settings,
|
||||
autofix: fix::FixMode,
|
||||
) -> Result<Diagnostics> {
|
||||
// Validate the `Settings` and return any errors.
|
||||
settings.validate()?;
|
||||
|
||||
// Lint the inputs.
|
||||
let (messages, fixed) = if matches!(autofix, fix::FixMode::Apply | fix::FixMode::Diff) {
|
||||
let (transformed, fixed, messages) = lint_fix(
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::must_use_candidate, dead_code)]
|
||||
|
||||
mod cli;
|
||||
mod args;
|
||||
|
||||
use clap::CommandFactory;
|
||||
|
||||
/// Returns the output of `ruff --help`.
|
||||
pub fn help() -> String {
|
||||
cli::Cli::command().render_help().to_string()
|
||||
args::Args::command().render_help().to_string()
|
||||
}
|
||||
|
||||
@@ -8,90 +8,35 @@
|
||||
)]
|
||||
|
||||
use std::io::{self};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::process::ExitCode;
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
use ::ruff::logging::{set_up_logging, LogLevel};
|
||||
use ::ruff::resolver::{
|
||||
resolve_settings_with_processor, ConfigProcessor, FileDiscovery, PyprojectDiscovery, Relativity,
|
||||
};
|
||||
use ::ruff::settings::configuration::Configuration;
|
||||
use ::ruff::settings::pyproject;
|
||||
use ::ruff::resolver::PyprojectDiscovery;
|
||||
use ::ruff::settings::types::SerializationFormat;
|
||||
use ::ruff::{fix, fs, warn_user_once};
|
||||
use anyhow::Result;
|
||||
use args::Args;
|
||||
use clap::{CommandFactory, Parser};
|
||||
use cli::{extract_log_level, Cli, Overrides};
|
||||
use colored::Colorize;
|
||||
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
||||
use path_absolutize::path_dedot;
|
||||
use printer::{Printer, Violations};
|
||||
use ruff::settings::{AllSettings, CliSettings};
|
||||
use ruff::settings::CliSettings;
|
||||
|
||||
pub(crate) mod args;
|
||||
mod cache;
|
||||
mod cli;
|
||||
mod commands;
|
||||
mod diagnostics;
|
||||
mod iterators;
|
||||
mod printer;
|
||||
mod resolve;
|
||||
#[cfg(all(feature = "update-informer"))]
|
||||
pub mod updates;
|
||||
|
||||
/// Resolve the relevant settings strategy and defaults for the current
|
||||
/// invocation.
|
||||
fn resolve(
|
||||
isolated: bool,
|
||||
config: Option<&Path>,
|
||||
overrides: &Overrides,
|
||||
stdin_filename: Option<&Path>,
|
||||
) -> Result<PyprojectDiscovery> {
|
||||
if isolated {
|
||||
// First priority: if we're running in isolated mode, use the default settings.
|
||||
let mut config = Configuration::default();
|
||||
overrides.process_config(&mut config);
|
||||
let settings = AllSettings::from_configuration(config, &path_dedot::CWD)?;
|
||||
Ok(PyprojectDiscovery::Fixed(settings))
|
||||
} else if let Some(pyproject) = config {
|
||||
// Second priority: the user specified a `pyproject.toml` file. Use that
|
||||
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
|
||||
// current working directory. (This matches ESLint's behavior.)
|
||||
let settings = resolve_settings_with_processor(pyproject, &Relativity::Cwd, overrides)?;
|
||||
Ok(PyprojectDiscovery::Fixed(settings))
|
||||
} else if let Some(pyproject) = pyproject::find_settings_toml(
|
||||
stdin_filename
|
||||
.as_ref()
|
||||
.unwrap_or(&path_dedot::CWD.as_path()),
|
||||
)? {
|
||||
// Third priority: find a `pyproject.toml` file in either an ancestor of
|
||||
// `stdin_filename` (if set) or the current working path all paths relative to
|
||||
// that directory. (With `Strategy::Hierarchical`, we'll end up finding
|
||||
// the "closest" `pyproject.toml` file for every Python file later on,
|
||||
// so these act as the "default" settings.)
|
||||
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Parent, overrides)?;
|
||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||
} else if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
||||
// Fourth priority: find a user-specific `pyproject.toml`, but resolve all paths
|
||||
// relative the current working directory. (With `Strategy::Hierarchical`, we'll
|
||||
// end up the "closest" `pyproject.toml` file for every Python file later on, so
|
||||
// these act as the "default" settings.)
|
||||
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Cwd, overrides)?;
|
||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||
} else {
|
||||
// Fallback: load Ruff's default settings, and resolve all paths relative to the
|
||||
// current working directory. (With `Strategy::Hierarchical`, we'll end up the
|
||||
// "closest" `pyproject.toml` file for every Python file later on, so these act
|
||||
// as the "default" settings.)
|
||||
let mut config = Configuration::default();
|
||||
overrides.process_config(&mut config);
|
||||
let settings = AllSettings::from_configuration(config, &path_dedot::CWD)?;
|
||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_main() -> Result<ExitCode> {
|
||||
// Extract command-line arguments.
|
||||
let (cli, overrides) = Cli::parse().partition();
|
||||
let args = Args::parse();
|
||||
|
||||
let default_panic_hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
@@ -108,11 +53,13 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
default_panic_hook(info);
|
||||
}));
|
||||
|
||||
let log_level = extract_log_level(&cli);
|
||||
let log_level: LogLevel = (&args.log_level_args).into();
|
||||
set_up_logging(&log_level)?;
|
||||
|
||||
let (cli, overrides) = args.partition();
|
||||
|
||||
if let Some(shell) = cli.generate_shell_completion {
|
||||
shell.generate(&mut Cli::command(), &mut io::stdout());
|
||||
shell.generate(&mut Args::command(), &mut io::stdout());
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.clean {
|
||||
@@ -122,31 +69,15 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
|
||||
// Construct the "default" settings. These are used when no `pyproject.toml`
|
||||
// files are present, or files are injected from outside of the hierarchy.
|
||||
let pyproject_strategy = resolve(
|
||||
let pyproject_strategy = resolve::resolve(
|
||||
cli.isolated,
|
||||
cli.config.as_deref(),
|
||||
&overrides,
|
||||
cli.stdin_filename.as_deref(),
|
||||
)?;
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
match &pyproject_strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => settings.lib.validate()?,
|
||||
PyprojectDiscovery::Hierarchical(settings) => settings.lib.validate()?,
|
||||
};
|
||||
|
||||
// Extract options that are included in `Settings`, but only apply at the top
|
||||
// level.
|
||||
let file_strategy = FileDiscovery {
|
||||
force_exclude: match &pyproject_strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => settings.lib.force_exclude,
|
||||
PyprojectDiscovery::Hierarchical(settings) => settings.lib.force_exclude,
|
||||
},
|
||||
respect_gitignore: match &pyproject_strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => settings.lib.respect_gitignore,
|
||||
PyprojectDiscovery::Hierarchical(settings) => settings.lib.respect_gitignore,
|
||||
},
|
||||
};
|
||||
let CliSettings {
|
||||
fix,
|
||||
fix_only,
|
||||
@@ -163,11 +94,11 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.show_settings {
|
||||
commands::show_settings(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||
commands::show_settings(&cli.files, &pyproject_strategy, &overrides)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.show_files {
|
||||
commands::show_files(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||
commands::show_files(&cli.files, &pyproject_strategy, &overrides)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
@@ -205,7 +136,13 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
}
|
||||
|
||||
let printer = Printer::new(&format, &log_level, &autofix, &violations);
|
||||
if cli.watch {
|
||||
|
||||
if cli.add_noqa {
|
||||
let modifications = commands::add_noqa(&cli.files, &pyproject_strategy, &overrides)?;
|
||||
if modifications > 0 && log_level >= LogLevel::Default {
|
||||
println!("Added {modifications} noqa directives.");
|
||||
}
|
||||
} else if cli.watch {
|
||||
if !matches!(autofix, fix::FixMode::None) {
|
||||
warn_user_once!("--fix is not enabled in watch mode.");
|
||||
}
|
||||
@@ -220,7 +157,6 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
let messages = commands::run(
|
||||
&cli.files,
|
||||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
cache.into(),
|
||||
fix::FixMode::None,
|
||||
@@ -250,7 +186,6 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
let messages = commands::run(
|
||||
&cli.files,
|
||||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
cache.into(),
|
||||
fix::FixMode::None,
|
||||
@@ -261,12 +196,6 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
}
|
||||
} else if cli.add_noqa {
|
||||
let modifications =
|
||||
commands::add_noqa(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||
if modifications > 0 && log_level >= LogLevel::Default {
|
||||
println!("Added {modifications} noqa directives.");
|
||||
}
|
||||
} else {
|
||||
let is_stdin = cli.files == vec![PathBuf::from("-")];
|
||||
|
||||
@@ -275,7 +204,6 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
commands::run_stdin(
|
||||
cli.stdin_filename.map(fs::normalize_path).as_deref(),
|
||||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
autofix,
|
||||
)?
|
||||
@@ -283,7 +211,6 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
commands::run(
|
||||
&cli.files,
|
||||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
cache.into(),
|
||||
autofix,
|
||||
|
||||
68
ruff_cli/src/resolve.rs
Normal file
68
ruff_cli/src/resolve.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use path_absolutize::path_dedot;
|
||||
use ruff::resolver::{
|
||||
resolve_settings_with_processor, ConfigProcessor, PyprojectDiscovery, Relativity,
|
||||
};
|
||||
use ruff::settings::configuration::Configuration;
|
||||
use ruff::settings::{pyproject, AllSettings};
|
||||
|
||||
use crate::args::Overrides;
|
||||
|
||||
/// Resolve the relevant settings strategy and defaults for the current
|
||||
/// invocation.
|
||||
pub fn resolve(
|
||||
isolated: bool,
|
||||
config: Option<&Path>,
|
||||
overrides: &Overrides,
|
||||
stdin_filename: Option<&Path>,
|
||||
) -> Result<PyprojectDiscovery> {
|
||||
// First priority: if we're running in isolated mode, use the default settings.
|
||||
if isolated {
|
||||
let mut config = Configuration::default();
|
||||
overrides.process_config(&mut config);
|
||||
let settings = AllSettings::from_configuration(config, &path_dedot::CWD)?;
|
||||
return Ok(PyprojectDiscovery::Fixed(settings));
|
||||
}
|
||||
|
||||
// Second priority: the user specified a `pyproject.toml` file. Use that
|
||||
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
|
||||
// current working directory. (This matches ESLint's behavior.)
|
||||
if let Some(pyproject) = config {
|
||||
let settings = resolve_settings_with_processor(pyproject, &Relativity::Cwd, overrides)?;
|
||||
return Ok(PyprojectDiscovery::Fixed(settings));
|
||||
}
|
||||
|
||||
// Third priority: find a `pyproject.toml` file in either an ancestor of
|
||||
// `stdin_filename` (if set) or the current working path all paths relative to
|
||||
// that directory. (With `Strategy::Hierarchical`, we'll end up finding
|
||||
// the "closest" `pyproject.toml` file for every Python file later on,
|
||||
// so these act as the "default" settings.)
|
||||
if let Some(pyproject) = pyproject::find_settings_toml(
|
||||
stdin_filename
|
||||
.as_ref()
|
||||
.unwrap_or(&path_dedot::CWD.as_path()),
|
||||
)? {
|
||||
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Parent, overrides)?;
|
||||
return Ok(PyprojectDiscovery::Hierarchical(settings));
|
||||
}
|
||||
|
||||
// Fourth priority: find a user-specific `pyproject.toml`, but resolve all paths
|
||||
// relative the current working directory. (With `Strategy::Hierarchical`, we'll
|
||||
// end up the "closest" `pyproject.toml` file for every Python file later on, so
|
||||
// these act as the "default" settings.)
|
||||
if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
||||
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Cwd, overrides)?;
|
||||
return Ok(PyprojectDiscovery::Hierarchical(settings));
|
||||
}
|
||||
|
||||
// Fallback: load Ruff's default settings, and resolve all paths relative to the
|
||||
// current working directory. (With `Strategy::Hierarchical`, we'll end up the
|
||||
// "closest" `pyproject.toml` file for every Python file later on, so these act
|
||||
// as the "default" settings.)
|
||||
let mut config = Configuration::default();
|
||||
overrides.process_config(&mut config);
|
||||
let settings = AllSettings::from_configuration(config, &path_dedot::CWD)?;
|
||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
//! Run all code and documentation generation steps.
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
|
||||
use crate::{generate_cli_help, generate_json_schema, generate_options, generate_rules_table};
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Cli {
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
/// Write the generated artifacts to stdout (rather than to the filesystem).
|
||||
#[arg(long)]
|
||||
dry_run: bool,
|
||||
}
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
generate_json_schema::main(&generate_json_schema::Cli {
|
||||
dry_run: cli.dry_run,
|
||||
pub fn main(args: &Args) -> Result<()> {
|
||||
generate_json_schema::main(&generate_json_schema::Args {
|
||||
dry_run: args.dry_run,
|
||||
})?;
|
||||
generate_rules_table::main(&generate_rules_table::Cli {
|
||||
dry_run: cli.dry_run,
|
||||
generate_rules_table::main(&generate_rules_table::Args {
|
||||
dry_run: args.dry_run,
|
||||
})?;
|
||||
generate_options::main(&generate_options::Cli {
|
||||
dry_run: cli.dry_run,
|
||||
generate_options::main(&generate_options::Args {
|
||||
dry_run: args.dry_run,
|
||||
})?;
|
||||
generate_cli_help::main(&generate_cli_help::Cli {
|
||||
dry_run: cli.dry_run,
|
||||
generate_cli_help::main(&generate_cli_help::Args {
|
||||
dry_run: args.dry_run,
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
//! Generate CLI help.
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
|
||||
use crate::utils::replace_readme_section;
|
||||
|
||||
const HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated cli help. -->";
|
||||
const HELP_END_PRAGMA: &str = "<!-- End auto-generated cli help. -->";
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Cli {
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
/// Write the generated help to stdout (rather than to `README.md`).
|
||||
#[arg(long)]
|
||||
pub(crate) dry_run: bool,
|
||||
@@ -19,10 +18,10 @@ fn trim_lines(s: &str) -> String {
|
||||
s.lines().map(str::trim_end).collect::<Vec<_>>().join("\n")
|
||||
}
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
pub fn main(args: &Args) -> Result<()> {
|
||||
let output = trim_lines(ruff_cli::help().trim());
|
||||
|
||||
if cli.dry_run {
|
||||
if args.dry_run {
|
||||
print!("{output}");
|
||||
} else {
|
||||
replace_readme_section(
|
||||
|
||||
@@ -2,22 +2,21 @@ use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
use ruff::settings::options::Options;
|
||||
use schemars::schema_for;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Cli {
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
/// Write the generated table to stdout (rather than to `ruff.schema.json`).
|
||||
#[arg(long)]
|
||||
pub(crate) dry_run: bool,
|
||||
}
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
pub fn main(args: &Args) -> Result<()> {
|
||||
let schema = schema_for!(Options);
|
||||
let schema_string = serde_json::to_string_pretty(&schema).unwrap();
|
||||
|
||||
if cli.dry_run {
|
||||
if args.dry_run {
|
||||
println!("{schema_string}");
|
||||
} else {
|
||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! Generate a Markdown-compatible listing of configuration options.
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
use itertools::Itertools;
|
||||
use ruff::settings::options::Options;
|
||||
use ruff::settings::options_base::{ConfigurationOptions, OptionEntry, OptionField};
|
||||
@@ -11,8 +10,8 @@ use crate::utils::replace_readme_section;
|
||||
const BEGIN_PRAGMA: &str = "<!-- Begin auto-generated options sections. -->";
|
||||
const END_PRAGMA: &str = "<!-- End auto-generated options sections. -->";
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Cli {
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
/// Write the generated table to stdout (rather than to `README.md`).
|
||||
#[arg(long)]
|
||||
pub(crate) dry_run: bool,
|
||||
@@ -39,7 +38,7 @@ fn emit_field(output: &mut String, field: &OptionField, group_name: Option<&str>
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
pub fn main(args: &Args) -> Result<()> {
|
||||
let mut output = String::new();
|
||||
|
||||
// Generate all the top-level fields.
|
||||
@@ -89,7 +88,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
if cli.dry_run {
|
||||
if args.dry_run {
|
||||
print!("{output}");
|
||||
} else {
|
||||
replace_readme_section(&output, BEGIN_PRAGMA, END_PRAGMA)?;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! Generate a Markdown-compatible table of supported lint rules.
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
use ruff::registry::{Linter, LinterCategory, Rule, RuleNamespace};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
@@ -13,8 +12,8 @@ const TABLE_END_PRAGMA: &str = "<!-- End auto-generated sections. -->";
|
||||
const TOC_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated table of contents. -->";
|
||||
const TOC_END_PRAGMA: &str = "<!-- End auto-generated table of contents. -->";
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Cli {
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
/// Write the generated table to stdout (rather than to `README.md`).
|
||||
#[arg(long)]
|
||||
pub(crate) dry_run: bool,
|
||||
@@ -43,7 +42,7 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>)
|
||||
table_out.push('\n');
|
||||
}
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
pub fn main(args: &Args) -> Result<()> {
|
||||
// Generate the table string.
|
||||
let mut table_out = String::new();
|
||||
let mut toc_out = String::new();
|
||||
@@ -96,7 +95,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
if cli.dry_run {
|
||||
if args.dry_run {
|
||||
print!("Table of Contents: {toc_out}\n Rules Tables: {table_out}");
|
||||
} else {
|
||||
// Extra newline in the markdown numbered list looks weird
|
||||
|
||||
@@ -33,45 +33,45 @@ use clap::{Parser, Subcommand};
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[command(propagate_version = true)]
|
||||
struct Cli {
|
||||
struct Args {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
enum Command {
|
||||
/// Run all code and documentation generation steps.
|
||||
GenerateAll(generate_all::Cli),
|
||||
GenerateAll(generate_all::Args),
|
||||
/// Generate JSON schema for the TOML configuration file.
|
||||
GenerateJSONSchema(generate_json_schema::Cli),
|
||||
GenerateJSONSchema(generate_json_schema::Args),
|
||||
/// Generate a Markdown-compatible table of supported lint rules.
|
||||
GenerateRulesTable(generate_rules_table::Cli),
|
||||
GenerateRulesTable(generate_rules_table::Args),
|
||||
/// Generate a Markdown-compatible listing of configuration options.
|
||||
GenerateOptions(generate_options::Cli),
|
||||
GenerateOptions(generate_options::Args),
|
||||
/// Generate CLI help.
|
||||
GenerateCliHelp(generate_cli_help::Cli),
|
||||
GenerateCliHelp(generate_cli_help::Args),
|
||||
/// Print the AST for a given Python file.
|
||||
PrintAST(print_ast::Cli),
|
||||
PrintAST(print_ast::Args),
|
||||
/// Print the LibCST CST for a given Python file.
|
||||
PrintCST(print_cst::Cli),
|
||||
PrintCST(print_cst::Args),
|
||||
/// Print the token stream for a given Python file.
|
||||
PrintTokens(print_tokens::Cli),
|
||||
PrintTokens(print_tokens::Args),
|
||||
/// Run round-trip source code generation on a given Python file.
|
||||
RoundTrip(round_trip::Cli),
|
||||
RoundTrip(round_trip::Args),
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
match &cli.command {
|
||||
Commands::GenerateAll(args) => generate_all::main(args)?,
|
||||
Commands::GenerateJSONSchema(args) => generate_json_schema::main(args)?,
|
||||
Commands::GenerateRulesTable(args) => generate_rules_table::main(args)?,
|
||||
Commands::GenerateOptions(args) => generate_options::main(args)?,
|
||||
Commands::GenerateCliHelp(args) => generate_cli_help::main(args)?,
|
||||
Commands::PrintAST(args) => print_ast::main(args)?,
|
||||
Commands::PrintCST(args) => print_cst::main(args)?,
|
||||
Commands::PrintTokens(args) => print_tokens::main(args)?,
|
||||
Commands::RoundTrip(args) => round_trip::main(args)?,
|
||||
let args = Args::parse();
|
||||
match &args.command {
|
||||
Command::GenerateAll(args) => generate_all::main(args)?,
|
||||
Command::GenerateJSONSchema(args) => generate_json_schema::main(args)?,
|
||||
Command::GenerateRulesTable(args) => generate_rules_table::main(args)?,
|
||||
Command::GenerateOptions(args) => generate_options::main(args)?,
|
||||
Command::GenerateCliHelp(args) => generate_cli_help::main(args)?,
|
||||
Command::PrintAST(args) => print_ast::main(args)?,
|
||||
Command::PrintCST(args) => print_cst::main(args)?,
|
||||
Command::PrintTokens(args) => print_tokens::main(args)?,
|
||||
Command::RoundTrip(args) => round_trip::main(args)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4,19 +4,18 @@ use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
use rustpython_parser::parser;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Cli {
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
/// Python file for which to generate the AST.
|
||||
#[arg(required = true)]
|
||||
file: PathBuf,
|
||||
}
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
let contents = fs::read_to_string(&cli.file)?;
|
||||
let python_ast = parser::parse_program(&contents, &cli.file.to_string_lossy())?;
|
||||
pub fn main(args: &Args) -> Result<()> {
|
||||
let contents = fs::read_to_string(&args.file)?;
|
||||
let python_ast = parser::parse_program(&contents, &args.file.to_string_lossy())?;
|
||||
println!("{python_ast:#?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4,17 +4,16 @@ use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use clap::Args;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Cli {
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
/// Python file for which to generate the CST.
|
||||
#[arg(required = true)]
|
||||
file: PathBuf,
|
||||
}
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
let contents = fs::read_to_string(&cli.file)?;
|
||||
pub fn main(args: &Args) -> Result<()> {
|
||||
let contents = fs::read_to_string(&args.file)?;
|
||||
match libcst_native::parse_module(&contents, None) {
|
||||
Ok(python_cst) => {
|
||||
println!("{python_cst:#?}");
|
||||
|
||||
@@ -4,18 +4,17 @@ use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
use rustpython_parser::lexer;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Cli {
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
/// Python file for which to generate the AST.
|
||||
#[arg(required = true)]
|
||||
file: PathBuf,
|
||||
}
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
let contents = fs::read_to_string(&cli.file)?;
|
||||
pub fn main(args: &Args) -> Result<()> {
|
||||
let contents = fs::read_to_string(&args.file)?;
|
||||
for (_, tok, _) in lexer::make_tokenizer(&contents).flatten() {
|
||||
println!("{tok:#?}");
|
||||
}
|
||||
|
||||
@@ -4,18 +4,17 @@ use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
use ruff::source_code::round_trip;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Cli {
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
/// Python file to round-trip.
|
||||
#[arg(required = true)]
|
||||
file: PathBuf,
|
||||
}
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
let contents = fs::read_to_string(&cli.file)?;
|
||||
println!("{}", round_trip(&contents, &cli.file.to_string_lossy())?);
|
||||
pub fn main(args: &Args) -> Result<()> {
|
||||
let contents = fs::read_to_string(&args.file)?;
|
||||
println!("{}", round_trip(&contents, &args.file.to_string_lossy())?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.235"
|
||||
version = "0.0.236"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::cmp::Reverse;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use proc_macro2::{Ident, Span};
|
||||
@@ -70,7 +71,7 @@ pub fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream>
|
||||
});
|
||||
}
|
||||
|
||||
parsed.sort_by_key(|(prefix, _)| prefix.len());
|
||||
parsed.sort_by_key(|(prefix, _)| Reverse(prefix.len()));
|
||||
|
||||
let mut if_statements = quote!();
|
||||
let mut into_iter_match_arms = quote!();
|
||||
|
||||
@@ -84,7 +84,7 @@ mod tests {
|
||||
fp.write("\n")
|
||||
|
||||
elif line.strip() == "/// Ruff-specific rules":
|
||||
fp.write(f"/// [{plugin}]({url})\n")
|
||||
fp.write(f"{indent}/// [{plugin}]({url})\n")
|
||||
fp.write(f'{indent}#[prefix = "{prefix_code}"]\n')
|
||||
fp.write(f"{indent}{pascal_case(plugin)},")
|
||||
fp.write("\n")
|
||||
|
||||
@@ -489,6 +489,17 @@ pub fn is_const_none(expr: &Expr) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
/// Return `true` if an [`Expr`] is `True`.
|
||||
pub fn is_const_true(expr: &Expr) -> bool {
|
||||
matches!(
|
||||
&expr.node,
|
||||
ExprKind::Constant {
|
||||
value: Constant::Bool(true),
|
||||
kind: None
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Return `true` if a keyword argument is present with a non-`None` value.
|
||||
pub fn has_non_none_keyword(keywords: &[Keyword], keyword: &str) -> bool {
|
||||
find_keyword(keywords, keyword).map_or(false, |keyword| {
|
||||
@@ -665,6 +676,24 @@ pub fn match_trailing_content(stmt: &Stmt, locator: &Locator) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// If a `Stmt` has a trailing comment, return the index of the hash.
|
||||
pub fn match_trailing_comment(stmt: &Stmt, locator: &Locator) -> Option<usize> {
|
||||
let range = Range::new(
|
||||
stmt.end_location.unwrap(),
|
||||
Location::new(stmt.end_location.unwrap().row() + 1, 0),
|
||||
);
|
||||
let suffix = locator.slice_source_code_range(&range);
|
||||
for (i, char) in suffix.chars().enumerate() {
|
||||
if char == '#' {
|
||||
return Some(i);
|
||||
}
|
||||
if !char.is_whitespace() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Return the number of trailing empty lines following a statement.
|
||||
pub fn count_trailing_lines(stmt: &Stmt, locator: &Locator) -> usize {
|
||||
let suffix =
|
||||
@@ -974,6 +1003,21 @@ impl<'a> SimpleCallArgs<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if the given `Expr` is a potential logging call. Matches
|
||||
/// `logging.error`, `logger.error`, `self.logger.error`, etc., but not
|
||||
/// arbitrary `foo.error` calls.
|
||||
pub fn is_logger_candidate(func: &Expr) -> bool {
|
||||
if let ExprKind::Attribute { value, .. } = &func.node {
|
||||
let call_path = collect_call_path(value);
|
||||
if let Some(tail) = call_path.last() {
|
||||
if *tail == "logging" || tail.ends_with("logger") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -89,9 +89,6 @@ pub struct Scope<'a> {
|
||||
pub uses_locals: bool,
|
||||
/// A map from bound name to binding index.
|
||||
pub values: FxHashMap<&'a str, usize>,
|
||||
/// A list of (name, index) pairs for bindings that were overridden in the
|
||||
/// scope.
|
||||
pub overridden: Vec<(&'a str, usize)>,
|
||||
}
|
||||
|
||||
impl<'a> Scope<'a> {
|
||||
@@ -102,11 +99,27 @@ impl<'a> Scope<'a> {
|
||||
import_starred: false,
|
||||
uses_locals: false,
|
||||
values: FxHashMap::default(),
|
||||
overridden: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pyflakes defines the following binding hierarchy (via inheritance):
|
||||
// Binding
|
||||
// ExportBinding
|
||||
// Annotation
|
||||
// Argument
|
||||
// Assignment
|
||||
// NamedExprAssignment
|
||||
// Definition
|
||||
// FunctionDefinition
|
||||
// ClassDefinition
|
||||
// Builtin
|
||||
// Importation
|
||||
// SubmoduleImportation
|
||||
// ImportationFrom
|
||||
// StarImportation
|
||||
// FutureImportation
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum BindingKind<'a> {
|
||||
Annotation,
|
||||
@@ -127,10 +140,12 @@ pub enum BindingKind<'a> {
|
||||
SubmoduleImportation(&'a str, &'a str),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone)]
|
||||
pub struct Binding<'a> {
|
||||
pub kind: BindingKind<'a>,
|
||||
pub range: Range,
|
||||
/// The context in which the binding was created.
|
||||
pub context: ExecutionContext,
|
||||
/// The statement in which the [`Binding`] was defined.
|
||||
pub source: Option<RefEquality<'a, Stmt>>,
|
||||
/// Tuple of (scope index, range) indicating the scope and range at which
|
||||
@@ -147,33 +162,16 @@ pub struct Binding<'a> {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum UsageContext {
|
||||
pub enum ExecutionContext {
|
||||
Runtime,
|
||||
Typing,
|
||||
}
|
||||
|
||||
// Pyflakes defines the following binding hierarchy (via inheritance):
|
||||
// Binding
|
||||
// ExportBinding
|
||||
// Annotation
|
||||
// Argument
|
||||
// Assignment
|
||||
// NamedExprAssignment
|
||||
// Definition
|
||||
// FunctionDefinition
|
||||
// ClassDefinition
|
||||
// Builtin
|
||||
// Importation
|
||||
// SubmoduleImportation
|
||||
// ImportationFrom
|
||||
// StarImportation
|
||||
// FutureImportation
|
||||
|
||||
impl<'a> Binding<'a> {
|
||||
pub fn mark_used(&mut self, scope: usize, range: Range, context: UsageContext) {
|
||||
pub fn mark_used(&mut self, scope: usize, range: Range, context: ExecutionContext) {
|
||||
match context {
|
||||
UsageContext::Runtime => self.runtime_usage = Some((scope, range)),
|
||||
UsageContext::Typing => self.typing_usage = Some((scope, range)),
|
||||
ExecutionContext::Runtime => self.runtime_usage = Some((scope, range)),
|
||||
ExecutionContext::Typing => self.typing_usage = Some((scope, range)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::ast::whitespace::LinesWithTrailingNewline;
|
||||
use crate::cst::helpers::compose_module_path;
|
||||
use crate::cst::matchers::match_module;
|
||||
use crate::fix::Fix;
|
||||
use crate::source_code::{Indexer, Locator};
|
||||
use crate::source_code::{Indexer, Locator, Stylist};
|
||||
|
||||
/// Determine if a body contains only a single statement, taking into account
|
||||
/// deleted.
|
||||
@@ -157,6 +157,7 @@ pub fn delete_stmt(
|
||||
deleted: &[&Stmt],
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
stylist: &Stylist,
|
||||
) -> Result<Fix> {
|
||||
if parent
|
||||
.map(|parent| is_lone_child(stmt, parent, deleted))
|
||||
@@ -179,7 +180,11 @@ pub fn delete_stmt(
|
||||
} else if helpers::preceded_by_continuation(stmt, indexer) {
|
||||
if is_end_of_file(stmt, locator) && stmt.location.column() == 0 {
|
||||
// Special-case: a file can't end in a continuation.
|
||||
Fix::replacement("\n".to_string(), stmt.location, stmt.end_location.unwrap())
|
||||
Fix::replacement(
|
||||
stylist.line_ending().to_string(),
|
||||
stmt.location,
|
||||
stmt.end_location.unwrap(),
|
||||
)
|
||||
} else {
|
||||
Fix::deletion(stmt.location, stmt.end_location.unwrap())
|
||||
}
|
||||
@@ -200,6 +205,7 @@ pub fn remove_unused_imports<'a>(
|
||||
deleted: &[&Stmt],
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
stylist: &Stylist,
|
||||
) -> Result<Fix> {
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(stmt));
|
||||
let mut tree = match_module(module_text)?;
|
||||
@@ -237,7 +243,7 @@ pub fn remove_unused_imports<'a>(
|
||||
if !found_star {
|
||||
bail!("Expected \'*\' for unused import");
|
||||
}
|
||||
return delete_stmt(stmt, parent, deleted, locator, indexer);
|
||||
return delete_stmt(stmt, parent, deleted, locator, indexer, stylist);
|
||||
} else {
|
||||
bail!("Expected: ImportNames::Aliases | ImportNames::Star");
|
||||
}
|
||||
@@ -298,9 +304,13 @@ pub fn remove_unused_imports<'a>(
|
||||
}
|
||||
|
||||
if aliases.is_empty() {
|
||||
delete_stmt(stmt, parent, deleted, locator, indexer)
|
||||
delete_stmt(stmt, parent, deleted, locator, indexer, stylist)
|
||||
} else {
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Lint rules based on AST traversal.
|
||||
|
||||
use std::iter;
|
||||
use std::path::Path;
|
||||
|
||||
use itertools::Itertools;
|
||||
@@ -19,8 +20,8 @@ use crate::ast::helpers::{binding_range, collect_call_path, extract_handler_name
|
||||
use crate::ast::operations::extract_all_names;
|
||||
use crate::ast::relocate::relocate_expr;
|
||||
use crate::ast::types::{
|
||||
Binding, BindingKind, CallPath, ClassDef, FunctionDef, Lambda, Node, Range, RefEquality, Scope,
|
||||
ScopeKind, UsageContext,
|
||||
Binding, BindingKind, CallPath, ClassDef, ExecutionContext, FunctionDef, Lambda, Node, Range,
|
||||
RefEquality, Scope, ScopeKind,
|
||||
};
|
||||
use crate::ast::visitor::{walk_excepthandler, Visitor};
|
||||
use crate::ast::{branch_detection, cast, helpers, operations, visitor};
|
||||
@@ -34,10 +35,11 @@ use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::{
|
||||
flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except, flake8_boolean_trap,
|
||||
flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_datetimez, flake8_debugger,
|
||||
flake8_errmsg, flake8_implicit_str_concat, flake8_import_conventions, flake8_pie, flake8_print,
|
||||
flake8_pytest_style, flake8_return, flake8_simplify, flake8_tidy_imports, flake8_type_checking,
|
||||
flake8_unused_arguments, flake8_use_pathlib, mccabe, pandas_vet, pep8_naming, pycodestyle,
|
||||
pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade, ruff, tryceratops,
|
||||
flake8_errmsg, flake8_implicit_str_concat, flake8_import_conventions, flake8_logging_format,
|
||||
flake8_pie, flake8_print, flake8_pytest_style, flake8_return, flake8_simplify,
|
||||
flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, flake8_use_pathlib, mccabe,
|
||||
pandas_vet, pep8_naming, pycodestyle, pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade,
|
||||
ruff, tryceratops,
|
||||
};
|
||||
use crate::settings::types::PythonVersion;
|
||||
use crate::settings::{flags, Settings};
|
||||
@@ -79,7 +81,7 @@ pub struct Checker<'a> {
|
||||
pub(crate) exprs: Vec<RefEquality<'a, Expr>>,
|
||||
pub(crate) scopes: Vec<Scope<'a>>,
|
||||
pub(crate) scope_stack: Vec<usize>,
|
||||
pub(crate) dead_scopes: Vec<usize>,
|
||||
pub(crate) dead_scopes: Vec<(usize, Vec<usize>)>,
|
||||
deferred_string_type_definitions: Vec<(Range, &'a str, bool, DeferralContext<'a>)>,
|
||||
deferred_type_definitions: Vec<(&'a Expr, bool, DeferralContext<'a>)>,
|
||||
deferred_functions: Vec<(&'a Stmt, DeferralContext<'a>, VisibleScope)>,
|
||||
@@ -100,7 +102,6 @@ pub struct Checker<'a> {
|
||||
except_handlers: Vec<Vec<Vec<&'a str>>>,
|
||||
// Check-specific state.
|
||||
pub(crate) flake8_bugbear_seen: Vec<&'a Expr>,
|
||||
pub(crate) type_checking_blocks: Vec<&'a Stmt>,
|
||||
}
|
||||
|
||||
impl<'a> Checker<'a> {
|
||||
@@ -161,7 +162,6 @@ impl<'a> Checker<'a> {
|
||||
except_handlers: vec![],
|
||||
// Check-specific state.
|
||||
flake8_bugbear_seen: vec![],
|
||||
type_checking_blocks: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,6 +330,7 @@ where
|
||||
let ranges = helpers::find_names(stmt, self.locator);
|
||||
if scope_index != GLOBAL_SCOPE_INDEX {
|
||||
// Add the binding to the current scope.
|
||||
let context = self.execution_context();
|
||||
let scope = &mut self.scopes[scope_index];
|
||||
let usage = Some((scope.id, Range::from_located(stmt)));
|
||||
for (name, range) in names.iter().zip(ranges.iter()) {
|
||||
@@ -341,6 +342,7 @@ where
|
||||
typing_usage: None,
|
||||
range: *range,
|
||||
source: Some(RefEquality(stmt)),
|
||||
context,
|
||||
});
|
||||
scope.values.insert(name, index);
|
||||
}
|
||||
@@ -357,6 +359,7 @@ where
|
||||
let scope_index = *self.scope_stack.last().expect("No current scope found");
|
||||
let ranges = helpers::find_names(stmt, self.locator);
|
||||
if scope_index != GLOBAL_SCOPE_INDEX {
|
||||
let context = self.execution_context();
|
||||
let scope = &mut self.scopes[scope_index];
|
||||
let usage = Some((scope.id, Range::from_located(stmt)));
|
||||
for (name, range) in names.iter().zip(ranges.iter()) {
|
||||
@@ -369,6 +372,7 @@ where
|
||||
typing_usage: None,
|
||||
range: *range,
|
||||
source: Some(RefEquality(stmt)),
|
||||
context,
|
||||
});
|
||||
scope.values.insert(name, index);
|
||||
}
|
||||
@@ -674,6 +678,8 @@ where
|
||||
for expr in &args.defaults {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
|
||||
let context = self.execution_context();
|
||||
self.add_binding(
|
||||
name,
|
||||
Binding {
|
||||
@@ -683,6 +689,7 @@ where
|
||||
typing_usage: None,
|
||||
range: Range::from_located(stmt),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -837,6 +844,7 @@ where
|
||||
typing_usage: None,
|
||||
range: Range::from_located(alias),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
@@ -876,6 +884,7 @@ where
|
||||
typing_usage: None,
|
||||
range: Range::from_located(alias),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1118,6 +1127,7 @@ where
|
||||
typing_usage: None,
|
||||
range: Range::from_located(alias),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1154,6 +1164,7 @@ where
|
||||
typing_usage: None,
|
||||
range: Range::from_located(stmt),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1228,6 +1239,7 @@ where
|
||||
typing_usage: None,
|
||||
range,
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1394,6 +1406,16 @@ where
|
||||
pyupgrade::rules::os_error_alias(self, &item);
|
||||
}
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::RaiseVanillaClass) {
|
||||
if let Some(expr) = exc {
|
||||
tryceratops::rules::raise_vanilla_class(self, expr);
|
||||
}
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::RaiseVanillaArgs) {
|
||||
if let Some(expr) = exc {
|
||||
tryceratops::rules::raise_vanilla_args(self, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
StmtKind::AugAssign { target, .. } => {
|
||||
self.handle_node_load(target);
|
||||
@@ -1607,6 +1629,9 @@ where
|
||||
if self.settings.rules.enabled(&Rule::RaiseWithinTry) {
|
||||
tryceratops::rules::raise_within_try(self, body);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::ErrorInsteadOfException) {
|
||||
tryceratops::rules::error_instead_of_exception(self, handlers);
|
||||
}
|
||||
}
|
||||
StmtKind::Assign { targets, value, .. } => {
|
||||
if self.settings.rules.enabled(&Rule::DoNotAssignLambda) {
|
||||
@@ -1736,6 +1761,7 @@ where
|
||||
typing_usage: None,
|
||||
range: Range::from_located(stmt),
|
||||
source: Some(RefEquality(stmt)),
|
||||
context: self.execution_context(),
|
||||
});
|
||||
self.scopes[GLOBAL_SCOPE_INDEX].values.insert(name, index);
|
||||
}
|
||||
@@ -1799,6 +1825,7 @@ where
|
||||
typing_usage: None,
|
||||
range: Range::from_located(stmt),
|
||||
source: Some(RefEquality(stmt)),
|
||||
context: self.execution_context(),
|
||||
});
|
||||
self.scopes[GLOBAL_SCOPE_INDEX].values.insert(name, index);
|
||||
}
|
||||
@@ -1862,7 +1889,6 @@ where
|
||||
if self.settings.rules.enabled(&Rule::EmptyTypeCheckingBlock) {
|
||||
flake8_type_checking::rules::empty_type_checking_block(self, test, body);
|
||||
}
|
||||
self.type_checking_blocks.push(stmt);
|
||||
|
||||
let prev_in_type_checking_block = self.in_type_checking_block;
|
||||
self.in_type_checking_block = true;
|
||||
@@ -1894,6 +1920,7 @@ where
|
||||
typing_usage: None,
|
||||
range: Range::from_located(stmt),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -2642,6 +2669,19 @@ where
|
||||
{
|
||||
flake8_use_pathlib::helpers::replaceable_by_pathlib(self, func);
|
||||
}
|
||||
|
||||
// flake8-logging-format
|
||||
if self.settings.rules.enabled(&Rule::LoggingStringFormat)
|
||||
|| self.settings.rules.enabled(&Rule::LoggingPercentFormat)
|
||||
|| self.settings.rules.enabled(&Rule::LoggingStringConcat)
|
||||
|| self.settings.rules.enabled(&Rule::LoggingFString)
|
||||
|| self.settings.rules.enabled(&Rule::LoggingWarn)
|
||||
|| self.settings.rules.enabled(&Rule::LoggingExtraAttrClash)
|
||||
|| self.settings.rules.enabled(&Rule::LoggingExcInfo)
|
||||
|| self.settings.rules.enabled(&Rule::LoggingRedundantExcInfo)
|
||||
{
|
||||
flake8_logging_format::rules::logging_call(self, func, args, keywords);
|
||||
}
|
||||
}
|
||||
ExprKind::Dict { keys, values } => {
|
||||
if self
|
||||
@@ -3584,6 +3624,7 @@ where
|
||||
typing_usage: None,
|
||||
range: Range::from_located(arg),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -3660,11 +3701,12 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
fn pop_scope(&mut self) {
|
||||
self.dead_scopes.push(
|
||||
self.dead_scopes.push((
|
||||
self.scope_stack
|
||||
.pop()
|
||||
.expect("Attempted to pop without scope"),
|
||||
);
|
||||
self.scope_stack.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
fn bind_builtins(&mut self) {
|
||||
@@ -3684,6 +3726,7 @@ impl<'a> Checker<'a> {
|
||||
synthetic_usage: None,
|
||||
typing_usage: None,
|
||||
source: None,
|
||||
context: ExecutionContext::Runtime,
|
||||
});
|
||||
scope.values.insert(builtin, index);
|
||||
}
|
||||
@@ -3720,6 +3763,18 @@ impl<'a> Checker<'a> {
|
||||
.map(|index| &self.scopes[*index])
|
||||
}
|
||||
|
||||
pub fn execution_context(&self) -> ExecutionContext {
|
||||
if self.in_type_checking_block
|
||||
|| self.in_annotation
|
||||
|| self.in_deferred_string_type_definition
|
||||
|| self.in_deferred_type_definition
|
||||
{
|
||||
ExecutionContext::Typing
|
||||
} else {
|
||||
ExecutionContext::Runtime
|
||||
}
|
||||
}
|
||||
|
||||
fn add_binding<'b>(&mut self, name: &'b str, binding: Binding<'a>)
|
||||
where
|
||||
'b: 'a,
|
||||
@@ -3837,17 +3892,8 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
if let Some(index) = scope.values.get(&id.as_str()) {
|
||||
let context = if self.in_type_checking_block
|
||||
|| self.in_annotation
|
||||
|| self.in_deferred_string_type_definition
|
||||
|| self.in_deferred_type_definition
|
||||
{
|
||||
UsageContext::Typing
|
||||
} else {
|
||||
UsageContext::Runtime
|
||||
};
|
||||
|
||||
// Mark the binding as used.
|
||||
let context = self.execution_context();
|
||||
self.bindings[*index].mark_used(scope_id, Range::from_located(expr), context);
|
||||
|
||||
if matches!(self.bindings[*index].kind, BindingKind::Annotation)
|
||||
@@ -4030,6 +4076,7 @@ impl<'a> Checker<'a> {
|
||||
typing_usage: None,
|
||||
range: Range::from_located(expr),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
return;
|
||||
@@ -4049,6 +4096,7 @@ impl<'a> Checker<'a> {
|
||||
typing_usage: None,
|
||||
range: Range::from_located(expr),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
return;
|
||||
@@ -4064,6 +4112,7 @@ impl<'a> Checker<'a> {
|
||||
typing_usage: None,
|
||||
range: Range::from_located(expr),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
return;
|
||||
@@ -4116,6 +4165,7 @@ impl<'a> Checker<'a> {
|
||||
typing_usage: None,
|
||||
range: Range::from_located(expr),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
return;
|
||||
@@ -4131,6 +4181,7 @@ impl<'a> Checker<'a> {
|
||||
typing_usage: None,
|
||||
range: Range::from_located(expr),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -4336,13 +4387,48 @@ impl<'a> Checker<'a> {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
for scope in self
|
||||
.dead_scopes
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|index| &self.scopes[*index])
|
||||
// Identify any valid runtime imports. If a module is imported at runtime, and
|
||||
// used at runtime, then by default, we avoid flagging any other
|
||||
// imports from that model as typing-only.
|
||||
let runtime_imports: Vec<Vec<&Binding>> = if !self.settings.flake8_type_checking.strict
|
||||
&& (self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::RuntimeImportInTypeCheckingBlock)
|
||||
|| self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::TypingOnlyFirstPartyImport)
|
||||
|| self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::TypingOnlyThirdPartyImport)
|
||||
|| self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::TypingOnlyStandardLibraryImport))
|
||||
{
|
||||
self.scopes
|
||||
.iter()
|
||||
.map(|scope| {
|
||||
scope
|
||||
.values
|
||||
.values()
|
||||
.map(|index| &self.bindings[*index])
|
||||
.filter(|binding| {
|
||||
flake8_type_checking::helpers::is_valid_runtime_import(binding)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
for (index, stack) in self.dead_scopes.iter().rev() {
|
||||
let scope = &self.scopes[*index];
|
||||
|
||||
// PLW0602
|
||||
if self
|
||||
.settings
|
||||
@@ -4481,25 +4567,28 @@ impl<'a> Checker<'a> {
|
||||
.rules
|
||||
.enabled(&Rule::TypingOnlyStandardLibraryImport)
|
||||
{
|
||||
for (.., index) in scope
|
||||
.values
|
||||
.iter()
|
||||
.chain(scope.overridden.iter().map(|(a, b)| (a, b)))
|
||||
{
|
||||
let runtime_imports: Vec<&Binding> = if self.settings.flake8_type_checking.strict {
|
||||
vec![]
|
||||
} else {
|
||||
stack
|
||||
.iter()
|
||||
.chain(iter::once(index))
|
||||
.flat_map(|index| runtime_imports[*index].iter())
|
||||
.copied()
|
||||
.collect()
|
||||
};
|
||||
for (.., index) in &scope.values {
|
||||
let binding = &self.bindings[*index];
|
||||
|
||||
if let Some(diagnostic) =
|
||||
flake8_type_checking::rules::runtime_import_in_type_checking_block(
|
||||
binding,
|
||||
&self.type_checking_blocks,
|
||||
)
|
||||
flake8_type_checking::rules::runtime_import_in_type_checking_block(binding)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
if let Some(diagnostic) =
|
||||
flake8_type_checking::rules::typing_only_runtime_import(
|
||||
binding,
|
||||
&self.type_checking_blocks,
|
||||
&runtime_imports,
|
||||
self.package,
|
||||
self.settings,
|
||||
)
|
||||
@@ -4522,11 +4611,7 @@ impl<'a> Checker<'a> {
|
||||
let mut ignored: FxHashMap<BindingContext, Vec<UnusedImport>> =
|
||||
FxHashMap::default();
|
||||
|
||||
for (name, index) in scope
|
||||
.values
|
||||
.iter()
|
||||
.chain(scope.overridden.iter().map(|(a, b)| (a, b)))
|
||||
{
|
||||
for (name, index) in &scope.values {
|
||||
let binding = &self.bindings[*index];
|
||||
|
||||
let full_name = match &binding.kind {
|
||||
@@ -4598,6 +4683,7 @@ impl<'a> Checker<'a> {
|
||||
&deleted,
|
||||
self.locator,
|
||||
self.indexer,
|
||||
self.stylist,
|
||||
) {
|
||||
Ok(fix) => {
|
||||
if fix.content.is_empty() || fix.content == "pass" {
|
||||
|
||||
@@ -32,9 +32,6 @@ pub fn check(path: &Path, contents: &str, autofix: bool) -> Result<Vec<Diagnosti
|
||||
// Load the relevant `Settings` for the given `Path`.
|
||||
let settings = resolve(path)?;
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
settings.validate()?;
|
||||
|
||||
// Tokenize once.
|
||||
let tokens: Vec<LexResult> = tokenize(contents);
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ use crate::registry::Rule;
|
||||
use crate::rules::{
|
||||
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_errmsg,
|
||||
flake8_implicit_str_concat, flake8_import_conventions, flake8_pytest_style, flake8_quotes,
|
||||
flake8_tidy_imports, flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle,
|
||||
pydocstyle, pylint, pyupgrade,
|
||||
flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming,
|
||||
pycodestyle, pydocstyle, pylint, pyupgrade,
|
||||
};
|
||||
use crate::rustpython_helpers::tokenize;
|
||||
use crate::settings::configuration::Configuration;
|
||||
@@ -141,13 +141,14 @@ pub fn defaultSettings() -> Result<JsValue, JsValue> {
|
||||
flake8_errmsg: Some(flake8_errmsg::settings::Settings::default().into()),
|
||||
flake8_pytest_style: Some(flake8_pytest_style::settings::Settings::default().into()),
|
||||
flake8_quotes: Some(flake8_quotes::settings::Settings::default().into()),
|
||||
flake8_tidy_imports: Some(flake8_tidy_imports::Settings::default().into()),
|
||||
flake8_implicit_str_concat: Some(
|
||||
flake8_implicit_str_concat::settings::Settings::default().into(),
|
||||
),
|
||||
flake8_import_conventions: Some(
|
||||
flake8_import_conventions::settings::Settings::default().into(),
|
||||
),
|
||||
flake8_tidy_imports: Some(flake8_tidy_imports::Settings::default().into()),
|
||||
flake8_type_checking: Some(flake8_type_checking::settings::Settings::default().into()),
|
||||
flake8_unused_arguments: Some(
|
||||
flake8_unused_arguments::settings::Settings::default().into(),
|
||||
),
|
||||
|
||||
@@ -42,9 +42,6 @@ pub fn check_path(
|
||||
autofix: flags::Autofix,
|
||||
noqa: flags::Noqa,
|
||||
) -> Result<Vec<Diagnostic>> {
|
||||
// Validate the `Settings` and return any errors.
|
||||
settings.validate()?;
|
||||
|
||||
// Aggregate all diagnostics.
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
@@ -187,9 +184,6 @@ const MAX_ITERATIONS: usize = 100;
|
||||
|
||||
/// Add any missing `#noqa` pragmas to the source code at the given `Path`.
|
||||
pub fn add_noqa_to_path(path: &Path, settings: &Settings) -> Result<usize> {
|
||||
// Validate the `Settings` and return any errors.
|
||||
settings.validate()?;
|
||||
|
||||
// Read the file from disk.
|
||||
let contents = fs::read_file(path)?;
|
||||
|
||||
|
||||
@@ -211,6 +211,7 @@ mod tests {
|
||||
use crate::ast::types::Range;
|
||||
use crate::noqa::{add_noqa_inner, NOQA_LINE_REGEX};
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::rules::pycodestyle::rules::AmbiguousVariableName;
|
||||
use crate::settings::hashable::HashableHashSet;
|
||||
use crate::source_code::LineEnding;
|
||||
use crate::violations;
|
||||
@@ -264,7 +265,7 @@ mod tests {
|
||||
|
||||
let diagnostics = vec
|
||||
#[prefix = "I"]
|
||||
Isort,
|
||||
/// [pep8-naming](https://pypi.org/project/pep8-naming/)
|
||||
#[prefix = "N"]
|
||||
PEP8Naming,
|
||||
/// [pydocstyle](https://pypi.org/project/pydocstyle/)
|
||||
#[prefix = "D"]
|
||||
Pydocstyle,
|
||||
/// [pyupgrade](https://pypi.org/project/pyupgrade/)
|
||||
#[prefix = "UP"]
|
||||
Pyupgrade,
|
||||
/// [pep8-naming](https://pypi.org/project/pep8-naming/)
|
||||
#[prefix = "N"]
|
||||
PEP8Naming,
|
||||
/// [flake8-2020](https://pypi.org/project/flake8-2020/)
|
||||
#[prefix = "YTT"]
|
||||
Flake82020,
|
||||
@@ -520,21 +532,39 @@ pub enum Linter {
|
||||
/// [flake8-builtins](https://pypi.org/project/flake8-builtins/)
|
||||
#[prefix = "A"]
|
||||
Flake8Builtins,
|
||||
/// [flake8-commas](https://pypi.org/project/flake8-commas/)
|
||||
#[prefix = "COM"]
|
||||
Flake8Commas,
|
||||
/// [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/)
|
||||
#[prefix = "C4"]
|
||||
Flake8Comprehensions,
|
||||
/// [flake8-datetimez](https://pypi.org/project/flake8-datetimez/)
|
||||
#[prefix = "DTZ"]
|
||||
Flake8Datetimez,
|
||||
/// [flake8-debugger](https://pypi.org/project/flake8-debugger/)
|
||||
#[prefix = "T10"]
|
||||
Flake8Debugger,
|
||||
/// [flake8-errmsg](https://pypi.org/project/flake8-errmsg/)
|
||||
#[prefix = "EM"]
|
||||
Flake8ErrMsg,
|
||||
/// [flake8-executable](https://pypi.org/project/flake8-executable/)
|
||||
#[prefix = "EXE"]
|
||||
Flake8Executable,
|
||||
/// [flake8-implicit-str-concat](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
#[prefix = "ISC"]
|
||||
Flake8ImplicitStrConcat,
|
||||
/// [flake8-import-conventions](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
#[prefix = "ICN"]
|
||||
Flake8ImportConventions,
|
||||
/// [flake8-logging-format](https://pypi.org/project/flake8-logging-format/0.9.0/)
|
||||
#[prefix = "G"]
|
||||
Flake8LoggingFormat,
|
||||
/// [flake8-no-pep420](https://pypi.org/project/flake8-no-pep420/)
|
||||
#[prefix = "INP"]
|
||||
Flake8NoPep420,
|
||||
/// [flake8-pie](https://pypi.org/project/flake8-pie/)
|
||||
#[prefix = "PIE"]
|
||||
Flake8Pie,
|
||||
/// [flake8-print](https://pypi.org/project/flake8-print/)
|
||||
#[prefix = "T20"]
|
||||
Flake8Print,
|
||||
@@ -553,12 +583,15 @@ pub enum Linter {
|
||||
/// [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/)
|
||||
#[prefix = "TID"]
|
||||
Flake8TidyImports,
|
||||
/// [flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
|
||||
#[prefix = "TCH"]
|
||||
Flake8TypeChecking,
|
||||
/// [flake8-unused-arguments](https://pypi.org/project/flake8-unused-arguments/)
|
||||
#[prefix = "ARG"]
|
||||
Flake8UnusedArguments,
|
||||
/// [flake8-datetimez](https://pypi.org/project/flake8-datetimez/)
|
||||
#[prefix = "DTZ"]
|
||||
Flake8Datetimez,
|
||||
/// [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/)
|
||||
#[prefix = "PTH"]
|
||||
Flake8UsePathlib,
|
||||
/// [eradicate](https://pypi.org/project/eradicate/)
|
||||
#[prefix = "ERA"]
|
||||
Eradicate,
|
||||
@@ -571,27 +604,9 @@ pub enum Linter {
|
||||
/// [Pylint](https://pypi.org/project/pylint/)
|
||||
#[prefix = "PL"]
|
||||
Pylint,
|
||||
/// [flake8-pie](https://pypi.org/project/flake8-pie/)
|
||||
#[prefix = "PIE"]
|
||||
Flake8Pie,
|
||||
/// [flake8-commas](https://pypi.org/project/flake8-commas/)
|
||||
#[prefix = "COM"]
|
||||
Flake8Commas,
|
||||
/// [flake8-no-pep420](https://pypi.org/project/flake8-no-pep420/)
|
||||
#[prefix = "INP"]
|
||||
Flake8NoPep420,
|
||||
/// [flake8-executable](https://pypi.org/project/flake8-executable/)
|
||||
#[prefix = "EXE"]
|
||||
Flake8Executable,
|
||||
/// [flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
|
||||
#[prefix = "TCH"]
|
||||
Flake8TypeChecking,
|
||||
/// [tryceratops](https://pypi.org/project/tryceratops/1.1.0/)
|
||||
#[prefix = "TRY"]
|
||||
Tryceratops,
|
||||
/// [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/)
|
||||
#[prefix = "PTH"]
|
||||
Flake8UsePathlib,
|
||||
/// Ruff-specific rules
|
||||
#[prefix = "RUF"]
|
||||
Ruff,
|
||||
|
||||
@@ -16,13 +16,6 @@ use crate::settings::configuration::Configuration;
|
||||
use crate::settings::pyproject::settings_toml;
|
||||
use crate::settings::{pyproject, AllSettings, Settings};
|
||||
|
||||
/// The strategy used to discover Python files in the filesystem..
|
||||
#[derive(Debug)]
|
||||
pub struct FileDiscovery {
|
||||
pub force_exclude: bool,
|
||||
pub respect_gitignore: bool,
|
||||
}
|
||||
|
||||
/// The strategy used to discover the relevant `pyproject.toml` file for each
|
||||
/// Python file.
|
||||
#[derive(Debug)]
|
||||
@@ -35,6 +28,15 @@ pub enum PyprojectDiscovery {
|
||||
Hierarchical(AllSettings),
|
||||
}
|
||||
|
||||
impl PyprojectDiscovery {
|
||||
fn top_level_settings(&self) -> &AllSettings {
|
||||
match self {
|
||||
PyprojectDiscovery::Fixed(settings) => settings,
|
||||
PyprojectDiscovery::Hierarchical(settings) => settings,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The strategy for resolving file paths in a `pyproject.toml`.
|
||||
pub enum Relativity {
|
||||
/// Resolve file paths relative to the current working directory.
|
||||
@@ -98,26 +100,6 @@ impl Resolver {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &AllSettings> {
|
||||
self.settings.values()
|
||||
}
|
||||
|
||||
/// Validate all resolved `Settings` in this `Resolver`.
|
||||
pub fn validate(&self, strategy: &PyprojectDiscovery) -> Result<()> {
|
||||
// TODO(charlie): This risks false positives (but not false negatives), since
|
||||
// some of the `Settings` in the path may ultimately be unused (or, e.g., they
|
||||
// could have their `required_version` overridden by other `Settings` in
|
||||
// the path). It'd be preferable to validate once we've determined the
|
||||
// `Settings` for each path, but that's more expensive.
|
||||
match &strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => {
|
||||
settings.lib.validate()?;
|
||||
}
|
||||
PyprojectDiscovery::Hierarchical(default) => {
|
||||
for settings in std::iter::once(default).chain(self.iter()) {
|
||||
settings.lib.validate()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ConfigProcessor: Copy + Send + Sync {
|
||||
@@ -232,7 +214,6 @@ pub fn is_python_entry(entry: &DirEntry) -> bool {
|
||||
pub fn python_files_in_path(
|
||||
paths: &[PathBuf],
|
||||
pyproject_strategy: &PyprojectDiscovery,
|
||||
file_strategy: &FileDiscovery,
|
||||
processor: impl ConfigProcessor,
|
||||
) -> Result<(Vec<Result<DirEntry, ignore::Error>>, Resolver)> {
|
||||
// Normalize every path (e.g., convert from relative to absolute).
|
||||
@@ -256,7 +237,7 @@ pub fn python_files_in_path(
|
||||
}
|
||||
|
||||
// Check if the paths themselves are excluded.
|
||||
if file_strategy.force_exclude {
|
||||
if pyproject_strategy.top_level_settings().lib.force_exclude {
|
||||
paths.retain(|path| !is_file_excluded(path, &resolver, pyproject_strategy));
|
||||
if paths.is_empty() {
|
||||
return Ok((vec![], resolver));
|
||||
@@ -272,7 +253,12 @@ pub fn python_files_in_path(
|
||||
for path in &paths[1..] {
|
||||
builder.add(path);
|
||||
}
|
||||
builder.standard_filters(file_strategy.respect_gitignore);
|
||||
builder.standard_filters(
|
||||
pyproject_strategy
|
||||
.top_level_settings()
|
||||
.lib
|
||||
.respect_gitignore,
|
||||
);
|
||||
builder.hidden(false);
|
||||
let walker = builder.build_parallel();
|
||||
|
||||
@@ -369,10 +355,9 @@ pub fn python_files_in_path(
|
||||
pub fn python_file_at_path(
|
||||
path: &Path,
|
||||
pyproject_strategy: &PyprojectDiscovery,
|
||||
file_strategy: &FileDiscovery,
|
||||
processor: impl ConfigProcessor,
|
||||
) -> Result<bool> {
|
||||
if !file_strategy.force_exclude {
|
||||
if !pyproject_strategy.top_level_settings().lib.force_exclude {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::Location;
|
||||
|
||||
use super::detection::comment_contains_code;
|
||||
use crate::ast::types::Range;
|
||||
use crate::define_violation;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::Locator;
|
||||
use crate::violations;
|
||||
use crate::violation::AlwaysAutofixableViolation;
|
||||
|
||||
define_violation!(
|
||||
pub struct CommentedOutCode;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for CommentedOutCode {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Found commented-out code")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Remove commented-out code".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn is_standalone_comment(line: &str) -> bool {
|
||||
for char in line.chars() {
|
||||
@@ -33,7 +49,7 @@ pub fn commented_out_code(
|
||||
|
||||
// Verify that the comment is on its own line, and that it contains code.
|
||||
if is_standalone_comment(line) && comment_contains_code(line, &settings.task_tags[..]) {
|
||||
let mut diagnostic = Diagnostic::new(violations::CommentedOutCode, Range::new(start, end));
|
||||
let mut diagnostic = Diagnostic::new(CommentedOutCode, Range::new(start, end));
|
||||
if matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&Rule::CommentedOutCode)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use rustpython_ast::{Expr, ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::helpers;
|
||||
use crate::ast::helpers::{find_keyword, is_const_true};
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Diagnostic;
|
||||
@@ -21,7 +23,7 @@ pub fn blind_except(
|
||||
for exception in ["BaseException", "Exception"] {
|
||||
if id == exception && checker.is_builtin(exception) {
|
||||
// If the exception is re-raised, don't flag an error.
|
||||
if !body.iter().any(|stmt| {
|
||||
if body.iter().any(|stmt| {
|
||||
if let StmtKind::Raise { exc, .. } = &stmt.node {
|
||||
if let Some(exc) = exc {
|
||||
if let ExprKind::Name { id, .. } = &exc.node {
|
||||
@@ -36,11 +38,38 @@ pub fn blind_except(
|
||||
false
|
||||
}
|
||||
}) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::BlindExcept(id.to_string()),
|
||||
Range::from_located(type_),
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the exception is logged, don't flag an error.
|
||||
if body.iter().any(|stmt| {
|
||||
if let StmtKind::Expr { value } = &stmt.node {
|
||||
if let ExprKind::Call { func, keywords, .. } = &value.node {
|
||||
if helpers::is_logger_candidate(func) {
|
||||
if let ExprKind::Attribute { attr, .. } = &func.node {
|
||||
if attr == "exception" {
|
||||
return true;
|
||||
}
|
||||
if attr == "error" {
|
||||
if let Some(keyword) = find_keyword(keywords, "exc_info") {
|
||||
if is_const_true(&keyword.node.value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::BlindExcept(id.to_string()),
|
||||
Range::from_located(type_),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,4 +72,34 @@ expression: diagnostics
|
||||
column: 20
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
BlindExcept: Exception
|
||||
location:
|
||||
row: 69
|
||||
column: 7
|
||||
end_location:
|
||||
row: 69
|
||||
column: 16
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
BlindExcept: Exception
|
||||
location:
|
||||
row: 75
|
||||
column: 7
|
||||
end_location:
|
||||
row: 75
|
||||
column: 16
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
BlindExcept: Exception
|
||||
location:
|
||||
row: 81
|
||||
column: 7
|
||||
end_location:
|
||||
row: 81
|
||||
column: 16
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
|
||||
@@ -1,10 +1,42 @@
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Arguments, ExprKind};
|
||||
use rustpython_parser::ast::{Constant, Expr};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::{Diagnostic, DiagnosticKind};
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
pub struct BooleanPositionalArgInFunctionDefinition;
|
||||
);
|
||||
impl Violation for BooleanPositionalArgInFunctionDefinition {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Boolean positional arg in function definition")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct BooleanDefaultValueInFunctionDefinition;
|
||||
);
|
||||
impl Violation for BooleanDefaultValueInFunctionDefinition {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Boolean default value in function definition")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct BooleanPositionalValueInFunctionCall;
|
||||
);
|
||||
impl Violation for BooleanPositionalValueInFunctionCall {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Boolean positional value in function call")
|
||||
}
|
||||
}
|
||||
|
||||
const FUNC_NAME_ALLOWLIST: &[&str] = &[
|
||||
"assertEqual",
|
||||
@@ -77,7 +109,7 @@ pub fn check_positional_boolean_in_def(checker: &mut Checker, arguments: &Argume
|
||||
continue;
|
||||
}
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::BooleanPositionalArgInFunctionDefinition,
|
||||
BooleanPositionalArgInFunctionDefinition,
|
||||
Range::from_located(arg),
|
||||
));
|
||||
}
|
||||
@@ -88,11 +120,7 @@ pub fn check_boolean_default_value_in_function_definition(
|
||||
arguments: &Arguments,
|
||||
) {
|
||||
for arg in &arguments.defaults {
|
||||
add_if_boolean(
|
||||
checker,
|
||||
arg,
|
||||
violations::BooleanDefaultValueInFunctionDefinition.into(),
|
||||
);
|
||||
add_if_boolean(checker, arg, BooleanDefaultValueInFunctionDefinition.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,10 +133,6 @@ pub fn check_boolean_positional_value_in_function_call(
|
||||
if allow_boolean_trap(func) {
|
||||
continue;
|
||||
}
|
||||
add_if_boolean(
|
||||
checker,
|
||||
arg,
|
||||
violations::BooleanPositionalValueInFunctionCall.into(),
|
||||
);
|
||||
add_if_boolean(checker, arg, BooleanPositionalValueInFunctionCall.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::ast::helpers::unparse_expr;
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::Fix;
|
||||
use crate::python::identifiers::is_identifier;
|
||||
use crate::python::identifiers::{is_identifier, is_mangled_private};
|
||||
use crate::python::keyword::KWLIST;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
@@ -41,12 +41,13 @@ pub fn getattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
|
||||
if !is_identifier(value) {
|
||||
return;
|
||||
}
|
||||
if KWLIST.contains(&value.as_str()) {
|
||||
if KWLIST.contains(&value.as_str()) || is_mangled_private(value.as_str()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(violations::GetAttrWithConstant, Range::from_located(expr));
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.amend(Fix::replacement(
|
||||
unparse_expr(&attribute(obj, value), checker.stylist),
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::ast::helpers::unparse_stmt;
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::Fix;
|
||||
use crate::python::identifiers::is_identifier;
|
||||
use crate::python::identifiers::{is_identifier, is_mangled_private};
|
||||
use crate::python::keyword::KWLIST;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::source_code::Stylist;
|
||||
@@ -51,7 +51,7 @@ pub fn setattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
|
||||
if !is_identifier(name) {
|
||||
return;
|
||||
}
|
||||
if KWLIST.contains(&name.as_str()) {
|
||||
if KWLIST.contains(&name.as_str()) || is_mangled_private(name.as_str()) {
|
||||
return;
|
||||
}
|
||||
// We can only replace a `setattr` call (which is an `Expr`) with an assignment
|
||||
@@ -61,6 +61,7 @@ pub fn setattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
|
||||
if expr == child.as_ref() {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(violations::SetAttrWithConstant, Range::from_located(expr));
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.amend(Fix::replacement(
|
||||
assignment(obj, name, value, checker.stylist),
|
||||
|
||||
@@ -5,109 +5,127 @@ expression: diagnostics
|
||||
- kind:
|
||||
GetAttrWithConstant: ~
|
||||
location:
|
||||
row: 18
|
||||
row: 19
|
||||
column: 0
|
||||
end_location:
|
||||
row: 18
|
||||
row: 19
|
||||
column: 19
|
||||
fix:
|
||||
content:
|
||||
- foo.bar
|
||||
location:
|
||||
row: 18
|
||||
row: 19
|
||||
column: 0
|
||||
end_location:
|
||||
row: 18
|
||||
row: 19
|
||||
column: 19
|
||||
parent: ~
|
||||
- kind:
|
||||
GetAttrWithConstant: ~
|
||||
location:
|
||||
row: 19
|
||||
row: 20
|
||||
column: 0
|
||||
end_location:
|
||||
row: 19
|
||||
row: 20
|
||||
column: 23
|
||||
fix:
|
||||
content:
|
||||
- foo._123abc
|
||||
location:
|
||||
row: 19
|
||||
row: 20
|
||||
column: 0
|
||||
end_location:
|
||||
row: 19
|
||||
row: 20
|
||||
column: 23
|
||||
parent: ~
|
||||
- kind:
|
||||
GetAttrWithConstant: ~
|
||||
location:
|
||||
row: 20
|
||||
row: 21
|
||||
column: 0
|
||||
end_location:
|
||||
row: 20
|
||||
row: 21
|
||||
column: 26
|
||||
fix:
|
||||
content:
|
||||
- foo.__123abc__
|
||||
location:
|
||||
row: 21
|
||||
column: 0
|
||||
end_location:
|
||||
row: 21
|
||||
column: 26
|
||||
parent: ~
|
||||
- kind:
|
||||
GetAttrWithConstant: ~
|
||||
location:
|
||||
row: 22
|
||||
column: 0
|
||||
end_location:
|
||||
row: 22
|
||||
column: 22
|
||||
fix:
|
||||
content:
|
||||
- foo.abc123
|
||||
location:
|
||||
row: 20
|
||||
row: 22
|
||||
column: 0
|
||||
end_location:
|
||||
row: 20
|
||||
row: 22
|
||||
column: 22
|
||||
parent: ~
|
||||
- kind:
|
||||
GetAttrWithConstant: ~
|
||||
location:
|
||||
row: 21
|
||||
row: 23
|
||||
column: 0
|
||||
end_location:
|
||||
row: 21
|
||||
row: 23
|
||||
column: 23
|
||||
fix:
|
||||
content:
|
||||
- foo.abc123
|
||||
location:
|
||||
row: 21
|
||||
row: 23
|
||||
column: 0
|
||||
end_location:
|
||||
row: 21
|
||||
row: 23
|
||||
column: 23
|
||||
parent: ~
|
||||
- kind:
|
||||
GetAttrWithConstant: ~
|
||||
location:
|
||||
row: 22
|
||||
row: 24
|
||||
column: 14
|
||||
end_location:
|
||||
row: 22
|
||||
row: 24
|
||||
column: 31
|
||||
fix:
|
||||
content:
|
||||
- x.bar
|
||||
location:
|
||||
row: 22
|
||||
row: 24
|
||||
column: 14
|
||||
end_location:
|
||||
row: 22
|
||||
row: 24
|
||||
column: 31
|
||||
parent: ~
|
||||
- kind:
|
||||
GetAttrWithConstant: ~
|
||||
location:
|
||||
row: 23
|
||||
row: 25
|
||||
column: 3
|
||||
end_location:
|
||||
row: 23
|
||||
row: 25
|
||||
column: 20
|
||||
fix:
|
||||
content:
|
||||
- x.bar
|
||||
location:
|
||||
row: 23
|
||||
row: 25
|
||||
column: 3
|
||||
end_location:
|
||||
row: 23
|
||||
row: 25
|
||||
column: 20
|
||||
parent: ~
|
||||
|
||||
|
||||
@@ -5,91 +5,109 @@ expression: diagnostics
|
||||
- kind:
|
||||
SetAttrWithConstant: ~
|
||||
location:
|
||||
row: 37
|
||||
row: 40
|
||||
column: 0
|
||||
end_location:
|
||||
row: 37
|
||||
row: 40
|
||||
column: 25
|
||||
fix:
|
||||
content:
|
||||
- foo.bar = None
|
||||
location:
|
||||
row: 37
|
||||
row: 40
|
||||
column: 0
|
||||
end_location:
|
||||
row: 37
|
||||
row: 40
|
||||
column: 25
|
||||
parent: ~
|
||||
- kind:
|
||||
SetAttrWithConstant: ~
|
||||
location:
|
||||
row: 38
|
||||
row: 41
|
||||
column: 0
|
||||
end_location:
|
||||
row: 38
|
||||
row: 41
|
||||
column: 29
|
||||
fix:
|
||||
content:
|
||||
- foo._123abc = None
|
||||
location:
|
||||
row: 38
|
||||
row: 41
|
||||
column: 0
|
||||
end_location:
|
||||
row: 38
|
||||
row: 41
|
||||
column: 29
|
||||
parent: ~
|
||||
- kind:
|
||||
SetAttrWithConstant: ~
|
||||
location:
|
||||
row: 39
|
||||
row: 42
|
||||
column: 0
|
||||
end_location:
|
||||
row: 39
|
||||
row: 42
|
||||
column: 32
|
||||
fix:
|
||||
content:
|
||||
- foo.__123abc__ = None
|
||||
location:
|
||||
row: 42
|
||||
column: 0
|
||||
end_location:
|
||||
row: 42
|
||||
column: 32
|
||||
parent: ~
|
||||
- kind:
|
||||
SetAttrWithConstant: ~
|
||||
location:
|
||||
row: 43
|
||||
column: 0
|
||||
end_location:
|
||||
row: 43
|
||||
column: 28
|
||||
fix:
|
||||
content:
|
||||
- foo.abc123 = None
|
||||
location:
|
||||
row: 39
|
||||
row: 43
|
||||
column: 0
|
||||
end_location:
|
||||
row: 39
|
||||
row: 43
|
||||
column: 28
|
||||
parent: ~
|
||||
- kind:
|
||||
SetAttrWithConstant: ~
|
||||
location:
|
||||
row: 40
|
||||
row: 44
|
||||
column: 0
|
||||
end_location:
|
||||
row: 40
|
||||
row: 44
|
||||
column: 29
|
||||
fix:
|
||||
content:
|
||||
- foo.abc123 = None
|
||||
location:
|
||||
row: 40
|
||||
row: 44
|
||||
column: 0
|
||||
end_location:
|
||||
row: 40
|
||||
row: 44
|
||||
column: 29
|
||||
parent: ~
|
||||
- kind:
|
||||
SetAttrWithConstant: ~
|
||||
location:
|
||||
row: 41
|
||||
row: 45
|
||||
column: 0
|
||||
end_location:
|
||||
row: 41
|
||||
row: 45
|
||||
column: 30
|
||||
fix:
|
||||
content:
|
||||
- foo.bar.baz = None
|
||||
location:
|
||||
row: 41
|
||||
row: 45
|
||||
column: 0
|
||||
end_location:
|
||||
row: 41
|
||||
row: 45
|
||||
column: 30
|
||||
parent: ~
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use itertools::Itertools;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_parser::lexer::{LexResult, Spanned};
|
||||
use rustpython_parser::token::Tok;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::define_violation;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::violations;
|
||||
use crate::violation::{AlwaysAutofixableViolation, Violation};
|
||||
|
||||
/// Simplified token type.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
@@ -107,6 +109,44 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct TrailingCommaMissing;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for TrailingCommaMissing {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Trailing comma missing")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Add trailing comma".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct TrailingCommaOnBareTupleProhibited;
|
||||
);
|
||||
impl Violation for TrailingCommaOnBareTupleProhibited {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Trailing comma on bare tuple prohibited")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct TrailingCommaProhibited;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for TrailingCommaProhibited {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Trailing comma prohibited")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Remove trailing comma".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// COM812, COM818, COM819
|
||||
pub fn trailing_commas(
|
||||
tokens: &[LexResult],
|
||||
@@ -212,7 +252,7 @@ pub fn trailing_commas(
|
||||
if comma_prohibited {
|
||||
let comma = prev.spanned.unwrap();
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::TrailingCommaProhibited,
|
||||
TrailingCommaProhibited,
|
||||
Range {
|
||||
location: comma.0,
|
||||
end_location: comma.2,
|
||||
@@ -233,7 +273,7 @@ pub fn trailing_commas(
|
||||
if bare_comma_prohibited {
|
||||
let comma = prev.spanned.unwrap();
|
||||
diagnostics.push(Diagnostic::new(
|
||||
violations::TrailingCommaOnBareTupleProhibited,
|
||||
TrailingCommaOnBareTupleProhibited,
|
||||
Range {
|
||||
location: comma.0,
|
||||
end_location: comma.2,
|
||||
@@ -258,7 +298,7 @@ pub fn trailing_commas(
|
||||
if comma_required {
|
||||
let missing_comma = prev_prev.spanned.unwrap();
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::TrailingCommaMissing,
|
||||
TrailingCommaMissing,
|
||||
Range {
|
||||
location: missing_comma.2,
|
||||
end_location: missing_comma.2,
|
||||
|
||||
@@ -9,7 +9,7 @@ use libcst_native::{
|
||||
use crate::ast::types::Range;
|
||||
use crate::cst::matchers::{match_expr, match_module};
|
||||
use crate::fix::Fix;
|
||||
use crate::source_code::Locator;
|
||||
use crate::source_code::{Locator, Stylist};
|
||||
|
||||
fn match_call<'a, 'b>(expr: &'a mut Expr<'b>) -> Result<&'a mut Call<'b>> {
|
||||
if let Expression::Call(call) = &mut expr.value {
|
||||
@@ -30,6 +30,7 @@ fn match_arg<'a, 'b>(call: &'a Call<'b>) -> Result<&'a Arg<'b>> {
|
||||
/// (C400) Convert `list(x for x in y)` to `[x for x in y]`.
|
||||
pub fn fix_unnecessary_generator_list(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
// Expr(Call(GeneratorExp)))) -> Expr(ListComp)))
|
||||
@@ -58,7 +59,11 @@ pub fn fix_unnecessary_generator_list(
|
||||
rpar: generator_exp.rpar.clone(),
|
||||
}));
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
@@ -71,6 +76,7 @@ pub fn fix_unnecessary_generator_list(
|
||||
/// (C401) Convert `set(x for x in y)` to `{x for x in y}`.
|
||||
pub fn fix_unnecessary_generator_set(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
// Expr(Call(GeneratorExp)))) -> Expr(SetComp)))
|
||||
@@ -99,7 +105,11 @@ pub fn fix_unnecessary_generator_set(
|
||||
rpar: generator_exp.rpar.clone(),
|
||||
}));
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
@@ -113,6 +123,7 @@ pub fn fix_unnecessary_generator_set(
|
||||
/// range(3)}`.
|
||||
pub fn fix_unnecessary_generator_dict(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(expr));
|
||||
@@ -157,7 +168,11 @@ pub fn fix_unnecessary_generator_dict(
|
||||
whitespace_after_colon: ParenthesizableWhitespace::SimpleWhitespace(SimpleWhitespace(" ")),
|
||||
}));
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
@@ -170,6 +185,7 @@ pub fn fix_unnecessary_generator_dict(
|
||||
/// (C403) Convert `set([x for x in y])` to `{x for x in y}`.
|
||||
pub fn fix_unnecessary_list_comprehension_set(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
// Expr(Call(ListComp)))) ->
|
||||
@@ -197,7 +213,11 @@ pub fn fix_unnecessary_list_comprehension_set(
|
||||
rpar: list_comp.rpar.clone(),
|
||||
}));
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
@@ -211,6 +231,7 @@ pub fn fix_unnecessary_list_comprehension_set(
|
||||
/// range(3)}`.
|
||||
pub fn fix_unnecessary_list_comprehension_dict(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(expr));
|
||||
@@ -248,7 +269,11 @@ pub fn fix_unnecessary_list_comprehension_dict(
|
||||
rpar: list_comp.rpar.clone(),
|
||||
}));
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
@@ -302,7 +327,11 @@ fn drop_trailing_comma<'a>(
|
||||
}
|
||||
|
||||
/// (C405) Convert `set((1, 2))` to `{1, 2}`.
|
||||
pub fn fix_unnecessary_literal_set(locator: &Locator, expr: &rustpython_ast::Expr) -> Result<Fix> {
|
||||
pub fn fix_unnecessary_literal_set(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
// Expr(Call(List|Tuple)))) -> Expr(Set)))
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(expr));
|
||||
let mut tree = match_module(module_text)?;
|
||||
@@ -334,7 +363,11 @@ pub fn fix_unnecessary_literal_set(locator: &Locator, expr: &rustpython_ast::Exp
|
||||
}));
|
||||
}
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
@@ -345,7 +378,11 @@ pub fn fix_unnecessary_literal_set(locator: &Locator, expr: &rustpython_ast::Exp
|
||||
}
|
||||
|
||||
/// (C406) Convert `dict([(1, 2)])` to `{1: 2}`.
|
||||
pub fn fix_unnecessary_literal_dict(locator: &Locator, expr: &rustpython_ast::Expr) -> Result<Fix> {
|
||||
pub fn fix_unnecessary_literal_dict(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
// Expr(Call(List|Tuple)))) -> Expr(Dict)))
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(expr));
|
||||
let mut tree = match_module(module_text)?;
|
||||
@@ -399,7 +436,11 @@ pub fn fix_unnecessary_literal_dict(locator: &Locator, expr: &rustpython_ast::Ex
|
||||
rpar: vec![],
|
||||
}));
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
@@ -412,6 +453,7 @@ pub fn fix_unnecessary_literal_dict(locator: &Locator, expr: &rustpython_ast::Ex
|
||||
/// (C408)
|
||||
pub fn fix_unnecessary_collection_call(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
// Expr(Call("list" | "tuple" | "dict")))) -> Expr(List|Tuple|Dict)
|
||||
@@ -508,7 +550,11 @@ pub fn fix_unnecessary_collection_call(
|
||||
}
|
||||
};
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
@@ -521,6 +567,7 @@ pub fn fix_unnecessary_collection_call(
|
||||
/// (C409) Convert `tuple([1, 2])` to `tuple(1, 2)`
|
||||
pub fn fix_unnecessary_literal_within_tuple_call(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(expr));
|
||||
@@ -562,7 +609,11 @@ pub fn fix_unnecessary_literal_within_tuple_call(
|
||||
}],
|
||||
}));
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
@@ -575,6 +626,7 @@ pub fn fix_unnecessary_literal_within_tuple_call(
|
||||
/// (C410) Convert `list([1, 2])` to `[1, 2]`
|
||||
pub fn fix_unnecessary_literal_within_list_call(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(expr));
|
||||
@@ -618,7 +670,11 @@ pub fn fix_unnecessary_literal_within_list_call(
|
||||
rpar: vec![],
|
||||
}));
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
@@ -629,7 +685,11 @@ pub fn fix_unnecessary_literal_within_list_call(
|
||||
}
|
||||
|
||||
/// (C411) Convert `list([i * i for i in x])` to `[i * i for i in x]`.
|
||||
pub fn fix_unnecessary_list_call(locator: &Locator, expr: &rustpython_ast::Expr) -> Result<Fix> {
|
||||
pub fn fix_unnecessary_list_call(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
// Expr(Call(List|Tuple)))) -> Expr(List|Tuple)))
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(expr));
|
||||
let mut tree = match_module(module_text)?;
|
||||
@@ -639,7 +699,11 @@ pub fn fix_unnecessary_list_call(locator: &Locator, expr: &rustpython_ast::Expr)
|
||||
|
||||
body.value = arg.value.clone();
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
@@ -654,6 +718,7 @@ pub fn fix_unnecessary_list_call(locator: &Locator, expr: &rustpython_ast::Expr)
|
||||
/// reverse=True)`.
|
||||
pub fn fix_unnecessary_call_around_sorted(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(expr));
|
||||
@@ -723,7 +788,11 @@ pub fn fix_unnecessary_call_around_sorted(
|
||||
}
|
||||
}
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
@@ -736,6 +805,7 @@ pub fn fix_unnecessary_call_around_sorted(
|
||||
/// (C416) Convert `[i for i in x]` to `list(x)`.
|
||||
pub fn fix_unnecessary_comprehension(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
expr: &rustpython_ast::Expr,
|
||||
) -> Result<Fix> {
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(expr));
|
||||
@@ -792,7 +862,11 @@ pub fn fix_unnecessary_comprehension(
|
||||
}
|
||||
}
|
||||
|
||||
let mut state = CodegenState::default();
|
||||
let mut state = CodegenState {
|
||||
default_newline: stylist.line_ending(),
|
||||
default_indent: stylist.indentation(),
|
||||
..CodegenState::default()
|
||||
};
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
|
||||
@@ -66,7 +66,7 @@ pub fn unnecessary_generator_list(
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(&Rule::UnnecessaryGeneratorList) {
|
||||
match fixes::fix_unnecessary_generator_list(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_generator_list(checker.locator, checker.stylist, expr) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
@@ -97,7 +97,7 @@ pub fn unnecessary_generator_set(
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(&Rule::UnnecessaryGeneratorSet) {
|
||||
match fixes::fix_unnecessary_generator_set(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_generator_set(checker.locator, checker.stylist, expr) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
@@ -127,7 +127,11 @@ pub fn unnecessary_generator_dict(
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(&Rule::UnnecessaryGeneratorDict) {
|
||||
match fixes::fix_unnecessary_generator_dict(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_generator_dict(
|
||||
checker.locator,
|
||||
checker.stylist,
|
||||
expr,
|
||||
) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
@@ -161,7 +165,11 @@ pub fn unnecessary_list_comprehension_set(
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(&Rule::UnnecessaryListComprehensionSet) {
|
||||
match fixes::fix_unnecessary_list_comprehension_set(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_list_comprehension_set(
|
||||
checker.locator,
|
||||
checker.stylist,
|
||||
expr,
|
||||
) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
@@ -200,7 +208,8 @@ pub fn unnecessary_list_comprehension_dict(
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(&Rule::UnnecessaryListComprehensionDict) {
|
||||
match fixes::fix_unnecessary_list_comprehension_dict(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_list_comprehension_dict(checker.locator, checker.stylist, expr)
|
||||
{
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
@@ -234,7 +243,7 @@ pub fn unnecessary_literal_set(
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(&Rule::UnnecessaryLiteralSet) {
|
||||
match fixes::fix_unnecessary_literal_set(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_literal_set(checker.locator, checker.stylist, expr) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
@@ -275,7 +284,7 @@ pub fn unnecessary_literal_dict(
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(&Rule::UnnecessaryLiteralDict) {
|
||||
match fixes::fix_unnecessary_literal_dict(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_literal_dict(checker.locator, checker.stylist, expr) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
@@ -316,7 +325,7 @@ pub fn unnecessary_collection_call(
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(&Rule::UnnecessaryCollectionCall) {
|
||||
match fixes::fix_unnecessary_collection_call(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_collection_call(checker.locator, checker.stylist, expr) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
@@ -349,7 +358,11 @@ pub fn unnecessary_literal_within_tuple_call(
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(&Rule::UnnecessaryLiteralWithinTupleCall) {
|
||||
match fixes::fix_unnecessary_literal_within_tuple_call(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_literal_within_tuple_call(
|
||||
checker.locator,
|
||||
checker.stylist,
|
||||
expr,
|
||||
) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
@@ -382,7 +395,11 @@ pub fn unnecessary_literal_within_list_call(
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(&Rule::UnnecessaryLiteralWithinListCall) {
|
||||
match fixes::fix_unnecessary_literal_within_list_call(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_literal_within_list_call(
|
||||
checker.locator,
|
||||
checker.stylist,
|
||||
expr,
|
||||
) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
@@ -406,7 +423,7 @@ pub fn unnecessary_list_call(checker: &mut Checker, expr: &Expr, func: &Expr, ar
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(violations::UnnecessaryListCall, Range::from_located(expr));
|
||||
if checker.patch(&Rule::UnnecessaryListCall) {
|
||||
match fixes::fix_unnecessary_list_call(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_list_call(checker.locator, checker.stylist, expr) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
@@ -449,7 +466,7 @@ pub fn unnecessary_call_around_sorted(
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(&Rule::UnnecessaryCallAroundSorted) {
|
||||
match fixes::fix_unnecessary_call_around_sorted(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_call_around_sorted(checker.locator, checker.stylist, expr) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
@@ -612,7 +629,7 @@ pub fn unnecessary_comprehension(
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(&Rule::UnnecessaryComprehension) {
|
||||
match fixes::fix_unnecessary_comprehension(checker.locator, expr) {
|
||||
match fixes::fix_unnecessary_comprehension(checker.locator, checker.stylist, expr) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
||||
static SHEBANG_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"(?P<spaces>\s*)#!(?P<directive>.*)").unwrap());
|
||||
Lazy::new(|| Regex::new(r"^(?P<spaces>\s*)#!(?P<directive>.*)").unwrap());
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ShebangDirective<'a> {
|
||||
@@ -67,7 +67,7 @@ mod tests {
|
||||
));
|
||||
assert!(matches!(
|
||||
extract_shebang("print('test') #!/usr/bin/python"),
|
||||
ShebangDirective::Match(2, 17, 32, "/usr/bin/python")
|
||||
ShebangDirective::None
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ impl Violation for ShebangPython {
|
||||
/// EXE003
|
||||
pub fn shebang_python(lineno: usize, shebang: &ShebangDirective) -> Option<Diagnostic> {
|
||||
if let ShebangDirective::Match(_, start, end, content) = shebang {
|
||||
if content.contains("python") {
|
||||
if content.contains("python") || content.contains("pytest") {
|
||||
None
|
||||
} else {
|
||||
let diagnostic = Diagnostic::new(
|
||||
|
||||
@@ -2,5 +2,14 @@
|
||||
source: src/rules/flake8_executable/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
- kind:
|
||||
ShebangMissingExecutableFile: ~
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 1
|
||||
column: 0
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::Stmt;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
pub struct ImportAliasIsNotConventional(pub String, pub String);
|
||||
);
|
||||
impl Violation for ImportAliasIsNotConventional {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let ImportAliasIsNotConventional(name, asname) = self;
|
||||
format!("`{name}` should be imported as `{asname}`")
|
||||
}
|
||||
}
|
||||
|
||||
/// ICN001
|
||||
pub fn check_conventional_import(
|
||||
@@ -25,10 +38,7 @@ pub fn check_conventional_import(
|
||||
}
|
||||
if !is_valid_import {
|
||||
return Some(Diagnostic::new(
|
||||
violations::ImportAliasIsNotConventional(
|
||||
name.to_string(),
|
||||
expected_alias.to_string(),
|
||||
),
|
||||
ImportAliasIsNotConventional(name.to_string(), expected_alias.to_string()),
|
||||
Range::from_located(import_from),
|
||||
));
|
||||
}
|
||||
|
||||
50
src/rules/flake8_logging_format/mod.rs
Normal file
50
src/rules/flake8_logging_format/mod.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
//! Rules from [flake8-logging-format](https://pypi.org/project/flake8-logging-format/0.9.0/).
|
||||
pub(crate) mod rules;
|
||||
pub(crate) mod violations;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings;
|
||||
|
||||
#[test_case(Path::new("G_argparse_parser_error_ok.py"); "G_argparse_parser_error_ok")]
|
||||
#[test_case(Path::new("G_extra_ok.py"); "G_extra_ok")]
|
||||
#[test_case(Path::new("G_extra_str_format_ok.py"); "G_extra_str_format_ok")]
|
||||
#[test_case(Path::new("G_simple_ok.py"); "G_simple_ok")]
|
||||
#[test_case(Path::new("G_warnings_ok.py"); "G_warnings_ok")]
|
||||
#[test_case(Path::new("G001.py"); "G001")]
|
||||
#[test_case(Path::new("G002.py"); "G002")]
|
||||
#[test_case(Path::new("G003.py"); "G003")]
|
||||
#[test_case(Path::new("G004.py"); "G004")]
|
||||
#[test_case(Path::new("G010.py"); "G010")]
|
||||
#[test_case(Path::new("G101_1.py"); "G101_1")]
|
||||
#[test_case(Path::new("G101_2.py"); "G101_2")]
|
||||
#[test_case(Path::new("G201.py"); "G201")]
|
||||
#[test_case(Path::new("G202.py"); "G202")]
|
||||
fn rules(path: &Path) -> Result<()> {
|
||||
let snapshot = path.to_string_lossy().into_owned();
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_logging_format")
|
||||
.join(path)
|
||||
.as_path(),
|
||||
&settings::Settings::for_rules(vec![
|
||||
Rule::LoggingStringFormat,
|
||||
Rule::LoggingPercentFormat,
|
||||
Rule::LoggingStringConcat,
|
||||
Rule::LoggingFString,
|
||||
Rule::LoggingWarn,
|
||||
Rule::LoggingExtraAttrClash,
|
||||
Rule::LoggingExcInfo,
|
||||
Rule::LoggingRedundantExcInfo,
|
||||
]),
|
||||
)?;
|
||||
insta::assert_yaml_snapshot!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
235
src/rules/flake8_logging_format/rules.rs
Normal file
235
src/rules/flake8_logging_format/rules.rs
Normal file
@@ -0,0 +1,235 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprKind, Keyword, Operator};
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::helpers::{find_keyword, is_logger_candidate, SimpleCallArgs};
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::flake8_logging_format::violations::{
|
||||
LoggingExcInfo, LoggingExtraAttrClash, LoggingFString, LoggingPercentFormat,
|
||||
LoggingRedundantExcInfo, LoggingStringConcat, LoggingStringFormat, LoggingWarn,
|
||||
};
|
||||
|
||||
enum LoggingLevel {
|
||||
Debug,
|
||||
Critical,
|
||||
Error,
|
||||
Exception,
|
||||
Info,
|
||||
Warn,
|
||||
Warning,
|
||||
}
|
||||
|
||||
impl LoggingLevel {
|
||||
fn from_str(level: &str) -> Option<Self> {
|
||||
match level {
|
||||
"debug" => Some(LoggingLevel::Debug),
|
||||
"critical" => Some(LoggingLevel::Critical),
|
||||
"error" => Some(LoggingLevel::Error),
|
||||
"exception" => Some(LoggingLevel::Exception),
|
||||
"info" => Some(LoggingLevel::Info),
|
||||
"warn" => Some(LoggingLevel::Warn),
|
||||
"warning" => Some(LoggingLevel::Warning),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const RESERVED_ATTRS: &[&str; 22] = &[
|
||||
"args",
|
||||
"asctime",
|
||||
"created",
|
||||
"exc_info",
|
||||
"exc_text",
|
||||
"filename",
|
||||
"funcName",
|
||||
"levelname",
|
||||
"levelno",
|
||||
"lineno",
|
||||
"module",
|
||||
"msecs",
|
||||
"message",
|
||||
"msg",
|
||||
"name",
|
||||
"pathname",
|
||||
"process",
|
||||
"processName",
|
||||
"relativeCreated",
|
||||
"stack_info",
|
||||
"thread",
|
||||
"threadName",
|
||||
];
|
||||
|
||||
/// Check logging messages for violations.
|
||||
fn check_msg(checker: &mut Checker, msg: &Expr) {
|
||||
match &msg.node {
|
||||
// Check for string concatenation and percent format.
|
||||
ExprKind::BinOp { op, .. } => match op {
|
||||
Operator::Add => {
|
||||
if checker.settings.rules.enabled(&Rule::LoggingStringConcat) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
LoggingStringConcat,
|
||||
Range::from_located(msg),
|
||||
));
|
||||
}
|
||||
}
|
||||
Operator::Mod => {
|
||||
if checker.settings.rules.enabled(&Rule::LoggingPercentFormat) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
LoggingPercentFormat,
|
||||
Range::from_located(msg),
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
// Check for f-strings.
|
||||
ExprKind::JoinedStr { .. } => {
|
||||
if checker.settings.rules.enabled(&Rule::LoggingFString) {
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(LoggingFString, Range::from_located(msg)));
|
||||
}
|
||||
}
|
||||
// Check for .format() calls.
|
||||
ExprKind::Call { func, .. } => {
|
||||
if checker.settings.rules.enabled(&Rule::LoggingStringFormat) {
|
||||
if let ExprKind::Attribute { value, attr, .. } = &func.node {
|
||||
if attr == "format" && matches!(value.node, ExprKind::Constant { .. }) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
LoggingStringFormat,
|
||||
Range::from_located(msg),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check contents of the `extra` argument to logging calls.
|
||||
fn check_log_record_attr_clash(checker: &mut Checker, extra: &Keyword) {
|
||||
match &extra.node.value.node {
|
||||
ExprKind::Dict { keys, .. } => {
|
||||
for key in keys {
|
||||
if let Some(key) = &key {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Str(string),
|
||||
..
|
||||
} = &key.node
|
||||
{
|
||||
if RESERVED_ATTRS.contains(&string.as_str()) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
LoggingExtraAttrClash(string.to_string()),
|
||||
Range::from_located(key),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::Call { func, keywords, .. } => {
|
||||
if checker
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| call_path.as_slice() == ["", "dict"])
|
||||
{
|
||||
for keyword in keywords {
|
||||
if let Some(key) = &keyword.node.arg {
|
||||
if RESERVED_ATTRS.contains(&key.as_str()) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
LoggingExtraAttrClash(key.to_string()),
|
||||
Range::from_located(keyword),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check logging calls for violations.
|
||||
pub fn logging_call(checker: &mut Checker, func: &Expr, args: &[Expr], keywords: &[Keyword]) {
|
||||
if !is_logger_candidate(func) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Attribute { value, attr, .. } = &func.node {
|
||||
if let Some(logging_level) = LoggingLevel::from_str(attr.as_str()) {
|
||||
let call_args = SimpleCallArgs::new(args, keywords);
|
||||
let level_call_range = Range::new(
|
||||
Location::new(
|
||||
func.location.row(),
|
||||
value.end_location.unwrap().column() + 1,
|
||||
),
|
||||
Location::new(
|
||||
func.end_location.unwrap().row(),
|
||||
func.end_location.unwrap().column(),
|
||||
),
|
||||
);
|
||||
|
||||
// G001 - G004
|
||||
if let Some(format_arg) = call_args.get_argument("msg", Some(0)) {
|
||||
check_msg(checker, format_arg);
|
||||
}
|
||||
|
||||
// G010
|
||||
if checker.settings.rules.enabled(&Rule::LoggingWarn)
|
||||
&& matches!(logging_level, LoggingLevel::Warn)
|
||||
{
|
||||
let mut diagnostic = Diagnostic::new(LoggingWarn, level_call_range);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.amend(Fix::replacement(
|
||||
"warning".to_string(),
|
||||
level_call_range.location,
|
||||
level_call_range.end_location,
|
||||
));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
// G101
|
||||
if checker.settings.rules.enabled(&Rule::LoggingExtraAttrClash) {
|
||||
if let Some(extra) = find_keyword(keywords, "extra") {
|
||||
check_log_record_attr_clash(checker, extra);
|
||||
}
|
||||
}
|
||||
|
||||
// G201, G202
|
||||
if checker.settings.rules.enabled(&Rule::LoggingExcInfo)
|
||||
|| checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::LoggingRedundantExcInfo)
|
||||
{
|
||||
if let Some(exc_info) = find_keyword(keywords, "exc_info") {
|
||||
match logging_level {
|
||||
LoggingLevel::Error => {
|
||||
if checker.settings.rules.enabled(&Rule::LoggingExcInfo) {
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(LoggingExcInfo, level_call_range));
|
||||
}
|
||||
}
|
||||
LoggingLevel::Exception => {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::LoggingRedundantExcInfo)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
LoggingRedundantExcInfo,
|
||||
Range::from_located(exc_info),
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
LoggingStringFormat: ~
|
||||
location:
|
||||
row: 3
|
||||
column: 13
|
||||
end_location:
|
||||
row: 3
|
||||
column: 40
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
LoggingPercentFormat: ~
|
||||
location:
|
||||
row: 3
|
||||
column: 13
|
||||
end_location:
|
||||
row: 3
|
||||
column: 34
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
LoggingStringConcat: ~
|
||||
location:
|
||||
row: 3
|
||||
column: 13
|
||||
end_location:
|
||||
row: 3
|
||||
column: 37
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
LoggingFString: ~
|
||||
location:
|
||||
row: 4
|
||||
column: 13
|
||||
end_location:
|
||||
row: 4
|
||||
column: 28
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
LoggingWarn: ~
|
||||
location:
|
||||
row: 3
|
||||
column: 8
|
||||
end_location:
|
||||
row: 3
|
||||
column: 12
|
||||
fix:
|
||||
content: warning
|
||||
location:
|
||||
row: 3
|
||||
column: 8
|
||||
end_location:
|
||||
row: 3
|
||||
column: 12
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
LoggingExtraAttrClash: name
|
||||
location:
|
||||
row: 6
|
||||
column: 8
|
||||
end_location:
|
||||
row: 6
|
||||
column: 14
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
LoggingExtraAttrClash: name
|
||||
location:
|
||||
row: 6
|
||||
column: 8
|
||||
end_location:
|
||||
row: 6
|
||||
column: 21
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
LoggingExcInfo: ~
|
||||
location:
|
||||
row: 3
|
||||
column: 8
|
||||
end_location:
|
||||
row: 3
|
||||
column: 13
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
LoggingRedundantExcInfo: ~
|
||||
location:
|
||||
row: 3
|
||||
column: 33
|
||||
end_location:
|
||||
row: 3
|
||||
column: 46
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: src/rules/flake8_logging_format/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
||||
91
src/rules/flake8_logging_format/violations.rs
Normal file
91
src/rules/flake8_logging_format/violations.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
use ruff_macros::derive_message_formats;
|
||||
|
||||
use crate::define_violation;
|
||||
use crate::violation::{AlwaysAutofixableViolation, Violation};
|
||||
|
||||
define_violation!(
|
||||
pub struct LoggingStringFormat;
|
||||
);
|
||||
impl Violation for LoggingStringFormat {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Logging statement uses `string.format()`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct LoggingPercentFormat;
|
||||
);
|
||||
impl Violation for LoggingPercentFormat {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Logging statement uses `%`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct LoggingStringConcat;
|
||||
);
|
||||
impl Violation for LoggingStringConcat {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Logging statement uses `+`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct LoggingFString;
|
||||
);
|
||||
impl Violation for LoggingFString {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Logging statement uses f-string")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct LoggingWarn;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for LoggingWarn {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Logging statement uses `warn` instead of `warning`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Convert to `warn`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct LoggingExtraAttrClash(pub String);
|
||||
);
|
||||
impl Violation for LoggingExtraAttrClash {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let LoggingExtraAttrClash(key) = self;
|
||||
format!(
|
||||
"Logging statement uses an extra field that clashes with a LogRecord field: `{key}`"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct LoggingExcInfo;
|
||||
);
|
||||
impl Violation for LoggingExcInfo {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Logging `.exception(...)` should be used instead of `.error(..., exc_info=True)`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct LoggingRedundantExcInfo;
|
||||
);
|
||||
impl Violation for LoggingRedundantExcInfo {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Logging statement has redundant `exc_info`")
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,28 @@
|
||||
use std::path::Path;
|
||||
|
||||
use ruff_macros::derive_message_formats;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::{fs, violations};
|
||||
use crate::violation::Violation;
|
||||
use crate::{define_violation, fs};
|
||||
|
||||
define_violation!(
|
||||
pub struct ImplicitNamespacePackage(pub String);
|
||||
);
|
||||
impl Violation for ImplicitNamespacePackage {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let ImplicitNamespacePackage(filename) = self;
|
||||
format!("File `{filename}` is part of an implicit namespace package. Add an `__init__.py`.")
|
||||
}
|
||||
}
|
||||
|
||||
/// INP001
|
||||
pub fn implicit_namespace_package(path: &Path, package: Option<&Path>) -> Option<Diagnostic> {
|
||||
if package.is_none() {
|
||||
Some(Diagnostic::new(
|
||||
violations::ImplicitNamespacePackage(fs::relativize_path(path)),
|
||||
ImplicitNamespacePackage(fs::relativize_path(path)),
|
||||
Range::default(),
|
||||
))
|
||||
} else {
|
||||
|
||||
@@ -1,16 +1,96 @@
|
||||
use log::error;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_ast::{Constant, Expr, ExprKind, Keyword, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::comparable::ComparableExpr;
|
||||
use crate::ast::helpers::unparse_expr;
|
||||
use crate::ast::helpers::{match_trailing_comment, unparse_expr};
|
||||
use crate::ast::types::{Range, RefEquality};
|
||||
use crate::autofix::helpers::delete_stmt;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::fix::Fix;
|
||||
use crate::message::Location;
|
||||
use crate::python::identifiers::is_identifier;
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::violations;
|
||||
use crate::violation::{AlwaysAutofixableViolation, Violation};
|
||||
|
||||
define_violation!(
|
||||
pub struct NoUnnecessaryPass;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for NoUnnecessaryPass {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Unnecessary `pass` statement")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Remove unnecessary `pass`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct DupeClassFieldDefinitions(pub String);
|
||||
);
|
||||
impl AlwaysAutofixableViolation for DupeClassFieldDefinitions {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let DupeClassFieldDefinitions(name) = self;
|
||||
format!("Class field `{name}` is defined multiple times")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let DupeClassFieldDefinitions(name) = self;
|
||||
format!("Remove duplicate field definition for `{name}`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PreferUniqueEnums {
|
||||
pub value: String,
|
||||
}
|
||||
);
|
||||
impl Violation for PreferUniqueEnums {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PreferUniqueEnums { value } = self;
|
||||
format!("Enum contains duplicate value: `{value}`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct NoUnnecessarySpread;
|
||||
);
|
||||
impl Violation for NoUnnecessarySpread {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Unnecessary spread `**`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct NoUnnecessaryDictKwargs;
|
||||
);
|
||||
impl Violation for NoUnnecessaryDictKwargs {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Unnecessary `dict` kwargs")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PreferListBuiltin;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for PreferListBuiltin {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Prefer `list` over useless lambda")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Replace with `list`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// PIE790
|
||||
pub fn no_unnecessary_pass(checker: &mut Checker, body: &[Stmt]) {
|
||||
@@ -30,17 +110,32 @@ pub fn no_unnecessary_pass(checker: &mut Checker, body: &[Stmt]) {
|
||||
}
|
||||
) {
|
||||
if matches!(pass_stmt.node, StmtKind::Pass) {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::NoUnnecessaryPass,
|
||||
Range::from_located(pass_stmt),
|
||||
);
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(NoUnnecessaryPass, Range::from_located(pass_stmt));
|
||||
if checker.patch(&Rule::NoUnnecessaryPass) {
|
||||
match delete_stmt(pass_stmt, None, &[], checker.locator, checker.indexer) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to delete `pass` statement: {}", e);
|
||||
if let Some(index) = match_trailing_comment(pass_stmt, checker.locator) {
|
||||
diagnostic.amend(Fix::deletion(
|
||||
pass_stmt.location,
|
||||
Location::new(
|
||||
pass_stmt.end_location.unwrap().row(),
|
||||
pass_stmt.end_location.unwrap().column() + index,
|
||||
),
|
||||
));
|
||||
} else {
|
||||
match delete_stmt(
|
||||
pass_stmt,
|
||||
None,
|
||||
&[],
|
||||
checker.locator,
|
||||
checker.indexer,
|
||||
checker.stylist,
|
||||
) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to delete `pass` statement: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,7 +179,7 @@ pub fn dupe_class_field_definitions<'a, 'b>(
|
||||
|
||||
if !seen_targets.insert(target) {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::DupeClassFieldDefinitions(target.to_string()),
|
||||
DupeClassFieldDefinitions(target.to_string()),
|
||||
Range::from_located(stmt),
|
||||
);
|
||||
if checker.patch(&Rule::DupeClassFieldDefinitions) {
|
||||
@@ -94,7 +189,14 @@ pub fn dupe_class_field_definitions<'a, 'b>(
|
||||
.map(std::convert::Into::into)
|
||||
.collect();
|
||||
let locator = checker.locator;
|
||||
match delete_stmt(stmt, Some(parent), &deleted, locator, checker.indexer) {
|
||||
match delete_stmt(
|
||||
stmt,
|
||||
Some(parent),
|
||||
&deleted,
|
||||
locator,
|
||||
checker.indexer,
|
||||
checker.stylist,
|
||||
) {
|
||||
Ok(fix) => {
|
||||
checker.deletions.insert(RefEquality(stmt));
|
||||
diagnostic.amend(fix);
|
||||
@@ -143,7 +245,7 @@ where
|
||||
|
||||
if !seen_targets.insert(ComparableExpr::from(value)) {
|
||||
let diagnostic = Diagnostic::new(
|
||||
violations::PreferUniqueEnums {
|
||||
PreferUniqueEnums {
|
||||
value: unparse_expr(value, checker.stylist),
|
||||
},
|
||||
Range::from_located(stmt),
|
||||
@@ -160,8 +262,7 @@ pub fn no_unnecessary_spread(checker: &mut Checker, keys: &[Option<Expr>], value
|
||||
// We only care about when the key is None which indicates a spread `**`
|
||||
// inside a dict.
|
||||
if let ExprKind::Dict { .. } = value.node {
|
||||
let diagnostic =
|
||||
Diagnostic::new(violations::NoUnnecessarySpread, Range::from_located(value));
|
||||
let diagnostic = Diagnostic::new(NoUnnecessarySpread, Range::from_located(value));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -192,10 +293,8 @@ pub fn no_unnecessary_dict_kwargs(checker: &mut Checker, expr: &Expr, kwargs: &[
|
||||
// handle case of foo(**{**bar})
|
||||
(keys.len() == 1 && keys[0].is_none())
|
||||
{
|
||||
let diagnostic = Diagnostic::new(
|
||||
violations::NoUnnecessaryDictKwargs,
|
||||
Range::from_located(expr),
|
||||
);
|
||||
let diagnostic =
|
||||
Diagnostic::new(NoUnnecessaryDictKwargs, Range::from_located(expr));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -211,8 +310,7 @@ pub fn prefer_list_builtin(checker: &mut Checker, expr: &Expr) {
|
||||
if args.args.is_empty() {
|
||||
if let ExprKind::List { elts, .. } = &body.node {
|
||||
if elts.is_empty() {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(violations::PreferListBuiltin, Range::from_located(expr));
|
||||
let mut diagnostic = Diagnostic::new(PreferListBuiltin, Range::from_located(expr));
|
||||
if checker.patch(&Rule::PreferListBuiltin) {
|
||||
diagnostic.amend(Fix::replacement(
|
||||
"list".to_string(),
|
||||
|
||||
@@ -290,4 +290,22 @@ expression: diagnostics
|
||||
row: 97
|
||||
column: 0
|
||||
parent: ~
|
||||
- kind:
|
||||
NoUnnecessaryPass: ~
|
||||
location:
|
||||
row: 101
|
||||
column: 4
|
||||
end_location:
|
||||
row: 101
|
||||
column: 8
|
||||
fix:
|
||||
content:
|
||||
- ""
|
||||
location:
|
||||
row: 101
|
||||
column: 4
|
||||
end_location:
|
||||
row: 101
|
||||
column: 10
|
||||
parent: ~
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ pub fn print_call(checker: &mut Checker, func: &Expr, keywords: &[Keyword]) {
|
||||
&deleted,
|
||||
checker.locator,
|
||||
checker.indexer,
|
||||
checker.stylist,
|
||||
) {
|
||||
Ok(fix) => {
|
||||
if fix.content.is_empty() || fix.content == "pass" {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user