Compare commits
2 Commits
zanie/shar
...
string-pre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d2513e5a9 | ||
|
|
272306bf5a |
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -9,5 +9,5 @@ updates:
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
interval: "daily"
|
||||
labels: ["internal"]
|
||||
|
||||
5
.github/release.yml
vendored
5
.github/release.yml
vendored
@@ -4,6 +4,7 @@ changelog:
|
||||
labels:
|
||||
- internal
|
||||
- documentation
|
||||
- formatter
|
||||
categories:
|
||||
- title: Breaking Changes
|
||||
labels:
|
||||
@@ -11,6 +12,7 @@ changelog:
|
||||
- title: Rules
|
||||
labels:
|
||||
- rule
|
||||
- autofix
|
||||
- title: Settings
|
||||
labels:
|
||||
- configuration
|
||||
@@ -18,9 +20,6 @@ changelog:
|
||||
- title: Bug Fixes
|
||||
labels:
|
||||
- bug
|
||||
- title: Formatter
|
||||
labels:
|
||||
- formatter
|
||||
- title: Preview
|
||||
labels:
|
||||
- preview
|
||||
|
||||
23
.github/workflows/ci.yaml
vendored
23
.github/workflows/ci.yaml
vendored
@@ -77,8 +77,6 @@ jobs:
|
||||
rustup component add clippy
|
||||
rustup target add wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Clippy"
|
||||
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
- name: "Clippy (wasm)"
|
||||
@@ -98,9 +96,8 @@ jobs:
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- run: pip install black[d]==23.1.0
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Run tests (Ubuntu)"
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: cargo insta test --all --all-features --unreferenced reject
|
||||
@@ -109,6 +106,10 @@ jobs:
|
||||
shell: bash
|
||||
# We can't reject unreferenced snapshots on windows because flake8_executable can't run on windows
|
||||
run: cargo insta test --all --all-features
|
||||
- run: cargo test --package ruff_cli --test black_compatibility_test -- --ignored
|
||||
# TODO: Skipped as it's currently broken. The resource were moved from the
|
||||
# ruff_cli to ruff crate, but this test was not updated.
|
||||
if: false
|
||||
# Check for broken links in the documentation.
|
||||
- run: cargo doc --all --no-deps
|
||||
env:
|
||||
@@ -150,8 +151,6 @@ jobs:
|
||||
cache-dependency-path: playground/package-lock.json
|
||||
- uses: jetli/wasm-pack-action@v0.4.0
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Run wasm-pack"
|
||||
run: |
|
||||
cd crates/ruff_wasm
|
||||
@@ -165,8 +164,6 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup component add rustfmt
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- run: ./scripts/add_rule.py --name DoTheThing --prefix PL --code C0999 --linter pylint
|
||||
- run: cargo check
|
||||
- run: cargo fmt --all --check
|
||||
@@ -235,8 +232,6 @@ jobs:
|
||||
# Only pinned to make caching work, update freely
|
||||
run: rustup toolchain install nightly-2023-06-08
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Install cargo-udeps"
|
||||
uses: taiki-e/install-action@cargo-udeps
|
||||
- name: "Run cargo-udeps"
|
||||
@@ -252,8 +247,6 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
@@ -279,8 +272,6 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Install pre-commit"
|
||||
run: pip install pre-commit
|
||||
- name: "Cache pre-commit"
|
||||
@@ -314,8 +305,6 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: pip install -r docs/requirements-insiders.txt
|
||||
@@ -368,8 +357,6 @@ jobs:
|
||||
tool: cargo-codspeed
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
|
||||
- name: "Build benchmarks"
|
||||
run: cargo codspeed build --features codspeed -p ruff_benchmark
|
||||
|
||||
2
.github/workflows/docs.yaml
vendored
2
.github/workflows/docs.yaml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
run: mkdocs build --strict -f mkdocs.generated.yml
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.3.1
|
||||
uses: cloudflare/wrangler-action@v3.1.1
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
5
.github/workflows/playground.yaml
vendored
5
.github/workflows/playground.yaml
vendored
@@ -40,9 +40,8 @@ jobs:
|
||||
working-directory: playground
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.3.1
|
||||
uses: cloudflare/wrangler-action@v3.1.1
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
# `github.head_ref` is only set during pull requests and for manual runs or tags we use `main` to deploy to production
|
||||
command: pages deploy playground/dist --project-name=ruff-playground --branch ${{ github.head_ref || 'main' }} --commit-hash ${GITHUB_SHA}
|
||||
command: pages deploy playground/dist --project-name=ruff-playground --branch ${GITHUB_HEAD_REF} --commit-hash ${GITHUB_SHA}
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -208,9 +208,3 @@ cython_debug/
|
||||
# VIM
|
||||
.*.sw?
|
||||
.sw?
|
||||
|
||||
# Custom re-inclusions for the resolver test cases
|
||||
!crates/ruff_python_resolver/resources/test/airflow/venv/
|
||||
!crates/ruff_python_resolver/resources/test/airflow/venv/lib
|
||||
!crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/_watchdog_fsevents.cpython-311-darwin.so
|
||||
!crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/orjson/orjson.cpython-311-darwin.so
|
||||
|
||||
@@ -23,7 +23,6 @@ repos:
|
||||
- id: mdformat
|
||||
additional_dependencies:
|
||||
- mdformat-mkdocs
|
||||
- mdformat-admon
|
||||
|
||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||
rev: v0.33.0
|
||||
|
||||
@@ -1,35 +1,5 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### The deprecated `format` setting has been removed
|
||||
|
||||
Ruff previously used the `format` setting, `--format` CLI option, and `RUFF_FORMAT` environment variable to
|
||||
configure the output format of the CLI. This usage was deprecated in `v0.0.291` — the `format` setting is now used
|
||||
to control Ruff's code formatting. As of this release:
|
||||
|
||||
- The `format` setting cannot be used to configure the output format, use `output-format` instead
|
||||
- The `RUFF_FORMAT` environment variable is ignored, use `RUFF_OUTPUT_FORMAT` instead
|
||||
- The `--format` option has been removed from `ruff check`, use `--output-format` instead
|
||||
|
||||
### Unsafe fixes are not applied by default ([#7769](https://github.com/astral-sh/ruff/pull/7769))
|
||||
|
||||
Ruff labels fixes as "safe" and "unsafe". The meaning and intent of your code will be retained when applying safe
|
||||
fixes, but the meaning could be changed when applying unsafe fixes. Previously, unsafe fixes were always displayed
|
||||
and applied when fixing was enabled. Now, unsafe fixes are hidden by default and not applied. The `--unsafe-fixes`
|
||||
flag or `unsafe-fixes` configuration option can be used to enable unsafe fixes.
|
||||
|
||||
See the [docs](https://docs.astral.sh/ruff/configuration/#fix-safety) for details.
|
||||
|
||||
### Remove formatter-conflicting rules from the default rule set ([#7900](https://github.com/astral-sh/ruff/pull/7900))
|
||||
|
||||
Previously, Ruff enabled all implemented rules in Pycodestyle (`E`) by default. Ruff now only includes the
|
||||
Pycodestyle prefixes `E4`, `E7`, and `E9` to exclude rules that conflict with automatic formatters. Consequently,
|
||||
the stable rule set no longer includes `line-too-long` (`E501`) and `mixed-spaces-and-tabs` (`E101`). Other
|
||||
excluded Pycodestyle rules include whitespace enforcement in `E1` and `E2`; these rules are currently in preview, and are already omitted by default.
|
||||
|
||||
This change only affects those using Ruff under its default rule set. Users that include `E` in their `select` will experience no change in behavior.
|
||||
|
||||
## 0.0.288
|
||||
|
||||
### Remove support for emoji identifiers ([#7212](https://github.com/astral-sh/ruff/pull/7212))
|
||||
|
||||
115
CHANGELOG.md
115
CHANGELOG.md
@@ -1,115 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
This is the first release which uses the `CHANGELOG` file. See [GitHub Releases](https://github.com/astral-sh/ruff/releases) for prior changelog entries.
|
||||
|
||||
Read Ruff's new [versioning policy](https://docs.astral.sh/ruff/versioning/).
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- Unsafe fixes are no longer displayed or applied without opt-in ([#7769](https://github.com/astral-sh/ruff/pull/7769))
|
||||
- Drop formatting specific rules from the default set ([#7900](https://github.com/astral-sh/ruff/pull/7900))
|
||||
- The deprecated `format` setting has been removed ([#7984](https://github.com/astral-sh/ruff/pull/7984))
|
||||
- The `format` setting cannot be used to configure the output format, use `output-format` instead
|
||||
- The `RUFF_FORMAT` environment variable is ignored, use `RUFF_OUTPUT_FORMAT` instead
|
||||
- The `--format` option has been removed from `ruff check`, use `--output-format` instead
|
||||
|
||||
### Rule changes
|
||||
|
||||
- Extend `reimplemented-starmap` (`FURB140`) to catch calls with a single and starred argument ([#7768](https://github.com/astral-sh/ruff/pull/7768))
|
||||
- Improve cases covered by `RUF015` ([#7848](https://github.com/astral-sh/ruff/pull/7848))
|
||||
- Update `SIM15` to allow `open` followed by `close` ([#7916](https://github.com/astral-sh/ruff/pull/7916))
|
||||
- Respect `msgspec.Struct` default-copy semantics in `RUF012` ([#7786](https://github.com/astral-sh/ruff/pull/7786))
|
||||
- Add `sqlalchemy` methods to \`flake8-boolean-trap\`\` exclusion list ([#7874](https://github.com/astral-sh/ruff/pull/7874))
|
||||
- Add fix for `PLR1714` ([#7910](https://github.com/astral-sh/ruff/pull/7910))
|
||||
- Add fix for `PIE804` ([#7884](https://github.com/astral-sh/ruff/pull/7884))
|
||||
- Add fix for `PLC0208` ([#7887](https://github.com/astral-sh/ruff/pull/7887))
|
||||
- Add fix for `PYI055` ([#7886](https://github.com/astral-sh/ruff/pull/7886))
|
||||
- Update `non-pep695-type-alias` to require `--unsafe-fixes` outside of stub files ([#7836](https://github.com/astral-sh/ruff/pull/7836))
|
||||
- Improve fix message for `UP018` ([#7913](https://github.com/astral-sh/ruff/pull/7913))
|
||||
- Update `PLW3201` to support `Enum` [sunder names](https://docs.python.org/3/library/enum.html#supported-sunder-names) ([#7987](https://github.com/astral-sh/ruff/pull/7987))
|
||||
|
||||
### Preview features
|
||||
|
||||
- Only show warnings for empty preview selectors when enabling rules ([#7842](https://github.com/astral-sh/ruff/pull/7842))
|
||||
- Add `unnecessary-key-check` to simplify `key in dct and dct[key]` to `dct.get(key)` ([#7895](https://github.com/astral-sh/ruff/pull/7895))
|
||||
- Add `assignment-in-assert` to prevent walrus expressions in assert statements ([#7856](https://github.com/astral-sh/ruff/pull/7856))
|
||||
- \[`refurb`\] Add `single-item-membership-test` (`FURB171`) ([#7815](https://github.com/astral-sh/ruff/pull/7815))
|
||||
- \[`pylint`\] Add `and-or-ternary` (`R1706`) ([#7811](https://github.com/astral-sh/ruff/pull/7811))
|
||||
|
||||
_New rules are added in [preview](https://docs.astral.sh/ruff/preview/)._
|
||||
|
||||
### Configuration
|
||||
|
||||
- Add `unsafe-fixes` setting ([#7769](https://github.com/astral-sh/ruff/pull/7769))
|
||||
- Add `extend-safe-fixes` and `extend-unsafe-fixes` for promoting and demoting fixes ([#7841](https://github.com/astral-sh/ruff/pull/7841))
|
||||
|
||||
### CLI
|
||||
|
||||
- Added `--unsafe-fixes` option for opt-in to display and apply unsafe fixes ([#7769](https://github.com/astral-sh/ruff/pull/7769))
|
||||
- Fix use of deprecated `--format` option in warning ([#7837](https://github.com/astral-sh/ruff/pull/7837))
|
||||
- Show changed files when running under `--check` ([#7788](https://github.com/astral-sh/ruff/pull/7788))
|
||||
- Write summary messages to stderr when fixing via stdin instead of omitting them ([#7838](https://github.com/astral-sh/ruff/pull/7838))
|
||||
- Update fix summary message in `check --diff` to include unsafe fix hints ([#7790](https://github.com/astral-sh/ruff/pull/7790))
|
||||
- Add notebook `cell` field to JSON output format ([#7664](https://github.com/astral-sh/ruff/pull/7664))
|
||||
- Rename applicability levels to `Safe`, `Unsafe`, and `Display` ([#7843](https://github.com/astral-sh/ruff/pull/7843))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Fix bug where f-strings were allowed in match pattern literal ([#7857](https://github.com/astral-sh/ruff/pull/7857))
|
||||
- Fix `SIM110` with a yield in the condition ([#7801](https://github.com/astral-sh/ruff/pull/7801))
|
||||
- Preserve trailing comments in `C414` fixes ([#7775](https://github.com/astral-sh/ruff/pull/7775))
|
||||
- Check sequence type before triggering `unnecessary-enumerate` `len` suggestion ([#7781](https://github.com/astral-sh/ruff/pull/7781))
|
||||
- Use correct start location for class/function clause header ([#7802](https://github.com/astral-sh/ruff/pull/7802))
|
||||
- Fix incorrect fixes for `SIM101` ([#7798](https://github.com/astral-sh/ruff/pull/7798))
|
||||
- Format comment before parameter default correctly ([#7870](https://github.com/astral-sh/ruff/pull/7870))
|
||||
- Fix `E251` false positive inside f-strings ([#7894](https://github.com/astral-sh/ruff/pull/7894))
|
||||
- Allow bindings to be created and referenced within annotations ([#7885](https://github.com/astral-sh/ruff/pull/7885))
|
||||
- Show per-cell diffs when analyzing notebooks over `stdin` ([#7789](https://github.com/astral-sh/ruff/pull/7789))
|
||||
- Avoid curly brace escape in f-string format spec ([#7780](https://github.com/astral-sh/ruff/pull/7780))
|
||||
- Fix lexing single-quoted f-string with multi-line format spec ([#7787](https://github.com/astral-sh/ruff/pull/7787))
|
||||
- Consider nursery rules to be in-preview for `ruff rule` ([#7812](https://github.com/astral-sh/ruff/pull/7812))
|
||||
- Report precise location for invalid conversion flag ([#7809](https://github.com/astral-sh/ruff/pull/7809))
|
||||
- Visit pattern match guard as a boolean test ([#7911](https://github.com/astral-sh/ruff/pull/7911))
|
||||
- Respect `--unfixable` in `ISC` rules ([#7917](https://github.com/astral-sh/ruff/pull/7917))
|
||||
- Fix edge case with `PIE804` ([#7922](https://github.com/astral-sh/ruff/pull/7922))
|
||||
- Show custom message in `PTH118` for `Path.joinpath` with starred arguments ([#7852](https://github.com/astral-sh/ruff/pull/7852))
|
||||
- Fix false negative in `outdated-version-block` when using greater than comparisons ([#7920](https://github.com/astral-sh/ruff/pull/7920))
|
||||
- Avoid converting f-strings within Django `gettext` calls ([#7898](https://github.com/astral-sh/ruff/pull/7898))
|
||||
- Fix false positive in `PLR6301` ([#7933](https://github.com/astral-sh/ruff/pull/7933))
|
||||
- Treat type aliases as typing-only expressions e.g. resolves false positive in `TCH004` ([#7968](https://github.com/astral-sh/ruff/pull/7968))
|
||||
- Resolve `cache-dir` relative to project root ([#7962](https://github.com/astral-sh/ruff/pull/7962))
|
||||
- Respect subscripted base classes in type-checking rules e.g. resolves false positive in `TCH003` ([#7954](https://github.com/astral-sh/ruff/pull/7954))
|
||||
- Fix JSON schema limit for `line-length` ([#7883](https://github.com/astral-sh/ruff/pull/7883))
|
||||
- Fix commented-out `coalesce` keyword ([#7876](https://github.com/astral-sh/ruff/pull/7876))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Document `reimplemented-starmap` performance effects ([#7846](https://github.com/astral-sh/ruff/pull/7846))
|
||||
- Default to following the system dark/light mode ([#7888](https://github.com/astral-sh/ruff/pull/7888))
|
||||
- Add documentation for fixes ([#7901](https://github.com/astral-sh/ruff/pull/7901))
|
||||
- Fix typo in docs of `PLR6301` ([#7831](https://github.com/astral-sh/ruff/pull/7831))
|
||||
- Update `UP038` docs to note that it results in slower code ([#7872](https://github.com/astral-sh/ruff/pull/7872))
|
||||
- crlf -> cr-lf ([#7766](https://github.com/astral-sh/ruff/pull/7766))
|
||||
- Add an example of an unsafe fix ([#7924](https://github.com/astral-sh/ruff/pull/7924))
|
||||
- Fix documented examples for `unnecessary-subscript-reversal` ([#7774](https://github.com/astral-sh/ruff/pull/7774))
|
||||
- Correct error in tuple example in ruff formatter docs ([#7822](https://github.com/astral-sh/ruff/pull/7822))
|
||||
- Add versioning policy to documentation ([#7923](https://github.com/astral-sh/ruff/pull/7923))
|
||||
- Fix invalid code in `FURB177` example ([#7832](https://github.com/astral-sh/ruff/pull/7832))
|
||||
|
||||
### Formatter
|
||||
|
||||
- Less scary `ruff format` message ([#7867](https://github.com/astral-sh/ruff/pull/7867))
|
||||
- Remove spaces from import statements ([#7859](https://github.com/astral-sh/ruff/pull/7859))
|
||||
- Formatter quoting for f-strings with triple quotes ([#7826](https://github.com/astral-sh/ruff/pull/7826))
|
||||
- Update `ruff_python_formatter` generate.py comment ([#7850](https://github.com/astral-sh/ruff/pull/7850))
|
||||
- Document one-call chaining deviation ([#7767](https://github.com/astral-sh/ruff/pull/7767))
|
||||
- Allow f-string modifications in line-shrinking cases ([#7818](https://github.com/astral-sh/ruff/pull/7818))
|
||||
- Add trailing comment deviation to README ([#7827](https://github.com/astral-sh/ruff/pull/7827))
|
||||
- Add trailing zero between dot and exponential ([#7956](https://github.com/astral-sh/ruff/pull/7956))
|
||||
- Force parentheses for power operations in unary expressions ([#7955](https://github.com/astral-sh/ruff/pull/7955))
|
||||
|
||||
### Playground
|
||||
|
||||
- Fix playground `Quick Fix` action ([#7824](https://github.com/astral-sh/ruff/pull/7824))
|
||||
@@ -170,8 +170,7 @@ At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
statements, like imports) or `analyze/expression.rs` (if your rule is based on analyzing
|
||||
expressions, like function calls).
|
||||
|
||||
1. Map the violation struct to a rule code in `crates/ruff_linter/src/codes.rs` (e.g., `B011`). New rules
|
||||
should be added in `RuleGroup::Preview`.
|
||||
1. Map the violation struct to a rule code in `crates/ruff_linter/src/codes.rs` (e.g., `B011`).
|
||||
|
||||
1. Add proper [testing](#rule-testing-fixtures-and-snapshots) for your rule.
|
||||
|
||||
@@ -205,7 +204,7 @@ As such, rule names should...
|
||||
For example, `AssertFalse` guards against `assert False` statements.
|
||||
|
||||
- _Not_ contain instructions on how to fix the violation, which instead belong in the rule
|
||||
documentation and the `fix_title`.
|
||||
documentation and the `autofix_title`.
|
||||
|
||||
- _Not_ contain a redundant prefix, like `Disallow` or `Banned`, which are already implied by the
|
||||
convention.
|
||||
|
||||
280
Cargo.lock
generated
280
Cargo.lock
generated
@@ -28,9 +28,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -74,9 +74,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.4"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -112,9 +112,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.1"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.48.0",
|
||||
@@ -221,7 +221,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata 0.3.9",
|
||||
"regex-automata 0.3.8",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -313,9 +313,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.6"
|
||||
version = "4.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
|
||||
checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -323,9 +323,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.6"
|
||||
version = "4.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
|
||||
checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -383,7 +383,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -608,7 +608,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -619,7 +619,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -810,7 +810,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.1.0"
|
||||
version = "0.0.290"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -872,15 +872,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.10"
|
||||
@@ -1044,9 +1035,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.17.7"
|
||||
version = "0.17.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
|
||||
checksum = "0b297dc40733f23a0e52728a58fa9489a5b7638a324932de16b41adc3ef80730"
|
||||
dependencies = [
|
||||
"console",
|
||||
"instant",
|
||||
@@ -1084,9 +1075,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.34.0"
|
||||
version = "1.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc"
|
||||
checksum = "a0770b0a3d4c70567f0d58331f3088b0e4c4f56c9b8d764efe654b4a5d46de3a"
|
||||
dependencies = [
|
||||
"console",
|
||||
"globset",
|
||||
@@ -1129,7 +1120,7 @@ dependencies = [
|
||||
"pmutil 0.6.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1268,9 +1259,9 @@ checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
|
||||
[[package]]
|
||||
name = "libcst"
|
||||
version = "1.1.0"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd5c2ff400caac657bf794181d885491bb97cc37c376f8cb4fa3a0cc2176a053"
|
||||
checksum = "7773d520d4292e200ab1838f2daabe2feed7549f93b0a3c7582160a09e79ffde"
|
||||
dependencies = [
|
||||
"chic",
|
||||
"libcst_derive",
|
||||
@@ -1283,9 +1274,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libcst_derive"
|
||||
version = "1.1.0"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d7f252282b20bfec6fae65d351ab1df7e4552a6270dd7bb779ca9d6135aabe9"
|
||||
checksum = "520197c50ba477f258cd7005ec5ed3a7393693ae6bec664990c7c8d9306a7c0d"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
@@ -1346,9 +1337,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.4"
|
||||
version = "2.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
@@ -1463,6 +1454,27 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.16"
|
||||
@@ -1472,6 +1484,16 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.4.0"
|
||||
@@ -1593,9 +1615,9 @@ checksum = "9fa00462b37ead6d11a82c9d568b26682d78e0477dc02d1966c013af80969739"
|
||||
|
||||
[[package]]
|
||||
name = "pep440_rs"
|
||||
version = "0.3.12"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "887f66cc62717ea72caac4f1eb4e6f392224da3ffff3f40ec13ab427802746d6"
|
||||
checksum = "b05bf2c44c4cd12f03b2c3ca095f3aa21f44e43c16021c332e511884719705be"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
@@ -1707,7 +1729,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1792,9 +1814,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
version = "1.0.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1876,9 +1898,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.0"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
||||
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
@@ -1886,12 +1908,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.0"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
||||
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1925,14 +1949,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
version = "1.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.3",
|
||||
"regex-syntax 0.8.2",
|
||||
"regex-automata 0.3.8",
|
||||
"regex-syntax 0.7.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1946,19 +1970,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.9"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9"
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.2",
|
||||
"regex-syntax 0.7.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1973,12 +1991,6 @@ version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "result-like"
|
||||
version = "0.4.6"
|
||||
@@ -2051,7 +2063,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.1.0"
|
||||
version = "0.0.290"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2155,7 +2167,6 @@ name = "ruff_diagnostics"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"is-macro",
|
||||
"log",
|
||||
"ruff_text_size",
|
||||
"serde",
|
||||
@@ -2188,9 +2199,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.1.0"
|
||||
version = "0.0.290"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
"bitflags 2.4.0",
|
||||
@@ -2208,6 +2218,8 @@ dependencies = [
|
||||
"log",
|
||||
"memchr",
|
||||
"natord",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"path-absolutize",
|
||||
"pathdiff",
|
||||
@@ -2259,7 +2271,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"ruff_python_trivia",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2290,6 +2302,8 @@ dependencies = [
|
||||
"is-macro",
|
||||
"itertools 0.11.0",
|
||||
"memchr",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"ruff_python_parser",
|
||||
"ruff_python_trivia",
|
||||
@@ -2334,7 +2348,6 @@ dependencies = [
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"similar",
|
||||
@@ -2366,6 +2379,7 @@ dependencies = [
|
||||
"is-macro",
|
||||
"itertools 0.11.0",
|
||||
"lexical-parse-float",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"unic-ucd-category",
|
||||
]
|
||||
@@ -2375,12 +2389,13 @@ name = "ruff_python_parser"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.4.0",
|
||||
"insta",
|
||||
"is-macro",
|
||||
"itertools 0.11.0",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"ruff_python_ast",
|
||||
"ruff_text_size",
|
||||
"rustc-hash",
|
||||
@@ -2406,6 +2421,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"is-macro",
|
||||
"num-traits",
|
||||
"ruff_index",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_parser",
|
||||
@@ -2509,7 +2525,6 @@ dependencies = [
|
||||
"glob",
|
||||
"globset",
|
||||
"ignore",
|
||||
"is-macro",
|
||||
"itertools 0.11.0",
|
||||
"log",
|
||||
"once_cell",
|
||||
@@ -2520,9 +2535,7 @@ dependencies = [
|
||||
"ruff_formatter",
|
||||
"ruff_linter",
|
||||
"ruff_macros",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_formatter",
|
||||
"ruff_source_file",
|
||||
"rustc-hash",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -2569,10 +2582,20 @@ checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-webpki",
|
||||
"rustls-webpki 0.101.4",
|
||||
"sct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.100.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e98ff011474fa39949b7e5c0428f9b4937eda7da7848bbb947786b7be0b27dab"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.101.4"
|
||||
@@ -2658,9 +2681,9 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.20"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
@@ -2690,7 +2713,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2752,7 +2775,7 @@ dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2781,9 +2804,9 @@ checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.3.0"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597"
|
||||
checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
@@ -2793,9 +2816,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.1"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
|
||||
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
@@ -2847,7 +2870,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2863,9 +2886,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.38"
|
||||
version = "2.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||
checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2952,7 +2975,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2964,28 +2987,28 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
"test-case-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.49"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4"
|
||||
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.49"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc"
|
||||
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3018,22 +3041,6 @@ dependencies = [
|
||||
"tikv-jemalloc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
@@ -3104,10 +3111,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.39"
|
||||
version = "0.1.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9"
|
||||
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
@@ -3116,20 +3124,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.27"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.32"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
@@ -3259,25 +3267,10 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_names2"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d5506ae2c3c1ccbdf468e52fc5ef536c2ccd981f01273a4cb81aa61021f3a5f"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/youknowone/unicode_names2.git?rev=4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde#4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde"
|
||||
dependencies = [
|
||||
"phf",
|
||||
"unicode_names2_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode_names2_generator"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6dfc680313e95bc6637fa278cd7a22390c3c2cd7b8b2bd28755bc6c0fc811e7"
|
||||
dependencies = [
|
||||
"getopts",
|
||||
"log",
|
||||
"phf_codegen",
|
||||
"rand",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3288,16 +3281,16 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "2.8.0"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3"
|
||||
checksum = "0b11c96ac7ee530603dcdf68ed1557050f374ce55a5a07193ebf8cbc9f8927e9"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"flate2",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls",
|
||||
"rustls-webpki",
|
||||
"rustls-webpki 0.100.2",
|
||||
"url",
|
||||
"webpki-roots",
|
||||
]
|
||||
@@ -3340,7 +3333,7 @@ checksum = "f7e1ba1f333bd65ce3c9f27de592fcbc256dafe3af2717f56d7c87761fbaccf4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3434,7 +3427,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -3468,7 +3461,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
"syn 2.0.37",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -3515,9 +3508,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.25.2"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
|
||||
checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338"
|
||||
dependencies = [
|
||||
"rustls-webpki 0.100.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
@@ -3532,9 +3528,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wild"
|
||||
version = "2.2.0"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10d01931a94d5a115a53f95292f51d316856b68a035618eb831bbba593a30b67"
|
||||
checksum = "05b116685a6be0c52f5a103334cbff26db643826c7b3735fc0a3ba9871310a74"
|
||||
dependencies = [
|
||||
"glob",
|
||||
]
|
||||
|
||||
33
Cargo.toml
33
Cargo.toml
@@ -15,46 +15,49 @@ license = "MIT"
|
||||
anyhow = { version = "1.0.69" }
|
||||
bitflags = { version = "2.3.1" }
|
||||
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.4.6", features = ["derive"] }
|
||||
clap = { version = "4.4.4", features = ["derive"] }
|
||||
colored = { version = "2.0.0" }
|
||||
filetime = { version = "0.2.20" }
|
||||
glob = { version = "0.3.1" }
|
||||
globset = { version = "0.4.10" }
|
||||
ignore = { version = "0.4.20" }
|
||||
insta = { version = "1.34.0", feature = ["filters", "glob"] }
|
||||
insta = { version = "1.31.0", feature = ["filters", "glob"] }
|
||||
is-macro = { version = "0.3.0" }
|
||||
itertools = { version = "0.11.0" }
|
||||
libcst = { version = "1.1.0", default-features = false }
|
||||
log = { version = "0.4.17" }
|
||||
memchr = { version = "2.6.4" }
|
||||
memchr = "2.6.3"
|
||||
num-bigint = { version = "0.4.3" }
|
||||
num-traits = { version = "0.2.15" }
|
||||
once_cell = { version = "1.17.1" }
|
||||
path-absolutize = { version = "3.1.1" }
|
||||
proc-macro2 = { version = "1.0.69" }
|
||||
proc-macro2 = { version = "1.0.67" }
|
||||
quote = { version = "1.0.23" }
|
||||
regex = { version = "1.10.2" }
|
||||
regex = { version = "1.9.5" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
schemars = { version = "0.8.15" }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = { version = "1.0.107" }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
similar = { version = "2.3.0", features = ["inline"] }
|
||||
smallvec = { version = "1.11.1" }
|
||||
similar = { version = "2.2.1", features = ["inline"] }
|
||||
smallvec = { version = "1.10.0" }
|
||||
static_assertions = "1.1.0"
|
||||
strum = { version = "0.25.0", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.25.2" }
|
||||
syn = { version = "2.0.38" }
|
||||
syn = { version = "2.0.37" }
|
||||
test-case = { version = "3.2.1" }
|
||||
thiserror = { version = "1.0.49" }
|
||||
thiserror = { version = "1.0.48" }
|
||||
toml = { version = "0.7.8" }
|
||||
tracing = { version = "0.1.39" }
|
||||
tracing-indicatif = { version = "0.3.4" }
|
||||
tracing = "0.1.37"
|
||||
tracing-indicatif = "0.3.4"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
unicode-ident = { version = "1.0.12" }
|
||||
unicode_names2 = { version = "1.2.0" }
|
||||
unicode-width = { version = "0.1.11" }
|
||||
unicode-ident = "1.0.12"
|
||||
unicode-width = "0.1.11"
|
||||
uuid = { version = "1.4.1", features = ["v4", "fast-rng", "macro-diagnostics", "js"] }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
# v1.0.1
|
||||
libcst = { version = "0.1.0", default-features = false }
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
|
||||
18
README.md
18
README.md
@@ -27,10 +27,10 @@ An extremely fast Python linter, written in Rust.
|
||||
- ⚡️ 10-100x faster than existing linters
|
||||
- 🐍 Installable via `pip`
|
||||
- 🛠️ `pyproject.toml` support
|
||||
- 🤝 Python 3.12 compatibility
|
||||
- 🤝 Python 3.11 compatibility
|
||||
- 📦 Built-in caching, to avoid re-analyzing unchanged files
|
||||
- 🔧 Fix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
- 📏 Over [700 built-in rules](https://docs.astral.sh/ruff/rules/)
|
||||
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
- 📏 Over [600 built-in rules](https://docs.astral.sh/ruff/rules/)
|
||||
- ⚖️ [Near-parity](https://docs.astral.sh/ruff/faq/#how-does-ruff-compare-to-flake8) with the
|
||||
built-in Flake8 rule set
|
||||
- 🔌 Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
|
||||
@@ -140,7 +140,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.1.0
|
||||
rev: v0.0.290
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -172,11 +172,11 @@ If left unspecified, the default configuration is equivalent to:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
||||
select = ["E4", "E7", "E9", "F"]
|
||||
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
|
||||
select = ["E", "F"]
|
||||
ignore = []
|
||||
|
||||
# Allow fix for all enabled rules (when `--fix`) is provided.
|
||||
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
||||
fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"]
|
||||
unfixable = []
|
||||
|
||||
@@ -233,13 +233,13 @@ linting command.
|
||||
|
||||
<!-- Begin section: Rules -->
|
||||
|
||||
**Ruff supports over 700 lint rules**, many of which are inspired by popular tools like Flake8,
|
||||
**Ruff supports over 600 lint rules**, many of which are inspired by popular tools like Flake8,
|
||||
isort, pyupgrade, and others. Regardless of the rule's origin, Ruff re-implements every rule in
|
||||
Rust as a first-party feature.
|
||||
|
||||
By default, Ruff enables Flake8's `E` and `F` rules. Ruff supports all rules from the `F` category,
|
||||
and a [subset](https://docs.astral.sh/ruff/rules/#error-e) of the `E` category, omitting those
|
||||
stylistic rules made obsolete by the use of a formatter, like
|
||||
stylistic rules made obsolete by the use of an autoformatter, like
|
||||
[Black](https://github.com/psf/black).
|
||||
|
||||
If you're just getting started with Ruff, **the default rule set is a great place to start**: it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.1.0"
|
||||
version = "0.0.290"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
@@ -23,7 +23,7 @@ configparser = { version = "3.0.2" }
|
||||
itertools = { workspace = true }
|
||||
log = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
pep440_rs = { version = "0.3.12", features = ["serde"] }
|
||||
pep440_rs = { version = "0.3.1", features = ["serde"] }
|
||||
regex = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
|
||||
@@ -13,12 +13,11 @@ use ruff_linter::rules::flake8_quotes::settings::Quote;
|
||||
use ruff_linter::rules::flake8_tidy_imports::settings::Strictness;
|
||||
use ruff_linter::rules::pydocstyle::settings::Convention;
|
||||
use ruff_linter::settings::types::PythonVersion;
|
||||
use ruff_linter::settings::DEFAULT_SELECTORS;
|
||||
use ruff_linter::warn_user;
|
||||
use ruff_workspace::options::{
|
||||
Flake8AnnotationsOptions, Flake8BugbearOptions, Flake8BuiltinsOptions, Flake8ErrMsgOptions,
|
||||
Flake8PytestStyleOptions, Flake8QuotesOptions, Flake8TidyImportsOptions, LintOptions,
|
||||
McCabeOptions, Options, Pep8NamingOptions, PydocstyleOptions,
|
||||
Flake8PytestStyleOptions, Flake8QuotesOptions, Flake8TidyImportsOptions, McCabeOptions,
|
||||
Options, Pep8NamingOptions, PydocstyleOptions,
|
||||
};
|
||||
use ruff_workspace::pyproject::Pyproject;
|
||||
|
||||
@@ -26,6 +25,11 @@ use super::external_config::ExternalConfig;
|
||||
use super::plugin::Plugin;
|
||||
use super::{parser, plugin};
|
||||
|
||||
const DEFAULT_SELECTORS: &[RuleSelector] = &[
|
||||
RuleSelector::Linter(Linter::Pyflakes),
|
||||
RuleSelector::Linter(Linter::Pycodestyle),
|
||||
];
|
||||
|
||||
pub(crate) fn convert(
|
||||
config: &HashMap<String, HashMap<String, Option<String>>>,
|
||||
external_config: &ExternalConfig,
|
||||
@@ -99,7 +103,6 @@ pub(crate) fn convert(
|
||||
|
||||
// Parse each supported option.
|
||||
let mut options = Options::default();
|
||||
let mut lint_options = LintOptions::default();
|
||||
let mut flake8_annotations = Flake8AnnotationsOptions::default();
|
||||
let mut flake8_bugbear = Flake8BugbearOptions::default();
|
||||
let mut flake8_builtins = Flake8BuiltinsOptions::default();
|
||||
@@ -147,7 +150,7 @@ pub(crate) fn convert(
|
||||
"per-file-ignores" | "per_file_ignores" => {
|
||||
match parser::parse_files_to_codes_mapping(value.as_ref()) {
|
||||
Ok(per_file_ignores) => {
|
||||
lint_options.per_file_ignores =
|
||||
options.per_file_ignores =
|
||||
Some(parser::collect_per_file_ignores(per_file_ignores));
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -355,47 +358,47 @@ pub(crate) fn convert(
|
||||
}
|
||||
|
||||
// Deduplicate and sort.
|
||||
lint_options.select = Some(
|
||||
options.select = Some(
|
||||
select
|
||||
.into_iter()
|
||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||
.collect(),
|
||||
);
|
||||
lint_options.ignore = Some(
|
||||
options.ignore = Some(
|
||||
ignore
|
||||
.into_iter()
|
||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||
.collect(),
|
||||
);
|
||||
if flake8_annotations != Flake8AnnotationsOptions::default() {
|
||||
lint_options.flake8_annotations = Some(flake8_annotations);
|
||||
options.flake8_annotations = Some(flake8_annotations);
|
||||
}
|
||||
if flake8_bugbear != Flake8BugbearOptions::default() {
|
||||
lint_options.flake8_bugbear = Some(flake8_bugbear);
|
||||
options.flake8_bugbear = Some(flake8_bugbear);
|
||||
}
|
||||
if flake8_builtins != Flake8BuiltinsOptions::default() {
|
||||
lint_options.flake8_builtins = Some(flake8_builtins);
|
||||
options.flake8_builtins = Some(flake8_builtins);
|
||||
}
|
||||
if flake8_errmsg != Flake8ErrMsgOptions::default() {
|
||||
lint_options.flake8_errmsg = Some(flake8_errmsg);
|
||||
options.flake8_errmsg = Some(flake8_errmsg);
|
||||
}
|
||||
if flake8_pytest_style != Flake8PytestStyleOptions::default() {
|
||||
lint_options.flake8_pytest_style = Some(flake8_pytest_style);
|
||||
options.flake8_pytest_style = Some(flake8_pytest_style);
|
||||
}
|
||||
if flake8_quotes != Flake8QuotesOptions::default() {
|
||||
lint_options.flake8_quotes = Some(flake8_quotes);
|
||||
options.flake8_quotes = Some(flake8_quotes);
|
||||
}
|
||||
if flake8_tidy_imports != Flake8TidyImportsOptions::default() {
|
||||
lint_options.flake8_tidy_imports = Some(flake8_tidy_imports);
|
||||
options.flake8_tidy_imports = Some(flake8_tidy_imports);
|
||||
}
|
||||
if mccabe != McCabeOptions::default() {
|
||||
lint_options.mccabe = Some(mccabe);
|
||||
options.mccabe = Some(mccabe);
|
||||
}
|
||||
if pep8_naming != Pep8NamingOptions::default() {
|
||||
lint_options.pep8_naming = Some(pep8_naming);
|
||||
options.pep8_naming = Some(pep8_naming);
|
||||
}
|
||||
if pydocstyle != PydocstyleOptions::default() {
|
||||
lint_options.pydocstyle = Some(pydocstyle);
|
||||
options.pydocstyle = Some(pydocstyle);
|
||||
}
|
||||
|
||||
// Extract any settings from the existing `pyproject.toml`.
|
||||
@@ -433,10 +436,6 @@ pub(crate) fn convert(
|
||||
}
|
||||
}
|
||||
|
||||
if lint_options != LintOptions::default() {
|
||||
options.lint = Some(lint_options);
|
||||
}
|
||||
|
||||
// Create the pyproject.toml.
|
||||
Pyproject::new(options)
|
||||
}
|
||||
@@ -465,7 +464,7 @@ mod tests {
|
||||
use ruff_linter::rules::flake8_quotes;
|
||||
use ruff_linter::rules::pydocstyle::settings::Convention;
|
||||
use ruff_linter::settings::types::PythonVersion;
|
||||
use ruff_workspace::options::{Flake8QuotesOptions, LintOptions, Options, PydocstyleOptions};
|
||||
use ruff_workspace::options::{Flake8QuotesOptions, Options, PydocstyleOptions};
|
||||
use ruff_workspace::pyproject::Pyproject;
|
||||
|
||||
use crate::converter::DEFAULT_SELECTORS;
|
||||
@@ -475,8 +474,8 @@ mod tests {
|
||||
use super::super::plugin::Plugin;
|
||||
use super::convert;
|
||||
|
||||
fn lint_default_options(plugins: impl IntoIterator<Item = RuleSelector>) -> LintOptions {
|
||||
LintOptions {
|
||||
fn default_options(plugins: impl IntoIterator<Item = RuleSelector>) -> Options {
|
||||
Options {
|
||||
ignore: Some(vec![]),
|
||||
select: Some(
|
||||
DEFAULT_SELECTORS
|
||||
@@ -486,7 +485,7 @@ mod tests {
|
||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||
.collect(),
|
||||
),
|
||||
..LintOptions::default()
|
||||
..Options::default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,10 +496,7 @@ mod tests {
|
||||
&ExternalConfig::default(),
|
||||
None,
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
lint: Some(lint_default_options([])),
|
||||
..Options::default()
|
||||
});
|
||||
let expected = Pyproject::new(default_options([]));
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
@@ -516,8 +512,7 @@ mod tests {
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: Some(LineLength::try_from(100).unwrap()),
|
||||
lint: Some(lint_default_options([])),
|
||||
..Options::default()
|
||||
..default_options([])
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@@ -534,8 +529,7 @@ mod tests {
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: Some(LineLength::try_from(100).unwrap()),
|
||||
lint: Some(lint_default_options([])),
|
||||
..Options::default()
|
||||
..default_options([])
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@@ -550,10 +544,7 @@ mod tests {
|
||||
&ExternalConfig::default(),
|
||||
Some(vec![]),
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
lint: Some(lint_default_options([])),
|
||||
..Options::default()
|
||||
});
|
||||
let expected = Pyproject::new(default_options([]));
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
@@ -568,16 +559,13 @@ mod tests {
|
||||
Some(vec![]),
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
lint: Some(LintOptions {
|
||||
flake8_quotes: Some(Flake8QuotesOptions {
|
||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||
multiline_quotes: None,
|
||||
docstring_quotes: None,
|
||||
avoid_escape: None,
|
||||
}),
|
||||
..lint_default_options([])
|
||||
flake8_quotes: Some(Flake8QuotesOptions {
|
||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||
multiline_quotes: None,
|
||||
docstring_quotes: None,
|
||||
avoid_escape: None,
|
||||
}),
|
||||
..Options::default()
|
||||
..default_options([])
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@@ -596,15 +584,12 @@ mod tests {
|
||||
Some(vec![Plugin::Flake8Docstrings]),
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
lint: Some(LintOptions {
|
||||
pydocstyle: Some(PydocstyleOptions {
|
||||
convention: Some(Convention::Numpy),
|
||||
ignore_decorators: None,
|
||||
property_decorators: None,
|
||||
}),
|
||||
..lint_default_options([Linter::Pydocstyle.into()])
|
||||
pydocstyle: Some(PydocstyleOptions {
|
||||
convention: Some(Convention::Numpy),
|
||||
ignore_decorators: None,
|
||||
property_decorators: None,
|
||||
}),
|
||||
..Options::default()
|
||||
..default_options([Linter::Pydocstyle.into()])
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@@ -620,16 +605,13 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
lint: Some(LintOptions {
|
||||
flake8_quotes: Some(Flake8QuotesOptions {
|
||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||
multiline_quotes: None,
|
||||
docstring_quotes: None,
|
||||
avoid_escape: None,
|
||||
}),
|
||||
..lint_default_options([Linter::Flake8Quotes.into()])
|
||||
flake8_quotes: Some(Flake8QuotesOptions {
|
||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||
multiline_quotes: None,
|
||||
docstring_quotes: None,
|
||||
avoid_escape: None,
|
||||
}),
|
||||
..Options::default()
|
||||
..default_options([Linter::Flake8Quotes.into()])
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@@ -648,8 +630,7 @@ mod tests {
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
target_version: Some(PythonVersion::Py38),
|
||||
lint: Some(lint_default_options([])),
|
||||
..Options::default()
|
||||
..default_options([])
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use ruff_linter::registry::Linter;
|
||||
use ruff_linter::rule_selector::PreviewOptions;
|
||||
use ruff_linter::settings::types::PreviewMode;
|
||||
use ruff_linter::RuleSelector;
|
||||
|
||||
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
@@ -332,7 +332,7 @@ pub(crate) fn infer_plugins_from_codes(selectors: &HashSet<RuleSelector>) -> Vec
|
||||
.filter(|plugin| {
|
||||
for selector in selectors {
|
||||
if selector
|
||||
.rules(&PreviewOptions::default())
|
||||
.rules(PreviewMode::Disabled)
|
||||
.any(|rule| Linter::from(plugin).rules().any(|r| r == rule))
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -35,7 +35,7 @@ once_cell.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
url = "2.3.1"
|
||||
ureq = "2.8.0"
|
||||
ureq = "2.6.2"
|
||||
criterion = { version = "0.5.1", default-features = false }
|
||||
codspeed-criterion-compat = { version="2.2.0", default-features = false, optional = true}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use ruff_benchmark::criterion::{
|
||||
criterion_group, criterion_main, BenchmarkId, Criterion, Throughput,
|
||||
};
|
||||
use ruff_benchmark::{TestCase, TestFile, TestFileDownloadError};
|
||||
use ruff_python_formatter::{format_module_ast, PyFormatOptions};
|
||||
use ruff_python_formatter::{format_node, PyFormatOptions};
|
||||
use ruff_python_index::CommentRangesBuilder;
|
||||
use ruff_python_parser::lexer::lex;
|
||||
use ruff_python_parser::{parse_tokens, Mode};
|
||||
@@ -65,14 +65,13 @@ fn benchmark_formatter(criterion: &mut Criterion) {
|
||||
let comment_ranges = comment_ranges.finish();
|
||||
|
||||
// Parse the AST.
|
||||
let module = parse_tokens(tokens, case.code(), Mode::Module, "<filename>")
|
||||
let python_ast = parse_tokens(tokens, Mode::Module, "<filename>")
|
||||
.expect("Input to be a valid python program");
|
||||
|
||||
b.iter(|| {
|
||||
let options = PyFormatOptions::from_extension(Path::new(case.name()));
|
||||
let formatted =
|
||||
format_module_ast(&module, &comment_ranges, case.code(), options)
|
||||
.expect("Formatting to succeed");
|
||||
let formatted = format_node(&python_ast, &comment_ranges, case.code(), options)
|
||||
.expect("Formatting to succeed");
|
||||
|
||||
formatted.print().expect("Printing to succeed")
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.1.0"
|
||||
version = "0.0.290"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -48,7 +48,7 @@ itoa = { version = "1.0.6" }
|
||||
log = { workspace = true }
|
||||
notify = { version = "6.1.1" }
|
||||
path-absolutize = { workspace = true, features = ["once_cell_cache"] }
|
||||
rayon = { version = "1.8.0" }
|
||||
rayon = { version = "1.7.0" }
|
||||
regex = { workspace = true }
|
||||
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
|
||||
rustc-hash = { workspace = true }
|
||||
@@ -70,7 +70,7 @@ insta = { workspace = true, features = ["filters"] }
|
||||
insta-cmd = { version = "0.4.0" }
|
||||
tempfile = "3.6.0"
|
||||
test-case = { workspace = true }
|
||||
ureq = { version = "2.8.0", features = [] }
|
||||
ureq = { version = "2.6.2", features = [] }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
mimalloc = "0.1.39"
|
||||
|
||||
3
crates/ruff_cli/resources/test/fixtures/cache_mutable/source.py
vendored
Normal file
3
crates/ruff_cli/resources/test/fixtures/cache_mutable/source.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
a = 1
|
||||
|
||||
__all__ = list(["a", "b"])
|
||||
@@ -9,7 +9,6 @@ use ruff_linter::logging::LogLevel;
|
||||
use ruff_linter::registry::Rule;
|
||||
use ruff_linter::settings::types::{
|
||||
FilePattern, PatternPrefixPair, PerFileIgnore, PreviewMode, PythonVersion, SerializationFormat,
|
||||
UnsafeFixes,
|
||||
};
|
||||
use ruff_linter::{RuleParser, RuleSelector, RuleSelectorParser};
|
||||
use ruff_workspace::configuration::{Configuration, RuleSelection};
|
||||
@@ -77,25 +76,19 @@ pub enum Command {
|
||||
pub struct CheckCommand {
|
||||
/// List of files or directories to check.
|
||||
pub files: Vec<PathBuf>,
|
||||
/// Apply fixes to resolve lint violations.
|
||||
/// Use `--no-fix` to disable or `--unsafe-fixes` to include unsafe fixes.
|
||||
/// Attempt to automatically fix lint violations.
|
||||
/// Use `--no-fix` to disable.
|
||||
#[arg(long, overrides_with("no_fix"))]
|
||||
fix: bool,
|
||||
#[clap(long, overrides_with("fix"), hide = true)]
|
||||
no_fix: bool,
|
||||
/// Include fixes that may not retain the original intent of the code.
|
||||
/// Use `--no-unsafe-fixes` to disable.
|
||||
#[arg(long, overrides_with("no_unsafe_fixes"))]
|
||||
unsafe_fixes: bool,
|
||||
#[arg(long, overrides_with("unsafe_fixes"), hide = true)]
|
||||
no_unsafe_fixes: bool,
|
||||
/// Show violations with source code.
|
||||
/// Use `--no-show-source` to disable.
|
||||
#[arg(long, overrides_with("no_show_source"))]
|
||||
show_source: bool,
|
||||
#[clap(long, overrides_with("show_source"), hide = true)]
|
||||
no_show_source: bool,
|
||||
/// Show an enumeration of all fixed lint violations.
|
||||
/// Show an enumeration of all autofixed lint violations.
|
||||
/// Use `--no-show-fixes` to disable.
|
||||
#[arg(long, overrides_with("no_show_fixes"))]
|
||||
show_fixes: bool,
|
||||
@@ -107,8 +100,8 @@ pub struct CheckCommand {
|
||||
/// Run in watch mode by re-running whenever files change.
|
||||
#[arg(short, long)]
|
||||
pub watch: bool,
|
||||
/// Apply fixes to resolve lint violations, but don't report on leftover violations. Implies `--fix`.
|
||||
/// Use `--no-fix-only` to disable or `--unsafe-fixes` to include unsafe fixes.
|
||||
/// Fix any fixable lint violations, but don't report on leftover violations. Implies `--fix`.
|
||||
/// Use `--no-fix-only` to disable.
|
||||
#[arg(long, overrides_with("no_fix_only"))]
|
||||
fix_only: bool,
|
||||
#[clap(long, overrides_with("fix_only"), hide = true)]
|
||||
@@ -117,6 +110,16 @@ pub struct CheckCommand {
|
||||
#[arg(long)]
|
||||
ignore_noqa: bool,
|
||||
|
||||
/// Output serialization format for violations. (Deprecated: Use `--output-format` instead).
|
||||
#[arg(
|
||||
long,
|
||||
value_enum,
|
||||
env = "RUFF_FORMAT",
|
||||
conflicts_with = "output_format",
|
||||
hide = true
|
||||
)]
|
||||
pub format: Option<SerializationFormat>,
|
||||
|
||||
/// Output serialization format for violations.
|
||||
#[arg(long, value_enum, env = "RUFF_OUTPUT_FORMAT")]
|
||||
pub output_format: Option<SerializationFormat>,
|
||||
@@ -199,7 +202,7 @@ pub struct CheckCommand {
|
||||
help_heading = "File selection"
|
||||
)]
|
||||
pub extend_exclude: Option<Vec<FilePattern>>,
|
||||
/// List of rule codes to treat as eligible for fix. Only applicable when fix itself is enabled (e.g., via `--fix`).
|
||||
/// List of rule codes to treat as eligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(
|
||||
long,
|
||||
value_delimiter = ',',
|
||||
@@ -209,7 +212,7 @@ pub struct CheckCommand {
|
||||
hide_possible_values = true
|
||||
)]
|
||||
pub fixable: Option<Vec<RuleSelector>>,
|
||||
/// List of rule codes to treat as ineligible for fix. Only applicable when fix itself is enabled (e.g., via `--fix`).
|
||||
/// List of rule codes to treat as ineligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(
|
||||
long,
|
||||
value_delimiter = ',',
|
||||
@@ -285,7 +288,7 @@ pub struct CheckCommand {
|
||||
conflicts_with = "exit_non_zero_on_fix"
|
||||
)]
|
||||
pub exit_zero: bool,
|
||||
/// Exit with a non-zero status code if any files were modified via fix, even if no lint violations remain.
|
||||
/// Exit with a non-zero status code if any files were modified via autofix, even if no lint violations remain.
|
||||
#[arg(long, help_heading = "Miscellaneous", conflicts_with = "exit_zero")]
|
||||
pub exit_non_zero_on_fix: bool,
|
||||
/// Show counts for every rule with at least one violation.
|
||||
@@ -494,10 +497,8 @@ impl CheckCommand {
|
||||
cache_dir: self.cache_dir,
|
||||
fix: resolve_bool_arg(self.fix, self.no_fix),
|
||||
fix_only: resolve_bool_arg(self.fix_only, self.no_fix_only),
|
||||
unsafe_fixes: resolve_bool_arg(self.unsafe_fixes, self.no_unsafe_fixes)
|
||||
.map(UnsafeFixes::from),
|
||||
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
|
||||
output_format: self.output_format,
|
||||
output_format: self.output_format.or(self.format),
|
||||
show_fixes: resolve_bool_arg(self.show_fixes, self.no_show_fixes),
|
||||
},
|
||||
)
|
||||
@@ -598,7 +599,6 @@ pub struct CliOverrides {
|
||||
pub cache_dir: Option<PathBuf>,
|
||||
pub fix: Option<bool>,
|
||||
pub fix_only: Option<bool>,
|
||||
pub unsafe_fixes: Option<UnsafeFixes>,
|
||||
pub force_exclude: Option<bool>,
|
||||
pub output_format: Option<SerializationFormat>,
|
||||
pub show_fixes: Option<bool>,
|
||||
@@ -610,7 +610,7 @@ impl ConfigurationTransformer for CliOverrides {
|
||||
config.cache_dir = Some(cache_dir.clone());
|
||||
}
|
||||
if let Some(dummy_variable_rgx) = &self.dummy_variable_rgx {
|
||||
config.lint.dummy_variable_rgx = Some(dummy_variable_rgx.clone());
|
||||
config.dummy_variable_rgx = Some(dummy_variable_rgx.clone());
|
||||
}
|
||||
if let Some(exclude) = &self.exclude {
|
||||
config.exclude = Some(exclude.clone());
|
||||
@@ -624,10 +624,7 @@ impl ConfigurationTransformer for CliOverrides {
|
||||
if let Some(fix_only) = &self.fix_only {
|
||||
config.fix_only = Some(*fix_only);
|
||||
}
|
||||
if self.unsafe_fixes.is_some() {
|
||||
config.unsafe_fixes = self.unsafe_fixes;
|
||||
}
|
||||
config.lint.rule_selections.push(RuleSelection {
|
||||
config.rule_selections.push(RuleSelection {
|
||||
select: self.select.clone(),
|
||||
ignore: self
|
||||
.ignore
|
||||
@@ -660,7 +657,7 @@ impl ConfigurationTransformer for CliOverrides {
|
||||
config.preview = Some(*preview);
|
||||
}
|
||||
if let Some(per_file_ignores) = &self.per_file_ignores {
|
||||
config.lint.per_file_ignores = Some(collect_per_file_ignores(per_file_ignores.clone()));
|
||||
config.per_file_ignores = Some(collect_per_file_ignores(per_file_ignores.clone()));
|
||||
}
|
||||
if let Some(respect_gitignore) = &self.respect_gitignore {
|
||||
config.respect_gitignore = Some(*respect_gitignore);
|
||||
|
||||
@@ -338,7 +338,6 @@ pub(crate) fn init(path: &Path) -> Result<()> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use filetime::{set_file_mtime, FileTime};
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
use std::env::temp_dir;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
@@ -411,7 +410,6 @@ mod tests {
|
||||
Some(&cache),
|
||||
flags::Noqa::Enabled,
|
||||
flags::FixMode::Generate,
|
||||
UnsafeFixes::Enabled,
|
||||
)
|
||||
.unwrap();
|
||||
if diagnostics
|
||||
@@ -457,7 +455,6 @@ mod tests {
|
||||
Some(&cache),
|
||||
flags::Noqa::Enabled,
|
||||
flags::FixMode::Generate,
|
||||
UnsafeFixes::Enabled,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
@@ -715,7 +712,6 @@ mod tests {
|
||||
Some(cache),
|
||||
flags::Noqa::Enabled,
|
||||
flags::FixMode::Generate,
|
||||
UnsafeFixes::Enabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ use log::{debug, error};
|
||||
use rayon::prelude::*;
|
||||
|
||||
use ruff_linter::linter::add_noqa_to_path;
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_linter::warn_user_once;
|
||||
use ruff_python_ast::{PySourceType, SourceType};
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig};
|
||||
|
||||
use crate::args::CliOverrides;
|
||||
use crate::diagnostics::LintSource;
|
||||
|
||||
/// Add `noqa` directives to a collection of files.
|
||||
pub(crate) fn add_noqa(
|
||||
@@ -57,8 +57,8 @@ pub(crate) fn add_noqa(
|
||||
.and_then(|parent| package_roots.get(parent))
|
||||
.and_then(|package| *package);
|
||||
let settings = resolver.resolve(path, pyproject_config);
|
||||
let source_kind = match SourceKind::from_path(path, source_type) {
|
||||
Ok(Some(source_kind)) => source_kind,
|
||||
let LintSource(source_kind) = match LintSource::try_from_path(path, source_type) {
|
||||
Ok(Some(source)) => source,
|
||||
Ok(None) => return None,
|
||||
Err(e) => {
|
||||
error!("Failed to extract source from {}: {e}", path.display());
|
||||
|
||||
@@ -11,7 +11,6 @@ use itertools::Itertools;
|
||||
use log::{debug, error, warn};
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use rayon::prelude::*;
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
@@ -36,8 +35,7 @@ pub(crate) fn check(
|
||||
overrides: &CliOverrides,
|
||||
cache: flags::Cache,
|
||||
noqa: flags::Noqa,
|
||||
fix_mode: flags::FixMode,
|
||||
unsafe_fixes: UnsafeFixes,
|
||||
autofix: flags::FixMode,
|
||||
) -> Result<Diagnostics> {
|
||||
// Collect all the Python files to check.
|
||||
let start = Instant::now();
|
||||
@@ -121,16 +119,7 @@ pub(crate) fn check(
|
||||
}
|
||||
});
|
||||
|
||||
lint_path(
|
||||
path,
|
||||
package,
|
||||
&settings.linter,
|
||||
cache,
|
||||
noqa,
|
||||
fix_mode,
|
||||
unsafe_fixes,
|
||||
)
|
||||
.map_err(|e| {
|
||||
lint_path(path, package, &settings.linter, cache, noqa, autofix).map_err(|e| {
|
||||
(Some(path.to_owned()), {
|
||||
let mut error = e.to_string();
|
||||
for cause in e.chain() {
|
||||
@@ -209,11 +198,10 @@ fn lint_path(
|
||||
settings: &LinterSettings,
|
||||
cache: Option<&Cache>,
|
||||
noqa: flags::Noqa,
|
||||
fix_mode: flags::FixMode,
|
||||
unsafe_fixes: UnsafeFixes,
|
||||
autofix: flags::FixMode,
|
||||
) -> Result<Diagnostics> {
|
||||
let result = catch_unwind(|| {
|
||||
crate::diagnostics::lint_path(path, package, settings, cache, noqa, fix_mode, unsafe_fixes)
|
||||
crate::diagnostics::lint_path(path, package, settings, cache, noqa, autofix)
|
||||
});
|
||||
|
||||
match result {
|
||||
@@ -245,8 +233,6 @@ mod test {
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
use rustc_hash::FxHashMap;
|
||||
use tempfile::TempDir;
|
||||
|
||||
@@ -299,7 +285,6 @@ mod test {
|
||||
flags::Cache::Disabled,
|
||||
flags::Noqa::Disabled,
|
||||
flags::FixMode::Generate,
|
||||
UnsafeFixes::Enabled,
|
||||
)
|
||||
.unwrap();
|
||||
let mut output = Vec::new();
|
||||
|
||||
@@ -16,7 +16,7 @@ pub(crate) fn check_stdin(
|
||||
pyproject_config: &PyprojectConfig,
|
||||
overrides: &CliOverrides,
|
||||
noqa: flags::Noqa,
|
||||
fix_mode: flags::FixMode,
|
||||
autofix: flags::FixMode,
|
||||
) -> Result<Diagnostics> {
|
||||
if let Some(filename) = filename {
|
||||
if !python_file_at_path(filename, pyproject_config, overrides)? {
|
||||
@@ -33,7 +33,7 @@ pub(crate) fn check_stdin(
|
||||
stdin,
|
||||
&pyproject_config.settings,
|
||||
noqa,
|
||||
fix_mode,
|
||||
autofix,
|
||||
)?;
|
||||
diagnostics.messages.sort_unstable();
|
||||
Ok(diagnostics)
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
use ruff_workspace::options::Options;
|
||||
use ruff_workspace::options_base::OptionsMetadata;
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
pub(crate) fn config(key: Option<&str>) -> Result<()> {
|
||||
match key {
|
||||
None => print!("{}", Options::metadata()),
|
||||
Some(key) => match Options::metadata().find(key) {
|
||||
Some(key) => match Options::metadata().get(key) {
|
||||
None => {
|
||||
return Err(anyhow!("Unknown option: {key}"));
|
||||
}
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::Result;
|
||||
use colored::Colorize;
|
||||
use itertools::Itertools;
|
||||
use log::error;
|
||||
use rayon::iter::Either::{Left, Right};
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
use thiserror::Error;
|
||||
use tracing::debug;
|
||||
|
||||
use ruff_diagnostics::SourceMap;
|
||||
use ruff_linter::fs;
|
||||
use ruff_linter::logging::LogLevel;
|
||||
use ruff_linter::source_kind::{SourceError, SourceKind};
|
||||
use ruff_linter::warn_user_once;
|
||||
use ruff_python_ast::{PySourceType, SourceType};
|
||||
use ruff_python_formatter::{format_module_source, FormatModuleError};
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use ruff_python_formatter::{format_module, FormatModuleError, PyFormatOptions};
|
||||
use ruff_source_file::{find_newline, LineEnding};
|
||||
use ruff_workspace::resolver::python_files_in_path;
|
||||
use ruff_workspace::FormatterSettings;
|
||||
|
||||
use crate::args::{CliOverrides, FormatArguments};
|
||||
use crate::panic::{catch_unwind, PanicError};
|
||||
@@ -66,23 +62,26 @@ pub(crate) fn format(
|
||||
.filter_map(|entry| {
|
||||
match entry {
|
||||
Ok(entry) => {
|
||||
let path = entry.into_path();
|
||||
let path = entry.path();
|
||||
|
||||
let SourceType::Python(source_type) = SourceType::from(&path) else {
|
||||
let SourceType::Python(
|
||||
source_type @ (PySourceType::Python | PySourceType::Stub),
|
||||
) = SourceType::from(path)
|
||||
else {
|
||||
// Ignore any non-Python files.
|
||||
return None;
|
||||
};
|
||||
|
||||
let resolved_settings = resolver.resolve(&path, &pyproject_config);
|
||||
let resolved_settings = resolver.resolve(path, &pyproject_config);
|
||||
let options = resolved_settings.formatter.to_format_options(source_type);
|
||||
debug!("Formatting {} with {:?}", path.display(), options);
|
||||
|
||||
Some(
|
||||
match catch_unwind(|| {
|
||||
format_path(&path, &resolved_settings.formatter, source_type, mode)
|
||||
}) {
|
||||
Ok(inner) => inner.map(|result| FormatPathResult { path, result }),
|
||||
Err(error) => Err(FormatCommandError::Panic(Some(path), error)),
|
||||
},
|
||||
)
|
||||
Some(match catch_unwind(|| format_path(path, options, mode)) {
|
||||
Ok(inner) => inner,
|
||||
Err(error) => {
|
||||
Err(FormatCommandError::Panic(Some(path.to_path_buf()), error))
|
||||
}
|
||||
})
|
||||
}
|
||||
Err(err) => Some(Err(FormatCommandError::Ignore(err))),
|
||||
}
|
||||
@@ -104,7 +103,7 @@ pub(crate) fn format(
|
||||
error!("{error}");
|
||||
}
|
||||
|
||||
let summary = FormatSummary::new(results.as_slice(), mode);
|
||||
let summary = FormatResultSummary::new(results, mode);
|
||||
|
||||
// Report on the formatting changes.
|
||||
if log_level >= LogLevel::Default {
|
||||
@@ -124,7 +123,7 @@ pub(crate) fn format(
|
||||
}
|
||||
FormatMode::Check => {
|
||||
if errors.is_empty() {
|
||||
if summary.any_formatted() {
|
||||
if summary.formatted > 0 {
|
||||
Ok(ExitStatus::Failure)
|
||||
} else {
|
||||
Ok(ExitStatus::Success)
|
||||
@@ -140,247 +139,102 @@ pub(crate) fn format(
|
||||
#[tracing::instrument(skip_all, fields(path = %path.display()))]
|
||||
fn format_path(
|
||||
path: &Path,
|
||||
settings: &FormatterSettings,
|
||||
source_type: PySourceType,
|
||||
options: PyFormatOptions,
|
||||
mode: FormatMode,
|
||||
) -> Result<FormatResult, FormatCommandError> {
|
||||
// Extract the sources from the file.
|
||||
let source_kind = match SourceKind::from_path(path, source_type) {
|
||||
Ok(Some(source_kind)) => source_kind,
|
||||
Ok(None) => return Ok(FormatResult::Unchanged),
|
||||
Err(err) => {
|
||||
return Err(FormatCommandError::Read(Some(path.to_path_buf()), err));
|
||||
}
|
||||
) -> Result<FormatCommandResult, FormatCommandError> {
|
||||
let unformatted = std::fs::read_to_string(path)
|
||||
.map_err(|err| FormatCommandError::Read(Some(path.to_path_buf()), err))?;
|
||||
|
||||
let line_ending = match find_newline(&unformatted) {
|
||||
Some((_, LineEnding::Lf)) | None => ruff_formatter::printer::LineEnding::LineFeed,
|
||||
Some((_, LineEnding::Cr)) => ruff_formatter::printer::LineEnding::CarriageReturn,
|
||||
Some((_, LineEnding::CrLf)) => ruff_formatter::printer::LineEnding::CarriageReturnLineFeed,
|
||||
};
|
||||
|
||||
// Format the source.
|
||||
match format_source(source_kind, source_type, Some(path), settings)? {
|
||||
FormattedSource::Formatted(formatted) => {
|
||||
if mode.is_write() {
|
||||
let mut writer = File::create(path).map_err(|err| {
|
||||
FormatCommandError::Write(Some(path.to_path_buf()), err.into())
|
||||
})?;
|
||||
formatted
|
||||
.write(&mut writer)
|
||||
.map_err(|err| FormatCommandError::Write(Some(path.to_path_buf()), err))?;
|
||||
}
|
||||
Ok(FormatResult::Formatted)
|
||||
let options = options.with_line_ending(line_ending);
|
||||
|
||||
let formatted = format_module(&unformatted, options)
|
||||
.map_err(|err| FormatCommandError::FormatModule(Some(path.to_path_buf()), err))?;
|
||||
|
||||
let formatted = formatted.as_code();
|
||||
if formatted.len() == unformatted.len() && formatted == unformatted {
|
||||
Ok(FormatCommandResult::Unchanged)
|
||||
} else {
|
||||
if mode.is_write() {
|
||||
std::fs::write(path, formatted.as_bytes())
|
||||
.map_err(|err| FormatCommandError::Write(Some(path.to_path_buf()), err))?;
|
||||
}
|
||||
FormattedSource::Unchanged(_) => Ok(FormatResult::Unchanged),
|
||||
Ok(FormatCommandResult::Formatted)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum FormattedSource {
|
||||
/// The source was formatted, and the [`SourceKind`] contains the transformed source code.
|
||||
Formatted(SourceKind),
|
||||
/// The source was unchanged, and the [`SourceKind`] contains the original source code.
|
||||
Unchanged(SourceKind),
|
||||
}
|
||||
|
||||
impl From<FormattedSource> for FormatResult {
|
||||
fn from(value: FormattedSource) -> Self {
|
||||
match value {
|
||||
FormattedSource::Formatted(_) => FormatResult::Formatted,
|
||||
FormattedSource::Unchanged(_) => FormatResult::Unchanged,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FormattedSource {
|
||||
pub(crate) fn source_kind(&self) -> &SourceKind {
|
||||
match self {
|
||||
FormattedSource::Formatted(source_kind) => source_kind,
|
||||
FormattedSource::Unchanged(source_kind) => source_kind,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Format a [`SourceKind`], returning the transformed [`SourceKind`], or `None` if the source was
|
||||
/// unchanged.
|
||||
pub(crate) fn format_source(
|
||||
source_kind: SourceKind,
|
||||
source_type: PySourceType,
|
||||
path: Option<&Path>,
|
||||
settings: &FormatterSettings,
|
||||
) -> Result<FormattedSource, FormatCommandError> {
|
||||
match source_kind {
|
||||
SourceKind::Python(unformatted) => {
|
||||
let options = settings.to_format_options(source_type, &unformatted);
|
||||
|
||||
let formatted = format_module_source(&unformatted, options)
|
||||
.map_err(|err| FormatCommandError::Format(path.map(Path::to_path_buf), err))?;
|
||||
|
||||
let formatted = formatted.into_code();
|
||||
if formatted.len() == unformatted.len() && formatted == *unformatted {
|
||||
Ok(FormattedSource::Unchanged(SourceKind::Python(unformatted)))
|
||||
} else {
|
||||
Ok(FormattedSource::Formatted(SourceKind::Python(formatted)))
|
||||
}
|
||||
}
|
||||
SourceKind::IpyNotebook(notebook) => {
|
||||
if !notebook.is_python_notebook() {
|
||||
return Ok(FormattedSource::Unchanged(SourceKind::IpyNotebook(
|
||||
notebook,
|
||||
)));
|
||||
}
|
||||
|
||||
let options = settings.to_format_options(source_type, notebook.source_code());
|
||||
|
||||
let mut output: Option<String> = None;
|
||||
let mut last: Option<TextSize> = None;
|
||||
let mut source_map = SourceMap::default();
|
||||
|
||||
// Format each cell individually.
|
||||
for (start, end) in notebook.cell_offsets().iter().tuple_windows::<(_, _)>() {
|
||||
let range = TextRange::new(*start, *end);
|
||||
let unformatted = ¬ebook.source_code()[range];
|
||||
|
||||
// Format the cell.
|
||||
let formatted = format_module_source(unformatted, options.clone())
|
||||
.map_err(|err| FormatCommandError::Format(path.map(Path::to_path_buf), err))?;
|
||||
|
||||
// If the cell is unchanged, skip it.
|
||||
let formatted = formatted.as_code();
|
||||
if formatted.len() == unformatted.len() && formatted == unformatted {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is the first newly-formatted cell, initialize the output.
|
||||
let output = output
|
||||
.get_or_insert_with(|| String::with_capacity(notebook.source_code().len()));
|
||||
|
||||
// Add all contents from `last` to the current cell.
|
||||
let slice = ¬ebook.source_code()
|
||||
[TextRange::new(last.unwrap_or_default(), range.start())];
|
||||
output.push_str(slice);
|
||||
|
||||
// Add the start source marker for the cell.
|
||||
source_map.push_marker(*start, output.text_len());
|
||||
|
||||
// Add the cell itself.
|
||||
output.push_str(formatted);
|
||||
|
||||
// Add the end source marker for the added cell.
|
||||
source_map.push_marker(*end, output.text_len());
|
||||
|
||||
// Track that the cell was formatted.
|
||||
last = Some(*end);
|
||||
}
|
||||
|
||||
// If the file was unchanged, return `None`.
|
||||
let (Some(mut output), Some(last)) = (output, last) else {
|
||||
return Ok(FormattedSource::Unchanged(SourceKind::IpyNotebook(
|
||||
notebook,
|
||||
)));
|
||||
};
|
||||
|
||||
// Add the remaining content.
|
||||
let slice = ¬ebook.source_code()[usize::from(last)..];
|
||||
output.push_str(slice);
|
||||
|
||||
// Update the notebook.
|
||||
let mut notebook = notebook.clone();
|
||||
notebook.update(&source_map, output);
|
||||
|
||||
Ok(FormattedSource::Formatted(SourceKind::IpyNotebook(
|
||||
notebook,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of an individual formatting operation.
|
||||
#[derive(Debug, Clone, Copy, is_macro::Is)]
|
||||
pub(crate) enum FormatResult {
|
||||
pub(crate) enum FormatCommandResult {
|
||||
/// The file was formatted.
|
||||
Formatted,
|
||||
/// The file was unchanged, as the formatted contents matched the existing contents.
|
||||
Unchanged,
|
||||
}
|
||||
|
||||
/// The coupling of a [`FormatResult`] with the path of the file that was analyzed.
|
||||
#[derive(Debug)]
|
||||
struct FormatPathResult {
|
||||
path: PathBuf,
|
||||
result: FormatResult,
|
||||
}
|
||||
|
||||
/// A summary of the formatting results.
|
||||
#[derive(Debug)]
|
||||
struct FormatSummary<'a> {
|
||||
/// The individual formatting results.
|
||||
results: &'a [FormatPathResult],
|
||||
struct FormatResultSummary {
|
||||
/// The format mode that was used.
|
||||
mode: FormatMode,
|
||||
/// The number of files that were formatted.
|
||||
formatted: usize,
|
||||
/// The number of files that were unchanged.
|
||||
unchanged: usize,
|
||||
}
|
||||
|
||||
impl<'a> FormatSummary<'a> {
|
||||
fn new(results: &'a [FormatPathResult], mode: FormatMode) -> Self {
|
||||
Self { results, mode }
|
||||
}
|
||||
|
||||
/// Returns `true` if any of the files require formatting.
|
||||
fn any_formatted(&self) -> bool {
|
||||
self.results
|
||||
.iter()
|
||||
.any(|result| result.result.is_formatted())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FormatSummary<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
// Compute the number of changed and unchanged files.
|
||||
let mut formatted = 0u32;
|
||||
let mut unchanged = 0u32;
|
||||
for result in self.results {
|
||||
match result.result {
|
||||
FormatResult::Formatted => {
|
||||
// If we're running in check mode, report on any files that would be formatted.
|
||||
if self.mode.is_check() {
|
||||
writeln!(
|
||||
f,
|
||||
"Would reformat: {}",
|
||||
fs::relativize_path(&result.path).bold()
|
||||
)?;
|
||||
}
|
||||
formatted += 1;
|
||||
}
|
||||
FormatResult::Unchanged => unchanged += 1,
|
||||
impl FormatResultSummary {
|
||||
fn new(diagnostics: Vec<FormatCommandResult>, mode: FormatMode) -> Self {
|
||||
let mut summary = Self {
|
||||
mode,
|
||||
formatted: 0,
|
||||
unchanged: 0,
|
||||
};
|
||||
for diagnostic in diagnostics {
|
||||
match diagnostic {
|
||||
FormatCommandResult::Formatted => summary.formatted += 1,
|
||||
FormatCommandResult::Unchanged => summary.unchanged += 1,
|
||||
}
|
||||
}
|
||||
summary
|
||||
}
|
||||
}
|
||||
|
||||
// Write out a summary of the formatting results.
|
||||
if formatted > 0 && unchanged > 0 {
|
||||
impl Display for FormatResultSummary {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
if self.formatted > 0 && self.unchanged > 0 {
|
||||
write!(
|
||||
f,
|
||||
"{} file{} {}, {} file{} left unchanged",
|
||||
formatted,
|
||||
if formatted == 1 { "" } else { "s" },
|
||||
self.formatted,
|
||||
if self.formatted == 1 { "" } else { "s" },
|
||||
match self.mode {
|
||||
FormatMode::Write => "reformatted",
|
||||
FormatMode::Check => "would be reformatted",
|
||||
},
|
||||
unchanged,
|
||||
if unchanged == 1 { "" } else { "s" },
|
||||
self.unchanged,
|
||||
if self.unchanged == 1 { "" } else { "s" },
|
||||
)
|
||||
} else if formatted > 0 {
|
||||
} else if self.formatted > 0 {
|
||||
write!(
|
||||
f,
|
||||
"{} file{} {}",
|
||||
formatted,
|
||||
if formatted == 1 { "" } else { "s" },
|
||||
self.formatted,
|
||||
if self.formatted == 1 { "" } else { "s" },
|
||||
match self.mode {
|
||||
FormatMode::Write => "reformatted",
|
||||
FormatMode::Check => "would be reformatted",
|
||||
}
|
||||
)
|
||||
} else if unchanged > 0 {
|
||||
} else if self.unchanged > 0 {
|
||||
write!(
|
||||
f,
|
||||
"{} file{} left unchanged",
|
||||
unchanged,
|
||||
if unchanged == 1 { "" } else { "s" },
|
||||
self.unchanged,
|
||||
if self.unchanged == 1 { "" } else { "s" },
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
@@ -392,10 +246,10 @@ impl Display for FormatSummary<'_> {
|
||||
#[derive(Error, Debug)]
|
||||
pub(crate) enum FormatCommandError {
|
||||
Ignore(#[from] ignore::Error),
|
||||
Read(Option<PathBuf>, io::Error),
|
||||
Write(Option<PathBuf>, io::Error),
|
||||
FormatModule(Option<PathBuf>, FormatModuleError),
|
||||
Panic(Option<PathBuf>, PanicError),
|
||||
Read(Option<PathBuf>, SourceError),
|
||||
Format(Option<PathBuf>, FormatModuleError),
|
||||
Write(Option<PathBuf>, SourceError),
|
||||
}
|
||||
|
||||
impl Display for FormatCommandError {
|
||||
@@ -448,7 +302,7 @@ impl Display for FormatCommandError {
|
||||
write!(f, "{}{} {err}", "Failed to write".bold(), ":".bold())
|
||||
}
|
||||
}
|
||||
Self::Format(path, err) => {
|
||||
Self::FormatModule(path, err) => {
|
||||
if let Some(path) = path {
|
||||
write!(
|
||||
f,
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
use std::io::stdout;
|
||||
use std::io::{stdout, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use log::error;
|
||||
use log::warn;
|
||||
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_python_ast::{PySourceType, SourceType};
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_formatter::{format_module, PyFormatOptions};
|
||||
use ruff_workspace::resolver::python_file_at_path;
|
||||
use ruff_workspace::FormatterSettings;
|
||||
|
||||
use crate::args::{CliOverrides, FormatArguments};
|
||||
use crate::commands::format::{format_source, FormatCommandError, FormatMode, FormatResult};
|
||||
use crate::commands::format::{FormatCommandError, FormatCommandResult, FormatMode};
|
||||
use crate::resolve::resolve;
|
||||
use crate::stdin::read_from_stdin;
|
||||
use crate::ExitStatus;
|
||||
@@ -35,19 +34,15 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
|
||||
}
|
||||
}
|
||||
|
||||
// Format the file.
|
||||
let path = cli.stdin_filename.as_deref();
|
||||
|
||||
let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
|
||||
return Ok(ExitStatus::Success);
|
||||
};
|
||||
let options = pyproject_config
|
||||
.settings
|
||||
.formatter
|
||||
.to_format_options(path.map(PySourceType::from).unwrap_or_default());
|
||||
|
||||
// Format the file.
|
||||
match format_source_code(
|
||||
path,
|
||||
&pyproject_config.settings.formatter,
|
||||
source_type,
|
||||
mode,
|
||||
) {
|
||||
match format_source(path, options, mode) {
|
||||
Ok(result) => match mode {
|
||||
FormatMode::Write => Ok(ExitStatus::Success),
|
||||
FormatMode::Check => {
|
||||
@@ -59,42 +54,32 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
warn!("{err}");
|
||||
Ok(ExitStatus::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Format source code read from `stdin`.
|
||||
fn format_source_code(
|
||||
fn format_source(
|
||||
path: Option<&Path>,
|
||||
settings: &FormatterSettings,
|
||||
source_type: PySourceType,
|
||||
options: PyFormatOptions,
|
||||
mode: FormatMode,
|
||||
) -> Result<FormatResult, FormatCommandError> {
|
||||
// Read the source from stdin.
|
||||
let source_code = read_from_stdin()
|
||||
.map_err(|err| FormatCommandError::Read(path.map(Path::to_path_buf), err.into()))?;
|
||||
|
||||
let source_kind = match SourceKind::from_source_code(source_code, source_type) {
|
||||
Ok(Some(source_kind)) => source_kind,
|
||||
Ok(None) => return Ok(FormatResult::Unchanged),
|
||||
Err(err) => {
|
||||
return Err(FormatCommandError::Read(path.map(Path::to_path_buf), err));
|
||||
) -> Result<FormatCommandResult, FormatCommandError> {
|
||||
let unformatted = read_from_stdin()
|
||||
.map_err(|err| FormatCommandError::Read(path.map(Path::to_path_buf), err))?;
|
||||
let formatted = format_module(&unformatted, options)
|
||||
.map_err(|err| FormatCommandError::FormatModule(path.map(Path::to_path_buf), err))?;
|
||||
let formatted = formatted.as_code();
|
||||
if formatted.len() == unformatted.len() && formatted == unformatted {
|
||||
Ok(FormatCommandResult::Unchanged)
|
||||
} else {
|
||||
if mode.is_write() {
|
||||
stdout()
|
||||
.lock()
|
||||
.write_all(formatted.as_bytes())
|
||||
.map_err(|err| FormatCommandError::Write(path.map(Path::to_path_buf), err))?;
|
||||
}
|
||||
};
|
||||
|
||||
// Format the source.
|
||||
let formatted = format_source(source_kind, source_type, path, settings)?;
|
||||
|
||||
// Write to stdout regardless of whether the source was formatted.
|
||||
if mode.is_write() {
|
||||
let mut writer = stdout().lock();
|
||||
formatted
|
||||
.source_kind()
|
||||
.write(&mut writer)
|
||||
.map_err(|err| FormatCommandError::Write(path.map(Path::to_path_buf), err))?;
|
||||
Ok(FormatCommandResult::Formatted)
|
||||
}
|
||||
|
||||
Ok(FormatResult::from(formatted))
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde::ser::SerializeSeq;
|
||||
use serde::{Serialize, Serializer};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use ruff_diagnostics::FixAvailability;
|
||||
use ruff_diagnostics::AutofixKind;
|
||||
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
|
||||
|
||||
use crate::args::HelpFormat;
|
||||
@@ -17,7 +17,7 @@ struct Explanation<'a> {
|
||||
linter: &'a str,
|
||||
summary: &'a str,
|
||||
message_formats: &'a [&'a str],
|
||||
fix: String,
|
||||
autofix: String,
|
||||
explanation: Option<&'a str>,
|
||||
preview: bool,
|
||||
}
|
||||
@@ -26,16 +26,16 @@ impl<'a> Explanation<'a> {
|
||||
fn from_rule(rule: &'a Rule) -> Self {
|
||||
let code = rule.noqa_code().to_string();
|
||||
let (linter, _) = Linter::parse_code(&code).unwrap();
|
||||
let fix = rule.fixable().to_string();
|
||||
let autofix = rule.autofixable().to_string();
|
||||
Self {
|
||||
name: rule.as_ref(),
|
||||
code,
|
||||
linter: linter.name(),
|
||||
summary: rule.message_formats()[0],
|
||||
message_formats: rule.message_formats(),
|
||||
fix,
|
||||
autofix,
|
||||
explanation: rule.explanation(),
|
||||
preview: rule.is_preview() || rule.is_nursery(),
|
||||
preview: rule.is_preview(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,17 +51,14 @@ fn format_rule_text(rule: Rule) -> String {
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
|
||||
let fix_availability = rule.fixable();
|
||||
if matches!(
|
||||
fix_availability,
|
||||
FixAvailability::Always | FixAvailability::Sometimes
|
||||
) {
|
||||
output.push_str(&fix_availability.to_string());
|
||||
let autofix = rule.autofixable();
|
||||
if matches!(autofix, AutofixKind::Always | AutofixKind::Sometimes) {
|
||||
output.push_str(&autofix.to_string());
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
if rule.is_preview() || rule.is_nursery() {
|
||||
if rule.is_preview() {
|
||||
output.push_str(
|
||||
r#"This rule is in preview and is not stable. The `--preview` flag is required for use."#,
|
||||
);
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
#![cfg_attr(target_family = "wasm", allow(dead_code))]
|
||||
|
||||
use std::fs::File;
|
||||
use std::fs::{write, File};
|
||||
use std::io;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::ops::AddAssign;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use colored::Colorize;
|
||||
use filetime::FileTime;
|
||||
use log::{debug, error, warn};
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
use rustc_hash::FxHashMap;
|
||||
use similar::TextDiff;
|
||||
use thiserror::Error;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_linter::linter::{lint_fix, lint_only, FixTable, FixerResult, LinterResult};
|
||||
@@ -21,12 +23,12 @@ use ruff_linter::message::Message;
|
||||
use ruff_linter::pyproject_toml::lint_pyproject_toml;
|
||||
use ruff_linter::registry::AsRule;
|
||||
use ruff_linter::settings::{flags, LinterSettings};
|
||||
use ruff_linter::source_kind::{SourceError, SourceKind};
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_linter::{fs, IOError, SyntaxError};
|
||||
use ruff_macros::CacheKey;
|
||||
use ruff_notebook::{Notebook, NotebookError, NotebookIndex};
|
||||
use ruff_notebook::{Cell, Notebook, NotebookError, NotebookIndex};
|
||||
use ruff_python_ast::imports::ImportMap;
|
||||
use ruff_python_ast::{SourceType, TomlSourceType};
|
||||
use ruff_python_ast::{PySourceType, SourceType, TomlSourceType};
|
||||
use ruff_source_file::{LineIndex, SourceCode, SourceFileBuilder};
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
use ruff_workspace::Settings;
|
||||
@@ -80,38 +82,15 @@ impl Diagnostics {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate [`Diagnostics`] based on a [`SourceError`].
|
||||
/// Generate [`Diagnostics`] based on a [`SourceExtractionError`].
|
||||
pub(crate) fn from_source_error(
|
||||
err: &SourceError,
|
||||
err: &SourceExtractionError,
|
||||
path: Option<&Path>,
|
||||
settings: &LinterSettings,
|
||||
) -> Self {
|
||||
let diagnostic = match err {
|
||||
// IO errors.
|
||||
SourceError::Io(_)
|
||||
| SourceError::Notebook(NotebookError::Io(_) | NotebookError::Json(_)) => {
|
||||
Diagnostic::new(
|
||||
IOError {
|
||||
message: err.to_string(),
|
||||
},
|
||||
TextRange::default(),
|
||||
)
|
||||
}
|
||||
// Syntax errors.
|
||||
SourceError::Notebook(
|
||||
NotebookError::InvalidJson(_)
|
||||
| NotebookError::InvalidSchema(_)
|
||||
| NotebookError::InvalidFormat(_),
|
||||
) => Diagnostic::new(
|
||||
SyntaxError {
|
||||
message: err.to_string(),
|
||||
},
|
||||
TextRange::default(),
|
||||
),
|
||||
};
|
||||
|
||||
let diagnostic = Diagnostic::from(err);
|
||||
if settings.rules.enabled(diagnostic.kind.rule()) {
|
||||
let name = path.map_or_else(|| "-".into(), Path::to_string_lossy);
|
||||
let name = path.map_or_else(|| "-".into(), std::path::Path::to_string_lossy);
|
||||
let dummy = SourceFileBuilder::new(name, "").finish();
|
||||
Self::new(
|
||||
vec![Message::from_diagnostic(
|
||||
@@ -168,8 +147,7 @@ pub(crate) fn lint_path(
|
||||
settings: &LinterSettings,
|
||||
cache: Option<&Cache>,
|
||||
noqa: flags::Noqa,
|
||||
fix_mode: flags::FixMode,
|
||||
unsafe_fixes: UnsafeFixes,
|
||||
autofix: flags::FixMode,
|
||||
) -> Result<Diagnostics> {
|
||||
// Check the cache.
|
||||
// TODO(charlie): `fixer::Mode::Apply` and `fixer::Mode::Diff` both have
|
||||
@@ -178,7 +156,7 @@ pub(crate) fn lint_path(
|
||||
// write the fixes to disk, thus invalidating the cache. But it's a bit hard
|
||||
// to reason about. We need to come up with a better solution here.)
|
||||
let caching = match cache {
|
||||
Some(cache) if noqa.into() && fix_mode.is_generate() => {
|
||||
Some(cache) if noqa.into() && autofix.is_generate() => {
|
||||
let relative_path = cache
|
||||
.relative_path(path)
|
||||
.expect("wrong package cache for file");
|
||||
@@ -205,12 +183,13 @@ pub(crate) fn lint_path(
|
||||
.iter_enabled()
|
||||
.any(|rule_code| rule_code.lint_source().is_pyproject_toml())
|
||||
{
|
||||
let contents = match std::fs::read_to_string(path).map_err(SourceError::from) {
|
||||
Ok(contents) => contents,
|
||||
Err(err) => {
|
||||
return Ok(Diagnostics::from_source_error(&err, Some(path), settings));
|
||||
}
|
||||
};
|
||||
let contents =
|
||||
match std::fs::read_to_string(path).map_err(SourceExtractionError::Io) {
|
||||
Ok(contents) => contents,
|
||||
Err(err) => {
|
||||
return Ok(Diagnostics::from_source_error(&err, Some(path), settings));
|
||||
}
|
||||
};
|
||||
let source_file = SourceFileBuilder::new(path.to_string_lossy(), contents).finish();
|
||||
lint_pyproject_toml(source_file, settings)
|
||||
} else {
|
||||
@@ -226,8 +205,8 @@ pub(crate) fn lint_path(
|
||||
};
|
||||
|
||||
// Extract the sources from the file.
|
||||
let source_kind = match SourceKind::from_path(path, source_type) {
|
||||
Ok(Some(source_kind)) => source_kind,
|
||||
let LintSource(source_kind) = match LintSource::try_from_path(path, source_type) {
|
||||
Ok(Some(sources)) => sources,
|
||||
Ok(None) => return Ok(Diagnostics::default()),
|
||||
Err(err) => {
|
||||
return Ok(Diagnostics::from_source_error(&err, Some(path), settings));
|
||||
@@ -241,36 +220,89 @@ pub(crate) fn lint_path(
|
||||
error: parse_error,
|
||||
},
|
||||
fixed,
|
||||
) = if matches!(fix_mode, flags::FixMode::Apply | flags::FixMode::Diff) {
|
||||
) = if matches!(autofix, flags::FixMode::Apply | flags::FixMode::Diff) {
|
||||
if let Ok(FixerResult {
|
||||
result,
|
||||
transformed,
|
||||
fixed,
|
||||
}) = lint_fix(
|
||||
path,
|
||||
package,
|
||||
noqa,
|
||||
unsafe_fixes,
|
||||
settings,
|
||||
&source_kind,
|
||||
source_type,
|
||||
) {
|
||||
}) = lint_fix(path, package, noqa, settings, &source_kind, source_type)
|
||||
{
|
||||
if !fixed.is_empty() {
|
||||
match fix_mode {
|
||||
flags::FixMode::Apply => transformed.write(&mut File::create(path)?)?,
|
||||
match autofix {
|
||||
flags::FixMode::Apply => match transformed.as_ref() {
|
||||
SourceKind::Python(transformed) => {
|
||||
write(path, transformed.as_bytes())?;
|
||||
}
|
||||
SourceKind::IpyNotebook(notebook) => {
|
||||
let mut writer = BufWriter::new(File::create(path)?);
|
||||
notebook.write(&mut writer)?;
|
||||
}
|
||||
},
|
||||
flags::FixMode::Diff => {
|
||||
source_kind.diff(
|
||||
transformed.as_ref(),
|
||||
Some(path),
|
||||
&mut io::stdout().lock(),
|
||||
)?;
|
||||
match transformed.as_ref() {
|
||||
SourceKind::Python(transformed) => {
|
||||
let mut stdout = io::stdout().lock();
|
||||
TextDiff::from_lines(source_kind.source_code(), transformed)
|
||||
.unified_diff()
|
||||
.header(&fs::relativize_path(path), &fs::relativize_path(path))
|
||||
.to_writer(&mut stdout)?;
|
||||
stdout.write_all(b"\n")?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
SourceKind::IpyNotebook(dest_notebook) => {
|
||||
// We need to load the notebook again, since we might've
|
||||
// mutated it.
|
||||
let src_notebook = source_kind.as_ipy_notebook().unwrap();
|
||||
let mut stdout = io::stdout().lock();
|
||||
for ((idx, src_cell), dest_cell) in src_notebook
|
||||
.cells()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.zip(dest_notebook.cells().iter())
|
||||
{
|
||||
let (Cell::Code(src_code_cell), Cell::Code(dest_code_cell)) =
|
||||
(src_cell, dest_cell)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
TextDiff::from_lines(
|
||||
&src_code_cell.source.to_string(),
|
||||
&dest_code_cell.source.to_string(),
|
||||
)
|
||||
.unified_diff()
|
||||
// Jupyter notebook cells don't necessarily have a newline
|
||||
// at the end. For example,
|
||||
//
|
||||
// ```python
|
||||
// print("hello")
|
||||
// ```
|
||||
//
|
||||
// For a cell containing the above code, there'll only be one line,
|
||||
// and it won't have a newline at the end. If it did, there'd be
|
||||
// two lines, and the second line would be empty:
|
||||
//
|
||||
// ```python
|
||||
// print("hello")
|
||||
//
|
||||
// ```
|
||||
.missing_newline_hint(false)
|
||||
.header(
|
||||
&format!("{}:cell {}", &fs::relativize_path(path), idx),
|
||||
&format!("{}:cell {}", &fs::relativize_path(path), idx),
|
||||
)
|
||||
.to_writer(&mut stdout)?;
|
||||
}
|
||||
stdout.write_all(b"\n")?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
flags::FixMode::Generate => {}
|
||||
}
|
||||
}
|
||||
(result, fixed)
|
||||
} else {
|
||||
// If we fail to fix, lint the original source code.
|
||||
// If we fail to autofix, lint the original source code.
|
||||
let result = lint_only(path, package, settings, noqa, &source_kind, source_type);
|
||||
let fixed = FxHashMap::default();
|
||||
(result, fixed)
|
||||
@@ -311,7 +343,13 @@ pub(crate) fn lint_path(
|
||||
}
|
||||
|
||||
let notebook_indexes = if let SourceKind::IpyNotebook(notebook) = source_kind {
|
||||
FxHashMap::from_iter([(path.to_string_lossy().to_string(), notebook.into_index())])
|
||||
FxHashMap::from_iter([(
|
||||
path.to_str()
|
||||
.ok_or_else(|| anyhow!("Unable to parse filename: {:?}", path))?
|
||||
.to_string(),
|
||||
// Index needs to be computed always to store in cache.
|
||||
notebook.index().clone(),
|
||||
)])
|
||||
} else {
|
||||
FxHashMap::default()
|
||||
};
|
||||
@@ -332,7 +370,7 @@ pub(crate) fn lint_stdin(
|
||||
contents: String,
|
||||
settings: &Settings,
|
||||
noqa: flags::Noqa,
|
||||
fix_mode: flags::FixMode,
|
||||
autofix: flags::FixMode,
|
||||
) -> Result<Diagnostics> {
|
||||
// TODO(charlie): Support `pyproject.toml`.
|
||||
let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
|
||||
@@ -340,8 +378,8 @@ pub(crate) fn lint_stdin(
|
||||
};
|
||||
|
||||
// Extract the sources from the file.
|
||||
let source_kind = match SourceKind::from_source_code(contents, source_type) {
|
||||
Ok(Some(source_kind)) => source_kind,
|
||||
let LintSource(source_kind) = match LintSource::try_from_source_code(contents, source_type) {
|
||||
Ok(Some(sources)) => sources,
|
||||
Ok(None) => return Ok(Diagnostics::default()),
|
||||
Err(err) => {
|
||||
return Ok(Diagnostics::from_source_error(&err, path, &settings.linter));
|
||||
@@ -355,7 +393,7 @@ pub(crate) fn lint_stdin(
|
||||
error: parse_error,
|
||||
},
|
||||
fixed,
|
||||
) = if matches!(fix_mode, flags::FixMode::Apply | flags::FixMode::Diff) {
|
||||
) = if matches!(autofix, flags::FixMode::Apply | flags::FixMode::Diff) {
|
||||
if let Ok(FixerResult {
|
||||
result,
|
||||
transformed,
|
||||
@@ -364,20 +402,32 @@ pub(crate) fn lint_stdin(
|
||||
path.unwrap_or_else(|| Path::new("-")),
|
||||
package,
|
||||
noqa,
|
||||
settings.unsafe_fixes,
|
||||
&settings.linter,
|
||||
&source_kind,
|
||||
source_type,
|
||||
) {
|
||||
match fix_mode {
|
||||
match autofix {
|
||||
flags::FixMode::Apply => {
|
||||
// Write the contents to stdout, regardless of whether any errors were fixed.
|
||||
transformed.write(&mut io::stdout().lock())?;
|
||||
io::stdout().write_all(transformed.source_code().as_bytes())?;
|
||||
}
|
||||
flags::FixMode::Diff => {
|
||||
// But only write a diff if it's non-empty.
|
||||
if !fixed.is_empty() {
|
||||
source_kind.diff(transformed.as_ref(), path, &mut io::stdout().lock())?;
|
||||
let text_diff = TextDiff::from_lines(
|
||||
source_kind.source_code(),
|
||||
transformed.source_code(),
|
||||
);
|
||||
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()?;
|
||||
}
|
||||
}
|
||||
flags::FixMode::Generate => {}
|
||||
@@ -385,7 +435,7 @@ pub(crate) fn lint_stdin(
|
||||
|
||||
(result, fixed)
|
||||
} else {
|
||||
// If we fail to fix, lint the original source code.
|
||||
// If we fail to autofix, lint the original source code.
|
||||
let result = lint_only(
|
||||
path.unwrap_or_else(|| Path::new("-")),
|
||||
package,
|
||||
@@ -397,8 +447,8 @@ pub(crate) fn lint_stdin(
|
||||
let fixed = FxHashMap::default();
|
||||
|
||||
// Write the contents to stdout anyway.
|
||||
if fix_mode.is_apply() {
|
||||
source_kind.write(&mut io::stdout().lock())?;
|
||||
if autofix.is_apply() {
|
||||
io::stdout().write_all(source_kind.source_code().as_bytes())?;
|
||||
}
|
||||
|
||||
(result, fixed)
|
||||
@@ -425,15 +475,6 @@ pub(crate) fn lint_stdin(
|
||||
);
|
||||
}
|
||||
|
||||
let notebook_indexes = if let SourceKind::IpyNotebook(notebook) = source_kind {
|
||||
FxHashMap::from_iter([(
|
||||
path.map_or_else(|| "-".into(), |path| path.to_string_lossy().to_string()),
|
||||
notebook.into_index(),
|
||||
)])
|
||||
} else {
|
||||
FxHashMap::default()
|
||||
};
|
||||
|
||||
Ok(Diagnostics {
|
||||
messages,
|
||||
fixed: FxHashMap::from_iter([(
|
||||
@@ -441,6 +482,83 @@ pub(crate) fn lint_stdin(
|
||||
fixed,
|
||||
)]),
|
||||
imports,
|
||||
notebook_indexes,
|
||||
notebook_indexes: FxHashMap::default(),
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct LintSource(pub(crate) SourceKind);
|
||||
|
||||
impl LintSource {
|
||||
/// Extract the lint [`LintSource`] from the given file path.
|
||||
pub(crate) fn try_from_path(
|
||||
path: &Path,
|
||||
source_type: PySourceType,
|
||||
) -> Result<Option<LintSource>, SourceExtractionError> {
|
||||
if source_type.is_ipynb() {
|
||||
let notebook = Notebook::from_path(path)?;
|
||||
Ok(notebook
|
||||
.is_python_notebook()
|
||||
.then_some(LintSource(SourceKind::IpyNotebook(notebook))))
|
||||
} else {
|
||||
// This is tested by ruff_cli integration test `unreadable_file`
|
||||
let contents = std::fs::read_to_string(path)?;
|
||||
Ok(Some(LintSource(SourceKind::Python(contents))))
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the lint [`LintSource`] from the raw string contents, optionally accompanied by a
|
||||
/// file path indicating the path to the file from which the contents were read. If provided,
|
||||
/// the file path should be used for diagnostics, but not for reading the file from disk.
|
||||
pub(crate) fn try_from_source_code(
|
||||
source_code: String,
|
||||
source_type: PySourceType,
|
||||
) -> Result<Option<LintSource>, SourceExtractionError> {
|
||||
if source_type.is_ipynb() {
|
||||
let notebook = Notebook::from_source_code(&source_code)?;
|
||||
Ok(notebook
|
||||
.is_python_notebook()
|
||||
.then_some(LintSource(SourceKind::IpyNotebook(notebook))))
|
||||
} else {
|
||||
Ok(Some(LintSource(SourceKind::Python(source_code))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub(crate) enum SourceExtractionError {
|
||||
/// The extraction failed due to an [`io::Error`].
|
||||
#[error(transparent)]
|
||||
Io(#[from] io::Error),
|
||||
/// The extraction failed due to a [`NotebookError`].
|
||||
#[error(transparent)]
|
||||
Notebook(#[from] NotebookError),
|
||||
}
|
||||
|
||||
impl From<&SourceExtractionError> for Diagnostic {
|
||||
fn from(err: &SourceExtractionError) -> Self {
|
||||
match err {
|
||||
// IO errors.
|
||||
SourceExtractionError::Io(_)
|
||||
| SourceExtractionError::Notebook(NotebookError::Io(_) | NotebookError::Json(_)) => {
|
||||
Diagnostic::new(
|
||||
IOError {
|
||||
message: err.to_string(),
|
||||
},
|
||||
TextRange::default(),
|
||||
)
|
||||
}
|
||||
// Syntax errors.
|
||||
SourceExtractionError::Notebook(
|
||||
NotebookError::InvalidJson(_)
|
||||
| NotebookError::InvalidSchema(_)
|
||||
| NotebookError::InvalidFormat(_),
|
||||
) => Diagnostic::new(
|
||||
SyntaxError {
|
||||
message: err.to_string(),
|
||||
},
|
||||
TextRange::default(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,11 @@ use std::sync::mpsc::channel;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::CommandFactory;
|
||||
use colored::Colorize;
|
||||
use log::warn;
|
||||
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
||||
|
||||
use ruff_linter::logging::{set_up_logging, LogLevel};
|
||||
use ruff_linter::settings::flags::FixMode;
|
||||
use ruff_linter::settings::flags;
|
||||
use ruff_linter::settings::types::SerializationFormat;
|
||||
use ruff_linter::{fs, warn_user, warn_user_once};
|
||||
use ruff_workspace::Settings;
|
||||
@@ -105,6 +104,8 @@ pub fn run(
|
||||
}: Args,
|
||||
) -> Result<ExitStatus> {
|
||||
{
|
||||
use colored::Colorize;
|
||||
|
||||
let default_panic_hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
#[allow(clippy::print_stderr)]
|
||||
@@ -165,7 +166,10 @@ pub fn run(
|
||||
}
|
||||
|
||||
fn format(args: FormatCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
warn_user_once!("`ruff format` is not yet stable, and subject to change in future versions.");
|
||||
warn_user_once!(
|
||||
"`ruff format` is a work-in-progress, subject to change at any time, and intended only for \
|
||||
experimentation."
|
||||
);
|
||||
|
||||
let (cli, overrides) = args.partition();
|
||||
|
||||
@@ -177,6 +181,14 @@ fn format(args: FormatCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
}
|
||||
|
||||
pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
if args.format.is_some() {
|
||||
if std::env::var("RUFF_FORMAT").is_ok() {
|
||||
warn_user!("The environment variable `RUFF_FORMAT` is deprecated. Use `RUFF_OUTPUT_FORMAT` instead.");
|
||||
} else {
|
||||
warn_user!("The argument `--format=<FORMAT>` is deprecated. Use `--output-format=<FORMAT>` instead.");
|
||||
}
|
||||
}
|
||||
|
||||
let (cli, overrides) = args.partition();
|
||||
|
||||
// Construct the "default" settings. These are used when no `pyproject.toml`
|
||||
@@ -196,7 +208,6 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
}
|
||||
_ => Box::new(BufWriter::new(io::stdout())),
|
||||
};
|
||||
let stderr_writer = Box::new(BufWriter::new(io::stderr()));
|
||||
|
||||
if cli.show_settings {
|
||||
commands::show_settings::show_settings(
|
||||
@@ -217,29 +228,25 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
let Settings {
|
||||
fix,
|
||||
fix_only,
|
||||
unsafe_fixes,
|
||||
output_format,
|
||||
show_fixes,
|
||||
show_source,
|
||||
..
|
||||
} = pyproject_config.settings;
|
||||
|
||||
// Fix rules are as follows:
|
||||
// Autofix rules are as follows:
|
||||
// - By default, generate all fixes, but don't apply them to the filesystem.
|
||||
// - If `--fix` or `--fix-only` is set, apply applicable fixes to the filesystem (or
|
||||
// - If `--fix` or `--fix-only` is set, always apply fixes to the filesystem (or
|
||||
// print them to stdout, if we're reading from stdin).
|
||||
// - If `--diff` or `--fix-only` are set, don't print any violations (only applicable fixes)
|
||||
// - By default, applicable fixes only include [`Applicablility::Automatic`], but if
|
||||
// `--unsafe-fixes` is set, then [`Applicablility::Suggested`] fixes are included.
|
||||
|
||||
let fix_mode = if cli.diff {
|
||||
FixMode::Diff
|
||||
// - If `--diff` or `--fix-only` are set, don't print any violations (only
|
||||
// fixes).
|
||||
let autofix = if cli.diff {
|
||||
flags::FixMode::Diff
|
||||
} else if fix || fix_only {
|
||||
FixMode::Apply
|
||||
flags::FixMode::Apply
|
||||
} else {
|
||||
FixMode::Generate
|
||||
flags::FixMode::Generate
|
||||
};
|
||||
|
||||
let cache = !cli.no_cache;
|
||||
let noqa = !cli.ignore_noqa;
|
||||
let mut printer_flags = PrinterFlags::empty();
|
||||
@@ -268,7 +275,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
}
|
||||
|
||||
if cli.add_noqa {
|
||||
if !fix_mode.is_generate() {
|
||||
if !autofix.is_generate() {
|
||||
warn_user!("--fix is incompatible with --add-noqa.");
|
||||
}
|
||||
let modifications =
|
||||
@@ -283,17 +290,11 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
|
||||
let printer = Printer::new(
|
||||
output_format,
|
||||
log_level,
|
||||
fix_mode,
|
||||
unsafe_fixes,
|
||||
printer_flags,
|
||||
);
|
||||
let printer = Printer::new(output_format, log_level, autofix, printer_flags);
|
||||
|
||||
if cli.watch {
|
||||
if output_format != SerializationFormat::Text {
|
||||
warn_user!("`--output-format text` is always used in watch mode.");
|
||||
warn_user!("--format 'text' is used in watch mode.");
|
||||
}
|
||||
|
||||
// Configure the file watcher.
|
||||
@@ -316,8 +317,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
&overrides,
|
||||
cache.into(),
|
||||
noqa.into(),
|
||||
fix_mode,
|
||||
unsafe_fixes,
|
||||
autofix,
|
||||
)?;
|
||||
printer.write_continuously(&mut writer, &messages)?;
|
||||
|
||||
@@ -349,8 +349,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
&overrides,
|
||||
cache.into(),
|
||||
noqa.into(),
|
||||
fix_mode,
|
||||
unsafe_fixes,
|
||||
autofix,
|
||||
)?;
|
||||
printer.write_continuously(&mut writer, &messages)?;
|
||||
}
|
||||
@@ -367,7 +366,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
&pyproject_config,
|
||||
&overrides,
|
||||
noqa.into(),
|
||||
fix_mode,
|
||||
autofix,
|
||||
)?
|
||||
} else {
|
||||
commands::check::check(
|
||||
@@ -376,23 +375,19 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
&overrides,
|
||||
cache.into(),
|
||||
noqa.into(),
|
||||
fix_mode,
|
||||
unsafe_fixes,
|
||||
autofix,
|
||||
)?
|
||||
};
|
||||
|
||||
// Always try to print violations (though the printer itself may suppress output)
|
||||
// If we're writing fixes via stdin, the transformed source code goes to the writer
|
||||
// so send the summary to stderr instead
|
||||
let mut summary_writer = if is_stdin && matches!(fix_mode, FixMode::Apply | FixMode::Diff) {
|
||||
stderr_writer
|
||||
} else {
|
||||
writer
|
||||
};
|
||||
if cli.statistics {
|
||||
printer.write_statistics(&diagnostics, &mut summary_writer)?;
|
||||
} else {
|
||||
printer.write_once(&diagnostics, &mut summary_writer)?;
|
||||
// Always try to print violations (the printer itself may suppress output),
|
||||
// unless we're writing fixes via stdin (in which case, the transformed
|
||||
// source code goes to stdout).
|
||||
if !(is_stdin && matches!(autofix, flags::FixMode::Apply | flags::FixMode::Diff)) {
|
||||
if cli.statistics {
|
||||
printer.write_statistics(&diagnostics, &mut writer)?;
|
||||
} else {
|
||||
printer.write_once(&diagnostics, &mut writer)?;
|
||||
}
|
||||
}
|
||||
|
||||
if !cli.exit_zero {
|
||||
|
||||
@@ -19,8 +19,8 @@ use ruff_linter::message::{
|
||||
};
|
||||
use ruff_linter::notify_user;
|
||||
use ruff_linter::registry::{AsRule, Rule};
|
||||
use ruff_linter::settings::flags::{self};
|
||||
use ruff_linter::settings::types::{SerializationFormat, UnsafeFixes};
|
||||
use ruff_linter::settings::flags;
|
||||
use ruff_linter::settings::types::SerializationFormat;
|
||||
|
||||
use crate::diagnostics::Diagnostics;
|
||||
|
||||
@@ -72,8 +72,7 @@ impl From<Rule> for SerializeRuleAsCode {
|
||||
pub(crate) struct Printer {
|
||||
format: SerializationFormat,
|
||||
log_level: LogLevel,
|
||||
fix_mode: flags::FixMode,
|
||||
unsafe_fixes: UnsafeFixes,
|
||||
autofix_level: flags::FixMode,
|
||||
flags: Flags,
|
||||
}
|
||||
|
||||
@@ -81,15 +80,13 @@ impl Printer {
|
||||
pub(crate) const fn new(
|
||||
format: SerializationFormat,
|
||||
log_level: LogLevel,
|
||||
fix_mode: flags::FixMode,
|
||||
unsafe_fixes: UnsafeFixes,
|
||||
autofix_level: flags::FixMode,
|
||||
flags: Flags,
|
||||
) -> Self {
|
||||
Self {
|
||||
format,
|
||||
log_level,
|
||||
fix_mode,
|
||||
unsafe_fixes,
|
||||
autofix_level,
|
||||
flags,
|
||||
}
|
||||
}
|
||||
@@ -102,15 +99,12 @@ impl Printer {
|
||||
|
||||
fn write_summary_text(&self, writer: &mut dyn Write, diagnostics: &Diagnostics) -> Result<()> {
|
||||
if self.log_level >= LogLevel::Default {
|
||||
let fixables = FixableStatistics::try_from(diagnostics, self.unsafe_fixes);
|
||||
|
||||
let fixed = diagnostics
|
||||
.fixed
|
||||
.values()
|
||||
.flat_map(std::collections::HashMap::values)
|
||||
.sum::<usize>();
|
||||
|
||||
if self.flags.intersects(Flags::SHOW_VIOLATIONS) {
|
||||
let fixed = diagnostics
|
||||
.fixed
|
||||
.values()
|
||||
.flat_map(std::collections::HashMap::values)
|
||||
.sum::<usize>();
|
||||
let remaining = diagnostics.messages.len();
|
||||
let total = fixed + remaining;
|
||||
if fixed > 0 {
|
||||
@@ -124,83 +118,32 @@ impl Printer {
|
||||
writeln!(writer, "Found {remaining} error{s}.")?;
|
||||
}
|
||||
|
||||
if let Some(fixables) = fixables {
|
||||
let fix_prefix = format!("[{}]", "*".cyan());
|
||||
|
||||
if self.unsafe_fixes.is_enabled() {
|
||||
if fixables.applicable > 0 {
|
||||
writeln!(
|
||||
writer,
|
||||
"{fix_prefix} {} fixable with the --fix option.",
|
||||
fixables.applicable
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
if fixables.applicable > 0 && fixables.unapplicable_unsafe > 0 {
|
||||
let es = if fixables.unapplicable_unsafe == 1 {
|
||||
""
|
||||
} else {
|
||||
"es"
|
||||
};
|
||||
writeln!(writer,
|
||||
"{fix_prefix} {} fixable with the `--fix` option ({} hidden fix{es} can be enabled with the `--unsafe-fixes` option).",
|
||||
fixables.applicable, fixables.unapplicable_unsafe
|
||||
)?;
|
||||
} else if fixables.applicable > 0 {
|
||||
// Only applicable fixes
|
||||
writeln!(
|
||||
writer,
|
||||
"{fix_prefix} {} fixable with the `--fix` option.",
|
||||
fixables.applicable,
|
||||
)?;
|
||||
} else {
|
||||
// Only unapplicable fixes
|
||||
let es = if fixables.unapplicable_unsafe == 1 {
|
||||
""
|
||||
} else {
|
||||
"es"
|
||||
};
|
||||
writeln!(writer,
|
||||
"No fixes available ({} hidden fix{es} can be enabled with the `--unsafe-fixes` option).",
|
||||
fixables.unapplicable_unsafe
|
||||
)?;
|
||||
}
|
||||
if show_fix_status(self.autofix_level) {
|
||||
let num_fixable = diagnostics
|
||||
.messages
|
||||
.iter()
|
||||
.filter(|message| message.fix.is_some())
|
||||
.count();
|
||||
if num_fixable > 0 {
|
||||
writeln!(
|
||||
writer,
|
||||
"[{}] {num_fixable} potentially fixable with the --fix option.",
|
||||
"*".cyan(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check if there are unapplied fixes
|
||||
let unapplied = {
|
||||
if let Some(fixables) = fixables {
|
||||
fixables.unapplicable_unsafe
|
||||
let fixed = diagnostics
|
||||
.fixed
|
||||
.values()
|
||||
.flat_map(std::collections::HashMap::values)
|
||||
.sum::<usize>();
|
||||
if fixed > 0 {
|
||||
let s = if fixed == 1 { "" } else { "s" };
|
||||
if self.autofix_level.is_apply() {
|
||||
writeln!(writer, "Fixed {fixed} error{s}.")?;
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
if unapplied > 0 {
|
||||
let es = if unapplied == 1 { "" } else { "es" };
|
||||
if fixed > 0 {
|
||||
let s = if fixed == 1 { "" } else { "s" };
|
||||
if self.fix_mode.is_apply() {
|
||||
writeln!(writer, "Fixed {fixed} error{s} ({unapplied} additional fix{es} available with `--unsafe-fixes`).")?;
|
||||
} else {
|
||||
writeln!(writer, "Would fix {fixed} error{s} ({unapplied} additional fix{es} available with `--unsafe-fixes`).")?;
|
||||
}
|
||||
} else {
|
||||
if self.fix_mode.is_apply() {
|
||||
writeln!(writer, "No errors fixed ({unapplied} fix{es} available with `--unsafe-fixes`).")?;
|
||||
} else {
|
||||
writeln!(writer, "No errors would be fixed ({unapplied} fix{es} available with `--unsafe-fixes`).")?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if fixed > 0 {
|
||||
let s = if fixed == 1 { "" } else { "s" };
|
||||
if self.fix_mode.is_apply() {
|
||||
writeln!(writer, "Fixed {fixed} error{s}.")?;
|
||||
} else {
|
||||
writeln!(writer, "Would fix {fixed} error{s}.")?;
|
||||
}
|
||||
writeln!(writer, "Would fix {fixed} error{s}.")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,7 +178,6 @@ impl Printer {
|
||||
}
|
||||
|
||||
let context = EmitterContext::new(&diagnostics.notebook_indexes);
|
||||
let fixables = FixableStatistics::try_from(diagnostics, self.unsafe_fixes);
|
||||
|
||||
match self.format {
|
||||
SerializationFormat::Json => {
|
||||
@@ -249,10 +191,9 @@ impl Printer {
|
||||
}
|
||||
SerializationFormat::Text => {
|
||||
TextEmitter::default()
|
||||
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
|
||||
.with_show_fix_status(show_fix_status(self.autofix_level))
|
||||
.with_show_fix_diff(self.flags.intersects(Flags::SHOW_FIX_DIFF))
|
||||
.with_show_source(self.flags.intersects(Flags::SHOW_SOURCE))
|
||||
.with_unsafe_fixes(self.unsafe_fixes)
|
||||
.emit(writer, &diagnostics.messages, &context)?;
|
||||
|
||||
if self.flags.intersects(Flags::SHOW_FIX_SUMMARY) {
|
||||
@@ -268,8 +209,7 @@ impl Printer {
|
||||
SerializationFormat::Grouped => {
|
||||
GroupedEmitter::default()
|
||||
.with_show_source(self.flags.intersects(Flags::SHOW_SOURCE))
|
||||
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
|
||||
.with_unsafe_fixes(self.unsafe_fixes)
|
||||
.with_show_fix_status(show_fix_status(self.autofix_level))
|
||||
.emit(writer, &diagnostics.messages, &context)?;
|
||||
|
||||
if self.flags.intersects(Flags::SHOW_FIX_SUMMARY) {
|
||||
@@ -419,8 +359,6 @@ impl Printer {
|
||||
);
|
||||
}
|
||||
|
||||
let fixables = FixableStatistics::try_from(diagnostics, self.unsafe_fixes);
|
||||
|
||||
if !diagnostics.messages.is_empty() {
|
||||
if self.log_level >= LogLevel::Default {
|
||||
writeln!(writer)?;
|
||||
@@ -428,9 +366,8 @@ impl Printer {
|
||||
|
||||
let context = EmitterContext::new(&diagnostics.notebook_indexes);
|
||||
TextEmitter::default()
|
||||
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
|
||||
.with_show_fix_status(show_fix_status(self.autofix_level))
|
||||
.with_show_source(self.flags.intersects(Flags::SHOW_SOURCE))
|
||||
.with_unsafe_fixes(self.unsafe_fixes)
|
||||
.emit(writer, &diagnostics.messages, &context)?;
|
||||
}
|
||||
writer.flush()?;
|
||||
@@ -453,13 +390,13 @@ fn num_digits(n: usize) -> usize {
|
||||
}
|
||||
|
||||
/// Return `true` if the [`Printer`] should indicate that a rule is fixable.
|
||||
fn show_fix_status(fix_mode: flags::FixMode, fixables: Option<&FixableStatistics>) -> bool {
|
||||
const fn show_fix_status(autofix_level: flags::FixMode) -> bool {
|
||||
// If we're in application mode, avoid indicating that a rule is fixable.
|
||||
// If the specific violation were truly fixable, it would've been fixed in
|
||||
// this pass! (We're occasionally unable to determine whether a specific
|
||||
// violation is fixable without trying to fix it, so if fix is not
|
||||
// violation is fixable without trying to fix it, so if autofix is not
|
||||
// enabled, we may inadvertently indicate that a rule is fixable.)
|
||||
(!fix_mode.is_apply()) && fixables.is_some_and(FixableStatistics::any_applicable_fixes)
|
||||
!autofix_level.is_apply()
|
||||
}
|
||||
|
||||
fn print_fix_summary(writer: &mut dyn Write, fixed: &FxHashMap<String, FixTable>) -> Result<()> {
|
||||
@@ -502,43 +439,3 @@ fn print_fix_summary(writer: &mut dyn Write, fixed: &FxHashMap<String, FixTable>
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Statistics for [applicable][ruff_diagnostics::Applicability] fixes.
|
||||
#[derive(Debug)]
|
||||
struct FixableStatistics {
|
||||
applicable: u32,
|
||||
unapplicable_unsafe: u32,
|
||||
}
|
||||
|
||||
impl FixableStatistics {
|
||||
fn try_from(diagnostics: &Diagnostics, unsafe_fixes: UnsafeFixes) -> Option<Self> {
|
||||
let mut applicable = 0;
|
||||
let mut unapplicable_unsafe = 0;
|
||||
|
||||
for message in &diagnostics.messages {
|
||||
if let Some(fix) = &message.fix {
|
||||
if fix.applies(unsafe_fixes.required_applicability()) {
|
||||
applicable += 1;
|
||||
} else {
|
||||
// Do not include unapplicable fixes at other levels that do not provide an opt-in
|
||||
if fix.applicability().is_unsafe() {
|
||||
unapplicable_unsafe += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if applicable == 0 && unapplicable_unsafe == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(Self {
|
||||
applicable,
|
||||
unapplicable_unsafe,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn any_applicable_fixes(&self) -> bool {
|
||||
self.applicable > 0
|
||||
}
|
||||
}
|
||||
|
||||
204
crates/ruff_cli/tests/black_compatibility_test.rs
Normal file
204
crates/ruff_cli/tests/black_compatibility_test.rs
Normal file
@@ -0,0 +1,204 @@
|
||||
#![cfg(not(target_family = "wasm"))]
|
||||
|
||||
use std::io::{ErrorKind, Read, Write};
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpListener, TcpStream};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use std::{fs, process, str};
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use insta_cmd::get_cargo_bin;
|
||||
use log::info;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use ruff_linter::logging::{set_up_logging, LogLevel};
|
||||
|
||||
/// Handles `blackd` process and allows submitting code to it for formatting.
|
||||
struct Blackd {
|
||||
address: SocketAddr,
|
||||
server: process::Child,
|
||||
client: ureq::Agent,
|
||||
}
|
||||
|
||||
const BIN_NAME: &str = "ruff";
|
||||
|
||||
impl Blackd {
|
||||
pub(crate) fn new() -> Result<Self> {
|
||||
// Get free TCP port to run on
|
||||
let address = TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?.local_addr()?;
|
||||
|
||||
let args = [
|
||||
"--bind-host",
|
||||
&address.ip().to_string(),
|
||||
"--bind-port",
|
||||
&address.port().to_string(),
|
||||
];
|
||||
let server = Command::new("blackd")
|
||||
.args(args)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
.context("Starting blackd")?;
|
||||
|
||||
// Wait up to four seconds for `blackd` to be ready.
|
||||
for _ in 0..20 {
|
||||
match TcpStream::connect(address) {
|
||||
Err(e) if e.kind() == ErrorKind::ConnectionRefused => {
|
||||
info!("`blackd` not ready yet; retrying...");
|
||||
sleep(Duration::from_millis(200));
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
Ok(_) => {
|
||||
info!("`blackd` ready");
|
||||
return Ok(Self {
|
||||
address,
|
||||
server,
|
||||
client: ureq::agent(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bail!("blackd {:?} failed to start", args)
|
||||
}
|
||||
|
||||
/// Format given code with blackd.
|
||||
pub(crate) fn check(&self, code: &[u8]) -> Result<Vec<u8>> {
|
||||
match self
|
||||
.client
|
||||
.post(&format!("http://{}/", self.address))
|
||||
.set("X-Line-Length", "88")
|
||||
.send_bytes(code)
|
||||
{
|
||||
// 204 indicates the input wasn't changed during formatting, so
|
||||
// we return the original.
|
||||
Ok(response) => {
|
||||
if response.status() == 204 {
|
||||
Ok(code.to_vec())
|
||||
} else {
|
||||
let mut buf = vec![];
|
||||
response
|
||||
.into_reader()
|
||||
.take((1024 * 1024) as u64)
|
||||
.read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
Err(ureq::Error::Status(_, response)) => Err(anyhow::anyhow!(
|
||||
"Formatting with `black` failed: {}",
|
||||
response.into_string()?
|
||||
)),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Blackd {
|
||||
fn drop(&mut self) {
|
||||
self.server.kill().expect("Couldn't end `blackd` process");
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test(path: &Path, blackd: &Blackd, ruff_args: &[&str]) -> Result<()> {
|
||||
let input = fs::read(path)?;
|
||||
|
||||
// Step 1: Run `ruff` on the input.
|
||||
let mut step_1 = Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(ruff_args)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()?;
|
||||
if let Some(mut stdin) = step_1.stdin.take() {
|
||||
stdin.write_all(input.as_ref())?;
|
||||
}
|
||||
let step_1_output = step_1.wait_with_output()?;
|
||||
if !step_1_output.status.success() {
|
||||
return Err(anyhow!(
|
||||
"Running input through ruff failed:\n{}",
|
||||
str::from_utf8(&step_1_output.stderr)?
|
||||
));
|
||||
}
|
||||
|
||||
// Step 2: Run `blackd` on the input.
|
||||
let step_2_output = blackd.check(&step_1_output.stdout.clone())?;
|
||||
|
||||
// Step 3: Re-run `ruff` on the input.
|
||||
let mut step_3 = Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(ruff_args)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()?;
|
||||
if let Some(mut stdin) = step_3.stdin.take() {
|
||||
stdin.write_all(step_2_output.as_ref())?;
|
||||
}
|
||||
let step_3_output = step_3.wait_with_output()?;
|
||||
if !step_3_output.status.success() {
|
||||
return Err(anyhow!(
|
||||
"Running input through ruff after black failed:\n{}",
|
||||
str::from_utf8(&step_3_output.stderr)?
|
||||
));
|
||||
}
|
||||
let step_3_output = step_3_output.stdout.clone();
|
||||
|
||||
assert_eq!(
|
||||
str::from_utf8(&step_2_output),
|
||||
str::from_utf8(&step_3_output),
|
||||
"Mismatch found for {}",
|
||||
path.display()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_ruff_black_compatibility() -> Result<()> {
|
||||
set_up_logging(&LogLevel::Default)?;
|
||||
|
||||
let blackd = Blackd::new()?;
|
||||
|
||||
let fixtures_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("resources")
|
||||
.join("test")
|
||||
.join("fixtures");
|
||||
|
||||
// Ignore some fixtures that currently trigger errors. `E999.py` especially, as
|
||||
// that is triggering a syntax error on purpose.
|
||||
let excludes = ["E999.py", "W605_1.py"];
|
||||
|
||||
let paths = WalkDir::new(fixtures_dir)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter(|entry| {
|
||||
entry
|
||||
.path()
|
||||
.extension()
|
||||
.is_some_and(|ext| ext == "py" || ext == "pyi")
|
||||
&& !excludes.contains(&entry.path().file_name().unwrap().to_str().unwrap())
|
||||
});
|
||||
|
||||
let ruff_args = [
|
||||
"-",
|
||||
"--silent",
|
||||
"--exit-zero",
|
||||
"--fix",
|
||||
"--line-length",
|
||||
"88",
|
||||
"--select",
|
||||
"ALL",
|
||||
// Exclude ruff codes, specifically RUF100, because it causes differences that are not a
|
||||
// problem. Ruff would add a `# noqa: W292` after the first run, black introduces a
|
||||
// newline, and ruff removes the `# noqa: W292` again.
|
||||
"--ignore",
|
||||
"RUF",
|
||||
];
|
||||
|
||||
for entry in paths {
|
||||
let path = entry.path();
|
||||
run_test(path, &blackd, &ruff_args).context(format!("Testing {}", path.display()))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
#![cfg(not(target_family = "wasm"))]
|
||||
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
use std::str;
|
||||
|
||||
use anyhow::Result;
|
||||
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||||
use tempfile::TempDir;
|
||||
|
||||
const BIN_NAME: &str = "ruff";
|
||||
|
||||
#[test]
|
||||
fn default_options() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["format", "--isolated"])
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
def foo(arg1, arg2,):
|
||||
print('Should\'t change quotes')
|
||||
|
||||
|
||||
if condition:
|
||||
|
||||
print('Hy "Micha"') # Should not change quotes
|
||||
|
||||
"#), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
def foo(
|
||||
arg1,
|
||||
arg2,
|
||||
):
|
||||
print("Should't change quotes")
|
||||
|
||||
|
||||
if condition:
|
||||
print('Hy "Micha"') # Should not change quotes
|
||||
|
||||
----- stderr -----
|
||||
warning: `ruff format` is not yet stable, and subject to change in future versions.
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_options() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
[format]
|
||||
indent-style = "tab"
|
||||
quote-style = "single"
|
||||
skip-magic-trailing-comma = true
|
||||
line-ending = "cr-lf"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["format", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
def foo(arg1, arg2,):
|
||||
print("Shouldn't change quotes")
|
||||
|
||||
|
||||
if condition:
|
||||
|
||||
print("Should change quotes")
|
||||
|
||||
"#), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
def foo(arg1, arg2):
|
||||
print("Shouldn't change quotes")
|
||||
|
||||
|
||||
if condition:
|
||||
print('Should change quotes')
|
||||
|
||||
----- stderr -----
|
||||
warning: `ruff format` is not yet stable, and subject to change in future versions.
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_option_inheritance() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
let base_toml = tempdir.path().join("base.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
extend = "base.toml"
|
||||
|
||||
[format]
|
||||
quote-style = "single"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
fs::write(
|
||||
base_toml,
|
||||
r#"
|
||||
[format]
|
||||
indent-style = "tab"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["format", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
def foo(arg1, arg2,):
|
||||
print("Shouldn't change quotes")
|
||||
|
||||
|
||||
if condition:
|
||||
|
||||
print("Should change quotes")
|
||||
|
||||
"#), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
def foo(
|
||||
arg1,
|
||||
arg2,
|
||||
):
|
||||
print("Shouldn't change quotes")
|
||||
|
||||
|
||||
if condition:
|
||||
print('Should change quotes')
|
||||
|
||||
----- stderr -----
|
||||
warning: `ruff format` is not yet stable, and subject to change in future versions.
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Since 0.1.0 the legacy format option is no longer supported
|
||||
#[test]
|
||||
fn legacy_format_option() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
format = "json"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
insta::with_settings!({filters => vec![
|
||||
(&*regex::escape(ruff_toml.to_str().unwrap()), "[RUFF-TOML-PATH]"),
|
||||
]}, {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["check", "--select", "F401", "--no-cache", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
import os
|
||||
"#), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
ruff failed
|
||||
Cause: Failed to parse `[RUFF-TOML-PATH]`: TOML parse error at line 2, column 10
|
||||
|
|
||||
2 | format = "json"
|
||||
| ^^^^^^
|
||||
invalid type: string "json", expected struct FormatOptions
|
||||
|
||||
"###);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#![cfg(not(target_family = "wasm"))]
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::fs;
|
||||
#[cfg(unix)]
|
||||
use std::fs::Permissions;
|
||||
@@ -18,6 +19,7 @@ use clap::Parser;
|
||||
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||||
#[cfg(unix)]
|
||||
use path_absolutize::path_dedot;
|
||||
#[cfg(unix)]
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -45,16 +47,16 @@ fn stdin_success() {
|
||||
fn stdin_error() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.pass_stdin("import os\n"), @r###"
|
||||
.pass_stdin("import os\n"), @r#"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:8: F401 [*] `os` imported but unused
|
||||
Found 1 error.
|
||||
[*] 1 fixable with the `--fix` option.
|
||||
[*] 1 potentially fixable with the --fix option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -68,14 +70,14 @@ fn stdin_filename() {
|
||||
----- stdout -----
|
||||
F401.py:1:8: F401 [*] `os` imported but unused
|
||||
Found 1 error.
|
||||
[*] 1 fixable with the `--fix` option.
|
||||
[*] 1 potentially fixable with the --fix option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
}
|
||||
|
||||
/// Raise `TCH` errors in `.py` files ...
|
||||
#[test]
|
||||
/// Raise `TCH` errors in `.py` files ...
|
||||
fn stdin_source_type_py() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
@@ -86,7 +88,7 @@ fn stdin_source_type_py() {
|
||||
----- stdout -----
|
||||
TCH.py:1:8: F401 [*] `os` imported but unused
|
||||
Found 1 error.
|
||||
[*] 1 fixable with the `--fix` option.
|
||||
[*] 1 potentially fixable with the --fix option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
@@ -135,7 +137,7 @@ fn stdin_json() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stdin_fix_py() {
|
||||
fn stdin_autofix() {
|
||||
let args = ["--fix"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
@@ -149,180 +151,11 @@ fn stdin_fix_py() {
|
||||
print(sys.version)
|
||||
|
||||
----- stderr -----
|
||||
Found 1 error (1 fixed, 0 remaining).
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stdin_fix_jupyter() {
|
||||
let args = ["--fix", "--stdin-filename", "Jupyter.ipynb"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin(r#"{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "dccc687c-96e2-4604-b957-a8a89b5bec06",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "19e1b029-f516-4662-a9b9-623b93edac1a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Foo"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "cdce7b92-b0fb-4c02-86f6-e233b26fa84f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "e40b33d2-7fe4-46c5-bdf0-8802f3052565",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"1\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a1899bc8-d46f-4ec0-b1d1-e1ca0f04bf60",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}"#), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "dccc687c-96e2-4604-b957-a8a89b5bec06",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "19e1b029-f516-4662-a9b9-623b93edac1a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Foo"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "cdce7b92-b0fb-4c02-86f6-e233b26fa84f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "e40b33d2-7fe4-46c5-bdf0-8802f3052565",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"1\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a1899bc8-d46f-4ec0-b1d1-e1ca0f04bf60",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
----- stderr -----
|
||||
Found 2 errors (2 fixed, 0 remaining).
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stdin_fix_when_not_fixable_should_still_print_contents() {
|
||||
fn stdin_autofix_when_not_fixable_should_still_print_contents() {
|
||||
let args = ["--fix"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
@@ -337,13 +170,11 @@ fn stdin_fix_when_not_fixable_should_still_print_contents() {
|
||||
print(sys.version)
|
||||
|
||||
----- stderr -----
|
||||
-:3:4: F634 If test is a tuple, which is always `True`
|
||||
Found 2 errors (1 fixed, 1 remaining).
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stdin_fix_when_no_issues_should_still_print_contents() {
|
||||
fn stdin_autofix_when_no_issues_should_still_print_contents() {
|
||||
let args = ["--fix"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
@@ -360,135 +191,6 @@ fn stdin_fix_when_no_issues_should_still_print_contents() {
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stdin_format_jupyter() {
|
||||
let args = ["format", "--stdin-filename", "Jupyter.ipynb", "--isolated"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(args)
|
||||
.pass_stdin(r#"{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "dccc687c-96e2-4604-b957-a8a89b5bec06",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"x=1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "19e1b029-f516-4662-a9b9-623b93edac1a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Foo"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cdce7b92-b0fb-4c02-86f6-e233b26fa84f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def func():\n",
|
||||
" pass\n",
|
||||
"print(1)\n",
|
||||
"import os"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.13"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
"#), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "dccc687c-96e2-4604-b957-a8a89b5bec06",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"x = 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "19e1b029-f516-4662-a9b9-623b93edac1a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Foo"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cdce7b92-b0fb-4c02-86f6-e233b26fa84f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def func():\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"print(1)\n",
|
||||
"import os"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.13"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
warning: `ruff format` is not yet stable, and subject to change in future versions.
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_source() {
|
||||
let args = ["--show-source"];
|
||||
@@ -736,42 +438,6 @@ fn preview_disabled_prefix_empty() {
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preview_disabled_does_not_warn_for_empty_ignore_selections() {
|
||||
// Does not warn that the selection is empty since the user is not trying to enable the rule
|
||||
let args = ["--ignore", "CPY"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:1: E741 Ambiguous variable name: `I`
|
||||
Found 1 error.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preview_disabled_does_not_warn_for_empty_fixable_selections() {
|
||||
// Does not warn that the selection is empty since the user is not trying to enable the rule
|
||||
let args = ["--fixable", "CPY"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:1: E741 Ambiguous variable name: `I`
|
||||
Found 1 error.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preview_group_selector() {
|
||||
// `--select PREVIEW` should error (selector was removed)
|
||||
@@ -900,7 +566,7 @@ fn check_input_from_argfile() -> Result<()> {
|
||||
----- stdout -----
|
||||
/path/to/a.py:1:8: F401 [*] `os` imported but unused
|
||||
Found 1 error.
|
||||
[*] 1 fixable with the `--fix` option.
|
||||
[*] 1 potentially fixable with the --fix option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
@@ -908,499 +574,3 @@ fn check_input_from_argfile() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_hints_hidden_unsafe_fixes() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format=text",
|
||||
"--isolated",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
"--no-cache",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
-:2:7: UP034 [*] Avoid extraneous parentheses
|
||||
Found 2 errors.
|
||||
[*] 1 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option).
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_hints_hidden_unsafe_fixes_with_no_safe_fixes() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["-", "--output-format", "text", "--no-cache", "--isolated", "--select", "F601"])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
Found 1 error.
|
||||
No fixes available (1 hidden fix can be enabled with the `--unsafe-fixes` option).
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_shows_unsafe_fixes_with_opt_in() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format=text",
|
||||
"--isolated",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
"--no-cache",
|
||||
"--unsafe-fixes",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:14: F601 [*] Dictionary key literal `'a'` repeated
|
||||
-:2:7: UP034 [*] Avoid extraneous parentheses
|
||||
Found 2 errors.
|
||||
[*] 2 fixable with the --fix option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_applies_safe_fixes_by_default() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
"--fix",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
x = {'a': 1, 'a': 1}
|
||||
print('foo')
|
||||
|
||||
----- stderr -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
Found 2 errors (1 fixed, 1 remaining).
|
||||
No fixes available (1 hidden fix can be enabled with the `--unsafe-fixes` option).
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_applies_unsafe_fixes_with_opt_in() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
"--fix",
|
||||
"--unsafe-fixes",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
x = {'a': 1}
|
||||
print('foo')
|
||||
|
||||
----- stderr -----
|
||||
Found 2 errors (2 fixed, 0 remaining).
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_does_not_apply_display_only_fixes() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"B006",
|
||||
"--fix",
|
||||
])
|
||||
.pass_stdin("def add_to_list(item, some_list=[]): ..."),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
def add_to_list(item, some_list=[]): ...
|
||||
----- stderr -----
|
||||
-:1:33: B006 Do not use mutable data structures for argument defaults
|
||||
Found 1 error.
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_does_not_apply_display_only_fixes_with_unsafe_fixes_enabled() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"B006",
|
||||
"--fix",
|
||||
"--unsafe-fixes",
|
||||
])
|
||||
.pass_stdin("def add_to_list(item, some_list=[]): ..."),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
def add_to_list(item, some_list=[]): ...
|
||||
----- stderr -----
|
||||
-:1:33: B006 Do not use mutable data structures for argument defaults
|
||||
Found 1 error.
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_only_unsafe_fixes_available() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601",
|
||||
"--fix",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
x = {'a': 1, 'a': 1}
|
||||
print(('foo'))
|
||||
|
||||
----- stderr -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
Found 1 error.
|
||||
No fixes available (1 hidden fix can be enabled with the `--unsafe-fixes` option).
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_only_flag_applies_safe_fixes_by_default() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
"--fix-only",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
x = {'a': 1, 'a': 1}
|
||||
print('foo')
|
||||
|
||||
----- stderr -----
|
||||
Fixed 1 error (1 additional fix available with `--unsafe-fixes`).
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_only_flag_applies_unsafe_fixes_with_opt_in() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
"--fix-only",
|
||||
"--unsafe-fixes",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
x = {'a': 1}
|
||||
print('foo')
|
||||
|
||||
----- stderr -----
|
||||
Fixed 2 errors.
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_shows_safe_fixes_by_default() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
"--diff",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
@@ -1,2 +1,2 @@
|
||||
x = {'a': 1, 'a': 1}
|
||||
-print(('foo'))
|
||||
+print('foo')
|
||||
|
||||
|
||||
----- stderr -----
|
||||
Would fix 1 error (1 additional fix available with `--unsafe-fixes`).
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_shows_unsafe_fixes_with_opt_in() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
"--diff",
|
||||
"--unsafe-fixes",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
@@ -1,2 +1,2 @@
|
||||
-x = {'a': 1, 'a': 1}
|
||||
-print(('foo'))
|
||||
+x = {'a': 1}
|
||||
+print('foo')
|
||||
|
||||
|
||||
----- stderr -----
|
||||
Would fix 2 errors.
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_does_not_show_display_only_fixes_with_unsafe_fixes_enabled() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"B006",
|
||||
"--diff",
|
||||
"--unsafe-fixes",
|
||||
])
|
||||
.pass_stdin("def add_to_list(item, some_list=[]): ..."),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_only_unsafe_fixes_available() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601",
|
||||
"--diff",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
No errors would be fixed (1 fix available with `--unsafe-fixes`).
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_extend_unsafe_fixes() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
[lint]
|
||||
extend-unsafe-fixes = ["UP034"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["check", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.args([
|
||||
"--output-format",
|
||||
"text",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
-:2:7: UP034 Avoid extraneous parentheses
|
||||
Found 2 errors.
|
||||
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_extend_safe_fixes() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
[lint]
|
||||
extend-safe-fixes = ["F601"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["check", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.args([
|
||||
"--output-format",
|
||||
"text",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:14: F601 [*] Dictionary key literal `'a'` repeated
|
||||
-:2:7: UP034 [*] Avoid extraneous parentheses
|
||||
Found 2 errors.
|
||||
[*] 2 fixable with the `--fix` option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_extend_unsafe_fixes_conflict_with_extend_safe_fixes() -> Result<()> {
|
||||
// Adding a rule to both options should result in it being treated as unsafe
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
[lint]
|
||||
extend-unsafe-fixes = ["UP034"]
|
||||
extend-safe-fixes = ["UP034"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["check", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.args([
|
||||
"--output-format",
|
||||
"text",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
-:2:7: UP034 Avoid extraneous parentheses
|
||||
Found 2 errors.
|
||||
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
//! Tests the interaction of the `lint` configuration section
|
||||
|
||||
#![cfg(not(target_family = "wasm"))]
|
||||
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
use std::str;
|
||||
|
||||
use anyhow::Result;
|
||||
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||||
use tempfile::TempDir;
|
||||
|
||||
const BIN_NAME: &str = "ruff";
|
||||
const STDIN_BASE_OPTIONS: &[&str] = &["--no-cache", "--output-format", "text"];
|
||||
|
||||
#[test]
|
||||
fn top_level_options() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
extend-select = ["B", "Q"]
|
||||
|
||||
[flake8-quotes]
|
||||
inline-quotes = "single"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.arg("--config")
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r#"a = "abcba".strip("aba")"#), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:5: Q000 [*] Double quotes found but single quotes preferred
|
||||
-:1:5: B005 Using `.strip()` with multi-character strings is misleading
|
||||
-:1:19: Q000 [*] Double quotes found but single quotes preferred
|
||||
Found 3 errors.
|
||||
[*] 2 fixable with the `--fix` option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lint_options() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
[lint]
|
||||
extend-select = ["B", "Q"]
|
||||
|
||||
[lint.flake8-quotes]
|
||||
inline-quotes = "single"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.arg("--config")
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r#"a = "abcba".strip("aba")"#), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:5: Q000 [*] Double quotes found but single quotes preferred
|
||||
-:1:5: B005 Using `.strip()` with multi-character strings is misleading
|
||||
-:1:19: Q000 [*] Double quotes found but single quotes preferred
|
||||
Found 3 errors.
|
||||
[*] 2 fixable with the `--fix` option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests that configurations from the top-level and `lint` section are merged together.
|
||||
#[test]
|
||||
fn mixed_levels() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
extend-select = ["B", "Q"]
|
||||
|
||||
[lint.flake8-quotes]
|
||||
inline-quotes = "single"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.arg("--config")
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r#"a = "abcba".strip("aba")"#), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:5: Q000 [*] Double quotes found but single quotes preferred
|
||||
-:1:5: B005 Using `.strip()` with multi-character strings is misleading
|
||||
-:1:19: Q000 [*] Double quotes found but single quotes preferred
|
||||
Found 3 errors.
|
||||
[*] 2 fixable with the `--fix` option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests that options in the `lint` section have higher precedence than top-level options (because they are more specific).
|
||||
#[test]
|
||||
fn precedence() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
[lint]
|
||||
extend-select = ["B", "Q"]
|
||||
|
||||
[flake8-quotes]
|
||||
inline-quotes = "double"
|
||||
|
||||
[lint.flake8-quotes]
|
||||
inline-quotes = "single"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.arg("--config")
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r#"a = "abcba".strip("aba")"#), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:5: Q000 [*] Double quotes found but single quotes preferred
|
||||
-:1:5: B005 Using `.strip()` with multi-character strings is misleading
|
||||
-:1:19: Q000 [*] Double quotes found but single quotes preferred
|
||||
Found 3 errors.
|
||||
[*] 2 fixable with the `--fix` option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
@@ -13,7 +13,7 @@ exit_code: 0
|
||||
|
||||
Derived from the **Pyflakes** linter.
|
||||
|
||||
Fix is sometimes available.
|
||||
Autofix is sometimes available.
|
||||
|
||||
## What it does
|
||||
Checks for unused imports.
|
||||
|
||||
@@ -6,7 +6,7 @@ info:
|
||||
- "-"
|
||||
- "--isolated"
|
||||
- "--no-cache"
|
||||
- "--output-format"
|
||||
- "--format"
|
||||
- json
|
||||
- "--stdin-filename"
|
||||
- F401.py
|
||||
@@ -17,7 +17,6 @@ exit_code: 1
|
||||
----- stdout -----
|
||||
[
|
||||
{
|
||||
"cell": null,
|
||||
"code": "F401",
|
||||
"end_location": {
|
||||
"column": 10,
|
||||
@@ -25,7 +24,7 @@ exit_code: 1
|
||||
},
|
||||
"filename": "/path/to/F401.py",
|
||||
"fix": {
|
||||
"applicability": "safe",
|
||||
"applicability": "Automatic",
|
||||
"edits": [
|
||||
{
|
||||
"content": "",
|
||||
|
||||
@@ -28,12 +28,12 @@ ruff_workspace = { path = "../ruff_workspace", features = ["schemars"]}
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
ignore = { workspace = true }
|
||||
indicatif = "0.17.7"
|
||||
indicatif = "0.17.5"
|
||||
itertools = { workspace = true }
|
||||
libcst = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
pretty_assertions = { version = "1.3.0" }
|
||||
rayon = "1.8.0"
|
||||
rayon = "1.7.0"
|
||||
regex = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
@@ -34,7 +34,7 @@ use ruff_formatter::{FormatError, LineWidth, PrintError};
|
||||
use ruff_linter::logging::LogLevel;
|
||||
use ruff_linter::settings::types::{FilePattern, FilePatternSet};
|
||||
use ruff_python_formatter::{
|
||||
format_module_source, FormatModuleError, MagicTrailingComma, PyFormatOptions,
|
||||
format_module, FormatModuleError, MagicTrailingComma, PyFormatOptions,
|
||||
};
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, Resolver};
|
||||
|
||||
@@ -549,6 +549,7 @@ fn format_dir_entry(
|
||||
|
||||
let settings = resolver.resolve(&path, pyproject_config);
|
||||
// That's a bad way of doing this but it's not worth doing something better for format_dev
|
||||
// TODO(micha) use formatter settings instead
|
||||
if settings.formatter.line_width != LineWidth::default() {
|
||||
options = options.with_line_width(settings.formatter.line_width);
|
||||
}
|
||||
@@ -799,7 +800,7 @@ fn format_dev_file(
|
||||
let content = fs::read_to_string(input_path)?;
|
||||
#[cfg(not(debug_assertions))]
|
||||
let start = Instant::now();
|
||||
let printed = match format_module_source(&content, options.clone()) {
|
||||
let printed = match format_module(&content, options.clone()) {
|
||||
Ok(printed) => printed,
|
||||
Err(err @ (FormatModuleError::LexError(_) | FormatModuleError::ParseError(_))) => {
|
||||
return Err(CheckFileError::SyntaxErrorInInput(err));
|
||||
@@ -826,7 +827,7 @@ fn format_dev_file(
|
||||
}
|
||||
|
||||
if stability_check {
|
||||
let reformatted = match format_module_source(formatted, options) {
|
||||
let reformatted = match format_module(formatted, options) {
|
||||
Ok(reformatted) => reformatted,
|
||||
Err(err @ (FormatModuleError::LexError(_) | FormatModuleError::ParseError(_))) => {
|
||||
return Err(CheckFileError::SyntaxErrorInOutput {
|
||||
|
||||
@@ -8,10 +8,9 @@ use anyhow::Result;
|
||||
use regex::{Captures, Regex};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use ruff_diagnostics::FixAvailability;
|
||||
use ruff_diagnostics::AutofixKind;
|
||||
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
|
||||
use ruff_workspace::options::Options;
|
||||
use ruff_workspace::options_base::OptionsMetadata;
|
||||
|
||||
use crate::ROOT_DIR;
|
||||
|
||||
@@ -37,19 +36,16 @@ pub(crate) fn main(args: &Args) -> Result<()> {
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
let fix_availability = rule.fixable();
|
||||
if matches!(
|
||||
fix_availability,
|
||||
FixAvailability::Always | FixAvailability::Sometimes
|
||||
) {
|
||||
output.push_str(&fix_availability.to_string());
|
||||
let autofix = rule.autofixable();
|
||||
if matches!(autofix, AutofixKind::Always | AutofixKind::Sometimes) {
|
||||
output.push_str(&autofix.to_string());
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
if rule.is_preview() || rule.is_nursery() {
|
||||
if rule.is_preview() {
|
||||
output.push_str(
|
||||
r#"This rule is unstable and in [preview](../preview.md). The `--preview` flag is required for use."#,
|
||||
r#"This rule is in preview and is not stable. The `--preview` flag is required for use."#,
|
||||
);
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
@@ -100,7 +96,10 @@ fn process_documentation(documentation: &str, out: &mut String) {
|
||||
if let Some(rest) = line.strip_prefix("- `") {
|
||||
let option = rest.trim_end().trim_end_matches('`');
|
||||
|
||||
assert!(Options::metadata().has(option), "unknown option {option}");
|
||||
assert!(
|
||||
Options::metadata().get(option).is_some(),
|
||||
"unknown option {option}"
|
||||
);
|
||||
|
||||
let anchor = option.replace('.', "-");
|
||||
out.push_str(&format!("- [`{option}`][{option}]\n"));
|
||||
|
||||
@@ -1,104 +1,22 @@
|
||||
//! Generate a Markdown-compatible listing of configuration options for `pyproject.toml`.
|
||||
//!
|
||||
//! Used for <https://docs.astral.sh/ruff/settings/>.
|
||||
use std::fmt::Write;
|
||||
|
||||
use itertools::Itertools;
|
||||
use ruff_workspace::options::Options;
|
||||
use ruff_workspace::options_base::{OptionField, OptionSet, OptionsMetadata, Visit};
|
||||
use ruff_workspace::options_base::{OptionEntry, OptionField};
|
||||
|
||||
pub(crate) fn generate() -> String {
|
||||
let mut output = String::new();
|
||||
generate_set(&mut output, &Set::Toplevel(Options::metadata()));
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn generate_set(output: &mut String, set: &Set) {
|
||||
if set.level() < 2 {
|
||||
writeln!(output, "### {title}\n", title = set.title()).unwrap();
|
||||
} else {
|
||||
writeln!(output, "#### {title}\n", title = set.title()).unwrap();
|
||||
}
|
||||
|
||||
if let Some(documentation) = set.metadata().documentation() {
|
||||
output.push_str(documentation);
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
let mut visitor = CollectOptionsVisitor::default();
|
||||
set.metadata().record(&mut visitor);
|
||||
|
||||
let (mut fields, mut sets) = (visitor.fields, visitor.groups);
|
||||
|
||||
fields.sort_unstable_by(|(name, _), (name2, _)| name.cmp(name2));
|
||||
sets.sort_unstable_by(|(name, _), (name2, _)| name.cmp(name2));
|
||||
|
||||
// Generate the fields.
|
||||
for (name, field) in &fields {
|
||||
emit_field(output, name, field, set);
|
||||
output.push_str("---\n\n");
|
||||
}
|
||||
|
||||
// Generate all the sub-sets.
|
||||
for (set_name, sub_set) in &sets {
|
||||
generate_set(output, &Set::Named(set_name, *sub_set, set.level() + 1));
|
||||
}
|
||||
}
|
||||
|
||||
enum Set<'a> {
|
||||
Toplevel(OptionSet),
|
||||
Named(&'a str, OptionSet, u32),
|
||||
}
|
||||
|
||||
impl<'a> Set<'a> {
|
||||
fn name(&self) -> Option<&'a str> {
|
||||
match self {
|
||||
Set::Toplevel(_) => None,
|
||||
Set::Named(name, _, _) => Some(name),
|
||||
}
|
||||
}
|
||||
|
||||
fn title(&self) -> &'a str {
|
||||
match self {
|
||||
Set::Toplevel(_) => "Top-level",
|
||||
Set::Named(name, _, _) => name,
|
||||
}
|
||||
}
|
||||
|
||||
fn metadata(&self) -> &OptionSet {
|
||||
match self {
|
||||
Set::Toplevel(set) => set,
|
||||
Set::Named(_, set, _) => set,
|
||||
}
|
||||
}
|
||||
|
||||
fn level(&self) -> u32 {
|
||||
match self {
|
||||
Set::Toplevel(_) => 0,
|
||||
Set::Named(_, _, level) => *level,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_field(output: &mut String, name: &str, field: &OptionField, parent_set: &Set) {
|
||||
let header_level = if parent_set.level() < 2 {
|
||||
"####"
|
||||
} else {
|
||||
"#####"
|
||||
};
|
||||
|
||||
// if there's a set name, we need to add it to the anchor
|
||||
if let Some(set_name) = parent_set.name() {
|
||||
fn emit_field(output: &mut String, name: &str, field: &OptionField, group_name: Option<&str>) {
|
||||
// if there's a group name, we need to add it to the anchor
|
||||
if let Some(group_name) = group_name {
|
||||
// the anchor used to just be the name, but now it's the group name
|
||||
// for backwards compatibility, we need to keep the old anchor
|
||||
output.push_str(&format!("<span id=\"{name}\"></span>\n"));
|
||||
|
||||
output.push_str(&format!(
|
||||
"{header_level} [`{name}`](#{set_name}-{name}) {{: #{set_name}-{name} }}\n"
|
||||
"#### [`{name}`](#{group_name}-{name}) {{: #{group_name}-{name} }}\n"
|
||||
));
|
||||
} else {
|
||||
output.push_str(&format!("{header_level} [`{name}`](#{name})\n"));
|
||||
output.push_str(&format!("#### [`{name}`](#{name})\n"));
|
||||
}
|
||||
output.push('\n');
|
||||
output.push_str(field.doc);
|
||||
@@ -109,8 +27,8 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, parent_set:
|
||||
output.push('\n');
|
||||
output.push_str(&format!(
|
||||
"**Example usage**:\n\n```toml\n[tool.ruff{}]\n{}\n```\n",
|
||||
if let Some(set_name) = parent_set.name() {
|
||||
format!(".{set_name}")
|
||||
if group_name.is_some() {
|
||||
format!(".{}", group_name.unwrap())
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
@@ -119,18 +37,38 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, parent_set:
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CollectOptionsVisitor {
|
||||
groups: Vec<(String, OptionSet)>,
|
||||
fields: Vec<(String, OptionField)>,
|
||||
}
|
||||
pub(crate) fn generate() -> String {
|
||||
let mut output: String = "### Top-level\n\n".into();
|
||||
|
||||
impl Visit for CollectOptionsVisitor {
|
||||
fn record_set(&mut self, name: &str, group: OptionSet) {
|
||||
self.groups.push((name.to_owned(), group));
|
||||
let sorted_options: Vec<_> = Options::metadata()
|
||||
.into_iter()
|
||||
.sorted_by_key(|(name, _)| *name)
|
||||
.collect();
|
||||
|
||||
// Generate all the top-level fields.
|
||||
for (name, entry) in &sorted_options {
|
||||
let OptionEntry::Field(field) = entry else {
|
||||
continue;
|
||||
};
|
||||
emit_field(&mut output, name, field, None);
|
||||
output.push_str("---\n\n");
|
||||
}
|
||||
|
||||
fn record_field(&mut self, name: &str, field: OptionField) {
|
||||
self.fields.push((name.to_owned(), field));
|
||||
// Generate all the sub-groups.
|
||||
for (group_name, entry) in &sorted_options {
|
||||
let OptionEntry::Group(fields) = entry else {
|
||||
continue;
|
||||
};
|
||||
output.push_str(&format!("### {group_name}\n"));
|
||||
output.push('\n');
|
||||
for (name, entry) in fields.iter().sorted_by_key(|(name, _)| name) {
|
||||
let OptionEntry::Field(field) = entry else {
|
||||
continue;
|
||||
};
|
||||
emit_field(&mut output, name, field, Some(group_name));
|
||||
output.push_str("---\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
use itertools::Itertools;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use ruff_diagnostics::FixAvailability;
|
||||
use ruff_diagnostics::AutofixKind;
|
||||
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
|
||||
use ruff_linter::upstream_categories::UpstreamCategoryAndPrefix;
|
||||
use ruff_workspace::options::Options;
|
||||
use ruff_workspace::options_base::OptionsMetadata;
|
||||
|
||||
const FIX_SYMBOL: &str = "🛠️";
|
||||
const PREVIEW_SYMBOL: &str = "🧪";
|
||||
@@ -20,11 +19,11 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>,
|
||||
table_out.push_str("| ---- | ---- | ------- | ------: |");
|
||||
table_out.push('\n');
|
||||
for rule in rules {
|
||||
let fix_token = match rule.fixable() {
|
||||
FixAvailability::Always | FixAvailability::Sometimes => {
|
||||
let fix_token = match rule.autofixable() {
|
||||
AutofixKind::Always | AutofixKind::Sometimes => {
|
||||
format!("<span style='opacity: 1'>{FIX_SYMBOL}</span>")
|
||||
}
|
||||
FixAvailability::None => format!("<span style='opacity: 0.1'>{FIX_SYMBOL}</span>"),
|
||||
AutofixKind::None => format!("<span style='opacity: 0.1'>{FIX_SYMBOL}</span>"),
|
||||
};
|
||||
let preview_token = if rule.is_preview() || rule.is_nursery() {
|
||||
format!("<span style='opacity: 1'>{PREVIEW_SYMBOL}</span>")
|
||||
@@ -105,7 +104,10 @@ pub(crate) fn generate() -> String {
|
||||
table_out.push('\n');
|
||||
}
|
||||
|
||||
if Options::metadata().has(linter.name()) {
|
||||
if Options::metadata()
|
||||
.iter()
|
||||
.any(|(name, _)| name == &linter.name())
|
||||
{
|
||||
table_out.push_str(&format!(
|
||||
"For related settings, see [{}](settings.md#{}).",
|
||||
linter.name(),
|
||||
|
||||
@@ -17,5 +17,4 @@ ruff_text_size = { path = "../ruff_text_size" }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
log = { workspace = true }
|
||||
is-macro = { workspace = true }
|
||||
serde = { workspace = true, optional = true, features = [] }
|
||||
|
||||
@@ -5,22 +5,27 @@ use ruff_text_size::{Ranged, TextSize};
|
||||
|
||||
use crate::edit::Edit;
|
||||
|
||||
/// Indicates if a fix can be applied.
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, is_macro::Is)]
|
||||
/// Indicates confidence in the correctness of a suggested fix.
|
||||
#[derive(Default, Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
|
||||
pub enum Applicability {
|
||||
/// The fix is unsafe and should only be displayed for manual application by the user.
|
||||
/// The fix is likely to be incorrect or the resulting code may have invalid syntax.
|
||||
Display,
|
||||
/// The fix is definitely what the user intended, or maintains the exact meaning of the code.
|
||||
/// This fix should be automatically applied.
|
||||
Automatic,
|
||||
|
||||
/// The fix is unsafe and should only be applied with user opt-in.
|
||||
/// The fix may be what the user intended, but it is uncertain; the resulting code will have valid syntax.
|
||||
Unsafe,
|
||||
/// The fix may be what the user intended, but it is uncertain.
|
||||
/// The fix should result in valid code if it is applied.
|
||||
/// The fix can be applied with user opt-in.
|
||||
Suggested,
|
||||
|
||||
/// The fix is safe and can always be applied.
|
||||
/// The fix is definitely what the user intended, or it maintains the exact meaning of the code.
|
||||
Safe,
|
||||
/// The fix has a good chance of being incorrect or the code be incomplete.
|
||||
/// The fix may result in invalid code if it is applied.
|
||||
/// The fix should only be manually applied by the user.
|
||||
Manual,
|
||||
|
||||
/// The applicability of the fix is unknown.
|
||||
#[default]
|
||||
Unspecified,
|
||||
}
|
||||
|
||||
/// Indicates the level of isolation required to apply a fix.
|
||||
@@ -47,62 +52,86 @@ pub struct Fix {
|
||||
}
|
||||
|
||||
impl Fix {
|
||||
/// Create a new [`Fix`] that is [safe](Applicability::Safe) to apply from an [`Edit`] element.
|
||||
pub fn safe_edit(edit: Edit) -> Self {
|
||||
/// Create a new [`Fix`] with an unspecified applicability from an [`Edit`] element.
|
||||
#[deprecated(
|
||||
note = "Use `Fix::automatic`, `Fix::suggested`, or `Fix::manual` instead to specify an applicability."
|
||||
)]
|
||||
pub fn unspecified(edit: Edit) -> Self {
|
||||
Self {
|
||||
edits: vec![edit],
|
||||
applicability: Applicability::Safe,
|
||||
applicability: Applicability::Unspecified,
|
||||
isolation_level: IsolationLevel::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`Fix`] that is [safe](Applicability::Safe) to apply from multiple [`Edit`] elements.
|
||||
pub fn safe_edits(edit: Edit, rest: impl IntoIterator<Item = Edit>) -> Self {
|
||||
let mut edits: Vec<Edit> = std::iter::once(edit).chain(rest).collect();
|
||||
edits.sort_by_key(|edit| (edit.start(), edit.end()));
|
||||
/// Create a new [`Fix`] with an unspecified applicability from multiple [`Edit`] elements.
|
||||
#[deprecated(
|
||||
note = "Use `Fix::automatic_edits`, `Fix::suggested_edits`, or `Fix::manual_edits` instead to specify an applicability."
|
||||
)]
|
||||
pub fn unspecified_edits(edit: Edit, rest: impl IntoIterator<Item = Edit>) -> Self {
|
||||
Self {
|
||||
edits,
|
||||
applicability: Applicability::Safe,
|
||||
edits: std::iter::once(edit).chain(rest).collect(),
|
||||
applicability: Applicability::Unspecified,
|
||||
isolation_level: IsolationLevel::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`Fix`] that is [unsafe](Applicability::Unsafe) to apply from an [`Edit`] element.
|
||||
pub fn unsafe_edit(edit: Edit) -> Self {
|
||||
/// Create a new [`Fix`] with [automatic applicability](Applicability::Automatic) from an [`Edit`] element.
|
||||
pub fn automatic(edit: Edit) -> Self {
|
||||
Self {
|
||||
edits: vec![edit],
|
||||
applicability: Applicability::Unsafe,
|
||||
applicability: Applicability::Automatic,
|
||||
isolation_level: IsolationLevel::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`Fix`] that is [unsafe](Applicability::Unsafe) to apply from multiple [`Edit`] elements.
|
||||
pub fn unsafe_edits(edit: Edit, rest: impl IntoIterator<Item = Edit>) -> Self {
|
||||
/// Create a new [`Fix`] with [automatic applicability](Applicability::Automatic) from multiple [`Edit`] elements.
|
||||
pub fn automatic_edits(edit: Edit, rest: impl IntoIterator<Item = Edit>) -> Self {
|
||||
let mut edits: Vec<Edit> = std::iter::once(edit).chain(rest).collect();
|
||||
edits.sort_by_key(|edit| (edit.start(), edit.end()));
|
||||
edits.sort_by_key(Ranged::start);
|
||||
Self {
|
||||
edits,
|
||||
applicability: Applicability::Unsafe,
|
||||
applicability: Applicability::Automatic,
|
||||
isolation_level: IsolationLevel::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`Fix`] that should only [display](Applicability::Display) and not apply from an [`Edit`] element .
|
||||
pub fn display_edit(edit: Edit) -> Self {
|
||||
/// Create a new [`Fix`] with [suggested applicability](Applicability::Suggested) from an [`Edit`] element.
|
||||
pub fn suggested(edit: Edit) -> Self {
|
||||
Self {
|
||||
edits: vec![edit],
|
||||
applicability: Applicability::Display,
|
||||
applicability: Applicability::Suggested,
|
||||
isolation_level: IsolationLevel::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`Fix`] that should only [display](Applicability::Display) and not apply from multiple [`Edit`] elements.
|
||||
pub fn display_edits(edit: Edit, rest: impl IntoIterator<Item = Edit>) -> Self {
|
||||
/// Create a new [`Fix`] with [suggested applicability](Applicability::Suggested) from multiple [`Edit`] elements.
|
||||
pub fn suggested_edits(edit: Edit, rest: impl IntoIterator<Item = Edit>) -> Self {
|
||||
let mut edits: Vec<Edit> = std::iter::once(edit).chain(rest).collect();
|
||||
edits.sort_by_key(|edit| (edit.start(), edit.end()));
|
||||
edits.sort_by_key(Ranged::start);
|
||||
Self {
|
||||
edits,
|
||||
applicability: Applicability::Display,
|
||||
applicability: Applicability::Suggested,
|
||||
isolation_level: IsolationLevel::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`Fix`] with [manual applicability](Applicability::Manual) from an [`Edit`] element.
|
||||
pub fn manual(edit: Edit) -> Self {
|
||||
Self {
|
||||
edits: vec![edit],
|
||||
applicability: Applicability::Manual,
|
||||
isolation_level: IsolationLevel::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`Fix`] with [manual applicability](Applicability::Manual) from multiple [`Edit`] elements.
|
||||
pub fn manual_edits(edit: Edit, rest: impl IntoIterator<Item = Edit>) -> Self {
|
||||
let mut edits: Vec<Edit> = std::iter::once(edit).chain(rest).collect();
|
||||
edits.sort_by_key(Ranged::start);
|
||||
Self {
|
||||
edits,
|
||||
applicability: Applicability::Manual,
|
||||
isolation_level: IsolationLevel::default(),
|
||||
}
|
||||
}
|
||||
@@ -133,16 +162,4 @@ impl Fix {
|
||||
self.isolation_level = isolation;
|
||||
self
|
||||
}
|
||||
|
||||
/// Return [`true`] if this [`Fix`] should be applied with at a given [`Applicability`].
|
||||
pub fn applies(&self, applicability: Applicability) -> bool {
|
||||
self.applicability >= applicability
|
||||
}
|
||||
|
||||
/// Create a new [`Fix`] with the given [`Applicability`].
|
||||
#[must_use]
|
||||
pub fn with_applicability(mut self, applicability: Applicability) -> Self {
|
||||
self.applicability = applicability;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ pub use diagnostic::{Diagnostic, DiagnosticKind};
|
||||
pub use edit::Edit;
|
||||
pub use fix::{Applicability, Fix, IsolationLevel};
|
||||
pub use source_map::{SourceMap, SourceMarker};
|
||||
pub use violation::{AlwaysFixableViolation, FixAvailability, Violation};
|
||||
pub use violation::{AlwaysAutofixableViolation, AutofixKind, Violation};
|
||||
|
||||
mod diagnostic;
|
||||
mod edit;
|
||||
|
||||
@@ -46,7 +46,10 @@ impl SourceMap {
|
||||
/// The `output_length` is the length of the transformed string before the
|
||||
/// edit is applied.
|
||||
pub fn push_start_marker(&mut self, edit: &Edit, output_length: TextSize) {
|
||||
self.push_marker(edit.start(), output_length);
|
||||
self.0.push(SourceMarker {
|
||||
source: edit.start(),
|
||||
dest: output_length,
|
||||
});
|
||||
}
|
||||
|
||||
/// Push the end marker for an [`Edit`].
|
||||
@@ -55,18 +58,16 @@ impl SourceMap {
|
||||
/// edit has been applied.
|
||||
pub fn push_end_marker(&mut self, edit: &Edit, output_length: TextSize) {
|
||||
if edit.is_insertion() {
|
||||
self.push_marker(edit.start(), output_length);
|
||||
self.0.push(SourceMarker {
|
||||
source: edit.start(),
|
||||
dest: output_length,
|
||||
});
|
||||
} else {
|
||||
// Deletion or replacement
|
||||
self.push_marker(edit.end(), output_length);
|
||||
self.0.push(SourceMarker {
|
||||
source: edit.end(),
|
||||
dest: output_length,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Push a new marker to the sourcemap.
|
||||
pub fn push_marker(&mut self, offset: TextSize, output_length: TextSize) {
|
||||
self.0.push(SourceMarker {
|
||||
source: offset,
|
||||
dest: output_length,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum FixAvailability {
|
||||
pub enum AutofixKind {
|
||||
Sometimes,
|
||||
Always,
|
||||
None,
|
||||
}
|
||||
|
||||
impl Display for FixAvailability {
|
||||
impl Display for AutofixKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
FixAvailability::Sometimes => write!(f, "Fix is sometimes available."),
|
||||
FixAvailability::Always => write!(f, "Fix is always available."),
|
||||
FixAvailability::None => write!(f, "Fix is not available."),
|
||||
AutofixKind::Sometimes => write!(f, "Autofix is sometimes available."),
|
||||
AutofixKind::Always => write!(f, "Autofix is always available."),
|
||||
AutofixKind::None => write!(f, "Autofix is not available."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Violation: Debug + PartialEq + Eq {
|
||||
/// `None` in the case an fix is never available or otherwise Some
|
||||
/// [`FixAvailability`] describing the available fix.
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::None;
|
||||
/// `None` in the case an autofix is never available or otherwise Some
|
||||
/// [`AutofixKind`] describing the available autofix.
|
||||
const AUTOFIX: AutofixKind = AutofixKind::None;
|
||||
|
||||
/// The message used to describe the violation.
|
||||
fn message(&self) -> String;
|
||||
@@ -30,13 +30,13 @@ pub trait Violation: Debug + PartialEq + Eq {
|
||||
None
|
||||
}
|
||||
|
||||
// TODO(micha): Move `fix_title` to `Fix`, add new `advice` method that is shown as an advice.
|
||||
// TODO(micha): Move `autofix_title` to `Fix`, add new `advice` method that is shown as an advice.
|
||||
// Change the `Diagnostic` renderer to show the advice, and render the fix message after the `Suggested fix: <here>`
|
||||
|
||||
/// Returns the title for the fix. The message is also shown as an advice as part of the diagnostics.
|
||||
/// Returns the title for the autofix. The message is also shown as an advice as part of the diagnostics.
|
||||
///
|
||||
/// Required for rules that have fixes.
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
/// Required for rules that have autofixes.
|
||||
fn autofix_title(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ pub trait Violation: Debug + PartialEq + Eq {
|
||||
}
|
||||
|
||||
/// This trait exists just to make implementing the [`Violation`] trait more
|
||||
/// convenient for violations that can always be fixed.
|
||||
pub trait AlwaysFixableViolation: Debug + PartialEq + Eq {
|
||||
/// convenient for violations that can always be autofixed.
|
||||
pub trait AlwaysAutofixableViolation: Debug + PartialEq + Eq {
|
||||
/// The message used to describe the violation.
|
||||
fn message(&self) -> String;
|
||||
|
||||
@@ -55,31 +55,31 @@ pub trait AlwaysFixableViolation: Debug + PartialEq + Eq {
|
||||
None
|
||||
}
|
||||
|
||||
/// The title displayed for the available fix.
|
||||
fn fix_title(&self) -> String;
|
||||
/// The title displayed for the available autofix.
|
||||
fn autofix_title(&self) -> String;
|
||||
|
||||
/// Returns the format strings used by
|
||||
/// [`message`](AlwaysFixableViolation::message).
|
||||
/// [`message`](AlwaysAutofixableViolation::message).
|
||||
fn message_formats() -> &'static [&'static str];
|
||||
}
|
||||
|
||||
/// A blanket implementation.
|
||||
impl<V: AlwaysFixableViolation> Violation for V {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Always;
|
||||
impl<VA: AlwaysAutofixableViolation> Violation for VA {
|
||||
const AUTOFIX: AutofixKind = AutofixKind::Always;
|
||||
|
||||
fn message(&self) -> String {
|
||||
<Self as AlwaysFixableViolation>::message(self)
|
||||
<Self as AlwaysAutofixableViolation>::message(self)
|
||||
}
|
||||
|
||||
fn explanation() -> Option<&'static str> {
|
||||
<Self as AlwaysFixableViolation>::explanation()
|
||||
<Self as AlwaysAutofixableViolation>::explanation()
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
Some(<Self as AlwaysFixableViolation>::fix_title(self))
|
||||
fn autofix_title(&self) -> Option<String> {
|
||||
Some(<Self as AlwaysAutofixableViolation>::autofix_title(self))
|
||||
}
|
||||
|
||||
fn message_formats() -> &'static [&'static str] {
|
||||
<Self as AlwaysFixableViolation>::message_formats()
|
||||
<Self as AlwaysAutofixableViolation>::message_formats()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ rustc-hash = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
static_assertions = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing = { version = "0.1.37", default-features = false, features = ["std"] }
|
||||
unicode-width = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -55,11 +55,7 @@ use ruff_macros::CacheKey;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, CacheKey)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
serde(rename_all = "kebab-case")
|
||||
)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
#[derive(Default)]
|
||||
pub enum IndentStyle {
|
||||
|
||||
@@ -54,7 +54,7 @@ impl<'a> Printer<'a> {
|
||||
|
||||
/// Prints the passed in element as well as all its content,
|
||||
/// starting at the specified indentation level
|
||||
#[tracing::instrument(level = "debug", name = "Printer::print", skip_all)]
|
||||
#[tracing::instrument(name = "Printer::print", skip_all)]
|
||||
pub fn print_with_indent(
|
||||
mut self,
|
||||
document: &'a Document,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::{FormatOptions, IndentStyle, IndentWidth, LineWidth};
|
||||
use ruff_macros::CacheKey;
|
||||
|
||||
/// Options that affect how the [`crate::Printer`] prints the format tokens
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Default)]
|
||||
@@ -120,7 +121,7 @@ impl SourceMapGeneration {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, CacheKey)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum LineEnding {
|
||||
/// Line Feed only (\n), common on Linux and macOS as well as inside git repos
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.1.0"
|
||||
version = "0.0.290"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -29,7 +29,6 @@ ruff_python_parser = { path = "../ruff_python_parser" }
|
||||
ruff_source_file = { path = "../ruff_source_file", features = ["serde"] }
|
||||
ruff_text_size = { path = "../ruff_text_size" }
|
||||
|
||||
aho-corasick = { version = "1.1.2" }
|
||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||
anyhow = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
@@ -46,20 +45,22 @@ libcst = { workspace = true }
|
||||
log = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
natord = { version = "1.0.9" }
|
||||
num-bigint = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
path-absolutize = { workspace = true, features = [
|
||||
"once_cell_cache",
|
||||
"use_unix_paths_on_wasm",
|
||||
] }
|
||||
pathdiff = { version = "0.2.1" }
|
||||
pep440_rs = { version = "0.3.12", features = ["serde"] }
|
||||
pep440_rs = { version = "0.3.1", features = ["serde"] }
|
||||
pyproject-toml = { version = "0.7.0" }
|
||||
quick-junit = { version = "0.3.2" }
|
||||
regex = { workspace = true }
|
||||
result-like = { version = "0.4.6" }
|
||||
rustc-hash = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
semver = { version = "1.0.20" }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
similar = { workspace = true }
|
||||
@@ -70,7 +71,7 @@ thiserror = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unicode-width = { workspace = true }
|
||||
unicode_names2 = { workspace = true }
|
||||
unicode_names2 = { version = "0.6.0", git = "https://github.com/youknowone/unicode_names2.git", rev = "4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde" }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -158,9 +158,3 @@ class Foo:
|
||||
@decorator()
|
||||
def __init__(self: "Foo", foo: int):
|
||||
...
|
||||
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7711
|
||||
class Class:
|
||||
def __init__(self):
|
||||
print(f"{self.attr=}")
|
||||
|
||||
@@ -20,4 +20,3 @@ os.chmod(keyfile, stat.S_IRWXO | stat.S_IRWXG | stat.S_IRWXU) # Error
|
||||
os.chmod("~/hidden_exec", stat.S_IXGRP) # Error
|
||||
os.chmod("~/hidden_exec", stat.S_IXOTH) # OK
|
||||
os.chmod("/etc/passwd", stat.S_IWOTH) # Error
|
||||
os.chmod("/etc/passwd", 0o100000000) # Error
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
from cryptography.hazmat import backends
|
||||
from cryptography.hazmat.primitives.asymmetric import dsa
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from Crypto.PublicKey import DSA as pycrypto_dsa
|
||||
from Crypto.PublicKey import RSA as pycrypto_rsa
|
||||
from Cryptodome.PublicKey import DSA as pycryptodomex_dsa
|
||||
from Cryptodome.PublicKey import RSA as pycryptodomex_rsa
|
||||
|
||||
# OK
|
||||
dsa.generate_private_key(key_size=2048, backend=backends.default_backend())
|
||||
ec.generate_private_key(curve=ec.SECP384R1, backend=backends.default_backend())
|
||||
rsa.generate_private_key(
|
||||
public_exponent=65537, key_size=2048, backend=backends.default_backend()
|
||||
)
|
||||
pycrypto_dsa.generate(bits=2048)
|
||||
pycrypto_rsa.generate(bits=2048)
|
||||
pycryptodomex_dsa.generate(bits=2048)
|
||||
pycryptodomex_rsa.generate(bits=2048)
|
||||
dsa.generate_private_key(2048, backends.default_backend())
|
||||
ec.generate_private_key(ec.SECP256K1, backends.default_backend())
|
||||
rsa.generate_private_key(3, 2048, backends.default_backend())
|
||||
pycrypto_dsa.generate(2048)
|
||||
pycrypto_rsa.generate(2048)
|
||||
pycryptodomex_dsa.generate(2048)
|
||||
pycryptodomex_rsa.generate(2048)
|
||||
|
||||
# Errors
|
||||
dsa.generate_private_key(key_size=2047, backend=backends.default_backend())
|
||||
ec.generate_private_key(curve=ec.SECT163R2, backend=backends.default_backend())
|
||||
rsa.generate_private_key(
|
||||
public_exponent=65537, key_size=2047, backend=backends.default_backend()
|
||||
)
|
||||
pycrypto_dsa.generate(bits=2047)
|
||||
pycrypto_rsa.generate(bits=2047)
|
||||
pycryptodomex_dsa.generate(bits=2047)
|
||||
pycryptodomex_rsa.generate(bits=2047)
|
||||
dsa.generate_private_key(2047, backends.default_backend())
|
||||
ec.generate_private_key(ec.SECT163R2, backends.default_backend())
|
||||
rsa.generate_private_key(3, 2047, backends.default_backend())
|
||||
pycrypto_dsa.generate(2047)
|
||||
pycrypto_rsa.generate(2047)
|
||||
pycryptodomex_dsa.generate(2047)
|
||||
pycryptodomex_rsa.generate(2047)
|
||||
|
||||
# Don't crash when the size is variable.
|
||||
rsa.generate_private_key(
|
||||
public_exponent=65537, key_size=some_key_size, backend=backends.default_backend()
|
||||
)
|
||||
|
||||
# Can't reliably know which curve was passed, in some cases like below.
|
||||
ec.generate_private_key(
|
||||
curve=curves[self.curve]["create"](self.size), backend=backends.default_backend()
|
||||
)
|
||||
@@ -1,9 +1,7 @@
|
||||
import paramiko
|
||||
from paramiko import client
|
||||
from paramiko.client import AutoAddPolicy, WarningPolicy
|
||||
|
||||
ssh_client = client.SSHClient()
|
||||
ssh_client_from_paramiko = paramiko.SSHClient()
|
||||
|
||||
# OK
|
||||
ssh_client.set_missing_host_key_policy(policy=foo)
|
||||
@@ -14,12 +12,10 @@ ssh_client.set_missing_host_key_policy(foo)
|
||||
# Errors
|
||||
ssh_client.set_missing_host_key_policy(client.AutoAddPolicy)
|
||||
ssh_client.set_missing_host_key_policy(client.WarningPolicy)
|
||||
ssh_client.set_missing_host_key_policy(client.AutoAddPolicy())
|
||||
ssh_client.set_missing_host_key_policy(AutoAddPolicy)
|
||||
ssh_client.set_missing_host_key_policy(policy=client.AutoAddPolicy)
|
||||
ssh_client.set_missing_host_key_policy(policy=client.WarningPolicy)
|
||||
ssh_client.set_missing_host_key_policy(policy=WarningPolicy)
|
||||
ssh_client_from_paramiko.set_missing_host_key_policy(paramiko.AutoAddPolicy)
|
||||
|
||||
# Unrelated
|
||||
set_missing_host_key_policy(client.AutoAddPolicy)
|
||||
|
||||
@@ -92,35 +92,3 @@ try:
|
||||
pass
|
||||
except Exception:
|
||||
logging.error("...", exc_info=True)
|
||||
|
||||
|
||||
from logging import error, exception
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
error("...")
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
error("...", exc_info=False)
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
error("...", exc_info=None)
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
exception("...")
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
error("...", exc_info=True)
|
||||
|
||||
@@ -70,8 +70,6 @@ settings.set_enable_developer_extras(True)
|
||||
foo.is_(True)
|
||||
bar.is_not(False)
|
||||
next(iter([]), False)
|
||||
sa.func.coalesce(tbl.c.valid, False)
|
||||
|
||||
|
||||
class Registry:
|
||||
def __init__(self) -> None:
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
# Move mutable arguments below imports and docstrings
|
||||
# https://github.com/astral-sh/ruff/issues/7616
|
||||
|
||||
|
||||
def import_module_wrong(value: dict[str, str] = {}):
|
||||
import os
|
||||
|
||||
|
||||
def import_module_with_values_wrong(value: dict[str, str] = {}):
|
||||
import os
|
||||
|
||||
return 2
|
||||
|
||||
|
||||
def import_modules_wrong(value: dict[str, str] = {}):
|
||||
import os
|
||||
import sys
|
||||
import itertools
|
||||
|
||||
|
||||
def from_import_module_wrong(value: dict[str, str] = {}):
|
||||
from os import path
|
||||
|
||||
|
||||
def from_imports_module_wrong(value: dict[str, str] = {}):
|
||||
from os import path
|
||||
from sys import version_info
|
||||
|
||||
|
||||
def import_and_from_imports_module_wrong(value: dict[str, str] = {}):
|
||||
import os
|
||||
from sys import version_info
|
||||
|
||||
|
||||
def import_docstring_module_wrong(value: dict[str, str] = {}):
|
||||
"""Docstring"""
|
||||
import os
|
||||
|
||||
|
||||
def import_module_wrong(value: dict[str, str] = {}):
|
||||
"""Docstring"""
|
||||
import os; import sys
|
||||
|
||||
|
||||
def import_module_wrong(value: dict[str, str] = {}):
|
||||
"""Docstring"""
|
||||
import os; import sys; x = 1
|
||||
|
||||
|
||||
def import_module_wrong(value: dict[str, str] = {}):
|
||||
"""Docstring"""
|
||||
import os; import sys
|
||||
|
||||
|
||||
def import_module_wrong(value: dict[str, str] = {}):
|
||||
import os; import sys
|
||||
|
||||
|
||||
def import_module_wrong(value: dict[str, str] = {}):
|
||||
import os; import sys; x = 1
|
||||
|
||||
|
||||
def import_module_wrong(value: dict[str, str] = {}):
|
||||
import os; import sys
|
||||
|
||||
|
||||
def import_module_wrong(value: dict[str, str] = {}): import os
|
||||
|
||||
|
||||
def import_module_wrong(value: dict[str, str] = {}): import os; import sys
|
||||
|
||||
|
||||
def import_module_wrong(value: dict[str, str] = {}): \
|
||||
import os
|
||||
@@ -1,5 +0,0 @@
|
||||
# Import followed by whitespace with no newline
|
||||
# Same as B006_2.py, but import instead of docstring
|
||||
|
||||
def foobar(foor, bar={}):
|
||||
import os
|
||||
@@ -1,5 +0,0 @@
|
||||
# Import with no newline
|
||||
# Same as B006_3.py, but import instead of docstring
|
||||
|
||||
def foobar(foor, bar={}):
|
||||
import os
|
||||
@@ -56,11 +56,3 @@ setattr(foo.bar, r"baz", None)
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1722458885
|
||||
assert getattr(func, '_rpc')is True
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1732387247
|
||||
getattr(*foo, "bar")
|
||||
setattr(*foo, "bar", None)
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1739800901
|
||||
getattr(self.
|
||||
registration.registry, '__name__')
|
||||
|
||||
@@ -76,15 +76,8 @@ except (ValueError, binascii.Error):
|
||||
pass
|
||||
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/6412
|
||||
# https://github.com/astral-sh/ruff/issues/6412
|
||||
try:
|
||||
pass
|
||||
except (ValueError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1739801758
|
||||
try:
|
||||
pas
|
||||
except(re.error, re.error):
|
||||
p
|
||||
|
||||
@@ -627,7 +627,7 @@ result = function(
|
||||
**{'ham': spam}
|
||||
)
|
||||
|
||||
# Make sure the COM812 and UP034 rules don't fix simultaneously and cause a syntax error.
|
||||
# Make sure the COM812 and UP034 rules don't autofix simultaneously and cause a syntax error.
|
||||
the_first_one = next(
|
||||
(i for i in range(10) if i // 2 == 0) # COM812 fix should include the final bracket
|
||||
)
|
||||
|
||||
@@ -32,16 +32,3 @@ sorted(sorted(x, key=lambda y: y))
|
||||
sorted(sorted(x, key=lambda y: y), key=lambda x: x)
|
||||
sorted(sorted(x), reverse=True)
|
||||
sorted(sorted(x, reverse=False), reverse=True)
|
||||
|
||||
# Preserve trailing comments.
|
||||
xxxxxxxxxxx_xxxxx_xxxxx = sorted(
|
||||
list(x_xxxx_xxxxxxxxxxx_xxxxx.xxxx()),
|
||||
# xxxxxxxxxxx xxxxx xxxx xxx xx Nxxx, xxx xxxxxx3 xxxxxxxxx xx
|
||||
# xx xxxx xxxxxxx xxxx xxx xxxxxxxx Nxxx
|
||||
key=lambda xxxxx: xxxxx or "",
|
||||
)
|
||||
|
||||
xxxxxxxxxxx_xxxxx_xxxxx = sorted(
|
||||
list(x_xxxx_xxxxxxxxxxx_xxxxx.xxxx()), # xxxxxxxxxxx xxxxx xxxx xxx xx Nxxx
|
||||
key=lambda xxxxx: xxxxx or "",
|
||||
)
|
||||
|
||||
@@ -59,23 +59,3 @@ _ = "abc" + "def" + foo
|
||||
_ = foo + bar + "abc"
|
||||
_ = "abc" + foo + bar
|
||||
_ = foo + "abc" + bar
|
||||
|
||||
# Multiple strings nested inside a f-string
|
||||
_ = f"a {'b' 'c' 'd'} e"
|
||||
_ = f"""abc {"def" "ghi"} jkl"""
|
||||
_ = f"""abc {
|
||||
"def"
|
||||
"ghi"
|
||||
} jkl"""
|
||||
|
||||
# Nested f-strings
|
||||
_ = "a" f"b {f"c" f"d"} e" "f"
|
||||
_ = f"b {f"c" f"d {f"e" f"f"} g"} h"
|
||||
_ = f"b {f"abc" \
|
||||
f"def"} g"
|
||||
|
||||
# Explicitly concatenated nested f-strings
|
||||
_ = f"a {f"first"
|
||||
+ f"second"} d"
|
||||
_ = f"a {f"first {f"middle"}"
|
||||
+ f"second"} d"
|
||||
|
||||
@@ -16,9 +16,3 @@ from logging import exception
|
||||
|
||||
exception("foo", exc_info=False) # LOG007
|
||||
exception("foo", exc_info=True) # OK
|
||||
|
||||
|
||||
exception = lambda *args, **kwargs: None
|
||||
|
||||
exception("foo", exc_info=False) # OK
|
||||
exception("foo", exc_info=True) # OK
|
||||
|
||||
@@ -16,12 +16,3 @@ from flask import current_app as app
|
||||
flask.current_app.logger.info("Hello {}".format("World!"))
|
||||
current_app.logger.info("Hello {}".format("World!"))
|
||||
app.logger.log(logging.INFO, "Hello {}".format("World!"))
|
||||
|
||||
from logging import info, log
|
||||
|
||||
info("Hello {}".format("World!"))
|
||||
log(logging.INFO, "Hello {}".format("World!"))
|
||||
info("Hello {}".format("World!"))
|
||||
log(logging.INFO, msg="Hello {}".format("World!"))
|
||||
log(level=logging.INFO, msg="Hello {}".format("World!"))
|
||||
log(msg="Hello {}".format("World!"), level=logging.INFO)
|
||||
|
||||
@@ -2,8 +2,3 @@ import logging
|
||||
|
||||
logging.info("Hello %s" % "World!")
|
||||
logging.log(logging.INFO, "Hello %s" % "World!")
|
||||
|
||||
from logging import info, log
|
||||
|
||||
info("Hello %s" % "World!")
|
||||
log(logging.INFO, "Hello %s" % "World!")
|
||||
|
||||
@@ -2,8 +2,3 @@ import logging
|
||||
|
||||
logging.info("Hello" + " " + "World!")
|
||||
logging.log(logging.INFO, "Hello" + " " + "World!")
|
||||
|
||||
from logging import info, log
|
||||
|
||||
info("Hello" + " " + "World!")
|
||||
log(logging.INFO, "Hello" + " " + "World!")
|
||||
|
||||
@@ -6,7 +6,3 @@ logging.log(logging.INFO, f"Hello {name}")
|
||||
|
||||
_LOGGER = logging.getLogger()
|
||||
_LOGGER.info(f"{__name__}")
|
||||
|
||||
from logging import info
|
||||
info(f"{name}")
|
||||
info(f"{__name__}")
|
||||
|
||||
@@ -8,8 +8,3 @@ log.warn("Hello world!") # This shouldn't be considered as a logger candidate
|
||||
logger.warn("Hello world!")
|
||||
|
||||
logging . warn("Hello World!")
|
||||
|
||||
from logging import warn, warning, exception
|
||||
warn("foo")
|
||||
warning("foo")
|
||||
exception("foo")
|
||||
|
||||
@@ -6,12 +6,3 @@ logging.info(
|
||||
"name": "foobar",
|
||||
},
|
||||
)
|
||||
|
||||
from logging import info
|
||||
|
||||
info(
|
||||
"Hello world!",
|
||||
extra={
|
||||
"name": "foobar",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -6,12 +6,3 @@ logging.info(
|
||||
name="foobar",
|
||||
),
|
||||
)
|
||||
|
||||
from logging import info
|
||||
|
||||
info(
|
||||
"Hello world!",
|
||||
extra=dict(
|
||||
name="foobar",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -19,24 +19,3 @@ except:
|
||||
logging.error("Hello World", exc_info=False)
|
||||
|
||||
logging.error("Hello World", exc_info=sys.exc_info())
|
||||
|
||||
# G201
|
||||
from logging import error
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
error("Hello World", exc_info=True)
|
||||
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
error("Hello World", exc_info=sys.exc_info())
|
||||
|
||||
# OK
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
error("Hello World", exc_info=False)
|
||||
|
||||
error("Hello World", exc_info=sys.exc_info())
|
||||
|
||||
|
||||
@@ -19,23 +19,3 @@ except:
|
||||
logging.exception("Hello World", exc_info=False)
|
||||
|
||||
logging.exception("Hello World", exc_info=True)
|
||||
|
||||
# G202
|
||||
from logging import exception
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
exception("Hello World", exc_info=True)
|
||||
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
exception("Hello World", exc_info=sys.exc_info())
|
||||
|
||||
# OK
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
exception("Hello World", exc_info=False)
|
||||
|
||||
exception("Hello World", exc_info=True)
|
||||
|
||||
@@ -123,28 +123,3 @@ except NetworkError:
|
||||
|
||||
def foo() -> None:
|
||||
pass
|
||||
|
||||
|
||||
def foo():
|
||||
print("foo")
|
||||
pass
|
||||
|
||||
|
||||
def foo():
|
||||
"""A docstring."""
|
||||
print("foo")
|
||||
pass
|
||||
|
||||
|
||||
for i in range(10):
|
||||
pass
|
||||
pass
|
||||
|
||||
for i in range(10):
|
||||
pass
|
||||
|
||||
pass
|
||||
|
||||
for i in range(10):
|
||||
pass # comment
|
||||
pass
|
||||
|
||||
@@ -8,8 +8,6 @@ Foo.objects.create(**{"_id": some_id}) # PIE804
|
||||
|
||||
Foo.objects.create(**{**bar}) # PIE804
|
||||
|
||||
foo(**{})
|
||||
|
||||
|
||||
foo(**{**data, "foo": "buzz"})
|
||||
foo(**buzz)
|
||||
|
||||
@@ -28,12 +28,4 @@ item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
|
||||
def func():
|
||||
# PYI055
|
||||
x: type[requests_mock.Mocker] | type[httpretty] | type[str] = requests_mock.Mocker
|
||||
y: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
|
||||
|
||||
|
||||
def func():
|
||||
from typing import Union as U
|
||||
|
||||
# PYI055
|
||||
x: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
|
||||
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
|
||||
@@ -21,5 +21,4 @@ item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
|
||||
def func():
|
||||
# PYI055
|
||||
item: type[requests_mock.Mocker] | type[httpretty] | type[str] = requests_mock.Mocker
|
||||
item2: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
|
||||
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
|
||||
@@ -33,10 +33,10 @@ message
|
||||
assert not (a or not (b or c))
|
||||
assert not (a or not (b and c))
|
||||
|
||||
# detected, but no fix for messages
|
||||
# detected, but no autofix for messages
|
||||
assert something and something_else, "error message"
|
||||
assert not (something or something_else and something_third), "with message"
|
||||
# detected, but no fix for mixed conditions (e.g. `a or b and c`)
|
||||
# detected, but no autofix for mixed conditions (e.g. `a or b and c`)
|
||||
assert not (something or something_else and something_third)
|
||||
|
||||
|
||||
|
||||
@@ -15,29 +15,3 @@ def ok_complex_logic():
|
||||
def error():
|
||||
resource = acquire_resource()
|
||||
yield resource
|
||||
|
||||
|
||||
import typing
|
||||
from typing import Generator
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def ok_complex_logic() -> typing.Generator[Resource, None, None]:
|
||||
if some_condition:
|
||||
resource = acquire_resource()
|
||||
yield resource
|
||||
resource.release()
|
||||
return
|
||||
yield None
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def error() -> typing.Generator[typing.Any, None, None]:
|
||||
resource = acquire_resource()
|
||||
yield resource
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def error() -> Generator[Resource, None, None]:
|
||||
resource = acquire_resource()
|
||||
yield resource
|
||||
|
||||
@@ -9,33 +9,3 @@ this_should_raise = (
|
||||
'This is a'
|
||||
'\'string\''
|
||||
)
|
||||
|
||||
# Same as above, but with f-strings
|
||||
f'This is a \'string\'' # Q003
|
||||
f'This is \\ a \\\'string\'' # Q003
|
||||
f'"This" is a \'string\''
|
||||
f"This is a 'string'"
|
||||
f"\"This\" is a 'string'"
|
||||
fr'This is a \'string\''
|
||||
fR'This is a \'string\''
|
||||
foo = (
|
||||
f'This is a'
|
||||
f'\'string\'' # Q003
|
||||
)
|
||||
|
||||
# Nested f-strings (Python 3.12+)
|
||||
#
|
||||
# The first one is interesting because the fix for it is valid pre 3.12:
|
||||
#
|
||||
# f"'foo' {'nested'}"
|
||||
#
|
||||
# but as the actual string itself is invalid pre 3.12, we don't catch it.
|
||||
f'\'foo\' {'nested'}' # Q003
|
||||
f'\'foo\' {f'nested'}' # Q003
|
||||
f'\'foo\' {f'\'nested\''} \'\'' # Q003
|
||||
|
||||
f'normal {f'nested'} normal'
|
||||
f'\'normal\' {f'nested'} normal' # Q003
|
||||
f'\'normal\' {f'nested'} "double quotes"'
|
||||
f'\'normal\' {f'\'nested\' {'other'} normal'} "double quotes"' # Q003
|
||||
f'\'normal\' {f'\'nested\' {'other'} "double quotes"'} normal' # Q00l
|
||||
|
||||
@@ -8,32 +8,3 @@ this_should_raise = (
|
||||
"This is a"
|
||||
"\"string\""
|
||||
)
|
||||
|
||||
# Same as above, but with f-strings
|
||||
f"This is a \"string\""
|
||||
f"'This' is a \"string\""
|
||||
f'This is a "string"'
|
||||
f'\'This\' is a "string"'
|
||||
fr"This is a \"string\""
|
||||
fR"This is a \"string\""
|
||||
foo = (
|
||||
f"This is a"
|
||||
f"\"string\""
|
||||
)
|
||||
|
||||
# Nested f-strings (Python 3.12+)
|
||||
#
|
||||
# The first one is interesting because the fix for it is valid pre 3.12:
|
||||
#
|
||||
# f'"foo" {"nested"}'
|
||||
#
|
||||
# but as the actual string itself is invalid pre 3.12, we don't catch it.
|
||||
f"\"foo\" {"foo"}" # Q003
|
||||
f"\"foo\" {f"foo"}" # Q003
|
||||
f"\"foo\" {f"\"foo\""} \"\"" # Q003
|
||||
|
||||
f"normal {f"nested"} normal"
|
||||
f"\"normal\" {f"nested"} normal" # Q003
|
||||
f"\"normal\" {f"nested"} 'single quotes'"
|
||||
f"\"normal\" {f"\"nested\" {"other"} normal"} 'single quotes'" # Q003
|
||||
f"\"normal\" {f"\"nested\" {"other"} 'single quotes'"} normal" # Q003
|
||||
|
||||
@@ -335,7 +335,7 @@ def foo():
|
||||
return x
|
||||
|
||||
|
||||
# Fix cases
|
||||
# Autofix cases
|
||||
def foo():
|
||||
a = 1
|
||||
b=a
|
||||
|
||||
@@ -31,15 +31,6 @@ if isinstance(a, bool) or isinstance(b, str):
|
||||
if isinstance(a, int) or isinstance(a.b, float):
|
||||
pass
|
||||
|
||||
# OK
|
||||
if isinstance(a, int) or unrelated_condition or isinstance(a, float):
|
||||
pass
|
||||
|
||||
if x or isinstance(a, int) or isinstance(a, float):
|
||||
pass
|
||||
|
||||
if x or y or isinstance(a, int) or isinstance(a, float) or z:
|
||||
pass
|
||||
|
||||
def f():
|
||||
# OK
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
try:
|
||||
from __builtin__ import bytes, str, open, super, range, zip, round, int, pow, object, input
|
||||
except ImportError: pass
|
||||
@@ -185,24 +185,3 @@ async def f():
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
async def f():
|
||||
# SIM110
|
||||
for x in await iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
def f():
|
||||
# OK (can't turn this into any() because the yield would end up inside a genexp)
|
||||
for x in iterable:
|
||||
if (yield check(x)):
|
||||
return True
|
||||
return False
|
||||
|
||||
def f():
|
||||
# OK (same)
|
||||
for x in iterable:
|
||||
if (yield from check(x)):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -42,7 +42,3 @@ with contextlib.ExitStack():
|
||||
with contextlib.ExitStack() as exit_stack:
|
||||
exit_stack_ = exit_stack
|
||||
f = exit_stack_.enter_context(open("filename"))
|
||||
|
||||
# OK (quick one-liner to clear file contents)
|
||||
open("filename", "w").close()
|
||||
pathlib.Path("filename").open("w").close()
|
||||
|
||||
@@ -121,16 +121,3 @@ async with b as b2:
|
||||
with c as c2:
|
||||
async with d as d2:
|
||||
f(b2, c2, d2)
|
||||
|
||||
# SIM117
|
||||
with A() as a:
|
||||
with B() as b:
|
||||
type ListOrSet[T] = list[T] | set[T]
|
||||
|
||||
class ClassA[T: str]:
|
||||
def method1(self) -> T:
|
||||
...
|
||||
|
||||
f" something { my_dict["key"] } something else "
|
||||
|
||||
f"foo {f"bar {x}"} baz"
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .foo import Record
|
||||
|
||||
type RecordOrThings = Record | int | str
|
||||
type RecordCallback[R: Record] = Callable[[R], None]
|
||||
|
||||
|
||||
def process_record[R: Record](record: R) -> None:
|
||||
...
|
||||
|
||||
|
||||
class RecordContainer[R: Record]:
|
||||
def add_record(self, record: R) -> None:
|
||||
...
|
||||
@@ -22,10 +22,3 @@ class C:
|
||||
|
||||
class D(C):
|
||||
x: UUID
|
||||
|
||||
|
||||
import collections
|
||||
|
||||
|
||||
class E(BaseModel[int]):
|
||||
x: collections.Awaitable
|
||||
|
||||
@@ -33,16 +33,3 @@ with open(p) as fp:
|
||||
fp.read()
|
||||
open(p).close()
|
||||
os.getcwdb(p)
|
||||
os.path.join(p, *q)
|
||||
os.sep.join(p, *q)
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/7620
|
||||
def opener(path, flags):
|
||||
return os.open(path, flags, dir_fd=os.open('somedir', os.O_RDONLY))
|
||||
|
||||
|
||||
open(p, closefd=False)
|
||||
open(p, opener=opener)
|
||||
open(p, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
|
||||
open(p, 'r', - 1, None, None, None, True, None)
|
||||
open(p, 'r', - 1, None, None, None, False, opener)
|
||||
|
||||
@@ -84,8 +84,3 @@ spam[ ~ham]
|
||||
x = [ #
|
||||
'some value',
|
||||
]
|
||||
|
||||
# F-strings
|
||||
f"{ {'a': 1} }"
|
||||
f"{[ { {'a': 1} } ]}"
|
||||
f"normal { {f"{ { [1, 2] } }" } } normal"
|
||||
|
||||
@@ -29,16 +29,5 @@ mdtypes_template = {
|
||||
'tag_smalldata':[('byte_count_mdtype', 'u4'), ('data', 'S4')],
|
||||
}
|
||||
|
||||
# E231
|
||||
f"{(a,b)}"
|
||||
|
||||
# Okay because it's hard to differentiate between the usages of a colon in a f-string
|
||||
f"{a:=1}"
|
||||
f"{ {'a':1} }"
|
||||
f"{a:.3f}"
|
||||
f"{(a:=1)}"
|
||||
f"{(lambda x:x)}"
|
||||
f"normal{f"{a:.3f}"}normal"
|
||||
|
||||
#: Okay
|
||||
a = (1,
|
||||
|
||||
@@ -48,14 +48,3 @@ def add(a: int=0, b: int =0, c: int= 0) -> int:
|
||||
#: Okay
|
||||
def add(a: int = _default(name='f')):
|
||||
return a
|
||||
|
||||
# F-strings
|
||||
f"{a=}"
|
||||
f"{a:=1}"
|
||||
f"{foo(a=1)}"
|
||||
f"normal {f"{a=}"} normal"
|
||||
|
||||
# Okay as the `=` is used inside a f-string...
|
||||
print(f"{foo = }")
|
||||
# ...but then it creates false negatives for now
|
||||
print(f"{foo(a = 1)}")
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# TODO: comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
# TODO(charlie): comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
# TODO comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
# TODO comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
# FIXME: comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
# FIXME comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
# FIXME comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
# FIXME(charlie): comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# OK (88 characters)
|
||||
"shape:" + "shape:" + "shape:" + "shape:" + "shape:" + "shape:" + "shape:" + "shape:aaa" # type: ignore
|
||||
|
||||
# OK (88 characters)
|
||||
"shape:" + "shape:" + "shape:" + "shape:" + "shape:" + "shape:" + "shape:" + "shape:aaa"# type: ignore
|
||||
|
||||
# OK (88 characters)
|
||||
"shape:" + "shape:" + "shape:" + "shape:" + "shape:" + "shape:" + "shape:" + "shape:aaa" # type: ignore
|
||||
|
||||
# Error (89 characters)
|
||||
"shape:" + "shape:" + "shape:" + "shape:" + "shape:" + "shape:" + "shape:" + "shape:aaaa" # type: ignore
|
||||
@@ -152,11 +152,3 @@ x = [
|
||||
multiline string with tab in it, different lines
|
||||
'''
|
||||
" single line string with tab in it"
|
||||
|
||||
f"test{
|
||||
tab_indented_should_be_flagged
|
||||
} <- this tab is fine"
|
||||
|
||||
f"""test{
|
||||
tab_indented_should_be_flagged
|
||||
} <- this tab is fine"""
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user