Compare commits
179 Commits
PYI050
...
4404_fix_e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2abdc2540a | ||
|
|
1e36145972 | ||
|
|
8e06140d1d | ||
|
|
015895bcae | ||
|
|
36e01ad6eb | ||
|
|
ddfdc3bb01 | ||
|
|
bd2c50be86 | ||
|
|
48f4f2d63d | ||
|
|
94abf7f088 | ||
|
|
e3c12764f8 | ||
|
|
b8d378b0a3 | ||
|
|
0e028142f4 | ||
|
|
361d45f2b2 | ||
|
|
be11cae619 | ||
|
|
2b82caa163 | ||
|
|
51bc758d18 | ||
|
|
a6cf31cc89 | ||
|
|
524a2045ba | ||
|
|
a0b750f74b | ||
|
|
195b36c429 | ||
|
|
5c416e4d9b | ||
|
|
763d38cafb | ||
|
|
653a0ebf2d | ||
|
|
95448ba669 | ||
|
|
f18e10183f | ||
|
|
98920909c6 | ||
|
|
e1e1d2d341 | ||
|
|
4b9b6829dc | ||
|
|
be107dad64 | ||
|
|
d0ad1ed0af | ||
|
|
b3240dbfa2 | ||
|
|
fd1dfc3bfa | ||
|
|
b9754bd5c5 | ||
|
|
307f7a735c | ||
|
|
3af9dfeb0a | ||
|
|
5526699535 | ||
|
|
fab2a4adf7 | ||
|
|
13813dc1b1 | ||
|
|
70c01257ca | ||
|
|
26d19655db | ||
|
|
1f856aa576 | ||
|
|
1e383483f7 | ||
|
|
89b328c6be | ||
|
|
6143065fc2 | ||
|
|
107a295af4 | ||
|
|
c811213302 | ||
|
|
5ea3e42513 | ||
|
|
66089e1a2e | ||
|
|
097823b56d | ||
|
|
ed8113267c | ||
|
|
c654280d84 | ||
|
|
99486b38f4 | ||
|
|
716cab2f19 | ||
|
|
9ab16fb417 | ||
|
|
458beccf14 | ||
|
|
ccbc863960 | ||
|
|
71b3130ff1 | ||
|
|
08cd140ea6 | ||
|
|
848f184b8c | ||
|
|
56476dfd61 | ||
|
|
bae183b823 | ||
|
|
65dbfd2556 | ||
|
|
86ff1febea | ||
|
|
a33bbe6335 | ||
|
|
c992cfa76e | ||
|
|
916f0889f8 | ||
|
|
c1fd2c8a8e | ||
|
|
732b0405d7 | ||
|
|
e7316c1cc6 | ||
|
|
6f10aeebaa | ||
|
|
c74ef77e85 | ||
|
|
1e497162d1 | ||
|
|
aa41ffcfde | ||
|
|
bf5fbf8971 | ||
|
|
fc6580592d | ||
|
|
4d9b0b925d | ||
|
|
0daeea1f42 | ||
|
|
3f6584b74f | ||
|
|
c2fa568b46 | ||
|
|
1895011ac2 | ||
|
|
364bd82aee | ||
|
|
f9f08d6b03 | ||
|
|
b0984a2868 | ||
|
|
a431dd0368 | ||
|
|
099a9152d1 | ||
|
|
19f972a305 | ||
|
|
b0f89fa814 | ||
|
|
65312bad01 | ||
|
|
7b4dde0c6c | ||
|
|
e1fd3965a2 | ||
|
|
95ee6dcb3b | ||
|
|
cc44349401 | ||
|
|
a477720f4e | ||
|
|
be2fa6d217 | ||
|
|
cbd4c10fdd | ||
|
|
e2130707f5 | ||
|
|
780336db0a | ||
|
|
7e37d8916c | ||
|
|
ab11dd08df | ||
|
|
4080f36850 | ||
|
|
6d861743c8 | ||
|
|
54e103fc99 | ||
|
|
3470dee7d4 | ||
|
|
cb4f086cbf | ||
|
|
a77d2df934 | ||
|
|
638c18f007 | ||
|
|
70e6c212d9 | ||
|
|
9db622afe1 | ||
|
|
d3aa81a474 | ||
|
|
d8f5d2d767 | ||
|
|
e586c27590 | ||
|
|
8161757229 | ||
|
|
6a5f317362 | ||
|
|
c3d1fa851e | ||
|
|
eac3a0cc3d | ||
|
|
31067e6ce2 | ||
|
|
68b6d30c46 | ||
|
|
ab3c02342b | ||
|
|
9f7cc86a22 | ||
|
|
445e1723ab | ||
|
|
42c8054268 | ||
|
|
2d597bc1fb | ||
|
|
7275c16d98 | ||
|
|
02b8ce82af | ||
|
|
5abb8ec0dc | ||
|
|
f401050878 | ||
|
|
c1ac50093c | ||
|
|
1d756dc3a7 | ||
|
|
e86f12a1ec | ||
|
|
5c502a3320 | ||
|
|
901bcb6f21 | ||
|
|
111e1f93ca | ||
|
|
68d52da43b | ||
|
|
646ab64850 | ||
|
|
1accbeffd6 | ||
|
|
548a3cbb3f | ||
|
|
16d1e63a5e | ||
|
|
d647105e97 | ||
|
|
63fdcea29e | ||
|
|
2bb32ee943 | ||
|
|
ee1f094834 | ||
|
|
efd8f3bdab | ||
|
|
293889a352 | ||
|
|
2c19000e4a | ||
|
|
aba073a791 | ||
|
|
d042eddccc | ||
|
|
775d247731 | ||
|
|
58d08219e8 | ||
|
|
902c4e7d77 | ||
|
|
68969240c5 | ||
|
|
07cc4bcb0f | ||
|
|
9c3fb23ace | ||
|
|
467df23e65 | ||
|
|
c8442e91ce | ||
|
|
6bef347a8e | ||
|
|
c1cc6f3be1 | ||
|
|
83cf6d6e2f | ||
|
|
23abad0bd5 | ||
|
|
651d89794c | ||
|
|
39a1f3980f | ||
|
|
4b78141f6b | ||
|
|
5235977abc | ||
|
|
01d3d4bbd2 | ||
|
|
ac4a4da50e | ||
|
|
a6d269f263 | ||
|
|
f17282d615 | ||
|
|
6950c93934 | ||
|
|
ae75b303f0 | ||
|
|
20240fc3d9 | ||
|
|
f990d9dcc5 | ||
|
|
5c48414093 | ||
|
|
bcf745c5ba | ||
|
|
2f125f4019 | ||
|
|
6ab3fc60f4 | ||
|
|
222ca98a41 | ||
|
|
ec609f5c3b | ||
|
|
b9060ea2bd | ||
|
|
b56a799417 | ||
|
|
780d153ae8 |
9
.github/CODEOWNERS
vendored
Normal file
9
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# GitHub code owners file. For more info: https://help.github.com/articles/about-codeowners/
|
||||
#
|
||||
# - Comment lines begin with `#` character.
|
||||
# - Each line is a file pattern followed by one or more owners.
|
||||
# - The '*' pattern is global owners.
|
||||
# - Order is important. The last matching pattern has the most precedence.
|
||||
|
||||
# Jupyter
|
||||
/crates/ruff/src/jupyter/ @dhruvmanila
|
||||
19
.github/workflows/benchmark.yaml
vendored
19
.github/workflows/benchmark.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
name: "Run | ${{ matrix.os }}"
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
@@ -29,10 +29,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: "PR - Build benchmarks"
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: bench
|
||||
args: -p ruff_benchmark --no-run
|
||||
run: cargo bench -p ruff_benchmark --no-run
|
||||
|
||||
- name: "PR - Run benchmarks"
|
||||
run: cargo benchmark --save-baseline=pr
|
||||
@@ -47,10 +44,7 @@ jobs:
|
||||
run: rustup show
|
||||
|
||||
- name: "Main - Build benchmarks"
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: bench
|
||||
args: -p ruff_benchmark --no-run
|
||||
run: cargo bench -p ruff_benchmark --no-run
|
||||
|
||||
- name: "Main - Run benchmarks"
|
||||
run: cargo benchmark --save-baseline=main
|
||||
@@ -78,11 +72,10 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
|
||||
- name: "Install cargo-binstall"
|
||||
uses: taiki-e/install-action@cargo-binstall
|
||||
|
||||
- name: "Install critcmp"
|
||||
run: cargo binstall critcmp -y
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: critcmp
|
||||
|
||||
- name: "Linux | Download PR benchmark results"
|
||||
uses: actions/download-artifact@v3
|
||||
|
||||
31
.github/workflows/ci.yaml
vendored
31
.github/workflows/ci.yaml
vendored
@@ -2,7 +2,7 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
cargo-test:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: "cargo test | ${{ matrix.os }}"
|
||||
steps:
|
||||
@@ -87,6 +87,22 @@ jobs:
|
||||
name: ruff
|
||||
path: target/debug/ruff
|
||||
|
||||
cargo-fuzz:
|
||||
runs-on: ubuntu-latest
|
||||
name: "cargo fuzz"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: "fuzz -> target"
|
||||
- name: "Install cargo-fuzz"
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-fuzz@0.11
|
||||
- run: cargo fuzz build -s none
|
||||
|
||||
cargo-test-wasm:
|
||||
runs-on: ubuntu-latest
|
||||
name: "cargo test (wasm)"
|
||||
@@ -178,12 +194,12 @@ jobs:
|
||||
- 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
|
||||
run: rustup toolchain install nightly-2023-06-08
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Install cargo-udeps"
|
||||
uses: taiki-e/install-action@cargo-udeps
|
||||
- name: "Run cargo-udeps"
|
||||
run: cargo +nightly-2023-03-30 udeps
|
||||
run: cargo +nightly-2023-06-08 udeps
|
||||
|
||||
|
||||
python-package:
|
||||
@@ -195,6 +211,7 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
@@ -207,6 +224,8 @@ jobs:
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Remove wheels from cache"
|
||||
run: rm -rf target/wheels
|
||||
|
||||
pre-commit:
|
||||
name: "pre-commit"
|
||||
@@ -224,8 +243,8 @@ jobs:
|
||||
- name: "Cache pre-commit"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
- name: "Run pre-commit"
|
||||
run: |
|
||||
echo '```console' > $GITHUB_STEP_SUMMARY
|
||||
|
||||
6
.github/workflows/flake8-to-ruff.yaml
vendored
6
.github/workflows/flake8-to-ruff.yaml
vendored
@@ -66,7 +66,7 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x64, x86]
|
||||
target: [ x64, x86 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x86_64, i686]
|
||||
target: [ x86_64, i686 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [aarch64, armv7, s390x, ppc64le, ppc64]
|
||||
target: [ aarch64, armv7, s390x, ppc64le, ppc64 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
|
||||
4
.github/workflows/pr-comment.yaml
vendored
4
.github/workflows/pr-comment.yaml
vendored
@@ -2,8 +2,8 @@ name: PR Check Comment
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [CI, Benchmark]
|
||||
types: [completed]
|
||||
workflows: [ CI, Benchmark ]
|
||||
types: [ completed ]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
workflow_run_id:
|
||||
|
||||
6
.github/workflows/release.yaml
vendored
6
.github/workflows/release.yaml
vendored
@@ -221,7 +221,7 @@ jobs:
|
||||
platform:
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
arch: aarch64
|
||||
# see https://github.com/charliermarsh/ruff/issues/3791
|
||||
# see https://github.com/astral-sh/ruff/issues/3791
|
||||
# and https://github.com/gnzlbg/jemallocator/issues/170#issuecomment-1503228963
|
||||
maturin_docker_options: -e JEMALLOC_SYS_WITH_LG_PAGE=16
|
||||
- target: armv7-unknown-linux-gnueabihf
|
||||
@@ -400,6 +400,8 @@ jobs:
|
||||
permissions:
|
||||
# For pypi trusted publishing
|
||||
id-token: write
|
||||
# For GitHub release publishing
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
@@ -415,7 +417,7 @@ jobs:
|
||||
with:
|
||||
name: binaries
|
||||
path: binaries
|
||||
- name: Release
|
||||
- name: "Publish to GitHub"
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: binaries/*
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -6,6 +6,11 @@ github_search*.jsonl
|
||||
schemastore
|
||||
.venv*
|
||||
scratch.py
|
||||
perf.data
|
||||
perf.data.old
|
||||
flamegraph.svg
|
||||
# Additional target directories that don't invalidate the main compile cache when changing linker settings
|
||||
/target*
|
||||
|
||||
###
|
||||
# Rust.gitignore
|
||||
|
||||
@@ -3,6 +3,7 @@ fail_fast: true
|
||||
exclude: |
|
||||
(?x)^(
|
||||
crates/ruff/resources/.*|
|
||||
crates/ruff/src/rules/.*/snapshots/.*|
|
||||
crates/ruff_python_formatter/resources/.*|
|
||||
crates/ruff_python_formatter/src/snapshots/.*
|
||||
)$
|
||||
@@ -37,29 +38,19 @@ repos:
|
||||
name: cargo fmt
|
||||
entry: cargo fmt --
|
||||
language: system
|
||||
types: [rust]
|
||||
- id: clippy
|
||||
name: clippy
|
||||
entry: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
language: system
|
||||
pass_filenames: false
|
||||
types: [ rust ]
|
||||
pass_filenames: false # This makes it a lot faster
|
||||
- id: ruff
|
||||
name: ruff
|
||||
entry: cargo run -p ruff_cli -- check --no-cache --force-exclude --fix --exit-non-zero-on-fix
|
||||
entry: cargo run --bin ruff -- check --no-cache --force-exclude --fix --exit-non-zero-on-fix
|
||||
language: system
|
||||
types_or: [python, pyi]
|
||||
types_or: [ python, pyi ]
|
||||
require_serial: true
|
||||
exclude: |
|
||||
(?x)^(
|
||||
crates/ruff/resources/.*|
|
||||
crates/ruff_python_formatter/resources/.*
|
||||
)$
|
||||
- id: dev-generate-all
|
||||
name: dev-generate-all
|
||||
entry: cargo dev generate-all
|
||||
language: system
|
||||
pass_filenames: false
|
||||
exclude: target
|
||||
|
||||
# Black
|
||||
- repo: https://github.com/psf/black
|
||||
@@ -68,4 +59,4 @@ repos:
|
||||
- id: black
|
||||
|
||||
ci:
|
||||
skip: [cargo-fmt, clippy, dev-generate-all]
|
||||
skip: [ cargo-fmt, dev-generate-all ]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## 0.0.268
|
||||
|
||||
### The `keep-runtime-typing` setting has been removed ([#4427](https://github.com/charliermarsh/ruff/pull/4427))
|
||||
### The `keep-runtime-typing` setting has been removed ([#4427](https://github.com/astral-sh/ruff/pull/4427))
|
||||
|
||||
Enabling the `keep-runtime-typing` option, located under the `pyupgrade` section, is equivalent
|
||||
to ignoring the `UP006` and `UP007` rules via Ruff's standard `ignore` mechanism. As there's no
|
||||
@@ -11,9 +11,9 @@ removed.
|
||||
|
||||
## 0.0.267
|
||||
|
||||
### `update-check` is no longer a valid configuration option ([#4313](https://github.com/charliermarsh/ruff/pull/4313))
|
||||
### `update-check` is no longer a valid configuration option ([#4313](https://github.com/astral-sh/ruff/pull/4313))
|
||||
|
||||
The `update-check` functionality was deprecated in [#2530](https://github.com/charliermarsh/ruff/pull/2530),
|
||||
The `update-check` functionality was deprecated in [#2530](https://github.com/astral-sh/ruff/pull/2530),
|
||||
in that the behavior itself was removed, and Ruff was changed to warn when that option was enabled.
|
||||
|
||||
Now, Ruff will throw an error when `update-check` is provided via a configuration file (e.g.,
|
||||
@@ -22,7 +22,7 @@ this option from their configuration.
|
||||
|
||||
## 0.0.265
|
||||
|
||||
### `--fix-only` now exits with a zero exit code, unless `--exit-non-zero-on-fix` is specified ([#4146](https://github.com/charliermarsh/ruff/pull/4146))
|
||||
### `--fix-only` now exits with a zero exit code, unless `--exit-non-zero-on-fix` is specified ([#4146](https://github.com/astral-sh/ruff/pull/4146))
|
||||
|
||||
Previously, `--fix-only` would exit with a non-zero exit code if any fixes were applied. This
|
||||
behavior was inconsistent with `--fix`, and further, meant that `--exit-non-zero-on-fix` was
|
||||
@@ -33,7 +33,7 @@ in which case it will exit with a non-zero exit code if any fixes were applied.
|
||||
|
||||
## 0.0.260
|
||||
|
||||
### Fixes are now represented as a list of edits ([#3709](https://github.com/charliermarsh/ruff/pull/3709))
|
||||
### Fixes are now represented as a list of edits ([#3709](https://github.com/astral-sh/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
|
||||
@@ -68,14 +68,14 @@ The updated representation instead includes a list of edits:
|
||||
|
||||
## 0.0.246
|
||||
|
||||
### `multiple-statements-on-one-line-def` (`E704`) was removed ([#2773](https://github.com/charliermarsh/ruff/pull/2773))
|
||||
### `multiple-statements-on-one-line-def` (`E704`) was removed ([#2773](https://github.com/astral-sh/ruff/pull/2773))
|
||||
|
||||
This rule was introduced in v0.0.245. However, it turns out that pycodestyle and Flake8 ignore this
|
||||
rule by default, as it is not part of PEP 8. As such, we've removed it from Ruff.
|
||||
|
||||
## 0.0.245
|
||||
|
||||
### Ruff's public `check` method was removed ([#2709](https://github.com/charliermarsh/ruff/pull/2709))
|
||||
### Ruff's public `check` method was removed ([#2709](https://github.com/astral-sh/ruff/pull/2709))
|
||||
|
||||
Previously, Ruff exposed a `check` method as a public Rust API. This method was used by few,
|
||||
if any clients, and was not well documented or supported. As such, it has been removed, with
|
||||
@@ -83,11 +83,11 @@ the intention of adding a stable public API in the future.
|
||||
|
||||
## 0.0.238
|
||||
|
||||
### `select`, `extend-select`, `ignore`, and `extend-ignore` have new semantics ([#2312](https://github.com/charliermarsh/ruff/pull/2312))
|
||||
### `select`, `extend-select`, `ignore`, and `extend-ignore` have new semantics ([#2312](https://github.com/astral-sh/ruff/pull/2312))
|
||||
|
||||
Previously, the interplay between `select` and its related options could lead to unexpected
|
||||
behavior. For example, `ruff --select E501 --ignore ALL` and `ruff --select E501 --extend-ignore ALL`
|
||||
behaved differently. (See [#2312](https://github.com/charliermarsh/ruff/pull/2312) for more
|
||||
behaved differently. (See [#2312](https://github.com/astral-sh/ruff/pull/2312) for more
|
||||
examples.)
|
||||
|
||||
When Ruff determines the enabled rule set, it has to reconcile `select` and `ignore` from a variety
|
||||
@@ -113,14 +113,14 @@ ignore = ["F401"]
|
||||
Running `ruff --select F` would previously have enabled all `F` rules, apart from `F401`. Now, it
|
||||
will enable all `F` rules, including `F401`, as the command line's `--select` resets the resolution.
|
||||
|
||||
### `remove-six-compat` (`UP016`) has been removed ([#2332](https://github.com/charliermarsh/ruff/pull/2332))
|
||||
### `remove-six-compat` (`UP016`) has been removed ([#2332](https://github.com/astral-sh/ruff/pull/2332))
|
||||
|
||||
The `remove-six-compat` rule has been removed. This rule was only useful for one-time Python 2-to-3
|
||||
upgrades.
|
||||
|
||||
## 0.0.237
|
||||
|
||||
### `--explain`, `--clean`, and `--generate-shell-completion` are now subcommands ([#2190](https://github.com/charliermarsh/ruff/pull/2190))
|
||||
### `--explain`, `--clean`, and `--generate-shell-completion` are now subcommands ([#2190](https://github.com/astral-sh/ruff/pull/2190))
|
||||
|
||||
`--explain`, `--clean`, and `--generate-shell-completion` are now implemented as subcommands:
|
||||
|
||||
@@ -163,14 +163,14 @@ no change in behavior. However, please note the following exceptions:
|
||||
|
||||
## 0.0.226
|
||||
|
||||
### `misplaced-comparison-constant` (`PLC2201`) was deprecated in favor of `SIM300` ([#1980](https://github.com/charliermarsh/ruff/pull/1980))
|
||||
### `misplaced-comparison-constant` (`PLC2201`) was deprecated in favor of `SIM300` ([#1980](https://github.com/astral-sh/ruff/pull/1980))
|
||||
|
||||
These two rules contain (nearly) identical logic. To deduplicate the rule set, we've upgraded
|
||||
`SIM300` to handle a few more cases, and deprecated `PLC2201` in favor of `SIM300`.
|
||||
|
||||
## 0.0.225
|
||||
|
||||
### `@functools.cache` rewrites have been moved to a standalone rule (`UP033`) ([#1938](https://github.com/charliermarsh/ruff/pull/1938))
|
||||
### `@functools.cache` rewrites have been moved to a standalone rule (`UP033`) ([#1938](https://github.com/astral-sh/ruff/pull/1938))
|
||||
|
||||
Previously, `UP011` handled both `@functools.lru_cache()`-to-`@functools.lru_cache` conversions,
|
||||
_and_ `@functools.lru_cache(maxsize=None)`-to-`@functools.cache` conversions. The latter has been
|
||||
@@ -179,7 +179,7 @@ to reflect the change in rule code.
|
||||
|
||||
## 0.0.222
|
||||
|
||||
### `--max-complexity` has been removed from the CLI ([#1877](https://github.com/charliermarsh/ruff/pull/1877))
|
||||
### `--max-complexity` has been removed from the CLI ([#1877](https://github.com/astral-sh/ruff/pull/1877))
|
||||
|
||||
The McCabe plugin's `--max-complexity` setting has been removed from the CLI, for consistency with
|
||||
the treatment of other, similar settings.
|
||||
@@ -194,7 +194,7 @@ max-complexity = 10
|
||||
|
||||
## 0.0.181
|
||||
|
||||
### Files excluded by `.gitignore` are now ignored ([#1234](https://github.com/charliermarsh/ruff/pull/1234))
|
||||
### Files excluded by `.gitignore` are now ignored ([#1234](https://github.com/astral-sh/ruff/pull/1234))
|
||||
|
||||
Ruff will now avoid checking files that are excluded by `.ignore`, `.gitignore`,
|
||||
`.git/info/exclude`, and global `gitignore` files. This behavior is powered by the [`ignore`](https://docs.rs/ignore/latest/ignore/struct.WalkBuilder.html#ignore-rules)
|
||||
@@ -207,7 +207,7 @@ default.
|
||||
|
||||
## 0.0.178
|
||||
|
||||
### Configuration files are now resolved hierarchically ([#1190](https://github.com/charliermarsh/ruff/pull/1190))
|
||||
### Configuration files are now resolved hierarchically ([#1190](https://github.com/astral-sh/ruff/pull/1190))
|
||||
|
||||
`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.
|
||||
|
||||
104
CONTRIBUTING.md
104
CONTRIBUTING.md
@@ -21,18 +21,18 @@ Ruff welcomes contributions in the form of Pull Requests.
|
||||
For small changes (e.g., bug fixes), feel free to submit a PR.
|
||||
|
||||
For larger changes (e.g., new lint rules, new functionality, new configuration options), consider
|
||||
creating an [**issue**](https://github.com/charliermarsh/ruff/issues) outlining your proposed
|
||||
change. You can also join us on [**Discord**](https://discord.gg/c9MhzV8aU5) to discuss your idea with
|
||||
the community.
|
||||
creating an [**issue**](https://github.com/astral-sh/ruff/issues) outlining your proposed change.
|
||||
You can also join us on [**Discord**](https://discord.gg/c9MhzV8aU5) to discuss your idea with the
|
||||
community.
|
||||
|
||||
If you're looking for a place to start, we recommend implementing a new lint rule (see:
|
||||
[_Adding a new lint rule_](#example-adding-a-new-lint-rule), which will allow you to learn from and
|
||||
pattern-match against the examples in the existing codebase. Many lint rules are inspired by
|
||||
existing Python plugins, which can be used as a reference implementation.
|
||||
|
||||
As a concrete example: consider taking on one of the rules from the [`flake8-pyi`](https://github.com/charliermarsh/ruff/issues/848)
|
||||
plugin, and looking to the originating [Python source](https://github.com/PyCQA/flake8-pyi)
|
||||
for guidance.
|
||||
As a concrete example: consider taking on one of the rules from the [`flake8-pyi`](https://github.com/astral-sh/ruff/issues/848)
|
||||
plugin, and looking to the originating [Python source](https://github.com/PyCQA/flake8-pyi) for
|
||||
guidance.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
@@ -45,6 +45,12 @@ You'll also need [Insta](https://insta.rs/docs/) to update snapshot tests:
|
||||
cargo install cargo-insta
|
||||
```
|
||||
|
||||
and pre-commit to run some validation checks:
|
||||
|
||||
```shell
|
||||
pipx install pre-commit # or `pip install pre-commit` if you have a virtualenv
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
After cloning the repository, run Ruff locally with:
|
||||
@@ -57,9 +63,9 @@ Prior to opening a pull request, ensure that your code has been auto-formatted,
|
||||
and that it passes both the lint and test validation checks:
|
||||
|
||||
```shell
|
||||
cargo fmt # Auto-formatting...
|
||||
cargo clippy --fix --workspace --all-targets --all-features # Linting...
|
||||
cargo test # Testing...
|
||||
cargo clippy --workspace --all-targets --all-features -- -D warnings # Rust linting
|
||||
RUFF_UPDATE_SCHEMA=1 cargo test # Rust testing and updating ruff.schema.json
|
||||
pre-commit run --all-files --show-diff-on-failure # Rust and Python formatting, Markdown and Python linting, etc.
|
||||
```
|
||||
|
||||
These checks will run on GitHub Actions when you open your Pull Request, but running them locally
|
||||
@@ -72,13 +78,6 @@ after running `cargo test` like so:
|
||||
cargo insta review
|
||||
```
|
||||
|
||||
If you have `pre-commit` [installed](https://pre-commit.com/#installation) then you can use it to
|
||||
assist with formatting and linting. The following command will run the `pre-commit` hooks:
|
||||
|
||||
```shell
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
Your Pull Request will be reviewed by a maintainer, which may involve a few rounds of iteration
|
||||
prior to merging.
|
||||
|
||||
@@ -93,64 +92,89 @@ The vast majority of the code, including all lint rules, lives in the `ruff` cra
|
||||
At time of writing, the repository includes the following crates:
|
||||
|
||||
- `crates/ruff`: library crate containing all lint rules and the core logic for running them.
|
||||
- `crates/ruff_benchmark`: binary crate for running micro-benchmarks.
|
||||
- `crates/ruff_cache`: library crate for caching lint results.
|
||||
- `crates/ruff_cli`: binary crate containing Ruff's command-line interface.
|
||||
- `crates/ruff_dev`: binary crate containing utilities used in the development of Ruff itself (e.g.,
|
||||
`cargo dev generate-all`).
|
||||
- `crates/ruff_diagnostics`: library crate for the lint diagnostics APIs.
|
||||
- `crates/ruff_formatter`: library crate for generic code formatting logic based on an intermediate
|
||||
representation.
|
||||
- `crates/ruff_index`: library crate inspired by `rustc_index`.
|
||||
- `crates/ruff_macros`: library crate containing macros used by Ruff.
|
||||
- `crates/ruff_python`: library crate implementing Python-specific functionality (e.g., lists of
|
||||
standard library modules by version).
|
||||
- `crates/flake8_to_ruff`: binary crate for generating Ruff configuration from Flake8 configuration.
|
||||
- `crates/ruff_python_ast`: library crate containing Python-specific AST types and utilities.
|
||||
- `crates/ruff_python_formatter`: library crate containing Python-specific code formatting logic.
|
||||
- `crates/ruff_python_semantic`: library crate containing Python-specific semantic analysis logic,
|
||||
including Ruff's semantic model.
|
||||
- `crates/ruff_python_stdlib`: library crate containing Python-specific standard library data.
|
||||
- `crates/ruff_python_whitespace`: library crate containing Python-specific whitespace analysis
|
||||
logic.
|
||||
- `crates/ruff_rustpython`: library crate containing `RustPython`-specific utilities.
|
||||
- `crates/ruff_testing_macros`: library crate containing macros used for testing Ruff.
|
||||
- `crates/ruff_textwrap`: library crate to indent and dedent Python source code.
|
||||
- `crates/ruff_wasm`: library crate for exposing Ruff as a WebAssembly module.
|
||||
|
||||
### Example: Adding a new lint rule
|
||||
|
||||
At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
|
||||
1. Determine a name for the new rule as per our [rule naming convention](#rule-naming-convention).
|
||||
1. Determine a name for the new rule as per our [rule naming convention](#rule-naming-convention)
|
||||
(e.g., `AssertFalse`, as in, "allow `assert False`").
|
||||
|
||||
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
|
||||
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/assert_false.rs`).
|
||||
|
||||
1. In that file, define a violation struct. You can grep for `#[violation]` to see examples.
|
||||
1. In that file, define a violation struct (e.g., `pub struct AssertFalse`). You can grep for
|
||||
`#[violation]` to see examples.
|
||||
|
||||
1. Map the violation struct to a rule code in `crates/ruff/src/codes.rs` (e.g., `E402`).
|
||||
1. In that file, define a function that adds the violation to the diagnostic list as appropriate
|
||||
(e.g., `pub(crate) fn assert_false`) based on whatever inputs are required for the rule (e.g.,
|
||||
an `ast::StmtAssert` node).
|
||||
|
||||
1. Define the logic for triggering the violation in `crates/ruff/src/checkers/ast/mod.rs` (for
|
||||
AST-based checks), `crates/ruff/src/checkers/tokens.rs` (for token-based checks),
|
||||
`crates/ruff/src/checkers/lines.rs` (for text-based checks), or
|
||||
`crates/ruff/src/checkers/filesystem.rs` (for filesystem-based checks).
|
||||
|
||||
1. Map the violation struct to a rule code in `crates/ruff/src/codes.rs` (e.g., `B011`).
|
||||
|
||||
1. Add proper [testing](#rule-testing-fixtures-and-snapshots) for your rule.
|
||||
|
||||
1. Update the generated files (documentation and generated code).
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
collecting diagnostics as it goes.
|
||||
To trigger the violation, you'll likely want to augment the logic in `crates/ruff/src/checkers/ast.rs`
|
||||
to call your new function at the appropriate time and with the appropriate inputs. The `Checker`
|
||||
defined therein is a Python AST visitor, which iterates over the AST, building up a semantic model,
|
||||
and calling out to lint rule analyzer functions as it goes.
|
||||
|
||||
If you need to inspect the AST, you can run `cargo dev print-ast` with a Python file. Grep
|
||||
for the `Check::new` invocations to understand how other, similar rules are implemented.
|
||||
for the `Diagnostic::new` invocations to understand how other, similar rules are implemented.
|
||||
|
||||
Once you're satisfied with your code, add tests for your rule. See [rule testing](#rule-testing-fixtures-and-snapshots)
|
||||
for more details.
|
||||
|
||||
Finally, regenerate the documentation and generated code with `cargo dev generate-all`.
|
||||
Finally, regenerate the documentation and other generated assets (like our JSON Schema) with:
|
||||
`cargo dev generate-all`.
|
||||
|
||||
#### Rule naming convention
|
||||
|
||||
The rule name should make sense when read as "allow _rule-name_" or "allow _rule-name_ items".
|
||||
Like Clippy, Ruff's rule names should make grammatical and logical sense when read as "allow
|
||||
${rule}" or "allow ${rule} items", as in the context of suppression comments.
|
||||
|
||||
This implies that rule names:
|
||||
For example, `AssertFalse` fits this convention: it flags `assert False` statements, and so a
|
||||
suppression comment would be framed as "allow `assert False`".
|
||||
|
||||
- should state the bad thing being checked for
|
||||
As such, rule names should...
|
||||
|
||||
- should not contain instructions on what you should use instead
|
||||
(these belong in the rule documentation and the `autofix_title` for rules that have autofix)
|
||||
- Highlight the pattern that is being linted against, rather than the preferred alternative.
|
||||
For example, `AssertFalse` guards against `assert False` statements.
|
||||
|
||||
When re-implementing rules from other linters, this convention is given more importance than
|
||||
- _Not_ contain instructions on how to fix the violation, which instead belong in the rule
|
||||
documentation and the `autofix_title`.
|
||||
|
||||
- _Not_ contain a redundant prefix, like `Disallow` or `Banned`, which are already implied by the
|
||||
convention.
|
||||
|
||||
When re-implementing rules from other linters, we prioritize adhering to this convention over
|
||||
preserving the original rule name.
|
||||
|
||||
#### Rule testing: fixtures and snapshots
|
||||
@@ -258,7 +282,7 @@ 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](https://github.com/charliermarsh/ruff/blob/main/scripts/Dockerfile.ecosystem).
|
||||
as `github_search.jsonl` and following the instructions in [scripts/Dockerfile.ecosystem](https://github.com/astral-sh/ruff/blob/main/scripts/Dockerfile.ecosystem).
|
||||
Note that this check will take a while to run.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
178
Cargo.lock
generated
178
Cargo.lock
generated
@@ -171,6 +171,12 @@ version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
@@ -256,7 +262,8 @@ dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"time",
|
||||
"serde",
|
||||
"time 0.1.45",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
@@ -555,6 +562,41 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
@@ -691,7 +733,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.271"
|
||||
version = "0.0.272"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -814,6 +856,12 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hexf-parse"
|
||||
version = "0.2.1"
|
||||
@@ -843,6 +891,12 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
@@ -1402,6 +1456,7 @@ version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
@@ -1425,6 +1480,19 @@ dependencies = [
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.1"
|
||||
@@ -1725,7 +1793,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.271"
|
||||
version = "0.0.272"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1752,6 +1820,7 @@ dependencies = [
|
||||
"path-absolutize",
|
||||
"pathdiff",
|
||||
"pep440_rs",
|
||||
"phf",
|
||||
"pretty_assertions",
|
||||
"pyproject-toml",
|
||||
"quick-junit",
|
||||
@@ -1760,10 +1829,10 @@ dependencies = [
|
||||
"ruff_cache",
|
||||
"ruff_diagnostics",
|
||||
"ruff_macros",
|
||||
"ruff_newlines",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_semantic",
|
||||
"ruff_python_stdlib",
|
||||
"ruff_python_whitespace",
|
||||
"ruff_rustpython",
|
||||
"ruff_text_size",
|
||||
"ruff_textwrap",
|
||||
@@ -1774,6 +1843,7 @@ dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"shellexpand",
|
||||
"similar",
|
||||
"smallvec",
|
||||
@@ -1810,6 +1880,7 @@ name = "ruff_cache"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"glob",
|
||||
"globset",
|
||||
"itertools",
|
||||
"regex",
|
||||
@@ -1818,7 +1889,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.271"
|
||||
version = "0.0.272"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1837,6 +1908,7 @@ dependencies = [
|
||||
"glob",
|
||||
"ignore",
|
||||
"itertools",
|
||||
"itoa",
|
||||
"log",
|
||||
"mimalloc",
|
||||
"notify",
|
||||
@@ -1871,17 +1943,20 @@ dependencies = [
|
||||
"clap",
|
||||
"itertools",
|
||||
"libcst",
|
||||
"log",
|
||||
"once_cell",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"ruff",
|
||||
"ruff_cli",
|
||||
"ruff_diagnostics",
|
||||
"ruff_python_formatter",
|
||||
"ruff_textwrap",
|
||||
"rustpython-format",
|
||||
"rustpython-parser",
|
||||
"schemars",
|
||||
"serde_json",
|
||||
"similar",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
]
|
||||
@@ -1930,14 +2005,6 @@ dependencies = [
|
||||
"syn 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_newlines"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"ruff_text_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_ast"
|
||||
version = "0.0.0"
|
||||
@@ -1952,7 +2019,7 @@ dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"ruff_newlines",
|
||||
"ruff_python_whitespace",
|
||||
"ruff_text_size",
|
||||
"rustc-hash",
|
||||
"rustpython-ast",
|
||||
@@ -1974,8 +2041,8 @@ dependencies = [
|
||||
"itertools",
|
||||
"once_cell",
|
||||
"ruff_formatter",
|
||||
"ruff_newlines",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_whitespace",
|
||||
"ruff_testing_macros",
|
||||
"ruff_text_size",
|
||||
"rustc-hash",
|
||||
@@ -2009,6 +2076,14 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_whitespace"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"ruff_text_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_rustpython"
|
||||
version = "0.0.0"
|
||||
@@ -2030,7 +2105,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruff_text_size"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd"
|
||||
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=8d74eee75031b68d2204219963fae54a3f31a394#8d74eee75031b68d2204219963fae54a3f31a394"
|
||||
dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -2040,7 +2115,7 @@ dependencies = [
|
||||
name = "ruff_textwrap"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"ruff_newlines",
|
||||
"ruff_python_whitespace",
|
||||
"ruff_text_size",
|
||||
]
|
||||
|
||||
@@ -2108,7 +2183,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd"
|
||||
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=8d74eee75031b68d2204219963fae54a3f31a394#8d74eee75031b68d2204219963fae54a3f31a394"
|
||||
dependencies = [
|
||||
"is-macro",
|
||||
"num-bigint",
|
||||
@@ -2119,7 +2194,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-format"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd"
|
||||
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=8d74eee75031b68d2204219963fae54a3f31a394#8d74eee75031b68d2204219963fae54a3f31a394"
|
||||
dependencies = [
|
||||
"bitflags 2.3.1",
|
||||
"itertools",
|
||||
@@ -2131,7 +2206,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-literal"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd"
|
||||
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=8d74eee75031b68d2204219963fae54a3f31a394#8d74eee75031b68d2204219963fae54a3f31a394"
|
||||
dependencies = [
|
||||
"hexf-parse",
|
||||
"is-macro",
|
||||
@@ -2143,7 +2218,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd"
|
||||
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=8d74eee75031b68d2204219963fae54a3f31a394#8d74eee75031b68d2204219963fae54a3f31a394"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"is-macro",
|
||||
@@ -2166,9 +2241,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd"
|
||||
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=8d74eee75031b68d2204219963fae54a3f31a394#8d74eee75031b68d2204219963fae54a3f31a394"
|
||||
dependencies = [
|
||||
"is-macro",
|
||||
"memchr",
|
||||
"ruff_text_size",
|
||||
]
|
||||
|
||||
@@ -2293,7 +2369,6 @@ version = "1.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
@@ -2308,6 +2383,34 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513"
|
||||
dependencies = [
|
||||
"base64 0.21.2",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with_macros",
|
||||
"time 0.3.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shellexpand"
|
||||
version = "3.1.0"
|
||||
@@ -2534,6 +2637,33 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
@@ -2752,7 +2882,7 @@ version = "2.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.13.1",
|
||||
"flate2",
|
||||
"log",
|
||||
"once_cell",
|
||||
|
||||
22
Cargo.toml
22
Cargo.toml
@@ -1,13 +1,15 @@
|
||||
[workspace]
|
||||
members = ["crates/*"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
homepage = "https://beta.ruff.rs/docs/"
|
||||
documentation = "https://beta.ruff.rs/docs/"
|
||||
repository = "https://github.com/charliermarsh/ruff"
|
||||
homepage = "https://beta.ruff.rs/docs"
|
||||
documentation = "https://beta.ruff.rs/docs"
|
||||
repository = "https://github.com/astral-sh/ruff"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
license = "MIT"
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = { version = "1.0.69" }
|
||||
@@ -34,16 +36,16 @@ proc-macro2 = { version = "1.0.51" }
|
||||
quote = { version = "1.0.23" }
|
||||
regex = { version = "1.7.1" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" }
|
||||
rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd", default-features = false, features = ["all-nodes-with-ranges"]}
|
||||
rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" }
|
||||
rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" }
|
||||
rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd", default-features = false, features = ["full-lexer", "all-nodes-with-ranges"] }
|
||||
ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "8d74eee75031b68d2204219963fae54a3f31a394" }
|
||||
rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "8d74eee75031b68d2204219963fae54a3f31a394" , default-features = false, features = ["all-nodes-with-ranges", "num-bigint"]}
|
||||
rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "8d74eee75031b68d2204219963fae54a3f31a394", default-features = false, features = ["num-bigint"] }
|
||||
rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "8d74eee75031b68d2204219963fae54a3f31a394" }
|
||||
rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "8d74eee75031b68d2204219963fae54a3f31a394" , default-features = false, features = ["full-lexer", "all-nodes-with-ranges", "num-bigint"] }
|
||||
schemars = { version = "0.8.12" }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = { version = "1.0.93", features = ["preserve_order"] }
|
||||
serde_json = { version = "1.0.93" }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
similar = { version = "2.2.1" }
|
||||
similar = { version = "2.2.1", features = ["inline"] }
|
||||
smallvec = { version = "1.10.0" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
|
||||
48
LICENSE
48
LICENSE
@@ -354,6 +354,29 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-slots, licensed as follows:
|
||||
"""
|
||||
Copyright (c) 2021 Dominic Davis-Foster
|
||||
|
||||
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-todos, licensed as follows:
|
||||
"""
|
||||
Copyright (c) 2019 EclecticIQ. All rights reserved.
|
||||
@@ -1176,6 +1199,31 @@ are:
|
||||
|
||||
- flake8-django, licensed under the GPL license.
|
||||
|
||||
- perflint, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Anthony Shaw
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
- rust-analyzer/text-size, licensed under the MIT license:
|
||||
"""
|
||||
Permission is hereby granted, free of charge, to any
|
||||
|
||||
26
README.md
26
README.md
@@ -2,11 +2,11 @@
|
||||
|
||||
# Ruff
|
||||
|
||||
[](https://github.com/charliermarsh/ruff)
|
||||
[](https://github.com/astral-sh/ruff)
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
[](https://github.com/charliermarsh/ruff/actions)
|
||||
[](https://github.com/astral-sh/ruff/actions)
|
||||
|
||||
[**Discord**](https://discord.gg/c9MhzV8aU5) | [**Docs**](https://beta.ruff.rs/docs/) | [**Playground**](https://play.ruff.rs/)
|
||||
|
||||
@@ -88,7 +88,7 @@ creator of [isort](https://github.com/PyCQA/isort):
|
||||
> Just switched my first project to Ruff. Only one downside so far: it's so fast I couldn't believe
|
||||
> it was working till I intentionally introduced some errors.
|
||||
|
||||
[**Tim Abbott**](https://github.com/charliermarsh/ruff/issues/465#issuecomment-1317400028), lead
|
||||
[**Tim Abbott**](https://github.com/astral-sh/ruff/issues/465#issuecomment-1317400028), lead
|
||||
developer of [Zulip](https://github.com/zulip/zulip):
|
||||
|
||||
> This is just ridiculously fast... `ruff` is amazing.
|
||||
@@ -139,7 +139,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.0.271
|
||||
rev: v0.0.272
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -254,13 +254,14 @@ quality tools, including:
|
||||
- [flake8-2020](https://pypi.org/project/flake8-2020/)
|
||||
- [flake8-annotations](https://pypi.org/project/flake8-annotations/)
|
||||
- [flake8-async](https://pypi.org/project/flake8-async)
|
||||
- [flake8-bandit](https://pypi.org/project/flake8-bandit/) ([#1646](https://github.com/charliermarsh/ruff/issues/1646))
|
||||
- [flake8-bandit](https://pypi.org/project/flake8-bandit/) ([#1646](https://github.com/astral-sh/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-copyright](https://pypi.org/project/flake8-copyright/)
|
||||
- [flake8-datetimez](https://pypi.org/project/flake8-datetimez/)
|
||||
- [flake8-debugger](https://pypi.org/project/flake8-debugger/)
|
||||
- [flake8-django](https://pypi.org/project/flake8-django/)
|
||||
@@ -283,12 +284,13 @@ quality tools, including:
|
||||
- [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-slots](https://pypi.org/project/flake8-slots/)
|
||||
- [flake8-super](https://pypi.org/project/flake8-super/)
|
||||
- [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/)
|
||||
- [flake8-todos](https://pypi.org/project/flake8-todos/)
|
||||
- [flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
|
||||
- [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/)
|
||||
- [flynt](https://pypi.org/project/flynt/) ([#2102](https://github.com/charliermarsh/ruff/issues/2102))
|
||||
- [flynt](https://pypi.org/project/flynt/) ([#2102](https://github.com/astral-sh/ruff/issues/2102))
|
||||
- [isort](https://pypi.org/project/isort/)
|
||||
- [mccabe](https://pypi.org/project/mccabe/)
|
||||
- [pandas-vet](https://pypi.org/project/pandas-vet/)
|
||||
@@ -311,8 +313,8 @@ You can also join us on [**Discord**](https://discord.gg/c9MhzV8aU5).
|
||||
|
||||
## Support
|
||||
|
||||
Having trouble? Check out the existing issues on [**GitHub**](https://github.com/charliermarsh/ruff/issues),
|
||||
or feel free to [**open a new one**](https://github.com/charliermarsh/ruff/issues/new).
|
||||
Having trouble? Check out the existing issues on [**GitHub**](https://github.com/astral-sh/ruff/issues),
|
||||
or feel free to [**open a new one**](https://github.com/astral-sh/ruff/issues/new).
|
||||
|
||||
You can also ask for help on [**Discord**](https://discord.gg/c9MhzV8aU5).
|
||||
|
||||
@@ -334,7 +336,7 @@ and again draws on both the APIs and implementation details of [Rome](https://gi
|
||||
Ruff is also influenced by a number of tools outside the Python ecosystem, like
|
||||
[Clippy](https://github.com/rust-lang/rust-clippy) and [ESLint](https://github.com/eslint/eslint).
|
||||
|
||||
Ruff is the beneficiary of a large number of [contributors](https://github.com/charliermarsh/ruff/graphs/contributors).
|
||||
Ruff is the beneficiary of a large number of [contributors](https://github.com/astral-sh/ruff/graphs/contributors).
|
||||
|
||||
Ruff is released under the MIT license.
|
||||
|
||||
@@ -413,21 +415,21 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
If you're using Ruff, consider adding the Ruff badge to project's `README.md`:
|
||||
|
||||
```md
|
||||
[](https://github.com/charliermarsh/ruff)
|
||||
[](https://github.com/astral-sh/ruff)
|
||||
```
|
||||
|
||||
...or `README.rst`:
|
||||
|
||||
```rst
|
||||
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
|
||||
:target: https://github.com/charliermarsh/ruff
|
||||
:target: https://github.com/astral-sh/ruff
|
||||
:alt: Ruff
|
||||
```
|
||||
|
||||
...or, as HTML:
|
||||
|
||||
```html
|
||||
<a href="https://github.com/charliermarsh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json" alt="Ruff" style="max-width:100%;"></a>
|
||||
<a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json" alt="Ruff" style="max-width:100%;"></a>
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
@@ -8,3 +8,4 @@ whos = "whos"
|
||||
spawnve = "spawnve"
|
||||
ned = "ned"
|
||||
poit = "poit"
|
||||
BA = "BA" # acronym for "Bad Allowed", used in testing.
|
||||
|
||||
BIN
assets/png/Astral.png
Normal file
BIN
assets/png/Astral.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -1,8 +1,16 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.271"
|
||||
version = "0.0.272"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
homepage = { workspace = true }
|
||||
documentation = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
license = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
ruff = { path = "../ruff", default-features = false }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# flake8-to-ruff
|
||||
|
||||
Convert existing Flake8 configuration files (`setup.cfg`, `tox.ini`, or `.flake8`) for use with
|
||||
[Ruff](https://github.com/charliermarsh/ruff).
|
||||
[Ruff](https://github.com/astral-sh/ruff).
|
||||
|
||||
Generates a Ruff-compatible `pyproject.toml` section.
|
||||
|
||||
@@ -96,4 +96,4 @@ MIT
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome and hugely appreciated. To get started, check out the
|
||||
[contributing guidelines](https://github.com/charliermarsh/ruff/blob/main/CONTRIBUTING.md).
|
||||
[contributing guidelines](https://github.com/astral-sh/ruff/blob/main/CONTRIBUTING.md).
|
||||
|
||||
@@ -23,7 +23,7 @@ description = "Convert existing Flake8 configuration to Ruff."
|
||||
requires-python = ">=3.7"
|
||||
|
||||
[project.urls]
|
||||
repository = "https://github.com/charliermarsh/ruff#subdirectory=crates/flake8_to_ruff"
|
||||
repository = "https://github.com/astral-sh/ruff#subdirectory=crates/flake8_to_ruff"
|
||||
|
||||
[build-system]
|
||||
requires = ["maturin>=1.0,<2.0"]
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.271"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
documentation.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
version = "0.0.272"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
homepage = { workspace = true }
|
||||
documentation = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
license = { workspace = true }
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
|
||||
[lib]
|
||||
name = "ruff"
|
||||
@@ -17,7 +18,7 @@ name = "ruff"
|
||||
ruff_cache = { path = "../ruff_cache" }
|
||||
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
|
||||
ruff_macros = { path = "../ruff_macros" }
|
||||
ruff_newlines = { path = "../ruff_newlines" }
|
||||
ruff_python_whitespace = { path = "../ruff_python_whitespace" }
|
||||
ruff_python_ast = { path = "../ruff_python_ast", features = ["serde"] }
|
||||
ruff_python_semantic = { path = "../ruff_python_semantic" }
|
||||
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
|
||||
@@ -52,6 +53,7 @@ path-absolutize = { workspace = true, features = [
|
||||
] }
|
||||
pathdiff = { version = "0.2.1" }
|
||||
pep440_rs = { version = "0.3.1", features = ["serde"] }
|
||||
phf = { version = "0.11", features = ["macros"] }
|
||||
pyproject-toml = { version = "0.6.0" }
|
||||
quick-junit = { version = "0.3.2" }
|
||||
regex = { workspace = true }
|
||||
@@ -63,7 +65,8 @@ schemars = { workspace = true, optional = true }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
similar = { workspace = true, features = ["inline"] }
|
||||
serde_with = { version = "3.0.0" }
|
||||
similar = { workspace = true }
|
||||
shellexpand = { workspace = true }
|
||||
smallvec = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
|
||||
@@ -149,7 +149,7 @@ for group in groupby(items, key=lambda p: p[1]):
|
||||
collect_shop_items("Joe", group[1])
|
||||
|
||||
|
||||
# https://github.com/charliermarsh/ruff/issues/4050
|
||||
# https://github.com/astral-sh/ruff/issues/4050
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
if _section == "greens":
|
||||
for item in section_items:
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
from itertools import count, cycle, repeat
|
||||
|
||||
# Errors
|
||||
zip()
|
||||
zip(range(3))
|
||||
zip("a", "b")
|
||||
@@ -5,6 +8,18 @@ zip("a", "b", *zip("c"))
|
||||
zip(zip("a"), strict=False)
|
||||
zip(zip("a", strict=True))
|
||||
|
||||
# OK
|
||||
zip(range(3), strict=True)
|
||||
zip("a", "b", strict=False)
|
||||
zip("a", "b", "c", strict=True)
|
||||
|
||||
# OK (infinite iterators).
|
||||
zip([1, 2, 3], cycle("ABCDEF"))
|
||||
zip([1, 2, 3], count())
|
||||
zip([1, 2, 3], repeat(1))
|
||||
zip([1, 2, 3], repeat(1, None))
|
||||
zip([1, 2, 3], repeat(1, times=None))
|
||||
|
||||
# Errors (limited iterators).
|
||||
zip([1, 2, 3], repeat(1, 1))
|
||||
zip([1, 2, 3], repeat(1, times=4))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class MyClass:
|
||||
ImportError = 4
|
||||
id = 5
|
||||
id: int
|
||||
dir = "/"
|
||||
|
||||
def __init__(self):
|
||||
@@ -10,3 +10,10 @@ class MyClass:
|
||||
|
||||
def str(self):
|
||||
pass
|
||||
|
||||
|
||||
from typing import TypedDict
|
||||
|
||||
|
||||
class MyClass(TypedDict):
|
||||
id: int
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
x = set(x for x in range(3))
|
||||
x = set(
|
||||
x for x in range(3)
|
||||
)
|
||||
y = f'{set(a if a < 6 else 0 for a in range(3))}'
|
||||
_ = '{}'.format(set(a if a < 6 else 0 for a in range(3)))
|
||||
print(f'Hello {set(a for a in range(3))} World')
|
||||
|
||||
def set(*args, **kwargs):
|
||||
return None
|
||||
x = set(x for x in range(3))
|
||||
y = f"{set(a if a < 6 else 0 for a in range(3))}"
|
||||
_ = "{}".format(set(a if a < 6 else 0 for a in range(3)))
|
||||
print(f"Hello {set(a for a in range(3))} World")
|
||||
|
||||
|
||||
set(x for x in range(3))
|
||||
def f(x):
|
||||
return x
|
||||
|
||||
|
||||
print(f'Hello {set(a for a in "abc")} World')
|
||||
print(f"Hello {set(a for a in 'abc')} World")
|
||||
print(f"Hello {set(f(a) for a in 'abc')} World")
|
||||
print(f"{set(a for a in 'abc') - set(a for a in 'ab')}")
|
||||
print(f"{ set(a for a in 'abc') - set(a for a in 'ab') }")
|
||||
|
||||
# The fix generated for this diagnostic is incorrect, as we add additional space
|
||||
# around the set comprehension.
|
||||
print(f"{ {set(a for a in 'abc')} }")
|
||||
|
||||
@@ -5,3 +5,14 @@ dict(
|
||||
dict(((x, x) for x in range(3)), z=3)
|
||||
y = f'{dict((x, x) for x in range(3))}'
|
||||
print(f'Hello {dict((x, x) for x in range(3))} World')
|
||||
print(f"Hello {dict((x, x) for x in 'abc')} World")
|
||||
print(f'Hello {dict((x, x) for x in "abc")} World')
|
||||
print(f'Hello {dict((x,x) for x in "abc")} World')
|
||||
|
||||
f'{dict((x, x) for x in range(3)) | dict((x, x) for x in range(3))}'
|
||||
f'{ dict((x, x) for x in range(3)) | dict((x, x) for x in range(3)) }'
|
||||
|
||||
def f(x):
|
||||
return x
|
||||
|
||||
print(f'Hello {dict((x,f(x)) for x in "abc")} World')
|
||||
|
||||
@@ -2,3 +2,14 @@ s = set([x for x in range(3)])
|
||||
s = set(
|
||||
[x for x in range(3)]
|
||||
)
|
||||
|
||||
s = f"{set([x for x in 'ab'])}"
|
||||
s = f'{set([x for x in "ab"])}'
|
||||
|
||||
def f(x):
|
||||
return x
|
||||
|
||||
s = f"{set([f(x) for x in 'ab'])}"
|
||||
|
||||
s = f"{ set([x for x in 'ab']) | set([x for x in 'ab']) }"
|
||||
s = f"{set([x for x in 'ab']) | set([x for x in 'ab'])}"
|
||||
|
||||
@@ -1,2 +1,13 @@
|
||||
dict([(i, i) for i in range(3)])
|
||||
dict([(i, i) for i in range(3)], z=4)
|
||||
|
||||
def f(x):
|
||||
return x
|
||||
|
||||
f'{dict([(s,s) for s in "ab"])}'
|
||||
f"{dict([(s,s) for s in 'ab'])}"
|
||||
f"{dict([(s, s) for s in 'ab'])}"
|
||||
f"{dict([(s,f(s)) for s in 'ab'])}"
|
||||
|
||||
f'{dict([(s,s) for s in "ab"]) | dict([(s,s) for s in "ab"])}'
|
||||
f'{ dict([(s,s) for s in "ab"]) | dict([(s,s) for s in "ab"]) }'
|
||||
|
||||
@@ -16,3 +16,11 @@ set(
|
||||
set(
|
||||
[1,]
|
||||
)
|
||||
f"{set([1,2,3])}"
|
||||
f"{set(['a', 'b'])}"
|
||||
f'{set(["a", "b"])}'
|
||||
|
||||
f"{set(['a', 'b']) - set(['a'])}"
|
||||
f"{ set(['a', 'b']) - set(['a']) }"
|
||||
f"a {set(['a', 'b']) - set(['a'])} b"
|
||||
f"a { set(['a', 'b']) - set(['a']) } b"
|
||||
|
||||
@@ -10,3 +10,13 @@ def list():
|
||||
|
||||
|
||||
a = list()
|
||||
|
||||
f"{dict(x='y')}"
|
||||
f'{dict(x="y")}'
|
||||
f"{dict()}"
|
||||
f"a {dict()} b"
|
||||
|
||||
f"{dict(x='y') | dict(y='z')}"
|
||||
f"{ dict(x='y') | dict(y='z') }"
|
||||
f"a {dict(x='y') | dict(y='z')} b"
|
||||
f"a { dict(x='y') | dict(y='z') } b"
|
||||
|
||||
@@ -34,3 +34,19 @@ _ = (
|
||||
b"abc"
|
||||
b"def"
|
||||
)
|
||||
|
||||
_ = """a""" """b"""
|
||||
|
||||
_ = """a
|
||||
b""" """c
|
||||
d"""
|
||||
|
||||
_ = f"""a""" f"""b"""
|
||||
|
||||
_ = f"a" "b"
|
||||
|
||||
_ = """a""" "b"
|
||||
|
||||
_ = 'a' "b"
|
||||
|
||||
_ = rf"a" rf"b"
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
from collections.abc import Set as AbstractSet # Ok
|
||||
def f():
|
||||
from collections.abc import Set as AbstractSet # Ok
|
||||
|
||||
|
||||
from collections.abc import Set # Ok
|
||||
def f():
|
||||
from collections.abc import Container, Sized, Set as AbstractSet, ValuesView # Ok
|
||||
|
||||
|
||||
from collections.abc import (
|
||||
Container,
|
||||
Sized,
|
||||
Set, # Ok
|
||||
ValuesView
|
||||
)
|
||||
def f():
|
||||
from collections.abc import Set # PYI025
|
||||
|
||||
from collections.abc import (
|
||||
Container,
|
||||
Sized,
|
||||
Set as AbstractSet, # Ok
|
||||
ValuesView
|
||||
)
|
||||
|
||||
def f():
|
||||
from collections.abc import Container, Sized, Set, ValuesView # PYI025
|
||||
|
||||
GLOBAL: Set[int] = set()
|
||||
|
||||
class Class:
|
||||
member: Set[int]
|
||||
|
||||
@@ -1,19 +1,50 @@
|
||||
from collections.abc import Set as AbstractSet # Ok
|
||||
def f():
|
||||
from collections.abc import Set as AbstractSet # Ok
|
||||
|
||||
def f():
|
||||
from collections.abc import Container, Sized, Set as AbstractSet, ValuesView # Ok
|
||||
|
||||
from collections.abc import Set # PYI025
|
||||
def f():
|
||||
from collections.abc import Set # PYI025
|
||||
|
||||
def f():
|
||||
from collections.abc import Container, Sized, Set, ValuesView # PYI025
|
||||
|
||||
from collections.abc import (
|
||||
Container,
|
||||
Sized,
|
||||
Set, # PYI025
|
||||
ValuesView
|
||||
)
|
||||
def f():
|
||||
"""Test: local symbol renaming."""
|
||||
if True:
|
||||
from collections.abc import Set
|
||||
else:
|
||||
Set = 1
|
||||
|
||||
from collections.abc import (
|
||||
Container,
|
||||
Sized,
|
||||
Set as AbstractSet,
|
||||
ValuesView # Ok
|
||||
)
|
||||
x: Set = set()
|
||||
|
||||
x: Set
|
||||
|
||||
del Set
|
||||
|
||||
def f():
|
||||
print(Set)
|
||||
|
||||
def Set():
|
||||
pass
|
||||
print(Set)
|
||||
|
||||
from collections.abc import Set
|
||||
|
||||
def f():
|
||||
"""Test: global symbol renaming."""
|
||||
global Set
|
||||
|
||||
Set = 1
|
||||
print(Set)
|
||||
|
||||
def f():
|
||||
"""Test: nonlocal symbol renaming."""
|
||||
from collections.abc import Set
|
||||
|
||||
def g():
|
||||
nonlocal Set
|
||||
|
||||
Set = 1
|
||||
print(Set)
|
||||
|
||||
7
crates/ruff/resources/test/fixtures/flake8_pyi/PYI044.py
vendored
Normal file
7
crates/ruff/resources/test/fixtures/flake8_pyi/PYI044.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Bad import.
|
||||
from __future__ import annotations # Not PYI044 (not a stubfile).
|
||||
|
||||
# Good imports.
|
||||
from __future__ import Something
|
||||
import sys
|
||||
from socket import AF_INET
|
||||
7
crates/ruff/resources/test/fixtures/flake8_pyi/PYI044.pyi
vendored
Normal file
7
crates/ruff/resources/test/fixtures/flake8_pyi/PYI044.pyi
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Bad import.
|
||||
from __future__ import annotations # PYI044.
|
||||
|
||||
# Good imports.
|
||||
from __future__ import Something
|
||||
import sys
|
||||
from socket import AF_INET
|
||||
32
crates/ruff/resources/test/fixtures/flake8_pyi/PYI050.py
vendored
Normal file
32
crates/ruff/resources/test/fixtures/flake8_pyi/PYI050.py
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
from typing import NoReturn, Never
|
||||
import typing_extensions
|
||||
|
||||
|
||||
def foo(arg):
|
||||
...
|
||||
|
||||
|
||||
def foo_int(arg: int):
|
||||
...
|
||||
|
||||
|
||||
def foo_no_return(arg: NoReturn):
|
||||
...
|
||||
|
||||
|
||||
def foo_no_return_typing_extensions(
|
||||
arg: typing_extensions.NoReturn,
|
||||
):
|
||||
...
|
||||
|
||||
|
||||
def foo_no_return_kwarg(arg: int, *, arg2: NoReturn):
|
||||
...
|
||||
|
||||
|
||||
def foo_no_return_pos_only(arg: int, /, arg2: NoReturn):
|
||||
...
|
||||
|
||||
|
||||
def foo_never(arg: Never):
|
||||
...
|
||||
12
crates/ruff/resources/test/fixtures/flake8_pyi/PYI050.pyi
vendored
Normal file
12
crates/ruff/resources/test/fixtures/flake8_pyi/PYI050.pyi
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
from typing import NoReturn, Never
|
||||
import typing_extensions
|
||||
|
||||
def foo(arg): ...
|
||||
def foo_int(arg: int): ...
|
||||
def foo_no_return(arg: NoReturn): ... # Error: PYI050
|
||||
def foo_no_return_typing_extensions(
|
||||
arg: typing_extensions.NoReturn,
|
||||
): ... # Error: PYI050
|
||||
def foo_no_return_kwarg(arg: int, *, arg2: NoReturn): ... # Error: PYI050
|
||||
def foo_no_return_pos_only(arg: int, /, arg2: NoReturn): ... # Error: PYI050
|
||||
def foo_never(arg: Never): ...
|
||||
@@ -1,17 +1,25 @@
|
||||
import pytest
|
||||
|
||||
|
||||
def test_xxx():
|
||||
pytest.fail("this is a failure") # Test OK arg
|
||||
# OK
|
||||
def f():
|
||||
pytest.fail("this is a failure")
|
||||
|
||||
|
||||
def test_xxx():
|
||||
pytest.fail(msg="this is a failure") # Test OK kwarg
|
||||
def f():
|
||||
pytest.fail(msg="this is a failure")
|
||||
|
||||
|
||||
def test_xxx(): # Error
|
||||
def f():
|
||||
pytest.fail(reason="this is a failure")
|
||||
|
||||
|
||||
# Errors
|
||||
def f():
|
||||
pytest.fail()
|
||||
pytest.fail("")
|
||||
pytest.fail(f"")
|
||||
pytest.fail(msg="")
|
||||
pytest.fail(msg=f"")
|
||||
pytest.fail(reason="")
|
||||
pytest.fail(reason=f"")
|
||||
|
||||
@@ -79,7 +79,7 @@ def x():
|
||||
return a
|
||||
|
||||
|
||||
# ignore unpacking
|
||||
# Ignore unpacking
|
||||
def x():
|
||||
b, a = [1, 2]
|
||||
return a
|
||||
@@ -109,7 +109,8 @@ def x():
|
||||
|
||||
# Considered OK, since functions can have side effects.
|
||||
def x():
|
||||
b, a = 1, 2
|
||||
a = 1
|
||||
b = 2
|
||||
print(b)
|
||||
return a
|
||||
|
||||
@@ -276,20 +277,25 @@ def str_to_bool(val):
|
||||
|
||||
# Mixed assignments
|
||||
def function_assignment(x):
|
||||
def f(): ...
|
||||
def f():
|
||||
...
|
||||
|
||||
return f
|
||||
|
||||
|
||||
def class_assignment(x):
|
||||
class Foo: ...
|
||||
class Foo:
|
||||
...
|
||||
|
||||
return Foo
|
||||
|
||||
|
||||
def mixed_function_assignment(x):
|
||||
if x:
|
||||
def f(): ...
|
||||
|
||||
def f():
|
||||
...
|
||||
|
||||
else:
|
||||
f = 42
|
||||
|
||||
@@ -298,8 +304,56 @@ def mixed_function_assignment(x):
|
||||
|
||||
def mixed_class_assignment(x):
|
||||
if x:
|
||||
class Foo: ...
|
||||
|
||||
class Foo:
|
||||
...
|
||||
|
||||
else:
|
||||
Foo = 42
|
||||
|
||||
return Foo
|
||||
|
||||
|
||||
# `with` statements
|
||||
def foo():
|
||||
with open("foo.txt", "r") as f:
|
||||
x = f.read()
|
||||
return x # RET504
|
||||
|
||||
|
||||
def foo():
|
||||
with open("foo.txt", "r") as f:
|
||||
x = f.read()
|
||||
print(x)
|
||||
return x
|
||||
|
||||
|
||||
def foo():
|
||||
with open("foo.txt", "r") as f:
|
||||
x = f.read()
|
||||
print(x)
|
||||
return x
|
||||
|
||||
|
||||
# Autofix cases
|
||||
def foo():
|
||||
a = 1
|
||||
b=a
|
||||
return b # RET504
|
||||
|
||||
|
||||
def foo():
|
||||
a = 1
|
||||
b =a
|
||||
return b # RET504
|
||||
|
||||
|
||||
def foo():
|
||||
a = 1
|
||||
b= a
|
||||
return b # RET504
|
||||
|
||||
|
||||
def foo():
|
||||
a = 1 # Comment
|
||||
return a
|
||||
|
||||
@@ -53,6 +53,9 @@ class Foo(metaclass=BazMeta):
|
||||
def __really_private_func(self, arg):
|
||||
super().__really_private_func(arg)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._private_thing == other._private_thing
|
||||
|
||||
|
||||
foo = Foo()
|
||||
|
||||
|
||||
@@ -171,3 +171,17 @@ def f():
|
||||
if x.isdigit():
|
||||
return True
|
||||
return False
|
||||
|
||||
async def f():
|
||||
# OK
|
||||
for x in iterable:
|
||||
if await check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
async def f():
|
||||
# SIM110
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -33,17 +33,17 @@ with A() as a:
|
||||
print("hello")
|
||||
a()
|
||||
|
||||
# OK
|
||||
# OK, can't merge async with and with.
|
||||
async with A() as a:
|
||||
with B() as b:
|
||||
print("hello")
|
||||
|
||||
# OK
|
||||
# OK, can't merge async with and with.
|
||||
with A() as a:
|
||||
async with B() as b:
|
||||
print("hello")
|
||||
|
||||
# OK
|
||||
# SIM117
|
||||
async with A() as a:
|
||||
async with B() as b:
|
||||
print("hello")
|
||||
@@ -99,4 +99,25 @@ with A("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as a:
|
||||
# SIM117 (not auto-fixable too long)
|
||||
with A("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ890") as a:
|
||||
with B("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as b:
|
||||
print("hello")
|
||||
print("hello")
|
||||
|
||||
# From issue #3025.
|
||||
async def main():
|
||||
async with A() as a: # SIM117.
|
||||
async with B() as b:
|
||||
print("async-inside!")
|
||||
|
||||
return 0
|
||||
|
||||
# OK. Can't merge across different kinds of with statements.
|
||||
with a as a2:
|
||||
async with b as b2:
|
||||
with c as c2:
|
||||
async with d as d2:
|
||||
f(a2, b2, c2, d2)
|
||||
|
||||
# OK. Can't merge across different kinds of with statements.
|
||||
async with b as b2:
|
||||
with c as c2:
|
||||
async with d as d2:
|
||||
f(b2, c2, d2)
|
||||
|
||||
6
crates/ruff/resources/test/fixtures/flake8_slots/SLOT000.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_slots/SLOT000.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
class Bad(str): # SLOT000
|
||||
pass
|
||||
|
||||
|
||||
class Good(str): # Ok
|
||||
__slots__ = ["foo"]
|
||||
21
crates/ruff/resources/test/fixtures/flake8_slots/SLOT001.py
vendored
Normal file
21
crates/ruff/resources/test/fixtures/flake8_slots/SLOT001.py
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
class Bad(tuple): # SLOT001
|
||||
pass
|
||||
|
||||
|
||||
class Good(tuple): # Ok
|
||||
__slots__ = ("foo",)
|
||||
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
class Bad(Tuple): # SLOT001
|
||||
pass
|
||||
|
||||
|
||||
class Bad(Tuple[str, int, float]): # SLOT001
|
||||
pass
|
||||
|
||||
|
||||
class Good(Tuple[str, int, float]): # OK
|
||||
__slots__ = ("foo",)
|
||||
14
crates/ruff/resources/test/fixtures/flake8_slots/SLOT002.py
vendored
Normal file
14
crates/ruff/resources/test/fixtures/flake8_slots/SLOT002.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
from collections import namedtuple
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class Bad(namedtuple("foo", ["str", "int"])): # SLOT002
|
||||
pass
|
||||
|
||||
|
||||
class Good(namedtuple("foo", ["str", "int"])): # OK
|
||||
__slots__ = ("foo",)
|
||||
|
||||
|
||||
class Good(NamedTuple): # Ok
|
||||
pass
|
||||
@@ -1,6 +1,6 @@
|
||||
# TDO003 - accepted
|
||||
# TODO: this comment has a link
|
||||
# https://github.com/charliermarsh/ruff/issues/3870
|
||||
# https://github.com/astral-sh/ruff/issues/3870
|
||||
|
||||
# TODO: this comment has an issue
|
||||
# TDO-3870
|
||||
|
||||
@@ -164,3 +164,11 @@ def f():
|
||||
)
|
||||
|
||||
x: DataFrame = 2
|
||||
|
||||
|
||||
def f():
|
||||
global Member
|
||||
|
||||
from module import Member
|
||||
|
||||
x: Member = 1
|
||||
|
||||
37
crates/ruff/resources/test/fixtures/jupyter/after_fix.ipynb
vendored
Normal file
37
crates/ruff/resources/test/fixtures/jupyter/after_fix.ipynb
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import math\n",
|
||||
"\n",
|
||||
"math.pi"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff)",
|
||||
"language": "python",
|
||||
"name": "ruff"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
38
crates/ruff/resources/test/fixtures/jupyter/before_fix.ipynb
vendored
Normal file
38
crates/ruff/resources/test/fixtures/jupyter/before_fix.ipynb
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import math\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"math.pi"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff)",
|
||||
"language": "python",
|
||||
"name": "ruff"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
8
crates/ruff/resources/test/fixtures/jupyter/cell/code_and_magic.json
vendored
Normal file
8
crates/ruff/resources/test/fixtures/jupyter/cell/code_and_magic.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"execution_count": null,
|
||||
"cell_type": "code",
|
||||
"id": "1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["def foo():\n", " pass\n", "\n", "%timeit foo()"]
|
||||
}
|
||||
6
crates/ruff/resources/test/fixtures/jupyter/cell/markdown.json
vendored
Normal file
6
crates/ruff/resources/test/fixtures/jupyter/cell/markdown.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1",
|
||||
"metadata": {},
|
||||
"source": ["This is a markdown cell\n", "Some more content"]
|
||||
}
|
||||
8
crates/ruff/resources/test/fixtures/jupyter/cell/only_code.json
vendored
Normal file
8
crates/ruff/resources/test/fixtures/jupyter/cell/only_code.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"execution_count": null,
|
||||
"cell_type": "code",
|
||||
"id": "1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["def foo():\n", " pass"]
|
||||
}
|
||||
8
crates/ruff/resources/test/fixtures/jupyter/cell/only_magic.json
vendored
Normal file
8
crates/ruff/resources/test/fixtures/jupyter/cell/only_magic.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"execution_count": null,
|
||||
"cell_type": "code",
|
||||
"id": "1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": "%timeit print('hello world')"
|
||||
}
|
||||
51
crates/ruff/resources/test/fixtures/jupyter/isort.ipynb
vendored
Normal file
51
crates/ruff/resources/test/fixtures/jupyter/isort.ipynb
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0c7535f6-43cb-423f-bfe1-d263b8f55da0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pathlib import Path\n",
|
||||
"import random\n",
|
||||
"import math"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c066fa1a-5682-47af-8c17-5afec3cf4ad0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import Any\n",
|
||||
"import collections\n",
|
||||
"# Newline should be added here\n",
|
||||
"def foo():\n",
|
||||
" pass"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff)",
|
||||
"language": "python",
|
||||
"name": "ruff"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
53
crates/ruff/resources/test/fixtures/jupyter/isort_expected.ipynb
vendored
Normal file
53
crates/ruff/resources/test/fixtures/jupyter/isort_expected.ipynb
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "663ba955-baca-4f34-9ebb-840d2573ae3f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import math\n",
|
||||
"import random\n",
|
||||
"from pathlib import Path"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d0adfe23-8aea-47e9-bf67-d856cfcb96ea",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import collections\n",
|
||||
"from typing import Any\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Newline should be added here\n",
|
||||
"def foo():\n",
|
||||
" pass"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff)",
|
||||
"language": "python",
|
||||
"name": "ruff"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -3,6 +3,16 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-03-08T23:01:09.782916Z",
|
||||
"start_time": "2023-03-08T23:01:09.705831Z"
|
||||
},
|
||||
"collapsed": false,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@@ -19,32 +29,26 @@
|
||||
" 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",
|
||||
"metadata": {},
|
||||
"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"
|
||||
"end_time": "2023-03-08T23:01:09.915760Z",
|
||||
"start_time": "2023-03-08T23:01:09.733809Z"
|
||||
},
|
||||
"collapsed": true,
|
||||
"jupyter": {
|
||||
"outputs_hidden": true
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
@@ -62,27 +66,66 @@
|
||||
"\n",
|
||||
"mutable_argument()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's create an empty cell"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Multi-line empty cell!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(\"after empty cells\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"display_name": "Python (ruff)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
"name": "ruff"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 2
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython2",
|
||||
"version": "2.7.6"
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
||||
11
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N801.py
vendored
Normal file
11
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N801.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
class badAllowed:
|
||||
pass
|
||||
|
||||
class stillBad:
|
||||
pass
|
||||
|
||||
class BAD_ALLOWED:
|
||||
pass
|
||||
|
||||
class STILL_BAD:
|
||||
pass
|
||||
14
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N802.py
vendored
Normal file
14
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N802.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import unittest
|
||||
|
||||
def badAllowed():
|
||||
pass
|
||||
|
||||
def stillBad():
|
||||
pass
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
def badAllowed(self):
|
||||
return super().tearDown()
|
||||
|
||||
def stillBad(self):
|
||||
return super().tearDown()
|
||||
12
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N803.py
vendored
Normal file
12
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N803.py
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
def func(_, a, badAllowed):
|
||||
return _, a, badAllowed
|
||||
|
||||
def func(_, a, stillBad):
|
||||
return _, a, stillBad
|
||||
|
||||
class Class:
|
||||
def method(self, _, a, badAllowed):
|
||||
return _, a, badAllowed
|
||||
|
||||
def method(self, _, a, stillBad):
|
||||
return _, a, stillBad
|
||||
22
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N804.py
vendored
Normal file
22
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N804.py
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
from abc import ABCMeta
|
||||
|
||||
|
||||
class Class:
|
||||
def __init_subclass__(self, default_name, **kwargs):
|
||||
...
|
||||
|
||||
@classmethod
|
||||
def badAllowed(self, x, /, other):
|
||||
...
|
||||
|
||||
@classmethod
|
||||
def stillBad(self, x, /, other):
|
||||
...
|
||||
|
||||
|
||||
class MetaClass(ABCMeta):
|
||||
def badAllowed(self):
|
||||
pass
|
||||
|
||||
def stillBad(self):
|
||||
pass
|
||||
42
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N805.py
vendored
Normal file
42
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N805.py
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
from abc import ABCMeta
|
||||
|
||||
import pydantic
|
||||
|
||||
|
||||
class Class:
|
||||
def badAllowed(this):
|
||||
pass
|
||||
|
||||
def stillBad(this):
|
||||
pass
|
||||
|
||||
if False:
|
||||
|
||||
def badAllowed(this):
|
||||
pass
|
||||
|
||||
def stillBad(this):
|
||||
pass
|
||||
|
||||
@pydantic.validator
|
||||
def badAllowed(cls, my_field: str) -> str:
|
||||
pass
|
||||
|
||||
@pydantic.validator
|
||||
def stillBad(cls, my_field: str) -> str:
|
||||
pass
|
||||
|
||||
@pydantic.validator("my_field")
|
||||
def badAllowed(cls, my_field: str) -> str:
|
||||
pass
|
||||
|
||||
@pydantic.validator("my_field")
|
||||
def stillBad(cls, my_field: str) -> str:
|
||||
pass
|
||||
|
||||
class PosOnlyClass:
|
||||
def badAllowed(this, blah, /, self, something: str):
|
||||
pass
|
||||
|
||||
def stillBad(this, blah, /, self, something: str):
|
||||
pass
|
||||
6
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N806.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N806.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
def assign():
|
||||
badAllowed = 0
|
||||
stillBad = 0
|
||||
|
||||
BAD_ALLOWED = 0
|
||||
STILL_BAD = 0
|
||||
13
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N807.py
vendored
Normal file
13
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N807.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
def __badAllowed__():
|
||||
pass
|
||||
|
||||
def __stillBad__():
|
||||
pass
|
||||
|
||||
|
||||
def nested():
|
||||
def __badAllowed__():
|
||||
pass
|
||||
|
||||
def __stillBad__():
|
||||
pass
|
||||
5
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N811.py
vendored
Normal file
5
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N811.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import mod.BAD_ALLOWED as badAllowed
|
||||
import mod.STILL_BAD as stillBad
|
||||
|
||||
from mod import BAD_ALLOWED as badAllowed
|
||||
from mod import STILL_BAD as stillBad
|
||||
5
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N812.py
vendored
Normal file
5
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N812.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import mod.badallowed as badAllowed
|
||||
import mod.stillbad as stillBad
|
||||
|
||||
from mod import badallowed as BadAllowed
|
||||
from mod import stillbad as StillBad
|
||||
8
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N813.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N813.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import mod.BadAllowed as badallowed
|
||||
import mod.stillBad as stillbad
|
||||
|
||||
from mod import BadAllowed as badallowed
|
||||
from mod import StillBad as stillbad
|
||||
|
||||
from mod import BadAllowed as bad_allowed
|
||||
from mod import StillBad as still_bad
|
||||
8
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N814.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N814.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import mod.BadAllowed as BADALLOWED
|
||||
import mod.StillBad as STILLBAD
|
||||
|
||||
from mod import BadAllowed as BADALLOWED
|
||||
from mod import StillBad as STILLBAD
|
||||
|
||||
from mod import BadAllowed as BAD_ALLOWED
|
||||
from mod import StillBad as STILL_BAD
|
||||
19
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N815.py
vendored
Normal file
19
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N815.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
class C:
|
||||
badAllowed = 0
|
||||
stillBad = 0
|
||||
|
||||
_badAllowed = 0
|
||||
_stillBad = 0
|
||||
|
||||
bad_Allowed = 0
|
||||
still_Bad = 0
|
||||
|
||||
class D(TypedDict):
|
||||
badAllowed: bool
|
||||
stillBad: bool
|
||||
|
||||
_badAllowed: list
|
||||
_stillBad: list
|
||||
|
||||
bad_Allowed: set
|
||||
still_Bad: set
|
||||
8
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N816.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N816.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
badAllowed = 0
|
||||
stillBad = 0
|
||||
|
||||
_badAllowed = 0
|
||||
_stillBad = 0
|
||||
|
||||
bad_Allowed = 0
|
||||
still_Bad = 0
|
||||
5
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N817.py
vendored
Normal file
5
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N817.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import mod.BadAllowed as BA
|
||||
import mod.StillBad as SB
|
||||
|
||||
from mod import BadAllowed as BA
|
||||
from mod import StillBad as SB
|
||||
11
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N818.py
vendored
Normal file
11
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N818.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
class BadAllowed(Exception):
|
||||
pass
|
||||
|
||||
class StillBad(Exception):
|
||||
pass
|
||||
|
||||
class BadAllowed(AnotherError):
|
||||
pass
|
||||
|
||||
class StillBad(AnotherError):
|
||||
pass
|
||||
0
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N999/badAllowed/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N999/badAllowed/__init__.py
vendored
Normal file
71
crates/ruff/resources/test/fixtures/perflint/PERF102.py
vendored
Normal file
71
crates/ruff/resources/test/fixtures/perflint/PERF102.py
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
some_dict = {"a": 12, "b": 32, "c": 44}
|
||||
|
||||
for _, value in some_dict.items(): # PERF102
|
||||
print(value)
|
||||
|
||||
|
||||
for key, _ in some_dict.items(): # PERF102
|
||||
print(key)
|
||||
|
||||
|
||||
for weird_arg_name, _ in some_dict.items(): # PERF102
|
||||
print(weird_arg_name)
|
||||
|
||||
|
||||
for name, (_, _) in some_dict.items(): # PERF102
|
||||
pass
|
||||
|
||||
|
||||
for name, (value1, _) in some_dict.items(): # OK
|
||||
pass
|
||||
|
||||
|
||||
for (key1, _), (_, _) in some_dict.items(): # PERF102
|
||||
pass
|
||||
|
||||
|
||||
for (_, (_, _)), (value, _) in some_dict.items(): # PERF102
|
||||
pass
|
||||
|
||||
|
||||
for (_, key2), (value1, _) in some_dict.items(): # OK
|
||||
pass
|
||||
|
||||
|
||||
for ((_, key2), (value1, _)) in some_dict.items(): # OK
|
||||
pass
|
||||
|
||||
|
||||
for ((_, key2), (_, _)) in some_dict.items(): # PERF102
|
||||
pass
|
||||
|
||||
|
||||
for (_, _, _, variants), (r_language, _, _, _) in some_dict.items(): # OK
|
||||
pass
|
||||
|
||||
|
||||
for (_, _, (_, variants)), (_, (_, (r_language, _))) in some_dict.items(): # OK
|
||||
pass
|
||||
|
||||
|
||||
for key, value in some_dict.items(): # OK
|
||||
print(key, value)
|
||||
|
||||
|
||||
for _, value in some_dict.items(12): # OK
|
||||
print(value)
|
||||
|
||||
|
||||
for key in some_dict.keys(): # OK
|
||||
print(key)
|
||||
|
||||
|
||||
for value in some_dict.values(): # OK
|
||||
print(value)
|
||||
|
||||
|
||||
for name, (_, _) in (some_function()).items(): # PERF102
|
||||
pass
|
||||
|
||||
for name, (_, _) in (some_function().some_attribute).items(): # PERF102
|
||||
pass
|
||||
@@ -60,3 +60,6 @@ match *0, 1, *2:
|
||||
#:
|
||||
class Foo:
|
||||
match: Optional[Match] = None
|
||||
#: E702:2:4
|
||||
while 1:
|
||||
1;...
|
||||
|
||||
32
crates/ruff/resources/test/fixtures/pyflakes/F401_17.py
vendored
Normal file
32
crates/ruff/resources/test/fixtures/pyflakes/F401_17.py
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
"""Test that runtime typing references are properly attributed to scoped imports."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from threading import Thread
|
||||
|
||||
|
||||
def fn(thread: Thread):
|
||||
from threading import Thread
|
||||
|
||||
# The `Thread` on the left-hand side should resolve to the `Thread` imported at the
|
||||
# top level.
|
||||
x: Thread
|
||||
|
||||
|
||||
def fn(thread: Thread):
|
||||
from threading import Thread
|
||||
|
||||
# The `Thread` on the left-hand side should resolve to the `Thread` imported at the
|
||||
# top level.
|
||||
cast("Thread", thread)
|
||||
|
||||
|
||||
def fn(thread: Thread):
|
||||
from threading import Thread
|
||||
|
||||
# The `Thread` on the right-hand side should resolve to the`Thread` imported within
|
||||
# `fn`.
|
||||
cast(Thread, thread)
|
||||
11
crates/ruff/resources/test/fixtures/pyflakes/F401_18.py
vendored
Normal file
11
crates/ruff/resources/test/fixtures/pyflakes/F401_18.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
"""Test that straight `__future__` imports are considered unused."""
|
||||
|
||||
|
||||
def f():
|
||||
import __future__
|
||||
|
||||
|
||||
def f():
|
||||
import __future__
|
||||
|
||||
print(__future__.absolute_import)
|
||||
@@ -7,3 +7,5 @@ hidden = {"a": "!"}
|
||||
|
||||
"%(a)s" % {"a": 1, r"b": "!"} # F504 ("b" not used)
|
||||
"%(a)s" % {'a': 1, u"b": "!"} # F504 ("b" not used)
|
||||
|
||||
'' % {'a''b' : ''} # F504 ("ab" not used)
|
||||
|
||||
@@ -19,3 +19,9 @@ if ("123" != x) is 3:
|
||||
|
||||
if "123" != (x is 3):
|
||||
pass
|
||||
|
||||
{2 is
|
||||
not ''}
|
||||
|
||||
{2 is
|
||||
not ''}
|
||||
|
||||
@@ -126,3 +126,22 @@ def f(x: int):
|
||||
def f():
|
||||
if any((key := (value := x)) for x in ["ok"]):
|
||||
print(key)
|
||||
|
||||
|
||||
def f() -> None:
|
||||
is_connected = False
|
||||
|
||||
class Foo:
|
||||
@property
|
||||
def is_connected(self):
|
||||
nonlocal is_connected
|
||||
return is_connected
|
||||
|
||||
def do_thing(self):
|
||||
# This should resolve to the `is_connected` in the function scope.
|
||||
nonlocal is_connected
|
||||
print(is_connected)
|
||||
|
||||
obj = Foo()
|
||||
obj.do_thing()
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
# Test case for https://github.com/charliermarsh/ruff/issues/1552
|
||||
# Test case for https://github.com/astral-sh/ruff/issues/1552
|
||||
def f():
|
||||
x = 0
|
||||
list()[x:]
|
||||
|
||||
|
||||
# Test case for https://github.com/charliermarsh/ruff/issues/2603
|
||||
# Test case for https://github.com/astral-sh/ruff/issues/2603
|
||||
def f():
|
||||
KeyTupleT = tuple[str, ...]
|
||||
|
||||
|
||||
@@ -108,3 +108,42 @@ def f():
|
||||
|
||||
def f():
|
||||
toplevel = tt = 1
|
||||
|
||||
|
||||
def f(provided: int) -> int:
|
||||
match provided:
|
||||
case [_, *x]:
|
||||
pass
|
||||
|
||||
|
||||
def f(provided: int) -> int:
|
||||
match provided:
|
||||
case x:
|
||||
pass
|
||||
|
||||
|
||||
def f(provided: int) -> int:
|
||||
match provided:
|
||||
case Foo(bar) as x:
|
||||
pass
|
||||
|
||||
|
||||
def f(provided: int) -> int:
|
||||
match provided:
|
||||
case {"foo": 0, **x}:
|
||||
pass
|
||||
|
||||
|
||||
def f(provided: int) -> int:
|
||||
match provided:
|
||||
case {**x}:
|
||||
pass
|
||||
|
||||
|
||||
global CONSTANT
|
||||
|
||||
|
||||
def f() -> None:
|
||||
global CONSTANT
|
||||
CONSTANT = 1
|
||||
CONSTANT = 2
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
if True:
|
||||
import foo1; x = 1
|
||||
import foo2; x = 1
|
||||
@@ -11,7 +10,6 @@ if True:
|
||||
import foo4 \
|
||||
; x = 1
|
||||
|
||||
|
||||
if True:
|
||||
x = 1; import foo5
|
||||
|
||||
@@ -20,12 +18,10 @@ if True:
|
||||
x = 1; \
|
||||
import foo6
|
||||
|
||||
|
||||
if True:
|
||||
x = 1 \
|
||||
; import foo7
|
||||
|
||||
|
||||
if True:
|
||||
x = 1; import foo8; x = 1
|
||||
x = 1; import foo9; x = 1
|
||||
@@ -40,12 +36,27 @@ if True:
|
||||
;import foo11 \
|
||||
;x = 1
|
||||
|
||||
if True:
|
||||
x = 1; \
|
||||
\
|
||||
import foo12
|
||||
|
||||
if True:
|
||||
x = 1; \
|
||||
\
|
||||
import foo13
|
||||
|
||||
|
||||
if True:
|
||||
x = 1; \
|
||||
# \
|
||||
import foo14
|
||||
|
||||
# Continuation, but not as the last content in the file.
|
||||
x = 1; \
|
||||
import foo12
|
||||
import foo15
|
||||
|
||||
# Continuation, followed by end-of-file. (Removing `import foo` would cause a syntax
|
||||
# error.)
|
||||
x = 1; \
|
||||
import foo13
|
||||
import foo16
|
||||
45
crates/ruff/resources/test/fixtures/pylint/comparison_with_itself.py
vendored
Normal file
45
crates/ruff/resources/test/fixtures/pylint/comparison_with_itself.py
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# Errors.
|
||||
foo == foo
|
||||
|
||||
foo != foo
|
||||
|
||||
foo > foo
|
||||
|
||||
foo >= foo
|
||||
|
||||
foo < foo
|
||||
|
||||
foo <= foo
|
||||
|
||||
foo is foo
|
||||
|
||||
foo is not foo
|
||||
|
||||
foo in foo
|
||||
|
||||
foo not in foo
|
||||
|
||||
# Non-errors.
|
||||
"foo" == "foo" # This is flagged by `comparison-of-constant` instead.
|
||||
|
||||
foo == "foo"
|
||||
|
||||
foo == bar
|
||||
|
||||
foo != bar
|
||||
|
||||
foo > bar
|
||||
|
||||
foo >= bar
|
||||
|
||||
foo < bar
|
||||
|
||||
foo <= bar
|
||||
|
||||
foo is bar
|
||||
|
||||
foo is not bar
|
||||
|
||||
foo in bar
|
||||
|
||||
foo not in bar
|
||||
@@ -73,3 +73,10 @@ def override_class():
|
||||
pass
|
||||
|
||||
CLASS()
|
||||
|
||||
|
||||
def multiple_assignment():
|
||||
"""Should warn on every assignment."""
|
||||
global CONSTANT # [global-statement]
|
||||
CONSTANT = 1
|
||||
CONSTANT = 2
|
||||
|
||||
@@ -14,6 +14,12 @@ __all__ = (x for x in ["Hello", "world"]) # [invalid-all-format]
|
||||
|
||||
__all__ = {x for x in ["Hello", "world"]} # [invalid-all-format]
|
||||
|
||||
__all__ = foo # [invalid-all-format]
|
||||
|
||||
__all__ = foo.bar # [invalid-all-format]
|
||||
|
||||
__all__ = foo["bar"] # [invalid-all-format]
|
||||
|
||||
__all__ = ["Hello"]
|
||||
|
||||
__all__ = ("Hello",)
|
||||
@@ -29,3 +35,8 @@ __all__ = list({"Hello", "world"})
|
||||
__all__ = list(["Hello"]) + list(["world"])
|
||||
|
||||
__all__ = tuple(["Hello"]) + ("world",)
|
||||
|
||||
__all__ = __all__ + ["Hello"]
|
||||
|
||||
__all__ = __all__ + multiprocessing.__all__
|
||||
|
||||
|
||||
@@ -31,6 +31,21 @@ with None as i:
|
||||
with None as j: # ok
|
||||
pass
|
||||
|
||||
# Async with -> with, variable reused
|
||||
async with None as i:
|
||||
with None as i: # error
|
||||
pass
|
||||
|
||||
# Async with -> with, different variable
|
||||
async with None as i:
|
||||
with None as j: # ok
|
||||
pass
|
||||
|
||||
# Async for -> for, variable reused
|
||||
async for i in []:
|
||||
for i in []: # error
|
||||
pass
|
||||
|
||||
# For -> for -> for, doubly nested variable reuse
|
||||
for i in []:
|
||||
for j in []:
|
||||
|
||||
@@ -134,6 +134,19 @@ class A(
|
||||
...
|
||||
|
||||
|
||||
class A(object, object):
|
||||
...
|
||||
|
||||
|
||||
@decorator()
|
||||
class A(object):
|
||||
...
|
||||
|
||||
@decorator() # class A(object):
|
||||
class A(object):
|
||||
...
|
||||
|
||||
|
||||
object = A
|
||||
|
||||
|
||||
|
||||
@@ -21,10 +21,6 @@ x = "foo {0}" \
|
||||
|
||||
"\N{snowman} {0}".format(1)
|
||||
|
||||
'{' '0}'.format(1)
|
||||
|
||||
# These will not change because we are waiting for libcst to fix this issue:
|
||||
# https://github.com/Instagram/LibCST/issues/846
|
||||
print(
|
||||
'foo{0}'
|
||||
'bar{1}'.format(1, 2)
|
||||
@@ -34,3 +30,5 @@ print(
|
||||
'foo{0}' # ohai\n"
|
||||
'bar{1}'.format(1, 2)
|
||||
)
|
||||
|
||||
'{' '0}'.format(1)
|
||||
|
||||
@@ -13,3 +13,5 @@ f"{0}".format(a)
|
||||
f"{0}".format(1)
|
||||
|
||||
print(f"{0}".format(1))
|
||||
|
||||
''.format(1)
|
||||
|
||||
43
crates/ruff/resources/test/fixtures/pyupgrade/UP039.py
vendored
Normal file
43
crates/ruff/resources/test/fixtures/pyupgrade/UP039.py
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Errors
|
||||
class A():
|
||||
pass
|
||||
|
||||
|
||||
class A() \
|
||||
:
|
||||
pass
|
||||
|
||||
|
||||
class A \
|
||||
():
|
||||
pass
|
||||
|
||||
|
||||
@decorator()
|
||||
class A():
|
||||
pass
|
||||
|
||||
@decorator
|
||||
class A():
|
||||
pass
|
||||
|
||||
# OK
|
||||
class A:
|
||||
pass
|
||||
|
||||
|
||||
class A(A):
|
||||
pass
|
||||
|
||||
|
||||
class A(metaclass=type):
|
||||
pass
|
||||
|
||||
|
||||
@decorator()
|
||||
class A:
|
||||
pass
|
||||
|
||||
@decorator
|
||||
class A:
|
||||
pass
|
||||
@@ -5,12 +5,11 @@ from typing import ClassVar, Sequence
|
||||
KNOWINGLY_MUTABLE_DEFAULT = []
|
||||
|
||||
|
||||
@dataclass()
|
||||
@dataclass
|
||||
class A:
|
||||
mutable_default: list[int] = []
|
||||
immutable_annotation: typing.Sequence[int] = []
|
||||
without_annotation = []
|
||||
ignored_via_comment: list[int] = [] # noqa: RUF008
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
perfectly_fine: list[int] = field(default_factory=list)
|
||||
class_variable: typing.ClassVar[list[int]] = []
|
||||
@@ -21,7 +20,6 @@ class B:
|
||||
mutable_default: list[int] = []
|
||||
immutable_annotation: Sequence[int] = []
|
||||
without_annotation = []
|
||||
ignored_via_comment: list[int] = [] # noqa: RUF008
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
perfectly_fine: list[int] = field(default_factory=list)
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
|
||||
@@ -28,3 +28,18 @@ def ascii(arg):
|
||||
|
||||
|
||||
f"{ascii(bla)}" # OK
|
||||
|
||||
(
|
||||
f"Member of tuple mismatches type at index {i}. Expected {of_shape_i}. Got "
|
||||
" intermediary content "
|
||||
f" that flows {repr(obj)} of type {type(obj)}.{additional_message}" # RUF010
|
||||
)
|
||||
|
||||
|
||||
f"{str(bla)}" # RUF010
|
||||
|
||||
f"{str(bla):20}" # RUF010
|
||||
|
||||
f"{bla!s}" # RUF010
|
||||
|
||||
f"{bla!s:20}" # OK
|
||||
|
||||
13
crates/ruff/resources/test/fixtures/ruff/RUF011.py
vendored
Normal file
13
crates/ruff/resources/test/fixtures/ruff/RUF011.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
data = ["some", "Data"]
|
||||
|
||||
# Ok
|
||||
{value: value.upper() for value in data}
|
||||
{value.lower(): value.upper() for value in data}
|
||||
{v: v*v for v in range(10)}
|
||||
{(0, "a", v): v*v for v in range(10)} # Tuple with variable
|
||||
|
||||
# Errors
|
||||
{"key": value.upper() for value in data}
|
||||
{True: value.upper() for value in data}
|
||||
{0: value.upper() for value in data}
|
||||
{(1, "a"): value.upper() for value in data} # constant tuple
|
||||
33
crates/ruff/resources/test/fixtures/ruff/RUF012.py
vendored
Normal file
33
crates/ruff/resources/test/fixtures/ruff/RUF012.py
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
import typing
|
||||
from typing import ClassVar, Sequence
|
||||
|
||||
KNOWINGLY_MUTABLE_DEFAULT = []
|
||||
|
||||
|
||||
class A:
|
||||
mutable_default: list[int] = []
|
||||
immutable_annotation: typing.Sequence[int] = []
|
||||
without_annotation = []
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
class_variable: typing.ClassVar[list[int]] = []
|
||||
|
||||
|
||||
class B:
|
||||
mutable_default: list[int] = []
|
||||
immutable_annotation: Sequence[int] = []
|
||||
without_annotation = []
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class C:
|
||||
mutable_default: list[int] = []
|
||||
immutable_annotation: Sequence[int] = []
|
||||
without_annotation = []
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
perfectly_fine: list[int] = field(default_factory=list)
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
187
crates/ruff/resources/test/fixtures/ruff/RUF013_0.py
vendored
Normal file
187
crates/ruff/resources/test/fixtures/ruff/RUF013_0.py
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
import typing
|
||||
from typing import Annotated, Any, Literal, Optional, Tuple, Union
|
||||
|
||||
|
||||
def f(arg: int):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg=None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Any = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: object = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: int = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: str = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: typing.List[str] = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Tuple[str] = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
# Optional
|
||||
|
||||
|
||||
def f(arg: Optional[int] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: typing.Optional[int] = None):
|
||||
pass
|
||||
|
||||
|
||||
# Union
|
||||
|
||||
|
||||
def f(arg: Union[None, int] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union[str, None] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: typing.Union[int, str, None] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union[int, str, Any] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union[int, str] = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: typing.Union[int, str] = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
# PEP 604 Union
|
||||
|
||||
|
||||
def f(arg: None | int = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: int | None = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: int | float | str | None = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: int | float = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: int | float | str | bytes = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
# Literal
|
||||
|
||||
|
||||
def f(arg: None = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Literal[1, 2, None, 3] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Literal[1, "foo"] = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: typing.Literal[1, "foo", True] = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
# Annotated
|
||||
|
||||
|
||||
def f(arg: Annotated[Optional[int], ...] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Annotated[Union[int, None], ...] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Annotated[Any, ...] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Annotated[int, ...] = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF011
|
||||
pass
|
||||
|
||||
|
||||
# Multiple arguments
|
||||
|
||||
|
||||
def f(
|
||||
arg1: Optional[int] = None,
|
||||
arg2: Union[int, None] = None,
|
||||
arg3: Literal[1, 2, None, 3] = None,
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def f(
|
||||
arg1: int = None, # RUF011
|
||||
arg2: Union[int, float] = None, # RUF011
|
||||
arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# Nested
|
||||
|
||||
|
||||
def f(arg: Literal[1, "foo", Literal[True, None]] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union[int, Union[float, Union[str, None]]] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union[int, Union[float, Optional[str]]] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union[int, Literal[True, None]] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union[Annotated[int, ...], Annotated[Optional[float], ...]] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF011
|
||||
pass
|
||||
5
crates/ruff/resources/test/fixtures/ruff/RUF013_1.py
vendored
Normal file
5
crates/ruff/resources/test/fixtures/ruff/RUF013_1.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# No `typing.Optional` import
|
||||
|
||||
|
||||
def f(arg: int = None): # RUF011
|
||||
pass
|
||||
@@ -1,13 +1,13 @@
|
||||
//! Interface for generating autofix edits from higher-level actions (e.g., "remove an argument").
|
||||
use anyhow::{bail, Result};
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use rustpython_parser::ast::{self, Excepthandler, Expr, Keyword, Ranged, Stmt};
|
||||
use rustpython_parser::ast::{self, ExceptHandler, Expr, Keyword, Ranged, Stmt};
|
||||
use rustpython_parser::{lexer, Mode, Tok};
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_newlines::NewlineWithTrailingNewline;
|
||||
use ruff_python_ast::helpers;
|
||||
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
|
||||
use ruff_python_whitespace::{is_python_whitespace, NewlineWithTrailingNewline, PythonWhitespace};
|
||||
|
||||
use crate::autofix::codemods;
|
||||
|
||||
@@ -29,7 +29,6 @@ pub(crate) fn delete_stmt(
|
||||
parent: Option<&Stmt>,
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
stylist: &Stylist,
|
||||
) -> Edit {
|
||||
if parent
|
||||
.map(|parent| is_lone_child(stmt, parent))
|
||||
@@ -39,18 +38,15 @@ pub(crate) fn delete_stmt(
|
||||
// it with a `pass`.
|
||||
Edit::range_replacement("pass".to_string(), stmt.range())
|
||||
} else {
|
||||
if let Some(semicolon) = trailing_semicolon(stmt, locator) {
|
||||
if let Some(semicolon) = trailing_semicolon(stmt.end(), locator) {
|
||||
let next = next_stmt_break(semicolon, locator);
|
||||
Edit::deletion(stmt.start(), next)
|
||||
} else if helpers::has_leading_content(stmt, locator) {
|
||||
} else if helpers::has_leading_content(stmt.start(), locator) {
|
||||
Edit::range_deletion(stmt.range())
|
||||
} else if helpers::preceded_by_continuation(stmt, indexer, locator) {
|
||||
if is_end_of_file(stmt, locator) && locator.is_at_start_of_line(stmt.start()) {
|
||||
// Special-case: a file can't end in a continuation.
|
||||
Edit::range_replacement(stylist.line_ending().to_string(), stmt.range())
|
||||
} else {
|
||||
Edit::range_deletion(stmt.range())
|
||||
}
|
||||
} else if let Some(start) =
|
||||
helpers::preceded_by_continuations(stmt.start(), locator, indexer)
|
||||
{
|
||||
Edit::range_deletion(TextRange::new(start, stmt.end()))
|
||||
} else {
|
||||
let range = locator.full_lines_range(stmt.range());
|
||||
Edit::range_deletion(range)
|
||||
@@ -68,7 +64,7 @@ pub(crate) fn remove_unused_imports<'a>(
|
||||
stylist: &Stylist,
|
||||
) -> Result<Edit> {
|
||||
match codemods::remove_imports(unused_imports, stmt, locator, stylist)? {
|
||||
None => Ok(delete_stmt(stmt, parent, locator, indexer, stylist)),
|
||||
None => Ok(delete_stmt(stmt, parent, locator, indexer)),
|
||||
Some(content) => Ok(Edit::range_replacement(content, stmt.range())),
|
||||
}
|
||||
}
|
||||
@@ -218,7 +214,7 @@ fn is_lone_child(child: &Stmt, parent: &Stmt) -> bool {
|
||||
|| is_only(orelse, child)
|
||||
|| is_only(finalbody, child)
|
||||
|| handlers.iter().any(|handler| match handler {
|
||||
Excepthandler::ExceptHandler(ast::ExcepthandlerExceptHandler {
|
||||
ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
|
||||
body, ..
|
||||
}) => is_only(body, child),
|
||||
})
|
||||
@@ -238,15 +234,15 @@ fn is_lone_child(child: &Stmt, parent: &Stmt) -> bool {
|
||||
|
||||
/// Return the location of a trailing semicolon following a `Stmt`, if it's part
|
||||
/// of a multi-statement line.
|
||||
fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<TextSize> {
|
||||
let contents = locator.after(stmt.end());
|
||||
fn trailing_semicolon(offset: TextSize, locator: &Locator) -> Option<TextSize> {
|
||||
let contents = locator.after(offset);
|
||||
|
||||
for line in NewlineWithTrailingNewline::from(contents) {
|
||||
let trimmed = line.trim_start();
|
||||
let trimmed = line.trim_whitespace_start();
|
||||
|
||||
if trimmed.starts_with(';') {
|
||||
let colon_offset = line.text_len() - trimmed.text_len();
|
||||
return Some(stmt.end() + line.start() + colon_offset);
|
||||
return Some(offset + line.start() + colon_offset);
|
||||
}
|
||||
|
||||
if !trimmed.starts_with('\\') {
|
||||
@@ -262,7 +258,7 @@ fn next_stmt_break(semicolon: TextSize, locator: &Locator) -> TextSize {
|
||||
|
||||
let contents = &locator.contents()[usize::from(start_location)..];
|
||||
for line in NewlineWithTrailingNewline::from(contents) {
|
||||
let trimmed = line.trim();
|
||||
let trimmed = line.trim_whitespace();
|
||||
// Skip past any continuations.
|
||||
if trimmed.starts_with('\\') {
|
||||
continue;
|
||||
@@ -276,7 +272,7 @@ fn next_stmt_break(semicolon: TextSize, locator: &Locator) -> TextSize {
|
||||
} else {
|
||||
// Otherwise, find the start of the next statement. (Or, anything that isn't
|
||||
// whitespace.)
|
||||
let relative_offset = line.find(|c: char| !c.is_whitespace()).unwrap();
|
||||
let relative_offset = line.find(|c: char| !is_python_whitespace(c)).unwrap();
|
||||
line.start() + TextSize::try_from(relative_offset).unwrap()
|
||||
};
|
||||
}
|
||||
@@ -284,16 +280,11 @@ fn next_stmt_break(semicolon: TextSize, locator: &Locator) -> TextSize {
|
||||
locator.line_end(start_location)
|
||||
}
|
||||
|
||||
/// Return `true` if a `Stmt` occurs at the end of a file.
|
||||
fn is_end_of_file(stmt: &Stmt, locator: &Locator) -> bool {
|
||||
stmt.end() == locator.contents().text_len()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
use ruff_text_size::TextSize;
|
||||
use rustpython_parser::ast::Suite;
|
||||
use rustpython_parser::ast::{Ranged, Suite};
|
||||
use rustpython_parser::Parse;
|
||||
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
@@ -306,19 +297,25 @@ mod tests {
|
||||
let program = Suite::parse(contents, "<filename>")?;
|
||||
let stmt = program.first().unwrap();
|
||||
let locator = Locator::new(contents);
|
||||
assert_eq!(trailing_semicolon(stmt, &locator), None);
|
||||
assert_eq!(trailing_semicolon(stmt.end(), &locator), None);
|
||||
|
||||
let contents = "x = 1; y = 1";
|
||||
let program = Suite::parse(contents, "<filename>")?;
|
||||
let stmt = program.first().unwrap();
|
||||
let locator = Locator::new(contents);
|
||||
assert_eq!(trailing_semicolon(stmt, &locator), Some(TextSize::from(5)));
|
||||
assert_eq!(
|
||||
trailing_semicolon(stmt.end(), &locator),
|
||||
Some(TextSize::from(5))
|
||||
);
|
||||
|
||||
let contents = "x = 1 ; y = 1";
|
||||
let program = Suite::parse(contents, "<filename>")?;
|
||||
let stmt = program.first().unwrap();
|
||||
let locator = Locator::new(contents);
|
||||
assert_eq!(trailing_semicolon(stmt, &locator), Some(TextSize::from(6)));
|
||||
assert_eq!(
|
||||
trailing_semicolon(stmt.end(), &locator),
|
||||
Some(TextSize::from(6))
|
||||
);
|
||||
|
||||
let contents = r#"
|
||||
x = 1 \
|
||||
@@ -328,7 +325,10 @@ x = 1 \
|
||||
let program = Suite::parse(contents, "<filename>")?;
|
||||
let stmt = program.first().unwrap();
|
||||
let locator = Locator::new(contents);
|
||||
assert_eq!(trailing_semicolon(stmt, &locator), Some(TextSize::from(10)));
|
||||
assert_eq!(
|
||||
trailing_semicolon(stmt.end(), &locator),
|
||||
Some(TextSize::from(10))
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,23 +2,31 @@ use std::collections::BTreeSet;
|
||||
|
||||
use itertools::Itertools;
|
||||
use nohash_hasher::IntSet;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, IsolationLevel};
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
|
||||
use crate::autofix::source_map::SourceMap;
|
||||
use crate::linter::FixTable;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
|
||||
pub(crate) mod codemods;
|
||||
pub(crate) mod edits;
|
||||
pub(crate) mod source_map;
|
||||
|
||||
pub(crate) struct FixResult {
|
||||
/// The resulting source code, after applying all fixes.
|
||||
pub(crate) code: String,
|
||||
/// The number of fixes applied for each [`Rule`].
|
||||
pub(crate) fixes: FixTable,
|
||||
/// Source map for the fixed source code.
|
||||
pub(crate) source_map: SourceMap,
|
||||
}
|
||||
|
||||
/// Auto-fix errors in a file, and write the fixed source code to disk.
|
||||
pub(crate) fn fix_file(
|
||||
diagnostics: &[Diagnostic],
|
||||
locator: &Locator,
|
||||
) -> Option<(String, FixTable)> {
|
||||
pub(crate) fn fix_file(diagnostics: &[Diagnostic], locator: &Locator) -> Option<FixResult> {
|
||||
let mut with_fixes = diagnostics
|
||||
.iter()
|
||||
.filter(|diag| diag.fix.is_some())
|
||||
@@ -35,12 +43,13 @@ pub(crate) fn fix_file(
|
||||
fn apply_fixes<'a>(
|
||||
diagnostics: impl Iterator<Item = &'a Diagnostic>,
|
||||
locator: &'a Locator<'a>,
|
||||
) -> (String, FixTable) {
|
||||
) -> FixResult {
|
||||
let mut output = String::with_capacity(locator.len());
|
||||
let mut last_pos: Option<TextSize> = None;
|
||||
let mut applied: BTreeSet<&Edit> = BTreeSet::default();
|
||||
let mut isolated: IntSet<u32> = IntSet::default();
|
||||
let mut fixed = FxHashMap::default();
|
||||
let mut source_map = SourceMap::default();
|
||||
|
||||
for (rule, fix) in diagnostics
|
||||
.filter_map(|diagnostic| {
|
||||
@@ -84,9 +93,15 @@ fn apply_fixes<'a>(
|
||||
let slice = locator.slice(TextRange::new(last_pos.unwrap_or_default(), edit.start()));
|
||||
output.push_str(slice);
|
||||
|
||||
// Add the start source marker for the patch.
|
||||
source_map.push_start_marker(edit, output.text_len());
|
||||
|
||||
// Add the patch itself.
|
||||
output.push_str(edit.content().unwrap_or_default());
|
||||
|
||||
// Add the end source marker for the added patch.
|
||||
source_map.push_end_marker(edit, output.text_len());
|
||||
|
||||
// Track that the edit was applied.
|
||||
last_pos = Some(edit.end());
|
||||
applied.insert(edit);
|
||||
@@ -99,7 +114,11 @@ fn apply_fixes<'a>(
|
||||
let slice = locator.after(last_pos.unwrap_or_default());
|
||||
output.push_str(slice);
|
||||
|
||||
(output, fixed)
|
||||
FixResult {
|
||||
code: output,
|
||||
fixes: fixed,
|
||||
source_map,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compare two fixes.
|
||||
@@ -110,6 +129,13 @@ fn cmp_fix(rule1: Rule, rule2: Rule, fix1: &Fix, fix2: &Fix) -> std::cmp::Orderi
|
||||
// Apply `EndsInPeriod` fixes before `NewLineAfterLastParagraph` fixes.
|
||||
(Rule::EndsInPeriod, Rule::NewLineAfterLastParagraph) => std::cmp::Ordering::Less,
|
||||
(Rule::NewLineAfterLastParagraph, Rule::EndsInPeriod) => std::cmp::Ordering::Greater,
|
||||
// Apply `IfElseBlockInsteadOfDictGet` fixes before `IfElseBlockInsteadOfIfExp` fixes.
|
||||
(Rule::IfElseBlockInsteadOfDictGet, Rule::IfElseBlockInsteadOfIfExp) => {
|
||||
std::cmp::Ordering::Less
|
||||
}
|
||||
(Rule::IfElseBlockInsteadOfIfExp, Rule::IfElseBlockInsteadOfDictGet) => {
|
||||
std::cmp::Ordering::Greater
|
||||
}
|
||||
_ => std::cmp::Ordering::Equal,
|
||||
})
|
||||
}
|
||||
@@ -123,7 +149,8 @@ mod tests {
|
||||
use ruff_diagnostics::Fix;
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
|
||||
use crate::autofix::apply_fixes;
|
||||
use crate::autofix::source_map::SourceMarker;
|
||||
use crate::autofix::{apply_fixes, FixResult};
|
||||
use crate::rules::pycodestyle::rules::MissingNewlineAtEndOfFile;
|
||||
|
||||
#[allow(deprecated)]
|
||||
@@ -143,9 +170,59 @@ mod tests {
|
||||
fn empty_file() {
|
||||
let locator = Locator::new(r#""#);
|
||||
let diagnostics = create_diagnostics([]);
|
||||
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
|
||||
assert_eq!(contents, "");
|
||||
assert_eq!(fixed.values().sum::<usize>(), 0);
|
||||
let FixResult {
|
||||
code,
|
||||
fixes,
|
||||
source_map,
|
||||
} = apply_fixes(diagnostics.iter(), &locator);
|
||||
assert_eq!(code, "");
|
||||
assert_eq!(fixes.values().sum::<usize>(), 0);
|
||||
assert!(source_map.markers().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_one_insertion() {
|
||||
let locator = Locator::new(
|
||||
r#"
|
||||
import os
|
||||
|
||||
print("hello world")
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
let diagnostics = create_diagnostics([Edit::insertion(
|
||||
"import sys\n".to_string(),
|
||||
TextSize::new(10),
|
||||
)]);
|
||||
let FixResult {
|
||||
code,
|
||||
fixes,
|
||||
source_map,
|
||||
} = apply_fixes(diagnostics.iter(), &locator);
|
||||
assert_eq!(
|
||||
code,
|
||||
r#"
|
||||
import os
|
||||
import sys
|
||||
|
||||
print("hello world")
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 1);
|
||||
assert_eq!(
|
||||
source_map.markers(),
|
||||
&[
|
||||
SourceMarker {
|
||||
source: 10.into(),
|
||||
dest: 10.into(),
|
||||
},
|
||||
SourceMarker {
|
||||
source: 10.into(),
|
||||
dest: 21.into(),
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -162,16 +239,33 @@ class A(object):
|
||||
TextSize::new(8),
|
||||
TextSize::new(14),
|
||||
)]);
|
||||
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
|
||||
let FixResult {
|
||||
code,
|
||||
fixes,
|
||||
source_map,
|
||||
} = apply_fixes(diagnostics.iter(), &locator);
|
||||
assert_eq!(
|
||||
contents,
|
||||
code,
|
||||
r#"
|
||||
class A(Bar):
|
||||
...
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
assert_eq!(fixed.values().sum::<usize>(), 1);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 1);
|
||||
assert_eq!(
|
||||
source_map.markers(),
|
||||
&[
|
||||
SourceMarker {
|
||||
source: 8.into(),
|
||||
dest: 8.into(),
|
||||
},
|
||||
SourceMarker {
|
||||
source: 14.into(),
|
||||
dest: 11.into(),
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -184,16 +278,33 @@ class A(object):
|
||||
.trim(),
|
||||
);
|
||||
let diagnostics = create_diagnostics([Edit::deletion(TextSize::new(7), TextSize::new(15))]);
|
||||
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
|
||||
let FixResult {
|
||||
code,
|
||||
fixes,
|
||||
source_map,
|
||||
} = apply_fixes(diagnostics.iter(), &locator);
|
||||
assert_eq!(
|
||||
contents,
|
||||
code,
|
||||
r#"
|
||||
class A:
|
||||
...
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
assert_eq!(fixed.values().sum::<usize>(), 1);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 1);
|
||||
assert_eq!(
|
||||
source_map.markers(),
|
||||
&[
|
||||
SourceMarker {
|
||||
source: 7.into(),
|
||||
dest: 7.into()
|
||||
},
|
||||
SourceMarker {
|
||||
source: 15.into(),
|
||||
dest: 7.into()
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -209,17 +320,42 @@ class A(object, object, object):
|
||||
Edit::deletion(TextSize::from(8), TextSize::from(16)),
|
||||
Edit::deletion(TextSize::from(22), TextSize::from(30)),
|
||||
]);
|
||||
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
|
||||
let FixResult {
|
||||
code,
|
||||
fixes,
|
||||
source_map,
|
||||
} = apply_fixes(diagnostics.iter(), &locator);
|
||||
|
||||
assert_eq!(
|
||||
contents,
|
||||
code,
|
||||
r#"
|
||||
class A(object):
|
||||
...
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
assert_eq!(fixed.values().sum::<usize>(), 2);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 2);
|
||||
assert_eq!(
|
||||
source_map.markers(),
|
||||
&[
|
||||
SourceMarker {
|
||||
source: 8.into(),
|
||||
dest: 8.into()
|
||||
},
|
||||
SourceMarker {
|
||||
source: 16.into(),
|
||||
dest: 8.into()
|
||||
},
|
||||
SourceMarker {
|
||||
source: 22.into(),
|
||||
dest: 14.into(),
|
||||
},
|
||||
SourceMarker {
|
||||
source: 30.into(),
|
||||
dest: 14.into(),
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -235,15 +371,32 @@ class A(object):
|
||||
Edit::deletion(TextSize::from(7), TextSize::from(15)),
|
||||
Edit::replacement("ignored".to_string(), TextSize::from(9), TextSize::from(11)),
|
||||
]);
|
||||
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
|
||||
let FixResult {
|
||||
code,
|
||||
fixes,
|
||||
source_map,
|
||||
} = apply_fixes(diagnostics.iter(), &locator);
|
||||
assert_eq!(
|
||||
contents,
|
||||
code,
|
||||
r#"
|
||||
class A:
|
||||
...
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
assert_eq!(fixed.values().sum::<usize>(), 1);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 1);
|
||||
assert_eq!(
|
||||
source_map.markers(),
|
||||
&[
|
||||
SourceMarker {
|
||||
source: 7.into(),
|
||||
dest: 7.into(),
|
||||
},
|
||||
SourceMarker {
|
||||
source: 15.into(),
|
||||
dest: 7.into(),
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
59
crates/ruff/src/autofix/source_map.rs
Normal file
59
crates/ruff/src/autofix/source_map.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use ruff_text_size::TextSize;
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
|
||||
/// Lightweight sourcemap marker representing the source and destination
|
||||
/// position for an [`Edit`].
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(crate) struct SourceMarker {
|
||||
/// Position of the marker in the original source.
|
||||
pub(crate) source: TextSize,
|
||||
/// Position of the marker in the transformed code.
|
||||
pub(crate) dest: TextSize,
|
||||
}
|
||||
|
||||
/// A collection of [`SourceMarker`].
|
||||
///
|
||||
/// Sourcemaps are used to map positions in the original source to positions in
|
||||
/// the transformed code. Here, only the boundaries of edits are tracked instead
|
||||
/// of every single character.
|
||||
#[derive(Default, PartialEq, Eq)]
|
||||
pub(crate) struct SourceMap(Vec<SourceMarker>);
|
||||
|
||||
impl SourceMap {
|
||||
/// Returns a slice of all the markers in the sourcemap in the order they
|
||||
/// were added.
|
||||
pub(crate) fn markers(&self) -> &[SourceMarker] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Push the start marker for an [`Edit`].
|
||||
///
|
||||
/// The `output_length` is the length of the transformed string before the
|
||||
/// edit is applied.
|
||||
pub(crate) fn push_start_marker(&mut self, edit: &Edit, output_length: TextSize) {
|
||||
self.0.push(SourceMarker {
|
||||
source: edit.start(),
|
||||
dest: output_length,
|
||||
});
|
||||
}
|
||||
|
||||
/// Push the end marker for an [`Edit`].
|
||||
///
|
||||
/// The `output_length` is the length of the transformed string after the
|
||||
/// edit has been applied.
|
||||
pub(crate) fn push_end_marker(&mut self, edit: &Edit, output_length: TextSize) {
|
||||
if edit.is_insertion() {
|
||||
self.0.push(SourceMarker {
|
||||
source: edit.start(),
|
||||
dest: output_length,
|
||||
});
|
||||
} else {
|
||||
// Deletion or replacement
|
||||
self.0.push(SourceMarker {
|
||||
source: edit.end(),
|
||||
dest: output_length,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
use ruff_text_size::TextRange;
|
||||
use rustpython_parser::ast::Expr;
|
||||
|
||||
use ruff_python_semantic::model::Snapshot;
|
||||
use ruff_python_semantic::{ScopeId, Snapshot};
|
||||
|
||||
/// A collection of AST nodes that are deferred for later analysis.
|
||||
/// Used to, e.g., store functions, whose bodies shouldn't be analyzed until all
|
||||
/// module-level definitions have been analyzed.
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct Deferred<'a> {
|
||||
pub(crate) scopes: Vec<ScopeId>,
|
||||
pub(crate) string_type_definitions: Vec<(TextRange, &'a str, Snapshot)>,
|
||||
pub(crate) future_type_definitions: Vec<(&'a Expr, Snapshot)>,
|
||||
pub(crate) functions: Vec<Snapshot>,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user