Compare commits

..

7 Commits

Author SHA1 Message Date
Charlie Marsh
827747ddcd Merge branch 'main' into simplify-SIM911 2024-01-11 14:32:10 -05:00
trag1c
e871037f7c Made violation logic take ExprCall instead of Expr 2024-01-11 20:03:31 +01:00
trag1c
957aba0be9 Removed redundant loop 2024-01-11 19:58:32 +01:00
trag1c
76e40b2ed3 Corrected variable name 2024-01-11 19:52:01 +01:00
trag1c
a4c6246ad7 Removed unnecessary preview mode check 2024-01-11 10:50:15 +01:00
trag1c
7e1309b854 Added newlines 2024-01-11 00:11:46 +01:00
trag1c
0ae19efe74 Implemented SIM911 2024-01-10 23:23:35 +01:00
516 changed files with 9403 additions and 18572 deletions

View File

@@ -35,7 +35,7 @@ jobs:
with:
fetch-depth: 0
- uses: tj-actions/changed-files@v41
- uses: tj-actions/changed-files@v40
id: changed
with:
files_yaml: |
@@ -76,7 +76,6 @@ jobs:
cargo-fmt:
name: "cargo fmt"
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: "Install Rust toolchain"
@@ -88,7 +87,6 @@ jobs:
runs-on: ubuntu-latest
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: "Install Rust toolchain"
@@ -102,11 +100,10 @@ jobs:
run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features --locked -- -D warnings
cargo-test-linux:
name: "cargo test (linux)"
runs-on: ubuntu-latest
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 20
name: "cargo test (linux)"
steps:
- uses: actions/checkout@v4
- name: "Install Rust toolchain"
@@ -129,11 +126,10 @@ jobs:
path: target/debug/ruff
cargo-test-windows:
name: "cargo test (windows)"
runs-on: windows-latest
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 20
name: "cargo test (windows)"
steps:
- uses: actions/checkout@v4
- name: "Install Rust toolchain"
@@ -149,11 +145,10 @@ jobs:
run: cargo insta test --all --all-features
cargo-test-wasm:
name: "cargo test (wasm)"
runs-on: ubuntu-latest
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 10
name: "cargo test (wasm)"
steps:
- uses: actions/checkout@v4
- name: "Install Rust toolchain"
@@ -171,11 +166,10 @@ jobs:
wasm-pack test --node
cargo-fuzz:
name: "cargo fuzz"
runs-on: ubuntu-latest
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 10
name: "cargo fuzz"
steps:
- uses: actions/checkout@v4
- name: "Install Rust toolchain"
@@ -194,7 +188,6 @@ jobs:
runs-on: ubuntu-latest
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- name: "Install Rust toolchain"
@@ -220,7 +213,6 @@ jobs:
if: github.event_name == 'pull_request' && ${{
needs.determine_changes.outputs.code == 'true'
}}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
@@ -330,7 +322,6 @@ jobs:
runs-on: ubuntu-latest
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: "Install nightly Rust toolchain"
@@ -345,7 +336,6 @@ jobs:
python-package:
name: "python package"
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
@@ -370,7 +360,6 @@ jobs:
pre-commit:
name: "pre-commit"
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
@@ -399,7 +388,6 @@ jobs:
docs:
name: "mkdocs"
runs-on: ubuntu-latest
timeout-minutes: 10
env:
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
steps:
@@ -437,7 +425,6 @@ jobs:
runs-on: ubuntu-latest
needs: determine_changes
if: needs.determine_changes.outputs.formatter == 'true' || github.ref == 'refs/heads/main'
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: "Install Rust toolchain"
@@ -454,7 +441,6 @@ jobs:
check-ruff-lsp:
name: "test ruff-lsp"
runs-on: ubuntu-latest
timeout-minutes: 5
needs:
- cargo-test-linux
- determine_changes
@@ -498,7 +484,6 @@ jobs:
runs-on: ubuntu-latest
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 20
steps:
- name: "Checkout Branch"
uses: actions/checkout@v4

View File

@@ -47,7 +47,7 @@ jobs:
run: mkdocs build --strict -f mkdocs.public.yml
- name: "Deploy to Cloudflare Pages"
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
uses: cloudflare/wrangler-action@v3.4.1
uses: cloudflare/wrangler-action@v3.3.2
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}

View File

@@ -40,7 +40,7 @@ jobs:
working-directory: playground
- name: "Deploy to Cloudflare Pages"
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
uses: cloudflare/wrangler-action@v3.4.1
uses: cloudflare/wrangler-action@v3.3.2
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}

View File

@@ -4,7 +4,7 @@ exclude: |
(?x)^(
crates/ruff_linter/resources/.*|
crates/ruff_linter/src/rules/.*/snapshots/.*|
crates/ruff/resources/.*|
crates/ruff_cli/resources/.*|
crates/ruff_python_formatter/resources/.*|
crates/ruff_python_formatter/tests/snapshots/.*|
crates/ruff_python_resolver/resources/.*|

View File

@@ -1,159 +1,5 @@
# Changelog
## 0.1.15
### Preview features
- Error when `NURSERY` selector is used with `--preview` ([#9682](https://github.com/astral-sh/ruff/pull/9682))
- Preserve indentation around multiline strings in formatter ([#9637](https://github.com/astral-sh/ruff/pull/9637))
- \[`flake8-return`\] Add fixes for all rules (`RET505`, `RET506`, `RET507`, `RET508`) ([#9595](https://github.com/astral-sh/ruff/pull/9595))
- \[`flake8-simplify`\] Add fix for `if-with-same-arms` (`SIM114`) ([#9591](https://github.com/astral-sh/ruff/pull/9591))
- \[`pycodestyle`\] Add fix for `multiple-imports-on-one-line` (`E401`) ([#9518](https://github.com/astral-sh/ruff/pull/9518))
- \[`pylint`\] Add fix for `collapsible-else-if` (`PLR5501`) ([#9594](https://github.com/astral-sh/ruff/pull/9594))
- \[`pylint`\] Add fix for `useless-else-on-loop` (`PLW0120`) ([#9590](https://github.com/astral-sh/ruff/pull/9590))
- \[`pylint`\] Implement `assigning-non-slot` (`E0237`) ([#9623](https://github.com/astral-sh/ruff/pull/9623))
- \[`pylint`\] Implement `potential-index-error` (`PLE0643`) ([#9545](https://github.com/astral-sh/ruff/pull/9545))
- \[`pylint`\] Implement `too-many-nested-blocks` (`PLR1702`) ([#9172](https://github.com/astral-sh/ruff/pull/9172))
- \[`ruff`\] Add rule to sort `__slots__` and `__match_args__` ([#9564](https://github.com/astral-sh/ruff/pull/9564))
- \[`ruff`\] Detect unnecessary `dict` comprehensions for iterables (`RUF025`) ([#9613](https://github.com/astral-sh/ruff/pull/9613))
- \[`ruff`\] Guard against use of `default_factory` as a keyword argument (`RUF026`) ([#9651](https://github.com/astral-sh/ruff/pull/9651))
- \[`ruff`\] Implement `mutable-fromkeys-value` (`RUF024`) ([#9597](https://github.com/astral-sh/ruff/pull/9597))
### CLI
- Enable auto-wrapping of `--help` output ([#9633](https://github.com/astral-sh/ruff/pull/9633))
### Bug fixes
- Avoid rendering display-only rules as fixable ([#9649](https://github.com/astral-sh/ruff/pull/9649))
- Detect automagic-like assignments in notebooks ([#9653](https://github.com/astral-sh/ruff/pull/9653))
- Generate custom JSON schema for dynamic setting ([#9632](https://github.com/astral-sh/ruff/pull/9632))
- \[`flake8-no-pep420`\] Include global `--config` when determining namespace packages ([#9603](https://github.com/astral-sh/ruff/pull/9603))
- \[`flake8-pie`\] Omit bound tuples passed to `.startswith` or `.endswith` ([#9661](https://github.com/astral-sh/ruff/pull/9661))
- \[`flake8-return`\] Avoid panic when fixing inlined else blocks ([#9657](https://github.com/astral-sh/ruff/pull/9657))
- \[`flake8-return`\] Consider exception suppression in unnecessary assignment ([#9673](https://github.com/astral-sh/ruff/pull/9673))
- \[`flake8-return`\] Take `NoReturn` annotation into account when analyzing implicit returns ([#9636](https://github.com/astral-sh/ruff/pull/9636))
- \[`flake8-simplify`\] Support inverted returns in `needless-bool` (`SIM103`) ([#9619](https://github.com/astral-sh/ruff/pull/9619))
- \[`flake8-type-checking`\] Add Pydantic's `BaseConfig` to default-copy list ([#9650](https://github.com/astral-sh/ruff/pull/9650))
- \[`flake8-type-checking`\] Avoid marking `InitVar` as a typing-only annotation ([#9688](https://github.com/astral-sh/ruff/pull/9688))
- \[`pycodestyle`\] Allow `dtype` comparisons in `type-comparison` ([#9676](https://github.com/astral-sh/ruff/pull/9676))
- \[`pydocstyle`\] Re-implement `last-line-after-section` (`D413`) ([#9654](https://github.com/astral-sh/ruff/pull/9654))
### Documentation
- \[`flake8-pytest-style`\] Add fix safety documentation for `duplicate-parameterize-test-cases` ([#9678](https://github.com/astral-sh/ruff/pull/9678))
- \[`pylint`\] Document `literal-membership` fix safety conditions ([#9677](https://github.com/astral-sh/ruff/pull/9677))
- \[`isort`\] Fix reference to `isort` rule code ([#9598](https://github.com/astral-sh/ruff/pull/9598))
## 0.1.14
### Preview features
- \[`flake8-bugbear`\] Add fix for `duplicate-value` (`B033`) ([#9510](https://github.com/astral-sh/ruff/pull/9510))
- \[`flake8-simplify`\] Implement `enumerate-for-loop` (`SIM113`) ([#7777](https://github.com/astral-sh/ruff/pull/7777))
- \[`pygrep_hooks`\] Add fix for `deprecated-log-warn` (`PGH002`) ([#9519](https://github.com/astral-sh/ruff/pull/9519))
- \[`pylint`\] Implement `import-private-name` (`C2701`) ([#5920](https://github.com/astral-sh/ruff/pull/5920))
- \[`refurb`\] Implement `regex-flag-alias` with fix (`FURB167`) ([#9516](https://github.com/astral-sh/ruff/pull/9516))
- \[`ruff`\] Add rule and fix to sort contents of `__all__` (`RUF022`) ([#9474](https://github.com/astral-sh/ruff/pull/9474))
- \[`tryceratops`\] Add fix for `error-instead-of-exception` (`TRY400`) ([#9520](https://github.com/astral-sh/ruff/pull/9520))
### Rule changes
- \[`flake8-pyi`\] Fix `PYI047` false negatives on PEP-695 type aliases ([#9566](https://github.com/astral-sh/ruff/pull/9566))
- \[`flake8-pyi`\] Fix `PYI049` false negatives on call-based `TypedDict`s ([#9567](https://github.com/astral-sh/ruff/pull/9567))
- \[`pylint`\] Exclude `self` and `cls` when counting method arguments (`PLR0917`) ([#9563](https://github.com/astral-sh/ruff/pull/9563))
### CLI
- `--show-settings` displays active settings in a far more readable format ([#9464](https://github.com/astral-sh/ruff/pull/9464))
- Add `--extension` support to the formatter ([#9483](https://github.com/astral-sh/ruff/pull/9483))
### Configuration
- Ignore preview status for fixable and unfixable selectors ([#9538](https://github.com/astral-sh/ruff/pull/9538))
- \[`pycodestyle`\] Use the configured tab size when expanding indents ([#9506](https://github.com/astral-sh/ruff/pull/9506))
### Bug fixes
- Recursively visit deferred AST nodes ([#9541](https://github.com/astral-sh/ruff/pull/9541))
- Visit deferred lambdas before type definitions ([#9540](https://github.com/astral-sh/ruff/pull/9540))
- \[`flake8-simplify`\] Avoid some more `enumerate-for-loop` false positives (`SIM113`) ([#9515](https://github.com/astral-sh/ruff/pull/9515))
- \[`pandas-vet`\] Limit inplace diagnostics to methods that accept inplace ([#9495](https://github.com/astral-sh/ruff/pull/9495))
- \[`pylint`\] Add the `__prepare__` method to the list of recognized dunder method ([#9529](https://github.com/astral-sh/ruff/pull/9529))
- \[`pylint`\] Ignore unnecessary dunder calls within dunder definitions ([#9496](https://github.com/astral-sh/ruff/pull/9496))
- \[`refurb`\] Avoid bailing when `reimplemented-operator` is called on function (`FURB118`) ([#9556](https://github.com/astral-sh/ruff/pull/9556))
- \[`ruff`\] Avoid treating named expressions as static keys (`RUF011`) ([#9494](https://github.com/astral-sh/ruff/pull/9494))
### Documentation
- Add instructions on using `noqa` with isort rules ([#9555](https://github.com/astral-sh/ruff/pull/9555))
- Documentation update for URL giving 'page not found' ([#9565](https://github.com/astral-sh/ruff/pull/9565))
- Fix admonition in dark mode ([#9502](https://github.com/astral-sh/ruff/pull/9502))
- Update contributing docs to use `cargo bench -p ruff_benchmark` ([#9535](https://github.com/astral-sh/ruff/pull/9535))
- Update emacs integration section to include `emacs-ruff-format` ([#9403](https://github.com/astral-sh/ruff/pull/9403))
- \[`flake8-blind-except`\] Document exceptions to `blind-except` rule ([#9580](https://github.com/astral-sh/ruff/pull/9580))
## 0.1.13
### Bug fixes
- Include base pyproject when initializing cache settings ([#9480](https://github.com/astral-sh/ruff/pull/9480))
- \[`flake8-simplify`\] Account for possibly-empty f-string values in truthiness logic ([#9484](https://github.com/astral-sh/ruff/pull/9484))
- \[`pylint`\] Add the missing period in `unnecessary-dunder-call` ([#9485](https://github.com/astral-sh/ruff/pull/9485))
- \[`pylint`\] Fix `__aenter__` message in `unnecessary-dunder-call` ([#9492](https://github.com/astral-sh/ruff/pull/9492))
## 0.1.12
### Preview features
- Formatter: Hug multiline-strings in preview style ([#9243](https://github.com/astral-sh/ruff/pull/9243))
- \[`flake8-bandit`\] Add `ssl-with-no-version` (`S504`) ([#9384](https://github.com/astral-sh/ruff/pull/9384))
- \[`flake8-bandit`\] Implement `ssl-insecure-version` (`S502`) ([#9390](https://github.com/astral-sh/ruff/pull/9390))
- \[`flake8-bandit`\] Implement `ssl-with-bad-defaults` (`S503`) ([#9391](https://github.com/astral-sh/ruff/pull/9391))
- \[`flake8-bandit`\] Implement suspicious import rules (`S4XX`) ([#8831](https://github.com/astral-sh/ruff/pull/8831))
- \[`flake8-simplify`\] Implement `zip-dict-keys-and-values` (`SIM911`) ([#9460](https://github.com/astral-sh/ruff/pull/9460))
- \[`pyflakes`\] Add a fix for `redefined-while-unused` (`F811`) ([#9419](https://github.com/astral-sh/ruff/pull/9419))
- \[`pylint`\] Implement `unnecessary-dunder-call` (`C2801`) ([#9166](https://github.com/astral-sh/ruff/pull/9166))
- \[`ruff`\] Add `parenthesize-chained-operators` (`RUF021`) to enforce parentheses in `a or b and c` ([#9440](https://github.com/astral-sh/ruff/pull/9440))
### Rule changes
- \[`flake8-boolean-trap`\] Allow Boolean positional arguments in setters ([#9429](https://github.com/astral-sh/ruff/pull/9429))
- \[`flake8-builtins`\] Restrict `builtin-attribute-shadowing` (`A003`) to actual shadowed references ([#9462](https://github.com/astral-sh/ruff/pull/9462))
- \[`flake8-pyi`\] Add fix for `generator-return-from-iter-method` (`PYI058`) ([#9355](https://github.com/astral-sh/ruff/pull/9355))
- \[`pyflakes`\] Don't flag `redefined-while-unused` (`F811`) in `if` branches ([#9418](https://github.com/astral-sh/ruff/pull/9418))
- \[`pyupgrade`\] Add some additional Python 3.12 typing members to `deprecated-import` ([#9445](https://github.com/astral-sh/ruff/pull/9445))
- \[`ruff`\] Add fix for `parenthesize-chained-operators` (`RUF021`) ([#9449](https://github.com/astral-sh/ruff/pull/9449))
- \[`ruff`\] Include subscripts and attributes in static key rule (`RUF011`) ([#9416](https://github.com/astral-sh/ruff/pull/9416))
- \[`ruff`\] Support variable keys in static dictionary key rule (`RUF011`) ([#9411](https://github.com/astral-sh/ruff/pull/9411))
### Formatter
- Generate deterministic IDs when formatting notebooks ([#9359](https://github.com/astral-sh/ruff/pull/9359))
- Allow `# fmt: skip` with interspersed same-line comments ([#9395](https://github.com/astral-sh/ruff/pull/9395))
- Parenthesize breaking named expressions in match guards ([#9396](https://github.com/astral-sh/ruff/pull/9396))
### Bug fixes
- Add cell indexes to all diagnostics ([#9387](https://github.com/astral-sh/ruff/pull/9387))
- Avoid infinite loop in constant vs. `None` comparisons ([#9376](https://github.com/astral-sh/ruff/pull/9376))
- Handle raises with implicit alternate branches ([#9377](https://github.com/astral-sh/ruff/pull/9377))
- Ignore trailing quotes for unclosed l-brace errors ([#9388](https://github.com/astral-sh/ruff/pull/9388))
- Respect multi-segment submodule imports when resolving qualified names ([#9382](https://github.com/astral-sh/ruff/pull/9382))
- Use `DisplayParseError` for stdin parser errors ([#9409](https://github.com/astral-sh/ruff/pull/9409))
- Use `comment_ranges` for isort directive extraction ([#9414](https://github.com/astral-sh/ruff/pull/9414))
- Use transformed source code for diagnostic locations ([#9408](https://github.com/astral-sh/ruff/pull/9408))
- \[`flake8-pyi`\] Exclude `warnings.deprecated` and `typing_extensions.deprecated` arguments ([#9423](https://github.com/astral-sh/ruff/pull/9423))
- \[`flake8-pyi`\] Fix false negative for `unused-private-protocol` (`PYI046`) with unused generic protocols ([#9405](https://github.com/astral-sh/ruff/pull/9405))
- \[`pydocstyle`\] Disambiguate argument descriptors from section headers ([#9427](https://github.com/astral-sh/ruff/pull/9427))
- \[`pylint`\] Homogenize `PLR0914` message to match other `PLR09XX` rules ([#9399](https://github.com/astral-sh/ruff/pull/9399))
- \[`ruff`\] Allow `Hashable = None` in type annotations (`RUF013`) ([#9442](https://github.com/astral-sh/ruff/pull/9442))
### Documentation
- Fix admonition hyperlink colouring ([#9385](https://github.com/astral-sh/ruff/pull/9385))
- Add missing preview link ([#9386](https://github.com/astral-sh/ruff/pull/9386))
## 0.1.11
### Preview features

View File

@@ -81,7 +81,7 @@ pre-commit install
After cloning the repository, run Ruff locally from the repository root with:
```shell
cargo run -p ruff -- check /path/to/file.py --no-cache
cargo run -p ruff_cli -- check /path/to/file.py --no-cache
```
Prior to opening a pull request, ensure that your code has been auto-formatted,
@@ -120,7 +120,7 @@ At the time of writing, the repository includes the following crates:
If you're working on a rule, this is the crate for you.
- `crates/ruff_benchmark`: binary crate for running micro-benchmarks.
- `crates/ruff_cache`: library crate for caching lint results.
- `crates/ruff`: binary crate containing Ruff's command-line interface.
- `crates/ruff_cli`: binary crate containing Ruff's command-line interface.
- `crates/ruff_dev`: binary crate containing utilities used in the development of Ruff itself (e.g.,
`cargo dev generate-all`), see the [`cargo dev`](#cargo-dev) section below.
- `crates/ruff_diagnostics`: library crate for the rule-independent abstractions in the lint
@@ -231,7 +231,7 @@ Once you've completed the code for the rule itself, you can define tests with th
For example, if you're adding a new rule named `E402`, you would run:
```shell
cargo run -p ruff -- check crates/ruff_linter/resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402
cargo run -p ruff_cli -- check crates/ruff_linter/resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402
```
**Note:** Only a subset of rules are enabled by default. When testing a new rule, ensure that
@@ -252,7 +252,7 @@ Once you've completed the code for the rule itself, you can define tests with th
Ruff's user-facing settings live in a few different places.
First, the command-line options are defined via the `Args` struct in `crates/ruff/src/args.rs`.
First, the command-line options are defined via the `Args` struct in `crates/ruff_cli/src/args.rs`.
Second, the `pyproject.toml` options are defined in `crates/ruff_workspace/src/options.rs` (via the
`Options` struct), `crates/ruff_workspace/src/configuration.rs` (via the `Configuration` struct),
@@ -518,10 +518,10 @@ if the benchmark improved/regressed compared to that baseline.
```shell
# Run once on your "baseline" code
cargo bench -p ruff_benchmark -- --save-baseline=main
cargo benchmark --save-baseline=main
# Then iterate with
cargo bench -p ruff_benchmark -- --baseline=main
cargo benchmark --baseline=main
```
#### PR Summary
@@ -531,10 +531,10 @@ This is useful to illustrate the improvements of a PR.
```shell
# On main
cargo bench -p ruff_benchmark -- --save-baseline=main
cargo benchmark --save-baseline=main
# After applying your changes
cargo bench -p ruff_benchmark -- --save-baseline=pr
cargo benchmark --save-baseline=pr
critcmp main pr
```
@@ -547,10 +547,10 @@ cargo install critcmp
#### Tips
- Use `cargo bench -p ruff_benchmark <filter>` to only run specific benchmarks. For example: `cargo benchmark lexer`
to only run the lexer benchmarks.
- Use `cargo bench -p ruff_benchmark -- --quiet` for a more cleaned up output (without statistical relevance)
- Use `cargo bench -p ruff_benchmark -- --quick` to get faster results (more prone to noise)
- Use `cargo benchmark <filter>` to only run specific benchmarks. For example: `cargo benchmark linter/pydantic`
to only run the pydantic tests.
- Use `cargo benchmark --quiet` for a more cleaned up output (without statistical relevance)
- Use `cargo benchmark --quick` to get faster results (more prone to noise)
### Profiling Projects
@@ -618,7 +618,7 @@ Otherwise, follow the instructions from the linux section.
utils with it:
- `cargo dev print-ast <file>`: Print the AST of a python file using the
[RustPython parser](https://github.com/astral-sh/ruff/tree/main/crates/ruff_python_parser) that is
[RustPython parser](https://github.com/astral-sh/RustPython-Parser/tree/main/parser) that is
mainly used in Ruff. For `if True: pass # comment`, you can see the syntax tree, the byte offsets
for start and stop of each node and also how the `:` token, the comment and whitespace are not
represented anymore:

206
Cargo.lock generated
View File

@@ -154,9 +154,9 @@ dependencies = [
[[package]]
name = "assert_cmd"
version = "2.0.13"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00ad3f3a942eee60335ab4342358c161ee296829e0d16ff42fc1d6cb07815467"
checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6"
dependencies = [
"anstyle",
"bstr",
@@ -273,14 +273,14 @@ dependencies = [
[[package]]
name = "chrono"
version = "0.4.33"
version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
dependencies = [
"android-tzdata",
"iana-time-zone",
"num-traits",
"windows-targets 0.52.0",
"windows-targets 0.48.5",
]
[[package]]
@@ -330,7 +330,6 @@ dependencies = [
"anstyle",
"clap_lex",
"strsim",
"terminal_size",
]
[[package]]
@@ -957,9 +956,9 @@ dependencies = [
[[package]]
name = "ignore"
version = "0.4.22"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1"
checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060"
dependencies = [
"crossbeam-deque",
"globset",
@@ -1147,9 +1146,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "js-sys"
version = "0.3.67"
version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
dependencies = [
"wasm-bindgen",
]
@@ -1856,9 +1855,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.8.1"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
@@ -1866,9 +1865,9 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.12.1"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
@@ -1991,21 +1990,55 @@ dependencies = [
[[package]]
name = "ring"
version = "0.17.7"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"getrandom",
"libc",
"once_cell",
"spin",
"untrusted",
"windows-sys 0.48.0",
"web-sys",
"winapi",
]
[[package]]
name = "ruff"
version = "0.1.15"
name = "ruff_benchmark"
version = "0.0.0"
dependencies = [
"codspeed-criterion-compat",
"criterion",
"mimalloc",
"once_cell",
"ruff_linter",
"ruff_python_ast",
"ruff_python_formatter",
"ruff_python_index",
"ruff_python_parser",
"serde",
"serde_json",
"tikv-jemallocator",
"ureq",
"url",
]
[[package]]
name = "ruff_cache"
version = "0.0.0"
dependencies = [
"filetime",
"glob",
"globset",
"itertools 0.12.0",
"regex",
"ruff_macros",
"seahash",
]
[[package]]
name = "ruff_cli"
version = "0.1.11"
dependencies = [
"anyhow",
"argfile",
@@ -2054,39 +2087,6 @@ dependencies = [
"wild",
]
[[package]]
name = "ruff_benchmark"
version = "0.0.0"
dependencies = [
"codspeed-criterion-compat",
"criterion",
"mimalloc",
"once_cell",
"ruff_linter",
"ruff_python_ast",
"ruff_python_formatter",
"ruff_python_index",
"ruff_python_parser",
"serde",
"serde_json",
"tikv-jemallocator",
"ureq",
"url",
]
[[package]]
name = "ruff_cache"
version = "0.0.0"
dependencies = [
"filetime",
"glob",
"globset",
"itertools 0.12.0",
"regex",
"ruff_macros",
"seahash",
]
[[package]]
name = "ruff_dev"
version = "0.0.0"
@@ -2103,7 +2103,7 @@ dependencies = [
"pretty_assertions",
"rayon",
"regex",
"ruff",
"ruff_cli",
"ruff_diagnostics",
"ruff_formatter",
"ruff_linter",
@@ -2165,7 +2165,7 @@ dependencies = [
[[package]]
name = "ruff_linter"
version = "0.1.15"
version = "0.1.11"
dependencies = [
"aho-corasick",
"annotate-snippets 0.9.2",
@@ -2417,7 +2417,7 @@ dependencies = [
[[package]]
name = "ruff_shrinking"
version = "0.1.15"
version = "0.1.11"
dependencies = [
"anyhow",
"clap",
@@ -2541,9 +2541,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.21.10"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8"
dependencies = [
"log",
"ring",
@@ -2553,9 +2553,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
version = "0.101.7"
version = "0.101.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d"
dependencies = [
"ring",
"untrusted",
@@ -2620,9 +2620,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sct"
version = "0.7.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
dependencies = [
"ring",
"untrusted",
@@ -2636,9 +2636,9 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "semver"
version = "1.0.21"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
@@ -2684,9 +2684,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.113"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9"
dependencies = [
"itoa",
"ryu",
@@ -2713,9 +2713,9 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.5.1"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5c9fdb6b00a489875b22efd4b78fe2b363b72265cc5f6eb2e2b9ee270e6140c"
checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
dependencies = [
"serde",
"serde_with_macros",
@@ -2723,9 +2723,9 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "3.5.1"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbff351eb4b33600a2e138dfa0b10b65a238ea8ff8fb2387c422c5022a3e8298"
checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
dependencies = [
"darling",
"proc-macro2",
@@ -2753,15 +2753,15 @@ dependencies = [
[[package]]
name = "shlex"
version = "1.3.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
[[package]]
name = "similar"
version = "2.4.0"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21"
checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597"
[[package]]
name = "siphasher"
@@ -2771,15 +2771,15 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "smallvec"
version = "1.13.1"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "spin"
version = "0.9.8"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "static_assertions"
@@ -2892,16 +2892,6 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
dependencies = [
"rustix",
"windows-sys 0.48.0",
]
[[package]]
name = "terminfo"
version = "0.8.0"
@@ -3270,9 +3260,9 @@ dependencies = [
[[package]]
name = "untrusted"
version = "0.9.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "ureq"
@@ -3403,9 +3393,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.90"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -3413,9 +3403,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.90"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
dependencies = [
"bumpalo",
"log",
@@ -3428,9 +3418,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.40"
version = "0.4.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461"
checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12"
dependencies = [
"cfg-if",
"js-sys",
@@ -3440,9 +3430,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.90"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3450,9 +3440,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.90"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
@@ -3463,15 +3453,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.90"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.40"
version = "0.3.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139bd73305d50e1c1c4333210c0db43d989395b64a237bd35c10ef3832a7f70c"
checksum = "2cf9242c0d27999b831eae4767b2a146feb0b27d332d553e605864acd2afd403"
dependencies = [
"console_error_panic_hook",
"js-sys",
@@ -3483,9 +3473,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.40"
version = "0.3.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70072aebfe5da66d2716002c729a14e4aec4da0e23cc2ea66323dac541c93928"
checksum = "794645f5408c9a039fd09f4d113cdfb2e7eba5ff1956b07bcf701cf4b394fe89"
dependencies = [
"proc-macro2",
"quote",
@@ -3504,9 +3494,9 @@ dependencies = [
[[package]]
name = "webpki-roots"
version = "0.25.3"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10"
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
[[package]]
name = "which"

View File

@@ -16,11 +16,11 @@ aho-corasick = { version = "1.1.2" }
annotate-snippets = { version = "0.9.2", features = ["color"] }
anyhow = { version = "1.0.79" }
argfile = { version = "0.1.6" }
assert_cmd = { version = "2.0.13" }
assert_cmd = { version = "2.0.8" }
bincode = { version = "1.3.3" }
bitflags = { version = "2.4.1" }
cachedir = { version = "0.3.1" }
chrono = { version = "0.4.33", default-features = false, features = ["clock"] }
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
clap = { version = "4.4.13", features = ["derive"] }
clap_complete_command = { version = "0.5.1" }
clearscreen = { version = "2.0.0" }
@@ -40,7 +40,7 @@ fs-err = { version ="2.11.0"}
glob = { version = "0.3.1" }
globset = { version = "0.4.14" }
hexf-parse = { version ="0.2.1"}
ignore = { version = "0.4.22" }
ignore = { version = "0.4.21" }
imara-diff ={ version = "0.1.5"}
imperative = { version = "1.0.4" }
indicatif ={ version = "0.17.7"}
@@ -50,7 +50,7 @@ insta-cmd = { version = "0.4.0" }
is-macro = { version = "0.3.4" }
is-wsl = { version = "0.4.0" }
itertools = { version = "0.12.0" }
js-sys = { version = "0.3.67" }
js-sys = { version = "0.3.66" }
lalrpop-util = { version = "0.20.0", default-features = false }
lexical-parse-float = { version = "0.8.0", features = ["format"] }
libcst = { version = "1.1.0", default-features = false }
@@ -69,22 +69,22 @@ pyproject-toml = { version = "0.8.1" }
quick-junit = { version = "0.3.5" }
quote = { version = "1.0.23" }
rand = { version = "0.8.5" }
rayon = { version = "1.8.1" }
rayon = { version = "1.8.0" }
regex = { version = "1.10.2" }
result-like = { version = "0.5.0" }
rustc-hash = { version = "1.1.0" }
schemars = { version = "0.8.16" }
seahash = { version ="4.1.0"}
semver = { version = "1.0.21" }
semver = { version = "1.0.20" }
serde = { version = "1.0.195", features = ["derive"] }
serde-wasm-bindgen = { version = "0.6.3" }
serde_json = { version = "1.0.113" }
serde_json = { version = "1.0.109" }
serde_test = { version = "1.0.152" }
serde_with = { version = "3.5.1", default-features = false, features = ["macros"] }
serde_with = { version = "3.4.0", default-features = false, features = ["macros"] }
shellexpand = { version = "3.0.0" }
shlex = { version ="1.3.0"}
similar = { version = "2.4.0", features = ["inline"] }
smallvec = { version = "1.13.1" }
shlex = { version ="1.2.0"}
similar = { version = "2.3.0", features = ["inline"] }
smallvec = { version = "1.11.2" }
static_assertions = "1.1.0"
strum = { version = "0.25.0", features = ["strum_macros"] }
strum_macros = { version = "0.25.3" }
@@ -107,7 +107,7 @@ url = { version = "2.5.0" }
uuid = { version = "1.6.1", features = ["v4", "fast-rng", "macro-diagnostics", "js"] }
walkdir = { version = "2.3.2" }
wasm-bindgen = { version = "0.2.84" }
wasm-bindgen-test = { version = "0.3.40" }
wasm-bindgen-test = { version = "0.3.39" }
wild = { version = "2" }
[workspace.lints.rust]

View File

@@ -150,7 +150,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.1.15
rev: v0.1.11
hooks:
# Run the linter.
- id: ruff

View File

@@ -1,5 +0,0 @@
---
source: crates/ruff/src/version.rs
expression: version
---
0.0.0

View File

@@ -1,149 +0,0 @@
//! A test suite that ensures deprecated command line options have appropriate warnings / behaviors
use ruff_linter::settings::types::SerializationFormat;
use std::process::Command;
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
const BIN_NAME: &str = "ruff";
const STDIN: &str = "l = 1";
fn ruff_check(show_source: Option<bool>, output_format: Option<String>) -> Command {
let mut cmd = Command::new(get_cargo_bin(BIN_NAME));
let output_format = output_format.unwrap_or(format!("{}", SerializationFormat::default(false)));
cmd.arg("--output-format");
cmd.arg(output_format);
cmd.arg("--no-cache");
match show_source {
Some(true) => {
cmd.arg("--show-source");
}
Some(false) => {
cmd.arg("--no-show-source");
}
None => {}
}
cmd.arg("-");
cmd
}
#[test]
fn ensure_show_source_is_deprecated() {
assert_cmd_snapshot!(ruff_check(Some(true), None).pass_stdin(STDIN), @r###"
success: false
exit_code: 1
----- stdout -----
-:1:1: E741 Ambiguous variable name: `l`
Found 1 error.
----- stderr -----
warning: The `--show-source` argument is deprecated and has been ignored in favor of `--output-format=concise`.
"###);
}
#[test]
fn ensure_no_show_source_is_deprecated() {
assert_cmd_snapshot!(ruff_check(Some(false), None).pass_stdin(STDIN), @r###"
success: false
exit_code: 1
----- stdout -----
-:1:1: E741 Ambiguous variable name: `l`
Found 1 error.
----- stderr -----
warning: The `--no-show-source` argument is deprecated and has been ignored in favor of `--output-format=concise`.
"###);
}
#[test]
fn ensure_output_format_is_deprecated() {
assert_cmd_snapshot!(ruff_check(None, Some("text".into())).pass_stdin(STDIN), @r###"
success: false
exit_code: 1
----- stdout -----
-:1:1: E741 Ambiguous variable name: `l`
Found 1 error.
----- stderr -----
warning: `--output-format=text` is deprecated. Use `--output-format=full` or `--output-format=concise` instead. `text` will be treated as `concise`.
"###);
}
#[test]
fn ensure_output_format_overrides_show_source() {
assert_cmd_snapshot!(ruff_check(Some(true), Some("concise".into())).pass_stdin(STDIN), @r###"
success: false
exit_code: 1
----- stdout -----
-:1:1: E741 Ambiguous variable name: `l`
Found 1 error.
----- stderr -----
warning: The `--show-source` argument is deprecated and has been ignored in favor of `--output-format=concise`.
"###);
}
#[test]
fn ensure_full_output_format_overrides_no_show_source() {
assert_cmd_snapshot!(ruff_check(Some(false), Some("full".into())).pass_stdin(STDIN), @r###"
success: false
exit_code: 1
----- stdout -----
-:1:1: E741 Ambiguous variable name: `l`
|
1 | l = 1
| ^ E741
|
Found 1 error.
----- stderr -----
warning: The `--no-show-source` argument is deprecated and has been ignored in favor of `--output-format=full`.
"###);
}
#[test]
fn ensure_output_format_uses_concise_over_no_show_source() {
assert_cmd_snapshot!(ruff_check(Some(false), Some("concise".into())).pass_stdin(STDIN), @r###"
success: false
exit_code: 1
----- stdout -----
-:1:1: E741 Ambiguous variable name: `l`
Found 1 error.
----- stderr -----
warning: The `--no-show-source` argument is deprecated and has been ignored in favor of `--output-format=concise`.
"###);
}
#[test]
fn ensure_deprecated_output_format_overrides_show_source() {
assert_cmd_snapshot!(ruff_check(Some(true), Some("text".into())).pass_stdin(STDIN), @r###"
success: false
exit_code: 1
----- stdout -----
-:1:1: E741 Ambiguous variable name: `l`
Found 1 error.
----- stderr -----
warning: The `--show-source` argument is deprecated and has been ignored in favor of `--output-format=text`.
warning: `--output-format=text` is deprecated. Use `--output-format=full` or `--output-format=concise` instead. `text` will be treated as `concise`.
"###);
}
#[test]
fn ensure_deprecated_output_format_overrides_no_show_source() {
assert_cmd_snapshot!(ruff_check(Some(false), Some("text".into())).pass_stdin(STDIN), @r###"
success: false
exit_code: 1
----- stdout -----
-:1:1: E741 Ambiguous variable name: `l`
Found 1 error.
----- stderr -----
warning: The `--no-show-source` argument is deprecated and has been ignored in favor of `--output-format=text`.
warning: `--output-format=text` is deprecated. Use `--output-format=full` or `--output-format=concise` instead. `text` will be treated as `concise`.
"###);
}

View File

@@ -1,29 +0,0 @@
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
use std::path::Path;
use std::process::Command;
const BIN_NAME: &str = "ruff";
#[cfg(not(target_os = "windows"))]
const TEST_FILTERS: &[(&str, &str)] = &[
("\"[^\\*\"]*/pyproject.toml", "\"[BASEPATH]/pyproject.toml"),
("\".*/crates", "\"[BASEPATH]/crates"),
("\".*/\\.ruff_cache", "\"[BASEPATH]/.ruff_cache"),
("\".*/ruff\"", "\"[BASEPATH]\""),
];
#[cfg(target_os = "windows")]
const TEST_FILTERS: &[(&str, &str)] = &[
(r#""[^\*"]*\\pyproject.toml"#, "\"[BASEPATH]/pyproject.toml"),
(r#"".*\\crates"#, "\"[BASEPATH]/crates"),
(r#"".*\\\.ruff_cache"#, "\"[BASEPATH]/.ruff_cache"),
(r#"".*\\ruff""#, "\"[BASEPATH]\""),
(r#"\\+(\w\w|\s|")"#, "/$1"),
];
#[test]
fn display_default_settings() {
insta::with_settings!({ filters => TEST_FILTERS.to_vec() }, {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(["check", "--show-settings", "unformatted.py"]).current_dir(Path::new("./resources/test/fixtures")));
});
}

View File

@@ -1,367 +0,0 @@
---
source: crates/ruff/tests/show_settings.rs
info:
program: ruff
args:
- check
- "--show-settings"
- unformatted.py
---
success: true
exit_code: 0
----- stdout -----
Resolved settings for: "[BASEPATH]/crates/ruff/resources/test/fixtures/unformatted.py"
Settings path: "[BASEPATH]/pyproject.toml"
# General Settings
cache_dir = "[BASEPATH]/.ruff_cache"
fix = false
fix_only = false
output_format = concise
show_fixes = false
unsafe_fixes = hint
# File Resolver Settings
file_resolver.exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"site-packages",
"venv",
]
file_resolver.extend_exclude = [
"crates/ruff_linter/resources/",
"crates/ruff_python_formatter/resources/",
]
file_resolver.force_exclude = false
file_resolver.include = [
"*.py",
"*.pyi",
"**/pyproject.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
file_resolver.project_root = "[BASEPATH]"
# Linter Settings
linter.exclude = []
linter.project_root = "[BASEPATH]"
linter.rules.enabled = [
MultipleImportsOnOneLine,
ModuleImportNotAtTopOfFile,
MultipleStatementsOnOneLineColon,
MultipleStatementsOnOneLineSemicolon,
UselessSemicolon,
NoneComparison,
TrueFalseComparison,
NotInTest,
NotIsTest,
TypeComparison,
BareExcept,
LambdaAssignment,
AmbiguousVariableName,
AmbiguousClassName,
AmbiguousFunctionName,
IOError,
SyntaxError,
UnusedImport,
ImportShadowedByLoopVar,
UndefinedLocalWithImportStar,
LateFutureImport,
UndefinedLocalWithImportStarUsage,
UndefinedLocalWithNestedImportStarUsage,
FutureFeatureNotDefined,
PercentFormatInvalidFormat,
PercentFormatExpectedMapping,
PercentFormatExpectedSequence,
PercentFormatExtraNamedArguments,
PercentFormatMissingArgument,
PercentFormatMixedPositionalAndNamed,
PercentFormatPositionalCountMismatch,
PercentFormatStarRequiresSequence,
PercentFormatUnsupportedFormatCharacter,
StringDotFormatInvalidFormat,
StringDotFormatExtraNamedArguments,
StringDotFormatExtraPositionalArguments,
StringDotFormatMissingArguments,
StringDotFormatMixingAutomatic,
FStringMissingPlaceholders,
MultiValueRepeatedKeyLiteral,
MultiValueRepeatedKeyVariable,
ExpressionsInStarAssignment,
MultipleStarredExpressions,
AssertTuple,
IsLiteral,
InvalidPrintSyntax,
IfTuple,
BreakOutsideLoop,
ContinueOutsideLoop,
YieldOutsideFunction,
ReturnOutsideFunction,
DefaultExceptNotLast,
ForwardAnnotationSyntaxError,
RedefinedWhileUnused,
UndefinedName,
UndefinedExport,
UndefinedLocal,
UnusedVariable,
UnusedAnnotation,
RaiseNotImplemented,
]
linter.rules.should_fix = [
MultipleImportsOnOneLine,
ModuleImportNotAtTopOfFile,
MultipleStatementsOnOneLineColon,
MultipleStatementsOnOneLineSemicolon,
UselessSemicolon,
NoneComparison,
TrueFalseComparison,
NotInTest,
NotIsTest,
TypeComparison,
BareExcept,
LambdaAssignment,
AmbiguousVariableName,
AmbiguousClassName,
AmbiguousFunctionName,
IOError,
SyntaxError,
UnusedImport,
ImportShadowedByLoopVar,
UndefinedLocalWithImportStar,
LateFutureImport,
UndefinedLocalWithImportStarUsage,
UndefinedLocalWithNestedImportStarUsage,
FutureFeatureNotDefined,
PercentFormatInvalidFormat,
PercentFormatExpectedMapping,
PercentFormatExpectedSequence,
PercentFormatExtraNamedArguments,
PercentFormatMissingArgument,
PercentFormatMixedPositionalAndNamed,
PercentFormatPositionalCountMismatch,
PercentFormatStarRequiresSequence,
PercentFormatUnsupportedFormatCharacter,
StringDotFormatInvalidFormat,
StringDotFormatExtraNamedArguments,
StringDotFormatExtraPositionalArguments,
StringDotFormatMissingArguments,
StringDotFormatMixingAutomatic,
FStringMissingPlaceholders,
MultiValueRepeatedKeyLiteral,
MultiValueRepeatedKeyVariable,
ExpressionsInStarAssignment,
MultipleStarredExpressions,
AssertTuple,
IsLiteral,
InvalidPrintSyntax,
IfTuple,
BreakOutsideLoop,
ContinueOutsideLoop,
YieldOutsideFunction,
ReturnOutsideFunction,
DefaultExceptNotLast,
ForwardAnnotationSyntaxError,
RedefinedWhileUnused,
UndefinedName,
UndefinedExport,
UndefinedLocal,
UnusedVariable,
UnusedAnnotation,
RaiseNotImplemented,
]
linter.per_file_ignores = {}
linter.safety_table.forced_safe = []
linter.safety_table.forced_unsafe = []
linter.target_version = Py37
linter.preview = disabled
linter.explicit_preview_rules = false
linter.extension.mapping = {}
linter.allowed_confusables = []
linter.builtins = []
linter.dummy_variable_rgx = ^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$
linter.external = []
linter.ignore_init_module_imports = false
linter.logger_objects = []
linter.namespace_packages = []
linter.src = ["[BASEPATH]"]
linter.tab_size = 4
linter.line_length = 88
linter.task_tags = [
TODO,
FIXME,
XXX,
]
linter.typing_modules = []
# Linter Plugins
linter.flake8_annotations.mypy_init_return = false
linter.flake8_annotations.suppress_dummy_args = false
linter.flake8_annotations.suppress_none_returning = false
linter.flake8_annotations.allow_star_arg_any = false
linter.flake8_annotations.ignore_fully_untyped = false
linter.flake8_bandit.hardcoded_tmp_directory = [
/tmp,
/var/tmp,
/dev/shm,
]
linter.flake8_bandit.check_typed_exception = false
linter.flake8_bugbear.extend_immutable_calls = []
linter.flake8_builtins.builtins_ignorelist = []
linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+(\(C\)\s+)?\d{4}(-\d{4})*
linter.flake8_copyright.author = none
linter.flake8_copyright.min_file_size = 0
linter.flake8_errmsg.max_string_length = 0
linter.flake8_gettext.functions_names = [
_,
gettext,
ngettext,
]
linter.flake8_implicit_str_concat.allow_multiline = true
linter.flake8_import_conventions.aliases = {"matplotlib": "mpl", "matplotlib.pyplot": "plt", "pandas": "pd", "seaborn": "sns", "tensorflow": "tf", "networkx": "nx", "plotly.express": "px", "polars": "pl", "numpy": "np", "panel": "pn", "pyarrow": "pa", "altair": "alt", "tkinter": "tk", "holoviews": "hv"}
linter.flake8_import_conventions.banned_aliases = {}
linter.flake8_import_conventions.banned_from = []
linter.flake8_pytest_style.fixture_parentheses = true
linter.flake8_pytest_style.parametrize_names_type = tuple
linter.flake8_pytest_style.parametrize_values_type = list
linter.flake8_pytest_style.parametrize_values_row_type = tuple
linter.flake8_pytest_style.raises_require_match_for = [
BaseException,
Exception,
ValueError,
OSError,
IOError,
EnvironmentError,
socket.error,
]
linter.flake8_pytest_style.raises_extend_require_match_for = []
linter.flake8_pytest_style.mark_parentheses = true
linter.flake8_quotes.inline_quotes = double
linter.flake8_quotes.multiline_quotes = double
linter.flake8_quotes.docstring_quotes = double
linter.flake8_quotes.avoid_escape = true
linter.flake8_self.ignore_names = [
_make,
_asdict,
_replace,
_fields,
_field_defaults,
_name_,
_value_,
]
linter.flake8_tidy_imports.ban_relative_imports = "parents"
linter.flake8_tidy_imports.banned_api = {}
linter.flake8_tidy_imports.banned_module_level_imports = []
linter.flake8_type_checking.strict = false
linter.flake8_type_checking.exempt_modules = [
typing,
typing_extensions,
]
linter.flake8_type_checking.runtime_required_base_classes = []
linter.flake8_type_checking.runtime_required_decorators = []
linter.flake8_type_checking.quote_annotations = false
linter.flake8_unused_arguments.ignore_variadic_names = false
linter.isort.required_imports = []
linter.isort.combine_as_imports = false
linter.isort.force_single_line = false
linter.isort.force_sort_within_sections = false
linter.isort.detect_same_package = true
linter.isort.case_sensitive = false
linter.isort.force_wrap_aliases = false
linter.isort.force_to_top = []
linter.isort.known_modules = {}
linter.isort.order_by_type = true
linter.isort.relative_imports_order = furthest_to_closest
linter.isort.single_line_exclusions = []
linter.isort.split_on_trailing_comma = true
linter.isort.classes = []
linter.isort.constants = []
linter.isort.variables = []
linter.isort.no_lines_before = []
linter.isort.lines_after_imports = -1
linter.isort.lines_between_types = 0
linter.isort.forced_separate = []
linter.isort.section_order = [
known { type = future },
known { type = standard_library },
known { type = third_party },
known { type = first_party },
known { type = local_folder },
]
linter.isort.no_sections = false
linter.isort.from_first = false
linter.isort.length_sort = false
linter.isort.length_sort_straight = false
linter.mccabe.max_complexity = 10
linter.pep8_naming.ignore_names = [
setUp,
tearDown,
setUpClass,
tearDownClass,
setUpModule,
tearDownModule,
asyncSetUp,
asyncTearDown,
setUpTestData,
failureException,
longMessage,
maxDiff,
]
linter.pep8_naming.classmethod_decorators = []
linter.pep8_naming.staticmethod_decorators = []
linter.pycodestyle.max_line_length = 88
linter.pycodestyle.max_doc_length = none
linter.pycodestyle.ignore_overlong_task_comments = false
linter.pyflakes.extend_generics = []
linter.pylint.allow_magic_value_types = [
str,
bytes,
]
linter.pylint.allow_dunder_method_names = []
linter.pylint.max_args = 5
linter.pylint.max_positional_args = 5
linter.pylint.max_returns = 6
linter.pylint.max_bool_expr = 5
linter.pylint.max_branches = 12
linter.pylint.max_statements = 50
linter.pylint.max_public_methods = 20
linter.pylint.max_locals = 15
linter.pyupgrade.keep_runtime_typing = false
# Formatter Settings
formatter.exclude = []
formatter.target_version = Py37
formatter.preview = disabled
formatter.line_width = 88
formatter.line_ending = auto
formatter.indent_style = space
formatter.indent_width = 4
formatter.quote_style = double
formatter.magic_trailing_comma = respect
formatter.docstring_code_format = disabled
formatter.docstring_code_line_width = dynamic
----- stderr -----

View File

@@ -1,16 +1,5 @@
# Ruff Benchmarks
The `ruff_benchmark` crate benchmarks the linter and the formatter on individual files:
```shell
# Run once on the "baseline".
cargo bench -p ruff_benchmark -- --save-baseline=main
# Compare against the "baseline".
cargo bench -p ruff_benchmark -- --baseline=main
# Run the lexer benchmarks.
cargo bench -p ruff_benchmark lexer -- --baseline=main
```
The `ruff_benchmark` crate benchmarks the linter and the formatter on individual files.
See [CONTRIBUTING.md](../../CONTRIBUTING.md) on how to use these benchmarks.

View File

@@ -7,7 +7,7 @@ use ruff_benchmark::{TestCase, TestFile, TestFileDownloadError};
use ruff_python_formatter::{format_module_ast, PreviewMode, PyFormatOptions};
use ruff_python_index::CommentRangesBuilder;
use ruff_python_parser::lexer::lex;
use ruff_python_parser::{allocate_tokens_vec, parse_tokens, Mode};
use ruff_python_parser::{parse_tokens, Mode};
#[cfg(target_os = "windows")]
#[global_allocator]
@@ -52,7 +52,7 @@ fn benchmark_formatter(criterion: &mut Criterion) {
BenchmarkId::from_parameter(case.name()),
&case,
|b, case| {
let mut tokens = allocate_tokens_vec(case.code());
let mut tokens = Vec::new();
let mut comment_ranges = CommentRangesBuilder::default();
for result in lex(case.code(), Mode::Module) {

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.1.15"
name = "ruff_cli"
version = "0.1.11"
publish = false
authors = { workspace = true }
edition = { workspace = true }
@@ -10,7 +10,9 @@ documentation = { workspace = true }
repository = { workspace = true }
license = { workspace = true }
readme = "../../README.md"
default-run = "ruff"
[[bin]]
name = "ruff"
[dependencies]
ruff_cache = { path = "../ruff_cache" }
@@ -30,7 +32,7 @@ bincode = { workspace = true }
bitflags = { workspace = true }
cachedir = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true, features = ["derive", "env", "wrap_help"] }
clap = { workspace = true, features = ["derive", "env"] }
clap_complete_command = { workspace = true }
clearscreen = { workspace = true }
colored = { workspace = true }

View File

@@ -11,7 +11,7 @@ use ruff_linter::settings::types::{
ExtensionPair, FilePattern, PatternPrefixPair, PerFileIgnore, PreviewMode, PythonVersion,
SerializationFormat, UnsafeFixes,
};
use ruff_linter::{warn_user, RuleParser, RuleSelector, RuleSelectorParser};
use ruff_linter::{RuleParser, RuleSelector, RuleSelectorParser};
use ruff_workspace::configuration::{Configuration, RuleSelection};
use ruff_workspace::options::PycodestyleOptions;
use ruff_workspace::resolver::ConfigurationTransformer;
@@ -83,7 +83,7 @@ pub enum Command {
},
}
// The `Parser` derive is for ruff_dev, for ruff `Args` would be sufficient
// The `Parser` derive is for ruff_dev, for ruff_cli `Args` would be sufficient
#[derive(Clone, Debug, clap::Parser)]
#[allow(clippy::struct_excessive_bools)]
pub struct CheckCommand {
@@ -104,7 +104,6 @@ pub struct CheckCommand {
no_unsafe_fixes: bool,
/// Show violations with source code.
/// Use `--no-show-source` to disable.
/// (Deprecated: use `--output-format=full` or `--output-format=concise` instead of `--show-source` and `--no-show-source`, respectively)
#[arg(long, overrides_with("no_show_source"))]
show_source: bool,
#[clap(long, overrides_with("show_source"), hide = true)]
@@ -132,8 +131,6 @@ pub struct CheckCommand {
ignore_noqa: bool,
/// Output serialization format for violations.
/// The default serialization format is "concise".
/// In preview mode, the default serialization format is "full".
#[arg(long, value_enum, env = "RUFF_OUTPUT_FORMAT")]
pub output_format: Option<SerializationFormat>,
@@ -293,10 +290,6 @@ pub struct CheckCommand {
/// The name of the file when passing it through stdin.
#[arg(long, help_heading = "Miscellaneous")]
pub stdin_filename: Option<PathBuf>,
/// List of mappings from file extension to language (one of ["python", "ipynb", "pyi"]). For
/// example, to treat `.ipy` files as IPython notebooks, use `--extension ipy:ipynb`.
#[arg(long, value_delimiter = ',')]
pub extension: Option<Vec<ExtensionPair>>,
/// Exit with status code "0", even upon detecting lint violations.
#[arg(
short,
@@ -359,6 +352,9 @@ pub struct CheckCommand {
conflicts_with = "watch",
)]
pub show_settings: bool,
/// List of mappings from file extension to language (one of ["python", "ipynb", "pyi"]).
#[arg(long, value_delimiter = ',', hide = true)]
pub extension: Option<Vec<ExtensionPair>>,
/// Dev-only argument to show fixes
#[arg(long, hide = true)]
pub ecosystem_ci: bool,
@@ -427,10 +423,6 @@ pub struct FormatCommand {
/// The name of the file when passing it through stdin.
#[arg(long, help_heading = "Miscellaneous")]
pub stdin_filename: Option<PathBuf>,
/// List of mappings from file extension to language (one of ["python", "ipynb", "pyi"]). For
/// example, to treat `.ipy` files as IPython notebooks, use `--extension ipy:ipynb`.
#[arg(long, value_delimiter = ',')]
pub extension: Option<Vec<ExtensionPair>>,
/// The minimum Python version that should be supported.
#[arg(long, value_enum)]
pub target_version: Option<PythonVersion>,
@@ -536,6 +528,7 @@ impl CheckCommand {
self.no_respect_gitignore,
),
select: self.select,
show_source: resolve_bool_arg(self.show_source, self.no_show_source),
target_version: self.target_version,
unfixable: self.unfixable,
// TODO(charlie): Included in `pyproject.toml`, but not inherited.
@@ -545,11 +538,7 @@ impl CheckCommand {
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: resolve_output_format(
self.output_format,
resolve_bool_arg(self.show_source, self.no_show_source),
resolve_bool_arg(self.preview, self.no_preview).unwrap_or_default(),
),
output_format: self.output_format,
show_fixes: resolve_bool_arg(self.show_fixes, self.no_show_fixes),
extension: self.extension,
},
@@ -582,7 +571,6 @@ impl FormatCommand {
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
target_version: self.target_version,
cache_dir: self.cache_dir,
extension: self.extension,
// Unsupported on the formatter CLI, but required on `Overrides`.
..CliOverrides::default()
@@ -600,43 +588,6 @@ fn resolve_bool_arg(yes: bool, no: bool) -> Option<bool> {
}
}
fn resolve_output_format(
output_format: Option<SerializationFormat>,
show_sources: Option<bool>,
preview: bool,
) -> Option<SerializationFormat> {
Some(match (output_format, show_sources) {
(Some(o), None) => o,
(Some(SerializationFormat::Grouped), Some(true)) => {
warn_user!("`--show-source` with `--output-format=grouped` is deprecated, and will not show source files. Use `--output-format=full` to show source information.");
SerializationFormat::Grouped
}
(Some(fmt), Some(true)) => {
warn_user!("The `--show-source` argument is deprecated and has been ignored in favor of `--output-format={fmt}`.");
fmt
}
(Some(fmt), Some(false)) => {
warn_user!("The `--no-show-source` argument is deprecated and has been ignored in favor of `--output-format={fmt}`.");
fmt
}
(None, Some(true)) => {
warn_user!("The `--show-source` argument is deprecated. Use `--output-format=full` instead.");
SerializationFormat::Full
}
(None, Some(false)) => {
warn_user!("The `--no-show-source` argument is deprecated. Use `--output-format=concise` instead.");
SerializationFormat::Concise
}
(None, None) => return None
}).map(|format| match format {
SerializationFormat::Text => {
warn_user!("`--output-format=text` is deprecated. Use `--output-format=full` or `--output-format=concise` instead. `text` will be treated as `{}`.", SerializationFormat::default(preview));
SerializationFormat::default(preview)
},
other => other
})
}
/// CLI settings that are distinct from configuration (commands, lists of files,
/// etc.).
#[allow(clippy::struct_excessive_bools)]
@@ -691,6 +642,7 @@ pub struct CliOverrides {
pub preview: Option<PreviewMode>,
pub respect_gitignore: Option<bool>,
pub select: Option<Vec<RuleSelector>>,
pub show_source: Option<bool>,
pub target_version: Option<PythonVersion>,
pub unfixable: Option<Vec<RuleSelector>>,
// TODO(charlie): Captured in pyproject.toml as a default, but not part of `Settings`.
@@ -777,6 +729,9 @@ impl ConfigurationTransformer for CliOverrides {
if let Some(respect_gitignore) = &self.respect_gitignore {
config.respect_gitignore = Some(*respect_gitignore);
}
if let Some(show_source) = &self.show_source {
config.show_source = Some(*show_source);
}
if let Some(show_fixes) = &self.show_fixes {
config.show_fixes = Some(*show_fixes);
}
@@ -784,7 +739,7 @@ impl ConfigurationTransformer for CliOverrides {
config.target_version = Some(*target_version);
}
if let Some(extension) = &self.extension {
config.extension = Some(extension.iter().cloned().collect());
config.lint.extension = Some(extension.clone().into_iter().collect());
}
config

View File

@@ -3,8 +3,8 @@ use std::process::ExitCode;
use clap::{Parser, Subcommand};
use colored::Colorize;
use ruff::args::{Args, Command};
use ruff::{run, ExitStatus};
use ruff_cli::args::{Args, Command};
use ruff_cli::{run, ExitStatus};
#[cfg(target_os = "windows")]
#[global_allocator]

View File

@@ -106,19 +106,13 @@ pub(crate) fn format(
match entry {
Ok(resolved_file) => {
let path = resolved_file.path();
let settings = resolver.resolve(path);
let source_type = match settings.formatter.extension.get(path) {
None => match SourceType::from(path) {
SourceType::Python(source_type) => source_type,
SourceType::Toml(_) => {
// Ignore any non-Python files.
return None;
}
},
Some(language) => PySourceType::from(language),
let SourceType::Python(source_type) = SourceType::from(&path) else {
// Ignore any non-Python files.
return None;
};
let settings = resolver.resolve(path);
// Ignore files that are excluded from formatting
if (settings.file_resolver.force_exclude || !resolved_file.is_root())
&& match_exclusion(

View File

@@ -53,23 +53,16 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
}
let path = cli.stdin_filename.as_deref();
let settings = &resolver.base_settings().formatter;
let source_type = match path.and_then(|path| settings.extension.get(path)) {
None => match path.map(SourceType::from).unwrap_or_default() {
SourceType::Python(source_type) => source_type,
SourceType::Toml(_) => {
if mode.is_write() {
parrot_stdin()?;
}
return Ok(ExitStatus::Success);
}
},
Some(language) => PySourceType::from(language),
let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
if mode.is_write() {
parrot_stdin()?;
}
return Ok(ExitStatus::Success);
};
// Format the file.
match format_source_code(path, settings, source_type, mode) {
match format_source_code(path, &resolver.base_settings().formatter, source_type, mode) {
Ok(result) => match mode {
FormatMode::Write => Ok(ExitStatus::Success),
FormatMode::Check | FormatMode::Diff => {

View File

@@ -35,7 +35,7 @@ pub(crate) fn show_settings(
if let Some(settings_path) = pyproject_config.path.as_ref() {
writeln!(writer, "Settings path: {settings_path:?}")?;
}
write!(writer, "{settings}")?;
writeln!(writer, "{settings:#?}")?;
Ok(())
}

View File

@@ -1,5 +1,5 @@
---
source: crates/ruff/src/commands/check.rs
source: crates/ruff_cli/src/commands/check.rs
---
/home/ferris/project/code.py:1:1: E902 Permission denied (os error 13)
/home/ferris/project/notebook.ipynb:1:1: E902 Permission denied (os error 13)

View File

@@ -17,7 +17,7 @@ use ruff_linter::logging::DisplayParseError;
use ruff_linter::message::Message;
use ruff_linter::pyproject_toml::lint_pyproject_toml;
use ruff_linter::registry::AsRule;
use ruff_linter::settings::types::UnsafeFixes;
use ruff_linter::settings::types::{ExtensionMapping, UnsafeFixes};
use ruff_linter::settings::{flags, LinterSettings};
use ruff_linter::source_kind::{SourceError, SourceKind};
use ruff_linter::{fs, IOError, SyntaxError};
@@ -179,6 +179,11 @@ impl AddAssign for FixMap {
}
}
fn override_source_type(path: Option<&Path>, extension: &ExtensionMapping) -> Option<PySourceType> {
let ext = path?.extension()?.to_str()?;
extension.get(ext).map(PySourceType::from)
}
/// Lint the source code at the given `Path`.
pub(crate) fn lint_path(
path: &Path,
@@ -223,7 +228,7 @@ pub(crate) fn lint_path(
debug!("Checking: {}", path.display());
let source_type = match settings.extension.get(path).map(PySourceType::from) {
let source_type = match override_source_type(Some(path), &settings.extension) {
Some(source_type) => source_type,
None => match SourceType::from(path) {
SourceType::Toml(TomlSourceType::Pyproject) => {
@@ -393,14 +398,15 @@ pub(crate) fn lint_stdin(
fix_mode: flags::FixMode,
) -> Result<Diagnostics> {
// TODO(charlie): Support `pyproject.toml`.
let source_type = match path.and_then(|path| settings.linter.extension.get(path)) {
None => match path.map(SourceType::from).unwrap_or_default() {
SourceType::Python(source_type) => source_type,
SourceType::Toml(_) => {
return Ok(Diagnostics::default());
}
},
Some(language) => PySourceType::from(language),
let source_type = if let Some(source_type) =
override_source_type(path, &settings.linter.extension)
{
source_type
} else {
let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
return Ok(Diagnostics::default());
};
source_type
};
// Extract the sources from the file.

View File

@@ -255,6 +255,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
unsafe_fixes,
output_format,
show_fixes,
show_source,
..
} = pyproject_config.settings;
@@ -283,6 +284,9 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
if show_fixes {
printer_flags |= PrinterFlags::SHOW_FIX_SUMMARY;
}
if show_source {
printer_flags |= PrinterFlags::SHOW_SOURCE;
}
if cli.ecosystem_ci {
warn_user!(
"The formatting of fixes emitted by this option is a work-in-progress, subject to \
@@ -321,14 +325,9 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
printer_flags,
);
let preview = overrides.preview.unwrap_or_default().is_enabled();
if cli.watch {
if output_format != SerializationFormat::default(preview) {
warn_user!(
"`--output-format {}` is always used in watch mode.",
SerializationFormat::default(preview)
);
if output_format != SerializationFormat::Text {
warn_user!("`--output-format text` is always used in watch mode.");
}
// Configure the file watcher.
@@ -354,7 +353,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
fix_mode,
unsafe_fixes,
)?;
printer.write_continuously(&mut writer, &messages, preview)?;
printer.write_continuously(&mut writer, &messages)?;
// In watch mode, we may need to re-resolve the configuration.
// TODO(charlie): Re-compute other derivative values, like the `printer`.
@@ -387,7 +386,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
fix_mode,
unsafe_fixes,
)?;
printer.write_continuously(&mut writer, &messages, preview)?;
printer.write_continuously(&mut writer, &messages)?;
}
Err(err) => return Err(err.into()),
}

View File

@@ -27,6 +27,8 @@ bitflags! {
pub(crate) struct Flags: u8 {
/// Whether to show violations when emitting diagnostics.
const SHOW_VIOLATIONS = 0b0000_0001;
/// Whether to show the source code when emitting diagnostics.
const SHOW_SOURCE = 0b000_0010;
/// Whether to show a summary of the fixed violations when emitting diagnostics.
const SHOW_FIX_SUMMARY = 0b0000_0100;
/// Whether to show a diff of each fixed violation when emitting diagnostics.
@@ -216,10 +218,7 @@ impl Printer {
if !self.flags.intersects(Flags::SHOW_VIOLATIONS) {
if matches!(
self.format,
SerializationFormat::Text
| SerializationFormat::Full
| SerializationFormat::Concise
| SerializationFormat::Grouped
SerializationFormat::Text | SerializationFormat::Grouped
) {
if self.flags.intersects(Flags::SHOW_FIX_SUMMARY) {
if !diagnostics.fixed.is_empty() {
@@ -246,12 +245,11 @@ impl Printer {
SerializationFormat::Junit => {
JunitEmitter.emit(writer, &diagnostics.messages, &context)?;
}
SerializationFormat::Concise
| SerializationFormat::Full => {
SerializationFormat::Text => {
TextEmitter::default()
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
.with_show_fix_diff(self.flags.intersects(Flags::SHOW_FIX_DIFF))
.with_show_source(self.format == SerializationFormat::Full)
.with_show_source(self.flags.intersects(Flags::SHOW_SOURCE))
.with_unsafe_fixes(self.unsafe_fixes)
.emit(writer, &diagnostics.messages, &context)?;
@@ -267,6 +265,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)
.emit(writer, &diagnostics.messages, &context)?;
@@ -295,7 +294,6 @@ impl Printer {
SerializationFormat::Sarif => {
SarifEmitter.emit(writer, &diagnostics.messages, &context)?;
}
SerializationFormat::Text => unreachable!("Text is deprecated and should have been automatically converted to the default serialization format")
}
writer.flush()?;
@@ -344,9 +342,7 @@ impl Printer {
}
match self.format {
SerializationFormat::Text
| SerializationFormat::Full
| SerializationFormat::Concise => {
SerializationFormat::Text => {
// Compute the maximum number of digits in the count and code, for all messages,
// to enable pretty-printing.
let count_width = num_digits(
@@ -407,7 +403,6 @@ impl Printer {
&self,
writer: &mut dyn Write,
diagnostics: &Diagnostics,
preview: bool,
) -> Result<()> {
if matches!(self.log_level, LogLevel::Silent) {
return Ok(());
@@ -435,7 +430,7 @@ 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_source(preview)
.with_show_source(self.flags.intersects(Flags::SHOW_SOURCE))
.with_unsafe_fixes(self.unsafe_fixes)
.emit(writer, &diagnostics.messages, &context)?;
}

View File

@@ -0,0 +1,5 @@
---
source: crates/ruff_cli/src/version.rs
expression: version
---
0.0.0

View File

@@ -1,5 +1,5 @@
---
source: crates/ruff/src/version.rs
source: crates/ruff_cli/src/version.rs
expression: version
---
0.0.0 (53b0f5d92 2023-10-19)

View File

@@ -1,5 +1,5 @@
---
source: crates/ruff/src/version.rs
source: crates/ruff_cli/src/version.rs
expression: version
---
0.0.0+24 (53b0f5d92 2023-10-19)

View File

@@ -1,5 +1,5 @@
---
source: crates/ruff/src/version.rs
source: crates/ruff_cli/src/version.rs
expression: version
---
{

View File

@@ -508,11 +508,6 @@ if __name__ == '__main__':
say_hy("dear Ruff contributor")
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration:
- 'extend-select' -> 'lint.extend-select'
- 'ignore' -> 'lint.ignore'
"###);
Ok(())
}
@@ -551,11 +546,6 @@ if __name__ == '__main__':
say_hy("dear Ruff contributor")
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration:
- 'extend-select' -> 'lint.extend-select'
- 'ignore' -> 'lint.ignore'
"###);
Ok(())
}
@@ -1478,73 +1468,3 @@ fn test_notebook_trailing_semicolon() {
----- stderr -----
"###);
}
#[test]
fn extension() -> Result<()> {
let tempdir = TempDir::new()?;
let ruff_toml = tempdir.path().join("ruff.toml");
fs::write(
&ruff_toml,
r#"
include = ["*.ipy"]
"#,
)?;
fs::write(
tempdir.path().join("main.ipy"),
r#"
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "ad6f36d9-4b7d-4562-8d00-f15a0f1fbb6d",
"metadata": {},
"outputs": [],
"source": [
"x=1"
]
}
],
"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.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
"#,
)?;
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.current_dir(tempdir.path())
.arg("format")
.arg("--no-cache")
.args(["--config", &ruff_toml.file_name().unwrap().to_string_lossy()])
.args(["--extension", "ipy:ipynb"])
.arg("."), @r###"
success: true
exit_code: 0
----- stdout -----
1 file reformatted
----- stderr -----
"###);
Ok(())
}

View File

@@ -20,9 +20,9 @@ use path_absolutize::path_dedot;
use tempfile::TempDir;
#[cfg(unix)]
use ruff::args::Args;
use ruff_cli::args::Args;
#[cfg(unix)]
use ruff::run;
use ruff_cli::run;
const BIN_NAME: &str = "ruff";
@@ -31,14 +31,25 @@ fn ruff_cmd() -> Command {
}
/// Builder for `ruff check` commands.
#[derive(Debug, Default)]
#[derive(Debug)]
struct RuffCheck<'a> {
output_format: Option<&'a str>,
output_format: &'a str,
config: Option<&'a Path>,
filename: Option<&'a str>,
args: Vec<&'a str>,
}
impl<'a> Default for RuffCheck<'a> {
fn default() -> RuffCheck<'a> {
RuffCheck {
output_format: "text",
config: None,
filename: None,
args: vec![],
}
}
}
impl<'a> RuffCheck<'a> {
/// Set the `--config` option.
#[must_use]
@@ -50,7 +61,7 @@ impl<'a> RuffCheck<'a> {
/// Set the `--output-format` option.
#[must_use]
fn output_format(mut self, format: &'a str) -> Self {
self.output_format = Some(format);
self.output_format = format;
self
}
@@ -71,11 +82,7 @@ impl<'a> RuffCheck<'a> {
/// Generate a [`Command`] for the `ruff check` command.
fn build(self) -> Command {
let mut cmd = ruff_cmd();
if let Some(output_format) = self.output_format {
cmd.args(["--output-format", output_format]);
}
cmd.arg("--no-cache");
cmd.args(["--output-format", self.output_format, "--no-cache"]);
if let Some(path) = self.config {
cmd.arg("--config");
cmd.arg(path);
@@ -736,28 +743,8 @@ fn stdin_parse_error() {
}
#[test]
fn full_output_preview() {
let mut cmd = RuffCheck::default().args(["--preview"]).build();
assert_cmd_snapshot!(cmd
.pass_stdin("l = 1"), @r###"
success: false
exit_code: 1
----- stdout -----
-:1:1: E741 Ambiguous variable name: `l`
|
1 | l = 1
| ^ E741
|
Found 1 error.
----- stderr -----
"###);
}
#[test]
fn full_output_format() {
let mut cmd = RuffCheck::default().output_format("full").build();
fn show_source() {
let mut cmd = RuffCheck::default().args(["--show-source"]).build();
assert_cmd_snapshot!(cmd
.pass_stdin("l = 1"), @r###"
success: false
@@ -852,12 +839,13 @@ fn nursery_direct() {
assert_cmd_snapshot!(cmd
.pass_stdin("I=42\n"), @r###"
success: false
exit_code: 2
exit_code: 1
----- stdout -----
-:1:2: E225 Missing whitespace around operator
Found 1 error.
----- stderr -----
ruff failed
Cause: Selection of unstable rule `E225` without the `--preview` flag is not allowed.
warning: Selection of nursery rule `E225` without the `--preview` flag is deprecated.
"###);
}
@@ -868,12 +856,14 @@ fn nursery_group_selector() {
assert_cmd_snapshot!(cmd
.pass_stdin("I=42\n"), @r###"
success: false
exit_code: 2
exit_code: 1
----- stdout -----
-:1:1: CPY001 Missing copyright notice at top of file
-:1:2: E225 Missing whitespace around operator
Found 2 errors.
----- stderr -----
ruff failed
Cause: The `NURSERY` selector was removed. Use the `--preview` flag instead.
warning: The `NURSERY` selector has been deprecated. Use the `--preview` flag instead.
"###);
}
@@ -886,12 +876,15 @@ fn nursery_group_selector_preview_enabled() {
assert_cmd_snapshot!(cmd
.pass_stdin("I=42\n"), @r###"
success: false
exit_code: 2
exit_code: 1
----- stdout -----
-:1:1: CPY001 Missing copyright notice at top of file
-:1:2: E225 [*] Missing whitespace around operator
Found 2 errors.
[*] 1 fixable with the `--fix` option.
----- stderr -----
ruff failed
Cause: The `NURSERY` selector was removed. Unstable rules should be selected individually or by their respective groups.
warning: The `NURSERY` selector has been deprecated.
"###);
}
@@ -907,18 +900,7 @@ fn preview_enabled_prefix() {
exit_code: 1
----- stdout -----
-:1:1: E741 Ambiguous variable name: `I`
|
1 | I=42
| ^ E741
|
-:1:2: E225 [*] Missing whitespace around operator
|
1 | I=42
| ^ E225
|
= help: Add missing whitespace
Found 2 errors.
[*] 1 fixable with the `--fix` option.
@@ -937,30 +919,9 @@ fn preview_enabled_all() {
exit_code: 1
----- stdout -----
-:1:1: E741 Ambiguous variable name: `I`
|
1 | I=42
| ^ E741
|
-:1:1: D100 Missing docstring in public module
|
1 | I=42
| D100
|
-:1:1: CPY001 Missing copyright notice at top of file
|
1 | I=42
| CPY001
|
-:1:2: E225 [*] Missing whitespace around operator
|
1 | I=42
| ^ E225
|
= help: Add missing whitespace
Found 4 errors.
[*] 1 fixable with the `--fix` option.
@@ -982,12 +943,6 @@ fn preview_enabled_direct() {
exit_code: 1
----- stdout -----
-:1:2: E225 [*] Missing whitespace around operator
|
1 | I=42
| ^ E225
|
= help: Add missing whitespace
Found 1 error.
[*] 1 fixable with the `--fix` option.
@@ -1095,317 +1050,6 @@ fn preview_enabled_group_ignore() {
"###);
}
#[test]
fn removed_direct() {
// Selection of a removed rule should fail
let mut cmd = RuffCheck::default().args(["--select", "PLR1706"]).build();
assert_cmd_snapshot!(cmd, @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Rule `PLR1706` was removed and cannot be selected.
"###);
}
#[test]
fn removed_indirect() {
// Selection _including_ a removed rule without matching should not fail
// nor should the rule be used
let mut cmd = RuffCheck::default().args(["--select", "PLR"]).build();
assert_cmd_snapshot!(cmd.pass_stdin(r###"
# This would have been a PLR1706 violation
x, y = 1, 2
maximum = x >= y and x or y
"""###), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
"###);
}
#[test]
fn deprecated_direct() {
// Selection of a deprecated rule without preview enabled should still work
// but a warning should be displayed
let mut cmd = RuffCheck::default().args(["--select", "TRY200"]).build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
def reciprocal(n):
try:
return 1 / n
except ZeroDivisionError:
raise ValueError
"###), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
warning: The rule `TRY200` is deprecated and will be removed in a future release.
"###);
}
#[test]
fn deprecated_multiple_direct() {
let mut cmd = RuffCheck::default()
.args(["--select", "ANN101", "--select", "ANN102"])
.build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
class Foo:
def a(self):
pass
@classmethod
def b(cls):
pass
"###), @r###"
success: false
exit_code: 1
----- stdout -----
-:3:11: ANN101 Missing type annotation for `self` in method
-:7:11: ANN102 Missing type annotation for `cls` in classmethod
Found 2 errors.
----- stderr -----
warning: The rule `ANN102` is deprecated and will be removed in a future release.
warning: The rule `ANN101` is deprecated and will be removed in a future release.
"###);
}
#[test]
fn deprecated_indirect() {
// `ANN` includes deprecated rules `ANN101` and `ANN102` but should not warn
// since it is not a "direct" selection
let mut cmd = RuffCheck::default().args(["--select", "ANN"]).build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
class Foo:
def a(self):
pass
@classmethod
def b(cls):
pass
"###), @r###"
success: false
exit_code: 1
----- stdout -----
-:3:9: ANN201 Missing return type annotation for public function `a`
-:3:11: ANN101 Missing type annotation for `self` in method
-:7:9: ANN206 Missing return type annotation for classmethod `b`
-:7:11: ANN102 Missing type annotation for `cls` in classmethod
Found 4 errors.
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
----- stderr -----
"###);
}
#[test]
fn deprecated_prefix() {
// `ANN1` only includes deprecated rules `ANN101` and `ANN102` so it should warn
let mut cmd = RuffCheck::default().args(["--select", "ANN1"]).build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
class Foo:
def a(self):
pass
@classmethod
def b(cls):
pass
"###), @r###"
success: false
exit_code: 1
----- stdout -----
-:3:11: ANN101 Missing type annotation for `self` in method
-:7:11: ANN102 Missing type annotation for `cls` in classmethod
Found 2 errors.
----- stderr -----
warning: The rule prefix `ANN1` is deprecated and will be removed in a future release.
"###);
}
#[test]
fn deprecated_redirected() {
// Selection of a redirected deprecated rule without preview enabled should still work
// but a warning should be displayed
let mut cmd = RuffCheck::default().args(["--select", "PGH001"]).build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
x = eval(input("Enter a number: "))
"###), @r###"
success: false
exit_code: 1
----- stdout -----
-:2:5: S307 Use of possibly insecure function; consider using `ast.literal_eval`
Found 1 error.
----- stderr -----
warning: The rule `PGH001` is deprecated and will be removed in a future release. Use `S307` instead.
"###);
}
#[test]
fn deprecated_direct_preview_enabled() {
// Direct selection of a deprecated rule in preview should fail
let mut cmd = RuffCheck::default()
.args(["--select", "TRY200", "--preview"])
.build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
def reciprocal(n):
try:
return 1 / n
except ZeroDivisionError:
raise ValueError
"###), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Selection of deprecated rule `TRY200` is not allowed when preview mode is enabled.
"###);
}
#[test]
fn deprecated_redirect_preview_enabled() {
// Direct selection of a deprecated rule in preview should fail
let mut cmd = RuffCheck::default()
.args(["--select", "PGH001", "--preview"])
.build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
x = eval(input("Enter a number: "))
"###), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Selection of deprecated rule `PGH001` is not allowed when preview mode is enabled. Use `S307` instead.
"###);
}
#[test]
fn deprecated_indirect_preview_enabled() {
// `TRY200` is deprecated and should be off by default in preview.
let mut cmd = RuffCheck::default()
.args(["--select", "TRY", "--preview"])
.build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
def reciprocal(n):
try:
return 1 / n
except ZeroDivisionError:
raise ValueError
"###), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
"###);
}
#[test]
fn deprecated_linter_preview_enabled() {
// e.g. all the rules in `PGH` are and should fail in preview
let mut cmd = RuffCheck::default()
.args(["--select", "PGH", "--preview"])
.build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
x = eval(input("Enter a number: "))
"###), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Selection of deprecated rule category `PGH` is not allowed when preview mode is enabled.
"###);
}
#[test]
fn deprecated_multiple_direct_preview_enabled() {
// Direct selection of the deprecated rules in preview should fail with
// a message listing all of the rule codes
let mut cmd = RuffCheck::default()
.args(["--select", "ANN101", "--select", "ANN102", "--preview"])
.build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
def reciprocal(n):
try:
return 1 / n
except ZeroDivisionError:
raise ValueError
"###), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Selection of deprecated rules is not allowed when preview mode is enabled. Remove selection of:
- ANN102
- ANN101
"###);
}
#[test]
fn deprecated_multiple_direct_and_redirected_preview_enabled() {
// Direct selection of the deprecated rules in preview should fail with
// a message listing all of the rule codes
// The redirected rules should include an alternative
let mut cmd = RuffCheck::default()
.args([
"--select",
"ANN101",
"--select",
"ANN102",
"--select",
"PGH001",
"--preview",
])
.build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
x = eval(input("Enter a number: "))
def reciprocal(n):
try:
return 1 / n
except ZeroDivisionError:
raise ValueError
"###), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Selection of deprecated rules is not allowed when preview mode is enabled. Remove selection of:
- ANN102
- ANN101
- PGH001 (use `S307` instead)
"###);
}
/// An unreadable pyproject.toml in non-isolated mode causes ruff to hard-error trying to build up
/// configuration globs
#[cfg(unix)]
@@ -2012,65 +1656,3 @@ def log(x, base) -> float:
);
Ok(())
}
#[test]
fn fix_preview() -> Result<()> {
let tempdir = TempDir::new()?;
let ruff_toml = tempdir.path().join("ruff.toml");
fs::write(
&ruff_toml,
r#"
[lint]
preview = true
explicit-preview-rules = true
select = ["RUF017"]
"#,
)?;
let mut cmd = RuffCheck::default().config(&ruff_toml).build();
assert_cmd_snapshot!(cmd
.pass_stdin("x = [1, 2, 3]\ny = [4, 5, 6]\nsum([x, y], [])"),
@r###"
success: false
exit_code: 1
----- stdout -----
-:3:1: RUF017 Avoid quadratic list summation
Found 1 error.
No fixes available (1 hidden fix can be enabled with the `--unsafe-fixes` option).
----- stderr -----
"###);
Ok(())
}
#[test]
fn unfixable_preview() -> Result<()> {
let tempdir = TempDir::new()?;
let ruff_toml = tempdir.path().join("ruff.toml");
fs::write(
&ruff_toml,
r#"
[lint]
preview = true
explicit-preview-rules = true
select = ["RUF017"]
unfixable = ["RUF"]
"#,
)?;
let mut cmd = RuffCheck::default().config(&ruff_toml).build();
assert_cmd_snapshot!(cmd
.pass_stdin("x = [1, 2, 3]\ny = [4, 5, 6]\nsum([x, y], [])"),
@r###"
success: false
exit_code: 1
----- stdout -----
-:3:1: RUF017 Avoid quadratic list summation
Found 1 error.
----- stderr -----
"###);
Ok(())
}

View File

@@ -11,7 +11,7 @@ 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", "concise"];
const STDIN_BASE_OPTIONS: &[&str] = &["--no-cache", "--output-format", "text"];
#[test]
fn top_level_options() -> Result<()> {
@@ -44,11 +44,6 @@ inline-quotes = "single"
[*] 2 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration:
- 'extend-select' -> 'lint.extend-select'
- 'flake8-quotes' -> 'lint.flake8-quotes'
"###);
Ok(())
}
@@ -119,10 +114,6 @@ inline-quotes = "single"
[*] 2 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration:
- 'extend-select' -> 'lint.extend-select'
"###);
Ok(())
}
@@ -162,10 +153,6 @@ inline-quotes = "single"
[*] 2 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration:
- 'flake8-quotes' -> 'lint.flake8-quotes'
"###);
Ok(())
}
@@ -241,10 +228,6 @@ OTHER = "OTHER"
[*] 3 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration:
- 'extend-select' -> 'lint.extend-select'
"###);
Ok(())
}
@@ -288,10 +271,6 @@ if __name__ == "__main__":
[*] 2 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration:
- 'extend-select' -> 'lint.extend-select'
"###);
Ok(())
}
@@ -330,11 +309,6 @@ _ = "---------------------------------------------------------------------------
Found 1 error.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration:
- 'select' -> 'lint.select'
- 'pycodestyle' -> 'lint.pycodestyle'
"###);
Ok(())
}
@@ -377,10 +351,6 @@ if __name__ == "__main__":
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration:
- 'extend-select' -> 'lint.extend-select'
"###);
Ok(())
}
@@ -423,10 +393,6 @@ if __name__ == "__main__":
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration:
- 'extend-select' -> 'lint.extend-select'
"###);
Ok(())
}
@@ -470,75 +436,3 @@ ignore = ["D203", "D212"]
"###);
Ok(())
}
#[test]
fn extension() -> Result<()> {
let tempdir = TempDir::new()?;
let ruff_toml = tempdir.path().join("ruff.toml");
fs::write(
&ruff_toml,
r#"
include = ["*.ipy"]
"#,
)?;
fs::write(
tempdir.path().join("main.ipy"),
r#"
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "ad6f36d9-4b7d-4562-8d00-f15a0f1fbb6d",
"metadata": {},
"outputs": [],
"source": [
"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.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
"#,
)?;
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.current_dir(tempdir.path())
.arg("check")
.args(STDIN_BASE_OPTIONS)
.args(["--config", &ruff_toml.file_name().unwrap().to_string_lossy()])
.args(["--extension", "ipy:ipynb"])
.arg("."), @r###"
success: false
exit_code: 1
----- stdout -----
main.ipy:cell 1:1:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
"###);
Ok(())
}

View File

@@ -39,10 +39,6 @@ fn check_project_include_defaults() {
[BASEPATH]/include-test/subdirectory/c.py
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration:
- 'select' -> 'lint.select'
"###);
});
}

View File

@@ -1,5 +1,5 @@
---
source: crates/ruff/tests/integration_test.rs
source: crates/ruff_cli/tests/integration_test.rs
info:
program: ruff
args:
@@ -51,7 +51,7 @@ else:
```
## Options
- `lint.pyflakes.extend-generics`
- `pyflakes.extend-generics`
## References
- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)

View File

@@ -1,5 +1,5 @@
---
source: crates/ruff/tests/integration_test.rs
source: crates/ruff_cli/tests/integration_test.rs
info:
program: ruff
args:

View File

@@ -11,21 +11,21 @@ repository = { workspace = true }
license = { workspace = true }
[dependencies]
ruff = { path = "../ruff" }
ruff_linter = { path = "../ruff_linter", features = ["schemars"] }
ruff_cli = { path = "../ruff_cli" }
ruff_diagnostics = { path = "../ruff_diagnostics" }
ruff_formatter = { path = "../ruff_formatter" }
ruff_linter = { path = "../ruff_linter", features = ["schemars"] }
ruff_notebook = { path = "../ruff_notebook" }
ruff_python_ast = { path = "../ruff_python_ast" }
ruff_python_codegen = { path = "../ruff_python_codegen" }
ruff_python_formatter = { path = "../ruff_python_formatter" }
ruff_notebook = { path = "../ruff_notebook" }
ruff_python_parser = { path = "../ruff_python_parser" }
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
ruff_python_trivia = { path = "../ruff_python_trivia" }
ruff_workspace = { path = "../ruff_workspace", features = ["schemars"]}
anyhow = { workspace = true }
clap = { workspace = true, features = ["wrap_help"] }
clap = { workspace = true }
ignore = { workspace = true }
imara-diff = { workspace = true }
indicatif = { workspace = true }

View File

@@ -27,8 +27,8 @@ use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::EnvFilter;
use ruff::args::{CliOverrides, FormatArguments, FormatCommand, LogLevelArgs};
use ruff::resolve::resolve;
use ruff_cli::args::{CliOverrides, FormatArguments, FormatCommand, LogLevelArgs};
use ruff_cli::resolve::resolve;
use ruff_formatter::{FormatError, LineWidth, PrintError};
use ruff_linter::logging::LogLevel;
use ruff_linter::settings::types::{FilePattern, FilePatternSet};
@@ -67,7 +67,7 @@ fn find_pyproject_config(
Ok(pyproject_config)
}
/// Find files that ruff would check so we can format them. Adapted from `ruff`.
/// Find files that ruff would check so we can format them. Adapted from `ruff_cli`.
#[allow(clippy::type_complexity)]
fn ruff_check_paths<'a>(
pyproject_config: &'a PyprojectConfig,
@@ -287,7 +287,7 @@ fn setup_logging(log_level_args: &LogLevelArgs, log_file: Option<&Path>) -> io::
LogLevel::Quiet => tracing::Level::WARN,
LogLevel::Silent => tracing::Level::ERROR,
};
// 1. `RUST_LOG=`, 2. explicit CLI log level, 3. info, the ruff default
// 1. `RUST_LOG=`, 2. explicit CLI log level, 3. info, the ruff_cli default
let filter_layer = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
EnvFilter::builder()
.with_default_directive(log_level.into())
@@ -461,7 +461,7 @@ fn format_dev_project(
// TODO(konstin): Respect black's excludes.
// Find files to check (or in this case, format twice). Adapted from ruff
// Find files to check (or in this case, format twice). Adapted from ruff_cli
// First argument is ignored
let (cli, overrides) = parse_cli(files)?;
let pyproject_config = find_pyproject_config(&cli, &overrides)?;

View File

@@ -8,7 +8,7 @@ use anyhow::{bail, Context, Result};
use clap::CommandFactory;
use pretty_assertions::StrComparison;
use ruff::args;
use ruff_cli::args;
use crate::generate_all::{Mode, REGENERATE_ALL_COMMAND};
use crate::ROOT_DIR;
@@ -114,15 +114,12 @@ pub(super) fn main(args: &Args) -> Result<()> {
/// Returns the output of `ruff help`.
fn help_text() -> String {
args::Args::command()
.term_width(79)
.render_help()
.to_string()
args::Args::command().render_help().to_string()
}
/// Returns the output of a given subcommand (e.g., `ruff help check`).
fn subcommand_help_text(subcommand: &str) -> Result<String> {
let mut cmd = args::Args::command().term_width(79);
let mut cmd = args::Args::command();
// The build call is necessary for the help output to contain `Usage: ruff
// check` instead of `Usage: check` see https://github.com/clap-rs/clap/issues/4685

View File

@@ -26,9 +26,9 @@ pub(crate) fn main(args: &Args) -> Result<()> {
for rule in Rule::iter() {
if let Some(explanation) = rule.explanation() {
let mut output = String::new();
output.push_str(&format!("# {} ({})", rule.as_ref(), rule.noqa_code()));
output.push('\n');
output.push('\n');
let (linter, _) = Linter::parse_code(&rule.noqa_code().to_string()).unwrap();
if linter.url().is_some() {
@@ -37,22 +37,6 @@ pub(crate) fn main(args: &Args) -> Result<()> {
output.push('\n');
}
if rule.is_deprecated() {
output.push_str(
r"**Warning: This rule is deprecated and will be removed in a future release.**",
);
output.push('\n');
output.push('\n');
}
if rule.is_removed() {
output.push_str(
r"**Warning: This rule has been removed and its documentation is only available for historical reasons.**",
);
output.push('\n');
output.push('\n');
}
let fix_availability = rule.fixable();
if matches!(
fix_availability,
@@ -132,7 +116,7 @@ fn process_documentation(documentation: &str, out: &mut String, rule_name: &str)
}
}
let anchor = option.replace('.', "_");
let anchor = option.replace('.', "-");
out.push_str(&format!("- [`{option}`][{option}]\n"));
after.push_str(&format!("[{option}]: ../settings.md#{anchor}\n"));
@@ -158,13 +142,13 @@ mod tests {
let mut output = String::new();
process_documentation(
"
See also [`lint.mccabe.max-complexity`] and [`lint.task-tags`].
See also [`mccabe.max-complexity`] and [`task-tags`].
Something [`else`][other].
## Options
- `lint.task-tags`
- `lint.mccabe.max-complexity`
- `task-tags`
- `mccabe.max-complexity`
[other]: http://example.com.",
&mut output,
@@ -173,18 +157,18 @@ Something [`else`][other].
assert_eq!(
output,
"
See also [`lint.mccabe.max-complexity`][lint.mccabe.max-complexity] and [`lint.task-tags`][lint.task-tags].
See also [`mccabe.max-complexity`][mccabe.max-complexity] and [`task-tags`][task-tags].
Something [`else`][other].
## Options
- [`lint.task-tags`][lint.task-tags]
- [`lint.mccabe.max-complexity`][lint.mccabe.max-complexity]
- [`task-tags`][task-tags]
- [`mccabe.max-complexity`][mccabe.max-complexity]
[other]: http://example.com.
[lint.task-tags]: ../settings.md#lint_task-tags
[lint.mccabe.max-complexity]: ../settings.md#lint_mccabe_max-complexity
[task-tags]: ../settings.md#task-tags
[mccabe.max-complexity]: ../settings.md#mccabe-max-complexity
"
);
}

View File

@@ -1,7 +1,6 @@
//! Generate a Markdown-compatible listing of configuration options for `pyproject.toml`.
//!
//! Used for <https://docs.astral.sh/ruff/settings/>.
use itertools::Itertools;
use std::fmt::Write;
use ruff_python_trivia::textwrap;
@@ -10,29 +9,16 @@ use ruff_workspace::options_base::{OptionField, OptionSet, OptionsMetadata, Visi
pub(crate) fn generate() -> String {
let mut output = String::new();
generate_set(
&mut output,
Set::Toplevel(Options::metadata()),
&mut Vec::new(),
);
generate_set(&mut output, &Set::Toplevel(Options::metadata()));
output
}
fn generate_set(output: &mut String, set: Set, parents: &mut Vec<Set>) {
match &set {
Set::Toplevel(_) => {
output.push_str("### Top-level\n");
}
Set::Named { name, .. } => {
let title = parents
.iter()
.filter_map(|set| set.name())
.chain(std::iter::once(name.as_str()))
.join(".");
writeln!(output, "#### `{title}`\n",).unwrap();
}
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() {
@@ -49,68 +35,72 @@ fn generate_set(output: &mut String, set: Set, parents: &mut Vec<Set>) {
fields.sort_unstable_by(|(name, _), (name2, _)| name.cmp(name2));
sets.sort_unstable_by(|(name, _), (name2, _)| name.cmp(name2));
parents.push(set);
// Generate the fields.
for (name, field) in &fields {
emit_field(output, name, field, parents.as_slice());
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 {
name: set_name.to_string(),
set: *sub_set,
},
parents,
);
generate_set(output, &Set::Named(set_name, *sub_set, set.level() + 1));
}
parents.pop();
}
enum Set {
enum Set<'a> {
Toplevel(OptionSet),
Named { name: String, set: OptionSet },
Named(&'a str, OptionSet, u32),
}
impl Set {
fn name(&self) -> Option<&str> {
impl<'a> Set<'a> {
fn name(&self) -> Option<&'a str> {
match self {
Set::Toplevel(_) => None,
Set::Named { name, .. } => Some(name),
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,
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, parents: &[Set]) {
let header_level = if parents.is_empty() { "####" } else { "#####" };
let parents_anchor = parents.iter().filter_map(|parent| parent.name()).join("_");
if parents_anchor.is_empty() {
output.push_str(&format!(
"{header_level} [`{name}`](#{name}) {{: #{name} }}\n"
));
fn emit_field(output: &mut String, name: &str, field: &OptionField, parent_set: &Set) {
let header_level = if parent_set.level() < 2 {
"####"
} else {
output.push_str(&format!(
"{header_level} [`{name}`](#{parents_anchor}_{name}) {{: #{parents_anchor}_{name} }}\n"
));
"#####"
};
// if there's a set name, we need to add it to the anchor
if let Some(set_name) = parent_set.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"
));
} else {
output.push_str(&format!("{header_level} [`{name}`](#{name})\n"));
}
output.push('\n');
if let Some(deprecated) = &field.deprecated {
@@ -139,12 +129,12 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, parents: &[S
output.push_str("**Example usage**:\n\n");
output.push_str(&format_tab(
"pyproject.toml",
&format_header(field.scope, parents, ConfigurationFile::PyprojectToml),
&format_header(field.scope, parent_set, ConfigurationFile::PyprojectToml),
field.example,
));
output.push_str(&format_tab(
"ruff.toml",
&format_header(field.scope, parents, ConfigurationFile::RuffToml),
&format_header(field.scope, parent_set, ConfigurationFile::RuffToml),
field.example,
));
output.push('\n');
@@ -162,22 +152,52 @@ fn format_tab(tab_name: &str, header: &str, content: &str) -> String {
/// Format the TOML header for the example usage for a given option.
///
/// For example: `[tool.ruff.format]` or `[tool.ruff.lint.isort]`.
fn format_header(scope: Option<&str>, parents: &[Set], configuration: ConfigurationFile) -> String {
let tool_parent = match configuration {
ConfigurationFile::PyprojectToml => Some("tool.ruff"),
ConfigurationFile::RuffToml => None,
};
let header = tool_parent
.into_iter()
.chain(parents.iter().filter_map(|parent| parent.name()))
.chain(scope)
.join(".");
if header.is_empty() {
String::new()
} else {
format!("[{header}]")
fn format_header(
scope: Option<&str>,
parent_set: &Set,
configuration: ConfigurationFile,
) -> String {
match configuration {
ConfigurationFile::PyprojectToml => {
let mut header = if let Some(set_name) = parent_set.name() {
if set_name == "format" {
String::from("tool.ruff.format")
} else {
format!("tool.ruff.lint.{set_name}")
}
} else {
"tool.ruff".to_string()
};
if let Some(scope) = scope {
if !header.is_empty() {
header.push('.');
}
header.push_str(scope);
}
format!("[{header}]")
}
ConfigurationFile::RuffToml => {
let mut header = if let Some(set_name) = parent_set.name() {
if set_name == "format" {
String::from("format")
} else {
format!("lint.{set_name}")
}
} else {
String::new()
};
if let Some(scope) = scope {
if !header.is_empty() {
header.push('.');
}
header.push_str(scope);
}
if header.is_empty() {
String::new()
} else {
format!("[{header}]")
}
}
}
}

View File

@@ -3,7 +3,6 @@
//! Used for <https://docs.astral.sh/ruff/rules/>.
use itertools::Itertools;
use ruff_linter::codes::RuleGroup;
use std::borrow::Cow;
use strum::IntoEnumIterator;
@@ -15,10 +14,6 @@ use ruff_workspace::options_base::OptionsMetadata;
const FIX_SYMBOL: &str = "🛠️";
const PREVIEW_SYMBOL: &str = "🧪";
const REMOVED_SYMBOL: &str = "";
const WARNING_SYMBOL: &str = "⚠️";
const STABLE_SYMBOL: &str = "✔️";
const SPACER: &str = "&nbsp;&nbsp;&nbsp;&nbsp;";
fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>, linter: &Linter) {
table_out.push_str("| Code | Name | Message | |");
@@ -26,33 +21,20 @@ 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 status_token = match rule.group() {
RuleGroup::Removed => {
format!("<span title='Rule has been removed'>{REMOVED_SYMBOL}</span>")
}
RuleGroup::Deprecated => {
format!("<span title='Rule has been deprecated'>{WARNING_SYMBOL}</span>")
}
#[allow(deprecated)]
RuleGroup::Preview | RuleGroup::Nursery => {
format!("<span title='Rule is in preview'>{PREVIEW_SYMBOL}</span>")
}
RuleGroup::Stable => {
// A full opacity checkmark is a bit aggressive for indicating stable
format!("<span title='Rule is stable' style='opacity: 0.6'>{STABLE_SYMBOL}</span>")
}
};
let fix_token = match rule.fixable() {
FixAvailability::Always | FixAvailability::Sometimes => {
format!("<span title='Automatic fix available'>{FIX_SYMBOL}</span>")
}
FixAvailability::None => {
format!("<span title='Automatic fix not available' style='opacity: 0.1' aria-hidden='true'>{FIX_SYMBOL}</span>")
format!("<span style='opacity: 0.1' aria-hidden='true'>{FIX_SYMBOL}</span>")
}
};
let tokens = format!("{status_token} {fix_token}");
let preview_token = if rule.is_preview() || rule.is_nursery() {
format!("<span title='Rule is in preview'>{PREVIEW_SYMBOL}</span>")
} else {
format!("<span style='opacity: 0.1' aria-hidden='true'>{PREVIEW_SYMBOL}</span>")
};
let status_token = format!("{fix_token} {preview_token}");
let rule_name = rule.as_ref();
@@ -66,20 +48,9 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>,
Cow::Borrowed(message)
};
// Start and end of style spans
let mut ss = "";
let mut se = "";
if rule.is_removed() {
ss = "<span style='opacity: 0.5', title='This rule has been removed'>";
se = "</span>";
} else if rule.is_deprecated() {
ss = "<span style='opacity: 0.8', title='This rule has been deprecated'>";
se = "</span>";
}
#[allow(clippy::or_fun_call)]
table_out.push_str(&format!(
"| {ss}{0}{1}{se} {{ #{0}{1} }} | {ss}{2}{se} | {ss}{3}{se} | {ss}{4}{se} |",
"| {0}{1} {{ #{0}{1} }} | {2} | {3} | {4} |",
linter.common_prefix(),
linter.code_for_rule(rule).unwrap(),
rule.explanation()
@@ -87,7 +58,7 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>,
.then_some(format_args!("[{rule_name}](rules/{rule_name}.md)"))
.unwrap_or(format_args!("{rule_name}")),
message,
tokens,
status_token,
));
table_out.push('\n');
}
@@ -98,33 +69,15 @@ pub(crate) fn generate() -> String {
// Generate the table string.
let mut table_out = String::new();
table_out.push_str("### Legend");
table_out.push_str(&format!(
"The {FIX_SYMBOL} emoji indicates that a rule is automatically fixable by the `--fix` command-line option."));
table_out.push('\n');
table_out.push('\n');
table_out.push_str(&format!(
"{SPACER}{STABLE_SYMBOL}{SPACER} The rule is stable."
"The {PREVIEW_SYMBOL} emoji indicates that a rule is in [\"preview\"](faq.md#what-is-preview)."
));
table_out.push_str("<br />");
table_out.push_str(&format!(
"{SPACER}{PREVIEW_SYMBOL}{SPACER} The rule is unstable and is in [\"preview\"](faq.md#what-is-preview)."
));
table_out.push_str("<br />");
table_out.push_str(&format!(
"{SPACER}{WARNING_SYMBOL}{SPACER} The rule has been deprecated and will be removed in a future release."
));
table_out.push_str("<br />");
table_out.push_str(&format!(
"{SPACER}{REMOVED_SYMBOL}{SPACER} The rule has been removed only the documentation is available."
));
table_out.push_str("<br />");
table_out.push_str(&format!(
"{SPACER}{FIX_SYMBOL}{SPACER} The rule is automatically fixable by the `--fix` command-line option."
));
table_out.push_str("<br />");
table_out.push('\n');
table_out.push('\n');
for linter in Linter::iter() {

View File

@@ -4,7 +4,7 @@
use anyhow::Result;
use clap::{Parser, Subcommand};
use ruff::check;
use ruff_cli::check;
use ruff_linter::logging::{set_up_logging, LogLevel};
use std::process::ExitCode;
@@ -56,9 +56,9 @@ enum Command {
/// Run a ruff command n times for profiling/benchmarking
Repeat {
#[clap(flatten)]
args: ruff::args::CheckCommand,
args: ruff_cli::args::CheckCommand,
#[clap(flatten)]
log_level_args: ruff::args::LogLevelArgs,
log_level_args: ruff_cli::args::LogLevelArgs,
/// Run this many times
#[clap(long)]
repeat: usize,

View File

@@ -35,7 +35,6 @@ mod source_code;
use crate::formatter::Formatter;
use crate::group_id::UniqueGroupIdBuilder;
use crate::prelude::TagKind;
use std::fmt;
use std::fmt::{Debug, Display};
use std::marker::PhantomData;
use std::num::{NonZeroU16, NonZeroU8, TryFromIntError};
@@ -114,12 +113,6 @@ impl Default for IndentWidth {
}
}
impl Display for IndentWidth {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl TryFrom<u8> for IndentWidth {
type Error = TryFromIntError;
@@ -153,12 +146,6 @@ impl Default for LineWidth {
}
}
impl Display for LineWidth {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl TryFrom<u16> for LineWidth {
type Error = TryFromIntError;

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_linter"
version = "0.1.15"
version = "0.1.11"
publish = false
authors = { workspace = true }
edition = { workspace = true }
@@ -85,6 +85,8 @@ tempfile = { workspace = true }
[features]
default = []
schemars = ["dep:schemars"]
# Enables the UnreachableCode rule
unreachable-code = []
[lints]
workspace = true

View File

@@ -1,87 +1,31 @@
import os
import subprocess
import time
from pathlib import Path
# Violation cases:
async def func():
async def foo():
open("foo")
async def func():
async def foo():
time.sleep(1)
async def func():
async def foo():
subprocess.run("foo")
async def func():
async def foo():
subprocess.call("foo")
async def func():
async def foo():
subprocess.foo(0)
async def func():
async def foo():
os.wait4(10)
async def func():
async def foo():
os.wait(12)
# Violation cases for pathlib:
async def func():
Path("foo").open() # ASYNC101
async def func():
p = Path("foo")
p.open() # ASYNC101
async def func():
with Path("foo").open() as f: # ASYNC101
pass
async def func() -> None:
p = Path("foo")
async def bar():
p.open() # ASYNC101
async def func() -> None:
(p1, p2) = (Path("foo"), Path("bar"))
p1.open() # ASYNC101
# Non-violation cases for pathlib:
class Foo:
def open(self):
pass
async def func():
Foo().open() # OK
async def func():
def open():
pass
open() # OK
def func():
Path("foo").open() # OK

View File

@@ -2,28 +2,10 @@
# Errors.
###
incorrect_set = {"value1", 23, 5, "value1"}
incorrect_set = {1, 1, 2}
incorrect_set_multiline = {
"value1",
23,
5,
"value1",
# B033
}
incorrect_set = {1, 1}
incorrect_set = {1, 1,}
incorrect_set = {0, 1, 1,}
incorrect_set = {0, 1, 1}
incorrect_set = {
0,
1,
1,
}
###
# Non-errors.
###
correct_set = {"value1", 23, 5}
correct_set = {5, "5"}
correct_set = {5}
correct_set = {}

View File

@@ -9,22 +9,6 @@ obj.startswith(foo) or obj.startswith("foo")
# error
obj.endswith(foo) or obj.startswith(foo) or obj.startswith("foo")
def func():
msg = "hello world"
x = "h"
y = ("h", "e", "l", "l", "o")
z = "w"
if msg.startswith(x) or msg.startswith(y) or msg.startswith(z): # Error
print("yes")
def func():
msg = "hello world"
if msg.startswith(("h", "e", "l", "l", "o")) or msg.startswith("h") or msg.startswith("w"): # Error
print("yes")
# ok
obj.startswith(("foo", "bar"))
# ok
@@ -33,46 +17,3 @@ obj.endswith(("foo", "bar"))
obj.startswith("foo") or obj.endswith("bar")
# ok
obj.startswith("foo") or abc.startswith("bar")
def func():
msg = "hello world"
x = "h"
y = ("h", "e", "l", "l", "o")
if msg.startswith(x) or msg.startswith(y): # OK
print("yes")
def func():
msg = "hello world"
y = ("h", "e", "l", "l", "o")
if msg.startswith(y): # OK
print("yes")
def func():
msg = "hello world"
y = ("h", "e", "l", "l", "o")
if msg.startswith(y) or msg.startswith(y): # OK
print("yes")
def func():
msg = "hello world"
y = ("h", "e", "l", "l", "o")
x = ("w", "o", "r", "l", "d")
if msg.startswith(y) or msg.startswith(x) or msg.startswith("h"): # OK
print("yes")
def func():
msg = "hello world"
y = ("h", "e", "l", "l", "o")
x = ("w", "o", "r", "l", "d")
if msg.startswith(y) or msg.endswith(x) or msg.startswith("h"): # OK
print("yes")

View File

@@ -20,12 +20,3 @@ else:
def func2(arg: _PrivateTypeAlias) -> None: ...
type _UnusedPEP695 = int
type _UnusedGeneric695[T] = list[T]
type _UsedPEP695 = str
type _UsedGeneric695[T] = tuple[T, ...]
def func4(arg: _UsedPEP695) -> None: ...
def func5(arg: _UsedGeneric695[bytes]) -> None: ...

View File

@@ -20,12 +20,3 @@ else:
def func2(arg: _PrivateTypeAlias) -> None: ...
type _UnusedPEP695 = int
type _UnusedGeneric695[T] = list[T]
type _UsedPEP695 = str
type _UsedGeneric695[T] = tuple[T, ...]
def func4(arg: _UsedPEP695) -> None: ...
def func5(arg: _UsedGeneric695[bytes]) -> None: ...

View File

@@ -16,8 +16,3 @@ class _UsedTypedDict(TypedDict):
class _CustomClass(_UsedTypedDict):
bar: list[int]
_UnusedTypedDict3 = TypedDict("_UnusedTypedDict3", {"foo": int})
_UsedTypedDict3 = TypedDict("_UsedTypedDict3", {"bar": bytes})
def uses_UsedTypedDict3(arg: _UsedTypedDict3) -> None: ...

View File

@@ -30,8 +30,3 @@ else:
class _CustomClass2(_UsedTypedDict2):
bar: list[int]
_UnusedTypedDict3 = TypedDict("_UnusedTypedDict3", {"foo": int})
_UsedTypedDict3 = TypedDict("_UsedTypedDict3", {"bar": bytes})
def uses_UsedTypedDict3(arg: _UsedTypedDict3) -> None: ...

View File

@@ -1,15 +1,14 @@
import _thread
import builtins
import os
import posix
from posix import abort
import sys as std_sys
import typing
from posix import abort
from typing import NoReturn
import _winapi
import pytest
import typing_extensions
import _thread
import _winapi
import pytest
from pytest import xfail as py_xfail
###
@@ -327,44 +326,3 @@ def end_of_file():
if False:
return 1
x = 2 \
# function return type annotation NoReturn
def foo(x: int) -> int:
def bar() -> NoReturn:
abort()
if x == 5:
return 5
bar()
def foo(string: str) -> str:
def raises(value: str) -> NoReturn:
raise RuntimeError("something went wrong")
match string:
case "a":
return "first"
case "b":
return "second"
case "c":
return "third"
case _:
raises(string)
def foo() -> int:
def baz() -> int:
return 1
def bar() -> NoReturn:
a = 1 + 2
raise AssertionError("Very bad")
if baz() > 3:
return 1
bar()

View File

@@ -363,46 +363,3 @@ def foo():
def mavko_debari(P_kbar):
D=0.4853881 + 3.6006116*P - 0.0117368*(P-1.3822)**2
return D
# contextlib suppress in with statement
import contextlib
def foo():
x = 2
with contextlib.suppress(Exception):
x = x + 1
return x
def foo(data):
with open("in.txt") as file_out, contextlib.suppress(IOError):
file_out.write(data)
data = 10
return data
def foo(data):
with open("in.txt") as file_out:
file_out.write(data)
with contextlib.suppress(IOError):
data = 10
return data
def foo():
y = 1
x = 2
with contextlib.suppress(Exception):
x = 1
y = y + 2
return y # RET504
def foo():
y = 1
if y > 0:
with contextlib.suppress(Exception):
y = 2
return y

View File

@@ -103,14 +103,6 @@ def bar4(x):
return None
def fibo(n):
if n<2:
return n;
else:
last = 1;
last2 = 0;
###
# Non-error
###
@@ -139,51 +131,6 @@ def bar3(x, y, z):
return None
def bar4(x):
if True:
return
else:
# comment
pass
def bar5():
if True:
return
else: # comment
pass
def bar6():
if True:
return
else\
:\
# comment
pass
def bar7():
if True:
return
else\
: # comment
pass
def bar8():
if True:
return
else: pass
def bar9():
if True:
return
else:\
pass
x = 0
if x == 1:

View File

@@ -1,195 +0,0 @@
def func():
# SIM113
idx = 0
for x in range(5):
g(x, idx)
idx += 1
h(x)
def func():
# SIM113
sum = 0
idx = 0
for x in range(5):
if g(x):
break
idx += 1
sum += h(x, idx)
def func():
# SIM113
idx = 0
for x, y in zip(range(5), range(5)):
g(x)
h(x, y)
idx += 1
def func():
# SIM113
idx = 0
sum = 0
for x in range(5):
sum += h(x, idx)
idx += 1
def func():
# SIM113
sum, idx = 0, 0
for x in range(5):
g(x, idx)
idx += 1
h(x)
def func():
# OK (index doesn't start at 0
idx = 10
for x in range(5):
g(x, idx)
idx += 1
h(x)
def func():
# OK (incremented by more than 1)
idx = 0
for x in range(5):
g(x, idx)
idx += 2
h(x)
def func():
# OK (incremented in `if`)
idx = 0
for x in range(5):
if g(x):
idx += 1
h(x)
def func():
# OK (`continue` in match-case)
idx = 0
for x in range(5):
match g(x):
case 1:
h(x)
case 2:
continue
case _:
h(idx)
idx += 1
def func():
# OK (`continue` inside `with` clause)
idx = 0
for x in range(5):
with context as c:
if g(x):
continue
h(x, idx, c)
idx += 1
def func():
# OK (incremented in `try` block)
idx = 0
for x in range(5):
try:
g(x, idx)
except:
h(x)
continue
idx += 1
def func(idx: 0):
# OK (index is an argument)
for x in range(5):
g(x, idx)
idx += 1
def func(x: int):
# OK (index _may_ be non-zero)
if x > 0:
idx = 1
else:
idx = 0
for x in range(5):
g(x, idx)
idx += 1
h(x)
def func():
# OK
idx = 0
for x in range(5):
g(x, idx)
idx += 1
idx += 1
h(x)
def func():
# OK
idx = 0
for x in range(5):
g(x, idx)
idx += 1
if idx > 10:
idx *= 2
h(x)
def func():
# OK (index used after loop)
idx = 0
for x in range(5):
g(x, idx)
idx += 1
h(x)
print(idx)
def func():
# OK (index within nested loop)
idx = 0
for x in range(5):
for y in range(5):
g(x, idx)
idx += 1
def func():
# OK (index used within inner)
idx = 0
def inner():
print(idx)
for x in range(5):
for y in range(5):
g(x, idx)
idx += 1
def func():
# OK (index used as nonlocal inner)
idx = 0
def inner():
nonlocal idx
idx = 3
for x in range(5):
for y in range(5):
g(x, idx)
idx += 1

View File

@@ -4,11 +4,6 @@ if a:
elif c:
b
if a: # we preserve comments, too!
b
elif c: # but not on the second branch
b
if x == 1:
for _ in range(20):
print("hello")
@@ -68,10 +63,6 @@ elif result.eofs == "F":
errors = 1
elif result.eofs == "E":
errors = 1
elif result.eofs == "X":
errors = 1
elif result.eofs == "C":
errors = 1
# OK
@@ -79,7 +70,7 @@ def complicated_calc(*arg, **kwargs):
return 42
def func(p):
def foo(p):
if p == 2:
return complicated_calc(microsecond=0)
elif p == 3:
@@ -112,7 +103,7 @@ elif c:
x = 1
def func():
def foo():
a = True
b = False
if a > b: # end-of-line
@@ -123,28 +114,3 @@ def func():
return 4
elif b is None:
return 4
def func():
"""Ensure that the named expression is parenthesized when merged."""
a = True
b = False
if a > b: # end-of-line
return 3
elif a := 1:
return 3
if a: # we preserve comments, too!
b
elif c: # but not on the second branch
b
if a: b # here's a comment
elif c: b
if(x > 200): pass
elif(100 < x and x < 200 and 300 < y and y < 800):
pass

View File

@@ -160,10 +160,3 @@ def secondToTime(s0: int) -> (int, int, int) or str:
def secondToTime(s0: int) -> ((int, int, int) or str):
m, s = divmod(s0, 60)
# Regression test for: https://github.com/astral-sh/ruff/issues/9479
print(f"{a}{b}" or "bar")
print(f"{a}{''}" or "bar")
print(f"{''}{''}" or "bar")
print(f"{1}{''}" or "bar")

View File

@@ -147,9 +147,3 @@ if (a and [] and False and []) == (a and []): # SIM223
if f(a and [] and False and []): # SIM223
pass
# Regression test for: https://github.com/astral-sh/ruff/issues/9479
print(f"{a}{b}" and "bar")
print(f"{a}{''}" and "bar")
print(f"{''}{''}" and "bar")
print(f"{1}{''}" and "bar")

View File

@@ -1,18 +0,0 @@
"""Test: avoid marking an `InitVar` as typing-only."""
from __future__ import annotations
from dataclasses import FrozenInstanceError, InitVar, dataclass
from pathlib import Path
@dataclass
class C:
i: int
j: int = None
database: InitVar[Path] = None
err: FrozenInstanceError = None
def __post_init__(self, database):
...

View File

@@ -31,6 +31,3 @@ import torch
torch.m.ReLU(inplace=True) # safe because this isn't a pandas call
(x.drop(["a"], axis=1, inplace=True))
# This method doesn't take exist in Pandas, so ignore it.
x.rotate_z(45, inplace=True)

View File

@@ -1,6 +1,5 @@
#: E401
import os, sys
#: Okay
import os
import sys
@@ -60,21 +59,3 @@ import foo
a = 1
import bar
#: E401
import re as regex, string # also with a comment!
import re as regex, string; x = 1
x = 1; import re as regex, string
def blah():
import datetime as dt, copy
def nested_and_tested():
import builtins, textwrap as tw
x = 1; import re as regex, string
import re as regex, string; x = 1
if True: import re as regex, string

View File

@@ -126,15 +126,3 @@ class Foo:
# Okay
if type(value) is str:
...
import numpy as np
#: Okay
x.dtype == float
#: Okay
np.dtype(int) == float
#: E721
dtype == float

View File

@@ -1,59 +0,0 @@
"""Do something.
Args:
x: the value
with a hanging indent
Returns:
the value
"""
def func():
"""Do something.
Args:
x: the value
with a hanging indent
Returns:
the value
"""
def func():
"""Do something.
Args:
x: the value
with a hanging indent
Returns:
the value
"""
def func():
"""Do something.
Args:
x: the value
with a hanging indent
Returns:
the value
"""
def func():
"""Do something.
Args:
x: the value
with a hanging indent
Returns:
the value"""

View File

@@ -1,10 +0,0 @@
"""Test: ensure that we visit type definitions and lambdas recursively."""
from __future__ import annotations
from collections import Counter
from typing import cast
def get_most_common_fn(k):
return lambda c: cast(Counter, c).most_common(k)

View File

@@ -1,11 +0,0 @@
"""Test: ensure that we visit type definitions and lambdas recursively."""
from __future__ import annotations
import re
import os
from typing import cast
cast(lambda: re.match, 1)
cast(lambda: cast(lambda: os.path, 1), 1)

View File

@@ -0,0 +1,73 @@
# OK
1<2 and 'b' and 'c'
1<2 or 'a' and 'b'
1<2 and 'a'
1<2 or 'a'
2>1
1<2 and 'a' or 'b' and 'c'
1<2 and 'a' or 'b' or 'c'
1<2 and 'a' or 'b' or 'c' or (lambda x: x+1)
1<2 and 'a' or 'b' or (lambda x: x+1) or 'c'
default = 'default'
if (not isinstance(default, bool) and isinstance(default, int)) \
or (isinstance(default, str) and default):
pass
docid, token = None, None
(docid is None and token is None) or (docid is not None and token is not None)
vendor, os_version = 'darwin', '14'
vendor == "debian" and os_version in ["12"] or vendor == "ubuntu" and os_version in []
# Don't emit if the parent is an `if` statement.
if (task_id in task_dict and task_dict[task_id] is not task) \
or task_id in used_group_ids:
pass
no_target, is_x64, target = True, False, 'target'
if (no_target and not is_x64) or target == 'ARM_APPL_RUST_TARGET':
pass
# Don't emit if the parent is a `bool_op` expression.
isinstance(val, str) and ((len(val) == 7 and val[0] == "#") or val in enums.NamedColor)
# Errors
1<2 and 'a' or 'b'
(lambda x: x+1) and 'a' or 'b'
'a' and (lambda x: x+1) or 'orange'
val = '#0000FF'
(len(val) == 7 and val[0] == "#") or val in {'green'}
marker = 'marker'
isinstance(marker, dict) and 'field' in marker or marker in {}
def has_oranges(oranges, apples=None) -> bool:
return apples and False or oranges
[x for x in l if a and b or c]
{x: y for x in l if a and b or c}
{x for x in l if a and b or c}
new_list = [
x
for sublist in all_lists
if a and b or c
for x in sublist
if (isinstance(operator, list) and x in operator) or x != operator
]

View File

@@ -87,13 +87,6 @@ class Apples:
def __special_custom_magic__(self):
pass
@classmethod
def __prepare__():
pass
def __mro_entries__(self, bases):
pass
def __foo_bar__(): # this is not checked by the [bad-dunder-name] rule
...

Some files were not shown because too many files have changed in this diff Show More