Compare commits

..

2 Commits

Author SHA1 Message Date
Charlie Marsh
9551a045ec Add BaseExceptionGroup, EncodingWarning 2023-02-23 07:31:39 -05:00
Jonathan Plasse
b6445de34d Fix ExceptionGroup F821 false positive 2023-02-23 09:58:36 +01:00
1386 changed files with 21850 additions and 42460 deletions

View File

@@ -17,6 +17,20 @@ env:
RUSTUP_MAX_RETRIES: 10
jobs:
cargo-build:
name: "cargo build"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@v1
- run: cargo build --all
- run: ./target/debug/ruff_dev generate-all
- run: git diff --quiet README.md || echo "::error file=README.md::This file is outdated. Run 'cargo dev generate-all'."
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. Run 'cargo dev generate-all'."
- run: git diff --exit-code -- README.md ruff.schema.json
cargo-fmt:
name: "cargo fmt"
runs-on: ubuntu-latest
@@ -47,7 +61,7 @@ jobs:
rustup component add clippy
rustup target add wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v1
- run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features -- -D warnings
- run: cargo clippy -p ruff --target wasm32-unknown-unknown --all-features -- -D warnings
cargo-test:
strategy:
@@ -80,26 +94,6 @@ jobs:
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
RUSTDOCFLAGS: "-D warnings"
cargo-test-wasm:
runs-on: ubuntu-latest
name: "cargo test (wasm)"
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: actions/setup-node@v3
with:
node-version: 18
cache: "npm"
cache-dependency-path: playground/package-lock.json
- uses: jetli/wasm-pack-action@v0.4.0
- uses: Swatinem/rust-cache@v1
- name: "Run wasm-pack"
run: |
cd crates/ruff_wasm
wasm-pack test --node
scripts:
name: "test scripts"
runs-on: ubuntu-latest
@@ -115,6 +109,21 @@ jobs:
./scripts/add_rule.py --name FirstRule --code TST001 --linter test
- run: cargo check
maturin-build:
name: "maturin build"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@v1
- uses: actions/setup-python@v4
with:
python-version: "3.11"
- run: pip install maturin
- run: maturin build -b bin
- run: python scripts/transform_readme.py --target pypi
typos:
name: "spell check"
runs-on: ubuntu-latest

View File

@@ -28,7 +28,7 @@ jobs:
- uses: jetli/wasm-pack-action@v0.4.0
- uses: jetli/wasm-bindgen-action@v0.2.0
- name: "Run wasm-pack"
run: wasm-pack build --target web --out-dir ../../playground/src/pkg crates/ruff_wasm
run: wasm-pack build --target web --out-dir ../../playground/src/pkg crates/ruff
- name: "Install Node dependencies"
run: npm ci
working-directory: playground

View File

@@ -5,14 +5,6 @@ repos:
hooks:
- id: validate-pyproject
- repo: https://github.com/executablebooks/mdformat
rev: 0.7.16
hooks:
- id: mdformat
additional_dependencies:
- mdformat-black
- black==23.1.0 # Must be the latest version of Black
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.33.0
hooks:
@@ -21,6 +13,7 @@ repos:
- --disable
- MD013 # line-length
- MD033 # no-inline-html
- MD041 # first-line-h1
- --
- repo: local
@@ -53,16 +46,5 @@ repos:
pass_filenames: false
exclude: target
# Black
- repo: https://github.com/psf/black
rev: 23.1.0
hooks:
- id: black
exclude: |
(?x)^(
crates/ruff/resources/.*|
crates/ruff_python_formatter/resources/.*
)$
ci:
skip: [cargo-fmt, clippy, dev-generate-all]

View File

@@ -20,7 +20,8 @@ the intention of adding a stable public API in the future.
### `select`, `extend-select`, `ignore`, and `extend-ignore` have new semantics ([#2312](https://github.com/charliermarsh/ruff/pull/2312))
Previously, the interplay between `select` and its related options could lead to unexpected
behavior. For example, `ruff --select E501 --ignore ALL` and `ruff --select E501 --extend-ignore ALL` behaved differently. (See [#2312](https://github.com/charliermarsh/ruff/pull/2312) for more
behavior. For example, `ruff --select E501 --ignore ALL` and `ruff --select E501 --extend-ignore
ALL` behaved differently. (See [#2312](https://github.com/charliermarsh/ruff/pull/2312) for more
examples.)
When Ruff determines the enabled rule set, it has to reconcile `select` and `ignore` from a variety
@@ -73,7 +74,7 @@ ruff rule E402 --format json # Works! (And preferred.)
This change is largely backwards compatible -- most users should experience
no change in behavior. However, please note the following exceptions:
- Subcommands will now fail when invoked with unsupported arguments, instead
* Subcommands will now fail when invoked with unsupported arguments, instead
of silently ignoring them. For example, the following will now fail:
```console
@@ -82,16 +83,16 @@ no change in behavior. However, please note the following exceptions:
(the `clean` command doesn't support `--respect-gitignore`.)
- The semantics of `ruff <arg>` have changed slightly when `<arg>` is a valid subcommand.
* The semantics of `ruff <arg>` have changed slightly when `<arg>` is a valid subcommand.
For example, prior to this release, running `ruff rule` would run `ruff` over a file or
directory called `rule`. Now, `ruff rule` would invoke the `rule` subcommand. This should
only impact projects with files or directories named `rule`, `check`, `explain`, `clean`,
or `generate-shell-completion`.
- Scripts that invoke ruff should supply `--` before any positional arguments.
* Scripts that invoke ruff should supply `--` before any positional arguments.
(The semantics of `ruff -- <arg>` have not changed.)
- `--explain` previously treated `--format grouped` as a synonym for `--format text`.
* `--explain` previously treated `--format grouped` as a synonym for `--format text`.
This is no longer supported; instead, use `--format text`.
## 0.0.226

View File

@@ -1,16 +1,16 @@
# Contributor Covenant Code of Conduct
- [Our Pledge](#our-pledge)
- [Our Standards](#our-standards)
- [Enforcement Responsibilities](#enforcement-responsibilities)
- [Scope](#scope)
- [Enforcement](#enforcement)
- [Enforcement Guidelines](#enforcement-guidelines)
- [1. Correction](#1-correction)
- [2. Warning](#2-warning)
- [3. Temporary Ban](#3-temporary-ban)
- [4. Permanent Ban](#4-permanent-ban)
- [Attribution](#attribution)
* [Our Pledge](#our-pledge)
* [Our Standards](#our-standards)
* [Enforcement Responsibilities](#enforcement-responsibilities)
* [Scope](#scope)
* [Enforcement](#enforcement)
* [Enforcement Guidelines](#enforcement-guidelines)
* [1. Correction](#1-correction)
* [2. Warning](#2-warning)
* [3. Temporary Ban](#3-temporary-ban)
* [4. Permanent Ban](#4-permanent-ban)
* [Attribution](#attribution)
## Our Pledge
@@ -29,23 +29,23 @@ diverse, inclusive, and healthy community.
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
@@ -132,7 +132,7 @@ version 2.0, available [here](https://www.contributor-covenant.org/version/2/0/c
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the [FAQ](https://www.contributor-covenant.org/faq).
Translations are available [here](https://www.contributor-covenant.org/translations).
[homepage]: https://www.contributor-covenant.org

View File

@@ -2,16 +2,16 @@
Welcome! We're happy to have you here. Thank you in advance for your contribution to Ruff.
- [The Basics](#the-basics)
- [Prerequisites](#prerequisites)
- [Development](#development)
- [Project Structure](#project-structure)
- [Example: Adding a new lint rule](#example-adding-a-new-lint-rule)
- [Rule naming convention](#rule-naming-convention)
- [Example: Adding a new configuration option](#example-adding-a-new-configuration-option)
- [MkDocs](#mkdocs)
- [Release Process](#release-process)
- [Benchmarks](#benchmarks)
* [The Basics](#the-basics)
* [Prerequisites](#prerequisites)
* [Development](#development)
* [Project Structure](#project-structure)
* [Example: Adding a new lint rule](#example-adding-a-new-lint-rule)
* [Rule naming convention](#rule-naming-convention)
* [Example: Adding a new configuration option](#example-adding-a-new-configuration-option)
* [MkDocs](#mkdocs)
* [Release Process](#release-process)
* [Benchmarks](#benchmarks)
## The Basics
@@ -56,9 +56,9 @@ Prior to opening a pull request, ensure that your code has been auto-formatted,
and that it passes both the lint and test validation checks:
```shell
cargo fmt # Auto-formatting...
cargo fmt --all # Auto-formatting...
cargo clippy --fix --workspace --all-targets --all-features # Linting...
cargo test # Testing...
cargo test --all # Testing...
```
These checks will run on GitHub Actions when you open your Pull Request, but running them locally
@@ -91,31 +91,31 @@ The vast majority of the code, including all lint rules, lives in the `ruff` cra
At time of writing, the repository includes the following crates:
- `crates/ruff`: library crate containing all lint rules and the core logic for running them.
- `crates/ruff_cli`: binary crate containing Ruff's command-line interface.
- `crates/ruff_dev`: binary crate containing utilities used in the development of Ruff itself (e.g., `cargo dev generate-all`).
- `crates/ruff_macros`: library crate containing macros used by Ruff.
- `crates/ruff_python`: library crate implementing Python-specific functionality (e.g., lists of standard library modules by versionb).
- `crates/flake8_to_ruff`: binary crate for generating Ruff configuration from Flake8 configuration.
* `crates/ruff`: library crate containing all lint rules and the core logic for running them.
* `crates/ruff_cli`: binary crate containing Ruff's command-line interface.
* `crates/ruff_dev`: binary crate containing utilities used in the development of Ruff itself (e.g., `cargo dev generate-all`).
* `crates/ruff_macros`: library crate containing macros used by Ruff.
* `crates/ruff_python`: library crate implementing Python-specific functionality (e.g., lists of standard library modules by versionb).
* `crates/flake8_to_ruff`: binary crate for generating Ruff configuration from Flake8 configuration.
### Example: Adding a new lint rule
At a high level, the steps involved in adding a new lint rule are as follows:
1. Determine a name for the new rule as per our [rule naming convention](#rule-naming-convention).
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
1. In that file, define a violation struct. You can grep for `#[violation]` to see examples.
1. Map the violation struct to a rule code in `crates/ruff/src/registry.rs` (e.g., `E402`).
1. Define the logic for triggering the violation in `crates/ruff/src/checkers/ast.rs` (for AST-based
2. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
3. In that file, define a violation struct. You can grep for `define_violation!` to see examples.
4. Map the violation struct to a rule code in `crates/ruff/src/registry.rs` (e.g., `E402`).
5. Define the logic for triggering the violation in `crates/ruff/src/checkers/ast.rs` (for AST-based
checks), `crates/ruff/src/checkers/tokens.rs` (for token-based checks), `crates/ruff/src/checkers/lines.rs`
(for text-based checks), or `crates/ruff/src/checkers/filesystem.rs` (for filesystem-based
checks).
1. Add a test fixture.
1. Update the generated files (documentation and generated code).
6. Add a test fixture.
7. Update the generated files (documentation and generated code).
To define the violation, start by creating a dedicated file for your rule under the appropriate
rule linter (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`). That file should
contain a struct defined via `#[violation]`, along with a function that creates the violation
contain a struct defined via `define_violation!`, along with a function that creates the violation
based on any required inputs. (Many of the existing examples live in `crates/ruff/src/violations.rs`,
but we're looking to place new rules in their own files.)
@@ -135,7 +135,7 @@ Run `cargo dev generate-all` to generate the code for your new fixture. Then run
locally with (e.g.) `cargo run -p ruff_cli -- check crates/ruff/resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402`.
Once you're satisfied with the output, codify the behavior as a snapshot test by adding a new
`test_case` macro in the relevant `crates/ruff/src/[linter]/mod.rs` file. Then, run `cargo test`.
`test_case` macro in the relevant `crates/ruff/src/[linter]/mod.rs` file. Then, run `cargo test --all`.
Your test will fail, but you'll be prompted to follow-up with `cargo insta review`. Accept the
generated snapshot, then commit the snapshot file alongside the rest of your changes.
@@ -147,9 +147,9 @@ The rule name should make sense when read as "allow _rule-name_" or "allow _rule
This implies that rule names:
- should state the bad thing being checked for
* should state the bad thing being checked for
- should not contain instructions on what you what you should use instead
* should not contain instructions on what you what you should use instead
(these belong in the rule documentation and the `autofix_title` for rules that have autofix)
When re-implementing rules from other linters, this convention is given more importance than
@@ -191,13 +191,13 @@ To preview any changes to the documentation locally:
pip install -r docs/requirements.txt
```
1. Generate the MkDocs site with:
2. Generate the MkDocs site with:
```shell
python scripts/generate_mkdocs.py
```
1. Run the development server with:
3. Run the development server with:
```shell
mkdocs serve

428
Cargo.lock generated
View File

@@ -2,12 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "Inflector"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
[[package]]
name = "adler"
version = "1.0.2"
@@ -92,7 +86,7 @@ version = "2.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9834fcc22e0874394a010230586367d4a3e9f11b560f469262678547e1d2575e"
dependencies = [
"bstr 1.3.0",
"bstr 1.2.0",
"doc-comment",
"predicates",
"predicates-core",
@@ -181,9 +175,9 @@ dependencies = [
[[package]]
name = "bstr"
version = "1.3.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1"
checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832"
dependencies = [
"memchr",
"once_cell",
@@ -289,9 +283,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.1.8"
version = "4.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3"
dependencies = [
"bitflags",
"clap_derive",
@@ -304,11 +298,11 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.1.2"
version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd125be87bf4c255ebc50de0b7f4d2a6201e8ac3dc86e39c0ad081dc5e7236fe"
checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75"
dependencies = [
"clap 4.1.8",
"clap 4.1.6",
]
[[package]]
@@ -317,26 +311,26 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4160b4a4f72ef58bd766bad27c09e6ef1cc9d82a22f6a0f55d152985a4a48e31"
dependencies = [
"clap 4.1.8",
"clap 4.1.6",
"clap_complete",
"clap_complete_fig",
]
[[package]]
name = "clap_complete_fig"
version = "4.1.1"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63a06158a72dbb088f864887b4409fd22600ba379f3cee3040f842234cc5c2d0"
checksum = "cf0c76d8fcf782a1102ccfcd10ca8246e7fdd609c1cd6deddbb96cb638e9bb5c"
dependencies = [
"clap 4.1.8",
"clap 4.1.6",
"clap_complete",
]
[[package]]
name = "clap_derive"
version = "4.1.8"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0"
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
dependencies = [
"heck",
"proc-macro-error",
@@ -427,9 +421,9 @@ dependencies = [
[[package]]
name = "console_log"
version = "0.2.1"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "878ad067d4089144a36ee412d665916c665430eb84c0057b9987f424a5d15c03"
checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494"
dependencies = [
"log",
"web-sys",
@@ -554,21 +548,11 @@ dependencies = [
"typenum",
]
[[package]]
name = "ctor"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "cxx"
version = "1.0.91"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62"
checksum = "90d59d9acd2a682b4e40605a242f6670eaa58c5957471cbf85e8aa6a0b97a5e8"
dependencies = [
"cc",
"cxxbridge-flags",
@@ -578,9 +562,9 @@ dependencies = [
[[package]]
name = "cxx-build"
version = "1.0.91"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690"
checksum = "ebfa40bda659dd5c864e65f4c9a2b0aff19bea56b017b9b77c73d3766a453a38"
dependencies = [
"cc",
"codespan-reporting",
@@ -593,15 +577,15 @@ dependencies = [
[[package]]
name = "cxxbridge-flags"
version = "1.0.91"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf"
checksum = "457ce6757c5c70dc6ecdbda6925b958aae7f959bda7d8fb9bde889e34a09dc03"
[[package]]
name = "cxxbridge-macro"
version = "1.0.91"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
checksum = "ebf883b7aacd7b2aeb2a7b338648ee19f57c140d4ee8e52c68979c6b2f7f2263"
dependencies = [
"proc-macro2",
"quote",
@@ -769,10 +753,10 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.254"
version = "0.0.252"
dependencies = [
"anyhow",
"clap 4.1.8",
"clap 4.1.6",
"colored",
"configparser",
"once_cell",
@@ -856,7 +840,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc"
dependencies = [
"aho-corasick",
"bstr 1.3.0",
"bstr 1.2.0",
"fnv",
"log",
"regex",
@@ -1006,9 +990,9 @@ dependencies = [
[[package]]
name = "insta"
version = "1.28.0"
version = "1.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea5b3894afe466b4bcf0388630fc15e11938a6074af0cd637c825ba2ec8a099"
checksum = "f6f0f08b46e4379744de2ab67aa8f7de3ffd1da3e275adc41fcc82053ede46ff"
dependencies = [
"console",
"lazy_static",
@@ -1039,24 +1023,11 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "is-macro"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7d079e129b77477a49c5c4f1cfe9ce6c2c909ef52520693e8e811a714c7b20"
dependencies = [
"Inflector",
"pmutil",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "is-terminal"
version = "0.4.4"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857"
checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef"
dependencies = [
"hermit-abi 0.3.1",
"io-lifetimes",
@@ -1079,6 +1050,12 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "joinery"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5"
[[package]]
name = "js-sys"
version = "0.3.61"
@@ -1185,7 +1162,7 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "libcst"
version = "0.1.0"
source = "git+https://github.com/charliermarsh/LibCST?rev=80e4c1399f95e5beb532fdd1e209ad2dbb470438#80e4c1399f95e5beb532fdd1e209ad2dbb470438"
source = "git+https://github.com/charliermarsh/LibCST?rev=f2f0b7a487a8725d161fe8b3ed73a6758b21e177#f2f0b7a487a8725d161fe8b3ed73a6758b21e177"
dependencies = [
"chic",
"itertools",
@@ -1200,7 +1177,7 @@ dependencies = [
[[package]]
name = "libcst_derive"
version = "0.1.0"
source = "git+https://github.com/charliermarsh/LibCST?rev=80e4c1399f95e5beb532fdd1e209ad2dbb470438#80e4c1399f95e5beb532fdd1e209ad2dbb470438"
source = "git+https://github.com/charliermarsh/LibCST?rev=f2f0b7a487a8725d161fe8b3ed73a6758b21e177#f2f0b7a487a8725d161fe8b3ed73a6758b21e177"
dependencies = [
"quote",
"syn",
@@ -1306,14 +1283,14 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.6"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.45.0",
"windows-sys 0.42.0",
]
[[package]]
@@ -1362,6 +1339,15 @@ dependencies = [
"version_check",
]
[[package]]
name = "nom8"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
dependencies = [
"memchr",
]
[[package]]
name = "notify"
version = "5.1.0"
@@ -1389,6 +1375,7 @@ dependencies = [
"autocfg",
"num-integer",
"num-traits",
"serde",
]
[[package]]
@@ -1398,6 +1385,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
dependencies = [
"num-traits",
"serde",
]
[[package]]
@@ -1430,10 +1418,31 @@ dependencies = [
]
[[package]]
name = "once_cell"
version = "1.17.1"
name = "num_enum"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "once_cell"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
[[package]]
name = "oorandom"
@@ -1447,15 +1456,6 @@ version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "output_vt100"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
dependencies = [
"winapi",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
@@ -1677,17 +1677,6 @@ dependencies = [
"plotters-backend",
]
[[package]]
name = "pmutil"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@@ -1728,15 +1717,13 @@ dependencies = [
]
[[package]]
name = "pretty_assertions"
name = "proc-macro-crate"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34"
dependencies = [
"ctor",
"diff",
"output_vt100",
"yansi",
"once_cell",
"toml_edit",
]
[[package]]
@@ -1842,9 +1829,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.7.0"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
dependencies = [
"either",
"rayon-core",
@@ -1852,9 +1839,9 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.11.0"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
@@ -1914,28 +1901,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "result-like"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccc7ce6435c33898517a30e85578cd204cbb696875efb93dec19a2d31294f810"
dependencies = [
"result-like-derive",
]
[[package]]
name = "result-like-derive"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fabf0a2e54f711c68c50d49f648a1a8a37adcb57353f518ac4df374f0788f42"
dependencies = [
"pmutil",
"proc-macro2",
"quote",
"syn",
"syn-ext",
]
[[package]]
name = "ring"
version = "0.16.20"
@@ -1953,24 +1918,28 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.254"
version = "0.0.252"
dependencies = [
"anyhow",
"bisection",
"bitflags",
"cfg-if",
"chrono",
"clap 4.1.8",
"clap 4.1.6",
"colored",
"console_error_panic_hook",
"console_log",
"criterion",
"dirs",
"fern",
"getrandom",
"glob",
"globset",
"ignore",
"imperative",
"insta",
"is-macro",
"itertools",
"js-sys",
"libcst",
"log",
"natord",
@@ -1980,19 +1949,15 @@ dependencies = [
"once_cell",
"path-absolutize",
"regex",
"result-like",
"ruff_cache",
"ruff_diagnostics",
"ruff_macros",
"ruff_python_ast",
"ruff_python_stdlib",
"ruff_rustpython",
"ruff_python",
"rustc-hash",
"rustpython-common",
"rustpython-parser",
"schemars",
"semver",
"serde",
"serde-wasm-bindgen",
"shellexpand",
"smallvec",
"strum",
@@ -2000,23 +1965,15 @@ dependencies = [
"test-case",
"textwrap",
"thiserror",
"titlecase",
"toml",
]
[[package]]
name = "ruff_cache"
version = "0.0.0"
dependencies = [
"filetime",
"globset",
"itertools",
"regex",
"ruff_macros",
"wasm-bindgen",
"wasm-bindgen-test",
]
[[package]]
name = "ruff_cli"
version = "0.0.254"
version = "0.0.252"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -2026,7 +1983,7 @@ dependencies = [
"bitflags",
"cachedir",
"chrono",
"clap 4.1.8",
"clap 4.1.6",
"clap_complete_command",
"clearscreen",
"colored",
@@ -2042,8 +1999,6 @@ dependencies = [
"rayon",
"regex",
"ruff",
"ruff_cache",
"ruff_diagnostics",
"rustc-hash",
"serde",
"serde_json",
@@ -2061,15 +2016,13 @@ name = "ruff_dev"
version = "0.0.0"
dependencies = [
"anyhow",
"clap 4.1.8",
"clap 4.1.6",
"itertools",
"libcst",
"once_cell",
"pretty_assertions",
"regex",
"ruff",
"ruff_cli",
"ruff_diagnostics",
"rustpython-common",
"rustpython-parser",
"schemars",
@@ -2079,15 +2032,6 @@ dependencies = [
"textwrap",
]
[[package]]
name = "ruff_diagnostics"
version = "0.0.0"
dependencies = [
"ruff_python_ast",
"rustpython-parser",
"serde",
]
[[package]]
name = "ruff_formatter"
version = "0.0.0"
@@ -2114,25 +2058,12 @@ dependencies = [
]
[[package]]
name = "ruff_python_ast"
name = "ruff_python"
version = "0.0.0"
dependencies = [
"anyhow",
"bitflags",
"is-macro",
"itertools",
"log",
"nohash-hasher",
"num-bigint",
"num-traits",
"once_cell",
"regex",
"ruff_python_stdlib",
"ruff_rustpython",
"rustc-hash",
"rustpython-common",
"rustpython-parser",
"smallvec",
]
[[package]]
@@ -2140,15 +2071,10 @@ name = "ruff_python_formatter"
version = "0.0.0"
dependencies = [
"anyhow",
"clap 4.1.8",
"clap 4.1.6",
"insta",
"is-macro",
"itertools",
"once_cell",
"ruff_formatter",
"ruff_python_ast",
"ruff_python_stdlib",
"ruff_rustpython",
"ruff_testing_macros",
"ruff_text_size",
"rustc-hash",
@@ -2158,30 +2084,12 @@ dependencies = [
"test-case",
]
[[package]]
name = "ruff_python_stdlib"
version = "0.0.0"
dependencies = [
"once_cell",
"regex",
"rustc-hash",
]
[[package]]
name = "ruff_rustpython"
version = "0.0.0"
dependencies = [
"anyhow",
"once_cell",
"rustpython-common",
"rustpython-parser",
]
[[package]]
name = "ruff_testing_macros"
version = "0.0.0"
dependencies = [
"glob",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
@@ -2197,25 +2105,6 @@ dependencies = [
"static_assertions",
]
[[package]]
name = "ruff_wasm"
version = "0.0.0"
dependencies = [
"console_error_panic_hook",
"console_log",
"getrandom",
"js-sys",
"log",
"ruff",
"ruff_python_ast",
"ruff_rustpython",
"rustpython-parser",
"serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
]
[[package]]
name = "rust-stemmers"
version = "1.2.0"
@@ -2261,7 +2150,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
source = "git+https://github.com/RustPython/RustPython.git?rev=edf5995a1e4c366976304ca05432dd27c913054e#edf5995a1e4c366976304ca05432dd27c913054e"
dependencies = [
"num-bigint",
"rustpython-compiler-core",
@@ -2270,7 +2159,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
source = "git+https://github.com/RustPython/RustPython.git?rev=edf5995a1e4c366976304ca05432dd27c913054e#edf5995a1e4c366976304ca05432dd27c913054e"
dependencies = [
"ascii",
"bitflags",
@@ -2295,21 +2184,24 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
source = "git+https://github.com/RustPython/RustPython.git?rev=edf5995a1e4c366976304ca05432dd27c913054e#edf5995a1e4c366976304ca05432dd27c913054e"
dependencies = [
"bincode",
"bitflags",
"bstr 0.2.17",
"itertools",
"lz4_flex",
"num-bigint",
"num-complex",
"num_enum",
"serde",
"thiserror",
]
[[package]]
name = "rustpython-parser"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
source = "git+https://github.com/RustPython/RustPython.git?rev=edf5995a1e4c366976304ca05432dd27c913054e#edf5995a1e4c366976304ca05432dd27c913054e"
dependencies = [
"ahash",
"anyhow",
@@ -2324,7 +2216,7 @@ dependencies = [
"rustc-hash",
"rustpython-ast",
"rustpython-compiler-core",
"serde",
"thiserror",
"tiny-keccak",
"unic-emoji-char",
"unic-ucd-ident",
@@ -2354,9 +2246,9 @@ dependencies = [
[[package]]
name = "schemars"
version = "0.8.12"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f"
checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307"
dependencies = [
"dyn-clone",
"schemars_derive",
@@ -2366,9 +2258,9 @@ dependencies = [
[[package]]
name = "schemars_derive"
version = "0.8.12"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c"
checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9"
dependencies = [
"proc-macro2",
"quote",
@@ -2421,9 +2313,9 @@ dependencies = [
[[package]]
name = "serde-wasm-bindgen"
version = "0.5.0"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e"
checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
dependencies = [
"js-sys",
"serde",
@@ -2580,24 +2472,15 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.109"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn-ext"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b86cb2b68c5b3c078cac02588bc23f3c04bb828c5d3aedd17980876ec6a7be6"
dependencies = [
"syn",
]
[[package]]
name = "tempfile"
version = "3.3.0"
@@ -2653,18 +2536,18 @@ checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8"
[[package]]
name = "test-case"
version = "3.0.0"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "679b019fb241da62cc449b33b224d19ebe1c6767b495569765115dd7f7f9fba4"
checksum = "21d6cf5a7dffb3f9dceec8e6b8ca528d9bd71d36c9f074defb548ce161f598c0"
dependencies = [
"test-case-macros",
]
[[package]]
name = "test-case-core"
version = "3.0.0"
name = "test-case-macros"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72dc21b5887f4032c4656502d085dc28f2afbb686f25f216472bb0526f4b1b88"
checksum = "e45b7bf6e19353ddd832745c8fcf77a17a93171df7151187f26623f2b75b5b26"
dependencies = [
"cfg-if",
"proc-macro-error",
@@ -2673,19 +2556,6 @@ dependencies = [
"syn",
]
[[package]]
name = "test-case-macros"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3786898e0be151a96f730fd529b0e8a10f5990fa2a7ea14e37ca27613c05190"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"test-case-core",
]
[[package]]
name = "textwrap"
version = "0.16.0"
@@ -2793,10 +2663,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.7.2"
name = "titlecase"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
checksum = "38397a8cdb017cfeb48bf6c154d6de975ac69ffeed35980fde199d2ee0842042"
dependencies = [
"joinery",
"lazy_static",
"regex",
]
[[package]]
name = "toml"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217"
dependencies = [
"serde",
"serde_spanned",
@@ -2806,24 +2687,24 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.6.1"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.4"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825"
checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b"
dependencies = [
"indexmap",
"nom8",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
@@ -2977,11 +2858,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "unicode_names2"
version = "0.6.0"
source = "git+https://github.com/youknowone/unicode_names2.git?rev=4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde#4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde"
dependencies = [
"phf",
]
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029df4cc8238cefc911704ff8fa210853a0f3bce2694d8f51181dd41ee0f3301"
[[package]]
name = "untrusted"
@@ -3314,15 +3193,6 @@ version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
[[package]]
name = "winnow"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf09497b8f8b5ac5d3bb4d05c0a99be20f26fd3d5f2db7b0716e946d5103658"
dependencies = [
"memchr",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"
@@ -3332,12 +3202,6 @@ dependencies = [
"linked-hash-map",
]
[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "yansi-term"
version = "0.1.2"

View File

@@ -3,45 +3,24 @@ members = ["crates/*"]
[workspace.package]
edition = "2021"
rust-version = "1.67"
rust-version = "1.67.0"
[workspace.dependencies]
anyhow = { version = "1.0.69" }
bitflags = { version = "1.3.2" }
chrono = { version = "0.4.23", default-features = false, features = ["clock"] }
clap = { version = "4.1.8", features = ["derive"] }
colored = { version = "2.0.0" }
filetime = { version = "0.2.20" }
glob = { version = "0.3.1" }
globset = { version = "0.4.10" }
ignore = { version = "0.4.20" }
insta = { version = "1.28.0" }
is-macro = { version = "0.2.2" }
anyhow = { version = "1.0.66" }
clap = { version = "4.0.1", features = ["derive"] }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "80e4c1399f95e5beb532fdd1e209ad2dbb470438" }
log = { version = "0.4.17" }
once_cell = { version = "1.17.1" }
path-absolutize = { version = "3.0.14" }
proc-macro2 = { version = "1.0.51" }
quote = { version = "1.0.23" }
regex = { version = "1.7.1" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
once_cell = { version = "1.16.0" }
regex = { version = "1.6.0" }
rustc-hash = { version = "1.1.0" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "1871a1632e310985414211222f5bf8069678892f" }
rustpython-parser = { features = [
"lalrpop",
"serde",
], git = "https://github.com/RustPython/RustPython.git", rev = "1871a1632e310985414211222f5bf8069678892f" }
schemars = { version = "0.8.12" }
serde = { version = "1.0.152", features = ["derive"] }
serde_json = { version = "1.0.93" }
shellexpand = { version = "3.0.0" }
similar = { version = "2.2.1" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "edf5995a1e4c366976304ca05432dd27c913054e" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "edf5995a1e4c366976304ca05432dd27c913054e" }
schemars = { version = "0.8.11" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }
syn = { version = "1.0.109" }
test-case = { version = "3.0.0" }
textwrap = { version = "0.16.0" }
toml = { version = "0.7.2" }
toml = { version = "0.6.0" }
[profile.release]
panic = "abort"

130
README.md
View File

@@ -24,17 +24,17 @@ An extremely fast Python linter, written in Rust.
<i>Linting the CPython codebase from scratch.</i>
</p>
- ⚡️ 10-100x faster than existing linters
- 🐍 Installable via `pip`
- 🛠️ `pyproject.toml` support
- 🤝 Python 3.11 compatibility
- 📦 Built-in caching, to avoid re-analyzing unchanged files
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
- 📏 Over [500 built-in rules](https://beta.ruff.rs/docs/rules/)
- ⚖️ [Near-parity](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) with the built-in Flake8 rule set
- 🔌 Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
- ⌨️ First-party editor integrations for [VS Code](https://github.com/charliermarsh/ruff-vscode) and [more](https://github.com/charliermarsh/ruff-lsp)
- 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](https://beta.ruff.rs/docs/configuration/#pyprojecttoml-discovery)
* ⚡️ 10-100x faster than existing linters
* 🐍 Installable via `pip`
* 🛠️ `pyproject.toml` support
* 🤝 Python 3.11 compatibility
* 📦 Built-in caching, to avoid re-analyzing unchanged files
* 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
* 📏 Over [400 built-in rules](https://beta.ruff.rs/docs/rules/) (and growing)
* ⚖️ [Near-parity](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) with the built-in Flake8 rule set
* 🔌 Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
* ⌨️ First-party editor integrations for [VS Code](https://github.com/charliermarsh/ruff-vscode) and [more](https://github.com/charliermarsh/ruff-lsp)
* 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](#pyprojecttoml-discovery)
Ruff aims to be orders of magnitude faster than alternative tools while integrating more
functionality behind a single, common interface.
@@ -47,11 +47,11 @@ all while executing tens or hundreds of times faster than any individual tool.
Ruff is extremely actively developed and used in major open-source projects like:
- [pandas](https://github.com/pandas-dev/pandas)
- [FastAPI](https://github.com/tiangolo/fastapi)
- [Transformers (Hugging Face)](https://github.com/huggingface/transformers)
- [Apache Airflow](https://github.com/apache/airflow)
- [SciPy](https://github.com/scipy/scipy)
* [pandas](https://github.com/pandas-dev/pandas)
* [FastAPI](https://github.com/tiangolo/fastapi)
* [Transformers (Hugging Face)](https://github.com/huggingface/transformers)
* [Apache Airflow](https://github.com/apache/airflow)
* [SciPy](https://github.com/scipy/scipy)
...and many more.
@@ -98,13 +98,13 @@ developer of [Zulip](https://github.com/zulip/zulip):
For more, see the [documentation](https://beta.ruff.rs/docs/).
1. [Getting Started](#getting-started)
1. [Configuration](#configuration)
1. [Rules](#rules)
1. [Contributing](#contributing)
1. [Support](#support)
1. [Acknowledgements](#acknowledgements)
1. [Who's Using Ruff?](#whos-using-ruff)
1. [License](#license)
2. [Configuration](#configuration)
3. [Rules](#rules)
4. [Contributing](#contributing)
5. [Support](#support)
6. [Acknowledgements](#acknowledgements)
7. [Who's Using Ruff?](#whos-using-ruff)
8. [License](#license)
## Getting Started
@@ -137,7 +137,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
```yaml
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.0.254'
rev: 'v0.0.252'
hooks:
- id: ruff
```
@@ -147,9 +147,9 @@ alongside any other editor through the [Ruff LSP](https://github.com/charliermar
### Configuration
Ruff can be configured through a `pyproject.toml`, `ruff.toml`, or `.ruff.toml` file (see:
[_Configuration_](https://beta.ruff.rs/docs/configuration/), or [_Settings_](https://beta.ruff.rs/docs/settings/)
for a complete list of all configuration options).
Ruff can be configured via a `pyproject.toml` file, a `ruff.toml` file, or through the command line
(see: [_Configuration_](https://beta.ruff.rs/docs/configuration/), or
[_Settings_](https://beta.ruff.rs/docs/settings/) for a complete list of all configuration options).
If left unspecified, the default configuration is equivalent to:
@@ -186,6 +186,7 @@ exclude = [
"node_modules",
"venv",
]
per-file-ignores = {}
# Same as Black.
line-length = 88
@@ -215,7 +216,7 @@ linting command.
<!-- Begin section: Rules -->
**Ruff supports over 500 lint rules**, many of which are inspired by popular tools like Flake8,
**Ruff supports over 400 lint rules**, many of which are inspired by popular tools like Flake8,
isort, pyupgrade, and others. Regardless of the rule's origin, Ruff re-implements every rule in
Rust as a first-party feature.
@@ -224,9 +225,6 @@ and a [subset](https://beta.ruff.rs/docs/rules/#error-e) of the `E` category, om
stylistic rules made obsolete by the use of an autoformatter, like
[Black](https://github.com/psf/black).
If you're just getting started with Ruff, **the default rule set is a great place to start**: it
catches a wide variety of common errors (like unused imports) with zero configuration.
<!-- End section: Rules -->
For a complete enumeration of the supported rules, see [_Rules_](https://beta.ruff.rs/docs/rules/).
@@ -271,41 +269,41 @@ Ruff is released under the MIT license.
Ruff is used in a number of major open-source projects, including:
- [pandas](https://github.com/pandas-dev/pandas)
- [FastAPI](https://github.com/tiangolo/fastapi)
- [Transformers (Hugging Face)](https://github.com/huggingface/transformers)
- [Diffusers (Hugging Face)](https://github.com/huggingface/diffusers)
- [Apache Airflow](https://github.com/apache/airflow)
- [SciPy](https://github.com/scipy/scipy)
- [Zulip](https://github.com/zulip/zulip)
- [Bokeh](https://github.com/bokeh/bokeh)
- [Pydantic](https://github.com/pydantic/pydantic)
- [Dagster](https://github.com/dagster-io/dagster)
- [Dagger](https://github.com/dagger/dagger)
- [Sphinx](https://github.com/sphinx-doc/sphinx)
- [Hatch](https://github.com/pypa/hatch)
- [PDM](https://github.com/pdm-project/pdm)
- [Jupyter](https://github.com/jupyter-server/jupyter_server)
- [Great Expectations](https://github.com/great-expectations/great_expectations)
- [ONNX](https://github.com/onnx/onnx)
- [Polars](https://github.com/pola-rs/polars)
- [Ibis](https://github.com/ibis-project/ibis)
- [Synapse (Matrix)](https://github.com/matrix-org/synapse)
- [SnowCLI (Snowflake)](https://github.com/Snowflake-Labs/snowcli)
- [Dispatch (Netflix)](https://github.com/Netflix/dispatch)
- [Saleor](https://github.com/saleor/saleor)
- [Pynecone](https://github.com/pynecone-io/pynecone)
- [OpenBB](https://github.com/OpenBB-finance/OpenBBTerminal)
- [Home Assistant](https://github.com/home-assistant/core)
- [Pylint](https://github.com/PyCQA/pylint)
- [Cryptography (PyCA)](https://github.com/pyca/cryptography)
- [cibuildwheel (PyPA)](https://github.com/pypa/cibuildwheel)
- [build (PyPA)](https://github.com/pypa/build)
- [Babel](https://github.com/python-babel/babel)
- [featuretools](https://github.com/alteryx/featuretools)
- [meson-python](https://github.com/mesonbuild/meson-python)
- [ZenML](https://github.com/zenml-io/zenml)
- [delta-rs](https://github.com/delta-io/delta-rs)
* [pandas](https://github.com/pandas-dev/pandas)
* [FastAPI](https://github.com/tiangolo/fastapi)
* [Transformers (Hugging Face)](https://github.com/huggingface/transformers)
* [Diffusers (Hugging Face)](https://github.com/huggingface/diffusers)
* [Apache Airflow](https://github.com/apache/airflow)
* [SciPy](https://github.com/scipy/scipy)
* [Zulip](https://github.com/zulip/zulip)
* [Bokeh](https://github.com/bokeh/bokeh)
* [Pydantic](https://github.com/pydantic/pydantic)
* [Dagster](https://github.com/dagster-io/dagster)
* [Dagger](https://github.com/dagger/dagger)
* [Sphinx](https://github.com/sphinx-doc/sphinx)
* [Hatch](https://github.com/pypa/hatch)
* [PDM](https://github.com/pdm-project/pdm)
* [Jupyter](https://github.com/jupyter-server/jupyter_server)
* [Great Expectations](https://github.com/great-expectations/great_expectations)
* [ONNX](https://github.com/onnx/onnx)
* [Polars](https://github.com/pola-rs/polars)
* [Ibis](https://github.com/ibis-project/ibis)
* [Synapse (Matrix)](https://github.com/matrix-org/synapse)
* [SnowCLI (Snowflake)](https://github.com/Snowflake-Labs/snowcli)
* [Dispatch (Netflix)](https://github.com/Netflix/dispatch)
* [Saleor](https://github.com/saleor/saleor)
* [Pynecone](https://github.com/pynecone-io/pynecone)
* [OpenBB](https://github.com/OpenBB-finance/OpenBBTerminal)
* [Home Assistant](https://github.com/home-assistant/core)
* [Pylint](https://github.com/PyCQA/pylint)
* [Cryptography (PyCA)](https://github.com/pyca/cryptography)
* [cibuildwheel (PyPA)](https://github.com/pypa/cibuildwheel)
* [build (PyPA)](https://github.com/pypa/build)
* [Babel](https://github.com/python-babel/babel)
* [featuretools](https://github.com/alteryx/featuretools)
* [meson-python](https://github.com/mesonbuild/meson-python)
* [ZenML](https://github.com/zenml-io/zenml)
* [delta-rs](https://github.com/delta-io/delta-rs)
## License

View File

@@ -1,18 +1,17 @@
[package]
name = "flake8-to-ruff"
version = "0.0.254"
version = "0.0.252"
edition = { workspace = true }
rust-version = { workspace = true }
[dependencies]
ruff = { path = "../ruff", default-features = false }
anyhow = { workspace = true }
clap = { workspace = true }
colored = { workspace = true }
colored = { version = "2.0.0" }
configparser = { version = "3.0.2" }
once_cell = { workspace = true }
regex = { workspace = true }
ruff = { path = "../ruff", default-features = false }
rustc-hash = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }

View File

@@ -84,7 +84,7 @@ flake8-to-ruff path/to/.flake8 --plugin flake8-builtins --plugin flake8-quotes
1. Ruff only supports a subset of the Flake configuration options. `flake8-to-ruff` will warn on and
ignore unsupported options in the `.flake8` file (or equivalent). (Similarly, Ruff has a few
configuration options that don't exist in Flake8.)
1. Ruff will omit any rule codes that are unimplemented or unsupported by Ruff, including rule
2. Ruff will omit any rule codes that are unimplemented or unsupported by Ruff, including rule
codes from unsupported plugins. (See the [Ruff README](https://github.com/charliermarsh/ruff#user-content-how-does-ruff-compare-to-flake8)
for the complete list of supported plugins.)

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.0.254"
version = "0.0.252"
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
edition = { workspace = true }
rust-version = { workspace = true }
@@ -16,59 +16,62 @@ crate-type = ["cdylib", "rlib"]
doctest = false
[dependencies]
ruff_cache = { path = "../ruff_cache" }
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
ruff_macros = { path = "../ruff_macros" }
ruff_python_ast = { path = "../ruff_python_ast" }
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
ruff_rustpython = { path = "../ruff_rustpython" }
anyhow = { workspace = true }
bisection = { version = "0.1.0" }
bitflags = { workspace = true }
chrono = { workspace = true }
bitflags = { version = "1.3.2" }
cfg-if = { version = "1.0.0" }
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
clap = { workspace = true, features = ["derive", "env", "string"] }
colored = { workspace = true }
colored = { version = "2.0.0" }
dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
glob = { workspace = true }
globset = { workspace = true }
ignore = { workspace = true }
imperative = { version = "1.0.4" }
is-macro = { workspace = true }
glob = { version = "0.3.0" }
globset = { version = "0.4.9" }
ignore = { version = "0.4.18" }
imperative = { version = "1.0.3" }
itertools = { workspace = true }
libcst = { workspace = true }
log = { workspace = true }
log = { version = "0.4.17" }
natord = { version = "1.0.9" }
nohash-hasher = { version = "0.2.0" }
num-bigint = { version = "0.4.3" }
num-traits = { version = "0.2.15" }
num-traits = "0.2.15"
once_cell = { workspace = true }
path-absolutize = { workspace = true, features = [
"once_cell_cache",
"use_unix_paths_on_wasm",
] }
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
regex = { workspace = true }
result-like = { version = "0.4.6" }
ruff_macros = { path = "../ruff_macros" }
ruff_python = { path = "../ruff_python" }
rustc-hash = { workspace = true }
rustpython-common = { workspace = true }
rustpython-parser = { workspace = true }
schemars = { workspace = true }
semver = { version = "1.0.16" }
serde = { workspace = true }
shellexpand = { workspace = true }
shellexpand = { version = "3.0.0" }
smallvec = { version = "1.10.0" }
strum = { workspace = true }
strum_macros = { workspace = true }
textwrap = { workspace = true }
thiserror = { version = "1.0.38" }
textwrap = { version = "0.16.0" }
thiserror = { version = "1.0" }
titlecase = { version = "2.2.1" }
toml = { workspace = true }
[dev-dependencies]
insta = { workspace = true, features = ["yaml", "redactions"] }
test-case = { workspace = true }
criterion = { version = "0.4.0" }
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
getrandom = { version = "0.2.7", features = ["js"] }
console_error_panic_hook = { version = "0.1.7" }
console_log = { version = "0.2.0" }
serde-wasm-bindgen = { version = "0.4" }
js-sys = { version = "0.3.60" }
wasm-bindgen = { version = "0.2.83" }
[dev-dependencies]
insta = { version = "1.19.0", features = ["yaml", "redactions"] }
test-case = { version = "2.2.2" }
wasm-bindgen-test = { version = "0.3.33" }
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
criterion = { version = "0.4.0" }
[features]
default = []

View File

@@ -107,7 +107,3 @@ class Foo:
# ANN101
def foo(self, /, a: int, b: int) -> int:
pass
# OK
def f(*args: *tuple[int]) -> None: ...

View File

@@ -19,8 +19,6 @@ token = "s3cr3t"
secrete = "s3cr3t"
safe = password = "s3cr3t"
password = safe = "s3cr3t"
PASSWORD = "s3cr3t"
PassWord = "s3cr3t"
d["password"] = "s3cr3t"
d["pass"] = "s3cr3t"
@@ -63,15 +61,3 @@ if token == "3\t4":
if token == "5\r6":
pass
# These should not be flagged
passed_msg = "You have passed!"
compassion = "Please don't match!"
impassable = "You shall not pass!"
passwords = ""
PASSWORDS = ""
passphrases = ""
PassPhrases = ""
tokens = ""
secrets = ""

View File

@@ -59,10 +59,6 @@ getattr(someobj, attrname, False)
mylist.index(True)
int(True)
str(int(False))
cfg.get("hello", True)
cfg.getint("hello", True)
cfg.getfloat("hello", True)
cfg.getboolean("hello", True)
class Registry:

View File

@@ -1,47 +0,0 @@
"""
Should emit:
B030:
- line 12, column 8
- line 17, column 9
- line 22, column 21
- line 27, column 37
"""
try:
pass
except 1: # error
pass
try:
pass
except (1, ValueError): # error
pass
try:
pass
except (ValueError, (RuntimeError, (KeyError, TypeError))): # error
pass
try:
pass
except (ValueError, *(RuntimeError, (KeyError, TypeError))): # error
pass
try:
pass
except (ValueError, *(RuntimeError, TypeError)): # ok
pass
try:
pass
except (ValueError, *[RuntimeError, *(TypeError,)]): # ok
pass
def what_to_catch():
return ...
try:
pass
except what_to_catch(): # ok
pass

View File

@@ -626,8 +626,3 @@ result = function(
bar,
**{'ham': spam}
)
# Make sure the COM812 and UP034 rules don't autofix simultaneously and cause a syntax error.
the_first_one = next(
(i for i in range(10) if i // 2 == 0) # COM812 fix should include the final bracket
)

View File

@@ -1,21 +0,0 @@
from django.shortcuts import render
def test_view1(request):
return render(request, "index.html", locals())
def test_view2(request):
return render(request, "index.html", context=locals())
def test_view3(request):
return render(request, "index.html")
def test_view4(request):
return render(request, "index.html", {})
def test_view5(request):
return render(request, "index.html", context={})

View File

@@ -1,11 +0,0 @@
from django import forms
class TestModelForm1(forms.ModelForm):
class Meta:
exclude = ["bar"]
class TestModelForm2(forms.ModelForm):
class Meta:
fields = ["foo"]

View File

@@ -1,16 +0,0 @@
from django import forms
class TestModelForm1(forms.ModelForm):
class Meta:
fields = "__all__"
class TestModelForm2(forms.ModelForm):
class Meta:
fields = b"__all__"
class TestModelForm3(forms.ModelForm):
class Meta:
fields = ["foo"]

View File

@@ -8,9 +8,3 @@ any({x.id for x in bar})
# PIE 802
any([x.id for x in bar])
all([x.id for x in bar])
any( # first comment
[x.id for x in bar], # second comment
) # third comment
all( # first comment
[x.id for x in bar], # second comment
) # third comment

View File

@@ -1,14 +0,0 @@
import sys
from sys import version_info as python_version
if sys.version_info < (3, 9): ... # OK
if sys.version_info >= (3, 9): ... # OK
if sys.version_info == (3, 9): ... # OK
if sys.version_info <= (3, 10): ... # OK
if sys.version_info > (3, 10): ... # OK
if python_version > (3, 10): ... # OK

View File

@@ -1,18 +0,0 @@
import sys
from sys import version_info as python_version
if sys.version_info < (3, 9): ... # OK
if sys.version_info >= (3, 9): ... # OK
if sys.version_info == (3, 9): ... # OK
if sys.version_info == (3, 9): ... # Error: PYI006 Use only `<` and `>=` for version info comparisons
if sys.version_info <= (3, 10): ... # Error: PYI006 Use only `<` and `>=` for version info comparisons
if sys.version_info <= (3, 10): ... # Error: PYI006 Use only `<` and `>=` for version info comparisons
if sys.version_info > (3, 10): ... # Error: PYI006 Use only `<` and `>=` for version info comparisons
if python_version > (3, 10): ... # Error: PYI006 Use only `<` and `>=` for version info comparisons

View File

@@ -1,14 +0,0 @@
def bar():
... # OK
def foo():
pass # OK, since we're not in a stub file
class Bar:
... # OK
class Foo:
pass # OK, since we're not in a stub file

View File

@@ -1,8 +0,0 @@
def bar(): ... # OK
def foo():
pass # ERROR PYI009, since we're in a stub file
class Bar: ... # OK
class Foo:
pass # ERROR PYI009, since we're in a stub file

View File

@@ -1,18 +0,0 @@
def bar():
... # OK
def foo():
"""foo""" # OK
def buzz():
print("buzz") # OK, not in stub file
def foo2():
123 # OK, not in a stub file
def bizz():
x = 123 # OK, not in a stub file

View File

@@ -1,12 +0,0 @@
def bar(): ... # OK
def foo():
"""foo""" # OK, strings are handled by another rule
def buzz():
print("buzz") # ERROR PYI010
def foo2():
123 # ERROR PYI010
def bizz():
x = 123 # ERROR PYI010

View File

@@ -1,79 +0,0 @@
def f12(
x,
y: str = os.pathsep, # OK
) -> None:
...
def f11(*, x: str = "x") -> None: # OK
...
def f13(
x: list[str] = [
"foo",
"bar",
"baz",
] # OK
) -> None:
...
def f14(
x: tuple[str, ...] = (
"foo",
"bar",
"baz",
) # OK
) -> None:
...
def f15(
x: set[str] = {
"foo",
"bar",
"baz",
} # OK
) -> None:
...
def f16(x: frozenset[bytes] = frozenset({b"foo", b"bar", b"baz"})) -> None: # OK
...
def f17(
x: str = "foo" + "bar", # OK
) -> None:
...
def f18(
x: str = b"foo" + b"bar", # OK
) -> None:
...
def f19(
x: object = "foo" + 4, # OK
) -> None:
...
def f20(
x: int = 5 + 5, # OK
) -> None:
...
def f21(
x: complex = 3j - 3j, # OK
) -> None:
...
def f22(
x: complex = -42.5j + 4.3j, # OK
) -> None:
...

View File

@@ -1,63 +0,0 @@
def f12(
x,
y: str = os.pathsep, # Error PYI011 Only simple default values allowed for typed arguments
) -> None: ...
def f11(*, x: str = "x") -> None: ... # OK
def f13(
x: list[
str
] = [ # Error PYI011 Only simple default values allowed for typed arguments
"foo",
"bar",
"baz",
]
) -> None: ...
def f14(
x: tuple[
str, ...
] = ( # Error PYI011 Only simple default values allowed for typed arguments
"foo",
"bar",
"baz",
)
) -> None: ...
def f15(
x: set[
str
] = { # Error PYI011 Only simple default values allowed for typed arguments
"foo",
"bar",
"baz",
}
) -> None: ...
def f16(
x: frozenset[
bytes
] = frozenset( # Error PYI011 Only simple default values allowed for typed arguments
{b"foo", b"bar", b"baz"}
)
) -> None: ...
def f17(
x: str = "foo" # Error PYI011 Only simple default values allowed for typed arguments
+ "bar",
) -> None: ...
def f18(
x: str = b"foo" # Error PYI011 Only simple default values allowed for typed arguments
+ b"bar",
) -> None: ...
def f19(
x: object = "foo" # Error PYI011 Only simple default values allowed for typed arguments
+ 4,
) -> None: ...
def f20(
x: int = 5
+ 5, # Error PYI011 Only simple default values allowed for typed arguments
) -> None: ...
def f21(
x: complex = 3j
- 3j, # Error PYI011 Only simple default values allowed for typed arguments
) -> None: ...
def f22(
x: complex = -42.5j # Error PYI011 Only simple default values allowed for typed arguments
+ 4.3j,
) -> None: ...

View File

@@ -1,79 +0,0 @@
def f12(
x,
y=os.pathsep, # OK
) -> None:
...
def f11(*, x="x") -> None:
... # OK
def f13(
x=[ # OK
"foo",
"bar",
"baz",
]
) -> None:
...
def f14(
x=( # OK
"foo",
"bar",
"baz",
)
) -> None:
...
def f15(
x={ # OK
"foo",
"bar",
"baz",
}
) -> None:
...
def f16(x=frozenset({b"foo", b"bar", b"baz"})) -> None:
... # OK
def f17(
x="foo" + "bar", # OK
) -> None:
...
def f18(
x=b"foo" + b"bar", # OK
) -> None:
...
def f19(
x="foo" + 4, # OK
) -> None:
...
def f20(
x=5 + 5, # OK
) -> None:
...
def f21(
x=3j - 3j, # OK
) -> None:
...
def f22(
x=-42.5j + 4.3j, # OK
) -> None:
...

View File

@@ -1,45 +0,0 @@
def f12(
x,
y=os.pathsep, # Error PYI014
) -> None: ...
def f11(*, x="x") -> None: ... # OK
def f13(
x=[ # Error PYI014
"foo",
"bar",
"baz",
]
) -> None: ...
def f14(
x=( # Error PYI014
"foo",
"bar",
"baz",
)
) -> None: ...
def f15(
x={ # Error PYI014
"foo",
"bar",
"baz",
}
) -> None: ...
def f16(x=frozenset({b"foo", b"bar", b"baz"})) -> None: ... # Error PYI014
def f17(
x="foo" + "bar", # Error PYI014
) -> None: ...
def f18(
x=b"foo" + b"bar", # Error PYI014
) -> None: ...
def f19(
x="foo" + 4, # Error PYI014
) -> None: ...
def f20(
x=5 + 5, # Error PYI014
) -> None: ...
def f21(
x=3j - 3j, # Error PYI014
) -> None: ...
def f22(
x=-42.5j + 4.3j, # Error PYI014
) -> None: ...

View File

@@ -1,14 +0,0 @@
"""foo""" # OK, not in stub
def foo():
"""foo""" # OK, doc strings are allowed in non-stubs
class Bar:
"""bar""" # OK, doc strings are allowed in non-stubs
def bar():
x = 1
"""foo""" # OK, not a doc string

View File

@@ -1,11 +0,0 @@
"""foo""" # ERROR PYI021
def foo():
"""foo""" # ERROR PYI021
class Bar:
"""bar""" # ERROR PYI021
def bar():
x = 1
"""foo""" # OK, not a doc string

View File

@@ -1,33 +0,0 @@
# From https://github.com/PyCQA/flake8-pyi/blob/4212bec43dbc4020a59b90e2957c9488575e57ba/tests/type_comments.pyi
from collections.abc import Sequence
from typing import TypeAlias
A: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
B: TypeAlias = None # type: str # And here's an extra comment about why it's that type # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
C: TypeAlias = None #type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
D: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
E: TypeAlias = None# type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
F: TypeAlias = None#type:int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
def func(
arg1, # type: dict[str, int] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
arg2 # type: Sequence[bytes] # And here's some more info about this arg # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
): ...
class Foo:
Attr: TypeAlias = None # type: set[str] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
G: TypeAlias = None # type: ignore
H: TypeAlias = None # type: ignore[attr-defined]
I: TypeAlias = None #type: ignore
J: TypeAlias = None # type: ignore
K: TypeAlias = None# type: ignore
L: TypeAlias = None#type:ignore
# Whole line commented out # type: int
M: TypeAlias = None # type: can't parse me!
class Bar:
N: TypeAlias = None # type: can't parse me either!
# This whole line is commented out and indented # type: str

View File

@@ -1,33 +0,0 @@
# From https://github.com/PyCQA/flake8-pyi/blob/4212bec43dbc4020a59b90e2957c9488575e57ba/tests/type_comments.pyi
from collections.abc import Sequence
from typing import TypeAlias
A: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
B: TypeAlias = None # type: str # And here's an extra comment about why it's that type # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
C: TypeAlias = None #type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
D: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
E: TypeAlias = None# type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
F: TypeAlias = None#type:int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
def func(
arg1, # type: dict[str, int] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
arg2 # type: Sequence[bytes] # And here's some more info about this arg # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
): ...
class Foo:
Attr: TypeAlias = None # type: set[str] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
G: TypeAlias = None # type: ignore
H: TypeAlias = None # type: ignore[attr-defined]
I: TypeAlias = None #type: ignore
J: TypeAlias = None # type: ignore
K: TypeAlias = None# type: ignore
L: TypeAlias = None#type:ignore
# Whole line commented out # type: int
M: TypeAlias = None # type: can't parse me!
class Bar:
N: TypeAlias = None # type: can't parse me either!
# This whole line is commented out and indented # type: str

View File

@@ -23,7 +23,7 @@ def test_error():
"""
# recursive case
assert not (a or not (b or c))
assert not (a or not (b or c)) # note that we only reduce once here
assert not (a or not (b and c))
# detected, but no autofix for messages

View File

@@ -293,30 +293,3 @@ def x(y):
def foo(baz: str) -> str:
return baz
def end_of_statement():
def example():
if True:
return ""
def example():
if True:
return ""
def example():
if True:
return "" # type: ignore
def example():
if True:
return "" ;
def example():
if True:
return "" \
; # type: ignore

View File

@@ -6,6 +6,22 @@ def x():
return a # error
def x():
a = 1
print(a)
a = 2
return a # error
def x():
a = 1
if True:
return a # error
a = 2
print(a)
return a
# Can be refactored false positives
# https://github.com/afonasev/flake8-return/issues/47#issuecomment-1122571066
def get_bar_if_exists(obj):
@@ -149,6 +165,44 @@ def my_func():
return foo
# Refactored incorrect false positives
# See test cases above marked: "Can be refactored false positives"
# https://github.com/afonasev/flake8-return/issues/47#issuecomment-1122571066
def get_bar_if_exists(obj):
if hasattr(obj, "bar"):
return str(obj.bar)
return ""
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def x():
formatted = _USER_AGENT_FORMATTER.format(format_string, **values)
# clean up after any blank components
return formatted.replace("()", "").replace(" ", " ").strip()
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def user_agent_username(username=None):
if not username:
return ""
username = username.replace(" ", "_") # Avoid spaces or %20.
try:
username.encode("ascii") # just test,
# but not actually use it
except UnicodeEncodeError:
username = quote(username.encode("utf-8"))
else:
# % is legal in the default $wgLegalTitleChars
# This is so that ops know the real pywikibot will not
# allow a useragent in the username to allow through a
# hand-coded percent-encoded value.
if "%" in username:
username = quote(username)
return username
# https://github.com/afonasev/flake8-return/issues/116#issue-1367575481
def no_exception_loop():
success = False
@@ -206,44 +260,3 @@ def nonlocal_assignment():
nonlocal X
X = 1
return X
def decorator() -> Flask:
app = Flask(__name__)
@app.route('/hello')
def hello() -> str:
"""Hello endpoint."""
return 'Hello, World!'
return app
def default():
y = 1
def f(x = y) -> X:
return x
return y
def get_queryset(option_1, option_2):
queryset: Any = None
queryset = queryset.filter(a=1)
if option_1:
queryset = queryset.annotate(b=Value(2))
if option_2:
queryset = queryset.filter(c=3)
return queryset
def get_queryset():
queryset = Model.filter(a=1)
queryset = queryset.filter(c=3)
return queryset
def get_queryset():
queryset = Model.filter(a=1)
return queryset # error

View File

@@ -74,13 +74,3 @@ elif b == b"two":
return 2
elif a == b"three":
return 3
# SIM116
if func_name == "create":
return "A"
elif func_name == "modify":
return "M"
elif func_name == "remove":
return "D"
elif func_name == "move":
return "MV"

View File

@@ -6,14 +6,11 @@
"yoda" <= compare # SIM300
"yoda" < compare # SIM300
42 > age # SIM300
-42 > age # SIM300
+42 > age # SIM300
YODA == age # SIM300
YODA > age # SIM300
YODA >= age # SIM300
JediOrder.YODA == age # SIM300
0 < (number - 100) # SIM300
SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
# OK
compare == "yoda"

View File

@@ -1,8 +1,7 @@
from typing import TYPE_CHECKING, Any, ClassVar
import attrs
from ....import unknown
from ..protocol import commands, definitions, responses
from ..server import example
from .. import server

View File

@@ -1,3 +0,0 @@
from typing import Tuple
x: Tuple

View File

@@ -1,38 +0,0 @@
from __future__ import annotations
import pathlib
from typing import TYPE_CHECKING
import pydantic
from pydantic import BaseModel
if TYPE_CHECKING:
import datetime # TCH004
from array import array # TCH004
import pandas # TCH004
import pyproj
class A(pydantic.BaseModel):
x: datetime.datetime
class B(BaseModel):
x: pandas.DataFrame
class C(BaseModel):
x: pathlib.Path
class E:
pass
class F(E):
x: pyproj.Transformer
class G(BaseModel):
x: array

View File

@@ -1,28 +0,0 @@
from __future__ import annotations
import geopandas as gpd # TCH002
import pydantic
import pyproj # TCH002
from pydantic import BaseModel
import numpy
class A(BaseModel):
x: pandas.DataFrame
class B(pydantic.BaseModel):
x: numpy.ndarray
class C:
pass
class D(C):
x: pyproj.Transformer
class E(C):
x: gpd.GeoDataFrame

View File

@@ -1,24 +0,0 @@
from __future__ import annotations
import datetime
import pathlib
from uuid import UUID # TCH003
import pydantic
from pydantic import BaseModel
class A(pydantic.BaseModel):
x: datetime.datetime
class B(BaseModel):
x: pathlib.Path
class C:
pass
class D(C):
x: UUID

View File

@@ -1,46 +0,0 @@
from __future__ import annotations
import pathlib
from typing import TYPE_CHECKING
import attrs
from attrs import frozen
import numpy
if TYPE_CHECKING:
import datetime # TCH004
from array import array # TCH004
import pandas # TCH004
import pyproj
@attrs.define(auto_attribs=True)
class A:
x: datetime.datetime
@attrs.define
class B:
x: pandas.DataFrame
@frozen(auto_attribs=True)
class C:
x: pathlib.Path
@frozen
class D:
x: array
@dataclass
class E:
x: pyproj.Transformer
@attrs.define
class F:
x: numpy.ndarray

View File

@@ -1,30 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
import attrs
import pandas
import pyproj
from attrs import frozen
import numpy # TCH002
@attrs.define(auto_attribs=True)
class A:
x: pyproj.Transformer
@attrs.define
class B:
x: pandas.DataFrame
@frozen
class C:
x: pandas.DataFrame
@dataclass
class D:
x: numpy.ndarray

View File

@@ -1,24 +0,0 @@
from __future__ import annotations
import datetime
from array import array
from dataclasses import dataclass
from uuid import UUID # TCH003
import attrs
from attrs import frozen
@attrs.define(auto_attribs=True)
class A:
x: datetime.datetime
@frozen
class B:
x: array
@dataclass
class C:
x: UUID

View File

@@ -1,15 +0,0 @@
from foo import ( # Comment on `foo`
Member as Alias, # Comment on `Alias`
)
from bar import ( # Comment on `bar`
Member, # Comment on `Member`
)
from baz import ( # Comment on `baz`
Member as Alias # Comment on `Alias`
)
from bop import ( # Comment on `bop`
Member # Comment on `Member`
)

View File

@@ -1,14 +0,0 @@
#: E211
spam (1)
#: E211 E211
dict ['key'] = list [index]
#: E211
dict['key'] ['subkey'] = list[index]
#: Okay
spam(1)
dict['key'] = list[index]
# This is not prohibited by PEP8, but avoid it.
class Foo (Bar, Baz):
pass

View File

@@ -1,15 +0,0 @@
#: E231
a = (1,2)
#: E231
a[b1,:]
#: E231
a = [{'a':''}]
#: Okay
a = (4,)
b = (5, )
c = {'text': text[5:]}
result = {
'key1': 'value',
'key2': 'value',
}

View File

@@ -1,50 +0,0 @@
#: E251 E251
def foo(bar = False):
'''Test function with an error in declaration'''
pass
#: E251
foo(bar= True)
#: E251
foo(bar =True)
#: E251 E251
foo(bar = True)
#: E251
y = bar(root= "sdasd")
#: E251:2:29
parser.add_argument('--long-option',
default=
"/rather/long/filesystem/path/here/blah/blah/blah")
#: E251:1:45
parser.add_argument('--long-option', default
="/rather/long/filesystem/path/here/blah/blah/blah")
#: E251:3:8 E251:3:10
foo(True,
baz=(1, 2),
biz = 'foo'
)
#: Okay
foo(bar=(1 == 1))
foo(bar=(1 != 1))
foo(bar=(1 >= 1))
foo(bar=(1 <= 1))
(options, args) = parser.parse_args()
d[type(None)] = _deepcopy_atomic
# Annotated Function Definitions
#: Okay
def munge(input: AnyStr, sep: AnyStr = None, limit=1000,
extra: Union[str, dict] = None) -> AnyStr:
pass
#: Okay
async def add(a: int = 0, b: int = 0) -> int:
return a + b
# Previously E251 four times
#: E271:1:6
async def add(a: int = 0, b: int = 0) -> int:
return a + b
#: E252:1:15 E252:1:16 E252:1:27 E252:1:36
def add(a: int=0, b: int =0, c: int= 0) -> int:
return a + b + c
#: Okay
def add(a: int = _default(name='f')):
return a

View File

@@ -1,145 +0,0 @@
#: W191
if False:
print # indented with 1 tab
#:
#: W191
y = x == 2 \
or x == 3
#: E101 W191 W504
if (
x == (
3
) or
y == 4):
pass
#: E101 W191
if x == 2 \
or y > 1 \
or x == 3:
pass
#: E101 W191
if x == 2 \
or y > 1 \
or x == 3:
pass
#:
#: E101 W191 W504
if (foo == bar and
baz == bop):
pass
#: E101 W191 W504
if (
foo == bar and
baz == bop
):
pass
#:
#: E101 E101 W191 W191
if start[1] > end_col and not (
over_indent == 4 and indent_next):
return (0, "E121 continuation line over-"
"indented for visual indent")
#:
#: E101 W191
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
#: E101 W191 W504
if ((row < 0 or self.moduleCount <= row or
col < 0 or self.moduleCount <= col)):
raise Exception("%s,%s - %s" % (row, col, self.moduleCount))
#: E101 E101 E101 E101 W191 W191 W191 W191 W191 W191
if bar:
return (
start, 'E121 lines starting with a '
'closing bracket should be indented '
"to match that of the opening "
"bracket's line"
)
#
#: E101 W191 W504
# you want vertical alignment, so use a parens
if ((foo.bar("baz") and
foo.bar("bop")
)):
print "yes"
#: E101 W191 W504
# also ok, but starting to look like LISP
if ((foo.bar("baz") and
foo.bar("bop"))):
print "yes"
#: E101 W191 W504
if (a == 2 or
b == "abc def ghi"
"jkl mno"):
return True
#: E101 W191 W504
if (a == 2 or
b == """abc def ghi
jkl mno"""):
return True
#: W191:2:1 W191:3:1 E101:3:2
if length > options.max_line_length:
return options.max_line_length, \
"E501 line too long (%d characters)" % length
#
#: E101 W191 W191 W504
if os.path.exists(os.path.join(path, PEP8_BIN)):
cmd = ([os.path.join(path, PEP8_BIN)] +
self._pep8_options(targetfile))
#: W191
'''
multiline string with tab in it'''
#: E101 W191
'''multiline string
with tabs
and spaces
'''
#: Okay
'''sometimes, you just need to go nuts in a multiline string
and allow all sorts of crap
like mixed tabs and spaces
or trailing whitespace
or long long long long long long long long long long long long long long long long long lines
''' # nopep8
#: Okay
'''this one
will get no warning
even though the noqa comment is not immediately after the string
''' + foo # noqa
#
#: E101 W191
if foo is None and bar is "bop" and \
blah == 'yeah':
blah = 'yeahnah'
#
#: W191 W191 W191
if True:
foo(
1,
2)
#: W191 W191 W191 W191 W191
def test_keys(self):
"""areas.json - All regions are accounted for."""
expected = set([
u'Norrbotten',
u'V\xe4sterbotten',
])
#: W191
x = [
'abc'
]
#:

View File

@@ -1,26 +0,0 @@
#: Okay
# 情
#: W291:1:6
print
#: W293:2:1
class Foo(object):
bang = 12
#: W291:2:35
'''multiline
string with trailing whitespace'''
#: W291 W292 noeol
x = 1
#: W191 W292 noeol
if False:
pass # indented with tabs
#: W292:1:36 noeol
# This line doesn't have a linefeed
#: W292:1:5 E225:1:2 noeol
1+ 1
#: W292:1:27 E261:1:12 noeol
import this # no line feed
#: W292:3:22 noeol
class Test(object):
def __repr__(self):
return 'test'

View File

@@ -2,8 +2,6 @@
from functools import cached_property
from gi.repository import GObject
# Bad examples
def bad_liouiwnlkjl():
@@ -78,11 +76,6 @@ class Thingy:
"""This property method docstring does not need to be written in imperative mood."""
return self._beep
@GObject.Property
def good_custom_property(self):
"""This property method docstring does not need to be written in imperative mood."""
return self._beep
@cached_property
def good_cached_property(self):
"""This property method docstring does not need to be written in imperative mood."""

View File

@@ -1 +0,0 @@
''' SAM macro definitions '''

View File

@@ -1,10 +0,0 @@
"""Test: imports within `ModuleNotFoundError` handlers."""
def check_orjson():
try:
import orjson
return True
except ModuleNotFoundError:
return False

View File

@@ -1,23 +0,0 @@
"""Test case: strings used within calls within type annotations."""
from typing import Callable
import bpy
from mypy_extensions import VarArg
from foo import Bar
class LightShow(bpy.types.Operator):
label = "Create Character"
name = "lightshow.letter_creation"
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # OK
def f(x: Callable[[VarArg("os")], None]): # F821
pass
f(Callable[["Bar"], None])
f(Callable[["Baz"], None])

View File

@@ -1,25 +0,0 @@
"""Test case: strings used within calls within type annotations."""
from __future__ import annotations
from typing import Callable
import bpy
from mypy_extensions import VarArg
from foo import Bar
class LightShow(bpy.types.Operator):
label = "Create Character"
name = "lightshow.letter_creation"
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # OK
def f(x: Callable[[VarArg("os")], None]): # F821
pass
f(Callable[["Bar"], None])
f(Callable[["Baz"], None])

View File

@@ -20,27 +20,7 @@ def f(provided: int) -> int:
match provided:
case True:
return captured # F821
def f(provided: int) -> int:
match provided:
case captured:
return captured
def f(provided: int) -> int:
match provided:
case [captured, *_]:
return captured
def f(provided: int) -> int:
match provided:
case [*captured]:
return captured
def f(provided: int) -> int:
match provided:
case {**captured}:
case captured:
return captured

View File

@@ -1,49 +0,0 @@
"""
Test for else-if-used
"""
def ok0():
"""Should not trigger on elif"""
if 1:
pass
elif 2:
pass
def ok1():
"""If the orelse has more than 1 item in it, shouldn't trigger"""
if 1:
pass
else:
print()
if 1:
pass
def ok2():
"""If the orelse has more than 1 item in it, shouldn't trigger"""
if 1:
pass
else:
if 1:
pass
print()
def not_ok0():
if 1:
pass
else:
if 2:
pass
def not_ok1():
if 1:
pass
else:
if 2:
pass
else:
pass

View File

@@ -1,19 +0,0 @@
x = "a string"
y = "another string"
z = ""
def errors():
if x is "" or x == "":
print("x is an empty string")
if y is not "" or y != "":
print("y is not an empty string")
if "" != z:
print("z is an empty string")
def ok():
if x and not y:
print("x is not an empty string, but y is an empty string")

View File

@@ -1,75 +0,0 @@
# Adapted from:
# https://github.com/PyCQA/pylint/blob/b70d2abd7fabe9bfd735a30b593b9cd5eaa36194/tests/functional/g/globals.py
CONSTANT = 1
def FUNC():
pass
class CLASS:
pass
def fix_constant(value):
"""All this is ok, but try not to use `global` ;)"""
global CONSTANT # [global-statement]
print(CONSTANT)
CONSTANT = value
def global_with_import():
"""Should only warn for global-statement when using `Import` node"""
global sys # [global-statement]
import sys
def global_with_import_from():
"""Should only warn for global-statement when using `ImportFrom` node"""
global namedtuple # [global-statement]
from collections import namedtuple
def global_del():
"""Deleting the global name prevents `global-variable-not-assigned`"""
global CONSTANT # [global-statement]
print(CONSTANT)
del CONSTANT
def global_operator_assign():
"""Operator assigns should only throw a global statement error"""
global CONSTANT # [global-statement]
print(CONSTANT)
CONSTANT += 1
def global_function_assign():
"""Function assigns should only throw a global statement error"""
global CONSTANT # [global-statement]
def CONSTANT():
pass
CONSTANT()
def override_func():
"""Overriding a function should only throw a global statement error"""
global FUNC # [global-statement]
def FUNC():
pass
FUNC()
def override_class():
"""Overriding a class should only throw a global statement error"""
global CLASS # [global-statement]
class CLASS:
pass
CLASS()

View File

@@ -7,18 +7,6 @@ logging.warning("Hello %s", "World!", "again", something="else")
logging.warning("Hello %s", "World!")
# do not handle calls without any args
logging.info("100% dynamic")
# do not handle calls with *args
logging.error("Example log %s, %s", "foo", "bar", "baz", *args)
# do not handle calls with **kwargs
logging.error("Example log %s, %s", "foo", "bar", "baz", **kwargs)
# do not handle keyword arguments
logging.error("%(objects)d modifications: %(modifications)d errors: %(errors)d")
import warning
warning.warning("Hello %s %s", "World!")

View File

@@ -2,19 +2,11 @@ import logging
logging.warning("Hello %s", "World!", "again") # [logging-too-many-args]
# do not handle calls with kwargs (like pylint)
logging.warning("Hello %s", "World!", "again", something="else")
logging.warning("Hello %s", "World!")
# do not handle calls with *args
logging.error("Example log %s, %s", "foo", "bar", "baz", *args)
# do not handle calls with **kwargs
logging.error("Example log %s, %s", "foo", "bar", "baz", **kwargs)
# do not handle keyword arguments
logging.error("%(objects)d modifications: %(modifications)d errors: %(errors)d", {"objects": 1, "modifications": 1, "errors": 1})
import warning
warning.warning("Hello %s", "World!", "again")

View File

@@ -11,9 +11,6 @@ if 10 == 100: # [comparison-of-constants] R0133
if 1 == 3: # [comparison-of-constants] R0133
pass
if 1 == -3: # [comparison-of-constants] R0133
pass
x = 0
if 4 == 3 == x: # [comparison-of-constants] R0133
pass
@@ -38,12 +35,6 @@ if argc != 1: # correct
if argc != 2: # [magic-value-comparison]
pass
if argc != -2: # [magic-value-comparison]
pass
if argc != +2: # [magic-value-comparison]
pass
if __name__ == "__main__": # correct
pass

View File

@@ -25,7 +25,22 @@ avoid-escape = true
max-complexity = 10
[tool.ruff.pep8-naming]
classmethod-decorators = ["pydantic.validator"]
ignore-names = [
"setUp",
"tearDown",
"setUpClass",
"tearDownClass",
"setUpModule",
"tearDownModule",
"asyncSetUp",
"asyncTearDown",
"setUpTestData",
"failureException",
"longMessage",
"maxDiff",
]
classmethod-decorators = ["classmethod", "pydantic.validator"]
staticmethod-decorators = ["staticmethod"]
[tool.ruff.flake8-tidy-imports]
ban-relative-imports = "parents"

View File

@@ -28,10 +28,3 @@ def f(x: IList[str]) -> None:
def f(x: "List[str]") -> None:
...
list = "abc"
def f(x: List[str]) -> None:
...

View File

@@ -41,8 +41,3 @@ def f(x: Union["str", int]) -> None:
def f(x: Union[("str", "int"), float]) -> None:
...
def f() -> None:
x: Optional[str]
x = Optional[str]

View File

@@ -12,17 +12,11 @@ Ipsum
""".encode(
"utf-8"
)
(
"Lorem "
"Ipsum".encode()
)
(
"Lorem " # Comment
"Ipsum".encode() # Comment
)
(
"Lorem " "Ipsum".encode()
)
# b"""
# Lorem
#
# Ipsum
# """
# `encode` on variables should not be processed.
string = "hello there"

View File

@@ -35,9 +35,3 @@ MyType = TypedDict("MyType", {"in": int, "x-y": int})
# unpacking (OK)
c = {"c": float}
MyType = TypedDict("MyType", {"a": int, "b": str, **c})
# Empty dict literal
MyType = TypedDict("MyType", {})
# Empty dict call
MyType = TypedDict("MyType", dict())

View File

@@ -23,9 +23,3 @@ MyType = NamedTuple(
# invalid identifiers (OK)
MyType = NamedTuple("MyType", [("x-y", int), ("b", tuple[str, ...])])
# no fields
MyType = typing.NamedTuple("MyType")
# empty fields
MyType = typing.NamedTuple("MyType", [])

View File

@@ -41,7 +41,7 @@ if True:
Good,
)
from typing import Callable, Match, Pattern, List, OrderedDict
from typing import Callable, Match, Pattern, List
if True: from collections import (
Mapping, Counter)

View File

@@ -1,7 +0,0 @@
isinstance(1, (int, float)) # UP038
issubclass("yes", (int, float, str)) # UP038
isinstance(1, int) # OK
issubclass("yes", int) # OK
isinstance(1, int | float) # OK
issubclass("yes", int | str) # OK

View File

@@ -0,0 +1,15 @@
def f(*args, **kwargs):
pass
a = (1, 2)
b = (3, 4)
c = (5, 6)
d = (7, 8)
f(a, b)
f(a, kw=b)
f(*a, kw=b)
f(kw=a, *b)
f(kw=a, *b, *c)
f(*a, kw=b, *c, kw1=d)

View File

@@ -30,11 +30,3 @@ def good():
raise e # This is verbose violation, shouldn't trigger no cause
except Exception:
raise # Just re-raising don't need 'from'
def good():
try:
from mod import f
except ImportError:
def f():
raise MyException() # Raising within a new scope is fine

View File

@@ -4,7 +4,7 @@ use rustc_hash::FxHashMap;
use rustpython_parser::ast::ExcepthandlerKind::ExceptHandler;
use rustpython_parser::ast::{Stmt, StmtKind};
use crate::types::RefEquality;
use crate::ast::types::RefEquality;
/// Return the common ancestor of `left` and `right` below `stop`, or `None`.
fn common_ancestor<'a>(

View File

@@ -1,14 +1,13 @@
//! An equivalent object hierarchy to the [`Expr`] hierarchy, but with the
//! ability to compare expressions for equality (via [`Eq`] and [`Hash`]).
use num_bigint::BigInt;
use rustpython_parser::ast::{
Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler,
ExcepthandlerKind, Expr, ExprContext, ExprKind, Keyword, MatchCase, Operator, Pattern,
PatternKind, Stmt, StmtKind, Unaryop, Withitem,
};
use num_bigint::BigInt;
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum ComparableExprContext {
Load,

View File

@@ -1,8 +1,8 @@
use rustpython_parser::ast::Expr;
use crate::context::Context;
use crate::helpers::{map_callable, to_call_path};
use crate::types::{Scope, ScopeKind};
use crate::ast::helpers::{map_callable, to_call_path};
use crate::ast::types::{Scope, ScopeKind};
use crate::checkers::ast::Checker;
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")];
@@ -16,7 +16,7 @@ pub enum FunctionType {
/// Classify a function based on its scope, name, and decorators.
pub fn classify(
ctx: &Context,
checker: &Checker,
scope: &Scope,
name: &str,
decorator_list: &[Expr],
@@ -29,12 +29,12 @@ pub fn classify(
if decorator_list.iter().any(|expr| {
// The method is decorated with a static method decorator (like
// `@staticmethod`).
ctx.resolve_call_path(map_callable(expr))
checker
.resolve_call_path(map_callable(expr))
.map_or(false, |call_path| {
call_path.as_slice() == ["", "staticmethod"]
|| staticmethod_decorators
.iter()
.any(|decorator| call_path == to_call_path(decorator))
staticmethod_decorators
.iter()
.any(|decorator| call_path == to_call_path(decorator))
})
}) {
FunctionType::StaticMethod
@@ -42,7 +42,7 @@ pub fn classify(
// Special-case class method, like `__new__`.
|| scope.bases.iter().any(|expr| {
// The class itself extends a known metaclass, so all methods are class methods.
ctx.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
checker.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
METACLASS_BASES
.iter()
.any(|(module, member)| call_path.as_slice() == [*module, *member])
@@ -50,8 +50,7 @@ pub fn classify(
})
|| decorator_list.iter().any(|expr| {
// The method is decorated with a class method decorator (like `@classmethod`).
ctx.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
call_path.as_slice() == ["", "classmethod"] ||
checker.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
classmethod_decorators
.iter()
.any(|decorator| call_path == to_call_path(decorator))

View File

@@ -2,7 +2,7 @@ use std::hash::Hash;
use rustpython_parser::ast::Expr;
use crate::comparable::ComparableExpr;
use crate::ast::comparable::ComparableExpr;
/// Wrapper around `Expr` that implements `Hash` and `PartialEq`.
pub struct HashableExpr<'a>(&'a Expr);
@@ -30,11 +30,11 @@ impl<'a> From<&'a Expr> for HashableExpr<'a> {
}
impl<'a> HashableExpr<'a> {
pub const fn from_expr(expr: &'a Expr) -> Self {
pub(crate) const fn from_expr(expr: &'a Expr) -> Self {
Self(expr)
}
pub const fn as_expr(&self) -> &'a Expr {
pub(crate) const fn as_expr(&self) -> &'a Expr {
self.0
}
}

View File

@@ -1,6 +1,5 @@
use std::path::Path;
use bitflags::bitflags;
use itertools::Itertools;
use log::error;
use once_cell::sync::Lazy;
@@ -13,11 +12,11 @@ use rustpython_parser::ast::{
use rustpython_parser::{lexer, Mode, StringKind, Tok};
use smallvec::{smallvec, SmallVec};
use crate::context::Context;
use crate::ast::types::{Binding, BindingKind, CallPath, Range};
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
use crate::checkers::ast::Checker;
use crate::source_code::{Generator, Indexer, Locator, Stylist};
use crate::types::{Binding, BindingKind, CallPath, Range};
use crate::visitor;
use crate::visitor::Visitor;
/// Create an `Expr` with default location from an `ExprKind`.
pub fn create_expr(node: ExprKind) -> Expr {
@@ -99,16 +98,17 @@ pub fn format_call_path(call_path: &[&str]) -> String {
}
/// Return `true` if the `Expr` contains a reference to `${module}.${target}`.
pub fn contains_call_path(ctx: &Context, expr: &Expr, target: &[&str]) -> bool {
pub fn contains_call_path(checker: &Checker, expr: &Expr, target: &[&str]) -> bool {
any_over_expr(expr, &|expr| {
ctx.resolve_call_path(expr)
checker
.resolve_call_path(expr)
.map_or(false, |call_path| call_path.as_slice() == target)
})
}
/// Return `true` if the `Expr` contains an expression that appears to include a
/// side-effect (like a function call).
pub fn contains_effect(ctx: &Context, expr: &Expr) -> bool {
pub fn contains_effect(checker: &Checker, expr: &Expr) -> bool {
any_over_expr(expr, &|expr| {
// Accept empty initializers.
if let ExprKind::Call {
@@ -124,7 +124,7 @@ pub fn contains_effect(ctx: &Context, expr: &Expr) -> bool {
|| id == "tuple"
|| id == "dict"
|| id == "frozenset")
&& ctx.is_builtin(id);
&& checker.is_builtin(id);
return !is_empty_initializer;
}
}
@@ -575,32 +575,33 @@ pub fn has_non_none_keyword(keywords: &[Keyword], keyword: &str) -> bool {
})
}
bitflags! {
pub struct Exceptions: u32 {
const NAME_ERROR = 0b0000_0001;
const MODULE_NOT_FOUND_ERROR = 0b0000_0010;
}
}
/// Extract the names of all handled exceptions.
pub fn extract_handled_exceptions(handlers: &[Excepthandler]) -> Vec<&Expr> {
let mut handled_exceptions = Vec::new();
pub fn extract_handler_names(handlers: &[Excepthandler]) -> Vec<CallPath> {
// TODO(charlie): Use `resolve_call_path` to avoid false positives for
// overridden builtins.
let mut handler_names = vec![];
for handler in handlers {
match &handler.node {
ExcepthandlerKind::ExceptHandler { type_, .. } => {
if let Some(type_) = type_ {
if let ExprKind::Tuple { elts, .. } = &type_.node {
for type_ in elts {
handled_exceptions.push(type_);
let call_path = collect_call_path(type_);
if !call_path.is_empty() {
handler_names.push(call_path);
}
}
} else {
handled_exceptions.push(type_);
let call_path = collect_call_path(type_);
if !call_path.is_empty() {
handler_names.push(call_path);
}
}
}
}
}
}
handled_exceptions
handler_names
}
/// Return the set of all bound argument names.
@@ -652,7 +653,7 @@ pub fn has_comments<T>(located: &Located<T>, locator: &Locator) -> bool {
/// Returns `true` if a [`Range`] includes at least one comment.
pub fn has_comments_in(range: Range, locator: &Locator) -> bool {
for tok in lexer::lex_located(locator.slice(range), Mode::Module, range.location) {
for tok in lexer::lex_located(locator.slice(&range), Mode::Module, range.location) {
match tok {
Ok((_, tok, _)) => {
if matches!(tok, Tok::Comment(..)) {
@@ -668,10 +669,10 @@ pub fn has_comments_in(range: Range, locator: &Locator) -> bool {
}
/// Return `true` if the body uses `locals()`, `globals()`, `vars()`, `eval()`.
pub fn uses_magic_variable_access(ctx: &Context, body: &[Stmt]) -> bool {
pub fn uses_magic_variable_access(checker: &Checker, body: &[Stmt]) -> bool {
any_over_body(body, &|expr| {
if let ExprKind::Call { func, .. } = &expr.node {
ctx.resolve_call_path(func).map_or(false, |call_path| {
checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["", "locals"]
|| call_path.as_slice() == ["", "globals"]
|| call_path.as_slice() == ["", "vars"]
@@ -741,7 +742,7 @@ pub fn to_module_path(package: &Path, path: &Path) -> Option<Vec<String>> {
.ok()?
.iter()
.map(Path::new)
.map(Path::file_stem)
.map(std::path::Path::file_stem)
.map(|path| path.and_then(|path| path.to_os_string().into_string().ok()))
.collect::<Option<Vec<String>>>()
}
@@ -764,7 +765,7 @@ pub fn from_relative_import<'a>(module: &'a [String], name: &'a str) -> CallPath
call_path
}
/// A [`Visitor`] that collects all `return` statements in a function or method.
/// A [`Visitor`] that collects all return statements in a function or method.
#[derive(Default)]
pub struct ReturnStatementVisitor<'a> {
pub returns: Vec<Option<&'a Expr>>,
@@ -785,48 +786,6 @@ where
}
}
/// A [`Visitor`] that collects all `raise` statements in a function or method.
#[derive(Default)]
pub struct RaiseStatementVisitor<'a> {
pub raises: Vec<(Range, Option<&'a Expr>, Option<&'a Expr>)>,
}
impl<'a, 'b> Visitor<'b> for RaiseStatementVisitor<'b>
where
'b: 'a,
{
fn visit_stmt(&mut self, stmt: &'b Stmt) {
match &stmt.node {
StmtKind::Raise { exc, cause } => {
self.raises
.push((Range::from(stmt), exc.as_deref(), cause.as_deref()));
}
StmtKind::ClassDef { .. }
| StmtKind::FunctionDef { .. }
| StmtKind::AsyncFunctionDef { .. }
| StmtKind::Try { .. }
| StmtKind::TryStar { .. } => {}
StmtKind::If { body, orelse, .. } => {
visitor::walk_body(self, body);
visitor::walk_body(self, orelse);
}
StmtKind::While { body, .. }
| StmtKind::With { body, .. }
| StmtKind::AsyncWith { body, .. }
| StmtKind::For { body, .. }
| StmtKind::AsyncFor { body, .. } => {
visitor::walk_body(self, body);
}
StmtKind::Match { cases, .. } => {
for case in cases {
visitor::walk_body(self, &case.body);
}
}
_ => {}
}
}
}
/// Convert a location within a file (relative to `base`) to an absolute
/// position.
pub fn to_absolute(relative: Location, base: Location) -> Location {
@@ -854,7 +813,7 @@ pub fn to_relative(absolute: Location, base: Location) -> Location {
/// Return `true` if a [`Located`] has leading content.
pub fn match_leading_content<T>(located: &Located<T>, locator: &Locator) -> bool {
let range = Range::new(Location::new(located.location.row(), 0), located.location);
let prefix = locator.slice(range);
let prefix = locator.slice(&range);
prefix.chars().any(|char| !char.is_whitespace())
}
@@ -864,7 +823,7 @@ pub fn match_trailing_content<T>(located: &Located<T>, locator: &Locator) -> boo
located.end_location.unwrap(),
Location::new(located.end_location.unwrap().row() + 1, 0),
);
let suffix = locator.slice(range);
let suffix = locator.slice(&range);
for char in suffix.chars() {
if char == '#' {
return false;
@@ -882,7 +841,7 @@ pub fn match_trailing_comment<T>(located: &Located<T>, locator: &Locator) -> Opt
located.end_location.unwrap(),
Location::new(located.end_location.unwrap().row() + 1, 0),
);
let suffix = locator.slice(range);
let suffix = locator.slice(&range);
for (i, char) in suffix.chars().enumerate() {
if char == '#' {
return Some(i);
@@ -940,7 +899,7 @@ pub fn identifier_range(stmt: &Stmt, locator: &Locator) -> Range {
| StmtKind::FunctionDef { .. }
| StmtKind::AsyncFunctionDef { .. }
) {
let contents = locator.slice(stmt);
let contents = locator.slice(&Range::from_located(stmt));
for (start, tok, end) in lexer::lex_located(contents, Mode::Module, stmt.location).flatten()
{
if matches!(tok, Tok::Name { .. }) {
@@ -949,7 +908,7 @@ pub fn identifier_range(stmt: &Stmt, locator: &Locator) -> Range {
}
error!("Failed to find identifier for {:?}", stmt);
}
Range::from(stmt)
Range::from_located(stmt)
}
/// Like `identifier_range`, but accepts a `Binding`.
@@ -967,12 +926,12 @@ pub fn binding_range(binding: &Binding, locator: &Locator) -> Range {
}
}
/// Return the ranges of [`Tok::Name`] tokens within a specified node.
pub fn find_names<'a, T>(
located: &'a Located<T>,
// Return the ranges of `Name` tokens within a specified node.
pub fn find_names<'a, T, U>(
located: &'a Located<T, U>,
locator: &'a Locator,
) -> impl Iterator<Item = Range> + 'a {
let contents = locator.slice(located);
let contents = locator.slice(&Range::from_located(located));
lexer::lex_located(contents, Mode::Module, located.location)
.flatten()
.filter(|(_, tok, _)| matches!(tok, Tok::Name { .. }))
@@ -990,7 +949,7 @@ pub fn excepthandler_name_range(handler: &Excepthandler, locator: &Locator) -> O
match (name, type_) {
(Some(_), Some(type_)) => {
let type_end_location = type_.end_location.unwrap();
let contents = locator.slice(Range::new(type_end_location, body[0].location));
let contents = locator.slice(&Range::new(type_end_location, body[0].location));
let range = lexer::lex_located(contents, Mode::Module, type_end_location)
.flatten()
.tuple_windows()
@@ -1014,7 +973,7 @@ pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range {
.expect("Expected body to be non-empty")
.location
};
let contents = locator.slice(Range {
let contents = locator.slice(&Range {
location: handler.location,
end_location: end,
});
@@ -1031,7 +990,7 @@ pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range {
/// Find f-strings that don't contain any formatted values in a `JoinedStr`.
pub fn find_useless_f_strings(expr: &Expr, locator: &Locator) -> Vec<(Range, Range)> {
let contents = locator.slice(expr);
let contents = locator.slice(&Range::from_located(expr));
lexer::lex_located(contents, Mode::Module, expr.location)
.flatten()
.filter_map(|(location, tok, end_location)| match tok {
@@ -1039,7 +998,7 @@ pub fn find_useless_f_strings(expr: &Expr, locator: &Locator) -> Vec<(Range, Ran
kind: StringKind::FString | StringKind::RawFString,
..
} => {
let first_char = locator.slice(Range {
let first_char = locator.slice(&Range {
location,
end_location: Location::new(location.row(), location.column() + 1),
});
@@ -1079,7 +1038,7 @@ pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
.expect("Expected body to be non-empty")
.end_location
.unwrap();
let contents = locator.slice(Range {
let contents = locator.slice(&Range {
location: body_end,
end_location: orelse
.first()
@@ -1101,7 +1060,7 @@ pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
/// Return the `Range` of the first `Tok::Colon` token in a `Range`.
pub fn first_colon_range(range: Range, locator: &Locator) -> Option<Range> {
let contents = locator.slice(range);
let contents = locator.slice(&range);
let range = lexer::lex_located(contents, Mode::Module, range.location)
.flatten()
.find(|(_, kind, _)| matches!(kind, Tok::Colon))
@@ -1112,32 +1071,6 @@ pub fn first_colon_range(range: Range, locator: &Locator) -> Option<Range> {
range
}
/// Given a statement, find its "logical end".
///
/// For example: the statement could be following by a trailing semicolon, by an end-of-line
/// comment, or by any number of continuation lines (and then by a comment, and so on).
pub fn end_of_statement(stmt: &Stmt, locator: &Locator) -> Location {
let contents = locator.skip(stmt.end_location.unwrap());
// End-of-file, so just return the end of the statement.
if contents.is_empty() {
return stmt.end_location.unwrap();
}
// Otherwise, find the end of the last line that's "part of" the statement.
for (lineno, line) in contents.lines().enumerate() {
if line.ends_with('\\') {
continue;
}
return to_absolute(
Location::new(lineno + 1, line.chars().count()),
stmt.end_location.unwrap(),
);
}
unreachable!("Expected to find end-of-statement")
}
/// Return the `Range` of the first `Elif` or `Else` token in an `If` statement.
pub fn elif_else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
let StmtKind::If { body, orelse, .. } = &stmt.node else {
@@ -1157,7 +1090,7 @@ pub fn elif_else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
[stmt, ..] => stmt.location,
_ => return None,
};
let contents = locator.slice(Range::new(start, end));
let contents = locator.slice(&Range::new(start, end));
let range = lexer::lex_located(contents, Mode::Module, start)
.flatten()
.find(|(_, kind, _)| matches!(kind, Tok::Elif | Tok::Else))
@@ -1250,15 +1183,10 @@ impl<'a> SimpleCallArgs<'a> {
None
}
/// Return the number of positional and keyword arguments.
/// Get the number of positional and keyword arguments used.
pub fn len(&self) -> usize {
self.args.len() + self.kwargs.len()
}
/// Return `true` if there are no positional or keyword arguments.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
/// Return `true` if the given `Expr` is a potential logging call. Matches
@@ -1282,11 +1210,11 @@ mod tests {
use rustpython_parser as parser;
use rustpython_parser::ast::Location;
use crate::helpers::{
use crate::ast::helpers::{
elif_else_range, else_range, first_colon_range, identifier_range, match_trailing_content,
};
use crate::ast::types::Range;
use crate::source_code::Locator;
use crate::types::Range;
#[test]
fn trailing_content() -> Result<()> {

View File

@@ -9,7 +9,7 @@ pub enum LoggingLevel {
}
impl LoggingLevel {
pub fn from_attribute(level: &str) -> Option<Self> {
pub fn from_str(level: &str) -> Option<Self> {
match level {
"debug" => Some(LoggingLevel::Debug),
"critical" => Some(LoggingLevel::Critical),

View File

@@ -1,17 +1,13 @@
pub mod branch_detection;
pub mod cast;
pub mod comparable;
pub mod context;
pub mod function_type;
pub mod hashable;
pub mod helpers;
pub mod logging;
pub mod operations;
pub mod relocate;
pub mod source_code;
pub mod str;
pub mod types;
pub mod typing;
pub mod visibility;
pub mod visitor;
pub mod whitespace;

View File

@@ -3,11 +3,11 @@ use rustc_hash::FxHashMap;
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Located, Stmt, StmtKind};
use rustpython_parser::{lexer, Mode, Tok};
use crate::context::Context;
use crate::helpers::any_over_expr;
use crate::types::{BindingKind, Scope};
use crate::visitor;
use crate::visitor::Visitor;
use crate::ast::helpers::any_over_expr;
use crate::ast::types::{BindingKind, Scope};
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
use crate::checkers::ast::Checker;
bitflags! {
#[derive(Default)]
@@ -19,7 +19,7 @@ bitflags! {
/// Extract the names bound to a given __all__ assignment.
pub fn extract_all_names(
ctx: &Context,
checker: &Checker,
stmt: &Stmt,
scope: &Scope,
) -> (Vec<String>, AllNamesFlags) {
@@ -38,7 +38,7 @@ pub fn extract_all_names(
}
fn extract_elts<'a>(
ctx: &'a Context,
checker: &'a Checker,
expr: &'a Expr,
) -> (Option<&'a Vec<Expr>>, AllNamesFlags) {
match &expr.node {
@@ -60,7 +60,7 @@ pub fn extract_all_names(
} => {
// Allow `tuple()` and `list()` calls.
if keywords.is_empty() && args.len() <= 1 {
if ctx.resolve_call_path(func).map_or(false, |call_path| {
if checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["", "tuple"]
|| call_path.as_slice() == ["", "list"]
}) {
@@ -96,7 +96,7 @@ pub fn extract_all_names(
// Grab the existing bound __all__ values.
if let StmtKind::AugAssign { .. } = &stmt.node {
if let Some(index) = scope.bindings.get("__all__") {
if let BindingKind::Export(existing) = &ctx.bindings[*index].kind {
if let BindingKind::Export(existing) = &checker.bindings[*index].kind {
names.extend_from_slice(existing);
}
}
@@ -113,7 +113,7 @@ pub fn extract_all_names(
let mut current_right = right;
loop {
// Process the right side, which should be a "real" value.
let (elts, new_flags) = extract_elts(ctx, current_right);
let (elts, new_flags) = extract_elts(checker, current_right);
flags |= new_flags;
if let Some(elts) = elts {
add_to_names(&mut names, elts, &mut flags);
@@ -125,7 +125,7 @@ pub fn extract_all_names(
current_left = left;
current_right = right;
} else {
let (elts, new_flags) = extract_elts(ctx, current_left);
let (elts, new_flags) = extract_elts(checker, current_left);
flags |= new_flags;
if let Some(elts) = elts {
add_to_names(&mut names, elts, &mut flags);
@@ -134,7 +134,7 @@ pub fn extract_all_names(
}
}
} else {
let (elts, new_flags) = extract_elts(ctx, value);
let (elts, new_flags) = extract_elts(checker, value);
flags |= new_flags;
if let Some(elts) = elts {
add_to_names(&mut names, elts, &mut flags);
@@ -348,7 +348,7 @@ pub fn locate_cmpops(contents: &str) -> Vec<LocatedCmpop> {
mod tests {
use rustpython_parser::ast::{Cmpop, Location};
use crate::operations::{locate_cmpops, LocatedCmpop};
use crate::ast::operations::{locate_cmpops, LocatedCmpop};
#[test]
fn locates_cmpops() {

View File

@@ -1,6 +1,6 @@
use rustpython_parser::ast::{Expr, ExprKind, Keyword};
use crate::types::Range;
use crate::ast::types::Range;
fn relocate_keyword(keyword: &mut Keyword, location: Range) {
keyword.location = location.location;

View File

@@ -28,17 +28,13 @@ impl Range {
end_location,
}
}
}
impl<T> From<&Located<T>> for Range {
fn from(located: &Located<T>) -> Self {
pub fn from_located<T, U>(located: &Located<T, U>) -> Self {
Range::new(located.location, located.end_location.unwrap())
}
}
impl<T> From<&Box<Located<T>>> for Range {
fn from(located: &Box<Located<T>>) -> Self {
Range::new(located.location, located.end_location.unwrap())
pub fn contains(&self, other: &Range) -> bool {
self.location <= other.location && self.end_location >= other.end_location
}
}
@@ -128,7 +124,7 @@ impl<'a> Scope<'a> {
// StarImportation
// FutureImportation
#[derive(Clone, Debug, is_macro::Is)]
#[derive(Clone, Debug)]
pub enum BindingKind<'a> {
Annotation,
Argument,

View File

@@ -1,7 +1,7 @@
use ruff_python_stdlib::typing::{PEP_585_BUILTINS_ELIGIBLE, PEP_593_SUBSCRIPTS, SUBSCRIPTS};
use ruff_python::typing::{PEP_585_BUILTINS_ELIGIBLE, PEP_593_SUBSCRIPTS, SUBSCRIPTS};
use rustpython_parser::ast::{Expr, ExprKind};
use crate::types::CallPath;
use crate::ast::types::CallPath;
pub enum Callable {
ForwardRef,

View File

@@ -2,13 +2,13 @@ use std::str::Lines;
use rustpython_parser::ast::{Located, Location};
use crate::ast::types::Range;
use crate::source_code::Locator;
use crate::types::Range;
/// Extract the leading indentation from a line.
pub fn indentation<'a, T>(locator: &'a Locator, located: &'a Located<T>) -> Option<&'a str> {
let range = Range::from(located);
let indentation = locator.slice(Range::new(
let range = Range::from_located(located);
let indentation = locator.slice(&Range::new(
Location::new(range.location.row(), 0),
Location::new(range.location.row(), range.location.column()),
));

View File

@@ -6,14 +6,14 @@ use libcst_native::{
use rustpython_parser::ast::{ExcepthandlerKind, Expr, Keyword, Location, Stmt, StmtKind};
use rustpython_parser::{lexer, Mode, Tok};
use ruff_diagnostics::Fix;
use ruff_python_ast::helpers;
use ruff_python_ast::helpers::to_absolute;
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use ruff_python_ast::whitespace::LinesWithTrailingNewline;
use crate::ast::helpers;
use crate::ast::helpers::to_absolute;
use crate::ast::types::Range;
use crate::ast::whitespace::LinesWithTrailingNewline;
use crate::cst::helpers::compose_module_path;
use crate::cst::matchers::match_module;
use crate::fix::Fix;
use crate::source_code::{Indexer, Locator, Stylist};
/// Determine if a body contains only a single statement, taking into account
/// deleted.
@@ -227,7 +227,7 @@ pub fn remove_unused_imports<'a>(
indexer: &Indexer,
stylist: &Stylist,
) -> Result<Fix> {
let module_text = locator.slice(stmt);
let module_text = locator.slice(&Range::from_located(stmt));
let mut tree = match_module(module_text)?;
let Some(Statement::Simple(body)) = tree.body.first_mut() else {
@@ -450,9 +450,8 @@ mod tests {
use rustpython_parser as parser;
use rustpython_parser::ast::Location;
use ruff_python_ast::source_code::Locator;
use crate::autofix::helpers::{next_stmt_break, trailing_semicolon};
use crate::source_code::Locator;
#[test]
fn find_semicolon() -> Result<()> {

View File

@@ -4,12 +4,11 @@ use itertools::Itertools;
use rustc_hash::FxHashMap;
use rustpython_parser::ast::Location;
use ruff_diagnostics::{Diagnostic, Fix};
use ruff_python_ast::source_code::Locator;
use ruff_python_ast::types::Range;
use crate::ast::types::Range;
use crate::fix::Fix;
use crate::linter::FixTable;
use crate::registry::AsRule;
use crate::registry::Diagnostic;
use crate::source_code::Locator;
pub mod helpers;
@@ -55,7 +54,7 @@ fn apply_fixes<'a>(
}
// Add all contents from `last_pos` to `fix.location`.
let slice = locator.slice(Range::new(last_pos, fix.location));
let slice = locator.slice(&Range::new(last_pos, fix.location));
output.push_str(slice);
// Add the patch itself.
@@ -79,7 +78,7 @@ pub(crate) fn apply_fix(fix: &Fix, locator: &Locator) -> String {
let mut output = String::with_capacity(locator.len());
// Add all contents from `last_pos` to `fix.location`.
let slice = locator.slice(Range::new(Location::new(1, 0), fix.location));
let slice = locator.slice(&Range::new(Location::new(1, 0), fix.location));
output.push_str(slice);
// Add the patch itself.
@@ -96,38 +95,43 @@ pub(crate) fn apply_fix(fix: &Fix, locator: &Locator) -> String {
mod tests {
use rustpython_parser::ast::Location;
use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::Fix;
use ruff_python_ast::source_code::Locator;
use crate::autofix::{apply_fix, apply_fixes};
use crate::fix::Fix;
use crate::registry::Diagnostic;
use crate::rules::pycodestyle::rules::NoNewLineAtEndOfFile;
fn create_diagnostics(fixes: impl IntoIterator<Item = Fix>) -> Vec<Diagnostic> {
fixes
.into_iter()
.map(|fix| Diagnostic {
use crate::source_code::Locator;
#[test]
fn empty_file() {
let fixes: Vec<Diagnostic> = vec![];
let locator = Locator::new(r#""#);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(contents, "");
assert_eq!(fixed.values().sum::<usize>(), 0);
}
impl From<Fix> for Diagnostic {
fn from(fix: Fix) -> Self {
Diagnostic {
// The choice of rule here is arbitrary.
kind: NoNewLineAtEndOfFile.into(),
location: fix.location,
end_location: fix.end_location,
fix: Some(fix),
parent: None,
})
.collect()
}
#[test]
fn empty_file() {
let locator = Locator::new(r#""#);
let diagnostics = create_diagnostics([]);
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
assert_eq!(contents, "");
assert_eq!(fixed.values().sum::<usize>(), 0);
}
}
}
#[test]
fn apply_one_replacement() {
let fixes: Vec<Diagnostic> = vec![Fix {
content: "Bar".to_string(),
location: Location::new(1, 8),
end_location: Location::new(1, 14),
}
.into()];
let locator = Locator::new(
r#"
class A(object):
@@ -135,12 +139,7 @@ class A(object):
"#
.trim(),
);
let diagnostics = create_diagnostics([Fix {
content: "Bar".to_string(),
location: Location::new(1, 8),
end_location: Location::new(1, 14),
}]);
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
@@ -154,6 +153,12 @@ class A(Bar):
#[test]
fn apply_one_removal() {
let fixes: Vec<Diagnostic> = vec![Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
}
.into()];
let locator = Locator::new(
r#"
class A(object):
@@ -161,12 +166,7 @@ class A(object):
"#
.trim(),
);
let diagnostics = create_diagnostics([Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
}]);
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
@@ -180,6 +180,20 @@ class A:
#[test]
fn apply_two_removals() {
let fixes: Vec<Diagnostic> = vec![
Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 16),
}
.into(),
Fix {
content: String::new(),
location: Location::new(1, 16),
end_location: Location::new(1, 23),
}
.into(),
];
let locator = Locator::new(
r#"
class A(object, object):
@@ -187,19 +201,7 @@ class A(object, object):
"#
.trim(),
);
let diagnostics = create_diagnostics([
Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 16),
},
Fix {
content: String::new(),
location: Location::new(1, 16),
end_location: Location::new(1, 23),
},
]);
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
@@ -214,6 +216,20 @@ class A:
#[test]
fn ignore_overlapping_fixes() {
let fixes: Vec<Diagnostic> = vec![
Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
}
.into(),
Fix {
content: "ignored".to_string(),
location: Location::new(1, 9),
end_location: Location::new(1, 11),
}
.into(),
];
let locator = Locator::new(
r#"
class A(object):
@@ -221,19 +237,7 @@ class A(object):
"#
.trim(),
);
let diagnostics = create_diagnostics([
Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
},
Fix {
content: "ignored".to_string(),
location: Location::new(1, 9),
end_location: Location::new(1, 11),
},
]);
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"

View File

@@ -1,9 +1,3 @@
mod cache_key;
pub mod filetime;
pub mod globset;
pub use cache_key::{CacheKey, CacheKeyHasher};
use std::path::{Path, PathBuf};
pub const CACHE_DIR_NAME: &str = ".ruff_cache";

View File

@@ -1,24 +0,0 @@
use rustpython_parser::ast::{Expr, Stmt};
use ruff_python_ast::types::Range;
use ruff_python_ast::types::RefEquality;
use ruff_python_ast::visibility::{Visibility, VisibleScope};
use crate::checkers::ast::AnnotationContext;
use crate::docstrings::definition::Definition;
type Context<'a> = (Vec<usize>, Vec<RefEquality<'a, Stmt>>);
/// A collection of AST nodes that are deferred for later analysis.
/// Used to, e.g., store functions, whose bodies shouldn't be analyzed until all
/// module-level definitions have been analyzed.
#[derive(Default)]
pub struct Deferred<'a> {
pub definitions: Vec<(Definition<'a>, Visibility, Context<'a>)>,
pub string_type_definitions: Vec<(Range, &'a str, AnnotationContext, Context<'a>)>,
pub type_definitions: Vec<(&'a Expr, AnnotationContext, Context<'a>)>,
pub functions: Vec<(&'a Stmt, Context<'a>, VisibleScope)>,
pub lambdas: Vec<(&'a Expr, Context<'a>)>,
pub for_loops: Vec<(&'a Stmt, Context<'a>)>,
pub assignments: Vec<Context<'a>>,
}

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use ruff_diagnostics::Diagnostic;
use crate::registry::Rule;
use crate::registry::{Diagnostic, Rule};
use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
use crate::rules::pep8_naming::rules::invalid_module_name;
use crate::settings::Settings;

View File

@@ -4,15 +4,13 @@ use std::path::Path;
use rustpython_parser::ast::Suite;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use ruff_python_ast::visitor::Visitor;
use crate::ast::visitor::Visitor;
use crate::directives::IsortDirectives;
use crate::registry::Rule;
use crate::registry::{Diagnostic, Rule};
use crate::rules::isort;
use crate::rules::isort::track::{Block, ImportTracker};
use crate::settings::{flags, Settings};
use crate::source_code::{Indexer, Locator, Stylist};
#[allow(clippy::too_many_arguments)]
pub fn check_imports(

View File

@@ -1,23 +1,17 @@
#![allow(dead_code, unused_imports, unused_variables)]
use bisection::bisect_left;
use itertools::Itertools;
use rustpython_parser::ast::Location;
use rustpython_parser::lexer::LexResult;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::{Locator, Stylist};
use ruff_python_ast::types::Range;
use crate::registry::{AsRule, Rule};
use crate::ast::types::Range;
use crate::registry::Diagnostic;
use crate::rules::pycodestyle::logical_lines::{iter_logical_lines, TokenFlags};
use crate::rules::pycodestyle::rules::{
extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword,
missing_whitespace_around_operator, space_around_operator, whitespace_around_keywords,
whitespace_around_named_parameter_equals, whitespace_before_comment,
whitespace_before_parameters,
extraneous_whitespace, indentation, space_around_operator, whitespace_around_keywords,
whitespace_before_comment,
};
use crate::settings::{flags, Settings};
use crate::settings::Settings;
use crate::source_code::{Locator, Stylist};
/// Return the amount of indentation, expanding tabs to the next multiple of 8.
fn expand_indent(mut line: &str) -> usize {
@@ -45,7 +39,6 @@ pub fn check_logical_lines(
locator: &Locator,
stylist: &Stylist,
settings: &Settings,
autofix: flags::Autofix,
) -> Vec<Diagnostic> {
let mut diagnostics = vec![];
@@ -59,7 +52,7 @@ pub fn check_logical_lines(
// Extract the indentation level.
let start_loc = line.mapping[0].1;
let start_line = locator.slice(Range::new(Location::new(start_loc.row(), 0), start_loc));
let start_line = locator.slice(&Range::new(Location::new(start_loc.row(), 0), start_loc));
let indent_level = expand_indent(start_line);
let indent_size = 4;
@@ -113,18 +106,6 @@ pub fn check_logical_lines(
});
}
}
for (location, kind) in missing_whitespace_after_keyword(&line.tokens) {
if settings.rules.enabled(kind.rule()) {
diagnostics.push(Diagnostic {
kind,
location,
end_location: location,
fix: None,
parent: None,
});
}
}
}
if line.flags.contains(TokenFlags::COMMENT) {
for (range, kind) in whitespace_before_comment(&line.tokens, locator) {
@@ -139,59 +120,6 @@ pub fn check_logical_lines(
}
}
}
if line.flags.contains(TokenFlags::OPERATOR) {
for (location, kind) in
whitespace_around_named_parameter_equals(&line.tokens, &line.text)
{
if settings.rules.enabled(kind.rule()) {
diagnostics.push(Diagnostic {
kind,
location,
end_location: location,
fix: None,
parent: None,
});
}
}
for (location, kind) in missing_whitespace_around_operator(&line.tokens) {
if settings.rules.enabled(kind.rule()) {
diagnostics.push(Diagnostic {
kind,
location,
end_location: location,
fix: None,
parent: None,
});
}
}
#[cfg(feature = "logical_lines")]
let should_fix = autofix.into() && settings.rules.should_fix(&Rule::MissingWhitespace);
#[cfg(not(feature = "logical_lines"))]
let should_fix = false;
for diagnostic in missing_whitespace(&line.text, start_loc.row(), should_fix) {
if settings.rules.enabled(diagnostic.kind.rule()) {
diagnostics.push(diagnostic);
}
}
}
if line.flags.contains(TokenFlags::BRACKET) {
#[cfg(feature = "logical_lines")]
let should_fix =
autofix.into() && settings.rules.should_fix(&Rule::WhitespaceBeforeParameters);
#[cfg(not(feature = "logical_lines"))]
let should_fix = false;
for diagnostic in whitespace_before_parameters(&line.tokens, should_fix) {
if settings.rules.enabled(diagnostic.kind.rule()) {
diagnostics.push(diagnostic);
}
}
}
for (index, kind) in indentation(
&line,
@@ -227,9 +155,8 @@ mod tests {
use rustpython_parser::lexer::LexResult;
use rustpython_parser::{lexer, Mode};
use ruff_python_ast::source_code::Locator;
use crate::checkers::logical_lines::iter_logical_lines;
use crate::source_code::Locator;
#[test]
fn split_logical_lines() {
@@ -306,7 +233,7 @@ f()"#;
.into_iter()
.map(|line| line.text)
.collect();
let expected = vec!["def f():", "\"xxxxxxxxxxxxxxxxxxxx\"", "", "x = 1", "f()"];
let expected = vec!["def f():", "\"xxx\"", "", "x = 1", "f()"];
assert_eq!(actual, expected);
}
}

View File

@@ -4,13 +4,12 @@ use log::warn;
use nohash_hasher::IntMap;
use rustpython_parser::ast::Location;
use ruff_diagnostics::{Diagnostic, Fix};
use ruff_python_ast::types::Range;
use crate::ast::types::Range;
use crate::codes::NoqaCode;
use crate::fix::Fix;
use crate::noqa;
use crate::noqa::{extract_file_exemption, Directive, Exemption};
use crate::registry::{AsRule, Rule};
use crate::registry::{Diagnostic, DiagnosticKind, Rule};
use crate::rule_redirects::get_redirect_target;
use crate::rules::ruff::rules::{UnusedCodes, UnusedNOQA};
use crate::settings::{flags, Settings};
@@ -66,7 +65,7 @@ pub fn check_noqa(
// Remove any ignored diagnostics.
for (index, diagnostic) in diagnostics.iter().enumerate() {
if matches!(diagnostic.kind.rule(), Rule::BlanketNOQA) {
if matches!(diagnostic.kind, DiagnosticKind::BlanketNOQA(..)) {
continue;
}
@@ -149,7 +148,8 @@ pub fn check_noqa(
UnusedNOQA { codes: None },
Range::new(Location::new(row + 1, start), Location::new(row + 1, end)),
);
if autofix.into() && settings.rules.should_fix(diagnostic.kind.rule()) {
if autofix.is_enabled() && settings.rules.should_fix(diagnostic.kind.rule())
{
diagnostic.amend(Fix::deletion(
Location::new(row + 1, start - spaces),
Location::new(row + 1, lines[row].chars().count()),
@@ -216,7 +216,8 @@ pub fn check_noqa(
},
Range::new(Location::new(row + 1, start), Location::new(row + 1, end)),
);
if autofix.into() && settings.rules.should_fix(diagnostic.kind.rule()) {
if autofix.is_enabled() && settings.rules.should_fix(diagnostic.kind.rule())
{
if valid_codes.is_empty() {
diagnostic.amend(Fix::deletion(
Location::new(row + 1, start - spaces),

View File

@@ -2,22 +2,19 @@
use std::path::Path;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::Stylist;
use crate::registry::Rule;
use crate::registry::{Diagnostic, Rule};
use crate::rules::flake8_executable::helpers::{extract_shebang, ShebangDirective};
use crate::rules::flake8_executable::rules::{
shebang_missing, shebang_newline, shebang_not_executable, shebang_python, shebang_whitespace,
};
use crate::rules::pycodestyle::rules::{
doc_line_too_long, indentation_contains_tabs, line_too_long, mixed_spaces_and_tabs,
no_newline_at_end_of_file, trailing_whitespace,
doc_line_too_long, line_too_long, mixed_spaces_and_tabs, no_newline_at_end_of_file,
};
use crate::rules::pygrep_hooks::rules::{blanket_noqa, blanket_type_ignore};
use crate::rules::pylint;
use crate::rules::pyupgrade::rules::unnecessary_coding_comment;
use crate::settings::{flags, Settings};
use crate::source_code::Stylist;
pub fn check_physical_lines(
path: &Path,
@@ -44,15 +41,11 @@ pub fn check_physical_lines(
let enforce_unnecessary_coding_comment = settings.rules.enabled(&Rule::UTF8EncodingDeclaration);
let enforce_mixed_spaces_and_tabs = settings.rules.enabled(&Rule::MixedSpacesAndTabs);
let enforce_bidirectional_unicode = settings.rules.enabled(&Rule::BidirectionalUnicode);
let enforce_trailing_whitespace = settings.rules.enabled(&Rule::TrailingWhitespace);
let enforce_blank_line_contains_whitespace =
settings.rules.enabled(&Rule::BlankLineContainsWhitespace);
let enforce_indentation_contains_tabs = settings.rules.enabled(&Rule::IndentationContainsTabs);
let fix_unnecessary_coding_comment =
autofix.into() && settings.rules.should_fix(&Rule::UTF8EncodingDeclaration);
autofix.is_enabled() && settings.rules.should_fix(&Rule::UTF8EncodingDeclaration);
let fix_shebang_whitespace =
autofix.into() && settings.rules.should_fix(&Rule::ShebangWhitespace);
autofix.is_enabled() && settings.rules.should_fix(&Rule::ShebangWhitespace);
let mut commented_lines_iter = commented_lines.iter().peekable();
let mut doc_lines_iter = doc_lines.iter().peekable();
@@ -146,25 +139,13 @@ pub fn check_physical_lines(
if enforce_bidirectional_unicode {
diagnostics.extend(pylint::rules::bidirectional_unicode(index, line));
}
if enforce_trailing_whitespace || enforce_blank_line_contains_whitespace {
if let Some(diagnostic) = trailing_whitespace(index, line, settings, autofix) {
diagnostics.push(diagnostic);
}
}
if enforce_indentation_contains_tabs {
if let Some(diagnostic) = indentation_contains_tabs(index, line) {
diagnostics.push(diagnostic);
}
}
}
if enforce_no_newline_at_end_of_file {
if let Some(diagnostic) = no_newline_at_end_of_file(
stylist,
contents,
autofix.into() && settings.rules.should_fix(&Rule::NoNewLineAtEndOfFile),
autofix.is_enabled() && settings.rules.should_fix(&Rule::NoNewLineAtEndOfFile),
) {
diagnostics.push(diagnostic);
}
@@ -181,14 +162,13 @@ pub fn check_physical_lines(
#[cfg(test)]
mod tests {
use std::path::Path;
use ruff_python_ast::source_code::{Locator, Stylist};
use super::check_physical_lines;
use crate::registry::Rule;
use crate::settings::{flags, Settings};
use super::check_physical_lines;
use crate::source_code::{Locator, Stylist};
#[test]
fn e501_non_ascii_char() {

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