Compare commits
155 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3aa841fc9 | ||
|
|
fdd894145b | ||
|
|
85f67b2ee3 | ||
|
|
e9c6f16c56 | ||
|
|
d3b18345c5 | ||
|
|
0e4d174551 | ||
|
|
73efbeb581 | ||
|
|
2fb312bb2b | ||
|
|
e8e66f3824 | ||
|
|
a8d080c825 | ||
|
|
ddd541b198 | ||
|
|
3090aec97d | ||
|
|
14c6419bc1 | ||
|
|
3bc29d6c0c | ||
|
|
67c5086aba | ||
|
|
cd82b83f89 | ||
|
|
39fb2cc732 | ||
|
|
9c732c7946 | ||
|
|
2332ea5753 | ||
|
|
6b1062ccc3 | ||
|
|
39fa38cb35 | ||
|
|
ddf7de7e86 | ||
|
|
e5101e8eac | ||
|
|
d9c3f8e249 | ||
|
|
7e0d018b35 | ||
|
|
4b05ca1198 | ||
|
|
f0465bf106 | ||
|
|
8134ec25f0 | ||
|
|
6049aabe27 | ||
|
|
badade3ccc | ||
|
|
fa26860296 | ||
|
|
140e0acf54 | ||
|
|
c711db11ce | ||
|
|
1fe6954150 | ||
|
|
2414469ac3 | ||
|
|
838ba1ca3d | ||
|
|
8f3f8d3e0b | ||
|
|
8ba9eb83af | ||
|
|
2c6efc2f5f | ||
|
|
d6930ca991 | ||
|
|
f70c286e6a | ||
|
|
dcff515ad8 | ||
|
|
b9e387013f | ||
|
|
a69451ff46 | ||
|
|
01b372a75c | ||
|
|
cd2e7fa72a | ||
|
|
fdf0b999cd | ||
|
|
45b5fa573f | ||
|
|
a0258f2205 | ||
|
|
0a68636de3 | ||
|
|
2f53781a77 | ||
|
|
7e7be05ddf | ||
|
|
f5afa8198c | ||
|
|
eeabfd6d18 | ||
|
|
490301f9fe | ||
|
|
f5be3d8e5b | ||
|
|
7617519b4f | ||
|
|
bc7ddd8f3a | ||
|
|
e6bb5cddcf | ||
|
|
dcedd5cd9d | ||
|
|
606b6ac3df | ||
|
|
ebda9b31d9 | ||
|
|
52f6663089 | ||
|
|
a6176d2c70 | ||
|
|
1d165f7e9d | ||
|
|
e96092291d | ||
|
|
67076b2dcb | ||
|
|
7e3ba7f32a | ||
|
|
09dbd2029c | ||
|
|
1380bd94da | ||
|
|
c10a4535b9 | ||
|
|
97802e7466 | ||
|
|
4fd4a65718 | ||
|
|
d78c614764 | ||
|
|
3f3dd7af99 | ||
|
|
871b92a385 | ||
|
|
9158f13ee6 | ||
|
|
72e0ffc1ac | ||
|
|
ffcf0618c7 | ||
|
|
1ccef5150d | ||
|
|
6a52577630 | ||
|
|
3c2f41b615 | ||
|
|
b76b4b6016 | ||
|
|
bbadbb5de5 | ||
|
|
ba6370e5d0 | ||
|
|
be6e00ef6e | ||
|
|
865205d992 | ||
|
|
572adf7994 | ||
|
|
3b26bf84f5 | ||
|
|
f4f88308ae | ||
|
|
ea3d3a655d | ||
|
|
fd34797d0f | ||
|
|
6532455672 | ||
|
|
257c571c43 | ||
|
|
ccdee55e6e | ||
|
|
6d6d7abf70 | ||
|
|
0096938789 | ||
|
|
853d8354cb | ||
|
|
5f64d2346f | ||
|
|
ddbe5a1243 | ||
|
|
04097d194c | ||
|
|
a2b8487ae3 | ||
|
|
8969ad5879 | ||
|
|
bfa1c28c00 | ||
|
|
cf7aa26aa4 | ||
|
|
d66ce76691 | ||
|
|
b8bb9e8b92 | ||
|
|
5e46dcbf21 | ||
|
|
045449ab12 | ||
|
|
d5ff8d7c43 | ||
|
|
d92fb11e80 | ||
|
|
3d947196f8 | ||
|
|
e846f2688b | ||
|
|
7b91a162c6 | ||
|
|
8c2cfade90 | ||
|
|
a435c0df4b | ||
|
|
48e1852893 | ||
|
|
03f141f53d | ||
|
|
8dea47afc1 | ||
|
|
d3b71f1e04 | ||
|
|
04e8e74499 | ||
|
|
318653c427 | ||
|
|
f08fd5cbf0 | ||
|
|
99a755f936 | ||
|
|
e7dfb35778 | ||
|
|
085fd37209 | ||
|
|
83536cf87b | ||
|
|
9366eb919d | ||
|
|
8be51942dd | ||
|
|
d365dab904 | ||
|
|
f23851130a | ||
|
|
efdf383f5e | ||
|
|
61f21a6513 | ||
|
|
43d6aa9173 | ||
|
|
c54e48dce5 | ||
|
|
b913e99bde | ||
|
|
4ac506526b | ||
|
|
cd41de2588 | ||
|
|
3344d367f5 | ||
|
|
d7a369e7dc | ||
|
|
1b1788c8ad | ||
|
|
4d5a339d9e | ||
|
|
0801f14046 | ||
|
|
edaf891042 | ||
|
|
3beff29026 | ||
|
|
5ac2c7d293 | ||
|
|
e66fdb83d0 | ||
|
|
a95bafefb0 | ||
|
|
539af34f58 | ||
|
|
983bb31577 | ||
|
|
b98b604071 | ||
|
|
cd27b39aff | ||
|
|
a9fc648faf | ||
|
|
c1f0661225 | ||
|
|
2c91412321 |
@@ -33,4 +33,5 @@ rustflags = [
|
||||
"-Wclippy::rc_buffer",
|
||||
"-Wclippy::rc_mutex",
|
||||
"-Wclippy::rest_pat_in_fully_bound_structs",
|
||||
"-Wunreachable_pub"
|
||||
]
|
||||
|
||||
26
.github/workflows/ci.yaml
vendored
26
.github/workflows/ci.yaml
vendored
@@ -15,6 +15,8 @@ env:
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
PACKAGE_NAME: ruff
|
||||
PYTHON_VERSION: "3.7" # to build abi3 wheels
|
||||
|
||||
jobs:
|
||||
cargo-fmt:
|
||||
@@ -194,6 +196,28 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
python-package:
|
||||
name: "python package"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
manylinux: auto
|
||||
args: --out dist
|
||||
- name: "Test wheel"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
|
||||
pre-commit:
|
||||
name: "pre-commit"
|
||||
runs-on: ubuntu-latest
|
||||
@@ -237,5 +261,7 @@ jobs:
|
||||
run: python scripts/transform_readme.py --target mkdocs
|
||||
- name: "Generate docs"
|
||||
run: python scripts/generate_mkdocs.py
|
||||
- name: "Check docs formatting"
|
||||
run: python scripts/check_docs_formatted.py
|
||||
- name: "Build docs"
|
||||
run: mkdocs build --strict
|
||||
|
||||
103
.github/workflows/release.yaml
vendored
103
.github/workflows/release.yaml
vendored
@@ -18,6 +18,31 @@ env:
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
|
||||
jobs:
|
||||
sdist:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build sdist"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
command: sdist
|
||||
args: --out dist
|
||||
- name: "Test sdist"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.tar.gz --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Upload sdist"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
|
||||
macos-x86_64:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
@@ -32,10 +57,12 @@ jobs:
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --out dist --sdist
|
||||
- name: "Install built wheel - x86_64"
|
||||
args: --release --out dist
|
||||
- name: "Test wheel - x86_64"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -43,9 +70,9 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-x86_64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/x86_64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
ARCHIVE_FILE=ruff-x86_64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/x86_64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -68,9 +95,11 @@ jobs:
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
args: --release --universal2 --out dist
|
||||
- name: "Install built wheel - universal2"
|
||||
- name: "Test wheel - universal2"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*universal2.whl --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -78,9 +107,9 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-aarch64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/aarch64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
ARCHIVE_FILE=ruff-aarch64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/aarch64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -113,11 +142,13 @@ jobs:
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: --release --out dist
|
||||
- name: "Install built wheel"
|
||||
- name: "Test wheel"
|
||||
if: ${{ !startsWith(matrix.platform.target, 'aarch64') }}
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -126,9 +157,9 @@ jobs:
|
||||
- name: "Archive binary"
|
||||
shell: bash
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.zip
|
||||
7z a $ARCHIVE_FILE ./target/${{ matrix.platform.target }}/release/ruff.exe
|
||||
sha256sum $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.zip
|
||||
7z a $ARCHIVE_FILE ./target/${{ matrix.platform.target }}/release/ruff.exe
|
||||
sha256sum $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -158,10 +189,12 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist
|
||||
- name: "Install built wheel"
|
||||
- name: "Test wheel"
|
||||
if: ${{ startsWith(matrix.target, 'x86_64') }}
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -169,9 +202,9 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
ARCHIVE_FILE=ruff-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -187,6 +220,9 @@ jobs:
|
||||
platform:
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
arch: aarch64
|
||||
# see https://github.com/charliermarsh/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
|
||||
arch: armv7
|
||||
- target: s390x-unknown-linux-gnu
|
||||
@@ -195,6 +231,7 @@ jobs:
|
||||
arch: ppc64le
|
||||
- target: powerpc64-unknown-linux-gnu
|
||||
arch: ppc64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
@@ -207,10 +244,11 @@ jobs:
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: auto
|
||||
docker-options: ${{ matrix.platform.maturin_docker_options }}
|
||||
args: --release --out dist
|
||||
- uses: uraimo/run-on-arch-action@v2
|
||||
if: matrix.platform.arch != 'ppc64'
|
||||
name: Install built wheel
|
||||
name: Test wheel
|
||||
with:
|
||||
arch: ${{ matrix.platform.arch }}
|
||||
distro: ubuntu20.04
|
||||
@@ -221,6 +259,7 @@ jobs:
|
||||
pip3 install -U pip
|
||||
run: |
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
ruff --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -228,9 +267,9 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -260,7 +299,7 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --out dist
|
||||
- name: "Install built wheel"
|
||||
- name: "Test wheel"
|
||||
if: matrix.target == 'x86_64-unknown-linux-musl'
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
@@ -269,6 +308,8 @@ jobs:
|
||||
run: |
|
||||
apk add py3-pip
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links /io/dist/ --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -276,9 +317,9 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
ARCHIVE_FILE=ruff-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -294,8 +335,10 @@ jobs:
|
||||
platform:
|
||||
- target: aarch64-unknown-linux-musl
|
||||
arch: aarch64
|
||||
maturin_docker_options: -e JEMALLOC_SYS_WITH_LG_PAGE=16
|
||||
- target: armv7-unknown-linux-musleabihf
|
||||
arch: armv7
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
@@ -309,8 +352,9 @@ jobs:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --out dist
|
||||
docker-options: ${{ matrix.platform.maturin_docker_options }}
|
||||
- uses: uraimo/run-on-arch-action@v2
|
||||
name: Install built wheel
|
||||
name: Test wheel
|
||||
with:
|
||||
arch: ${{ matrix.platform.arch }}
|
||||
distro: alpine_latest
|
||||
@@ -319,6 +363,7 @@ jobs:
|
||||
apk add py3-pip
|
||||
run: |
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
ruff check --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -326,9 +371,9 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,7 +3,8 @@
|
||||
crates/ruff/resources/test/cpython
|
||||
mkdocs.yml
|
||||
.overrides
|
||||
github_search.jsonl
|
||||
ruff-old
|
||||
github_search*.jsonl
|
||||
|
||||
###
|
||||
# Rust.gitignore
|
||||
|
||||
@@ -63,11 +63,6 @@ repos:
|
||||
rev: 23.1.0
|
||||
hooks:
|
||||
- id: black
|
||||
exclude: |
|
||||
(?x)^(
|
||||
crates/ruff/resources/.*|
|
||||
crates/ruff_python_formatter/resources/.*
|
||||
)$
|
||||
|
||||
ci:
|
||||
skip: [cargo-fmt, clippy, dev-generate-all]
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.0.268
|
||||
|
||||
### The `keep-runtime-typing` setting has been removed ([#4427](https://github.com/charliermarsh/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
|
||||
need for a dedicated setting to disable these rules, the `keep-runtime-typing` option has been
|
||||
removed.
|
||||
|
||||
## 0.0.267
|
||||
|
||||
### `update-check` is no longer a valid configuration option ([#4313](https://github.com/charliermarsh/ruff/pull/4313))
|
||||
|
||||
The `update-check` functionality was deprecated in [#2530](https://github.com/charliermarsh/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.,
|
||||
`update-check = false`) or through the command-line, since it has no effect. Users should remove
|
||||
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))
|
||||
|
||||
@@ -106,7 +106,7 @@ At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
|
||||
1. In that file, define a violation struct. You can grep for `#[violation]` to see examples.
|
||||
1. Map the violation struct to a rule code in `crates/ruff/src/registry.rs` (e.g., `E402`).
|
||||
1. Define the logic for triggering the violation in `crates/ruff/src/checkers/ast.rs` (for AST-based
|
||||
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).
|
||||
|
||||
667
Cargo.lock
generated
667
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = { version = "1.0.69" }
|
||||
bitflags = { version = "2.1.0" }
|
||||
bitflags = { version = "2.2.1" }
|
||||
chrono = { version = "0.4.23", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.1.8", features = ["derive"] }
|
||||
colored = { version = "2.0.0" }
|
||||
@@ -30,10 +30,11 @@ path-absolutize = { version = "3.0.14" }
|
||||
proc-macro2 = { version = "1.0.51" }
|
||||
quote = { version = "1.0.23" }
|
||||
regex = { version = "1.7.1" }
|
||||
ruff_text_size = { git = "https://github.com/charliermarsh/RustPython.git", rev = "c3147d2c1524ebd0e90cf1c2938d770314fd5a5a" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-common = { git = "https://github.com/charliermarsh/RustPython.git", rev = "c3147d2c1524ebd0e90cf1c2938d770314fd5a5a" }
|
||||
rustpython-parser = { git = "https://github.com/charliermarsh/RustPython.git", rev = "c3147d2c1524ebd0e90cf1c2938d770314fd5a5a" }
|
||||
ruff_text_size = { git = "https://github.com/RustPython/Parser.git", rev = "e820928f11a2453314ad4d5ce23f066d1d3faf73" }
|
||||
rustpython-format = { git = "https://github.com/RustPython/Parser.git", rev = "e820928f11a2453314ad4d5ce23f066d1d3faf73" }
|
||||
rustpython-literal = { git = "https://github.com/RustPython/Parser.git", rev = "e820928f11a2453314ad4d5ce23f066d1d3faf73" }
|
||||
rustpython-parser = { git = "https://github.com/RustPython/Parser.git", rev = "e820928f11a2453314ad4d5ce23f066d1d3faf73", default-features = false, features = ["full-lexer", "all-nodes-with-ranges"] }
|
||||
schemars = { version = "0.8.12" }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = { version = "1.0.93", features = ["preserve_order"] }
|
||||
|
||||
80
LICENSE
80
LICENSE
@@ -354,6 +354,37 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-todos, licensed as follows:
|
||||
"""
|
||||
Copyright (c) 2019 EclecticIQ. All rights reserved.
|
||||
Copyright (c) 2020 Gram <gram@orsinium.dev>. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
|
||||
- flake8-unused-arguments, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
@@ -550,6 +581,30 @@ are:
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
|
||||
- flynt, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019-2022 Ilya Kamenshchikov
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
- isort, licensed as follows:
|
||||
"""
|
||||
@@ -759,6 +814,31 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-async, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Cooper Lees
|
||||
|
||||
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-type-checking, licensed as follows:
|
||||
"""
|
||||
Copyright (c) 2021, Sondre Lillebø Gundersen
|
||||
|
||||
46
README.md
46
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
# Ruff
|
||||
|
||||
[](https://github.com/charliermarsh/ruff)
|
||||
[](https://github.com/charliermarsh/ruff)
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
@@ -137,7 +137,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.0.265'
|
||||
rev: 'v0.0.267'
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -183,6 +183,7 @@ exclude = [
|
||||
".direnv",
|
||||
".eggs",
|
||||
".git",
|
||||
".git-rewrite",
|
||||
".hg",
|
||||
".mypy_cache",
|
||||
".nox",
|
||||
@@ -248,6 +249,7 @@ quality tools, including:
|
||||
- [eradicate](https://pypi.org/project/eradicate/)
|
||||
- [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-blind-except](https://pypi.org/project/flake8-blind-except/)
|
||||
- [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap/)
|
||||
@@ -262,6 +264,7 @@ quality tools, including:
|
||||
- [flake8-eradicate](https://pypi.org/project/flake8-eradicate/)
|
||||
- [flake8-errmsg](https://pypi.org/project/flake8-errmsg/)
|
||||
- [flake8-executable](https://pypi.org/project/flake8-executable/)
|
||||
- [flake8-future-annotations](https://pypi.org/project/flake8-future-annotations/)
|
||||
- [flake8-gettext](https://pypi.org/project/flake8-gettext/)
|
||||
- [flake8-implicit-str-concat](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
- [flake8-import-conventions](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
@@ -278,14 +281,16 @@ quality tools, including:
|
||||
- [flake8-simplify](https://pypi.org/project/flake8-simplify/)
|
||||
- [flake8-super](https://pypi.org/project/flake8-super/)
|
||||
- [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/)
|
||||
- [flake8-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))
|
||||
- [isort](https://pypi.org/project/isort/)
|
||||
- [mccabe](https://pypi.org/project/mccabe/)
|
||||
- [pandas-vet](https://pypi.org/project/pandas-vet/)
|
||||
- [pep8-naming](https://pypi.org/project/pep8-naming/)
|
||||
- [pydocstyle](https://pypi.org/project/pydocstyle/)
|
||||
- [pygrep-hooks](https://github.com/pre-commit/pygrep-hooks) ([#980](https://github.com/charliermarsh/ruff/issues/980))
|
||||
- [pygrep-hooks](https://github.com/pre-commit/pygrep-hooks)
|
||||
- [pyupgrade](https://pypi.org/project/pyupgrade/)
|
||||
- [tryceratops](https://pypi.org/project/tryceratops/)
|
||||
- [yesqa](https://pypi.org/project/yesqa/)
|
||||
@@ -341,9 +346,9 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [Babel](https://github.com/python-babel/babel)
|
||||
- [Bokeh](https://github.com/bokeh/bokeh)
|
||||
- [Cryptography (PyCA)](https://github.com/pyca/cryptography)
|
||||
- [DVC](https://github.com/iterative/dvc)
|
||||
- [Dagger](https://github.com/dagger/dagger)
|
||||
- [Dagster](https://github.com/dagster-io/dagster)
|
||||
- [DVC](https://github.com/iterative/dvc)
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Gradio](https://github.com/gradio-app/gradio)
|
||||
- [Great Expectations](https://github.com/great-expectations/great_expectations)
|
||||
@@ -359,7 +364,7 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- Modern Treasury ([Python SDK](https://github.com/Modern-Treasury/modern-treasury-python-sdk))
|
||||
- Mozilla ([Firefox](https://github.com/mozilla/gecko-dev))
|
||||
- [MegaLinter](https://github.com/oxsecurity/megalinter)
|
||||
- Microsoft ([Semantic Kernel](https://github.com/microsoft/semantic-kernel), [ONNX Runtime](https://github.com/microsoft/onnxruntime))
|
||||
- Microsoft ([Semantic Kernel](https://github.com/microsoft/semantic-kernel), [ONNX Runtime](https://github.com/microsoft/onnxruntime), [LightGBM](https://github.com/microsoft/LightGBM))
|
||||
- Netflix ([Dispatch](https://github.com/Netflix/dispatch))
|
||||
- [Neon](https://github.com/neondatabase/neon)
|
||||
- [ONNX](https://github.com/onnx/onnx)
|
||||
@@ -371,8 +376,9 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [Polars](https://github.com/pola-rs/polars)
|
||||
- [PostHog](https://github.com/PostHog/posthog)
|
||||
- Prefect ([Python SDK](https://github.com/PrefectHQ/prefect), [Marvin](https://github.com/PrefectHQ/marvin))
|
||||
- [Pydantic](https://github.com/pydantic/pydantic)
|
||||
- [PyInstaller](https://github.com/pyinstaller/pyinstaller)
|
||||
- [PyTorch](https://github.com/pytorch/pytorch)
|
||||
- [Pydantic](https://github.com/pydantic/pydantic)
|
||||
- [Pylint](https://github.com/PyCQA/pylint)
|
||||
- [Pynecone](https://github.com/pynecone-io/pynecone)
|
||||
- [Robyn](https://github.com/sansyrox/robyn)
|
||||
@@ -395,6 +401,34 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [meson-python](https://github.com/mesonbuild/meson-python)
|
||||
- [nox](https://github.com/wntrblm/nox)
|
||||
|
||||
### Show Your Support
|
||||
|
||||
If you're using Ruff, consider adding the Ruff badge to project's `README.md`:
|
||||
|
||||
```md
|
||||
[](https://github.com/charliermarsh/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
|
||||
: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>
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
<div align="center">
|
||||
<a target="_blank" href="https://astral.sh" style="background:none">
|
||||
<img src="https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/svg/Astral.svg">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
8
assets/badge/v2.json
Normal file
8
assets/badge/v2.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"label": "",
|
||||
"message": "Ruff",
|
||||
"logoSvg": "<svg width=\"510\" height=\"622\" viewBox=\"0 0 510 622\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M206.701 0C200.964 0 196.314 4.64131 196.314 10.3667V41.4667C196.314 47.192 191.663 51.8333 185.927 51.8333H156.843C151.107 51.8333 146.456 56.4746 146.456 62.2V145.133C146.456 150.859 141.806 155.5 136.069 155.5H106.986C101.249 155.5 96.5988 160.141 96.5988 165.867V222.883C96.5988 228.609 91.9484 233.25 86.2118 233.25H57.1283C51.3917 233.25 46.7413 237.891 46.7413 243.617V300.633C46.7413 306.359 42.0909 311 36.3544 311H10.387C4.6504 311 0 315.641 0 321.367V352.467C0 358.192 4.6504 362.833 10.387 362.833H145.418C151.154 362.833 155.804 367.475 155.804 373.2V430.217C155.804 435.942 151.154 440.583 145.418 440.583H116.334C110.597 440.583 105.947 445.225 105.947 450.95V507.967C105.947 513.692 101.297 518.333 95.5601 518.333H66.4766C60.74 518.333 56.0896 522.975 56.0896 528.7V611.633C56.0896 617.359 60.74 622 66.4766 622H149.572C155.309 622 159.959 617.359 159.959 611.633V570.167H201.507C207.244 570.167 211.894 565.525 211.894 559.8V528.7C211.894 522.975 216.544 518.333 222.281 518.333H251.365C257.101 518.333 261.752 513.692 261.752 507.967V476.867C261.752 471.141 266.402 466.5 272.138 466.5H301.222C306.959 466.5 311.609 461.859 311.609 456.133V425.033C311.609 419.308 316.259 414.667 321.996 414.667H351.079C356.816 414.667 361.466 410.025 361.466 404.3V373.2C361.466 367.475 366.117 362.833 371.853 362.833H400.937C406.673 362.833 411.324 358.192 411.324 352.467V321.367C411.324 315.641 415.974 311 421.711 311H450.794C456.531 311 461.181 306.359 461.181 300.633V217.7C461.181 211.975 456.531 207.333 450.794 207.333H420.672C414.936 207.333 410.285 202.692 410.285 196.967V165.867C410.285 160.141 414.936 155.5 420.672 155.5H449.756C455.492 155.5 460.143 150.859 460.143 145.133V114.033C460.143 108.308 464.793 103.667 470.53 103.667H499.613C505.35 103.667 510 99.0253 510 93.3V10.3667C510 4.64132 505.35 0 499.613 0H206.701ZM168.269 440.583C162.532 440.583 157.882 445.225 157.882 450.95V507.967C157.882 513.692 153.231 518.333 147.495 518.333H118.411C112.675 518.333 108.024 522.975 108.024 528.7V559.8C108.024 565.525 112.675 570.167 118.411 570.167H159.959V528.7C159.959 522.975 164.61 518.333 170.346 518.333H199.43C205.166 518.333 209.817 513.692 209.817 507.967V476.867C209.817 471.141 214.467 466.5 220.204 466.5H249.287C255.024 466.5 259.674 461.859 259.674 456.133V425.033C259.674 419.308 264.325 414.667 270.061 414.667H299.145C304.881 414.667 309.532 410.025 309.532 404.3V373.2C309.532 367.475 314.182 362.833 319.919 362.833H349.002C354.739 362.833 359.389 358.192 359.389 352.467V321.367C359.389 315.641 364.039 311 369.776 311H398.859C404.596 311 409.246 306.359 409.246 300.633V269.533C409.246 263.808 404.596 259.167 398.859 259.167H318.88C313.143 259.167 308.493 254.525 308.493 248.8V217.7C308.493 211.975 313.143 207.333 318.88 207.333H347.963C353.7 207.333 358.35 202.692 358.35 196.967V165.867C358.35 160.141 363.001 155.5 368.737 155.5H397.821C403.557 155.5 408.208 150.859 408.208 145.133V114.033C408.208 108.308 412.858 103.667 418.595 103.667H447.678C453.415 103.667 458.065 99.0253 458.065 93.3V62.2C458.065 56.4746 453.415 51.8333 447.678 51.8333H208.778C203.041 51.8333 198.391 56.4746 198.391 62.2V145.133C198.391 150.859 193.741 155.5 188.004 155.5H158.921C153.184 155.5 148.534 160.141 148.534 165.867V222.883C148.534 228.609 143.883 233.25 138.147 233.25H109.063C103.327 233.25 98.6762 237.891 98.6762 243.617V300.633C98.6762 306.359 103.327 311 109.063 311H197.352C203.089 311 207.739 315.641 207.739 321.367V430.217C207.739 435.942 203.089 440.583 197.352 440.583H168.269Z\" fill=\"#D7FF64\"/></svg>",
|
||||
"logoWidth": 10,
|
||||
"labelColor": "grey",
|
||||
"color": "#261230"
|
||||
}
|
||||
24
assets/svg/Astral.svg
Normal file
24
assets/svg/Astral.svg
Normal file
@@ -0,0 +1,24 @@
|
||||
<svg width="139" height="24" viewBox="0 0 139 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="138.764" height="24" rx="2.18182" fill="#261230"/>
|
||||
<path
|
||||
d="M8.72798 15.2726H9.91316V11.8697L9.6887 10.4062L9.8952 10.3343L12.1309 15.1649L14.3486 10.3343L14.5461 10.4062L14.3486 11.8607V15.2726H15.5248V8.72714H13.9535L12.2117 12.7137H12.0142L10.2723 8.72714H8.72798V15.2726Z"
|
||||
fill="#D7FF64"/>
|
||||
<path
|
||||
d="M22.3432 15.2726H23.6631L21.3017 8.72714H19.7574L17.4589 15.2726H18.7069L19.1558 13.9797H21.9033L22.3432 15.2726ZM19.497 13.0279L19.901 11.8607L20.4308 10.0021H20.6463L21.176 11.8607L21.5711 13.0279H19.497Z"
|
||||
fill="#D7FF64"/>
|
||||
<path
|
||||
d="M25.4209 15.2726H28.1234C30.1077 15.2726 30.9876 14.1413 30.9876 12.0044C30.9876 9.92131 30.1706 8.72714 28.1234 8.72714H25.4209V15.2726ZM26.624 14.2131V9.77765H28.0965C29.147 9.77765 29.7306 10.1907 29.7306 11.4477V12.5521C29.7306 13.6923 29.2817 14.2131 28.0965 14.2131H26.624Z"
|
||||
fill="#D7FF64"/>
|
||||
<path
|
||||
d="M33.079 15.2726H37.6491V14.2131H34.2822V12.3815H37.2002V11.3938H34.2822V9.77765H37.6491V8.72714H33.079V15.2726Z"
|
||||
fill="#D7FF64"/>
|
||||
<path
|
||||
d="M42.923 15.2726H46.2451C47.4572 15.2726 48.2025 14.5812 48.2025 13.5487C48.2025 12.7675 47.8343 12.175 47.0532 11.9954V11.7799C47.6637 11.5734 48.0319 11.0436 48.0319 10.3433C48.0319 9.38259 47.4572 8.72714 46.281 8.72714H42.923V15.2726ZM44.0992 11.4746V9.65195H45.9578C46.4875 9.65195 46.7928 9.92131 46.7928 10.3523V10.7653C46.7928 11.1873 46.4965 11.4746 45.9758 11.4746H44.0992ZM44.0992 14.3388V12.3904H46.0296C46.5863 12.3904 46.9365 12.6418 46.9365 13.1806V13.5666C46.9365 14.0425 46.5684 14.3388 45.9309 14.3388H44.0992Z"
|
||||
fill="#D7FF64"/>
|
||||
<path
|
||||
d="M49.6959 8.72714L52.174 12.579V14.1952H50.1898V15.2726H53.3772V12.579L55.8553 8.72714H54.4456L53.5119 10.2535L52.8744 11.3759H52.6679L52.0483 10.2715L51.1056 8.72714H49.6959Z"
|
||||
fill="#D7FF64"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M74.1824 7.63626C74.1824 7.03377 74.6708 6.54535 75.2733 6.54535H84.0006C84.6031 6.54535 85.0915 7.03377 85.0915 7.63626V9.81808H80.0733V8.94535H79.2006V10.6908H84.0006C84.6031 10.6908 85.0915 11.1792 85.0915 11.7817V16.3635C85.0915 16.966 84.6031 17.4544 84.0006 17.4544H75.2733C74.6708 17.4544 74.1824 16.966 74.1824 16.3635V14.1817L79.2006 14.1817V15.0544H80.0733V13.309L75.2733 13.309C74.6708 13.309 74.1824 12.8206 74.1824 12.2181V7.63626ZM63.4912 6.54545C62.8887 6.54545 62.4003 7.03387 62.4003 7.63636V17.4545H67.4185V14.1818H68.2912V17.4545H73.3094V7.63636C73.3094 7.03387 72.821 6.54545 72.2185 6.54545H63.4912ZM69.164 10.6909V11.5636H66.5458V10.6909H69.164ZM110.619 6.54545C110.016 6.54545 109.528 7.03387 109.528 7.63636V17.4545H114.546V14.1818H115.419V17.4545H120.437V7.63636C120.437 7.03387 119.948 6.54545 119.346 6.54545H110.619ZM116.291 10.6909V11.5636H113.673V10.6909H116.291ZM91.8549 8.29091H96.8731V11.3455C96.8731 11.9479 96.3847 12.4364 95.7822 12.4364H91.8549V13.3091H96.8731V17.4545H87.9276C87.3251 17.4545 86.8367 16.9661 86.8367 16.3636V12.4364H85.964V8.29091H86.8367V6.54545H91.8549V8.29091ZM108.655 7.63636C108.655 7.03387 108.166 6.54545 107.564 6.54545H97.7458V17.4545H102.764V14.1818H103.637V17.4545H108.655V13.3091H106.473V12.4364H107.564C108.166 12.4364 108.655 11.9479 108.655 11.3455V7.63636ZM104.509 10.6909V11.5636H101.891V10.6909H104.509ZM132.218 13.3091L126.327 13.3091V6.54547L121.309 6.54547V17.4546H132.218V13.3091Z"
|
||||
fill="#D7FF64"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.265"
|
||||
version = "0.0.267"
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ requires-python = ">=3.7"
|
||||
repository = "https://github.com/charliermarsh/ruff#subdirectory=crates/flake8_to_ruff"
|
||||
|
||||
[build-system]
|
||||
requires = ["maturin>=0.14,<0.15"]
|
||||
requires = ["maturin>=0.15.2,<0.16"]
|
||||
build-backend = "maturin"
|
||||
|
||||
[tool.maturin]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.265"
|
||||
version = "0.0.267"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
@@ -54,9 +54,9 @@ quick-junit = { version = "0.3.2" }
|
||||
regex = { workspace = true }
|
||||
result-like = { version = "0.4.6" }
|
||||
rustc-hash = { workspace = true }
|
||||
rustpython-common = { workspace = true }
|
||||
rustpython-format = { workspace = true }
|
||||
rustpython-parser = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
@@ -80,5 +80,6 @@ colored = { workspace = true, features = ["no-color"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
logical_lines = []
|
||||
schemars = ["dep:schemars"]
|
||||
jupyter_notebook = []
|
||||
ecosystem_ci = []
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Any, Type
|
||||
from typing_extensions import override
|
||||
|
||||
# Error
|
||||
def foo(a, b):
|
||||
@@ -94,6 +95,31 @@ class Foo:
|
||||
def foo(self: "Foo", a: int, *params: str, **options: Any) -> int:
|
||||
pass
|
||||
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: Any, *params: str, **options: str) -> int:
|
||||
pass
|
||||
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: int, *params: str, **options: str) -> Any:
|
||||
pass
|
||||
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: int, *params: Any, **options: Any) -> int:
|
||||
pass
|
||||
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: int, *params: Any, **options: str) -> int:
|
||||
pass
|
||||
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: int, *params: str, **options: Any) -> int:
|
||||
pass
|
||||
|
||||
# OK
|
||||
@classmethod
|
||||
def foo(cls: Type["Foo"], a: int, b: int) -> int:
|
||||
|
||||
23
crates/ruff/resources/test/fixtures/flake8_async/ASYNC100.py
vendored
Normal file
23
crates/ruff/resources/test/fixtures/flake8_async/ASYNC100.py
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import urllib.request
|
||||
import requests
|
||||
import httpx
|
||||
|
||||
|
||||
async def foo():
|
||||
urllib.request.urlopen("http://example.com/foo/bar").read()
|
||||
|
||||
|
||||
async def foo():
|
||||
requests.get()
|
||||
|
||||
|
||||
async def foo():
|
||||
httpx.get()
|
||||
|
||||
|
||||
async def foo():
|
||||
requests.post()
|
||||
|
||||
|
||||
async def foo():
|
||||
httpx.post()
|
||||
31
crates/ruff/resources/test/fixtures/flake8_async/ASYNC101.py
vendored
Normal file
31
crates/ruff/resources/test/fixtures/flake8_async/ASYNC101.py
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
|
||||
async def foo():
|
||||
open("foo")
|
||||
|
||||
|
||||
async def foo():
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
async def foo():
|
||||
subprocess.run("foo")
|
||||
|
||||
|
||||
async def foo():
|
||||
subprocess.call("foo")
|
||||
|
||||
|
||||
async def foo():
|
||||
subprocess.foo(0)
|
||||
|
||||
|
||||
async def foo():
|
||||
os.wait4(10)
|
||||
|
||||
|
||||
async def foo():
|
||||
os.wait(12)
|
||||
13
crates/ruff/resources/test/fixtures/flake8_async/ASYNC102.py
vendored
Normal file
13
crates/ruff/resources/test/fixtures/flake8_async/ASYNC102.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import os
|
||||
|
||||
|
||||
async def foo():
|
||||
os.popen()
|
||||
|
||||
|
||||
async def foo():
|
||||
os.spawnl()
|
||||
|
||||
|
||||
async def foo():
|
||||
os.fspath("foo")
|
||||
@@ -74,8 +74,8 @@ def query40():
|
||||
|
||||
def query41():
|
||||
return (
|
||||
"SELECT *"
|
||||
"FROM table"
|
||||
"SELECT * "
|
||||
"FROM table "
|
||||
f"WHERE var = {var}"
|
||||
)
|
||||
|
||||
@@ -84,7 +84,7 @@ query42 = cursor.execute("SELECT * FROM table WHERE var = %s" % var)
|
||||
query43 = cursor.execute(f"SELECT * FROM table WHERE var = {var}")
|
||||
query44 = cursor.execute("SELECT * FROM table WHERE var = {}".format(var))
|
||||
query45 = cursor.executemany("SELECT * FROM table WHERE var = %s" % var, [])
|
||||
|
||||
|
||||
# # pass
|
||||
query = "SELECT * FROM table WHERE id = 1"
|
||||
query = "DELETE FROM table WHERE id = 1"
|
||||
@@ -93,3 +93,12 @@ query = "UPDATE table SET id = 1"
|
||||
cursor.execute('SELECT * FROM table WHERE id = %s', var)
|
||||
cursor.execute('SELECT * FROM table WHERE id = 1')
|
||||
cursor.executemany('SELECT * FROM table WHERE id = %s', [var, var2])
|
||||
|
||||
# # INSERT without INTO (e.g. MySQL and derivatives)
|
||||
query = "INSERT table VALUES (%s)" % (var,)
|
||||
|
||||
# # REPLACE (e.g. MySQL and derivatives, SQLite)
|
||||
query = "REPLACE INTO table VALUES (%s)" % (var,)
|
||||
query = "REPLACE table VALUES (%s)" % (var,)
|
||||
|
||||
query = "Deselect something that is not SQL even though it has a ' from ' somewhere in %s." % "there"
|
||||
|
||||
@@ -4,7 +4,12 @@ B027 - on lines 13, 16, 19, 23
|
||||
"""
|
||||
import abc
|
||||
from abc import ABC
|
||||
from abc import abstractmethod, abstractproperty
|
||||
from abc import (
|
||||
abstractmethod,
|
||||
abstractproperty,
|
||||
abstractclassmethod,
|
||||
abstractstaticmethod,
|
||||
)
|
||||
from abc import abstractmethod as notabstract
|
||||
from abc import abstractproperty as notabstract_property
|
||||
|
||||
@@ -55,6 +60,22 @@ class AbstractClass(ABC):
|
||||
def abstract_6(self):
|
||||
...
|
||||
|
||||
@abstractclassmethod
|
||||
def abstract_7(self):
|
||||
pass
|
||||
|
||||
@abc.abstractclassmethod
|
||||
def abstract_8(self):
|
||||
...
|
||||
|
||||
@abstractstaticmethod
|
||||
def abstract_9(self):
|
||||
pass
|
||||
|
||||
@abc.abstractstaticmethod
|
||||
def abstract_10(self):
|
||||
...
|
||||
|
||||
def body_1(self):
|
||||
print("foo")
|
||||
...
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
"""
|
||||
Should emit:
|
||||
B027 - on lines 13, 16, 19, 23
|
||||
"""
|
||||
from abc import ABC
|
||||
|
||||
|
||||
class AbstractClass(ABC):
|
||||
def empty_1(self): # error
|
||||
...
|
||||
|
||||
def empty_2(self): # error
|
||||
pass
|
||||
|
||||
def body_1(self):
|
||||
print("foo")
|
||||
...
|
||||
|
||||
def body_2(self):
|
||||
self.body_1()
|
||||
|
||||
|
||||
def foo():
|
||||
class InnerAbstractClass(ABC):
|
||||
def empty_1(self): # error
|
||||
...
|
||||
|
||||
def empty_2(self): # error
|
||||
pass
|
||||
|
||||
def body_1(self):
|
||||
print("foo")
|
||||
...
|
||||
|
||||
def body_2(self):
|
||||
self.body_1()
|
||||
|
||||
return InnerAbstractClass
|
||||
|
||||
@@ -631,3 +631,11 @@ result = function(
|
||||
the_first_one = next(
|
||||
(i for i in range(10) if i // 2 == 0) # COM812 fix should include the final bracket
|
||||
)
|
||||
|
||||
foo = namedtuple(
|
||||
name="foo",
|
||||
status="bar",
|
||||
message="sfdsdfsdgs fsdfsdf output!dsfdfsdjkg ghfskdjghkdssd sd fsdf s\n"[
|
||||
:20
|
||||
],
|
||||
)
|
||||
|
||||
@@ -17,3 +17,23 @@ all((x.id for x in bar))
|
||||
|
||||
async def f() -> bool:
|
||||
return all([await use_greeting(greeting) for greeting in await greetings()])
|
||||
|
||||
|
||||
# Special comment handling
|
||||
any(
|
||||
[ # lbracket comment
|
||||
# second line comment
|
||||
i.bit_count()
|
||||
# random middle comment
|
||||
for i in range(5) # rbracket comment
|
||||
] # rpar comment
|
||||
# trailing comment
|
||||
)
|
||||
|
||||
# Weird case where the function call, opening bracket, and comment are all
|
||||
# on the same line.
|
||||
any([ # lbracket comment
|
||||
# second line comment
|
||||
i.bit_count() for i in range(5) # rbracket comment
|
||||
] # rpar comment
|
||||
)
|
||||
|
||||
7
crates/ruff/resources/test/fixtures/flake8_future_annotations/edge_case.py
vendored
Normal file
7
crates/ruff/resources/test/fixtures/flake8_future_annotations/edge_case.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
from typing import List
|
||||
import typing as t
|
||||
|
||||
|
||||
def main(_: List[int]) -> None:
|
||||
a_list: t.List[str] = []
|
||||
a_list.append("hello")
|
||||
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/from_typing_import.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/from_typing_import.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
from typing import List
|
||||
|
||||
|
||||
def main() -> None:
|
||||
a_list: List[str] = []
|
||||
a_list.append("hello")
|
||||
8
crates/ruff/resources/test/fixtures/flake8_future_annotations/from_typing_import_many.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/flake8_future_annotations/from_typing_import_many.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
from typing import Dict, List, Optional, Set, Union, cast
|
||||
|
||||
|
||||
def main() -> None:
|
||||
a_list: List[Optional[str]] = []
|
||||
a_list.append("hello")
|
||||
a_dict = cast(Dict[int | None, Union[int, Set[bool]]], {})
|
||||
a_dict[1] = {True, False}
|
||||
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/import_typing.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/import_typing.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import typing
|
||||
|
||||
|
||||
def main() -> None:
|
||||
a_list: typing.List[str] = []
|
||||
a_list.append("hello")
|
||||
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/import_typing_as.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/import_typing_as.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import typing as t
|
||||
|
||||
|
||||
def main() -> None:
|
||||
a_list: t.List[str] = []
|
||||
a_list.append("hello")
|
||||
@@ -0,0 +1,7 @@
|
||||
def main() -> None:
|
||||
a_list: list[str] = []
|
||||
a_list.append("hello")
|
||||
|
||||
|
||||
def hello(y: dict[str, int]) -> None:
|
||||
del y
|
||||
7
crates/ruff/resources/test/fixtures/flake8_future_annotations/no_future_import_uses_union.py
vendored
Normal file
7
crates/ruff/resources/test/fixtures/flake8_future_annotations/no_future_import_uses_union.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
def main() -> None:
|
||||
a_list: list[str] | None = []
|
||||
a_list.append("hello")
|
||||
|
||||
|
||||
def hello(y: dict[str, int] | None) -> None:
|
||||
del y
|
||||
@@ -0,0 +1,8 @@
|
||||
def main() -> None:
|
||||
a_list: list[str | None] = []
|
||||
a_list.append("hello")
|
||||
|
||||
|
||||
def hello(y: dict[str | None, int]) -> None:
|
||||
z: tuple[str, str | None, str] = tuple(y)
|
||||
del z
|
||||
3
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_no_types.py
vendored
Normal file
3
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_no_types.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
def main() -> str:
|
||||
a_str = "hello"
|
||||
return a_str
|
||||
10
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_non_simplifiable_types.py
vendored
Normal file
10
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_non_simplifiable_types.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class Stuff(NamedTuple):
|
||||
x: int
|
||||
|
||||
|
||||
def main() -> None:
|
||||
a_list = Stuff(5)
|
||||
print(a_list)
|
||||
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_uses_future.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_uses_future.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def main() -> None:
|
||||
a_list: list[str] = []
|
||||
a_list.append("hello")
|
||||
8
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_variable_name.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_variable_name.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import typing
|
||||
|
||||
IRRELEVANT = typing.TypeVar
|
||||
|
||||
|
||||
def main() -> None:
|
||||
List: list[str] = []
|
||||
List.append("hello")
|
||||
@@ -7,3 +7,12 @@ foo.info("Hello {}".format("World!"))
|
||||
logging.log(logging.INFO, msg="Hello {}".format("World!"))
|
||||
logging.log(level=logging.INFO, msg="Hello {}".format("World!"))
|
||||
logging.log(msg="Hello {}".format("World!"), level=logging.INFO)
|
||||
|
||||
# Flask support
|
||||
import flask
|
||||
from flask import current_app
|
||||
from flask import current_app as app
|
||||
|
||||
flask.current_app.logger.info("Hello {}".format("World!"))
|
||||
current_app.logger.info("Hello {}".format("World!"))
|
||||
app.logger.log(logging.INFO, "Hello {}".format("World!"))
|
||||
|
||||
93
crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.py
vendored
Normal file
93
crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.py
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
import builtins
|
||||
import typing
|
||||
from typing import TypeAlias, Final
|
||||
|
||||
field1: int
|
||||
field2: int = ...
|
||||
field3 = ... # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
field4: int = 0
|
||||
field41: int = 0xFFFFFFFF
|
||||
field42: int = 1234567890
|
||||
field43: int = -0xFFFFFFFF
|
||||
field44: int = -1234567890
|
||||
field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
|
||||
field6 = 0 # Y052 Need type annotation for "field6"
|
||||
field7 = b"" # Y052 Need type annotation for "field7"
|
||||
field71 = "foo" # Y052 Need type annotation for "field71"
|
||||
field72: str = "foo"
|
||||
field8 = False # Y052 Need type annotation for "field8"
|
||||
field81 = -1 # Y052 Need type annotation for "field81"
|
||||
field82: float = -98.43
|
||||
field83 = -42j # Y052 Need type annotation for "field83"
|
||||
field84 = 5 + 42j # Y052 Need type annotation for "field84"
|
||||
field85 = -5 - 42j # Y052 Need type annotation for "field85"
|
||||
field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None"
|
||||
Field95: TypeAlias = None
|
||||
Field96: TypeAlias = int | None
|
||||
Field97: TypeAlias = None | typing.SupportsInt | builtins.str | float | bool
|
||||
field19 = [1, 2, 3] # Y052 Need type annotation for "field19"
|
||||
field191: list[int] = [1, 2, 3]
|
||||
field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
|
||||
field201: tuple[int, ...] = (1, 2, 3)
|
||||
field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
|
||||
field211: set[int] = {1, 2, 3}
|
||||
field212 = {"foo": "bar"} # Y052 Need type annotation for "field212"
|
||||
field213: dict[str, str] = {"foo": "bar"}
|
||||
field22: Final = {"foo": 5}
|
||||
field221: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Y015 Only simple default values are allowed for assignments
|
||||
field223: list[int] = [*range(10)] # Y015 Only simple default values are allowed for assignments
|
||||
field224: list[int] = list(range(10)) # Y015 Only simple default values are allowed for assignments
|
||||
field225: list[object] = [{}, 1, 2] # Y015 Only simple default values are allowed for assignments
|
||||
field226: tuple[str | tuple[str, ...], ...] = ("foo", ("foo", "bar")) # Y015 Only simple default values are allowed for assignments
|
||||
field227: dict[str, object] = {"foo": {"foo": "bar"}} # Y015 Only simple default values are allowed for assignments
|
||||
field228: dict[str, list[object]] = {"foo": []} # Y015 Only simple default values are allowed for assignments
|
||||
# When parsed, this case results in `None` being placed in the `.keys` list for the `ast.Dict` node
|
||||
field229: dict[int, int] = {1: 2, **{3: 4}} # Y015 Only simple default values are allowed for assignments
|
||||
field23 = "foo" + "bar" # Y015 Only simple default values are allowed for assignments
|
||||
field24 = b"foo" + b"bar" # Y015 Only simple default values are allowed for assignments
|
||||
field25 = 5 * 5 # Y015 Only simple default values are allowed for assignments
|
||||
|
||||
# We shouldn't emit Y015 within functions
|
||||
def f():
|
||||
field26: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
|
||||
# We shouldn't emit Y015 for __slots__ or __match_args__
|
||||
class Class1:
|
||||
__slots__ = (
|
||||
'_one',
|
||||
'_two',
|
||||
'_three',
|
||||
'_four',
|
||||
'_five',
|
||||
'_six',
|
||||
'_seven',
|
||||
'_eight',
|
||||
'_nine',
|
||||
'_ten',
|
||||
'_eleven',
|
||||
)
|
||||
|
||||
__match_args__ = (
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
'four',
|
||||
'five',
|
||||
'six',
|
||||
'seven',
|
||||
'eight',
|
||||
'nine',
|
||||
'ten',
|
||||
'eleven',
|
||||
)
|
||||
|
||||
# We shouldn't emit Y015 for __all__
|
||||
__all__ = ["Class1"]
|
||||
|
||||
# Ignore the following for PYI015
|
||||
field26 = typing.Sequence[int]
|
||||
field27 = list[str]
|
||||
field28 = builtins.str
|
||||
field29 = str
|
||||
field30 = str | bytes | None
|
||||
100
crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.pyi
vendored
Normal file
100
crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.pyi
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
import builtins
|
||||
import typing
|
||||
from typing import TypeAlias, Final, NewType, TypeVar, TypeVarTuple, ParamSpec
|
||||
|
||||
# We shouldn't emit Y015 for simple default values
|
||||
field1: int
|
||||
field2: int = ...
|
||||
field3 = ... # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
field4: int = 0
|
||||
field41: int = 0xFFFFFFFF
|
||||
field42: int = 1234567890
|
||||
field43: int = -0xFFFFFFFF
|
||||
field44: int = -1234567890
|
||||
field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
|
||||
field6 = 0 # Y052 Need type annotation for "field6"
|
||||
field7 = b"" # Y052 Need type annotation for "field7"
|
||||
field71 = "foo" # Y052 Need type annotation for "field71"
|
||||
field72: str = "foo"
|
||||
field8 = False # Y052 Need type annotation for "field8"
|
||||
field81 = -1 # Y052 Need type annotation for "field81"
|
||||
field82: float = -98.43
|
||||
field83 = -42j # Y052 Need type annotation for "field83"
|
||||
field84 = 5 + 42j # Y052 Need type annotation for "field84"
|
||||
field85 = -5 - 42j # Y052 Need type annotation for "field85"
|
||||
field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None"
|
||||
Field95: TypeAlias = None
|
||||
Field96: TypeAlias = int | None
|
||||
Field97: TypeAlias = None | typing.SupportsInt | builtins.str | float | bool
|
||||
Field98 = NewType('MyInt', int)
|
||||
Field99 = TypeVar('Field99')
|
||||
Field100 = TypeVarTuple('Field100')
|
||||
Field101 = ParamSpec('Field101')
|
||||
field19 = [1, 2, 3] # Y052 Need type annotation for "field19"
|
||||
field191: list[int] = [1, 2, 3]
|
||||
field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
|
||||
field201: tuple[int, ...] = (1, 2, 3)
|
||||
field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
|
||||
field211: set[int] = {1, 2, 3}
|
||||
field212 = {"foo": "bar"} # Y052 Need type annotation for "field212"
|
||||
field213: dict[str, str] = {"foo": "bar"}
|
||||
field22: Final = {"foo": 5}
|
||||
|
||||
# We *should* emit Y015 for more complex default values
|
||||
field221: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Y015 Only simple default values are allowed for assignments
|
||||
field223: list[int] = [*range(10)] # Y015 Only simple default values are allowed for assignments
|
||||
field224: list[int] = list(range(10)) # Y015 Only simple default values are allowed for assignments
|
||||
field225: list[object] = [{}, 1, 2] # Y015 Only simple default values are allowed for assignments
|
||||
field226: tuple[str | tuple[str, ...], ...] = ("foo", ("foo", "bar")) # Y015 Only simple default values are allowed for assignments
|
||||
field227: dict[str, object] = {"foo": {"foo": "bar"}} # Y015 Only simple default values are allowed for assignments
|
||||
field228: dict[str, list[object]] = {"foo": []} # Y015 Only simple default values are allowed for assignments
|
||||
# When parsed, this case results in `None` being placed in the `.keys` list for the `ast.Dict` node
|
||||
field229: dict[int, int] = {1: 2, **{3: 4}} # Y015 Only simple default values are allowed for assignments
|
||||
field23 = "foo" + "bar" # Y015 Only simple default values are allowed for assignments
|
||||
field24 = b"foo" + b"bar" # Y015 Only simple default values are allowed for assignments
|
||||
field25 = 5 * 5 # Y015 Only simple default values are allowed for assignments
|
||||
|
||||
# We shouldn't emit Y015 within functions
|
||||
def f():
|
||||
field26: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
|
||||
# We shouldn't emit Y015 for __slots__ or __match_args__
|
||||
class Class1:
|
||||
__slots__ = (
|
||||
'_one',
|
||||
'_two',
|
||||
'_three',
|
||||
'_four',
|
||||
'_five',
|
||||
'_six',
|
||||
'_seven',
|
||||
'_eight',
|
||||
'_nine',
|
||||
'_ten',
|
||||
'_eleven',
|
||||
)
|
||||
|
||||
__match_args__ = (
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
'four',
|
||||
'five',
|
||||
'six',
|
||||
'seven',
|
||||
'eight',
|
||||
'nine',
|
||||
'ten',
|
||||
'eleven',
|
||||
)
|
||||
|
||||
# We shouldn't emit Y015 for __all__
|
||||
__all__ = ["Class1"]
|
||||
|
||||
# Ignore the following for PYI015
|
||||
field26 = typing.Sequence[int]
|
||||
field27 = list[str]
|
||||
field28 = builtins.str
|
||||
field29 = str
|
||||
field30 = str | bytes | None
|
||||
@@ -39,3 +39,8 @@ def test_error():
|
||||
message
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
assert something # OK
|
||||
assert something and something_else # Error
|
||||
assert something and something_else and something_third # Error
|
||||
|
||||
@@ -1,73 +1,96 @@
|
||||
def foo():
|
||||
pass
|
||||
|
||||
try:
|
||||
foo()
|
||||
except ValueError: # SIM105
|
||||
pass
|
||||
|
||||
try:
|
||||
foo()
|
||||
except (ValueError, OSError): # SIM105
|
||||
pass
|
||||
|
||||
try:
|
||||
foo()
|
||||
except: # SIM105
|
||||
pass
|
||||
|
||||
try:
|
||||
foo()
|
||||
except (a.Error, b.Error): # SIM105
|
||||
pass
|
||||
|
||||
# SIM105
|
||||
try:
|
||||
foo()
|
||||
except ValueError:
|
||||
print('foo')
|
||||
pass
|
||||
|
||||
# SIM105
|
||||
try:
|
||||
foo()
|
||||
except (ValueError, OSError):
|
||||
pass
|
||||
|
||||
# SIM105
|
||||
try:
|
||||
foo()
|
||||
except:
|
||||
pass
|
||||
|
||||
# SIM105
|
||||
try:
|
||||
foo()
|
||||
except (a.Error, b.Error):
|
||||
pass
|
||||
|
||||
# OK
|
||||
try:
|
||||
foo()
|
||||
except ValueError:
|
||||
print("foo")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
# OK
|
||||
try:
|
||||
foo()
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
print('bar')
|
||||
print("bar")
|
||||
|
||||
# OK
|
||||
try:
|
||||
foo()
|
||||
except ValueError:
|
||||
pass
|
||||
finally:
|
||||
print('bar')
|
||||
print("bar")
|
||||
|
||||
# OK
|
||||
try:
|
||||
foo()
|
||||
foo()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# OK
|
||||
try:
|
||||
for i in range(3):
|
||||
foo()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
def bar():
|
||||
# OK
|
||||
try:
|
||||
return foo()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
def with_ellipsis():
|
||||
# OK
|
||||
try:
|
||||
foo()
|
||||
except ValueError:
|
||||
...
|
||||
|
||||
|
||||
def with_ellipsis_and_return():
|
||||
# OK
|
||||
try:
|
||||
return foo()
|
||||
except ValueError:
|
||||
...
|
||||
|
||||
|
||||
def with_comment():
|
||||
try:
|
||||
foo()
|
||||
except (ValueError, OSError):
|
||||
pass # Trailing comment.
|
||||
@@ -1,7 +1,8 @@
|
||||
"""Case: There's a random import, so it should add `contextlib` after it."""
|
||||
import math
|
||||
|
||||
# SIM105
|
||||
try:
|
||||
math.sqrt(-1)
|
||||
except ValueError: # SIM105
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@@ -6,6 +6,7 @@ def foo():
|
||||
pass
|
||||
|
||||
|
||||
# SIM105
|
||||
try:
|
||||
foo()
|
||||
except ValueError:
|
||||
|
||||
16
crates/ruff/resources/test/fixtures/flake8_simplify/SIM105_3.py
vendored
Normal file
16
crates/ruff/resources/test/fixtures/flake8_simplify/SIM105_3.py
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
"""Case: `contextlib` is imported after the call site."""
|
||||
|
||||
|
||||
def foo():
|
||||
pass
|
||||
|
||||
|
||||
def bar():
|
||||
# SIM105
|
||||
try:
|
||||
foo()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
import contextlib
|
||||
@@ -12,3 +12,10 @@ if not a == b: # OK
|
||||
|
||||
if not a != b: # OK
|
||||
pass
|
||||
|
||||
a = not not b # SIM208
|
||||
|
||||
f(not not a) # SIM208
|
||||
|
||||
if 1 + (not (not a)): # SIM208
|
||||
pass
|
||||
|
||||
@@ -6,6 +6,7 @@ a = True if b + c else False # SIM210
|
||||
|
||||
a = False if b else True # OK
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
def bool():
|
||||
|
||||
8
crates/ruff/resources/test/fixtures/flake8_todos/TD001.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/flake8_todos/TD001.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# T001 - accepted
|
||||
# TODO (evanrittenhouse): this is a valid TODO
|
||||
# SOME_OTHER_TAG: this is impossible to determine
|
||||
# this is not a TODO
|
||||
|
||||
# T001 - errors
|
||||
# XXX (evanrittenhouse): this is not fine
|
||||
# FIXME (evanrittenhouse): this is not fine
|
||||
7
crates/ruff/resources/test/fixtures/flake8_todos/TD002.py
vendored
Normal file
7
crates/ruff/resources/test/fixtures/flake8_todos/TD002.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# T002 - accepted
|
||||
# TODO (evanrittenhouse): this has an author
|
||||
# TODO(evanrittenhouse): this also has an author
|
||||
# T002 - errors
|
||||
# TODO: this has no author
|
||||
# FIXME: neither does this
|
||||
# TODO : and neither does this
|
||||
29
crates/ruff/resources/test/fixtures/flake8_todos/TD003.py
vendored
Normal file
29
crates/ruff/resources/test/fixtures/flake8_todos/TD003.py
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# TDO003 - accepted
|
||||
# TODO: this comment has a link
|
||||
# https://github.com/charliermarsh/ruff/issues/3870
|
||||
|
||||
# TODO: this comment has an issue
|
||||
# TDO-3870
|
||||
|
||||
# TDO003 - errors
|
||||
# TODO: this comment has no
|
||||
# link after it
|
||||
|
||||
# TODO: here's a TODO with no link after it
|
||||
def foo(x):
|
||||
return x
|
||||
|
||||
# TODO: here's a TODO on the last line with no link
|
||||
# Here's more content.
|
||||
# TDO-3870
|
||||
|
||||
# TODO: here's a TODO on the last line with no link
|
||||
# Here's more content, with a space.
|
||||
|
||||
# TDO-3870
|
||||
|
||||
# TODO: here's a TODO without an issue link
|
||||
# TODO: followed by a new TODO with an issue link
|
||||
# TDO-3870
|
||||
|
||||
# TODO: here's a TODO on the last line with no link
|
||||
6
crates/ruff/resources/test/fixtures/flake8_todos/TD004.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_todos/TD004.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# T004 - accepted
|
||||
# TODO(evanrittenhouse): this has a colon
|
||||
# T004 - errors
|
||||
# TODO this has no colon
|
||||
# TODO(evanrittenhouse 😀) this has no colon
|
||||
# FIXME add a colon
|
||||
6
crates/ruff/resources/test/fixtures/flake8_todos/TD005.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_todos/TD005.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# T005 - accepted
|
||||
# TODO(evanrittenhouse): this has text, while the errors do not
|
||||
# T005 - errors
|
||||
# TODO(evanrittenhouse):
|
||||
# TODO(evanrittenhouse)
|
||||
# FIXME
|
||||
5
crates/ruff/resources/test/fixtures/flake8_todos/TD006.py
vendored
Normal file
5
crates/ruff/resources/test/fixtures/flake8_todos/TD006.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# TDO006 - accepted
|
||||
# TODO (evanrittenhouse): this is a valid TODO
|
||||
# TDO006 - error
|
||||
# ToDo (evanrittenhouse): invalid capitalization
|
||||
# todo (evanrittenhouse): another invalid capitalization
|
||||
8
crates/ruff/resources/test/fixtures/flake8_todos/TD007.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/flake8_todos/TD007.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# T007 - accepted
|
||||
# TODO(evanrittenhouse): this has a space after a colon
|
||||
# TODO: so does this
|
||||
# T007 - errors
|
||||
# TODO(evanrittenhouse):this has no space after a colon
|
||||
# TODO (evanrittenhouse):this doesn't either
|
||||
# TODO:neither does this
|
||||
# FIXME:and lastly neither does this
|
||||
18
crates/ruff/resources/test/fixtures/flynt/FLY002.py
vendored
Normal file
18
crates/ruff/resources/test/fixtures/flynt/FLY002.py
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import secrets
|
||||
from random import random, choice
|
||||
|
||||
a = "Hello"
|
||||
ok1 = " ".join([a, " World"]) # OK
|
||||
ok2 = "".join(["Finally, ", a, " World"]) # OK
|
||||
ok3 = "x".join(("1", "2", "3")) # OK
|
||||
ok4 = "y".join([1, 2, 3]) # Technically OK, though would've been an error originally
|
||||
ok5 = "a".join([random(), random()]) # OK (simple calls)
|
||||
ok6 = "a".join([secrets.token_urlsafe(), secrets.token_hex()]) # OK (attr calls)
|
||||
|
||||
nok1 = "x".join({"4", "5", "yee"}) # Not OK (set)
|
||||
nok2 = a.join(["1", "2", "3"]) # Not OK (not a static joiner)
|
||||
nok3 = "a".join(a) # Not OK (not a static joinee)
|
||||
nok4 = "a".join([a, a, *a]) # Not OK (not a static length)
|
||||
nok5 = "a".join([choice("flarp")]) # Not OK (not a simple call)
|
||||
nok6 = "a".join(x for x in "feefoofum") # Not OK (generator)
|
||||
nok7 = "a".join([f"foo{8}", "bar"]) # Not OK (contains an f-string)
|
||||
@@ -22,3 +22,7 @@ if True:
|
||||
x.drop(["a"], axis=1, **kwargs, inplace=True)
|
||||
x.drop(["a"], axis=1, inplace=True, **kwargs)
|
||||
f(x.drop(["a"], axis=1, inplace=True))
|
||||
|
||||
x.apply(lambda x: x.sort_values('a', inplace=True))
|
||||
import torch
|
||||
torch.m.ReLU(inplace=True) # safe because this isn't a pandas call
|
||||
|
||||
@@ -40,3 +40,27 @@ def start():
|
||||
#: E117 W191
|
||||
def start():
|
||||
print()
|
||||
#: E112
|
||||
if False: #
|
||||
print()
|
||||
#:
|
||||
if False:
|
||||
print()
|
||||
#:
|
||||
if False: #
|
||||
print()
|
||||
#:
|
||||
if False:
|
||||
print()
|
||||
|
||||
print()
|
||||
#:
|
||||
if False:
|
||||
print()
|
||||
if False:
|
||||
|
||||
print()
|
||||
#:
|
||||
if False:
|
||||
|
||||
print()
|
||||
|
||||
@@ -76,3 +76,11 @@ if x == 4:
|
||||
a[b1, :] == a[b1, ...]
|
||||
b = a[:, b1]
|
||||
#:
|
||||
|
||||
#: E201:1:6
|
||||
spam[ ~ham]
|
||||
|
||||
#: Okay
|
||||
x = [ #
|
||||
'some value',
|
||||
]
|
||||
|
||||
@@ -160,6 +160,7 @@ if alpha[:-i]:
|
||||
*a, b = (1, 2, 3)
|
||||
|
||||
|
||||
@decorator
|
||||
def squares(n):
|
||||
return (i**2 for i in range(n))
|
||||
|
||||
@@ -168,4 +169,14 @@ ENG_PREFIXES = {
|
||||
-6: "\u03bc", # Greek letter mu
|
||||
-3: "m",
|
||||
}
|
||||
|
||||
i = (
|
||||
i + #
|
||||
1
|
||||
)
|
||||
|
||||
x[~y]
|
||||
|
||||
if i == -1:
|
||||
pass
|
||||
#:
|
||||
|
||||
@@ -18,3 +18,13 @@ def foo() -> None:
|
||||
#: E231
|
||||
if (1,2):
|
||||
pass
|
||||
|
||||
#: Okay
|
||||
a = (1,\
|
||||
2)
|
||||
|
||||
#: E231:2:20
|
||||
mdtypes_template = {
|
||||
'tag_full': [('mdtype', 'u4'), ('byte_count', 'u4')],
|
||||
'tag_smalldata':[('byte_count_mdtype', 'u4'), ('data', 'S4')],
|
||||
}
|
||||
|
||||
@@ -64,3 +64,11 @@ a = 42 # (One space one NBSP)
|
||||
#: E262:2:9
|
||||
# (Two spaces) Ok for block comment
|
||||
a = 42 # (Two spaces)
|
||||
|
||||
#: E265:5:1
|
||||
### Means test is not done yet
|
||||
# E Means test is giving error (E)
|
||||
# F Means test is failing (F)
|
||||
# EF Means test is giving error and Failing
|
||||
#! Means test is segfaulting
|
||||
# 8 Means test runs forever
|
||||
|
||||
@@ -56,3 +56,7 @@ if True:
|
||||
def f():
|
||||
print((yield))
|
||||
x = (yield)
|
||||
#: Okay
|
||||
if (a and
|
||||
b):
|
||||
pass
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""Here's a top-level docstring that's over the limit."""
|
||||
|
||||
|
||||
def f():
|
||||
def f1():
|
||||
"""Here's a docstring that's also over the limit."""
|
||||
|
||||
x = 1 # Here's a comment that's over the limit, but it's not standalone.
|
||||
@@ -16,3 +16,16 @@ def f():
|
||||
|
||||
|
||||
"This is also considered a docstring, and is over the limit."
|
||||
|
||||
|
||||
def f2():
|
||||
"""Here's a multi-line docstring.
|
||||
|
||||
It's over the limit on this line, which isn't the first line in the docstring.
|
||||
"""
|
||||
|
||||
|
||||
def f3():
|
||||
"""Here's a multi-line docstring.
|
||||
|
||||
It's over the limit on this line, which isn't the first line in the docstring."""
|
||||
|
||||
4
crates/ruff/resources/test/fixtures/pycodestyle/shebang.py
vendored
Normal file
4
crates/ruff/resources/test/fixtures/pycodestyle/shebang.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
#!
|
||||
#:
|
||||
@@ -13,3 +13,15 @@ def another_function():
|
||||
|
||||
def utf8_function():
|
||||
"""éste docstring is capitalized."""
|
||||
|
||||
def uppercase_char_not_possible():
|
||||
"""'args' is not capitalized."""
|
||||
|
||||
def non_alphabetic():
|
||||
"""th!is is not capitalized."""
|
||||
|
||||
def non_ascii():
|
||||
"""th•s is not capitalized."""
|
||||
|
||||
def all_caps():
|
||||
"""th•s is not capitalized."""
|
||||
|
||||
18
crates/ruff/resources/test/fixtures/pydocstyle/all.py
vendored
Normal file
18
crates/ruff/resources/test/fixtures/pydocstyle/all.py
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
def public_func():
|
||||
pass
|
||||
|
||||
|
||||
def private_func():
|
||||
pass
|
||||
|
||||
|
||||
class PublicClass:
|
||||
class PublicNestedClass:
|
||||
pass
|
||||
|
||||
|
||||
class PrivateClass:
|
||||
pass
|
||||
|
||||
|
||||
__all__ = ("public_func", "PublicClass")
|
||||
19
crates/ruff/resources/test/fixtures/pygrep-hooks/PGH005_0.py
vendored
Normal file
19
crates/ruff/resources/test/fixtures/pygrep-hooks/PGH005_0.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# Errors
|
||||
assert my_mock.not_called()
|
||||
assert my_mock.called_once_with()
|
||||
assert my_mock.not_called
|
||||
assert my_mock.called_once_with
|
||||
my_mock.assert_not_called
|
||||
my_mock.assert_called
|
||||
my_mock.assert_called_once_with
|
||||
my_mock.assert_called_once_with
|
||||
MyMock.assert_called_once_with
|
||||
|
||||
# OK
|
||||
assert my_mock.call_count == 1
|
||||
assert my_mock.called
|
||||
my_mock.assert_not_called()
|
||||
my_mock.assert_called()
|
||||
my_mock.assert_called_once_with()
|
||||
"""like :meth:`Mock.assert_called_once_with`"""
|
||||
"""like :meth:`MagicMock.assert_called_once_with`"""
|
||||
24
crates/ruff/resources/test/fixtures/pylint/duplicate_bases.py
vendored
Normal file
24
crates/ruff/resources/test/fixtures/pylint/duplicate_bases.py
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
###
|
||||
# Errors.
|
||||
###
|
||||
class A:
|
||||
...
|
||||
|
||||
|
||||
class B(A, A):
|
||||
...
|
||||
|
||||
|
||||
###
|
||||
# Non-errors.
|
||||
###
|
||||
class C:
|
||||
...
|
||||
|
||||
|
||||
class D(C):
|
||||
...
|
||||
|
||||
|
||||
class E(A, C):
|
||||
...
|
||||
27
crates/ruff/resources/test/fixtures/pylint/nested_min_max.py
vendored
Normal file
27
crates/ruff/resources/test/fixtures/pylint/nested_min_max.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
min(1, 2, 3)
|
||||
min(1, min(2, 3))
|
||||
min(1, min(2, min(3, 4)))
|
||||
min(1, foo("a", "b"), min(3, 4))
|
||||
min(1, max(2, 3))
|
||||
max(1, 2, 3)
|
||||
max(1, max(2, 3))
|
||||
max(1, max(2, max(3, 4)))
|
||||
max(1, foo("a", "b"), max(3, 4))
|
||||
|
||||
# These should not trigger; we do not flag cases with keyword args.
|
||||
min(1, min(2, 3), key=test)
|
||||
min(1, min(2, 3, key=test))
|
||||
# This will still trigger, to merge the calls without keyword args.
|
||||
min(1, min(2, 3, key=test), min(4, 5))
|
||||
|
||||
# Don't provide a fix if there are comments within the call.
|
||||
min(
|
||||
1, # This is a comment.
|
||||
min(2, 3),
|
||||
)
|
||||
|
||||
# Handle iterable expressions.
|
||||
min(1, min(a))
|
||||
min(1, min(i for i in range(10)))
|
||||
max(1, max(a))
|
||||
max(1, max(i for i in range(10)))
|
||||
5
crates/ruff/resources/test/fixtures/pylint/sys_exit_alias_7.py
vendored
Normal file
5
crates/ruff/resources/test/fixtures/pylint/sys_exit_alias_7.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
def main():
|
||||
exit(0)
|
||||
|
||||
|
||||
import functools
|
||||
5
crates/ruff/resources/test/fixtures/pylint/sys_exit_alias_8.py
vendored
Normal file
5
crates/ruff/resources/test/fixtures/pylint/sys_exit_alias_8.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
from sys import argv
|
||||
|
||||
|
||||
def main():
|
||||
exit(0)
|
||||
5
crates/ruff/resources/test/fixtures/pylint/sys_exit_alias_9.py
vendored
Normal file
5
crates/ruff/resources/test/fixtures/pylint/sys_exit_alias_9.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
def main():
|
||||
exit(0)
|
||||
|
||||
|
||||
from sys import argv
|
||||
@@ -22,13 +22,13 @@ def f(x=1, y=1, z=1): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, /, u, v, w): # OK
|
||||
def f(x, y, z, /, u, v, w): # Too many arguments (6/5)
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, *, u, v, w): # OK
|
||||
def f(x, y, z, *, u, v, w): # Too many arguments (6/5)
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, a, b, c, *, u, v, w): # Too many arguments (6/5)
|
||||
def f(x, y, z, a, b, c, *, u, v, w): # Too many arguments (9/5)
|
||||
pass
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
class TestClass:
|
||||
def __bool__(self):
|
||||
...
|
||||
|
||||
|
||||
def __bool__(self, x): # too many mandatory args
|
||||
...
|
||||
|
||||
|
||||
def __bool__(self, x=1): # additional optional args OK
|
||||
...
|
||||
|
||||
def __bool__(self, *args): # varargs OK
|
||||
...
|
||||
|
||||
|
||||
def __bool__(): # ignored; should be caughty by E0211/N805
|
||||
...
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __bool__():
|
||||
...
|
||||
@@ -21,31 +18,58 @@ class TestClass:
|
||||
@staticmethod
|
||||
def __bool__(x): # too many mandatory args
|
||||
...
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __bool__(x=1): # additional optional args OK
|
||||
...
|
||||
|
||||
|
||||
def __eq__(self, other): # multiple args
|
||||
...
|
||||
|
||||
|
||||
def __eq__(self, other=1): # expected arg is optional
|
||||
...
|
||||
|
||||
|
||||
def __eq__(self): # too few mandatory args
|
||||
...
|
||||
|
||||
|
||||
def __eq__(self, other, other_other): # too many mandatory args
|
||||
...
|
||||
|
||||
def __round__(self): # allow zero additional args.
|
||||
|
||||
def __round__(self): # allow zero additional args
|
||||
...
|
||||
|
||||
def __round__(self, x): # allow one additional args.
|
||||
|
||||
def __round__(self, x): # allow one additional args
|
||||
...
|
||||
|
||||
|
||||
def __round__(self, x, y): # disallow 2 args
|
||||
...
|
||||
|
||||
|
||||
def __round__(self, x, y, z=2): # disallow 3 args even when one is optional
|
||||
...
|
||||
...
|
||||
|
||||
def __eq__(self, *args): # ignore *args
|
||||
...
|
||||
|
||||
def __eq__(self, x, *args): # extra *args is ok
|
||||
...
|
||||
|
||||
def __eq__(self, x, y, *args): # too many args with *args
|
||||
...
|
||||
|
||||
def __round__(self, *args): # allow zero additional args
|
||||
...
|
||||
|
||||
def __round__(self, x, *args): # allow one additional args
|
||||
...
|
||||
|
||||
def __round__(self, x, y, *args): # disallow 2 args
|
||||
...
|
||||
|
||||
def __eq__(self, **kwargs): # ignore **kwargs
|
||||
...
|
||||
|
||||
def __eq__(self, /, other=42): # ignore positional-only args
|
||||
...
|
||||
|
||||
def __eq__(self, *, other=42): # ignore positional-only args
|
||||
...
|
||||
|
||||
@@ -56,3 +56,11 @@ def f(x: "List['Li' 'st[str]']") -> None:
|
||||
|
||||
def f(x: "Li" "st['List[str]']") -> None:
|
||||
...
|
||||
|
||||
|
||||
def f(x: typing.Deque[str]) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f(x: typing.DefaultDict[str, str]) -> None:
|
||||
...
|
||||
|
||||
@@ -59,3 +59,14 @@ u"foo".encode("utf-8") # b"foo"
|
||||
R"foo\o".encode("utf-8") # br"foo\o"
|
||||
U"foo".encode("utf-8") # b"foo"
|
||||
print("foo".encode()) # print(b"foo")
|
||||
|
||||
# `encode` on parenthesized strings.
|
||||
(
|
||||
"abc"
|
||||
"def"
|
||||
).encode()
|
||||
|
||||
((
|
||||
"abc"
|
||||
"def"
|
||||
)).encode()
|
||||
|
||||
@@ -25,3 +25,4 @@ bytes()
|
||||
bytes(b"foo")
|
||||
bytes(b"""
|
||||
foo""")
|
||||
f"{str()}"
|
||||
|
||||
1
crates/ruff/resources/test/fixtures/pyupgrade/UP032_1.py
vendored
Normal file
1
crates/ruff/resources/test/fixtures/pyupgrade/UP032_1.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
"{} {}".format(a, b) # Intentionally at start-of-file, to ensure graceful handling.
|
||||
@@ -43,3 +43,6 @@ second = first + [
|
||||
|
||||
[] + foo + [ # This will be preserved, but doesn't prevent the fix
|
||||
]
|
||||
|
||||
# Uses the non-preferred quote style, which should be retained.
|
||||
f"{[*a(), 'b']}"
|
||||
|
||||
26
crates/ruff/resources/test/fixtures/ruff/RUF010.py
vendored
Normal file
26
crates/ruff/resources/test/fixtures/ruff/RUF010.py
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
bla = b"bla"
|
||||
d = {"a": b"bla", "b": b"bla", "c": b"bla"}
|
||||
|
||||
|
||||
def foo(one_arg):
|
||||
pass
|
||||
|
||||
|
||||
f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010
|
||||
|
||||
f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010
|
||||
|
||||
f"{foo(bla)}" # OK
|
||||
|
||||
f"{str(bla, 'ascii')}, {str(bla, encoding='cp1255')}" # OK
|
||||
|
||||
f"{bla!s} {[]!r} {'bar'!a}" # OK
|
||||
|
||||
"Not an f-string {str(bla)}, {repr(bla)}, {ascii(bla)}" # OK
|
||||
|
||||
|
||||
def ascii(arg):
|
||||
pass
|
||||
|
||||
|
||||
f"{ascii(bla)}" # OK
|
||||
@@ -88,3 +88,12 @@ import sys # noqa: F401, RUF100
|
||||
print(sys.path)
|
||||
|
||||
"shape: (6,)\nSeries: '' [duration[μs]]\n[\n\t0µs\n\t1µs\n\t2µs\n\t3µs\n\t4µs\n\t5µs\n]" # noqa: F401
|
||||
|
||||
|
||||
def f():
|
||||
# Ensure that the `noqa` applies to both the overlong line _and_ the unused
|
||||
# variable.
|
||||
a = """Lorem ipsum dolor sit amet.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
""" # noqa
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
"""
|
||||
Violation:
|
||||
|
||||
Checks for `raise` statements within `try` blocks.
|
||||
"""
|
||||
class MyException(Exception):
|
||||
pass
|
||||
|
||||
@@ -45,3 +50,10 @@ def good():
|
||||
logger.exception("a failed")
|
||||
except Exception:
|
||||
logger.exception("something failed")
|
||||
|
||||
|
||||
def fine():
|
||||
try:
|
||||
a = process() # This throws the exception now
|
||||
finally:
|
||||
print("finally")
|
||||
|
||||
111
crates/ruff/resources/test/fixtures/tryceratops/TRY302.py
vendored
Normal file
111
crates/ruff/resources/test/fixtures/tryceratops/TRY302.py
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
Violation:
|
||||
|
||||
Checks for uses of `raise` directly after a `rescue`.
|
||||
"""
|
||||
class MyException(Exception):
|
||||
pass
|
||||
|
||||
def bad():
|
||||
try:
|
||||
process()
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def bad():
|
||||
try:
|
||||
process()
|
||||
except Exception:
|
||||
raise
|
||||
print("this code is pointless!")
|
||||
|
||||
def bad():
|
||||
try:
|
||||
process()
|
||||
except:
|
||||
# I am a comment, not a statement!
|
||||
raise
|
||||
|
||||
def bad():
|
||||
try:
|
||||
process()
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def bad():
|
||||
try:
|
||||
process()
|
||||
except Exception as e:
|
||||
raise
|
||||
|
||||
def bad():
|
||||
try:
|
||||
process()
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def bad():
|
||||
try:
|
||||
process()
|
||||
except MyException:
|
||||
raise
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def bad():
|
||||
try:
|
||||
process()
|
||||
except MyException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def bad():
|
||||
try:
|
||||
process()
|
||||
except MyException as ex:
|
||||
raise ex
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def fine():
|
||||
try:
|
||||
process()
|
||||
except Exception as e:
|
||||
raise ex
|
||||
|
||||
def fine():
|
||||
try:
|
||||
process()
|
||||
except MyException:
|
||||
raise
|
||||
except Exception:
|
||||
print("bar")
|
||||
|
||||
def fine():
|
||||
try:
|
||||
process()
|
||||
except Exception:
|
||||
print("initiating rapid unscheduled disassembly of program")
|
||||
|
||||
def fine():
|
||||
try:
|
||||
process()
|
||||
except MyException:
|
||||
print("oh no!")
|
||||
raise
|
||||
|
||||
def fine():
|
||||
try:
|
||||
process()
|
||||
except Exception:
|
||||
if True:
|
||||
raise
|
||||
|
||||
def fine():
|
||||
try:
|
||||
process()
|
||||
finally:
|
||||
# I am a comment, not a statement!
|
||||
print("but i am a statement")
|
||||
raise
|
||||
@@ -5,7 +5,7 @@ use libcst_native::{
|
||||
Codegen, CodegenState, ImportNames, ParenthesizableWhitespace, SmallStatement, Statement,
|
||||
};
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use rustpython_parser::ast::{ExcepthandlerKind, Expr, Keyword, Stmt, StmtKind};
|
||||
use rustpython_parser::ast::{self, Excepthandler, Expr, Keyword, Ranged, Stmt};
|
||||
use rustpython_parser::{lexer, Mode, Tok};
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
@@ -27,22 +27,22 @@ fn has_single_child(body: &[Stmt], deleted: &[&Stmt]) -> bool {
|
||||
|
||||
/// Determine if a child is the only statement in its body.
|
||||
fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool> {
|
||||
match &parent.node {
|
||||
StmtKind::FunctionDef { body, .. }
|
||||
| StmtKind::AsyncFunctionDef { body, .. }
|
||||
| StmtKind::ClassDef { body, .. }
|
||||
| StmtKind::With { body, .. }
|
||||
| StmtKind::AsyncWith { body, .. } => {
|
||||
match parent {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef { body, .. })
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { body, .. })
|
||||
| Stmt::ClassDef(ast::StmtClassDef { body, .. })
|
||||
| Stmt::With(ast::StmtWith { body, .. })
|
||||
| Stmt::AsyncWith(ast::StmtAsyncWith { body, .. }) => {
|
||||
if body.iter().contains(child) {
|
||||
Ok(has_single_child(body, deleted))
|
||||
} else {
|
||||
bail!("Unable to find child in parent body")
|
||||
}
|
||||
}
|
||||
StmtKind::For { body, orelse, .. }
|
||||
| StmtKind::AsyncFor { body, orelse, .. }
|
||||
| StmtKind::While { body, orelse, .. }
|
||||
| StmtKind::If { body, orelse, .. } => {
|
||||
Stmt::For(ast::StmtFor { body, orelse, .. })
|
||||
| Stmt::AsyncFor(ast::StmtAsyncFor { body, orelse, .. })
|
||||
| Stmt::While(ast::StmtWhile { body, orelse, .. })
|
||||
| Stmt::If(ast::StmtIf { body, orelse, .. }) => {
|
||||
if body.iter().contains(child) {
|
||||
Ok(has_single_child(body, deleted))
|
||||
} else if orelse.iter().contains(child) {
|
||||
@@ -51,26 +51,28 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
|
||||
bail!("Unable to find child in parent body")
|
||||
}
|
||||
}
|
||||
StmtKind::Try {
|
||||
Stmt::Try(ast::StmtTry {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
}
|
||||
| StmtKind::TryStar {
|
||||
range: _,
|
||||
})
|
||||
| Stmt::TryStar(ast::StmtTryStar {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
} => {
|
||||
range: _,
|
||||
}) => {
|
||||
if body.iter().contains(child) {
|
||||
Ok(has_single_child(body, deleted))
|
||||
} else if orelse.iter().contains(child) {
|
||||
Ok(has_single_child(orelse, deleted))
|
||||
} else if finalbody.iter().contains(child) {
|
||||
Ok(has_single_child(finalbody, deleted))
|
||||
} else if let Some(body) = handlers.iter().find_map(|handler| match &handler.node {
|
||||
ExcepthandlerKind::ExceptHandler { body, .. } => {
|
||||
} else if let Some(body) = handlers.iter().find_map(|handler| match handler {
|
||||
Excepthandler::ExceptHandler(ast::ExcepthandlerExceptHandler { body, .. }) => {
|
||||
if body.iter().contains(child) {
|
||||
Some(body)
|
||||
} else {
|
||||
@@ -83,7 +85,7 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
|
||||
bail!("Unable to find child in parent body")
|
||||
}
|
||||
}
|
||||
StmtKind::Match { cases, .. } => {
|
||||
Stmt::Match(ast::StmtMatch { cases, .. }) => {
|
||||
if let Some(body) = cases.iter().find_map(|case| {
|
||||
if case.body.iter().contains(child) {
|
||||
Some(&case.body)
|
||||
@@ -166,7 +168,7 @@ fn is_end_of_file(stmt: &Stmt, locator: &Locator) -> bool {
|
||||
/// remove the entire start and end lines.
|
||||
/// - If the `Stmt` is the last statement in its parent body, replace it with a
|
||||
/// `pass` instead.
|
||||
pub fn delete_stmt(
|
||||
pub(crate) fn delete_stmt(
|
||||
stmt: &Stmt,
|
||||
parent: Option<&Stmt>,
|
||||
deleted: &[&Stmt],
|
||||
@@ -203,7 +205,7 @@ pub fn delete_stmt(
|
||||
}
|
||||
|
||||
/// Generate a `Fix` to remove any unused imports from an `import` statement.
|
||||
pub fn remove_unused_imports<'a>(
|
||||
pub(crate) fn remove_unused_imports<'a>(
|
||||
unused_imports: impl Iterator<Item = &'a str>,
|
||||
stmt: &Stmt,
|
||||
parent: Option<&Stmt>,
|
||||
@@ -266,10 +268,7 @@ pub fn remove_unused_imports<'a>(
|
||||
let module = module.map(compose_module_path);
|
||||
let member = compose_module_path(&alias.name);
|
||||
let mut full_name = String::with_capacity(
|
||||
relative.len()
|
||||
+ module.as_ref().map_or(0, std::string::String::len)
|
||||
+ member.len()
|
||||
+ 1,
|
||||
relative.len() + module.as_ref().map_or(0, String::len) + member.len() + 1,
|
||||
);
|
||||
for _ in 0..relative.len() {
|
||||
full_name.push('.');
|
||||
@@ -328,7 +327,7 @@ pub fn remove_unused_imports<'a>(
|
||||
///
|
||||
/// Supports the removal of parentheses when this is the only (kw)arg left.
|
||||
/// For this behavior, set `remove_parentheses` to `true`.
|
||||
pub fn remove_argument(
|
||||
pub(crate) fn remove_argument(
|
||||
locator: &Locator,
|
||||
call_at: TextSize,
|
||||
expr_range: TextRange,
|
||||
@@ -350,7 +349,7 @@ pub fn remove_argument(
|
||||
if n_arguments == 1 {
|
||||
// Case 1: there is only one argument.
|
||||
let mut count: usize = 0;
|
||||
for (tok, range) in lexer::lex_located(contents, Mode::Module, call_at).flatten() {
|
||||
for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() {
|
||||
if matches!(tok, Tok::Lpar) {
|
||||
if count == 0 {
|
||||
fix_start = Some(if remove_parentheses {
|
||||
@@ -382,7 +381,7 @@ pub fn remove_argument(
|
||||
{
|
||||
// Case 2: argument or keyword is _not_ the last node.
|
||||
let mut seen_comma = false;
|
||||
for (tok, range) in lexer::lex_located(contents, Mode::Module, call_at).flatten() {
|
||||
for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() {
|
||||
if seen_comma {
|
||||
if matches!(tok, Tok::NonLogicalNewline) {
|
||||
// Also delete any non-logical newlines after the comma.
|
||||
@@ -405,7 +404,7 @@ pub fn remove_argument(
|
||||
} else {
|
||||
// Case 3: argument or keyword is the last node, so we have to find the last
|
||||
// comma in the stmt.
|
||||
for (tok, range) in lexer::lex_located(contents, Mode::Module, call_at).flatten() {
|
||||
for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() {
|
||||
if range.start() == expr_range.start() {
|
||||
fix_end = Some(expr_range.end());
|
||||
break;
|
||||
@@ -432,17 +431,29 @@ pub fn remove_argument(
|
||||
/// name on which the `lru_cache` symbol would be made available (`"functools.lru_cache"`).
|
||||
///
|
||||
/// Attempts to reuse existing imports when possible.
|
||||
pub fn get_or_import_symbol(
|
||||
pub(crate) fn get_or_import_symbol(
|
||||
module: &str,
|
||||
member: &str,
|
||||
at: TextSize,
|
||||
context: &Context,
|
||||
importer: &Importer,
|
||||
locator: &Locator,
|
||||
) -> Result<(Edit, String)> {
|
||||
if let Some((source, binding)) = context.resolve_qualified_import_name(module, member) {
|
||||
// If the symbol is already available in the current scope, use it.
|
||||
//
|
||||
// We also add a no-nop edit to force conflicts with any other fixes that might try to
|
||||
|
||||
// The exception: the symbol source (i.e., the import statement) comes after the current
|
||||
// location. For example, we could be generating an edit within a function, and the import
|
||||
// could be defined in the module scope, but after the function definition. In this case,
|
||||
// it's unclear whether we can use the symbol (the function could be called between the
|
||||
// import and the current location, and thus the symbol would not be available). It's also
|
||||
// unclear whether should add an import statement at the top of the file, since it could
|
||||
// be shadowed between the import and the current location.
|
||||
if source.start() > at {
|
||||
bail!("Unable to use existing symbol `{binding}` due to late-import");
|
||||
}
|
||||
|
||||
// We also add a no-op edit to force conflicts with any other fixes that might try to
|
||||
// remove the import. Consider:
|
||||
//
|
||||
// ```py
|
||||
@@ -462,7 +473,7 @@ pub fn get_or_import_symbol(
|
||||
Edit::range_replacement(locator.slice(source.range()).to_string(), source.range());
|
||||
Ok((import_edit, binding))
|
||||
} else {
|
||||
if let Some(stmt) = importer.get_import_from(module) {
|
||||
if let Some(stmt) = importer.find_import_from(module, at) {
|
||||
// Case 1: `from functools import lru_cache` is in scope, and we're trying to reference
|
||||
// `functools.cache`; thus, we add `cache` to the import, and return `"cache"` as the
|
||||
// bound name.
|
||||
@@ -473,10 +484,7 @@ pub fn get_or_import_symbol(
|
||||
let import_edit = importer.add_member(stmt, member)?;
|
||||
Ok((import_edit, member.to_string()))
|
||||
} else {
|
||||
bail!(
|
||||
"Unable to insert `{}` into scope due to name conflict",
|
||||
member
|
||||
)
|
||||
bail!("Unable to insert `{member}` into scope due to name conflict")
|
||||
}
|
||||
} else {
|
||||
// Case 2: No `functools` import is in scope; thus, we add `import functools`, and
|
||||
@@ -485,13 +493,11 @@ pub fn get_or_import_symbol(
|
||||
.find_binding(module)
|
||||
.map_or(true, |binding| binding.kind.is_builtin())
|
||||
{
|
||||
let import_edit = importer.add_import(&AnyImport::Import(Import::module(module)));
|
||||
let import_edit =
|
||||
importer.add_import(&AnyImport::Import(Import::module(module)), at);
|
||||
Ok((import_edit, format!("{module}.{member}")))
|
||||
} else {
|
||||
bail!(
|
||||
"Unable to insert `{}` into scope due to name conflict",
|
||||
module
|
||||
)
|
||||
bail!("Unable to insert `{module}` into scope due to name conflict")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,16 @@ use ruff_python_ast::source_code::Locator;
|
||||
use crate::linter::FixTable;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
|
||||
pub mod actions;
|
||||
pub(crate) mod actions;
|
||||
|
||||
/// Auto-fix errors in a file, and write the fixed source code to disk.
|
||||
pub fn fix_file(diagnostics: &[Diagnostic], locator: &Locator) -> Option<(String, FixTable)> {
|
||||
pub(crate) fn fix_file(
|
||||
diagnostics: &[Diagnostic],
|
||||
locator: &Locator,
|
||||
) -> Option<(String, FixTable)> {
|
||||
let mut with_fixes = diagnostics
|
||||
.iter()
|
||||
.filter(|diag| !diag.fix.is_empty())
|
||||
.filter(|diag| diag.fix.is_some())
|
||||
.peekable();
|
||||
|
||||
if with_fixes.peek().is_none() {
|
||||
@@ -38,11 +41,10 @@ fn apply_fixes<'a>(
|
||||
|
||||
for (rule, fix) in diagnostics
|
||||
.filter_map(|diagnostic| {
|
||||
if diagnostic.fix.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some((diagnostic.kind.rule(), &diagnostic.fix))
|
||||
}
|
||||
diagnostic
|
||||
.fix
|
||||
.as_ref()
|
||||
.map(|fix| (diagnostic.kind.rule(), fix))
|
||||
})
|
||||
.sorted_by(|(rule1, fix1), (rule2, fix2)| cmp_fix(*rule1, *rule2, fix1, fix2))
|
||||
{
|
||||
@@ -103,18 +105,20 @@ mod tests {
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_diagnostics::Fix;
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
|
||||
use crate::autofix::apply_fixes;
|
||||
use crate::rules::pycodestyle::rules::MissingNewlineAtEndOfFile;
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn create_diagnostics(edit: impl IntoIterator<Item = Edit>) -> Vec<Diagnostic> {
|
||||
edit.into_iter()
|
||||
.map(|edit| Diagnostic {
|
||||
// The choice of rule here is arbitrary.
|
||||
kind: MissingNewlineAtEndOfFile.into(),
|
||||
range: edit.range(),
|
||||
fix: edit.into(),
|
||||
fix: Some(Fix::unspecified(edit)),
|
||||
parent: None,
|
||||
})
|
||||
.collect()
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
use ruff_text_size::TextRange;
|
||||
use rustpython_parser::ast::{Expr, Stmt};
|
||||
use rustpython_parser::ast::Expr;
|
||||
|
||||
use ruff_python_ast::types::RefEquality;
|
||||
use ruff_python_semantic::analyze::visibility::{Visibility, VisibleScope};
|
||||
use ruff_python_semantic::scope::ScopeId;
|
||||
|
||||
use crate::checkers::ast::AnnotationContext;
|
||||
use crate::docstrings::definition::Definition;
|
||||
|
||||
type Context<'a> = (ScopeId, Vec<RefEquality<'a, Stmt>>);
|
||||
use ruff_python_semantic::context::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(Default)]
|
||||
pub struct Deferred<'a> {
|
||||
pub definitions: Vec<(Definition<'a>, Visibility, Context<'a>)>,
|
||||
pub string_type_definitions: Vec<(TextRange, &'a str, AnnotationContext, Context<'a>)>,
|
||||
pub type_definitions: Vec<(&'a Expr, AnnotationContext, Context<'a>)>,
|
||||
pub functions: Vec<(&'a Stmt, Context<'a>, VisibleScope)>,
|
||||
pub lambdas: Vec<(&'a Expr, Context<'a>)>,
|
||||
pub for_loops: Vec<(&'a Stmt, Context<'a>)>,
|
||||
pub assignments: Vec<Context<'a>>,
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct Deferred<'a> {
|
||||
pub(crate) string_type_definitions: Vec<(TextRange, &'a str, Snapshot)>,
|
||||
pub(crate) future_type_definitions: Vec<(&'a Expr, Snapshot)>,
|
||||
pub(crate) functions: Vec<Snapshot>,
|
||||
pub(crate) lambdas: Vec<(&'a Expr, Snapshot)>,
|
||||
pub(crate) for_loops: Vec<Snapshot>,
|
||||
pub(crate) assignments: Vec<Snapshot>,
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
|
||||
use crate::rules::pep8_naming::rules::invalid_module_name;
|
||||
use crate::settings::Settings;
|
||||
|
||||
pub fn check_file_path(
|
||||
pub(crate) fn check_file_path(
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
settings: &Settings,
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
|
||||
use rustpython_parser::ast::{StmtKind, Suite};
|
||||
use rustpython_parser::ast::{self, Ranged, Stmt, Suite};
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::helpers::to_module_path;
|
||||
use ruff_python_ast::imports::{ImportMap, ModuleImport};
|
||||
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
use ruff_python_ast::statement_visitor::StatementVisitor;
|
||||
use ruff_python_stdlib::path::is_python_stub_file;
|
||||
|
||||
use crate::directives::IsortDirectives;
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::isort;
|
||||
use crate::rules::isort::track::{Block, ImportTracker};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::settings::Settings;
|
||||
|
||||
fn extract_import_map(path: &Path, package: Option<&Path>, blocks: &[&Block]) -> Option<ImportMap> {
|
||||
let Some(package) = package else {
|
||||
@@ -28,21 +28,23 @@ fn extract_import_map(path: &Path, package: Option<&Path>, blocks: &[&Block]) ->
|
||||
let num_imports = blocks.iter().map(|block| block.imports.len()).sum();
|
||||
let mut module_imports = Vec::with_capacity(num_imports);
|
||||
for stmt in blocks.iter().flat_map(|block| &block.imports) {
|
||||
match &stmt.node {
|
||||
StmtKind::Import { names } => {
|
||||
match stmt {
|
||||
Stmt::Import(ast::StmtImport { names, range: _ }) => {
|
||||
module_imports.extend(
|
||||
names
|
||||
.iter()
|
||||
.map(|name| ModuleImport::new(name.node.name.clone(), stmt.range())),
|
||||
.map(|name| ModuleImport::new(name.name.to_string(), stmt.range())),
|
||||
);
|
||||
}
|
||||
StmtKind::ImportFrom {
|
||||
Stmt::ImportFrom(ast::StmtImportFrom {
|
||||
module,
|
||||
names,
|
||||
level,
|
||||
} => {
|
||||
let level = level.unwrap_or(0);
|
||||
range: _,
|
||||
}) => {
|
||||
let level = level.map_or(0, |level| level.to_usize());
|
||||
let module = if let Some(module) = module {
|
||||
let module: &String = module.as_ref();
|
||||
if level == 0 {
|
||||
Cow::Borrowed(module)
|
||||
} else {
|
||||
@@ -59,10 +61,10 @@ fn extract_import_map(path: &Path, package: Option<&Path>, blocks: &[&Block]) ->
|
||||
Cow::Owned(module_path[..module_path.len() - level].join("."))
|
||||
};
|
||||
module_imports.extend(names.iter().map(|name| {
|
||||
ModuleImport::new(format!("{}.{}", module, name.node.name), name.range())
|
||||
ModuleImport::new(format!("{}.{}", module, name.name), name.range())
|
||||
}));
|
||||
}
|
||||
_ => panic!("Expected StmtKind::Import | StmtKind::ImportFrom"),
|
||||
_ => panic!("Expected Stmt::Import | Stmt::ImportFrom"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,14 +74,13 @@ fn extract_import_map(path: &Path, package: Option<&Path>, blocks: &[&Block]) ->
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn check_imports(
|
||||
pub(crate) fn check_imports(
|
||||
python_ast: &Suite,
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
directives: &IsortDirectives,
|
||||
settings: &Settings,
|
||||
stylist: &Stylist,
|
||||
autofix: flags::Autofix,
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
) -> (Vec<Diagnostic>, Option<ImportMap>) {
|
||||
@@ -99,7 +100,7 @@ pub fn check_imports(
|
||||
for block in &blocks {
|
||||
if !block.imports.is_empty() {
|
||||
if let Some(diagnostic) = isort::rules::organize_imports(
|
||||
block, locator, stylist, indexer, settings, autofix, package,
|
||||
block, locator, stylist, indexer, settings, package,
|
||||
) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -108,7 +109,7 @@ pub fn check_imports(
|
||||
}
|
||||
if settings.rules.enabled(Rule::MissingRequiredImport) {
|
||||
diagnostics.extend(isort::rules::add_required_imports(
|
||||
&blocks, python_ast, locator, stylist, settings, autofix, is_stub,
|
||||
&blocks, python_ast, locator, stylist, settings, is_stub,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use ruff_text_size::TextRange;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, DiagnosticKind, Fix};
|
||||
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
||||
use ruff_python_ast::source_code::{Locator, Stylist};
|
||||
use ruff_python_ast::token_kind::TokenKind;
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::rules::pycodestyle::rules::logical_lines::{
|
||||
whitespace_around_named_parameter_equals, whitespace_before_comment,
|
||||
whitespace_before_parameters, LogicalLines, TokenFlags,
|
||||
};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::settings::Settings;
|
||||
|
||||
/// Return the amount of indentation, expanding tabs to the next multiple of 8.
|
||||
fn expand_indent(line: &str) -> usize {
|
||||
@@ -30,28 +30,18 @@ fn expand_indent(line: &str) -> usize {
|
||||
indent
|
||||
}
|
||||
|
||||
pub fn check_logical_lines(
|
||||
pub(crate) fn check_logical_lines(
|
||||
tokens: &[LexResult],
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
settings: &Settings,
|
||||
autofix: flags::Autofix,
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut context = LogicalLinesContext::new(settings);
|
||||
|
||||
#[cfg(feature = "logical_lines")]
|
||||
let should_fix_missing_whitespace =
|
||||
autofix.into() && settings.rules.should_fix(Rule::MissingWhitespace);
|
||||
let should_fix_missing_whitespace = settings.rules.should_fix(Rule::MissingWhitespace);
|
||||
|
||||
#[cfg(not(feature = "logical_lines"))]
|
||||
let should_fix_missing_whitespace = false;
|
||||
|
||||
#[cfg(feature = "logical_lines")]
|
||||
let should_fix_whitespace_before_parameters =
|
||||
autofix.into() && settings.rules.should_fix(Rule::WhitespaceBeforeParameters);
|
||||
|
||||
#[cfg(not(feature = "logical_lines"))]
|
||||
let should_fix_whitespace_before_parameters = false;
|
||||
settings.rules.should_fix(Rule::WhitespaceBeforeParameters);
|
||||
|
||||
let mut prev_line = None;
|
||||
let mut prev_indent_level = None;
|
||||
@@ -67,17 +57,18 @@ pub fn check_logical_lines(
|
||||
|
||||
if line
|
||||
.flags()
|
||||
.contains(TokenFlags::OPERATOR | TokenFlags::PUNCTUATION)
|
||||
.intersects(TokenFlags::OPERATOR | TokenFlags::BRACKET | TokenFlags::PUNCTUATION)
|
||||
{
|
||||
extraneous_whitespace(&line, &mut context);
|
||||
}
|
||||
|
||||
if line.flags().contains(TokenFlags::KEYWORD) {
|
||||
whitespace_around_keywords(&line, &mut context);
|
||||
missing_whitespace_after_keyword(&line, &mut context);
|
||||
}
|
||||
|
||||
if line.flags().contains(TokenFlags::COMMENT) {
|
||||
whitespace_before_comment(&line, locator, prev_line.is_none(), &mut context);
|
||||
whitespace_before_comment(&line, locator, &mut context);
|
||||
}
|
||||
|
||||
if line.flags().contains(TokenFlags::BRACKET) {
|
||||
@@ -138,115 +129,21 @@ impl<'a> LogicalLinesContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push<K: Into<DiagnosticKind>>(&mut self, kind: K, range: TextRange) {
|
||||
pub(crate) fn push<K: Into<DiagnosticKind>>(&mut self, kind: K, range: TextRange) {
|
||||
let kind = kind.into();
|
||||
if self.settings.rules.enabled(kind.rule()) {
|
||||
self.diagnostics.push(Diagnostic {
|
||||
kind,
|
||||
range,
|
||||
fix: Fix::empty(),
|
||||
fix: None,
|
||||
parent: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_diagnostic(&mut self, diagnostic: Diagnostic) {
|
||||
pub(crate) fn push_diagnostic(&mut self, diagnostic: Diagnostic) {
|
||||
if self.settings.rules.enabled(diagnostic.kind.rule()) {
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
use rustpython_parser::{lexer, Mode};
|
||||
|
||||
use crate::rules::pycodestyle::rules::logical_lines::LogicalLines;
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
|
||||
#[test]
|
||||
fn split_logical_lines() {
|
||||
let contents = r#"
|
||||
x = 1
|
||||
y = 2
|
||||
z = x + 1"#;
|
||||
let lxr: Vec<LexResult> = lexer::lex(contents, Mode::Module).collect();
|
||||
let locator = Locator::new(contents);
|
||||
let actual: Vec<String> = LogicalLines::from_tokens(&lxr, &locator)
|
||||
.into_iter()
|
||||
.map(|line| line.text_trimmed().to_string())
|
||||
.collect();
|
||||
let expected = vec![
|
||||
"x = 1".to_string(),
|
||||
"y = 2".to_string(),
|
||||
"z = x + 1".to_string(),
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let contents = r#"
|
||||
x = [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
y = 2
|
||||
z = x + 1"#;
|
||||
let lxr: Vec<LexResult> = lexer::lex(contents, Mode::Module).collect();
|
||||
let locator = Locator::new(contents);
|
||||
let actual: Vec<String> = LogicalLines::from_tokens(&lxr, &locator)
|
||||
.into_iter()
|
||||
.map(|line| line.text_trimmed().to_string())
|
||||
.collect();
|
||||
let expected = vec![
|
||||
"x = [\n 1,\n 2,\n 3,\n]".to_string(),
|
||||
"y = 2".to_string(),
|
||||
"z = x + 1".to_string(),
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let contents = "x = 'abc'";
|
||||
let lxr: Vec<LexResult> = lexer::lex(contents, Mode::Module).collect();
|
||||
let locator = Locator::new(contents);
|
||||
let actual: Vec<String> = LogicalLines::from_tokens(&lxr, &locator)
|
||||
.into_iter()
|
||||
.map(|line| line.text_trimmed().to_string())
|
||||
.collect();
|
||||
let expected = vec!["x = 'abc'".to_string()];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let contents = r#"
|
||||
def f():
|
||||
x = 1
|
||||
f()"#;
|
||||
let lxr: Vec<LexResult> = lexer::lex(contents, Mode::Module).collect();
|
||||
let locator = Locator::new(contents);
|
||||
let actual: Vec<String> = LogicalLines::from_tokens(&lxr, &locator)
|
||||
.into_iter()
|
||||
.map(|line| line.text_trimmed().to_string())
|
||||
.collect();
|
||||
let expected = vec!["def f():", "x = 1", "f()"];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let contents = r#"
|
||||
def f():
|
||||
"""Docstring goes here."""
|
||||
# Comment goes here.
|
||||
x = 1
|
||||
f()"#;
|
||||
let lxr: Vec<LexResult> = lexer::lex(contents, Mode::Module).collect();
|
||||
let locator = Locator::new(contents);
|
||||
let actual: Vec<String> = LogicalLines::from_tokens(&lxr, &locator)
|
||||
.into_iter()
|
||||
.map(|line| line.text_trimmed().to_string())
|
||||
.collect();
|
||||
let expected = vec![
|
||||
"def f():",
|
||||
"\"\"\"Docstring goes here.\"\"\"",
|
||||
"",
|
||||
"x = 1",
|
||||
"f()",
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
pub mod ast;
|
||||
pub mod filesystem;
|
||||
pub mod imports;
|
||||
#[cfg(feature = "logical_lines")]
|
||||
pub(crate) mod ast;
|
||||
pub(crate) mod filesystem;
|
||||
pub(crate) mod imports;
|
||||
pub(crate) mod logical_lines;
|
||||
pub mod noqa;
|
||||
pub mod physical_lines;
|
||||
pub mod tokens;
|
||||
pub(crate) mod noqa;
|
||||
pub(crate) mod physical_lines;
|
||||
pub(crate) mod tokens;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use itertools::Itertools;
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit};
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
|
||||
use crate::noqa;
|
||||
@@ -11,15 +11,14 @@ use crate::noqa::{Directive, FileExemption, NoqaDirectives, NoqaMapping};
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::rule_redirects::get_redirect_target;
|
||||
use crate::rules::ruff::rules::{UnusedCodes, UnusedNOQA};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::settings::Settings;
|
||||
|
||||
pub fn check_noqa(
|
||||
pub(crate) fn check_noqa(
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
locator: &Locator,
|
||||
comment_ranges: &[TextRange],
|
||||
noqa_line_for: &NoqaMapping,
|
||||
settings: &Settings,
|
||||
autofix: flags::Autofix,
|
||||
) -> Vec<usize> {
|
||||
let enforce_noqa = settings.rules.enabled(Rule::UnusedNOQA);
|
||||
|
||||
@@ -101,8 +100,9 @@ pub fn check_noqa(
|
||||
if line.matches.is_empty() {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(UnusedNOQA { codes: None }, *noqa_range);
|
||||
if autofix.into() && settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(delete_noqa(
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
#[allow(deprecated)]
|
||||
diagnostic.set_fix_from_edit(delete_noqa(
|
||||
*leading_spaces,
|
||||
*noqa_range,
|
||||
*trailing_spaces,
|
||||
@@ -169,19 +169,21 @@ pub fn check_noqa(
|
||||
},
|
||||
*range,
|
||||
);
|
||||
if autofix.into() && settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
if valid_codes.is_empty() {
|
||||
diagnostic.set_fix(delete_noqa(
|
||||
#[allow(deprecated)]
|
||||
diagnostic.set_fix_from_edit(delete_noqa(
|
||||
*leading_spaces,
|
||||
*range,
|
||||
*trailing_spaces,
|
||||
locator,
|
||||
));
|
||||
} else {
|
||||
diagnostic.set_fix(Edit::range_replacement(
|
||||
#[allow(deprecated)]
|
||||
diagnostic.set_fix(Fix::unspecified(Edit::range_replacement(
|
||||
format!("# noqa: {}", valid_codes.join(", ")),
|
||||
*range,
|
||||
));
|
||||
)));
|
||||
}
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
|
||||
@@ -19,16 +19,15 @@ use crate::rules::pycodestyle::rules::{
|
||||
use crate::rules::pygrep_hooks::rules::{blanket_noqa, blanket_type_ignore};
|
||||
use crate::rules::pylint;
|
||||
use crate::rules::pyupgrade::rules::unnecessary_coding_comment;
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::settings::Settings;
|
||||
|
||||
pub fn check_physical_lines(
|
||||
pub(crate) fn check_physical_lines(
|
||||
path: &Path,
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
indexer: &Indexer,
|
||||
doc_lines: &[TextSize],
|
||||
settings: &Settings,
|
||||
autofix: flags::Autofix,
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
let mut has_any_shebang = false;
|
||||
@@ -51,14 +50,11 @@ pub fn check_physical_lines(
|
||||
settings.rules.enabled(Rule::BlankLineWithWhitespace);
|
||||
let enforce_tab_indentation = settings.rules.enabled(Rule::TabIndentation);
|
||||
|
||||
let fix_unnecessary_coding_comment =
|
||||
autofix.into() && settings.rules.should_fix(Rule::UTF8EncodingDeclaration);
|
||||
let fix_shebang_whitespace =
|
||||
autofix.into() && settings.rules.should_fix(Rule::ShebangLeadingWhitespace);
|
||||
let fix_unnecessary_coding_comment = settings.rules.should_fix(Rule::UTF8EncodingDeclaration);
|
||||
let fix_shebang_whitespace = settings.rules.should_fix(Rule::ShebangLeadingWhitespace);
|
||||
|
||||
let mut commented_lines_iter = indexer.comment_ranges().iter().peekable();
|
||||
let mut doc_lines_iter = doc_lines.iter().peekable();
|
||||
let string_lines = indexer.triple_quoted_string_ranges();
|
||||
|
||||
for (index, line) in locator.contents().universal_newlines().enumerate() {
|
||||
while commented_lines_iter
|
||||
@@ -121,7 +117,7 @@ pub fn check_physical_lines(
|
||||
}
|
||||
|
||||
while doc_lines_iter
|
||||
.next_if(|doc_line_start| line.range().contains(**doc_line_start))
|
||||
.next_if(|doc_line_start| line.range().contains_inclusive(**doc_line_start))
|
||||
.is_some()
|
||||
{
|
||||
if enforce_doc_line_too_long {
|
||||
@@ -148,13 +144,13 @@ pub fn check_physical_lines(
|
||||
}
|
||||
|
||||
if enforce_trailing_whitespace || enforce_blank_line_contains_whitespace {
|
||||
if let Some(diagnostic) = trailing_whitespace(&line, settings, autofix) {
|
||||
if let Some(diagnostic) = trailing_whitespace(&line, settings) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_tab_indentation {
|
||||
if let Some(diagnostic) = tab_indentation(&line, string_lines) {
|
||||
if let Some(diagnostic) = tab_indentation(&line, indexer) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -164,7 +160,7 @@ pub fn check_physical_lines(
|
||||
if let Some(diagnostic) = no_newline_at_end_of_file(
|
||||
locator,
|
||||
stylist,
|
||||
autofix.into() && settings.rules.should_fix(Rule::MissingNewlineAtEndOfFile),
|
||||
settings.rules.should_fix(Rule::MissingNewlineAtEndOfFile),
|
||||
) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -188,7 +184,7 @@ mod tests {
|
||||
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::settings::Settings;
|
||||
|
||||
use super::check_physical_lines;
|
||||
|
||||
@@ -211,7 +207,6 @@ mod tests {
|
||||
line_length,
|
||||
..Settings::for_rule(Rule::LineTooLong)
|
||||
},
|
||||
flags::Autofix::Enabled,
|
||||
)
|
||||
};
|
||||
assert_eq!(check_with_max_line_length(8), vec![]);
|
||||
|
||||
@@ -7,18 +7,17 @@ use crate::lex::docstring_detection::StateMachine;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::rules::ruff::rules::Context;
|
||||
use crate::rules::{
|
||||
eradicate, flake8_commas, flake8_implicit_str_concat, flake8_pyi, flake8_quotes, pycodestyle,
|
||||
pylint, pyupgrade, ruff,
|
||||
eradicate, flake8_commas, flake8_implicit_str_concat, flake8_pyi, flake8_quotes, flake8_todos,
|
||||
pycodestyle, pylint, pyupgrade, ruff,
|
||||
};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::settings::Settings;
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
|
||||
pub fn check_tokens(
|
||||
pub(crate) fn check_tokens(
|
||||
locator: &Locator,
|
||||
tokens: &[LexResult],
|
||||
settings: &Settings,
|
||||
autofix: flags::Autofix,
|
||||
is_stub: bool,
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
@@ -60,6 +59,15 @@ pub fn check_tokens(
|
||||
]);
|
||||
let enforce_extraneous_parenthesis = settings.rules.enabled(Rule::ExtraneousParentheses);
|
||||
let enforce_type_comment_in_stub = settings.rules.enabled(Rule::TypeCommentInStub);
|
||||
let enforce_todos = settings.rules.any_enabled(&[
|
||||
Rule::InvalidTodoTag,
|
||||
Rule::MissingTodoAuthor,
|
||||
Rule::MissingTodoLink,
|
||||
Rule::MissingTodoColon,
|
||||
Rule::MissingTodoDescription,
|
||||
Rule::InvalidTodoCapitalization,
|
||||
Rule::MissingSpaceAfterTodoColon,
|
||||
]);
|
||||
|
||||
// RUF001, RUF002, RUF003
|
||||
if enforce_ambiguous_unicode_character {
|
||||
@@ -85,7 +93,6 @@ pub fn check_tokens(
|
||||
Context::Comment
|
||||
},
|
||||
settings,
|
||||
autofix,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -96,7 +103,7 @@ pub fn check_tokens(
|
||||
for (tok, range) in tokens.iter().flatten() {
|
||||
if matches!(tok, Tok::Comment(_)) {
|
||||
if let Some(diagnostic) =
|
||||
eradicate::rules::commented_out_code(locator, *range, settings, autofix)
|
||||
eradicate::rules::commented_out_code(locator, *range, settings)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -111,7 +118,7 @@ pub fn check_tokens(
|
||||
diagnostics.extend(pycodestyle::rules::invalid_escape_sequence(
|
||||
locator,
|
||||
*range,
|
||||
autofix.into() && settings.rules.should_fix(Rule::InvalidEscapeSequence),
|
||||
settings.rules.should_fix(Rule::InvalidEscapeSequence),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -121,7 +128,7 @@ pub fn check_tokens(
|
||||
for (tok, range) in tokens.iter().flatten() {
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
diagnostics.extend(
|
||||
pylint::rules::invalid_string_characters(locator, *range, autofix.into())
|
||||
pylint::rules::invalid_string_characters(locator, *range)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
@@ -132,7 +139,7 @@ pub fn check_tokens(
|
||||
// E701, E702, E703
|
||||
if enforce_compound_statements {
|
||||
diagnostics.extend(
|
||||
pycodestyle::rules::compound_statements(tokens, settings, autofix)
|
||||
pycodestyle::rules::compound_statements(tokens, settings)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
@@ -141,7 +148,7 @@ pub fn check_tokens(
|
||||
// Q001, Q002, Q003
|
||||
if enforce_quotes {
|
||||
diagnostics.extend(
|
||||
flake8_quotes::rules::from_tokens(tokens, locator, settings, autofix)
|
||||
flake8_quotes::rules::from_tokens(tokens, locator, settings)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
@@ -163,7 +170,7 @@ pub fn check_tokens(
|
||||
// COM812, COM818, COM819
|
||||
if enforce_trailing_comma {
|
||||
diagnostics.extend(
|
||||
flake8_commas::rules::trailing_commas(tokens, locator, settings, autofix)
|
||||
flake8_commas::rules::trailing_commas(tokens, locator, settings)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
@@ -172,8 +179,7 @@ pub fn check_tokens(
|
||||
// UP034
|
||||
if enforce_extraneous_parenthesis {
|
||||
diagnostics.extend(
|
||||
pyupgrade::rules::extraneous_parentheses(tokens, locator, settings, autofix)
|
||||
.into_iter(),
|
||||
pyupgrade::rules::extraneous_parentheses(tokens, locator, settings).into_iter(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -182,5 +188,14 @@ pub fn check_tokens(
|
||||
diagnostics.extend(flake8_pyi::rules::type_comment_in_stub(tokens));
|
||||
}
|
||||
|
||||
// TD001, TD002, TD003, TD004, TD005, TD006, TD007
|
||||
if enforce_todos {
|
||||
diagnostics.extend(
|
||||
flake8_todos::rules::todos(tokens, settings)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
diagnostics
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
use libcst_native::{Expression, NameOrAttribute};
|
||||
|
||||
fn compose_call_path_inner<'a>(expr: &'a Expression, parts: &mut Vec<&'a str>) {
|
||||
match &expr {
|
||||
match expr {
|
||||
Expression::Call(expr) => {
|
||||
compose_call_path_inner(&expr.func, parts);
|
||||
}
|
||||
@@ -16,7 +16,7 @@ fn compose_call_path_inner<'a>(expr: &'a Expression, parts: &mut Vec<&'a str>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compose_call_path(expr: &Expression) -> Option<String> {
|
||||
pub(crate) fn compose_call_path(expr: &Expression) -> Option<String> {
|
||||
let mut segments = vec![];
|
||||
compose_call_path_inner(expr, &mut segments);
|
||||
if segments.is_empty() {
|
||||
@@ -26,7 +26,7 @@ pub fn compose_call_path(expr: &Expression) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compose_module_path(module: &NameOrAttribute) -> String {
|
||||
pub(crate) fn compose_module_path(module: &NameOrAttribute) -> String {
|
||||
match module {
|
||||
NameOrAttribute::N(name) => name.value.to_string(),
|
||||
NameOrAttribute::A(attr) => {
|
||||
|
||||
@@ -4,21 +4,21 @@ use libcst_native::{
|
||||
ImportNames, Module, SimpleString, SmallStatement, Statement,
|
||||
};
|
||||
|
||||
pub fn match_module(module_text: &str) -> Result<Module> {
|
||||
pub(crate) fn match_module(module_text: &str) -> Result<Module> {
|
||||
match libcst_native::parse_module(module_text, None) {
|
||||
Ok(module) => Ok(module),
|
||||
Err(_) => bail!("Failed to extract CST from source"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_expression(expression_text: &str) -> Result<Expression> {
|
||||
pub(crate) fn match_expression(expression_text: &str) -> Result<Expression> {
|
||||
match libcst_native::parse_expression(expression_text) {
|
||||
Ok(expression) => Ok(expression),
|
||||
Err(_) => bail!("Failed to extract CST from source"),
|
||||
Err(_) => bail!("Failed to extract expression from source"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_expr<'a, 'b>(module: &'a mut Module<'b>) -> Result<&'a mut Expr<'b>> {
|
||||
pub(crate) fn match_expr<'a, 'b>(module: &'a mut Module<'b>) -> Result<&'a mut Expr<'b>> {
|
||||
if let Some(Statement::Simple(expr)) = module.body.first_mut() {
|
||||
if let Some(SmallStatement::Expr(expr)) = expr.body.first_mut() {
|
||||
Ok(expr)
|
||||
@@ -30,7 +30,7 @@ pub fn match_expr<'a, 'b>(module: &'a mut Module<'b>) -> Result<&'a mut Expr<'b>
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_import<'a, 'b>(module: &'a mut Module<'b>) -> Result<&'a mut Import<'b>> {
|
||||
pub(crate) fn match_import<'a, 'b>(module: &'a mut Module<'b>) -> Result<&'a mut Import<'b>> {
|
||||
if let Some(Statement::Simple(expr)) = module.body.first_mut() {
|
||||
if let Some(SmallStatement::Import(expr)) = expr.body.first_mut() {
|
||||
Ok(expr)
|
||||
@@ -42,7 +42,9 @@ pub fn match_import<'a, 'b>(module: &'a mut Module<'b>) -> Result<&'a mut Import
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_import_from<'a, 'b>(module: &'a mut Module<'b>) -> Result<&'a mut ImportFrom<'b>> {
|
||||
pub(crate) fn match_import_from<'a, 'b>(
|
||||
module: &'a mut Module<'b>,
|
||||
) -> Result<&'a mut ImportFrom<'b>> {
|
||||
if let Some(Statement::Simple(expr)) = module.body.first_mut() {
|
||||
if let Some(SmallStatement::ImportFrom(expr)) = expr.body.first_mut() {
|
||||
Ok(expr)
|
||||
@@ -54,7 +56,7 @@ pub fn match_import_from<'a, 'b>(module: &'a mut Module<'b>) -> Result<&'a mut I
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_aliases<'a, 'b>(
|
||||
pub(crate) fn match_aliases<'a, 'b>(
|
||||
import_from: &'a mut ImportFrom<'b>,
|
||||
) -> Result<&'a mut Vec<ImportAlias<'b>>> {
|
||||
if let ImportNames::Aliases(aliases) = &mut import_from.names {
|
||||
@@ -64,7 +66,7 @@ pub fn match_aliases<'a, 'b>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_call<'a, 'b>(expression: &'a mut Expression<'b>) -> Result<&'a mut Call<'b>> {
|
||||
pub(crate) fn match_call<'a, 'b>(expression: &'a mut Expression<'b>) -> Result<&'a mut Call<'b>> {
|
||||
if let Expression::Call(call) = expression {
|
||||
Ok(call)
|
||||
} else {
|
||||
@@ -72,7 +74,7 @@ pub fn match_call<'a, 'b>(expression: &'a mut Expression<'b>) -> Result<&'a mut
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_comparison<'a, 'b>(
|
||||
pub(crate) fn match_comparison<'a, 'b>(
|
||||
expression: &'a mut Expression<'b>,
|
||||
) -> Result<&'a mut Comparison<'b>> {
|
||||
if let Expression::Comparison(comparison) = expression {
|
||||
@@ -82,7 +84,7 @@ pub fn match_comparison<'a, 'b>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_dict<'a, 'b>(expression: &'a mut Expression<'b>) -> Result<&'a mut Dict<'b>> {
|
||||
pub(crate) fn match_dict<'a, 'b>(expression: &'a mut Expression<'b>) -> Result<&'a mut Dict<'b>> {
|
||||
if let Expression::Dict(dict) = expression {
|
||||
Ok(dict)
|
||||
} else {
|
||||
@@ -90,7 +92,7 @@ pub fn match_dict<'a, 'b>(expression: &'a mut Expression<'b>) -> Result<&'a mut
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_attribute<'a, 'b>(
|
||||
pub(crate) fn match_attribute<'a, 'b>(
|
||||
expression: &'a mut Expression<'b>,
|
||||
) -> Result<&'a mut Attribute<'b>> {
|
||||
if let Expression::Attribute(attribute) = expression {
|
||||
@@ -100,7 +102,7 @@ pub fn match_attribute<'a, 'b>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_simple_string<'a, 'b>(
|
||||
pub(crate) fn match_simple_string<'a, 'b>(
|
||||
expression: &'a mut Expression<'b>,
|
||||
) -> Result<&'a mut SimpleString<'b>> {
|
||||
if let Expression::SimpleString(simple_string) = expression {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
pub mod helpers;
|
||||
pub mod matchers;
|
||||
pub(crate) mod helpers;
|
||||
pub(crate) mod matchers;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
//! Extract `# noqa` and `# isort: skip` directives from tokenized source.
|
||||
|
||||
use crate::noqa::NoqaMapping;
|
||||
use bitflags::bitflags;
|
||||
use ruff_python_ast::source_code::{Indexer, Locator};
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
use rustpython_parser::Tok;
|
||||
|
||||
use ruff_python_ast::source_code::{Indexer, Locator};
|
||||
|
||||
use crate::noqa::NoqaMapping;
|
||||
use crate::settings::Settings;
|
||||
|
||||
bitflags! {
|
||||
@@ -102,7 +103,10 @@ pub fn extract_noqa_line_for(
|
||||
..
|
||||
} => {
|
||||
if locator.contains_line_break(*range) {
|
||||
string_mappings.push(*range);
|
||||
string_mappings.push(TextRange::new(
|
||||
locator.line_start(range.start()),
|
||||
range.end(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,11 +223,12 @@ pub fn extract_isort_directives(lxr: &[LexResult], locator: &Locator) -> IsortDi
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ruff_python_ast::source_code::{Indexer, Locator};
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
use rustpython_parser::{lexer, Mode};
|
||||
|
||||
use ruff_python_ast::source_code::{Indexer, Locator};
|
||||
|
||||
use crate::directives::{extract_isort_directives, extract_noqa_line_for};
|
||||
use crate::noqa::NoqaMapping;
|
||||
|
||||
@@ -271,7 +276,7 @@ y = 2
|
||||
z = x + 1";
|
||||
assert_eq!(
|
||||
noqa_mappings(contents),
|
||||
NoqaMapping::from_iter([TextRange::new(TextSize::from(4), TextSize::from(22)),])
|
||||
NoqaMapping::from_iter([TextRange::new(TextSize::from(0), TextSize::from(22)),])
|
||||
);
|
||||
|
||||
let contents = "x = 1
|
||||
@@ -282,7 +287,7 @@ ghi
|
||||
z = 2";
|
||||
assert_eq!(
|
||||
noqa_mappings(contents),
|
||||
NoqaMapping::from_iter([TextRange::new(TextSize::from(10), TextSize::from(28))])
|
||||
NoqaMapping::from_iter([TextRange::new(TextSize::from(6), TextSize::from(28))])
|
||||
);
|
||||
|
||||
let contents = "x = 1
|
||||
@@ -292,7 +297,7 @@ ghi
|
||||
'''";
|
||||
assert_eq!(
|
||||
noqa_mappings(contents),
|
||||
NoqaMapping::from_iter([TextRange::new(TextSize::from(10), TextSize::from(28))])
|
||||
NoqaMapping::from_iter([TextRange::new(TextSize::from(6), TextSize::from(28))])
|
||||
);
|
||||
|
||||
let contents = r#"x = \
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user