Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e4b1533ad | ||
|
|
df4f5358f9 | ||
|
|
58c383401c | ||
|
|
018b9a2977 | ||
|
|
658cb87ddd | ||
|
|
0d35087bc6 | ||
|
|
b721125af9 | ||
|
|
20d6b21d77 | ||
|
|
1a27992f47 | ||
|
|
89cebe1ce2 | ||
|
|
bdb1505262 | ||
|
|
8c018e8261 | ||
|
|
debd909b2c | ||
|
|
fa54538bd1 | ||
|
|
939f738a71 | ||
|
|
b0f30bef8f | ||
|
|
28c45eb2a3 | ||
|
|
4dc45912e8 | ||
|
|
5ef8bff341 | ||
|
|
2ab8f77223 | ||
|
|
8b72f55a09 | ||
|
|
19121219fb | ||
|
|
d9355c989a | ||
|
|
ec80d1cd85 | ||
|
|
9bb470c7d4 | ||
|
|
dca3fcd8d1 | ||
|
|
10f75c9620 | ||
|
|
cc2110449c | ||
|
|
f6ca49e05f | ||
|
|
9a7331b2e2 | ||
|
|
4888afd423 | ||
|
|
0dc523b081 | ||
|
|
63772e335d | ||
|
|
7f4ff1e38f | ||
|
|
32ebc1d227 | ||
|
|
4ded155dc0 | ||
|
|
201e1250de | ||
|
|
102b049a32 | ||
|
|
74f49eda64 | ||
|
|
9da3e2cca1 | ||
|
|
e290050821 | ||
|
|
bc9ed0a4ef | ||
|
|
20b9b44973 | ||
|
|
6e5a553235 | ||
|
|
2a08a63f17 | ||
|
|
d4290e6721 |
43
.github/workflows/ci.yaml
vendored
43
.github/workflows/ci.yaml
vendored
@@ -12,7 +12,7 @@ env:
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
|
||||
jobs:
|
||||
cargo_build:
|
||||
cargo-build:
|
||||
name: "cargo build"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -42,8 +42,14 @@ jobs:
|
||||
- run: ./target/release/ruff_dev generate-check-code-prefix
|
||||
- run: git diff --quiet src/checks_gen.rs || echo "::error file=src/checks_gen.rs::This file is outdated. You may have to rerun 'cargo dev generate-check-code-prefix'."
|
||||
- run: git diff --exit-code -- README.md src/checks_gen.rs
|
||||
- run: ./target/release/ruff_dev generate-json-schema
|
||||
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. You may have to rerun 'cargo dev generate-json-schema'."
|
||||
- run: git diff --exit-code -- ruff.schema.json
|
||||
- run: ./target/release/ruff_dev generate-playground-options
|
||||
- run: git diff --quiet playground/src/ruff_options.rs || echo "::error file=playground/src/ruff_options.ts::This file is outdated. You may have to rerun 'cargo dev generate-playground-options'."
|
||||
- run: git diff --exit-code -- README.md src/checks_gen.rs playground/src/ruff_options.ts
|
||||
|
||||
cargo_fmt:
|
||||
cargo-fmt:
|
||||
name: "cargo fmt"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -79,6 +85,7 @@ jobs:
|
||||
toolchain: nightly-2022-11-01
|
||||
override: true
|
||||
components: clippy
|
||||
target: wasm32-unknown-unknown
|
||||
- uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-cargo
|
||||
@@ -92,8 +99,9 @@ jobs:
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic
|
||||
- run: cargo clippy --workspace --target wasm32-unknown-unknown --all-features -- -D warnings -W clippy::pedantic
|
||||
|
||||
cargo_test:
|
||||
cargo-test:
|
||||
name: "cargo test"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -119,7 +127,32 @@ jobs:
|
||||
- run: cargo test --all
|
||||
- run: cargo test --package ruff --test black_compatibility_test -- --ignored
|
||||
|
||||
maturin_build:
|
||||
wasm-pack-test:
|
||||
name: "wasm-pack test"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-11-01
|
||||
override: true
|
||||
- uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-cargo
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- uses: jetli/wasm-pack-action@v0.4.0
|
||||
- run: wasm-pack test --node
|
||||
|
||||
maturin-build:
|
||||
name: "maturin build"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -131,7 +164,7 @@ jobs:
|
||||
override: true
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
- run: pip install maturin
|
||||
- uses: actions/cache@v3
|
||||
env:
|
||||
|
||||
51
.github/workflows/playground.yaml
vendored
Normal file
51
.github/workflows/playground.yaml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: "[Playground] Release"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_RETRY: 10
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-11-01
|
||||
override: true
|
||||
target: wasm32-unknown-unknown
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: "npm"
|
||||
cache-dependency-path: playground/package-lock.json
|
||||
- uses: jetli/wasm-pack-action@v0.4.0
|
||||
- name: "Run wasm-pack"
|
||||
run: wasm-pack build --target web --out-dir playground/src/pkg
|
||||
- name: "Install Node dependencies"
|
||||
run: npm ci
|
||||
working-directory: playground
|
||||
- name: "Run TypeScript checks"
|
||||
run: npm run check
|
||||
working-directory: playground
|
||||
- name: "Build JavaScript bundle"
|
||||
run: npm run build
|
||||
working-directory: playground
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
env:
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||||
if: ${{ env.CF_API_TOKEN }} != null
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
command: pages publish playground/dist --project-name=ruff --branch ${GITHUB_HEAD_REF} --commit-hash ${GITHUB_SHA}
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.192
|
||||
rev: v0.0.195
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
@@ -48,8 +48,8 @@ prior to merging.
|
||||
There are four phases to adding a new lint rule:
|
||||
|
||||
1. Define the rule in `src/checks.rs`.
|
||||
2. Define the _logic_ for triggering the rule in `src/check_ast.rs` (for AST-based checks),
|
||||
`src/check_tokens.rs` (for token-based checks), or `src/check_lines.rs` (for text-based checks).
|
||||
2. Define the _logic_ for triggering the rule 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).
|
||||
3. Add a test fixture.
|
||||
4. Update the generated files (documentation and generated code).
|
||||
|
||||
@@ -105,7 +105,8 @@ You may also want to add the new configuration option to the `flake8-to-ruff` to
|
||||
responsible for converting `flake8` configuration files to Ruff's TOML format. This logic
|
||||
lives in `flake8_to_ruff/src/converter.rs`.
|
||||
|
||||
To update the documentation for supported configuration options, run `cargo dev generate-options`.
|
||||
Run `cargo dev generate-options` to update the documentation for supported configuration options,
|
||||
and `cargo dev generate-json-schema` to update the JSON schema for `tool.ruff` in `pyproject.toml`.
|
||||
|
||||
## Release process
|
||||
|
||||
|
||||
312
Cargo.lock
generated
312
Cargo.lock
generated
@@ -61,9 +61,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.66"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
||||
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||
|
||||
[[package]]
|
||||
name = "ascii"
|
||||
@@ -86,7 +86,7 @@ version = "2.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa3d466004a8b4cb1bc34044240a2fd29d17607e2e3bd613eb44fd48e8100da3"
|
||||
dependencies = [
|
||||
"bstr 1.0.1",
|
||||
"bstr 1.1.0",
|
||||
"doc-comment",
|
||||
"predicates",
|
||||
"predicates-core",
|
||||
@@ -160,9 +160,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.0.1"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fca0852af221f458706eb0725c03e4ed6c46af9ac98e6a689d5e634215d594dd"
|
||||
checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"once_cell",
|
||||
@@ -193,9 +193,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.77"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -280,9 +280,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.0.29"
|
||||
version = "4.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
|
||||
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
@@ -295,11 +295,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.0.6"
|
||||
version = "4.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7b3c9eae0de7bf8e3f904a5e40612b21fb2e2e566456d177809a48b892d24da"
|
||||
checksum = "10861370d2ba66b0f5989f83ebf35db6421713fd92351790e7fdd6c36774c56b"
|
||||
dependencies = [
|
||||
"clap 4.0.29",
|
||||
"clap 4.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -308,7 +308,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4160b4a4f72ef58bd766bad27c09e6ef1cc9d82a22f6a0f55d152985a4a48e31"
|
||||
dependencies = [
|
||||
"clap 4.0.29",
|
||||
"clap 4.0.32",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
]
|
||||
@@ -319,7 +319,7 @@ version = "4.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46b30e010e669cd021e5004f3be26cff6b7c08d2a8a0d65b48d43a8cc0efd6c3"
|
||||
dependencies = [
|
||||
"clap 4.0.29",
|
||||
"clap 4.0.32",
|
||||
"clap_complete",
|
||||
]
|
||||
|
||||
@@ -422,6 +422,26 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494"
|
||||
dependencies = [
|
||||
"log",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.3"
|
||||
@@ -524,9 +544,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.83"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf"
|
||||
checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@@ -536,9 +556,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.83"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39"
|
||||
checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -551,15 +571,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.83"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12"
|
||||
checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.83"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6"
|
||||
checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -644,6 +664,12 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
@@ -706,9 +732,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.18"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3"
|
||||
checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
@@ -724,10 +750,10 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.192-dev.0"
|
||||
version = "0.0.195-dev.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
"clap 4.0.32",
|
||||
"configparser",
|
||||
"once_cell",
|
||||
"regex",
|
||||
@@ -946,9 +972,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.22.0"
|
||||
version = "1.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "197f4e300af8b23664d4077bf5c40e0afa9ba66a567bb5a51d3def3c7b287d1c"
|
||||
checksum = "e48b08a091dfe5b09a6a9688c468fdd5b4396e92ce09e2eb932f0884b02788a4"
|
||||
dependencies = [
|
||||
"console",
|
||||
"lazy_static",
|
||||
@@ -979,9 +1005,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
||||
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
|
||||
dependencies = [
|
||||
"hermit-abi 0.2.6",
|
||||
"io-lifetimes",
|
||||
@@ -1000,9 +1026,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
|
||||
[[package]]
|
||||
name = "joinery"
|
||||
@@ -1109,9 +1135,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.138"
|
||||
version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "libcst"
|
||||
@@ -1139,9 +1165,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.7"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
|
||||
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
@@ -1154,9 +1180,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
|
||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@@ -1228,6 +1254,12 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "natord"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308d96db8debc727c3fd9744aac51751243420e46edf401010908da7f8d5e57c"
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.4"
|
||||
@@ -1328,11 +1360,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.14.0"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.19",
|
||||
"hermit-abi 0.2.6",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -1379,9 +1411,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.9"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
||||
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
|
||||
|
||||
[[package]]
|
||||
name = "path-absolutize"
|
||||
@@ -1619,9 +1651,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
version = "1.0.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1651,9 +1683,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1747,11 +1779,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.6.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
|
||||
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
@@ -1847,7 +1878,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.192"
|
||||
version = "0.0.195"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1856,12 +1887,15 @@ dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
"cachedir",
|
||||
"cfg-if 1.0.0",
|
||||
"chrono",
|
||||
"clap 4.0.29",
|
||||
"clap 4.0.32",
|
||||
"clap_complete_command",
|
||||
"clearscreen",
|
||||
"colored",
|
||||
"common-path",
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"criterion",
|
||||
"dirs 4.0.0",
|
||||
"fern",
|
||||
@@ -1872,8 +1906,10 @@ dependencies = [
|
||||
"ignore",
|
||||
"insta",
|
||||
"itertools",
|
||||
"js-sys",
|
||||
"libcst",
|
||||
"log",
|
||||
"natord",
|
||||
"nohash-hasher",
|
||||
"notify",
|
||||
"num-bigint",
|
||||
@@ -1888,7 +1924,10 @@ dependencies = [
|
||||
"rustpython-ast",
|
||||
"rustpython-common",
|
||||
"rustpython-parser",
|
||||
"schemars",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
"strum",
|
||||
@@ -1900,14 +1939,16 @@ dependencies = [
|
||||
"update-informer",
|
||||
"ureq",
|
||||
"walkdir",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.192"
|
||||
version = "0.0.195"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
"clap 4.0.32",
|
||||
"codegen",
|
||||
"itertools",
|
||||
"libcst",
|
||||
@@ -1916,13 +1957,16 @@ dependencies = [
|
||||
"rustpython-ast",
|
||||
"rustpython-common",
|
||||
"rustpython-parser",
|
||||
"schemars",
|
||||
"serde_json",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.192"
|
||||
version = "0.0.195"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1938,9 +1982,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.4"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23"
|
||||
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -1965,7 +2009,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=247e815880766d556ef1ca6f0af69daf1a5fe59a#247e815880766d556ef1ca6f0af69daf1a5fe59a"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-common",
|
||||
@@ -1975,7 +2019,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-common"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=247e815880766d556ef1ca6f0af69daf1a5fe59a#247e815880766d556ef1ca6f0af69daf1a5fe59a"
|
||||
dependencies = [
|
||||
"ascii",
|
||||
"cfg-if 1.0.0",
|
||||
@@ -1998,7 +2042,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=247e815880766d556ef1ca6f0af69daf1a5fe59a#247e815880766d556ef1ca6f0af69daf1a5fe59a"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
@@ -2015,7 +2059,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=247e815880766d556ef1ca6f0af69daf1a5fe59a#247e815880766d556ef1ca6f0af69daf1a5fe59a"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -2039,15 +2083,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.9"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
|
||||
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
@@ -2058,6 +2102,36 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"schemars_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
@@ -2066,9 +2140,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
|
||||
checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
@@ -2082,24 +2156,46 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.14"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
|
||||
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.148"
|
||||
version = "1.0.151"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
|
||||
checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.148"
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
|
||||
checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.151"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2108,9 +2204,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.89"
|
||||
version = "1.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
|
||||
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -2164,9 +2260,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "str_indices"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d9199fa80c817e074620be84374a520062ebac833f358d74b37060ce4a0f2c0"
|
||||
checksum = "5f026164926842ec52deb1938fae44f83dfdb82d0a5b0270c5bd5935ab74d6dd"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
@@ -2211,9 +2307,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.105"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2318,18 +2414,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2403,9 +2499,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.9"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
|
||||
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -2492,9 +2588,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
@@ -2663,6 +2759,18 @@ dependencies = [
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
@@ -2692,6 +2800,30 @@ version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d2fff962180c3fadf677438054b1db62bee4aa32af26a45388af07d1287e1d"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
"scoped-tls",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-bindgen-test-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4683da3dfc016f704c9f82cf401520c4f1cb3ee440f7f52b3d6ac29506a49ca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.60"
|
||||
@@ -2714,9 +2846,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.22.5"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be"
|
||||
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
40
Cargo.toml
40
Cargo.toml
@@ -6,12 +6,13 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.192"
|
||||
version = "0.0.195"
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
[lib]
|
||||
name = "ruff"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||
@@ -20,9 +21,10 @@ atty = { version = "0.2.14" }
|
||||
bincode = { version = "1.3.3" }
|
||||
bitflags = { version = "1.3.2" }
|
||||
cachedir = { version = "0.3.0" }
|
||||
cfg-if = { version = "1.0.0" }
|
||||
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.0.1", features = ["derive"] }
|
||||
clap_complete_command = "0.4.0"
|
||||
clap_complete_command = { version = "0.4.0" }
|
||||
colored = { version = "2.0.0" }
|
||||
common-path = { version = "1.0.0" }
|
||||
dirs = { version = "4.0.0" }
|
||||
@@ -34,20 +36,22 @@ ignore = { version = "0.4.18" }
|
||||
itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
|
||||
log = { version = "0.4.17" }
|
||||
natord = { version = "1.0.9" }
|
||||
nohash-hasher = { version = "0.2.0" }
|
||||
notify = { version = "5.0.0" }
|
||||
num-bigint = { version = "0.4.3" }
|
||||
once_cell = { version = "1.16.0" }
|
||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
||||
quick-junit = { version = "0.3.2" }
|
||||
rayon = { version = "1.5.3" }
|
||||
regex = { version = "1.6.0" }
|
||||
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
|
||||
ruff_macros = { version = "0.0.192", path = "ruff_macros" }
|
||||
ruff_macros = { version = "0.0.195", path = "ruff_macros" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "247e815880766d556ef1ca6f0af69daf1a5fe59a" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "247e815880766d556ef1ca6f0af69daf1a5fe59a" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "247e815880766d556ef1ca6f0af69daf1a5fe59a" }
|
||||
schemars = { version = "0.8.11" }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = { version = "1.0.87" }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
@@ -56,23 +60,32 @@ strum_macros = { version = "0.24.3" }
|
||||
textwrap = { version = "0.16.0" }
|
||||
titlecase = { version = "2.2.1" }
|
||||
toml = { version = "0.5.9" }
|
||||
update-informer = { version = "0.5.0", default-features = false, features = ["pypi"], optional = true }
|
||||
walkdir = { version = "2.3.2" }
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||
clearscreen = { version = "1.0.10" } # uses which
|
||||
clearscreen = { version = "1.0.10" }
|
||||
rayon = { version = "1.5.3" }
|
||||
update-informer = { version = "0.5.0", default-features = false, features = ["pypi"], optional = true }
|
||||
|
||||
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
|
||||
# For (future) wasm-pack support
|
||||
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
|
||||
getrandom = { version = "0.2.7", features = ["js"] }
|
||||
console_error_panic_hook = { version = "0.1.7" }
|
||||
console_log = { version = "0.2.0" }
|
||||
serde-wasm-bindgen = { version = "0.4" }
|
||||
js-sys = { version = "0.3.60" }
|
||||
wasm-bindgen = { version = "0.2.83" }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = { version = "2.0.4" }
|
||||
criterion = { version = "0.4.0" }
|
||||
insta = { version = "1.19.1", features = ["yaml"] }
|
||||
test-case = { version = "2.2.2" }
|
||||
ureq = { version = "2.5.0", features = [] }
|
||||
wasm-bindgen-test = { version = "0.3.33" }
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
|
||||
assert_cmd = { version = "2.0.4" }
|
||||
criterion = { version = "0.4.0" }
|
||||
|
||||
[features]
|
||||
default = ["update-informer"]
|
||||
@@ -90,6 +103,11 @@ opt-level = 3
|
||||
[profile.dev.package.similar]
|
||||
opt-level = 3
|
||||
|
||||
# Reduce complexity of a parser function that would trigger a locals limit in a wasm tool.
|
||||
# https://github.com/bytecodealliance/wasm-tools/blob/b5c3d98e40590512a3b12470ef358d5c7b983b15/crates/wasmparser/src/limits.rs#L29
|
||||
[profile.dev.package.rustpython-parser]
|
||||
opt-level = 1
|
||||
|
||||
[[bench]]
|
||||
name = "source_code_locator"
|
||||
harness = false
|
||||
|
||||
427
README.md
427
README.md
@@ -17,7 +17,7 @@ An extremely fast Python linter, written in Rust.
|
||||
|
||||
- ⚡️ 10-100x faster than existing linters
|
||||
- 🐍 Installable via `pip`
|
||||
- 🤝 Python 3.10 compatibility
|
||||
- 🤝 Python 3.11 compatibility
|
||||
- 🛠️ `pyproject.toml` support
|
||||
- 📦 Built-in caching, to avoid re-analyzing unchanged files
|
||||
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
@@ -162,7 +162,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.0.192'
|
||||
rev: 'v0.0.195'
|
||||
hooks:
|
||||
- id: ruff
|
||||
# Respect `exclude` and `extend-exclude` settings.
|
||||
@@ -214,13 +214,6 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
# Assume Python 3.10.
|
||||
target-version = "py310"
|
||||
|
||||
[tool.ruff.flake8-import-conventions.aliases]
|
||||
altair = "alt"
|
||||
"matplotlib.pyplot" = "plt"
|
||||
numpy = "np"
|
||||
pandas = "pd"
|
||||
seaborn = "sns"
|
||||
|
||||
[tool.ruff.mccabe]
|
||||
# Unlike Flake8, default to a complexity level of 10.
|
||||
max-complexity = 10
|
||||
@@ -259,6 +252,27 @@ select = ["E", "F", "Q"]
|
||||
docstring-quotes = "double"
|
||||
```
|
||||
|
||||
As an alternative to `pyproject.toml`, Ruff will also respect a `ruff.toml` file, which implements
|
||||
an equivalent schema (though the `[tool.ruff]` hierarchy can be omitted). For example, the above
|
||||
`pyproject.toml` described above would be represented via the following `ruff.toml`:
|
||||
|
||||
```toml
|
||||
# Enable Pyflakes and pycodestyle rules.
|
||||
select = ["E", "F"]
|
||||
|
||||
# Never enforce `E501` (line length violations).
|
||||
ignore = ["E501"]
|
||||
|
||||
# Always autofix, but never try to fix `F401` (unused imports).
|
||||
fix = true
|
||||
unfixable = ["F401"]
|
||||
|
||||
# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
|
||||
[per-file-ignores]
|
||||
"__init__.py" = ["E402"]
|
||||
"path/to/file.py" = ["E402"]
|
||||
```
|
||||
|
||||
For a full list of configurable options, see the [API reference](#reference).
|
||||
|
||||
Some common configuration settings can be provided via the command-line:
|
||||
@@ -279,7 +293,7 @@ Arguments:
|
||||
|
||||
Options:
|
||||
--config <CONFIG>
|
||||
Path to the `pyproject.toml` file to use for configuration
|
||||
Path to the `pyproject.toml` or `ruff.toml` file to use for configuration
|
||||
-v, --verbose
|
||||
Enable verbose logging
|
||||
-q, --quiet
|
||||
@@ -292,6 +306,8 @@ Options:
|
||||
Run in watch mode by re-running whenever files change
|
||||
--fix
|
||||
Attempt to automatically fix lint errors
|
||||
--fix-only
|
||||
Fix any fixable lint errors, but don't report on leftover violations. Implies `--fix`
|
||||
-n, --no-cache
|
||||
Disable cache reads
|
||||
--select <SELECT>
|
||||
@@ -318,6 +334,8 @@ Options:
|
||||
Show violations with source code
|
||||
--respect-gitignore
|
||||
Respect file exclusions via `.gitignore` and other standard ignore files
|
||||
--force-exclude
|
||||
Enforce exclusions, even for paths passed to Ruff directly on the command-line
|
||||
--show-files
|
||||
See the files Ruff will be run against with the current settings
|
||||
--show-settings
|
||||
@@ -336,6 +354,8 @@ Options:
|
||||
The name of the file when passing it through stdin
|
||||
--explain <EXPLAIN>
|
||||
Explain a rule
|
||||
--cache-dir <CACHE_DIR>
|
||||
Path to the cache directory
|
||||
-h, --help
|
||||
Print help information
|
||||
-V, --version
|
||||
@@ -379,6 +399,9 @@ extend = "../pyproject.toml"
|
||||
line-length = 100
|
||||
```
|
||||
|
||||
All of the above rules apply equivalently to `ruff.toml` files. If Ruff detects both a `ruff.toml`
|
||||
and `pyproject.toml` file, it will defer to the `ruff.toml`.
|
||||
|
||||
### Python file discovery
|
||||
|
||||
When passed a path on the command-line, Ruff will automatically discover all Python files in that
|
||||
@@ -536,8 +559,8 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
|
||||
| E743 | AmbiguousFunctionName | Ambiguous function name: `...` | |
|
||||
| E902 | IOError | IOError: `...` | |
|
||||
| E999 | SyntaxError | SyntaxError: `...` | |
|
||||
| W292 | NoNewLineAtEndOfFile | No newline at end of file | |
|
||||
| W605 | InvalidEscapeSequence | Invalid escape sequence: '\c' | |
|
||||
| W292 | NoNewLineAtEndOfFile | No newline at end of file | 🛠 |
|
||||
| W605 | InvalidEscapeSequence | Invalid escape sequence: '\c' | 🛠 |
|
||||
|
||||
### mccabe (C90)
|
||||
|
||||
@@ -629,6 +652,8 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
|
||||
| UP015 | RedundantOpenModes | Unnecessary open mode parameters | 🛠 |
|
||||
| UP016 | RemoveSixCompat | Unnecessary `six` compatibility usage | 🛠 |
|
||||
| UP017 | DatetimeTimezoneUTC | Use `datetime.UTC` alias | 🛠 |
|
||||
| UP018 | NativeLiterals | Unnecessary call to `str` and `bytes` | 🛠 |
|
||||
| UP019 | TypingTextStrAlias | `typing.Text` is deprecated, use `str` | 🛠 |
|
||||
|
||||
### pep8-naming (N)
|
||||
|
||||
@@ -952,7 +977,7 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI.
|
||||
| RUF001 | AmbiguousUnicodeCharacterString | String contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
|
||||
| RUF002 | AmbiguousUnicodeCharacterDocstring | Docstring contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
|
||||
| RUF003 | AmbiguousUnicodeCharacterComment | Comment contains ambiguous unicode character '𝐁' (did you mean 'B'?) | |
|
||||
| RUF100 | UnusedNOQA | Unused `noqa` directive | 🛠 |
|
||||
| RUF100 | UnusedNOQA | Unused blanket `noqa` directive | 🛠 |
|
||||
|
||||
<!-- End auto-generated sections. -->
|
||||
|
||||
@@ -1018,19 +1043,17 @@ local configs = require 'lspconfig.configs'
|
||||
if not configs.ruff_lsp then
|
||||
configs.ruff_lsp = {
|
||||
default_config = {
|
||||
cmd = { "ruff-lsp" },
|
||||
filetypes = {'python'},
|
||||
root_dir = require('lspconfig').util.find_git_ancestor,
|
||||
settings = {
|
||||
ruff_lsp = {
|
||||
-- Any extra CLI arguments for `ruff` go here.
|
||||
args = {}
|
||||
cmd = { 'ruff-lsp' },
|
||||
filetypes = { 'python' },
|
||||
root_dir = require('lspconfig').util.find_git_ancestor,
|
||||
init_options = {
|
||||
settings = {
|
||||
args = {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
require('lspconfig').ruff_lsp.setup {
|
||||
on_attach = on_attach,
|
||||
}
|
||||
@@ -1040,6 +1063,8 @@ Upon successful installation, you should see Ruff's diagnostics surfaced directl
|
||||
|
||||

|
||||
|
||||
To use `ruff-lsp` with other editors, including Sublime Text and Helix, see the [`ruff-lsp` documentation](https://github.com/charliermarsh/ruff-lsp#installation-and-usage).
|
||||
|
||||
### Language Server Protocol (Unofficial)
|
||||
|
||||
Ruff is also available as the [`python-lsp-ruff`](https://github.com/python-lsp/python-lsp-ruff)
|
||||
@@ -1078,24 +1103,15 @@ require'lspconfig'.pylsp.setup {
|
||||
}
|
||||
```
|
||||
|
||||
### PyCharm
|
||||
|
||||
Ruff can be installed as an [External Tool](https://www.jetbrains.com/help/pycharm/configuring-third-party-tools.html)
|
||||
in PyCharm. Open the Preferences pane, then navigate to "Tools", then "External Tools". From there,
|
||||
add a new tool with the following configuration:
|
||||
|
||||

|
||||
|
||||
Ruff should then appear as a runnable action:
|
||||
|
||||

|
||||
|
||||
### Vim & Neovim
|
||||
|
||||
Ruff can be integrated into any editor that supports the Language Server Protocol via [`ruff-lsp`](https://github.com/charliermarsh/ruff-lsp)
|
||||
(see: [Language Server Protocol](#language-server-protocol-official)).
|
||||
(see: [Language Server Protocol](#language-server-protocol-official)), including Vim and Neovim.
|
||||
|
||||
Ruff is also available as part of the [coc-pyright](https://github.com/fannheyward/coc-pyright)
|
||||
It's recommended that you use [`ruff-lsp`](https://github.com/charliermarsh/ruff-lsp), the
|
||||
officially supported LSP server for Ruff.
|
||||
|
||||
However, Ruff is also available as part of the [coc-pyright](https://github.com/fannheyward/coc-pyright)
|
||||
extension for `coc.nvim`.
|
||||
|
||||
<details>
|
||||
@@ -1112,7 +1128,6 @@ tools:
|
||||
format-command: 'ruff --stdin-filename ${INPUT} --config ~/myconfigs/linters/ruff.toml --fix --exit-zero --quiet -'
|
||||
format-stdin: true
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -1149,9 +1164,26 @@ null_ls.setup({
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### PyCharm (External Tool)
|
||||
|
||||
Ruff can be installed as an [External Tool](https://www.jetbrains.com/help/pycharm/configuring-third-party-tools.html)
|
||||
in PyCharm. Open the Preferences pane, then navigate to "Tools", then "External Tools". From there,
|
||||
add a new tool with the following configuration:
|
||||
|
||||

|
||||
|
||||
Ruff should then appear as a runnable action:
|
||||
|
||||

|
||||
|
||||
### PyCharm (Unofficial)
|
||||
|
||||
Ruff is also available as the [Ruff](https://plugins.jetbrains.com/plugin/20574-ruff) plugin on the
|
||||
IntelliJ Marketplace (maintained by @koxudaxi).
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
GitHub Actions has everything you need to run Ruff out-of-the-box:
|
||||
@@ -1167,7 +1199,7 @@ jobs:
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
@@ -1225,7 +1257,7 @@ natively, including:
|
||||
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (17/33)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (19/33)
|
||||
- [`yesqa`](https://github.com/asottile/yesqa)
|
||||
|
||||
Note that, in some cases, Ruff uses different error code prefixes than would be found in the
|
||||
@@ -1282,7 +1314,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
Ruff can also replace [`isort`](https://pypi.org/project/isort/),
|
||||
[`yesqa`](https://github.com/asottile/yesqa), [`eradicate`](https://pypi.org/project/eradicate/),
|
||||
[`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10), and a subset of the rules
|
||||
implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (17/33).
|
||||
implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (19/33).
|
||||
|
||||
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.
|
||||
|
||||
@@ -1569,8 +1601,8 @@ Summary
|
||||
|
||||
#### [`allowed-confusables`](#allowed-confusables)
|
||||
|
||||
A list of allowed "confusable" Unicode characters to ignore when enforcing `RUF001`,
|
||||
`RUF002`, and `RUF003`.
|
||||
A list of allowed "confusable" Unicode characters to ignore when
|
||||
enforcing `RUF001`, `RUF002`, and `RUF003`.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
@@ -1587,11 +1619,37 @@ allowed-confusables = ["−", "ρ", "∗"]
|
||||
|
||||
---
|
||||
|
||||
#### [`cache-dir`](#cache-dir)
|
||||
|
||||
A path to the cache directory.
|
||||
|
||||
By default, Ruff stores cache results in a `.ruff_cache` directory in
|
||||
the current project root.
|
||||
|
||||
However, Ruff will also respect the `RUFF_CACHE_DIR` environment
|
||||
variable, which takes precedence over that default.
|
||||
|
||||
This setting will override even the `RUFF_CACHE_DIR` environment
|
||||
variable, if set.
|
||||
|
||||
**Default value**: `.ruff_cache`
|
||||
|
||||
**Type**: `PathBuf`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
cache-dir = "~/.cache/ruff"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`dummy-variable-rgx`](#dummy-variable-rgx)
|
||||
|
||||
A regular expression used to identify "dummy" variables, or those which should be
|
||||
ignored when evaluating (e.g.) unused-variable checks. The default expression matches
|
||||
`_`, `__`, and `_var`, but not `_var_`.
|
||||
A regular expression used to identify "dummy" variables, or those which
|
||||
should be ignored when evaluating (e.g.) unused-variable checks. The
|
||||
default expression matches `_`, `__`, and `_var`, but not `_var_`.
|
||||
|
||||
**Default value**: `"^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"`
|
||||
|
||||
@@ -1613,15 +1671,16 @@ A list of file patterns to exclude from linting.
|
||||
|
||||
Exclusions are based on globs, and can be either:
|
||||
|
||||
- Single-path patterns, like `.mypy_cache` (to exclude any directory named `.mypy_cache` in the
|
||||
tree), `foo.py` (to exclude any file named `foo.py`), or `foo_*.py` (to exclude any file matching
|
||||
`foo_*.py` ).
|
||||
- Relative patterns, like `directory/foo.py` (to exclude that specific file) or `directory/*.py`
|
||||
(to exclude any Python files in `directory`). Note that these paths are relative to the
|
||||
project root (e.g., the directory containing your `pyproject.toml`).
|
||||
- Single-path patterns, like `.mypy_cache` (to exclude any directory
|
||||
named `.mypy_cache` in the tree), `foo.py` (to exclude any file named
|
||||
`foo.py`), or `foo_*.py` (to exclude any file matching `foo_*.py` ).
|
||||
- Relative patterns, like `directory/foo.py` (to exclude that specific
|
||||
file) or `directory/*.py` (to exclude any Python files in
|
||||
`directory`). Note that these paths are relative to the project root
|
||||
(e.g., the directory containing your `pyproject.toml`).
|
||||
|
||||
Note that you'll typically want to use [`extend-exclude`](#extend-exclude) to modify
|
||||
the excluded paths.
|
||||
Note that you'll typically want to use
|
||||
[`extend-exclude`](#extend-exclude) to modify the excluded paths.
|
||||
|
||||
**Default value**: `[".bzr", ".direnv", ".eggs", ".git", ".hg", ".mypy_cache", ".nox", ".pants.d", ".ruff_cache", ".svn", ".tox", ".venv", "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", "venv"]`
|
||||
|
||||
@@ -1638,12 +1697,13 @@ exclude = [".venv"]
|
||||
|
||||
#### [`extend`](#extend)
|
||||
|
||||
A path to a local `pyproject.toml` file to merge into this configuration. User home
|
||||
directory and environment variables will be expanded.
|
||||
A path to a local `pyproject.toml` file to merge into this
|
||||
configuration. User home directory and environment variables will be
|
||||
expanded.
|
||||
|
||||
To resolve the current `pyproject.toml` file, Ruff will first resolve this base
|
||||
configuration file, then merge in any properties defined in the current configuration
|
||||
file.
|
||||
To resolve the current `pyproject.toml` file, Ruff will first resolve
|
||||
this base configuration file, then merge in any properties defined
|
||||
in the current configuration file.
|
||||
|
||||
**Default value**: `None`
|
||||
|
||||
@@ -1663,7 +1723,8 @@ line-length = 100
|
||||
|
||||
#### [`extend-exclude`](#extend-exclude)
|
||||
|
||||
A list of file patterns to omit from linting, in addition to those specified by `exclude`.
|
||||
A list of file patterns to omit from linting, in addition to those
|
||||
specified by `exclude`.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
@@ -1681,7 +1742,8 @@ extend-exclude = ["tests", "src/bad.py"]
|
||||
|
||||
#### [`extend-ignore`](#extend-ignore)
|
||||
|
||||
A list of check code prefixes to ignore, in addition to those specified by `ignore`.
|
||||
A list of check code prefixes to ignore, in addition to those specified
|
||||
by `ignore`.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
@@ -1699,7 +1761,8 @@ extend-ignore = ["F841"]
|
||||
|
||||
#### [`extend-select`](#extend-select)
|
||||
|
||||
A list of check code prefixes to enable, in addition to those specified by `select`.
|
||||
A list of check code prefixes to enable, in addition to those specified
|
||||
by `select`.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
@@ -1717,9 +1780,10 @@ extend-select = ["B", "Q"]
|
||||
|
||||
#### [`external`](#external)
|
||||
|
||||
A list of check codes that are unsupported by Ruff, but should be preserved when (e.g.)
|
||||
validating `# noqa` directives. Useful for retaining `# noqa` directives that cover plugins not
|
||||
yet implemented in Ruff.
|
||||
A list of check codes that are unsupported by Ruff, but should be
|
||||
preserved when (e.g.) validating `# noqa` directives. Useful for
|
||||
retaining `# noqa` directives that cover plugins not yet implemented
|
||||
in Ruff.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
@@ -1754,6 +1818,23 @@ fix = true
|
||||
|
||||
---
|
||||
|
||||
#### [`fix-only`](#fix-only)
|
||||
|
||||
Like `fix`, but disables reporting on leftover violation. Implies `fix`.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
fix-only = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`fixable`](#fixable)
|
||||
|
||||
A list of check code prefixes to consider autofix-able.
|
||||
@@ -1774,14 +1855,16 @@ fixable = ["E", "F"]
|
||||
|
||||
#### [`force-exclude`](#force-exclude)
|
||||
|
||||
Whether to enforce `exclude` and `extend-exclude` patterns, even for paths that are
|
||||
passed to Ruff explicitly. Typically, Ruff will lint any paths passed in directly, even
|
||||
if they would typically be excluded. Setting `force-exclude = true` will cause Ruff to
|
||||
Whether to enforce `exclude` and `extend-exclude` patterns, even for
|
||||
paths that are passed to Ruff explicitly. Typically, Ruff will lint
|
||||
any paths passed in directly, even if they would typically be
|
||||
excluded. Setting `force-exclude = true` will cause Ruff to
|
||||
respect these exclusions unequivocally.
|
||||
|
||||
This is useful for [`pre-commit`](https://pre-commit.com/), which explicitly passes all
|
||||
changed files to the [`ruff-pre-commit`](https://github.com/charliermarsh/ruff-pre-commit)
|
||||
plugin, regardless of whether they're marked as excluded by Ruff's own settings.
|
||||
plugin, regardless of whether they're marked as excluded by Ruff's own
|
||||
settings.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
@@ -1798,9 +1881,10 @@ force-exclude = true
|
||||
|
||||
#### [`format`](#format)
|
||||
|
||||
The style in which violation messages should be formatted: `"text"` (default),
|
||||
`"grouped"` (group messages by file), `"json"` (machine-readable), `"junit"`
|
||||
(machine-readable XML), or `"github"` (GitHub Actions annotations).
|
||||
The style in which violation messages should be formatted: `"text"`
|
||||
(default), `"grouped"` (group messages by file), `"json"`
|
||||
(machine-readable), `"junit"` (machine-readable XML), or `"github"`
|
||||
(GitHub Actions annotations).
|
||||
|
||||
**Default value**: `"text"`
|
||||
|
||||
@@ -1818,11 +1902,13 @@ format = "grouped"
|
||||
|
||||
#### [`ignore`](#ignore)
|
||||
|
||||
A list of check code prefixes to ignore. Prefixes can specify exact checks (like
|
||||
`F841`), entire categories (like `F`), or anything in between.
|
||||
A list of check code prefixes to ignore. Prefixes can specify exact
|
||||
checks (like `F841`), entire categories (like `F`), or anything in
|
||||
between.
|
||||
|
||||
When breaking ties between enabled and disabled checks (via `select` and `ignore`,
|
||||
respectively), more specific prefixes override less specific prefixes.
|
||||
When breaking ties between enabled and disabled checks (via `select` and
|
||||
`ignore`, respectively), more specific prefixes override less
|
||||
specific prefixes.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
@@ -1840,10 +1926,11 @@ ignore = ["F841"]
|
||||
|
||||
#### [`ignore-init-module-imports`](#ignore-init-module-imports)
|
||||
|
||||
Avoid automatically removing unused imports in `__init__.py` files. Such imports will
|
||||
still be +flagged, but with a dedicated message suggesting that the import is either
|
||||
added to the module' +`__all__` symbol, or re-exported with a redundant alias (e.g.,
|
||||
`import os as os`).
|
||||
Avoid automatically removing unused imports in `__init__.py` files. Such
|
||||
imports will still be +flagged, but with a dedicated message
|
||||
suggesting that the import is either added to the module' +`__all__`
|
||||
symbol, or re-exported with a redundant alias (e.g., `import os as
|
||||
os`).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
@@ -1860,7 +1947,8 @@ ignore-init-module-imports = true
|
||||
|
||||
#### [`line-length`](#line-length)
|
||||
|
||||
The line length to use when enforcing long-lines violations (like E501).
|
||||
The line length to use when enforcing long-lines violations (like
|
||||
`E501`).
|
||||
|
||||
**Default value**: `88`
|
||||
|
||||
@@ -1878,8 +1966,8 @@ line-length = 120
|
||||
|
||||
#### [`per-file-ignores`](#per-file-ignores)
|
||||
|
||||
A list of mappings from file pattern to check code prefixes to exclude, when considering
|
||||
any matching files.
|
||||
A list of mappings from file pattern to check code prefixes to exclude,
|
||||
when considering any matching files.
|
||||
|
||||
**Default value**: `{}`
|
||||
|
||||
@@ -1897,10 +1985,30 @@ any matching files.
|
||||
|
||||
---
|
||||
|
||||
#### [`required-version`](#required-version)
|
||||
|
||||
Require a specific version of Ruff to be running (useful for unifying
|
||||
results across many environments, e.g., with a `pyproject.toml`
|
||||
file).
|
||||
|
||||
**Default value**: `None`
|
||||
|
||||
**Type**: `String`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
required-version = "0.0.193"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`respect-gitignore`](#respect-gitignore)
|
||||
|
||||
Whether to automatically exclude files that are ignored by `.ignore`, `.gitignore`,
|
||||
`.git/info/exclude`, and global `gitignore` files. Enabled by default.
|
||||
Whether to automatically exclude files that are ignored by `.ignore`,
|
||||
`.gitignore`, `.git/info/exclude`, and global `gitignore` files.
|
||||
Enabled by default.
|
||||
|
||||
**Default value**: `true`
|
||||
|
||||
@@ -1917,11 +2025,13 @@ respect_gitignore = false
|
||||
|
||||
#### [`select`](#select)
|
||||
|
||||
A list of check code prefixes to enable. Prefixes can specify exact checks (like
|
||||
`F841`), entire categories (like `F`), or anything in between.
|
||||
A list of check code prefixes to enable. Prefixes can specify exact
|
||||
checks (like `F841`), entire categories (like `F`), or anything in
|
||||
between.
|
||||
|
||||
When breaking ties between enabled and disabled checks (via `select` and `ignore`,
|
||||
respectively), more specific prefixes override less specific prefixes.
|
||||
When breaking ties between enabled and disabled checks (via `select` and
|
||||
`ignore`, respectively), more specific prefixes override less
|
||||
specific prefixes.
|
||||
|
||||
**Default value**: `["E", "F"]`
|
||||
|
||||
@@ -1939,8 +2049,8 @@ select = ["E", "F", "B", "Q"]
|
||||
|
||||
#### [`show-source`](#show-source)
|
||||
|
||||
Whether to show source code snippets when reporting lint error violations (overridden by
|
||||
the `--show-source` command-line flag).
|
||||
Whether to show source code snippets when reporting lint error
|
||||
violations (overridden by the `--show-source` command-line flag).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
@@ -1958,7 +2068,8 @@ show-source = true
|
||||
|
||||
#### [`src`](#src)
|
||||
|
||||
The source code paths to consider, e.g., when resolving first- vs. third-party imports.
|
||||
The source code paths to consider, e.g., when resolving first- vs.
|
||||
third-party imports.
|
||||
|
||||
As an example: given a Python package structure like:
|
||||
|
||||
@@ -1972,13 +2083,15 @@ my_package/
|
||||
bar.py
|
||||
```
|
||||
|
||||
The `src` directory should be included in `source` (e.g., `source = ["src"]`), such that
|
||||
when resolving imports, `my_package.foo` is considered a first-party import.
|
||||
The `src` directory should be included in `source` (e.g., `source =
|
||||
["src"]`), such that when resolving imports, `my_package.foo` is
|
||||
considered a first-party import.
|
||||
|
||||
This field supports globs. For example, if you have a series of Python packages in
|
||||
a `python_modules` directory, `src = ["python_modules/*"]` would expand to incorporate
|
||||
all of the packages in that directory. User home directory and environment variables
|
||||
will also be expanded.
|
||||
This field supports globs. For example, if you have a series of Python
|
||||
packages in a `python_modules` directory, `src =
|
||||
["python_modules/*"]` would expand to incorporate all of the
|
||||
packages in that directory. User home directory and environment
|
||||
variables will also be expanded.
|
||||
|
||||
**Default value**: `["."]`
|
||||
|
||||
@@ -1996,9 +2109,10 @@ src = ["src", "test"]
|
||||
|
||||
#### [`target-version`](#target-version)
|
||||
|
||||
The Python version to target, e.g., when considering automatic code upgrades, like
|
||||
rewriting type annotations. Note that the target version will _not_ be inferred from the
|
||||
_current_ Python version, and instead must be specified explicitly (as seen below).
|
||||
The Python version to target, e.g., when considering automatic code
|
||||
upgrades, like rewriting type annotations. Note that the target
|
||||
version will _not_ be inferred from the _current_ Python version,
|
||||
and instead must be specified explicitly (as seen below).
|
||||
|
||||
**Default value**: `"py310"`
|
||||
|
||||
@@ -2036,7 +2150,8 @@ unfixable = ["F401"]
|
||||
|
||||
#### [`allow-star-arg-any`](#allow-star-arg-any)
|
||||
|
||||
Whether to suppress `ANN401` for dynamically typed `*args` and `**kwargs` arguments.
|
||||
Whether to suppress `ANN401` for dynamically typed `*args` and
|
||||
`**kwargs` arguments.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
@@ -2053,8 +2168,8 @@ allow-star-arg-any = true
|
||||
|
||||
#### [`mypy-init-return`](#mypy-init-return)
|
||||
|
||||
Whether to allow the omission of a return type hint for `__init__` if at least one
|
||||
argument is annotated.
|
||||
Whether to allow the omission of a return type hint for `__init__` if at
|
||||
least one argument is annotated.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
@@ -2071,8 +2186,8 @@ mypy-init-return = true
|
||||
|
||||
#### [`suppress-dummy-args`](#suppress-dummy-args)
|
||||
|
||||
Whether to suppress `ANN000`-level errors for arguments matching the "dummy" variable
|
||||
regex (like `_`).
|
||||
Whether to suppress `ANN000`-level errors for arguments matching the
|
||||
"dummy" variable regex (like `_`).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
@@ -2089,11 +2204,12 @@ suppress-dummy-args = true
|
||||
|
||||
#### [`suppress-none-returning`](#suppress-none-returning)
|
||||
|
||||
Whether to suppress `ANN200`-level errors for functions that meet either of the
|
||||
following criteria:
|
||||
Whether to suppress `ANN200`-level errors for functions that meet either
|
||||
of the following criteria:
|
||||
|
||||
- Contain no `return` statement.
|
||||
- Explicit `return` statement(s) all return `None` (explicitly or implicitly).
|
||||
- Explicit `return` statement(s) all return `None` (explicitly or
|
||||
implicitly).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
@@ -2112,8 +2228,8 @@ suppress-none-returning = true
|
||||
|
||||
#### [`extend-immutable-calls`](#extend-immutable-calls)
|
||||
|
||||
Additional callable functions to consider "immutable" when evaluating, e.g.,
|
||||
`no-mutable-default-argument` checks (`B006`).
|
||||
Additional callable functions to consider "immutable" when evaluating,
|
||||
e.g., `no-mutable-default-argument` checks (`B006`).
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
@@ -2152,7 +2268,8 @@ max-string-length = 20
|
||||
|
||||
#### [`aliases`](#aliases)
|
||||
|
||||
The conventional aliases for imports. These aliases can be extended by the `extend_aliases` option.
|
||||
The conventional aliases for imports. These aliases can be extended by
|
||||
the `extend_aliases` option.
|
||||
|
||||
**Default value**: `{"altair": "alt", "matplotlib.pyplot": "plt", "numpy": "np", "pandas": "pd", "seaborn": "sns"}`
|
||||
|
||||
@@ -2174,7 +2291,8 @@ seaborn = "sns"
|
||||
|
||||
#### [`extend-aliases`](#extend-aliases)
|
||||
|
||||
A mapping of modules to their conventional import aliases. These aliases will be added to the `aliases` mapping.
|
||||
A mapping of modules to their conventional import aliases. These aliases
|
||||
will be added to the `aliases` mapping.
|
||||
|
||||
**Default value**: `{}`
|
||||
|
||||
@@ -2194,8 +2312,8 @@ A mapping of modules to their conventional import aliases. These aliases will be
|
||||
|
||||
#### [`avoid-escape`](#avoid-escape)
|
||||
|
||||
Whether to avoid using single quotes if a string contains single quotes, or vice-versa
|
||||
with double quotes, as per [PEP8](https://peps.python.org/pep-0008/#string-quotes).
|
||||
Whether to avoid using single quotes if a string contains single quotes,
|
||||
or vice-versa with double quotes, as per [PEP8](https://peps.python.org/pep-0008/#string-quotes).
|
||||
This minimizes the need to escape quotation marks within strings.
|
||||
|
||||
**Default value**: `true`
|
||||
@@ -2214,7 +2332,8 @@ avoid-escape = false
|
||||
|
||||
#### [`docstring-quotes`](#docstring-quotes)
|
||||
|
||||
Quote style to prefer for docstrings (either "single" (`'`) or "double" (`"`)).
|
||||
Quote style to prefer for docstrings (either "single" (`'`) or "double"
|
||||
(`"`)).
|
||||
|
||||
**Default value**: `"double"`
|
||||
|
||||
@@ -2231,7 +2350,8 @@ docstring-quotes = "single"
|
||||
|
||||
#### [`inline-quotes`](#inline-quotes)
|
||||
|
||||
Quote style to prefer for inline strings (either "single" (`'`) or "double" (`"`)).
|
||||
Quote style to prefer for inline strings (either "single" (`'`) or
|
||||
"double" (`"`)).
|
||||
|
||||
**Default value**: `"double"`
|
||||
|
||||
@@ -2248,7 +2368,8 @@ inline-quotes = "single"
|
||||
|
||||
#### [`multiline-quotes`](#multiline-quotes)
|
||||
|
||||
Quote style to prefer for multiline strings (either "single" (`'`) or "double" (`"`)).
|
||||
Quote style to prefer for multiline strings (either "single" (`'`) or
|
||||
"double" (`"`)).
|
||||
|
||||
**Default value**: `"double"`
|
||||
|
||||
@@ -2267,8 +2388,8 @@ multiline-quotes = "single"
|
||||
|
||||
#### [`ban-relative-imports`](#ban-relative-imports)
|
||||
|
||||
Whether to ban all relative imports (`"all"`), or only those imports that extend into
|
||||
the parent module and beyond (`"parents"`).
|
||||
Whether to ban all relative imports (`"all"`), or only those imports
|
||||
that extend into the parent module and beyond (`"parents"`).
|
||||
|
||||
**Default value**: `"parents"`
|
||||
|
||||
@@ -2325,8 +2446,8 @@ combine-as-imports = true
|
||||
|
||||
#### [`extra-standard-library`](#extra-standard-library)
|
||||
|
||||
A list of modules to consider standard-library, in addition to those known to Ruff in
|
||||
advance.
|
||||
A list of modules to consider standard-library, in addition to those
|
||||
known to Ruff in advance.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
@@ -2343,9 +2464,10 @@ extra-standard-library = ["path"]
|
||||
|
||||
#### [`force-wrap-aliases`](#force-wrap-aliases)
|
||||
|
||||
Force `import from` statements with multiple members and at least one alias (e.g.,
|
||||
`import A as B`) to wrap such that every line contains exactly one member. For example,
|
||||
this formatting would be retained, rather than condensing to a single line:
|
||||
Force `import from` statements with multiple members and at least one
|
||||
alias (e.g., `import A as B`) to wrap such that every line contains
|
||||
exactly one member. For example, this formatting would be retained,
|
||||
rather than condensing to a single line:
|
||||
|
||||
```py
|
||||
from .utils import (
|
||||
@@ -2354,9 +2476,10 @@ from .utils import (
|
||||
)
|
||||
```
|
||||
|
||||
Note that this setting is only effective when combined with `combine-as-imports = true`.
|
||||
When `combine-as-imports` isn't enabled, every aliased `import from` will be given its
|
||||
own line, in which case, wrapping is not necessary.
|
||||
Note that this setting is only effective when combined with
|
||||
`combine-as-imports = true`. When `combine-as-imports` isn't
|
||||
enabled, every aliased `import from` will be given its own line, in
|
||||
which case, wrapping is not necessary.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
@@ -2374,8 +2497,8 @@ combine-as-imports = true
|
||||
|
||||
#### [`known-first-party`](#known-first-party)
|
||||
|
||||
A list of modules to consider first-party, regardless of whether they can be identified
|
||||
as such via introspection of the local filesystem.
|
||||
A list of modules to consider first-party, regardless of whether they
|
||||
can be identified as such via introspection of the local filesystem.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
@@ -2392,8 +2515,8 @@ known-first-party = ["src"]
|
||||
|
||||
#### [`known-third-party`](#known-third-party)
|
||||
|
||||
A list of modules to consider third-party, regardless of whether they can be identified
|
||||
as such via introspection of the local filesystem.
|
||||
A list of modules to consider third-party, regardless of whether they
|
||||
can be identified as such via introspection of the local filesystem.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
@@ -2408,6 +2531,26 @@ known-third-party = ["src"]
|
||||
|
||||
---
|
||||
|
||||
#### [`split-on-trailing-comma`](#split-on-trailing-comma)
|
||||
|
||||
If a comma is placed after the last member in a multi-line import, then
|
||||
the imports will never be folded into one line.
|
||||
|
||||
See isort's [`split-on-trailing-comma`](https://pycqa.github.io/isort/docs/configuration/options.html#split-on-trailing-comma) option.
|
||||
|
||||
**Default value**: `true`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.isort]
|
||||
split-on-trailing-comma = false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `mccabe`
|
||||
|
||||
#### [`max-complexity`](#max-complexity)
|
||||
@@ -2432,9 +2575,10 @@ max-complexity = 5
|
||||
|
||||
#### [`classmethod-decorators`](#classmethod-decorators)
|
||||
|
||||
A list of decorators that, when applied to a method, indicate that the method should be
|
||||
treated as a class method. For example, Ruff will expect that any method decorated by a
|
||||
decorator in this list takes a `cls` argument as its first argument.
|
||||
A list of decorators that, when applied to a method, indicate that the
|
||||
method should be treated as a class method. For example, Ruff will
|
||||
expect that any method decorated by a decorator in this list takes a
|
||||
`cls` argument as its first argument.
|
||||
|
||||
**Default value**: `["classmethod"]`
|
||||
|
||||
@@ -2469,9 +2613,10 @@ ignore-names = ["callMethod"]
|
||||
|
||||
#### [`staticmethod-decorators`](#staticmethod-decorators)
|
||||
|
||||
A list of decorators that, when applied to a method, indicate that the method should be
|
||||
treated as a static method. For example, Ruff will expect that any method decorated by a
|
||||
decorator in this list has no `self` or `cls` argument.
|
||||
A list of decorators that, when applied to a method, indicate that the
|
||||
method should be treated as a static method. For example, Ruff will
|
||||
expect that any method decorated by a decorator in this list has no
|
||||
`self` or `cls` argument.
|
||||
|
||||
**Default value**: `["staticmethod"]`
|
||||
|
||||
@@ -2491,7 +2636,11 @@ staticmethod-decorators = ["staticmethod", "stcmthd"]
|
||||
|
||||
#### [`keep-runtime-typing`](#keep-runtime-typing)
|
||||
|
||||
Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604 (`Optional[str]` -> `str | None`) rewrites even if a file imports `from __future__ import annotations`. Note that this setting is only applicable when the target Python version is below 3.9 and 3.10 respectively.
|
||||
Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604
|
||||
(`Optional[str]` -> `str | None`) rewrites even if a file imports `from
|
||||
__future__ import annotations`. Note that this setting is only
|
||||
applicable when the target Python version is below 3.9 and 3.10
|
||||
respectively.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
|
||||
12
flake8_to_ruff/Cargo.lock
generated
12
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.192"
|
||||
version = "0.0.195"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.192"
|
||||
version = "0.0.195"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@@ -2028,7 +2028,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-common",
|
||||
@@ -2038,7 +2038,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-common"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
|
||||
dependencies = [
|
||||
"ascii",
|
||||
"cfg-if 1.0.0",
|
||||
@@ -2061,7 +2061,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
@@ -2078,7 +2078,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.192-dev.0"
|
||||
version = "0.0.195-dev.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -12,6 +12,7 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Topic :: Software Development :: Quality Assurance",
|
||||
|
||||
@@ -67,10 +67,7 @@ pub fn convert(
|
||||
}
|
||||
let from_codes = plugin::infer_plugins_from_codes(&referenced_codes);
|
||||
if !from_codes.is_empty() {
|
||||
eprintln!(
|
||||
"Inferred plugins from referenced check codes: {:#?}",
|
||||
from_codes
|
||||
);
|
||||
eprintln!("Inferred plugins from referenced check codes: {from_codes:#?}");
|
||||
}
|
||||
from_options.into_iter().chain(from_codes).collect()
|
||||
}),
|
||||
@@ -298,6 +295,7 @@ mod tests {
|
||||
extend_select: None,
|
||||
external: None,
|
||||
fix: None,
|
||||
fix_only: None,
|
||||
fixable: None,
|
||||
format: None,
|
||||
force_exclude: None,
|
||||
@@ -305,6 +303,7 @@ mod tests {
|
||||
ignore_init_module_imports: None,
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
@@ -315,6 +314,7 @@ mod tests {
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
@@ -352,6 +352,7 @@ mod tests {
|
||||
extend_select: None,
|
||||
external: None,
|
||||
fix: None,
|
||||
fix_only: None,
|
||||
fixable: None,
|
||||
format: None,
|
||||
force_exclude: None,
|
||||
@@ -359,6 +360,7 @@ mod tests {
|
||||
ignore_init_module_imports: None,
|
||||
line_length: Some(100),
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
@@ -369,6 +371,7 @@ mod tests {
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
@@ -406,6 +409,7 @@ mod tests {
|
||||
extend_select: None,
|
||||
external: None,
|
||||
fix: None,
|
||||
fix_only: None,
|
||||
fixable: None,
|
||||
format: None,
|
||||
force_exclude: None,
|
||||
@@ -413,6 +417,7 @@ mod tests {
|
||||
ignore_init_module_imports: None,
|
||||
line_length: Some(100),
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
@@ -423,6 +428,7 @@ mod tests {
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
@@ -460,6 +466,7 @@ mod tests {
|
||||
extend_select: None,
|
||||
external: None,
|
||||
fix: None,
|
||||
fix_only: None,
|
||||
fixable: None,
|
||||
format: None,
|
||||
force_exclude: None,
|
||||
@@ -467,6 +474,7 @@ mod tests {
|
||||
ignore_init_module_imports: None,
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
@@ -477,6 +485,7 @@ mod tests {
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
@@ -514,6 +523,7 @@ mod tests {
|
||||
extend_select: None,
|
||||
external: None,
|
||||
fix: None,
|
||||
fix_only: None,
|
||||
fixable: None,
|
||||
format: None,
|
||||
force_exclude: None,
|
||||
@@ -521,6 +531,7 @@ mod tests {
|
||||
ignore_init_module_imports: None,
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
@@ -531,6 +542,7 @@ mod tests {
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
@@ -576,6 +588,7 @@ mod tests {
|
||||
extend_select: None,
|
||||
external: None,
|
||||
fix: None,
|
||||
fix_only: None,
|
||||
fixable: None,
|
||||
format: None,
|
||||
force_exclude: None,
|
||||
@@ -583,6 +596,7 @@ mod tests {
|
||||
ignore_init_module_imports: None,
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::D100,
|
||||
@@ -629,6 +643,7 @@ mod tests {
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
@@ -666,6 +681,7 @@ mod tests {
|
||||
extend_select: None,
|
||||
external: None,
|
||||
fix: None,
|
||||
fix_only: None,
|
||||
fixable: None,
|
||||
format: None,
|
||||
force_exclude: None,
|
||||
@@ -673,6 +689,7 @@ mod tests {
|
||||
ignore_init_module_imports: None,
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
@@ -684,6 +701,7 @@ mod tests {
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
|
||||
25
playground/.eslintrc
Normal file
25
playground/.eslintrc
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint", "prettier"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react/jsx-runtime",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:import/recommended",
|
||||
"plugin:import/typescript",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"rules": {
|
||||
// Disable some recommended rules that we don't want to enforce.
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-empty-function": "off"
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
}
|
||||
}
|
||||
130
playground/.gitignore
vendored
Normal file
130
playground/.gitignore
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
1
playground/.prettierignore
Normal file
1
playground/.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
src/ruff_options.ts
|
||||
10
playground/README.md
Normal file
10
playground/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# playground
|
||||
|
||||
In-browser playground for Ruff. Available [https://ruff.pages.dev/](https://ruff.pages.dev/).
|
||||
|
||||
## Getting started
|
||||
|
||||
- To build the WASM module, run `wasm-pack build --target web --out-dir playground/src/pkg` from the
|
||||
root directory.
|
||||
- Install TypeScript dependencies with: `npm install`.
|
||||
- Start the development server with: `npm run dev`.
|
||||
29
playground/index.html
Normal file
29
playground/index.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="description"
|
||||
content="An in-browser playground for Ruff, an extremely fast Python linter written in Rust."
|
||||
/>
|
||||
<meta name="keywords" content="ruff, python, rust, webassembly, wasm" />
|
||||
<title>Ruff Playground</title>
|
||||
<link
|
||||
rel="icon"
|
||||
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🛠️</text></svg>"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<div style="display: flex; position: fixed; right: 16px; top: 16px">
|
||||
<a href="https://GitHub.com/charliermarsh/ruff"
|
||||
><img
|
||||
src="https://img.shields.io/github/stars/charliermarsh/ruff.svg?style=social&label=GitHub&maxAge=2592000&?logoWidth=100"
|
||||
alt="GitHub stars"
|
||||
style="width: 120px"
|
||||
/></a>
|
||||
</div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
5870
playground/package-lock.json
generated
Normal file
5870
playground/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
playground/package.json
Normal file
38
playground/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "playground",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc && vite build",
|
||||
"check": "npm run lint && npm run tsc",
|
||||
"dev": "vite",
|
||||
"fmt": "prettier --cache -w .",
|
||||
"lint": "eslint --cache --ext .ts,.tsx src",
|
||||
"preview": "vite preview",
|
||||
"tsc": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"lz-string": "^1.4.4",
|
||||
"monaco-editor": "^0.34.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"@vitejs/plugin-react-swc": "^3.0.0",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.31.11",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"prettier": "^2.8.1",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^4.0.0"
|
||||
}
|
||||
}
|
||||
200
playground/src/App.tsx
Normal file
200
playground/src/App.tsx
Normal file
@@ -0,0 +1,200 @@
|
||||
import lzstring from "lz-string";
|
||||
import Editor, { useMonaco } from "@monaco-editor/react";
|
||||
import { MarkerSeverity } from "monaco-editor/esm/vs/editor/editor.api";
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
|
||||
import init, { Check, check } from "./pkg/ruff.js";
|
||||
import { AVAILABLE_OPTIONS } from "./ruff_options";
|
||||
import { Config, getDefaultConfig, toRuffConfig } from "./config";
|
||||
import { Options } from "./Options";
|
||||
|
||||
const DEFAULT_SOURCE =
|
||||
"# Define a function that takes an integer n and returns the nth number in the Fibonacci\n" +
|
||||
"# sequence.\n" +
|
||||
"def fibonacci(n):\n" +
|
||||
" if n == 0:\n" +
|
||||
" return 0\n" +
|
||||
" elif n == 1:\n" +
|
||||
" return 1\n" +
|
||||
" else:\n" +
|
||||
" return fibonacci(n-1) + fibonacci(n-2)\n" +
|
||||
"\n" +
|
||||
"# Use a for loop to generate and print the first 10 numbers in the Fibonacci sequence.\n" +
|
||||
"for i in range(10):\n" +
|
||||
" print(fibonacci(i))\n" +
|
||||
"\n" +
|
||||
"# Output:\n" +
|
||||
"# 0\n" +
|
||||
"# 1\n" +
|
||||
"# 1\n" +
|
||||
"# 2\n" +
|
||||
"# 3\n" +
|
||||
"# 5\n" +
|
||||
"# 8\n" +
|
||||
"# 13\n" +
|
||||
"# 21\n" +
|
||||
"# 34\n";
|
||||
|
||||
function restoreConfigAndSource(): [Config, string] {
|
||||
const value = lzstring.decompressFromEncodedURIComponent(
|
||||
window.location.hash.slice(1)
|
||||
);
|
||||
let config = {};
|
||||
let source = DEFAULT_SOURCE;
|
||||
|
||||
if (value) {
|
||||
const parts = value.split("$$$");
|
||||
config = JSON.parse(parts[0]);
|
||||
source = parts[1];
|
||||
}
|
||||
|
||||
return [config, source];
|
||||
}
|
||||
|
||||
function persistConfigAndSource(config: Config, source: string) {
|
||||
window.location.hash = lzstring.compressToEncodedURIComponent(
|
||||
JSON.stringify(config) + "$$$" + source
|
||||
);
|
||||
}
|
||||
|
||||
const defaultConfig = getDefaultConfig(AVAILABLE_OPTIONS);
|
||||
|
||||
export default function App() {
|
||||
const monaco = useMonaco();
|
||||
const [initialized, setInitialized] = useState<boolean>(false);
|
||||
const [config, setConfig] = useState<Config | null>(null);
|
||||
const [source, setSource] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
init().then(() => setInitialized(true));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (source == null && config == null && monaco) {
|
||||
const [config, source] = restoreConfigAndSource();
|
||||
setConfig(config);
|
||||
setSource(source);
|
||||
}
|
||||
}, [monaco, source, config]);
|
||||
|
||||
useEffect(() => {
|
||||
if (config != null && source != null) {
|
||||
persistConfigAndSource(config, source);
|
||||
}
|
||||
}, [config, source]);
|
||||
|
||||
useEffect(() => {
|
||||
const editor = monaco?.editor;
|
||||
const model = editor?.getModels()[0];
|
||||
if (!editor || !model || !initialized || source == null || config == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let checks: Check[];
|
||||
try {
|
||||
checks = check(source, toRuffConfig(config));
|
||||
setError(null);
|
||||
} catch (e) {
|
||||
setError(String(e));
|
||||
return;
|
||||
}
|
||||
|
||||
editor.setModelMarkers(
|
||||
model,
|
||||
"owner",
|
||||
checks.map((check) => ({
|
||||
startLineNumber: check.location.row,
|
||||
startColumn: check.location.column + 1,
|
||||
endLineNumber: check.end_location.row,
|
||||
endColumn: check.end_location.column + 1,
|
||||
message: `${check.code}: ${check.message}`,
|
||||
severity: MarkerSeverity.Error,
|
||||
}))
|
||||
);
|
||||
|
||||
const codeActionProvider = monaco?.languages.registerCodeActionProvider(
|
||||
"python",
|
||||
{
|
||||
// @ts-expect-error: The type definition is wrong.
|
||||
provideCodeActions: function (model, position) {
|
||||
const actions = checks
|
||||
.filter((check) => position.startLineNumber === check.location.row)
|
||||
.filter((check) => check.fix)
|
||||
.map((check) => ({
|
||||
title: `Fix ${check.code}`,
|
||||
id: `fix-${check.code}`,
|
||||
kind: "quickfix",
|
||||
edit: check.fix
|
||||
? {
|
||||
edits: [
|
||||
{
|
||||
resource: model.uri,
|
||||
versionId: model.getVersionId(),
|
||||
edit: {
|
||||
range: {
|
||||
startLineNumber: check.fix.location.row,
|
||||
startColumn: check.fix.location.column + 1,
|
||||
endLineNumber: check.fix.end_location.row,
|
||||
endColumn: check.fix.end_location.column + 1,
|
||||
},
|
||||
text: check.fix.content,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
}));
|
||||
return { actions, dispose: () => {} };
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
codeActionProvider?.dispose();
|
||||
};
|
||||
}, [config, source, monaco, initialized]);
|
||||
|
||||
const handleEditorChange = useCallback(
|
||||
(value: string | undefined) => {
|
||||
setSource(value || "");
|
||||
},
|
||||
[setSource]
|
||||
);
|
||||
|
||||
const handleOptionChange = useCallback(
|
||||
(groupName: string, fieldName: string, value: string) => {
|
||||
const group = Object.assign({}, (config || {})[groupName]);
|
||||
if (value === defaultConfig[groupName][fieldName] || value === "") {
|
||||
delete group[fieldName];
|
||||
} else {
|
||||
group[fieldName] = value;
|
||||
}
|
||||
|
||||
setConfig({
|
||||
...config,
|
||||
[groupName]: group,
|
||||
});
|
||||
},
|
||||
[config]
|
||||
);
|
||||
|
||||
return (
|
||||
<div id="app">
|
||||
<Options
|
||||
config={config}
|
||||
defaultConfig={defaultConfig}
|
||||
onChange={handleOptionChange}
|
||||
/>
|
||||
<Editor
|
||||
options={{ readOnly: false, minimap: { enabled: false } }}
|
||||
wrapperProps={{ className: "editor" }}
|
||||
defaultLanguage="python"
|
||||
value={source || ""}
|
||||
theme={"light"}
|
||||
onChange={handleEditorChange}
|
||||
/>
|
||||
{error && <div id="error">{error}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
72
playground/src/Options.tsx
Normal file
72
playground/src/Options.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { Config } from "./config";
|
||||
import { AVAILABLE_OPTIONS } from "./ruff_options";
|
||||
|
||||
function OptionEntry({
|
||||
config,
|
||||
defaultConfig,
|
||||
groupName,
|
||||
fieldName,
|
||||
onChange,
|
||||
}: {
|
||||
config: Config | null;
|
||||
defaultConfig: Config;
|
||||
groupName: string;
|
||||
fieldName: string;
|
||||
onChange: (groupName: string, fieldName: string, value: string) => void;
|
||||
}) {
|
||||
const value =
|
||||
config && config[groupName] && config[groupName][fieldName]
|
||||
? config[groupName][fieldName]
|
||||
: "";
|
||||
|
||||
return (
|
||||
<span>
|
||||
<label>
|
||||
{fieldName}
|
||||
<input
|
||||
value={value}
|
||||
placeholder={defaultConfig[groupName][fieldName]}
|
||||
type="text"
|
||||
onChange={(event) => {
|
||||
onChange(groupName, fieldName, event.target.value);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function Options({
|
||||
config,
|
||||
defaultConfig,
|
||||
onChange,
|
||||
}: {
|
||||
config: Config | null;
|
||||
defaultConfig: Config;
|
||||
onChange: (groupName: string, fieldName: string, value: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="options">
|
||||
{AVAILABLE_OPTIONS.map((group) => (
|
||||
<details key={group.name}>
|
||||
<summary>{group.name}</summary>
|
||||
<div>
|
||||
<ul>
|
||||
{group.fields.map((field) => (
|
||||
<li key={field.name}>
|
||||
<OptionEntry
|
||||
config={config}
|
||||
defaultConfig={defaultConfig}
|
||||
groupName={group.name}
|
||||
fieldName={field.name}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
52
playground/src/config.ts
Normal file
52
playground/src/config.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { OptionGroup } from "./ruff_options";
|
||||
|
||||
export type Config = { [key: string]: { [key: string]: string } };
|
||||
|
||||
export function getDefaultConfig(availableOptions: OptionGroup[]): Config {
|
||||
const config: Config = {};
|
||||
availableOptions.forEach((group) => {
|
||||
config[group.name] = {};
|
||||
group.fields.forEach((f) => {
|
||||
config[group.name][f.name] = f.default;
|
||||
});
|
||||
});
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the config in the application to something Ruff accepts.
|
||||
*
|
||||
* Application config is always nested one level. Ruff allows for some
|
||||
* top-level options.
|
||||
*
|
||||
* Any option value is parsed as JSON to convert it to a native JS object.
|
||||
* If that fails, e.g. while a user is typing, we let the application handle that
|
||||
* and show an error.
|
||||
*/
|
||||
export function toRuffConfig(config: Config): any {
|
||||
const convertValue = (value: string): any => {
|
||||
return value === "None" ? null : JSON.parse(value);
|
||||
};
|
||||
|
||||
const result: any = {};
|
||||
Object.keys(config).forEach((group_name) => {
|
||||
const fields = config[group_name];
|
||||
if (!fields || Object.keys(fields).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (group_name === "globals") {
|
||||
Object.keys(fields).forEach((field_name) => {
|
||||
result[field_name] = convertValue(fields[field_name]);
|
||||
});
|
||||
} else {
|
||||
result[group_name] = {};
|
||||
|
||||
Object.keys(fields).forEach((field_name) => {
|
||||
result[group_name][field_name] = convertValue(fields[field_name]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
6
playground/src/custom.d.ts
vendored
Normal file
6
playground/src/custom.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
declare module "lz-string" {
|
||||
function decompressFromEncodedURIComponent(
|
||||
input: string | null
|
||||
): string | null;
|
||||
function compressToEncodedURIComponent(input: string | null): string;
|
||||
}
|
||||
10
playground/src/main.tsx
Normal file
10
playground/src/main.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
import "./style.css";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
222
playground/src/ruff_options.ts
Normal file
222
playground/src/ruff_options.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
|
||||
// This file is auto-generated by `cargo dev generate-playground-options`.
|
||||
export interface OptionGroup {
|
||||
name: string;
|
||||
fields: {
|
||||
name: string;
|
||||
default: string;
|
||||
type: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export const AVAILABLE_OPTIONS: OptionGroup[] = [
|
||||
{"name": "globals", "fields": [
|
||||
{
|
||||
"name": "allowed-confusables",
|
||||
"default": '[]',
|
||||
"type": 'Vec<char>',
|
||||
},
|
||||
{
|
||||
"name": "dummy-variable-rgx",
|
||||
"default": '"^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"',
|
||||
"type": 'Regex',
|
||||
},
|
||||
{
|
||||
"name": "extend-ignore",
|
||||
"default": '[]',
|
||||
"type": 'Vec<CheckCodePrefix>',
|
||||
},
|
||||
{
|
||||
"name": "extend-select",
|
||||
"default": '[]',
|
||||
"type": 'Vec<CheckCodePrefix>',
|
||||
},
|
||||
{
|
||||
"name": "external",
|
||||
"default": '[]',
|
||||
"type": 'Vec<String>',
|
||||
},
|
||||
{
|
||||
"name": "fix-only",
|
||||
"default": 'false',
|
||||
"type": 'bool',
|
||||
},
|
||||
{
|
||||
"name": "ignore",
|
||||
"default": '[]',
|
||||
"type": 'Vec<CheckCodePrefix>',
|
||||
},
|
||||
{
|
||||
"name": "line-length",
|
||||
"default": '88',
|
||||
"type": 'usize',
|
||||
},
|
||||
{
|
||||
"name": "required-version",
|
||||
"default": 'None',
|
||||
"type": 'String',
|
||||
},
|
||||
{
|
||||
"name": "select",
|
||||
"default": '["E", "F"]',
|
||||
"type": 'Vec<CheckCodePrefix>',
|
||||
},
|
||||
{
|
||||
"name": "target-version",
|
||||
"default": '"py310"',
|
||||
"type": 'PythonVersion',
|
||||
},
|
||||
{
|
||||
"name": "unfixable",
|
||||
"default": '[]',
|
||||
"type": 'Vec<CheckCodePrefix>',
|
||||
},
|
||||
]},
|
||||
{"name": "flake8-annotations", "fields": [
|
||||
{
|
||||
"name": "allow-star-arg-any",
|
||||
"default": 'false',
|
||||
"type": 'bool',
|
||||
},
|
||||
{
|
||||
"name": "mypy-init-return",
|
||||
"default": 'false',
|
||||
"type": 'bool',
|
||||
},
|
||||
{
|
||||
"name": "suppress-dummy-args",
|
||||
"default": 'false',
|
||||
"type": 'bool',
|
||||
},
|
||||
{
|
||||
"name": "suppress-none-returning",
|
||||
"default": 'false',
|
||||
"type": 'bool',
|
||||
},
|
||||
]},
|
||||
{"name": "flake8-bugbear", "fields": [
|
||||
{
|
||||
"name": "extend-immutable-calls",
|
||||
"default": '[]',
|
||||
"type": 'Vec<String>',
|
||||
},
|
||||
]},
|
||||
{"name": "flake8-errmsg", "fields": [
|
||||
{
|
||||
"name": "max-string-length",
|
||||
"default": '0',
|
||||
"type": 'usize',
|
||||
},
|
||||
]},
|
||||
{"name": "flake8-import-conventions", "fields": [
|
||||
{
|
||||
"name": "aliases",
|
||||
"default": '{"altair": "alt", "matplotlib.pyplot": "plt", "numpy": "np", "pandas": "pd", "seaborn": "sns"}',
|
||||
"type": 'FxHashMap<String, String>',
|
||||
},
|
||||
{
|
||||
"name": "extend-aliases",
|
||||
"default": '{}',
|
||||
"type": 'FxHashMap<String, String>',
|
||||
},
|
||||
]},
|
||||
{"name": "flake8-quotes", "fields": [
|
||||
{
|
||||
"name": "avoid-escape",
|
||||
"default": 'true',
|
||||
"type": 'bool',
|
||||
},
|
||||
{
|
||||
"name": "docstring-quotes",
|
||||
"default": '"double"',
|
||||
"type": 'Quote',
|
||||
},
|
||||
{
|
||||
"name": "inline-quotes",
|
||||
"default": '"double"',
|
||||
"type": 'Quote',
|
||||
},
|
||||
{
|
||||
"name": "multiline-quotes",
|
||||
"default": '"double"',
|
||||
"type": 'Quote',
|
||||
},
|
||||
]},
|
||||
{"name": "flake8-tidy-imports", "fields": [
|
||||
{
|
||||
"name": "ban-relative-imports",
|
||||
"default": '"parents"',
|
||||
"type": 'Strictness',
|
||||
},
|
||||
]},
|
||||
{"name": "flake8-unused-arguments", "fields": [
|
||||
{
|
||||
"name": "ignore-variadic-names",
|
||||
"default": 'false',
|
||||
"type": 'bool',
|
||||
},
|
||||
]},
|
||||
{"name": "isort", "fields": [
|
||||
{
|
||||
"name": "combine-as-imports",
|
||||
"default": 'false',
|
||||
"type": 'bool',
|
||||
},
|
||||
{
|
||||
"name": "extra-standard-library",
|
||||
"default": '[]',
|
||||
"type": 'Vec<String>',
|
||||
},
|
||||
{
|
||||
"name": "force-wrap-aliases",
|
||||
"default": 'false',
|
||||
"type": 'bool',
|
||||
},
|
||||
{
|
||||
"name": "known-first-party",
|
||||
"default": '[]',
|
||||
"type": 'Vec<String>',
|
||||
},
|
||||
{
|
||||
"name": "known-third-party",
|
||||
"default": '[]',
|
||||
"type": 'Vec<String>',
|
||||
},
|
||||
{
|
||||
"name": "split-on-trailing-comma",
|
||||
"default": 'true',
|
||||
"type": 'bool',
|
||||
},
|
||||
]},
|
||||
{"name": "mccabe", "fields": [
|
||||
{
|
||||
"name": "max-complexity",
|
||||
"default": '10',
|
||||
"type": 'usize',
|
||||
},
|
||||
]},
|
||||
{"name": "pep8-naming", "fields": [
|
||||
{
|
||||
"name": "classmethod-decorators",
|
||||
"default": '["classmethod"]',
|
||||
"type": 'Vec<String>',
|
||||
},
|
||||
{
|
||||
"name": "ignore-names",
|
||||
"default": '["setUp", "tearDown", "setUpClass", "tearDownClass", "setUpModule", "tearDownModule", "asyncSetUp", "asyncTearDown", "setUpTestData", "failureException", "longMessage", "maxDiff"]',
|
||||
"type": 'Vec<String>',
|
||||
},
|
||||
{
|
||||
"name": "staticmethod-decorators",
|
||||
"default": '["staticmethod"]',
|
||||
"type": 'Vec<String>',
|
||||
},
|
||||
]},
|
||||
{"name": "pyupgrade", "fields": [
|
||||
{
|
||||
"name": "keep-runtime-typing",
|
||||
"default": 'false',
|
||||
"type": 'bool',
|
||||
},
|
||||
]},
|
||||
];
|
||||
60
playground/src/style.css
Normal file
60
playground/src/style.css
Normal file
@@ -0,0 +1,60 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body,
|
||||
html,
|
||||
#root,
|
||||
#app {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.options {
|
||||
height: 100vh;
|
||||
overflow-y: scroll;
|
||||
padding: 1em;
|
||||
min-width: 300px;
|
||||
border-right: 1px solid lightgray;
|
||||
}
|
||||
|
||||
.options ul {
|
||||
padding-left: 1em;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.options li {
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.options details {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.options summary {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.options input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.editor {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#error {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
min-height: 1em;
|
||||
padding: 1em;
|
||||
background: darkred;
|
||||
color: white;
|
||||
}
|
||||
1
playground/src/vite-env.d.ts
vendored
Normal file
1
playground/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
21
playground/tsconfig.json
Normal file
21
playground/tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
9
playground/tsconfig.node.json
Normal file
9
playground/tsconfig.node.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
7
playground/vite.config.ts
Normal file
7
playground/vite.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
});
|
||||
@@ -12,6 +12,7 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Topic :: Software Development :: Quality Assurance",
|
||||
|
||||
12
resources/test/fixtures/flake8_return/RET504.py
vendored
12
resources/test/fixtures/flake8_return/RET504.py
vendored
@@ -237,3 +237,15 @@ def close(self):
|
||||
any_failed = True
|
||||
report(traceback.format_exc())
|
||||
return any_failed
|
||||
|
||||
def global_assignment():
|
||||
global X
|
||||
X = 1
|
||||
return X
|
||||
|
||||
def nonlocal_assignment():
|
||||
X = 1
|
||||
def inner():
|
||||
nonlocal X
|
||||
X = 1
|
||||
return X
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from abc import abstractmethod
|
||||
from typing import overload
|
||||
from typing_extensions import override
|
||||
|
||||
|
||||
@@ -135,3 +136,20 @@ class C:
|
||||
@override
|
||||
def f(x):
|
||||
print("Hello, world!")
|
||||
|
||||
|
||||
###
|
||||
# Unused arguments attached to overloads (OK).
|
||||
###
|
||||
@overload
|
||||
def f(a: str, b: str) -> str:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def f(a: int, b: int) -> str:
|
||||
...
|
||||
|
||||
|
||||
def f(a, b):
|
||||
return f"{a}{b}"
|
||||
|
||||
3
resources/test/fixtures/isort/comments.py
vendored
3
resources/test/fixtures/isort/comments.py
vendored
@@ -23,3 +23,6 @@ from A import (
|
||||
b, # Comment 10
|
||||
c, # Comment 11
|
||||
)
|
||||
|
||||
from D import a_long_name_to_force_multiple_lines # Comment 12
|
||||
from D import another_long_name_to_force_multiple_lines # Comment 13
|
||||
|
||||
38
resources/test/fixtures/isort/magic_trailing_comma.py
vendored
Normal file
38
resources/test/fixtures/isort/magic_trailing_comma.py
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# This has a magic trailing comma, will be sorted, but not rolled into one line
|
||||
from sys import (
|
||||
stderr,
|
||||
argv,
|
||||
stdout,
|
||||
exit,
|
||||
)
|
||||
|
||||
# No magic comma, this will be rolled into one line.
|
||||
from os import (
|
||||
path,
|
||||
environ,
|
||||
execl,
|
||||
execv
|
||||
)
|
||||
|
||||
from glob import (
|
||||
glob,
|
||||
iglob,
|
||||
escape, # Ends with a comment, should still treat as magic trailing comma.
|
||||
)
|
||||
|
||||
# These will be combined, but without a trailing comma.
|
||||
from foo import bar
|
||||
from foo import baz
|
||||
|
||||
# These will be combined, _with_ a trailing comma.
|
||||
from module1 import member1
|
||||
from module1 import (
|
||||
member2,
|
||||
member3,
|
||||
)
|
||||
|
||||
# These will be combined, _with_ a trailing comma.
|
||||
from module2 import member1, member2
|
||||
from module2 import (
|
||||
member3,
|
||||
)
|
||||
16
resources/test/fixtures/isort/natural_order.py
vendored
Normal file
16
resources/test/fixtures/isort/natural_order.py
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import numpy1
|
||||
import numpy10
|
||||
import numpy2
|
||||
from numpy import (
|
||||
cos,
|
||||
int8,
|
||||
sin,
|
||||
int32,
|
||||
int64,
|
||||
tan,
|
||||
uint8,
|
||||
uint16,
|
||||
int16,
|
||||
uint32,
|
||||
uint64,
|
||||
)
|
||||
4
resources/test/fixtures/pycodestyle/E721.py
vendored
4
resources/test/fixtures/pycodestyle/E721.py
vendored
@@ -15,7 +15,7 @@ import types
|
||||
if type(res) is not types.ListType:
|
||||
pass
|
||||
#: E721
|
||||
assert type(res) == type(False) or type(res) == type(None)
|
||||
assert type(res) == type(False)
|
||||
#: E721
|
||||
assert type(res) == type([])
|
||||
#: E721
|
||||
@@ -52,3 +52,5 @@ if isinstance(res, types.MethodType):
|
||||
pass
|
||||
if type(a) != type(b) or type(a) == type(ccc):
|
||||
pass
|
||||
|
||||
assert type(res) == type(None)
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
def fn() -> None:
|
||||
pass
|
||||
print("Newline present (no W292)")
|
||||
|
||||
0
resources/test/fixtures/pycodestyle/W292_3.py
vendored
Normal file
0
resources/test/fixtures/pycodestyle/W292_3.py
vendored
Normal file
1
resources/test/fixtures/pycodestyle/W292_4.py
vendored
Normal file
1
resources/test/fixtures/pycodestyle/W292_4.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
25
resources/test/fixtures/pyupgrade/UP018.py
vendored
Normal file
25
resources/test/fixtures/pyupgrade/UP018.py
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# These remain unchanged
|
||||
str(1)
|
||||
str(*a)
|
||||
str("foo", *a)
|
||||
str(**k)
|
||||
str("foo", **k)
|
||||
str("foo", encoding="UTF-8")
|
||||
str("foo"
|
||||
"bar")
|
||||
bytes("foo", encoding="UTF-8")
|
||||
bytes(*a)
|
||||
bytes("foo", *a)
|
||||
bytes("foo", **a)
|
||||
bytes(b"foo"
|
||||
b"bar")
|
||||
|
||||
# These become string or byte literals
|
||||
str()
|
||||
str("foo")
|
||||
str("""
|
||||
foo""")
|
||||
bytes()
|
||||
bytes(b"foo")
|
||||
bytes(b"""
|
||||
foo""")
|
||||
20
resources/test/fixtures/pyupgrade/UP019.py
vendored
Normal file
20
resources/test/fixtures/pyupgrade/UP019.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import typing
|
||||
import typing as Hello
|
||||
from typing import Text
|
||||
from typing import Text as Goodbye
|
||||
|
||||
|
||||
def print_word(word: Text) -> None:
|
||||
print(word)
|
||||
|
||||
|
||||
def print_second_word(word: typing.Text) -> None:
|
||||
print(word)
|
||||
|
||||
|
||||
def print_third_word(word: Hello.Text) -> None:
|
||||
print(word)
|
||||
|
||||
|
||||
def print_fourth_word(word: Goodbye) -> None:
|
||||
print(word)
|
||||
4
resources/test/fixtures/ruff/RUF100.py
vendored
4
resources/test/fixtures/ruff/RUF100.py
vendored
@@ -15,8 +15,8 @@ def f() -> None:
|
||||
# Invalid
|
||||
d = 1 # noqa: F841, E501
|
||||
|
||||
# Invalid (and unimplemented)
|
||||
d = 1 # noqa: F841, W191
|
||||
# Invalid (and unimplemented or not enabled)
|
||||
d = 1 # noqa: F841, W191, F821
|
||||
|
||||
# Invalid (but external)
|
||||
d = 1 # noqa: F841, V101
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
[tool.ruff]
|
||||
extend = "../../pyproject.toml"
|
||||
src = ["."]
|
||||
# Enable I001, and re-enable F841, to test extension priority.
|
||||
1224
ruff.schema.json
Normal file
1224
ruff.schema.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.192"
|
||||
version = "0.0.195"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -11,8 +11,11 @@ itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
|
||||
once_cell = { version = "1.16.0" }
|
||||
ruff = { path = ".." }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "247e815880766d556ef1ca6f0af69daf1a5fe59a" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "247e815880766d556ef1ca6f0af69daf1a5fe59a" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "247e815880766d556ef1ca6f0af69daf1a5fe59a" }
|
||||
schemars = { version = "0.8.11" }
|
||||
serde_json = {version="1.0.91"}
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
textwrap = { version = "0.16.0" }
|
||||
|
||||
@@ -65,7 +65,8 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
.derive("Ord")
|
||||
.derive("Clone")
|
||||
.derive("Serialize")
|
||||
.derive("Deserialize");
|
||||
.derive("Deserialize")
|
||||
.derive("JsonSchema");
|
||||
for prefix in prefix_to_codes.keys() {
|
||||
gen = gen.push_variant(Variant::new(prefix.to_string()));
|
||||
}
|
||||
@@ -138,8 +139,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
_ => panic!("Invalid prefix: {prefix}"),
|
||||
};
|
||||
gen = gen.line(format!(
|
||||
"CheckCodePrefix::{prefix} => SuffixLength::{},",
|
||||
specificity
|
||||
"CheckCodePrefix::{prefix} => SuffixLength::{specificity},"
|
||||
));
|
||||
}
|
||||
gen.line("}");
|
||||
@@ -152,6 +152,8 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
output.push('\n');
|
||||
output.push_str("use colored::Colorize;");
|
||||
output.push('\n');
|
||||
output.push_str("use schemars::JsonSchema;");
|
||||
output.push('\n');
|
||||
output.push_str("use serde::{Deserialize, Serialize};");
|
||||
output.push('\n');
|
||||
output.push_str("use strum_macros::{AsRefStr, EnumString};");
|
||||
|
||||
30
ruff_dev/src/generate_json_schema.rs
Normal file
30
ruff_dev/src/generate_json_schema.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
use ruff::settings::options::Options;
|
||||
use schemars::schema_for;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Cli {
|
||||
/// Write the generated table to stdout (rather than to `ruff.schema.json`).
|
||||
#[arg(long)]
|
||||
dry_run: bool,
|
||||
}
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
let schema = schema_for!(Options);
|
||||
let schema_string = serde_json::to_string_pretty(&schema).unwrap();
|
||||
|
||||
if cli.dry_run {
|
||||
println!("{schema_string}");
|
||||
} else {
|
||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.expect("Failed to find root directory")
|
||||
.join("ruff.schema.json");
|
||||
fs::write(file, schema_string.as_bytes())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
142
ruff_dev/src/generate_playground_options.rs
Normal file
142
ruff_dev/src/generate_playground_options.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
//! Generate typescript file defining options to be used by the web playground.
|
||||
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
use itertools::Itertools;
|
||||
use ruff::settings::options::Options;
|
||||
use ruff::settings::options_base::{ConfigurationOptions, OptionEntry, OptionField};
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Cli {
|
||||
/// Write the generated table to stdout (rather than to `TODO`).
|
||||
#[arg(long)]
|
||||
dry_run: bool,
|
||||
}
|
||||
|
||||
fn emit_field(output: &mut String, field: &OptionField) {
|
||||
output.push_str(&textwrap::indent(
|
||||
&textwrap::dedent(&format!(
|
||||
"
|
||||
{{
|
||||
\"name\": \"{}\",
|
||||
\"default\": '{}',
|
||||
\"type\": '{}',
|
||||
}},",
|
||||
field.name, field.default, field.value_type
|
||||
)),
|
||||
" ",
|
||||
));
|
||||
}
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
let mut output = String::new();
|
||||
|
||||
// Generate all the top-level fields.
|
||||
output.push_str(&format!("{{\"name\": \"{}\", \"fields\": [", "globals"));
|
||||
for field in Options::get_available_options()
|
||||
.into_iter()
|
||||
.filter_map(|entry| {
|
||||
if let OptionEntry::Field(field) = entry {
|
||||
Some(field)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
// Filter out options that don't make sense in the playground.
|
||||
.filter(|field| {
|
||||
!matches!(
|
||||
field.name,
|
||||
"src"
|
||||
| "fix"
|
||||
| "format"
|
||||
| "exclude"
|
||||
| "extend"
|
||||
| "extend-exclude"
|
||||
| "fixable"
|
||||
| "force-exclude"
|
||||
| "ignore-init-module-imports"
|
||||
| "respect-gitignore"
|
||||
| "show-source"
|
||||
| "cache-dir"
|
||||
| "per-file-ignores"
|
||||
)
|
||||
})
|
||||
.sorted_by_key(|field| field.name)
|
||||
{
|
||||
emit_field(&mut output, &field);
|
||||
}
|
||||
output.push_str("\n]},\n");
|
||||
|
||||
// Generate all the sub-groups.
|
||||
for group in Options::get_available_options()
|
||||
.into_iter()
|
||||
.filter_map(|entry| {
|
||||
if let OptionEntry::Group(group) = entry {
|
||||
Some(group)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.sorted_by_key(|group| group.name)
|
||||
{
|
||||
output.push_str(&format!("{{\"name\": \"{}\", \"fields\": [", group.name));
|
||||
for field in group
|
||||
.fields
|
||||
.iter()
|
||||
.filter_map(|entry| {
|
||||
if let OptionEntry::Field(field) = entry {
|
||||
Some(field)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.sorted_by_key(|field| field.name)
|
||||
{
|
||||
emit_field(&mut output, field);
|
||||
}
|
||||
output.push_str("\n]},\n");
|
||||
}
|
||||
|
||||
let prefix = textwrap::dedent(
|
||||
r"
|
||||
// This file is auto-generated by `cargo dev generate-playground-options`.
|
||||
export interface OptionGroup {
|
||||
name: string;
|
||||
fields: {
|
||||
name: string;
|
||||
default: string;
|
||||
type: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export const AVAILABLE_OPTIONS: OptionGroup[] = [
|
||||
",
|
||||
);
|
||||
let postfix = "];";
|
||||
|
||||
if cli.dry_run {
|
||||
print!("{output}");
|
||||
} else {
|
||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.expect("Failed to find root directory")
|
||||
.join("playground")
|
||||
.join("src")
|
||||
.join("ruff_options.ts");
|
||||
|
||||
let mut f = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(file)?;
|
||||
write!(f, "{prefix}")?;
|
||||
write!(f, "{}", textwrap::indent(&output, " "))?;
|
||||
write!(f, "{postfix}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -12,7 +12,9 @@
|
||||
)]
|
||||
|
||||
pub mod generate_check_code_prefix;
|
||||
pub mod generate_json_schema;
|
||||
pub mod generate_options;
|
||||
pub mod generate_playground_options;
|
||||
pub mod generate_rules_table;
|
||||
pub mod generate_source_code;
|
||||
pub mod print_ast;
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
use ruff_dev::{
|
||||
generate_check_code_prefix, generate_options, generate_rules_table, generate_source_code,
|
||||
print_ast, print_cst, print_tokens,
|
||||
generate_check_code_prefix, generate_json_schema, generate_options,
|
||||
generate_playground_options, generate_rules_table, generate_source_code, print_ast, print_cst,
|
||||
print_tokens,
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
@@ -30,10 +31,15 @@ struct Cli {
|
||||
enum Commands {
|
||||
/// Generate the `CheckCodePrefix` enum.
|
||||
GenerateCheckCodePrefix(generate_check_code_prefix::Cli),
|
||||
/// Generate JSON schema for the TOML configuration file.
|
||||
GenerateJSONSchema(generate_json_schema::Cli),
|
||||
/// Generate a Markdown-compatible table of supported lint rules.
|
||||
GenerateRulesTable(generate_rules_table::Cli),
|
||||
/// Generate a Markdown-compatible listing of configuration options.
|
||||
GenerateOptions(generate_options::Cli),
|
||||
/// Generate typescript file defining options to be used by the web
|
||||
/// playground.
|
||||
GeneratePlaygroundOptions(generate_playground_options::Cli),
|
||||
/// Run round-trip source code generation on a given Python file.
|
||||
GenerateSourceCode(generate_source_code::Cli),
|
||||
/// Print the AST for a given Python file.
|
||||
@@ -48,9 +54,11 @@ fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
match &cli.command {
|
||||
Commands::GenerateCheckCodePrefix(args) => generate_check_code_prefix::main(args)?,
|
||||
Commands::GenerateJSONSchema(args) => generate_json_schema::main(args)?,
|
||||
Commands::GenerateRulesTable(args) => generate_rules_table::main(args)?,
|
||||
Commands::GenerateSourceCode(args) => generate_source_code::main(args)?,
|
||||
Commands::GenerateOptions(args) => generate_options::main(args)?,
|
||||
Commands::GeneratePlaygroundOptions(args) => generate_playground_options::main(args)?,
|
||||
Commands::PrintAST(args) => print_ast::main(args)?,
|
||||
Commands::PrintCST(args) => print_cst::main(args)?,
|
||||
Commands::PrintTokens(args) => print_tokens::main(args)?,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.192"
|
||||
version = "0.0.195"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -13,13 +13,14 @@
|
||||
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::token::Comma;
|
||||
use syn::{
|
||||
parse_macro_input, AngleBracketedGenericArguments, Attribute, Data, DataStruct, DeriveInput,
|
||||
Field, Fields, Lit, LitStr, Path, PathArguments, PathSegment, Token, Type, TypePath,
|
||||
};
|
||||
|
||||
#[proc_macro_derive(ConfigurationOptions, attributes(option, option_group))]
|
||||
#[proc_macro_derive(ConfigurationOptions, attributes(option, doc, option_group))]
|
||||
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
@@ -39,11 +40,28 @@ fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
|
||||
let mut output = vec![];
|
||||
|
||||
for field in fields.named.iter() {
|
||||
if let Some(attr) = field.attrs.iter().find(|a| a.path.is_ident("option")) {
|
||||
output.push(handle_option(field, attr)?);
|
||||
let docs: Vec<&Attribute> = field
|
||||
.attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.path.is_ident("doc"))
|
||||
.collect();
|
||||
|
||||
if docs.is_empty() {
|
||||
return Err(syn::Error::new(
|
||||
field.span(),
|
||||
"Missing documentation for field",
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(attr) = field.attrs.iter().find(|attr| attr.path.is_ident("option")) {
|
||||
output.push(handle_option(field, attr, docs)?);
|
||||
};
|
||||
|
||||
if field.attrs.iter().any(|a| a.path.is_ident("option_group")) {
|
||||
if field
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|attr| attr.path.is_ident("option_group"))
|
||||
{
|
||||
output.push(handle_option_group(field)?);
|
||||
};
|
||||
}
|
||||
@@ -70,8 +88,10 @@ fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
|
||||
/// deriving `ConfigurationOptions`, create code that calls retrieves options
|
||||
/// from that group: `Foobar::get_available_options()`
|
||||
fn handle_option_group(field: &Field) -> syn::Result<proc_macro2::TokenStream> {
|
||||
// unwrap is safe because we're only going over named fields
|
||||
let ident = field.ident.as_ref().unwrap();
|
||||
let ident = field
|
||||
.ident
|
||||
.as_ref()
|
||||
.expect("Expected to handle named fields");
|
||||
|
||||
match &field.ty {
|
||||
Type::Path(TypePath {
|
||||
@@ -103,17 +123,49 @@ fn handle_option_group(field: &Field) -> syn::Result<proc_macro2::TokenStream> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a `doc` attribute into it a string literal.
|
||||
fn parse_doc(doc: &Attribute) -> syn::Result<String> {
|
||||
let doc = doc
|
||||
.parse_meta()
|
||||
.map_err(|e| syn::Error::new(doc.span(), e))?;
|
||||
|
||||
match doc {
|
||||
syn::Meta::NameValue(syn::MetaNameValue {
|
||||
lit: Lit::Str(lit_str),
|
||||
..
|
||||
}) => Ok(lit_str.value()),
|
||||
_ => Err(syn::Error::new(doc.span(), "Expected doc attribute.")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an `#[option(doc="...", default="...", value_type="...",
|
||||
/// example="...")]` attribute and return data in the form of an `OptionField`.
|
||||
fn handle_option(field: &Field, attr: &Attribute) -> syn::Result<proc_macro2::TokenStream> {
|
||||
// unwrap is safe because we're only going over named fields
|
||||
let ident = field.ident.as_ref().unwrap();
|
||||
fn handle_option(
|
||||
field: &Field,
|
||||
attr: &Attribute,
|
||||
docs: Vec<&Attribute>,
|
||||
) -> syn::Result<proc_macro2::TokenStream> {
|
||||
// Convert the list of `doc` attributes into a single string.
|
||||
let doc = textwrap::dedent(
|
||||
&docs
|
||||
.into_iter()
|
||||
.map(parse_doc)
|
||||
.collect::<syn::Result<Vec<_>>>()?
|
||||
.join("\n"),
|
||||
)
|
||||
.trim_matches('\n')
|
||||
.to_string();
|
||||
|
||||
let ident = field
|
||||
.ident
|
||||
.as_ref()
|
||||
.expect("Expected to handle named fields");
|
||||
|
||||
let FieldAttributes {
|
||||
doc,
|
||||
default,
|
||||
value_type,
|
||||
example,
|
||||
..
|
||||
} = attr.parse_args::<FieldAttributes>()?;
|
||||
let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span());
|
||||
|
||||
@@ -130,7 +182,6 @@ fn handle_option(field: &Field, attr: &Attribute) -> syn::Result<proc_macro2::To
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FieldAttributes {
|
||||
doc: String,
|
||||
default: String,
|
||||
value_type: String,
|
||||
example: String,
|
||||
@@ -138,8 +189,6 @@ struct FieldAttributes {
|
||||
|
||||
impl Parse for FieldAttributes {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let doc = _parse_key_value(input, "doc")?;
|
||||
input.parse::<Comma>()?;
|
||||
let default = _parse_key_value(input, "default")?;
|
||||
input.parse::<Comma>()?;
|
||||
let value_type = _parse_key_value(input, "value_type")?;
|
||||
@@ -150,7 +199,6 @@ impl Parse for FieldAttributes {
|
||||
}
|
||||
|
||||
Ok(FieldAttributes {
|
||||
doc: textwrap::dedent(&doc).trim_matches('\n').to_string(),
|
||||
default,
|
||||
value_type,
|
||||
example: textwrap::dedent(&example).trim_matches('\n').to_string(),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use itertools::Itertools;
|
||||
use log::error;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
@@ -396,6 +397,37 @@ pub fn identifier_range(stmt: &Stmt, locator: &SourceCodeLocator) -> Range {
|
||||
Range::from_located(stmt)
|
||||
}
|
||||
|
||||
/// Return the `Range` of `name` in `Excepthandler`.
|
||||
pub fn excepthandler_name_range(
|
||||
handler: &Excepthandler,
|
||||
locator: &SourceCodeLocator,
|
||||
) -> Option<Range> {
|
||||
let ExcepthandlerKind::ExceptHandler {
|
||||
name, type_, body, ..
|
||||
} = &handler.node;
|
||||
match (name, type_) {
|
||||
(Some(_), Some(type_)) => {
|
||||
let type_end_location = type_.end_location.unwrap();
|
||||
let contents = locator.slice_source_code_range(&Range {
|
||||
location: type_end_location,
|
||||
end_location: body[0].location,
|
||||
});
|
||||
let range = lexer::make_tokenizer(&contents)
|
||||
.flatten()
|
||||
.tuple_windows()
|
||||
.find(|(tok, next_tok)| {
|
||||
matches!(tok.1, Tok::As) && matches!(next_tok.1, Tok::Name { .. })
|
||||
})
|
||||
.map(|((..), (start, _, end))| Range {
|
||||
location: to_absolute(start, type_end_location),
|
||||
end_location: to_absolute(end, type_end_location),
|
||||
});
|
||||
range
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if a `Stmt` appears to be part of a multi-statement line, with
|
||||
/// other statements preceding it.
|
||||
pub fn preceded_by_continuation(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
|
||||
|
||||
27
src/cache.rs
27
src/cache.rs
@@ -3,7 +3,7 @@ use std::fs;
|
||||
use std::fs::{create_dir_all, File, Metadata};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use filetime::FileTime;
|
||||
@@ -36,8 +36,12 @@ struct CheckResult {
|
||||
messages: Vec<Message>,
|
||||
}
|
||||
|
||||
fn cache_dir() -> &'static Path {
|
||||
Path::new(CACHE_DIR.as_ref().map_or(".ruff_cache", String::as_str))
|
||||
/// Return the cache directory for a given project root. Defers to the
|
||||
/// `RUFF_CACHE_DIR` environment variable, if set.
|
||||
pub fn cache_dir(project_root: &Path) -> PathBuf {
|
||||
CACHE_DIR
|
||||
.as_ref()
|
||||
.map_or_else(|| project_root.join(".ruff_cache"), PathBuf::from)
|
||||
}
|
||||
|
||||
fn content_dir() -> &'static Path {
|
||||
@@ -53,10 +57,8 @@ fn cache_key<P: AsRef<Path>>(path: P, settings: &Settings, autofix: fixer::Mode)
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
/// Initialize the cache directory.
|
||||
pub fn init() -> Result<()> {
|
||||
let path = cache_dir();
|
||||
|
||||
/// Initialize the cache at the specified `Path`.
|
||||
pub fn init(path: &Path) -> Result<()> {
|
||||
// Create the cache directories.
|
||||
create_dir_all(path.join(content_dir()))?;
|
||||
|
||||
@@ -75,15 +77,15 @@ pub fn init() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_sync(key: u64, value: &[u8]) -> Result<(), std::io::Error> {
|
||||
fn write_sync(cache_dir: &Path, key: u64, value: &[u8]) -> Result<(), std::io::Error> {
|
||||
fs::write(
|
||||
cache_dir().join(content_dir()).join(format!("{key:x}")),
|
||||
cache_dir.join(content_dir()).join(format!("{key:x}")),
|
||||
value,
|
||||
)
|
||||
}
|
||||
|
||||
fn read_sync(key: u64) -> Result<Vec<u8>, std::io::Error> {
|
||||
fs::read(cache_dir().join(content_dir()).join(format!("{key:x}")))
|
||||
fn read_sync(cache_dir: &Path, key: u64) -> Result<Vec<u8>, std::io::Error> {
|
||||
fs::read(cache_dir.join(content_dir()).join(format!("{key:x}")))
|
||||
}
|
||||
|
||||
/// Get a value from the cache.
|
||||
@@ -98,7 +100,7 @@ pub fn get<P: AsRef<Path>>(
|
||||
return None;
|
||||
};
|
||||
|
||||
let encoded = read_sync(cache_key(path, settings, autofix)).ok()?;
|
||||
let encoded = read_sync(&settings.cache_dir, cache_key(path, settings, autofix)).ok()?;
|
||||
let (mtime, messages) = match bincode::deserialize::<CheckResult>(&encoded[..]) {
|
||||
Ok(CheckResult {
|
||||
metadata: CacheMetadata { mtime },
|
||||
@@ -135,6 +137,7 @@ pub fn set<P: AsRef<Path>>(
|
||||
messages,
|
||||
};
|
||||
if let Err(e) = write_sync(
|
||||
&settings.cache_dir,
|
||||
cache_key(path, settings, autofix),
|
||||
&bincode::serialize(&check_result).unwrap(),
|
||||
) {
|
||||
|
||||
@@ -1067,17 +1067,11 @@ where
|
||||
}
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::EM101)
|
||||
| self.settings.enabled.contains(&CheckCode::EM102)
|
||||
| self.settings.enabled.contains(&CheckCode::EM103)
|
||||
|| self.settings.enabled.contains(&CheckCode::EM102)
|
||||
|| self.settings.enabled.contains(&CheckCode::EM103)
|
||||
{
|
||||
if let Some(exc) = exc {
|
||||
self.add_checks(
|
||||
flake8_errmsg::checks::check_string_in_exception(
|
||||
exc,
|
||||
self.settings.flake8_errmsg.max_string_length,
|
||||
)
|
||||
.into_iter(),
|
||||
);
|
||||
flake8_errmsg::plugins::string_in_exception(self, exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1157,7 +1151,7 @@ where
|
||||
if self.settings.enabled.contains(&CheckCode::B014)
|
||||
|| self.settings.enabled.contains(&CheckCode::B025)
|
||||
{
|
||||
flake8_bugbear::plugins::duplicate_exceptions(self, stmt, handlers);
|
||||
flake8_bugbear::plugins::duplicate_exceptions(self, handlers);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::B013) {
|
||||
flake8_bugbear::plugins::redundant_tuple_in_exception_handler(self, handlers);
|
||||
@@ -1493,6 +1487,10 @@ where
|
||||
ExprKind::Name { id, ctx } => {
|
||||
match ctx {
|
||||
ExprContext::Load => {
|
||||
if self.settings.enabled.contains(&CheckCode::UP019) {
|
||||
pyupgrade::plugins::typing_text_str_alias(self, expr);
|
||||
}
|
||||
|
||||
// Ex) List[...]
|
||||
if !self.in_deferred_string_type_definition
|
||||
&& self.settings.enabled.contains(&CheckCode::UP006)
|
||||
@@ -1549,6 +1547,9 @@ where
|
||||
pyupgrade::plugins::use_pep585_annotation(self, expr, attr);
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::UP019) {
|
||||
pyupgrade::plugins::typing_text_str_alias(self, expr);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::UP016) {
|
||||
pyupgrade::plugins::remove_six_compat(self, expr);
|
||||
}
|
||||
@@ -1650,6 +1651,9 @@ where
|
||||
if self.settings.enabled.contains(&CheckCode::UP016) {
|
||||
pyupgrade::plugins::remove_six_compat(self, expr);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::UP018) {
|
||||
pyupgrade::plugins::native_literals(self, expr, func, args, keywords);
|
||||
}
|
||||
|
||||
// flake8-super
|
||||
if self.settings.enabled.contains(&CheckCode::UP008) {
|
||||
@@ -2667,12 +2671,15 @@ where
|
||||
|
||||
self.check_builtin_shadowing(name, excepthandler, false);
|
||||
|
||||
let name_range =
|
||||
helpers::excepthandler_name_range(excepthandler, self.locator).unwrap();
|
||||
|
||||
if self.current_scope().values.contains_key(&name.as_str()) {
|
||||
self.handle_node_store(
|
||||
name,
|
||||
&Expr::new(
|
||||
excepthandler.location,
|
||||
excepthandler.end_location.unwrap(),
|
||||
name_range.location,
|
||||
name_range.end_location,
|
||||
ExprKind::Name {
|
||||
id: name.to_string(),
|
||||
ctx: ExprContext::Store,
|
||||
@@ -2685,8 +2692,8 @@ where
|
||||
self.handle_node_store(
|
||||
name,
|
||||
&Expr::new(
|
||||
excepthandler.location,
|
||||
excepthandler.end_location.unwrap(),
|
||||
name_range.location,
|
||||
name_range.end_location,
|
||||
ExprKind::Name {
|
||||
id: name.to_string(),
|
||||
ctx: ExprContext::Store,
|
||||
@@ -2705,7 +2712,7 @@ where
|
||||
if self.settings.enabled.contains(&CheckCode::F841) {
|
||||
self.add_check(Check::new(
|
||||
CheckKind::UnusedVariable(name.to_string()),
|
||||
Range::from_located(excepthandler),
|
||||
name_range,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,11 @@ pub fn check_lines(
|
||||
}
|
||||
|
||||
if enforce_no_newline_at_end_of_file {
|
||||
if let Some(check) = no_newline_at_end_of_file(contents) {
|
||||
if let Some(check) = no_newline_at_end_of_file(
|
||||
contents,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.fixable.contains(&CheckCode::W292),
|
||||
) {
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
//! `NoQA` enforcement and validation.
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use nohash_hasher::IntMap;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::checks::{Check, CheckCode, CheckKind, CODE_REDIRECTS};
|
||||
use crate::checks::{Check, CheckCode, CheckKind, UnusedCodes, CODE_REDIRECTS};
|
||||
use crate::noqa;
|
||||
use crate::noqa::{is_file_exempt, Directive};
|
||||
use crate::settings::{flags, Settings};
|
||||
@@ -98,18 +100,29 @@ pub fn check_noqa(
|
||||
}
|
||||
}
|
||||
Directive::Codes(spaces, start, end, codes) => {
|
||||
let mut invalid_codes = vec![];
|
||||
let mut disabled_codes = vec![];
|
||||
let mut unknown_codes = vec![];
|
||||
let mut unmatched_codes = vec![];
|
||||
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 == CheckCode::RUF100.as_ref() {
|
||||
self_ignore = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if matches.contains(&code) || settings.external.contains(code) {
|
||||
valid_codes.push(code);
|
||||
} else {
|
||||
if matches.contains(&code) || settings.external.contains(code) {
|
||||
valid_codes.push(code);
|
||||
if let Ok(check_code) = CheckCode::from_str(code) {
|
||||
if settings.enabled.contains(&check_code) {
|
||||
unmatched_codes.push(code);
|
||||
} else {
|
||||
disabled_codes.push(code);
|
||||
}
|
||||
} else {
|
||||
invalid_codes.push(code);
|
||||
unknown_codes.push(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,14 +131,25 @@ pub fn check_noqa(
|
||||
continue;
|
||||
}
|
||||
|
||||
if !invalid_codes.is_empty() {
|
||||
if !(disabled_codes.is_empty()
|
||||
&& unknown_codes.is_empty()
|
||||
&& unmatched_codes.is_empty())
|
||||
{
|
||||
let mut check = Check::new(
|
||||
CheckKind::UnusedNOQA(Some(
|
||||
invalid_codes
|
||||
CheckKind::UnusedNOQA(Some(UnusedCodes {
|
||||
disabled: disabled_codes
|
||||
.iter()
|
||||
.map(|code| (*code).to_string())
|
||||
.collect(),
|
||||
)),
|
||||
unknown: unknown_codes
|
||||
.iter()
|
||||
.map(|code| (*code).to_string())
|
||||
.collect(),
|
||||
unmatched: unmatched_codes
|
||||
.iter()
|
||||
.map(|code| (*code).to_string())
|
||||
.collect(),
|
||||
})),
|
||||
Range {
|
||||
location: Location::new(row + 1, start),
|
||||
end_location: Location::new(row + 1, end),
|
||||
|
||||
@@ -89,7 +89,11 @@ pub fn check_tokens(
|
||||
if enforce_invalid_escape_sequence {
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
checks.extend(pycodestyle::checks::invalid_escape_sequence(
|
||||
locator, start, end,
|
||||
locator,
|
||||
start,
|
||||
end,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.fixable.contains(&CheckCode::W605),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
277
src/checks.rs
277
src/checks.rs
@@ -1,5 +1,4 @@
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
@@ -226,6 +225,8 @@ pub enum CheckCode {
|
||||
UP015,
|
||||
UP016,
|
||||
UP017,
|
||||
UP018,
|
||||
UP019,
|
||||
// pydocstyle
|
||||
D100,
|
||||
D101,
|
||||
@@ -635,6 +636,13 @@ impl fmt::Display for Branch {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct UnusedCodes {
|
||||
pub unknown: Vec<String>,
|
||||
pub disabled: Vec<String>,
|
||||
pub unmatched: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(AsRefStr, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum CheckKind {
|
||||
// pycodestyle errors
|
||||
@@ -815,6 +823,7 @@ pub enum CheckKind {
|
||||
// pyupgrade
|
||||
TypeOfPrimitive(Primitive),
|
||||
UselessMetaclassType,
|
||||
TypingTextStrAlias,
|
||||
DeprecatedUnittestAlias(String, String),
|
||||
UselessObjectInheritance(String),
|
||||
UsePEP585Annotation(String),
|
||||
@@ -829,6 +838,7 @@ pub enum CheckKind {
|
||||
RedundantOpenModes,
|
||||
RemoveSixCompat,
|
||||
DatetimeTimezoneUTC,
|
||||
NativeLiterals,
|
||||
// pydocstyle
|
||||
BlankLineAfterLastSection(String),
|
||||
BlankLineAfterSection(String),
|
||||
@@ -941,7 +951,7 @@ pub enum CheckKind {
|
||||
AmbiguousUnicodeCharacterString(char, char),
|
||||
AmbiguousUnicodeCharacterDocstring(char, char),
|
||||
AmbiguousUnicodeCharacterComment(char, char),
|
||||
UnusedNOQA(Option<Vec<String>>),
|
||||
UnusedNOQA(Option<UnusedCodes>),
|
||||
// flake8-datetimez
|
||||
CallDatetimeWithoutTzinfo,
|
||||
CallDatetimeToday,
|
||||
@@ -1203,6 +1213,8 @@ impl CheckCode {
|
||||
CheckCode::UP015 => CheckKind::RedundantOpenModes,
|
||||
CheckCode::UP016 => CheckKind::RemoveSixCompat,
|
||||
CheckCode::UP017 => CheckKind::DatetimeTimezoneUTC,
|
||||
CheckCode::UP018 => CheckKind::NativeLiterals,
|
||||
CheckCode::UP019 => CheckKind::TypingTextStrAlias,
|
||||
// pydocstyle
|
||||
CheckCode::D100 => CheckKind::PublicModule,
|
||||
CheckCode::D101 => CheckKind::PublicClass,
|
||||
@@ -1621,6 +1633,8 @@ impl CheckCode {
|
||||
CheckCode::UP015 => CheckCategory::Pyupgrade,
|
||||
CheckCode::UP016 => CheckCategory::Pyupgrade,
|
||||
CheckCode::UP017 => CheckCategory::Pyupgrade,
|
||||
CheckCode::UP018 => CheckCategory::Pyupgrade,
|
||||
CheckCode::UP019 => CheckCategory::Pyupgrade,
|
||||
CheckCode::W292 => CheckCategory::Pycodestyle,
|
||||
CheckCode::W605 => CheckCategory::Pycodestyle,
|
||||
CheckCode::YTT101 => CheckCategory::Flake82020,
|
||||
@@ -1642,9 +1656,9 @@ impl CheckKind {
|
||||
pub fn code(&self) -> &'static CheckCode {
|
||||
match self {
|
||||
// pycodestyle errors
|
||||
CheckKind::AmbiguousClassName(_) => &CheckCode::E742,
|
||||
CheckKind::AmbiguousFunctionName(_) => &CheckCode::E743,
|
||||
CheckKind::AmbiguousVariableName(_) => &CheckCode::E741,
|
||||
CheckKind::AmbiguousClassName(..) => &CheckCode::E742,
|
||||
CheckKind::AmbiguousFunctionName(..) => &CheckCode::E743,
|
||||
CheckKind::AmbiguousVariableName(..) => &CheckCode::E741,
|
||||
CheckKind::AssertTuple => &CheckCode::F631,
|
||||
CheckKind::BreakOutsideLoop => &CheckCode::F701,
|
||||
CheckKind::ContinueOutsideLoop => &CheckCode::F702,
|
||||
@@ -1653,14 +1667,14 @@ impl CheckKind {
|
||||
CheckKind::DoNotUseBareExcept => &CheckCode::E722,
|
||||
CheckKind::DuplicateArgumentName => &CheckCode::F831,
|
||||
CheckKind::FStringMissingPlaceholders => &CheckCode::F541,
|
||||
CheckKind::ForwardAnnotationSyntaxError(_) => &CheckCode::F722,
|
||||
CheckKind::FutureFeatureNotDefined(_) => &CheckCode::F407,
|
||||
CheckKind::IOError(_) => &CheckCode::E902,
|
||||
CheckKind::ForwardAnnotationSyntaxError(..) => &CheckCode::F722,
|
||||
CheckKind::FutureFeatureNotDefined(..) => &CheckCode::F407,
|
||||
CheckKind::IOError(..) => &CheckCode::E902,
|
||||
CheckKind::IfTuple => &CheckCode::F634,
|
||||
CheckKind::ImportShadowedByLoopVar(..) => &CheckCode::F402,
|
||||
CheckKind::ImportStarNotPermitted(_) => &CheckCode::F406,
|
||||
CheckKind::ImportStarNotPermitted(..) => &CheckCode::F406,
|
||||
CheckKind::ImportStarUsage(..) => &CheckCode::F405,
|
||||
CheckKind::ImportStarUsed(_) => &CheckCode::F403,
|
||||
CheckKind::ImportStarUsed(..) => &CheckCode::F403,
|
||||
CheckKind::InvalidPrintSyntax => &CheckCode::F633,
|
||||
CheckKind::IsLiteral => &CheckCode::F632,
|
||||
CheckKind::LateFutureImport => &CheckCode::F404,
|
||||
@@ -1668,42 +1682,42 @@ impl CheckKind {
|
||||
CheckKind::MultipleImportsOnOneLine => &CheckCode::E401,
|
||||
CheckKind::ModuleImportNotAtTopOfFile => &CheckCode::E402,
|
||||
CheckKind::MultiValueRepeatedKeyLiteral => &CheckCode::F601,
|
||||
CheckKind::MultiValueRepeatedKeyVariable(_) => &CheckCode::F602,
|
||||
CheckKind::NoneComparison(_) => &CheckCode::E711,
|
||||
CheckKind::MultiValueRepeatedKeyVariable(..) => &CheckCode::F602,
|
||||
CheckKind::NoneComparison(..) => &CheckCode::E711,
|
||||
CheckKind::NotInTest => &CheckCode::E713,
|
||||
CheckKind::NotIsTest => &CheckCode::E714,
|
||||
CheckKind::PercentFormatExpectedMapping => &CheckCode::F502,
|
||||
CheckKind::PercentFormatExpectedSequence => &CheckCode::F503,
|
||||
CheckKind::PercentFormatExtraNamedArguments(_) => &CheckCode::F504,
|
||||
CheckKind::PercentFormatInvalidFormat(_) => &CheckCode::F501,
|
||||
CheckKind::PercentFormatMissingArgument(_) => &CheckCode::F505,
|
||||
CheckKind::PercentFormatExtraNamedArguments(..) => &CheckCode::F504,
|
||||
CheckKind::PercentFormatInvalidFormat(..) => &CheckCode::F501,
|
||||
CheckKind::PercentFormatMissingArgument(..) => &CheckCode::F505,
|
||||
CheckKind::PercentFormatMixedPositionalAndNamed => &CheckCode::F506,
|
||||
CheckKind::PercentFormatPositionalCountMismatch(..) => &CheckCode::F507,
|
||||
CheckKind::PercentFormatStarRequiresSequence => &CheckCode::F508,
|
||||
CheckKind::PercentFormatUnsupportedFormatCharacter(_) => &CheckCode::F509,
|
||||
CheckKind::PercentFormatUnsupportedFormatCharacter(..) => &CheckCode::F509,
|
||||
CheckKind::RaiseNotImplemented => &CheckCode::F901,
|
||||
CheckKind::ReturnOutsideFunction => &CheckCode::F706,
|
||||
CheckKind::StringDotFormatExtraNamedArguments(_) => &CheckCode::F522,
|
||||
CheckKind::StringDotFormatExtraPositionalArguments(_) => &CheckCode::F523,
|
||||
CheckKind::StringDotFormatInvalidFormat(_) => &CheckCode::F521,
|
||||
CheckKind::StringDotFormatMissingArguments(_) => &CheckCode::F524,
|
||||
CheckKind::StringDotFormatExtraNamedArguments(..) => &CheckCode::F522,
|
||||
CheckKind::StringDotFormatExtraPositionalArguments(..) => &CheckCode::F523,
|
||||
CheckKind::StringDotFormatInvalidFormat(..) => &CheckCode::F521,
|
||||
CheckKind::StringDotFormatMissingArguments(..) => &CheckCode::F524,
|
||||
CheckKind::StringDotFormatMixingAutomatic => &CheckCode::F525,
|
||||
CheckKind::SyntaxError(_) => &CheckCode::E999,
|
||||
CheckKind::SyntaxError(..) => &CheckCode::E999,
|
||||
CheckKind::ExpressionsInStarAssignment => &CheckCode::F621,
|
||||
CheckKind::TrueFalseComparison(..) => &CheckCode::E712,
|
||||
CheckKind::TwoStarredExpressions => &CheckCode::F622,
|
||||
CheckKind::TypeComparison => &CheckCode::E721,
|
||||
CheckKind::UndefinedExport(_) => &CheckCode::F822,
|
||||
CheckKind::UndefinedLocal(_) => &CheckCode::F823,
|
||||
CheckKind::UndefinedExport(..) => &CheckCode::F822,
|
||||
CheckKind::UndefinedLocal(..) => &CheckCode::F823,
|
||||
CheckKind::RedefinedWhileUnused(..) => &CheckCode::F811,
|
||||
CheckKind::UndefinedName(_) => &CheckCode::F821,
|
||||
CheckKind::UndefinedName(..) => &CheckCode::F821,
|
||||
CheckKind::UnusedImport(..) => &CheckCode::F401,
|
||||
CheckKind::UnusedVariable(_) => &CheckCode::F841,
|
||||
CheckKind::UnusedAnnotation(_) => &CheckCode::F842,
|
||||
CheckKind::YieldOutsideFunction(_) => &CheckCode::F704,
|
||||
CheckKind::UnusedVariable(..) => &CheckCode::F841,
|
||||
CheckKind::UnusedAnnotation(..) => &CheckCode::F842,
|
||||
CheckKind::YieldOutsideFunction(..) => &CheckCode::F704,
|
||||
// pycodestyle warnings
|
||||
CheckKind::NoNewLineAtEndOfFile => &CheckCode::W292,
|
||||
CheckKind::InvalidEscapeSequence(_) => &CheckCode::W605,
|
||||
CheckKind::InvalidEscapeSequence(..) => &CheckCode::W605,
|
||||
// pylint
|
||||
CheckKind::AwaitOutsideAsync => &CheckCode::PLE1142,
|
||||
CheckKind::ConsiderMergingIsinstance(..) => &CheckCode::PLR1701,
|
||||
@@ -1712,96 +1726,96 @@ impl CheckKind {
|
||||
CheckKind::MisplacedComparisonConstant(..) => &CheckCode::PLC2201,
|
||||
CheckKind::PropertyWithParameters => &CheckCode::PLR0206,
|
||||
CheckKind::UnnecessaryDirectLambdaCall => &CheckCode::PLC3002,
|
||||
CheckKind::UseSysExit(_) => &CheckCode::PLR1722,
|
||||
CheckKind::UseSysExit(..) => &CheckCode::PLR1722,
|
||||
CheckKind::NonlocalWithoutBinding(..) => &CheckCode::PLE0117,
|
||||
CheckKind::UsedPriorGlobalDeclaration(..) => &CheckCode::PLE0118,
|
||||
CheckKind::UselessElseOnLoop => &CheckCode::PLW0120,
|
||||
CheckKind::UselessImportAlias => &CheckCode::PLC0414,
|
||||
// flake8-builtins
|
||||
CheckKind::BuiltinVariableShadowing(_) => &CheckCode::A001,
|
||||
CheckKind::BuiltinArgumentShadowing(_) => &CheckCode::A002,
|
||||
CheckKind::BuiltinAttributeShadowing(_) => &CheckCode::A003,
|
||||
CheckKind::BuiltinVariableShadowing(..) => &CheckCode::A001,
|
||||
CheckKind::BuiltinArgumentShadowing(..) => &CheckCode::A002,
|
||||
CheckKind::BuiltinAttributeShadowing(..) => &CheckCode::A003,
|
||||
// flake8-bugbear
|
||||
CheckKind::AbstractBaseClassWithoutAbstractMethod(_) => &CheckCode::B024,
|
||||
CheckKind::AbstractBaseClassWithoutAbstractMethod(..) => &CheckCode::B024,
|
||||
CheckKind::AssignmentToOsEnviron => &CheckCode::B003,
|
||||
CheckKind::CachedInstanceMethod => &CheckCode::B019,
|
||||
CheckKind::CannotRaiseLiteral => &CheckCode::B016,
|
||||
CheckKind::DoNotAssertFalse => &CheckCode::B011,
|
||||
CheckKind::DuplicateHandlerException(_) => &CheckCode::B014,
|
||||
CheckKind::DuplicateTryBlockException(_) => &CheckCode::B025,
|
||||
CheckKind::EmptyMethodWithoutAbstractDecorator(_) => &CheckCode::B027,
|
||||
CheckKind::DuplicateHandlerException(..) => &CheckCode::B014,
|
||||
CheckKind::DuplicateTryBlockException(..) => &CheckCode::B025,
|
||||
CheckKind::EmptyMethodWithoutAbstractDecorator(..) => &CheckCode::B027,
|
||||
CheckKind::FStringDocstring => &CheckCode::B021,
|
||||
CheckKind::FunctionCallArgumentDefault(_) => &CheckCode::B008,
|
||||
CheckKind::FunctionUsesLoopVariable(_) => &CheckCode::B023,
|
||||
CheckKind::FunctionCallArgumentDefault(..) => &CheckCode::B008,
|
||||
CheckKind::FunctionUsesLoopVariable(..) => &CheckCode::B023,
|
||||
CheckKind::GetAttrWithConstant => &CheckCode::B009,
|
||||
CheckKind::JumpStatementInFinally(_) => &CheckCode::B012,
|
||||
CheckKind::LoopVariableOverridesIterator(_) => &CheckCode::B020,
|
||||
CheckKind::JumpStatementInFinally(..) => &CheckCode::B012,
|
||||
CheckKind::LoopVariableOverridesIterator(..) => &CheckCode::B020,
|
||||
CheckKind::MutableArgumentDefault => &CheckCode::B006,
|
||||
CheckKind::NoAssertRaisesException => &CheckCode::B017,
|
||||
CheckKind::RaiseWithoutFromInsideExcept => &CheckCode::B904,
|
||||
CheckKind::ZipWithoutExplicitStrict => &CheckCode::B905,
|
||||
CheckKind::RedundantTupleInExceptionHandler(_) => &CheckCode::B013,
|
||||
CheckKind::RedundantTupleInExceptionHandler(..) => &CheckCode::B013,
|
||||
CheckKind::SetAttrWithConstant => &CheckCode::B010,
|
||||
CheckKind::StarArgUnpackingAfterKeywordArg => &CheckCode::B026,
|
||||
CheckKind::StripWithMultiCharacters => &CheckCode::B005,
|
||||
CheckKind::UnaryPrefixIncrement => &CheckCode::B002,
|
||||
CheckKind::UnreliableCallableCheck => &CheckCode::B004,
|
||||
CheckKind::UnusedLoopControlVariable(_) => &CheckCode::B007,
|
||||
CheckKind::UnusedLoopControlVariable(..) => &CheckCode::B007,
|
||||
CheckKind::UselessComparison => &CheckCode::B015,
|
||||
CheckKind::UselessContextlibSuppress => &CheckCode::B022,
|
||||
CheckKind::UselessExpression => &CheckCode::B018,
|
||||
// flake8-blind-except
|
||||
CheckKind::BlindExcept(_) => &CheckCode::BLE001,
|
||||
CheckKind::BlindExcept(..) => &CheckCode::BLE001,
|
||||
// flake8-comprehensions
|
||||
CheckKind::UnnecessaryGeneratorList => &CheckCode::C400,
|
||||
CheckKind::UnnecessaryGeneratorSet => &CheckCode::C401,
|
||||
CheckKind::UnnecessaryGeneratorDict => &CheckCode::C402,
|
||||
CheckKind::UnnecessaryListComprehensionSet => &CheckCode::C403,
|
||||
CheckKind::UnnecessaryListComprehensionDict => &CheckCode::C404,
|
||||
CheckKind::UnnecessaryLiteralSet(_) => &CheckCode::C405,
|
||||
CheckKind::UnnecessaryLiteralDict(_) => &CheckCode::C406,
|
||||
CheckKind::UnnecessaryCollectionCall(_) => &CheckCode::C408,
|
||||
CheckKind::UnnecessaryLiteralSet(..) => &CheckCode::C405,
|
||||
CheckKind::UnnecessaryLiteralDict(..) => &CheckCode::C406,
|
||||
CheckKind::UnnecessaryCollectionCall(..) => &CheckCode::C408,
|
||||
CheckKind::UnnecessaryLiteralWithinTupleCall(..) => &CheckCode::C409,
|
||||
CheckKind::UnnecessaryLiteralWithinListCall(..) => &CheckCode::C410,
|
||||
CheckKind::UnnecessaryListCall => &CheckCode::C411,
|
||||
CheckKind::UnnecessaryCallAroundSorted(_) => &CheckCode::C413,
|
||||
CheckKind::UnnecessaryCallAroundSorted(..) => &CheckCode::C413,
|
||||
CheckKind::UnnecessaryDoubleCastOrProcess(..) => &CheckCode::C414,
|
||||
CheckKind::UnnecessarySubscriptReversal(_) => &CheckCode::C415,
|
||||
CheckKind::UnnecessarySubscriptReversal(..) => &CheckCode::C415,
|
||||
CheckKind::UnnecessaryComprehension(..) => &CheckCode::C416,
|
||||
CheckKind::UnnecessaryMap(_) => &CheckCode::C417,
|
||||
CheckKind::UnnecessaryMap(..) => &CheckCode::C417,
|
||||
// flake8-debugger
|
||||
CheckKind::Debugger(_) => &CheckCode::T100,
|
||||
CheckKind::Debugger(..) => &CheckCode::T100,
|
||||
// flake8-tidy-imports
|
||||
CheckKind::BannedRelativeImport(_) => &CheckCode::TID252,
|
||||
CheckKind::BannedRelativeImport(..) => &CheckCode::TID252,
|
||||
// flake8-return
|
||||
CheckKind::UnnecessaryReturnNone => &CheckCode::RET501,
|
||||
CheckKind::ImplicitReturnValue => &CheckCode::RET502,
|
||||
CheckKind::ImplicitReturn => &CheckCode::RET503,
|
||||
CheckKind::UnnecessaryAssign => &CheckCode::RET504,
|
||||
CheckKind::SuperfluousElseReturn(_) => &CheckCode::RET505,
|
||||
CheckKind::SuperfluousElseRaise(_) => &CheckCode::RET506,
|
||||
CheckKind::SuperfluousElseContinue(_) => &CheckCode::RET507,
|
||||
CheckKind::SuperfluousElseBreak(_) => &CheckCode::RET508,
|
||||
CheckKind::SuperfluousElseReturn(..) => &CheckCode::RET505,
|
||||
CheckKind::SuperfluousElseRaise(..) => &CheckCode::RET506,
|
||||
CheckKind::SuperfluousElseContinue(..) => &CheckCode::RET507,
|
||||
CheckKind::SuperfluousElseBreak(..) => &CheckCode::RET508,
|
||||
// flake8-print
|
||||
CheckKind::PrintFound => &CheckCode::T201,
|
||||
CheckKind::PPrintFound => &CheckCode::T203,
|
||||
// flake8-quotes
|
||||
CheckKind::BadQuotesInlineString(_) => &CheckCode::Q000,
|
||||
CheckKind::BadQuotesMultilineString(_) => &CheckCode::Q001,
|
||||
CheckKind::BadQuotesDocstring(_) => &CheckCode::Q002,
|
||||
CheckKind::BadQuotesInlineString(..) => &CheckCode::Q000,
|
||||
CheckKind::BadQuotesMultilineString(..) => &CheckCode::Q001,
|
||||
CheckKind::BadQuotesDocstring(..) => &CheckCode::Q002,
|
||||
CheckKind::AvoidQuoteEscape => &CheckCode::Q003,
|
||||
// flake8-annotations
|
||||
CheckKind::MissingTypeFunctionArgument(_) => &CheckCode::ANN001,
|
||||
CheckKind::MissingTypeArgs(_) => &CheckCode::ANN002,
|
||||
CheckKind::MissingTypeKwargs(_) => &CheckCode::ANN003,
|
||||
CheckKind::MissingTypeSelf(_) => &CheckCode::ANN101,
|
||||
CheckKind::MissingTypeCls(_) => &CheckCode::ANN102,
|
||||
CheckKind::MissingReturnTypePublicFunction(_) => &CheckCode::ANN201,
|
||||
CheckKind::MissingReturnTypePrivateFunction(_) => &CheckCode::ANN202,
|
||||
CheckKind::MissingReturnTypeSpecialMethod(_) => &CheckCode::ANN204,
|
||||
CheckKind::MissingReturnTypeStaticMethod(_) => &CheckCode::ANN205,
|
||||
CheckKind::MissingReturnTypeClassMethod(_) => &CheckCode::ANN206,
|
||||
CheckKind::DynamicallyTypedExpression(_) => &CheckCode::ANN401,
|
||||
CheckKind::MissingTypeFunctionArgument(..) => &CheckCode::ANN001,
|
||||
CheckKind::MissingTypeArgs(..) => &CheckCode::ANN002,
|
||||
CheckKind::MissingTypeKwargs(..) => &CheckCode::ANN003,
|
||||
CheckKind::MissingTypeSelf(..) => &CheckCode::ANN101,
|
||||
CheckKind::MissingTypeCls(..) => &CheckCode::ANN102,
|
||||
CheckKind::MissingReturnTypePublicFunction(..) => &CheckCode::ANN201,
|
||||
CheckKind::MissingReturnTypePrivateFunction(..) => &CheckCode::ANN202,
|
||||
CheckKind::MissingReturnTypeSpecialMethod(..) => &CheckCode::ANN204,
|
||||
CheckKind::MissingReturnTypeStaticMethod(..) => &CheckCode::ANN205,
|
||||
CheckKind::MissingReturnTypeClassMethod(..) => &CheckCode::ANN206,
|
||||
CheckKind::DynamicallyTypedExpression(..) => &CheckCode::ANN401,
|
||||
// flake8-2020
|
||||
CheckKind::SysVersionSlice3Referenced => &CheckCode::YTT101,
|
||||
CheckKind::SysVersion2Referenced => &CheckCode::YTT102,
|
||||
@@ -1816,29 +1830,31 @@ impl CheckKind {
|
||||
// flake8-simplify
|
||||
CheckKind::KeyInDict(..) => &CheckCode::SIM118,
|
||||
// pyupgrade
|
||||
CheckKind::TypeOfPrimitive(_) => &CheckCode::UP003,
|
||||
CheckKind::TypeOfPrimitive(..) => &CheckCode::UP003,
|
||||
CheckKind::UselessMetaclassType => &CheckCode::UP001,
|
||||
CheckKind::DeprecatedUnittestAlias(..) => &CheckCode::UP005,
|
||||
CheckKind::UsePEP585Annotation(_) => &CheckCode::UP006,
|
||||
CheckKind::UsePEP585Annotation(..) => &CheckCode::UP006,
|
||||
CheckKind::UsePEP604Annotation => &CheckCode::UP007,
|
||||
CheckKind::UselessObjectInheritance(_) => &CheckCode::UP004,
|
||||
CheckKind::UselessObjectInheritance(..) => &CheckCode::UP004,
|
||||
CheckKind::SuperCallWithParameters => &CheckCode::UP008,
|
||||
CheckKind::PEP3120UnnecessaryCodingComment => &CheckCode::UP009,
|
||||
CheckKind::UnnecessaryFutureImport(_) => &CheckCode::UP010,
|
||||
CheckKind::UnnecessaryFutureImport(..) => &CheckCode::UP010,
|
||||
CheckKind::UnnecessaryLRUCacheParams => &CheckCode::UP011,
|
||||
CheckKind::UnnecessaryEncodeUTF8 => &CheckCode::UP012,
|
||||
CheckKind::ConvertTypedDictFunctionalToClass(_) => &CheckCode::UP013,
|
||||
CheckKind::ConvertNamedTupleFunctionalToClass(_) => &CheckCode::UP014,
|
||||
CheckKind::ConvertTypedDictFunctionalToClass(..) => &CheckCode::UP013,
|
||||
CheckKind::ConvertNamedTupleFunctionalToClass(..) => &CheckCode::UP014,
|
||||
CheckKind::RedundantOpenModes => &CheckCode::UP015,
|
||||
CheckKind::RemoveSixCompat => &CheckCode::UP016,
|
||||
CheckKind::DatetimeTimezoneUTC => &CheckCode::UP017,
|
||||
CheckKind::NativeLiterals => &CheckCode::UP018,
|
||||
CheckKind::TypingTextStrAlias => &CheckCode::UP019,
|
||||
// pydocstyle
|
||||
CheckKind::BlankLineAfterLastSection(_) => &CheckCode::D413,
|
||||
CheckKind::BlankLineAfterSection(_) => &CheckCode::D410,
|
||||
CheckKind::BlankLineBeforeSection(_) => &CheckCode::D411,
|
||||
CheckKind::CapitalizeSectionName(_) => &CheckCode::D405,
|
||||
CheckKind::DashedUnderlineAfterSection(_) => &CheckCode::D407,
|
||||
CheckKind::DocumentAllArguments(_) => &CheckCode::D417,
|
||||
CheckKind::BlankLineAfterLastSection(..) => &CheckCode::D413,
|
||||
CheckKind::BlankLineAfterSection(..) => &CheckCode::D410,
|
||||
CheckKind::BlankLineBeforeSection(..) => &CheckCode::D411,
|
||||
CheckKind::CapitalizeSectionName(..) => &CheckCode::D405,
|
||||
CheckKind::DashedUnderlineAfterSection(..) => &CheckCode::D407,
|
||||
CheckKind::DocumentAllArguments(..) => &CheckCode::D417,
|
||||
CheckKind::EndsInPeriod => &CheckCode::D400,
|
||||
CheckKind::EndsInPunctuation => &CheckCode::D415,
|
||||
CheckKind::FirstLineCapitalized => &CheckCode::D403,
|
||||
@@ -1848,21 +1864,21 @@ impl CheckKind {
|
||||
CheckKind::MultiLineSummaryFirstLine => &CheckCode::D212,
|
||||
CheckKind::MultiLineSummarySecondLine => &CheckCode::D213,
|
||||
CheckKind::NewLineAfterLastParagraph => &CheckCode::D209,
|
||||
CheckKind::NewLineAfterSectionName(_) => &CheckCode::D406,
|
||||
CheckKind::NoBlankLineAfterFunction(_) => &CheckCode::D202,
|
||||
CheckKind::NewLineAfterSectionName(..) => &CheckCode::D406,
|
||||
CheckKind::NoBlankLineAfterFunction(..) => &CheckCode::D202,
|
||||
CheckKind::BlankLineAfterSummary => &CheckCode::D205,
|
||||
CheckKind::NoBlankLineBeforeClass(_) => &CheckCode::D211,
|
||||
CheckKind::NoBlankLineBeforeFunction(_) => &CheckCode::D201,
|
||||
CheckKind::NoBlankLinesBetweenHeaderAndContent(_) => &CheckCode::D412,
|
||||
CheckKind::NoBlankLineBeforeClass(..) => &CheckCode::D211,
|
||||
CheckKind::NoBlankLineBeforeFunction(..) => &CheckCode::D201,
|
||||
CheckKind::NoBlankLinesBetweenHeaderAndContent(..) => &CheckCode::D412,
|
||||
CheckKind::NoOverIndentation => &CheckCode::D208,
|
||||
CheckKind::NoSignature => &CheckCode::D402,
|
||||
CheckKind::NoSurroundingWhitespace => &CheckCode::D210,
|
||||
CheckKind::NoThisPrefix => &CheckCode::D404,
|
||||
CheckKind::NoUnderIndentation => &CheckCode::D207,
|
||||
CheckKind::NonEmpty => &CheckCode::D419,
|
||||
CheckKind::NonEmptySection(_) => &CheckCode::D414,
|
||||
CheckKind::OneBlankLineAfterClass(_) => &CheckCode::D204,
|
||||
CheckKind::OneBlankLineBeforeClass(_) => &CheckCode::D203,
|
||||
CheckKind::NonEmptySection(..) => &CheckCode::D414,
|
||||
CheckKind::OneBlankLineAfterClass(..) => &CheckCode::D204,
|
||||
CheckKind::OneBlankLineBeforeClass(..) => &CheckCode::D203,
|
||||
CheckKind::PublicClass => &CheckCode::D101,
|
||||
CheckKind::PublicFunction => &CheckCode::D103,
|
||||
CheckKind::PublicInit => &CheckCode::D107,
|
||||
@@ -1870,18 +1886,18 @@ impl CheckKind {
|
||||
CheckKind::PublicModule => &CheckCode::D100,
|
||||
CheckKind::PublicNestedClass => &CheckCode::D106,
|
||||
CheckKind::PublicPackage => &CheckCode::D104,
|
||||
CheckKind::SectionNameEndsInColon(_) => &CheckCode::D416,
|
||||
CheckKind::SectionNotOverIndented(_) => &CheckCode::D214,
|
||||
CheckKind::SectionUnderlineAfterName(_) => &CheckCode::D408,
|
||||
CheckKind::SectionUnderlineMatchesSectionLength(_) => &CheckCode::D409,
|
||||
CheckKind::SectionUnderlineNotOverIndented(_) => &CheckCode::D215,
|
||||
CheckKind::SectionNameEndsInColon(..) => &CheckCode::D416,
|
||||
CheckKind::SectionNotOverIndented(..) => &CheckCode::D214,
|
||||
CheckKind::SectionUnderlineAfterName(..) => &CheckCode::D408,
|
||||
CheckKind::SectionUnderlineMatchesSectionLength(..) => &CheckCode::D409,
|
||||
CheckKind::SectionUnderlineNotOverIndented(..) => &CheckCode::D215,
|
||||
CheckKind::SkipDocstring => &CheckCode::D418,
|
||||
CheckKind::UsesRPrefixForBackslashedContent => &CheckCode::D301,
|
||||
CheckKind::UsesTripleQuotes => &CheckCode::D300,
|
||||
// pep8-naming
|
||||
CheckKind::InvalidClassName(_) => &CheckCode::N801,
|
||||
CheckKind::InvalidFunctionName(_) => &CheckCode::N802,
|
||||
CheckKind::InvalidArgumentName(_) => &CheckCode::N803,
|
||||
CheckKind::InvalidClassName(..) => &CheckCode::N801,
|
||||
CheckKind::InvalidFunctionName(..) => &CheckCode::N802,
|
||||
CheckKind::InvalidArgumentName(..) => &CheckCode::N803,
|
||||
CheckKind::InvalidFirstArgumentNameForClassMethod => &CheckCode::N804,
|
||||
CheckKind::InvalidFirstArgumentNameForMethod => &CheckCode::N805,
|
||||
CheckKind::NonLowercaseVariableInFunction(..) => &CheckCode::N806,
|
||||
@@ -1954,7 +1970,7 @@ impl CheckKind {
|
||||
CheckKind::AmbiguousUnicodeCharacterString(..) => &CheckCode::RUF001,
|
||||
CheckKind::AmbiguousUnicodeCharacterDocstring(..) => &CheckCode::RUF002,
|
||||
CheckKind::AmbiguousUnicodeCharacterComment(..) => &CheckCode::RUF003,
|
||||
CheckKind::UnusedNOQA(_) => &CheckCode::RUF100,
|
||||
CheckKind::UnusedNOQA(..) => &CheckCode::RUF100,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2522,6 +2538,7 @@ impl CheckKind {
|
||||
format!("Use `{}` instead of `type(...)`", primitive.builtin())
|
||||
}
|
||||
CheckKind::UselessMetaclassType => "`__metaclass__ = type` is implied".to_string(),
|
||||
CheckKind::TypingTextStrAlias => "`typing.Text` is deprecated, use `str`".to_string(),
|
||||
CheckKind::DeprecatedUnittestAlias(alias, target) => {
|
||||
format!("`{alias}` is deprecated, use `{target}` instead")
|
||||
}
|
||||
@@ -2555,6 +2572,7 @@ impl CheckKind {
|
||||
CheckKind::RedundantOpenModes => "Unnecessary open mode parameters".to_string(),
|
||||
CheckKind::RemoveSixCompat => "Unnecessary `six` compatibility usage".to_string(),
|
||||
CheckKind::DatetimeTimezoneUTC => "Use `datetime.UTC` alias".to_string(),
|
||||
CheckKind::NativeLiterals => "Unnecessary call to `str` and `bytes`".to_string(),
|
||||
CheckKind::ConvertTypedDictFunctionalToClass(name) => {
|
||||
format!("Convert `{name}` from `TypedDict` functional to class syntax")
|
||||
}
|
||||
@@ -2599,13 +2617,13 @@ impl CheckKind {
|
||||
CheckKind::NoBlankLineAfterFunction(num_lines) => {
|
||||
format!("No blank lines allowed after function docstring (found {num_lines})")
|
||||
}
|
||||
CheckKind::NoBlankLineBeforeClass(_) => {
|
||||
CheckKind::NoBlankLineBeforeClass(..) => {
|
||||
"No blank lines allowed before class docstring".to_string()
|
||||
}
|
||||
CheckKind::OneBlankLineBeforeClass(_) => {
|
||||
CheckKind::OneBlankLineBeforeClass(..) => {
|
||||
"1 blank line required before class docstring".to_string()
|
||||
}
|
||||
CheckKind::OneBlankLineAfterClass(_) => {
|
||||
CheckKind::OneBlankLineAfterClass(..) => {
|
||||
"1 blank line required after class docstring".to_string()
|
||||
}
|
||||
CheckKind::PublicModule => "Missing docstring in public module".to_string(),
|
||||
@@ -2851,19 +2869,44 @@ impl CheckKind {
|
||||
)
|
||||
}
|
||||
CheckKind::UnusedNOQA(codes) => match codes {
|
||||
None => "Unused `noqa` directive".to_string(),
|
||||
None => "Unused blanket `noqa` directive".to_string(),
|
||||
Some(codes) => {
|
||||
let codes = codes
|
||||
.iter()
|
||||
.map(|code| {
|
||||
if CheckCode::from_str(code).is_ok() {
|
||||
code.to_string()
|
||||
} else {
|
||||
format!("{code} (not implemented)")
|
||||
}
|
||||
})
|
||||
.join(", ");
|
||||
format!("Unused `noqa` directive for: {codes}")
|
||||
let mut codes_by_reason = vec![];
|
||||
if !codes.unmatched.is_empty() {
|
||||
codes_by_reason.push(format!(
|
||||
"unused: {}",
|
||||
codes
|
||||
.unmatched
|
||||
.iter()
|
||||
.map(|code| format!("`{code}`"))
|
||||
.join(", ")
|
||||
));
|
||||
}
|
||||
if !codes.disabled.is_empty() {
|
||||
codes_by_reason.push(format!(
|
||||
"non-enabled: {}",
|
||||
codes
|
||||
.disabled
|
||||
.iter()
|
||||
.map(|code| format!("`{code}`"))
|
||||
.join(", ")
|
||||
));
|
||||
}
|
||||
if !codes.unknown.is_empty() {
|
||||
codes_by_reason.push(format!(
|
||||
"unknown: {}",
|
||||
codes
|
||||
.unknown
|
||||
.iter()
|
||||
.map(|code| format!("`{code}`"))
|
||||
.join(", ")
|
||||
));
|
||||
}
|
||||
if codes_by_reason.is_empty() {
|
||||
"Unused `noqa` directive".to_string()
|
||||
} else {
|
||||
format!("Unused `noqa` directive ({})", codes_by_reason.join("; "))
|
||||
}
|
||||
}
|
||||
},
|
||||
// flake8-datetimez
|
||||
@@ -2956,6 +2999,7 @@ impl CheckKind {
|
||||
| CheckKind::ConvertNamedTupleFunctionalToClass(..)
|
||||
| CheckKind::ConvertTypedDictFunctionalToClass(..)
|
||||
| CheckKind::DashedUnderlineAfterSection(..)
|
||||
| CheckKind::DatetimeTimezoneUTC
|
||||
| CheckKind::DeprecatedUnittestAlias(..)
|
||||
| CheckKind::DoNotAssertFalse
|
||||
| CheckKind::DoNotAssignLambda
|
||||
@@ -2965,16 +3009,19 @@ impl CheckKind {
|
||||
| CheckKind::GetAttrWithConstant
|
||||
| CheckKind::ImplicitReturn
|
||||
| CheckKind::ImplicitReturnValue
|
||||
| CheckKind::InvalidEscapeSequence(..)
|
||||
| CheckKind::IsLiteral
|
||||
| CheckKind::KeyInDict(..)
|
||||
| CheckKind::MisplacedComparisonConstant(..)
|
||||
| CheckKind::MissingReturnTypeSpecialMethod(..)
|
||||
| CheckKind::NativeLiterals
|
||||
| CheckKind::NewLineAfterLastParagraph
|
||||
| CheckKind::NewLineAfterSectionName(..)
|
||||
| CheckKind::NoBlankLineAfterFunction(..)
|
||||
| CheckKind::NoBlankLineBeforeClass(..)
|
||||
| CheckKind::NoBlankLineBeforeFunction(..)
|
||||
| CheckKind::NoBlankLinesBetweenHeaderAndContent(..)
|
||||
| CheckKind::NoNewLineAtEndOfFile
|
||||
| CheckKind::NoOverIndentation
|
||||
| CheckKind::NoSurroundingWhitespace
|
||||
| CheckKind::NoUnderIndentation
|
||||
@@ -2991,7 +3038,6 @@ impl CheckKind {
|
||||
| CheckKind::RedundantOpenModes
|
||||
| CheckKind::RedundantTupleInExceptionHandler(..)
|
||||
| CheckKind::RemoveSixCompat
|
||||
| CheckKind::DatetimeTimezoneUTC
|
||||
| CheckKind::SectionNameEndsInColon(..)
|
||||
| CheckKind::SectionNotOverIndented(..)
|
||||
| CheckKind::SectionUnderlineAfterName(..)
|
||||
@@ -3002,6 +3048,7 @@ impl CheckKind {
|
||||
| CheckKind::SuperCallWithParameters
|
||||
| CheckKind::TrueFalseComparison(..)
|
||||
| CheckKind::TypeOfPrimitive(..)
|
||||
| CheckKind::TypingTextStrAlias
|
||||
| CheckKind::UnnecessaryCallAroundSorted(..)
|
||||
| CheckKind::UnnecessaryCollectionCall(..)
|
||||
| CheckKind::UnnecessaryComprehension(..)
|
||||
@@ -3076,6 +3123,7 @@ pub static PREFIX_REDIRECTS: Lazy<FxHashMap<&'static str, CheckCodePrefix>> = La
|
||||
("U015", CheckCodePrefix::UP015),
|
||||
("U016", CheckCodePrefix::UP016),
|
||||
("U017", CheckCodePrefix::UP017),
|
||||
("U019", CheckCodePrefix::UP019),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("I252", CheckCodePrefix::TID252),
|
||||
("M001", CheckCodePrefix::RUF100),
|
||||
@@ -3151,6 +3199,7 @@ pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(
|
||||
("U015", CheckCode::UP015),
|
||||
("U016", CheckCode::UP016),
|
||||
("U017", CheckCode::UP017),
|
||||
("U019", CheckCode::UP019),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("I252", CheckCode::TID252),
|
||||
("M001", CheckCode::RUF100),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! File automatically generated by `examples/generate_check_code_prefix.rs`.
|
||||
|
||||
use colored::Colorize;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::{AsRefStr, EnumString};
|
||||
|
||||
@@ -8,7 +9,17 @@ use crate::checks::CheckCode;
|
||||
use crate::one_time_warning;
|
||||
|
||||
#[derive(
|
||||
EnumString, AsRefStr, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize,
|
||||
EnumString,
|
||||
AsRefStr,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Clone,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
JsonSchema,
|
||||
)]
|
||||
pub enum CheckCodePrefix {
|
||||
A,
|
||||
@@ -493,6 +504,7 @@ pub enum CheckCodePrefix {
|
||||
U015,
|
||||
U016,
|
||||
U017,
|
||||
U019,
|
||||
UP,
|
||||
UP0,
|
||||
UP00,
|
||||
@@ -513,6 +525,8 @@ pub enum CheckCodePrefix {
|
||||
UP015,
|
||||
UP016,
|
||||
UP017,
|
||||
UP018,
|
||||
UP019,
|
||||
W,
|
||||
W2,
|
||||
W29,
|
||||
@@ -2088,6 +2102,8 @@ impl CheckCodePrefix {
|
||||
CheckCode::UP015,
|
||||
CheckCode::UP016,
|
||||
CheckCode::UP017,
|
||||
CheckCode::UP018,
|
||||
CheckCode::UP019,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U0 => {
|
||||
@@ -2114,6 +2130,8 @@ impl CheckCodePrefix {
|
||||
CheckCode::UP015,
|
||||
CheckCode::UP016,
|
||||
CheckCode::UP017,
|
||||
CheckCode::UP018,
|
||||
CheckCode::UP019,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U00 => {
|
||||
@@ -2222,6 +2240,8 @@ impl CheckCodePrefix {
|
||||
CheckCode::UP015,
|
||||
CheckCode::UP016,
|
||||
CheckCode::UP017,
|
||||
CheckCode::UP018,
|
||||
CheckCode::UP019,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U010 => {
|
||||
@@ -2296,6 +2316,15 @@ impl CheckCodePrefix {
|
||||
);
|
||||
vec![CheckCode::UP017]
|
||||
}
|
||||
CheckCodePrefix::U019 => {
|
||||
one_time_warning!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U019` has been remapped to `UP019`".bold()
|
||||
);
|
||||
vec![CheckCode::UP019]
|
||||
}
|
||||
CheckCodePrefix::UP => vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
@@ -2313,6 +2342,8 @@ impl CheckCodePrefix {
|
||||
CheckCode::UP015,
|
||||
CheckCode::UP016,
|
||||
CheckCode::UP017,
|
||||
CheckCode::UP018,
|
||||
CheckCode::UP019,
|
||||
],
|
||||
CheckCodePrefix::UP0 => vec![
|
||||
CheckCode::UP001,
|
||||
@@ -2331,6 +2362,8 @@ impl CheckCodePrefix {
|
||||
CheckCode::UP015,
|
||||
CheckCode::UP016,
|
||||
CheckCode::UP017,
|
||||
CheckCode::UP018,
|
||||
CheckCode::UP019,
|
||||
],
|
||||
CheckCodePrefix::UP00 => vec![
|
||||
CheckCode::UP001,
|
||||
@@ -2359,6 +2392,8 @@ impl CheckCodePrefix {
|
||||
CheckCode::UP015,
|
||||
CheckCode::UP016,
|
||||
CheckCode::UP017,
|
||||
CheckCode::UP018,
|
||||
CheckCode::UP019,
|
||||
],
|
||||
CheckCodePrefix::UP010 => vec![CheckCode::UP010],
|
||||
CheckCodePrefix::UP011 => vec![CheckCode::UP011],
|
||||
@@ -2368,6 +2403,8 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::UP015 => vec![CheckCode::UP015],
|
||||
CheckCodePrefix::UP016 => vec![CheckCode::UP016],
|
||||
CheckCodePrefix::UP017 => vec![CheckCode::UP017],
|
||||
CheckCodePrefix::UP018 => vec![CheckCode::UP018],
|
||||
CheckCodePrefix::UP019 => vec![CheckCode::UP019],
|
||||
CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605],
|
||||
CheckCodePrefix::W2 => vec![CheckCode::W292],
|
||||
CheckCodePrefix::W29 => vec![CheckCode::W292],
|
||||
@@ -2903,6 +2940,7 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::U015 => SuffixLength::Three,
|
||||
CheckCodePrefix::U016 => SuffixLength::Three,
|
||||
CheckCodePrefix::U017 => SuffixLength::Three,
|
||||
CheckCodePrefix::U019 => SuffixLength::Three,
|
||||
CheckCodePrefix::UP => SuffixLength::Zero,
|
||||
CheckCodePrefix::UP0 => SuffixLength::One,
|
||||
CheckCodePrefix::UP00 => SuffixLength::Two,
|
||||
@@ -2923,6 +2961,8 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::UP015 => SuffixLength::Three,
|
||||
CheckCodePrefix::UP016 => SuffixLength::Three,
|
||||
CheckCodePrefix::UP017 => SuffixLength::Three,
|
||||
CheckCodePrefix::UP018 => SuffixLength::Three,
|
||||
CheckCodePrefix::UP019 => SuffixLength::Three,
|
||||
CheckCodePrefix::W => SuffixLength::Zero,
|
||||
CheckCodePrefix::W2 => SuffixLength::One,
|
||||
CheckCodePrefix::W29 => SuffixLength::Two,
|
||||
|
||||
16
src/cli.rs
16
src/cli.rs
@@ -19,7 +19,8 @@ use crate::settings::types::{
|
||||
pub struct Cli {
|
||||
#[arg(required_unless_present_any = ["explain", "generate_shell_completion"])]
|
||||
pub files: Vec<PathBuf>,
|
||||
/// Path to the `pyproject.toml` file to use for configuration.
|
||||
/// Path to the `pyproject.toml` or `ruff.toml` file to use for
|
||||
/// configuration.
|
||||
#[arg(long)]
|
||||
pub config: Option<PathBuf>,
|
||||
/// Enable verbose logging.
|
||||
@@ -43,6 +44,12 @@ pub struct Cli {
|
||||
fix: bool,
|
||||
#[clap(long, overrides_with("fix"), hide = true)]
|
||||
no_fix: bool,
|
||||
/// Fix any fixable lint errors, but don't report on leftover violations.
|
||||
/// Implies `--fix`.
|
||||
#[arg(long, overrides_with("no_fix_only"))]
|
||||
fix_only: bool,
|
||||
#[clap(long, overrides_with("fix_only"), hide = true)]
|
||||
no_fix_only: bool,
|
||||
/// Disable cache reads.
|
||||
#[arg(short, long)]
|
||||
pub no_cache: bool,
|
||||
@@ -133,6 +140,9 @@ pub struct Cli {
|
||||
/// Generate shell completion
|
||||
#[arg(long, hide = true, value_name = "SHELL")]
|
||||
pub generate_shell_completion: Option<clap_complete_command::Shell>,
|
||||
/// Path to the cache directory.
|
||||
#[arg(long)]
|
||||
pub cache_dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
@@ -178,8 +188,10 @@ impl Cli {
|
||||
unfixable: self.unfixable,
|
||||
// TODO(charlie): Included in `pyproject.toml`, but not inherited.
|
||||
fix: resolve_bool_arg(self.fix, self.no_fix),
|
||||
fix_only: resolve_bool_arg(self.fix_only, self.no_fix_only),
|
||||
format: self.format,
|
||||
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
|
||||
cache_dir: self.cache_dir,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -236,8 +248,10 @@ pub struct Overrides {
|
||||
pub unfixable: Option<Vec<CheckCodePrefix>>,
|
||||
// TODO(charlie): Captured in pyproject.toml as a default, but not part of `Settings`.
|
||||
pub fix: Option<bool>,
|
||||
pub fix_only: Option<bool>,
|
||||
pub format: Option<SerializationFormat>,
|
||||
pub force_exclude: Option<bool>,
|
||||
pub cache_dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// Map the CLI settings to a `LogLevel`.
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::message::Message;
|
||||
use crate::resolver::{FileDiscovery, PyprojectDiscovery};
|
||||
use crate::settings::flags;
|
||||
use crate::settings::types::SerializationFormat;
|
||||
use crate::{packages, resolver};
|
||||
use crate::{cache, packages, resolver};
|
||||
|
||||
/// Run the linter over a collection of files.
|
||||
pub fn run(
|
||||
@@ -38,6 +38,33 @@ pub fn run(
|
||||
let duration = start.elapsed();
|
||||
debug!("Identified files to lint in: {:?}", duration);
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
resolver.validate(pyproject_strategy)?;
|
||||
|
||||
// Initialize the cache.
|
||||
if matches!(cache, flags::Cache::Enabled) {
|
||||
match &pyproject_strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => {
|
||||
if let Err(e) = cache::init(&settings.cache_dir) {
|
||||
error!(
|
||||
"Failed to initialize cache at {}: {e:?}",
|
||||
settings.cache_dir.to_string_lossy()
|
||||
);
|
||||
}
|
||||
}
|
||||
PyprojectDiscovery::Hierarchical(default) => {
|
||||
for settings in std::iter::once(default).chain(resolver.iter()) {
|
||||
if let Err(e) = cache::init(&settings.cache_dir) {
|
||||
error!(
|
||||
"Failed to initialize cache at {}: {e:?}",
|
||||
settings.cache_dir.to_string_lossy()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Discover the package root for each Python file.
|
||||
let package_roots = packages::detect_package_roots(
|
||||
&paths
|
||||
@@ -152,6 +179,9 @@ pub fn add_noqa(
|
||||
let duration = start.elapsed();
|
||||
debug!("Identified files to lint in: {:?}", duration);
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
resolver.validate(pyproject_strategy)?;
|
||||
|
||||
let start = Instant::now();
|
||||
let modifications: usize = par_iter(&paths)
|
||||
.flatten()
|
||||
@@ -188,6 +218,9 @@ pub fn autoformat(
|
||||
let duration = start.elapsed();
|
||||
debug!("Identified files to lint in: {:?}", duration);
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
resolver.validate(pyproject_strategy)?;
|
||||
|
||||
let start = Instant::now();
|
||||
let modifications = par_iter(&paths)
|
||||
.flatten()
|
||||
@@ -221,6 +254,9 @@ pub fn show_settings(
|
||||
let (paths, resolver) =
|
||||
resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?;
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
resolver.validate(pyproject_strategy)?;
|
||||
|
||||
// Print the list of files.
|
||||
let Some(entry) = paths
|
||||
.iter()
|
||||
@@ -244,9 +280,12 @@ pub fn show_files(
|
||||
overrides: &Overrides,
|
||||
) -> Result<()> {
|
||||
// Collect all files in the hierarchy.
|
||||
let (paths, _resolver) =
|
||||
let (paths, resolver) =
|
||||
resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?;
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
resolver.validate(pyproject_strategy)?;
|
||||
|
||||
// Print the list of files.
|
||||
for entry in paths
|
||||
.iter()
|
||||
|
||||
@@ -1,51 +1,53 @@
|
||||
//! Settings for the `flake-annotations` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
|
||||
)]
|
||||
#[serde(
|
||||
deny_unknown_fields,
|
||||
rename_all = "kebab-case",
|
||||
rename = "Flake8AnnotationsOptions"
|
||||
)]
|
||||
pub struct Options {
|
||||
#[option(
|
||||
doc = r#"
|
||||
Whether to allow the omission of a return type hint for `__init__` if at least one
|
||||
argument is annotated.
|
||||
"#,
|
||||
default = "false",
|
||||
value_type = "bool",
|
||||
example = "mypy-init-return = true"
|
||||
)]
|
||||
/// Whether to allow the omission of a return type hint for `__init__` if at
|
||||
/// least one argument is annotated.
|
||||
pub mypy_init_return: Option<bool>,
|
||||
#[option(
|
||||
doc = r#"
|
||||
Whether to suppress `ANN000`-level errors for arguments matching the "dummy" variable
|
||||
regex (like `_`).
|
||||
"#,
|
||||
default = "false",
|
||||
value_type = "bool",
|
||||
example = "suppress-dummy-args = true"
|
||||
)]
|
||||
/// Whether to suppress `ANN000`-level errors for arguments matching the
|
||||
/// "dummy" variable regex (like `_`).
|
||||
pub suppress_dummy_args: Option<bool>,
|
||||
#[option(
|
||||
doc = r#"
|
||||
Whether to suppress `ANN200`-level errors for functions that meet either of the
|
||||
following criteria:
|
||||
|
||||
- Contain no `return` statement.
|
||||
- Explicit `return` statement(s) all return `None` (explicitly or implicitly).
|
||||
"#,
|
||||
default = "false",
|
||||
value_type = "bool",
|
||||
example = "suppress-none-returning = true"
|
||||
)]
|
||||
/// Whether to suppress `ANN200`-level errors for functions that meet either
|
||||
/// of the following criteria:
|
||||
///
|
||||
/// - Contain no `return` statement.
|
||||
/// - Explicit `return` statement(s) all return `None` (explicitly or
|
||||
/// implicitly).
|
||||
pub suppress_none_returning: Option<bool>,
|
||||
#[option(
|
||||
doc = "Whether to suppress `ANN401` for dynamically typed `*args` and `**kwargs` \
|
||||
arguments.",
|
||||
default = "false",
|
||||
value_type = "bool",
|
||||
example = "allow-star-arg-any = true"
|
||||
)]
|
||||
/// Whether to suppress `ANN401` for dynamically typed `*args` and
|
||||
/// `**kwargs` arguments.
|
||||
pub allow_star_arg_any: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_ast::{
|
||||
Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Location, Stmt,
|
||||
};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Location};
|
||||
|
||||
use crate::ast::helpers;
|
||||
use crate::ast::types::Range;
|
||||
@@ -26,17 +24,17 @@ fn duplicate_handler_exceptions<'a>(
|
||||
checker: &mut Checker,
|
||||
expr: &'a Expr,
|
||||
elts: &'a [Expr],
|
||||
) -> FxHashSet<Vec<&'a str>> {
|
||||
let mut seen: FxHashSet<Vec<&str>> = FxHashSet::default();
|
||||
) -> FxHashMap<Vec<&'a str>, &'a Expr> {
|
||||
let mut seen: FxHashMap<Vec<&str>, &Expr> = FxHashMap::default();
|
||||
let mut duplicates: FxHashSet<Vec<&str>> = FxHashSet::default();
|
||||
let mut unique_elts: Vec<&Expr> = Vec::default();
|
||||
for type_ in elts {
|
||||
let call_path = helpers::collect_call_paths(type_);
|
||||
if !call_path.is_empty() {
|
||||
if seen.contains(&call_path) {
|
||||
if seen.contains_key(&call_path) {
|
||||
duplicates.insert(call_path);
|
||||
} else {
|
||||
seen.insert(call_path);
|
||||
seen.entry(call_path).or_insert(type_);
|
||||
unique_elts.push(type_);
|
||||
}
|
||||
}
|
||||
@@ -77,9 +75,9 @@ fn duplicate_handler_exceptions<'a>(
|
||||
seen
|
||||
}
|
||||
|
||||
pub fn duplicate_exceptions(checker: &mut Checker, stmt: &Stmt, handlers: &[Excepthandler]) {
|
||||
pub fn duplicate_exceptions(checker: &mut Checker, handlers: &[Excepthandler]) {
|
||||
let mut seen: FxHashSet<Vec<&str>> = FxHashSet::default();
|
||||
let mut duplicates: FxHashSet<Vec<&str>> = FxHashSet::default();
|
||||
let mut duplicates: FxHashMap<Vec<&str>, Vec<&Expr>> = FxHashMap::default();
|
||||
for handler in handlers {
|
||||
let ExcepthandlerKind::ExceptHandler { type_: Some(type_), .. } = &handler.node else {
|
||||
continue;
|
||||
@@ -89,16 +87,16 @@ pub fn duplicate_exceptions(checker: &mut Checker, stmt: &Stmt, handlers: &[Exce
|
||||
let call_path = helpers::collect_call_paths(type_);
|
||||
if !call_path.is_empty() {
|
||||
if seen.contains(&call_path) {
|
||||
duplicates.insert(call_path);
|
||||
duplicates.entry(call_path).or_default().push(type_);
|
||||
} else {
|
||||
seen.insert(call_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::Tuple { elts, .. } => {
|
||||
for name in duplicate_handler_exceptions(checker, type_, elts) {
|
||||
for (name, expr) in duplicate_handler_exceptions(checker, type_, elts) {
|
||||
if seen.contains(&name) {
|
||||
duplicates.insert(name);
|
||||
duplicates.entry(name).or_default().push(expr);
|
||||
} else {
|
||||
seen.insert(name);
|
||||
}
|
||||
@@ -109,11 +107,13 @@ pub fn duplicate_exceptions(checker: &mut Checker, stmt: &Stmt, handlers: &[Exce
|
||||
}
|
||||
|
||||
if checker.settings.enabled.contains(&CheckCode::B025) {
|
||||
for duplicate in duplicates.into_iter().sorted() {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::DuplicateTryBlockException(duplicate.join(".")),
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
for (name, exprs) in duplicates {
|
||||
for expr in exprs {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::DuplicateTryBlockException(name.join(".")),
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
//! Settings for the `flake8-bugbear` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
|
||||
)]
|
||||
#[serde(
|
||||
deny_unknown_fields,
|
||||
rename_all = "kebab-case",
|
||||
rename = "Flake8BugbearOptions"
|
||||
)]
|
||||
pub struct Options {
|
||||
#[option(
|
||||
doc = r#"
|
||||
Additional callable functions to consider "immutable" when evaluating, e.g.,
|
||||
`no-mutable-default-argument` checks (`B006`).
|
||||
"#,
|
||||
default = r#"[]"#,
|
||||
value_type = "Vec<String>",
|
||||
example = r#"
|
||||
@@ -18,6 +21,8 @@ pub struct Options {
|
||||
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]
|
||||
"#
|
||||
)]
|
||||
/// Additional callable functions to consider "immutable" when evaluating,
|
||||
/// e.g., `no-mutable-default-argument` checks (`B006`).
|
||||
pub extend_immutable_calls: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
|
||||
@@ -5,37 +5,37 @@ expression: checks
|
||||
- kind:
|
||||
DuplicateTryBlockException: ValueError
|
||||
location:
|
||||
row: 15
|
||||
column: 0
|
||||
row: 19
|
||||
column: 7
|
||||
end_location:
|
||||
row: 20
|
||||
column: 9
|
||||
row: 19
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind:
|
||||
DuplicateTryBlockException: pickle.PickleError
|
||||
location:
|
||||
row: 22
|
||||
column: 0
|
||||
row: 28
|
||||
column: 7
|
||||
end_location:
|
||||
row: 29
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind:
|
||||
DuplicateTryBlockException: TypeError
|
||||
location:
|
||||
row: 31
|
||||
column: 0
|
||||
end_location:
|
||||
row: 38
|
||||
column: 9
|
||||
row: 28
|
||||
column: 25
|
||||
fix: ~
|
||||
- kind:
|
||||
DuplicateTryBlockException: ValueError
|
||||
location:
|
||||
row: 31
|
||||
column: 0
|
||||
row: 35
|
||||
column: 7
|
||||
end_location:
|
||||
row: 38
|
||||
column: 9
|
||||
row: 35
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind:
|
||||
DuplicateTryBlockException: TypeError
|
||||
location:
|
||||
row: 37
|
||||
column: 17
|
||||
end_location:
|
||||
row: 37
|
||||
column: 26
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
pub fn check_string_in_exception(exc: &Expr, max_string_length: usize) -> Vec<Check> {
|
||||
let mut checks = vec![];
|
||||
|
||||
if let ExprKind::Call { args, .. } = &exc.node {
|
||||
if let Some(first) = args.first() {
|
||||
match &first.node {
|
||||
// Check for string literals
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(string),
|
||||
..
|
||||
} => {
|
||||
if string.len() > max_string_length {
|
||||
checks.push(Check::new(
|
||||
CheckKind::RawStringInException,
|
||||
Range::from_located(first),
|
||||
));
|
||||
}
|
||||
}
|
||||
// Check for f-strings
|
||||
ExprKind::JoinedStr { .. } => checks.push(Check::new(
|
||||
CheckKind::FStringInException,
|
||||
Range::from_located(first),
|
||||
)),
|
||||
// Check for .format() calls
|
||||
ExprKind::Call { func, .. } => {
|
||||
if let ExprKind::Attribute { value, attr, .. } = &func.node {
|
||||
if attr == "format" && matches!(value.node, ExprKind::Constant { .. }) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::DotFormatInException,
|
||||
Range::from_located(first),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checks
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
pub mod checks;
|
||||
pub mod plugins;
|
||||
pub mod settings;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
52
src/flake8_errmsg/plugins.rs
Normal file
52
src/flake8_errmsg/plugins.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
|
||||
/// EM101, EM102, EM103
|
||||
pub fn string_in_exception(checker: &mut Checker, exc: &Expr) {
|
||||
if let ExprKind::Call { args, .. } = &exc.node {
|
||||
if let Some(first) = args.first() {
|
||||
match &first.node {
|
||||
// Check for string literals
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(string),
|
||||
..
|
||||
} => {
|
||||
if checker.settings.enabled.contains(&CheckCode::EM101) {
|
||||
if string.len() > checker.settings.flake8_errmsg.max_string_length {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::RawStringInException,
|
||||
Range::from_located(first),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for f-strings
|
||||
ExprKind::JoinedStr { .. } => {
|
||||
if checker.settings.enabled.contains(&CheckCode::EM102) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::FStringInException,
|
||||
Range::from_located(first),
|
||||
));
|
||||
}
|
||||
}
|
||||
// Check for .format() calls
|
||||
ExprKind::Call { func, .. } => {
|
||||
if checker.settings.enabled.contains(&CheckCode::EM103) {
|
||||
if let ExprKind::Attribute { value, attr, .. } = &func.node {
|
||||
if attr == "format" && matches!(value.node, ExprKind::Constant { .. }) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::DotFormatInException,
|
||||
Range::from_located(first),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,24 @@
|
||||
//! Settings for the `flake8-errmsg` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
|
||||
)]
|
||||
#[serde(
|
||||
deny_unknown_fields,
|
||||
rename_all = "kebab-case",
|
||||
rename = "Flake8ErrMsgOptions"
|
||||
)]
|
||||
pub struct Options {
|
||||
#[option(
|
||||
doc = r#"
|
||||
Maximum string length for string literals in exception messages.
|
||||
"#,
|
||||
default = "0",
|
||||
value_type = "usize",
|
||||
example = "max-string-length = 20"
|
||||
)]
|
||||
/// Maximum string length for string literals in exception messages.
|
||||
pub max_string_length: Option<usize>,
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::hash::{Hash, Hasher};
|
||||
use itertools::Itertools;
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use rustc_hash::FxHashMap;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[
|
||||
@@ -15,12 +16,16 @@ const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[
|
||||
("seaborn", "sns"),
|
||||
];
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
|
||||
)]
|
||||
#[serde(
|
||||
deny_unknown_fields,
|
||||
rename_all = "kebab-case",
|
||||
rename = "Flake8ImportConventionsOptions"
|
||||
)]
|
||||
pub struct Options {
|
||||
#[option(
|
||||
doc = "The conventional aliases for imports. These aliases can be extended by the \
|
||||
`extend_aliases` option.",
|
||||
default = r#"{"altair": "alt", "matplotlib.pyplot": "plt", "numpy": "np", "pandas": "pd", "seaborn": "sns"}"#,
|
||||
value_type = "FxHashMap<String, String>",
|
||||
example = r#"
|
||||
@@ -32,10 +37,10 @@ pub struct Options {
|
||||
seaborn = "sns"
|
||||
"#
|
||||
)]
|
||||
/// The conventional aliases for imports. These aliases can be extended by
|
||||
/// the `extend_aliases` option.
|
||||
pub aliases: Option<FxHashMap<String, String>>,
|
||||
#[option(
|
||||
doc = "A mapping of modules to their conventional import aliases. These aliases will be \
|
||||
added to the `aliases` mapping.",
|
||||
default = r#"{}"#,
|
||||
value_type = "FxHashMap<String, String>",
|
||||
example = r#"
|
||||
@@ -43,6 +48,8 @@ pub struct Options {
|
||||
"dask.dataframe" = "dd"
|
||||
"#
|
||||
)]
|
||||
/// A mapping of modules to their conventional import aliases. These aliases
|
||||
/// will be added to the `aliases` mapping.
|
||||
pub extend_aliases: Option<FxHashMap<String, String>>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,57 +1,56 @@
|
||||
//! Settings for the `flake8-quotes` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
pub enum Quote {
|
||||
Single,
|
||||
Double,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
|
||||
)]
|
||||
#[serde(
|
||||
deny_unknown_fields,
|
||||
rename_all = "kebab-case",
|
||||
rename = "Flake8QuotesOptions"
|
||||
)]
|
||||
pub struct Options {
|
||||
#[option(
|
||||
doc = r#"
|
||||
Quote style to prefer for inline strings (either "single" (`'`) or "double" (`"`)).
|
||||
"#,
|
||||
default = r#""double""#,
|
||||
value_type = "Quote",
|
||||
example = r#"
|
||||
inline-quotes = "single"
|
||||
"#
|
||||
)]
|
||||
/// Quote style to prefer for inline strings (either "single" (`'`) or
|
||||
/// "double" (`"`)).
|
||||
pub inline_quotes: Option<Quote>,
|
||||
#[option(
|
||||
doc = r#"
|
||||
Quote style to prefer for multiline strings (either "single" (`'`) or "double" (`"`)).
|
||||
"#,
|
||||
default = r#""double""#,
|
||||
value_type = "Quote",
|
||||
example = r#"
|
||||
multiline-quotes = "single"
|
||||
"#
|
||||
)]
|
||||
/// Quote style to prefer for multiline strings (either "single" (`'`) or
|
||||
/// "double" (`"`)).
|
||||
pub multiline_quotes: Option<Quote>,
|
||||
#[option(
|
||||
doc = r#"
|
||||
Quote style to prefer for docstrings (either "single" (`'`) or "double" (`"`)).
|
||||
"#,
|
||||
default = r#""double""#,
|
||||
value_type = "Quote",
|
||||
example = r#"
|
||||
docstring-quotes = "single"
|
||||
"#
|
||||
)]
|
||||
/// Quote style to prefer for docstrings (either "single" (`'`) or "double"
|
||||
/// (`"`)).
|
||||
pub docstring_quotes: Option<Quote>,
|
||||
#[option(
|
||||
doc = r#"
|
||||
Whether to avoid using single quotes if a string contains single quotes, or vice-versa
|
||||
with double quotes, as per [PEP8](https://peps.python.org/pep-0008/#string-quotes).
|
||||
This minimizes the need to escape quotation marks within strings.
|
||||
"#,
|
||||
default = r#"true"#,
|
||||
value_type = "bool",
|
||||
example = r#"
|
||||
@@ -59,6 +58,9 @@ pub struct Options {
|
||||
avoid-escape = false
|
||||
"#
|
||||
)]
|
||||
/// Whether to avoid using single quotes if a string contains single quotes,
|
||||
/// or vice-versa with double quotes, as per [PEP8](https://peps.python.org/pep-0008/#string-quotes).
|
||||
/// This minimizes the need to escape quotation marks within strings.
|
||||
pub avoid_escape: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
@@ -203,6 +203,10 @@ fn unnecessary_assign(checker: &mut Checker, stack: &Stack, expr: &Expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if stack.non_locals.contains(id.as_str()) {
|
||||
return;
|
||||
}
|
||||
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::UnnecessaryAssign,
|
||||
Range::from_located(expr),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_ast::{Expr, ExprKind, Location, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::visitor;
|
||||
@@ -10,6 +10,7 @@ pub struct Stack<'a> {
|
||||
pub ifs: Vec<&'a Stmt>,
|
||||
pub elifs: Vec<&'a Stmt>,
|
||||
pub refs: FxHashMap<&'a str, Vec<Location>>,
|
||||
pub non_locals: FxHashSet<&'a str>,
|
||||
pub assigns: FxHashMap<&'a str, Vec<Location>>,
|
||||
pub loops: Vec<(Location, Location)>,
|
||||
pub tries: Vec<(Location, Location)>,
|
||||
@@ -48,6 +49,11 @@ impl<'a> ReturnVisitor<'a> {
|
||||
impl<'a> Visitor<'a> for ReturnVisitor<'a> {
|
||||
fn visit_stmt(&mut self, stmt: &'a Stmt) {
|
||||
match &stmt.node {
|
||||
StmtKind::Global { names } | StmtKind::Nonlocal { names } => {
|
||||
self.stack
|
||||
.non_locals
|
||||
.extend(names.iter().map(std::string::String::as_str));
|
||||
}
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||
// Don't recurse.
|
||||
}
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
//! Settings for the `flake8-tidy-imports` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
pub enum Strictness {
|
||||
Parents,
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
|
||||
)]
|
||||
#[serde(
|
||||
deny_unknown_fields,
|
||||
rename_all = "kebab-case",
|
||||
rename = "Flake8TidyImportsOptions"
|
||||
)]
|
||||
pub struct Options {
|
||||
#[option(
|
||||
doc = r#"
|
||||
Whether to ban all relative imports (`"all"`), or only those imports that extend into
|
||||
the parent module and beyond (`"parents"`).
|
||||
"#,
|
||||
default = r#""parents""#,
|
||||
value_type = "Strictness",
|
||||
example = r#"
|
||||
@@ -25,6 +28,8 @@ pub struct Options {
|
||||
ban-relative-imports = "all"
|
||||
"#
|
||||
)]
|
||||
/// Whether to ban all relative imports (`"all"`), or only those imports
|
||||
/// that extend into the parent module and beyond (`"parents"`).
|
||||
pub ban_relative_imports: Option<Strictness>,
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ pub fn unused_arguments(
|
||||
.settings
|
||||
.enabled
|
||||
.contains(Argumentable::Function.check_code())
|
||||
&& !visibility::is_overload(checker, decorator_list)
|
||||
{
|
||||
function(
|
||||
&Argumentable::Function,
|
||||
@@ -154,6 +155,7 @@ pub fn unused_arguments(
|
||||
&& !helpers::is_empty(body)
|
||||
&& !visibility::is_abstract(checker, decorator_list)
|
||||
&& !visibility::is_override(checker, decorator_list)
|
||||
&& !visibility::is_overload(checker, decorator_list)
|
||||
{
|
||||
method(
|
||||
&Argumentable::Method,
|
||||
@@ -178,6 +180,7 @@ pub fn unused_arguments(
|
||||
&& !helpers::is_empty(body)
|
||||
&& !visibility::is_abstract(checker, decorator_list)
|
||||
&& !visibility::is_override(checker, decorator_list)
|
||||
&& !visibility::is_overload(checker, decorator_list)
|
||||
{
|
||||
method(
|
||||
&Argumentable::ClassMethod,
|
||||
@@ -202,6 +205,7 @@ pub fn unused_arguments(
|
||||
&& !helpers::is_empty(body)
|
||||
&& !visibility::is_abstract(checker, decorator_list)
|
||||
&& !visibility::is_override(checker, decorator_list)
|
||||
&& !visibility::is_overload(checker, decorator_list)
|
||||
{
|
||||
function(
|
||||
&Argumentable::StaticMethod,
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
//! Settings for the `flake8-unused-arguments` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
|
||||
)]
|
||||
#[serde(
|
||||
deny_unknown_fields,
|
||||
rename_all = "kebab-case",
|
||||
rename = "Flake8UnusedArgumentsOptions"
|
||||
)]
|
||||
pub struct Options {
|
||||
#[option(
|
||||
doc = r#"
|
||||
Whether to allow unused variadic arguments, like `*args` and `**kwargs`.
|
||||
"#,
|
||||
default = "false",
|
||||
value_type = "bool",
|
||||
example = "ignore-variadic-names = true"
|
||||
)]
|
||||
/// Whether to allow unused variadic arguments, like `*args` and `**kwargs`.
|
||||
pub ignore_variadic_names: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
@@ -5,73 +5,73 @@ expression: checks
|
||||
- kind:
|
||||
UnusedFunctionArgument: self
|
||||
location:
|
||||
row: 8
|
||||
row: 9
|
||||
column: 6
|
||||
end_location:
|
||||
row: 8
|
||||
row: 9
|
||||
column: 10
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: x
|
||||
location:
|
||||
row: 8
|
||||
row: 9
|
||||
column: 12
|
||||
end_location:
|
||||
row: 8
|
||||
row: 9
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: cls
|
||||
location:
|
||||
row: 12
|
||||
row: 13
|
||||
column: 6
|
||||
end_location:
|
||||
row: 12
|
||||
row: 13
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: x
|
||||
location:
|
||||
row: 12
|
||||
row: 13
|
||||
column: 11
|
||||
end_location:
|
||||
row: 12
|
||||
row: 13
|
||||
column: 12
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: self
|
||||
location:
|
||||
row: 16
|
||||
row: 17
|
||||
column: 6
|
||||
end_location:
|
||||
row: 16
|
||||
row: 17
|
||||
column: 10
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: x
|
||||
location:
|
||||
row: 16
|
||||
row: 17
|
||||
column: 12
|
||||
end_location:
|
||||
row: 16
|
||||
row: 17
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: cls
|
||||
location:
|
||||
row: 20
|
||||
row: 21
|
||||
column: 6
|
||||
end_location:
|
||||
row: 20
|
||||
row: 21
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: x
|
||||
location:
|
||||
row: 20
|
||||
row: 21
|
||||
column: 11
|
||||
end_location:
|
||||
row: 20
|
||||
row: 21
|
||||
column: 12
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -5,28 +5,28 @@ expression: checks
|
||||
- kind:
|
||||
UnusedMethodArgument: x
|
||||
location:
|
||||
row: 34
|
||||
row: 35
|
||||
column: 16
|
||||
end_location:
|
||||
row: 34
|
||||
row: 35
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedMethodArgument: x
|
||||
location:
|
||||
row: 37
|
||||
row: 38
|
||||
column: 19
|
||||
end_location:
|
||||
row: 37
|
||||
row: 38
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedMethodArgument: x
|
||||
location:
|
||||
row: 40
|
||||
row: 41
|
||||
column: 15
|
||||
end_location:
|
||||
row: 40
|
||||
row: 41
|
||||
column: 16
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ expression: checks
|
||||
- kind:
|
||||
UnusedClassMethodArgument: x
|
||||
location:
|
||||
row: 44
|
||||
row: 45
|
||||
column: 15
|
||||
end_location:
|
||||
row: 44
|
||||
row: 45
|
||||
column: 16
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -5,28 +5,28 @@ expression: checks
|
||||
- kind:
|
||||
UnusedStaticMethodArgument: cls
|
||||
location:
|
||||
row: 48
|
||||
row: 49
|
||||
column: 10
|
||||
end_location:
|
||||
row: 48
|
||||
row: 49
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedStaticMethodArgument: x
|
||||
location:
|
||||
row: 48
|
||||
row: 49
|
||||
column: 15
|
||||
end_location:
|
||||
row: 48
|
||||
row: 49
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedStaticMethodArgument: x
|
||||
location:
|
||||
row: 52
|
||||
row: 53
|
||||
column: 10
|
||||
end_location:
|
||||
row: 52
|
||||
row: 53
|
||||
column: 11
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ expression: checks
|
||||
- kind:
|
||||
UnusedLambdaArgument: x
|
||||
location:
|
||||
row: 27
|
||||
row: 28
|
||||
column: 7
|
||||
end_location:
|
||||
row: 27
|
||||
row: 28
|
||||
column: 8
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ pub fn format_import_from(
|
||||
line_length: usize,
|
||||
force_wrap_aliases: bool,
|
||||
is_first: bool,
|
||||
trailing_comma: bool,
|
||||
) -> String {
|
||||
if aliases.len() == 1
|
||||
&& aliases
|
||||
@@ -53,9 +54,10 @@ pub fn format_import_from(
|
||||
|
||||
// We can only inline if: (1) none of the aliases have atop comments, and (3)
|
||||
// only the last alias (if any) has inline comments.
|
||||
if aliases
|
||||
.iter()
|
||||
.all(|(_, CommentSet { atop, .. })| atop.is_empty())
|
||||
if !trailing_comma
|
||||
&& aliases
|
||||
.iter()
|
||||
.all(|(_, CommentSet { atop, .. })| atop.is_empty())
|
||||
&& aliases
|
||||
.iter()
|
||||
.rev()
|
||||
|
||||
@@ -1,7 +1,37 @@
|
||||
use rustpython_ast::Stmt;
|
||||
use rustpython_parser::lexer;
|
||||
use rustpython_parser::lexer::Tok;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::isort::types::TrailingComma;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
/// Return `true` if a `StmtKind::ImportFrom` statement ends with a magic
|
||||
/// trailing comma.
|
||||
pub fn trailing_comma(stmt: &Stmt, locator: &SourceCodeLocator) -> TrailingComma {
|
||||
let contents = locator.slice_source_code_range(&Range::from_located(stmt));
|
||||
let mut count: usize = 0;
|
||||
let mut trailing_comma = TrailingComma::Absent;
|
||||
for (_, tok, _) in lexer::make_tokenizer(&contents).flatten() {
|
||||
if matches!(tok, Tok::Lpar) {
|
||||
count += 1;
|
||||
}
|
||||
if matches!(tok, Tok::Rpar) {
|
||||
count -= 1;
|
||||
}
|
||||
if count == 1 {
|
||||
if matches!(tok, Tok::Newline | Tok::Indent | Tok::Dedent | Tok::Comment) {
|
||||
continue;
|
||||
} else if matches!(tok, Tok::Comma) {
|
||||
trailing_comma = TrailingComma::Present;
|
||||
} else {
|
||||
trailing_comma = TrailingComma::Absent;
|
||||
}
|
||||
}
|
||||
}
|
||||
trailing_comma
|
||||
}
|
||||
|
||||
/// Return `true` if a `Stmt` is preceded by a "comment break"
|
||||
pub fn has_comment_break(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
|
||||
// Starting from the `Stmt` (`def f(): pass`), we want to detect patterns like
|
||||
|
||||
204
src/isort/mod.rs
204
src/isort/mod.rs
@@ -1,4 +1,4 @@
|
||||
use std::cmp::Reverse;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -9,11 +9,14 @@ use rustpython_ast::{Stmt, StmtKind};
|
||||
|
||||
use crate::isort::categorize::{categorize, ImportType};
|
||||
use crate::isort::comments::Comment;
|
||||
use crate::isort::sorting::{member_key, module_key};
|
||||
use crate::isort::helpers::trailing_comma;
|
||||
use crate::isort::sorting::{cmp_import_froms, cmp_members, cmp_modules};
|
||||
use crate::isort::track::{Block, Trailer};
|
||||
use crate::isort::types::{
|
||||
AliasData, CommentSet, ImportBlock, ImportFromData, Importable, OrderedImportBlock,
|
||||
TrailingComma,
|
||||
};
|
||||
use crate::SourceCodeLocator;
|
||||
|
||||
mod categorize;
|
||||
mod comments;
|
||||
@@ -32,6 +35,7 @@ pub struct AnnotatedAliasData<'a> {
|
||||
pub atop: Vec<Comment<'a>>,
|
||||
pub inline: Vec<Comment<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AnnotatedImport<'a> {
|
||||
Import {
|
||||
@@ -45,12 +49,15 @@ pub enum AnnotatedImport<'a> {
|
||||
level: Option<&'a usize>,
|
||||
atop: Vec<Comment<'a>>,
|
||||
inline: Vec<Comment<'a>>,
|
||||
trailing_comma: TrailingComma,
|
||||
},
|
||||
}
|
||||
|
||||
fn annotate_imports<'a>(
|
||||
imports: &'a [&'a Stmt],
|
||||
comments: Vec<Comment<'a>>,
|
||||
locator: &SourceCodeLocator,
|
||||
split_on_trailing_comma: bool,
|
||||
) -> Vec<AnnotatedImport<'a>> {
|
||||
let mut annotated = vec![];
|
||||
let mut comments_iter = comments.into_iter().peekable();
|
||||
@@ -137,6 +144,11 @@ fn annotate_imports<'a>(
|
||||
module: module.as_ref(),
|
||||
names: aliases,
|
||||
level: level.as_ref(),
|
||||
trailing_comma: if split_on_trailing_comma {
|
||||
trailing_comma(import, locator)
|
||||
} else {
|
||||
TrailingComma::default()
|
||||
},
|
||||
atop,
|
||||
inline,
|
||||
});
|
||||
@@ -190,34 +202,27 @@ fn normalize_imports(imports: Vec<AnnotatedImport>, combine_as_imports: bool) ->
|
||||
level,
|
||||
atop,
|
||||
inline,
|
||||
trailing_comma,
|
||||
} => {
|
||||
// Associate the comments with the first alias (best effort).
|
||||
let single_import = names.len() == 1;
|
||||
|
||||
// If we're dealing with a multi-import block (i.e., a non-star, non-aliased
|
||||
// import), associate the comments with the first alias (best
|
||||
// effort).
|
||||
if let Some(alias) = names.first() {
|
||||
if alias.name == "*" {
|
||||
let entry = block
|
||||
let entry = if alias.name == "*" {
|
||||
block
|
||||
.import_from_star
|
||||
.entry(ImportFromData { module, level })
|
||||
.or_default();
|
||||
for comment in atop {
|
||||
entry.atop.push(comment.value);
|
||||
}
|
||||
for comment in inline {
|
||||
entry.inline.push(comment.value);
|
||||
}
|
||||
.or_default()
|
||||
} else if alias.asname.is_none() || combine_as_imports {
|
||||
let entry = &mut block
|
||||
&mut block
|
||||
.import_from
|
||||
.entry(ImportFromData { module, level })
|
||||
.or_default()
|
||||
.0;
|
||||
for comment in atop {
|
||||
entry.atop.push(comment.value);
|
||||
}
|
||||
for comment in inline {
|
||||
entry.inline.push(comment.value);
|
||||
}
|
||||
.0
|
||||
} else {
|
||||
let entry = block
|
||||
block
|
||||
.import_from_as
|
||||
.entry((
|
||||
ImportFromData { module, level },
|
||||
@@ -226,30 +231,19 @@ fn normalize_imports(imports: Vec<AnnotatedImport>, combine_as_imports: bool) ->
|
||||
asname: alias.asname,
|
||||
},
|
||||
))
|
||||
.or_default();
|
||||
for comment in atop {
|
||||
entry.atop.push(comment.value);
|
||||
}
|
||||
for comment in inline {
|
||||
entry.inline.push(comment.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
.or_default()
|
||||
};
|
||||
|
||||
// Create an entry for every alias.
|
||||
for alias in names {
|
||||
if alias.name == "*" {
|
||||
let entry = block
|
||||
.import_from_star
|
||||
.entry(ImportFromData { module, level })
|
||||
.or_default();
|
||||
for comment in alias.atop {
|
||||
entry.atop.push(comment.value);
|
||||
}
|
||||
for comment in alias.inline {
|
||||
entry.inline.push(comment.value);
|
||||
}
|
||||
} else if alias.asname.is_none() || combine_as_imports {
|
||||
for comment in atop {
|
||||
entry.atop.push(comment.value);
|
||||
}
|
||||
|
||||
// Associate inline comments with first alias if multiple names have been
|
||||
// imported, i.e., the comment applies to all names; otherwise, associate
|
||||
// with the alias.
|
||||
if single_import
|
||||
&& (alias.name != "*" && (alias.asname.is_none() || combine_as_imports))
|
||||
{
|
||||
let entry = block
|
||||
.import_from
|
||||
.entry(ImportFromData { module, level })
|
||||
@@ -260,14 +254,36 @@ fn normalize_imports(imports: Vec<AnnotatedImport>, combine_as_imports: bool) ->
|
||||
asname: alias.asname,
|
||||
})
|
||||
.or_default();
|
||||
for comment in alias.atop {
|
||||
entry.atop.push(comment.value);
|
||||
}
|
||||
for comment in alias.inline {
|
||||
for comment in inline {
|
||||
entry.inline.push(comment.value);
|
||||
}
|
||||
} else {
|
||||
let entry = block
|
||||
for comment in inline {
|
||||
entry.inline.push(comment.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create an entry for every alias.
|
||||
for alias in names {
|
||||
let entry = if alias.name == "*" {
|
||||
block
|
||||
.import_from_star
|
||||
.entry(ImportFromData { module, level })
|
||||
.or_default()
|
||||
} else if alias.asname.is_none() || combine_as_imports {
|
||||
block
|
||||
.import_from
|
||||
.entry(ImportFromData { module, level })
|
||||
.or_default()
|
||||
.1
|
||||
.entry(AliasData {
|
||||
name: alias.name,
|
||||
asname: alias.asname,
|
||||
})
|
||||
.or_default()
|
||||
} else {
|
||||
block
|
||||
.import_from_as
|
||||
.entry((
|
||||
ImportFromData { module, level },
|
||||
@@ -276,13 +292,23 @@ fn normalize_imports(imports: Vec<AnnotatedImport>, combine_as_imports: bool) ->
|
||||
asname: alias.asname,
|
||||
},
|
||||
))
|
||||
.or_default();
|
||||
entry
|
||||
.atop
|
||||
.extend(alias.atop.into_iter().map(|comment| comment.value));
|
||||
for comment in alias.inline {
|
||||
entry.inline.push(comment.value);
|
||||
}
|
||||
.or_default()
|
||||
};
|
||||
|
||||
for comment in alias.atop {
|
||||
entry.atop.push(comment.value);
|
||||
}
|
||||
for comment in alias.inline {
|
||||
entry.inline.push(comment.value);
|
||||
}
|
||||
}
|
||||
|
||||
// Propagate trailing commas.
|
||||
if matches!(trailing_comma, TrailingComma::Present) {
|
||||
if let Some(entry) =
|
||||
block.import_from.get_mut(&ImportFromData { module, level })
|
||||
{
|
||||
entry.2 = trailing_comma;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -379,7 +405,7 @@ fn sort_imports(block: ImportBlock) -> OrderedImportBlock {
|
||||
block
|
||||
.import
|
||||
.into_iter()
|
||||
.sorted_by_cached_key(|(alias, _)| module_key(alias.name, alias.asname)),
|
||||
.sorted_by(|(alias1, _), (alias2, _)| cmp_modules(alias1, alias2)),
|
||||
);
|
||||
|
||||
// Sort `StmtKind::ImportFrom`.
|
||||
@@ -408,6 +434,7 @@ fn sort_imports(block: ImportBlock) -> OrderedImportBlock {
|
||||
inline: comments.inline,
|
||||
},
|
||||
)]),
|
||||
TrailingComma::Absent,
|
||||
),
|
||||
)
|
||||
}),
|
||||
@@ -435,37 +462,36 @@ fn sort_imports(block: ImportBlock) -> OrderedImportBlock {
|
||||
inline: comments.inline,
|
||||
},
|
||||
)]),
|
||||
TrailingComma::Absent,
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.map(|(import_from, (comments, aliases))| {
|
||||
.map(|(import_from, (comments, aliases, locations))| {
|
||||
// Within each `StmtKind::ImportFrom`, sort the members.
|
||||
(
|
||||
import_from,
|
||||
comments,
|
||||
locations,
|
||||
aliases
|
||||
.into_iter()
|
||||
.sorted_by_cached_key(|(alias, _)| member_key(alias.name, alias.asname))
|
||||
.sorted_by(|(alias1, _), (alias2, _)| cmp_members(alias1, alias2))
|
||||
.collect::<Vec<(AliasData, CommentSet)>>(),
|
||||
)
|
||||
})
|
||||
.sorted_by_cached_key(|(import_from, _, aliases)| {
|
||||
// Sort each `StmtKind::ImportFrom` by module key, breaking ties based on
|
||||
// members.
|
||||
(
|
||||
Reverse(import_from.level),
|
||||
import_from
|
||||
.module
|
||||
.as_ref()
|
||||
.map(|module| module_key(module, None)),
|
||||
aliases
|
||||
.first()
|
||||
.map(|(alias, _)| member_key(alias.name, alias.asname)),
|
||||
)
|
||||
}),
|
||||
.sorted_by(
|
||||
|(import_from1, _, _, aliases1), (import_from2, _, _, aliases2)| {
|
||||
cmp_import_froms(import_from1, import_from2).then_with(|| {
|
||||
match (aliases1.first(), aliases2.first()) {
|
||||
(None, None) => Ordering::Equal,
|
||||
(None, Some(_)) => Ordering::Less,
|
||||
(Some(_), None) => Ordering::Greater,
|
||||
(Some((alias1, _)), Some((alias2, _))) => cmp_members(alias1, alias2),
|
||||
}
|
||||
})
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
ordered
|
||||
}
|
||||
|
||||
@@ -473,6 +499,7 @@ fn sort_imports(block: ImportBlock) -> OrderedImportBlock {
|
||||
pub fn format_imports(
|
||||
block: &Block,
|
||||
comments: Vec<Comment>,
|
||||
locator: &SourceCodeLocator,
|
||||
line_length: usize,
|
||||
src: &[PathBuf],
|
||||
package: Option<&Path>,
|
||||
@@ -481,9 +508,10 @@ pub fn format_imports(
|
||||
extra_standard_library: &BTreeSet<String>,
|
||||
combine_as_imports: bool,
|
||||
force_wrap_aliases: bool,
|
||||
split_on_trailing_comma: bool,
|
||||
) -> String {
|
||||
let trailer = &block.trailer;
|
||||
let block = annotate_imports(&block.imports, comments);
|
||||
let block = annotate_imports(&block.imports, comments, locator, split_on_trailing_comma);
|
||||
|
||||
// Normalize imports (i.e., deduplicate, aggregate `from` imports).
|
||||
let block = normalize_imports(block, combine_as_imports);
|
||||
@@ -521,7 +549,7 @@ pub fn format_imports(
|
||||
}
|
||||
|
||||
// Format `StmtKind::ImportFrom` statements.
|
||||
for (import_from, comments, aliases) in &import_block.import_from {
|
||||
for (import_from, comments, trailing_comma, aliases) in &import_block.import_from {
|
||||
output.append(&format::format_import_from(
|
||||
import_from,
|
||||
comments,
|
||||
@@ -529,6 +557,7 @@ pub fn format_imports(
|
||||
line_length,
|
||||
force_wrap_aliases,
|
||||
is_first_statement,
|
||||
split_on_trailing_comma && matches!(trailing_comma, TrailingComma::Present),
|
||||
));
|
||||
is_first_statement = false;
|
||||
}
|
||||
@@ -569,6 +598,7 @@ mod tests {
|
||||
#[test_case(Path::new("insert_empty_lines.py"))]
|
||||
#[test_case(Path::new("insert_empty_lines.pyi"))]
|
||||
#[test_case(Path::new("leading_prefix.py"))]
|
||||
#[test_case(Path::new("natural_order.py"))]
|
||||
#[test_case(Path::new("no_reorder_within_section.py"))]
|
||||
#[test_case(Path::new("no_wrap_star.py"))]
|
||||
#[test_case(Path::new("order_by_type.py"))]
|
||||
@@ -587,6 +617,7 @@ mod tests {
|
||||
#[test_case(Path::new("split.py"))]
|
||||
#[test_case(Path::new("trailing_suffix.py"))]
|
||||
#[test_case(Path::new("type_comments.py"))]
|
||||
#[test_case(Path::new("magic_trailing_comma.py"))]
|
||||
fn default(path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}", path.to_string_lossy());
|
||||
let mut checks = test_path(
|
||||
@@ -645,4 +676,25 @@ mod tests {
|
||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Path::new("magic_trailing_comma.py"))]
|
||||
fn no_split_on_trailing_comma(path: &Path) -> Result<()> {
|
||||
let snapshot = format!("split_on_trailing_comma_{}", path.to_string_lossy());
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/isort")
|
||||
.join(path)
|
||||
.as_path(),
|
||||
&Settings {
|
||||
isort: isort::settings::Settings {
|
||||
split_on_trailing_comma: false,
|
||||
..isort::settings::Settings::default()
|
||||
},
|
||||
src: vec![Path::new("resources/test/fixtures/isort").to_path_buf()],
|
||||
..Settings::for_rule(CheckCode::I001)
|
||||
},
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ pub fn check_imports(
|
||||
let expected = format_imports(
|
||||
block,
|
||||
comments,
|
||||
locator,
|
||||
settings.line_length - indentation.len(),
|
||||
&settings.src,
|
||||
package,
|
||||
@@ -80,6 +81,7 @@ pub fn check_imports(
|
||||
&settings.isort.extra_standard_library,
|
||||
settings.isort.combine_as_imports,
|
||||
settings.isort.force_wrap_aliases,
|
||||
settings.isort.split_on_trailing_comma,
|
||||
);
|
||||
|
||||
// Expand the span the entire range, including leading and trailing space.
|
||||
|
||||
@@ -3,40 +3,19 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
|
||||
)]
|
||||
#[serde(
|
||||
deny_unknown_fields,
|
||||
rename_all = "kebab-case",
|
||||
rename = "IsortOptions"
|
||||
)]
|
||||
pub struct Options {
|
||||
#[option(
|
||||
doc = r#"
|
||||
Combines as imports on the same line. See isort's [`combine-as-imports`](https://pycqa.github.io/isort/docs/configuration/options.html#combine-as-imports)
|
||||
option.
|
||||
"#,
|
||||
default = r#"false"#,
|
||||
value_type = "bool",
|
||||
example = r#"
|
||||
combine-as-imports = true
|
||||
"#
|
||||
)]
|
||||
pub combine_as_imports: Option<bool>,
|
||||
#[option(
|
||||
doc = r#"
|
||||
Force `import from` statements with multiple members and at least one alias (e.g.,
|
||||
`import A as B`) to wrap such that every line contains exactly one member. For example,
|
||||
this formatting would be retained, rather than condensing to a single line:
|
||||
|
||||
```py
|
||||
from .utils import (
|
||||
test_directory as test_directory,
|
||||
test_id as test_id
|
||||
)
|
||||
```
|
||||
|
||||
Note that this setting is only effective when combined with `combine-as-imports = true`.
|
||||
When `combine-as-imports` isn't enabled, every aliased `import from` will be given its
|
||||
own line, in which case, wrapping is not necessary.
|
||||
"#,
|
||||
default = r#"false"#,
|
||||
value_type = "bool",
|
||||
example = r#"
|
||||
@@ -44,49 +23,82 @@ pub struct Options {
|
||||
combine-as-imports = true
|
||||
"#
|
||||
)]
|
||||
/// Force `import from` statements with multiple members and at least one
|
||||
/// alias (e.g., `import A as B`) to wrap such that every line contains
|
||||
/// exactly one member. For example, this formatting would be retained,
|
||||
/// rather than condensing to a single line:
|
||||
///
|
||||
/// ```py
|
||||
/// from .utils import (
|
||||
/// test_directory as test_directory,
|
||||
/// test_id as test_id
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// Note that this setting is only effective when combined with
|
||||
/// `combine-as-imports = true`. When `combine-as-imports` isn't
|
||||
/// enabled, every aliased `import from` will be given its own line, in
|
||||
/// which case, wrapping is not necessary.
|
||||
pub force_wrap_aliases: Option<bool>,
|
||||
#[option(
|
||||
doc = r#"
|
||||
A list of modules to consider first-party, regardless of whether they can be identified
|
||||
as such via introspection of the local filesystem.
|
||||
"#,
|
||||
default = r#"false"#,
|
||||
value_type = "bool",
|
||||
example = r#"
|
||||
combine-as-imports = true
|
||||
"#
|
||||
)]
|
||||
/// Combines as imports on the same line. See isort's [`combine-as-imports`](https://pycqa.github.io/isort/docs/configuration/options.html#combine-as-imports)
|
||||
/// option.
|
||||
pub combine_as_imports: Option<bool>,
|
||||
#[option(
|
||||
default = r#"true"#,
|
||||
value_type = "bool",
|
||||
example = r#"
|
||||
split-on-trailing-comma = false
|
||||
"#
|
||||
)]
|
||||
/// If a comma is placed after the last member in a multi-line import, then
|
||||
/// the imports will never be folded into one line.
|
||||
///
|
||||
/// See isort's [`split-on-trailing-comma`](https://pycqa.github.io/isort/docs/configuration/options.html#split-on-trailing-comma) option.
|
||||
pub split_on_trailing_comma: Option<bool>,
|
||||
#[option(
|
||||
default = r#"[]"#,
|
||||
value_type = "Vec<String>",
|
||||
example = r#"
|
||||
known-first-party = ["src"]
|
||||
"#
|
||||
)]
|
||||
/// A list of modules to consider first-party, regardless of whether they
|
||||
/// can be identified as such via introspection of the local filesystem.
|
||||
pub known_first_party: Option<Vec<String>>,
|
||||
#[option(
|
||||
doc = r#"
|
||||
A list of modules to consider third-party, regardless of whether they can be identified
|
||||
as such via introspection of the local filesystem.
|
||||
"#,
|
||||
default = r#"[]"#,
|
||||
value_type = "Vec<String>",
|
||||
example = r#"
|
||||
known-third-party = ["src"]
|
||||
"#
|
||||
)]
|
||||
/// A list of modules to consider third-party, regardless of whether they
|
||||
/// can be identified as such via introspection of the local filesystem.
|
||||
pub known_third_party: Option<Vec<String>>,
|
||||
#[option(
|
||||
doc = r#"
|
||||
A list of modules to consider standard-library, in addition to those known to Ruff in
|
||||
advance.
|
||||
"#,
|
||||
default = r#"[]"#,
|
||||
value_type = "Vec<String>",
|
||||
example = r#"
|
||||
extra-standard-library = ["path"]
|
||||
"#
|
||||
)]
|
||||
/// A list of modules to consider standard-library, in addition to those
|
||||
/// known to Ruff in advance.
|
||||
pub extra_standard_library: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, Default)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct Settings {
|
||||
pub combine_as_imports: bool,
|
||||
pub force_wrap_aliases: bool,
|
||||
pub split_on_trailing_comma: bool,
|
||||
pub known_first_party: BTreeSet<String>,
|
||||
pub known_third_party: BTreeSet<String>,
|
||||
pub extra_standard_library: BTreeSet<String>,
|
||||
@@ -95,8 +107,9 @@ pub struct Settings {
|
||||
impl Settings {
|
||||
pub fn from_options(options: Options) -> Self {
|
||||
Self {
|
||||
combine_as_imports: options.combine_as_imports.unwrap_or_default(),
|
||||
force_wrap_aliases: options.force_wrap_aliases.unwrap_or_default(),
|
||||
combine_as_imports: options.combine_as_imports.unwrap_or(false),
|
||||
force_wrap_aliases: options.force_wrap_aliases.unwrap_or(false),
|
||||
split_on_trailing_comma: options.split_on_trailing_comma.unwrap_or(true),
|
||||
known_first_party: BTreeSet::from_iter(options.known_first_party.unwrap_or_default()),
|
||||
known_third_party: BTreeSet::from_iter(options.known_third_party.unwrap_or_default()),
|
||||
extra_standard_library: BTreeSet::from_iter(
|
||||
@@ -105,3 +118,16 @@ impl Settings {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
combine_as_imports: false,
|
||||
force_wrap_aliases: false,
|
||||
split_on_trailing_comma: true,
|
||||
known_first_party: BTreeSet::new(),
|
||||
known_third_party: BTreeSet::new(),
|
||||
extra_standard_library: BTreeSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ expression: checks
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 26
|
||||
row: 29
|
||||
column: 0
|
||||
fix:
|
||||
content: "import B # Comment 4\n\n# Comment 3a\n# Comment 3b\nimport C\nimport D\n\n# Comment 5\n# Comment 6\nfrom A import (\n a, # Comment 7 # Comment 9\n b, # Comment 10\n c, # Comment 8 # Comment 11\n)\n"
|
||||
content: "import B # Comment 4\n\n# Comment 3a\n# Comment 3b\nimport C\nimport D\n\n# Comment 5\n# Comment 6\nfrom A import (\n a, # Comment 7 # Comment 9\n b, # Comment 10\n c, # Comment 8 # Comment 11\n)\nfrom D import (\n a_long_name_to_force_multiple_lines, # Comment 12\n another_long_name_to_force_multiple_lines, # Comment 13\n)\n"
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 26
|
||||
row: 29
|
||||
column: 0
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ expression: checks
|
||||
row: 5
|
||||
column: 0
|
||||
fix:
|
||||
content: "import a\n\n# Don't take this comment into account when determining whether the next import can fit on one line.\nfrom b import c\nfrom d import ( # Do take this comment into account when determining whether the next import can fit on one line.\n e,\n)\n"
|
||||
content: "import a\n\n# Don't take this comment into account when determining whether the next import can fit on one line.\nfrom b import c\nfrom d import (\n e, # Do take this comment into account when determining whether the next import can fit on one line.\n)\n"
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/isort/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 39
|
||||
column: 0
|
||||
fix:
|
||||
content: "from glob import (\n escape, # Ends with a comment, should still treat as magic trailing comma.\n glob,\n iglob,\n)\n\n# No magic comma, this will be rolled into one line.\nfrom os import environ, execl, execv, path\nfrom sys import (\n argv,\n exit,\n stderr,\n stdout,\n)\n\n# These will be combined, but without a trailing comma.\nfrom foo import bar, baz\n\n# These will be combined, _with_ a trailing comma.\nfrom module1 import (\n member1,\n member2,\n member3,\n)\n\n# These will be combined, _with_ a trailing comma.\nfrom module2 import (\n member1,\n member2,\n member3,\n)\n"
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 39
|
||||
column: 0
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/isort/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 17
|
||||
column: 0
|
||||
fix:
|
||||
content: "import numpy1\nimport numpy2\nimport numpy10\nfrom numpy import (\n cos,\n int8,\n int16,\n int32,\n int64,\n sin,\n tan,\n uint8,\n uint16,\n uint32,\n uint64,\n)\n"
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 17
|
||||
column: 0
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/isort/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 39
|
||||
column: 0
|
||||
fix:
|
||||
content: "from glob import (\n escape, # Ends with a comment, should still treat as magic trailing comma.\n glob,\n iglob,\n)\n\n# No magic comma, this will be rolled into one line.\nfrom os import environ, execl, execv, path\nfrom sys import argv, exit, stderr, stdout\n\n# These will be combined, but without a trailing comma.\nfrom foo import bar, baz\n\n# These will be combined, _with_ a trailing comma.\nfrom module1 import member1, member2, member3\n\n# These will be combined, _with_ a trailing comma.\nfrom module2 import member1, member2, member3\n"
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 39
|
||||
column: 0
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
/// See: <https://github.com/PyCQA/isort/blob/12cc5fbd67eebf92eb2213b03c07b138ae1fb448/isort/sorting.py#L13>
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::isort::types::{AliasData, ImportFromData};
|
||||
use crate::python::string;
|
||||
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq)]
|
||||
@@ -8,29 +11,57 @@ pub enum Prefix {
|
||||
Variables,
|
||||
}
|
||||
|
||||
pub fn module_key<'a>(
|
||||
name: &'a str,
|
||||
asname: Option<&'a String>,
|
||||
) -> (String, &'a str, Option<&'a String>) {
|
||||
(name.to_lowercase(), name, asname)
|
||||
fn prefix(name: &str) -> Prefix {
|
||||
if name.len() > 1 && string::is_upper(name) {
|
||||
// Ex) `CONSTANT`
|
||||
Prefix::Constants
|
||||
} else if name.chars().next().map_or(false, char::is_uppercase) {
|
||||
// Ex) `Class`
|
||||
Prefix::Classes
|
||||
} else {
|
||||
// Ex) `variable`
|
||||
Prefix::Variables
|
||||
}
|
||||
}
|
||||
|
||||
pub fn member_key<'a>(
|
||||
name: &'a str,
|
||||
asname: Option<&'a String>,
|
||||
) -> (Prefix, String, Option<&'a String>) {
|
||||
(
|
||||
if name.len() > 1 && string::is_upper(name) {
|
||||
// Ex) `CONSTANT`
|
||||
Prefix::Constants
|
||||
} else if name.chars().next().map_or(false, char::is_uppercase) {
|
||||
// Ex) `Class`
|
||||
Prefix::Classes
|
||||
} else {
|
||||
// Ex) `variable`
|
||||
Prefix::Variables
|
||||
},
|
||||
name.to_lowercase(),
|
||||
asname,
|
||||
)
|
||||
/// Compare two top-level modules.
|
||||
pub fn cmp_modules(alias1: &AliasData, alias2: &AliasData) -> Ordering {
|
||||
natord::compare_ignore_case(alias1.name, alias2.name)
|
||||
.then_with(|| natord::compare(alias1.name, alias2.name))
|
||||
.then_with(|| match (alias1.asname, alias2.asname) {
|
||||
(None, None) => Ordering::Equal,
|
||||
(None, Some(_)) => Ordering::Less,
|
||||
(Some(_), None) => Ordering::Greater,
|
||||
(Some(asname1), Some(asname2)) => natord::compare(asname1, asname2),
|
||||
})
|
||||
}
|
||||
|
||||
/// Compare two member imports within `StmtKind::ImportFrom` blocks.
|
||||
pub fn cmp_members(alias1: &AliasData, alias2: &AliasData) -> Ordering {
|
||||
prefix(alias1.name)
|
||||
.cmp(&prefix(alias2.name))
|
||||
.then_with(|| cmp_modules(alias1, alias2))
|
||||
}
|
||||
|
||||
/// Compare two relative import levels.
|
||||
pub fn cmp_levels(level1: Option<&usize>, level2: Option<&usize>) -> Ordering {
|
||||
match (level1, level2) {
|
||||
(None, None) => Ordering::Equal,
|
||||
(None, Some(_)) => Ordering::Less,
|
||||
(Some(_), None) => Ordering::Greater,
|
||||
(Some(level1), Some(level2)) => level2.cmp(level1),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compare two `StmtKind::ImportFrom` blocks.
|
||||
pub fn cmp_import_froms(import_from1: &ImportFromData, import_from2: &ImportFromData) -> Ordering {
|
||||
cmp_levels(import_from1.level, import_from2.level).then_with(|| {
|
||||
match (&import_from1.module, import_from2.module) {
|
||||
(None, None) => Ordering::Equal,
|
||||
(None, Some(_)) => Ordering::Less,
|
||||
(Some(_), None) => Ordering::Greater,
|
||||
(Some(module1), Some(module2)) => natord::compare_ignore_case(module1, module2)
|
||||
.then_with(|| natord::compare(module1, module2)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,18 @@ use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::ast;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TrailingComma {
|
||||
Present,
|
||||
Absent,
|
||||
}
|
||||
|
||||
impl Default for TrailingComma {
|
||||
fn default() -> Self {
|
||||
TrailingComma::Absent
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub struct ImportFromData<'a> {
|
||||
pub module: Option<&'a String>,
|
||||
@@ -54,8 +66,14 @@ pub struct ImportBlock<'a> {
|
||||
pub import: FxHashMap<AliasData<'a>, CommentSet<'a>>,
|
||||
// Map from (module, level) to `AliasData`, used to track 'from' imports.
|
||||
// Ex) `from module import member`
|
||||
pub import_from:
|
||||
FxHashMap<ImportFromData<'a>, (CommentSet<'a>, FxHashMap<AliasData<'a>, CommentSet<'a>>)>,
|
||||
pub import_from: FxHashMap<
|
||||
ImportFromData<'a>,
|
||||
(
|
||||
CommentSet<'a>,
|
||||
FxHashMap<AliasData<'a>, CommentSet<'a>>,
|
||||
TrailingComma,
|
||||
),
|
||||
>,
|
||||
// Set of (module, level, name, asname), used to track re-exported 'from' imports.
|
||||
// Ex) `from module import member as member`
|
||||
pub import_from_as: FxHashMap<(ImportFromData<'a>, AliasData<'a>), CommentSet<'a>>,
|
||||
@@ -72,6 +90,7 @@ pub struct OrderedImportBlock<'a> {
|
||||
pub import_from: Vec<(
|
||||
ImportFromData<'a>,
|
||||
CommentSet<'a>,
|
||||
TrailingComma,
|
||||
Vec<AliasDataWithComments<'a>>,
|
||||
)>,
|
||||
}
|
||||
|
||||
72
src/lib.rs
72
src/lib.rs
@@ -11,19 +11,10 @@
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use path_absolutize::path_dedot;
|
||||
use rustpython_helpers::tokenize;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
use settings::{pyproject, Settings};
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
use crate::checks::Check;
|
||||
use crate::linter::check_path;
|
||||
use crate::resolver::Relativity;
|
||||
use crate::settings::configuration::Configuration;
|
||||
use crate::settings::flags;
|
||||
use crate::settings::Settings;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
mod ast;
|
||||
@@ -34,7 +25,6 @@ pub mod checks;
|
||||
pub mod checks_gen;
|
||||
pub mod cli;
|
||||
pub mod code_gen;
|
||||
pub mod commands;
|
||||
mod cst;
|
||||
mod directives;
|
||||
mod docstrings;
|
||||
@@ -66,7 +56,6 @@ pub mod logging;
|
||||
pub mod mccabe;
|
||||
pub mod message;
|
||||
mod noqa;
|
||||
mod packages;
|
||||
mod pandas_vet;
|
||||
pub mod pep8_naming;
|
||||
pub mod printer;
|
||||
@@ -82,55 +71,20 @@ mod ruff;
|
||||
mod rustpython_helpers;
|
||||
pub mod settings;
|
||||
pub mod source_code_locator;
|
||||
#[cfg(feature = "update-informer")]
|
||||
pub mod updates;
|
||||
mod vendored;
|
||||
pub mod visibility;
|
||||
|
||||
/// Load the relevant `Settings` for a given `Path`.
|
||||
fn resolve(path: &Path) -> Result<Settings> {
|
||||
if let Some(pyproject) = pyproject::find_pyproject_toml(path)? {
|
||||
// First priority: `pyproject.toml` in the current `Path`.
|
||||
resolver::resolve_settings(&pyproject, &Relativity::Parent, None)
|
||||
} else if let Some(pyproject) = pyproject::find_user_pyproject_toml() {
|
||||
// Second priority: user-specific `pyproject.toml`.
|
||||
resolver::resolve_settings(&pyproject, &Relativity::Cwd, None)
|
||||
cfg_if! {
|
||||
if #[cfg(not(target_family = "wasm"))] {
|
||||
pub mod commands;
|
||||
mod packages;
|
||||
#[cfg(all(feature = "update-informer"))]
|
||||
pub mod updates;
|
||||
|
||||
mod lib_native;
|
||||
pub use lib_native::check;
|
||||
} else {
|
||||
// Fallback: default settings.
|
||||
Settings::from_configuration(Configuration::default(), &path_dedot::CWD)
|
||||
mod lib_wasm;
|
||||
pub use lib_wasm::check;
|
||||
}
|
||||
}
|
||||
|
||||
/// Run Ruff over Python source code directly.
|
||||
pub fn check(path: &Path, contents: &str, autofix: bool) -> Result<Vec<Check>> {
|
||||
// Load the relevant `Settings` for the given `Path`.
|
||||
let settings = resolve(path)?;
|
||||
|
||||
// Tokenize once.
|
||||
let tokens: Vec<LexResult> = tokenize(contents);
|
||||
|
||||
// Initialize the SourceCodeLocator (which computes offsets lazily).
|
||||
let locator = SourceCodeLocator::new(contents);
|
||||
|
||||
// Extract the `# noqa` and `# isort: skip` directives from the source.
|
||||
let directives = directives::extract_directives(
|
||||
&tokens,
|
||||
&locator,
|
||||
directives::Flags::from_settings(&settings),
|
||||
);
|
||||
|
||||
// Generate checks.
|
||||
let checks = check_path(
|
||||
path,
|
||||
packages::detect_package_root(path),
|
||||
contents,
|
||||
tokens,
|
||||
&locator,
|
||||
&directives,
|
||||
&settings,
|
||||
autofix.into(),
|
||||
flags::Noqa::Enabled,
|
||||
)?;
|
||||
|
||||
Ok(checks)
|
||||
}
|
||||
|
||||
65
src/lib_native.rs
Normal file
65
src/lib_native.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use path_absolutize::path_dedot;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
|
||||
use crate::checks::Check;
|
||||
use crate::linter::check_path;
|
||||
use crate::resolver::Relativity;
|
||||
use crate::rustpython_helpers::tokenize;
|
||||
use crate::settings::configuration::Configuration;
|
||||
use crate::settings::{flags, pyproject, Settings};
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
use crate::{directives, packages, resolver};
|
||||
|
||||
/// Load the relevant `Settings` for a given `Path`.
|
||||
fn resolve(path: &Path) -> Result<Settings> {
|
||||
if let Some(pyproject) = pyproject::find_settings_toml(path)? {
|
||||
// First priority: `pyproject.toml` in the current `Path`.
|
||||
resolver::resolve_settings(&pyproject, &Relativity::Parent, None)
|
||||
} else if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
||||
// Second priority: user-specific `pyproject.toml`.
|
||||
resolver::resolve_settings(&pyproject, &Relativity::Cwd, None)
|
||||
} else {
|
||||
// Fallback: default settings.
|
||||
Settings::from_configuration(Configuration::default(), &path_dedot::CWD)
|
||||
}
|
||||
}
|
||||
|
||||
/// Run Ruff over Python source code directly.
|
||||
pub fn check(path: &Path, contents: &str, autofix: bool) -> Result<Vec<Check>> {
|
||||
// Load the relevant `Settings` for the given `Path`.
|
||||
let settings = resolve(path)?;
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
settings.validate()?;
|
||||
|
||||
// Tokenize once.
|
||||
let tokens: Vec<LexResult> = tokenize(contents);
|
||||
|
||||
// Initialize the SourceCodeLocator (which computes offsets lazily).
|
||||
let locator = SourceCodeLocator::new(contents);
|
||||
|
||||
// Extract the `# noqa` and `# isort: skip` directives from the source.
|
||||
let directives = directives::extract_directives(
|
||||
&tokens,
|
||||
&locator,
|
||||
directives::Flags::from_settings(&settings),
|
||||
);
|
||||
|
||||
// Generate checks.
|
||||
let checks = check_path(
|
||||
path,
|
||||
packages::detect_package_root(path),
|
||||
contents,
|
||||
tokens,
|
||||
&locator,
|
||||
&directives,
|
||||
&settings,
|
||||
autofix.into(),
|
||||
flags::Noqa::Enabled,
|
||||
)?;
|
||||
|
||||
Ok(checks)
|
||||
}
|
||||
156
src/lib_wasm.rs
Normal file
156
src/lib_wasm.rs
Normal file
@@ -0,0 +1,156 @@
|
||||
use std::path::Path;
|
||||
|
||||
use rustpython_ast::Location;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::autofix::Fix;
|
||||
use crate::checks::CheckCode;
|
||||
use crate::directives;
|
||||
use crate::linter::check_path;
|
||||
use crate::rustpython_helpers::tokenize;
|
||||
use crate::settings::configuration::Configuration;
|
||||
use crate::settings::options::Options;
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
const TYPES: &'static str = r#"
|
||||
export interface Check {
|
||||
code: string;
|
||||
message: string;
|
||||
location: {
|
||||
row: number;
|
||||
column: number;
|
||||
};
|
||||
end_location: {
|
||||
row: number;
|
||||
column: number;
|
||||
};
|
||||
fix: {
|
||||
content: string;
|
||||
location: {
|
||||
row: number;
|
||||
column: number;
|
||||
};
|
||||
end_location: {
|
||||
row: number;
|
||||
column: number;
|
||||
};
|
||||
} | null;
|
||||
};
|
||||
"#;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct Message {
|
||||
code: CheckCode,
|
||||
message: String,
|
||||
location: Location,
|
||||
end_location: Location,
|
||||
fix: Option<Fix>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
use log::Level;
|
||||
console_error_panic_hook::set_once();
|
||||
console_log::init_with_level(Level::Debug).expect("Initializing logger went wrong.");
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn check(contents: &str, options: JsValue) -> Result<JsValue, JsValue> {
|
||||
let options: Options = serde_wasm_bindgen::from_value(options).map_err(|e| e.to_string())?;
|
||||
let configuration =
|
||||
Configuration::from_options(options, Path::new(".")).map_err(|e| e.to_string())?;
|
||||
let settings =
|
||||
Settings::from_configuration(configuration, Path::new(".")).map_err(|e| e.to_string())?;
|
||||
|
||||
// Tokenize once.
|
||||
let tokens: Vec<LexResult> = tokenize(contents);
|
||||
|
||||
// Initialize the SourceCodeLocator (which computes offsets lazily).
|
||||
let locator = SourceCodeLocator::new(contents);
|
||||
|
||||
// Extract the `# noqa` and `# isort: skip` directives from the source.
|
||||
let directives = directives::extract_directives(&tokens, &locator, directives::Flags::empty());
|
||||
|
||||
// Generate checks.
|
||||
let checks = check_path(
|
||||
Path::new("<filename>"),
|
||||
None,
|
||||
contents,
|
||||
tokens,
|
||||
&locator,
|
||||
&directives,
|
||||
&settings,
|
||||
flags::Autofix::Enabled,
|
||||
flags::Noqa::Enabled,
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let messages: Vec<Message> = checks
|
||||
.into_iter()
|
||||
.map(|check| Message {
|
||||
code: check.kind.code().clone(),
|
||||
message: check.kind.body(),
|
||||
location: check.location,
|
||||
end_location: check.end_location,
|
||||
fix: check.fix,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(serde_wasm_bindgen::to_value(&messages)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use js_sys;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
macro_rules! check {
|
||||
($source:expr, $config:expr, $expected:expr) => {{
|
||||
let foo = js_sys::JSON::parse($config).unwrap();
|
||||
match check($source, foo) {
|
||||
Ok(output) => {
|
||||
let result: Vec<Message> = serde_wasm_bindgen::from_value(output).unwrap();
|
||||
assert_eq!(result, $expected);
|
||||
}
|
||||
Err(e) => assert!(false, "{:#?}", e),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn empty_config() {
|
||||
check!(
|
||||
"if (1, 2): pass",
|
||||
r#"{}"#,
|
||||
[Message {
|
||||
code: CheckCode::F634,
|
||||
message: "If test is a tuple, which is always `True`".to_string(),
|
||||
location: Location::new(1, 0),
|
||||
end_location: Location::new(1, 15),
|
||||
fix: None,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn partial_config() {
|
||||
check!("if (1, 2): pass", r#"{"ignore": ["F"]}"#, []);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn partial_nested_config() {
|
||||
let config = r#"{
|
||||
"select": ["Q"],
|
||||
"flake8-quotes": {
|
||||
"inline-quotes": "single"
|
||||
}
|
||||
}"#;
|
||||
check!(r#"print('hello world')"#, config, []);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ use std::ops::AddAssign;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use log::debug;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
|
||||
@@ -59,6 +58,9 @@ pub(crate) fn check_path(
|
||||
autofix: flags::Autofix,
|
||||
noqa: flags::Noqa,
|
||||
) -> Result<Vec<Check>> {
|
||||
// Validate the `Settings` and return any errors.
|
||||
settings.validate()?;
|
||||
|
||||
// Aggregate all checks.
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
@@ -175,6 +177,9 @@ pub fn lint_path(
|
||||
cache: flags::Cache,
|
||||
autofix: fixer::Mode,
|
||||
) -> Result<Diagnostics> {
|
||||
// Validate the `Settings` and return any errors.
|
||||
settings.validate()?;
|
||||
|
||||
let metadata = path.metadata()?;
|
||||
|
||||
// Check the cache.
|
||||
@@ -202,6 +207,9 @@ pub fn lint_path(
|
||||
|
||||
/// Add any missing `#noqa` pragmas to the source code at the given `Path`.
|
||||
pub fn add_noqa_to_path(path: &Path, settings: &Settings) -> Result<usize> {
|
||||
// Validate the `Settings` and return any errors.
|
||||
settings.validate()?;
|
||||
|
||||
// Read the file from disk.
|
||||
let contents = fs::read_file(path)?;
|
||||
|
||||
@@ -241,7 +249,10 @@ pub fn add_noqa_to_path(path: &Path, settings: &Settings) -> Result<usize> {
|
||||
}
|
||||
|
||||
/// Apply autoformatting to the source code at the given `Path`.
|
||||
pub fn autoformat_path(path: &Path, _settings: &Settings) -> Result<()> {
|
||||
pub fn autoformat_path(path: &Path, settings: &Settings) -> Result<()> {
|
||||
// Validate the `Settings` and return any errors.
|
||||
settings.validate()?;
|
||||
|
||||
// Read the file from disk.
|
||||
let contents = fs::read_file(path)?;
|
||||
|
||||
@@ -266,6 +277,9 @@ pub fn lint_stdin(
|
||||
settings: &Settings,
|
||||
autofix: fixer::Mode,
|
||||
) -> Result<Diagnostics> {
|
||||
// Validate the `Settings` and return any errors.
|
||||
settings.validate()?;
|
||||
|
||||
// Read the file from disk.
|
||||
let contents = stdin.to_string();
|
||||
|
||||
|
||||
251
src/main.rs
251
src/main.rs
@@ -11,256 +11,23 @@
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
use std::io::{self};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::ExitCode;
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
use ::ruff::autofix::fixer;
|
||||
use ::ruff::cli::{extract_log_level, Cli, Overrides};
|
||||
use ::ruff::logging::{set_up_logging, LogLevel};
|
||||
use ::ruff::printer::Printer;
|
||||
use ::ruff::resolver::{resolve_settings, FileDiscovery, PyprojectDiscovery, Relativity};
|
||||
use ::ruff::settings::configuration::Configuration;
|
||||
use ::ruff::settings::types::SerializationFormat;
|
||||
use ::ruff::settings::{pyproject, Settings};
|
||||
#[cfg(feature = "update-informer")]
|
||||
use ::ruff::updates;
|
||||
use ::ruff::{cache, commands};
|
||||
use anyhow::Result;
|
||||
use clap::{CommandFactory, Parser};
|
||||
use cfg_if::cfg_if;
|
||||
use colored::Colorize;
|
||||
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
||||
use path_absolutize::path_dedot;
|
||||
|
||||
/// Resolve the relevant settings strategy and defaults for the current
|
||||
/// invocation.
|
||||
fn resolve(
|
||||
config: Option<&Path>,
|
||||
overrides: &Overrides,
|
||||
stdin_filename: Option<&Path>,
|
||||
) -> Result<PyprojectDiscovery> {
|
||||
if let Some(pyproject) = config {
|
||||
// First priority: the user specified a `pyproject.toml` file. Use that
|
||||
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
|
||||
// current working directory. (This matches ESLint's behavior.)
|
||||
let settings = resolve_settings(pyproject, &Relativity::Cwd, Some(overrides))?;
|
||||
Ok(PyprojectDiscovery::Fixed(settings))
|
||||
} else if let Some(pyproject) = pyproject::find_pyproject_toml(
|
||||
stdin_filename
|
||||
.as_ref()
|
||||
.unwrap_or(&path_dedot::CWD.as_path()),
|
||||
)? {
|
||||
// Second priority: find a `pyproject.toml` file in either an ancestor of
|
||||
// `stdin_filename` (if set) or the current working path all paths relative to
|
||||
// that directory. (With `Strategy::Hierarchical`, we'll end up finding
|
||||
// the "closest" `pyproject.toml` file for every Python file later on,
|
||||
// so these act as the "default" settings.)
|
||||
let settings = resolve_settings(&pyproject, &Relativity::Parent, Some(overrides))?;
|
||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||
} else if let Some(pyproject) = pyproject::find_user_pyproject_toml() {
|
||||
// Third priority: find a user-specific `pyproject.toml`, but resolve all paths
|
||||
// relative the current working directory. (With `Strategy::Hierarchical`, we'll
|
||||
// end up the "closest" `pyproject.toml` file for every Python file later on, so
|
||||
// these act as the "default" settings.)
|
||||
let settings = resolve_settings(&pyproject, &Relativity::Cwd, Some(overrides))?;
|
||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||
cfg_if! {
|
||||
if #[cfg(not(target_family = "wasm"))] {
|
||||
mod main_native;
|
||||
use main_native::inner_main;
|
||||
} else {
|
||||
// Fallback: load Ruff's default settings, and resolve all paths relative to the
|
||||
// current working directory. (With `Strategy::Hierarchical`, we'll end up the
|
||||
// "closest" `pyproject.toml` file for every Python file later on, so these act
|
||||
// as the "default" settings.)
|
||||
let mut config = Configuration::default();
|
||||
// Apply command-line options that override defaults.
|
||||
config.apply(overrides.clone());
|
||||
let settings = Settings::from_configuration(config, &path_dedot::CWD)?;
|
||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||
}
|
||||
}
|
||||
use anyhow::Result;
|
||||
|
||||
fn inner_main() -> Result<ExitCode> {
|
||||
// Extract command-line arguments.
|
||||
let (cli, overrides) = Cli::parse().partition();
|
||||
let log_level = extract_log_level(&cli);
|
||||
set_up_logging(&log_level)?;
|
||||
|
||||
if cli.show_settings && cli.show_files {
|
||||
anyhow::bail!("specify --show-settings or show-files (not both)")
|
||||
}
|
||||
if let Some(shell) = cli.generate_shell_completion {
|
||||
shell.generate(&mut Cli::command(), &mut io::stdout());
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
// Construct the "default" settings. These are used when no `pyproject.toml`
|
||||
// files are present, or files are injected from outside of the hierarchy.
|
||||
let pyproject_strategy = resolve(
|
||||
cli.config.as_deref(),
|
||||
&overrides,
|
||||
cli.stdin_filename.as_deref(),
|
||||
)?;
|
||||
|
||||
// Extract options that are included in `Settings`, but only apply at the top
|
||||
// level.
|
||||
let file_strategy = FileDiscovery {
|
||||
force_exclude: match &pyproject_strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => settings.force_exclude,
|
||||
PyprojectDiscovery::Hierarchical(settings) => settings.force_exclude,
|
||||
},
|
||||
respect_gitignore: match &pyproject_strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => settings.respect_gitignore,
|
||||
PyprojectDiscovery::Hierarchical(settings) => settings.respect_gitignore,
|
||||
},
|
||||
};
|
||||
let (fix, format) = match &pyproject_strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => (settings.fix, settings.format),
|
||||
PyprojectDiscovery::Hierarchical(settings) => (settings.fix, settings.format),
|
||||
};
|
||||
let autofix = if fix {
|
||||
fixer::Mode::Apply
|
||||
} else if matches!(format, SerializationFormat::Json) {
|
||||
fixer::Mode::Generate
|
||||
} else {
|
||||
fixer::Mode::None
|
||||
};
|
||||
|
||||
if let Some(code) = cli.explain {
|
||||
commands::explain(&code, &format)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.show_settings {
|
||||
commands::show_settings(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.show_files {
|
||||
commands::show_files(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
// Initialize the cache.
|
||||
let mut cache_enabled: bool = !cli.no_cache;
|
||||
if cache_enabled && cache::init().is_err() {
|
||||
eprintln!("Unable to initialize cache; disabling...");
|
||||
cache_enabled = false;
|
||||
}
|
||||
|
||||
let printer = Printer::new(&format, &log_level);
|
||||
if cli.watch {
|
||||
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||
eprintln!("Warning: --fix is not enabled in watch mode.");
|
||||
}
|
||||
if cli.add_noqa {
|
||||
eprintln!("Warning: --no-qa is not enabled in watch mode.");
|
||||
}
|
||||
if cli.autoformat {
|
||||
eprintln!("Warning: --autoformat is not enabled in watch mode.");
|
||||
}
|
||||
if format != SerializationFormat::Text {
|
||||
eprintln!("Warning: --format 'text' is used in watch mode.");
|
||||
}
|
||||
|
||||
// Perform an initial run instantly.
|
||||
printer.clear_screen()?;
|
||||
printer.write_to_user("Starting linter in watch mode...\n");
|
||||
|
||||
let messages = commands::run(
|
||||
&cli.files,
|
||||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
cache_enabled.into(),
|
||||
fixer::Mode::None,
|
||||
)?;
|
||||
printer.write_continuously(&messages)?;
|
||||
|
||||
// Configure the file watcher.
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = recommended_watcher(tx)?;
|
||||
for file in &cli.files {
|
||||
watcher.watch(file, RecursiveMode::Recursive)?;
|
||||
}
|
||||
|
||||
loop {
|
||||
match rx.recv() {
|
||||
Ok(event) => {
|
||||
let paths = event?.paths;
|
||||
let py_changed = paths.iter().any(|path| {
|
||||
path.extension()
|
||||
.map(|ext| ext == "py" || ext == "pyi")
|
||||
.unwrap_or_default()
|
||||
});
|
||||
if py_changed {
|
||||
printer.clear_screen()?;
|
||||
printer.write_to_user("File change detected...\n");
|
||||
|
||||
let messages = commands::run(
|
||||
&cli.files,
|
||||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
cache_enabled.into(),
|
||||
fixer::Mode::None,
|
||||
)?;
|
||||
printer.write_continuously(&messages)?;
|
||||
}
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
}
|
||||
} else if cli.add_noqa {
|
||||
let modifications =
|
||||
commands::add_noqa(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||
if modifications > 0 && log_level >= LogLevel::Default {
|
||||
println!("Added {modifications} noqa directives.");
|
||||
}
|
||||
} else if cli.autoformat {
|
||||
let modifications =
|
||||
commands::autoformat(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||
if modifications > 0 && log_level >= LogLevel::Default {
|
||||
println!("Formatted {modifications} files.");
|
||||
}
|
||||
} else {
|
||||
let is_stdin = cli.files == vec![PathBuf::from("-")];
|
||||
|
||||
// Generate lint violations.
|
||||
let diagnostics = if is_stdin {
|
||||
commands::run_stdin(
|
||||
cli.stdin_filename.as_deref(),
|
||||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
autofix,
|
||||
)?
|
||||
} else {
|
||||
commands::run(
|
||||
&cli.files,
|
||||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
cache_enabled.into(),
|
||||
autofix,
|
||||
)?
|
||||
};
|
||||
|
||||
// Always try to print violations (the printer itself may suppress output),
|
||||
// unless we're writing fixes via stdin (in which case, the transformed
|
||||
// source code goes to stdout).
|
||||
if !(is_stdin && matches!(autofix, fixer::Mode::Apply)) {
|
||||
printer.write_once(&diagnostics, autofix)?;
|
||||
}
|
||||
|
||||
// Check for updates if we're in a non-silent log level.
|
||||
#[cfg(feature = "update-informer")]
|
||||
if !is_stdin && log_level >= LogLevel::Default && atty::is(atty::Stream::Stdout) {
|
||||
drop(updates::check_for_updates());
|
||||
}
|
||||
|
||||
if !diagnostics.messages.is_empty() && !cli.exit_zero {
|
||||
return Ok(ExitCode::FAILURE);
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn inner_main() -> Result<ExitCode> {
|
||||
Ok(ExitCode::FAILURE)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
|
||||
270
src/main_native.rs
Normal file
270
src/main_native.rs
Normal file
@@ -0,0 +1,270 @@
|
||||
#![allow(
|
||||
clippy::collapsible_else_if,
|
||||
clippy::collapsible_if,
|
||||
clippy::implicit_hasher,
|
||||
clippy::match_same_arms,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::must_use_candidate,
|
||||
clippy::similar_names,
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
use std::io::{self};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::ExitCode;
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
use ::ruff::autofix::fixer;
|
||||
use ::ruff::cli::{extract_log_level, Cli, Overrides};
|
||||
use ::ruff::commands;
|
||||
use ::ruff::logging::{set_up_logging, LogLevel};
|
||||
use ::ruff::printer::{Printer, Violations};
|
||||
use ::ruff::resolver::{resolve_settings, FileDiscovery, PyprojectDiscovery, Relativity};
|
||||
use ::ruff::settings::configuration::Configuration;
|
||||
use ::ruff::settings::types::SerializationFormat;
|
||||
use ::ruff::settings::{pyproject, Settings};
|
||||
#[cfg(feature = "update-informer")]
|
||||
use ::ruff::updates;
|
||||
use anyhow::Result;
|
||||
use clap::{CommandFactory, Parser};
|
||||
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
||||
use path_absolutize::path_dedot;
|
||||
|
||||
/// Resolve the relevant settings strategy and defaults for the current
|
||||
/// invocation.
|
||||
fn resolve(
|
||||
config: Option<&Path>,
|
||||
overrides: &Overrides,
|
||||
stdin_filename: Option<&Path>,
|
||||
) -> Result<PyprojectDiscovery> {
|
||||
if let Some(pyproject) = config {
|
||||
// First priority: the user specified a `pyproject.toml` file. Use that
|
||||
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
|
||||
// current working directory. (This matches ESLint's behavior.)
|
||||
let settings = resolve_settings(pyproject, &Relativity::Cwd, Some(overrides))?;
|
||||
Ok(PyprojectDiscovery::Fixed(settings))
|
||||
} else if let Some(pyproject) = pyproject::find_settings_toml(
|
||||
stdin_filename
|
||||
.as_ref()
|
||||
.unwrap_or(&path_dedot::CWD.as_path()),
|
||||
)? {
|
||||
// Second priority: find a `pyproject.toml` file in either an ancestor of
|
||||
// `stdin_filename` (if set) or the current working path all paths relative to
|
||||
// that directory. (With `Strategy::Hierarchical`, we'll end up finding
|
||||
// the "closest" `pyproject.toml` file for every Python file later on,
|
||||
// so these act as the "default" settings.)
|
||||
let settings = resolve_settings(&pyproject, &Relativity::Parent, Some(overrides))?;
|
||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||
} else if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
||||
// Third priority: find a user-specific `pyproject.toml`, but resolve all paths
|
||||
// relative the current working directory. (With `Strategy::Hierarchical`, we'll
|
||||
// end up the "closest" `pyproject.toml` file for every Python file later on, so
|
||||
// these act as the "default" settings.)
|
||||
let settings = resolve_settings(&pyproject, &Relativity::Cwd, Some(overrides))?;
|
||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||
} else {
|
||||
// Fallback: load Ruff's default settings, and resolve all paths relative to the
|
||||
// current working directory. (With `Strategy::Hierarchical`, we'll end up the
|
||||
// "closest" `pyproject.toml` file for every Python file later on, so these act
|
||||
// as the "default" settings.)
|
||||
let mut config = Configuration::default();
|
||||
// Apply command-line options that override defaults.
|
||||
config.apply(overrides.clone());
|
||||
let settings = Settings::from_configuration(config, &path_dedot::CWD)?;
|
||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn inner_main() -> Result<ExitCode> {
|
||||
// Extract command-line arguments.
|
||||
let (cli, overrides) = Cli::parse().partition();
|
||||
let log_level = extract_log_level(&cli);
|
||||
set_up_logging(&log_level)?;
|
||||
|
||||
if cli.show_settings && cli.show_files {
|
||||
anyhow::bail!("specify --show-settings or show-files (not both)")
|
||||
}
|
||||
if let Some(shell) = cli.generate_shell_completion {
|
||||
shell.generate(&mut Cli::command(), &mut io::stdout());
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
// Construct the "default" settings. These are used when no `pyproject.toml`
|
||||
// files are present, or files are injected from outside of the hierarchy.
|
||||
let pyproject_strategy = resolve(
|
||||
cli.config.as_deref(),
|
||||
&overrides,
|
||||
cli.stdin_filename.as_deref(),
|
||||
)?;
|
||||
|
||||
// Validate the `Settings` and return any errors.
|
||||
match &pyproject_strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => settings.validate()?,
|
||||
PyprojectDiscovery::Hierarchical(settings) => settings.validate()?,
|
||||
};
|
||||
|
||||
// Extract options that are included in `Settings`, but only apply at the top
|
||||
// level.
|
||||
let file_strategy = FileDiscovery {
|
||||
force_exclude: match &pyproject_strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => settings.force_exclude,
|
||||
PyprojectDiscovery::Hierarchical(settings) => settings.force_exclude,
|
||||
},
|
||||
respect_gitignore: match &pyproject_strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => settings.respect_gitignore,
|
||||
PyprojectDiscovery::Hierarchical(settings) => settings.respect_gitignore,
|
||||
},
|
||||
};
|
||||
let (fix, fix_only, format) = match &pyproject_strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => (settings.fix, settings.fix_only, settings.format),
|
||||
PyprojectDiscovery::Hierarchical(settings) => {
|
||||
(settings.fix, settings.fix_only, settings.format)
|
||||
}
|
||||
};
|
||||
let autofix = if fix || fix_only {
|
||||
fixer::Mode::Apply
|
||||
} else if matches!(format, SerializationFormat::Json) {
|
||||
fixer::Mode::Generate
|
||||
} else {
|
||||
fixer::Mode::None
|
||||
};
|
||||
let violations = if fix_only {
|
||||
Violations::Hide
|
||||
} else {
|
||||
Violations::Show
|
||||
};
|
||||
let cache = !cli.no_cache;
|
||||
|
||||
if let Some(code) = cli.explain {
|
||||
commands::explain(&code, &format)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.show_settings {
|
||||
commands::show_settings(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.show_files {
|
||||
commands::show_files(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
let printer = Printer::new(&format, &log_level, &autofix, &violations);
|
||||
if cli.watch {
|
||||
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||
eprintln!("Warning: --fix is not enabled in watch mode.");
|
||||
}
|
||||
if cli.add_noqa {
|
||||
eprintln!("Warning: --add-noqa is not enabled in watch mode.");
|
||||
}
|
||||
if cli.autoformat {
|
||||
eprintln!("Warning: --autoformat is not enabled in watch mode.");
|
||||
}
|
||||
if format != SerializationFormat::Text {
|
||||
eprintln!("Warning: --format 'text' is used in watch mode.");
|
||||
}
|
||||
|
||||
// Perform an initial run instantly.
|
||||
printer.clear_screen()?;
|
||||
printer.write_to_user("Starting linter in watch mode...\n");
|
||||
|
||||
let messages = commands::run(
|
||||
&cli.files,
|
||||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
cache.into(),
|
||||
fixer::Mode::None,
|
||||
)?;
|
||||
printer.write_continuously(&messages)?;
|
||||
|
||||
// Configure the file watcher.
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = recommended_watcher(tx)?;
|
||||
for file in &cli.files {
|
||||
watcher.watch(file, RecursiveMode::Recursive)?;
|
||||
}
|
||||
|
||||
loop {
|
||||
match rx.recv() {
|
||||
Ok(event) => {
|
||||
let paths = event?.paths;
|
||||
let py_changed = paths.iter().any(|path| {
|
||||
path.extension()
|
||||
.map(|ext| ext == "py" || ext == "pyi")
|
||||
.unwrap_or_default()
|
||||
});
|
||||
if py_changed {
|
||||
printer.clear_screen()?;
|
||||
printer.write_to_user("File change detected...\n");
|
||||
|
||||
let messages = commands::run(
|
||||
&cli.files,
|
||||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
cache.into(),
|
||||
fixer::Mode::None,
|
||||
)?;
|
||||
printer.write_continuously(&messages)?;
|
||||
}
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
}
|
||||
} else if cli.add_noqa {
|
||||
let modifications =
|
||||
commands::add_noqa(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||
if modifications > 0 && log_level >= LogLevel::Default {
|
||||
println!("Added {modifications} noqa directives.");
|
||||
}
|
||||
} else if cli.autoformat {
|
||||
let modifications =
|
||||
commands::autoformat(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||
if modifications > 0 && log_level >= LogLevel::Default {
|
||||
println!("Formatted {modifications} files.");
|
||||
}
|
||||
} else {
|
||||
let is_stdin = cli.files == vec![PathBuf::from("-")];
|
||||
|
||||
// Generate lint violations.
|
||||
let diagnostics = if is_stdin {
|
||||
commands::run_stdin(
|
||||
cli.stdin_filename.as_deref(),
|
||||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
autofix,
|
||||
)?
|
||||
} else {
|
||||
commands::run(
|
||||
&cli.files,
|
||||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
cache.into(),
|
||||
autofix,
|
||||
)?
|
||||
};
|
||||
|
||||
// Always try to print violations (the printer itself may suppress output),
|
||||
// unless we're writing fixes via stdin (in which case, the transformed
|
||||
// source code goes to stdout).
|
||||
if !(is_stdin && matches!(autofix, fixer::Mode::Apply)) {
|
||||
printer.write_once(&diagnostics)?;
|
||||
}
|
||||
|
||||
// Check for updates if we're in a non-silent log level.
|
||||
#[cfg(feature = "update-informer")]
|
||||
if !is_stdin && log_level >= LogLevel::Default && atty::is(atty::Stream::Stdout) {
|
||||
drop(updates::check_for_updates());
|
||||
}
|
||||
|
||||
if !diagnostics.messages.is_empty() && !cli.exit_zero && !fix_only {
|
||||
return Ok(ExitCode::FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user