Compare commits
129 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d4f0a8320 | ||
|
|
4149bc7be8 | ||
|
|
e6316b185e | ||
|
|
a2183be96e | ||
|
|
df39a95925 | ||
|
|
ff859ead85 | ||
|
|
b2be30cb07 | ||
|
|
b9c1a3c5c1 | ||
|
|
9751951d10 | ||
|
|
64c79bde83 | ||
|
|
da0374f360 | ||
|
|
c26b58ba28 | ||
|
|
56f935640a | ||
|
|
85ca6cde49 | ||
|
|
38addbe50d | ||
|
|
924e35b1c3 | ||
|
|
d05ea4dbac | ||
|
|
b5ac93d2ee | ||
|
|
924e264156 | ||
|
|
14c5000ad5 | ||
|
|
d985473f4f | ||
|
|
47e0b2521a | ||
|
|
a319980a7c | ||
|
|
3336dd63f4 | ||
|
|
ae20a721a1 | ||
|
|
a26b1f43e9 | ||
|
|
139a6d8331 | ||
|
|
04ef674195 | ||
|
|
db852a0b11 | ||
|
|
87c3b0e4e2 | ||
|
|
82784a7607 | ||
|
|
f2da855048 | ||
|
|
81b60cf9fe | ||
|
|
c96ba6dec4 | ||
|
|
0f8f250bea | ||
|
|
a3ffaa5d9b | ||
|
|
187ed874e9 | ||
|
|
a30c77e752 | ||
|
|
7e9b9cc7b3 | ||
|
|
d4cef9305a | ||
|
|
a074625121 | ||
|
|
9c55ab35df | ||
|
|
a95474f2b1 | ||
|
|
3e6fe46bc4 | ||
|
|
bc81cea4f4 | ||
|
|
cb0f226962 | ||
|
|
fa56fabed9 | ||
|
|
bdcab87d2f | ||
|
|
ec8b827d26 | ||
|
|
b232c43824 | ||
|
|
ee01e666c5 | ||
|
|
2b0de8ccd9 | ||
|
|
739c57b31b | ||
|
|
c3e0137f22 | ||
|
|
77716108af | ||
|
|
335395adec | ||
|
|
65f8f1a6f7 | ||
|
|
858af8debb | ||
|
|
5f1bbf0b6b | ||
|
|
40cb905ae5 | ||
|
|
e89b4a5de5 | ||
|
|
651f6b6bce | ||
|
|
d3c3198b24 | ||
|
|
ec6054edce | ||
|
|
a0df78cb7d | ||
|
|
ac41c33d1f | ||
|
|
b4b8782243 | ||
|
|
8e53a4d1d3 | ||
|
|
668860cba3 | ||
|
|
038e8cfba0 | ||
|
|
ebfa55cea3 | ||
|
|
aa0fc0f9c2 | ||
|
|
aa85c81280 | ||
|
|
f5fd6f59ea | ||
|
|
540e31f5f4 | ||
|
|
8136cc9238 | ||
|
|
2c71535016 | ||
|
|
cce8fb9882 | ||
|
|
9e59c99133 | ||
|
|
b032f50775 | ||
|
|
1c2fc38853 | ||
|
|
f16f3a4a03 | ||
|
|
30a09ec211 | ||
|
|
ec7b25290b | ||
|
|
68422d4ff2 | ||
|
|
2abaffd65b | ||
|
|
06cbf5a2ae | ||
|
|
f432ce291a | ||
|
|
1eb331143d | ||
|
|
db1b1672b8 | ||
|
|
6861e59103 | ||
|
|
778c644ee3 | ||
|
|
e66a6b6d05 | ||
|
|
faea478ca5 | ||
|
|
39b5fa0e24 | ||
|
|
df413d1ece | ||
|
|
cfd0693ae5 | ||
|
|
56ad160c05 | ||
|
|
9d8c6ba671 | ||
|
|
1ea88ea56b | ||
|
|
7f44ffb55c | ||
|
|
dbd640d90f | ||
|
|
e5082c7d6c | ||
|
|
841d176289 | ||
|
|
c15595325c | ||
|
|
e97b1a4280 | ||
|
|
82ec884a61 | ||
|
|
7c1a6bce7b | ||
|
|
84a8b628b8 | ||
|
|
142b627bb8 | ||
|
|
fbf231e1b8 | ||
|
|
1dd9ccf7f6 | ||
|
|
d601abe01b | ||
|
|
15d4774b6b | ||
|
|
293c7e00d5 | ||
|
|
c3a3195922 | ||
|
|
39d98d3488 | ||
|
|
cd3d82213a | ||
|
|
a9a0026f2f | ||
|
|
da4618d77b | ||
|
|
1b0748d19d | ||
|
|
0b7fa64481 | ||
|
|
09d593b124 | ||
|
|
adc134ced0 | ||
|
|
6051a0c1c8 | ||
|
|
00495e8620 | ||
|
|
ad8693e3de | ||
|
|
69e20c4554 | ||
|
|
b5816634b3 |
@@ -1,2 +1,28 @@
|
||||
[alias]
|
||||
dev = "run --package ruff_dev --bin ruff_dev"
|
||||
|
||||
[target.'cfg(all())']
|
||||
rustflags = [
|
||||
# CLIPPY LINT SETTINGS
|
||||
# This is a workaround to configure lints for the entire workspace, pending the ability to configure this via TOML.
|
||||
# See: `https://github.com/rust-lang/cargo/issues/5034`
|
||||
# `https://github.com/EmbarkStudios/rust-ecosystem/issues/22#issuecomment-947011395`
|
||||
"-Dunsafe_code",
|
||||
"-Wclippy::pedantic",
|
||||
# Allowed pedantic lints
|
||||
"-Wclippy::char_lit_as_u8",
|
||||
"-Aclippy::collapsible_else_if",
|
||||
"-Aclippy::collapsible_if",
|
||||
"-Aclippy::implicit_hasher",
|
||||
"-Aclippy::match_same_arms",
|
||||
"-Aclippy::missing_errors_doc",
|
||||
"-Aclippy::missing_panics_doc",
|
||||
"-Aclippy::module_name_repetitions",
|
||||
"-Aclippy::must_use_candidate",
|
||||
"-Aclippy::similar_names",
|
||||
"-Aclippy::too_many_lines",
|
||||
# Disallowed restriction lints
|
||||
"-Wclippy::print_stdout",
|
||||
"-Wclippy::print_stderr",
|
||||
"-Wclippy::dbg_macro",
|
||||
]
|
||||
|
||||
8
.github/ISSUE_TEMPLATE.md
vendored
8
.github/ISSUE_TEMPLATE.md
vendored
@@ -3,8 +3,8 @@ Thank you for taking the time to report an issue! We're glad to have you involve
|
||||
|
||||
If you're filing a bug report, please consider including the following information:
|
||||
|
||||
- A minimal code snippet that reproduces the bug.
|
||||
- The command you invoked (e.g., `ruff /path/to/file.py --fix`), ideally including the `--isolated` flag.
|
||||
- The current Ruff settings (any relevant sections from your `pyproject.toml`).
|
||||
- The current Ruff version (`ruff --version`).
|
||||
* A minimal code snippet that reproduces the bug.
|
||||
* The command you invoked (e.g., `ruff /path/to/file.py --fix`), ideally including the `--isolated` flag.
|
||||
* The current Ruff settings (any relevant sections from your `pyproject.toml`).
|
||||
* The current Ruff version (`ruff --version`).
|
||||
-->
|
||||
|
||||
18
.github/workflows/ci.yaml
vendored
18
.github/workflows/ci.yaml
vendored
@@ -50,13 +50,13 @@ jobs:
|
||||
rustup component add clippy
|
||||
rustup target add wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic
|
||||
- run: cargo clippy -p ruff --target wasm32-unknown-unknown --all-features -- -D warnings -W clippy::pedantic
|
||||
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
- run: cargo clippy -p ruff --target wasm32-unknown-unknown --all-features -- -D warnings
|
||||
|
||||
cargo-test:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: "cargo test | ${{ matrix.os }}"
|
||||
steps:
|
||||
@@ -95,8 +95,8 @@ jobs:
|
||||
- run: ./scripts/add_rule.py --name DoTheThing --code PLC999 --linter pylint
|
||||
- run: cargo check
|
||||
- run: |
|
||||
./scripts/add_plugin.py test --url https://pypi.org/project/-test/0.1.0/ --prefix TST
|
||||
./scripts/add_rule.py --name FirstRule --code TST001 --linter test
|
||||
./scripts/add_plugin.py test --url https://pypi.org/project/-test/0.1.0/ --prefix TST
|
||||
./scripts/add_rule.py --name FirstRule --code TST001 --linter test
|
||||
- run: cargo check
|
||||
|
||||
maturin-build:
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
name: "spell check"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: crate-ci/typos@master
|
||||
with:
|
||||
files: .
|
||||
- uses: actions/checkout@v3
|
||||
- uses: crate-ci/typos@master
|
||||
with:
|
||||
files: .
|
||||
|
||||
3
.github/workflows/docs.yaml
vendored
3
.github/workflows/docs.yaml
vendored
@@ -6,10 +6,9 @@ on:
|
||||
- README.md
|
||||
- mkdocs.template.yml
|
||||
- .github/workflows/docs.yaml
|
||||
branches: [ main ]
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
jobs:
|
||||
mkdocs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
2
.github/workflows/playground.yaml
vendored
2
.github/workflows/playground.yaml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
|
||||
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
|
||||
2
.github/workflows/ruff.yaml
vendored
2
.github/workflows/ruff.yaml
vendored
@@ -138,7 +138,7 @@ jobs:
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --no-default-features --release --out dist
|
||||
args: --release --out dist
|
||||
- uses: uraimo/run-on-arch-action@v2.5.0
|
||||
if: matrix.target != 'ppc64'
|
||||
name: Install built wheel
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
||||
resources/test/cpython
|
||||
docs/
|
||||
mkdocs.yml
|
||||
.overrides
|
||||
|
||||
###
|
||||
# Rust.gitignore
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.238
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix]
|
||||
exclude: ^resources
|
||||
|
||||
- repo: https://github.com/abravalheri/validate-pyproject
|
||||
rev: v0.10.1
|
||||
hooks:
|
||||
- id: validate-pyproject
|
||||
|
||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||
rev: v0.33.0
|
||||
hooks:
|
||||
- id: markdownlint-fix
|
||||
args:
|
||||
- --disable
|
||||
- MD013 # line-length
|
||||
- MD033 # no-inline-html
|
||||
- --
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: cargo-fmt
|
||||
@@ -20,12 +23,22 @@ repos:
|
||||
types: [rust]
|
||||
- id: clippy
|
||||
name: clippy
|
||||
entry: cargo clippy --workspace --all-targets --all-features
|
||||
entry: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
language: rust
|
||||
pass_filenames: false
|
||||
- id: ruff
|
||||
name: ruff
|
||||
entry: cargo run -- --no-cache --fix
|
||||
language: rust
|
||||
types_or: [python, pyi]
|
||||
require_serial: true
|
||||
exclude: ^resources
|
||||
- id: dev-generate-all
|
||||
name: dev-generate-all
|
||||
entry: cargo dev generate-all
|
||||
language: rust
|
||||
pass_filenames: false
|
||||
exclude: target
|
||||
|
||||
ci:
|
||||
skip: [cargo-fmt, clippy, dev-generate-all]
|
||||
|
||||
@@ -37,22 +37,24 @@ will enable all `F` rules, including `F401`, as the command line's `--select` re
|
||||
The `remove-six-compat` rule has been removed. This rule was only useful for one-time Python 2-to-3
|
||||
upgrades.
|
||||
|
||||
## 0.0.238
|
||||
## 0.0.237
|
||||
|
||||
### `--explain`, `--clean`, and `--generate-shell-completion` are now subcommands ([#2190](https://github.com/charliermarsh/ruff/pull/2190))
|
||||
|
||||
`--explain`, `--clean`, and `--generate-shell-completion` are now implemented as subcommands:
|
||||
|
||||
ruff . # Still works! And will always work.
|
||||
ruff check . # New! Also works.
|
||||
```console
|
||||
ruff . # Still works! And will always work.
|
||||
ruff check . # New! Also works.
|
||||
|
||||
ruff --explain E402 # Still works.
|
||||
ruff rule E402 # New! Also works. (And preferred.)
|
||||
ruff --explain E402 # Still works.
|
||||
ruff rule E402 # New! Also works. (And preferred.)
|
||||
|
||||
# Oops! The command has to come first.
|
||||
ruff --format json --explain E402 # No longer works.
|
||||
ruff --explain E402 --format json # Still works!
|
||||
ruff rule E402 --format json # Works! (And preferred.)
|
||||
# Oops! The command has to come first.
|
||||
ruff --format json --explain E402 # No longer works.
|
||||
ruff --explain E402 --format json # Still works!
|
||||
ruff rule E402 --format json # Works! (And preferred.)
|
||||
```
|
||||
|
||||
This change is largely backwards compatible -- most users should experience
|
||||
no change in behavior. However, please note the following exceptions:
|
||||
@@ -60,7 +62,9 @@ no change in behavior. However, please note the following exceptions:
|
||||
* Subcommands will now fail when invoked with unsupported arguments, instead
|
||||
of silently ignoring them. For example, the following will now fail:
|
||||
|
||||
ruff --clean --respect-gitignore
|
||||
```console
|
||||
ruff --clean --respect-gitignore
|
||||
```
|
||||
|
||||
(the `clean` command doesn't support `--respect-gitignore`.)
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
@@ -115,14 +115,12 @@ the community.
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
version 2.0, available [here](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html).
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
For answers to common questions about this code of conduct, see the [FAQ](https://www.contributor-covenant.org/faq).
|
||||
Translations are available [here](https://www.contributor-covenant.org/translations).
|
||||
|
||||
144
CONTRIBUTING.md
144
CONTRIBUTING.md
@@ -4,10 +4,14 @@ Welcome! We're happy to have you here. Thank you in advance for your contributio
|
||||
|
||||
## The basics
|
||||
|
||||
Ruff welcomes contributions in the form of Pull Requests. For small changes (e.g., bug fixes), feel
|
||||
free to submit a PR. For larger changes (e.g., new lint rules, new functionality, new configuration
|
||||
options), consider submitting an [Issue](https://github.com/charliermarsh/ruff/issues) outlining
|
||||
your proposed change.
|
||||
Ruff welcomes contributions in the form of Pull Requests.
|
||||
|
||||
For small changes (e.g., bug fixes), feel free to submit a PR.
|
||||
|
||||
For larger changes (e.g., new lint rules, new functionality, new configuration options), consider
|
||||
creating an [**issue**](https://github.com/charliermarsh/ruff/issues) outlining your proposed
|
||||
change. You can also join us on [**Discord**](https://discord.gg/Z8KbeK24) to discuss your idea with
|
||||
the community.
|
||||
|
||||
If you're looking for a place to start, we recommend implementing a new lint rule (see:
|
||||
[_Adding a new lint rule_](#example-adding-a-new-lint-rule), which will allow you to learn from and
|
||||
@@ -50,7 +54,14 @@ cargo test --all # Testing...
|
||||
These checks will run on GitHub Actions when you open your Pull Request, but running them locally
|
||||
will save you time and expedite the merge process.
|
||||
|
||||
If you have `pre-commit` [installed](https://pre-commit.com/#installation) then you can use it to
|
||||
Note that many code changes also require updating the snapshot tests, which is done interactively
|
||||
after running `cargo test` like so:
|
||||
|
||||
```shell
|
||||
cargo insta review
|
||||
```
|
||||
|
||||
If you have `pre-commit` [installed](https://pre-commit.com/#installation) then you can use it to
|
||||
assist with formatting and linting. The following command will run the `pre-commit` hooks:
|
||||
|
||||
```shell
|
||||
@@ -135,3 +146,126 @@ them to [PyPI](https://pypi.org/project/ruff/).
|
||||
|
||||
Ruff follows the [semver](https://semver.org/) versioning standard. However, as pre-1.0 software,
|
||||
even patch releases may contain [non-backwards-compatible changes](https://semver.org/#spec-item-4).
|
||||
|
||||
## Benchmarks
|
||||
|
||||
First, clone [CPython](https://github.com/python/cpython). It's a large and diverse Python codebase,
|
||||
which makes it a good target for benchmarking.
|
||||
|
||||
```shell
|
||||
git clone --branch 3.10 https://github.com/python/cpython.git resources/test/cpython
|
||||
```
|
||||
|
||||
To benchmark the release build:
|
||||
|
||||
```shell
|
||||
cargo build --release && hyperfine --ignore-failure --warmup 10 \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache" \
|
||||
"./target/release/ruff ./resources/test/cpython/"
|
||||
|
||||
Benchmark 1: ./target/release/ruff ./resources/test/cpython/ --no-cache
|
||||
Time (mean ± σ): 293.8 ms ± 3.2 ms [User: 2384.6 ms, System: 90.3 ms]
|
||||
Range (min … max): 289.9 ms … 301.6 ms 10 runs
|
||||
|
||||
Warning: Ignoring non-zero exit code.
|
||||
|
||||
Benchmark 2: ./target/release/ruff ./resources/test/cpython/
|
||||
Time (mean ± σ): 48.0 ms ± 3.1 ms [User: 65.2 ms, System: 124.7 ms]
|
||||
Range (min … max): 45.0 ms … 66.7 ms 62 runs
|
||||
|
||||
Warning: Ignoring non-zero exit code.
|
||||
|
||||
Summary
|
||||
'./target/release/ruff ./resources/test/cpython/' ran
|
||||
6.12 ± 0.41 times faster than './target/release/ruff ./resources/test/cpython/ --no-cache'
|
||||
```
|
||||
|
||||
To benchmark against the ecosystem's existing tools:
|
||||
|
||||
```shell
|
||||
hyperfine --ignore-failure --warmup 5 \
|
||||
"./target/release/ruff ./resources/test/cpython/ --no-cache" \
|
||||
"pyflakes resources/test/cpython" \
|
||||
"autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython" \
|
||||
"pycodestyle resources/test/cpython" \
|
||||
"flake8 resources/test/cpython"
|
||||
|
||||
Benchmark 1: ./target/release/ruff ./resources/test/cpython/ --no-cache
|
||||
Time (mean ± σ): 294.3 ms ± 3.3 ms [User: 2467.5 ms, System: 89.6 ms]
|
||||
Range (min … max): 291.1 ms … 302.8 ms 10 runs
|
||||
|
||||
Warning: Ignoring non-zero exit code.
|
||||
|
||||
Benchmark 2: pyflakes resources/test/cpython
|
||||
Time (mean ± σ): 15.786 s ± 0.143 s [User: 15.560 s, System: 0.214 s]
|
||||
Range (min … max): 15.640 s … 16.157 s 10 runs
|
||||
|
||||
Warning: Ignoring non-zero exit code.
|
||||
|
||||
Benchmark 3: autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython
|
||||
Time (mean ± σ): 6.175 s ± 0.169 s [User: 54.102 s, System: 1.057 s]
|
||||
Range (min … max): 5.950 s … 6.391 s 10 runs
|
||||
|
||||
Benchmark 4: pycodestyle resources/test/cpython
|
||||
Time (mean ± σ): 46.921 s ± 0.508 s [User: 46.699 s, System: 0.202 s]
|
||||
Range (min … max): 46.171 s … 47.863 s 10 runs
|
||||
|
||||
Warning: Ignoring non-zero exit code.
|
||||
|
||||
Benchmark 5: flake8 resources/test/cpython
|
||||
Time (mean ± σ): 12.260 s ± 0.321 s [User: 102.934 s, System: 1.230 s]
|
||||
Range (min … max): 11.848 s … 12.933 s 10 runs
|
||||
|
||||
Warning: Ignoring non-zero exit code.
|
||||
|
||||
Summary
|
||||
'./target/release/ruff ./resources/test/cpython/ --no-cache' ran
|
||||
20.98 ± 0.62 times faster than 'autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython'
|
||||
41.66 ± 1.18 times faster than 'flake8 resources/test/cpython'
|
||||
53.64 ± 0.77 times faster than 'pyflakes resources/test/cpython'
|
||||
159.43 ± 2.48 times faster than 'pycodestyle resources/test/cpython'
|
||||
```
|
||||
|
||||
You can run `poetry install` from `./scripts` to create a working environment for the above. All
|
||||
reported benchmarks were computed using the versions specified by `./scripts/pyproject.toml`
|
||||
on Python 3.11.
|
||||
|
||||
To benchmark Pylint, remove the following files from the CPython repository:
|
||||
|
||||
```shell
|
||||
rm Lib/test/bad_coding.py \
|
||||
Lib/test/bad_coding2.py \
|
||||
Lib/test/bad_getattr.py \
|
||||
Lib/test/bad_getattr2.py \
|
||||
Lib/test/bad_getattr3.py \
|
||||
Lib/test/badcert.pem \
|
||||
Lib/test/badkey.pem \
|
||||
Lib/test/badsyntax_3131.py \
|
||||
Lib/test/badsyntax_future10.py \
|
||||
Lib/test/badsyntax_future3.py \
|
||||
Lib/test/badsyntax_future4.py \
|
||||
Lib/test/badsyntax_future5.py \
|
||||
Lib/test/badsyntax_future6.py \
|
||||
Lib/test/badsyntax_future7.py \
|
||||
Lib/test/badsyntax_future8.py \
|
||||
Lib/test/badsyntax_future9.py \
|
||||
Lib/test/badsyntax_pep3120.py \
|
||||
Lib/test/test_asyncio/test_runners.py \
|
||||
Lib/test/test_copy.py \
|
||||
Lib/test/test_inspect.py \
|
||||
Lib/test/test_typing.py
|
||||
```
|
||||
|
||||
Then, from `resources/test/cpython`, run: `time pylint -j 0 -E $(git ls-files '*.py')`. This
|
||||
will execute Pylint with maximum parallelism and only report errors.
|
||||
|
||||
To benchmark Pyupgrade, run the following from `resources/test/cpython`:
|
||||
|
||||
```shell
|
||||
hyperfine --ignore-failure --warmup 5 --prepare "git reset --hard HEAD" \
|
||||
"find . -type f -name \"*.py\" | xargs -P 0 pyupgrade --py311-plus"
|
||||
|
||||
Benchmark 1: find . -type f -name "*.py" | xargs -P 0 pyupgrade --py311-plus
|
||||
Time (mean ± σ): 30.119 s ± 0.195 s [User: 28.638 s, System: 0.390 s]
|
||||
Range (min … max): 29.813 s … 30.356 s 10 runs
|
||||
```
|
||||
|
||||
84
Cargo.lock
generated
84
Cargo.lock
generated
@@ -608,15 +608,6 @@ dependencies = [
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "4.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
@@ -750,7 +741,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.238"
|
||||
version = "0.0.241"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.4",
|
||||
@@ -945,7 +936,7 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f92123bf2fe0d9f1b5df1964727b970ca3b2d0203d47cf97fb1f36d856b6398"
|
||||
dependencies = [
|
||||
"phf 0.11.1",
|
||||
"phf",
|
||||
"rust-stemmers",
|
||||
]
|
||||
|
||||
@@ -1571,15 +1562,6 @@ dependencies = [
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
|
||||
dependencies = [
|
||||
"phf_shared 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.1"
|
||||
@@ -1589,36 +1571,16 @@ dependencies = [
|
||||
"phf_shared 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
|
||||
dependencies = [
|
||||
"phf_generator 0.10.0",
|
||||
"phf_shared 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770"
|
||||
dependencies = [
|
||||
"phf_generator 0.11.1",
|
||||
"phf_generator",
|
||||
"phf_shared 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
|
||||
dependencies = [
|
||||
"phf_shared 0.10.0",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.1"
|
||||
@@ -1922,7 +1884,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.238"
|
||||
version = "0.0.241"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
@@ -1977,7 +1939,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.238"
|
||||
version = "0.0.241"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2007,14 +1969,13 @@ dependencies = [
|
||||
"similar",
|
||||
"strum",
|
||||
"textwrap",
|
||||
"update-informer",
|
||||
"ureq",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.238"
|
||||
version = "0.0.241"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.4",
|
||||
@@ -2035,7 +1996,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.238"
|
||||
version = "0.0.241"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
@@ -2089,7 +2050,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=4f38cb68e4a97aeea9eb19673803a0bd5f655383#4f38cb68e4a97aeea9eb19673803a0bd5f655383"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=adc23253e4b58980b407ba2760dbe61681d752fc#adc23253e4b58980b407ba2760dbe61681d752fc"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-common",
|
||||
@@ -2099,7 +2060,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-common"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=4f38cb68e4a97aeea9eb19673803a0bd5f655383#4f38cb68e4a97aeea9eb19673803a0bd5f655383"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=adc23253e4b58980b407ba2760dbe61681d752fc#adc23253e4b58980b407ba2760dbe61681d752fc"
|
||||
dependencies = [
|
||||
"ascii",
|
||||
"bitflags",
|
||||
@@ -2124,7 +2085,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=4f38cb68e4a97aeea9eb19673803a0bd5f655383#4f38cb68e4a97aeea9eb19673803a0bd5f655383"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=adc23253e4b58980b407ba2760dbe61681d752fc#adc23253e4b58980b407ba2760dbe61681d752fc"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
@@ -2141,7 +2102,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=4f38cb68e4a97aeea9eb19673803a0bd5f655383#4f38cb68e4a97aeea9eb19673803a0bd5f655383"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=adc23253e4b58980b407ba2760dbe61681d752fc#adc23253e4b58980b407ba2760dbe61681d752fc"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -2151,8 +2112,8 @@ dependencies = [
|
||||
"log",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"phf 0.10.1",
|
||||
"phf_codegen 0.10.0",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
"rustc-hash",
|
||||
"rustpython-ast",
|
||||
"rustpython-compiler-core",
|
||||
@@ -2455,8 +2416,8 @@ dependencies = [
|
||||
"dirs",
|
||||
"fnv",
|
||||
"nom",
|
||||
"phf 0.11.1",
|
||||
"phf_codegen 0.11.1",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2758,19 +2719,6 @@ version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "update-informer"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "152ff185ca29f7f487c51ca785b0f1d85970c4581f4cdd12ed499227890200f5"
|
||||
dependencies = [
|
||||
"directories",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"ureq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "2.6.2"
|
||||
@@ -2782,8 +2730,6 @@ dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
"webpki",
|
||||
"webpki-roots",
|
||||
|
||||
12
Cargo.toml
12
Cargo.toml
@@ -8,7 +8,7 @@ default-members = [".", "ruff_cli"]
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.238"
|
||||
version = "0.0.241"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
@@ -46,11 +46,11 @@ num-traits = "0.2.15"
|
||||
once_cell = { version = "1.16.0" }
|
||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
||||
regex = { version = "1.6.0" }
|
||||
ruff_macros = { version = "0.0.238", path = "ruff_macros" }
|
||||
ruff_macros = { version = "0.0.241", path = "ruff_macros" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" }
|
||||
schemars = { version = "0.8.11" }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
@@ -77,7 +77,7 @@ wasm-bindgen = { version = "0.2.83" }
|
||||
is_executable = "1.0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { version = "1.19.1", features = ["yaml", "redactions"] }
|
||||
insta = { version = "1.19.0", features = ["yaml", "redactions"] }
|
||||
test-case = { version = "2.2.2" }
|
||||
wasm-bindgen-test = { version = "0.3.33" }
|
||||
|
||||
|
||||
30
LICENSE
30
LICENSE
@@ -1005,3 +1005,33 @@ are:
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
- flake8-raise, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Jon Dufresne
|
||||
|
||||
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-self, licensed as follows:
|
||||
"""
|
||||
Freely Distributable
|
||||
"""
|
||||
|
||||
2964
flake8_to_ruff/Cargo.lock
generated
2964
flake8_to_ruff/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.238"
|
||||
version = "0.0.241"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
//! Utility to generate Ruff's `pyproject.toml` section from a Flake8 INI file.
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(
|
||||
clippy::collapsible_else_if,
|
||||
clippy::collapsible_if,
|
||||
clippy::implicit_hasher,
|
||||
clippy::match_same_arms,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::must_use_candidate,
|
||||
clippy::similar_names,
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -61,7 +47,11 @@ fn main() -> Result<()> {
|
||||
|
||||
// Create Ruff's pyproject.toml section.
|
||||
let pyproject = flake8_to_ruff::convert(&config, &external_config, args.plugin)?;
|
||||
println!("{}", toml::to_string_pretty(&pyproject)?);
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
{
|
||||
println!("{}", toml::to_string_pretty(&pyproject)?);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ theme:
|
||||
toggle:
|
||||
icon: material/weather-night
|
||||
name: Switch to light mode
|
||||
custom_dir: .overrides
|
||||
repo_url: https://github.com/charliermarsh/ruff
|
||||
repo_name: ruff
|
||||
site_author: charliermarsh
|
||||
|
||||
@@ -4,10 +4,10 @@ In-browser playground for Ruff. Available [https://ruff.pages.dev/](https://ruff
|
||||
|
||||
## Getting started
|
||||
|
||||
- To build the WASM module, run `wasm-pack build --target web --out-dir playground/src/pkg` from the
|
||||
* To build the WASM module, run `wasm-pack build --target web --out-dir playground/src/pkg` from the
|
||||
root directory.
|
||||
- Install TypeScript dependencies with: `npm install`.
|
||||
- Start the development server with: `npm run dev`.
|
||||
* Install TypeScript dependencies with: `npm install`.
|
||||
* Start the development server with: `npm run dev`.
|
||||
|
||||
## Implementation
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
@@ -14,6 +14,7 @@
|
||||
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚡</text></svg>"
|
||||
/>
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
||||
<script src="https://cdn.usefathom.com/script.js" data-site="XWUDIXNB" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -7,33 +7,36 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "ruff"
|
||||
version = "0.0.238"
|
||||
version = "0.0.241"
|
||||
description = "An extremely fast Python linter, written in Rust."
|
||||
authors = [
|
||||
{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" },
|
||||
]
|
||||
maintainers = [
|
||||
{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" },
|
||||
]
|
||||
authors = [{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" }]
|
||||
maintainers = [{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" }]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.7"
|
||||
license = { file = "LICENSE" }
|
||||
keywords = ["automation", "flake8", "pycodestyle", "pyflakes", "pylint", "clippy"]
|
||||
keywords = [
|
||||
"automation",
|
||||
"flake8",
|
||||
"pycodestyle",
|
||||
"pyflakes",
|
||||
"pylint",
|
||||
"clippy",
|
||||
]
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Environment :: Console",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Topic :: Software Development :: Quality Assurance",
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Environment :: Console",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Topic :: Software Development :: Quality Assurance",
|
||||
]
|
||||
urls = { repository = "https://github.com/charliermarsh/ruff" }
|
||||
|
||||
@@ -42,3 +45,6 @@ bindings = "bin"
|
||||
manifest-path = "ruff_cli/Cargo.toml"
|
||||
python-source = "python"
|
||||
strip = true
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
"setup.py" = ["INP001"]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import os
|
||||
import sys
|
||||
import sysconfig
|
||||
from pathlib import Path
|
||||
|
||||
if __name__ == "__main__":
|
||||
ruff = os.path.join(sysconfig.get_path("scripts"), "ruff")
|
||||
ruff = Path(sysconfig.get_path("scripts")) / "ruff"
|
||||
sys.exit(os.spawnv(os.P_WAIT, ruff, [ruff, *sys.argv[1:]]))
|
||||
|
||||
@@ -4,6 +4,7 @@ d = {}
|
||||
safe = "s3cr3t"
|
||||
password = True
|
||||
password = safe
|
||||
password = ""
|
||||
password is True
|
||||
password == 1
|
||||
d["safe"] = "s3cr3t"
|
||||
|
||||
@@ -7,6 +7,7 @@ string = "Hello World"
|
||||
# OK
|
||||
func("s3cr3t")
|
||||
func(1, password=string)
|
||||
func(1, password="")
|
||||
func(pos="s3cr3t", password=string)
|
||||
|
||||
# Error
|
||||
|
||||
@@ -28,3 +28,7 @@ def ok_all(first, /, pos, default="posonly", *, kwonly="kwonly"):
|
||||
|
||||
def default_all(first, /, pos, secret="posonly", *, password="kwonly"):
|
||||
pass
|
||||
|
||||
|
||||
def ok_empty(first, password=""):
|
||||
pass
|
||||
|
||||
37
resources/test/fixtures/flake8_bugbear/B007.py
vendored
37
resources/test/fixtures/flake8_bugbear/B007.py
vendored
@@ -44,4 +44,39 @@ for foo, bar in [(1, 2)]:
|
||||
print(FMT.format(**vars()))
|
||||
|
||||
for foo, bar in [(1, 2)]:
|
||||
print(FMT.format(foo=foo, bar=eval('bar')))
|
||||
print(FMT.format(foo=foo, bar=eval("bar")))
|
||||
|
||||
|
||||
def f():
|
||||
# Fixable.
|
||||
for foo, bar, baz in (["1", "2", "3"],):
|
||||
if foo or baz:
|
||||
break
|
||||
|
||||
|
||||
def f():
|
||||
# Unfixable due to usage of `bar` outside of loop.
|
||||
for foo, bar, baz in (["1", "2", "3"],):
|
||||
if foo or baz:
|
||||
break
|
||||
|
||||
print(bar)
|
||||
|
||||
|
||||
def f():
|
||||
# Fixable.
|
||||
for foo, bar, baz in (["1", "2", "3"],):
|
||||
if foo or baz:
|
||||
break
|
||||
|
||||
bar = 1
|
||||
|
||||
|
||||
def f():
|
||||
# Fixable.
|
||||
for foo, bar, baz in (["1", "2", "3"],):
|
||||
if foo or baz:
|
||||
break
|
||||
|
||||
bar = 1
|
||||
print(bar)
|
||||
|
||||
0
resources/test/fixtures/flake8_no_pep420/test_pass_pyi/example.pyi
vendored
Normal file
0
resources/test/fixtures/flake8_no_pep420/test_pass_pyi/example.pyi
vendored
Normal file
@@ -1,2 +1,4 @@
|
||||
this_should_be_linted = "double quote string"
|
||||
this_should_be_linted = u"double quote string"
|
||||
this_should_be_linted = f"double quote string"
|
||||
this_should_be_linted = f"double {'quote'} string"
|
||||
|
||||
@@ -4,3 +4,8 @@ this_is_fine = '"This" is a \'string\''
|
||||
this_is_fine = "This is a 'string'"
|
||||
this_is_fine = "\"This\" is a 'string'"
|
||||
this_is_fine = r'This is a \'string\''
|
||||
this_is_fine = R'This is a \'string\''
|
||||
this_should_raise = (
|
||||
'This is a'
|
||||
'\'string\''
|
||||
)
|
||||
|
||||
27
resources/test/fixtures/flake8_quotes/doubles_implicit.py
vendored
Normal file
27
resources/test/fixtures/flake8_quotes/doubles_implicit.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
x = (
|
||||
"This"
|
||||
"is"
|
||||
"not"
|
||||
)
|
||||
|
||||
x = (
|
||||
"This" \
|
||||
"is" \
|
||||
"not"
|
||||
)
|
||||
|
||||
x = (
|
||||
"This"
|
||||
"is 'actually'"
|
||||
"fine"
|
||||
)
|
||||
|
||||
x = (
|
||||
"This" \
|
||||
"is 'actually'" \
|
||||
"fine"
|
||||
)
|
||||
|
||||
if True:
|
||||
"This can use 'double' quotes"
|
||||
"But this needs to be changed"
|
||||
@@ -1,2 +1,4 @@
|
||||
this_should_be_linted = 'single quote string'
|
||||
this_should_be_linted = u'double quote string'
|
||||
this_should_be_linted = f'double quote string'
|
||||
this_should_be_linted = f'double {"quote"} string'
|
||||
|
||||
@@ -3,3 +3,8 @@ this_is_fine = "'This' is a \"string\""
|
||||
this_is_fine = 'This is a "string"'
|
||||
this_is_fine = '\'This\' is a "string"'
|
||||
this_is_fine = r"This is a \"string\""
|
||||
this_is_fine = R"This is a \"string\""
|
||||
this_should_raise = (
|
||||
"This is a"
|
||||
"\"string\""
|
||||
)
|
||||
|
||||
27
resources/test/fixtures/flake8_quotes/singles_implicit.py
vendored
Normal file
27
resources/test/fixtures/flake8_quotes/singles_implicit.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
x = (
|
||||
'This'
|
||||
'is'
|
||||
'not'
|
||||
)
|
||||
|
||||
x = (
|
||||
'This' \
|
||||
'is' \
|
||||
'not'
|
||||
)
|
||||
|
||||
x = (
|
||||
'This'
|
||||
'is "actually"'
|
||||
'fine'
|
||||
)
|
||||
|
||||
x = (
|
||||
'This' \
|
||||
'is "actually"' \
|
||||
'fine'
|
||||
)
|
||||
|
||||
if True:
|
||||
'This can use "single" quotes'
|
||||
'But this needs to be changed'
|
||||
15
resources/test/fixtures/flake8_raise/RSE102.py
vendored
Normal file
15
resources/test/fixtures/flake8_raise/RSE102.py
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
try:
|
||||
y = 6 + "7"
|
||||
except TypeError:
|
||||
raise ValueError() # RSE102
|
||||
|
||||
try:
|
||||
x = 1 / 0
|
||||
except ZeroDivisionError:
|
||||
raise
|
||||
|
||||
raise TypeError() # RSE102
|
||||
|
||||
raise AssertionError
|
||||
|
||||
raise AttributeError("test message")
|
||||
59
resources/test/fixtures/flake8_self/SLF001.py
vendored
Normal file
59
resources/test/fixtures/flake8_self/SLF001.py
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
class BazMeta(type):
|
||||
_private_count = 1
|
||||
|
||||
def __new__(mcs, name, bases, attrs):
|
||||
if mcs._private_count <= 5:
|
||||
mcs.some_method()
|
||||
|
||||
return super().__new__(mcs, name, bases, attrs)
|
||||
|
||||
def some_method():
|
||||
pass
|
||||
|
||||
|
||||
class Bar:
|
||||
_private = True
|
||||
|
||||
@classmethod
|
||||
def is_private(cls):
|
||||
return cls._private
|
||||
|
||||
|
||||
class Foo(metaclass=BazMeta):
|
||||
|
||||
def __init__(self):
|
||||
self.public_thing = "foo"
|
||||
self._private_thing = "bar"
|
||||
self.__really_private_thing = "baz"
|
||||
self.bar = Bar()
|
||||
|
||||
def __str__(self):
|
||||
return "foo"
|
||||
|
||||
def get_bar():
|
||||
if self.bar._private: # SLF001
|
||||
return None
|
||||
return self.bar
|
||||
|
||||
def public_func(self):
|
||||
pass
|
||||
|
||||
def _private_func(self):
|
||||
pass
|
||||
|
||||
def __really_private_func(self, arg):
|
||||
pass
|
||||
|
||||
|
||||
foo = Foo()
|
||||
|
||||
print(foo.public_thing)
|
||||
print(foo.public_func())
|
||||
print(foo.__dict__)
|
||||
print(foo.__str__())
|
||||
|
||||
print(foo._private_thing) # SLF001
|
||||
print(foo.__really_private_thing) # SLF001
|
||||
print(foo._private_func()) # SLF001
|
||||
print(foo.__really_private_func(1)) # SLF001
|
||||
print(foo.bar._private) # SLF001
|
||||
@@ -21,3 +21,10 @@ if isinstance(a, int) and isinstance(b, bool) or isinstance(a, float):
|
||||
|
||||
if isinstance(a, bool) or isinstance(b, str):
|
||||
pass
|
||||
|
||||
def f():
|
||||
# OK
|
||||
def isinstance(a, b):
|
||||
return False
|
||||
if isinstance(a, int) or isinstance(a, float):
|
||||
pass
|
||||
|
||||
@@ -6,6 +6,14 @@ def f():
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# SIM103
|
||||
if a == b:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# SIM103
|
||||
if a:
|
||||
@@ -50,3 +58,29 @@ def f():
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
if a:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
if a:
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
def bool():
|
||||
return False
|
||||
if a:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -54,7 +54,7 @@ else:
|
||||
randbytes = _get_random_bytes
|
||||
|
||||
|
||||
# OK (includes comments)
|
||||
# SIM108 (without fix due to comments)
|
||||
if x > 0:
|
||||
# test test
|
||||
abc = x
|
||||
@@ -93,8 +93,20 @@ if True:
|
||||
b = ddddddddddddddddddddddddddddddddddddd
|
||||
|
||||
|
||||
# OK (trailing comments)
|
||||
# SIM108 (without fix due to trailing comment)
|
||||
if True:
|
||||
exitcode = 0
|
||||
else:
|
||||
exitcode = 1 # Trailing comment
|
||||
|
||||
|
||||
# SIM108
|
||||
if True: x = 3 # Foo
|
||||
else: x = 5
|
||||
|
||||
|
||||
# SIM108
|
||||
if True: # Foo
|
||||
x = 3
|
||||
else:
|
||||
x = 5
|
||||
|
||||
@@ -115,3 +115,43 @@ def f():
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
def any(exp):
|
||||
pass
|
||||
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
def all(exp):
|
||||
pass
|
||||
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
# SIM110
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
# SIM111
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -115,3 +115,43 @@ def f():
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
def any(exp):
|
||||
pass
|
||||
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
def all(exp):
|
||||
pass
|
||||
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
# SIM110
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
# SIM111
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -5,3 +5,10 @@ a = True if b != c else False # SIM210
|
||||
a = True if b + c else False # SIM210
|
||||
|
||||
a = False if b else True # OK
|
||||
|
||||
def f():
|
||||
# OK
|
||||
def bool():
|
||||
return False
|
||||
|
||||
a = True if b else False
|
||||
|
||||
@@ -10,6 +10,7 @@ YODA == age # SIM300
|
||||
YODA > age # SIM300
|
||||
YODA >= age # SIM300
|
||||
JediOrder.YODA == age # SIM300
|
||||
0 < (number - 100) # SIM300
|
||||
|
||||
# OK
|
||||
compare == "yoda"
|
||||
@@ -24,3 +25,4 @@ age < YODA
|
||||
age <= YODA
|
||||
YODA == YODA
|
||||
age == JediOrder.YODA
|
||||
(number - 100) > 0
|
||||
|
||||
@@ -4,15 +4,22 @@ if TYPE_CHECKING:
|
||||
pass # TCH005
|
||||
|
||||
|
||||
if False:
|
||||
pass # TCH005
|
||||
|
||||
if 0:
|
||||
pass # TCH005
|
||||
|
||||
|
||||
def example():
|
||||
if TYPE_CHECKING:
|
||||
pass # TYP005
|
||||
pass # TCH005
|
||||
return
|
||||
|
||||
|
||||
class Test:
|
||||
if TYPE_CHECKING:
|
||||
pass # TYP005
|
||||
pass # TCH005
|
||||
x = 2
|
||||
|
||||
|
||||
@@ -23,3 +30,10 @@ if TYPE_CHECKING:
|
||||
|
||||
if TYPE_CHECKING:
|
||||
x: List
|
||||
|
||||
|
||||
if False:
|
||||
x: List
|
||||
|
||||
if 0:
|
||||
x: List
|
||||
|
||||
8
resources/test/fixtures/isort/forced_separate.py
vendored
Normal file
8
resources/test/fixtures/isort/forced_separate.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# office_helper and tests are both first-party,
|
||||
# but we want tests and experiments to be separated, in that order
|
||||
from office_helper.core import CoreState
|
||||
import tests.common.foo as tcf
|
||||
from tests.common import async_mock_service
|
||||
from experiments.starry import *
|
||||
from experiments.weird import varieties
|
||||
from office_helper.assistants import entity_registry as er
|
||||
13
resources/test/fixtures/isort/lines_after_imports_class_after.py
vendored
Normal file
13
resources/test/fixtures/isort/lines_after_imports_class_after.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from requests import Session
|
||||
|
||||
from my_first_party import my_first_party_object
|
||||
|
||||
from . import my_local_folder_object
|
||||
class Thing(object):
|
||||
name: str
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
22
resources/test/fixtures/isort/lines_after_imports_func_after.py
vendored
Normal file
22
resources/test/fixtures/isort/lines_after_imports_func_after.py
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from requests import Session
|
||||
|
||||
from my_first_party import my_first_party_object
|
||||
|
||||
from . import my_local_folder_object
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
my_local_folder_object.get()
|
||||
9
resources/test/fixtures/isort/lines_after_imports_nothing_after.py
vendored
Normal file
9
resources/test/fixtures/isort/lines_after_imports_nothing_after.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from requests import Session
|
||||
|
||||
from my_first_party import my_first_party_object
|
||||
|
||||
from . import my_local_folder_object
|
||||
13
resources/test/fixtures/isort/preserve_tabs_2.py
vendored
Normal file
13
resources/test/fixtures/isort/preserve_tabs_2.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
from numpy import (
|
||||
cos,
|
||||
int8,
|
||||
int16,
|
||||
int32,
|
||||
int64,
|
||||
sin,
|
||||
tan,
|
||||
uint8,
|
||||
uint16,
|
||||
uint32,
|
||||
uint64,
|
||||
)
|
||||
3
resources/test/fixtures/isort/pyproject.toml
vendored
3
resources/test/fixtures/isort/pyproject.toml
vendored
@@ -1,2 +1,5 @@
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
|
||||
[tool.ruff.isort]
|
||||
lines-after-imports = 3
|
||||
|
||||
53
resources/test/fixtures/pyflakes/F811_21.py
vendored
Normal file
53
resources/test/fixtures/pyflakes/F811_21.py
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
"""Test: noqa directives."""
|
||||
|
||||
from typing_extensions import List, Sequence
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa: F811
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import (
|
||||
List, # noqa: F811
|
||||
Sequence, # noqa: F811
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import (
|
||||
List, # noqa
|
||||
Sequence, # noqa
|
||||
)
|
||||
|
||||
# This should ignore the first error.
|
||||
from typing import (
|
||||
List, # noqa: F811
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import List, Sequence # noqa: F811
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import List, Sequence # noqa
|
||||
|
||||
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa: F811
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
3
resources/test/fixtures/pyflakes/F822_1.py
vendored
Normal file
3
resources/test/fixtures/pyflakes/F822_1.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
a = 1
|
||||
|
||||
__all__ = list(["a", "b"])
|
||||
8
resources/test/fixtures/pyflakes/F841_0.py
vendored
8
resources/test/fixtures/pyflakes/F841_0.py
vendored
@@ -86,3 +86,11 @@ def f():
|
||||
open("") as ((this, that)),
|
||||
):
|
||||
print("hello")
|
||||
|
||||
|
||||
def f():
|
||||
exponential, base_multiplier = 1, 2
|
||||
hash_map = {
|
||||
(exponential := (exponential * base_multiplier) % 3): i + 1 for i in range(2)
|
||||
}
|
||||
return hash_map
|
||||
|
||||
@@ -9,6 +9,7 @@ from collections import OrderedDict as o_dict
|
||||
import os.path as path # [consider-using-from-import]
|
||||
import os.path as p
|
||||
import foo.bar.foobar as foobar # [consider-using-from-import]
|
||||
import foo.bar.foobar as foobar, sys # [consider-using-from-import]
|
||||
import os
|
||||
import os as OS
|
||||
from sys import version
|
||||
|
||||
@@ -6,6 +6,10 @@ __all__ += {"world"} # [invalid-all-format]
|
||||
|
||||
__all__ = {"world"} + ["Hello"] # [invalid-all-format]
|
||||
|
||||
__all__ = {"world"} + list(["Hello"]) # [invalid-all-format]
|
||||
|
||||
__all__ = list(["Hello"]) + {"world"} # [invalid-all-format]
|
||||
|
||||
__all__ = (x for x in ["Hello", "world"]) # [invalid-all-format]
|
||||
|
||||
__all__ = {x for x in ["Hello", "world"]} # [invalid-all-format]
|
||||
@@ -17,3 +21,11 @@ __all__ = ("Hello",)
|
||||
__all__ = ["Hello"] + ("world",)
|
||||
|
||||
__all__ = [x for x in ["Hello", "world"]]
|
||||
|
||||
__all__ = list(["Hello", "world"])
|
||||
|
||||
__all__ = list({"Hello", "world"})
|
||||
|
||||
__all__ = list(["Hello"]) + list(["world"])
|
||||
|
||||
__all__ = tuple(["Hello"]) + ("world",)
|
||||
|
||||
@@ -4,8 +4,12 @@ __all__ = (
|
||||
Worm,
|
||||
)
|
||||
|
||||
__all__ = list([None, "Fruit", "Worm"]) # [invalid-all-object]
|
||||
|
||||
|
||||
class Fruit:
|
||||
pass
|
||||
|
||||
|
||||
class Worm:
|
||||
pass
|
||||
|
||||
56
resources/test/fixtures/pylint/too_many_statements.py
vendored
Normal file
56
resources/test/fixtures/pylint/too_many_statements.py
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
def f(): # OK
|
||||
return
|
||||
|
||||
|
||||
async def f(): # Too many statements (52/50)
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
8
resources/test/fixtures/pylint/too_many_statements_params.py
vendored
Normal file
8
resources/test/fixtures/pylint/too_many_statements_params.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Too may statements (2/1) for max_statements=1
|
||||
def f(x):
|
||||
pass
|
||||
|
||||
|
||||
def f(x):
|
||||
def g(x):
|
||||
pass
|
||||
50
resources/test/fixtures/pyupgrade/UP035.py
vendored
Normal file
50
resources/test/fixtures/pyupgrade/UP035.py
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
# UP035
|
||||
from collections import Mapping
|
||||
|
||||
from collections import Mapping as MAP
|
||||
|
||||
from collections import Mapping, Sequence
|
||||
|
||||
from collections import Counter, Mapping
|
||||
|
||||
from collections import (Counter, Mapping)
|
||||
|
||||
from collections import (Counter,
|
||||
Mapping)
|
||||
|
||||
from collections import Counter, \
|
||||
Mapping
|
||||
|
||||
from collections import Counter, Mapping, Sequence
|
||||
|
||||
from collections import Mapping as mapping, Counter
|
||||
|
||||
if True:
|
||||
from collections import Mapping, Counter
|
||||
|
||||
if True:
|
||||
if True:
|
||||
pass
|
||||
from collections import Mapping, Counter
|
||||
|
||||
if True: from collections import Mapping
|
||||
|
||||
import os
|
||||
from collections import Counter, Mapping
|
||||
import sys
|
||||
|
||||
if True:
|
||||
from collections import (
|
||||
Mapping,
|
||||
Callable,
|
||||
Bad,
|
||||
Good,
|
||||
)
|
||||
|
||||
from typing import Callable, Match, Pattern, List
|
||||
|
||||
if True: from collections import (
|
||||
Mapping, Counter)
|
||||
|
||||
# OK
|
||||
from a import b
|
||||
180
resources/test/fixtures/pyupgrade/UP036_0.py
vendored
Normal file
180
resources/test/fixtures/pyupgrade/UP036_0.py
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
print("py2")
|
||||
else:
|
||||
print("py3")
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
if True:
|
||||
print("py2!")
|
||||
else:
|
||||
print("???")
|
||||
else:
|
||||
print("py3")
|
||||
|
||||
if sys.version_info < (3,0): print("PY2!")
|
||||
else: print("PY3!")
|
||||
|
||||
if True:
|
||||
if sys.version_info < (3,0):
|
||||
print("PY2")
|
||||
else:
|
||||
print("PY3")
|
||||
|
||||
if sys.version_info < (3,0): print(1 if True else 3)
|
||||
else:
|
||||
print("py3")
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
def f():
|
||||
print("py2")
|
||||
else:
|
||||
def f():
|
||||
print("py3")
|
||||
print("This the next")
|
||||
|
||||
if sys.version_info > (3,0):
|
||||
print("py3")
|
||||
else:
|
||||
print("py2")
|
||||
|
||||
|
||||
x = 1
|
||||
|
||||
if sys.version_info > (3,0):
|
||||
print("py3")
|
||||
else:
|
||||
print("py2")
|
||||
# ohai
|
||||
|
||||
x = 1
|
||||
|
||||
if sys.version_info > (3,0): print("py3")
|
||||
else: print("py2")
|
||||
|
||||
if sys.version_info > (3,):
|
||||
print("py3")
|
||||
else:
|
||||
print("py2")
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3,):
|
||||
print("py3")
|
||||
else:
|
||||
print("py2")
|
||||
|
||||
if sys.version_info < (3,):
|
||||
print("py2")
|
||||
else:
|
||||
print("py3")
|
||||
|
||||
def f():
|
||||
if sys.version_info < (3,0):
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
pass
|
||||
else:
|
||||
yield
|
||||
|
||||
|
||||
class C:
|
||||
def g():
|
||||
pass
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
def f(py2):
|
||||
pass
|
||||
else:
|
||||
def f(py3):
|
||||
pass
|
||||
|
||||
def h():
|
||||
pass
|
||||
|
||||
if True:
|
||||
if sys.version_info < (3,0):
|
||||
2
|
||||
else:
|
||||
3
|
||||
|
||||
# comment
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
def f():
|
||||
print("py2")
|
||||
def g():
|
||||
print("py2")
|
||||
else:
|
||||
def f():
|
||||
print("py3")
|
||||
def g():
|
||||
print("py3")
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3,):
|
||||
print(3)
|
||||
# comment
|
||||
print(2+3)
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3,): print(3)
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3,):
|
||||
print(3)
|
||||
|
||||
|
||||
if True:
|
||||
if sys.version_info <= (3, 0):
|
||||
expected_error = []
|
||||
else:
|
||||
expected_error = [
|
||||
"<stdin>:1:5: Generator expression must be parenthesized",
|
||||
"max(1 for i in range(10), key=lambda x: x+1)",
|
||||
" ^",
|
||||
]
|
||||
|
||||
|
||||
if sys.version_info <= (3, 0):
|
||||
expected_error = []
|
||||
else:
|
||||
expected_error = [
|
||||
"<stdin>:1:5: Generator expression must be parenthesized",
|
||||
"max(1 for i in range(10), key=lambda x: x+1)",
|
||||
" ^",
|
||||
]
|
||||
|
||||
|
||||
if sys.version_info > (3,0):
|
||||
"""this
|
||||
is valid"""
|
||||
|
||||
"""the indentation on
|
||||
this line is significant"""
|
||||
|
||||
"this is" \
|
||||
"allowed too"
|
||||
|
||||
("so is"
|
||||
"this for some reason")
|
||||
|
||||
if sys.version_info > (3, 0): expected_error = \
|
||||
[]
|
||||
|
||||
if sys.version_info > (3, 0): expected_error = []
|
||||
|
||||
if sys.version_info > (3, 0): \
|
||||
expected_error = []
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3, 0): expected_error = \
|
||||
[]
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3, 0): expected_error = []
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3, 0): \
|
||||
expected_error = []
|
||||
76
resources/test/fixtures/pyupgrade/UP036_1.py
vendored
Normal file
76
resources/test/fixtures/pyupgrade/UP036_1.py
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info == 2:
|
||||
2
|
||||
else:
|
||||
3
|
||||
|
||||
if sys.version_info < (3,):
|
||||
2
|
||||
else:
|
||||
3
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
2
|
||||
else:
|
||||
3
|
||||
|
||||
if sys.version_info == 3:
|
||||
3
|
||||
else:
|
||||
2
|
||||
|
||||
if sys.version_info > (3,):
|
||||
3
|
||||
else:
|
||||
2
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
3
|
||||
else:
|
||||
2
|
||||
|
||||
from sys import version_info
|
||||
|
||||
if version_info > (3,):
|
||||
3
|
||||
else:
|
||||
2
|
||||
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info < (3,0):
|
||||
print(2)
|
||||
else:
|
||||
print(3)
|
||||
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info > (3,):
|
||||
print(3)
|
||||
else:
|
||||
print(2)
|
||||
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info > (3,):
|
||||
print(3)
|
||||
|
||||
def f():
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info > (3,):
|
||||
print(3)
|
||||
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info < (3,0):
|
||||
print(2)
|
||||
else:
|
||||
print(3)
|
||||
|
||||
def f():
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info > (3,):
|
||||
print(3)
|
||||
62
resources/test/fixtures/pyupgrade/UP036_2.py
vendored
Normal file
62
resources/test/fixtures/pyupgrade/UP036_2.py
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
import sys
|
||||
from sys import version_info
|
||||
|
||||
if sys.version_info > (3, 5):
|
||||
3+6
|
||||
else:
|
||||
3-5
|
||||
|
||||
if version_info > (3, 5):
|
||||
3+6
|
||||
else:
|
||||
3-5
|
||||
|
||||
if sys.version_info >= (3,6):
|
||||
3+6
|
||||
else:
|
||||
3-5
|
||||
|
||||
if version_info >= (3,6):
|
||||
3+6
|
||||
else:
|
||||
3-5
|
||||
|
||||
if sys.version_info < (3,6):
|
||||
3-5
|
||||
else:
|
||||
3+6
|
||||
|
||||
if sys.version_info <= (3,5):
|
||||
3-5
|
||||
else:
|
||||
3+6
|
||||
|
||||
if sys.version_info <= (3, 5):
|
||||
3-5
|
||||
else:
|
||||
3+6
|
||||
|
||||
if sys.version_info >= (3, 5):
|
||||
pass
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
pass
|
||||
|
||||
if True:
|
||||
if sys.version_info < (3,0):
|
||||
pass
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
pass
|
||||
elif False:
|
||||
pass
|
||||
|
||||
if sys.version_info > (3,):
|
||||
pass
|
||||
elif False:
|
||||
pass
|
||||
|
||||
if sys.version_info[0] > "2":
|
||||
3
|
||||
else:
|
||||
2
|
||||
24
resources/test/fixtures/pyupgrade/UP036_3.py
vendored
Normal file
24
resources/test/fixtures/pyupgrade/UP036_3.py
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
print("py2")
|
||||
for item in range(10):
|
||||
print(f"PY2-{item}")
|
||||
else :
|
||||
print("py3")
|
||||
for item in range(10):
|
||||
print(f"PY3-{item}")
|
||||
|
||||
if False:
|
||||
if sys.version_info < (3,0):
|
||||
print("py2")
|
||||
for item in range(10):
|
||||
print(f"PY2-{item}")
|
||||
else :
|
||||
print("py3")
|
||||
for item in range(10):
|
||||
print(f"PY3-{item}")
|
||||
|
||||
|
||||
if sys.version_info < (3,0): print("PY2!")
|
||||
else : print("PY3!")
|
||||
45
resources/test/fixtures/pyupgrade/UP036_4.py
vendored
Normal file
45
resources/test/fixtures/pyupgrade/UP036_4.py
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
import sys
|
||||
|
||||
if True:
|
||||
if sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
|
||||
|
||||
if True:
|
||||
if foo:
|
||||
pass
|
||||
elif sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
|
||||
if True:
|
||||
if foo:
|
||||
pass
|
||||
elif sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
elif foo:
|
||||
cmd = [sys.executable, "-m", "test", "-j0"]
|
||||
|
||||
if foo:
|
||||
pass
|
||||
elif sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
|
||||
if sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
|
||||
if foo:
|
||||
pass
|
||||
elif sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
else:
|
||||
cmd = [sys.executable, "-m", "test", "-j0"]
|
||||
|
||||
if sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
else:
|
||||
cmd = [sys.executable, "-m", "test", "-j0"]
|
||||
|
||||
if sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
elif foo:
|
||||
cmd = [sys.executable, "-m", "test", "-j0"]
|
||||
6
resources/test/fixtures/ruff/RUF005.py
vendored
6
resources/test/fixtures/ruff/RUF005.py
vendored
@@ -37,3 +37,9 @@ second = first + [
|
||||
# touch
|
||||
6,
|
||||
]
|
||||
|
||||
[] + foo + [
|
||||
]
|
||||
|
||||
[] + foo + [ # This will be preserved, but doesn't prevent the fix
|
||||
]
|
||||
|
||||
2
resources/test/fixtures/ruff/RUF100_0.py
vendored
2
resources/test/fixtures/ruff/RUF100_0.py
vendored
@@ -86,3 +86,5 @@ import shelve # noqa: RUF100
|
||||
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
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
[tool.ruff]
|
||||
src = ["."]
|
||||
|
||||
# This will make sure that `exclude` paths are rooted
|
||||
# to where the configuration file was found; this file exists
|
||||
# in a `resources/test` hierarchy.
|
||||
exclude = ["resources"]
|
||||
|
||||
4
resources/test/package/resources/ignored.py
Normal file
4
resources/test/package/resources/ignored.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# This file should be ignored, but it would otherwise trigger
|
||||
# an unused import error:
|
||||
|
||||
import math
|
||||
@@ -7,7 +7,7 @@ behaviors.
|
||||
|
||||
Running from the repo root should pick up and enforce the appropriate settings for each package:
|
||||
|
||||
```
|
||||
```console
|
||||
∴ cargo run resources/test/project/
|
||||
resources/test/project/examples/.dotfiles/script.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
resources/test/project/examples/.dotfiles/script.py:1:8: F401 `numpy` imported but unused
|
||||
@@ -22,7 +22,7 @@ Found 7 errors.
|
||||
|
||||
Running from the project directory itself should exhibit the same behavior:
|
||||
|
||||
```
|
||||
```console
|
||||
∴ (cd resources/test/project/ && cargo run .)
|
||||
examples/.dotfiles/script.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
examples/.dotfiles/script.py:1:8: F401 `numpy` imported but unused
|
||||
@@ -38,7 +38,7 @@ Found 7 errors.
|
||||
Running from the sub-package directory should exhibit the same behavior, but omit the top-level
|
||||
files:
|
||||
|
||||
```
|
||||
```console
|
||||
∴ (cd resources/test/project/examples/docs && cargo run .)
|
||||
docs/file.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
docs/file.py:8:5: F841 Local variable `x` is assigned to but never used
|
||||
@@ -49,7 +49,7 @@ Found 2 errors.
|
||||
`--config` should force Ruff to use the specified `pyproject.toml` for all files, and resolve
|
||||
file paths from the current working directory:
|
||||
|
||||
```
|
||||
```console
|
||||
∴ (cargo run -- --config=resources/test/project/pyproject.toml resources/test/project/)
|
||||
resources/test/project/examples/.dotfiles/script.py:1:8: F401 `numpy` imported but unused
|
||||
resources/test/project/examples/.dotfiles/script.py:2:17: F401 `app.app_file` imported but unused
|
||||
@@ -67,7 +67,7 @@ Found 9 errors.
|
||||
Running from a parent directory should "ignore" the `exclude` (hence, `concepts/file.py` gets
|
||||
included in the output):
|
||||
|
||||
```
|
||||
```console
|
||||
∴ (cd resources/test/project/examples && cargo run -- --config=docs/ruff.toml .)
|
||||
docs/docs/concepts/file.py:5:5: F841 Local variable `x` is assigned to but never used
|
||||
docs/docs/file.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
@@ -79,7 +79,7 @@ Found 4 errors.
|
||||
|
||||
Passing an excluded directory directly should report errors in the contained files:
|
||||
|
||||
```
|
||||
```console
|
||||
∴ cargo run resources/test/project/examples/excluded/
|
||||
resources/test/project/examples/excluded/script.py:1:8: F401 `os` imported but unused
|
||||
Found 1 error.
|
||||
@@ -88,7 +88,7 @@ Found 1 error.
|
||||
|
||||
Unless we `--force-exclude`:
|
||||
|
||||
```
|
||||
```console
|
||||
∴ cargo run resources/test/project/examples/excluded/ --force-exclude
|
||||
warning: No Python files found under the given path(s)
|
||||
∴ cargo run resources/test/project/examples/excluded/script.py --force-exclude
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
]
|
||||
},
|
||||
"exclude": {
|
||||
"description": "A list of file patterns to exclude from linting.\n\nExclusions are based on globs, and can be either:\n\n- Single-path patterns, like `.mypy_cache` (to exclude any directory named `.mypy_cache` in the tree), `foo.py` (to exclude any file named `foo.py`), or `foo_*.py` (to exclude any file matching `foo_*.py` ). - Relative patterns, like `directory/foo.py` (to exclude that specific file) or `directory/*.py` (to exclude any Python files in `directory`). Note that these paths are relative to the project root (e.g., the directory containing your `pyproject.toml`).\n\nFor more information on the glob syntax, refer to the [`globset` documentation](https://docs.rs/globset/latest/globset/#syntax).\n\nNote that you'll typically want to use [`extend-exclude`](#extend-exclude) to modify the excluded paths.",
|
||||
"description": "A list of file patterns to exclude from linting.\n\nExclusions are based on globs, and can be either:\n\n* Single-path patterns, like `.mypy_cache` (to exclude any directory named `.mypy_cache` in the tree), `foo.py` (to exclude any file named `foo.py`), or `foo_*.py` (to exclude any file matching `foo_*.py` ). * Relative patterns, like `directory/foo.py` (to exclude that specific file) or `directory/*.py` (to exclude any Python files in `directory`). Note that these paths are relative to the project root (e.g., the directory containing your `pyproject.toml`).\n\nFor more information on the glob syntax, refer to the [`globset` documentation](https://docs.rs/globset/latest/globset/#syntax).\n\nNote that you'll typically want to use [`extend-exclude`](#extend-exclude) to modify the excluded paths.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
@@ -57,7 +57,7 @@
|
||||
]
|
||||
},
|
||||
"extend-exclude": {
|
||||
"description": "A list of file patterns to omit from linting, in addition to those specified by `exclude`.\n\nExclusions are based on globs, and can be either:\n\n- Single-path patterns, like `.mypy_cache` (to exclude any directory named `.mypy_cache` in the tree), `foo.py` (to exclude any file named `foo.py`), or `foo_*.py` (to exclude any file matching `foo_*.py` ). - Relative patterns, like `directory/foo.py` (to exclude that specific file) or `directory/*.py` (to exclude any Python files in `directory`). Note that these paths are relative to the project root (e.g., the directory containing your `pyproject.toml`).\n\nFor more information on the glob syntax, refer to the [`globset` documentation](https://docs.rs/globset/latest/globset/#syntax).",
|
||||
"description": "A list of file patterns to omit from linting, in addition to those specified by `exclude`.\n\nExclusions are based on globs, and can be either:\n\n* Single-path patterns, like `.mypy_cache` (to exclude any directory named `.mypy_cache` in the tree), `foo.py` (to exclude any file named `foo.py`), or `foo_*.py` (to exclude any file matching `foo_*.py` ). * Relative patterns, like `directory/foo.py` (to exclude that specific file) or `directory/*.py` (to exclude any Python files in `directory`). Note that these paths are relative to the project root (e.g., the directory containing your `pyproject.toml`).\n\nFor more information on the glob syntax, refer to the [`globset` documentation](https://docs.rs/globset/latest/globset/#syntax).",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
@@ -556,7 +556,7 @@
|
||||
]
|
||||
},
|
||||
"suppress-none-returning": {
|
||||
"description": "Whether to suppress `ANN200`-level violations for functions that meet either of the following criteria:\n\n- Contain no `return` statement. - Explicit `return` statement(s) all return `None` (explicitly or implicitly).",
|
||||
"description": "Whether to suppress `ANN200`-level violations for functions that meet either of the following criteria:\n\n* Contain no `return` statement. * Explicit `return` statement(s) all return `None` (explicitly or implicitly).",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
@@ -702,7 +702,7 @@
|
||||
]
|
||||
},
|
||||
"parametrize-names-type": {
|
||||
"description": "Expected type for multiple argument names in `@pytest.mark.parametrize`. The following values are supported: * `csv` — a comma-separated list, e.g. `@pytest.mark.parametrize('name1,name2', ...)` * `tuple` (default) — e.g. `@pytest.mark.parametrize(('name1', 'name2'), ...)` * `list` — e.g. `@pytest.mark.parametrize(['name1', 'name2'], ...)`",
|
||||
"description": "Expected type for multiple argument names in `@pytest.mark.parametrize`. The following values are supported:\n\n* `csv` — a comma-separated list, e.g. `@pytest.mark.parametrize('name1,name2', ...)` * `tuple` (default) — e.g. `@pytest.mark.parametrize(('name1', 'name2'), ...)` * `list` — e.g. `@pytest.mark.parametrize(['name1', 'name2'], ...)`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ParametrizeNameType"
|
||||
@@ -713,7 +713,7 @@
|
||||
]
|
||||
},
|
||||
"parametrize-values-row-type": {
|
||||
"description": "Expected type for each row of values in `@pytest.mark.parametrize` in case of multiple parameters. The following values are supported: * `tuple` (default) — e.g. `@pytest.mark.parametrize(('name1', 'name2'), [(1, 2), (3, 4)])` * `list` — e.g. `@pytest.mark.parametrize(('name1', 'name2'), [[1, 2], [3, 4]])`",
|
||||
"description": "Expected type for each row of values in `@pytest.mark.parametrize` in case of multiple parameters. The following values are supported:\n\n* `tuple` (default) — e.g. `@pytest.mark.parametrize(('name1', 'name2'), [(1, 2), (3, 4)])` * `list` — e.g. `@pytest.mark.parametrize(('name1', 'name2'), [[1, 2], [3, 4]])`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ParametrizeValuesRowType"
|
||||
@@ -724,7 +724,7 @@
|
||||
]
|
||||
},
|
||||
"parametrize-values-type": {
|
||||
"description": "Expected type for the list of values rows in `@pytest.mark.parametrize`. The following values are supported: * `tuple` — e.g. `@pytest.mark.parametrize('name', (1, 2, 3))` * `list` (default) — e.g. `@pytest.mark.parametrize('name', [1, 2, 3])`",
|
||||
"description": "Expected type for the list of values rows in `@pytest.mark.parametrize`. The following values are supported:\n\n* `tuple` — e.g. `@pytest.mark.parametrize('name', (1, 2, 3))` * `list` (default) — e.g. `@pytest.mark.parametrize('name', [1, 2, 3])`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ParametrizeValuesType"
|
||||
@@ -768,7 +768,7 @@
|
||||
]
|
||||
},
|
||||
"docstring-quotes": {
|
||||
"description": "Quote style to prefer for docstrings (either \"single\" (`'`) or \"double\" (`\"`)).",
|
||||
"description": "Quote style to prefer for docstrings (either \"single\" or \"double\").",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Quote"
|
||||
@@ -779,7 +779,7 @@
|
||||
]
|
||||
},
|
||||
"inline-quotes": {
|
||||
"description": "Quote style to prefer for inline strings (either \"single\" (`'`) or \"double\" (`\"`)).",
|
||||
"description": "Quote style to prefer for inline strings (either \"single\" or \"double\").",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Quote"
|
||||
@@ -790,7 +790,7 @@
|
||||
]
|
||||
},
|
||||
"multiline-quotes": {
|
||||
"description": "Quote style to prefer for multiline strings (either \"single\" (`'`) or \"double\" (`\"`)).",
|
||||
"description": "Quote style to prefer for multiline strings (either \"single\" or \"double\").",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Quote"
|
||||
@@ -844,7 +844,7 @@
|
||||
}
|
||||
},
|
||||
"strict": {
|
||||
"description": "Enforce TC001, TC002, and TC003 rules even when valid runtime imports are present for the same module. See: https://github.com/snok/flake8-type-checking#strict.",
|
||||
"description": "Enforce TC001, TC002, and TC003 rules even when valid runtime imports are present for the same module. See flake8-type-checking's [strict](https://github.com/snok/flake8-type-checking#strict) option.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
@@ -937,6 +937,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"forced-separate": {
|
||||
"description": "A list of modules to separate into auxiliary block(s) of imports, in the order specified.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"known-first-party": {
|
||||
"description": "A list of modules to consider first-party, regardless of whether they can be identified as such via introspection of the local filesystem.",
|
||||
"type": [
|
||||
@@ -957,6 +967,14 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"lines-after-imports": {
|
||||
"description": "The number of blank lines to place after imports. -1 for automatic determination.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int"
|
||||
},
|
||||
"no-lines-before": {
|
||||
"description": "A list of sections that should _not_ be delineated from the previous section via empty lines.",
|
||||
"type": [
|
||||
@@ -1102,7 +1120,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"keep-runtime-typing": {
|
||||
"description": "Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604 (`Optional[str]` -> `str | None`) rewrites even if a file imports `from __future__ import annotations`. Note that this setting is only applicable when the target Python version is below 3.9 and 3.10 respectively.",
|
||||
"description": "Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604 (`Optional[str]` -> `str | None`) rewrites even if a file imports `from __future__ import annotations`. Note that this setting is only applicable when the target Python version is below 3.9 and 3.10 respectively, and enabling it is equivalent to disabling `use-pep585-annotation` (`UP006`) and `use-pep604-annotation` (`UP007`) entirely.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
@@ -1115,7 +1133,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ignore-overlong-task-comments": {
|
||||
"description": "Whether or not line-length violations (`E501`) should be triggered for comments starting with `task-tags` (by default: [\"TODO\", \"FIXME\", and \"XXX\"]).",
|
||||
"description": "Whether line-length violations (`E501`) should be triggered for comments starting with `task-tags` (by default: [\"TODO\", \"FIXME\", and \"XXX\"]).",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
@@ -1171,6 +1189,15 @@
|
||||
],
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"max-statements": {
|
||||
"description": "Maximum number of statements allowed for a method or a statement (see: `PLR0915`).",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -1188,14 +1215,14 @@
|
||||
"Quote": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Use single quotes (`'`).",
|
||||
"description": "Use single quotes.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"single"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Use double quotes (`\"`).",
|
||||
"description": "Use double quotes.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"double"
|
||||
@@ -1643,6 +1670,7 @@
|
||||
"PLR09",
|
||||
"PLR091",
|
||||
"PLR0913",
|
||||
"PLR0915",
|
||||
"PLR1",
|
||||
"PLR17",
|
||||
"PLR170",
|
||||
@@ -1739,6 +1767,10 @@
|
||||
"RET506",
|
||||
"RET507",
|
||||
"RET508",
|
||||
"RSE",
|
||||
"RSE1",
|
||||
"RSE10",
|
||||
"RSE102",
|
||||
"RUF",
|
||||
"RUF0",
|
||||
"RUF00",
|
||||
@@ -1816,6 +1848,10 @@
|
||||
"SIM4",
|
||||
"SIM40",
|
||||
"SIM401",
|
||||
"SLF",
|
||||
"SLF0",
|
||||
"SLF00",
|
||||
"SLF001",
|
||||
"T",
|
||||
"T1",
|
||||
"T10",
|
||||
@@ -1892,6 +1928,8 @@
|
||||
"UP032",
|
||||
"UP033",
|
||||
"UP034",
|
||||
"UP035",
|
||||
"UP036",
|
||||
"W",
|
||||
"W2",
|
||||
"W29",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.238"
|
||||
version = "0.0.241"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
@@ -51,7 +51,6 @@ serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = { version = "1.0.87" }
|
||||
similar = { version = "2.2.1" }
|
||||
textwrap = { version = "0.16.0" }
|
||||
update-informer = { version = "0.6.0", default-features = false, features = ["pypi"], optional = true }
|
||||
walkdir = { version = "2.3.2" }
|
||||
strum = "0.24.1"
|
||||
|
||||
@@ -60,10 +59,6 @@ assert_cmd = { version = "2.0.4" }
|
||||
strum = { version = "0.24.1" }
|
||||
ureq = { version = "2.5.0", features = [] }
|
||||
|
||||
[features]
|
||||
default = ["update-informer"]
|
||||
update-informer = ["dep:update-informer"]
|
||||
|
||||
[package.metadata.maturin]
|
||||
name = "ruff"
|
||||
# Setting the name here is necessary for maturin to include the package in its builds.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{command, Parser};
|
||||
@@ -36,7 +38,7 @@ pub enum Command {
|
||||
#[clap(alias = "--explain")]
|
||||
Rule {
|
||||
#[arg(value_parser=Rule::from_code)]
|
||||
rule: &'static Rule,
|
||||
rule: Rule,
|
||||
|
||||
/// Output format
|
||||
#[arg(long, value_enum, default_value = "text")]
|
||||
@@ -209,11 +211,12 @@ pub struct CheckArgs {
|
||||
/// Exit with status code "0", even upon detecting lint violations.
|
||||
#[arg(short, long, help_heading = "Miscellaneous")]
|
||||
pub exit_zero: bool,
|
||||
/// Enable or disable automatic update checks.
|
||||
/// Does nothing and will be removed in the future.
|
||||
#[arg(
|
||||
long,
|
||||
overrides_with("no_update_check"),
|
||||
help_heading = "Miscellaneous"
|
||||
help_heading = "Miscellaneous",
|
||||
hide = true
|
||||
)]
|
||||
update_check: bool,
|
||||
#[clap(long, overrides_with("update_check"), hide = true)]
|
||||
@@ -237,6 +240,7 @@ pub struct CheckArgs {
|
||||
conflicts_with = "statistics",
|
||||
conflicts_with = "stdin_filename",
|
||||
conflicts_with = "watch",
|
||||
conflicts_with = "fix",
|
||||
)]
|
||||
pub add_noqa: bool,
|
||||
/// See the files Ruff will be run against with the current settings.
|
||||
@@ -309,13 +313,13 @@ pub struct LogLevelArgs {
|
||||
impl From<&LogLevelArgs> for LogLevel {
|
||||
fn from(args: &LogLevelArgs) -> Self {
|
||||
if args.silent {
|
||||
LogLevel::Silent
|
||||
Self::Silent
|
||||
} else if args.quiet {
|
||||
LogLevel::Quiet
|
||||
Self::Quiet
|
||||
} else if args.verbose {
|
||||
LogLevel::Verbose
|
||||
Self::Verbose
|
||||
} else {
|
||||
LogLevel::Default
|
||||
Self::Default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,10 @@ fn read_sync(cache_dir: &Path, key: u64) -> Result<Vec<u8>, std::io::Error> {
|
||||
fs::read(cache_dir.join(content_dir()).join(format!("{key:x}")))
|
||||
}
|
||||
|
||||
fn del_sync(cache_dir: &Path, key: u64) -> Result<(), std::io::Error> {
|
||||
fs::remove_file(cache_dir.join(content_dir()).join(format!("{key:x}")))
|
||||
}
|
||||
|
||||
/// Get a value from the cache.
|
||||
pub fn get<P: AsRef<Path>>(
|
||||
path: P,
|
||||
@@ -137,3 +141,16 @@ pub fn set<P: AsRef<Path>>(
|
||||
error!("Failed to write to cache: {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a value from the cache.
|
||||
pub fn del<P: AsRef<Path>>(
|
||||
path: P,
|
||||
package: Option<&P>,
|
||||
settings: &AllSettings,
|
||||
autofix: flags::Autofix,
|
||||
) {
|
||||
drop(del_sync(
|
||||
&settings.cli.cache_dir,
|
||||
cache_key(path, package, &settings.lib, autofix),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::fs::remove_dir_all;
|
||||
use std::io::{self, Read};
|
||||
use std::io::Write;
|
||||
use std::io::{self, BufWriter, Read};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -11,6 +12,9 @@ use log::{debug, error};
|
||||
use path_absolutize::path_dedot;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use rayon::prelude::*;
|
||||
use serde::Serialize;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use ruff::cache::CACHE_DIR_NAME;
|
||||
use ruff::linter::add_noqa_to_path;
|
||||
use ruff::logging::LogLevel;
|
||||
@@ -19,8 +23,6 @@ use ruff::registry::{Linter, Rule, RuleNamespace};
|
||||
use ruff::resolver::PyprojectDiscovery;
|
||||
use ruff::settings::flags;
|
||||
use ruff::{fix, fs, packaging, resolver, warn_user_once, AutofixAvailability, IOError};
|
||||
use serde::Serialize;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::args::{HelpFormat, Overrides};
|
||||
use crate::cache;
|
||||
@@ -136,7 +138,7 @@ pub fn run(
|
||||
|
||||
diagnostics.messages.sort_unstable();
|
||||
let duration = start.elapsed();
|
||||
debug!("Checked files in: {:?}", duration);
|
||||
debug!("Checked {:?} files in: {:?}", paths.len(), duration);
|
||||
|
||||
Ok(diagnostics)
|
||||
}
|
||||
@@ -230,8 +232,10 @@ pub fn show_settings(
|
||||
};
|
||||
let path = entry.path();
|
||||
let settings = resolver.resolve(path, pyproject_strategy);
|
||||
println!("Resolved settings for: {path:?}");
|
||||
println!("{settings:#?}");
|
||||
|
||||
let mut stdout = BufWriter::new(io::stdout().lock());
|
||||
write!(stdout, "Resolved settings for: {path:?}")?;
|
||||
write!(stdout, "{settings:#?}")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -251,12 +255,13 @@ pub fn show_files(
|
||||
}
|
||||
|
||||
// Print the list of files.
|
||||
let mut stdout = BufWriter::new(io::stdout().lock());
|
||||
for entry in paths
|
||||
.iter()
|
||||
.flatten()
|
||||
.sorted_by(|a, b| a.path().cmp(b.path()))
|
||||
{
|
||||
println!("{}", entry.path().to_string_lossy());
|
||||
writeln!(stdout, "{}", entry.path().to_string_lossy())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -272,34 +277,39 @@ struct Explanation<'a> {
|
||||
/// Explain a `Rule` to the user.
|
||||
pub fn rule(rule: &Rule, format: HelpFormat) -> Result<()> {
|
||||
let (linter, _) = Linter::parse_code(rule.code()).unwrap();
|
||||
let mut stdout = BufWriter::new(io::stdout().lock());
|
||||
match format {
|
||||
HelpFormat::Text => {
|
||||
println!("{}\n", rule.as_ref());
|
||||
println!("Code: {} ({})\n", rule.code(), linter.name());
|
||||
writeln!(stdout, "{}\n", rule.as_ref())?;
|
||||
writeln!(stdout, "Code: {} ({})\n", rule.code(), linter.name())?;
|
||||
|
||||
if let Some(autofix) = rule.autofixable() {
|
||||
println!(
|
||||
writeln!(
|
||||
stdout,
|
||||
"{}",
|
||||
match autofix.available {
|
||||
AutofixAvailability::Sometimes => "Autofix is sometimes available.\n",
|
||||
AutofixAvailability::Always => "Autofix is always available.\n",
|
||||
}
|
||||
);
|
||||
)?;
|
||||
}
|
||||
println!("Message formats:\n");
|
||||
|
||||
writeln!(stdout, "Message formats:\n")?;
|
||||
|
||||
for format in rule.message_formats() {
|
||||
println!("* {format}");
|
||||
writeln!(stdout, "* {format}")?;
|
||||
}
|
||||
}
|
||||
HelpFormat::Json => {
|
||||
println!(
|
||||
writeln!(
|
||||
stdout,
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&Explanation {
|
||||
code: rule.code(),
|
||||
linter: linter.name(),
|
||||
summary: rule.message_formats()[0],
|
||||
})?
|
||||
);
|
||||
)?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
@@ -307,6 +317,7 @@ pub fn rule(rule: &Rule, format: HelpFormat) -> Result<()> {
|
||||
|
||||
/// Clear any caches in the current directory or any subdirectories.
|
||||
pub fn clean(level: LogLevel) -> Result<()> {
|
||||
let mut stderr = BufWriter::new(io::stderr().lock());
|
||||
for entry in WalkDir::new(&*path_dedot::CWD)
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
@@ -315,7 +326,11 @@ pub fn clean(level: LogLevel) -> Result<()> {
|
||||
let cache = entry.path().join(CACHE_DIR_NAME);
|
||||
if cache.is_dir() {
|
||||
if level >= LogLevel::Default {
|
||||
eprintln!("Removing cache at: {}", fs::relativize_path(&cache).bold());
|
||||
writeln!(
|
||||
stderr,
|
||||
"Removing cache at: {}",
|
||||
fs::relativize_path(&cache).bold()
|
||||
)?;
|
||||
}
|
||||
remove_dir_all(&cache)?;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use itertools::Itertools;
|
||||
use serde::Serialize;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use ruff::registry::{Linter, LinterCategory, RuleNamespace};
|
||||
use ruff::registry::{Linter, RuleNamespace, UpstreamCategory};
|
||||
|
||||
use crate::args::HelpFormat;
|
||||
|
||||
@@ -12,14 +12,18 @@ pub fn linter(format: HelpFormat) {
|
||||
for linter in Linter::iter() {
|
||||
let prefix = match linter.common_prefix() {
|
||||
"" => linter
|
||||
.categories()
|
||||
.upstream_categories()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|LinterCategory(prefix, ..)| prefix)
|
||||
.map(|UpstreamCategory(prefix, ..)| prefix.as_ref())
|
||||
.join("/"),
|
||||
prefix => prefix.to_string(),
|
||||
};
|
||||
println!("{:>4} {}", prefix, linter.name());
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
{
|
||||
println!("{:>4} {}", prefix, linter.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +32,10 @@ pub fn linter(format: HelpFormat) {
|
||||
.map(|linter_info| LinterInfo {
|
||||
prefix: linter_info.common_prefix(),
|
||||
name: linter_info.name(),
|
||||
categories: linter_info.categories().map(|cats| {
|
||||
categories: linter_info.upstream_categories().map(|cats| {
|
||||
cats.iter()
|
||||
.map(|LinterCategory(prefix, name, ..)| LinterCategoryInfo {
|
||||
prefix,
|
||||
.map(|UpstreamCategory(prefix, name, ..)| LinterCategoryInfo {
|
||||
prefix: prefix.as_ref(),
|
||||
name,
|
||||
})
|
||||
.collect()
|
||||
@@ -39,7 +43,10 @@ pub fn linter(format: HelpFormat) {
|
||||
})
|
||||
.collect();
|
||||
|
||||
println!("{}", serde_json::to_string_pretty(&linters).unwrap());
|
||||
#[allow(clippy::print_stdout)]
|
||||
{
|
||||
println!("{}", serde_json::to_string_pretty(&linters).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@ use std::ops::AddAssign;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use colored::Colorize;
|
||||
use log::debug;
|
||||
use ruff::linter::{lint_fix, lint_only};
|
||||
use ruff::linter::{lint_fix, lint_only, LinterResult};
|
||||
use ruff::message::Message;
|
||||
use ruff::settings::{flags, AllSettings, Settings};
|
||||
use ruff::{fix, fs};
|
||||
@@ -67,38 +68,69 @@ pub fn lint_path(
|
||||
let contents = fs::read_file(path)?;
|
||||
|
||||
// Lint the file.
|
||||
let (messages, fixed) = if matches!(autofix, fix::FixMode::Apply | fix::FixMode::Diff) {
|
||||
let (transformed, fixed, messages) = lint_fix(&contents, path, package, &settings.lib)?;
|
||||
if fixed > 0 {
|
||||
if matches!(autofix, fix::FixMode::Apply) {
|
||||
write(path, transformed)?;
|
||||
} else if matches!(autofix, fix::FixMode::Diff) {
|
||||
let mut stdout = io::stdout().lock();
|
||||
TextDiff::from_lines(&contents, &transformed)
|
||||
.unified_diff()
|
||||
.header(&fs::relativize_path(path), &fs::relativize_path(path))
|
||||
.to_writer(&mut stdout)?;
|
||||
stdout.write_all(b"\n")?;
|
||||
stdout.flush()?;
|
||||
let (
|
||||
LinterResult {
|
||||
data: messages,
|
||||
error: parse_error,
|
||||
},
|
||||
fixed,
|
||||
) = if matches!(autofix, fix::FixMode::Apply | fix::FixMode::Diff) {
|
||||
if let Ok((result, transformed, fixed)) = lint_fix(&contents, path, package, &settings.lib)
|
||||
{
|
||||
if fixed > 0 {
|
||||
if matches!(autofix, fix::FixMode::Apply) {
|
||||
write(path, transformed.as_bytes())?;
|
||||
} else if matches!(autofix, fix::FixMode::Diff) {
|
||||
let mut stdout = io::stdout().lock();
|
||||
TextDiff::from_lines(contents.as_str(), &transformed)
|
||||
.unified_diff()
|
||||
.header(&fs::relativize_path(path), &fs::relativize_path(path))
|
||||
.to_writer(&mut stdout)?;
|
||||
stdout.write_all(b"\n")?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
}
|
||||
(result, fixed)
|
||||
} else {
|
||||
// If we fail to autofix, lint the original source code.
|
||||
let result = lint_only(&contents, path, package, &settings.lib, autofix.into());
|
||||
let fixed = 0;
|
||||
(result, fixed)
|
||||
}
|
||||
(messages, fixed)
|
||||
} else {
|
||||
let messages = lint_only(&contents, path, package, &settings.lib, autofix.into())?;
|
||||
let result = lint_only(&contents, path, package, &settings.lib, autofix.into());
|
||||
let fixed = 0;
|
||||
(messages, fixed)
|
||||
(result, fixed)
|
||||
};
|
||||
|
||||
// Re-populate the cache.
|
||||
if let Some(metadata) = metadata {
|
||||
cache::set(
|
||||
path,
|
||||
package.as_ref(),
|
||||
&metadata,
|
||||
settings,
|
||||
autofix.into(),
|
||||
&messages,
|
||||
);
|
||||
if let Some(err) = parse_error {
|
||||
// Notify the user of any parse errors.
|
||||
#[allow(clippy::print_stderr)]
|
||||
{
|
||||
eprintln!(
|
||||
"{}{} {}{}{} {err}",
|
||||
"error".red().bold(),
|
||||
":".bold(),
|
||||
"Failed to parse ".bold(),
|
||||
fs::relativize_path(path).bold(),
|
||||
":".bold()
|
||||
);
|
||||
}
|
||||
|
||||
// Purge the cache.
|
||||
cache::del(path, package.as_ref(), settings, autofix.into());
|
||||
} else {
|
||||
// Re-populate the cache.
|
||||
if let Some(metadata) = metadata {
|
||||
cache::set(
|
||||
path,
|
||||
package.as_ref(),
|
||||
&metadata,
|
||||
settings,
|
||||
autofix.into(),
|
||||
&messages,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Diagnostics { messages, fixed })
|
||||
@@ -114,45 +146,80 @@ pub fn lint_stdin(
|
||||
autofix: fix::FixMode,
|
||||
) -> Result<Diagnostics> {
|
||||
// Lint the inputs.
|
||||
let (messages, fixed) = if matches!(autofix, fix::FixMode::Apply | fix::FixMode::Diff) {
|
||||
let (transformed, fixed, messages) = lint_fix(
|
||||
let (
|
||||
LinterResult {
|
||||
data: messages,
|
||||
error: parse_error,
|
||||
},
|
||||
fixed,
|
||||
) = if matches!(autofix, fix::FixMode::Apply | fix::FixMode::Diff) {
|
||||
if let Ok((result, transformed, fixed)) = lint_fix(
|
||||
contents,
|
||||
path.unwrap_or_else(|| Path::new("-")),
|
||||
package,
|
||||
settings,
|
||||
)?;
|
||||
) {
|
||||
if matches!(autofix, fix::FixMode::Apply) {
|
||||
// Write the contents to stdout, regardless of whether any errors were fixed.
|
||||
io::stdout().write_all(transformed.as_bytes())?;
|
||||
} else if matches!(autofix, fix::FixMode::Diff) {
|
||||
// But only write a diff if it's non-empty.
|
||||
if fixed > 0 {
|
||||
let text_diff = TextDiff::from_lines(contents, &transformed);
|
||||
let mut unified_diff = text_diff.unified_diff();
|
||||
if let Some(path) = path {
|
||||
unified_diff.header(&fs::relativize_path(path), &fs::relativize_path(path));
|
||||
}
|
||||
|
||||
if matches!(autofix, fix::FixMode::Apply) {
|
||||
// Write the contents to stdout, regardless of whether any errors were fixed.
|
||||
io::stdout().write_all(transformed.as_bytes())?;
|
||||
} else if matches!(autofix, fix::FixMode::Diff) {
|
||||
// But only write a diff if it's non-empty.
|
||||
if fixed > 0 {
|
||||
let text_diff = TextDiff::from_lines(contents, &transformed);
|
||||
let mut unified_diff = text_diff.unified_diff();
|
||||
if let Some(path) = path {
|
||||
unified_diff.header(&fs::relativize_path(path), &fs::relativize_path(path));
|
||||
let mut stdout = io::stdout().lock();
|
||||
unified_diff.to_writer(&mut stdout)?;
|
||||
stdout.write_all(b"\n")?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
|
||||
let mut stdout = io::stdout().lock();
|
||||
unified_diff.to_writer(&mut stdout)?;
|
||||
stdout.write_all(b"\n")?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
}
|
||||
|
||||
(messages, fixed)
|
||||
(result, fixed)
|
||||
} else {
|
||||
// If we fail to autofix, lint the original source code.
|
||||
let result = lint_only(
|
||||
contents,
|
||||
path.unwrap_or_else(|| Path::new("-")),
|
||||
package,
|
||||
settings,
|
||||
autofix.into(),
|
||||
);
|
||||
let fixed = 0;
|
||||
|
||||
// Write the contents to stdout anyway.
|
||||
if matches!(autofix, fix::FixMode::Apply) {
|
||||
io::stdout().write_all(contents.as_bytes())?;
|
||||
}
|
||||
|
||||
(result, fixed)
|
||||
}
|
||||
} else {
|
||||
let messages = lint_only(
|
||||
let result = lint_only(
|
||||
contents,
|
||||
path.unwrap_or_else(|| Path::new("-")),
|
||||
package,
|
||||
settings,
|
||||
autofix.into(),
|
||||
)?;
|
||||
);
|
||||
let fixed = 0;
|
||||
(messages, fixed)
|
||||
(result, fixed)
|
||||
};
|
||||
|
||||
if let Some(err) = parse_error {
|
||||
#[allow(clippy::print_stderr)]
|
||||
{
|
||||
eprintln!(
|
||||
"{}{} Failed to parse {}: {err}",
|
||||
"error".red().bold(),
|
||||
":".bold(),
|
||||
path.map_or_else(|| "-".into(), fs::relativize_path).bold()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Diagnostics { messages, fixed })
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
//! to automatically update the `ruff help` output in the `README.md`.
|
||||
//!
|
||||
//! For the actual Ruff library, see [`ruff`].
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::must_use_candidate, dead_code)]
|
||||
|
||||
mod args;
|
||||
|
||||
|
||||
@@ -1,28 +1,20 @@
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(
|
||||
clippy::match_same_arms,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
use std::io::{self};
|
||||
use std::path::PathBuf;
|
||||
use std::process::ExitCode;
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
use ::ruff::logging::{set_up_logging, LogLevel};
|
||||
use ::ruff::resolver::PyprojectDiscovery;
|
||||
use ::ruff::settings::types::SerializationFormat;
|
||||
use ::ruff::{fix, fs, warn_user_once};
|
||||
use anyhow::Result;
|
||||
use args::{Args, CheckArgs, Command};
|
||||
use clap::{CommandFactory, Parser, Subcommand};
|
||||
use colored::Colorize;
|
||||
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
||||
|
||||
use ::ruff::logging::{set_up_logging, LogLevel};
|
||||
use ::ruff::resolver::PyprojectDiscovery;
|
||||
use ::ruff::settings::types::SerializationFormat;
|
||||
use ::ruff::settings::CliSettings;
|
||||
use ::ruff::{fix, fs, warn_user_once};
|
||||
use args::{Args, CheckArgs, Command};
|
||||
use printer::{Printer, Violations};
|
||||
use ruff::settings::CliSettings;
|
||||
|
||||
pub(crate) mod args;
|
||||
mod cache;
|
||||
@@ -31,8 +23,6 @@ mod diagnostics;
|
||||
mod iterators;
|
||||
mod printer;
|
||||
mod resolve;
|
||||
#[cfg(all(feature = "update-informer"))]
|
||||
pub mod updates;
|
||||
|
||||
fn inner_main() -> Result<ExitCode> {
|
||||
let mut args: Vec<_> = std::env::args_os().collect();
|
||||
@@ -58,26 +48,32 @@ fn inner_main() -> Result<ExitCode> {
|
||||
log_level_args,
|
||||
} = Args::parse_from(args);
|
||||
|
||||
let default_panic_hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
eprintln!(
|
||||
r#"
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
let default_panic_hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
#[allow(clippy::print_stderr)]
|
||||
{
|
||||
eprintln!(
|
||||
r#"
|
||||
{}: `ruff` crashed. This indicates a bug in `ruff`. If you could open an issue at:
|
||||
|
||||
https://github.com/charliermarsh/ruff/issues/new?title=%5BPanic%5D
|
||||
|
||||
quoting the executed command, along with the relevant file contents and `pyproject.toml` settings, we'd be very appreciative!
|
||||
"#,
|
||||
"error".red().bold(),
|
||||
);
|
||||
default_panic_hook(info);
|
||||
}));
|
||||
"error".red().bold(),
|
||||
);
|
||||
}
|
||||
default_panic_hook(info);
|
||||
}));
|
||||
}
|
||||
|
||||
let log_level: LogLevel = (&log_level_args).into();
|
||||
set_up_logging(&log_level)?;
|
||||
|
||||
match command {
|
||||
Command::Rule { rule, format } => commands::rule(rule, format)?,
|
||||
Command::Rule { rule, format } => commands::rule(&rule, format)?,
|
||||
Command::Linter { format } => commands::linter::linter(format),
|
||||
Command::Clean => commands::clean(log_level)?,
|
||||
Command::GenerateShellCompletion { shell } => {
|
||||
@@ -108,6 +104,7 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitCode> {
|
||||
}
|
||||
if cli.show_files {
|
||||
commands::show_files(&cli.files, &pyproject_strategy, &overrides)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
// Extract options that are included in `Settings`, but only apply at the top
|
||||
@@ -157,9 +154,15 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitCode> {
|
||||
}
|
||||
|
||||
if cli.add_noqa {
|
||||
if !matches!(autofix, fix::FixMode::None) {
|
||||
warn_user_once!("--fix is incompatible with --add-noqa.");
|
||||
}
|
||||
let modifications = commands::add_noqa(&cli.files, &pyproject_strategy, &overrides)?;
|
||||
if modifications > 0 && log_level >= LogLevel::Default {
|
||||
println!("Added {modifications} noqa directives.");
|
||||
#[allow(clippy::print_stderr)]
|
||||
{
|
||||
eprintln!("Added {modifications} noqa directives.");
|
||||
}
|
||||
}
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
@@ -168,7 +171,7 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitCode> {
|
||||
|
||||
if cli.watch {
|
||||
if !matches!(autofix, fix::FixMode::None) {
|
||||
warn_user_once!("--fix is not enabled in watch mode.");
|
||||
warn_user_once!("--fix is unsupported in watch mode.");
|
||||
}
|
||||
if format != SerializationFormat::Text {
|
||||
warn_user_once!("--format 'text' is used in watch mode.");
|
||||
@@ -252,14 +255,10 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitCode> {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for updates if we're in a non-silent log level.
|
||||
#[cfg(feature = "update-informer")]
|
||||
if update_check
|
||||
&& !is_stdin
|
||||
&& log_level >= LogLevel::Default
|
||||
&& atty::is(atty::Stream::Stdout)
|
||||
{
|
||||
drop(updates::check_for_updates());
|
||||
if update_check {
|
||||
warn_user_once!(
|
||||
"update-check has been removed; setting it will cause an error in a future version."
|
||||
);
|
||||
}
|
||||
|
||||
if !cli.exit_zero {
|
||||
@@ -289,7 +288,10 @@ pub fn main() -> ExitCode {
|
||||
match inner_main() {
|
||||
Ok(code) => code,
|
||||
Err(err) => {
|
||||
eprintln!("{}{} {err:?}", "error".red().bold(), ":".bold());
|
||||
#[allow(clippy::print_stderr)]
|
||||
{
|
||||
eprintln!("{}{} {err:?}", "error".red().bold(), ":".bold());
|
||||
}
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use annotate_snippets::display_list::{DisplayList, FormatOptions};
|
||||
use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation};
|
||||
use anyhow::Result;
|
||||
use colored::Colorize;
|
||||
use itertools::iterate;
|
||||
use itertools::{iterate, Itertools};
|
||||
use ruff::fs::relativize_path;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::message::{Location, Message};
|
||||
@@ -75,7 +75,7 @@ pub struct Printer<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Printer<'a> {
|
||||
pub fn new(
|
||||
pub const fn new(
|
||||
format: &'a SerializationFormat,
|
||||
log_level: &'a LogLevel,
|
||||
autofix: &'a fix::FixMode,
|
||||
@@ -122,7 +122,7 @@ impl<'a> Printer<'a> {
|
||||
if num_fixable > 0 {
|
||||
writeln!(
|
||||
stdout,
|
||||
"{num_fixable} potentially fixable with the --fix option."
|
||||
"[*] {num_fixable} potentially fixable with the --fix option."
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -344,13 +344,16 @@ impl<'a> Printer<'a> {
|
||||
}
|
||||
|
||||
pub fn write_statistics(&self, diagnostics: &Diagnostics) -> Result<()> {
|
||||
let mut violations = diagnostics
|
||||
let violations = diagnostics
|
||||
.messages
|
||||
.iter()
|
||||
.map(|message| message.kind.rule())
|
||||
.sorted()
|
||||
.dedup()
|
||||
.collect::<Vec<_>>();
|
||||
violations.sort();
|
||||
violations.dedup();
|
||||
if violations.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let statistics = violations
|
||||
.iter()
|
||||
@@ -472,17 +475,31 @@ fn num_digits(n: usize) -> usize {
|
||||
|
||||
/// Print a single `Message` with full details.
|
||||
fn print_message<T: Write>(stdout: &mut T, message: &Message) -> Result<()> {
|
||||
let label = format!(
|
||||
"{}{}{}{}{}{} {} {}",
|
||||
relativize_path(Path::new(&message.filename)).bold(),
|
||||
":".cyan(),
|
||||
message.location.row(),
|
||||
":".cyan(),
|
||||
message.location.column(),
|
||||
":".cyan(),
|
||||
message.kind.rule().code().red().bold(),
|
||||
message.kind.body(),
|
||||
);
|
||||
let label = if message.kind.fixable() {
|
||||
format!(
|
||||
"{}{}{}{}{}{} {} [*] {}",
|
||||
relativize_path(Path::new(&message.filename)).bold(),
|
||||
":".cyan(),
|
||||
message.location.row(),
|
||||
":".cyan(),
|
||||
message.location.column(),
|
||||
":".cyan(),
|
||||
message.kind.rule().code().red().bold(),
|
||||
message.kind.body(),
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{}{}{}{}{}{} {} {}",
|
||||
relativize_path(Path::new(&message.filename)).bold(),
|
||||
":".cyan(),
|
||||
message.location.row(),
|
||||
":".cyan(),
|
||||
message.location.column(),
|
||||
":".cyan(),
|
||||
message.kind.rule().code().red().bold(),
|
||||
message.kind.body(),
|
||||
)
|
||||
};
|
||||
writeln!(stdout, "{label}")?;
|
||||
if let Some(source) = &message.source {
|
||||
let commit = message.kind.commit();
|
||||
@@ -537,16 +554,29 @@ fn print_grouped_message<T: Write>(
|
||||
row_length: usize,
|
||||
column_length: usize,
|
||||
) -> Result<()> {
|
||||
let label = format!(
|
||||
" {}{}{}{}{} {} {}",
|
||||
" ".repeat(row_length - num_digits(message.location.row())),
|
||||
message.location.row(),
|
||||
":".cyan(),
|
||||
message.location.column(),
|
||||
" ".repeat(column_length - num_digits(message.location.column())),
|
||||
message.kind.rule().code().red().bold(),
|
||||
message.kind.body(),
|
||||
);
|
||||
let label = if message.kind.fixable() {
|
||||
format!(
|
||||
" {}{}{}{}{} {} [*] {}",
|
||||
" ".repeat(row_length - num_digits(message.location.row())),
|
||||
message.location.row(),
|
||||
":".cyan(),
|
||||
message.location.column(),
|
||||
" ".repeat(column_length - num_digits(message.location.column())),
|
||||
message.kind.rule().code().red().bold(),
|
||||
message.kind.body(),
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
" {}{}{}{}{} {} {}",
|
||||
" ".repeat(row_length - num_digits(message.location.row())),
|
||||
message.location.row(),
|
||||
":".cyan(),
|
||||
message.location.column(),
|
||||
" ".repeat(column_length - num_digits(message.location.column())),
|
||||
message.kind.rule().code().red().bold(),
|
||||
message.kind.body(),
|
||||
)
|
||||
};
|
||||
writeln!(stdout, "{label}")?;
|
||||
if let Some(source) = &message.source {
|
||||
let commit = message.kind.commit();
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
use std::fs::{create_dir_all, read_to_string, File};
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use colored::Colorize;
|
||||
|
||||
const CARGO_PKG_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
fn cache_dir() -> &'static str {
|
||||
"./.ruff_cache"
|
||||
}
|
||||
|
||||
fn file_path() -> PathBuf {
|
||||
Path::new(cache_dir()).join(".update-informer")
|
||||
}
|
||||
|
||||
/// Get the "latest" version for which the user has been informed.
|
||||
fn get_latest() -> Result<Option<String>> {
|
||||
let path = file_path();
|
||||
if path.exists() {
|
||||
Ok(Some(read_to_string(path)?.trim().to_string()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the "latest" version for which the user has been informed.
|
||||
fn set_latest(version: &str) -> Result<()> {
|
||||
create_dir_all(cache_dir())?;
|
||||
let path = file_path();
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(version.trim().as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update the user if a newer version is available.
|
||||
pub fn check_for_updates() -> Result<()> {
|
||||
use update_informer::{registry, Check};
|
||||
|
||||
let informer = update_informer::new(registry::PyPI, CARGO_PKG_NAME, CARGO_PKG_VERSION);
|
||||
|
||||
if let Some(new_version) = informer
|
||||
.check_version()
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|version| version.to_string())
|
||||
{
|
||||
// If we've already notified the user about this version, return early.
|
||||
if let Some(latest_version) = get_latest()? {
|
||||
if latest_version == new_version {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
set_latest(&new_version)?;
|
||||
|
||||
let msg = format!(
|
||||
"A new version of {pkg_name} is available: v{pkg_version} -> {new_version}",
|
||||
pkg_name = CARGO_PKG_NAME.italic().cyan(),
|
||||
pkg_version = CARGO_PKG_VERSION,
|
||||
new_version = new_version.green()
|
||||
);
|
||||
|
||||
let cmd = format!(
|
||||
"Run to update: {cmd} {pkg_name}",
|
||||
cmd = "pip3 install --upgrade".green(),
|
||||
pkg_name = CARGO_PKG_NAME.green()
|
||||
);
|
||||
|
||||
println!("\n{msg}\n{cmd}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -31,8 +31,10 @@ fn test_stdin_error() -> Result<()> {
|
||||
.failure();
|
||||
assert_eq!(
|
||||
str::from_utf8(&output.get_output().stdout)?,
|
||||
"-:1:8: F401 `os` imported but unused\nFound 1 error.\n1 potentially fixable with the \
|
||||
--fix option.\n"
|
||||
r#"-:1:8: F401 [*] `os` imported but unused
|
||||
Found 1 error.
|
||||
[*] 1 potentially fixable with the --fix option.
|
||||
"#
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -54,8 +56,10 @@ fn test_stdin_filename() -> Result<()> {
|
||||
.failure();
|
||||
assert_eq!(
|
||||
str::from_utf8(&output.get_output().stdout)?,
|
||||
"F401.py:1:8: F401 `os` imported but unused\nFound 1 error.\n1 potentially fixable with \
|
||||
the --fix option.\n"
|
||||
r#"F401.py:1:8: F401 [*] `os` imported but unused
|
||||
Found 1 error.
|
||||
[*] 1 potentially fixable with the --fix option.
|
||||
"#
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.238"
|
||||
version = "0.0.241"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -11,9 +11,9 @@ libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a87
|
||||
once_cell = { version = "1.16.0" }
|
||||
ruff = { path = ".." }
|
||||
ruff_cli = { path = "../ruff_cli" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" }
|
||||
schemars = { version = "0.8.11" }
|
||||
serde_json = { version = "1.0.91" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
//! Generate CLI help.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use crate::utils::replace_readme_section;
|
||||
use anyhow::Result;
|
||||
use std::str;
|
||||
|
||||
const COMMAND_HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated command help. -->";
|
||||
const COMMAND_HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated command help. -->\n";
|
||||
const COMMAND_HELP_END_PRAGMA: &str = "<!-- End auto-generated command help. -->";
|
||||
|
||||
const SUBCOMMAND_HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated subcommand help. -->";
|
||||
const SUBCOMMAND_HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated subcommand help. -->\n";
|
||||
const SUBCOMMAND_HELP_END_PRAGMA: &str = "<!-- End auto-generated subcommand help. -->";
|
||||
|
||||
#[derive(clap::Args)]
|
||||
@@ -33,12 +34,12 @@ pub fn main(args: &Args) -> Result<()> {
|
||||
print!("{subcommand_help}");
|
||||
} else {
|
||||
replace_readme_section(
|
||||
&format!("```\n{command_help}\n```\n"),
|
||||
&format!("```text\n{command_help}\n```\n\n"),
|
||||
COMMAND_HELP_BEGIN_PRAGMA,
|
||||
COMMAND_HELP_END_PRAGMA,
|
||||
)?;
|
||||
replace_readme_section(
|
||||
&format!("```\n{subcommand_help}\n```\n"),
|
||||
&format!("```text\n{subcommand_help}\n```\n\n"),
|
||||
SUBCOMMAND_HELP_BEGIN_PRAGMA,
|
||||
SUBCOMMAND_HELP_END_PRAGMA,
|
||||
)?;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -5,6 +7,8 @@ use anyhow::Result;
|
||||
use ruff::settings::options::Options;
|
||||
use schemars::schema_for;
|
||||
|
||||
use crate::ROOT_DIR;
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
/// Write the generated table to stdout (rather than to `ruff.schema.json`).
|
||||
@@ -19,10 +23,7 @@ pub fn main(args: &Args) -> Result<()> {
|
||||
if args.dry_run {
|
||||
println!("{schema_string}");
|
||||
} else {
|
||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.expect("Failed to find root directory")
|
||||
.join("ruff.schema.json");
|
||||
let file = PathBuf::from(ROOT_DIR).join("ruff.schema.json");
|
||||
fs::write(file, schema_string.as_bytes())?;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Generate a Markdown-compatible listing of configuration options.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use anyhow::Result;
|
||||
use itertools::Itertools;
|
||||
@@ -7,7 +8,7 @@ use ruff::settings::options_base::{ConfigurationOptions, OptionEntry, OptionFiel
|
||||
|
||||
use crate::utils::replace_readme_section;
|
||||
|
||||
const BEGIN_PRAGMA: &str = "<!-- Begin auto-generated options sections. -->";
|
||||
const BEGIN_PRAGMA: &str = "<!-- Begin auto-generated options sections. -->\n";
|
||||
const END_PRAGMA: &str = "<!-- End auto-generated options sections. -->";
|
||||
|
||||
#[derive(clap::Args)]
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
//! Generate a Markdown-compatible table of supported lint rules.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use anyhow::Result;
|
||||
use itertools::Itertools;
|
||||
use ruff::registry::{Linter, LinterCategory, Rule, RuleNamespace};
|
||||
use ruff::registry::{Linter, Rule, RuleNamespace, UpstreamCategory};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::utils::replace_readme_section;
|
||||
|
||||
const TABLE_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated sections. -->";
|
||||
const TABLE_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated sections. -->\n";
|
||||
const TABLE_END_PRAGMA: &str = "<!-- End auto-generated sections. -->";
|
||||
|
||||
const TOC_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated table of contents. -->";
|
||||
@@ -50,10 +51,10 @@ pub fn main(args: &Args) -> Result<()> {
|
||||
for linter in Linter::iter() {
|
||||
let codes_csv: String = match linter.common_prefix() {
|
||||
"" => linter
|
||||
.categories()
|
||||
.upstream_categories()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|LinterCategory(prefix, ..)| prefix)
|
||||
.map(|UpstreamCategory(prefix, ..)| prefix.as_ref())
|
||||
.join(", "),
|
||||
prefix => prefix.to_string(),
|
||||
};
|
||||
@@ -93,11 +94,12 @@ pub fn main(args: &Args) -> Result<()> {
|
||||
table_out.push('\n');
|
||||
}
|
||||
|
||||
if let Some(categories) = linter.categories() {
|
||||
for LinterCategory(prefix, name, selector) in categories {
|
||||
table_out.push_str(&format!("#### {name} ({prefix})"));
|
||||
if let Some(categories) = linter.upstream_categories() {
|
||||
for UpstreamCategory(prefix, name) in categories {
|
||||
table_out.push_str(&format!("#### {name} ({})", prefix.as_ref()));
|
||||
table_out.push('\n');
|
||||
generate_table(&mut table_out, selector);
|
||||
table_out.push('\n');
|
||||
generate_table(&mut table_out, prefix);
|
||||
}
|
||||
} else {
|
||||
generate_table(&mut table_out, &linter);
|
||||
|
||||
@@ -1,20 +1,6 @@
|
||||
//! This crate implements an internal CLI for developers of Ruff.
|
||||
//!
|
||||
//! Within the ruff repository you can run it with `cargo dev`.
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(
|
||||
clippy::collapsible_else_if,
|
||||
clippy::collapsible_if,
|
||||
clippy::implicit_hasher,
|
||||
clippy::match_same_arms,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::must_use_candidate,
|
||||
clippy::similar_names,
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
mod generate_all;
|
||||
mod generate_cli_help;
|
||||
@@ -30,6 +16,8 @@ mod utils;
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
const ROOT_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../");
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[command(propagate_version = true)]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Print the AST for a given Python file.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Print the `LibCST` CST for a given Python file.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Print the token stream for a given Python file.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Run round-trip source code generation on a given Python file.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -5,12 +5,11 @@ use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::ROOT_DIR;
|
||||
|
||||
pub fn replace_readme_section(content: &str, begin_pragma: &str, end_pragma: &str) -> Result<()> {
|
||||
// Read the existing file.
|
||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.expect("Failed to find root directory")
|
||||
.join("README.md");
|
||||
let file = PathBuf::from(ROOT_DIR).join("README.md");
|
||||
let existing = fs::read_to_string(&file)?;
|
||||
|
||||
// Extract the prefix.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.238"
|
||||
version = "0.0.241"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -176,7 +176,7 @@ impl Parse for FieldAttributes {
|
||||
input.parse::<Comma>()?;
|
||||
}
|
||||
|
||||
Ok(FieldAttributes {
|
||||
Ok(Self {
|
||||
default,
|
||||
value_type,
|
||||
example: textwrap::dedent(&example).trim_matches('\n').to_string(),
|
||||
|
||||
@@ -29,7 +29,7 @@ pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
|
||||
.extend(quote! {Self::#name => <#path as Violation>::message_formats(),});
|
||||
rule_autofixable_match_arms.extend(quote! {Self::#name => <#path as Violation>::AUTOFIX,});
|
||||
rule_code_match_arms.extend(quote! {Self::#name => #code_str,});
|
||||
rule_from_code_match_arms.extend(quote! {#code_str => Ok(&Rule::#name), });
|
||||
rule_from_code_match_arms.extend(quote! {#code_str => Ok(Rule::#name), });
|
||||
diagkind_code_match_arms.extend(quote! {Self::#name(..) => &Rule::#name, });
|
||||
diagkind_body_match_arms.extend(quote! {Self::#name(x) => Violation::message(x), });
|
||||
diagkind_fixable_match_arms
|
||||
@@ -96,7 +96,7 @@ pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
|
||||
match self { #rule_code_match_arms }
|
||||
}
|
||||
|
||||
pub fn from_code(code: &str) -> Result<&'static Self, FromCodeError> {
|
||||
pub fn from_code(code: &str) -> Result<Self, FromCodeError> {
|
||||
match code {
|
||||
#rule_from_code_match_arms
|
||||
_ => Err(FromCodeError::Unknown),
|
||||
@@ -148,6 +148,6 @@ impl Parse for Mapping {
|
||||
let _: Token![,] = input.parse()?;
|
||||
entries.push((code, path, name));
|
||||
}
|
||||
Ok(Mapping { entries })
|
||||
Ok(Self { entries })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
//! This crate implements internal macros for the `ruff` library.
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(
|
||||
clippy::collapsible_else_if,
|
||||
clippy::collapsible_if,
|
||||
clippy::implicit_hasher,
|
||||
clippy::match_same_arms,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::must_use_candidate,
|
||||
clippy::similar_names,
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput, ItemFn};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user