Compare commits
272 Commits
useless_re
...
v0.0.263
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd7ccb4c9e | ||
|
|
ae6f38344a | ||
|
|
bbf658d4c5 | ||
|
|
1f3b0fd602 | ||
|
|
37483f3ac9 | ||
|
|
4d3a1e0581 | ||
|
|
9e5f348a17 | ||
|
|
5e91211e6d | ||
|
|
df77595426 | ||
|
|
407af6e0ae | ||
|
|
d64146683e | ||
|
|
0e7914010f | ||
|
|
cfc7d8a2b5 | ||
|
|
f5cd659292 | ||
|
|
260138b427 | ||
|
|
2da149fd7e | ||
|
|
e33887718d | ||
|
|
ba4f4f4672 | ||
|
|
b7a57ce120 | ||
|
|
82abbc7234 | ||
|
|
ba98149022 | ||
|
|
7fd44a3e12 | ||
|
|
6e8d561090 | ||
|
|
cb762f4cad | ||
|
|
eed6866b7e | ||
|
|
25a6bfa9ee | ||
|
|
b3f8f2a5c1 | ||
|
|
cc8b5a543b | ||
|
|
10d5415bcb | ||
|
|
827cbe7f97 | ||
|
|
0d84517fbc | ||
|
|
7fa1da20fb | ||
|
|
f13a161ead | ||
|
|
c4cda301aa | ||
|
|
13fda30051 | ||
|
|
a3146ab1ca | ||
|
|
c0cf87356e | ||
|
|
6c3e4ef441 | ||
|
|
6c038830a8 | ||
|
|
064a293b80 | ||
|
|
79c47e29ee | ||
|
|
be87a29a9d | ||
|
|
280dffb5e1 | ||
|
|
336993ea06 | ||
|
|
516cb10000 | ||
|
|
1cdd5e3424 | ||
|
|
bd78c6ade2 | ||
|
|
5ce35faa86 | ||
|
|
484b572e6b | ||
|
|
81805a45f0 | ||
|
|
c457752f36 | ||
|
|
289289bfd3 | ||
|
|
09274307e8 | ||
|
|
d8718dcf54 | ||
|
|
fb9eeba422 | ||
|
|
2d2630ef07 | ||
|
|
1f22e035e3 | ||
|
|
a6a7584d79 | ||
|
|
ffac4f6ec3 | ||
|
|
032a84b167 | ||
|
|
3357aaef4b | ||
|
|
d9ed43d112 | ||
|
|
e160a52bfd | ||
|
|
9067ae47d1 | ||
|
|
71e807b3be | ||
|
|
1e2df07544 | ||
|
|
860841468c | ||
|
|
ed4ecc3255 | ||
|
|
b999e4b1e2 | ||
|
|
8ce227047d | ||
|
|
523515f936 | ||
|
|
10da3bc8dd | ||
|
|
eb0dd74040 | ||
|
|
61200d2171 | ||
|
|
e8aebee3f6 | ||
|
|
210083bdd8 | ||
|
|
c33c9dc585 | ||
|
|
056c212975 | ||
|
|
381203c084 | ||
|
|
76c47a9a43 | ||
|
|
9209e57c5a | ||
|
|
333f1bd9ce | ||
|
|
002caadf9e | ||
|
|
311ba29d0f | ||
|
|
237a64d922 | ||
|
|
d4af2dd5cf | ||
|
|
a36ce585ce | ||
|
|
29ec6df24f | ||
|
|
8b17508ef1 | ||
|
|
abaf0a198d | ||
|
|
454c6d9c2f | ||
|
|
cae5503e34 | ||
|
|
34e9786a41 | ||
|
|
5467d45dfa | ||
|
|
ac87137c1c | ||
|
|
e0bccfd2d9 | ||
|
|
7b6e55a2e0 | ||
|
|
5c374b5793 | ||
|
|
ffdd0de522 | ||
|
|
5370968839 | ||
|
|
255b094b33 | ||
|
|
b6155232ac | ||
|
|
390d7dcf39 | ||
|
|
251340a246 | ||
|
|
d919adc13c | ||
|
|
46bcb1f725 | ||
|
|
2b21effa77 | ||
|
|
10504eb9ed | ||
|
|
76e111c874 | ||
|
|
e006b922a6 | ||
|
|
60f6a8571a | ||
|
|
f4173b2a93 | ||
|
|
449e08ed08 | ||
|
|
5625410936 | ||
|
|
3744e9ab3f | ||
|
|
b52cb93e58 | ||
|
|
849091d846 | ||
|
|
d2f2544f6e | ||
|
|
25771cd4b9 | ||
|
|
924bebbb4a | ||
|
|
08e5b3fa61 | ||
|
|
d822e08111 | ||
|
|
2f90157ce2 | ||
|
|
88308ef9cc | ||
|
|
6d80c79bac | ||
|
|
2fbc620ad3 | ||
|
|
27e40e9b31 | ||
|
|
b6276e2d95 | ||
|
|
66d72b1c7b | ||
|
|
968c7df770 | ||
|
|
fe38597279 | ||
|
|
f3f9a9f297 | ||
|
|
48d8680e71 | ||
|
|
82584ad101 | ||
|
|
13e52b1f76 | ||
|
|
dfc872c9a0 | ||
|
|
cf7e1ddd08 | ||
|
|
9de1f82658 | ||
|
|
54ad9397e5 | ||
|
|
29c8b75fd4 | ||
|
|
0b586d5451 | ||
|
|
01357f62e5 | ||
|
|
d7113d3995 | ||
|
|
a142d71e0b | ||
|
|
f79506f5a4 | ||
|
|
44ae3237b8 | ||
|
|
f4cda31708 | ||
|
|
4328448a2f | ||
|
|
88298759ce | ||
|
|
3c0e789b19 | ||
|
|
8601dcc09b | ||
|
|
134fdd1609 | ||
|
|
2e6eddc7bd | ||
|
|
cb588d1d6d | ||
|
|
9d3b8eb67b | ||
|
|
e1e5532ab1 | ||
|
|
7d962bf80c | ||
|
|
595cd065f3 | ||
|
|
b6f1fed424 | ||
|
|
5501fc9572 | ||
|
|
5977862a60 | ||
|
|
224e85c6d7 | ||
|
|
515e436cfa | ||
|
|
f322bcd2bd | ||
|
|
22d5b0071d | ||
|
|
990b378c4d | ||
|
|
e88fbae926 | ||
|
|
81de3a16bc | ||
|
|
bfecf684ce | ||
|
|
756e9956a2 | ||
|
|
f68c26a506 | ||
|
|
000394f428 | ||
|
|
2fdf98ef4e | ||
|
|
1d724b1495 | ||
|
|
113a8b8fda | ||
|
|
c3917eab38 | ||
|
|
0eb5a22dd1 | ||
|
|
450c6780ff | ||
|
|
5cb120327c | ||
|
|
8dbffb576d | ||
|
|
31fff4b10e | ||
|
|
2326335f5c | ||
|
|
6ed6da3e82 | ||
|
|
cd75b57036 | ||
|
|
e603382cf0 | ||
|
|
32be63fd1e | ||
|
|
d594179275 | ||
|
|
c0befb4670 | ||
|
|
a66481ed28 | ||
|
|
5c7898124f | ||
|
|
50a7916e84 | ||
|
|
6a40a5c5a2 | ||
|
|
fec4fa39a7 | ||
|
|
2659336ed1 | ||
|
|
8ac7584756 | ||
|
|
4a1740a4c4 | ||
|
|
2083134a96 | ||
|
|
c721eedc37 | ||
|
|
c1d89d8c93 | ||
|
|
b8ae1e0e05 | ||
|
|
63adf9f5e8 | ||
|
|
7af83460ce | ||
|
|
dc4d7619ee | ||
|
|
1bac206995 | ||
|
|
efc6e8cb39 | ||
|
|
7f3b748401 | ||
|
|
7da06b9741 | ||
|
|
0f95056f13 | ||
|
|
028329854b | ||
|
|
ba43d6bd0b | ||
|
|
e8d17d23cb | ||
|
|
aea925a898 | ||
|
|
f58345dee3 | ||
|
|
71c0da27bb | ||
|
|
8a2d1a3029 | ||
|
|
6161e56ea4 | ||
|
|
189c9d4683 | ||
|
|
615887a7fe | ||
|
|
07808a58f2 | ||
|
|
fe568c08d2 | ||
|
|
7741d43ae5 | ||
|
|
1b3e54231c | ||
|
|
3a8e98341b | ||
|
|
8593739f88 | ||
|
|
242dd3dae1 | ||
|
|
875f61cb62 | ||
|
|
3ec1ea8ac2 | ||
|
|
1e45b13958 | ||
|
|
9e61956711 | ||
|
|
5eae3fbbfb | ||
|
|
41e38ffa98 | ||
|
|
27903cdb11 | ||
|
|
3b1709ba1e | ||
|
|
33394e4a69 | ||
|
|
7b9bdc494a | ||
|
|
b06ca25421 | ||
|
|
c42f8b93d2 | ||
|
|
f59a22b6e5 | ||
|
|
b5edc6dfc9 | ||
|
|
626169e2ef | ||
|
|
e9f359ac5e | ||
|
|
318c2c80e2 | ||
|
|
92aa3a8178 | ||
|
|
22a4ab51f9 | ||
|
|
f70a49ed8b | ||
|
|
f039bf36a2 | ||
|
|
169dd72328 | ||
|
|
fd39ec4bdd | ||
|
|
7c0f17279c | ||
|
|
81d0884974 | ||
|
|
a45753f462 | ||
|
|
b08326162b | ||
|
|
3a65af4dae | ||
|
|
474aa0b196 | ||
|
|
4892167217 | ||
|
|
a5494b8541 | ||
|
|
9ac9a1c69e | ||
|
|
f06dff8af8 | ||
|
|
fe7443ce2f | ||
|
|
4bdb2dd362 | ||
|
|
53a4743631 | ||
|
|
4ffcd8366a | ||
|
|
dfb772c6f1 | ||
|
|
c21eb06922 | ||
|
|
16a350c731 | ||
|
|
fa04861724 | ||
|
|
404504ab41 | ||
|
|
621e4353e3 | ||
|
|
0c4926ff7b | ||
|
|
61653b9f27 | ||
|
|
8dd3959e74 | ||
|
|
50f9db21da |
@@ -26,4 +26,11 @@ rustflags = [
|
||||
"-Wclippy::print_stdout",
|
||||
"-Wclippy::print_stderr",
|
||||
"-Wclippy::dbg_macro",
|
||||
"-Wclippy::empty_drop",
|
||||
"-Wclippy::empty_structs_with_brackets",
|
||||
"-Wclippy::exit",
|
||||
"-Wclippy::get_unwrap",
|
||||
"-Wclippy::rc_buffer",
|
||||
"-Wclippy::rc_mutex",
|
||||
"-Wclippy::rest_pat_in_fully_bound_structs",
|
||||
]
|
||||
|
||||
96
.github/workflows/benchmark.yaml
vendored
96
.github/workflows/benchmark.yaml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: "PR - Install Rust toolchain"
|
||||
run: rustup show
|
||||
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: "PR - Build benchmarks"
|
||||
uses: actions-rs/cargo@v1
|
||||
@@ -75,59 +75,61 @@ jobs:
|
||||
- run-benchmark
|
||||
|
||||
steps:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
|
||||
- name: "Install critcmp"
|
||||
# Use debug build: Building takes much longer than the "slowness" of using the debug build.
|
||||
run: cargo install --debug critcmp
|
||||
- name: "Install cargo-binstall"
|
||||
uses: taiki-e/install-action@cargo-binstall
|
||||
|
||||
- name: "Linux | Download PR benchmark results"
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: benchmark-results-ubuntu-latest
|
||||
path: ./target/criterion
|
||||
- name: "Install critcmp"
|
||||
run: cargo binstall critcmp -y
|
||||
|
||||
- name: "Linux | Compare benchmark results"
|
||||
shell: bash
|
||||
run: |
|
||||
echo "### Benchmark" >> summary.md
|
||||
echo "#### Linux" >> summary.md
|
||||
echo "\`\`\`" >> summary.md
|
||||
critcmp main pr >> summary.md
|
||||
echo "\`\`\`" >> summary.md
|
||||
echo "" >> summary.md
|
||||
- name: "Linux | Download PR benchmark results"
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: benchmark-results-ubuntu-latest
|
||||
path: ./target/criterion
|
||||
|
||||
- name: "Linux | Cleanup benchmark results"
|
||||
run: rm -rf ./target/criterion
|
||||
- name: "Linux | Compare benchmark results"
|
||||
shell: bash
|
||||
run: |
|
||||
echo "### Benchmark" >> summary.md
|
||||
echo "#### Linux" >> summary.md
|
||||
echo "\`\`\`" >> summary.md
|
||||
critcmp main pr >> summary.md
|
||||
echo "\`\`\`" >> summary.md
|
||||
echo "" >> summary.md
|
||||
|
||||
- name: "Windows | Download PR benchmark results"
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: benchmark-results-windows-latest
|
||||
path: ./target/criterion
|
||||
- name: "Linux | Cleanup benchmark results"
|
||||
run: rm -rf ./target/criterion
|
||||
|
||||
- name: "Windows | Compare benchmark results"
|
||||
shell: bash
|
||||
run: |
|
||||
echo "#### Windows" >> summary.md
|
||||
echo "\`\`\`" >> summary.md
|
||||
critcmp main pr >> summary.md
|
||||
echo "\`\`\`" >> summary.md
|
||||
echo "" >> summary.md
|
||||
- name: "Windows | Download PR benchmark results"
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: benchmark-results-windows-latest
|
||||
path: ./target/criterion
|
||||
|
||||
echo ${{ github.event.pull_request.number }} > pr-number
|
||||
- name: "Windows | Compare benchmark results"
|
||||
shell: bash
|
||||
run: |
|
||||
echo "#### Windows" >> summary.md
|
||||
echo "\`\`\`" >> summary.md
|
||||
critcmp main pr >> summary.md
|
||||
echo "\`\`\`" >> summary.md
|
||||
echo "" >> summary.md
|
||||
|
||||
cat summary.md > $GITHUB_STEP_SUMMARY
|
||||
echo ${{ github.event.pull_request.number }} > pr-number
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: Upload PR Number
|
||||
with:
|
||||
name: pr-number
|
||||
path: pr-number
|
||||
cat summary.md > $GITHUB_STEP_SUMMARY
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: Upload Summary
|
||||
with:
|
||||
name: summary
|
||||
path: summary.md
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: Upload PR Number
|
||||
with:
|
||||
name: pr-number
|
||||
path: pr-number
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: Upload Summary
|
||||
with:
|
||||
name: summary
|
||||
path: summary.md
|
||||
|
||||
62
.github/workflows/ci.yaml
vendored
62
.github/workflows/ci.yaml
vendored
@@ -85,7 +85,6 @@ jobs:
|
||||
name: ruff
|
||||
path: target/debug/ruff
|
||||
|
||||
|
||||
cargo-test-wasm:
|
||||
runs-on: ubuntu-latest
|
||||
name: "cargo test (wasm)"
|
||||
@@ -111,14 +110,16 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
run: rustup component add rustfmt
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: ./scripts/add_rule.py --name DoTheThing --code PLC999 --linter pylint
|
||||
- run: ./scripts/add_rule.py --name DoTheThing --prefix PL --code C0999 --linter pylint
|
||||
- run: cargo check
|
||||
- run: cargo fmt --all --check
|
||||
- run: |
|
||||
./scripts/add_plugin.py test --url https://pypi.org/project/-test/0.1.0/ --prefix TST
|
||||
./scripts/add_rule.py --name FirstRule --code TST001 --linter test
|
||||
./scripts/add_rule.py --name FirstRule --prefix TST --code 001 --linter test
|
||||
- run: cargo check
|
||||
- run: cargo fmt --all --check
|
||||
|
||||
typos:
|
||||
name: "spell check"
|
||||
@@ -176,3 +177,56 @@ jobs:
|
||||
with:
|
||||
name: ecosystem-result
|
||||
path: ecosystem-result
|
||||
|
||||
cargo-udeps:
|
||||
name: "cargo udeps"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install nightly Rust toolchain"
|
||||
# Only pinned to make caching work, update freely
|
||||
run: rustup toolchain install nightly-2023-03-30
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Install cargo-udeps"
|
||||
uses: taiki-e/install-action@cargo-udeps
|
||||
- name: "Run cargo-udeps"
|
||||
run: |
|
||||
unused_dependencies=$(cargo +nightly-2023-03-30 udeps > unused.txt && cat unused.txt | cut -d $'\n' -f 2-)
|
||||
if [ -z "$unused_dependencies" ]; then
|
||||
echo "No unused dependencies found" > $GITHUB_STEP_SUMMARY
|
||||
exit 0
|
||||
else
|
||||
echo "Found unused dependencies" > $GITHUB_STEP_SUMMARY
|
||||
echo '```console' >> $GITHUB_STEP_SUMMARY
|
||||
echo "$unused_dependencies" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
pre-commit:
|
||||
name: "pre-commit"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Install pre-commit"
|
||||
run: pip install pre-commit
|
||||
- name: "Cache pre-commit"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
- name: "Run pre-commit"
|
||||
run: |
|
||||
echo '```console' > $GITHUB_STEP_SUMMARY
|
||||
# Enable color output for pre-commit and remove it for the summary
|
||||
SKIP=cargo-fmt,clippy,dev-generate-all pre-commit run --all-files --show-diff-on-failure --color=always | \
|
||||
tee >(sed -E 's/\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[mGK]//g' >> $GITHUB_STEP_SUMMARY) >&1
|
||||
exit_code=${PIPESTATUS[0]}
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
exit $exit_code
|
||||
|
||||
1
.github/workflows/pr-comment.yaml
vendored
1
.github/workflows/pr-comment.yaml
vendored
@@ -65,6 +65,7 @@ jobs:
|
||||
then
|
||||
echo "### Ecosystem" >> $GITHUB_OUTPUT
|
||||
cat pr/ecosystem/ecosystem-result >> $GITHUB_OUTPUT
|
||||
echo "" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if [[ -f pr/benchmark/summary.md ]]
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
||||
crates/ruff/resources/test/cpython
|
||||
mkdocs.yml
|
||||
.overrides
|
||||
github_search.jsonl
|
||||
|
||||
###
|
||||
# Rust.gitignore
|
||||
|
||||
@@ -28,17 +28,17 @@ repos:
|
||||
- id: cargo-fmt
|
||||
name: cargo fmt
|
||||
entry: cargo fmt --
|
||||
language: rust
|
||||
language: system
|
||||
types: [rust]
|
||||
- id: clippy
|
||||
name: clippy
|
||||
entry: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
language: rust
|
||||
language: system
|
||||
pass_filenames: false
|
||||
- id: ruff
|
||||
name: ruff
|
||||
entry: cargo run -p ruff_cli -- check --no-cache --force-exclude --fix --exit-non-zero-on-fix
|
||||
language: rust
|
||||
language: system
|
||||
types_or: [python, pyi]
|
||||
require_serial: true
|
||||
exclude: |
|
||||
@@ -49,7 +49,7 @@ repos:
|
||||
- id: dev-generate-all
|
||||
name: dev-generate-all
|
||||
entry: cargo dev generate-all
|
||||
language: rust
|
||||
language: system
|
||||
pass_filenames: false
|
||||
exclude: target
|
||||
|
||||
|
||||
@@ -1,5 +1,40 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.0.260
|
||||
|
||||
### Fixes are now represented as a list of edits ([#3709](https://github.com/charliermarsh/ruff/pull/3709))
|
||||
|
||||
Previously, Ruff represented each fix as a single edit, which prohibited Ruff from automatically
|
||||
fixing violations that required multiple edits across a file. As such, Ruff now represents each
|
||||
fix as a list of edits.
|
||||
|
||||
This primarily affects the JSON API. Ruff's JSON representation used to represent the `fix` field as
|
||||
a single edit, like so:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Remove unused import: `sys`",
|
||||
"content": "",
|
||||
"location": {"row": 1, "column": 0},
|
||||
"end_location": {"row": 2, "column": 0}
|
||||
}
|
||||
```
|
||||
|
||||
The updated representation instead includes a list of edits:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Remove unused import: `sys`",
|
||||
"edits": [
|
||||
{
|
||||
"content": "",
|
||||
"location": {"row": 1, "column": 0},
|
||||
"end_location": {"row": 2, "column": 0},
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 0.0.246
|
||||
|
||||
### `multiple-statements-on-one-line-def` (`E704`) was removed ([#2773](https://github.com/charliermarsh/ruff/pull/2773))
|
||||
@@ -145,4 +180,4 @@ default.
|
||||
`pyproject.toml` files are now resolved hierarchically, such that for each Python file, we find
|
||||
the first `pyproject.toml` file in its path, and use that to determine its lint settings.
|
||||
|
||||
See the [README](https://github.com/charliermarsh/ruff#pyprojecttoml-discovery) for more.
|
||||
See the [documentation](https://beta.ruff.rs/docs/configuration/#python-file-discovery) for more.
|
||||
|
||||
@@ -116,8 +116,7 @@ At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
To define the violation, start by creating a dedicated file for your rule under the appropriate
|
||||
rule linter (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`). That file should
|
||||
contain a struct defined via `#[violation]`, along with a function that creates the violation
|
||||
based on any required inputs. (Many of the existing examples live in `crates/ruff/src/violations.rs`,
|
||||
but we're looking to place new rules in their own files.)
|
||||
based on any required inputs.
|
||||
|
||||
To trigger the violation, you'll likely want to augment the logic in `crates/ruff/src/checkers/ast.rs`,
|
||||
which defines the Python AST visitor, responsible for iterating over the abstract syntax tree and
|
||||
@@ -215,6 +214,20 @@ them to [PyPI](https://pypi.org/project/ruff/).
|
||||
Ruff follows the [semver](https://semver.org/) versioning standard. However, as pre-1.0 software,
|
||||
even patch releases may contain [non-backwards-compatible changes](https://semver.org/#spec-item-4).
|
||||
|
||||
## Ecosystem CI
|
||||
|
||||
GitHub Actions will run your changes against a number of real-world projects from GitHub and
|
||||
report on any diagnostic differences. You can also run those checks locally via:
|
||||
|
||||
```shell
|
||||
python scripts/check_ecosystem.py path/to/your/ruff path/to/older/ruff
|
||||
```
|
||||
|
||||
You can also run the Ecosystem CI check in a Docker container across a larger set of projects by
|
||||
downloading the [`known-github-tomls.json`](https://github.com/akx/ruff-usage-aggregate/blob/master/data/known-github-tomls.jsonl)
|
||||
as `github_search.jsonl` and following the instructions in [scripts/Dockerfile.ecosystem](scripts/Dockerfile.ecosystem).
|
||||
Note that this check will take a while to run.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
First, clone [CPython](https://github.com/python/cpython). It's a large and diverse Python codebase,
|
||||
|
||||
914
Cargo.lock
generated
914
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
15
Cargo.toml
15
Cargo.toml
@@ -3,7 +3,7 @@ members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
rust-version = "1.67"
|
||||
rust-version = "1.69"
|
||||
homepage = "https://beta.ruff.rs/docs/"
|
||||
documentation = "https://beta.ruff.rs/docs/"
|
||||
repository = "https://github.com/charliermarsh/ruff"
|
||||
@@ -11,7 +11,7 @@ authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = { version = "1.0.69" }
|
||||
bitflags = { version = "1.3.2" }
|
||||
bitflags = { version = "2.1.0" }
|
||||
chrono = { version = "0.4.23", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.1.8", features = ["derive"] }
|
||||
colored = { version = "2.0.0" }
|
||||
@@ -24,6 +24,7 @@ is-macro = { version = "0.2.2" }
|
||||
itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "80e4c1399f95e5beb532fdd1e209ad2dbb470438" }
|
||||
log = { version = "0.4.17" }
|
||||
nohash-hasher = { version = "0.2.0" }
|
||||
once_cell = { version = "1.17.1" }
|
||||
path-absolutize = { version = "3.0.14" }
|
||||
proc-macro2 = { version = "1.0.51" }
|
||||
@@ -37,21 +38,19 @@ rustpython-parser = { features = [
|
||||
], git = "https://github.com/RustPython/RustPython.git", rev = "c15f670f2c30cfae6b41a1874893590148c74bc4" }
|
||||
schemars = { version = "0.8.12" }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = { version = "1.0.93" }
|
||||
serde_json = { version = "1.0.93", features = ["preserve_order"] }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
similar = { version = "2.2.1" }
|
||||
smallvec = { version = "1.10.0" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
syn = { version = "1.0.109" }
|
||||
syn = { version = "2.0.15" }
|
||||
test-case = { version = "3.0.0" }
|
||||
textwrap = { version = "0.16.0" }
|
||||
toml = { version = "0.7.2" }
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
lto = "fat"
|
||||
|
||||
[profile.dev.package.insta]
|
||||
opt-level = 3
|
||||
|
||||
35
LICENSE
35
LICENSE
@@ -195,6 +195,15 @@ are:
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-gettext, licensed as follows:
|
||||
"""
|
||||
BSD Zero Clause License
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-implicit-str-concat, licensed as follows:
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
@@ -393,7 +402,6 @@ are:
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
|
||||
|
||||
- autoflake, licensed as follows:
|
||||
"""
|
||||
Copyright (C) 2012-2018 Steven Myint
|
||||
@@ -417,6 +425,31 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- autotyping, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Jelle Zijlstra
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- Flake8, licensed as follows:
|
||||
"""
|
||||
== Flake8 License (MIT) ==
|
||||
|
||||
171
README.md
171
README.md
@@ -47,16 +47,16 @@ all while executing tens or hundreds of times faster than any individual tool.
|
||||
|
||||
Ruff is extremely actively developed and used in major open-source projects like:
|
||||
|
||||
- [pandas](https://github.com/pandas-dev/pandas)
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Transformers (Hugging Face)](https://github.com/huggingface/transformers)
|
||||
- [Apache Airflow](https://github.com/apache/airflow)
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Hugging Face](https://github.com/huggingface/transformers)
|
||||
- [Pandas](https://github.com/pandas-dev/pandas)
|
||||
- [SciPy](https://github.com/scipy/scipy)
|
||||
|
||||
...and many more.
|
||||
|
||||
Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster) or
|
||||
the most recent [project update](https://notes.crmarsh.com/ruff-the-first-200-releases).
|
||||
Ruff is backed by [Astral](https://astral.sh). Read the [launch post](https://astral.sh/blog/announcing-astral-the-company-behind-ruff),
|
||||
or the original [project announcement](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster).
|
||||
|
||||
## Testimonials
|
||||
|
||||
@@ -137,7 +137,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.0.256'
|
||||
rev: 'v0.0.263'
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -145,6 +145,20 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
Ruff can also be used as a [VS Code extension](https://github.com/charliermarsh/ruff-vscode) or
|
||||
alongside any other editor through the [Ruff LSP](https://github.com/charliermarsh/ruff-lsp).
|
||||
|
||||
Ruff can also be used as a [GitHub Action](https://github.com/features/actions) via
|
||||
[`ruff-action`](https://github.com/chartboost/ruff-action):
|
||||
|
||||
```yaml
|
||||
name: Ruff
|
||||
on: [ push, pull_request ]
|
||||
jobs:
|
||||
ruff:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: chartboost/ruff-action@v1
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
Ruff can be configured through a `pyproject.toml`, `ruff.toml`, or `.ruff.toml` file (see:
|
||||
@@ -160,7 +174,7 @@ select = ["E", "F"]
|
||||
ignore = []
|
||||
|
||||
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
||||
fixable = ["A", "B", "C", "D", "E", "F", "..."]
|
||||
fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"]
|
||||
unfixable = []
|
||||
|
||||
# Exclude a variety of commonly ignored directories.
|
||||
@@ -227,6 +241,55 @@ stylistic rules made obsolete by the use of an autoformatter, like
|
||||
If you're just getting started with Ruff, **the default rule set is a great place to start**: it
|
||||
catches a wide variety of common errors (like unused imports) with zero configuration.
|
||||
|
||||
Beyond the defaults, Ruff re-implements some of the most popular Flake8 plugins and related code
|
||||
quality tools, including:
|
||||
|
||||
- [autoflake](https://pypi.org/project/autoflake/)
|
||||
- [eradicate](https://pypi.org/project/eradicate/)
|
||||
- [flake8-2020](https://pypi.org/project/flake8-2020/)
|
||||
- [flake8-annotations](https://pypi.org/project/flake8-annotations/)
|
||||
- [flake8-bandit](https://pypi.org/project/flake8-bandit/) ([#1646](https://github.com/charliermarsh/ruff/issues/1646))
|
||||
- [flake8-blind-except](https://pypi.org/project/flake8-blind-except/)
|
||||
- [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap/)
|
||||
- [flake8-bugbear](https://pypi.org/project/flake8-bugbear/)
|
||||
- [flake8-builtins](https://pypi.org/project/flake8-builtins/)
|
||||
- [flake8-commas](https://pypi.org/project/flake8-commas/)
|
||||
- [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [flake8-datetimez](https://pypi.org/project/flake8-datetimez/)
|
||||
- [flake8-debugger](https://pypi.org/project/flake8-debugger/)
|
||||
- [flake8-django](https://pypi.org/project/flake8-django/)
|
||||
- [flake8-docstrings](https://pypi.org/project/flake8-docstrings/)
|
||||
- [flake8-eradicate](https://pypi.org/project/flake8-eradicate/)
|
||||
- [flake8-errmsg](https://pypi.org/project/flake8-errmsg/)
|
||||
- [flake8-executable](https://pypi.org/project/flake8-executable/)
|
||||
- [flake8-gettext](https://pypi.org/project/flake8-gettext/)
|
||||
- [flake8-implicit-str-concat](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
- [flake8-import-conventions](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
- [flake8-logging-format](https://pypi.org/project/flake8-logging-format/)
|
||||
- [flake8-no-pep420](https://pypi.org/project/flake8-no-pep420)
|
||||
- [flake8-pie](https://pypi.org/project/flake8-pie/)
|
||||
- [flake8-print](https://pypi.org/project/flake8-print/)
|
||||
- [flake8-pyi](https://pypi.org/project/flake8-pyi/)
|
||||
- [flake8-pytest-style](https://pypi.org/project/flake8-pytest-style/)
|
||||
- [flake8-quotes](https://pypi.org/project/flake8-quotes/)
|
||||
- [flake8-raise](https://pypi.org/project/flake8-raise/)
|
||||
- [flake8-return](https://pypi.org/project/flake8-return/)
|
||||
- [flake8-self](https://pypi.org/project/flake8-self/)
|
||||
- [flake8-simplify](https://pypi.org/project/flake8-simplify/)
|
||||
- [flake8-super](https://pypi.org/project/flake8-super/)
|
||||
- [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/)
|
||||
- [flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
|
||||
- [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/)
|
||||
- [isort](https://pypi.org/project/isort/)
|
||||
- [mccabe](https://pypi.org/project/mccabe/)
|
||||
- [pandas-vet](https://pypi.org/project/pandas-vet/)
|
||||
- [pep8-naming](https://pypi.org/project/pep8-naming/)
|
||||
- [pydocstyle](https://pypi.org/project/pydocstyle/)
|
||||
- [pygrep-hooks](https://github.com/pre-commit/pygrep-hooks) ([#980](https://github.com/charliermarsh/ruff/issues/980))
|
||||
- [pyupgrade](https://pypi.org/project/pyupgrade/)
|
||||
- [tryceratops](https://pypi.org/project/tryceratops/)
|
||||
- [yesqa](https://pypi.org/project/yesqa/)
|
||||
|
||||
<!-- End section: Rules -->
|
||||
|
||||
For a complete enumeration of the supported rules, see [_Rules_](https://beta.ruff.rs/docs/rules/).
|
||||
@@ -269,50 +332,68 @@ Ruff is released under the MIT license.
|
||||
|
||||
## Who's Using Ruff?
|
||||
|
||||
Ruff is used in a number of major open-source projects, including:
|
||||
Ruff is used by a number of major open-source projects and companies, including:
|
||||
|
||||
- [pandas](https://github.com/pandas-dev/pandas)
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Transformers (Hugging Face)](https://github.com/huggingface/transformers)
|
||||
- [Diffusers (Hugging Face)](https://github.com/huggingface/diffusers)
|
||||
- Amazon ([AWS SAM](https://github.com/aws/serverless-application-model))
|
||||
- [Apache Airflow](https://github.com/apache/airflow)
|
||||
- [SciPy](https://github.com/scipy/scipy)
|
||||
- [Zulip](https://github.com/zulip/zulip)
|
||||
- [Bokeh](https://github.com/bokeh/bokeh)
|
||||
- [Pydantic](https://github.com/pydantic/pydantic)
|
||||
- [Dagster](https://github.com/dagster-io/dagster)
|
||||
- [Dagger](https://github.com/dagger/dagger)
|
||||
- [Sphinx](https://github.com/sphinx-doc/sphinx)
|
||||
- [Hatch](https://github.com/pypa/hatch)
|
||||
- [PDM](https://github.com/pdm-project/pdm)
|
||||
- [Jupyter](https://github.com/jupyter-server/jupyter_server)
|
||||
- [Great Expectations](https://github.com/great-expectations/great_expectations)
|
||||
- [ONNX](https://github.com/onnx/onnx)
|
||||
- [Polars](https://github.com/pola-rs/polars)
|
||||
- [Ibis](https://github.com/ibis-project/ibis)
|
||||
- [Synapse (Matrix)](https://github.com/matrix-org/synapse)
|
||||
- [SnowCLI (Snowflake)](https://github.com/Snowflake-Labs/snowcli)
|
||||
- [Dispatch (Netflix)](https://github.com/Netflix/dispatch)
|
||||
- [Saleor](https://github.com/saleor/saleor)
|
||||
- [Pynecone](https://github.com/pynecone-io/pynecone)
|
||||
- [OpenBB](https://github.com/OpenBB-finance/OpenBBTerminal)
|
||||
- [Home Assistant](https://github.com/home-assistant/core)
|
||||
- [Pylint](https://github.com/PyCQA/pylint)
|
||||
- [Cryptography (PyCA)](https://github.com/pyca/cryptography)
|
||||
- [cibuildwheel (PyPA)](https://github.com/pypa/cibuildwheel)
|
||||
- [build (PyPA)](https://github.com/pypa/build)
|
||||
- AstraZeneca ([Magnus](https://github.com/AstraZeneca/magnus-core))
|
||||
- Benchling ([Refac](https://github.com/benchling/refac))
|
||||
- [Babel](https://github.com/python-babel/babel)
|
||||
- [Bokeh](https://github.com/bokeh/bokeh)
|
||||
- [Cryptography (PyCA)](https://github.com/pyca/cryptography)
|
||||
- [Dagger](https://github.com/dagger/dagger)
|
||||
- [Dagster](https://github.com/dagster-io/dagster)
|
||||
- [DVC](https://github.com/iterative/dvc)
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Gradio](https://github.com/gradio-app/gradio)
|
||||
- [Great Expectations](https://github.com/great-expectations/great_expectations)
|
||||
- Hugging Face ([Transformers](https://github.com/huggingface/transformers), [Datasets](https://github.com/huggingface/datasets), [Diffusers](https://github.com/huggingface/diffusers))
|
||||
- [Hatch](https://github.com/pypa/hatch)
|
||||
- [Home Assistant](https://github.com/home-assistant/core)
|
||||
- [Ibis](https://github.com/ibis-project/ibis)
|
||||
- [Jupyter](https://github.com/jupyter-server/jupyter_server)
|
||||
- [LangChain](https://github.com/hwchase17/langchain)
|
||||
- [LlamaIndex](https://github.com/jerryjliu/llama_index)
|
||||
- Matrix ([Synapse](https://github.com/matrix-org/synapse))
|
||||
- Meltano ([Meltano CLI](https://github.com/meltano/meltano), [Singer SDK](https://github.com/meltano/sdk))
|
||||
- Modern Treasury ([Python SDK](https://github.com/Modern-Treasury/modern-treasury-python-sdk))
|
||||
- Mozilla ([Firefox](https://github.com/mozilla/gecko-dev))
|
||||
- [MegaLinter](https://github.com/oxsecurity/megalinter)
|
||||
- Microsoft ([Semantic Kernel](https://github.com/microsoft/semantic-kernel), [ONNX Runtime](https://github.com/microsoft/onnxruntime))
|
||||
- Netflix ([Dispatch](https://github.com/Netflix/dispatch))
|
||||
- [Neon](https://github.com/neondatabase/neon)
|
||||
- [ONNX](https://github.com/onnx/onnx)
|
||||
- [OpenBB](https://github.com/OpenBB-finance/OpenBBTerminal)
|
||||
- [PDM](https://github.com/pdm-project/pdm)
|
||||
- [PaddlePaddle](https://github.com/PaddlePaddle/Paddle)
|
||||
- [Pandas](https://github.com/pandas-dev/pandas)
|
||||
- [Poetry](https://github.com/python-poetry/poetry)
|
||||
- [Polars](https://github.com/pola-rs/polars)
|
||||
- [PostHog](https://github.com/PostHog/posthog)
|
||||
- Prefect ([Python SDK](https://github.com/PrefectHQ/prefect), [Marvin](https://github.com/PrefectHQ/marvin))
|
||||
- [Pydantic](https://github.com/pydantic/pydantic)
|
||||
- [PyInstaller](https://github.com/pyinstaller/pyinstaller)
|
||||
- [Pylint](https://github.com/PyCQA/pylint)
|
||||
- [Pynecone](https://github.com/pynecone-io/pynecone)
|
||||
- [Robyn](https://github.com/sansyrox/robyn)
|
||||
- Scale AI ([Launch SDK](https://github.com/scaleapi/launch-python-client))
|
||||
- Snowflake ([SnowCLI](https://github.com/Snowflake-Labs/snowcli))
|
||||
- [Saleor](https://github.com/saleor/saleor)
|
||||
- [SciPy](https://github.com/scipy/scipy)
|
||||
- [Sphinx](https://github.com/sphinx-doc/sphinx)
|
||||
- [Stable Baselines3](https://github.com/DLR-RM/stable-baselines3)
|
||||
- [Starlite](https://github.com/starlite-api/starlite)
|
||||
- [The Algorithms](https://github.com/TheAlgorithms/Python)
|
||||
- [Vega-Altair](https://github.com/altair-viz/altair)
|
||||
- WordPress ([Openverse](https://github.com/WordPress/openverse))
|
||||
- [ZenML](https://github.com/zenml-io/zenml)
|
||||
- [Zulip](https://github.com/zulip/zulip)
|
||||
- [build (PyPA)](https://github.com/pypa/build)
|
||||
- [cibuildwheel (PyPA)](https://github.com/pypa/cibuildwheel)
|
||||
- [delta-rs](https://github.com/delta-io/delta-rs)
|
||||
- [featuretools](https://github.com/alteryx/featuretools)
|
||||
- [meson-python](https://github.com/mesonbuild/meson-python)
|
||||
- [ZenML](https://github.com/zenml-io/zenml)
|
||||
- [delta-rs](https://github.com/delta-io/delta-rs)
|
||||
- [Starlite](https://github.com/starlite-api/starlite)
|
||||
- [telemetry-airflow (Mozilla)](https://github.com/mozilla/telemetry-airflow)
|
||||
- [Stable Baselines3](https://github.com/DLR-RM/stable-baselines3)
|
||||
- [PaddlePaddle](https://github.com/PaddlePaddle/Paddle)
|
||||
- [nox](https://github.com/wntrblm/nox)
|
||||
- [Neon](https://github.com/neondatabase/neon)
|
||||
- [The Algorithms](https://github.com/TheAlgorithms/Python)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -5,3 +5,4 @@ extend-exclude = ["snapshots", "black"]
|
||||
trivias = "trivias"
|
||||
hel = "hel"
|
||||
whos = "whos"
|
||||
spawnve = "spawnve"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.256"
|
||||
version = "0.0.263"
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
|
||||
|
||||
@@ -85,8 +85,9 @@ flake8-to-ruff path/to/.flake8 --plugin flake8-builtins --plugin flake8-quotes
|
||||
ignore unsupported options in the `.flake8` file (or equivalent). (Similarly, Ruff has a few
|
||||
configuration options that don't exist in Flake8.)
|
||||
1. Ruff will omit any rule codes that are unimplemented or unsupported by Ruff, including rule
|
||||
codes from unsupported plugins. (See the [Ruff README](https://github.com/charliermarsh/ruff#user-content-how-does-ruff-compare-to-flake8)
|
||||
for the complete list of supported plugins.)
|
||||
codes from unsupported plugins. (See the
|
||||
[documentation](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) for the complete
|
||||
list of supported plugins.)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.256"
|
||||
version = "0.0.263"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
@@ -18,16 +18,18 @@ ruff_cache = { path = "../ruff_cache" }
|
||||
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
|
||||
ruff_macros = { path = "../ruff_macros" }
|
||||
ruff_python_ast = { path = "../ruff_python_ast" }
|
||||
ruff_python_semantic = { path = "../ruff_python_semantic" }
|
||||
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
|
||||
ruff_rustpython = { path = "../ruff_rustpython" }
|
||||
ruff_text_size = { path = "../ruff_text_size" }
|
||||
|
||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||
anyhow = { workspace = true }
|
||||
bisection = { version = "0.1.0" }
|
||||
bitflags = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive", "string"], optional = true }
|
||||
colored = { workspace = true }
|
||||
dirs = { version = "4.0.0" }
|
||||
dirs = { version = "5.0.0" }
|
||||
fern = { version = "0.6.1" }
|
||||
glob = { workspace = true }
|
||||
globset = { workspace = true }
|
||||
@@ -38,7 +40,7 @@ itertools = { workspace = true }
|
||||
libcst = { workspace = true }
|
||||
log = { workspace = true }
|
||||
natord = { version = "1.0.9" }
|
||||
nohash-hasher = { version = "0.2.0" }
|
||||
nohash-hasher = { workspace = true }
|
||||
num-bigint = { version = "0.4.3" }
|
||||
num-traits = { version = "0.2.15" }
|
||||
once_cell = { workspace = true }
|
||||
@@ -47,9 +49,8 @@ path-absolutize = { workspace = true, features = [
|
||||
"use_unix_paths_on_wasm",
|
||||
] }
|
||||
pathdiff = { version = "0.2.1" }
|
||||
pep440_rs = { git = "https://github.com/konstin/pep440-rs.git", features = [
|
||||
"serde",
|
||||
], rev = "a8fef4ec47f4c25b070b39cdbe6a0b9847e49941" }
|
||||
pep440_rs = { version = "0.3.1", features = ["serde"] }
|
||||
quick-junit = { version = "0.3.2" }
|
||||
regex = { workspace = true }
|
||||
result-like = { version = "0.4.6" }
|
||||
rustc-hash = { workspace = true }
|
||||
@@ -58,20 +59,26 @@ rustpython-parser = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
similar = { workspace = true, features = ["inline"] }
|
||||
shellexpand = { workspace = true }
|
||||
smallvec = { version = "1.10.0" }
|
||||
smallvec = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
textwrap = { workspace = true }
|
||||
thiserror = { version = "1.0.38" }
|
||||
toml = { workspace = true }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unicode-width = { version = "0.1.10" }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.4.0" }
|
||||
insta = { workspace = true, features = ["yaml", "redactions"] }
|
||||
insta = { workspace = true }
|
||||
pretty_assertions = "1.3.0"
|
||||
test-case = { workspace = true }
|
||||
# Disable colored output in tests
|
||||
colored = { workspace = true, features = ["no-color"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
logical_lines = []
|
||||
jupyter_notebook = []
|
||||
|
||||
@@ -2,3 +2,7 @@ avoid-*
|
||||
do-not-*
|
||||
uses-*
|
||||
*-used
|
||||
rewrite-*
|
||||
prefer-*
|
||||
consider-*
|
||||
use-*
|
||||
|
||||
@@ -9,6 +9,7 @@ def foo(x, y, z):
|
||||
print(x, y, z)
|
||||
|
||||
# This is a real comment.
|
||||
# # This is a (nested) comment.
|
||||
#return True
|
||||
return False
|
||||
|
||||
|
||||
42
crates/ruff/resources/test/fixtures/flake8_annotations/simple_magic_methods.py
vendored
Normal file
42
crates/ruff/resources/test/fixtures/flake8_annotations/simple_magic_methods.py
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
class Foo:
|
||||
def __str__(self):
|
||||
...
|
||||
|
||||
def __repr__(self):
|
||||
...
|
||||
|
||||
def __len__(self):
|
||||
...
|
||||
|
||||
def __length_hint__(self):
|
||||
...
|
||||
|
||||
def __init__(self):
|
||||
...
|
||||
|
||||
def __del__(self):
|
||||
...
|
||||
|
||||
def __bool__(self):
|
||||
...
|
||||
|
||||
def __bytes__(self):
|
||||
...
|
||||
|
||||
def __format__(self, format_spec):
|
||||
...
|
||||
|
||||
def __contains__(self, item):
|
||||
...
|
||||
|
||||
def __complex__(self):
|
||||
...
|
||||
|
||||
def __int__(self):
|
||||
...
|
||||
|
||||
def __float__(self):
|
||||
...
|
||||
|
||||
def __index__(self):
|
||||
...
|
||||
@@ -1,11 +1,13 @@
|
||||
# Error
|
||||
assert True
|
||||
assert True # S101
|
||||
|
||||
|
||||
def fn():
|
||||
x = 1
|
||||
assert x == 1 # S101
|
||||
assert x == 2 # S101
|
||||
|
||||
# Error
|
||||
assert x == 1
|
||||
|
||||
# Error
|
||||
assert x == 2
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert True # OK
|
||||
|
||||
3
crates/ruff/resources/test/fixtures/flake8_bandit/S301.py
vendored
Normal file
3
crates/ruff/resources/test/fixtures/flake8_bandit/S301.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import pickle
|
||||
|
||||
pickle.loads()
|
||||
3
crates/ruff/resources/test/fixtures/flake8_bandit/S312.py
vendored
Normal file
3
crates/ruff/resources/test/fixtures/flake8_bandit/S312.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
from telnetlib import Telnet
|
||||
|
||||
Telnet("localhost", 23)
|
||||
20
crates/ruff/resources/test/fixtures/flake8_bandit/S602.py
vendored
Normal file
20
crates/ruff/resources/test/fixtures/flake8_bandit/S602.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
from subprocess import Popen, call, check_call, check_output, run
|
||||
|
||||
# Check different Popen wrappers are checked.
|
||||
Popen("true", shell=True)
|
||||
call("true", shell=True)
|
||||
check_call("true", shell=True)
|
||||
check_output("true", shell=True)
|
||||
run("true", shell=True)
|
||||
|
||||
# Check values that truthy values are treated as true.
|
||||
Popen("true", shell=1)
|
||||
Popen("true", shell=[1])
|
||||
Popen("true", shell={1: 1})
|
||||
Popen("true", shell=(1,))
|
||||
|
||||
# Check command argument looks unsafe.
|
||||
var_string = "true"
|
||||
Popen(var_string, shell=True)
|
||||
Popen([var_string], shell=True)
|
||||
Popen([var_string, ""], shell=True)
|
||||
20
crates/ruff/resources/test/fixtures/flake8_bandit/S603.py
vendored
Normal file
20
crates/ruff/resources/test/fixtures/flake8_bandit/S603.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
from subprocess import Popen, call, check_call, check_output, run
|
||||
|
||||
# Different Popen wrappers are checked.
|
||||
Popen("true", shell=False)
|
||||
call("true", shell=False)
|
||||
check_call("true", shell=False)
|
||||
check_output("true", shell=False)
|
||||
run("true", shell=False)
|
||||
|
||||
# Values that falsey values are treated as false.
|
||||
Popen("true", shell=0)
|
||||
Popen("true", shell=[])
|
||||
Popen("true", shell={})
|
||||
Popen("true", shell=None)
|
||||
|
||||
# Unknown values are treated as falsey.
|
||||
Popen("true", shell=True if True else False)
|
||||
|
||||
# No value is also caught.
|
||||
Popen("true")
|
||||
5
crates/ruff/resources/test/fixtures/flake8_bandit/S604.py
vendored
Normal file
5
crates/ruff/resources/test/fixtures/flake8_bandit/S604.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
def foo(shell):
|
||||
pass
|
||||
|
||||
|
||||
foo(shell=True)
|
||||
25
crates/ruff/resources/test/fixtures/flake8_bandit/S605.py
vendored
Normal file
25
crates/ruff/resources/test/fixtures/flake8_bandit/S605.py
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import os
|
||||
|
||||
import commands
|
||||
import popen2
|
||||
|
||||
# Check all shell functions.
|
||||
os.system("true")
|
||||
os.popen("true")
|
||||
os.popen2("true")
|
||||
os.popen3("true")
|
||||
os.popen4("true")
|
||||
popen2.popen2("true")
|
||||
popen2.popen3("true")
|
||||
popen2.popen4("true")
|
||||
popen2.Popen3("true")
|
||||
popen2.Popen4("true")
|
||||
commands.getoutput("true")
|
||||
commands.getstatusoutput("true")
|
||||
|
||||
|
||||
# Check command argument looks unsafe.
|
||||
var_string = "true"
|
||||
os.system(var_string)
|
||||
os.system([var_string])
|
||||
os.system([var_string, ""])
|
||||
20
crates/ruff/resources/test/fixtures/flake8_bandit/S606.py
vendored
Normal file
20
crates/ruff/resources/test/fixtures/flake8_bandit/S606.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import os
|
||||
|
||||
# Check all shell functions.
|
||||
os.execl("true")
|
||||
os.execle("true")
|
||||
os.execlp("true")
|
||||
os.execlpe("true")
|
||||
os.execv("true")
|
||||
os.execve("true")
|
||||
os.execvp("true")
|
||||
os.execvpe("true")
|
||||
os.spawnl("true")
|
||||
os.spawnle("true")
|
||||
os.spawnlp("true")
|
||||
os.spawnlpe("true")
|
||||
os.spawnv("true")
|
||||
os.spawnve("true")
|
||||
os.spawnvp("true")
|
||||
os.spawnvpe("true")
|
||||
os.startfile("true")
|
||||
44
crates/ruff/resources/test/fixtures/flake8_bandit/S607.py
vendored
Normal file
44
crates/ruff/resources/test/fixtures/flake8_bandit/S607.py
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import os
|
||||
|
||||
# Check all functions.
|
||||
subprocess.Popen("true")
|
||||
subprocess.call("true")
|
||||
subprocess.check_call("true")
|
||||
subprocess.check_output("true")
|
||||
subprocess.run("true")
|
||||
os.system("true")
|
||||
os.popen("true")
|
||||
os.popen2("true")
|
||||
os.popen3("true")
|
||||
os.popen4("true")
|
||||
popen2.popen2("true")
|
||||
popen2.popen3("true")
|
||||
popen2.popen4("true")
|
||||
popen2.Popen3("true")
|
||||
popen2.Popen4("true")
|
||||
commands.getoutput("true")
|
||||
commands.getstatusoutput("true")
|
||||
os.execl("true")
|
||||
os.execle("true")
|
||||
os.execlp("true")
|
||||
os.execlpe("true")
|
||||
os.execv("true")
|
||||
os.execve("true")
|
||||
os.execvp("true")
|
||||
os.execvpe("true")
|
||||
os.spawnl("true")
|
||||
os.spawnle("true")
|
||||
os.spawnlp("true")
|
||||
os.spawnlpe("true")
|
||||
os.spawnv("true")
|
||||
os.spawnve("true")
|
||||
os.spawnvp("true")
|
||||
os.spawnvpe("true")
|
||||
os.startfile("true")
|
||||
|
||||
# Check it does not fail for full paths.
|
||||
os.system("/bin/ls")
|
||||
os.system("./bin/ls")
|
||||
os.system(["/bin/ls"])
|
||||
os.system(["/bin/ls", "/tmp"])
|
||||
os.system(r"C:\\bin\ls")
|
||||
@@ -1,7 +1,9 @@
|
||||
import collections
|
||||
import datetime as dt
|
||||
from decimal import Decimal
|
||||
import logging
|
||||
import operator
|
||||
from pathlib import Path
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
@@ -165,6 +167,26 @@ def float_str_not_inf_or_nan_is_wrong(value=float("3.14")):
|
||||
pass
|
||||
|
||||
|
||||
# Allow decimals
|
||||
def decimal_okay(value=Decimal("0.1")):
|
||||
pass
|
||||
|
||||
# Allow dates
|
||||
def date_okay(value=dt.date(2023, 3, 27)):
|
||||
pass
|
||||
|
||||
# Allow datetimes
|
||||
def datetime_okay(value=dt.datetime(2023, 3, 27, 13, 51, 59)):
|
||||
pass
|
||||
|
||||
# Allow timedeltas
|
||||
def timedelta_okay(value=dt.timedelta(hours=1)):
|
||||
pass
|
||||
|
||||
# Allow paths
|
||||
def path_okay(value=Path(".")):
|
||||
pass
|
||||
|
||||
# B006 and B008
|
||||
# We should handle arbitrary nesting of these B008.
|
||||
def nested_combo(a=[float(3), dt.datetime.now()]):
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
"""
|
||||
Should emit:
|
||||
B017 - on lines 20
|
||||
B017 - on lines 23 and 41
|
||||
"""
|
||||
import asyncio
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
CONSTANT = True
|
||||
|
||||
@@ -34,3 +35,14 @@ class Foobar(unittest.TestCase):
|
||||
def raises_with_absolute_reference(self):
|
||||
with self.assertRaises(asyncio.CancelledError):
|
||||
Foo()
|
||||
|
||||
|
||||
def test_pytest_raises():
|
||||
with pytest.raises(Exception):
|
||||
raise ValueError("Hello")
|
||||
|
||||
with pytest.raises(Exception, "hello"):
|
||||
raise ValueError("This is fine")
|
||||
|
||||
with pytest.raises(Exception, match="hello"):
|
||||
raise ValueError("This is also fine")
|
||||
|
||||
@@ -57,3 +57,9 @@ def foo3():
|
||||
|
||||
def foo4():
|
||||
...
|
||||
|
||||
|
||||
def foo5():
|
||||
foo.bar # Attribute (raise)
|
||||
object().__class__ # Attribute (raise)
|
||||
"foo" + "bar" # BinOp (raise)
|
||||
|
||||
@@ -28,6 +28,11 @@ except (ValueError, *(RuntimeError, (KeyError, TypeError))): # error
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (*a, *(RuntimeError, (KeyError, TypeError))): # error
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (ValueError, *(RuntimeError, TypeError)): # ok
|
||||
@@ -38,10 +43,36 @@ try:
|
||||
except (ValueError, *[RuntimeError, *(TypeError,)]): # ok
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (*a, *b): # ok
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (*a, *(RuntimeError, TypeError)): # ok
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (*a, *(b, c)): # ok
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (*a, *(*b, *c)): # ok
|
||||
pass
|
||||
|
||||
|
||||
def what_to_catch():
|
||||
return ...
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except what_to_catch(): # ok
|
||||
pass
|
||||
pass
|
||||
|
||||
187
crates/ruff/resources/test/fixtures/flake8_bugbear/B031.py
vendored
Normal file
187
crates/ruff/resources/test/fixtures/flake8_bugbear/B031.py
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
import itertools
|
||||
from itertools import groupby
|
||||
|
||||
shoppers = ["Jane", "Joe", "Sarah"]
|
||||
items = [
|
||||
("lettuce", "greens"),
|
||||
("tomatoes", "greens"),
|
||||
("cucumber", "greens"),
|
||||
("chicken breast", "meats & fish"),
|
||||
("salmon", "meats & fish"),
|
||||
("ice cream", "frozen items"),
|
||||
]
|
||||
|
||||
carts = {shopper: [] for shopper in shoppers}
|
||||
|
||||
|
||||
def collect_shop_items(shopper, items):
|
||||
# Imagine this an expensive database query or calculation that is
|
||||
# advantageous to batch.
|
||||
carts[shopper] += items
|
||||
|
||||
|
||||
# Invoking the `groupby` function directly
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
for shopper in shoppers:
|
||||
shopper = shopper.title()
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
# We're outside the nested loop and used the group again.
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
collect_shop_items("Jane", section_items)
|
||||
collect_shop_items("Joe", section_items) # B031
|
||||
|
||||
|
||||
# Make sure to detect in other loop constructs as well - `while` loop
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
countdown = 3
|
||||
while countdown > 0:
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
countdown -= 1
|
||||
|
||||
# Make sure to detect in other loop constructs as well - `list` comprehension
|
||||
collection = []
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
collection.append([list(section_items) for _ in range(3)]) # B031
|
||||
|
||||
unique_items = set()
|
||||
another_set = set()
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
# For nested loops, it should not flag the usage of the name
|
||||
for item in section_items:
|
||||
unique_items.add(item)
|
||||
|
||||
# But it should be detected when used again
|
||||
for item in section_items: # B031
|
||||
another_set.add(item)
|
||||
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
# Variable has been overridden, skip checking
|
||||
section_items = list(unique_items)
|
||||
collect_shop_items("Jane", section_items)
|
||||
collect_shop_items("Jane", section_items)
|
||||
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
# Variable has been overridden, skip checking
|
||||
# Not a realistic situation, just for testing purpose
|
||||
(section_items := list(unique_items))
|
||||
collect_shop_items("Jane", section_items)
|
||||
collect_shop_items("Jane", section_items)
|
||||
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
# This is ok
|
||||
collect_shop_items("Jane", section_items)
|
||||
|
||||
# Invocation via the `itertools` module
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
for shopper in shoppers:
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
_ = [collect_shop_items(shopper, section_items) for shopper in shoppers] # B031
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
# The variable is overridden, skip checking.
|
||||
_ = [_ for section_items in range(3)]
|
||||
_ = [collect_shop_items(shopper, section_items) for shopper in shoppers]
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
_ = [item for item in section_items]
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
# The iterator is being used for the second time.
|
||||
_ = [(item1, item2) for item1 in section_items for item2 in section_items] # B031
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
if _section == "greens":
|
||||
collect_shop_items(shopper, section_items)
|
||||
else:
|
||||
collect_shop_items(shopper, section_items)
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
# Mutually exclusive branches shouldn't trigger the warning
|
||||
if _section == "greens":
|
||||
collect_shop_items(shopper, section_items)
|
||||
if _section == "greens":
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
elif _section == "frozen items":
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
else:
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
elif _section == "frozen items":
|
||||
# Mix `match` and `if` statements
|
||||
match shopper:
|
||||
case "Jane":
|
||||
collect_shop_items(shopper, section_items)
|
||||
if _section == "fourth":
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
case _:
|
||||
collect_shop_items(shopper, section_items)
|
||||
else:
|
||||
collect_shop_items(shopper, section_items)
|
||||
# Now, it should detect
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
# Mutually exclusive branches shouldn't trigger the warning
|
||||
match _section:
|
||||
case "greens":
|
||||
collect_shop_items(shopper, section_items)
|
||||
match shopper:
|
||||
case "Jane":
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
case _:
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
case "frozen items":
|
||||
collect_shop_items(shopper, section_items)
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
case _:
|
||||
collect_shop_items(shopper, section_items)
|
||||
# Now, it should detect
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
|
||||
for group in groupby(items, key=lambda p: p[1]):
|
||||
# This is bad, but not detected currently
|
||||
collect_shop_items("Jane", group[1])
|
||||
collect_shop_items("Joe", group[1])
|
||||
|
||||
|
||||
# https://github.com/charliermarsh/ruff/issues/4050
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
if _section == "greens":
|
||||
for item in section_items:
|
||||
collect_shop_items(shopper, item)
|
||||
elif _section == "frozen items":
|
||||
_ = [item for item in section_items]
|
||||
else:
|
||||
collect_shop_items(shopper, section_items)
|
||||
|
||||
# Make sure we ignore - but don't fail on more complicated invocations
|
||||
for _key, (_value1, _value2) in groupby(
|
||||
[("a", (1, 2)), ("b", (3, 4)), ("a", (5, 6))], key=lambda p: p[1]
|
||||
):
|
||||
collect_shop_items("Jane", group[1])
|
||||
collect_shop_items("Joe", group[1])
|
||||
|
||||
# Make sure we ignore - but don't fail on more complicated invocations
|
||||
for (_key1, _key2), (_value1, _value2) in groupby(
|
||||
[(("a", "a"), (1, 2)), (("b", "b"), (3, 4)), (("a", "a"), (5, 6))],
|
||||
key=lambda p: p[1],
|
||||
):
|
||||
collect_shop_items("Jane", group[1])
|
||||
collect_shop_items("Joe", group[1])
|
||||
|
||||
|
||||
# Let's redefine the `groupby` function to make sure we pick up the correct one.
|
||||
# NOTE: This should always be at the end of the file.
|
||||
def groupby(data, key=None):
|
||||
pass
|
||||
|
||||
|
||||
for name, group in groupby(items):
|
||||
collect_shop_items("Jane", items)
|
||||
# This shouldn't be flagged as the `groupby` function is different
|
||||
collect_shop_items("Joe", items)
|
||||
@@ -7,11 +7,14 @@ set(set(x))
|
||||
set(list(x))
|
||||
set(tuple(x))
|
||||
set(sorted(x))
|
||||
set(sorted(x, key=lambda y: y))
|
||||
set(reversed(x))
|
||||
sorted(list(x))
|
||||
sorted(tuple(x))
|
||||
sorted(sorted(x))
|
||||
sorted(sorted(x, key=lambda y: y))
|
||||
sorted(reversed(x))
|
||||
sorted(list(x), key=lambda y: y)
|
||||
tuple(
|
||||
list(
|
||||
[x, 3, "hell"\
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
x = [1, 2, 3]
|
||||
y = [("a", 1), ("b", 2), ("c", 3)]
|
||||
z = [(1,), (2,), (3,)]
|
||||
d = {"a": 1, "b": 2, "c": 3}
|
||||
|
||||
[i for i in x]
|
||||
{i for i in x}
|
||||
{k: v for k, v in y}
|
||||
{k: v for k, v in d.items()}
|
||||
|
||||
[i for i, in z]
|
||||
[i for i, j in y]
|
||||
[i for i in x if i > 1]
|
||||
[i for i in x for j in x]
|
||||
|
||||
{v: k for k, v in y}
|
||||
{k.foo: k for k in y}
|
||||
{k["foo"]: k for k in y}
|
||||
{k: v if v else None for k, v in y}
|
||||
|
||||
10
crates/ruff/resources/test/fixtures/flake8_comprehensions/C418.py
vendored
Normal file
10
crates/ruff/resources/test/fixtures/flake8_comprehensions/C418.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
dict({})
|
||||
dict({'a': 1})
|
||||
dict({'x': 1 for x in range(10)})
|
||||
dict(
|
||||
{'x': 1 for x in range(10)}
|
||||
)
|
||||
|
||||
dict({}, a=1)
|
||||
dict({x: 1 for x in range(1)}, a=1)
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
# no error
|
||||
all((x.id for x in bar))
|
||||
all(x.id for x in bar)
|
||||
all(x.id for x in bar)
|
||||
any(x.id for x in bar)
|
||||
any({x.id for x in bar})
|
||||
|
||||
# PIE 802
|
||||
any([x.id for x in bar])
|
||||
all([x.id for x in bar])
|
||||
any( # first comment
|
||||
@@ -14,3 +6,14 @@ any( # first comment
|
||||
all( # first comment
|
||||
[x.id for x in bar], # second comment
|
||||
) # third comment
|
||||
any({x.id for x in bar})
|
||||
|
||||
# OK
|
||||
all(x.id for x in bar)
|
||||
all(x.id for x in bar)
|
||||
any(x.id for x in bar)
|
||||
all((x.id for x in bar))
|
||||
|
||||
|
||||
async def f() -> bool:
|
||||
return all([await use_greeting(greeting) for greeting in await greetings()])
|
||||
113
crates/ruff/resources/test/fixtures/flake8_django/DJ012.py
vendored
Normal file
113
crates/ruff/resources/test/fixtures/flake8_django/DJ012.py
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
from django.db import models
|
||||
from django.db.models import Model
|
||||
|
||||
|
||||
class StrBeforeRandomField(models.Model):
|
||||
"""Model with `__str__` before a random property."""
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
verbose_name_plural = "tests"
|
||||
|
||||
def __str__(self):
|
||||
return ""
|
||||
|
||||
random_property = "foo"
|
||||
|
||||
|
||||
class StrBeforeFieldModel(models.Model):
|
||||
"""Model with `__str__` before fields."""
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
verbose_name_plural = "tests"
|
||||
|
||||
def __str__(self):
|
||||
return "foobar"
|
||||
|
||||
first_name = models.CharField(max_length=32)
|
||||
|
||||
|
||||
class ManagerBeforeField(models.Model):
|
||||
"""Model with manager before fields."""
|
||||
|
||||
objects = "manager"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
verbose_name_plural = "tests"
|
||||
|
||||
def __str__(self):
|
||||
return "foobar"
|
||||
|
||||
first_name = models.CharField(max_length=32)
|
||||
|
||||
|
||||
class CustomMethodBeforeStr(models.Model):
|
||||
"""Model with a custom method before `__str__`."""
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
verbose_name_plural = "tests"
|
||||
|
||||
def my_method(self):
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return "foobar"
|
||||
|
||||
|
||||
class GetAbsoluteUrlBeforeSave(Model):
|
||||
"""Model with `get_absolute_url` method before `save` method.
|
||||
|
||||
Subclass this directly using the `Model` class.
|
||||
"""
|
||||
|
||||
def get_absolute_url(self):
|
||||
pass
|
||||
|
||||
def save(self):
|
||||
pass
|
||||
|
||||
|
||||
class ConstantsAreNotFields(models.Model):
|
||||
"""Model with an assignment to a constant after `__str__`."""
|
||||
|
||||
first_name = models.CharField(max_length=32)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
verbose_name_plural = "tests"
|
||||
|
||||
def __str__(self):
|
||||
pass
|
||||
|
||||
MY_CONSTANT = id(1)
|
||||
|
||||
|
||||
class PerfectlyFine(models.Model):
|
||||
"""Model which has everything in perfect order."""
|
||||
|
||||
first_name = models.CharField(max_length=32)
|
||||
last_name = models.CharField(max_length=32)
|
||||
objects = "manager"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
verbose_name_plural = "tests"
|
||||
|
||||
def __str__(self):
|
||||
return "Perfectly fine!"
|
||||
|
||||
def save(self, **kwargs):
|
||||
super(PerfectlyFine, self).save(**kwargs)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "http://%s" % self
|
||||
|
||||
def my_method(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def random_property(self):
|
||||
return "%s" % self
|
||||
1
crates/ruff/resources/test/fixtures/flake8_gettext/INT001.py
vendored
Normal file
1
crates/ruff/resources/test/fixtures/flake8_gettext/INT001.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
_(f"{'value'}")
|
||||
1
crates/ruff/resources/test/fixtures/flake8_gettext/INT002.py
vendored
Normal file
1
crates/ruff/resources/test/fixtures/flake8_gettext/INT002.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
_("{}".format("line"))
|
||||
1
crates/ruff/resources/test/fixtures/flake8_gettext/INT003.py
vendored
Normal file
1
crates/ruff/resources/test/fixtures/flake8_gettext/INT003.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
_("%s" % "line")
|
||||
16
crates/ruff/resources/test/fixtures/flake8_import_conventions/custom_banned.py
vendored
Normal file
16
crates/ruff/resources/test/fixtures/flake8_import_conventions/custom_banned.py
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import typing as t # banned
|
||||
import typing as ty # banned
|
||||
|
||||
import numpy as nmp # banned
|
||||
import numpy as npy # banned
|
||||
import tensorflow.keras.backend as K # banned
|
||||
import torch.nn.functional as F # banned
|
||||
from tensorflow.keras import backend as K # banned
|
||||
from torch.nn import functional as F # banned
|
||||
|
||||
from typing import Any # ok
|
||||
|
||||
import numpy as np # ok
|
||||
import tensorflow as tf # ok
|
||||
import torch.nn as nn # ok
|
||||
from tensorflow.keras import backend # ok
|
||||
10
crates/ruff/resources/test/fixtures/flake8_import_conventions/custom_banned_from.py
vendored
Normal file
10
crates/ruff/resources/test/fixtures/flake8_import_conventions/custom_banned_from.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
from logging.config import BaseConfigurator # banned
|
||||
from typing import Any, Dict # banned
|
||||
from typing import * # banned
|
||||
|
||||
from pandas import DataFrame # banned
|
||||
from pandas import * # banned
|
||||
|
||||
import logging.config # ok
|
||||
import typing # ok
|
||||
import pandas # ok
|
||||
@@ -1,3 +1,9 @@
|
||||
import logging
|
||||
import logging as foo
|
||||
|
||||
logging.info("Hello {}".format("World!"))
|
||||
logging.log(logging.INFO, "Hello {}".format("World!"))
|
||||
foo.info("Hello {}".format("World!"))
|
||||
logging.log(logging.INFO, msg="Hello {}".format("World!"))
|
||||
logging.log(level=logging.INFO, msg="Hello {}".format("World!"))
|
||||
logging.log(msg="Hello {}".format("World!"), level=logging.INFO)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import logging
|
||||
|
||||
logging.info("Hello %s" % "World!")
|
||||
logging.log(logging.INFO, "Hello %s" % "World!")
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import logging
|
||||
|
||||
logging.info("Hello" + " " + "World!")
|
||||
logging.log(logging.INFO, "Hello" + " " + "World!")
|
||||
|
||||
@@ -2,3 +2,4 @@ import logging
|
||||
|
||||
name = "world"
|
||||
logging.info(f"Hello {name}")
|
||||
logging.log(logging.INFO, f"Hello {name}")
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import logging
|
||||
from distutils import log
|
||||
|
||||
logging.warn("Hello World!")
|
||||
log.warn("Hello world!") # This shouldn't be considered as a logger candidate
|
||||
|
||||
@@ -11,3 +11,7 @@ _T = TypeVar("_T") # OK
|
||||
_TTuple = TypeVarTuple("_TTuple") # OK
|
||||
|
||||
_P = ParamSpec("_P") # OK
|
||||
|
||||
|
||||
def f():
|
||||
T = TypeVar("T") # OK
|
||||
|
||||
@@ -11,3 +11,6 @@ _T = TypeVar("_T") # OK
|
||||
_TTuple = TypeVarTuple("_TTuple") # OK
|
||||
|
||||
_P = ParamSpec("_P") # OK
|
||||
|
||||
def f():
|
||||
T = TypeVar("T") # OK
|
||||
|
||||
@@ -1,79 +1,161 @@
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
from math import inf
|
||||
|
||||
import numpy as np
|
||||
|
||||
def f12(
|
||||
x,
|
||||
y: str = os.pathsep, # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f11(*, x: str = "x") -> None: # OK
|
||||
...
|
||||
|
||||
|
||||
) -> None: ...
|
||||
def f11(*, x: str = "x") -> None: ... # OK
|
||||
def f13(
|
||||
x: list[str] = [
|
||||
x: list[str] = [ # OK
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
] # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
]
|
||||
) -> None: ...
|
||||
def f14(
|
||||
x: tuple[str, ...] = (
|
||||
x: tuple[str, ...] = ( # OK
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
) # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
)
|
||||
) -> None: ...
|
||||
def f15(
|
||||
x: set[str] = {
|
||||
x: set[str] = { # OK
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
} # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f16(x: frozenset[bytes] = frozenset({b"foo", b"bar", b"baz"})) -> None: # OK
|
||||
...
|
||||
|
||||
|
||||
}
|
||||
) -> None: ...
|
||||
def f151(x: dict[int, int] = {1: 2}) -> None: ... # Ok
|
||||
def f152(
|
||||
x: dict[
|
||||
int, int
|
||||
] = { # OK
|
||||
1: 2,
|
||||
**{3: 4},
|
||||
}
|
||||
) -> None: ...
|
||||
def f153(
|
||||
x: list[
|
||||
int
|
||||
] = [ # OK
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
]
|
||||
) -> None: ...
|
||||
def f154(
|
||||
x: tuple[
|
||||
str, tuple[str, ...]
|
||||
] = ( # OK
|
||||
"foo",
|
||||
("bar", "baz"),
|
||||
)
|
||||
) -> None: ...
|
||||
def f141(
|
||||
x: list[
|
||||
int
|
||||
] = [ # OK
|
||||
*range(10)
|
||||
],
|
||||
) -> None: ...
|
||||
def f142(
|
||||
x: list[
|
||||
int
|
||||
] = list( # OK
|
||||
range(10)
|
||||
),
|
||||
) -> None: ...
|
||||
def f16(
|
||||
x: frozenset[
|
||||
bytes
|
||||
] = frozenset( # OK
|
||||
{b"foo", b"bar", b"baz"}
|
||||
)
|
||||
) -> None: ...
|
||||
def f17(
|
||||
x: str = "foo" + "bar", # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
x: str = "foo" # OK
|
||||
+ "bar",
|
||||
) -> None: ...
|
||||
def f18(
|
||||
x: str = b"foo" + b"bar", # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
x: str = b"foo" # OK
|
||||
+ b"bar",
|
||||
) -> None: ...
|
||||
def f19(
|
||||
x: object = "foo" + 4, # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
x: object = "foo" # OK
|
||||
+ 4,
|
||||
) -> None: ...
|
||||
def f20(
|
||||
x: int = 5 + 5, # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
x: int = 5
|
||||
+ 5, # OK
|
||||
) -> None: ...
|
||||
def f21(
|
||||
x: complex = 3j - 3j, # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
x: complex = 3j
|
||||
- 3j, # OK
|
||||
) -> None: ...
|
||||
def f22(
|
||||
x: complex = -42.5j + 4.3j, # OK
|
||||
) -> None:
|
||||
...
|
||||
x: complex = -42.5j # OK
|
||||
+ 4.3j,
|
||||
) -> None: ...
|
||||
def f23(
|
||||
x: bool = True, # OK
|
||||
) -> None: ...
|
||||
def f24(
|
||||
x: float = 3.14, # OK
|
||||
) -> None: ...
|
||||
def f25(
|
||||
x: float = -3.14, # OK
|
||||
) -> None: ...
|
||||
def f26(
|
||||
x: complex = -3.14j, # OK
|
||||
) -> None: ...
|
||||
def f27(
|
||||
x: complex = -3 - 3.14j, # OK
|
||||
) -> None: ...
|
||||
def f28(
|
||||
x: float = math.tau, # OK
|
||||
) -> None: ...
|
||||
def f29(
|
||||
x: float = math.inf, # OK
|
||||
) -> None: ...
|
||||
def f30(
|
||||
x: float = -math.inf, # OK
|
||||
) -> None: ...
|
||||
def f31(
|
||||
x: float = inf, # OK
|
||||
) -> None: ...
|
||||
def f32(
|
||||
x: float = np.inf, # OK
|
||||
) -> None: ...
|
||||
def f33(
|
||||
x: float = math.nan, # OK
|
||||
) -> None: ...
|
||||
def f34(
|
||||
x: float = -math.nan, # OK
|
||||
) -> None: ...
|
||||
def f35(
|
||||
x: complex = math.inf # OK
|
||||
+ 1j,
|
||||
) -> None: ...
|
||||
def f36(
|
||||
*,
|
||||
x: str = sys.version, # OK
|
||||
) -> None: ...
|
||||
def f37(
|
||||
*,
|
||||
x: str = "" # OK
|
||||
+ "",
|
||||
) -> None: ...
|
||||
|
||||
@@ -11,32 +11,74 @@ def f12(
|
||||
) -> None: ...
|
||||
def f11(*, x: str = "x") -> None: ... # OK
|
||||
def f13(
|
||||
x: list[
|
||||
str
|
||||
] = [ # Error PYI011 Only simple default values allowed for typed arguments
|
||||
x: list[str] = [ # OK
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
]
|
||||
) -> None: ...
|
||||
def f14(
|
||||
x: tuple[
|
||||
str, ...
|
||||
] = ( # Error PYI011 Only simple default values allowed for typed arguments
|
||||
x: tuple[str, ...] = ( # OK
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
)
|
||||
) -> None: ...
|
||||
def f15(
|
||||
x: set[
|
||||
str
|
||||
] = { # Error PYI011 Only simple default values allowed for typed arguments
|
||||
x: set[str] = { # OK
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
}
|
||||
) -> None: ...
|
||||
def f151(x: dict[int, int] = {1: 2}) -> None: ... # Ok
|
||||
def f152(
|
||||
x: dict[
|
||||
int, int
|
||||
] = { # Error PYI011 Only simple default values allowed for typed arguments
|
||||
1: 2,
|
||||
**{3: 4},
|
||||
}
|
||||
) -> None: ...
|
||||
def f153(
|
||||
x: list[
|
||||
int
|
||||
] = [ # Error PYI011 Only simple default values allowed for typed arguments
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
]
|
||||
) -> None: ...
|
||||
def f154(
|
||||
x: tuple[
|
||||
str, tuple[str, ...]
|
||||
] = ( # Error PYI011 Only simple default values allowed for typed arguments
|
||||
"foo",
|
||||
("bar", "baz"),
|
||||
)
|
||||
) -> None: ...
|
||||
def f141(
|
||||
x: list[
|
||||
int
|
||||
] = [ # Error PYI011 Only simple default values allowed for typed arguments
|
||||
*range(10)
|
||||
],
|
||||
) -> None: ...
|
||||
def f142(
|
||||
x: list[
|
||||
int
|
||||
] = list( # Error PYI011 Only simple default values allowed for typed arguments
|
||||
range(10)
|
||||
),
|
||||
) -> None: ...
|
||||
def f16(
|
||||
x: frozenset[
|
||||
bytes
|
||||
@@ -109,8 +151,11 @@ def f35(
|
||||
+ 1j,
|
||||
) -> None: ...
|
||||
def f36(
|
||||
*, x: str = sys.version, # OK
|
||||
*,
|
||||
x: str = sys.version, # OK
|
||||
) -> None: ...
|
||||
def f37(
|
||||
*, x: str = "" + "", # Error PYI011 Only simple default values allowed for typed arguments
|
||||
*,
|
||||
x: str = "" # Error PYI011 Only simple default values allowed for typed arguments
|
||||
+ "",
|
||||
) -> None: ...
|
||||
|
||||
75
crates/ruff/resources/test/fixtures/flake8_pyi/PYI012.py
vendored
Normal file
75
crates/ruff/resources/test/fixtures/flake8_pyi/PYI012.py
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
# Violations of PYI012
|
||||
|
||||
|
||||
class OneAttributeClass:
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
|
||||
class OneAttributeClassRev:
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
value: int
|
||||
|
||||
|
||||
class DocstringClass:
|
||||
"""
|
||||
My body only contains pass.
|
||||
"""
|
||||
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
|
||||
class NonEmptyChild(Exception):
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
|
||||
class NonEmptyChild2(Exception):
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
value: int
|
||||
|
||||
|
||||
class NonEmptyWithInit:
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
|
||||
# Not violations (of PYI012)
|
||||
|
||||
|
||||
class EmptyClass:
|
||||
pass # Y009 Empty body should contain `...`, not `pass`
|
||||
|
||||
|
||||
class EmptyOneLine:
|
||||
pass # Y009 Empty body should contain `...`, not `pass`
|
||||
|
||||
|
||||
class Dog:
|
||||
eyes: int = 2
|
||||
|
||||
|
||||
class EmptyEllipsis:
|
||||
...
|
||||
|
||||
|
||||
class NonEmptyEllipsis:
|
||||
value: int
|
||||
... # Y013 Non-empty class body must not contain `...`
|
||||
|
||||
|
||||
class WithInit:
|
||||
value: int = 0
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
|
||||
def function():
|
||||
pass
|
||||
|
||||
|
||||
pass
|
||||
59
crates/ruff/resources/test/fixtures/flake8_pyi/PYI012.pyi
vendored
Normal file
59
crates/ruff/resources/test/fixtures/flake8_pyi/PYI012.pyi
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
# Violations of PYI012
|
||||
|
||||
class OneAttributeClass:
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
class OneAttributeClassRev:
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
value: int
|
||||
|
||||
class DocstringClass:
|
||||
"""
|
||||
My body only contains pass.
|
||||
"""
|
||||
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
class NonEmptyChild(Exception):
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
class NonEmptyChild2(Exception):
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
value: int
|
||||
|
||||
class NonEmptyWithInit:
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
# Not violations (of PYI012)
|
||||
|
||||
class EmptyClass:
|
||||
pass # Y009 Empty body should contain `...`, not `pass`
|
||||
|
||||
class EmptyOneLine:
|
||||
pass # Y009 Empty body should contain `...`, not `pass`
|
||||
|
||||
class Dog:
|
||||
eyes: int = 2
|
||||
|
||||
class EmptyEllipsis: ...
|
||||
|
||||
class NonEmptyEllipsis:
|
||||
value: int
|
||||
... # Y013 Non-empty class body must not contain `...`
|
||||
|
||||
class WithInit:
|
||||
value: int = 0
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
def function():
|
||||
pass
|
||||
|
||||
pass
|
||||
@@ -39,6 +39,58 @@ def f15(
|
||||
...
|
||||
|
||||
|
||||
def f151(x={1: 2}) -> None: # Ok
|
||||
...
|
||||
|
||||
|
||||
def f152(
|
||||
x={ # OK
|
||||
1: 2,
|
||||
**{3: 4},
|
||||
}
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f153(
|
||||
x=[ # OK
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
]
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f154(
|
||||
x=( # OK
|
||||
"foo",
|
||||
("bar", "baz"),
|
||||
)
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f141(
|
||||
x=[*range(10)], # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f142(
|
||||
x=list(range(10)), # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f16(x=frozenset({b"foo", b"bar", b"baz"})) -> None:
|
||||
... # OK
|
||||
|
||||
|
||||
@@ -4,26 +4,60 @@ def f12(
|
||||
) -> None: ...
|
||||
def f11(*, x="x") -> None: ... # OK
|
||||
def f13(
|
||||
x=[ # Error PYI014
|
||||
x=[ # OK
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
]
|
||||
) -> None: ...
|
||||
def f14(
|
||||
x=( # Error PYI014
|
||||
x=( # OK
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
)
|
||||
) -> None: ...
|
||||
def f15(
|
||||
x={ # Error PYI014
|
||||
x={ # OK
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
}
|
||||
) -> None: ...
|
||||
def f151(x={1: 2}) -> None: ...
|
||||
def f152(
|
||||
x={ # Error PYI014
|
||||
1: 2,
|
||||
**{3: 4},
|
||||
}
|
||||
) -> None: ...
|
||||
def f153(
|
||||
x=[ # Error PYI014
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
]
|
||||
) -> None: ...
|
||||
def f154(
|
||||
x=( # Error PYI014
|
||||
"foo",
|
||||
("bar", "baz"),
|
||||
)
|
||||
) -> None: ...
|
||||
def f141(
|
||||
x=[*range(10)], # Error PYI014
|
||||
) -> None: ...
|
||||
def f142(
|
||||
x=list(range(10)), # Error PYI014
|
||||
) -> None: ...
|
||||
def f16(x=frozenset({b"foo", b"bar", b"baz"})) -> None: ... # Error PYI014
|
||||
def f17(
|
||||
x="foo" + "bar", # Error PYI014
|
||||
@@ -44,5 +78,5 @@ def f22(
|
||||
x=-42.5j + 4.3j, # Error PYI014
|
||||
) -> None: ...
|
||||
def f23(
|
||||
x=True, # OK
|
||||
x=True, # OK
|
||||
) -> None: ...
|
||||
|
||||
93
crates/ruff/resources/test/fixtures/flake8_pyi/PYI015.py
vendored
Normal file
93
crates/ruff/resources/test/fixtures/flake8_pyi/PYI015.py
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
import builtins
|
||||
import typing
|
||||
from typing import TypeAlias, Final
|
||||
|
||||
field1: int
|
||||
field2: int = ...
|
||||
field3 = ... # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
field4: int = 0
|
||||
field41: int = 0xFFFFFFFF
|
||||
field42: int = 1234567890
|
||||
field43: int = -0xFFFFFFFF
|
||||
field44: int = -1234567890
|
||||
field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
|
||||
field6 = 0 # Y052 Need type annotation for "field6"
|
||||
field7 = b"" # Y052 Need type annotation for "field7"
|
||||
field71 = "foo" # Y052 Need type annotation for "field71"
|
||||
field72: str = "foo"
|
||||
field8 = False # Y052 Need type annotation for "field8"
|
||||
field81 = -1 # Y052 Need type annotation for "field81"
|
||||
field82: float = -98.43
|
||||
field83 = -42j # Y052 Need type annotation for "field83"
|
||||
field84 = 5 + 42j # Y052 Need type annotation for "field84"
|
||||
field85 = -5 - 42j # Y052 Need type annotation for "field85"
|
||||
field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None"
|
||||
Field95: TypeAlias = None
|
||||
Field96: TypeAlias = int | None
|
||||
Field97: TypeAlias = None | typing.SupportsInt | builtins.str | float | bool
|
||||
field19 = [1, 2, 3] # Y052 Need type annotation for "field19"
|
||||
field191: list[int] = [1, 2, 3]
|
||||
field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
|
||||
field201: tuple[int, ...] = (1, 2, 3)
|
||||
field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
|
||||
field211: set[int] = {1, 2, 3}
|
||||
field212 = {"foo": "bar"} # Y052 Need type annotation for "field212"
|
||||
field213: dict[str, str] = {"foo": "bar"}
|
||||
field22: Final = {"foo": 5}
|
||||
field221: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Y015 Only simple default values are allowed for assignments
|
||||
field223: list[int] = [*range(10)] # Y015 Only simple default values are allowed for assignments
|
||||
field224: list[int] = list(range(10)) # Y015 Only simple default values are allowed for assignments
|
||||
field225: list[object] = [{}, 1, 2] # Y015 Only simple default values are allowed for assignments
|
||||
field226: tuple[str | tuple[str, ...], ...] = ("foo", ("foo", "bar")) # Y015 Only simple default values are allowed for assignments
|
||||
field227: dict[str, object] = {"foo": {"foo": "bar"}} # Y015 Only simple default values are allowed for assignments
|
||||
field228: dict[str, list[object]] = {"foo": []} # Y015 Only simple default values are allowed for assignments
|
||||
# When parsed, this case results in `None` being placed in the `.keys` list for the `ast.Dict` node
|
||||
field229: dict[int, int] = {1: 2, **{3: 4}} # Y015 Only simple default values are allowed for assignments
|
||||
field23 = "foo" + "bar" # Y015 Only simple default values are allowed for assignments
|
||||
field24 = b"foo" + b"bar" # Y015 Only simple default values are allowed for assignments
|
||||
field25 = 5 * 5 # Y015 Only simple default values are allowed for assignments
|
||||
|
||||
# We shouldn't emit Y015 within functions
|
||||
def f():
|
||||
field26: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
|
||||
# We shouldn't emit Y015 for __slots__ or __match_args__
|
||||
class Class1:
|
||||
__slots__ = (
|
||||
'_one',
|
||||
'_two',
|
||||
'_three',
|
||||
'_four',
|
||||
'_five',
|
||||
'_six',
|
||||
'_seven',
|
||||
'_eight',
|
||||
'_nine',
|
||||
'_ten',
|
||||
'_eleven',
|
||||
)
|
||||
|
||||
__match_args__ = (
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
'four',
|
||||
'five',
|
||||
'six',
|
||||
'seven',
|
||||
'eight',
|
||||
'nine',
|
||||
'ten',
|
||||
'eleven',
|
||||
)
|
||||
|
||||
# We shouldn't emit Y015 for __all__
|
||||
__all__ = ["Class1"]
|
||||
|
||||
# Ignore the following for PYI015
|
||||
field26 = typing.Sequence[int]
|
||||
field27 = list[str]
|
||||
field28 = builtins.str
|
||||
field29 = str
|
||||
field30 = str | bytes | None
|
||||
100
crates/ruff/resources/test/fixtures/flake8_pyi/PYI015.pyi
vendored
Normal file
100
crates/ruff/resources/test/fixtures/flake8_pyi/PYI015.pyi
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
import builtins
|
||||
import typing
|
||||
from typing import TypeAlias, Final, NewType, TypeVar, TypeVarTuple, ParamSpec
|
||||
|
||||
# We shouldn't emit Y015 for simple default values
|
||||
field1: int
|
||||
field2: int = ...
|
||||
field3 = ... # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
field4: int = 0
|
||||
field41: int = 0xFFFFFFFF
|
||||
field42: int = 1234567890
|
||||
field43: int = -0xFFFFFFFF
|
||||
field44: int = -1234567890
|
||||
field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
|
||||
field6 = 0 # Y052 Need type annotation for "field6"
|
||||
field7 = b"" # Y052 Need type annotation for "field7"
|
||||
field71 = "foo" # Y052 Need type annotation for "field71"
|
||||
field72: str = "foo"
|
||||
field8 = False # Y052 Need type annotation for "field8"
|
||||
field81 = -1 # Y052 Need type annotation for "field81"
|
||||
field82: float = -98.43
|
||||
field83 = -42j # Y052 Need type annotation for "field83"
|
||||
field84 = 5 + 42j # Y052 Need type annotation for "field84"
|
||||
field85 = -5 - 42j # Y052 Need type annotation for "field85"
|
||||
field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None"
|
||||
Field95: TypeAlias = None
|
||||
Field96: TypeAlias = int | None
|
||||
Field97: TypeAlias = None | typing.SupportsInt | builtins.str | float | bool
|
||||
Field98 = NewType('MyInt', int)
|
||||
Field99 = TypeVar('Field99')
|
||||
Field100 = TypeVarTuple('Field100')
|
||||
Field101 = ParamSpec('Field101')
|
||||
field19 = [1, 2, 3] # Y052 Need type annotation for "field19"
|
||||
field191: list[int] = [1, 2, 3]
|
||||
field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
|
||||
field201: tuple[int, ...] = (1, 2, 3)
|
||||
field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
|
||||
field211: set[int] = {1, 2, 3}
|
||||
field212 = {"foo": "bar"} # Y052 Need type annotation for "field212"
|
||||
field213: dict[str, str] = {"foo": "bar"}
|
||||
field22: Final = {"foo": 5}
|
||||
|
||||
# We *should* emit Y015 for more complex default values
|
||||
field221: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Y015 Only simple default values are allowed for assignments
|
||||
field223: list[int] = [*range(10)] # Y015 Only simple default values are allowed for assignments
|
||||
field224: list[int] = list(range(10)) # Y015 Only simple default values are allowed for assignments
|
||||
field225: list[object] = [{}, 1, 2] # Y015 Only simple default values are allowed for assignments
|
||||
field226: tuple[str | tuple[str, ...], ...] = ("foo", ("foo", "bar")) # Y015 Only simple default values are allowed for assignments
|
||||
field227: dict[str, object] = {"foo": {"foo": "bar"}} # Y015 Only simple default values are allowed for assignments
|
||||
field228: dict[str, list[object]] = {"foo": []} # Y015 Only simple default values are allowed for assignments
|
||||
# When parsed, this case results in `None` being placed in the `.keys` list for the `ast.Dict` node
|
||||
field229: dict[int, int] = {1: 2, **{3: 4}} # Y015 Only simple default values are allowed for assignments
|
||||
field23 = "foo" + "bar" # Y015 Only simple default values are allowed for assignments
|
||||
field24 = b"foo" + b"bar" # Y015 Only simple default values are allowed for assignments
|
||||
field25 = 5 * 5 # Y015 Only simple default values are allowed for assignments
|
||||
|
||||
# We shouldn't emit Y015 within functions
|
||||
def f():
|
||||
field26: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
|
||||
# We shouldn't emit Y015 for __slots__ or __match_args__
|
||||
class Class1:
|
||||
__slots__ = (
|
||||
'_one',
|
||||
'_two',
|
||||
'_three',
|
||||
'_four',
|
||||
'_five',
|
||||
'_six',
|
||||
'_seven',
|
||||
'_eight',
|
||||
'_nine',
|
||||
'_ten',
|
||||
'_eleven',
|
||||
)
|
||||
|
||||
__match_args__ = (
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
'four',
|
||||
'five',
|
||||
'six',
|
||||
'seven',
|
||||
'eight',
|
||||
'nine',
|
||||
'ten',
|
||||
'eleven',
|
||||
)
|
||||
|
||||
# We shouldn't emit Y015 for __all__
|
||||
__all__ = ["Class1"]
|
||||
|
||||
# Ignore the following for PYI015
|
||||
field26 = typing.Sequence[int]
|
||||
field27 = list[str]
|
||||
field28 = builtins.str
|
||||
field29 = str
|
||||
field30 = str | bytes | None
|
||||
35
crates/ruff/resources/test/fixtures/flake8_pyi/PYI016.py
vendored
Normal file
35
crates/ruff/resources/test/fixtures/flake8_pyi/PYI016.py
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# Shouldn't affect non-union field types.
|
||||
field1: str
|
||||
|
||||
# Should emit for duplicate field types.
|
||||
field2: str | str # PYI016: Duplicate union member `str`
|
||||
|
||||
|
||||
# Should emit for union types in arguments.
|
||||
def func1(arg1: int | int): # PYI016: Duplicate union member `int`
|
||||
print(arg1)
|
||||
|
||||
|
||||
# Should emit for unions in return types.
|
||||
def func2() -> str | str: # PYI016: Duplicate union member `str`
|
||||
return "my string"
|
||||
|
||||
|
||||
# Should emit in longer unions, even if not directly adjacent.
|
||||
field3: str | str | int # PYI016: Duplicate union member `str`
|
||||
field4: int | int | str # PYI016: Duplicate union member `int`
|
||||
field5: str | int | str # PYI016: Duplicate union member `str`
|
||||
field6: int | bool | str | int # PYI016: Duplicate union member `int`
|
||||
|
||||
# Shouldn't emit for non-type unions.
|
||||
field7 = str | str
|
||||
|
||||
# Should emit for strangely-bracketed unions.
|
||||
field8: int | (str | int) # PYI016: Duplicate union member `int`
|
||||
|
||||
# Should handle user brackets when fixing.
|
||||
field9: int | (int | str) # PYI016: Duplicate union member `int`
|
||||
field10: (str | int) | str # PYI016: Duplicate union member `str`
|
||||
|
||||
# Should emit for nested unions.
|
||||
field11: dict[int | int, str]
|
||||
32
crates/ruff/resources/test/fixtures/flake8_pyi/PYI016.pyi
vendored
Normal file
32
crates/ruff/resources/test/fixtures/flake8_pyi/PYI016.pyi
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Shouldn't affect non-union field types.
|
||||
field1: str
|
||||
|
||||
# Should emit for duplicate field types.
|
||||
field2: str | str # PYI016: Duplicate union member `str`
|
||||
|
||||
# Should emit for union types in arguments.
|
||||
def func1(arg1: int | int): # PYI016: Duplicate union member `int`
|
||||
print(arg1)
|
||||
|
||||
# Should emit for unions in return types.
|
||||
def func2() -> str | str: # PYI016: Duplicate union member `str`
|
||||
return "my string"
|
||||
|
||||
# Should emit in longer unions, even if not directly adjacent.
|
||||
field3: str | str | int # PYI016: Duplicate union member `str`
|
||||
field4: int | int | str # PYI016: Duplicate union member `int`
|
||||
field5: str | int | str # PYI016: Duplicate union member `str`
|
||||
field6: int | bool | str | int # PYI016: Duplicate union member `int`
|
||||
|
||||
# Shouldn't emit for non-type unions.
|
||||
field7 = str | str
|
||||
|
||||
# Should emit for strangely-bracketed unions.
|
||||
field8: int | (str | int) # PYI016: Duplicate union member `int`
|
||||
|
||||
# Should handle user brackets when fixing.
|
||||
field9: int | (int | str) # PYI016: Duplicate union member `int`
|
||||
field10: (str | int) | str # PYI016: Duplicate union member `str`
|
||||
|
||||
# Should emit for nested unions.
|
||||
field11: dict[int | int, str]
|
||||
@@ -49,3 +49,18 @@ def test_list_expressions(param1, param2):
|
||||
@pytest.mark.parametrize([some_expr, "param2"], [1, 2, 3])
|
||||
def test_list_mixed_expr_literal(param1, param2):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("param1, " "param2, " "param3"), [(1, 2, 3), (4, 5, 6)])
|
||||
def test_implicit_str_concat_with_parens(param1, param2, param3):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize("param1, " "param2, " "param3", [(1, 2, 3), (4, 5, 6)])
|
||||
def test_implicit_str_concat_no_parens(param1, param2, param3):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize((("param1, " "param2, " "param3")), [(1, 2, 3), (4, 5, 6)])
|
||||
def test_implicit_str_concat_with_multi_parens(param1, param2, param3):
|
||||
...
|
||||
|
||||
@@ -2,3 +2,13 @@ def x(y):
|
||||
if not y:
|
||||
return
|
||||
return None # error
|
||||
|
||||
|
||||
class BaseCache:
|
||||
def get(self, key: str) -> str | None:
|
||||
print(f"{key} not found")
|
||||
return None
|
||||
|
||||
def get(self, key: str) -> None:
|
||||
print(f"{key} not found")
|
||||
return None
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
###
|
||||
def x():
|
||||
a = 1
|
||||
return a # error
|
||||
return a # RET504
|
||||
|
||||
|
||||
# Can be refactored false positives
|
||||
@@ -211,10 +211,10 @@ def nonlocal_assignment():
|
||||
def decorator() -> Flask:
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/hello')
|
||||
@app.route("/hello")
|
||||
def hello() -> str:
|
||||
"""Hello endpoint."""
|
||||
return 'Hello, World!'
|
||||
return "Hello, World!"
|
||||
|
||||
return app
|
||||
|
||||
@@ -222,12 +222,13 @@ def decorator() -> Flask:
|
||||
def default():
|
||||
y = 1
|
||||
|
||||
def f(x = y) -> X:
|
||||
def f(x=y) -> X:
|
||||
return x
|
||||
|
||||
return y
|
||||
|
||||
|
||||
# Multiple assignment
|
||||
def get_queryset(option_1, option_2):
|
||||
queryset: Any = None
|
||||
queryset = queryset.filter(a=1)
|
||||
@@ -246,4 +247,28 @@ def get_queryset():
|
||||
|
||||
def get_queryset():
|
||||
queryset = Model.filter(a=1)
|
||||
return queryset # error
|
||||
return queryset # RET504
|
||||
|
||||
|
||||
# Function arguments
|
||||
def str_to_bool(val):
|
||||
if isinstance(val, bool):
|
||||
return val
|
||||
val = val.strip().lower()
|
||||
if val in ("1", "true", "yes"):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def str_to_bool(val):
|
||||
if isinstance(val, bool):
|
||||
return val
|
||||
val = 1
|
||||
return val # RET504
|
||||
|
||||
|
||||
def str_to_bool(val):
|
||||
if isinstance(val, bool):
|
||||
return some_obj
|
||||
return val
|
||||
|
||||
@@ -16,15 +16,26 @@ if isinstance(a, int) or isinstance(b, bool) or isinstance(a, float): # SIM101
|
||||
if (isinstance(a, int) or isinstance(a, float)) and isinstance(b, bool): # SIM101
|
||||
pass
|
||||
|
||||
if isinstance(a.b, int) or isinstance(a.b, float): # SIM101
|
||||
pass
|
||||
|
||||
if isinstance(a(), int) or isinstance(a(), float): # SIM101
|
||||
pass
|
||||
|
||||
if isinstance(a, int) and isinstance(b, bool) or isinstance(a, float):
|
||||
pass
|
||||
|
||||
if isinstance(a, bool) or isinstance(b, str):
|
||||
pass
|
||||
|
||||
if isinstance(a, int) or isinstance(a.b, float):
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
def isinstance(a, b):
|
||||
return False
|
||||
|
||||
if isinstance(a, int) or isinstance(a, float):
|
||||
pass
|
||||
|
||||
@@ -46,10 +46,10 @@ if a:
|
||||
if b:
|
||||
c
|
||||
|
||||
while True:
|
||||
while x > 0:
|
||||
# SIM102
|
||||
if True:
|
||||
if True:
|
||||
if y > 0:
|
||||
if z > 0:
|
||||
"""this
|
||||
is valid"""
|
||||
|
||||
@@ -64,8 +64,8 @@ is valid"""
|
||||
|
||||
|
||||
# SIM102
|
||||
if True:
|
||||
if True:
|
||||
if x > 0:
|
||||
if y > 0:
|
||||
"""this
|
||||
is valid"""
|
||||
|
||||
@@ -78,7 +78,7 @@ is valid"""
|
||||
("so is"
|
||||
"this for some reason")
|
||||
|
||||
while True:
|
||||
while x > 0:
|
||||
# SIM102
|
||||
if node.module:
|
||||
if node.module == "multiprocessing" or node.module.startswith(
|
||||
@@ -129,3 +129,15 @@ if a:
|
||||
print("baz")
|
||||
else:
|
||||
print("bar")
|
||||
|
||||
|
||||
# OK
|
||||
if False:
|
||||
if a:
|
||||
pass
|
||||
|
||||
|
||||
# OK
|
||||
if True:
|
||||
if a:
|
||||
pass
|
||||
|
||||
@@ -59,3 +59,15 @@ def bar():
|
||||
return foo()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def with_ellipsis():
|
||||
try:
|
||||
foo()
|
||||
except ValueError:
|
||||
...
|
||||
|
||||
def with_ellipsis_and_return():
|
||||
try:
|
||||
return foo()
|
||||
except ValueError:
|
||||
...
|
||||
|
||||
@@ -9,6 +9,17 @@ os.environ.get('foo', 'bar')
|
||||
|
||||
os.getenv('foo')
|
||||
|
||||
env = os.environ.get('foo')
|
||||
|
||||
env = os.environ['foo']
|
||||
|
||||
if env := os.environ.get('foo'):
|
||||
pass
|
||||
|
||||
if env := os.environ['foo']:
|
||||
pass
|
||||
|
||||
|
||||
# Good
|
||||
os.environ['FOO']
|
||||
|
||||
@@ -17,3 +28,13 @@ os.environ.get('FOO')
|
||||
os.environ.get('FOO', 'bar')
|
||||
|
||||
os.getenv('FOO')
|
||||
|
||||
env = os.getenv('FOO')
|
||||
|
||||
if env := os.getenv('FOO'):
|
||||
pass
|
||||
|
||||
env = os.environ['FOO']
|
||||
|
||||
if env := os.environ['FOO']:
|
||||
pass
|
||||
|
||||
@@ -20,3 +20,5 @@ for key in list(obj.keys()):
|
||||
{k: k for k in obj.keys()} # SIM118
|
||||
|
||||
(k for k in obj.keys()) # SIM118
|
||||
|
||||
key in (obj or {}).keys() # SIM118
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
if a or True: # SIM223
|
||||
if a or True: # SIM222
|
||||
pass
|
||||
|
||||
if (a or b) or True: # SIM223
|
||||
if (a or b) or True: # SIM222
|
||||
pass
|
||||
|
||||
if a or (b or True): # SIM223
|
||||
if a or (b or True): # SIM222
|
||||
pass
|
||||
|
||||
if a and True: # OK
|
||||
@@ -16,3 +16,139 @@ if True: # OK
|
||||
|
||||
def validate(self, value):
|
||||
return json.loads(value) or True # OK
|
||||
|
||||
|
||||
if a or f() or b or g() or True: # OK
|
||||
pass
|
||||
|
||||
if a or f() or True or g() or b: # SIM222
|
||||
pass
|
||||
|
||||
if True or f() or a or g() or b: # SIM222
|
||||
pass
|
||||
|
||||
if a or True or f() or b or g(): # SIM222
|
||||
pass
|
||||
|
||||
|
||||
if a and f() and b and g() and False: # OK
|
||||
pass
|
||||
|
||||
if a and f() and False and g() and b: # OK
|
||||
pass
|
||||
|
||||
if False and f() and a and g() and b: # OK
|
||||
pass
|
||||
|
||||
if a and False and f() and b and g(): # OK
|
||||
pass
|
||||
|
||||
|
||||
a or "" or True # SIM222
|
||||
|
||||
a or "foo" or True or "bar" # SIM222
|
||||
|
||||
a or 0 or True # SIM222
|
||||
|
||||
a or 1 or True or 2 # SIM222
|
||||
|
||||
a or 0.0 or True # SIM222
|
||||
|
||||
a or 0.1 or True or 0.2 # SIM222
|
||||
|
||||
a or [] or True # SIM222
|
||||
|
||||
a or list([]) or True # SIM222
|
||||
|
||||
a or [1] or True or [2] # SIM222
|
||||
|
||||
a or list([1]) or True or list([2]) # SIM222
|
||||
|
||||
a or {} or True # SIM222
|
||||
|
||||
a or dict() or True # SIM222
|
||||
|
||||
a or {1: 1} or True or {2: 2} # SIM222
|
||||
|
||||
a or dict({1: 1}) or True or dict({2: 2}) # SIM222
|
||||
|
||||
a or set() or True # SIM222
|
||||
|
||||
a or set(set()) or True # SIM222
|
||||
|
||||
a or {1} or True or {2} # SIM222
|
||||
|
||||
a or set({1}) or True or set({2}) # SIM222
|
||||
|
||||
a or () or True # SIM222
|
||||
|
||||
a or tuple(()) or True # SIM222
|
||||
|
||||
a or (1,) or True or (2,) # SIM222
|
||||
|
||||
a or tuple((1,)) or True or tuple((2,)) # SIM222
|
||||
|
||||
a or frozenset() or True # SIM222
|
||||
|
||||
a or frozenset(frozenset()) or True # SIM222
|
||||
|
||||
a or frozenset({1}) or True or frozenset({2}) # SIM222
|
||||
|
||||
a or frozenset(frozenset({1})) or True or frozenset(frozenset({2})) # SIM222
|
||||
|
||||
|
||||
# Inside test `a` is simplified.
|
||||
|
||||
bool(a or [1] or True or [2]) # SIM222
|
||||
|
||||
assert a or [1] or True or [2] # SIM222
|
||||
|
||||
if (a or [1] or True or [2]) and (a or [1] or True or [2]): # SIM222
|
||||
pass
|
||||
|
||||
0 if a or [1] or True or [2] else 1 # SIM222
|
||||
|
||||
while a or [1] or True or [2]: # SIM222
|
||||
pass
|
||||
|
||||
[
|
||||
0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a or [1] or True or [2] # SIM222
|
||||
if b or [1] or True or [2] # SIM222
|
||||
]
|
||||
|
||||
{
|
||||
0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a or [1] or True or [2] # SIM222
|
||||
if b or [1] or True or [2] # SIM222
|
||||
}
|
||||
|
||||
{
|
||||
0: 0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a or [1] or True or [2] # SIM222
|
||||
if b or [1] or True or [2] # SIM222
|
||||
}
|
||||
|
||||
(
|
||||
0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a or [1] or True or [2] # SIM222
|
||||
if b or [1] or True or [2] # SIM222
|
||||
)
|
||||
|
||||
# Outside test `a` is not simplified.
|
||||
|
||||
a or [1] or True or [2] # SIM222
|
||||
|
||||
if (a or [1] or True or [2]) == (a or [1]): # SIM222
|
||||
pass
|
||||
|
||||
if f(a or [1] or True or [2]): # SIM222
|
||||
pass
|
||||
|
||||
@@ -12,3 +12,138 @@ if a or False:
|
||||
|
||||
if False:
|
||||
pass
|
||||
|
||||
if a and f() and b and g() and False: # OK
|
||||
pass
|
||||
|
||||
if a and f() and False and g() and b: # SIM223
|
||||
pass
|
||||
|
||||
if False and f() and a and g() and b: # SIM223
|
||||
pass
|
||||
|
||||
if a and False and f() and b and g(): # SIM223
|
||||
pass
|
||||
|
||||
|
||||
if a or f() or b or g() or True: # OK
|
||||
pass
|
||||
|
||||
if a or f() or True or g() or b: # OK
|
||||
pass
|
||||
|
||||
if True or f() or a or g() or b: # OK
|
||||
pass
|
||||
|
||||
if a or True or f() or b or g(): # OK
|
||||
pass
|
||||
|
||||
|
||||
a and "" and False # SIM223
|
||||
|
||||
a and "foo" and False and "bar" # SIM223
|
||||
|
||||
a and 0 and False # SIM223
|
||||
|
||||
a and 1 and False and 2 # SIM223
|
||||
|
||||
a and 0.0 and False # SIM223
|
||||
|
||||
a and 0.1 and False and 0.2 # SIM223
|
||||
|
||||
a and [] and False # SIM223
|
||||
|
||||
a and list([]) and False # SIM223
|
||||
|
||||
a and [1] and False and [2] # SIM223
|
||||
|
||||
a and list([1]) and False and list([2]) # SIM223
|
||||
|
||||
a and {} and False # SIM223
|
||||
|
||||
a and dict() and False # SIM223
|
||||
|
||||
a and {1: 1} and False and {2: 2} # SIM223
|
||||
|
||||
a and dict({1: 1}) and False and dict({2: 2}) # SIM223
|
||||
|
||||
a and set() and False # SIM223
|
||||
|
||||
a and set(set()) and False # SIM223
|
||||
|
||||
a and {1} and False and {2} # SIM223
|
||||
|
||||
a and set({1}) and False and set({2}) # SIM223
|
||||
|
||||
a and () and False # SIM222
|
||||
|
||||
a and tuple(()) and False # SIM222
|
||||
|
||||
a and (1,) and False and (2,) # SIM222
|
||||
|
||||
a and tuple((1,)) and False and tuple((2,)) # SIM222
|
||||
|
||||
a and frozenset() and False # SIM222
|
||||
|
||||
a and frozenset(frozenset()) and False # SIM222
|
||||
|
||||
a and frozenset({1}) and False and frozenset({2}) # SIM222
|
||||
|
||||
a and frozenset(frozenset({1})) and False and frozenset(frozenset({2})) # SIM222
|
||||
|
||||
|
||||
# Inside test `a` is simplified.
|
||||
|
||||
bool(a and [] and False and []) # SIM223
|
||||
|
||||
assert a and [] and False and [] # SIM223
|
||||
|
||||
if (a and [] and False and []) or (a and [] and False and []): # SIM223
|
||||
pass
|
||||
|
||||
0 if a and [] and False and [] else 1 # SIM222
|
||||
|
||||
while a and [] and False and []: # SIM223
|
||||
pass
|
||||
|
||||
[
|
||||
0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a and [] and False and [] # SIM223
|
||||
if b and [] and False and [] # SIM223
|
||||
]
|
||||
|
||||
{
|
||||
0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a and [] and False and [] # SIM223
|
||||
if b and [] and False and [] # SIM223
|
||||
}
|
||||
|
||||
{
|
||||
0: 0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a and [] and False and [] # SIM223
|
||||
if b and [] and False and [] # SIM223
|
||||
}
|
||||
|
||||
(
|
||||
0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a and [] and False and [] # SIM223
|
||||
if b and [] and False and [] # SIM223
|
||||
)
|
||||
|
||||
# Outside test `a` is not simplified.
|
||||
|
||||
a and [] and False and [] # SIM223
|
||||
|
||||
if (a and [] and False and []) == (a and []): # SIM223
|
||||
pass
|
||||
|
||||
if f(a and [] and False and []): # SIM223
|
||||
pass
|
||||
|
||||
27
crates/ruff/resources/test/fixtures/flake8_simplify/SIM910.py
vendored
Normal file
27
crates/ruff/resources/test/fixtures/flake8_simplify/SIM910.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# SIM910
|
||||
{}.get(key, None)
|
||||
|
||||
# SIM910
|
||||
{}.get("key", None)
|
||||
|
||||
# OK
|
||||
{}.get(key)
|
||||
|
||||
# OK
|
||||
{}.get("key")
|
||||
|
||||
# OK
|
||||
{}.get(key, False)
|
||||
|
||||
# OK
|
||||
{}.get("key", False)
|
||||
|
||||
# SIM910
|
||||
if a := {}.get(key, None):
|
||||
pass
|
||||
|
||||
# SIM910
|
||||
a = {}.get(key, None)
|
||||
|
||||
# SIM910
|
||||
({}).get(key, None)
|
||||
@@ -7,3 +7,4 @@ from ..protocol import commands, definitions, responses
|
||||
from ..server import example
|
||||
from .. import server
|
||||
from . import logger, models
|
||||
from ..protocol.UpperCaseModule import some_function
|
||||
@@ -0,0 +1,2 @@
|
||||
def some_function():
|
||||
pass
|
||||
@@ -31,3 +31,6 @@ typing.TypedDict.anything()
|
||||
# import aliases are resolved
|
||||
import typing as totally_not_typing
|
||||
totally_not_typing.TypedDict
|
||||
|
||||
# relative imports are respected
|
||||
from .typing import TypedDict
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# module members cannot be imported with that syntax
|
||||
import typing.TypedDict
|
||||
|
||||
# we don't track reassignments
|
||||
import typing, other
|
||||
typing = other
|
||||
typing.TypedDict()
|
||||
|
||||
# yet another false positive
|
||||
def foo(typing):
|
||||
typing.TypedDict()
|
||||
@@ -1,3 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def f():
|
||||
# Even in strict mode, this shouldn't rase an error, since `pkg` is used at runtime,
|
||||
# and implicitly imports `pkg.bar`.
|
||||
|
||||
@@ -2,7 +2,9 @@ from a import a1 # import_from
|
||||
from c import * # import_from_star
|
||||
import a # import
|
||||
import c.d
|
||||
from z import z1
|
||||
import b as b1 # import_as
|
||||
import z
|
||||
|
||||
from ..parent import *
|
||||
from .my import fn
|
||||
|
||||
4
crates/ruff/resources/test/fixtures/isort/propagate_inline_comments.py
vendored
Normal file
4
crates/ruff/resources/test/fixtures/isort/propagate_inline_comments.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
from mypackage.subpackage import ( # long comment that seems to be a problem
|
||||
a_long_variable_name_that_causes_problems,
|
||||
items,
|
||||
)
|
||||
3
crates/ruff/resources/test/fixtures/isort/required_imports/docstring.pyi
vendored
Normal file
3
crates/ruff/resources/test/fixtures/isort/required_imports/docstring.pyi
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
"""Hello, world!"""
|
||||
|
||||
x = 1
|
||||
7
crates/ruff/resources/test/fixtures/isort/sections.py
vendored
Normal file
7
crates/ruff/resources/test/fixtures/isort/sections.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
from __future__ import annotations
|
||||
import os
|
||||
import sys
|
||||
import pytz
|
||||
import django.settings
|
||||
from library import foo
|
||||
from . import local
|
||||
@@ -0,0 +1,8 @@
|
||||
import sys
|
||||
import baz
|
||||
from foo import bar, baz
|
||||
from foo.bar import blah, blub
|
||||
from foo.bar.baz import something
|
||||
import foo
|
||||
import foo.bar
|
||||
import foo.bar.baz
|
||||
32
crates/ruff/resources/test/fixtures/jupyter/R.ipynb
vendored
Normal file
32
crates/ruff/resources/test/fixtures/jupyter/R.ipynb
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(paste(\"Hello\",\"WoRld\"))\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "R",
|
||||
"language": "R",
|
||||
"name": "ir"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": "r",
|
||||
"file_extension": ".r",
|
||||
"mimetype": "text/x-r-source",
|
||||
"name": "R",
|
||||
"pygments_lexer": "r",
|
||||
"version": "3.2.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
24
crates/ruff/resources/test/fixtures/jupyter/invalid_extension.ipynb
vendored
Normal file
24
crates/ruff/resources/test/fixtures/jupyter/invalid_extension.ipynb
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# In[1]:
|
||||
|
||||
|
||||
def unused_variable():
|
||||
x = 1
|
||||
y = 2
|
||||
print(f"cell one: {y}")
|
||||
|
||||
unused_variable()
|
||||
|
||||
|
||||
# Let's do another mistake
|
||||
|
||||
# In[2]:
|
||||
|
||||
|
||||
def mutable_argument(z=set()):
|
||||
print(f"cell two: {z}")
|
||||
|
||||
mutable_argument()
|
||||
|
||||
1
crates/ruff/resources/test/fixtures/jupyter/not_json.ipynb
vendored
Normal file
1
crates/ruff/resources/test/fixtures/jupyter/not_json.ipynb
vendored
Normal file
@@ -0,0 +1 @@
|
||||
broken "§=($/=(")
|
||||
88
crates/ruff/resources/test/fixtures/jupyter/valid.ipynb
vendored
Normal file
88
crates/ruff/resources/test/fixtures/jupyter/valid.ipynb
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"cell one: 2\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def unused_variable():\n",
|
||||
" x = 1\n",
|
||||
" y = 2\n",
|
||||
" print(f\"cell one: {y}\")\n",
|
||||
"\n",
|
||||
"unused_variable()"
|
||||
],
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"ExecuteTime": {
|
||||
"start_time": "2023-03-08T23:01:09.705831Z",
|
||||
"end_time": "2023-03-08T23:01:09.782916Z"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"Let's do another mistake"
|
||||
],
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"collapsed": true,
|
||||
"ExecuteTime": {
|
||||
"start_time": "2023-03-08T23:01:09.733809Z",
|
||||
"end_time": "2023-03-08T23:01:09.915760Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"cell two: set()\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def mutable_argument(z=set()):\n",
|
||||
" print(f\"cell two: {z}\")\n",
|
||||
"\n",
|
||||
"mutable_argument()\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 2
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython2",
|
||||
"version": "2.7.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
1
crates/ruff/resources/test/fixtures/jupyter/wrong_schema.ipynb
vendored
Normal file
1
crates/ruff/resources/test/fixtures/jupyter/wrong_schema.ipynb
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -18,3 +18,7 @@ if True:
|
||||
columns=["a"],
|
||||
axis=1,
|
||||
)
|
||||
|
||||
x.drop(["a"], axis=1, **kwargs, inplace=True)
|
||||
x.drop(["a"], axis=1, inplace=True, **kwargs)
|
||||
f(x.drop(["a"], axis=1, inplace=True))
|
||||
|
||||
@@ -39,3 +39,11 @@ class Test(unittest.TestCase):
|
||||
|
||||
def testTest(self):
|
||||
assert True
|
||||
|
||||
|
||||
from typing import override
|
||||
|
||||
|
||||
@override
|
||||
def BAD_FUNC():
|
||||
pass
|
||||
|
||||
@@ -13,3 +13,11 @@ class C:
|
||||
myObj2 = namedtuple("MyObj2", ["a", "b"])
|
||||
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
|
||||
Point2D = TypedDict('Point2D', {'in': int, 'x-y': int})
|
||||
|
||||
|
||||
class D(TypedDict):
|
||||
lower: int
|
||||
CONSTANT: str
|
||||
mixedCase: bool
|
||||
_mixedCase: list
|
||||
mixed_Case: set
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user