Compare commits
1 Commits
v0.0.278
...
charlie/do
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fbec8e6a2 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -4,4 +4,3 @@ crates/ruff/resources/test/fixtures/isort/line_ending_crlf.py text eol=crlf
|
||||
crates/ruff/resources/test/fixtures/pycodestyle/W605_1.py text eol=crlf
|
||||
|
||||
ruff.schema.json linguist-generated=true text=auto eol=lf
|
||||
*.md.snap linguist-language=Markdown
|
||||
|
||||
67
.github/workflows/ci.yaml
vendored
67
.github/workflows/ci.yaml
vendored
@@ -16,7 +16,7 @@ env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
PACKAGE_NAME: ruff
|
||||
PYTHON_VERSION: "3.11" # to build abi3 wheels
|
||||
PYTHON_VERSION: "3.7" # to build abi3 wheels
|
||||
|
||||
jobs:
|
||||
cargo-fmt:
|
||||
@@ -31,6 +31,17 @@ jobs:
|
||||
cargo-clippy:
|
||||
name: "cargo clippy"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
run: |
|
||||
rustup component add clippy
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
|
||||
cargo-clippy-wasm:
|
||||
name: "cargo clippy (wasm)"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -38,10 +49,7 @@ jobs:
|
||||
rustup component add clippy
|
||||
rustup target add wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Clippy"
|
||||
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
- name: "Clippy (wasm)"
|
||||
run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features -- -D warnings
|
||||
- run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features -- -D warnings
|
||||
|
||||
cargo-test:
|
||||
strategy:
|
||||
@@ -54,19 +62,21 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
# cargo insta 1.30.0 fails for some reason (https://github.com/mitsuhiko/insta/issues/392)
|
||||
- run: cargo install cargo-insta@=1.29.0
|
||||
- run: cargo install cargo-insta
|
||||
- run: pip install black[d]==23.1.0
|
||||
- name: "Run tests (Ubuntu)"
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: cargo insta test --all --all-features --unreferenced reject
|
||||
run: |
|
||||
cargo insta test --all --all-features --delete-unreferenced-snapshots
|
||||
git diff --exit-code
|
||||
- name: "Run tests (Windows)"
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
shell: bash
|
||||
# We can't reject unreferenced snapshots on windows because flake8_executable can't run on windows
|
||||
run: cargo insta test --all --all-features
|
||||
run: |
|
||||
cargo insta test --all --all-features
|
||||
git diff --exit-code
|
||||
- run: cargo test --package ruff_cli --test black_compatibility_test -- --ignored
|
||||
# TODO: Skipped as it's currently broken. The resource were moved from the
|
||||
# Skipped as it's currently broken. The resource were moved from the
|
||||
# ruff_cli to ruff crate, but this test was not updated.
|
||||
if: false
|
||||
# Check for broken links in the documentation.
|
||||
@@ -142,7 +152,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
python-version: "3.11"
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
name: Download Ruff binary
|
||||
@@ -226,7 +236,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
python-version: "3.11"
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -250,24 +260,13 @@ jobs:
|
||||
docs:
|
||||
name: "mkdocs"
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
- name: "Add SSH key"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
uses: webfactory/ssh-agent@v0.8.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: pip install -r docs/requirements-insiders.txt
|
||||
- name: "Install dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: pip install -r docs/requirements.txt
|
||||
- name: "Update README File"
|
||||
run: python scripts/transform_readme.py --target mkdocs
|
||||
@@ -275,23 +274,5 @@ jobs:
|
||||
run: python scripts/generate_mkdocs.py
|
||||
- name: "Check docs formatting"
|
||||
run: python scripts/check_docs_formatted.py
|
||||
- name: "Build Insiders docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.insiders.yml
|
||||
- name: "Build docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.generated.yml
|
||||
|
||||
check-formatter-stability:
|
||||
name: "Check formatter stability"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Cache rust"
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: "Clone CPython 3.10"
|
||||
run: git clone --branch 3.10 --depth 1 https://github.com/python/cpython.git crates/ruff/resources/test/cpython
|
||||
- name: "Check stability"
|
||||
run: cargo run --bin ruff_dev -- format-dev --stability-check crates/ruff/resources/test/cpython
|
||||
run: mkdocs build --strict
|
||||
|
||||
20
.github/workflows/docs.yaml
vendored
20
.github/workflows/docs.yaml
vendored
@@ -10,34 +10,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
|
||||
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
- name: "Add SSH key"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
uses: webfactory/ssh-agent@v0.8.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: pip install -r docs/requirements-insiders.txt
|
||||
- name: "Install dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: pip install -r docs/requirements.txt
|
||||
run: |
|
||||
pip install -r docs/requirements.txt
|
||||
- name: "Copy README File"
|
||||
run: |
|
||||
python scripts/transform_readme.py --target mkdocs
|
||||
python scripts/generate_mkdocs.py
|
||||
- name: "Build Insiders docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.insiders.yml
|
||||
- name: "Build docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.generated.yml
|
||||
mkdocs build --strict
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
|
||||
2
.github/workflows/flake8-to-ruff.yaml
vendored
2
.github/workflows/flake8-to-ruff.yaml
vendored
@@ -9,7 +9,7 @@ concurrency:
|
||||
env:
|
||||
PACKAGE_NAME: flake8-to-ruff
|
||||
CRATE_NAME: flake8_to_ruff
|
||||
PYTHON_VERSION: "3.11"
|
||||
PYTHON_VERSION: "3.7" # to build abi3 wheels
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
8
.github/workflows/release.yaml
vendored
8
.github/workflows/release.yaml
vendored
@@ -20,7 +20,7 @@ concurrency:
|
||||
|
||||
env:
|
||||
PACKAGE_NAME: ruff
|
||||
PYTHON_VERSION: "3.11"
|
||||
PYTHON_VERSION: "3.7" # to build abi3 wheels
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -491,16 +491,16 @@ jobs:
|
||||
- name: "Publish to GitHub"
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
draft: false
|
||||
files: binaries/*
|
||||
tag_name: v${{ inputs.tag }}
|
||||
|
||||
# After the release has been published, we update downstream repositories
|
||||
# This is separate because if this fails the release is still fine, we just need to do some manual workflow triggers
|
||||
update-dependents:
|
||||
name: Update dependents
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: publish-release
|
||||
needs: release
|
||||
steps:
|
||||
- name: "Update pre-commit mirror"
|
||||
uses: actions/github-script@v6
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,7 +1,8 @@
|
||||
# Benchmarking cpython (CONTRIBUTING.md)
|
||||
crates/ruff/resources/test/cpython
|
||||
# generate_mkdocs.py
|
||||
mkdocs.generated.yml
|
||||
mkdocs.yml
|
||||
.overrides
|
||||
# check_ecosystem.py
|
||||
ruff-old
|
||||
github_search*.jsonl
|
||||
|
||||
@@ -6,9 +6,7 @@ exclude: |
|
||||
crates/ruff/src/rules/.*/snapshots/.*|
|
||||
crates/ruff_cli/resources/.*|
|
||||
crates/ruff_python_formatter/resources/.*|
|
||||
crates/ruff_python_formatter/tests/snapshots/.*|
|
||||
crates/ruff_python_resolver/resources/.*|
|
||||
crates/ruff_python_resolver/tests/snapshots/.*
|
||||
crates/ruff_python_formatter/src/snapshots/.*
|
||||
)$
|
||||
|
||||
repos:
|
||||
|
||||
@@ -1,67 +1,5 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.0.277
|
||||
|
||||
### `.ipynb_checkpoints`, `.pyenv`, `.pytest_cache`, and `.vscode` are now excluded by default ([#5513](https://github.com/astral-sh/ruff/pull/5513))
|
||||
|
||||
Ruff maintains a list of default exclusions, which now consists of the following patterns:
|
||||
|
||||
- `.bzr`
|
||||
- `.direnv`
|
||||
- `.eggs`
|
||||
- `.git`
|
||||
- `.git-rewrite`
|
||||
- `.hg`
|
||||
- `.ipynb_checkpoints`
|
||||
- `.mypy_cache`
|
||||
- `.nox`
|
||||
- `.pants.d`
|
||||
- `.pyenv`
|
||||
- `.pytest_cache`
|
||||
- `.pytype`
|
||||
- `.ruff_cache`
|
||||
- `.svn`
|
||||
- `.tox`
|
||||
- `.venv`
|
||||
- `.vscode`
|
||||
- `__pypackages__`
|
||||
- `_build`
|
||||
- `buck-out`
|
||||
- `build`
|
||||
- `dist`
|
||||
- `node_modules`
|
||||
- `venv`
|
||||
|
||||
Previously, the `.ipynb_checkpoints`, `.pyenv`, `.pytest_cache`, and `.vscode` directories were not
|
||||
excluded by default. This change brings Ruff's default exclusions in line with other tools like
|
||||
Black.
|
||||
|
||||
## 0.0.276
|
||||
|
||||
### The `keep-runtime-typing` setting has been reinstated ([#5470](https://github.com/astral-sh/ruff/pull/5470))
|
||||
|
||||
The `keep-runtime-typing` setting has been reinstated with revised semantics. This setting was
|
||||
removed in [#4427](https://github.com/astral-sh/ruff/pull/4427), as it was equivalent to ignoring
|
||||
the `UP006` and `UP007` rules via Ruff's standard `ignore` mechanism.
|
||||
|
||||
Taking `UP006` (rewrite `List[int]` to `list[int]`) as an example, the setting now behaves as
|
||||
follows:
|
||||
|
||||
- On Python 3.7 and Python 3.8, setting `keep-runtime-typing = true` will cause Ruff to ignore
|
||||
`UP006` violations, even if `from __future__ import annotations` is present in the file.
|
||||
While such annotations are valid in Python 3.7 and Python 3.8 when combined with
|
||||
`from __future__ import annotations`, they aren't supported by libraries like Pydantic and
|
||||
FastAPI, which rely on runtime type checking.
|
||||
- On Python 3.9 and above, the setting has no effect, as `list[int]` is a valid type annotation,
|
||||
and libraries like Pydantic and FastAPI support it without issue.
|
||||
|
||||
In short: `keep-runtime-typing` can be used to ensure that Ruff doesn't introduce type annotations
|
||||
that are not supported at runtime by the current Python version, which are unsupported by libraries
|
||||
like Pydantic and FastAPI.
|
||||
|
||||
Note that this is not a breaking change, but is included here to complement the previous removal
|
||||
of `keep-runtime-typing`.
|
||||
|
||||
## 0.0.268
|
||||
|
||||
### The `keep-runtime-typing` setting has been removed ([#4427](https://github.com/astral-sh/ruff/pull/4427))
|
||||
|
||||
149
CONTRIBUTING.md
149
CONTRIBUTING.md
@@ -256,11 +256,7 @@ To preview any changes to the documentation locally:
|
||||
1. Run the development server with:
|
||||
|
||||
```shell
|
||||
# For contributors.
|
||||
mkdocs serve -f mkdocs.generated.yml
|
||||
|
||||
# For members of the Astral org, which has access to MkDocs Insiders via sponsorship.
|
||||
mkdocs serve -f mkdocs.insiders.yml
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
The documentation should then be available locally at
|
||||
@@ -331,18 +327,22 @@ git clone --branch 3.10 https://github.com/python/cpython.git crates/ruff/resour
|
||||
To benchmark the release build:
|
||||
|
||||
```shell
|
||||
cargo build --release && hyperfine --warmup 10 \
|
||||
"./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache -e" \
|
||||
"./target/release/ruff ./crates/ruff/resources/test/cpython/ -e"
|
||||
cargo build --release && hyperfine --ignore-failure --warmup 10 \
|
||||
"./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache" \
|
||||
"./target/release/ruff ./crates/ruff/resources/test/cpython/"
|
||||
|
||||
Benchmark 1: ./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache
|
||||
Time (mean ± σ): 293.8 ms ± 3.2 ms [User: 2384.6 ms, System: 90.3 ms]
|
||||
Range (min … max): 289.9 ms … 301.6 ms 10 runs
|
||||
|
||||
Warning: Ignoring non-zero exit code.
|
||||
|
||||
Benchmark 2: ./target/release/ruff ./crates/ruff/resources/test/cpython/
|
||||
Time (mean ± σ): 48.0 ms ± 3.1 ms [User: 65.2 ms, System: 124.7 ms]
|
||||
Range (min … max): 45.0 ms … 66.7 ms 62 runs
|
||||
|
||||
Warning: Ignoring non-zero exit code.
|
||||
|
||||
Summary
|
||||
'./target/release/ruff ./crates/ruff/resources/test/cpython/' ran
|
||||
6.12 ± 0.41 times faster than './target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache'
|
||||
@@ -503,7 +503,7 @@ examples.
|
||||
Install `perf` and build `ruff_benchmark` with the `release-debug` profile and then run it with perf
|
||||
|
||||
```shell
|
||||
cargo bench -p ruff_benchmark --no-run --profile=release-debug && perf record --call-graph dwarf -F 9999 cargo bench -p ruff_benchmark --profile=release-debug -- --profile-time=1
|
||||
cargo bench -p ruff_benchmark --no-run --profile=release-debug && perf record -g -F 9999 cargo bench -p ruff_benchmark --profile=release-debug -- --profile-time=1
|
||||
```
|
||||
|
||||
You can also use the `ruff_dev` launcher to run `ruff check` multiple times on a repository to
|
||||
@@ -550,134 +550,3 @@ cargo instruments -t time --bench linter --profile release-debug -p ruff_benchma
|
||||
- You may want to pass an additional filter to run a single test file
|
||||
|
||||
Otherwise, follow the instructions from the linux section.
|
||||
|
||||
## `cargo dev`
|
||||
|
||||
`cargo dev` is a shortcut for `cargo run --package ruff_dev --bin ruff_dev`. You can run some useful
|
||||
utils with it:
|
||||
|
||||
- `cargo dev print-ast <file>`: Print the AST of a python file using the
|
||||
[RustPython parser](https://github.com/astral-sh/RustPython-Parser/tree/main/parser) that is
|
||||
mainly used in Ruff. For `if True: pass # comment`, you can see the syntax tree, the byte offsets
|
||||
for start and stop of each node and also how the `:` token, the comment and whitespace are not
|
||||
represented anymore:
|
||||
|
||||
```text
|
||||
[
|
||||
If(
|
||||
StmtIf {
|
||||
range: 0..13,
|
||||
test: Constant(
|
||||
ExprConstant {
|
||||
range: 3..7,
|
||||
value: Bool(
|
||||
true,
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
),
|
||||
body: [
|
||||
Pass(
|
||||
StmtPass {
|
||||
range: 9..13,
|
||||
},
|
||||
),
|
||||
],
|
||||
orelse: [],
|
||||
},
|
||||
),
|
||||
]
|
||||
```
|
||||
|
||||
- `cargo dev print-tokens <file>`: Print the tokens that the AST is built upon. Again for
|
||||
`if True: pass # comment`:
|
||||
|
||||
```text
|
||||
0 If 2
|
||||
3 True 7
|
||||
7 Colon 8
|
||||
9 Pass 13
|
||||
14 Comment(
|
||||
"# comment",
|
||||
) 23
|
||||
23 Newline 24
|
||||
```
|
||||
|
||||
- `cargo dev print-cst <file>`: Print the CST of a python file using
|
||||
[LibCST](https://github.com/Instagram/LibCST), which is used in addition to the RustPython parser
|
||||
in Ruff. E.g. for `if True: pass # comment` everything including the whitespace is represented:
|
||||
|
||||
```text
|
||||
Module {
|
||||
body: [
|
||||
Compound(
|
||||
If(
|
||||
If {
|
||||
test: Name(
|
||||
Name {
|
||||
value: "True",
|
||||
lpar: [],
|
||||
rpar: [],
|
||||
},
|
||||
),
|
||||
body: SimpleStatementSuite(
|
||||
SimpleStatementSuite {
|
||||
body: [
|
||||
Pass(
|
||||
Pass {
|
||||
semicolon: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
leading_whitespace: SimpleWhitespace(
|
||||
" ",
|
||||
),
|
||||
trailing_whitespace: TrailingWhitespace {
|
||||
whitespace: SimpleWhitespace(
|
||||
" ",
|
||||
),
|
||||
comment: Some(
|
||||
Comment(
|
||||
"# comment",
|
||||
),
|
||||
),
|
||||
newline: Newline(
|
||||
None,
|
||||
Real,
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
orelse: None,
|
||||
leading_lines: [],
|
||||
whitespace_before_test: SimpleWhitespace(
|
||||
" ",
|
||||
),
|
||||
whitespace_after_test: SimpleWhitespace(
|
||||
"",
|
||||
),
|
||||
is_elif: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
header: [],
|
||||
footer: [],
|
||||
default_indent: " ",
|
||||
default_newline: "\n",
|
||||
has_trailing_newline: true,
|
||||
encoding: "utf-8",
|
||||
}
|
||||
```
|
||||
|
||||
- `cargo dev generate-all`: Update `ruff.schema.json`, `docs/configuration.md` and `docs/rules`.
|
||||
You can also set `RUFF_UPDATE_SCHEMA=1` to update `ruff.schema.json` during `cargo test`.
|
||||
- `cargo dev generate-cli-help`, `cargo dev generate-docs` and `cargo dev generate-json-schema`:
|
||||
Update just `docs/configuration.md`, `docs/rules` and `ruff.schema.json` respectively.
|
||||
- `cargo dev generate-options`: Generate a markdown-compatible table of all `pyproject.toml`
|
||||
options. Used for <https://beta.ruff.rs/docs/settings/>
|
||||
- `cargo dev generate-rules-table`: Generate a markdown-compatible table of all rules. Used for <https://beta.ruff.rs/docs/rules/>
|
||||
- `cargo dev round-trip <python file or jupyter notebook>`: Read a Python file or Jupyter Notebook,
|
||||
parse it, serialize the parsed representation and write it back. Used to check how good our
|
||||
representation is so that fixes don't rewrite irrelevant parts of a file.
|
||||
- `cargo dev format_dev`: See ruff_python_formatter README.md
|
||||
|
||||
622
Cargo.lock
generated
622
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
26
Cargo.toml
26
Cargo.toml
@@ -21,7 +21,7 @@ filetime = { version = "0.2.20" }
|
||||
glob = { version = "0.3.1" }
|
||||
globset = { version = "0.4.10" }
|
||||
ignore = { version = "0.4.20" }
|
||||
insta = { version = "1.30.0" }
|
||||
insta = { version = "1.28.0" }
|
||||
is-macro = { version = "0.2.2" }
|
||||
itertools = { version = "0.10.5" }
|
||||
log = { version = "0.4.17" }
|
||||
@@ -45,20 +45,20 @@ strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
syn = { version = "2.0.15" }
|
||||
test-case = { version = "3.0.0" }
|
||||
thiserror = { version = "1.0.43" }
|
||||
toml = { version = "0.7.2" }
|
||||
|
||||
# v1.0.1
|
||||
libcst = { git = "https://github.com/Instagram/LibCST.git", rev = "3cacca1a1029f05707e50703b49fe3dd860aa839", default-features = false }
|
||||
|
||||
# Please tag the RustPython version every time you update its revision here and in fuzz/Cargo.toml
|
||||
# Tagging the version ensures that older ruff versions continue to build from source even when we rebase our RustPython fork.
|
||||
# Current tag: v0.0.7
|
||||
ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "c174bbf1f29527edd43d432326327f16f47ab9e0" }
|
||||
rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "c174bbf1f29527edd43d432326327f16f47ab9e0" , default-features = false, features = ["num-bigint"]}
|
||||
rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "c174bbf1f29527edd43d432326327f16f47ab9e0", default-features = false, features = ["num-bigint"] }
|
||||
rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "c174bbf1f29527edd43d432326327f16f47ab9e0", default-features = false }
|
||||
rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "c174bbf1f29527edd43d432326327f16f47ab9e0" , default-features = false, features = ["full-lexer", "num-bigint"] }
|
||||
# v0.0.1
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "80e4c1399f95e5beb532fdd1e209ad2dbb470438" }
|
||||
# v0.0.3
|
||||
ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "08ebbe40d7776cac6e3ba66277d435056f2b8dca" }
|
||||
# v0.0.3
|
||||
rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "08ebbe40d7776cac6e3ba66277d435056f2b8dca" , default-features = false, features = ["all-nodes-with-ranges", "num-bigint"]}
|
||||
# v0.0.3
|
||||
rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "08ebbe40d7776cac6e3ba66277d435056f2b8dca", default-features = false, features = ["num-bigint"] }
|
||||
# v0.0.3
|
||||
rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "08ebbe40d7776cac6e3ba66277d435056f2b8dca", default-features = false }
|
||||
# v0.0.3
|
||||
rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "08ebbe40d7776cac6e3ba66277d435056f2b8dca" , default-features = false, features = ["full-lexer", "all-nodes-with-ranges", "num-bigint"] }
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
|
||||
26
LICENSE
26
LICENSE
@@ -1224,32 +1224,6 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- Pyright, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Pyright - A static type checker for the Python language
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
"""
|
||||
|
||||
- rust-analyzer/text-size, licensed under the MIT license:
|
||||
"""
|
||||
Permission is hereby granted, free of charge, to any
|
||||
|
||||
21
README.md
21
README.md
@@ -34,8 +34,7 @@ An extremely fast Python linter, written in Rust.
|
||||
- ⚖️ [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](https://beta.ruff.rs/docs/editor-integrations/) for
|
||||
[VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://github.com/astral-sh/ruff-lsp)
|
||||
- ⌨️ First-party editor integrations for [VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://github.com/astral-sh/ruff-lsp)
|
||||
- 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](https://beta.ruff.rs/docs/configuration/#pyprojecttoml-discovery)
|
||||
|
||||
Ruff aims to be orders of magnitude faster than alternative tools while integrating more
|
||||
@@ -140,7 +139,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.0.278
|
||||
rev: v0.0.274
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -331,11 +330,9 @@ We're grateful to the maintainers of these tools for their work, and for all
|
||||
the value they've provided to the Python community.
|
||||
|
||||
Ruff's autoformatter is built on a fork of Rome's [`rome_formatter`](https://github.com/rome/tools/tree/main/crates/rome_formatter),
|
||||
and again draws on both API and implementation details from [Rome](https://github.com/rome/tools),
|
||||
and again draws on both the APIs and implementation details of [Rome](https://github.com/rome/tools),
|
||||
[Prettier](https://github.com/prettier/prettier), and [Black](https://github.com/psf/black).
|
||||
|
||||
Ruff's import resolver is based on the import resolution algorithm from [Pyright](https://github.com/microsoft/pyright).
|
||||
|
||||
Ruff is also influenced by a number of tools outside the Python ecosystem, like
|
||||
[Clippy](https://github.com/rust-lang/rust-clippy) and [ESLint](https://github.com/eslint/eslint).
|
||||
|
||||
@@ -348,7 +345,6 @@ Ruff is released under the MIT license.
|
||||
Ruff is used by a number of major open-source projects and companies, including:
|
||||
|
||||
- Amazon ([AWS SAM](https://github.com/aws/serverless-application-model))
|
||||
- Anthropic ([Python SDK](https://github.com/anthropics/anthropic-sdk-python))
|
||||
- [Apache Airflow](https://github.com/apache/airflow)
|
||||
- AstraZeneca ([Magnus](https://github.com/AstraZeneca/magnus-core))
|
||||
- Benchling ([Refac](https://github.com/benchling/refac))
|
||||
@@ -358,30 +354,26 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [DVC](https://github.com/iterative/dvc)
|
||||
- [Dagger](https://github.com/dagger/dagger)
|
||||
- [Dagster](https://github.com/dagster-io/dagster)
|
||||
- Databricks ([MLflow](https://github.com/mlflow/mlflow))
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Gradio](https://github.com/gradio-app/gradio)
|
||||
- [Great Expectations](https://github.com/great-expectations/great_expectations)
|
||||
- [HTTPX](https://github.com/encode/httpx)
|
||||
- Hugging Face ([Transformers](https://github.com/huggingface/transformers),
|
||||
[Datasets](https://github.com/huggingface/datasets),
|
||||
[Diffusers](https://github.com/huggingface/diffusers))
|
||||
- [Hatch](https://github.com/pypa/hatch)
|
||||
- [Home Assistant](https://github.com/home-assistant/core)
|
||||
- ING Bank ([popmon](https://github.com/ing-bank/popmon), [probatus](https://github.com/ing-bank/probatus))
|
||||
- [Ibis](https://github.com/ibis-project/ibis)
|
||||
- [Jupyter](https://github.com/jupyter-server/jupyter_server)
|
||||
- [LangChain](https://github.com/hwchase17/langchain)
|
||||
- [LlamaIndex](https://github.com/jerryjliu/llama_index)
|
||||
- Matrix ([Synapse](https://github.com/matrix-org/synapse))
|
||||
- [MegaLinter](https://github.com/oxsecurity/megalinter)
|
||||
- Meltano ([Meltano CLI](https://github.com/meltano/meltano), [Singer SDK](https://github.com/meltano/sdk))
|
||||
- Modern Treasury ([Python SDK](https://github.com/Modern-Treasury/modern-treasury-python-sdk))
|
||||
- Mozilla ([Firefox](https://github.com/mozilla/gecko-dev))
|
||||
- [MegaLinter](https://github.com/oxsecurity/megalinter)
|
||||
- Microsoft ([Semantic Kernel](https://github.com/microsoft/semantic-kernel),
|
||||
[ONNX Runtime](https://github.com/microsoft/onnxruntime),
|
||||
[LightGBM](https://github.com/microsoft/LightGBM))
|
||||
- Modern Treasury ([Python SDK](https://github.com/Modern-Treasury/modern-treasury-python-sdk))
|
||||
- Mozilla ([Firefox](https://github.com/mozilla/gecko-dev))
|
||||
- [Mypy](https://github.com/python/mypy)
|
||||
- Netflix ([Dispatch](https://github.com/Netflix/dispatch))
|
||||
- [Neon](https://github.com/neondatabase/neon)
|
||||
- [ONNX](https://github.com/onnx/onnx)
|
||||
@@ -417,7 +409,6 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [featuretools](https://github.com/alteryx/featuretools)
|
||||
- [meson-python](https://github.com/mesonbuild/meson-python)
|
||||
- [nox](https://github.com/wntrblm/nox)
|
||||
- [pip](https://github.com/pypa/pip)
|
||||
|
||||
### Show Your Support
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
extend-exclude = ["resources", "snapshots"]
|
||||
|
||||
[default.extend-words]
|
||||
trivias = "trivias"
|
||||
hel = "hel"
|
||||
whos = "whos"
|
||||
spawnve = "spawnve"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.278"
|
||||
version = "0.0.274"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.278"
|
||||
version = "0.0.274"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -17,7 +17,6 @@ name = "ruff"
|
||||
[dependencies]
|
||||
ruff_cache = { path = "../ruff_cache" }
|
||||
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
|
||||
ruff_index = { path = "../ruff_index" }
|
||||
ruff_macros = { path = "../ruff_macros" }
|
||||
ruff_python_whitespace = { path = "../ruff_python_whitespace" }
|
||||
ruff_python_ast = { path = "../ruff_python_ast", features = ["serde"] }
|
||||
@@ -43,7 +42,6 @@ is-macro = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
libcst = { workspace = true }
|
||||
log = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
natord = { version = "1.0.9" }
|
||||
nohash-hasher = { workspace = true }
|
||||
num-bigint = { workspace = true }
|
||||
@@ -73,7 +71,7 @@ shellexpand = { workspace = true }
|
||||
smallvec = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
thiserror = { version = "1.0.43" }
|
||||
thiserror = { version = "1.0.38" }
|
||||
toml = { workspace = true }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unicode-width = { version = "0.1.10" }
|
||||
@@ -89,5 +87,4 @@ colored = { workspace = true, features = ["no-color"] }
|
||||
[features]
|
||||
default = []
|
||||
schemars = ["dep:schemars"]
|
||||
# Enables the UnreachableCode rule
|
||||
unreachable-code = []
|
||||
jupyter_notebook = []
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
def func():
|
||||
assert True
|
||||
|
||||
def func():
|
||||
assert False
|
||||
|
||||
def func():
|
||||
assert True, "oops"
|
||||
|
||||
def func():
|
||||
assert False, "oops"
|
||||
@@ -1,41 +0,0 @@
|
||||
def func():
|
||||
async for i in range(5):
|
||||
print(i)
|
||||
|
||||
def func():
|
||||
async for i in range(20):
|
||||
print(i)
|
||||
else:
|
||||
return 0
|
||||
|
||||
def func():
|
||||
async for i in range(10):
|
||||
if i == 5:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def func():
|
||||
async for i in range(111):
|
||||
if i == 5:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
return 2
|
||||
|
||||
def func():
|
||||
async for i in range(12):
|
||||
continue
|
||||
|
||||
def func():
|
||||
async for i in range(1110):
|
||||
if True:
|
||||
continue
|
||||
|
||||
def func():
|
||||
async for i in range(13):
|
||||
break
|
||||
|
||||
def func():
|
||||
async for i in range(1110):
|
||||
if True:
|
||||
break
|
||||
@@ -1,41 +0,0 @@
|
||||
def func():
|
||||
for i in range(5):
|
||||
print(i)
|
||||
|
||||
def func():
|
||||
for i in range(20):
|
||||
print(i)
|
||||
else:
|
||||
return 0
|
||||
|
||||
def func():
|
||||
for i in range(10):
|
||||
if i == 5:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def func():
|
||||
for i in range(111):
|
||||
if i == 5:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
return 2
|
||||
|
||||
def func():
|
||||
for i in range(12):
|
||||
continue
|
||||
|
||||
def func():
|
||||
for i in range(1110):
|
||||
if True:
|
||||
continue
|
||||
|
||||
def func():
|
||||
for i in range(13):
|
||||
break
|
||||
|
||||
def func():
|
||||
for i in range(1110):
|
||||
if True:
|
||||
break
|
||||
@@ -1,108 +0,0 @@
|
||||
def func():
|
||||
if False:
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def func():
|
||||
if True:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def func():
|
||||
if False:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
def func():
|
||||
if True:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def func():
|
||||
if False:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
return "unreachable"
|
||||
|
||||
def func():
|
||||
if True:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
return "unreachable"
|
||||
|
||||
def func():
|
||||
if True:
|
||||
if True:
|
||||
return 1
|
||||
return 2
|
||||
else:
|
||||
return 3
|
||||
return "unreachable2"
|
||||
|
||||
def func():
|
||||
if False:
|
||||
return 0
|
||||
|
||||
def func():
|
||||
if True:
|
||||
return 1
|
||||
|
||||
def func():
|
||||
if True:
|
||||
return 1
|
||||
elif False:
|
||||
return 2
|
||||
else:
|
||||
return 0
|
||||
|
||||
def func():
|
||||
if False:
|
||||
return 1
|
||||
elif True:
|
||||
return 2
|
||||
else:
|
||||
return 0
|
||||
|
||||
def func():
|
||||
if True:
|
||||
if False:
|
||||
return 0
|
||||
elif True:
|
||||
return 1
|
||||
else:
|
||||
return 2
|
||||
return 3
|
||||
elif True:
|
||||
return 4
|
||||
else:
|
||||
return 5
|
||||
return 6
|
||||
|
||||
def func():
|
||||
if False:
|
||||
return "unreached"
|
||||
elif False:
|
||||
return "also unreached"
|
||||
return "reached"
|
||||
|
||||
# Test case found in the Bokeh repository that trigger a false positive.
|
||||
def func(self, obj: BytesRep) -> bytes:
|
||||
data = obj["data"]
|
||||
|
||||
if isinstance(data, str):
|
||||
return base64.b64decode(data)
|
||||
elif isinstance(data, Buffer):
|
||||
buffer = data
|
||||
else:
|
||||
id = data["id"]
|
||||
|
||||
if id in self._buffers:
|
||||
buffer = self._buffers[id]
|
||||
else:
|
||||
self.error(f"can't resolve buffer '{id}'")
|
||||
|
||||
return buffer.data
|
||||
@@ -1,131 +0,0 @@
|
||||
def func(status):
|
||||
match status:
|
||||
case _:
|
||||
return 0
|
||||
return "unreachable"
|
||||
|
||||
def func(status):
|
||||
match status:
|
||||
case 1:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def func(status):
|
||||
match status:
|
||||
case 1:
|
||||
return 1
|
||||
case _:
|
||||
return 0
|
||||
|
||||
def func(status):
|
||||
match status:
|
||||
case 1 | 2 | 3:
|
||||
return 5
|
||||
return 6
|
||||
|
||||
def func(status):
|
||||
match status:
|
||||
case 1 | 2 | 3:
|
||||
return 5
|
||||
case _:
|
||||
return 10
|
||||
return 0
|
||||
|
||||
def func(status):
|
||||
match status:
|
||||
case 0:
|
||||
return 0
|
||||
case 1:
|
||||
return 1
|
||||
case 1:
|
||||
return "1 again"
|
||||
case _:
|
||||
return 3
|
||||
|
||||
def func(status):
|
||||
i = 0
|
||||
match status, i:
|
||||
case _, _:
|
||||
return 0
|
||||
|
||||
def func(status):
|
||||
i = 0
|
||||
match status, i:
|
||||
case _, 0:
|
||||
return 0
|
||||
case _, 2:
|
||||
return 0
|
||||
|
||||
def func(point):
|
||||
match point:
|
||||
case (0, 0):
|
||||
print("Origin")
|
||||
case _:
|
||||
raise ValueError("oops")
|
||||
|
||||
def func(point):
|
||||
match point:
|
||||
case (0, 0):
|
||||
print("Origin")
|
||||
case (0, y):
|
||||
print(f"Y={y}")
|
||||
case (x, 0):
|
||||
print(f"X={x}")
|
||||
case (x, y):
|
||||
print(f"X={x}, Y={y}")
|
||||
case _:
|
||||
raise ValueError("Not a point")
|
||||
|
||||
def where_is(point):
|
||||
class Point:
|
||||
x: int
|
||||
y: int
|
||||
|
||||
match point:
|
||||
case Point(x=0, y=0):
|
||||
print("Origin")
|
||||
case Point(x=0, y=y):
|
||||
print(f"Y={y}")
|
||||
case Point(x=x, y=0):
|
||||
print(f"X={x}")
|
||||
case Point():
|
||||
print("Somewhere else")
|
||||
case _:
|
||||
print("Not a point")
|
||||
|
||||
def func(points):
|
||||
match points:
|
||||
case []:
|
||||
print("No points")
|
||||
case [Point(0, 0)]:
|
||||
print("The origin")
|
||||
case [Point(x, y)]:
|
||||
print(f"Single point {x}, {y}")
|
||||
case [Point(0, y1), Point(0, y2)]:
|
||||
print(f"Two on the Y axis at {y1}, {y2}")
|
||||
case _:
|
||||
print("Something else")
|
||||
|
||||
def func(point):
|
||||
match point:
|
||||
case Point(x, y) if x == y:
|
||||
print(f"Y=X at {x}")
|
||||
case Point(x, y):
|
||||
print(f"Not on the diagonal")
|
||||
|
||||
def func():
|
||||
from enum import Enum
|
||||
class Color(Enum):
|
||||
RED = 'red'
|
||||
GREEN = 'green'
|
||||
BLUE = 'blue'
|
||||
|
||||
color = Color(input("Enter your choice of 'red', 'blue' or 'green': "))
|
||||
|
||||
match color:
|
||||
case Color.RED:
|
||||
print("I see red!")
|
||||
case Color.GREEN:
|
||||
print("Grass is green")
|
||||
case Color.BLUE:
|
||||
print("I'm feeling the blues :(")
|
||||
@@ -1,5 +0,0 @@
|
||||
def func():
|
||||
raise Exception
|
||||
|
||||
def func():
|
||||
raise "a glass!"
|
||||
@@ -1,23 +0,0 @@
|
||||
def func():
|
||||
pass
|
||||
|
||||
def func():
|
||||
pass
|
||||
|
||||
def func():
|
||||
return
|
||||
|
||||
def func():
|
||||
return 1
|
||||
|
||||
def func():
|
||||
return 1
|
||||
return "unreachable"
|
||||
|
||||
def func():
|
||||
i = 0
|
||||
|
||||
def func():
|
||||
i = 0
|
||||
i += 2
|
||||
return i
|
||||
@@ -1,41 +0,0 @@
|
||||
def func():
|
||||
try:
|
||||
...
|
||||
except Exception:
|
||||
...
|
||||
except OtherException as e:
|
||||
...
|
||||
else:
|
||||
...
|
||||
finally:
|
||||
...
|
||||
|
||||
def func():
|
||||
try:
|
||||
...
|
||||
except Exception:
|
||||
...
|
||||
|
||||
def func():
|
||||
try:
|
||||
...
|
||||
except Exception:
|
||||
...
|
||||
except OtherException as e:
|
||||
...
|
||||
|
||||
def func():
|
||||
try:
|
||||
...
|
||||
except Exception:
|
||||
...
|
||||
except OtherException as e:
|
||||
...
|
||||
else:
|
||||
...
|
||||
|
||||
def func():
|
||||
try:
|
||||
...
|
||||
finally:
|
||||
...
|
||||
@@ -1,121 +0,0 @@
|
||||
def func():
|
||||
while False:
|
||||
return "unreachable"
|
||||
return 1
|
||||
|
||||
def func():
|
||||
while False:
|
||||
return "unreachable"
|
||||
else:
|
||||
return 1
|
||||
|
||||
def func():
|
||||
while False:
|
||||
return "unreachable"
|
||||
else:
|
||||
return 1
|
||||
return "also unreachable"
|
||||
|
||||
def func():
|
||||
while True:
|
||||
return 1
|
||||
return "unreachable"
|
||||
|
||||
def func():
|
||||
while True:
|
||||
return 1
|
||||
else:
|
||||
return "unreachable"
|
||||
|
||||
def func():
|
||||
while True:
|
||||
return 1
|
||||
else:
|
||||
return "unreachable"
|
||||
return "also unreachable"
|
||||
|
||||
def func():
|
||||
i = 0
|
||||
while False:
|
||||
i += 1
|
||||
return i
|
||||
|
||||
def func():
|
||||
i = 0
|
||||
while True:
|
||||
i += 1
|
||||
return i
|
||||
|
||||
def func():
|
||||
while True:
|
||||
pass
|
||||
return 1
|
||||
|
||||
def func():
|
||||
i = 0
|
||||
while True:
|
||||
if True:
|
||||
print("ok")
|
||||
i += 1
|
||||
return i
|
||||
|
||||
def func():
|
||||
i = 0
|
||||
while True:
|
||||
if False:
|
||||
print("ok")
|
||||
i += 1
|
||||
return i
|
||||
|
||||
def func():
|
||||
while True:
|
||||
if True:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def func():
|
||||
while True:
|
||||
continue
|
||||
|
||||
def func():
|
||||
while False:
|
||||
continue
|
||||
|
||||
def func():
|
||||
while True:
|
||||
break
|
||||
|
||||
def func():
|
||||
while False:
|
||||
break
|
||||
|
||||
def func():
|
||||
while True:
|
||||
if True:
|
||||
continue
|
||||
|
||||
def func():
|
||||
while True:
|
||||
if True:
|
||||
break
|
||||
|
||||
'''
|
||||
TODO: because `try` statements aren't handled this triggers a false positive as
|
||||
the last statement is reached, but the rules thinks it isn't (it doesn't
|
||||
see/process the break statement).
|
||||
|
||||
# Test case found in the Bokeh repository that trigger a false positive.
|
||||
def bokeh2(self, host: str = DEFAULT_HOST, port: int = DEFAULT_PORT) -> None:
|
||||
self.stop_serving = False
|
||||
while True:
|
||||
try:
|
||||
self.server = HTTPServer((host, port), HtmlOnlyHandler)
|
||||
self.host = host
|
||||
self.port = port
|
||||
break
|
||||
except OSError:
|
||||
log.debug(f"port {port} is in use, trying to next one")
|
||||
port += 1
|
||||
|
||||
self.thread = threading.Thread(target=self._run_web_server)
|
||||
'''
|
||||
@@ -1,12 +0,0 @@
|
||||
import os
|
||||
|
||||
print(eval("1+1")) # S307
|
||||
print(eval("os.getcwd()")) # S307
|
||||
|
||||
|
||||
class Class(object):
|
||||
def eval(self):
|
||||
print("hi")
|
||||
|
||||
def foo(self):
|
||||
self.eval() # OK
|
||||
@@ -23,10 +23,6 @@ class Foobar(unittest.TestCase):
|
||||
with self.assertRaises(Exception):
|
||||
raise Exception("Evil I say!")
|
||||
|
||||
def also_evil_raises(self) -> None:
|
||||
with self.assertRaises(BaseException):
|
||||
raise Exception("Evil I say!")
|
||||
|
||||
def context_manager_raises(self) -> None:
|
||||
with self.assertRaises(Exception) as ex:
|
||||
raise Exception("Context manager is good")
|
||||
@@ -45,9 +41,6 @@ def test_pytest_raises():
|
||||
with pytest.raises(Exception):
|
||||
raise ValueError("Hello")
|
||||
|
||||
with pytest.raises(Exception), pytest.raises(ValueError):
|
||||
raise ValueError("Hello")
|
||||
|
||||
with pytest.raises(Exception, "hello"):
|
||||
raise ValueError("This is fine")
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import re
|
||||
from re import sub
|
||||
|
||||
# B034
|
||||
re.sub("a", "b", "aaa", re.IGNORECASE)
|
||||
re.sub("a", "b", "aaa", 5)
|
||||
re.sub("a", "b", "aaa", 5, re.IGNORECASE)
|
||||
re.subn("a", "b", "aaa", re.IGNORECASE)
|
||||
re.subn("a", "b", "aaa", 5)
|
||||
re.subn("a", "b", "aaa", 5, re.IGNORECASE)
|
||||
re.split(" ", "a a a a", re.I)
|
||||
re.split(" ", "a a a a", 2)
|
||||
re.split(" ", "a a a a", 2, re.I)
|
||||
sub("a", "b", "aaa", re.IGNORECASE)
|
||||
|
||||
# OK
|
||||
re.sub("a", "b", "aaa")
|
||||
re.sub("a", "b", "aaa", flags=re.IGNORECASE)
|
||||
re.sub("a", "b", "aaa", count=5)
|
||||
re.sub("a", "b", "aaa", count=5, flags=re.IGNORECASE)
|
||||
re.subn("a", "b", "aaa")
|
||||
re.subn("a", "b", "aaa", flags=re.IGNORECASE)
|
||||
re.subn("a", "b", "aaa", count=5)
|
||||
re.subn("a", "b", "aaa", count=5, flags=re.IGNORECASE)
|
||||
re.split(" ", "a a a a", flags=re.I)
|
||||
re.split(" ", "a a a a", maxsplit=2)
|
||||
re.split(" ", "a a a a", maxsplit=2, flags=re.I)
|
||||
@@ -25,15 +25,10 @@ map(lambda x=2, y=1: x + y, nums, nums)
|
||||
set(map(lambda x, y: x, nums, nums))
|
||||
|
||||
|
||||
def func(arg1: int, arg2: int = 4):
|
||||
def myfunc(arg1: int, arg2: int = 4):
|
||||
return 2 * arg1 + arg2
|
||||
|
||||
|
||||
# Non-error: `func` is not a lambda.
|
||||
list(map(func, nums))
|
||||
list(map(myfunc, nums))
|
||||
|
||||
# False positive: need to preserve the late-binding of `x` in the inner lambda.
|
||||
map(lambda x: lambda: x, range(4))
|
||||
|
||||
# Error: the `x` is overridden by the inner lambda.
|
||||
map(lambda x: lambda x: x, range(4))
|
||||
[x for x in nums]
|
||||
|
||||
@@ -19,6 +19,3 @@ from datetime import datetime
|
||||
|
||||
# no args unqualified
|
||||
datetime(2000, 1, 1, 0, 0, 0)
|
||||
|
||||
# uses `astimezone` method
|
||||
datetime(2000, 1, 1, 0, 0, 0).astimezone()
|
||||
|
||||
@@ -7,6 +7,3 @@ from datetime import datetime
|
||||
|
||||
# unqualified
|
||||
datetime.today()
|
||||
|
||||
# uses `astimezone` method
|
||||
datetime.today().astimezone()
|
||||
|
||||
@@ -7,6 +7,3 @@ from datetime import datetime
|
||||
|
||||
# unqualified
|
||||
datetime.utcnow()
|
||||
|
||||
# uses `astimezone` method
|
||||
datetime.utcnow().astimezone()
|
||||
|
||||
@@ -7,6 +7,3 @@ from datetime import datetime
|
||||
|
||||
# unqualified
|
||||
datetime.utcfromtimestamp(1234)
|
||||
|
||||
# uses `astimezone` method
|
||||
datetime.utcfromtimestamp(1234).astimezone()
|
||||
|
||||
@@ -16,6 +16,3 @@ from datetime import datetime
|
||||
|
||||
# no args unqualified
|
||||
datetime.now()
|
||||
|
||||
# uses `astimezone` method
|
||||
datetime.now().astimezone()
|
||||
|
||||
@@ -16,6 +16,3 @@ from datetime import datetime
|
||||
|
||||
# no args unqualified
|
||||
datetime.fromtimestamp(1234)
|
||||
|
||||
# uses `astimezone` method
|
||||
datetime.fromtimestamp(1234).astimezone()
|
||||
|
||||
@@ -111,19 +111,3 @@ class PerfectlyFine(models.Model):
|
||||
@property
|
||||
def random_property(self):
|
||||
return "%s" % self
|
||||
|
||||
|
||||
class MultipleConsecutiveFields(models.Model):
|
||||
"""Model that contains multiple out-of-order field definitions in a row."""
|
||||
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
|
||||
first_name = models.CharField(max_length=32)
|
||||
last_name = models.CharField(max_length=32)
|
||||
|
||||
def get_absolute_url(self):
|
||||
pass
|
||||
|
||||
middle_name = models.CharField(max_length=32)
|
||||
|
||||
@@ -5,18 +5,15 @@ import matplotlib.pyplot # unconventional
|
||||
import numpy # unconventional
|
||||
import pandas # unconventional
|
||||
import seaborn # unconventional
|
||||
import tkinter # unconventional
|
||||
|
||||
import altair as altr # unconventional
|
||||
import matplotlib.pyplot as plot # unconventional
|
||||
import numpy as nmp # unconventional
|
||||
import pandas as pdas # unconventional
|
||||
import seaborn as sbrn # unconventional
|
||||
import tkinter as tkr # unconventional
|
||||
|
||||
import altair as alt # conventional
|
||||
import matplotlib.pyplot as plt # conventional
|
||||
import numpy as np # conventional
|
||||
import pandas as pd # conventional
|
||||
import seaborn as sns # conventional
|
||||
import tkinter as tk # conventional
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import sys
|
||||
|
||||
if sys.version == 'Python 2.7.10': ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
if 'linux' == sys.platform: ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
if hasattr(sys, 'maxint'): ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
if sys.maxsize == 42: ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
@@ -1,6 +0,0 @@
|
||||
import sys
|
||||
|
||||
if sys.version == 'Python 2.7.10': ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
if 'linux' == sys.platform: ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
if hasattr(sys, 'maxint'): ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
if sys.maxsize == 42: ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
@@ -1,31 +0,0 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] == 2: ...
|
||||
if sys.version_info[0] == True: ... # Y003 Unrecognized sys.version_info check # E712 comparison to True should be 'if cond is True:' or 'if cond:'
|
||||
if sys.version_info[0.0] == 2: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[False] == 2: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[0j] == 2: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[0] == (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[0] == '2': ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[1:] >= (7, 11): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[::-1] < (11, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:3] >= (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:True] >= (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:1] == (2,): ...
|
||||
if sys.version_info[:1] == (True,): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:1] == (2, 7): ... # Y005 Version comparison must be against a length-1 tuple
|
||||
if sys.version_info[:2] == (2, 7): ...
|
||||
if sys.version_info[:2] == (2,): ... # Y005 Version comparison must be against a length-2 tuple
|
||||
if sys.version_info[:2] == "lol": ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:2.0] >= (3, 9): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:2j] >= (3, 9): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:, :] >= (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info < [3, 0]: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info < ('3', '0'): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info >= (3, 4, 3): ... # Y004 Version comparison must use only major and minor version
|
||||
if sys.version_info == (3, 4): ... # Y006 Use only < and >= for version comparisons
|
||||
if sys.version_info > (3, 0): ... # Y006 Use only < and >= for version comparisons
|
||||
if sys.version_info <= (3, 0): ... # Y006 Use only < and >= for version comparisons
|
||||
if sys.version_info < (3, 5): ...
|
||||
if sys.version_info >= (3, 5): ...
|
||||
if (2, 7) <= sys.version_info < (3, 5): ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
@@ -1,31 +0,0 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] == 2: ...
|
||||
if sys.version_info[0] == True: ... # Y003 Unrecognized sys.version_info check # E712 comparison to True should be 'if cond is True:' or 'if cond:'
|
||||
if sys.version_info[0.0] == 2: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[False] == 2: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[0j] == 2: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[0] == (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[0] == '2': ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[1:] >= (7, 11): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[::-1] < (11, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:3] >= (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:True] >= (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:1] == (2,): ...
|
||||
if sys.version_info[:1] == (True,): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:1] == (2, 7): ... # Y005 Version comparison must be against a length-1 tuple
|
||||
if sys.version_info[:2] == (2, 7): ...
|
||||
if sys.version_info[:2] == (2,): ... # Y005 Version comparison must be against a length-2 tuple
|
||||
if sys.version_info[:2] == "lol": ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:2.0] >= (3, 9): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:2j] >= (3, 9): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:, :] >= (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info < [3, 0]: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info < ('3', '0'): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info >= (3, 4, 3): ... # Y004 Version comparison must use only major and minor version
|
||||
if sys.version_info == (3, 4): ... # Y006 Use only < and >= for version comparisons
|
||||
if sys.version_info > (3, 0): ... # Y006 Use only < and >= for version comparisons
|
||||
if sys.version_info <= (3, 0): ... # Y006 Use only < and >= for version comparisons
|
||||
if sys.version_info < (3, 5): ...
|
||||
if sys.version_info >= (3, 5): ...
|
||||
if (2, 7) <= sys.version_info < (3, 5): ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
@@ -1,15 +0,0 @@
|
||||
import sys
|
||||
from sys import version_info
|
||||
|
||||
if sys.version_info >= (3, 4, 3): ... # PYI004
|
||||
if sys.version_info < (3, 4, 3): ... # PYI004
|
||||
if sys.version_info == (3, 4, 3): ... # PYI004
|
||||
if sys.version_info != (3, 4, 3): ... # PYI004
|
||||
|
||||
if sys.version_info[0] == 2: ...
|
||||
if version_info[0] == 2: ...
|
||||
if sys.version_info < (3, 5): ...
|
||||
if version_info >= (3, 5): ...
|
||||
if sys.version_info[:2] == (2, 7): ...
|
||||
if sys.version_info[:1] == (2,): ...
|
||||
if sys.platform == 'linux': ...
|
||||
@@ -1,15 +0,0 @@
|
||||
import sys
|
||||
from sys import version_info
|
||||
|
||||
if sys.version_info >= (3, 4, 3): ... # PYI004
|
||||
if sys.version_info < (3, 4, 3): ... # PYI004
|
||||
if sys.version_info == (3, 4, 3): ... # PYI004
|
||||
if sys.version_info != (3, 4, 3): ... # PYI004
|
||||
|
||||
if sys.version_info[0] == 2: ...
|
||||
if version_info[0] == 2: ...
|
||||
if sys.version_info < (3, 5): ...
|
||||
if version_info >= (3, 5): ...
|
||||
if sys.version_info[:2] == (2, 7): ...
|
||||
if sys.version_info[:1] == (2,): ...
|
||||
if sys.platform == 'linux': ...
|
||||
@@ -1,14 +0,0 @@
|
||||
import sys
|
||||
from sys import platform, version_info
|
||||
|
||||
if sys.version_info[:1] == (2, 7): ... # Y005
|
||||
if sys.version_info[:2] == (2,): ... # Y005
|
||||
|
||||
|
||||
if sys.version_info[0] == 2: ...
|
||||
if version_info[0] == 2: ...
|
||||
if sys.version_info < (3, 5): ...
|
||||
if version_info >= (3, 5): ...
|
||||
if sys.version_info[:2] == (2, 7): ...
|
||||
if sys.version_info[:1] == (2,): ...
|
||||
if platform == 'linux': ...
|
||||
@@ -1,14 +0,0 @@
|
||||
import sys
|
||||
from sys import platform, version_info
|
||||
|
||||
if sys.version_info[:1] == (2, 7): ... # Y005
|
||||
if sys.version_info[:2] == (2,): ... # Y005
|
||||
|
||||
|
||||
if sys.version_info[0] == 2: ...
|
||||
if version_info[0] == 2: ...
|
||||
if sys.version_info < (3, 5): ...
|
||||
if version_info >= (3, 5): ...
|
||||
if sys.version_info[:2] == (2, 7): ...
|
||||
if sys.version_info[:1] == (2,): ...
|
||||
if platform == 'linux': ...
|
||||
@@ -91,4 +91,3 @@ field27 = list[str]
|
||||
field28 = builtins.str
|
||||
field29 = str
|
||||
field30 = str | bytes | None
|
||||
field31: typing.Final = field30
|
||||
|
||||
@@ -98,4 +98,3 @@ field27 = list[str]
|
||||
field28 = builtins.str
|
||||
field29 = str
|
||||
field30 = str | bytes | None
|
||||
field31: typing.Final = field30
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import typing
|
||||
|
||||
# Shouldn't affect non-union field types.
|
||||
field1: str
|
||||
|
||||
@@ -32,42 +30,3 @@ field10: (str | int) | str # PYI016: Duplicate union member `str`
|
||||
|
||||
# Should emit for nested unions.
|
||||
field11: dict[int | int, str]
|
||||
|
||||
# Should emit for unions with more than two cases
|
||||
field12: int | int | int # Error
|
||||
field13: int | int | int | int # Error
|
||||
|
||||
# Should emit for unions with more than two cases, even if not directly adjacent
|
||||
field14: int | int | str | int # Error
|
||||
|
||||
# Should emit for duplicate literal types; also covered by PYI030
|
||||
field15: typing.Literal[1] | typing.Literal[1] # Error
|
||||
|
||||
# Shouldn't emit if in new parent type
|
||||
field16: int | dict[int, str] # OK
|
||||
|
||||
# Shouldn't emit if not in a union parent
|
||||
field17: dict[int, int] # OK
|
||||
|
||||
# Should emit in cases with newlines
|
||||
field18: typing.Union[
|
||||
set[
|
||||
int # foo
|
||||
],
|
||||
set[
|
||||
int # bar
|
||||
],
|
||||
] # Error, newline and comment will not be emitted in message
|
||||
|
||||
|
||||
# Should emit in cases with `typing.Union` instead of `|`
|
||||
field19: typing.Union[int, int] # Error
|
||||
|
||||
# Should emit in cases with nested `typing.Union`
|
||||
field20: typing.Union[int, typing.Union[int, str]] # Error
|
||||
|
||||
# Should emit in cases with mixed `typing.Union` and `|`
|
||||
field21: typing.Union[int, int | str] # Error
|
||||
|
||||
# Should emit only once in cases with multiple nested `typing.Union`
|
||||
field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
from typing import Literal
|
||||
# Shouldn't emit for any cases in the non-stub file for compatibility with flake8-pyi.
|
||||
# Note that this rule could be applied here in the future.
|
||||
|
||||
field1: Literal[1] # OK
|
||||
field2: Literal[1] | Literal[2] # OK
|
||||
|
||||
def func1(arg1: Literal[1] | Literal[2]): # OK
|
||||
print(arg1)
|
||||
|
||||
|
||||
def func2() -> Literal[1] | Literal[2]: # OK
|
||||
return "my Literal[1]ing"
|
||||
|
||||
|
||||
field3: Literal[1] | Literal[2] | str # OK
|
||||
field4: str | Literal[1] | Literal[2] # OK
|
||||
field5: Literal[1] | str | Literal[2] # OK
|
||||
field6: Literal[1] | bool | Literal[2] | str # OK
|
||||
field7 = Literal[1] | Literal[2] # OK
|
||||
field8: Literal[1] | (Literal[2] | str) # OK
|
||||
field9: Literal[1] | (Literal[2] | str) # OK
|
||||
field10: (Literal[1] | str) | Literal[2] # OK
|
||||
field11: dict[Literal[1] | Literal[2], str] # OK
|
||||
@@ -1,86 +0,0 @@
|
||||
import typing
|
||||
import typing_extensions
|
||||
from typing import Literal
|
||||
|
||||
# Shouldn't affect non-union field types.
|
||||
field1: Literal[1] # OK
|
||||
|
||||
# Should emit for duplicate field types.
|
||||
field2: Literal[1] | Literal[2] # Error
|
||||
|
||||
# Should emit for union types in arguments.
|
||||
def func1(arg1: Literal[1] | Literal[2]): # Error
|
||||
print(arg1)
|
||||
|
||||
|
||||
# Should emit for unions in return types.
|
||||
def func2() -> Literal[1] | Literal[2]: # Error
|
||||
return "my Literal[1]ing"
|
||||
|
||||
|
||||
# Should emit in longer unions, even if not directly adjacent.
|
||||
field3: Literal[1] | Literal[2] | str # Error
|
||||
field4: str | Literal[1] | Literal[2] # Error
|
||||
field5: Literal[1] | str | Literal[2] # Error
|
||||
field6: Literal[1] | bool | Literal[2] | str # Error
|
||||
|
||||
# Should emit for non-type unions.
|
||||
field7 = Literal[1] | Literal[2] # Error
|
||||
|
||||
# Should emit for parenthesized unions.
|
||||
field8: Literal[1] | (Literal[2] | str) # Error
|
||||
|
||||
# Should handle user parentheses when fixing.
|
||||
field9: Literal[1] | (Literal[2] | str) # Error
|
||||
field10: (Literal[1] | str) | Literal[2] # Error
|
||||
|
||||
# Should emit for union in generic parent type.
|
||||
field11: dict[Literal[1] | Literal[2], str] # Error
|
||||
|
||||
# Should emit for unions with more than two cases
|
||||
field12: Literal[1] | Literal[2] | Literal[3] # Error
|
||||
field13: Literal[1] | Literal[2] | Literal[3] | Literal[4] # Error
|
||||
|
||||
# Should emit for unions with more than two cases, even if not directly adjacent
|
||||
field14: Literal[1] | Literal[2] | str | Literal[3] # Error
|
||||
|
||||
# Should emit for unions with mixed literal internal types
|
||||
field15: Literal[1] | Literal["foo"] | Literal[True] # Error
|
||||
|
||||
# Shouldn't emit for duplicate field types with same value; covered by Y016
|
||||
field16: Literal[1] | Literal[1] # OK
|
||||
|
||||
# Shouldn't emit if in new parent type
|
||||
field17: Literal[1] | dict[Literal[2], str] # OK
|
||||
|
||||
# Shouldn't emit if not in a union parent
|
||||
field18: dict[Literal[1], Literal[2]] # OK
|
||||
|
||||
# Should respect name of literal type used
|
||||
field19: typing.Literal[1] | typing.Literal[2] # Error
|
||||
|
||||
# Should emit in cases with newlines
|
||||
field20: typing.Union[
|
||||
Literal[
|
||||
1 # test
|
||||
],
|
||||
Literal[2],
|
||||
] # Error, newline and comment will not be emitted in message
|
||||
|
||||
# Should handle multiple unions with multiple members
|
||||
field21: Literal[1, 2] | Literal[3, 4] # Error
|
||||
|
||||
# Should emit in cases with `typing.Union` instead of `|`
|
||||
field22: typing.Union[Literal[1], Literal[2]] # Error
|
||||
|
||||
# Should emit in cases with `typing_extensions.Literal`
|
||||
field23: typing_extensions.Literal[1] | typing_extensions.Literal[2] # Error
|
||||
|
||||
# Should emit in cases with nested `typing.Union`
|
||||
field24: typing.Union[Literal[1], typing.Union[Literal[2], str]] # Error
|
||||
|
||||
# Should emit in cases with mixed `typing.Union` and `|`
|
||||
field25: typing.Union[Literal[1], Literal[2] | str] # Error
|
||||
|
||||
# Should emit only once in cases with multiple nested `typing.Union`
|
||||
field24: typing.Union[Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]]] # Error
|
||||
@@ -36,11 +36,3 @@ bar: str = "51 character stringgggggggggggggggggggggggggggggggg"
|
||||
baz: bytes = b"50 character byte stringgggggggggggggggggggggggggg"
|
||||
|
||||
qux: bytes = b"51 character byte stringggggggggggggggggggggggggggg\xff"
|
||||
|
||||
|
||||
class Demo:
|
||||
"""Docstrings are excluded from this rule. Some padding."""
|
||||
|
||||
|
||||
def func() -> None:
|
||||
"""Docstrings are excluded from this rule. Some padding."""
|
||||
|
||||
@@ -28,9 +28,3 @@ bar: str = "51 character stringgggggggggggggggggggggggggggggggg" # Error: PYI05
|
||||
baz: bytes = b"50 character byte stringgggggggggggggggggggggggggg" # OK
|
||||
|
||||
qux: bytes = b"51 character byte stringggggggggggggggggggggggggggg\xff" # Error: PYI053
|
||||
|
||||
class Demo:
|
||||
"""Docstrings are excluded from this rule. Some padding.""" # OK
|
||||
|
||||
def func() -> None:
|
||||
"""Docstrings are excluded from this rule. Some padding.""" # OK
|
||||
|
||||
@@ -29,26 +29,6 @@ raise TypeError(
|
||||
# Hello, world!
|
||||
)
|
||||
|
||||
# OK
|
||||
raise AssertionError
|
||||
|
||||
# OK
|
||||
raise AttributeError("test message")
|
||||
|
||||
|
||||
def return_error():
|
||||
return ValueError("Something")
|
||||
|
||||
|
||||
# OK
|
||||
raise return_error()
|
||||
|
||||
|
||||
class Class:
|
||||
@staticmethod
|
||||
def error():
|
||||
return ValueError("Something")
|
||||
|
||||
|
||||
# OK
|
||||
raise Class.error()
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
# T002 - accepted
|
||||
# TODO (evanrittenhouse): this has an author
|
||||
# TODO(evanrittenhouse): this has an author
|
||||
# TODO (evanrittenhouse) and more: this has an author
|
||||
# TODO(evanrittenhouse) and more: this has an author
|
||||
# TODO@mayrholu: this has an author
|
||||
# TODO @mayrholu: this has an author
|
||||
# TODO@mayrholu and more: this has an author
|
||||
# TODO @mayrholu and more: this has an author
|
||||
# TODO(evanrittenhouse): this also has an author
|
||||
# T002 - errors
|
||||
# TODO: this has no author
|
||||
# FIXME: neither does this
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import A
|
||||
import B
|
||||
import b
|
||||
import C
|
||||
import d
|
||||
import E
|
||||
import f
|
||||
from g import a, B, c
|
||||
from h import A, b, C
|
||||
@@ -26,9 +26,3 @@ def f():
|
||||
import os # isort:skip
|
||||
import collections
|
||||
import abc
|
||||
|
||||
|
||||
def f():
|
||||
import sys; import os # isort:skip
|
||||
import sys; import os # isort:skip # isort:skip
|
||||
import sys; import os
|
||||
|
||||
@@ -19,13 +19,3 @@ if True:
|
||||
|
||||
import D
|
||||
import B
|
||||
|
||||
|
||||
import e
|
||||
import f
|
||||
|
||||
# isort: split
|
||||
# isort: split
|
||||
|
||||
import d
|
||||
import c
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Do this (new version)
|
||||
from numpy.random import default_rng
|
||||
|
||||
rng = default_rng()
|
||||
vals = rng.standard_normal(10)
|
||||
more_vals = rng.standard_normal(10)
|
||||
@@ -8,13 +7,11 @@ numbers = rng.integers(high, size=5)
|
||||
|
||||
# instead of this (legacy version)
|
||||
from numpy import random
|
||||
|
||||
vals = random.standard_normal(10)
|
||||
more_vals = random.standard_normal(10)
|
||||
numbers = random.integers(high, size=5)
|
||||
|
||||
import numpy
|
||||
|
||||
numpy.random.seed()
|
||||
numpy.random.get_state()
|
||||
numpy.random.set_state()
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import numpy as np
|
||||
|
||||
np.round_(np.random.rand(5, 5), 2)
|
||||
np.product(np.random.rand(5, 5))
|
||||
np.cumproduct(np.random.rand(5, 5))
|
||||
np.sometrue(np.random.rand(5, 5))
|
||||
np.alltrue(np.random.rand(5, 5))
|
||||
|
||||
from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
|
||||
round_(np.random.rand(5, 5), 2)
|
||||
product(np.random.rand(5, 5))
|
||||
cumproduct(np.random.rand(5, 5))
|
||||
sometrue(np.random.rand(5, 5))
|
||||
alltrue(np.random.rand(5, 5))
|
||||
@@ -4,9 +4,7 @@ x = pd.DataFrame()
|
||||
|
||||
x.drop(["a"], axis=1, inplace=True)
|
||||
|
||||
x.y.drop(["a"], axis=1, inplace=True)
|
||||
|
||||
x["y"].drop(["a"], axis=1, inplace=True)
|
||||
x.drop(["a"], axis=1, inplace=True)
|
||||
|
||||
x.drop(
|
||||
inplace=True,
|
||||
@@ -25,7 +23,6 @@ x.drop(["a"], axis=1, **kwargs, inplace=True)
|
||||
x.drop(["a"], axis=1, inplace=True, **kwargs)
|
||||
f(x.drop(["a"], axis=1, inplace=True))
|
||||
|
||||
x.apply(lambda x: x.sort_values("a", inplace=True))
|
||||
x.apply(lambda x: x.sort_values('a', inplace=True))
|
||||
import torch
|
||||
|
||||
torch.m.ReLU(inplace=True) # safe because this isn't a pandas call
|
||||
torch.m.ReLU(inplace=True) # safe because this isn't a pandas call
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import abc
|
||||
from abc import ABCMeta
|
||||
|
||||
import pydantic
|
||||
|
||||
@@ -19,10 +19,6 @@ class Class:
|
||||
def class_method(cls):
|
||||
pass
|
||||
|
||||
@abc.abstractclassmethod
|
||||
def abstract_class_method(cls):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def static_method(x):
|
||||
return x
|
||||
@@ -45,7 +41,7 @@ class Class:
|
||||
...
|
||||
|
||||
|
||||
class MetaClass(abc.ABCMeta):
|
||||
class MetaClass(ABCMeta):
|
||||
def bad_method(self):
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import abc
|
||||
from abc import ABCMeta
|
||||
|
||||
import pydantic
|
||||
|
||||
@@ -34,23 +34,6 @@ class Class:
|
||||
def stillBad(cls, my_field: str) -> str:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def badAllowed(cls):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def stillBad(cls):
|
||||
pass
|
||||
|
||||
@abc.abstractclassmethod
|
||||
def badAllowed(cls):
|
||||
pass
|
||||
|
||||
@abc.abstractclassmethod
|
||||
def stillBad(cls):
|
||||
pass
|
||||
|
||||
|
||||
class PosOnlyClass:
|
||||
def badAllowed(this, blah, /, self, something: str):
|
||||
pass
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
foo_tuple = (1, 2, 3)
|
||||
foo_list = [1, 2, 3]
|
||||
foo_set = {1, 2, 3}
|
||||
foo_dict = {1: 2, 3: 4}
|
||||
foo_int = 123
|
||||
|
||||
for i in list(foo_tuple): # PERF101
|
||||
pass
|
||||
|
||||
for i in list(foo_list): # PERF101
|
||||
pass
|
||||
|
||||
for i in list(foo_set): # PERF101
|
||||
pass
|
||||
|
||||
for i in list((1, 2, 3)): # PERF101
|
||||
pass
|
||||
|
||||
for i in list([1, 2, 3]): # PERF101
|
||||
pass
|
||||
|
||||
for i in list({1, 2, 3}): # PERF101
|
||||
pass
|
||||
|
||||
for i in list(
|
||||
{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
}
|
||||
):
|
||||
pass
|
||||
|
||||
for i in list( # Comment
|
||||
{1, 2, 3}
|
||||
): # PERF101
|
||||
pass
|
||||
|
||||
for i in list(foo_dict): # Ok
|
||||
pass
|
||||
|
||||
for i in list(1): # Ok
|
||||
pass
|
||||
|
||||
for i in list(foo_int): # Ok
|
||||
pass
|
||||
|
||||
|
||||
import itertools
|
||||
|
||||
for i in itertools.product(foo_int): # Ok
|
||||
pass
|
||||
@@ -1,28 +0,0 @@
|
||||
for i in range(10):
|
||||
try: # PERF203
|
||||
print(f"{i}")
|
||||
except:
|
||||
print("error")
|
||||
|
||||
try:
|
||||
for i in range(10):
|
||||
print(f"{i}")
|
||||
except:
|
||||
print("error")
|
||||
|
||||
i = 0
|
||||
while i < 10: # PERF203
|
||||
try:
|
||||
print(f"{i}")
|
||||
except:
|
||||
print("error")
|
||||
|
||||
i += 1
|
||||
|
||||
try:
|
||||
i = 0
|
||||
while i < 10:
|
||||
print(f"{i}")
|
||||
i += 1
|
||||
except:
|
||||
print("error")
|
||||
@@ -1,47 +0,0 @@
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
if i % 2:
|
||||
result.append(i) # PERF401
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
result.append(i * i) # PERF401
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
if i % 2:
|
||||
result.append(i) # PERF401
|
||||
elif i % 2:
|
||||
result.append(i) # PERF401
|
||||
else:
|
||||
result.append(i) # PERF401
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
result.append(i) # OK
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = {}
|
||||
for i in items:
|
||||
result[i].append(i) # OK
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
if i not in result:
|
||||
result.append(i) # OK
|
||||
@@ -1,26 +0,0 @@
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
result.append(i) # PERF402
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
result.insert(0, i) # PERF402
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
result.append(i * i) # OK
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = {}
|
||||
for i in items:
|
||||
result[i].append(i * i) # OK
|
||||
@@ -1,135 +1,51 @@
|
||||
def scope():
|
||||
# E731
|
||||
#: E731
|
||||
f = lambda x: 2 * x
|
||||
#: E731
|
||||
f = lambda x: 2 * x
|
||||
#: E731
|
||||
while False:
|
||||
this = lambda y, z: 2 * x
|
||||
#: E731
|
||||
f = lambda: (yield 1)
|
||||
#: E731
|
||||
f = lambda: (yield from g())
|
||||
#: E731
|
||||
class F:
|
||||
f = lambda x: 2 * x
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
f = lambda x: 2 * x
|
||||
f = object()
|
||||
f.method = lambda: "Method"
|
||||
f = {}
|
||||
f["a"] = lambda x: x**2
|
||||
f = []
|
||||
f.append(lambda x: x**2)
|
||||
f = g = lambda x: x**2
|
||||
lambda: "no-op"
|
||||
|
||||
# Annotated
|
||||
from typing import Callable, ParamSpec
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
while False:
|
||||
this = lambda y, z: 2 * x
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
f = lambda: (yield 1)
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
f = lambda: (yield from g())
|
||||
|
||||
|
||||
def scope():
|
||||
# OK
|
||||
f = object()
|
||||
f.method = lambda: "Method"
|
||||
|
||||
|
||||
def scope():
|
||||
# OK
|
||||
f = {}
|
||||
f["a"] = lambda x: x**2
|
||||
|
||||
|
||||
def scope():
|
||||
# OK
|
||||
f = []
|
||||
f.append(lambda x: x**2)
|
||||
|
||||
|
||||
def scope():
|
||||
# OK
|
||||
f = g = lambda x: x**2
|
||||
|
||||
|
||||
def scope():
|
||||
# OK
|
||||
lambda: "no-op"
|
||||
|
||||
|
||||
class Scope:
|
||||
# E731
|
||||
f = lambda x: 2 * x
|
||||
|
||||
|
||||
class Scope:
|
||||
from typing import Callable
|
||||
|
||||
# E731
|
||||
f: Callable[[int], int] = lambda x: 2 * x
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
from typing import Callable
|
||||
|
||||
x: Callable[[int], int]
|
||||
if True:
|
||||
x = lambda: 1
|
||||
else:
|
||||
x = lambda: 2
|
||||
return x
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
|
||||
from typing import Callable, ParamSpec
|
||||
|
||||
# ParamSpec cannot be used in this context, so do not preserve the annotation.
|
||||
P = ParamSpec("P")
|
||||
f: Callable[P, int] = lambda *args: len(args)
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
|
||||
from typing import Callable
|
||||
|
||||
f: Callable[[], None] = lambda: None
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
|
||||
from typing import Callable
|
||||
|
||||
f: Callable[..., None] = lambda a, b: None
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
|
||||
from typing import Callable
|
||||
|
||||
f: Callable[[int], int] = lambda x: 2 * x
|
||||
P = ParamSpec("P")
|
||||
|
||||
# ParamSpec cannot be used in this context, so do not preserve the annotation.
|
||||
f: Callable[P, int] = lambda *args: len(args)
|
||||
f: Callable[[], None] = lambda: None
|
||||
f: Callable[..., None] = lambda a, b: None
|
||||
f: Callable[[int], int] = lambda x: 2 * x
|
||||
|
||||
# Let's use the `Callable` type from `collections.abc` instead.
|
||||
def scope():
|
||||
# E731
|
||||
from collections.abc import Callable
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
f: Callable[[str, int], str] = lambda a, b: a * b
|
||||
f: Callable[[str, int], str] = lambda a, b: a * b
|
||||
f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
|
||||
f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
|
||||
# Override `Callable`
|
||||
class Callable:
|
||||
pass
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
|
||||
# Do not copy the annotation from here on out.
|
||||
f: Callable[[str, int], str] = lambda a, b: a * b
|
||||
|
||||
@@ -19,14 +19,6 @@ with \_ somewhere
|
||||
in the middle
|
||||
"""
|
||||
|
||||
#: W605:1:38
|
||||
value = 'new line\nand invalid escape \_ here'
|
||||
|
||||
|
||||
def f():
|
||||
#: W605:1:11
|
||||
return'\.png$'
|
||||
|
||||
#: Okay
|
||||
regex = r'\.png$'
|
||||
regex = '\\.png$'
|
||||
|
||||
@@ -19,11 +19,6 @@ with \_ somewhere
|
||||
in the middle
|
||||
"""
|
||||
|
||||
|
||||
def f():
|
||||
#: W605:1:11
|
||||
return'\.png$'
|
||||
|
||||
#: Okay
|
||||
regex = r'\.png$'
|
||||
regex = '\\.png$'
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
def double_quotes_backslash():
|
||||
"""Sum\\mary."""
|
||||
|
||||
|
||||
def double_quotes_backslash_raw():
|
||||
r"""Sum\mary."""
|
||||
|
||||
|
||||
def double_quotes_backslash_uppercase():
|
||||
R"""Sum\\mary."""
|
||||
|
||||
|
||||
def make_unique_pod_id(pod_id: str) -> str | None:
|
||||
r"""
|
||||
Generate a unique Pod name.
|
||||
|
||||
Kubernetes pod names must consist of one or more lowercase
|
||||
rfc1035/rfc1123 labels separated by '.' with a maximum length of 253
|
||||
characters.
|
||||
|
||||
Name must pass the following regex for validation
|
||||
``^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$``
|
||||
|
||||
For more details, see:
|
||||
https://github.com/kubernetes/kubernetes/blob/release-1.1/docs/design/identifiers.md
|
||||
|
||||
:param pod_id: requested pod name
|
||||
:return: ``str`` valid Pod name of appropriate length
|
||||
"""
|
||||
@@ -1,25 +0,0 @@
|
||||
def f(a: int, b: int) -> int:
|
||||
"""Showcase function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
a : int
|
||||
_description_
|
||||
b : int
|
||||
_description_
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
_description
|
||||
"""
|
||||
return b - a
|
||||
|
||||
|
||||
def f() -> int:
|
||||
"""Showcase function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
Returns
|
||||
-------
|
||||
"""
|
||||
@@ -513,19 +513,3 @@ def implicit_string_concatenation():
|
||||
A value of some sort.
|
||||
|
||||
""""Extra content"
|
||||
|
||||
|
||||
def replace_equals_with_dash():
|
||||
"""Equal length equals should be replaced with dashes.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
"""
|
||||
|
||||
|
||||
def replace_equals_with_dash2():
|
||||
"""Here, the length of equals is not the same.
|
||||
|
||||
Parameters
|
||||
===========
|
||||
"""
|
||||
|
||||
@@ -37,10 +37,7 @@ f"{{test}}"
|
||||
f'{{ 40 }}'
|
||||
f"{{a {{x}}"
|
||||
f"{{{{x}}}}"
|
||||
""f""
|
||||
''f""
|
||||
(""f""r"")
|
||||
|
||||
# To be fixed
|
||||
# Error: f-string: single '}' is not allowed at line 41 column 8
|
||||
# f"\{{x}}"
|
||||
# f"\{{x}}"
|
||||
|
||||
@@ -48,8 +48,3 @@ x = {
|
||||
|
||||
x = {"a": 1, "a": 1}
|
||||
x = {"a": 1, "b": 2, "a": 1}
|
||||
|
||||
x = {
|
||||
('a', 'b'): 'asdf',
|
||||
('a', 'b'): 'qwer',
|
||||
}
|
||||
|
||||
@@ -36,6 +36,3 @@ for item in set(("apples", "lemons", "water")): # set constructor is fine
|
||||
|
||||
for number in {i for i in range(10)}: # set comprehensions are fine
|
||||
print(number)
|
||||
|
||||
for item in {*numbers_set, 4, 5, 6}: # set unpacking is fine
|
||||
print(f"I like {item}.")
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
# Errors.
|
||||
class Foo:
|
||||
__slots__ = "bar"
|
||||
|
||||
def __init__(self, bar):
|
||||
self.bar = bar
|
||||
|
||||
|
||||
class Foo:
|
||||
__slots__: str = "bar"
|
||||
|
||||
def __init__(self, bar):
|
||||
self.bar = bar
|
||||
|
||||
|
||||
class Foo:
|
||||
__slots__: str = f"bar"
|
||||
|
||||
def __init__(self, bar):
|
||||
self.bar = bar
|
||||
|
||||
|
||||
# Non-errors.
|
||||
class Foo:
|
||||
__slots__ = ("bar",)
|
||||
|
||||
def __init__(self, bar):
|
||||
self.bar = bar
|
||||
|
||||
|
||||
class Foo:
|
||||
__slots__: tuple[str, ...] = ("bar",)
|
||||
|
||||
def __init__(self, bar):
|
||||
self.bar = bar
|
||||
@@ -1,37 +0,0 @@
|
||||
from typing import ParamSpec, TypeVar
|
||||
|
||||
# Errors.
|
||||
|
||||
T = TypeVar("T", covariant=True, contravariant=True)
|
||||
T = TypeVar(name="T", covariant=True, contravariant=True)
|
||||
|
||||
T = ParamSpec("T", covariant=True, contravariant=True)
|
||||
T = ParamSpec(name="T", covariant=True, contravariant=True)
|
||||
|
||||
# Non-errors.
|
||||
|
||||
T = TypeVar("T")
|
||||
T = TypeVar("T", covariant=False)
|
||||
T = TypeVar("T", contravariant=False)
|
||||
T = TypeVar("T", covariant=False, contravariant=False)
|
||||
T = TypeVar("T", covariant=True)
|
||||
T = TypeVar("T", covariant=True, contravariant=False)
|
||||
T = TypeVar(name="T", covariant=True, contravariant=False)
|
||||
T = TypeVar(name="T", covariant=True)
|
||||
T = TypeVar("T", contravariant=True)
|
||||
T = TypeVar("T", covariant=False, contravariant=True)
|
||||
T = TypeVar(name="T", covariant=False, contravariant=True)
|
||||
T = TypeVar(name="T", contravariant=True)
|
||||
|
||||
T = ParamSpec("T")
|
||||
T = ParamSpec("T", covariant=False)
|
||||
T = ParamSpec("T", contravariant=False)
|
||||
T = ParamSpec("T", covariant=False, contravariant=False)
|
||||
T = ParamSpec("T", covariant=True)
|
||||
T = ParamSpec("T", covariant=True, contravariant=False)
|
||||
T = ParamSpec(name="T", covariant=True, contravariant=False)
|
||||
T = ParamSpec(name="T", covariant=True)
|
||||
T = ParamSpec("T", contravariant=True)
|
||||
T = ParamSpec("T", covariant=False, contravariant=True)
|
||||
T = ParamSpec(name="T", covariant=False, contravariant=True)
|
||||
T = ParamSpec(name="T", contravariant=True)
|
||||
@@ -1,68 +0,0 @@
|
||||
from typing import ParamSpec, TypeVar
|
||||
|
||||
# Errors.
|
||||
|
||||
T = TypeVar("T", covariant=True)
|
||||
T = TypeVar("T", covariant=True, contravariant=False)
|
||||
T = TypeVar("T", contravariant=True)
|
||||
T = TypeVar("T", covariant=False, contravariant=True)
|
||||
P = ParamSpec("P", covariant=True)
|
||||
P = ParamSpec("P", covariant=True, contravariant=False)
|
||||
P = ParamSpec("P", contravariant=True)
|
||||
P = ParamSpec("P", covariant=False, contravariant=True)
|
||||
|
||||
T_co = TypeVar("T_co")
|
||||
T_co = TypeVar("T_co", covariant=False)
|
||||
T_co = TypeVar("T_co", contravariant=False)
|
||||
T_co = TypeVar("T_co", covariant=False, contravariant=False)
|
||||
T_co = TypeVar("T_co", contravariant=True)
|
||||
T_co = TypeVar("T_co", covariant=False, contravariant=True)
|
||||
P_co = ParamSpec("P_co")
|
||||
P_co = ParamSpec("P_co", covariant=False)
|
||||
P_co = ParamSpec("P_co", contravariant=False)
|
||||
P_co = ParamSpec("P_co", covariant=False, contravariant=False)
|
||||
P_co = ParamSpec("P_co", contravariant=True)
|
||||
P_co = ParamSpec("P_co", covariant=False, contravariant=True)
|
||||
|
||||
T_contra = TypeVar("T_contra")
|
||||
T_contra = TypeVar("T_contra", covariant=False)
|
||||
T_contra = TypeVar("T_contra", contravariant=False)
|
||||
T_contra = TypeVar("T_contra", covariant=False, contravariant=False)
|
||||
T_contra = TypeVar("T_contra", covariant=True)
|
||||
T_contra = TypeVar("T_contra", covariant=True, contravariant=False)
|
||||
P_contra = ParamSpec("P_contra")
|
||||
P_contra = ParamSpec("P_contra", covariant=False)
|
||||
P_contra = ParamSpec("P_contra", contravariant=False)
|
||||
P_contra = ParamSpec("P_contra", covariant=False, contravariant=False)
|
||||
P_contra = ParamSpec("P_contra", covariant=True)
|
||||
P_contra = ParamSpec("P_contra", covariant=True, contravariant=False)
|
||||
|
||||
# Non-errors.
|
||||
|
||||
T = TypeVar("T")
|
||||
T = TypeVar("T", covariant=False)
|
||||
T = TypeVar("T", contravariant=False)
|
||||
T = TypeVar("T", covariant=False, contravariant=False)
|
||||
P = ParamSpec("P")
|
||||
P = ParamSpec("P", covariant=False)
|
||||
P = ParamSpec("P", contravariant=False)
|
||||
P = ParamSpec("P", covariant=False, contravariant=False)
|
||||
|
||||
T_co = TypeVar("T_co", covariant=True)
|
||||
T_co = TypeVar("T_co", covariant=True, contravariant=False)
|
||||
P_co = ParamSpec("P_co", covariant=True)
|
||||
P_co = ParamSpec("P_co", covariant=True, contravariant=False)
|
||||
|
||||
T_contra = TypeVar("T_contra", contravariant=True)
|
||||
T_contra = TypeVar("T_contra", covariant=False, contravariant=True)
|
||||
P_contra = ParamSpec("P_contra", contravariant=True)
|
||||
P_contra = ParamSpec("P_contra", covariant=False, contravariant=True)
|
||||
|
||||
# Bivariate types are errors, but not covered by this check.
|
||||
|
||||
T = TypeVar("T", covariant=True, contravariant=True)
|
||||
P = ParamSpec("P", covariant=True, contravariant=True)
|
||||
T_co = TypeVar("T_co", covariant=True, contravariant=True)
|
||||
P_co = ParamSpec("P_co", covariant=True, contravariant=True)
|
||||
T_contra = TypeVar("T_contra", covariant=True, contravariant=True)
|
||||
P_contra = ParamSpec("P_contra", covariant=True, contravariant=True)
|
||||
@@ -1,56 +0,0 @@
|
||||
from typing import TypeVar, ParamSpec, NewType, TypeVarTuple
|
||||
|
||||
# Errors.
|
||||
|
||||
X = TypeVar("T")
|
||||
X = TypeVar(name="T")
|
||||
|
||||
Y = ParamSpec("T")
|
||||
Y = ParamSpec(name="T")
|
||||
|
||||
Z = NewType("T", int)
|
||||
Z = NewType(name="T", tp=int)
|
||||
|
||||
Ws = TypeVarTuple("Ts")
|
||||
Ws = TypeVarTuple(name="Ts")
|
||||
|
||||
# Non-errors.
|
||||
|
||||
T = TypeVar("T")
|
||||
T = TypeVar(name="T")
|
||||
|
||||
T = ParamSpec("T")
|
||||
T = ParamSpec(name="T")
|
||||
|
||||
T = NewType("T", int)
|
||||
T = NewType(name="T", tp=int)
|
||||
|
||||
Ts = TypeVarTuple("Ts")
|
||||
Ts = TypeVarTuple(name="Ts")
|
||||
|
||||
# Errors, but not covered by this rule.
|
||||
|
||||
# Non-string literal name.
|
||||
T = TypeVar(some_str)
|
||||
T = TypeVar(name=some_str)
|
||||
T = TypeVar(1)
|
||||
T = TypeVar(name=1)
|
||||
T = ParamSpec(some_str)
|
||||
T = ParamSpec(name=some_str)
|
||||
T = ParamSpec(1)
|
||||
T = ParamSpec(name=1)
|
||||
T = NewType(some_str, int)
|
||||
T = NewType(name=some_str, tp=int)
|
||||
T = NewType(1, int)
|
||||
T = NewType(name=1, tp=int)
|
||||
Ts = TypeVarTuple(some_str)
|
||||
Ts = TypeVarTuple(name=some_str)
|
||||
Ts = TypeVarTuple(1)
|
||||
Ts = TypeVarTuple(name=1)
|
||||
|
||||
# No names provided.
|
||||
T = TypeVar()
|
||||
T = ParamSpec()
|
||||
T = NewType()
|
||||
T = NewType(tp=int)
|
||||
Ts = TypeVarTuple()
|
||||
@@ -70,8 +70,3 @@ print("foo".encode()) # print(b"foo")
|
||||
"abc"
|
||||
"def"
|
||||
)).encode()
|
||||
|
||||
(f"foo{bar}").encode("utf-8")
|
||||
(f"foo{bar}").encode(encoding="utf-8")
|
||||
("unicode text©").encode("utf-8")
|
||||
("unicode text©").encode(encoding="utf-8")
|
||||
|
||||
@@ -54,14 +54,6 @@ print("foo {} ".format(x))
|
||||
|
||||
'''{[b]}'''.format(a)
|
||||
|
||||
"{}".format(
|
||||
1
|
||||
)
|
||||
|
||||
"123456789 {}".format(
|
||||
1111111111111111111111111111111111111111111111111111111111111111111111111,
|
||||
)
|
||||
|
||||
###
|
||||
# Non-errors
|
||||
###
|
||||
@@ -95,9 +87,6 @@ r'"\N{snowman} {}".format(a)'
|
||||
|
||||
"{a}" "{b}".format(a=1, b=1)
|
||||
|
||||
"123456789 {}".format(
|
||||
11111111111111111111111111111111111111111111111111111111111111111111111111,
|
||||
)
|
||||
|
||||
async def c():
|
||||
return "{}".format(await 3)
|
||||
|
||||
@@ -48,12 +48,3 @@ if True: from collections import (
|
||||
|
||||
# OK
|
||||
from a import b
|
||||
|
||||
# Ok: `typing_extensions` contains backported improvements.
|
||||
from typing_extensions import SupportsIndex
|
||||
|
||||
# Ok: `typing_extensions` contains backported improvements.
|
||||
from typing_extensions import NamedTuple
|
||||
|
||||
# Ok: `typing_extensions` supports `frozen_default` (backported from 3.12).
|
||||
from typing_extensions import dataclass_transform
|
||||
|
||||
@@ -6,7 +6,6 @@ from fractions import Fraction
|
||||
from pathlib import Path
|
||||
from typing import ClassVar, NamedTuple
|
||||
|
||||
|
||||
def default_function() -> list[int]:
|
||||
return []
|
||||
|
||||
@@ -26,13 +25,12 @@ class A:
|
||||
fine_timedelta: datetime.timedelta = datetime.timedelta(hours=7)
|
||||
fine_tuple: tuple[int] = tuple([1])
|
||||
fine_regex: re.Pattern = re.compile(r".*")
|
||||
fine_float: float = float("-inf")
|
||||
fine_float: float = float('-inf')
|
||||
fine_int: int = int(12)
|
||||
fine_complex: complex = complex(1, 2)
|
||||
fine_str: str = str("foo")
|
||||
fine_bool: bool = bool("foo")
|
||||
fine_fraction: Fraction = Fraction(1, 2)
|
||||
|
||||
fine_fraction: Fraction = Fraction(1,2)
|
||||
|
||||
DEFAULT_IMMUTABLETYPE_FOR_ALL_DATACLASSES = ImmutableType(40)
|
||||
DEFAULT_A_FOR_ALL_DATACLASSES = A([1, 2, 3])
|
||||
@@ -47,25 +45,3 @@ class B:
|
||||
okay_variant: A = DEFAULT_A_FOR_ALL_DATACLASSES
|
||||
|
||||
fine_dataclass_function: list[int] = field(default_factory=list)
|
||||
|
||||
|
||||
class IntConversionDescriptor:
|
||||
def __init__(self, *, default):
|
||||
self._default = default
|
||||
|
||||
def __set_name__(self, owner, name):
|
||||
self._name = "_" + name
|
||||
|
||||
def __get__(self, obj, type):
|
||||
if obj is None:
|
||||
return self._default
|
||||
|
||||
return getattr(obj, self._name, self._default)
|
||||
|
||||
def __set__(self, obj, value):
|
||||
setattr(obj, self._name, int(value))
|
||||
|
||||
|
||||
@dataclass
|
||||
class InventoryItem:
|
||||
quantity_on_hand: IntConversionDescriptor = IntConversionDescriptor(default=100)
|
||||
|
||||
@@ -34,7 +34,3 @@ f"{ascii(bla)}" # OK
|
||||
" intermediary content "
|
||||
f" that flows {repr(obj)} of type {type(obj)}.{additional_message}" # RUF010
|
||||
)
|
||||
|
||||
|
||||
# OK
|
||||
f"{str({})}"
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
import typing
|
||||
from typing import ClassVar, Sequence, Final
|
||||
|
||||
KNOWINGLY_MUTABLE_DEFAULT = []
|
||||
|
||||
|
||||
class A:
|
||||
__slots__ = {
|
||||
"mutable_default": "A mutable default value",
|
||||
}
|
||||
mutable_default: list[int] = []
|
||||
immutable_annotation: typing.Sequence[int] = []
|
||||
without_annotation = []
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
class_variable: typing.ClassVar[list[int]] = []
|
||||
final_variable: typing.Final[list[int]] = []
|
||||
|
||||
|
||||
class B:
|
||||
mutable_default: list[int] = []
|
||||
immutable_annotation: Sequence[int] = []
|
||||
without_annotation = []
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
final_variable: Final[list[int]] = []
|
||||
|
||||
@@ -21,6 +30,7 @@ class C:
|
||||
mutable_default: list[int] = []
|
||||
immutable_annotation: Sequence[int] = []
|
||||
without_annotation = []
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
perfectly_fine: list[int] = field(default_factory=list)
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
final_variable: Final[list[int]] = []
|
||||
@@ -33,5 +43,7 @@ class D(BaseModel):
|
||||
mutable_default: list[int] = []
|
||||
immutable_annotation: Sequence[int] = []
|
||||
without_annotation = []
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
perfectly_fine: list[int] = field(default_factory=list)
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
final_variable: Final[list[int]] = []
|
||||
|
||||
@@ -221,23 +221,3 @@ def f(arg: Union["No" "ne", "int"] = None):
|
||||
# Avoid flagging when there's a parse error in the forward reference
|
||||
def f(arg: Union["<>", "int"] = None):
|
||||
pass
|
||||
|
||||
|
||||
# Type aliases
|
||||
|
||||
Text = str | bytes
|
||||
|
||||
|
||||
def f(arg: Text = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: "Text" = None):
|
||||
pass
|
||||
|
||||
|
||||
from custom_typing import MaybeInt
|
||||
|
||||
|
||||
def f(arg: MaybeInt = None):
|
||||
pass
|
||||
|
||||
185
crates/ruff/resources/test/fixtures/ruff/RUF014.py
vendored
185
crates/ruff/resources/test/fixtures/ruff/RUF014.py
vendored
@@ -1,185 +0,0 @@
|
||||
def after_return():
|
||||
return "reachable"
|
||||
return "unreachable"
|
||||
|
||||
async def also_works_on_async_functions():
|
||||
return "reachable"
|
||||
return "unreachable"
|
||||
|
||||
def if_always_true():
|
||||
if True:
|
||||
return "reachable"
|
||||
return "unreachable"
|
||||
|
||||
def if_always_false():
|
||||
if False:
|
||||
return "unreachable"
|
||||
return "reachable"
|
||||
|
||||
def if_elif_always_false():
|
||||
if False:
|
||||
return "unreachable"
|
||||
elif False:
|
||||
return "also unreachable"
|
||||
return "reachable"
|
||||
|
||||
def if_elif_always_true():
|
||||
if False:
|
||||
return "unreachable"
|
||||
elif True:
|
||||
return "reachable"
|
||||
return "also unreachable"
|
||||
|
||||
def ends_with_if():
|
||||
if False:
|
||||
return "unreachable"
|
||||
else:
|
||||
return "reachable"
|
||||
|
||||
def infinite_loop():
|
||||
while True:
|
||||
continue
|
||||
return "unreachable"
|
||||
|
||||
''' TODO: we could determine these, but we don't yet.
|
||||
def for_range_return():
|
||||
for i in range(10):
|
||||
if i == 5:
|
||||
return "reachable"
|
||||
return "unreachable"
|
||||
|
||||
def for_range_else():
|
||||
for i in range(111):
|
||||
if i == 5:
|
||||
return "reachable"
|
||||
else:
|
||||
return "unreachable"
|
||||
return "also unreachable"
|
||||
|
||||
def for_range_break():
|
||||
for i in range(13):
|
||||
return "reachable"
|
||||
return "unreachable"
|
||||
|
||||
def for_range_if_break():
|
||||
for i in range(1110):
|
||||
if True:
|
||||
return "reachable"
|
||||
return "unreachable"
|
||||
'''
|
||||
|
||||
def match_wildcard(status):
|
||||
match status:
|
||||
case _:
|
||||
return "reachable"
|
||||
return "unreachable"
|
||||
|
||||
def match_case_and_wildcard(status):
|
||||
match status:
|
||||
case 1:
|
||||
return "reachable"
|
||||
case _:
|
||||
return "reachable"
|
||||
return "unreachable"
|
||||
|
||||
def raise_exception():
|
||||
raise Exception
|
||||
return "unreachable"
|
||||
|
||||
def while_false():
|
||||
while False:
|
||||
return "unreachable"
|
||||
return "reachable"
|
||||
|
||||
def while_false_else():
|
||||
while False:
|
||||
return "unreachable"
|
||||
else:
|
||||
return "reachable"
|
||||
|
||||
def while_false_else_return():
|
||||
while False:
|
||||
return "unreachable"
|
||||
else:
|
||||
return "reachable"
|
||||
return "also unreachable"
|
||||
|
||||
def while_true():
|
||||
while True:
|
||||
return "reachable"
|
||||
return "unreachable"
|
||||
|
||||
def while_true_else():
|
||||
while True:
|
||||
return "reachable"
|
||||
else:
|
||||
return "unreachable"
|
||||
|
||||
def while_true_else_return():
|
||||
while True:
|
||||
return "reachable"
|
||||
else:
|
||||
return "unreachable"
|
||||
return "also unreachable"
|
||||
|
||||
def while_false_var_i():
|
||||
i = 0
|
||||
while False:
|
||||
i += 1
|
||||
return i
|
||||
|
||||
def while_true_var_i():
|
||||
i = 0
|
||||
while True:
|
||||
i += 1
|
||||
return i
|
||||
|
||||
def while_infinite():
|
||||
while True:
|
||||
pass
|
||||
return "unreachable"
|
||||
|
||||
def while_if_true():
|
||||
while True:
|
||||
if True:
|
||||
return "reachable"
|
||||
return "unreachable"
|
||||
|
||||
# Test case found in the Bokeh repository that trigger a false positive.
|
||||
def bokeh1(self, obj: BytesRep) -> bytes:
|
||||
data = obj["data"]
|
||||
|
||||
if isinstance(data, str):
|
||||
return base64.b64decode(data)
|
||||
elif isinstance(data, Buffer):
|
||||
buffer = data
|
||||
else:
|
||||
id = data["id"]
|
||||
|
||||
if id in self._buffers:
|
||||
buffer = self._buffers[id]
|
||||
else:
|
||||
self.error(f"can't resolve buffer '{id}'")
|
||||
|
||||
return buffer.data
|
||||
|
||||
'''
|
||||
TODO: because `try` statements aren't handled this triggers a false positive as
|
||||
the last statement is reached, but the rules thinks it isn't (it doesn't
|
||||
see/process the break statement).
|
||||
|
||||
# Test case found in the Bokeh repository that trigger a false positive.
|
||||
def bokeh2(self, host: str = DEFAULT_HOST, port: int = DEFAULT_PORT) -> None:
|
||||
self.stop_serving = False
|
||||
while True:
|
||||
try:
|
||||
self.server = HTTPServer((host, port), HtmlOnlyHandler)
|
||||
self.host = host
|
||||
self.port = port
|
||||
break
|
||||
except OSError:
|
||||
log.debug(f"port {port} is in use, trying to next one")
|
||||
port += 1
|
||||
|
||||
self.thread = threading.Thread(target=self._run_web_server)
|
||||
'''
|
||||
@@ -1,44 +0,0 @@
|
||||
x = range(10)
|
||||
|
||||
# RUF015
|
||||
list(x)[0]
|
||||
list(x)[:1]
|
||||
list(x)[:1:1]
|
||||
list(x)[:1:2]
|
||||
tuple(x)[0]
|
||||
tuple(x)[:1]
|
||||
tuple(x)[:1:1]
|
||||
tuple(x)[:1:2]
|
||||
list(i for i in x)[0]
|
||||
list(i for i in x)[:1]
|
||||
list(i for i in x)[:1:1]
|
||||
list(i for i in x)[:1:2]
|
||||
[i for i in x][0]
|
||||
[i for i in x][:1]
|
||||
[i for i in x][:1:1]
|
||||
[i for i in x][:1:2]
|
||||
|
||||
# OK (not indexing (solely) the first element)
|
||||
list(x)
|
||||
list(x)[1]
|
||||
list(x)[-1]
|
||||
list(x)[1:]
|
||||
list(x)[:3:2]
|
||||
list(x)[::2]
|
||||
list(x)[::]
|
||||
[i for i in x]
|
||||
[i for i in x][1]
|
||||
[i for i in x][-1]
|
||||
[i for i in x][1:]
|
||||
[i for i in x][:3:2]
|
||||
[i for i in x][::2]
|
||||
[i for i in x][::]
|
||||
|
||||
# OK (doesn't mirror the underlying list)
|
||||
[i + 1 for i in x][0]
|
||||
[i for i in x if i > 5][0]
|
||||
[(i, i + 1) for i in x][0]
|
||||
|
||||
# OK (multiple generators)
|
||||
y = range(10)
|
||||
[i + j for i in x for j in y][0]
|
||||
115
crates/ruff/resources/test/fixtures/ruff/RUF016.py
vendored
115
crates/ruff/resources/test/fixtures/ruff/RUF016.py
vendored
@@ -1,115 +0,0 @@
|
||||
# Should not emit for valid access with index
|
||||
var = "abc"[0]
|
||||
var = f"abc"[0]
|
||||
var = [1, 2, 3][0]
|
||||
var = (1, 2, 3)[0]
|
||||
var = b"abc"[0]
|
||||
|
||||
# Should not emit for valid access with slice
|
||||
var = "abc"[0:2]
|
||||
var = f"abc"[0:2]
|
||||
var = b"abc"[0:2]
|
||||
var = [1, 2, 3][0:2]
|
||||
var = (1, 2, 3)[0:2]
|
||||
var = [1, 2, 3][None:2]
|
||||
var = [1, 2, 3][0:None]
|
||||
var = [1, 2, 3][:2]
|
||||
var = [1, 2, 3][0:]
|
||||
|
||||
# Should emit for invalid access on strings
|
||||
var = "abc"["x"]
|
||||
var = f"abc"["x"]
|
||||
|
||||
# Should emit for invalid access on bytes
|
||||
var = b"abc"["x"]
|
||||
|
||||
# Should emit for invalid access on lists and tuples
|
||||
var = [1, 2, 3]["x"]
|
||||
var = (1, 2, 3)["x"]
|
||||
|
||||
# Should emit for invalid access on list comprehensions
|
||||
var = [x for x in range(10)]["x"]
|
||||
|
||||
# Should emit for invalid access using tuple
|
||||
var = "abc"[1, 2]
|
||||
|
||||
# Should emit for invalid access using string
|
||||
var = [1, 2]["x"]
|
||||
|
||||
# Should emit for invalid access using float
|
||||
var = [1, 2][0.25]
|
||||
|
||||
# Should emit for invalid access using dict
|
||||
var = [1, 2][{"x": "y"}]
|
||||
|
||||
# Should emit for invalid access using dict comp
|
||||
var = [1, 2][{x: "y" for x in range(2)}]
|
||||
|
||||
# Should emit for invalid access using list
|
||||
var = [1, 2][2, 3]
|
||||
|
||||
# Should emit for invalid access using list comp
|
||||
var = [1, 2][[x for x in range(2)]]
|
||||
|
||||
# Should emit on invalid access using set
|
||||
var = [1, 2][{"x", "y"}]
|
||||
|
||||
# Should emit on invalid access using set comp
|
||||
var = [1, 2][{x for x in range(2)}]
|
||||
|
||||
# Should emit on invalid access using bytes
|
||||
var = [1, 2][b"x"]
|
||||
|
||||
# Should emit for non-integer slice start
|
||||
var = [1, 2, 3]["x":2]
|
||||
var = [1, 2, 3][f"x":2]
|
||||
var = [1, 2, 3][1.2:2]
|
||||
var = [1, 2, 3][{"x"}:2]
|
||||
var = [1, 2, 3][{x for x in range(2)}:2]
|
||||
var = [1, 2, 3][{"x": x for x in range(2)}:2]
|
||||
var = [1, 2, 3][[x for x in range(2)]:2]
|
||||
|
||||
# Should emit for non-integer slice end
|
||||
var = [1, 2, 3][0:"x"]
|
||||
var = [1, 2, 3][0:f"x"]
|
||||
var = [1, 2, 3][0:1.2]
|
||||
var = [1, 2, 3][0:{"x"}]
|
||||
var = [1, 2, 3][0:{x for x in range(2)}]
|
||||
var = [1, 2, 3][0:{"x": x for x in range(2)}]
|
||||
var = [1, 2, 3][0:[x for x in range(2)]]
|
||||
|
||||
# Should emit for non-integer slice step
|
||||
var = [1, 2, 3][0:1:"x"]
|
||||
var = [1, 2, 3][0:1:f"x"]
|
||||
var = [1, 2, 3][0:1:1.2]
|
||||
var = [1, 2, 3][0:1:{"x"}]
|
||||
var = [1, 2, 3][0:1:{x for x in range(2)}]
|
||||
var = [1, 2, 3][0:1:{"x": x for x in range(2)}]
|
||||
var = [1, 2, 3][0:1:[x for x in range(2)]]
|
||||
|
||||
# Should emit for non-integer slice start and end; should emit twice with specific ranges
|
||||
var = [1, 2, 3]["x":"y"]
|
||||
|
||||
# Should emit once for repeated invalid access
|
||||
var = [1, 2, 3]["x"]["y"]["z"]
|
||||
|
||||
# Cannot emit on invalid access using variable in index
|
||||
x = "x"
|
||||
var = "abc"[x]
|
||||
|
||||
# Cannot emit on invalid access using call
|
||||
def func():
|
||||
return 1
|
||||
var = "abc"[func()]
|
||||
|
||||
# Cannot emit on invalid access using a variable in parent
|
||||
x = [1, 2, 3]
|
||||
var = x["y"]
|
||||
|
||||
# Cannot emit for invalid access on byte array
|
||||
var = bytearray(b"abc")["x"]
|
||||
|
||||
# Cannot emit for slice bound using variable
|
||||
x = "x"
|
||||
var = [1, 2, 3][0:x]
|
||||
var = [1, 2, 3][x:1]
|
||||
@@ -57,10 +57,3 @@ def fine():
|
||||
a = process() # This throws the exception now
|
||||
finally:
|
||||
print("finally")
|
||||
|
||||
|
||||
def fine():
|
||||
try:
|
||||
raise ValueError("a doesn't exist")
|
||||
except TypeError: # A different exception is caught
|
||||
print("A different exception is caught")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use anyhow::{bail, Result};
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use rustpython_parser::ast::{self, ExceptHandler, Expr, Keyword, Ranged, Stmt};
|
||||
use rustpython_parser::{lexer, Mode};
|
||||
use rustpython_parser::{lexer, Mode, Tok};
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_python_ast::helpers;
|
||||
@@ -98,7 +98,7 @@ pub(crate) fn remove_argument(
|
||||
// Case 1: there is only one argument.
|
||||
let mut count = 0u32;
|
||||
for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() {
|
||||
if tok.is_lpar() {
|
||||
if matches!(tok, Tok::Lpar) {
|
||||
if count == 0 {
|
||||
fix_start = Some(if remove_parentheses {
|
||||
range.start()
|
||||
@@ -109,7 +109,7 @@ pub(crate) fn remove_argument(
|
||||
count = count.saturating_add(1);
|
||||
}
|
||||
|
||||
if tok.is_rpar() {
|
||||
if matches!(tok, Tok::Rpar) {
|
||||
count = count.saturating_sub(1);
|
||||
if count == 0 {
|
||||
fix_end = Some(if remove_parentheses {
|
||||
@@ -131,11 +131,11 @@ pub(crate) fn remove_argument(
|
||||
let mut seen_comma = false;
|
||||
for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() {
|
||||
if seen_comma {
|
||||
if tok.is_non_logical_newline() {
|
||||
if matches!(tok, Tok::NonLogicalNewline) {
|
||||
// Also delete any non-logical newlines after the comma.
|
||||
continue;
|
||||
}
|
||||
fix_end = Some(if tok.is_newline() {
|
||||
fix_end = Some(if matches!(tok, Tok::Newline) {
|
||||
range.end()
|
||||
} else {
|
||||
range.start()
|
||||
@@ -145,7 +145,7 @@ pub(crate) fn remove_argument(
|
||||
if range.start() == expr_range.start() {
|
||||
fix_start = Some(range.start());
|
||||
}
|
||||
if fix_start.is_some() && tok.is_comma() {
|
||||
if fix_start.is_some() && matches!(tok, Tok::Comma) {
|
||||
seen_comma = true;
|
||||
}
|
||||
}
|
||||
@@ -157,7 +157,7 @@ pub(crate) fn remove_argument(
|
||||
fix_end = Some(expr_range.end());
|
||||
break;
|
||||
}
|
||||
if tok.is_comma() {
|
||||
if matches!(tok, Tok::Comma) {
|
||||
fix_start = Some(range.start());
|
||||
}
|
||||
}
|
||||
@@ -317,10 +317,10 @@ mod tests {
|
||||
Some(TextSize::from(6))
|
||||
);
|
||||
|
||||
let contents = r"
|
||||
let contents = r#"
|
||||
x = 1 \
|
||||
; y = 1
|
||||
"
|
||||
"#
|
||||
.trim();
|
||||
let program = Suite::parse(contents, "<filename>")?;
|
||||
let stmt = program.first().unwrap();
|
||||
@@ -349,10 +349,10 @@ x = 1 \
|
||||
TextSize::from(6)
|
||||
);
|
||||
|
||||
let contents = r"
|
||||
let contents = r#"
|
||||
x = 1 \
|
||||
; y = 1
|
||||
"
|
||||
"#
|
||||
.trim();
|
||||
let locator = Locator::new(contents);
|
||||
assert_eq!(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@
|
||||
|
||||
use itertools::Itertools;
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use rustpython_parser::ast::Ranged;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
@@ -23,7 +22,7 @@ pub(crate) fn check_noqa(
|
||||
settings: &Settings,
|
||||
) -> Vec<usize> {
|
||||
// Identify any codes that are globally exempted (within the current file).
|
||||
let exemption = FileExemption::try_extract(locator.contents(), comment_ranges, locator);
|
||||
let exemption = noqa::file_exemption(locator.contents(), comment_ranges);
|
||||
|
||||
// Extract all `noqa` directives.
|
||||
let mut noqa_directives = NoqaDirectives::from_commented_ranges(comment_ranges, locator);
|
||||
@@ -38,19 +37,19 @@ pub(crate) fn check_noqa(
|
||||
}
|
||||
|
||||
match &exemption {
|
||||
Some(FileExemption::All) => {
|
||||
FileExemption::All => {
|
||||
// If the file is exempted, ignore all diagnostics.
|
||||
ignored_diagnostics.push(index);
|
||||
continue;
|
||||
}
|
||||
Some(FileExemption::Codes(codes)) => {
|
||||
FileExemption::Codes(codes) => {
|
||||
// If the diagnostic is ignored by a global exemption, ignore it.
|
||||
if codes.contains(&diagnostic.kind.rule().noqa_code()) {
|
||||
ignored_diagnostics.push(index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
FileExemption::None => {}
|
||||
}
|
||||
|
||||
let noqa_offsets = diagnostic
|
||||
@@ -64,15 +63,15 @@ pub(crate) fn check_noqa(
|
||||
if let Some(directive_line) = noqa_directives.find_line_with_directive_mut(noqa_offset)
|
||||
{
|
||||
let suppressed = match &directive_line.directive {
|
||||
Directive::All(_) => {
|
||||
Directive::All(..) => {
|
||||
directive_line
|
||||
.matches
|
||||
.push(diagnostic.kind.rule().noqa_code());
|
||||
ignored_diagnostics.push(index);
|
||||
true
|
||||
}
|
||||
Directive::Codes(directive) => {
|
||||
if noqa::includes(diagnostic.kind.rule(), directive.codes()) {
|
||||
Directive::Codes(.., codes, _) => {
|
||||
if noqa::includes(diagnostic.kind.rule(), codes) {
|
||||
directive_line
|
||||
.matches
|
||||
.push(diagnostic.kind.rule().noqa_code());
|
||||
@@ -82,6 +81,7 @@ pub(crate) fn check_noqa(
|
||||
false
|
||||
}
|
||||
}
|
||||
Directive::None => unreachable!(),
|
||||
};
|
||||
|
||||
if suppressed {
|
||||
@@ -95,31 +95,36 @@ pub(crate) fn check_noqa(
|
||||
if analyze_directives && settings.rules.enabled(Rule::UnusedNOQA) {
|
||||
for line in noqa_directives.lines() {
|
||||
match &line.directive {
|
||||
Directive::All(directive) => {
|
||||
Directive::All(leading_spaces, noqa_range, trailing_spaces) => {
|
||||
if line.matches.is_empty() {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(UnusedNOQA { codes: None }, directive.range());
|
||||
Diagnostic::new(UnusedNOQA { codes: None }, *noqa_range);
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
#[allow(deprecated)]
|
||||
diagnostic.set_fix_from_edit(delete_noqa(directive.range(), locator));
|
||||
diagnostic.set_fix_from_edit(delete_noqa(
|
||||
*leading_spaces,
|
||||
*noqa_range,
|
||||
*trailing_spaces,
|
||||
locator,
|
||||
));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
Directive::Codes(directive) => {
|
||||
Directive::Codes(leading_spaces, range, codes, trailing_spaces) => {
|
||||
let mut disabled_codes = vec![];
|
||||
let mut unknown_codes = vec![];
|
||||
let mut unmatched_codes = vec![];
|
||||
let mut valid_codes = vec![];
|
||||
let mut self_ignore = false;
|
||||
for code in directive.codes() {
|
||||
for code in codes {
|
||||
let code = get_redirect_target(code).unwrap_or(code);
|
||||
if Rule::UnusedNOQA.noqa_code() == code {
|
||||
self_ignore = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if line.matches.iter().any(|match_| *match_ == code)
|
||||
if line.matches.iter().any(|m| *m == code)
|
||||
|| settings.external.contains(code)
|
||||
{
|
||||
valid_codes.push(code);
|
||||
@@ -161,24 +166,29 @@ pub(crate) fn check_noqa(
|
||||
.collect(),
|
||||
}),
|
||||
},
|
||||
directive.range(),
|
||||
*range,
|
||||
);
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
if valid_codes.is_empty() {
|
||||
#[allow(deprecated)]
|
||||
diagnostic
|
||||
.set_fix_from_edit(delete_noqa(directive.range(), locator));
|
||||
diagnostic.set_fix_from_edit(delete_noqa(
|
||||
*leading_spaces,
|
||||
*range,
|
||||
*trailing_spaces,
|
||||
locator,
|
||||
));
|
||||
} else {
|
||||
#[allow(deprecated)]
|
||||
diagnostic.set_fix(Fix::unspecified(Edit::range_replacement(
|
||||
format!("# noqa: {}", valid_codes.join(", ")),
|
||||
directive.range(),
|
||||
*range,
|
||||
)));
|
||||
}
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
Directive::None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,46 +198,38 @@ pub(crate) fn check_noqa(
|
||||
}
|
||||
|
||||
/// Generate a [`Edit`] to delete a `noqa` directive.
|
||||
fn delete_noqa(range: TextRange, locator: &Locator) -> Edit {
|
||||
let line_range = locator.line_range(range.start());
|
||||
|
||||
// Compute the leading space.
|
||||
let prefix = locator.slice(TextRange::new(line_range.start(), range.start()));
|
||||
let leading_space = prefix
|
||||
.rfind(|c: char| !c.is_whitespace())
|
||||
.map_or(prefix.len(), |i| prefix.len() - i - 1);
|
||||
let leading_space_len = TextSize::try_from(leading_space).unwrap();
|
||||
|
||||
// Compute the trailing space.
|
||||
let suffix = locator.slice(TextRange::new(range.end(), line_range.end()));
|
||||
let trailing_space = suffix
|
||||
.find(|c: char| !c.is_whitespace())
|
||||
.map_or(suffix.len(), |i| i);
|
||||
let trailing_space_len = TextSize::try_from(trailing_space).unwrap();
|
||||
fn delete_noqa(
|
||||
leading_spaces: TextSize,
|
||||
noqa_range: TextRange,
|
||||
trailing_spaces: TextSize,
|
||||
locator: &Locator,
|
||||
) -> Edit {
|
||||
let line_range = locator.line_range(noqa_range.start());
|
||||
|
||||
// Ex) `# noqa`
|
||||
if line_range
|
||||
== TextRange::new(
|
||||
range.start() - leading_space_len,
|
||||
range.end() + trailing_space_len,
|
||||
noqa_range.start() - leading_spaces,
|
||||
noqa_range.end() + trailing_spaces,
|
||||
)
|
||||
{
|
||||
let full_line_end = locator.full_line_end(line_range.end());
|
||||
Edit::deletion(line_range.start(), full_line_end)
|
||||
}
|
||||
// Ex) `x = 1 # noqa`
|
||||
else if range.end() + trailing_space_len == line_range.end() {
|
||||
Edit::deletion(range.start() - leading_space_len, line_range.end())
|
||||
else if noqa_range.end() + trailing_spaces == line_range.end() {
|
||||
Edit::deletion(noqa_range.start() - leading_spaces, line_range.end())
|
||||
}
|
||||
// Ex) `x = 1 # noqa # type: ignore`
|
||||
else if locator.contents()[usize::from(range.end() + trailing_space_len)..].starts_with('#') {
|
||||
Edit::deletion(range.start(), range.end() + trailing_space_len)
|
||||
else if locator.contents()[usize::from(noqa_range.end() + trailing_spaces)..].starts_with('#')
|
||||
{
|
||||
Edit::deletion(noqa_range.start(), noqa_range.end() + trailing_spaces)
|
||||
}
|
||||
// Ex) `x = 1 # noqa here`
|
||||
else {
|
||||
Edit::deletion(
|
||||
range.start() + "# ".text_len(),
|
||||
range.end() + trailing_space_len,
|
||||
noqa_range.start() + "# ".text_len(),
|
||||
noqa_range.end() + trailing_spaces,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use ruff_python_whitespace::UniversalNewlines;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::flake8_copyright::rules::missing_copyright_notice;
|
||||
use crate::rules::flake8_executable::helpers::ShebangDirective;
|
||||
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,
|
||||
};
|
||||
@@ -87,35 +87,33 @@ pub(crate) fn check_physical_lines(
|
||||
|| enforce_shebang_newline
|
||||
|| enforce_shebang_python
|
||||
{
|
||||
if let Some(shebang) = ShebangDirective::try_extract(&line) {
|
||||
has_any_shebang = true;
|
||||
if enforce_shebang_not_executable {
|
||||
if let Some(diagnostic) =
|
||||
shebang_not_executable(path, line.range(), &shebang)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
let shebang = extract_shebang(&line);
|
||||
if enforce_shebang_not_executable {
|
||||
if let Some(diagnostic) = shebang_not_executable(path, line.range(), &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
if enforce_shebang_whitespace {
|
||||
if let Some(diagnostic) =
|
||||
shebang_whitespace(line.range(), &shebang, fix_shebang_whitespace)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if enforce_shebang_missing {
|
||||
if !has_any_shebang && matches!(shebang, ShebangDirective::Match(..)) {
|
||||
has_any_shebang = true;
|
||||
}
|
||||
if enforce_shebang_newline {
|
||||
if let Some(diagnostic) =
|
||||
shebang_newline(line.range(), &shebang, index == 0)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if enforce_shebang_whitespace {
|
||||
if let Some(diagnostic) =
|
||||
shebang_whitespace(line.range(), &shebang, fix_shebang_whitespace)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
if enforce_shebang_python {
|
||||
if let Some(diagnostic) = shebang_python(line.range(), &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if enforce_shebang_newline {
|
||||
if let Some(diagnostic) = shebang_newline(line.range(), &shebang, index == 0) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if enforce_shebang_python {
|
||||
if let Some(diagnostic) = shebang_python(line.range(), &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
use rustpython_parser::Tok;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::source_code::{Indexer, Locator};
|
||||
|
||||
use crate::directives::TodoComment;
|
||||
use crate::lex::docstring_detection::StateMachine;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
@@ -15,6 +12,8 @@ use crate::rules::{
|
||||
flake8_todos, pycodestyle, pylint, pyupgrade, ruff,
|
||||
};
|
||||
use crate::settings::Settings;
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::source_code::{Indexer, Locator};
|
||||
|
||||
pub(crate) fn check_tokens(
|
||||
locator: &Locator,
|
||||
@@ -89,11 +88,10 @@ pub(crate) fn check_tokens(
|
||||
};
|
||||
|
||||
if matches!(tok, Tok::String { .. } | Tok::Comment(_)) {
|
||||
ruff::rules::ambiguous_unicode_character(
|
||||
&mut diagnostics,
|
||||
diagnostics.extend(ruff::rules::ambiguous_unicode_character(
|
||||
locator,
|
||||
range,
|
||||
if tok.is_string() {
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
if is_docstring {
|
||||
Context::Docstring
|
||||
} else {
|
||||
@@ -103,77 +101,93 @@ pub(crate) fn check_tokens(
|
||||
Context::Comment
|
||||
},
|
||||
settings,
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ERA001
|
||||
if enforce_commented_out_code {
|
||||
eradicate::rules::commented_out_code(&mut diagnostics, locator, indexer, settings);
|
||||
diagnostics.extend(eradicate::rules::commented_out_code(
|
||||
locator, indexer, settings,
|
||||
));
|
||||
}
|
||||
|
||||
// W605
|
||||
if enforce_invalid_escape_sequence {
|
||||
for (tok, range) in tokens.iter().flatten() {
|
||||
if tok.is_string() {
|
||||
pycodestyle::rules::invalid_escape_sequence(
|
||||
&mut diagnostics,
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
diagnostics.extend(pycodestyle::rules::invalid_escape_sequence(
|
||||
locator,
|
||||
*range,
|
||||
settings.rules.should_fix(Rule::InvalidEscapeSequence),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
// PLE2510, PLE2512, PLE2513
|
||||
if enforce_invalid_string_character {
|
||||
for (tok, range) in tokens.iter().flatten() {
|
||||
if tok.is_string() {
|
||||
pylint::rules::invalid_string_characters(&mut diagnostics, *range, locator);
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
diagnostics.extend(
|
||||
pylint::rules::invalid_string_characters(locator, *range)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// E701, E702, E703
|
||||
if enforce_compound_statements {
|
||||
pycodestyle::rules::compound_statements(
|
||||
&mut diagnostics,
|
||||
tokens,
|
||||
locator,
|
||||
indexer,
|
||||
settings,
|
||||
diagnostics.extend(
|
||||
pycodestyle::rules::compound_statements(tokens, locator, indexer, settings)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
// Q001, Q002, Q003
|
||||
if enforce_quotes {
|
||||
flake8_quotes::rules::from_tokens(&mut diagnostics, tokens, locator, settings);
|
||||
diagnostics.extend(
|
||||
flake8_quotes::rules::from_tokens(tokens, locator, settings)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
// ISC001, ISC002
|
||||
if enforce_implicit_string_concatenation {
|
||||
flake8_implicit_str_concat::rules::implicit(
|
||||
&mut diagnostics,
|
||||
tokens,
|
||||
&settings.flake8_implicit_str_concat,
|
||||
locator,
|
||||
diagnostics.extend(
|
||||
flake8_implicit_str_concat::rules::implicit(
|
||||
tokens,
|
||||
&settings.flake8_implicit_str_concat,
|
||||
locator,
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
// COM812, COM818, COM819
|
||||
if enforce_trailing_comma {
|
||||
flake8_commas::rules::trailing_commas(&mut diagnostics, tokens, locator, settings);
|
||||
diagnostics.extend(
|
||||
flake8_commas::rules::trailing_commas(tokens, locator, settings)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
// UP034
|
||||
if enforce_extraneous_parenthesis {
|
||||
pyupgrade::rules::extraneous_parentheses(&mut diagnostics, tokens, locator, settings);
|
||||
diagnostics.extend(
|
||||
pyupgrade::rules::extraneous_parentheses(tokens, locator, settings).into_iter(),
|
||||
);
|
||||
}
|
||||
|
||||
// PYI033
|
||||
if enforce_type_comment_in_stub && is_stub {
|
||||
flake8_pyi::rules::type_comment_in_stub(&mut diagnostics, locator, indexer);
|
||||
diagnostics.extend(flake8_pyi::rules::type_comment_in_stub(locator, indexer));
|
||||
}
|
||||
|
||||
// TD001, TD002, TD003, TD004, TD005, TD006, TD007
|
||||
@@ -189,12 +203,18 @@ pub(crate) fn check_tokens(
|
||||
})
|
||||
.collect();
|
||||
|
||||
flake8_todos::rules::todos(&mut diagnostics, &todo_comments, locator, indexer, settings);
|
||||
diagnostics.extend(
|
||||
flake8_todos::rules::todos(&todo_comments, locator, indexer, settings)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
|
||||
flake8_fixme::rules::todos(&mut diagnostics, &todo_comments);
|
||||
diagnostics.extend(
|
||||
flake8_fixme::rules::todos(&todo_comments)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
diagnostics.retain(|diagnostic| settings.rules.enabled(diagnostic.kind.rule()));
|
||||
|
||||
diagnostics
|
||||
}
|
||||
|
||||
@@ -14,18 +14,6 @@ use crate::rules;
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct NoqaCode(&'static str, &'static str);
|
||||
|
||||
impl NoqaCode {
|
||||
/// Return the prefix for the [`NoqaCode`], e.g., `SIM` for `SIM101`.
|
||||
pub fn prefix(&self) -> &str {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Return the suffix for the [`NoqaCode`], e.g., `101` for `SIM101`.
|
||||
pub fn suffix(&self) -> &str {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for NoqaCode {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self, f)
|
||||
@@ -168,10 +156,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pyflakes, "901") => (RuleGroup::Unspecified, rules::pyflakes::rules::RaiseNotImplemented),
|
||||
|
||||
// pylint
|
||||
(Pylint, "C0105") => (RuleGroup::Unspecified, rules::pylint::rules::TypeNameIncorrectVariance),
|
||||
(Pylint, "C0131") => (RuleGroup::Unspecified, rules::pylint::rules::TypeBivariance),
|
||||
(Pylint, "C0132") => (RuleGroup::Unspecified, rules::pylint::rules::TypeParamNameMismatch),
|
||||
(Pylint, "C0205") => (RuleGroup::Unspecified, rules::pylint::rules::SingleStringSlots),
|
||||
(Pylint, "C0414") => (RuleGroup::Unspecified, rules::pylint::rules::UselessImportAlias),
|
||||
(Pylint, "C1901") => (RuleGroup::Nursery, rules::pylint::rules::CompareToEmptyString),
|
||||
(Pylint, "C3002") => (RuleGroup::Unspecified, rules::pylint::rules::UnnecessaryDirectLambdaCall),
|
||||
@@ -266,7 +250,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Bugbear, "031") => (RuleGroup::Unspecified, rules::flake8_bugbear::rules::ReuseOfGroupbyGenerator),
|
||||
(Flake8Bugbear, "032") => (RuleGroup::Unspecified, rules::flake8_bugbear::rules::UnintentionalTypeAnnotation),
|
||||
(Flake8Bugbear, "033") => (RuleGroup::Unspecified, rules::flake8_bugbear::rules::DuplicateValue),
|
||||
(Flake8Bugbear, "034") => (RuleGroup::Unspecified, rules::flake8_bugbear::rules::ReSubPositionalArgs),
|
||||
(Flake8Bugbear, "904") => (RuleGroup::Unspecified, rules::flake8_bugbear::rules::RaiseWithoutFromInsideExcept),
|
||||
(Flake8Bugbear, "905") => (RuleGroup::Unspecified, rules::flake8_bugbear::rules::ZipWithoutExplicitStrict),
|
||||
|
||||
@@ -391,8 +374,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Simplify, "401") => (RuleGroup::Unspecified, rules::flake8_simplify::rules::IfElseBlockInsteadOfDictGet),
|
||||
(Flake8Simplify, "910") => (RuleGroup::Unspecified, rules::flake8_simplify::rules::DictGetWithNoneDefault),
|
||||
|
||||
// flake8-copyright
|
||||
(Flake8Copyright, "001") => (RuleGroup::Nursery, rules::flake8_copyright::rules::MissingCopyrightNotice),
|
||||
// copyright
|
||||
(Copyright, "001") => (RuleGroup::Nursery, rules::flake8_copyright::rules::MissingCopyrightNotice),
|
||||
|
||||
// pyupgrade
|
||||
(Pyupgrade, "001") => (RuleGroup::Unspecified, rules::pyupgrade::rules::UselessMetaclassType),
|
||||
@@ -612,10 +595,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
|
||||
// flake8-pyi
|
||||
(Flake8Pyi, "001") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnprefixedTypeParam),
|
||||
(Flake8Pyi, "002") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::ComplexIfStatementInStub),
|
||||
(Flake8Pyi, "003") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnrecognizedVersionInfoCheck),
|
||||
(Flake8Pyi, "004") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::PatchVersionComparison),
|
||||
(Flake8Pyi, "005") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::WrongTupleLengthVersionComparison),
|
||||
(Flake8Pyi, "006") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::BadVersionInfoComparison),
|
||||
(Flake8Pyi, "007") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnrecognizedPlatformCheck),
|
||||
(Flake8Pyi, "008") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnrecognizedPlatformName),
|
||||
@@ -632,7 +611,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Pyi, "024") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::CollectionsNamedTuple),
|
||||
(Flake8Pyi, "025") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnaliasedCollectionsAbcSetImport),
|
||||
(Flake8Pyi, "029") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::StrOrReprDefinedInStub),
|
||||
(Flake8Pyi, "030") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnnecessaryLiteralUnion),
|
||||
(Flake8Pyi, "032") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::AnyEqNeAnnotation),
|
||||
(Flake8Pyi, "033") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::TypeCommentInStub),
|
||||
(Flake8Pyi, "034") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::NonSelfReturnType),
|
||||
@@ -763,7 +741,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
// numpy
|
||||
(Numpy, "001") => (RuleGroup::Unspecified, rules::numpy::rules::NumpyDeprecatedTypeAlias),
|
||||
(Numpy, "002") => (RuleGroup::Unspecified, rules::numpy::rules::NumpyLegacyRandom),
|
||||
(Numpy, "003") => (RuleGroup::Unspecified, rules::numpy::rules::NumpyDeprecatedFunction),
|
||||
|
||||
// ruff
|
||||
(Ruff, "001") => (RuleGroup::Unspecified, rules::ruff::rules::AmbiguousUnicodeCharacterString),
|
||||
@@ -778,10 +755,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Ruff, "011") => (RuleGroup::Unspecified, rules::ruff::rules::StaticKeyDictComprehension),
|
||||
(Ruff, "012") => (RuleGroup::Unspecified, rules::ruff::rules::MutableClassDefault),
|
||||
(Ruff, "013") => (RuleGroup::Unspecified, rules::ruff::rules::ImplicitOptional),
|
||||
#[cfg(feature = "unreachable-code")]
|
||||
(Ruff, "014") => (RuleGroup::Nursery, rules::ruff::rules::UnreachableCode),
|
||||
(Ruff, "015") => (RuleGroup::Unspecified, rules::ruff::rules::UnnecessaryIterableAllocationForFirstElement),
|
||||
(Ruff, "016") => (RuleGroup::Unspecified, rules::ruff::rules::InvalidIndexType),
|
||||
(Ruff, "100") => (RuleGroup::Unspecified, rules::ruff::rules::UnusedNOQA),
|
||||
(Ruff, "200") => (RuleGroup::Unspecified, rules::ruff::rules::InvalidPyprojectToml),
|
||||
|
||||
@@ -811,11 +784,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Airflow, "001") => (RuleGroup::Unspecified, rules::airflow::rules::AirflowVariableNameTaskIdMismatch),
|
||||
|
||||
// perflint
|
||||
(Perflint, "101") => (RuleGroup::Unspecified, rules::perflint::rules::UnnecessaryListCast),
|
||||
(Perflint, "102") => (RuleGroup::Unspecified, rules::perflint::rules::IncorrectDictIterator),
|
||||
(Perflint, "203") => (RuleGroup::Unspecified, rules::perflint::rules::TryExceptInLoop),
|
||||
(Perflint, "401") => (RuleGroup::Unspecified, rules::perflint::rules::ManualListComprehension),
|
||||
(Perflint, "402") => (RuleGroup::Unspecified, rules::perflint::rules::ManualListCopy),
|
||||
|
||||
// flake8-fixme
|
||||
(Flake8Fixme, "001") => (RuleGroup::Unspecified, rules::flake8_fixme::rules::LineContainsFixme),
|
||||
|
||||
@@ -427,22 +427,22 @@ ghi
|
||||
NoqaMapping::from_iter([TextRange::new(TextSize::from(6), TextSize::from(28))])
|
||||
);
|
||||
|
||||
let contents = r"x = \
|
||||
1";
|
||||
let contents = r#"x = \
|
||||
1"#;
|
||||
assert_eq!(
|
||||
noqa_mappings(contents),
|
||||
NoqaMapping::from_iter([TextRange::new(TextSize::from(0), TextSize::from(6))])
|
||||
);
|
||||
|
||||
let contents = r"from foo import \
|
||||
let contents = r#"from foo import \
|
||||
bar as baz, \
|
||||
qux as quux";
|
||||
qux as quux"#;
|
||||
assert_eq!(
|
||||
noqa_mappings(contents),
|
||||
NoqaMapping::from_iter([TextRange::new(TextSize::from(0), TextSize::from(36))])
|
||||
);
|
||||
|
||||
let contents = r"
|
||||
let contents = r#"
|
||||
# Foo
|
||||
from foo import \
|
||||
bar as baz, \
|
||||
@@ -450,7 +450,7 @@ from foo import \
|
||||
x = \
|
||||
1
|
||||
y = \
|
||||
2";
|
||||
2"#;
|
||||
assert_eq!(
|
||||
noqa_mappings(contents),
|
||||
NoqaMapping::from_iter([
|
||||
|
||||
@@ -18,6 +18,7 @@ pub(crate) struct Docstring<'a> {
|
||||
pub(crate) expr: &'a Expr,
|
||||
/// The content of the docstring, including the leading and trailing quotes.
|
||||
pub(crate) contents: &'a str,
|
||||
|
||||
/// The range of the docstring body (without the quotes). The range is relative to [`Self::contents`].
|
||||
pub(crate) body_range: TextRange,
|
||||
pub(crate) indentation: &'a str,
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruff_python_ast::docstrings::{leading_space, leading_words};
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
use ruff_python_whitespace::{Line, UniversalNewlineIterator, UniversalNewlines};
|
||||
use ruff_python_whitespace::{UniversalNewlineIterator, UniversalNewlines};
|
||||
|
||||
use crate::docstrings::styles::SectionStyle;
|
||||
use crate::docstrings::{Docstring, DocstringBody};
|
||||
@@ -144,13 +144,15 @@ impl<'a> SectionContexts<'a> {
|
||||
|
||||
let mut contexts = Vec::new();
|
||||
let mut last: Option<SectionContextData> = None;
|
||||
let mut previous_line = None;
|
||||
|
||||
let mut lines = contents.universal_newlines().peekable();
|
||||
for line in contents.universal_newlines() {
|
||||
if previous_line.is_none() {
|
||||
// skip the first line
|
||||
previous_line = Some(line.as_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip the first line, which is the summary.
|
||||
let mut previous_line = lines.next();
|
||||
|
||||
while let Some(line) = lines.next() {
|
||||
if let Some(section_kind) = suspected_as_section(&line, style) {
|
||||
let indent = leading_space(&line);
|
||||
let section_name = leading_words(&line);
|
||||
@@ -160,8 +162,7 @@ impl<'a> SectionContexts<'a> {
|
||||
if is_docstring_section(
|
||||
&line,
|
||||
section_name_range,
|
||||
previous_line.as_ref(),
|
||||
lines.peek(),
|
||||
previous_line.unwrap_or_default(),
|
||||
) {
|
||||
if let Some(mut last) = last.take() {
|
||||
last.range = TextRange::new(last.range.start(), line.start());
|
||||
@@ -177,7 +178,7 @@ impl<'a> SectionContexts<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
previous_line = Some(line);
|
||||
previous_line = Some(line.as_str());
|
||||
}
|
||||
|
||||
if let Some(mut last) = last.take() {
|
||||
@@ -387,13 +388,7 @@ fn suspected_as_section(line: &str, style: SectionStyle) -> Option<SectionKind>
|
||||
}
|
||||
|
||||
/// Check if the suspected context is really a section header.
|
||||
fn is_docstring_section(
|
||||
line: &Line,
|
||||
section_name_range: TextRange,
|
||||
previous_line: Option<&Line>,
|
||||
next_line: Option<&Line>,
|
||||
) -> bool {
|
||||
// Determine whether the current line looks like a section header, e.g., "Args:".
|
||||
fn is_docstring_section(line: &str, section_name_range: TextRange, previous_lines: &str) -> bool {
|
||||
let section_name_suffix = line[usize::from(section_name_range.end())..].trim();
|
||||
let this_looks_like_a_section_name =
|
||||
section_name_suffix == ":" || section_name_suffix.is_empty();
|
||||
@@ -401,29 +396,13 @@ fn is_docstring_section(
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine whether the next line is an underline, e.g., "-----".
|
||||
let next_line_is_underline = next_line.map_or(false, |next_line| {
|
||||
let next_line = next_line.trim();
|
||||
if next_line.is_empty() {
|
||||
false
|
||||
} else {
|
||||
let next_line_is_underline = next_line.chars().all(|char| matches!(char, '-' | '='));
|
||||
next_line_is_underline
|
||||
}
|
||||
});
|
||||
if next_line_is_underline {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Determine whether the previous line looks like the end of a paragraph.
|
||||
let previous_line_looks_like_end_of_paragraph = previous_line.map_or(true, |previous_line| {
|
||||
let previous_line = previous_line.trim();
|
||||
let previous_line_ends_with_punctuation = [',', ';', '.', '-', '\\', '/', ']', '}', ')']
|
||||
.into_iter()
|
||||
.any(|char| previous_line.ends_with(char));
|
||||
previous_line_ends_with_punctuation || previous_line.is_empty()
|
||||
});
|
||||
if !previous_line_looks_like_end_of_paragraph {
|
||||
let prev_line = previous_lines.trim();
|
||||
let prev_line_ends_with_punctuation = [',', ';', '.', '-', '\\', '/', ']', '}', ')']
|
||||
.into_iter()
|
||||
.any(|char| prev_line.ends_with(char));
|
||||
let prev_line_looks_like_end_of_paragraph =
|
||||
prev_line_ends_with_punctuation || prev_line.is_empty();
|
||||
if !prev_line_looks_like_end_of_paragraph {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -333,7 +333,7 @@ pub(crate) fn infer_plugins_from_codes(selectors: &HashSet<RuleSelector>) -> Vec
|
||||
for selector in selectors {
|
||||
if selector
|
||||
.into_iter()
|
||||
.any(|rule| Linter::from(plugin).rules().any(|r| r == rule))
|
||||
.any(|rule| Linter::from(plugin).into_iter().any(|r| r == rule))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user