Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf56955ba6 | ||
|
|
8a8939afd8 | ||
|
|
6acf2accc6 | ||
|
|
ec0c7647ab | ||
|
|
045229630e | ||
|
|
c600991905 | ||
|
|
f6a93a4c3d | ||
|
|
de54ff114e | ||
|
|
64b398c72b | ||
|
|
c99bd3fa60 | ||
|
|
8ac930f886 | ||
|
|
ad80fdc2cd | ||
|
|
a0ea8fe22f | ||
|
|
3c3da8a88c | ||
|
|
16e79c8db6 | ||
|
|
8f6d8e215c | ||
|
|
8993baab01 | ||
|
|
2568627c4c | ||
|
|
9603a024b3 | ||
|
|
a122d95ef5 | ||
|
|
6ddfe50ac4 | ||
|
|
26901a78c9 | ||
|
|
6649225167 | ||
|
|
9e3083aa2c | ||
|
|
6d11ff3822 | ||
|
|
6cf770a692 | ||
|
|
3534e370e1 | ||
|
|
dbcab5128c | ||
|
|
3810250bb6 | ||
|
|
3c1c1e1dd3 | ||
|
|
5b7bd93b91 | ||
|
|
9e096b4a4c | ||
|
|
d8645acd1f | ||
|
|
92dd073191 | ||
|
|
ff96219e62 | ||
|
|
d33424ec9d | ||
|
|
34412a0a01 | ||
|
|
ceb48d3a32 | ||
|
|
969a6f0d53 | ||
|
|
7628876ff2 | ||
|
|
ef355e5c2c | ||
|
|
97f55b8e97 | ||
|
|
ff2be35f51 | ||
|
|
1e803f7108 | ||
|
|
1ab0273aa7 | ||
|
|
5a7d8c25f4 | ||
|
|
26d6414558 | ||
|
|
dae95626ae | ||
|
|
9a3e525930 | ||
|
|
b9c6cfc0ab | ||
|
|
b1f10c8339 | ||
|
|
83346de6e0 | ||
|
|
b23cc31863 | ||
|
|
462d81beb7 | ||
|
|
715ea2d374 | ||
|
|
6c7e60b4f9 | ||
|
|
868d0b3e29 | ||
|
|
cdb4700813 | ||
|
|
ea4d54a90f | ||
|
|
51b917cfbf | ||
|
|
c880d744fd | ||
|
|
84d1df08be | ||
|
|
b9bb5acff8 | ||
|
|
ca7c3c2175 | ||
|
|
8891e2e62b | ||
|
|
53265e0ed4 | ||
|
|
072849a8a9 | ||
|
|
70ea4b25e8 | ||
|
|
30e133f3d8 | ||
|
|
aa812de07e | ||
|
|
57ac6a8444 | ||
|
|
a6566b1b34 | ||
|
|
580da1fa6b | ||
|
|
b78b6f275e | ||
|
|
6868bb46f5 | ||
|
|
601848d9a8 | ||
|
|
f4da7635f0 | ||
|
|
74a8a218f3 | ||
|
|
1730f2a603 | ||
|
|
a4862857de | ||
|
|
6e88c60c46 | ||
|
|
2ed1f78873 | ||
|
|
f3bf008aed | ||
|
|
3b4aaa53c1 | ||
|
|
6abf71639f | ||
|
|
c0845a8c28 | ||
|
|
019ecc4add | ||
|
|
f4cf48d885 | ||
|
|
005f5d7911 | ||
|
|
2fce580693 | ||
|
|
8862565a0f | ||
|
|
5bf6da0db7 | ||
|
|
ee655c1a88 | ||
|
|
2236b4bd59 | ||
|
|
fbf311f7d5 | ||
|
|
8c18b28bc4 | ||
|
|
42031b8574 |
3
.github/workflows/ruff.yaml
vendored
3
.github/workflows/ruff.yaml
vendored
@@ -293,3 +293,6 @@ jobs:
|
||||
run: |
|
||||
pip install --upgrade twine
|
||||
twine upload --skip-existing *
|
||||
- name: Update pre-commit mirror
|
||||
run: |
|
||||
curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.RUFF_PRE_COMMIT_PAT }}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/charliermarsh/ruff-pre-commit/dispatches --data '{"event_type": "pypi_release"}'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.223
|
||||
rev: v0.0.227
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
@@ -8,3 +8,11 @@ repos:
|
||||
rev: v0.10.1
|
||||
hooks:
|
||||
- id: validate-pyproject
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: cargo-fmt
|
||||
name: cargo fmt
|
||||
entry: cargo fmt --
|
||||
language: rust
|
||||
types: [rust]
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.0.226
|
||||
|
||||
### `misplaced-comparison-constant` (`PLC2201`) was deprecated in favor of `SIM300` ([#1980](https://github.com/charliermarsh/ruff/pull/1980))
|
||||
|
||||
These two rules contain (nearly) identical logic. To deduplicate the rule set, we've upgraded
|
||||
`SIM300` to handle a few more cases, and deprecated `PLC2201` in favor of `SIM300`.
|
||||
|
||||
## 0.0.225
|
||||
|
||||
### `@functools.cache` rewrites have been moved to a standalone rule (`UP033`) ([#1938](https://github.com/charliermarsh/ruff/pull/1938))
|
||||
|
||||
Previously, `UP011` handled both `@functools.lru_cache()`-to-`@functools.lru_cache` conversions,
|
||||
_and_ `@functools.lru_cache(maxsize=None)`-to-`@functools.cache` conversions. The latter has been
|
||||
moved out to its own rule (`UP033`). As such, some `# noqa: UP011` comments may need to be updated
|
||||
to reflect the change in rule code.
|
||||
|
||||
## 0.0.222
|
||||
|
||||
### `--max-complexity` has been removed from the CLI ([#1877](https://github.com/charliermarsh/ruff/pull/1877))
|
||||
|
||||
@@ -54,18 +54,22 @@ prior to merging.
|
||||
|
||||
### Example: Adding a new lint rule
|
||||
|
||||
There are four phases to adding a new lint rule:
|
||||
At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
|
||||
1. Define the violation struct in `src/violations.rs` (e.g., `ModuleImportNotAtTopOfFile`).
|
||||
2. Map the violation struct to a rule code in `src/registry.rs` (e.g., `E402`).
|
||||
3. Define the logic for triggering the violation in `src/checkers/ast.rs` (for AST-based checks),
|
||||
`src/checkers/tokens.rs` (for token-based checks), or `src/checkers/lines.rs` (for text-based checks).
|
||||
4. Add a test fixture.
|
||||
5. Update the generated files (documentation and generated code).
|
||||
1. Create a file for your rule (e.g., `src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
|
||||
2. In that file, define a violation struct. You can grep for `define_violation!` to see examples.
|
||||
3. Map the violation struct to a rule code in `src/registry.rs` (e.g., `E402`).
|
||||
4. Define the logic for triggering the violation in `src/checkers/ast.rs` (for AST-based checks),
|
||||
`src/checkers/tokens.rs` (for token-based checks), `src/checkers/lines.rs` (for text-based
|
||||
checks), or `src/checkers/filesystem.rs` (for filesystem-based checks).
|
||||
5. Add a test fixture.
|
||||
6. Update the generated files (documentation and generated code).
|
||||
|
||||
To define the violation, open up `src/violations.rs`, and define a new struct using the
|
||||
`define_violation!` macro. There are plenty of examples in that file, so feel free to pattern-match
|
||||
against the existing structs.
|
||||
To define the violation, start by creating a dedicated file for your rule under the appropriate
|
||||
rule origin (e.g., `src/rules/flake8_bugbear/rules/abstract_base_class.rs`). That file should
|
||||
contain a struct defined via `define_violation!`, along with a function that creates the violation
|
||||
based on any required inputs. (Many of the existing examples live in `src/violations.rs`, but we're
|
||||
looking to place new rules in their own files.)
|
||||
|
||||
To trigger the violation, you'll likely want to augment the logic in `src/checkers/ast.rs`, which
|
||||
defines the Python AST visitor, responsible for iterating over the abstract syntax tree and
|
||||
|
||||
20
Cargo.lock
generated
20
Cargo.lock
generated
@@ -735,7 +735,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.223"
|
||||
version = "0.0.227"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.32",
|
||||
@@ -1906,7 +1906,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.223"
|
||||
version = "0.0.227"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
@@ -1946,10 +1946,12 @@ dependencies = [
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"shellexpand",
|
||||
"smallvec",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"test-case",
|
||||
"textwrap",
|
||||
"thiserror",
|
||||
"titlecase",
|
||||
"toml_edit",
|
||||
"wasm-bindgen",
|
||||
@@ -1958,7 +1960,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.223"
|
||||
version = "0.0.227"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1995,7 +1997,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.223"
|
||||
version = "0.0.227"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.32",
|
||||
@@ -2016,7 +2018,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.223"
|
||||
version = "0.0.227"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
@@ -2060,7 +2062,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=acbc517b55406c76da83d7b2711941d8d3f65b87#acbc517b55406c76da83d7b2711941d8d3f65b87"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa#ff90fe52eea578c8ebdd9d95e078cc041a5959fa"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-common",
|
||||
@@ -2070,7 +2072,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-common"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=acbc517b55406c76da83d7b2711941d8d3f65b87#acbc517b55406c76da83d7b2711941d8d3f65b87"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa#ff90fe52eea578c8ebdd9d95e078cc041a5959fa"
|
||||
dependencies = [
|
||||
"ascii",
|
||||
"bitflags",
|
||||
@@ -2095,7 +2097,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=acbc517b55406c76da83d7b2711941d8d3f65b87#acbc517b55406c76da83d7b2711941d8d3f65b87"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa#ff90fe52eea578c8ebdd9d95e078cc041a5959fa"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
@@ -2112,7 +2114,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=acbc517b55406c76da83d7b2711941d8d3f65b87#acbc517b55406c76da83d7b2711941d8d3f65b87"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa#ff90fe52eea578c8ebdd9d95e078cc041a5959fa"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
|
||||
12
Cargo.toml
12
Cargo.toml
@@ -8,7 +8,7 @@ default-members = [".", "ruff_cli"]
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.223"
|
||||
version = "0.0.227"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
@@ -46,20 +46,22 @@ 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" }
|
||||
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
|
||||
ruff_macros = { version = "0.0.223", path = "ruff_macros" }
|
||||
ruff_macros = { version = "0.0.227", path = "ruff_macros" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "acbc517b55406c76da83d7b2711941d8d3f65b87" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "acbc517b55406c76da83d7b2711941d8d3f65b87" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "acbc517b55406c76da83d7b2711941d8d3f65b87" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
schemars = { version = "0.8.11" }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
smallvec = { version = "1.10.0" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
textwrap = { version = "0.16.0" }
|
||||
titlecase = { version = "2.2.1" }
|
||||
toml_edit = { version = "0.17.1", features = ["easy"] }
|
||||
thiserror = { version = "1.0" }
|
||||
|
||||
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
|
||||
# For (future) wasm-pack support
|
||||
|
||||
84
build.rs
Normal file
84
build.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
use std::fs;
|
||||
use std::io::{BufRead, BufReader, BufWriter, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn main() {
|
||||
let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||
generate_origin_name_and_url(&out_dir);
|
||||
}
|
||||
|
||||
const RULES_SUBMODULE_DOC_PREFIX: &str = "//! Rules from ";
|
||||
|
||||
/// The `src/rules/*/mod.rs` files are expected to have a first line such as the
|
||||
/// following:
|
||||
///
|
||||
/// //! Rules from [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/).
|
||||
///
|
||||
/// This function extracts the link label and url from these comments and
|
||||
/// generates the `name` and `url` functions for the `RuleOrigin` enum
|
||||
/// accordingly, so that they can be used by `ruff_dev::generate_rules_table`.
|
||||
fn generate_origin_name_and_url(out_dir: &Path) {
|
||||
println!("cargo:rerun-if-changed=src/rules/");
|
||||
|
||||
let mut name_match_arms: String = r#"RuleOrigin::Ruff => "Ruff-specific rules","#.into();
|
||||
let mut url_match_arms: String = r#"RuleOrigin::Ruff => None,"#.into();
|
||||
|
||||
for file in fs::read_dir("src/rules/")
|
||||
.unwrap()
|
||||
.flatten()
|
||||
.filter(|f| f.file_type().unwrap().is_dir() && f.file_name() != "ruff")
|
||||
{
|
||||
let mod_rs_path = file.path().join("mod.rs");
|
||||
let mod_rs_path = mod_rs_path.to_str().unwrap();
|
||||
let first_line = BufReader::new(fs::File::open(mod_rs_path).unwrap())
|
||||
.lines()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
let Some(comment) = first_line.strip_prefix(RULES_SUBMODULE_DOC_PREFIX) else {
|
||||
panic!("expected first line in {mod_rs_path} to start with `{RULES_SUBMODULE_DOC_PREFIX}`")
|
||||
};
|
||||
let md_link = comment.trim_end_matches('.');
|
||||
|
||||
let (name, url) = md_link
|
||||
.strip_prefix('[')
|
||||
.unwrap()
|
||||
.strip_suffix(')')
|
||||
.unwrap()
|
||||
.split_once("](")
|
||||
.unwrap();
|
||||
|
||||
let dirname = file.file_name();
|
||||
let dirname = dirname.to_str().unwrap();
|
||||
|
||||
let variant_name = dirname
|
||||
.split('_')
|
||||
.map(|part| match part {
|
||||
"errmsg" => "ErrMsg".to_string(),
|
||||
"mccabe" => "McCabe".to_string(),
|
||||
"pep8" => "PEP8".to_string(),
|
||||
_ => format!("{}{}", part[..1].to_uppercase(), &part[1..]),
|
||||
})
|
||||
.collect::<String>();
|
||||
|
||||
name_match_arms.push_str(&format!(r#"RuleOrigin::{variant_name} => "{name}","#));
|
||||
url_match_arms.push_str(&format!(r#"RuleOrigin::{variant_name} => Some("{url}"),"#));
|
||||
}
|
||||
|
||||
write!(
|
||||
BufWriter::new(fs::File::create(out_dir.join("origin.rs")).unwrap()),
|
||||
"
|
||||
impl RuleOrigin {{
|
||||
pub fn name(&self) -> &'static str {{
|
||||
match self {{ {name_match_arms} }}
|
||||
}}
|
||||
|
||||
pub fn url(&self) -> Option<&'static str> {{
|
||||
match self {{ {url_match_arms} }}
|
||||
}}
|
||||
}}
|
||||
"
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
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.223"
|
||||
version = "0.0.227"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.223"
|
||||
version = "0.0.227"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.223"
|
||||
version = "0.0.227"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
21
licenses/LICENSE_adamchainz_flake8-no-pep420
Normal file
21
licenses/LICENSE_adamchainz_flake8-no-pep420
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Adam Johnson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -7,7 +7,7 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "ruff"
|
||||
version = "0.0.223"
|
||||
version = "0.0.227"
|
||||
description = "An extremely fast Python linter, written in Rust."
|
||||
authors = [
|
||||
{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" },
|
||||
@@ -41,9 +41,3 @@ bindings = "bin"
|
||||
manifest-path = "ruff_cli/Cargo.toml"
|
||||
python-source = "python"
|
||||
strip = true
|
||||
|
||||
[tool.setuptools]
|
||||
license-files = [
|
||||
"LICENSE",
|
||||
"licenses/*",
|
||||
]
|
||||
|
||||
0
resources/test/fixtures/flake8_no_pep420/test_fail_empty/example.py
vendored
Normal file
0
resources/test/fixtures/flake8_no_pep420/test_fail_empty/example.py
vendored
Normal file
1
resources/test/fixtures/flake8_no_pep420/test_fail_nonempty/example.py
vendored
Normal file
1
resources/test/fixtures/flake8_no_pep420/test_fail_nonempty/example.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
print('hi')
|
||||
2
resources/test/fixtures/flake8_no_pep420/test_fail_shebang/example.py
vendored
Normal file
2
resources/test/fixtures/flake8_no_pep420/test_fail_shebang/example.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/env/python
|
||||
print('hi')
|
||||
1
resources/test/fixtures/flake8_no_pep420/test_ignored/example.py
vendored
Normal file
1
resources/test/fixtures/flake8_no_pep420/test_ignored/example.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
import os # noqa: INP001
|
||||
0
resources/test/fixtures/flake8_no_pep420/test_pass/__init__.py
vendored
Normal file
0
resources/test/fixtures/flake8_no_pep420/test_pass/__init__.py
vendored
Normal file
0
resources/test/fixtures/flake8_no_pep420/test_pass/example.py
vendored
Normal file
0
resources/test/fixtures/flake8_no_pep420/test_pass/example.py
vendored
Normal file
66
resources/test/fixtures/flake8_pie/PIE796.py
vendored
Normal file
66
resources/test/fixtures/flake8_pie/PIE796.py
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
import enum
|
||||
from enum import Enum, unique
|
||||
|
||||
|
||||
class FakeEnum1(enum.Enum):
|
||||
A = "A"
|
||||
B = "B"
|
||||
C = "B" # PIE796
|
||||
|
||||
|
||||
class FakeEnum2(Enum):
|
||||
A = 1
|
||||
B = 2
|
||||
C = 2 # PIE796
|
||||
|
||||
|
||||
class FakeEnum3(str, Enum):
|
||||
A = "1"
|
||||
B = "2"
|
||||
C = "2" # PIE796
|
||||
|
||||
|
||||
class FakeEnum4(Enum):
|
||||
A = 1.0
|
||||
B = 2.5
|
||||
C = 2.5 # PIE796
|
||||
|
||||
|
||||
class FakeEnum5(Enum):
|
||||
A = 1.0
|
||||
B = True
|
||||
C = False
|
||||
D = False # PIE796
|
||||
|
||||
|
||||
class FakeEnum6(Enum):
|
||||
A = 1
|
||||
B = 2
|
||||
C = None
|
||||
D = None # PIE796
|
||||
|
||||
|
||||
@enum.unique
|
||||
class FakeEnum7(enum.Enum):
|
||||
A = "A"
|
||||
B = "B"
|
||||
C = "C"
|
||||
|
||||
|
||||
@unique
|
||||
class FakeEnum8(Enum):
|
||||
A = 1
|
||||
B = 2
|
||||
C = 2 # PIE796
|
||||
|
||||
|
||||
class FakeEnum9(enum.Enum):
|
||||
A = "A"
|
||||
B = "B"
|
||||
C = "C"
|
||||
|
||||
|
||||
class FakeEnum10(enum.Enum):
|
||||
A = enum.auto()
|
||||
B = enum.auto()
|
||||
C = enum.auto()
|
||||
@@ -53,3 +53,25 @@ def test_list_of_tuples(param1, param2):
|
||||
)
|
||||
def test_list_of_lists(param1, param2):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param1,param2",
|
||||
[
|
||||
[1, 2],
|
||||
[3, 4],
|
||||
],
|
||||
)
|
||||
def test_csv_name_list_of_lists(param1, param2):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param",
|
||||
[
|
||||
[1, 2],
|
||||
[3, 4],
|
||||
],
|
||||
)
|
||||
def test_single_list_of_lists(param):
|
||||
...
|
||||
|
||||
@@ -1,25 +1,88 @@
|
||||
if a: # SIM102
|
||||
# SIM102
|
||||
if a:
|
||||
if b:
|
||||
c
|
||||
|
||||
|
||||
# SIM102
|
||||
if a:
|
||||
pass
|
||||
elif b: # SIM102
|
||||
elif b:
|
||||
if c:
|
||||
d
|
||||
|
||||
# SIM102
|
||||
if a:
|
||||
# Unfixable due to placement of this comment.
|
||||
if b:
|
||||
c
|
||||
|
||||
# SIM102
|
||||
if a:
|
||||
if b:
|
||||
# Fixable due to placement of this comment.
|
||||
c
|
||||
|
||||
# OK
|
||||
if a:
|
||||
if b:
|
||||
c
|
||||
else:
|
||||
d
|
||||
|
||||
# OK
|
||||
if __name__ == "__main__":
|
||||
if foo():
|
||||
...
|
||||
|
||||
# OK
|
||||
if a:
|
||||
d
|
||||
if b:
|
||||
c
|
||||
|
||||
while True:
|
||||
# SIM102
|
||||
if True:
|
||||
if True:
|
||||
"""this
|
||||
is valid"""
|
||||
|
||||
"""the indentation on
|
||||
this line is significant"""
|
||||
|
||||
"this is" \
|
||||
"allowed too"
|
||||
|
||||
("so is"
|
||||
"this for some reason")
|
||||
|
||||
|
||||
# SIM102
|
||||
if True:
|
||||
if True:
|
||||
"""this
|
||||
is valid"""
|
||||
|
||||
"""the indentation on
|
||||
this line is significant"""
|
||||
|
||||
"this is" \
|
||||
"allowed too"
|
||||
|
||||
("so is"
|
||||
"this for some reason")
|
||||
|
||||
while True:
|
||||
# SIM102
|
||||
if node.module:
|
||||
if node.module == "multiprocessing" or node.module.startswith(
|
||||
"multiprocessing."
|
||||
):
|
||||
print("Bad module!")
|
||||
|
||||
# SIM102
|
||||
if node.module:
|
||||
if node.module == "multiprocessing" or node.module.startswith(
|
||||
"multiprocessing."
|
||||
):
|
||||
print("Bad module!")
|
||||
|
||||
@@ -1,12 +1,35 @@
|
||||
def f():
|
||||
if a: # SIM103
|
||||
# SIM103
|
||||
if a:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
if a: # OK
|
||||
# SIM103
|
||||
if a:
|
||||
return 1
|
||||
elif b:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# SIM103
|
||||
if a:
|
||||
return 1
|
||||
else:
|
||||
if b:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
if a:
|
||||
foo()
|
||||
return True
|
||||
else:
|
||||
@@ -14,7 +37,8 @@ def f():
|
||||
|
||||
|
||||
def f():
|
||||
if a: # OK
|
||||
# OK
|
||||
if a:
|
||||
return "foo"
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -41,3 +41,21 @@ except ValueError:
|
||||
pass
|
||||
finally:
|
||||
print('bar')
|
||||
|
||||
try:
|
||||
foo()
|
||||
foo()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
for i in range(3):
|
||||
foo()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def bar():
|
||||
try:
|
||||
return foo()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@@ -1,7 +1,31 @@
|
||||
# Bad
|
||||
# SIM109
|
||||
if a == b or a == c:
|
||||
d
|
||||
|
||||
# Good
|
||||
# SIM109
|
||||
if (a == b or a == c) and None:
|
||||
d
|
||||
|
||||
# SIM109
|
||||
if a == b or a == c or None:
|
||||
d
|
||||
|
||||
# SIM109
|
||||
if a == b or None or a == c:
|
||||
d
|
||||
|
||||
# OK
|
||||
if a in (b, c):
|
||||
d
|
||||
d
|
||||
|
||||
# OK
|
||||
if a == b or a == c():
|
||||
d
|
||||
|
||||
# OK
|
||||
if (
|
||||
a == b
|
||||
# This comment prevents us from raising SIM109
|
||||
or a == c
|
||||
):
|
||||
d
|
||||
|
||||
@@ -1,6 +1,36 @@
|
||||
f = open('foo.txt') # SIM115
|
||||
import contextlib
|
||||
|
||||
# SIM115
|
||||
f = open("foo.txt")
|
||||
data = f.read()
|
||||
f.close()
|
||||
|
||||
with open('foo.txt') as f: # OK
|
||||
# OK
|
||||
with open("foo.txt") as f:
|
||||
data = f.read()
|
||||
|
||||
# OK
|
||||
with contextlib.ExitStack() as exit_stack:
|
||||
f = exit_stack.enter_context(open("filename"))
|
||||
|
||||
# OK
|
||||
with contextlib.ExitStack() as stack:
|
||||
files = [stack.enter_context(open(fname)) for fname in filenames]
|
||||
close_files = stack.pop_all().close
|
||||
|
||||
# OK
|
||||
with contextlib.AsyncExitStack() as exit_stack:
|
||||
f = await exit_stack.enter_async_context(open("filename"))
|
||||
|
||||
# OK (false negative)
|
||||
with contextlib.ExitStack():
|
||||
f = exit_stack.enter_context(open("filename"))
|
||||
|
||||
# SIM115
|
||||
with contextlib.ExitStack():
|
||||
f = open("filename")
|
||||
|
||||
# OK
|
||||
with contextlib.ExitStack() as exit_stack:
|
||||
exit_stack_ = exit_stack
|
||||
f = exit_stack_.enter_context(open("filename"))
|
||||
|
||||
@@ -1,30 +1,92 @@
|
||||
with A() as a: # SIM117
|
||||
# SIM117
|
||||
with A() as a:
|
||||
with B() as b:
|
||||
print("hello")
|
||||
|
||||
with A(): # SIM117
|
||||
# SIM117
|
||||
with A():
|
||||
with B():
|
||||
with C():
|
||||
print("hello")
|
||||
|
||||
# SIM117
|
||||
with A() as a:
|
||||
# Unfixable due to placement of this comment.
|
||||
with B() as b:
|
||||
print("hello")
|
||||
|
||||
# SIM117
|
||||
with A() as a:
|
||||
with B() as b:
|
||||
# Fixable due to placement of this comment.
|
||||
print("hello")
|
||||
|
||||
# OK
|
||||
with A() as a:
|
||||
a()
|
||||
with B() as b:
|
||||
print("hello")
|
||||
|
||||
# OK
|
||||
with A() as a:
|
||||
with B() as b:
|
||||
print("hello")
|
||||
a()
|
||||
|
||||
# OK
|
||||
async with A() as a:
|
||||
with B() as b:
|
||||
print("hello")
|
||||
|
||||
# OK
|
||||
with A() as a:
|
||||
async with B() as b:
|
||||
print("hello")
|
||||
|
||||
# OK
|
||||
async with A() as a:
|
||||
async with B() as b:
|
||||
print("hello")
|
||||
|
||||
while True:
|
||||
# SIM117
|
||||
with A() as a:
|
||||
with B() as b:
|
||||
"""this
|
||||
is valid"""
|
||||
|
||||
"""the indentation on
|
||||
this line is significant"""
|
||||
|
||||
"this is" \
|
||||
"allowed too"
|
||||
|
||||
("so is"
|
||||
"this for some reason")
|
||||
|
||||
# SIM117
|
||||
with (
|
||||
A() as a,
|
||||
B() as b,
|
||||
):
|
||||
with C() as c:
|
||||
print("hello")
|
||||
|
||||
# SIM117
|
||||
with A() as a:
|
||||
with (
|
||||
B() as b,
|
||||
C() as c,
|
||||
):
|
||||
print("hello")
|
||||
|
||||
# SIM117
|
||||
with (
|
||||
A() as a,
|
||||
B() as b,
|
||||
):
|
||||
with (
|
||||
C() as c,
|
||||
D() as d,
|
||||
):
|
||||
print("hello")
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
if not a == b: # SIM201
|
||||
# SIM201
|
||||
if not a == b:
|
||||
pass
|
||||
|
||||
if not a == (b + c): # SIM201
|
||||
# SIM201
|
||||
if not a == (b + c):
|
||||
pass
|
||||
|
||||
if not (a + b) == c: # SIM201
|
||||
# SIM201
|
||||
if not (a + b) == c:
|
||||
pass
|
||||
|
||||
if not a != b: # OK
|
||||
# OK
|
||||
if not a != b:
|
||||
pass
|
||||
|
||||
if a == b: # OK
|
||||
# OK
|
||||
if a == b:
|
||||
pass
|
||||
|
||||
if not a == b: # OK
|
||||
# OK
|
||||
if not a == b:
|
||||
raise ValueError()
|
||||
|
||||
# OK
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
if not a != b: # SIM202
|
||||
# SIM202
|
||||
if not a != b:
|
||||
pass
|
||||
|
||||
if not a != (b + c): # SIM202
|
||||
# SIM202
|
||||
if not a != (b + c):
|
||||
pass
|
||||
|
||||
if not (a + b) != c: # SIM202
|
||||
# SIM202
|
||||
if not (a + b) != c:
|
||||
pass
|
||||
|
||||
if not a == b: # OK
|
||||
# OK
|
||||
if not a == b:
|
||||
pass
|
||||
|
||||
if a != b: # OK
|
||||
# OK
|
||||
if a != b:
|
||||
pass
|
||||
|
||||
# OK
|
||||
if not a != b:
|
||||
raise ValueError()
|
||||
|
||||
# OK
|
||||
def __eq__(self, other):
|
||||
return not self != other
|
||||
|
||||
@@ -7,8 +7,12 @@ if (a or b) or True: # SIM223
|
||||
if a or (b or True): # SIM223
|
||||
pass
|
||||
|
||||
if a and True:
|
||||
if a and True: # OK
|
||||
pass
|
||||
|
||||
if True:
|
||||
if True: # OK
|
||||
pass
|
||||
|
||||
|
||||
def validate(self, value):
|
||||
return json.loads(value) or True # OK
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
"yoda" == compare # SIM300
|
||||
'yoda' == compare # SIM300
|
||||
42 == age # SIM300
|
||||
"yoda" <= compare # SIM300
|
||||
'yoda' < compare # SIM300
|
||||
42 > age # SIM300
|
||||
|
||||
# OK
|
||||
compare == "yoda"
|
||||
|
||||
@@ -85,3 +85,26 @@ if key in a_dict:
|
||||
var = a_dict[key]
|
||||
else:
|
||||
var = foo()
|
||||
|
||||
# OK (complex default value)
|
||||
if key in a_dict:
|
||||
var = a_dict[key]
|
||||
else:
|
||||
var = a_dict["fallback"]
|
||||
|
||||
# OK (false negative for elif)
|
||||
if foo():
|
||||
pass
|
||||
elif key in a_dict:
|
||||
vars[idx] = a_dict[key]
|
||||
else:
|
||||
vars[idx] = "default"
|
||||
|
||||
# OK (false negative for nested else)
|
||||
if foo():
|
||||
pass
|
||||
else:
|
||||
if key in a_dict:
|
||||
vars[idx] = a_dict[key]
|
||||
else:
|
||||
vars[idx] = "default"
|
||||
|
||||
9
resources/test/fixtures/isort/no_lines_before.py
vendored
Normal file
9
resources/test/fixtures/isort/no_lines_before.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from requests import Session
|
||||
|
||||
from my_first_party import my_first_party_object
|
||||
|
||||
from . import my_local_folder_object
|
||||
2
resources/test/fixtures/isort/order_by_type_with_custom_constants.py
vendored
Normal file
2
resources/test/fixtures/isort/order_by_type_with_custom_constants.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
from sklearn.svm import XYZ, func, variable, Const, Klass, constant
|
||||
from subprocess import First, var, func, Class, konst, A_constant, Last, STDOUT
|
||||
2
resources/test/fixtures/isort/order_by_type_with_custom_variables.py
vendored
Normal file
2
resources/test/fixtures/isort/order_by_type_with_custom_variables.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
from sklearn.svm import VAR, Class, MyVar, CONST, abc
|
||||
from subprocess import utils, var_ABC, Variable, Klass, CONSTANT, exe
|
||||
27
resources/test/fixtures/pydocstyle/D.py
vendored
27
resources/test/fixtures/pydocstyle/D.py
vendored
@@ -579,3 +579,30 @@ def multiline_trailing_and_leading_space():
|
||||
"or exclamation point (not '\"')")
|
||||
def endswith_quote():
|
||||
"""Whitespace at the end, but also a quote" """
|
||||
|
||||
|
||||
@expect('D209: Multi-line docstring closing quotes should be on a separate '
|
||||
'line')
|
||||
@expect('D213: Multi-line docstring summary should start at the second line')
|
||||
def asdfljdjgf24():
|
||||
"""Summary.
|
||||
|
||||
Description. """
|
||||
|
||||
|
||||
@expect('D200: One-line docstring should fit on one line with quotes '
|
||||
'(found 3)')
|
||||
@expect('D212: Multi-line docstring summary should start at the first line')
|
||||
def one_liner():
|
||||
"""
|
||||
|
||||
Wrong."""
|
||||
|
||||
|
||||
@expect('D200: One-line docstring should fit on one line with quotes '
|
||||
'(found 3)')
|
||||
@expect('D212: Multi-line docstring summary should start at the first line')
|
||||
def one_liner():
|
||||
r"""Wrong.
|
||||
|
||||
"""
|
||||
|
||||
22
resources/test/fixtures/pyflakes/F841_3.py
vendored
22
resources/test/fixtures/pyflakes/F841_3.py
vendored
@@ -44,3 +44,25 @@ def f():
|
||||
1 / 0
|
||||
except (ValueError, ZeroDivisionError) as x2:
|
||||
pass
|
||||
|
||||
|
||||
def f(a, b):
|
||||
x = (
|
||||
a()
|
||||
if a is not None
|
||||
else b
|
||||
)
|
||||
|
||||
y = \
|
||||
a() if a is not None else b
|
||||
|
||||
|
||||
def f(a, b):
|
||||
x = (
|
||||
a
|
||||
if a is not None
|
||||
else b
|
||||
)
|
||||
|
||||
y = \
|
||||
a if a is not None else b
|
||||
|
||||
@@ -1,71 +1,69 @@
|
||||
"""Check that magic values are not used in comparisons"""
|
||||
import cmath
|
||||
"""Check that magic values are not used in comparisons."""
|
||||
|
||||
user_input = 10
|
||||
|
||||
if 10 > user_input: # [magic-value-comparison]
|
||||
if 10 > user_input: # [magic-value-comparison]
|
||||
pass
|
||||
|
||||
if 10 == 100: # [comparison-of-constants] R0133
|
||||
if 10 == 100: # [comparison-of-constants] R0133
|
||||
pass
|
||||
|
||||
if 1 == 3: # [comparison-of-constants] R0133
|
||||
if 1 == 3: # [comparison-of-constants] R0133
|
||||
pass
|
||||
|
||||
x = 0
|
||||
if 4 == 3 == x: # [comparison-of-constants] R0133
|
||||
if 4 == 3 == x: # [comparison-of-constants] R0133
|
||||
pass
|
||||
|
||||
time_delta = 7224
|
||||
ONE_HOUR = 3600
|
||||
|
||||
if time_delta > ONE_HOUR: # correct
|
||||
if time_delta > ONE_HOUR: # correct
|
||||
pass
|
||||
|
||||
argc = 1
|
||||
|
||||
if argc != -1: # correct
|
||||
if argc != -1: # correct
|
||||
pass
|
||||
|
||||
if argc != 0: # correct
|
||||
if argc != 0: # correct
|
||||
pass
|
||||
|
||||
if argc != 1: # correct
|
||||
if argc != 1: # correct
|
||||
pass
|
||||
|
||||
if argc != 2: # [magic-value-comparison]
|
||||
if argc != 2: # [magic-value-comparison]
|
||||
pass
|
||||
|
||||
if __name__ == "__main__": # correct
|
||||
if __name__ == "__main__": # correct
|
||||
pass
|
||||
|
||||
ADMIN_PASSWORD = "SUPERSECRET"
|
||||
input_password = "password"
|
||||
|
||||
if input_password == "": # correct
|
||||
if input_password == "": # correct
|
||||
pass
|
||||
|
||||
if input_password == ADMIN_PASSWORD: # correct
|
||||
if input_password == ADMIN_PASSWORD: # correct
|
||||
pass
|
||||
|
||||
if input_password == "Hunter2": # [magic-value-comparison]
|
||||
if input_password == "Hunter2": # [magic-value-comparison]
|
||||
pass
|
||||
|
||||
PI = 3.141592653589793238
|
||||
pi_estimation = 3.14
|
||||
|
||||
if pi_estimation == 3.141592653589793238: # [magic-value-comparison]
|
||||
if pi_estimation == 3.141592653589793238: # [magic-value-comparison]
|
||||
pass
|
||||
|
||||
if pi_estimation == PI: # correct
|
||||
if pi_estimation == PI: # correct
|
||||
pass
|
||||
|
||||
HELLO_WORLD = b"Hello, World!"
|
||||
user_input = b"Hello, There!"
|
||||
|
||||
if user_input == b"something": # [magic-value-comparison]
|
||||
if user_input == b"something": # [magic-value-comparison]
|
||||
pass
|
||||
|
||||
if user_input == HELLO_WORLD: # correct
|
||||
if user_input == HELLO_WORLD: # correct
|
||||
pass
|
||||
|
||||
|
||||
13
resources/test/fixtures/pyupgrade/UP003.py
vendored
13
resources/test/fixtures/pyupgrade/UP003.py
vendored
@@ -1,5 +1,12 @@
|
||||
type('')
|
||||
type(b'')
|
||||
type("")
|
||||
type(b"")
|
||||
type(0)
|
||||
type(0.)
|
||||
type(0.0)
|
||||
type(0j)
|
||||
|
||||
# OK
|
||||
y = x.dtype.type(0.0)
|
||||
|
||||
# OK
|
||||
type = lambda *args, **kwargs: None
|
||||
type("")
|
||||
|
||||
67
resources/test/fixtures/pyupgrade/UP011.py
vendored
Normal file
67
resources/test/fixtures/pyupgrade/UP011.py
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
import functools
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
@functools.lru_cache()
|
||||
def fixme():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def fixme():
|
||||
pass
|
||||
|
||||
|
||||
@other_decorator
|
||||
@functools.lru_cache()
|
||||
def fixme():
|
||||
pass
|
||||
|
||||
|
||||
@functools.lru_cache()
|
||||
@other_decorator
|
||||
def fixme():
|
||||
pass
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def ok():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def ok():
|
||||
pass
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=64)
|
||||
def ok():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(maxsize=64)
|
||||
def ok():
|
||||
pass
|
||||
|
||||
|
||||
def user_func():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(user_func)
|
||||
def ok():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(user_func, maxsize=None)
|
||||
def ok():
|
||||
pass
|
||||
|
||||
|
||||
def lru_cache(maxsize=None):
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def ok():
|
||||
pass
|
||||
103
resources/test/fixtures/pyupgrade/UP011_0.py
vendored
103
resources/test/fixtures/pyupgrade/UP011_0.py
vendored
@@ -1,103 +0,0 @@
|
||||
import functools
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def fixme1():
|
||||
pass
|
||||
|
||||
|
||||
@other_deco_after
|
||||
@functools.lru_cache()
|
||||
def fixme2():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def fixme3():
|
||||
pass
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
@other_deco_before
|
||||
def fixme4():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache( # A
|
||||
) # B
|
||||
def fixme5():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(
|
||||
# A
|
||||
) # B
|
||||
def fixme6():
|
||||
pass
|
||||
|
||||
|
||||
@functools.lru_cache(
|
||||
# A
|
||||
maxsize = None) # B
|
||||
def fixme7():
|
||||
pass
|
||||
|
||||
|
||||
@functools.lru_cache(
|
||||
# A1
|
||||
maxsize = None
|
||||
# A2
|
||||
) # B
|
||||
def fixme8():
|
||||
pass
|
||||
|
||||
|
||||
@functools.lru_cache(
|
||||
# A1
|
||||
maxsize =
|
||||
None
|
||||
# A2
|
||||
|
||||
)
|
||||
def fixme9():
|
||||
pass
|
||||
|
||||
|
||||
@functools.lru_cache(
|
||||
# A1
|
||||
maxsize =
|
||||
None
|
||||
# A2
|
||||
)
|
||||
def fixme10():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache
|
||||
def correct1():
|
||||
pass
|
||||
|
||||
|
||||
@functools.lru_cache
|
||||
def correct2():
|
||||
pass
|
||||
|
||||
|
||||
@functoools.lru_cache(maxsize=64)
|
||||
def correct3():
|
||||
pass
|
||||
|
||||
|
||||
def user_func():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(user_func)
|
||||
def correct4():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(user_func, maxsize=None)
|
||||
def correct5():
|
||||
pass
|
||||
15
resources/test/fixtures/pyupgrade/UP011_1.py
vendored
15
resources/test/fixtures/pyupgrade/UP011_1.py
vendored
@@ -1,15 +0,0 @@
|
||||
import functools
|
||||
|
||||
|
||||
def lru_cache(maxsize=None):
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def dont_fixme():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def dont_fixme():
|
||||
pass
|
||||
86
resources/test/fixtures/pyupgrade/UP032.py
vendored
Normal file
86
resources/test/fixtures/pyupgrade/UP032.py
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
###
|
||||
# Errors
|
||||
###
|
||||
|
||||
"{} {}".format(a, b)
|
||||
|
||||
"{1} {0}".format(a, b)
|
||||
|
||||
"{x.y}".format(x=z)
|
||||
|
||||
"{.x} {.y}".format(a, b)
|
||||
|
||||
"{} {}".format(a.b, c.d)
|
||||
|
||||
"{}".format(a())
|
||||
|
||||
"{}".format(a.b())
|
||||
|
||||
"{}".format(a.b().c())
|
||||
|
||||
"hello {}!".format(name)
|
||||
|
||||
"{}{b}{}".format(a, c, b=b)
|
||||
|
||||
"{}".format(0x0)
|
||||
|
||||
"{} {}".format(a, b)
|
||||
|
||||
"""{} {}""".format(a, b)
|
||||
|
||||
"foo{}".format(1)
|
||||
|
||||
r"foo{}".format(1)
|
||||
|
||||
x = "{a}".format(a=1)
|
||||
|
||||
print("foo {} ".format(x))
|
||||
|
||||
"{a[b]}".format(a=a)
|
||||
|
||||
"{a.a[b]}".format(a=a)
|
||||
|
||||
"{}{{}}{}".format(escaped, y)
|
||||
|
||||
"{}".format(a)
|
||||
|
||||
###
|
||||
# Non-errors
|
||||
###
|
||||
|
||||
# False-negative: RustPython doesn't parse the `\N{snowman}`.
|
||||
"\N{snowman} {}".format(a)
|
||||
|
||||
"{".format(a)
|
||||
|
||||
"}".format(a)
|
||||
|
||||
"{} {}".format(*a)
|
||||
|
||||
"{0} {0}".format(arg)
|
||||
|
||||
"{x} {x}".format(arg)
|
||||
|
||||
"{x.y} {x.z}".format(arg)
|
||||
|
||||
b"{} {}".format(a, b)
|
||||
|
||||
"{:{}}".format(x, y)
|
||||
|
||||
"{}{}".format(a)
|
||||
|
||||
"" "{}".format(a["\\"])
|
||||
|
||||
"{}".format(a["b"])
|
||||
|
||||
r'"\N{snowman} {}".format(a)'
|
||||
|
||||
"{a}" "{b}".format(a=1, b=1)
|
||||
|
||||
|
||||
async def c():
|
||||
return "{}".format(await 3)
|
||||
|
||||
|
||||
async def c():
|
||||
return "{}".format(1 + await 3)
|
||||
67
resources/test/fixtures/pyupgrade/UP033.py
vendored
Normal file
67
resources/test/fixtures/pyupgrade/UP033.py
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
import functools
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def fixme():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def fixme():
|
||||
pass
|
||||
|
||||
|
||||
@other_decorator
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def fixme():
|
||||
pass
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
@other_decorator
|
||||
def fixme():
|
||||
pass
|
||||
|
||||
|
||||
@functools.lru_cache()
|
||||
def ok():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def ok():
|
||||
pass
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=64)
|
||||
def ok():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(maxsize=64)
|
||||
def ok():
|
||||
pass
|
||||
|
||||
|
||||
def user_func():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(user_func)
|
||||
def ok():
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(user_func, maxsize=None)
|
||||
def ok():
|
||||
pass
|
||||
|
||||
|
||||
def lru_cache(maxsize=None):
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def ok():
|
||||
pass
|
||||
22
resources/test/fixtures/ruff/RUF005.py
vendored
Normal file
22
resources/test/fixtures/ruff/RUF005.py
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
class Fun:
|
||||
words = ("how", "fun!")
|
||||
|
||||
def yay(self):
|
||||
return self.words
|
||||
|
||||
yay = Fun().yay
|
||||
|
||||
foo = [4, 5, 6]
|
||||
bar = [1, 2, 3] + foo
|
||||
zoob = tuple(bar)
|
||||
quux = (7, 8, 9) + zoob
|
||||
spam = quux + (10, 11, 12)
|
||||
spom = list(spam)
|
||||
eggs = spom + [13, 14, 15]
|
||||
elatement = ("we all say", ) + yay()
|
||||
excitement = ("we all think", ) + Fun().yay()
|
||||
astonishment = ("we all feel", ) + Fun.words
|
||||
|
||||
chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob)
|
||||
|
||||
baz = () + zoob
|
||||
@@ -227,7 +227,7 @@
|
||||
]
|
||||
},
|
||||
"format": {
|
||||
"description": "The style in which violation messages should be formatted: `\"text\"` (default), `\"grouped\"` (group messages by file), `\"json\"` (machine-readable), `\"junit\"` (machine-readable XML), `\"github\"` (GitHub Actions annotations) or `\"gitlab\"` (GitLab CI code quality report).",
|
||||
"description": "The style in which violation messages should be formatted: `\"text\"` (default), `\"grouped\"` (group messages by file), `\"json\"` (machine-readable), `\"junit\"` (machine-readable XML), `\"github\"` (GitHub Actions annotations), `\"gitlab\"` (GitLab CI code quality report), or `\"pylint\"` (Pylint text format).",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/SerializationFormat"
|
||||
@@ -341,6 +341,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"pylint": {
|
||||
"description": "Options for the `pylint` plugin.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PylintOptions"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pyupgrade": {
|
||||
"description": "Options for the `pyupgrade` plugin.",
|
||||
"anyOf": [
|
||||
@@ -448,7 +459,7 @@
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"BannedApi": {
|
||||
"ApiBan": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"msg"
|
||||
@@ -461,6 +472,17 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ConstantType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"bytes",
|
||||
"complex",
|
||||
"float",
|
||||
"int",
|
||||
"str",
|
||||
"tuple"
|
||||
]
|
||||
},
|
||||
"Convention": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -743,7 +765,7 @@
|
||||
"null"
|
||||
],
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/BannedApi"
|
||||
"$ref": "#/definitions/ApiBan"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -762,6 +784,16 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ImportType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"future",
|
||||
"standard-library",
|
||||
"third-party",
|
||||
"first-party",
|
||||
"local-folder"
|
||||
]
|
||||
},
|
||||
"IsortOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -782,6 +814,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"constants": {
|
||||
"description": "An override list of tokens to always recognize as a CONSTANT for `order-by-type` regardless of casing.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"extra-standard-library": {
|
||||
"description": "A list of modules to consider standard-library, in addition to those known to Ruff in advance.",
|
||||
"type": [
|
||||
@@ -833,6 +875,16 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"no-lines-before": {
|
||||
"description": "A list of sections that should _not_ be delineated from the previous section via empty lines.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/ImportType"
|
||||
}
|
||||
},
|
||||
"order-by-type": {
|
||||
"description": "Order imports by type, which is determined by case, in addition to alphabetically.",
|
||||
"type": [
|
||||
@@ -877,6 +929,16 @@
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"variables": {
|
||||
"description": "An override list of tokens to always recognize as a var for `order-by-type` regardless of casing.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -1006,6 +1068,22 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PylintOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allow-magic-value-types": {
|
||||
"description": "Constant types to ignore when used as \"magic values\".",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/ConstantType"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PythonVersion": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
@@ -1356,6 +1434,10 @@
|
||||
"ICN0",
|
||||
"ICN00",
|
||||
"ICN001",
|
||||
"INP",
|
||||
"INP0",
|
||||
"INP00",
|
||||
"INP001",
|
||||
"ISC",
|
||||
"ISC0",
|
||||
"ISC00",
|
||||
@@ -1431,6 +1513,7 @@
|
||||
"PIE79",
|
||||
"PIE790",
|
||||
"PIE794",
|
||||
"PIE796",
|
||||
"PIE8",
|
||||
"PIE80",
|
||||
"PIE807",
|
||||
@@ -1439,10 +1522,6 @@
|
||||
"PLC04",
|
||||
"PLC041",
|
||||
"PLC0414",
|
||||
"PLC2",
|
||||
"PLC22",
|
||||
"PLC220",
|
||||
"PLC2201",
|
||||
"PLC3",
|
||||
"PLC30",
|
||||
"PLC300",
|
||||
@@ -1552,6 +1631,7 @@
|
||||
"RUF002",
|
||||
"RUF003",
|
||||
"RUF004",
|
||||
"RUF005",
|
||||
"RUF1",
|
||||
"RUF10",
|
||||
"RUF100",
|
||||
@@ -1686,6 +1766,8 @@
|
||||
"UP029",
|
||||
"UP03",
|
||||
"UP030",
|
||||
"UP032",
|
||||
"UP033",
|
||||
"W",
|
||||
"W2",
|
||||
"W29",
|
||||
@@ -1723,7 +1805,8 @@
|
||||
"junit",
|
||||
"grouped",
|
||||
"github",
|
||||
"gitlab"
|
||||
"gitlab",
|
||||
"pylint"
|
||||
]
|
||||
},
|
||||
"Strictness": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.223"
|
||||
version = "0.0.227"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
@@ -4,7 +4,7 @@ use clap::{command, Parser};
|
||||
use regex::Regex;
|
||||
use ruff::fs;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::registry::{RuleCode, RuleCodePrefix};
|
||||
use ruff::registry::{Rule, RuleCodePrefix};
|
||||
use ruff::resolver::ConfigProcessor;
|
||||
use ruff::settings::types::{
|
||||
FilePattern, PatternPrefixPair, PerFileIgnore, PythonVersion, SerializationFormat,
|
||||
@@ -169,6 +169,7 @@ pub struct Cli {
|
||||
/// Explain a rule.
|
||||
#[arg(
|
||||
long,
|
||||
value_parser=Rule::from_code,
|
||||
// Fake subcommands.
|
||||
conflicts_with = "add_noqa",
|
||||
conflicts_with = "clean",
|
||||
@@ -180,7 +181,7 @@ pub struct Cli {
|
||||
conflicts_with = "stdin_filename",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub explain: Option<RuleCode>,
|
||||
pub explain: Option<&'static Rule>,
|
||||
/// Generate shell completion
|
||||
#[arg(
|
||||
long,
|
||||
@@ -302,7 +303,7 @@ pub struct Arguments {
|
||||
pub config: Option<PathBuf>,
|
||||
pub diff: bool,
|
||||
pub exit_zero: bool,
|
||||
pub explain: Option<RuleCode>,
|
||||
pub explain: Option<&'static Rule>,
|
||||
pub files: Vec<PathBuf>,
|
||||
pub generate_shell_completion: Option<clap_complete_command::Shell>,
|
||||
pub isolated: bool,
|
||||
|
||||
@@ -15,11 +15,11 @@ use ruff::cache::CACHE_DIR_NAME;
|
||||
use ruff::linter::add_noqa_to_path;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::message::{Location, Message};
|
||||
use ruff::registry::RuleCode;
|
||||
use ruff::registry::Rule;
|
||||
use ruff::resolver::{FileDiscovery, PyprojectDiscovery};
|
||||
use ruff::settings::flags;
|
||||
use ruff::settings::types::SerializationFormat;
|
||||
use ruff::{fix, fs, packaging, resolver, warn_user_once, IOError};
|
||||
use ruff::{fix, fs, packaging, resolver, warn_user_once, AutofixAvailability, IOError};
|
||||
use serde::Serialize;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
@@ -114,7 +114,7 @@ pub fn run(
|
||||
.unwrap_or_else(|(path, message)| {
|
||||
if let Some(path) = &path {
|
||||
let settings = resolver.resolve(path, pyproject_strategy);
|
||||
if settings.enabled.contains(&RuleCode::E902) {
|
||||
if settings.rules.enabled(&Rule::IOError) {
|
||||
Diagnostics::new(vec![Message {
|
||||
kind: IOError(message).into(),
|
||||
location: Location::default(),
|
||||
@@ -289,24 +289,34 @@ struct Explanation<'a> {
|
||||
summary: &'a str,
|
||||
}
|
||||
|
||||
/// Explain a `RuleCode` to the user.
|
||||
pub fn explain(code: &RuleCode, format: SerializationFormat) -> Result<()> {
|
||||
/// Explain a `Rule` to the user.
|
||||
pub fn explain(rule: &Rule, format: SerializationFormat) -> Result<()> {
|
||||
match format {
|
||||
SerializationFormat::Text | SerializationFormat::Grouped => {
|
||||
println!(
|
||||
"{} ({}): {}",
|
||||
code.as_ref(),
|
||||
code.origin().title(),
|
||||
code.kind().summary()
|
||||
);
|
||||
println!("{}\n", rule.as_ref());
|
||||
println!("Code: {} ({})\n", rule.code(), rule.origin().name());
|
||||
|
||||
if let Some(autofix) = rule.autofixable() {
|
||||
println!(
|
||||
"{}",
|
||||
match autofix.available {
|
||||
AutofixAvailability::Sometimes => "Autofix is sometimes available.\n",
|
||||
AutofixAvailability::Always => "Autofix is always available.\n",
|
||||
}
|
||||
);
|
||||
}
|
||||
println!("Message formats:\n");
|
||||
for format in rule.message_formats() {
|
||||
println!("* {format}");
|
||||
}
|
||||
}
|
||||
SerializationFormat::Json => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&Explanation {
|
||||
code: code.as_ref(),
|
||||
origin: code.origin().title(),
|
||||
summary: &code.kind().summary(),
|
||||
code: rule.code(),
|
||||
origin: rule.origin().name(),
|
||||
summary: rule.message_formats()[0],
|
||||
})?
|
||||
);
|
||||
}
|
||||
@@ -319,6 +329,9 @@ pub fn explain(code: &RuleCode, format: SerializationFormat) -> Result<()> {
|
||||
SerializationFormat::Gitlab => {
|
||||
bail!("`--explain` does not support GitLab format")
|
||||
}
|
||||
SerializationFormat::Pylint => {
|
||||
bail!("`--explain` does not support pylint format")
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -91,6 +91,22 @@ fn resolve(
|
||||
pub fn main() -> Result<ExitCode> {
|
||||
// Extract command-line arguments.
|
||||
let (cli, overrides) = Cli::parse().partition();
|
||||
|
||||
let default_panic_hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
eprintln!(
|
||||
r#"
|
||||
{}: `ruff` crashed. This indicates a bug in `ruff`. If you could open an issue at:
|
||||
|
||||
https://github.com/charliermarsh/ruff/issues/new?title=%5BPanic%5D
|
||||
|
||||
quoting the executed command, along with the relevant file contents and `pyproject.toml` settings, we'd be very appreciative!
|
||||
"#,
|
||||
"error".red().bold(),
|
||||
);
|
||||
default_panic_hook(info);
|
||||
}));
|
||||
|
||||
let log_level = extract_log_level(&cli);
|
||||
set_up_logging(&log_level)?;
|
||||
|
||||
@@ -141,8 +157,8 @@ pub fn main() -> Result<ExitCode> {
|
||||
PyprojectDiscovery::Hierarchical(settings) => settings.cli.clone(),
|
||||
};
|
||||
|
||||
if let Some(code) = cli.explain {
|
||||
commands::explain(&code, format)?;
|
||||
if let Some(rule) = cli.explain {
|
||||
commands::explain(rule, format)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.show_settings {
|
||||
|
||||
@@ -11,7 +11,7 @@ use itertools::iterate;
|
||||
use ruff::fs::relativize_path;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::message::{Location, Message};
|
||||
use ruff::registry::RuleCode;
|
||||
use ruff::registry::Rule;
|
||||
use ruff::settings::types::SerializationFormat;
|
||||
use ruff::{fix, notify_user};
|
||||
use serde::Serialize;
|
||||
@@ -35,7 +35,7 @@ struct ExpandedFix<'a> {
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ExpandedMessage<'a> {
|
||||
code: &'a RuleCode,
|
||||
code: SerializeRuleAsCode<'a>,
|
||||
message: String,
|
||||
fix: Option<ExpandedFix<'a>>,
|
||||
location: Location,
|
||||
@@ -43,6 +43,23 @@ struct ExpandedMessage<'a> {
|
||||
filename: &'a str,
|
||||
}
|
||||
|
||||
struct SerializeRuleAsCode<'a>(&'a Rule);
|
||||
|
||||
impl Serialize for SerializeRuleAsCode<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.0.code())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Rule> for SerializeRuleAsCode<'a> {
|
||||
fn from(rule: &'a Rule) -> Self {
|
||||
Self(rule)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Printer<'a> {
|
||||
format: &'a SerializationFormat,
|
||||
log_level: &'a LogLevel,
|
||||
@@ -143,7 +160,7 @@ impl<'a> Printer<'a> {
|
||||
.messages
|
||||
.iter()
|
||||
.map(|message| ExpandedMessage {
|
||||
code: message.kind.code(),
|
||||
code: message.kind.rule().into(),
|
||||
message: message.kind.body(),
|
||||
fix: message.fix.as_ref().map(|fix| ExpandedFix {
|
||||
content: &fix.content,
|
||||
@@ -177,8 +194,10 @@ impl<'a> Printer<'a> {
|
||||
message.location.column(),
|
||||
message.kind.body()
|
||||
));
|
||||
let mut case =
|
||||
TestCase::new(format!("org.ruff.{}", message.kind.code()), status);
|
||||
let mut case = TestCase::new(
|
||||
format!("org.ruff.{}", message.kind.rule().code()),
|
||||
status,
|
||||
);
|
||||
let file_path = Path::new(filename);
|
||||
let file_stem = file_path.file_stem().unwrap().to_str().unwrap();
|
||||
let classname = file_path.parent().unwrap().join(file_stem);
|
||||
@@ -248,14 +267,14 @@ impl<'a> Printer<'a> {
|
||||
":",
|
||||
message.location.column(),
|
||||
":",
|
||||
message.kind.code().as_ref(),
|
||||
message.kind.rule().code(),
|
||||
message.kind.body(),
|
||||
);
|
||||
writeln!(
|
||||
stdout,
|
||||
"::error title=Ruff \
|
||||
({}),file={},line={},col={},endLine={},endColumn={}::{}",
|
||||
message.kind.code(),
|
||||
message.kind.rule().code(),
|
||||
message.filename,
|
||||
message.location.row(),
|
||||
message.location.column(),
|
||||
@@ -266,7 +285,7 @@ impl<'a> Printer<'a> {
|
||||
}
|
||||
}
|
||||
SerializationFormat::Gitlab => {
|
||||
// Generate JSON with errors in GitLab CI format
|
||||
// Generate JSON with violations in GitLab CI format
|
||||
// https://docs.gitlab.com/ee/ci/testing/code_quality.html#implementing-a-custom-tool
|
||||
writeln!(stdout,
|
||||
"{}",
|
||||
@@ -276,9 +295,9 @@ impl<'a> Printer<'a> {
|
||||
.iter()
|
||||
.map(|message| {
|
||||
json!({
|
||||
"description": format!("({}) {}", message.kind.code(), message.kind.body()),
|
||||
"description": format!("({}) {}", message.kind.rule().code(), message.kind.body()),
|
||||
"severity": "major",
|
||||
"fingerprint": message.kind.code(),
|
||||
"fingerprint": message.kind.rule().code(),
|
||||
"location": {
|
||||
"path": message.filename,
|
||||
"lines": {
|
||||
@@ -293,6 +312,20 @@ impl<'a> Printer<'a> {
|
||||
)?
|
||||
)?;
|
||||
}
|
||||
SerializationFormat::Pylint => {
|
||||
// Generate violations in Pylint format.
|
||||
// See: https://flake8.pycqa.org/en/latest/internal/formatters.html#pylint-formatter
|
||||
for message in &diagnostics.messages {
|
||||
let label = format!(
|
||||
"{}:{}: [{}] {}",
|
||||
relativize_path(Path::new(&message.filename)),
|
||||
message.location.row(),
|
||||
message.kind.rule().code(),
|
||||
message.kind.body(),
|
||||
);
|
||||
writeln!(stdout, "{label}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stdout.flush()?;
|
||||
@@ -361,7 +394,7 @@ fn print_message<T: Write>(stdout: &mut T, message: &Message) -> Result<()> {
|
||||
":".cyan(),
|
||||
message.location.column(),
|
||||
":".cyan(),
|
||||
message.kind.code().as_ref().red().bold(),
|
||||
message.kind.rule().code().red().bold(),
|
||||
message.kind.body(),
|
||||
);
|
||||
writeln!(stdout, "{label}")?;
|
||||
@@ -388,7 +421,7 @@ fn print_message<T: Write>(stdout: &mut T, message: &Message) -> Result<()> {
|
||||
source: &source.contents,
|
||||
line_start: message.location.row(),
|
||||
annotations: vec![SourceAnnotation {
|
||||
label: message.kind.code().as_ref(),
|
||||
label: message.kind.rule().code(),
|
||||
annotation_type: AnnotationType::Error,
|
||||
range: source.range,
|
||||
}],
|
||||
@@ -425,7 +458,7 @@ fn print_grouped_message<T: Write>(
|
||||
":".cyan(),
|
||||
message.location.column(),
|
||||
" ".repeat(column_length - num_digits(message.location.column())),
|
||||
message.kind.code().as_ref().red().bold(),
|
||||
message.kind.rule().code().red().bold(),
|
||||
message.kind.body(),
|
||||
);
|
||||
writeln!(stdout, "{label}")?;
|
||||
@@ -452,7 +485,7 @@ fn print_grouped_message<T: Write>(
|
||||
source: &source.contents,
|
||||
line_start: message.location.row(),
|
||||
annotations: vec![SourceAnnotation {
|
||||
label: message.kind.code().as_ref(),
|
||||
label: message.kind.rule().code(),
|
||||
annotation_type: AnnotationType::Error,
|
||||
range: source.range,
|
||||
}],
|
||||
|
||||
@@ -151,3 +151,12 @@ fn test_show_source() -> Result<()> {
|
||||
assert!(str::from_utf8(&output.get_output().stdout)?.contains("l = 1"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explain_status_codes() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
cmd.args(["-", "--explain", "F401"]).assert().success();
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
cmd.args(["-", "--explain", "RUF404"]).assert().failure();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.223"
|
||||
version = "0.0.227"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -11,9 +11,9 @@ libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a87
|
||||
once_cell = { version = "1.16.0" }
|
||||
ruff = { path = ".." }
|
||||
ruff_cli = { path = "../ruff_cli" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "acbc517b55406c76da83d7b2711941d8d3f65b87" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "acbc517b55406c76da83d7b2711941d8d3f65b87" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "acbc517b55406c76da83d7b2711941d8d3f65b87" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
schemars = { version = "0.8.11" }
|
||||
serde_json = {version="1.0.91"}
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
|
||||
@@ -25,14 +25,17 @@ fn generate_table(table_out: &mut String, prefix: &RuleCodePrefix) {
|
||||
table_out.push('\n');
|
||||
table_out.push_str("| ---- | ---- | ------- | --- |");
|
||||
table_out.push('\n');
|
||||
for rule_code in prefix.codes() {
|
||||
let kind = rule_code.kind();
|
||||
let fix_token = if kind.fixable() { "🛠" } else { "" };
|
||||
for rule in prefix.codes() {
|
||||
let fix_token = match rule.autofixable() {
|
||||
None => "",
|
||||
Some(_) => "🛠",
|
||||
};
|
||||
|
||||
table_out.push_str(&format!(
|
||||
"| {} | {} | {} | {} |",
|
||||
kind.code().as_ref(),
|
||||
kind.as_ref(),
|
||||
kind.summary().replace('|', r"\|"),
|
||||
rule.code(),
|
||||
rule.as_ref(),
|
||||
rule.message_formats()[0].replace('|', r"\|"),
|
||||
fix_token
|
||||
));
|
||||
table_out.push('\n');
|
||||
@@ -47,24 +50,37 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
for origin in RuleOrigin::iter() {
|
||||
let prefixes = origin.prefixes();
|
||||
let codes_csv: String = prefixes.as_list(", ");
|
||||
table_out.push_str(&format!("### {} ({codes_csv})", origin.title()));
|
||||
table_out.push_str(&format!("### {} ({codes_csv})", origin.name()));
|
||||
table_out.push('\n');
|
||||
table_out.push('\n');
|
||||
|
||||
toc_out.push_str(&format!(
|
||||
" 1. [{} ({})](#{}-{})\n",
|
||||
origin.title(),
|
||||
origin.name(),
|
||||
codes_csv,
|
||||
origin.title().to_lowercase().replace(' ', "-"),
|
||||
origin.name().to_lowercase().replace(' ', "-"),
|
||||
codes_csv.to_lowercase().replace(',', "-").replace(' ', "")
|
||||
));
|
||||
|
||||
if let Some((url, platform)) = origin.url() {
|
||||
if let Some(url) = origin.url() {
|
||||
let host = url
|
||||
.trim_start_matches("https://")
|
||||
.split('/')
|
||||
.next()
|
||||
.unwrap();
|
||||
table_out.push_str(&format!(
|
||||
"For more, see [{}]({}) on {}.",
|
||||
origin.title(),
|
||||
origin.name(),
|
||||
url,
|
||||
platform
|
||||
match host {
|
||||
"pypi.org" => "PyPI",
|
||||
"github.com" => "GitHub",
|
||||
host => panic!(
|
||||
"unexpected host in URL of {}, expected pypi.org or github.com but found \
|
||||
{host}",
|
||||
origin.name()
|
||||
),
|
||||
}
|
||||
));
|
||||
table_out.push('\n');
|
||||
table_out.push('\n');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.223"
|
||||
version = "0.0.227"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -1,35 +1,44 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::parse::Parse;
|
||||
use syn::{Ident, Path, Token};
|
||||
use syn::{Ident, LitStr, Path, Token};
|
||||
|
||||
pub fn define_rule_mapping(mapping: Mapping) -> proc_macro2::TokenStream {
|
||||
let mut rulecode_variants = quote!();
|
||||
pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
|
||||
let mut rule_variants = quote!();
|
||||
let mut diagkind_variants = quote!();
|
||||
let mut rulecode_kind_match_arms = quote!();
|
||||
let mut rulecode_origin_match_arms = quote!();
|
||||
let mut rule_message_formats_match_arms = quote!();
|
||||
let mut rule_autofixable_match_arms = quote!();
|
||||
let mut rule_origin_match_arms = quote!();
|
||||
let mut rule_code_match_arms = quote!();
|
||||
let mut rule_from_code_match_arms = quote!();
|
||||
let mut diagkind_code_match_arms = quote!();
|
||||
let mut diagkind_body_match_arms = quote!();
|
||||
let mut diagkind_fixable_match_arms = quote!();
|
||||
let mut diagkind_commit_match_arms = quote!();
|
||||
let mut from_impls_for_diagkind = quote!();
|
||||
|
||||
for (code, path, name) in mapping.entries {
|
||||
rulecode_variants.extend(quote! {#code,});
|
||||
for (code, path, name) in &mapping.entries {
|
||||
let code_str = LitStr::new(&code.to_string(), Span::call_site());
|
||||
rule_variants.extend(quote! {
|
||||
#[doc = #code_str]
|
||||
#name,
|
||||
});
|
||||
diagkind_variants.extend(quote! {#name(#path),});
|
||||
rulecode_kind_match_arms.extend(
|
||||
quote! {RuleCode::#code => DiagnosticKind::#name(<#path as Violation>::placeholder()),},
|
||||
);
|
||||
let origin = get_origin(&code);
|
||||
rulecode_origin_match_arms.extend(quote! {RuleCode::#code => RuleOrigin::#origin,});
|
||||
diagkind_code_match_arms.extend(quote! {DiagnosticKind::#name(..) => &RuleCode::#code, });
|
||||
diagkind_body_match_arms
|
||||
.extend(quote! {DiagnosticKind::#name(x) => Violation::message(x), });
|
||||
rule_message_formats_match_arms
|
||||
.extend(quote! {Self::#name => <#path as Violation>::message_formats(),});
|
||||
rule_autofixable_match_arms.extend(quote! {Self::#name => <#path as Violation>::AUTOFIX,});
|
||||
let origin = get_origin(code);
|
||||
rule_origin_match_arms.extend(quote! {Self::#name => RuleOrigin::#origin,});
|
||||
rule_code_match_arms.extend(quote! {Self::#name => #code_str,});
|
||||
rule_from_code_match_arms.extend(quote! {#code_str => Ok(&Rule::#name), });
|
||||
diagkind_code_match_arms.extend(quote! {Self::#name(..) => &Rule::#name, });
|
||||
diagkind_body_match_arms.extend(quote! {Self::#name(x) => Violation::message(x), });
|
||||
diagkind_fixable_match_arms
|
||||
.extend(quote! {DiagnosticKind::#name(x) => x.autofix_title_formatter().is_some(),});
|
||||
diagkind_commit_match_arms.extend(
|
||||
quote! {DiagnosticKind::#name(x) => x.autofix_title_formatter().map(|f| f(x)), },
|
||||
);
|
||||
.extend(quote! {Self::#name(x) => x.autofix_title_formatter().is_some(),});
|
||||
diagkind_commit_match_arms
|
||||
.extend(quote! {Self::#name(x) => x.autofix_title_formatter().map(|f| f(x)), });
|
||||
from_impls_for_diagkind.extend(quote! {
|
||||
impl From<#path> for DiagnosticKind {
|
||||
fn from(x: #path) -> Self {
|
||||
@@ -39,44 +48,73 @@ pub fn define_rule_mapping(mapping: Mapping) -> proc_macro2::TokenStream {
|
||||
});
|
||||
}
|
||||
|
||||
let code_to_name: HashMap<_, _> = mapping
|
||||
.entries
|
||||
.iter()
|
||||
.map(|(code, _, name)| (code.to_string(), name))
|
||||
.collect();
|
||||
|
||||
let rulecodeprefix = super::rule_code_prefix::expand(
|
||||
&Ident::new("Rule", Span::call_site()),
|
||||
&Ident::new("RuleCodePrefix", Span::call_site()),
|
||||
mapping.entries.iter().map(|(code, ..)| code),
|
||||
|code| code_to_name[code],
|
||||
);
|
||||
|
||||
quote! {
|
||||
#[derive(
|
||||
AsRefStr,
|
||||
RuleCodePrefix,
|
||||
EnumIter,
|
||||
EnumString,
|
||||
Debug,
|
||||
Display,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Clone,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Hash,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
AsRefStr,
|
||||
)]
|
||||
pub enum RuleCode { #rulecode_variants }
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum Rule { #rule_variants }
|
||||
|
||||
#[derive(AsRefStr, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DiagnosticKind { #diagkind_variants }
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum FromCodeError {
|
||||
#[error("unknown rule code")]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl RuleCode {
|
||||
/// A placeholder representation of the `DiagnosticKind` for the diagnostic.
|
||||
pub fn kind(&self) -> DiagnosticKind {
|
||||
match self { #rulecode_kind_match_arms }
|
||||
impl Rule {
|
||||
/// Returns the format strings used to report violations of this rule.
|
||||
pub fn message_formats(&self) -> &'static [&'static str] {
|
||||
match self { #rule_message_formats_match_arms }
|
||||
}
|
||||
|
||||
pub fn autofixable(&self) -> Option<crate::violation::AutofixKind> {
|
||||
match self { #rule_autofixable_match_arms }
|
||||
}
|
||||
|
||||
pub fn origin(&self) -> RuleOrigin {
|
||||
match self { #rulecode_origin_match_arms }
|
||||
match self { #rule_origin_match_arms }
|
||||
}
|
||||
|
||||
pub fn code(&self) -> &'static str {
|
||||
match self { #rule_code_match_arms }
|
||||
}
|
||||
|
||||
pub fn from_code(code: &str) -> Result<&'static Self, FromCodeError> {
|
||||
match code {
|
||||
#rule_from_code_match_arms
|
||||
_ => Err(FromCodeError::Unknown),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl DiagnosticKind {
|
||||
/// A four-letter shorthand code for the diagnostic.
|
||||
pub fn code(&self) -> &'static RuleCode {
|
||||
/// The rule of the diagnostic.
|
||||
pub fn rule(&self) -> &'static Rule {
|
||||
match self { #diagkind_code_match_arms }
|
||||
}
|
||||
|
||||
@@ -97,6 +135,8 @@ pub fn define_rule_mapping(mapping: Mapping) -> proc_macro2::TokenStream {
|
||||
}
|
||||
|
||||
#from_impls_for_diagkind
|
||||
|
||||
#rulecodeprefix
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
55
ruff_macros/src/derive_message_formats.rs
Normal file
55
ruff_macros/src/derive_message_formats.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Block, Expr, ItemFn, Stmt};
|
||||
|
||||
pub fn derive_message_formats(func: &ItemFn) -> proc_macro2::TokenStream {
|
||||
let mut strings = quote!();
|
||||
|
||||
if let Err(err) = parse_block(&func.block, &mut strings) {
|
||||
return err;
|
||||
}
|
||||
|
||||
quote! {
|
||||
#func
|
||||
fn message_formats() -> &'static [&'static str] {
|
||||
&[#strings]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_block(block: &Block, strings: &mut TokenStream) -> Result<(), TokenStream> {
|
||||
let Some(Stmt::Expr(last)) = block.stmts.last() else {panic!("expected last statement in block to be an expression")};
|
||||
parse_expr(last, strings)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_expr(expr: &Expr, strings: &mut TokenStream) -> Result<(), TokenStream> {
|
||||
match expr {
|
||||
Expr::Macro(mac) if mac.mac.path.is_ident("format") => {
|
||||
let Some(first_token) = mac.mac.tokens.to_token_stream().into_iter().next() else {
|
||||
return Err(quote_spanned!(expr.span() => compile_error!("expected format! to have an argument")))
|
||||
};
|
||||
strings.extend(quote! {#first_token,});
|
||||
Ok(())
|
||||
}
|
||||
Expr::Block(block) => parse_block(&block.block, strings),
|
||||
Expr::If(expr) => {
|
||||
parse_block(&expr.then_branch, strings)?;
|
||||
if let Some((_, then)) = &expr.else_branch {
|
||||
parse_expr(then, strings)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Expr::Match(block) => {
|
||||
for arm in &block.arms {
|
||||
parse_expr(&arm.body, strings)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(quote_spanned!(
|
||||
expr.span() =>
|
||||
compile_error!("expected last expression to be a format! macro or a match block")
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,12 @@
|
||||
)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput, ItemFn};
|
||||
|
||||
mod config;
|
||||
mod define_rule_mapping;
|
||||
mod derive_message_formats;
|
||||
mod prefixes;
|
||||
mod rule_code_prefix;
|
||||
|
||||
@@ -29,17 +31,14 @@ pub fn derive_config(input: proc_macro::TokenStream) -> proc_macro::TokenStream
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(RuleCodePrefix)]
|
||||
pub fn derive_rule_code_prefix(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
rule_code_prefix::derive_impl(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn define_rule_mapping(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let mapping = parse_macro_input!(item as define_rule_mapping::Mapping);
|
||||
define_rule_mapping::define_rule_mapping(mapping).into()
|
||||
define_rule_mapping::define_rule_mapping(&mapping).into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn derive_message_formats(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let func = parse_macro_input!(item as ItemFn);
|
||||
derive_message_formats::derive_message_formats(&func).into()
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use once_cell::sync::Lazy;
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token::Comma;
|
||||
use syn::{DataEnum, DeriveInput, Ident, Variant};
|
||||
use syn::Ident;
|
||||
|
||||
const ALL: &str = "ALL";
|
||||
|
||||
@@ -86,43 +84,17 @@ pub static PREFIX_REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::n
|
||||
])
|
||||
});
|
||||
|
||||
pub fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
|
||||
let DeriveInput { ident, data, .. } = input;
|
||||
let syn::Data::Enum(DataEnum { variants, .. }) = data else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"Can only derive `RuleCodePrefix` from enums.",
|
||||
));
|
||||
};
|
||||
|
||||
let prefix_ident = Ident::new(&format!("{ident}Prefix"), ident.span());
|
||||
let prefix = expand(&ident, &prefix_ident, &variants);
|
||||
let expanded = quote! {
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum SuffixLength {
|
||||
None,
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
}
|
||||
|
||||
#prefix
|
||||
};
|
||||
Ok(expanded)
|
||||
}
|
||||
|
||||
fn expand(
|
||||
ident: &Ident,
|
||||
pub fn expand<'a>(
|
||||
rule_type: &Ident,
|
||||
prefix_ident: &Ident,
|
||||
variants: &Punctuated<Variant, Comma>,
|
||||
variants: impl Iterator<Item = &'a Ident>,
|
||||
variant_name: impl Fn(&str) -> &'a Ident,
|
||||
) -> proc_macro2::TokenStream {
|
||||
// Build up a map from prefix to matching RuleCodes.
|
||||
let mut prefix_to_codes: BTreeMap<Ident, BTreeSet<String>> = BTreeMap::default();
|
||||
for variant in variants {
|
||||
let span = variant.ident.span();
|
||||
let code_str = variant.ident.to_string();
|
||||
let span = variant.span();
|
||||
let code_str = variant.to_string();
|
||||
let code_prefix_len = code_str
|
||||
.chars()
|
||||
.take_while(|char| char.is_alphabetic())
|
||||
@@ -158,7 +130,7 @@ fn expand(
|
||||
}
|
||||
});
|
||||
|
||||
let prefix_impl = generate_impls(ident, prefix_ident, &prefix_to_codes);
|
||||
let prefix_impl = generate_impls(rule_type, prefix_ident, &prefix_to_codes, variant_name);
|
||||
|
||||
let prefix_redirects = PREFIX_REDIRECTS.iter().map(|(alias, rule_code)| {
|
||||
let code = Ident::new(rule_code, Span::call_site());
|
||||
@@ -168,6 +140,16 @@ fn expand(
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum SuffixLength {
|
||||
None,
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
::strum_macros::EnumString,
|
||||
::strum_macros::AsRefStr,
|
||||
@@ -196,16 +178,17 @@ fn expand(
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_impls(
|
||||
ident: &Ident,
|
||||
fn generate_impls<'a>(
|
||||
rule_type: &Ident,
|
||||
prefix_ident: &Ident,
|
||||
prefix_to_codes: &BTreeMap<Ident, BTreeSet<String>>,
|
||||
variant_name: impl Fn(&str) -> &'a Ident,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let codes_match_arms = prefix_to_codes.iter().map(|(prefix, codes)| {
|
||||
let codes = codes.iter().map(|code| {
|
||||
let code = Ident::new(code, Span::call_site());
|
||||
let rule_variant = variant_name(code);
|
||||
quote! {
|
||||
#ident::#code
|
||||
#rule_type::#rule_variant
|
||||
}
|
||||
});
|
||||
let prefix_str = prefix.to_string();
|
||||
@@ -265,7 +248,7 @@ fn generate_impls(
|
||||
|
||||
quote! {
|
||||
impl #prefix_ident {
|
||||
pub fn codes(&self) -> Vec<#ident> {
|
||||
pub fn codes(&self) -> Vec<#rule_type> {
|
||||
use colored::Colorize;
|
||||
|
||||
#[allow(clippy::match_same_arms)]
|
||||
|
||||
@@ -47,11 +47,11 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::linter::test_path;
|
||||
use crate::settings;
|
||||
|
||||
fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
||||
let diagnostics =test_path(
|
||||
Path::new("./resources/test/fixtures/%s")
|
||||
|
||||
@@ -44,9 +44,9 @@ def main(*, name: str, code: str, origin: str) -> None:
|
||||
|
||||
with open(mod_rs, "w") as fp:
|
||||
for line in content.splitlines():
|
||||
if line.strip() == "fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {":
|
||||
indent = line.split("fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {")[0]
|
||||
fp.write(f'{indent}#[test_case(RuleCode::{code}, Path::new("{code}.py"); "{code}")]')
|
||||
if line.strip() == "fn rules(rule_code: Rule, path: &Path) -> Result<()> {":
|
||||
indent = line.split("fn rules(rule_code: Rule, path: &Path) -> Result<()> {")[0]
|
||||
fp.write(f'{indent}#[test_case(Rule::{code}, Path::new("{code}.py"); "{code}")]')
|
||||
fp.write("\n")
|
||||
|
||||
fp.write(line)
|
||||
|
||||
25
scripts/benchmarks/README.md
Normal file
25
scripts/benchmarks/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# benchmarks
|
||||
|
||||
Utilities for benchmarking Ruff.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Run `./scripts/benchmarks/run.sh` to clone the benchmarking target (CPython).
|
||||
|
||||
If you're looking to benchmark Ruff against other tools, you'll also need to run `poetry
|
||||
install` to create a virtual environment with the required dependencies.
|
||||
|
||||
## Running Benchmarks
|
||||
|
||||
Run `./scripts/benchmarks/run.sh` to run Ruff over the target repo (CPython). The
|
||||
`./scripts/benchmarks` folder contains a few other benchmarks (e.g., `scripts/benchmarks/run_comparisons.sh`
|
||||
compares Ruff to a variety of other tools).
|
||||
|
||||
## Generating Plots
|
||||
|
||||
The Vega specification for the benchmark plot depicted in the root README can be found at
|
||||
`scripts/benchmarks/graph-spec.json`. You can render this JSON spec in the [Vega Editor](https://vega.github.io/editor/#/edited).
|
||||
|
||||
The images seen in the README are generated by exporting the rendered Vega spec as SVG (at around
|
||||
688px wide) and manually bolding the Ruff title and benchmark time. The dark mode variant is
|
||||
generated by changing the fill from `fill="#333333"` to `fill="#C9D1D9"`.
|
||||
1
scripts/benchmarks/dark.svg
Normal file
1
scripts/benchmarks/dark.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 9.0 KiB |
209
scripts/benchmarks/graph-spec.json
Normal file
209
scripts/benchmarks/graph-spec.json
Normal file
@@ -0,0 +1,209 @@
|
||||
{
|
||||
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
|
||||
"data": {
|
||||
"values": [
|
||||
{
|
||||
"tool": "Ruff",
|
||||
"time": 0.2943,
|
||||
"timeFormat": "0.29s"
|
||||
},
|
||||
{
|
||||
"tool": "Autoflake",
|
||||
"time": 6.175,
|
||||
"timeFormat": "6.18s"
|
||||
},
|
||||
{
|
||||
"tool": "Flake8",
|
||||
"time": 12.26,
|
||||
"timeFormat": "12.26s"
|
||||
},
|
||||
{
|
||||
"tool": "Pyflakes",
|
||||
"time": 15.786,
|
||||
"timeFormat": "15.79s"
|
||||
},
|
||||
{
|
||||
"tool": "Pycodestyle",
|
||||
"time": 46.921,
|
||||
"timeFormat": "46.92s"
|
||||
},
|
||||
{
|
||||
"tool": "Pylint",
|
||||
"time": 62.0,
|
||||
"timeFormat": "> 60s"
|
||||
}
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"params": [
|
||||
{
|
||||
"name": "defaultFont",
|
||||
"value": "-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\""
|
||||
},
|
||||
{
|
||||
"name": "titleColor",
|
||||
"value": "#333333"
|
||||
},
|
||||
{
|
||||
"name": "labelColor",
|
||||
"value": "#333333"
|
||||
}
|
||||
],
|
||||
"header": {
|
||||
"labelFont": {
|
||||
"expr": "defaultFont"
|
||||
},
|
||||
"titleFont": {
|
||||
"expr": "defaultFont"
|
||||
},
|
||||
"titleFontWeight": 500
|
||||
},
|
||||
"text": {
|
||||
"font": {
|
||||
"expr": "defaultFont"
|
||||
},
|
||||
"color": {
|
||||
"expr": "labelColor"
|
||||
}
|
||||
},
|
||||
"mark": {
|
||||
"font": {
|
||||
"expr": "defaultFont"
|
||||
},
|
||||
"color": {
|
||||
"expr": "labelColor"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"font": {
|
||||
"expr": "defaultFont"
|
||||
},
|
||||
"subtitleFont": {
|
||||
"expr": "defaultFont"
|
||||
},
|
||||
"fontWeight": 500
|
||||
},
|
||||
"axis": {
|
||||
"labelColor": {
|
||||
"expr": "labelColor"
|
||||
},
|
||||
"labelFont": {
|
||||
"expr": "defaultFont"
|
||||
},
|
||||
"titleFont": {
|
||||
"expr": "defaultFont"
|
||||
},
|
||||
"titleFontWeight": 500,
|
||||
"titleColor": {
|
||||
"expr": "titleColor"
|
||||
},
|
||||
"titleFontSize": 12
|
||||
},
|
||||
"legend": {
|
||||
"titleFontWeight": 500,
|
||||
"titleColor": {
|
||||
"expr": "titleColor"
|
||||
},
|
||||
"titleFontSize": 12,
|
||||
"labelColor": {
|
||||
"expr": "labelColor"
|
||||
},
|
||||
"labelFont": {
|
||||
"expr": "defaultFont"
|
||||
},
|
||||
"titleFont": {
|
||||
"expr": "defaultFont"
|
||||
}
|
||||
},
|
||||
"view": {
|
||||
"stroke": null
|
||||
},
|
||||
"background": "transparent"
|
||||
},
|
||||
"background": "transparent",
|
||||
"encoding": {
|
||||
"y": {
|
||||
"field": "tool",
|
||||
"type": "nominal",
|
||||
"axis": {
|
||||
"grid": false,
|
||||
"title": null,
|
||||
"labelFontSize": 12,
|
||||
"ticks": false,
|
||||
"labelPadding": 10,
|
||||
"domain": false
|
||||
},
|
||||
"sort": null
|
||||
},
|
||||
"x": {
|
||||
"field": "time",
|
||||
"type": "quantitative",
|
||||
"axis": {
|
||||
"title": null,
|
||||
"labelExpr": "datum.value + 's'",
|
||||
"tickCount": 3,
|
||||
"tickSize": 0,
|
||||
"labelPadding": 6,
|
||||
"labelAlign": "center",
|
||||
"labelFontSize": 12,
|
||||
"tickColor": "rgba(127,127,127,0.25)",
|
||||
"gridColor": "rgba(127,127,127,0.25)",
|
||||
"domain": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"height": 140,
|
||||
"width": "container",
|
||||
"layer": [
|
||||
{
|
||||
"mark": "bar",
|
||||
"encoding": {
|
||||
"size": {
|
||||
"value": 13
|
||||
},
|
||||
"color": {
|
||||
"value": "#E15759"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"transform": [
|
||||
{
|
||||
"filter": "datum.tool !== 'ruff'"
|
||||
}
|
||||
],
|
||||
"mark": {
|
||||
"type": "text",
|
||||
"align": "left",
|
||||
"baseline": "middle",
|
||||
"dx": 6,
|
||||
"fontSize": 12
|
||||
},
|
||||
"encoding": {
|
||||
"text": {
|
||||
"field": "timeFormat"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"transform": [
|
||||
{
|
||||
"filter": "datum.tool === 'ruff'"
|
||||
}
|
||||
],
|
||||
"mark": {
|
||||
"type": "text",
|
||||
"align": "left",
|
||||
"baseline": "middle",
|
||||
"dx": 6,
|
||||
"fontSize": 12,
|
||||
"fontWeight": "bold"
|
||||
},
|
||||
"encoding": {
|
||||
"text": {
|
||||
"field": "timeFormat"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
1
scripts/benchmarks/light.svg
Normal file
1
scripts/benchmarks/light.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 9.0 KiB |
1005
scripts/benchmarks/poetry.lock
generated
Normal file
1005
scripts/benchmarks/poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
77
scripts/benchmarks/pyproject.toml
Normal file
77
scripts/benchmarks/pyproject.toml
Normal file
@@ -0,0 +1,77 @@
|
||||
[tool.poetry]
|
||||
name = "scripts"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Charles Marsh <charlie.r.marsh@gmail.com>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.10,<3.12"
|
||||
autoflake = "^2.0.0"
|
||||
flake8 = "^6.0.0"
|
||||
pycodestyle = "^2.10.0"
|
||||
pyflakes = "^3.0.1"
|
||||
pylint = "^2.15.10"
|
||||
black = "^22.12.0"
|
||||
isort = "^5.11.4"
|
||||
flake8-2020 = { version = "*", optional = true }
|
||||
flake8-annotations = { version = "*", optional = true }
|
||||
flake8-bandit = { version = "*", optional = true }
|
||||
flake8-blind-except = { version = "*", optional = true }
|
||||
# flake8-boolean-trap = { version = "*", optional = true }
|
||||
flake8-bugbear = { version = "*", optional = true }
|
||||
flake8-builtins = { version = "*", optional = true }
|
||||
flake8-commas = { version = "*", optional = true }
|
||||
flake8-comprehensions = { version = "*", optional = true }
|
||||
flake8-datetimez = { version = "*", optional = true }
|
||||
flake8-debugger = { version = "*", optional = true }
|
||||
flake8-docstrings = { version = "*", optional = true }
|
||||
# flake8-eradicate = { version = "*", optional = true }
|
||||
flake8-errmsg = { version = "*", optional = true }
|
||||
flake8-implicit-str-concat = { version = "*", optional = true }
|
||||
# flake8-import-conventions = { version = "*", optional = true }
|
||||
flake8-isort = { version = "*", optional = true }
|
||||
flake8-pie = { version = "*", optional = true }
|
||||
flake8-print = { version = "*", optional = true }
|
||||
flake8-quotes = { version = "*", optional = true }
|
||||
flake8-return = { version = "*", optional = true }
|
||||
flake8-simplify = { version = "*", optional = true }
|
||||
flake8-super = { version = "*", optional = true }
|
||||
flake8-tidy-imports = { version = "*", optional = true }
|
||||
pandas-vet = { version = "*", optional = true }
|
||||
pep8-naming = { version = "*", optional = true }
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
[tool.poetry.extras]
|
||||
plugins = [
|
||||
"flake8-2020",
|
||||
"flake8-annotations",
|
||||
"flake8-bandit",
|
||||
"flake8-blind-except",
|
||||
# "flake8-boolean-trap",
|
||||
"flake8-bugbear",
|
||||
"flake8-builtins",
|
||||
"flake8-commas",
|
||||
"flake8-comprehensions",
|
||||
"flake8-datetimez",
|
||||
"flake8-debugger",
|
||||
"flake8-docstrings",
|
||||
# "flake8-eradicate",
|
||||
"flake8-errmsg",
|
||||
"flake8-implicit-str-concat",
|
||||
# "flake8-import-conventions",
|
||||
"flake8-isort",
|
||||
"flake8-pie",
|
||||
"flake8-print",
|
||||
"flake8-quotes",
|
||||
"flake8-return",
|
||||
"flake8-simplify",
|
||||
"flake8-super",
|
||||
"flake8-tidy-imports",
|
||||
"pandas-vet",
|
||||
"pep8-naming",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
8
scripts/benchmarks/run.sh
Executable file
8
scripts/benchmarks/run.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
###
|
||||
# Benchmark Ruff on the CPython codebase.
|
||||
###
|
||||
|
||||
cargo build --release && hyperfine --ignore-failure --warmup 10 \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache"
|
||||
26
scripts/benchmarks/run_all.sh
Executable file
26
scripts/benchmarks/run_all.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
###
|
||||
# Benchmark Ruff's performance against a variety of similar tools, suppressing output as much as
|
||||
# possible (so as to reduce I/O overhead).
|
||||
###
|
||||
|
||||
# Note: Flake8's `checker.py` requires the following variant of `mp_run`:
|
||||
# def _mp_run(filename: str) -> tuple[str, Results, dict[str, int]]:
|
||||
# try:
|
||||
# return FileChecker(
|
||||
# filename=filename, plugins=_mp_plugins, options=_mp_options
|
||||
# ).run_checks()
|
||||
# except:
|
||||
# return (filename, [], {
|
||||
# "files": 0,
|
||||
# "logical lines": 0,
|
||||
# "physical lines": 0,
|
||||
# "tokens": 0,
|
||||
# })
|
||||
|
||||
hyperfine --ignore-failure --warmup 5 \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --select ALL" \
|
||||
"flake8 resources/test/cpython -qq --docstring-convention=all" \
|
||||
"pycodestyle resources/test/cpython -qq" \
|
||||
"pylint resources/test/cpython -j 0 --recursive=y --disable=E,W,C,R"
|
||||
12
scripts/benchmarks/run_comparisons.sh
Executable file
12
scripts/benchmarks/run_comparisons.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
###
|
||||
# Benchmark Ruff's performance against a variety of similar tools.
|
||||
###
|
||||
|
||||
hyperfine --ignore-failure --warmup 5 \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache" \
|
||||
"pyflakes resources/test/cpython" \
|
||||
"autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython" \
|
||||
"pycodestyle resources/test/cpython" \
|
||||
"flake8 resources/test/cpython"
|
||||
43
scripts/benchmarks/run_plugins.sh
Normal file
43
scripts/benchmarks/run_plugins.sh
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
###
|
||||
# Benchmark the incremental performance of each subsequent plugin.
|
||||
###
|
||||
|
||||
cargo build --release && hyperfine --ignore-failure --warmup 10 \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select C90" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select I" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select D" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select UP" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select N" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select YTT" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select ANN" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select S" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select BLE" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select FBT" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select B" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select A" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select C4" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select T10" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select EM" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select ISC" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select ICN" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select T20" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select PT" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select Q" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select RET" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select SIM" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select TID" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select ARG" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select DTZ" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select ERA" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select PD" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select PGH" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select PLC" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select PLE" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select PLR" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select PLW" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select PIE" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select COM" \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent --extend-select RUF"
|
||||
12
scripts/benchmarks/run_silent.sh
Executable file
12
scripts/benchmarks/run_silent.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
###
|
||||
# Benchmark Ruff's performance against a variety of similar tools, suppressing output as much as
|
||||
# possible (so as to reduce I/O overhead).
|
||||
###
|
||||
|
||||
hyperfine --ignore-failure --warmup 5 \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache --silent" \
|
||||
"pycodestyle resources/test/cpython -qq" \
|
||||
"flake8 resources/test/cpython -qq" \
|
||||
"pylint resources/test/cpython -j 0 --recursive=y --disable=E,W,C,R"
|
||||
7
scripts/benchmarks/setup.sh
Executable file
7
scripts/benchmarks/setup.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
###
|
||||
# Setup the CPython repository to enable benchmarking.
|
||||
###
|
||||
|
||||
git clone --branch 3.10 https://github.com/python/cpython.git resources/test/cpython
|
||||
305
scripts/poetry.lock
generated
305
scripts/poetry.lock
generated
@@ -1,305 +0,0 @@
|
||||
# This file is automatically @generated by Poetry and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "astroid"
|
||||
version = "2.12.13"
|
||||
description = "An abstract syntax tree for Python with inference support."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.2"
|
||||
files = [
|
||||
{file = "astroid-2.12.13-py3-none-any.whl", hash = "sha256:10e0ad5f7b79c435179d0d0f0df69998c4eef4597534aae44910db060baeb907"},
|
||||
{file = "astroid-2.12.13.tar.gz", hash = "sha256:1493fe8bd3dfd73dc35bd53c9d5b6e49ead98497c47b2307662556a5692d29d7"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
lazy-object-proxy = ">=1.4.0"
|
||||
wrapt = {version = ">=1.11,<2", markers = "python_version < \"3.11\""}
|
||||
|
||||
[[package]]
|
||||
name = "autoflake"
|
||||
version = "1.7.8"
|
||||
description = "Removes unused imports and unused variables"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "autoflake-1.7.8-py3-none-any.whl", hash = "sha256:46373ef69b6714f5064c923bb28bd797c4f8a9497f557d87fc36665c6d956b39"},
|
||||
{file = "autoflake-1.7.8.tar.gz", hash = "sha256:e7e46372dee46fa1c97acf310d99d922b63d369718a270809d7c278d34a194cf"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pyflakes = ">=1.1.0,<3"
|
||||
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dill"
|
||||
version = "0.3.6"
|
||||
description = "serialize all of python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"},
|
||||
{file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
graph = ["objgraph (>=1.7.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "flake8"
|
||||
version = "5.0.4"
|
||||
description = "the modular source code checker: pep8 pyflakes and co"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
files = [
|
||||
{file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"},
|
||||
{file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
mccabe = ">=0.7.0,<0.8.0"
|
||||
pycodestyle = ">=2.9.0,<2.10.0"
|
||||
pyflakes = ">=2.5.0,<2.6.0"
|
||||
|
||||
[[package]]
|
||||
name = "isort"
|
||||
version = "5.11.4"
|
||||
description = "A Python utility / library to sort Python imports."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
{file = "isort-5.11.4-py3-none-any.whl", hash = "sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b"},
|
||||
{file = "isort-5.11.4.tar.gz", hash = "sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
colors = ["colorama (>=0.4.3,<0.5.0)"]
|
||||
pipfile-deprecated-finder = ["pipreqs", "requirementslib"]
|
||||
plugins = ["setuptools"]
|
||||
requirements-deprecated-finder = ["pip-api", "pipreqs"]
|
||||
|
||||
[[package]]
|
||||
name = "lazy-object-proxy"
|
||||
version = "1.8.0"
|
||||
description = "A fast and thorough lazy object proxy."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "lazy-object-proxy-1.8.0.tar.gz", hash = "sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp310-cp310-win32.whl", hash = "sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp311-cp311-win32.whl", hash = "sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp37-cp37m-win32.whl", hash = "sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp38-cp38-win32.whl", hash = "sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp39-cp39-win32.whl", hash = "sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f"},
|
||||
{file = "lazy_object_proxy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0"},
|
||||
{file = "lazy_object_proxy-1.8.0-pp37-pypy37_pp73-any.whl", hash = "sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891"},
|
||||
{file = "lazy_object_proxy-1.8.0-pp38-pypy38_pp73-any.whl", hash = "sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec"},
|
||||
{file = "lazy_object_proxy-1.8.0-pp39-pypy39_pp73-any.whl", hash = "sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mccabe"
|
||||
version = "0.7.0"
|
||||
description = "McCabe checker, plugin for flake8"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
|
||||
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "2.6.2"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"},
|
||||
{file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
|
||||
|
||||
[[package]]
|
||||
name = "pycodestyle"
|
||||
version = "2.9.1"
|
||||
description = "Python style guide checker"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"},
|
||||
{file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyflakes"
|
||||
version = "2.5.0"
|
||||
description = "passive checker of Python programs"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"},
|
||||
{file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pylint"
|
||||
version = "2.15.9"
|
||||
description = "python code static checker"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.2"
|
||||
files = [
|
||||
{file = "pylint-2.15.9-py3-none-any.whl", hash = "sha256:349c8cd36aede4d50a0754a8c0218b43323d13d5d88f4b2952ddfe3e169681eb"},
|
||||
{file = "pylint-2.15.9.tar.gz", hash = "sha256:18783cca3cfee5b83c6c5d10b3cdb66c6594520ffae61890858fe8d932e1c6b4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
astroid = ">=2.12.13,<=2.14.0-dev0"
|
||||
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
|
||||
dill = {version = ">=0.2", markers = "python_version < \"3.11\""}
|
||||
isort = ">=4.2.5,<6"
|
||||
mccabe = ">=0.6,<0.8"
|
||||
platformdirs = ">=2.2.0"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
tomlkit = ">=0.10.1"
|
||||
|
||||
[package.extras]
|
||||
spelling = ["pyenchant (>=3.2,<4.0)"]
|
||||
testutils = ["gitpython (>3)"]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
description = "A lil' TOML parser"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomlkit"
|
||||
version = "0.11.6"
|
||||
description = "Style preserving TOML library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"},
|
||||
{file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wrapt"
|
||||
version = "1.14.1"
|
||||
description = "Module for decorators, wrappers and monkey patching."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
||||
files = [
|
||||
{file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"},
|
||||
{file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"},
|
||||
{file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"},
|
||||
{file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"},
|
||||
{file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"},
|
||||
{file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"},
|
||||
{file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"},
|
||||
{file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"},
|
||||
]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.10,<3.11"
|
||||
content-hash = "959633dfe6335ab3f943a95c5fdd12ff1bb66cd03c5917704f10ae38d3a5009c"
|
||||
@@ -1,27 +0,0 @@
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 120
|
||||
select = ["E", "F", "W", "I", "C", "RET", "ANN", "UP"]
|
||||
target-version = "py310"
|
||||
|
||||
[tool.poetry]
|
||||
name = "scripts"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Charles Marsh <charlie.r.marsh@gmail.com>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.10,<3.11"
|
||||
autoflake = "^1.4"
|
||||
flake8 = "^5.0.4"
|
||||
pycodestyle = "^2.9.1"
|
||||
pyflakes = "^2.5.0"
|
||||
pylint = "^2.15.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Wrapper around Flake8 to enable multiprocessing on all operating systems.
|
||||
|
||||
As of Python 3.8, macOS's default "start method" for multiprocessing is `spawn`. Flake8
|
||||
requires a "start method" of `fork`, and disables multiprocessing if it detects `spawn`
|
||||
or some other "start method". This script enables the `fork` start method before passing
|
||||
along any command-line arguments to `flake8`.
|
||||
|
||||
This has never caused me any problems, but note that they disabled this for a reason:
|
||||
Flake8's plugin interface doesn't work with `spawn`, and the maintainer says that `fork`
|
||||
is "pretty broken" on macOS.
|
||||
|
||||
See:
|
||||
|
||||
- https://github.com/pycqa/flake8/issues/955
|
||||
- https://github.com/PyCQA/flake8/issues/1337
|
||||
- https://github.com/PyCQA/flake8/issues/342
|
||||
- https://github.com/PyCQA/flake8/pull/1621
|
||||
|
||||
Example usage: python -m run_flake8 --select=E501 .
|
||||
"""
|
||||
import multiprocessing
|
||||
import sys
|
||||
|
||||
from flake8.main import cli
|
||||
|
||||
if __name__ == "__main__":
|
||||
multiprocessing.set_start_method("fork", force=True)
|
||||
cli.main(sys.argv[1:])
|
||||
@@ -33,7 +33,7 @@ pub fn classify(
|
||||
checker.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
METACLASS_BASES
|
||||
.iter()
|
||||
.any(|(module, member)| call_path == [*module, *member])
|
||||
.any(|(module, member)| call_path.as_slice() == [*module, *member])
|
||||
})
|
||||
})
|
||||
|| decorator_list.iter().any(|expr| {
|
||||
|
||||
@@ -10,8 +10,9 @@ use rustpython_ast::{
|
||||
use rustpython_parser::lexer;
|
||||
use rustpython_parser::lexer::Tok;
|
||||
use rustpython_parser::token::StringKind;
|
||||
use smallvec::smallvec;
|
||||
|
||||
use crate::ast::types::{Binding, BindingKind, Range};
|
||||
use crate::ast::types::{Binding, BindingKind, CallPath, Range};
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::source_code::{Generator, Indexer, Locator, Stylist};
|
||||
|
||||
@@ -39,7 +40,7 @@ pub fn unparse_stmt(stmt: &Stmt, stylist: &Stylist) -> String {
|
||||
generator.generate()
|
||||
}
|
||||
|
||||
fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) {
|
||||
fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut CallPath<'a>) {
|
||||
match &expr.node {
|
||||
ExprKind::Call { func, .. } => {
|
||||
collect_call_path_inner(func, parts);
|
||||
@@ -55,9 +56,9 @@ fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an `Expr` to its call path segments (like ["typing", "List"]).
|
||||
pub fn collect_call_path(expr: &Expr) -> Vec<&str> {
|
||||
let mut segments = vec![];
|
||||
/// Convert an `Expr` to its [`CallPath`] segments (like `["typing", "List"]`).
|
||||
pub fn collect_call_path(expr: &Expr) -> CallPath {
|
||||
let mut segments = smallvec![];
|
||||
collect_call_path_inner(expr, &mut segments);
|
||||
segments
|
||||
}
|
||||
@@ -90,7 +91,47 @@ pub fn contains_call_path(checker: &Checker, expr: &Expr, target: &[&str]) -> bo
|
||||
any_over_expr(expr, &|expr| {
|
||||
checker
|
||||
.resolve_call_path(expr)
|
||||
.map_or(false, |call_path| call_path == target)
|
||||
.map_or(false, |call_path| call_path.as_slice() == target)
|
||||
})
|
||||
}
|
||||
|
||||
/// Return `true` if the `Expr` contains an expression that appears to include a
|
||||
/// side-effect (like a function call).
|
||||
pub fn contains_effect(checker: &Checker, expr: &Expr) -> bool {
|
||||
any_over_expr(expr, &|expr| {
|
||||
// Accept empty initializers.
|
||||
if let ExprKind::Call {
|
||||
func,
|
||||
args,
|
||||
keywords,
|
||||
} = &expr.node
|
||||
{
|
||||
if args.is_empty() && keywords.is_empty() {
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
let is_empty_initializer = (id == "set"
|
||||
|| id == "list"
|
||||
|| id == "tuple"
|
||||
|| id == "dict"
|
||||
|| id == "frozenset")
|
||||
&& checker.is_builtin(id);
|
||||
return !is_empty_initializer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, avoid all complex expressions.
|
||||
matches!(
|
||||
expr.node,
|
||||
ExprKind::Await { .. }
|
||||
| ExprKind::Call { .. }
|
||||
| ExprKind::DictComp { .. }
|
||||
| ExprKind::GeneratorExp { .. }
|
||||
| ExprKind::ListComp { .. }
|
||||
| ExprKind::SetComp { .. }
|
||||
| ExprKind::Subscript { .. }
|
||||
| ExprKind::Yield { .. }
|
||||
| ExprKind::YieldFrom { .. }
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -274,7 +315,9 @@ pub fn has_non_none_keyword(keywords: &[Keyword], keyword: &str) -> bool {
|
||||
}
|
||||
|
||||
/// Extract the names of all handled exceptions.
|
||||
pub fn extract_handler_names(handlers: &[Excepthandler]) -> Vec<Vec<&str>> {
|
||||
pub fn extract_handler_names(handlers: &[Excepthandler]) -> Vec<CallPath> {
|
||||
// TODO(charlie): Use `resolve_call_path` to avoid false positives for
|
||||
// overridden builtins.
|
||||
let mut handler_names = vec![];
|
||||
for handler in handlers {
|
||||
match &handler.node {
|
||||
@@ -322,10 +365,9 @@ pub fn collect_arg_names<'a>(arguments: &'a Arguments) -> FxHashSet<&'a str> {
|
||||
}
|
||||
|
||||
/// Returns `true` if a statement or expression includes at least one comment.
|
||||
pub fn has_comments<T>(located: &Located<T>, locator: &Locator) -> bool {
|
||||
lexer::make_tokenizer(&locator.slice_source_code_range(&Range::from_located(located)))
|
||||
.flatten()
|
||||
.any(|(_, tok, _)| matches!(tok, Tok::Comment(..)))
|
||||
pub fn has_comments_in(range: Range, locator: &Locator) -> bool {
|
||||
lexer::make_tokenizer(&locator.slice_source_code_range(&range))
|
||||
.any(|result| result.map_or(false, |(_, tok, _)| matches!(tok, Tok::Comment(..))))
|
||||
}
|
||||
|
||||
/// Returns `true` if a call is an argumented `super` invocation.
|
||||
@@ -377,11 +419,11 @@ pub fn format_import_from_member(
|
||||
}
|
||||
|
||||
/// Split a target string (like `typing.List`) into (`typing`, `List`).
|
||||
pub fn to_call_path(target: &str) -> Vec<&str> {
|
||||
pub fn to_call_path(target: &str) -> CallPath {
|
||||
if target.contains('.') {
|
||||
target.split('.').collect()
|
||||
} else {
|
||||
vec!["", target]
|
||||
smallvec!["", target]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -242,3 +242,5 @@ impl<'a> From<&RefEquality<'a, Expr>> for &'a Expr {
|
||||
r.0
|
||||
}
|
||||
}
|
||||
|
||||
pub type CallPath<'a> = smallvec::SmallVec<[&'a str; 8]>;
|
||||
|
||||
@@ -4,15 +4,20 @@ use std::str::Lines;
|
||||
use rustpython_ast::{Located, Location};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::source_code::Locator;
|
||||
|
||||
/// Extract the leading indentation from a line.
|
||||
pub fn indentation<'a, T>(checker: &'a Checker, located: &'a Located<T>) -> Cow<'a, str> {
|
||||
pub fn indentation<'a, T>(locator: &'a Locator, located: &'a Located<T>) -> Option<Cow<'a, str>> {
|
||||
let range = Range::from_located(located);
|
||||
checker.locator.slice_source_code_range(&Range::new(
|
||||
let indentation = locator.slice_source_code_range(&Range::new(
|
||||
Location::new(range.location.row(), 0),
|
||||
Location::new(range.location.row(), range.location.column()),
|
||||
))
|
||||
));
|
||||
if indentation.chars().all(char::is_whitespace) {
|
||||
Some(indentation)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the leading words from a line of text.
|
||||
|
||||
1880
src/checkers/ast.rs
1880
src/checkers/ast.rs
File diff suppressed because it is too large
Load Diff
18
src/checkers/filesystem.rs
Normal file
18
src/checkers/filesystem.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
|
||||
use crate::settings::Settings;
|
||||
|
||||
pub fn check_file_path(path: &Path, settings: &Settings) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
// flake8-no-pep420
|
||||
if settings.rules.enabled(&Rule::ImplicitNamespacePackage) {
|
||||
if let Some(diagnostic) = implicit_namespace_package(path) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics
|
||||
}
|
||||
@@ -6,7 +6,7 @@ use rustpython_parser::ast::Suite;
|
||||
|
||||
use crate::ast::visitor::Visitor;
|
||||
use crate::directives::IsortDirectives;
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::isort;
|
||||
use crate::rules::isort::track::{Block, ImportTracker};
|
||||
use crate::settings::{flags, Settings};
|
||||
@@ -36,7 +36,7 @@ pub fn check_imports(
|
||||
|
||||
// Enforce import rules.
|
||||
let mut diagnostics = vec![];
|
||||
if settings.enabled.contains(&RuleCode::I001) {
|
||||
if settings.rules.enabled(&Rule::UnsortedImports) {
|
||||
for block in &blocks {
|
||||
if !block.imports.is_empty() {
|
||||
if let Some(diagnostic) = isort::rules::organize_imports(
|
||||
@@ -47,7 +47,7 @@ pub fn check_imports(
|
||||
}
|
||||
}
|
||||
}
|
||||
if settings.enabled.contains(&RuleCode::I002) {
|
||||
if settings.rules.enabled(&Rule::MissingRequiredImport) {
|
||||
diagnostics.extend(isort::rules::add_required_imports(
|
||||
&blocks, python_ast, locator, settings, autofix,
|
||||
));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Lint rules based on checking raw physical lines.
|
||||
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::pycodestyle::rules::{
|
||||
doc_line_too_long, line_too_long, no_newline_at_end_of_file,
|
||||
};
|
||||
@@ -17,12 +17,14 @@ pub fn check_lines(
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
let enforce_blanket_noqa = settings.enabled.contains(&RuleCode::PGH004);
|
||||
let enforce_blanket_type_ignore = settings.enabled.contains(&RuleCode::PGH003);
|
||||
let enforce_doc_line_too_long = settings.enabled.contains(&RuleCode::W505);
|
||||
let enforce_line_too_long = settings.enabled.contains(&RuleCode::E501);
|
||||
let enforce_no_newline_at_end_of_file = settings.enabled.contains(&RuleCode::W292);
|
||||
let enforce_unnecessary_coding_comment = settings.enabled.contains(&RuleCode::UP009);
|
||||
let enforce_blanket_noqa = settings.rules.enabled(&Rule::BlanketNOQA);
|
||||
let enforce_blanket_type_ignore = settings.rules.enabled(&Rule::BlanketTypeIgnore);
|
||||
let enforce_doc_line_too_long = settings.rules.enabled(&Rule::DocLineTooLong);
|
||||
let enforce_line_too_long = settings.rules.enabled(&Rule::LineTooLong);
|
||||
let enforce_no_newline_at_end_of_file = settings.rules.enabled(&Rule::NoNewLineAtEndOfFile);
|
||||
let enforce_unnecessary_coding_comment = settings
|
||||
.rules
|
||||
.enabled(&Rule::PEP3120UnnecessaryCodingComment);
|
||||
|
||||
let mut commented_lines_iter = commented_lines.iter().peekable();
|
||||
let mut doc_lines_iter = doc_lines.iter().peekable();
|
||||
@@ -37,7 +39,9 @@ pub fn check_lines(
|
||||
index,
|
||||
line,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.fixable.contains(&RuleCode::UP009),
|
||||
&& settings
|
||||
.rules
|
||||
.should_fix(&Rule::PEP3120UnnecessaryCodingComment),
|
||||
) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -79,7 +83,7 @@ pub fn check_lines(
|
||||
if let Some(diagnostic) = no_newline_at_end_of_file(
|
||||
contents,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.fixable.contains(&RuleCode::W292),
|
||||
&& settings.rules.should_fix(&Rule::NoNewLineAtEndOfFile),
|
||||
) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -92,7 +96,7 @@ pub fn check_lines(
|
||||
mod tests {
|
||||
|
||||
use super::check_lines;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::{flags, Settings};
|
||||
|
||||
#[test]
|
||||
@@ -105,7 +109,7 @@ mod tests {
|
||||
&[],
|
||||
&Settings {
|
||||
line_length,
|
||||
..Settings::for_rule(RuleCode::E501)
|
||||
..Settings::for_rule(Rule::LineTooLong)
|
||||
},
|
||||
flags::Autofix::Enabled,
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod ast;
|
||||
pub mod filesystem;
|
||||
pub mod imports;
|
||||
pub mod lines;
|
||||
pub mod noqa;
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
//! `NoQA` enforcement and validation.
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use nohash_hasher::IntMap;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::fix::Fix;
|
||||
use crate::noqa::{is_file_exempt, Directive};
|
||||
use crate::registry::{Diagnostic, DiagnosticKind, RuleCode, CODE_REDIRECTS};
|
||||
use crate::registry::{Diagnostic, DiagnosticKind, Rule, CODE_REDIRECTS};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::violations::UnusedCodes;
|
||||
use crate::{noqa, violations};
|
||||
@@ -24,7 +22,7 @@ pub fn check_noqa(
|
||||
let mut noqa_directives: IntMap<usize, (Directive, Vec<&str>)> = IntMap::default();
|
||||
let mut ignored = vec![];
|
||||
|
||||
let enforce_noqa = settings.enabled.contains(&RuleCode::RUF100);
|
||||
let enforce_noqa = settings.rules.enabled(&Rule::UnusedNOQA);
|
||||
|
||||
let lines: Vec<&str> = contents.lines().collect();
|
||||
for lineno in commented_lines {
|
||||
@@ -56,13 +54,13 @@ pub fn check_noqa(
|
||||
});
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(diagnostic.kind.code().as_ref());
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
ignored.push(index);
|
||||
continue;
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if noqa::includes(diagnostic.kind.code(), codes) {
|
||||
matches.push(diagnostic.kind.code().as_ref());
|
||||
if noqa::includes(diagnostic.kind.rule(), codes) {
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
ignored.push(index);
|
||||
continue;
|
||||
}
|
||||
@@ -83,12 +81,12 @@ pub fn check_noqa(
|
||||
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno - 1]), vec![]));
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(diagnostic.kind.code().as_ref());
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
ignored.push(index);
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if noqa::includes(diagnostic.kind.code(), codes) {
|
||||
matches.push(diagnostic.kind.code().as_ref());
|
||||
if noqa::includes(diagnostic.kind.rule(), codes) {
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
ignored.push(index);
|
||||
}
|
||||
}
|
||||
@@ -108,7 +106,7 @@ pub fn check_noqa(
|
||||
Range::new(Location::new(row + 1, start), Location::new(row + 1, end)),
|
||||
);
|
||||
if matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.fixable.contains(diagnostic.kind.code())
|
||||
&& settings.rules.should_fix(diagnostic.kind.rule())
|
||||
{
|
||||
diagnostic.amend(Fix::deletion(
|
||||
Location::new(row + 1, start - spaces),
|
||||
@@ -125,8 +123,8 @@ pub fn check_noqa(
|
||||
let mut valid_codes = vec![];
|
||||
let mut self_ignore = false;
|
||||
for code in codes {
|
||||
let code = CODE_REDIRECTS.get(code).map_or(code, AsRef::as_ref);
|
||||
if code == RuleCode::RUF100.as_ref() {
|
||||
let code = CODE_REDIRECTS.get(code).map_or(code, |r| r.code());
|
||||
if code == Rule::UnusedNOQA.code() {
|
||||
self_ignore = true;
|
||||
break;
|
||||
}
|
||||
@@ -134,8 +132,8 @@ pub fn check_noqa(
|
||||
if matches.contains(&code) || settings.external.contains(code) {
|
||||
valid_codes.push(code);
|
||||
} else {
|
||||
if let Ok(rule_code) = RuleCode::from_str(code) {
|
||||
if settings.enabled.contains(&rule_code) {
|
||||
if let Ok(rule) = Rule::from_code(code) {
|
||||
if settings.rules.enabled(rule) {
|
||||
unmatched_codes.push(code);
|
||||
} else {
|
||||
disabled_codes.push(code);
|
||||
@@ -172,7 +170,7 @@ pub fn check_noqa(
|
||||
Range::new(Location::new(row + 1, start), Location::new(row + 1, end)),
|
||||
);
|
||||
if matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.fixable.contains(diagnostic.kind.code())
|
||||
&& settings.rules.should_fix(diagnostic.kind.rule())
|
||||
{
|
||||
if valid_codes.is_empty() {
|
||||
diagnostic.amend(Fix::deletion(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use rustpython_parser::lexer::{LexResult, Tok};
|
||||
|
||||
use crate::lex::docstring_detection::StateMachine;
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::ruff::rules::Context;
|
||||
use crate::rules::{
|
||||
eradicate, flake8_commas, flake8_implicit_str_concat, flake8_quotes, pycodestyle, ruff,
|
||||
@@ -19,20 +19,32 @@ pub fn check_tokens(
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
let enforce_ambiguous_unicode_character = settings.enabled.contains(&RuleCode::RUF001)
|
||||
|| settings.enabled.contains(&RuleCode::RUF002)
|
||||
|| settings.enabled.contains(&RuleCode::RUF003);
|
||||
let enforce_quotes = settings.enabled.contains(&RuleCode::Q000)
|
||||
|| settings.enabled.contains(&RuleCode::Q001)
|
||||
|| settings.enabled.contains(&RuleCode::Q002)
|
||||
|| settings.enabled.contains(&RuleCode::Q003);
|
||||
let enforce_commented_out_code = settings.enabled.contains(&RuleCode::ERA001);
|
||||
let enforce_invalid_escape_sequence = settings.enabled.contains(&RuleCode::W605);
|
||||
let enforce_implicit_string_concatenation = settings.enabled.contains(&RuleCode::ISC001)
|
||||
|| settings.enabled.contains(&RuleCode::ISC002);
|
||||
let enforce_trailing_comma = settings.enabled.contains(&RuleCode::COM812)
|
||||
|| settings.enabled.contains(&RuleCode::COM818)
|
||||
|| settings.enabled.contains(&RuleCode::COM819);
|
||||
let enforce_ambiguous_unicode_character = settings
|
||||
.rules
|
||||
.enabled(&Rule::AmbiguousUnicodeCharacterString)
|
||||
|| settings
|
||||
.rules
|
||||
.enabled(&Rule::AmbiguousUnicodeCharacterDocstring)
|
||||
|| settings
|
||||
.rules
|
||||
.enabled(&Rule::AmbiguousUnicodeCharacterComment);
|
||||
let enforce_quotes = settings.rules.enabled(&Rule::BadQuotesInlineString)
|
||||
|| settings.rules.enabled(&Rule::BadQuotesMultilineString)
|
||||
|| settings.rules.enabled(&Rule::BadQuotesDocstring)
|
||||
|| settings.rules.enabled(&Rule::AvoidQuoteEscape);
|
||||
let enforce_commented_out_code = settings.rules.enabled(&Rule::CommentedOutCode);
|
||||
let enforce_invalid_escape_sequence = settings.rules.enabled(&Rule::InvalidEscapeSequence);
|
||||
let enforce_implicit_string_concatenation = settings
|
||||
.rules
|
||||
.enabled(&Rule::SingleLineImplicitStringConcatenation)
|
||||
|| settings
|
||||
.rules
|
||||
.enabled(&Rule::MultiLineImplicitStringConcatenation);
|
||||
let enforce_trailing_comma = settings.rules.enabled(&Rule::TrailingCommaMissing)
|
||||
|| settings
|
||||
.rules
|
||||
.enabled(&Rule::TrailingCommaOnBareTupleProhibited)
|
||||
|| settings.rules.enabled(&Rule::TrailingCommaProhibited);
|
||||
|
||||
let mut state_machine = StateMachine::default();
|
||||
for &(start, ref tok, end) in tokens.iter().flatten() {
|
||||
@@ -75,7 +87,7 @@ pub fn check_tokens(
|
||||
settings,
|
||||
autofix,
|
||||
) {
|
||||
if settings.enabled.contains(diagnostic.kind.code()) {
|
||||
if settings.rules.enabled(diagnostic.kind.rule()) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -101,7 +113,7 @@ pub fn check_tokens(
|
||||
start,
|
||||
end,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.fixable.contains(&RuleCode::W605),
|
||||
&& settings.rules.should_fix(&Rule::InvalidEscapeSequence),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -112,16 +124,16 @@ pub fn check_tokens(
|
||||
diagnostics.extend(
|
||||
flake8_implicit_str_concat::rules::implicit(tokens)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.enabled.contains(diagnostic.kind.code())),
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
// COM812, COM818, COM819
|
||||
if enforce_trailing_comma {
|
||||
diagnostics.extend(
|
||||
flake8_commas::rules::trailing_commas(tokens, locator)
|
||||
flake8_commas::rules::trailing_commas(tokens, settings, autofix)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.enabled.contains(diagnostic.kind.code())),
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ bitflags! {
|
||||
impl Flags {
|
||||
pub fn from_settings(settings: &Settings) -> Self {
|
||||
if settings
|
||||
.enabled
|
||||
.iter()
|
||||
.rules
|
||||
.iter_enabled()
|
||||
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::Imports))
|
||||
{
|
||||
Flags::NOQA | Flags::ISORT
|
||||
|
||||
@@ -7,3 +7,7 @@ pub const TRIPLE_QUOTE_PREFIXES: &[&str] = &[
|
||||
pub const SINGLE_QUOTE_PREFIXES: &[&str] = &[
|
||||
"u\"", "u'", "r\"", "r'", "u\"", "u'", "r\"", "r'", "U\"", "U'", "R\"", "R'", "\"", "'",
|
||||
];
|
||||
|
||||
pub const TRIPLE_QUOTE_SUFFIXES: &[&str] = &["\"\"\"", "'''"];
|
||||
|
||||
pub const SINGLE_QUOTE_SUFFIXES: &[&str] = &["\"", "'"];
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::rules::flake8_pytest_style::types::{
|
||||
ParametrizeNameType, ParametrizeValuesRowType, ParametrizeValuesType,
|
||||
};
|
||||
use crate::rules::flake8_quotes::settings::Quote;
|
||||
use crate::rules::flake8_tidy_imports::settings::Strictness;
|
||||
use crate::rules::flake8_tidy_imports::relative_imports::Strictness;
|
||||
use crate::rules::pydocstyle::settings::Convention;
|
||||
use crate::rules::{
|
||||
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_pytest_style, flake8_quotes,
|
||||
@@ -93,7 +93,7 @@ pub fn convert(
|
||||
let mut flake8_errmsg = flake8_errmsg::settings::Options::default();
|
||||
let mut flake8_pytest_style = flake8_pytest_style::settings::Options::default();
|
||||
let mut flake8_quotes = flake8_quotes::settings::Options::default();
|
||||
let mut flake8_tidy_imports = flake8_tidy_imports::settings::Options::default();
|
||||
let mut flake8_tidy_imports = flake8_tidy_imports::options::Options::default();
|
||||
let mut mccabe = mccabe::settings::Options::default();
|
||||
let mut pep8_naming = pep8_naming::settings::Options::default();
|
||||
let mut pydocstyle = pydocstyle::settings::Options::default();
|
||||
@@ -354,7 +354,7 @@ pub fn convert(
|
||||
if flake8_quotes != flake8_quotes::settings::Options::default() {
|
||||
options.flake8_quotes = Some(flake8_quotes);
|
||||
}
|
||||
if flake8_tidy_imports != flake8_tidy_imports::settings::Options::default() {
|
||||
if flake8_tidy_imports != flake8_tidy_imports::options::Options::default() {
|
||||
options.flake8_tidy_imports = Some(flake8_tidy_imports);
|
||||
}
|
||||
if mccabe != mccabe::settings::Options::default() {
|
||||
@@ -454,6 +454,7 @@ mod tests {
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pylint: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -520,6 +521,7 @@ mod tests {
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pylint: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -586,6 +588,7 @@ mod tests {
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pylint: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -652,6 +655,7 @@ mod tests {
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pylint: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -723,6 +727,7 @@ mod tests {
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pylint: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -795,6 +800,7 @@ mod tests {
|
||||
pydocstyle: Some(pydocstyle::settings::Options {
|
||||
convention: Some(Convention::Numpy),
|
||||
}),
|
||||
pylint: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -867,6 +873,7 @@ mod tests {
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pylint: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
14
src/fs.rs
14
src/fs.rs
@@ -4,11 +4,11 @@ use std::io::{BufReader, Read};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use globset::GlobMatcher;
|
||||
use path_absolutize::{path_dedot, Absolutize};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::hashable::{HashableGlobMatcher, HashableHashSet};
|
||||
|
||||
/// Extract the absolute path and basename (as strings) from a Path.
|
||||
pub fn extract_path_names(path: &Path) -> Result<(&str, &str)> {
|
||||
@@ -26,15 +26,19 @@ pub fn extract_path_names(path: &Path) -> Result<(&str, &str)> {
|
||||
/// Create a set with codes matching the pattern/code pairs.
|
||||
pub(crate) fn ignores_from_path<'a>(
|
||||
path: &Path,
|
||||
pattern_code_pairs: &'a [(GlobMatcher, GlobMatcher, FxHashSet<RuleCode>)],
|
||||
) -> Result<FxHashSet<&'a RuleCode>> {
|
||||
pattern_code_pairs: &'a [(
|
||||
HashableGlobMatcher,
|
||||
HashableGlobMatcher,
|
||||
HashableHashSet<Rule>,
|
||||
)],
|
||||
) -> Result<FxHashSet<&'a Rule>> {
|
||||
let (file_path, file_basename) = extract_path_names(path)?;
|
||||
Ok(pattern_code_pairs
|
||||
.iter()
|
||||
.filter(|(absolute, basename, _)| {
|
||||
basename.is_match(file_basename) || absolute.is_match(file_path)
|
||||
})
|
||||
.flat_map(|(_, _, codes)| codes)
|
||||
.flat_map(|(_, _, codes)| codes.iter())
|
||||
.collect())
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
extern crate core;
|
||||
|
||||
mod ast;
|
||||
mod autofix;
|
||||
pub mod cache;
|
||||
@@ -49,6 +47,7 @@ mod violations;
|
||||
mod visibility;
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
pub use violation::{AutofixKind, Availability as AutofixAvailability};
|
||||
pub use violations::IOError;
|
||||
|
||||
cfg_if! {
|
||||
|
||||
@@ -7,17 +7,16 @@ use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::directives;
|
||||
use crate::linter::check_path;
|
||||
use crate::registry::{RuleCode, RuleCodePrefix};
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::{
|
||||
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_errmsg, flake8_import_conventions,
|
||||
flake8_pytest_style, flake8_quotes, flake8_tidy_imports, flake8_unused_arguments, isort,
|
||||
mccabe, pep8_naming, pycodestyle, pydocstyle, pyupgrade,
|
||||
mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, pyupgrade,
|
||||
};
|
||||
use crate::rustpython_helpers::tokenize;
|
||||
use crate::settings::configuration::Configuration;
|
||||
use crate::settings::options::Options;
|
||||
use crate::settings::types::PythonVersion;
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::settings::{defaults, flags, Settings};
|
||||
use crate::source_code::{Indexer, Locator, Stylist};
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
@@ -52,13 +51,30 @@ export interface Diagnostic {
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ExpandedMessage {
|
||||
code: RuleCode,
|
||||
code: SerializeRuleAsCode,
|
||||
message: String,
|
||||
location: Location,
|
||||
end_location: Location,
|
||||
fix: Option<ExpandedFix>,
|
||||
}
|
||||
|
||||
struct SerializeRuleAsCode(Rule);
|
||||
|
||||
impl Serialize for SerializeRuleAsCode {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.0.code())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rule> for SerializeRuleAsCode {
|
||||
fn from(rule: Rule) -> Self {
|
||||
Self(rule)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ExpandedFix {
|
||||
content: String,
|
||||
@@ -87,14 +103,14 @@ pub fn defaultSettings() -> Result<JsValue, JsValue> {
|
||||
// Propagate defaults.
|
||||
allowed_confusables: Some(Vec::default()),
|
||||
builtins: Some(Vec::default()),
|
||||
dummy_variable_rgx: Some("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$".to_string()),
|
||||
dummy_variable_rgx: Some(defaults::DUMMY_VARIABLE_RGX.as_str().to_string()),
|
||||
extend_ignore: Some(Vec::default()),
|
||||
extend_select: Some(Vec::default()),
|
||||
external: Some(Vec::default()),
|
||||
ignore: Some(Vec::default()),
|
||||
line_length: Some(88),
|
||||
select: Some(vec![RuleCodePrefix::E, RuleCodePrefix::F]),
|
||||
target_version: Some(PythonVersion::default()),
|
||||
line_length: Some(defaults::LINE_LENGTH),
|
||||
select: Some(defaults::PREFIXES.to_vec()),
|
||||
target_version: Some(defaults::TARGET_VERSION),
|
||||
// Ignore a bunch of options that don't make sense in a single-file editor.
|
||||
cache_dir: None,
|
||||
exclude: None,
|
||||
@@ -123,7 +139,7 @@ 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::Settings::default().into()),
|
||||
flake8_tidy_imports: Some(flake8_tidy_imports::Settings::default().into()),
|
||||
flake8_import_conventions: Some(
|
||||
flake8_import_conventions::settings::Settings::default().into(),
|
||||
),
|
||||
@@ -135,6 +151,7 @@ pub fn defaultSettings() -> Result<JsValue, JsValue> {
|
||||
pep8_naming: Some(pep8_naming::settings::Settings::default().into()),
|
||||
pycodestyle: Some(pycodestyle::settings::Settings::default().into()),
|
||||
pydocstyle: Some(pydocstyle::settings::Settings::default().into()),
|
||||
pylint: Some(pylint::settings::Settings::default().into()),
|
||||
pyupgrade: Some(pyupgrade::settings::Settings::default().into()),
|
||||
})?)
|
||||
}
|
||||
@@ -182,7 +199,7 @@ pub fn check(contents: &str, options: JsValue) -> Result<JsValue, JsValue> {
|
||||
let messages: Vec<ExpandedMessage> = diagnostics
|
||||
.into_iter()
|
||||
.map(|diagnostic| ExpandedMessage {
|
||||
code: diagnostic.kind.code().clone(),
|
||||
code: diagnostic.kind.rule().clone().into(),
|
||||
message: diagnostic.kind.body(),
|
||||
location: diagnostic.location,
|
||||
end_location: diagnostic.end_location,
|
||||
@@ -224,7 +241,7 @@ mod test {
|
||||
"if (1, 2): pass",
|
||||
r#"{}"#,
|
||||
[ExpandedMessage {
|
||||
code: RuleCode::F634,
|
||||
code: Rule::IfTuple.into(),
|
||||
message: "If test is a tuple, which is always `True`".to_string(),
|
||||
location: Location::new(1, 0),
|
||||
end_location: Location::new(1, 15),
|
||||
|
||||
@@ -7,6 +7,7 @@ use rustpython_parser::lexer::LexResult;
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::fix_file;
|
||||
use crate::checkers::ast::check_ast;
|
||||
use crate::checkers::filesystem::check_file_path;
|
||||
use crate::checkers::imports::check_imports;
|
||||
use crate::checkers::lines::check_lines;
|
||||
use crate::checkers::noqa::check_noqa;
|
||||
@@ -15,7 +16,7 @@ use crate::directives::Directives;
|
||||
use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens};
|
||||
use crate::message::{Message, Source};
|
||||
use crate::noqa::add_noqa;
|
||||
use crate::registry::{Diagnostic, LintSource, RuleCode};
|
||||
use crate::registry::{Diagnostic, LintSource, Rule};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::{Indexer, Locator, Stylist};
|
||||
use crate::{directives, fs, rustpython_helpers, violations};
|
||||
@@ -47,7 +48,7 @@ pub fn check_path(
|
||||
|
||||
// Collect doc lines. This requires a rare mix of tokens (for comments) and AST
|
||||
// (for docstrings), which demands special-casing at this level.
|
||||
let use_doc_lines = settings.enabled.contains(&RuleCode::W505);
|
||||
let use_doc_lines = settings.rules.enabled(&Rule::DocLineTooLong);
|
||||
let mut doc_lines = vec![];
|
||||
if use_doc_lines {
|
||||
doc_lines.extend(doc_lines_from_tokens(&tokens));
|
||||
@@ -55,22 +56,31 @@ pub fn check_path(
|
||||
|
||||
// Run the token-based rules.
|
||||
if settings
|
||||
.enabled
|
||||
.iter()
|
||||
.rules
|
||||
.iter_enabled()
|
||||
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::Tokens))
|
||||
{
|
||||
diagnostics.extend(check_tokens(locator, &tokens, settings, autofix));
|
||||
}
|
||||
|
||||
// Run the filesystem-based rules.
|
||||
if settings
|
||||
.rules
|
||||
.iter_enabled()
|
||||
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::Filesystem))
|
||||
{
|
||||
diagnostics.extend(check_file_path(path, settings));
|
||||
}
|
||||
|
||||
// Run the AST-based rules.
|
||||
let use_ast = settings
|
||||
.enabled
|
||||
.iter()
|
||||
.rules
|
||||
.iter_enabled()
|
||||
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::Ast));
|
||||
let use_imports = !directives.isort.skip_file
|
||||
&& settings
|
||||
.enabled
|
||||
.iter()
|
||||
.rules
|
||||
.iter_enabled()
|
||||
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::Imports));
|
||||
if use_ast || use_imports || use_doc_lines {
|
||||
match rustpython_helpers::parse_program_tokens(tokens, "<filename>") {
|
||||
@@ -106,7 +116,7 @@ pub fn check_path(
|
||||
}
|
||||
}
|
||||
Err(parse_error) => {
|
||||
if settings.enabled.contains(&RuleCode::E999) {
|
||||
if settings.rules.enabled(&Rule::SyntaxError) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
violations::SyntaxError(parse_error.error.to_string()),
|
||||
Range::new(parse_error.location, parse_error.location),
|
||||
@@ -124,8 +134,8 @@ pub fn check_path(
|
||||
|
||||
// Run the lines-based rules.
|
||||
if settings
|
||||
.enabled
|
||||
.iter()
|
||||
.rules
|
||||
.iter_enabled()
|
||||
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::Lines))
|
||||
{
|
||||
diagnostics.extend(check_lines(
|
||||
@@ -140,8 +150,8 @@ pub fn check_path(
|
||||
// Enforce `noqa` directives.
|
||||
if (matches!(noqa, flags::Noqa::Enabled) && !diagnostics.is_empty())
|
||||
|| settings
|
||||
.enabled
|
||||
.iter()
|
||||
.rules
|
||||
.iter_enabled()
|
||||
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::NoQa))
|
||||
{
|
||||
check_noqa(
|
||||
@@ -160,7 +170,7 @@ pub fn check_path(
|
||||
if !ignores.is_empty() {
|
||||
return Ok(diagnostics
|
||||
.into_iter()
|
||||
.filter(|diagnostic| !ignores.contains(&diagnostic.kind.code()))
|
||||
.filter(|diagnostic| !ignores.contains(&diagnostic.kind.rule()))
|
||||
.collect());
|
||||
}
|
||||
}
|
||||
@@ -340,16 +350,15 @@ pub fn lint_fix(
|
||||
}
|
||||
|
||||
eprintln!(
|
||||
"
|
||||
r#"
|
||||
{}: Failed to converge after {} iterations.
|
||||
|
||||
This likely indicates a bug in `{}`. If you could open an issue at:
|
||||
|
||||
{}/issues
|
||||
{}/issues/new?title=%5BInfinite%20loop%5D
|
||||
|
||||
quoting the contents of `{}`, along with the `pyproject.toml` settings and executed command, we'd \
|
||||
be very appreciative!
|
||||
",
|
||||
quoting the contents of `{}`, along with the `pyproject.toml` settings and executed command, we'd be very appreciative!
|
||||
"#,
|
||||
"warning".yellow().bold(),
|
||||
MAX_ITERATIONS,
|
||||
CARGO_PKG_NAME,
|
||||
|
||||
@@ -44,13 +44,14 @@ macro_rules! notify_user {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
|
||||
#[derive(Debug, Default, PartialOrd, Ord, PartialEq, Eq)]
|
||||
pub enum LogLevel {
|
||||
// No output (+ `log::LevelFilter::Off`).
|
||||
Silent,
|
||||
// Only show lint violations, with no decorative output (+ `log::LevelFilter::Off`).
|
||||
Quiet,
|
||||
// All user-facing output (+ `log::LevelFilter::Info`).
|
||||
#[default]
|
||||
Default,
|
||||
// All user-facing output (+ `log::LevelFilter::Debug`).
|
||||
Verbose,
|
||||
@@ -67,12 +68,6 @@ impl LogLevel {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LogLevel {
|
||||
fn default() -> Self {
|
||||
LogLevel::Default
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_up_logging(level: &LogLevel) -> Result<()> {
|
||||
fern::Dispatch::new()
|
||||
.format(|out, message, record| {
|
||||
|
||||
39
src/noqa.rs
39
src/noqa.rs
@@ -8,7 +8,8 @@ use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use crate::registry::{Diagnostic, RuleCode, CODE_REDIRECTS};
|
||||
use crate::registry::{Diagnostic, Rule, CODE_REDIRECTS};
|
||||
use crate::settings::hashable::HashableHashSet;
|
||||
use crate::source_code::LineEnding;
|
||||
|
||||
static NOQA_LINE_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
@@ -68,11 +69,11 @@ pub fn extract_noqa_directive(line: &str) -> Directive {
|
||||
|
||||
/// Returns `true` if the string list of `codes` includes `code` (or an alias
|
||||
/// thereof).
|
||||
pub fn includes(needle: &RuleCode, haystack: &[&str]) -> bool {
|
||||
let needle: &str = needle.as_ref();
|
||||
pub fn includes(needle: &Rule, haystack: &[&str]) -> bool {
|
||||
let needle: &str = needle.code();
|
||||
haystack.iter().any(|candidate| {
|
||||
if let Some(candidate) = CODE_REDIRECTS.get(candidate) {
|
||||
needle == candidate.as_ref()
|
||||
needle == candidate.code()
|
||||
} else {
|
||||
&needle == candidate
|
||||
}
|
||||
@@ -84,7 +85,7 @@ pub fn add_noqa(
|
||||
diagnostics: &[Diagnostic],
|
||||
contents: &str,
|
||||
noqa_line_for: &IntMap<usize, usize>,
|
||||
external: &FxHashSet<String>,
|
||||
external: &HashableHashSet<String>,
|
||||
line_ending: &LineEnding,
|
||||
) -> Result<usize> {
|
||||
let (count, output) =
|
||||
@@ -97,24 +98,24 @@ fn add_noqa_inner(
|
||||
diagnostics: &[Diagnostic],
|
||||
contents: &str,
|
||||
noqa_line_for: &IntMap<usize, usize>,
|
||||
external: &FxHashSet<String>,
|
||||
external: &HashableHashSet<String>,
|
||||
line_ending: &LineEnding,
|
||||
) -> (usize, String) {
|
||||
let mut matches_by_line: FxHashMap<usize, FxHashSet<&RuleCode>> = FxHashMap::default();
|
||||
let mut matches_by_line: FxHashMap<usize, FxHashSet<&Rule>> = FxHashMap::default();
|
||||
for (lineno, line) in contents.lines().enumerate() {
|
||||
// If we hit an exemption for the entire file, bail.
|
||||
if is_file_exempt(line) {
|
||||
return (0, contents.to_string());
|
||||
}
|
||||
|
||||
let mut codes: FxHashSet<&RuleCode> = FxHashSet::default();
|
||||
let mut codes: FxHashSet<&Rule> = FxHashSet::default();
|
||||
for diagnostic in diagnostics {
|
||||
// TODO(charlie): Consider respecting parent `noqa` directives. For now, we'll
|
||||
// add a `noqa` for every diagnostic, on its own line. This could lead to
|
||||
// duplication, whereby some parent `noqa` directives become
|
||||
// redundant.
|
||||
if diagnostic.location.row() == lineno + 1 {
|
||||
codes.insert(diagnostic.kind.code());
|
||||
codes.insert(diagnostic.kind.rule());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +138,7 @@ fn add_noqa_inner(
|
||||
output.push_str(line);
|
||||
output.push_str(line_ending);
|
||||
}
|
||||
Some(codes) => {
|
||||
Some(rules) => {
|
||||
match extract_noqa_directive(line) {
|
||||
Directive::None => {
|
||||
// Add existing content.
|
||||
@@ -147,7 +148,7 @@ fn add_noqa_inner(
|
||||
output.push_str(" # noqa: ");
|
||||
|
||||
// Add codes.
|
||||
let codes: Vec<&str> = codes.iter().map(AsRef::as_ref).collect();
|
||||
let codes: Vec<&str> = rules.iter().map(|r| r.code()).collect();
|
||||
let suffix = codes.join(", ");
|
||||
output.push_str(&suffix);
|
||||
output.push_str(line_ending);
|
||||
@@ -162,7 +163,7 @@ fn add_noqa_inner(
|
||||
|
||||
// Add codes.
|
||||
let codes: Vec<&str> =
|
||||
codes.iter().map(AsRef::as_ref).sorted_unstable().collect();
|
||||
rules.iter().map(|r| r.code()).sorted_unstable().collect();
|
||||
let suffix = codes.join(", ");
|
||||
output.push_str(&suffix);
|
||||
output.push_str(line_ending);
|
||||
@@ -180,9 +181,9 @@ fn add_noqa_inner(
|
||||
formatted.push_str(" # noqa: ");
|
||||
|
||||
// Add codes.
|
||||
let codes: Vec<&str> = codes
|
||||
let codes: Vec<&str> = rules
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(|r| r.code())
|
||||
.chain(existing.into_iter().filter(|code| external.contains(*code)))
|
||||
.sorted_unstable()
|
||||
.collect();
|
||||
@@ -208,12 +209,12 @@ fn add_noqa_inner(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nohash_hasher::IntMap;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::noqa::{add_noqa_inner, NOQA_LINE_REGEX};
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::settings::hashable::HashableHashSet;
|
||||
use crate::source_code::LineEnding;
|
||||
use crate::violations;
|
||||
|
||||
@@ -236,7 +237,7 @@ mod tests {
|
||||
let diagnostics = vec![];
|
||||
let contents = "x = 1";
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = FxHashSet::default();
|
||||
let external = HashableHashSet::default();
|
||||
let (count, output) = add_noqa_inner(
|
||||
&diagnostics,
|
||||
contents,
|
||||
@@ -253,7 +254,7 @@ mod tests {
|
||||
)];
|
||||
let contents = "x = 1";
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = FxHashSet::default();
|
||||
let external = HashableHashSet::default();
|
||||
let (count, output) = add_noqa_inner(
|
||||
&diagnostics,
|
||||
contents,
|
||||
@@ -276,7 +277,7 @@ mod tests {
|
||||
];
|
||||
let contents = "x = 1 # noqa: E741\n";
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = FxHashSet::default();
|
||||
let external = HashableHashSet::default();
|
||||
let (count, output) = add_noqa_inner(
|
||||
&diagnostics,
|
||||
contents,
|
||||
@@ -299,7 +300,7 @@ mod tests {
|
||||
];
|
||||
let contents = "x = 1 # noqa";
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = FxHashSet::default();
|
||||
let external = HashableHashSet::default();
|
||||
let (count, output) = add_noqa_inner(
|
||||
&diagnostics,
|
||||
contents,
|
||||
|
||||
@@ -249,3 +249,13 @@ pub fn is_pep585_builtin(checker: &Checker, expr: &Expr) -> bool {
|
||||
PEP_585_BUILTINS_ELIGIBLE.contains(&call_path.as_slice())
|
||||
})
|
||||
}
|
||||
|
||||
pub enum Callable {
|
||||
ForwardRef,
|
||||
Cast,
|
||||
NewType,
|
||||
TypeVar,
|
||||
NamedTuple,
|
||||
TypedDict,
|
||||
MypyExtension,
|
||||
}
|
||||
|
||||
414
src/registry.rs
414
src/registry.rs
@@ -1,19 +1,16 @@
|
||||
//! Registry of [`RuleCode`] to [`DiagnosticKind`] mappings.
|
||||
|
||||
use std::fmt;
|
||||
//! Registry of [`Rule`] to [`DiagnosticKind`] mappings.
|
||||
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use ruff_macros::RuleCodePrefix;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_parser::ast::Location;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::{AsRefStr, Display, EnumIter, EnumString};
|
||||
use strum_macros::{AsRefStr, EnumIter};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::fix::Fix;
|
||||
use crate::violation::Violation;
|
||||
use crate::violations;
|
||||
use crate::{rules, violations};
|
||||
|
||||
ruff_macros::define_rule_mapping!(
|
||||
// pycodestyle errors
|
||||
@@ -82,7 +79,6 @@ ruff_macros::define_rule_mapping!(
|
||||
F901 => violations::RaiseNotImplemented,
|
||||
// pylint
|
||||
PLC0414 => violations::UselessImportAlias,
|
||||
PLC2201 => violations::MisplacedComparisonConstant,
|
||||
PLC3002 => violations::UnnecessaryDirectLambdaCall,
|
||||
PLE0117 => violations::NonlocalWithoutBinding,
|
||||
PLE0118 => violations::UsedPriorGlobalDeclaration,
|
||||
@@ -152,8 +148,8 @@ ruff_macros::define_rule_mapping!(
|
||||
// mccabe
|
||||
C901 => violations::FunctionIsTooComplex,
|
||||
// flake8-tidy-imports
|
||||
TID251 => violations::BannedApi,
|
||||
TID252 => violations::BannedRelativeImport,
|
||||
TID251 => rules::flake8_tidy_imports::banned_api::BannedApi,
|
||||
TID252 => rules::flake8_tidy_imports::relative_imports::RelativeImports,
|
||||
// flake8-return
|
||||
RET501 => violations::UnnecessaryReturnNone,
|
||||
RET502 => violations::ImplicitReturnValue,
|
||||
@@ -234,7 +230,7 @@ ruff_macros::define_rule_mapping!(
|
||||
UP008 => violations::SuperCallWithParameters,
|
||||
UP009 => violations::PEP3120UnnecessaryCodingComment,
|
||||
UP010 => violations::UnnecessaryFutureImport,
|
||||
UP011 => violations::UnnecessaryLRUCacheParams,
|
||||
UP011 => violations::LRUCacheWithoutParameters,
|
||||
UP012 => violations::UnnecessaryEncodeUTF8,
|
||||
UP013 => violations::ConvertTypedDictFunctionalToClass,
|
||||
UP014 => violations::ConvertNamedTupleFunctionalToClass,
|
||||
@@ -254,6 +250,8 @@ ruff_macros::define_rule_mapping!(
|
||||
UP028 => violations::RewriteYieldFrom,
|
||||
UP029 => violations::UnnecessaryBuiltinImport,
|
||||
UP030 => violations::FormatLiterals,
|
||||
UP032 => violations::FString,
|
||||
UP033 => violations::FunctoolsCache,
|
||||
// pydocstyle
|
||||
D100 => violations::PublicModule,
|
||||
D101 => violations::PublicClass,
|
||||
@@ -410,16 +408,20 @@ ruff_macros::define_rule_mapping!(
|
||||
// flake8-pie
|
||||
PIE790 => violations::NoUnnecessaryPass,
|
||||
PIE794 => violations::DupeClassFieldDefinitions,
|
||||
PIE796 => violations::PreferUniqueEnums,
|
||||
PIE807 => violations::PreferListBuiltin,
|
||||
// flake8-commas
|
||||
COM812 => violations::TrailingCommaMissing,
|
||||
COM818 => violations::TrailingCommaOnBareTupleProhibited,
|
||||
COM819 => violations::TrailingCommaProhibited,
|
||||
// flake8-no-pep420
|
||||
INP001 => violations::ImplicitNamespacePackage,
|
||||
// Ruff
|
||||
RUF001 => violations::AmbiguousUnicodeCharacterString,
|
||||
RUF002 => violations::AmbiguousUnicodeCharacterDocstring,
|
||||
RUF003 => violations::AmbiguousUnicodeCharacterComment,
|
||||
RUF004 => violations::KeywordArgumentBeforeStarArgument,
|
||||
RUF005 => violations::UnpackInsteadOfConcatenatingToCollectionLiteral,
|
||||
RUF100 => violations::UnusedNOQA,
|
||||
);
|
||||
|
||||
@@ -458,23 +460,10 @@ pub enum RuleOrigin {
|
||||
Pylint,
|
||||
Flake8Pie,
|
||||
Flake8Commas,
|
||||
Flake8NoPep420,
|
||||
Ruff,
|
||||
}
|
||||
|
||||
pub enum Platform {
|
||||
PyPI,
|
||||
GitHub,
|
||||
}
|
||||
|
||||
impl fmt::Display for Platform {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Platform::PyPI => fmt.write_str("PyPI"),
|
||||
Platform::GitHub => fmt.write_str("GitHub"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Prefixes {
|
||||
Single(RuleCodePrefix),
|
||||
Multiple(Vec<(RuleCodePrefix, &'static str)>),
|
||||
@@ -492,46 +481,9 @@ impl Prefixes {
|
||||
}
|
||||
}
|
||||
|
||||
impl RuleOrigin {
|
||||
pub fn title(&self) -> &'static str {
|
||||
match self {
|
||||
RuleOrigin::Eradicate => "eradicate",
|
||||
RuleOrigin::Flake82020 => "flake8-2020",
|
||||
RuleOrigin::Flake8Annotations => "flake8-annotations",
|
||||
RuleOrigin::Flake8Bandit => "flake8-bandit",
|
||||
RuleOrigin::Flake8BlindExcept => "flake8-blind-except",
|
||||
RuleOrigin::Flake8BooleanTrap => "flake8-boolean-trap",
|
||||
RuleOrigin::Flake8Bugbear => "flake8-bugbear",
|
||||
RuleOrigin::Flake8Builtins => "flake8-builtins",
|
||||
RuleOrigin::Flake8Comprehensions => "flake8-comprehensions",
|
||||
RuleOrigin::Flake8Debugger => "flake8-debugger",
|
||||
RuleOrigin::Flake8ErrMsg => "flake8-errmsg",
|
||||
RuleOrigin::Flake8ImplicitStrConcat => "flake8-implicit-str-concat",
|
||||
RuleOrigin::Flake8ImportConventions => "flake8-import-conventions",
|
||||
RuleOrigin::Flake8Print => "flake8-print",
|
||||
RuleOrigin::Flake8PytestStyle => "flake8-pytest-style",
|
||||
RuleOrigin::Flake8Quotes => "flake8-quotes",
|
||||
RuleOrigin::Flake8Return => "flake8-return",
|
||||
RuleOrigin::Flake8TidyImports => "flake8-tidy-imports",
|
||||
RuleOrigin::Flake8Simplify => "flake8-simplify",
|
||||
RuleOrigin::Flake8UnusedArguments => "flake8-unused-arguments",
|
||||
RuleOrigin::Flake8Datetimez => "flake8-datetimez",
|
||||
RuleOrigin::Isort => "isort",
|
||||
RuleOrigin::McCabe => "mccabe",
|
||||
RuleOrigin::PandasVet => "pandas-vet",
|
||||
RuleOrigin::PEP8Naming => "pep8-naming",
|
||||
RuleOrigin::Pycodestyle => "pycodestyle",
|
||||
RuleOrigin::Pydocstyle => "pydocstyle",
|
||||
RuleOrigin::Pyflakes => "Pyflakes",
|
||||
RuleOrigin::PygrepHooks => "pygrep-hooks",
|
||||
RuleOrigin::Pylint => "Pylint",
|
||||
RuleOrigin::Pyupgrade => "pyupgrade",
|
||||
RuleOrigin::Flake8Pie => "flake8-pie",
|
||||
RuleOrigin::Flake8Commas => "flake8-commas",
|
||||
RuleOrigin::Ruff => "Ruff-specific rules",
|
||||
}
|
||||
}
|
||||
include!(concat!(env!("OUT_DIR"), "/origin.rs"));
|
||||
|
||||
impl RuleOrigin {
|
||||
pub fn prefixes(&self) -> Prefixes {
|
||||
match self {
|
||||
RuleOrigin::Eradicate => Prefixes::Single(RuleCodePrefix::ERA),
|
||||
@@ -575,134 +527,10 @@ impl RuleOrigin {
|
||||
RuleOrigin::Pyupgrade => Prefixes::Single(RuleCodePrefix::UP),
|
||||
RuleOrigin::Flake8Pie => Prefixes::Single(RuleCodePrefix::PIE),
|
||||
RuleOrigin::Flake8Commas => Prefixes::Single(RuleCodePrefix::COM),
|
||||
RuleOrigin::Flake8NoPep420 => Prefixes::Single(RuleCodePrefix::INP),
|
||||
RuleOrigin::Ruff => Prefixes::Single(RuleCodePrefix::RUF),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn url(&self) -> Option<(&'static str, &'static Platform)> {
|
||||
match self {
|
||||
RuleOrigin::Eradicate => {
|
||||
Some(("https://pypi.org/project/eradicate/2.1.0/", &Platform::PyPI))
|
||||
}
|
||||
RuleOrigin::Flake82020 => Some((
|
||||
"https://pypi.org/project/flake8-2020/1.7.0/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8Annotations => Some((
|
||||
"https://pypi.org/project/flake8-annotations/2.9.1/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8Bandit => Some((
|
||||
"https://pypi.org/project/flake8-bandit/4.1.1/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8BlindExcept => Some((
|
||||
"https://pypi.org/project/flake8-blind-except/0.2.1/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8BooleanTrap => Some((
|
||||
"https://pypi.org/project/flake8-boolean-trap/0.1.0/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8Bugbear => Some((
|
||||
"https://pypi.org/project/flake8-bugbear/22.10.27/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8Builtins => Some((
|
||||
"https://pypi.org/project/flake8-builtins/2.0.1/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8Comprehensions => Some((
|
||||
"https://pypi.org/project/flake8-comprehensions/3.10.1/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8Debugger => Some((
|
||||
"https://pypi.org/project/flake8-debugger/4.1.2/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8ErrMsg => Some((
|
||||
"https://pypi.org/project/flake8-errmsg/0.4.0/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8ImplicitStrConcat => Some((
|
||||
"https://pypi.org/project/flake8-implicit-str-concat/0.3.0/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8ImportConventions => None,
|
||||
RuleOrigin::Flake8Print => Some((
|
||||
"https://pypi.org/project/flake8-print/5.0.0/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8PytestStyle => Some((
|
||||
"https://pypi.org/project/flake8-pytest-style/1.6.0/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8Quotes => Some((
|
||||
"https://pypi.org/project/flake8-quotes/3.3.1/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8Return => Some((
|
||||
"https://pypi.org/project/flake8-return/1.2.0/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8Simplify => Some((
|
||||
"https://pypi.org/project/flake8-simplify/0.19.3/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8TidyImports => Some((
|
||||
"https://pypi.org/project/flake8-tidy-imports/4.8.0/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8UnusedArguments => Some((
|
||||
"https://pypi.org/project/flake8-unused-arguments/0.0.12/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8Datetimez => Some((
|
||||
"https://pypi.org/project/flake8-datetimez/20.10.0/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Isort => Some(("https://pypi.org/project/isort/5.10.1/", &Platform::PyPI)),
|
||||
RuleOrigin::McCabe => Some(("https://pypi.org/project/mccabe/0.7.0/", &Platform::PyPI)),
|
||||
RuleOrigin::PandasVet => Some((
|
||||
"https://pypi.org/project/pandas-vet/0.2.3/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::PEP8Naming => Some((
|
||||
"https://pypi.org/project/pep8-naming/0.13.2/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Pycodestyle => Some((
|
||||
"https://pypi.org/project/pycodestyle/2.9.1/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Pydocstyle => Some((
|
||||
"https://pypi.org/project/pydocstyle/6.1.1/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Pyflakes => {
|
||||
Some(("https://pypi.org/project/pyflakes/2.5.0/", &Platform::PyPI))
|
||||
}
|
||||
RuleOrigin::Pylint => {
|
||||
Some(("https://pypi.org/project/pylint/2.15.7/", &Platform::PyPI))
|
||||
}
|
||||
RuleOrigin::PygrepHooks => Some((
|
||||
"https://github.com/pre-commit/pygrep-hooks",
|
||||
&Platform::GitHub,
|
||||
)),
|
||||
RuleOrigin::Pyupgrade => {
|
||||
Some(("https://pypi.org/project/pyupgrade/3.2.0/", &Platform::PyPI))
|
||||
}
|
||||
RuleOrigin::Flake8Pie => Some((
|
||||
"https://pypi.org/project/flake8-pie/0.16.0/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Flake8Commas => Some((
|
||||
"https://pypi.org/project/flake8-commas/2.1.0/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
RuleOrigin::Ruff => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LintSource {
|
||||
@@ -712,82 +540,43 @@ pub enum LintSource {
|
||||
Tokens,
|
||||
Imports,
|
||||
NoQa,
|
||||
Filesystem,
|
||||
}
|
||||
|
||||
impl RuleCode {
|
||||
impl Rule {
|
||||
/// The source for the diagnostic (either the AST, the filesystem, or the
|
||||
/// physical lines).
|
||||
pub fn lint_source(&self) -> &'static LintSource {
|
||||
match self {
|
||||
RuleCode::RUF100 => &LintSource::NoQa,
|
||||
RuleCode::E501
|
||||
| RuleCode::W292
|
||||
| RuleCode::W505
|
||||
| RuleCode::UP009
|
||||
| RuleCode::PGH003
|
||||
| RuleCode::PGH004 => &LintSource::Lines,
|
||||
RuleCode::ERA001
|
||||
| RuleCode::ISC001
|
||||
| RuleCode::ISC002
|
||||
| RuleCode::Q000
|
||||
| RuleCode::Q001
|
||||
| RuleCode::Q002
|
||||
| RuleCode::Q003
|
||||
| RuleCode::W605
|
||||
| RuleCode::COM812
|
||||
| RuleCode::COM818
|
||||
| RuleCode::COM819
|
||||
| RuleCode::RUF001
|
||||
| RuleCode::RUF002
|
||||
| RuleCode::RUF003 => &LintSource::Tokens,
|
||||
RuleCode::E902 => &LintSource::Io,
|
||||
RuleCode::I001 | RuleCode::I002 => &LintSource::Imports,
|
||||
Rule::UnusedNOQA => &LintSource::NoQa,
|
||||
Rule::LineTooLong
|
||||
| Rule::NoNewLineAtEndOfFile
|
||||
| Rule::DocLineTooLong
|
||||
| Rule::PEP3120UnnecessaryCodingComment
|
||||
| Rule::BlanketTypeIgnore
|
||||
| Rule::BlanketNOQA => &LintSource::Lines,
|
||||
Rule::CommentedOutCode
|
||||
| Rule::SingleLineImplicitStringConcatenation
|
||||
| Rule::MultiLineImplicitStringConcatenation
|
||||
| Rule::BadQuotesInlineString
|
||||
| Rule::BadQuotesMultilineString
|
||||
| Rule::BadQuotesDocstring
|
||||
| Rule::AvoidQuoteEscape
|
||||
| Rule::InvalidEscapeSequence
|
||||
| Rule::TrailingCommaMissing
|
||||
| Rule::TrailingCommaOnBareTupleProhibited
|
||||
| Rule::TrailingCommaProhibited
|
||||
| Rule::AmbiguousUnicodeCharacterString
|
||||
| Rule::AmbiguousUnicodeCharacterDocstring
|
||||
| Rule::AmbiguousUnicodeCharacterComment => &LintSource::Tokens,
|
||||
Rule::IOError => &LintSource::Io,
|
||||
Rule::UnsortedImports | Rule::MissingRequiredImport => &LintSource::Imports,
|
||||
Rule::ImplicitNamespacePackage => &LintSource::Filesystem,
|
||||
_ => &LintSource::Ast,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DiagnosticKind {
|
||||
/// The summary text for the diagnostic. Typically a truncated form of the
|
||||
/// body text.
|
||||
pub fn summary(&self) -> String {
|
||||
match self {
|
||||
DiagnosticKind::UnaryPrefixIncrement(..) => {
|
||||
"Python does not support the unary prefix increment".to_string()
|
||||
}
|
||||
DiagnosticKind::UnusedLoopControlVariable(violations::UnusedLoopControlVariable(
|
||||
name,
|
||||
)) => {
|
||||
format!("Loop control variable `{name}` not used within the loop body")
|
||||
}
|
||||
DiagnosticKind::NoAssertRaisesException(..) => {
|
||||
"`assertRaises(Exception)` should be considered evil".to_string()
|
||||
}
|
||||
DiagnosticKind::StarArgUnpackingAfterKeywordArg(..) => {
|
||||
"Star-arg unpacking after a keyword argument is strongly discouraged".to_string()
|
||||
}
|
||||
|
||||
// flake8-datetimez
|
||||
DiagnosticKind::CallDatetimeToday(..) => {
|
||||
"The use of `datetime.datetime.today()` is not allowed".to_string()
|
||||
}
|
||||
DiagnosticKind::CallDatetimeUtcnow(..) => {
|
||||
"The use of `datetime.datetime.utcnow()` is not allowed".to_string()
|
||||
}
|
||||
DiagnosticKind::CallDatetimeUtcfromtimestamp(..) => {
|
||||
"The use of `datetime.datetime.utcfromtimestamp()` is not allowed".to_string()
|
||||
}
|
||||
DiagnosticKind::CallDateToday(..) => {
|
||||
"The use of `datetime.date.today()` is not allowed.".to_string()
|
||||
}
|
||||
DiagnosticKind::CallDateFromtimestamp(..) => {
|
||||
"The use of `datetime.date.fromtimestamp()` is not allowed".to_string()
|
||||
}
|
||||
_ => self.body(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Diagnostic {
|
||||
pub kind: DiagnosticKind,
|
||||
@@ -820,95 +609,80 @@ impl Diagnostic {
|
||||
}
|
||||
|
||||
/// Pairs of checks that shouldn't be enabled together.
|
||||
pub const INCOMPATIBLE_CODES: &[(RuleCode, RuleCode, &str)] = &[(
|
||||
RuleCode::D203,
|
||||
RuleCode::D211,
|
||||
pub const INCOMPATIBLE_CODES: &[(Rule, Rule, &str)] = &[(
|
||||
Rule::OneBlankLineBeforeClass,
|
||||
Rule::NoBlankLineBeforeClass,
|
||||
"`D203` (OneBlankLineBeforeClass) and `D211` (NoBlankLinesBeforeClass) are incompatible. \
|
||||
Consider adding `D203` to `ignore`.",
|
||||
)];
|
||||
|
||||
/// A hash map from deprecated to latest `RuleCode`.
|
||||
pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, RuleCode>> = Lazy::new(|| {
|
||||
/// A hash map from deprecated to latest `Rule`.
|
||||
pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, Rule>> = Lazy::new(|| {
|
||||
FxHashMap::from_iter([
|
||||
// TODO(charlie): Remove by 2023-01-01.
|
||||
("U001", RuleCode::UP001),
|
||||
("U003", RuleCode::UP003),
|
||||
("U004", RuleCode::UP004),
|
||||
("U005", RuleCode::UP005),
|
||||
("U006", RuleCode::UP006),
|
||||
("U007", RuleCode::UP007),
|
||||
("U008", RuleCode::UP008),
|
||||
("U009", RuleCode::UP009),
|
||||
("U010", RuleCode::UP010),
|
||||
("U011", RuleCode::UP011),
|
||||
("U012", RuleCode::UP012),
|
||||
("U013", RuleCode::UP013),
|
||||
("U014", RuleCode::UP014),
|
||||
("U015", RuleCode::UP015),
|
||||
("U016", RuleCode::UP016),
|
||||
("U017", RuleCode::UP017),
|
||||
("U019", RuleCode::UP019),
|
||||
("U001", Rule::UselessMetaclassType),
|
||||
("U003", Rule::TypeOfPrimitive),
|
||||
("U004", Rule::UselessObjectInheritance),
|
||||
("U005", Rule::DeprecatedUnittestAlias),
|
||||
("U006", Rule::UsePEP585Annotation),
|
||||
("U007", Rule::UsePEP604Annotation),
|
||||
("U008", Rule::SuperCallWithParameters),
|
||||
("U009", Rule::PEP3120UnnecessaryCodingComment),
|
||||
("U010", Rule::UnnecessaryFutureImport),
|
||||
("U011", Rule::LRUCacheWithoutParameters),
|
||||
("U012", Rule::UnnecessaryEncodeUTF8),
|
||||
("U013", Rule::ConvertTypedDictFunctionalToClass),
|
||||
("U014", Rule::ConvertNamedTupleFunctionalToClass),
|
||||
("U015", Rule::RedundantOpenModes),
|
||||
("U016", Rule::RemoveSixCompat),
|
||||
("U017", Rule::DatetimeTimezoneUTC),
|
||||
("U019", Rule::TypingTextStrAlias),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("I252", RuleCode::TID252),
|
||||
("M001", RuleCode::RUF100),
|
||||
("I252", Rule::RelativeImports),
|
||||
("M001", Rule::UnusedNOQA),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("PDV002", RuleCode::PD002),
|
||||
("PDV003", RuleCode::PD003),
|
||||
("PDV004", RuleCode::PD004),
|
||||
("PDV007", RuleCode::PD007),
|
||||
("PDV008", RuleCode::PD008),
|
||||
("PDV009", RuleCode::PD009),
|
||||
("PDV010", RuleCode::PD010),
|
||||
("PDV011", RuleCode::PD011),
|
||||
("PDV012", RuleCode::PD012),
|
||||
("PDV013", RuleCode::PD013),
|
||||
("PDV015", RuleCode::PD015),
|
||||
("PDV901", RuleCode::PD901),
|
||||
("PDV002", Rule::UseOfInplaceArgument),
|
||||
("PDV003", Rule::UseOfDotIsNull),
|
||||
("PDV004", Rule::UseOfDotNotNull),
|
||||
("PDV007", Rule::UseOfDotIx),
|
||||
("PDV008", Rule::UseOfDotAt),
|
||||
("PDV009", Rule::UseOfDotIat),
|
||||
("PDV010", Rule::UseOfDotPivotOrUnstack),
|
||||
("PDV011", Rule::UseOfDotValues),
|
||||
("PDV012", Rule::UseOfDotReadTable),
|
||||
("PDV013", Rule::UseOfDotStack),
|
||||
("PDV015", Rule::UseOfPdMerge),
|
||||
("PDV901", Rule::DfIsABadVariableName),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("R501", RuleCode::RET501),
|
||||
("R502", RuleCode::RET502),
|
||||
("R503", RuleCode::RET503),
|
||||
("R504", RuleCode::RET504),
|
||||
("R505", RuleCode::RET505),
|
||||
("R506", RuleCode::RET506),
|
||||
("R507", RuleCode::RET507),
|
||||
("R508", RuleCode::RET508),
|
||||
("R501", Rule::UnnecessaryReturnNone),
|
||||
("R502", Rule::ImplicitReturnValue),
|
||||
("R503", Rule::ImplicitReturn),
|
||||
("R504", Rule::UnnecessaryAssign),
|
||||
("R505", Rule::SuperfluousElseReturn),
|
||||
("R506", Rule::SuperfluousElseRaise),
|
||||
("R507", Rule::SuperfluousElseContinue),
|
||||
("R508", Rule::SuperfluousElseBreak),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("IC001", RuleCode::ICN001),
|
||||
("IC002", RuleCode::ICN001),
|
||||
("IC003", RuleCode::ICN001),
|
||||
("IC004", RuleCode::ICN001),
|
||||
("IC001", Rule::ImportAliasIsNotConventional),
|
||||
("IC002", Rule::ImportAliasIsNotConventional),
|
||||
("IC003", Rule::ImportAliasIsNotConventional),
|
||||
("IC004", Rule::ImportAliasIsNotConventional),
|
||||
])
|
||||
});
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
|
||||
#[test]
|
||||
fn check_code_serialization() {
|
||||
for check_code in RuleCode::iter() {
|
||||
for rule in Rule::iter() {
|
||||
assert!(
|
||||
RuleCode::from_str(check_code.as_ref()).is_ok(),
|
||||
"{check_code:?} could not be round-trip serialized."
|
||||
Rule::from_code(rule.code()).is_ok(),
|
||||
"{rule:?} could not be round-trip serialized."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixable_codes() {
|
||||
for check_code in RuleCode::iter() {
|
||||
let kind = check_code.kind();
|
||||
if kind.fixable() {
|
||||
assert!(
|
||||
kind.commit().is_some(),
|
||||
"{check_code:?} is fixable but has no commit message."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
//! Rules from [eradicate](https://pypi.org/project/eradicate/2.1.0/).
|
||||
pub(crate) mod detection;
|
||||
pub(crate) mod rules;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::AsRef;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings;
|
||||
|
||||
#[test_case(RuleCode::ERA001, Path::new("ERA001.py"); "ERA001")]
|
||||
fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
||||
#[test_case(Rule::CommentedOutCode, Path::new("ERA001.py"); "ERA001")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/eradicate")
|
||||
.join(path)
|
||||
|
||||
@@ -3,7 +3,7 @@ use rustpython_ast::Location;
|
||||
use super::detection::comment_contains_code;
|
||||
use crate::ast::types::Range;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::Locator;
|
||||
use crate::violations;
|
||||
@@ -35,7 +35,7 @@ pub fn commented_out_code(
|
||||
if is_standalone_comment(&line) && comment_contains_code(&line, &settings.task_tags[..]) {
|
||||
let mut diagnostic = Diagnostic::new(violations::CommentedOutCode, Range::new(start, end));
|
||||
if matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.fixable.contains(&RuleCode::ERA001)
|
||||
&& settings.rules.should_fix(&Rule::CommentedOutCode)
|
||||
{
|
||||
diagnostic.amend(Fix::deletion(location, end_location));
|
||||
}
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
//! Rules from [flake8-2020](https://pypi.org/project/flake8-2020/1.7.0/).
|
||||
pub(crate) mod rules;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::AsRef;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings;
|
||||
|
||||
#[test_case(RuleCode::YTT101, Path::new("YTT101.py"); "YTT101")]
|
||||
#[test_case(RuleCode::YTT102, Path::new("YTT102.py"); "YTT102")]
|
||||
#[test_case(RuleCode::YTT103, Path::new("YTT103.py"); "YTT103")]
|
||||
#[test_case(RuleCode::YTT201, Path::new("YTT201.py"); "YTT201")]
|
||||
#[test_case(RuleCode::YTT202, Path::new("YTT202.py"); "YTT202")]
|
||||
#[test_case(RuleCode::YTT203, Path::new("YTT203.py"); "YTT203")]
|
||||
#[test_case(RuleCode::YTT204, Path::new("YTT204.py"); "YTT204")]
|
||||
#[test_case(RuleCode::YTT301, Path::new("YTT301.py"); "YTT301")]
|
||||
#[test_case(RuleCode::YTT302, Path::new("YTT302.py"); "YTT302")]
|
||||
#[test_case(RuleCode::YTT303, Path::new("YTT303.py"); "YTT303")]
|
||||
fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
||||
#[test_case(Rule::SysVersionSlice3Referenced, Path::new("YTT101.py"); "YTT101")]
|
||||
#[test_case(Rule::SysVersion2Referenced, Path::new("YTT102.py"); "YTT102")]
|
||||
#[test_case(Rule::SysVersionCmpStr3, Path::new("YTT103.py"); "YTT103")]
|
||||
#[test_case(Rule::SysVersionInfo0Eq3Referenced, Path::new("YTT201.py"); "YTT201")]
|
||||
#[test_case(Rule::SixPY3Referenced, Path::new("YTT202.py"); "YTT202")]
|
||||
#[test_case(Rule::SysVersionInfo1CmpInt, Path::new("YTT203.py"); "YTT203")]
|
||||
#[test_case(Rule::SysVersionInfoMinorCmpInt, Path::new("YTT204.py"); "YTT204")]
|
||||
#[test_case(Rule::SysVersion0Referenced, Path::new("YTT301.py"); "YTT301")]
|
||||
#[test_case(Rule::SysVersionCmpStr10, Path::new("YTT302.py"); "YTT302")]
|
||||
#[test_case(Rule::SysVersionSlice1Referenced, Path::new("YTT303.py"); "YTT303")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_2020")
|
||||
.join(path)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user