Compare commits
33 Commits
zb/fuzz-ca
...
micha/unio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
242a1bcccb | ||
|
|
3c52d2d1bd | ||
|
|
942d6eeb9f | ||
|
|
4ccacc80f9 | ||
|
|
b2bb119c6a | ||
|
|
cef12f4925 | ||
|
|
aa7ac2ce0f | ||
|
|
70d9c90827 | ||
|
|
adfa723464 | ||
|
|
844c07f1f0 | ||
|
|
11d20a1a51 | ||
|
|
e9079e7d95 | ||
|
|
c400725713 | ||
|
|
1081694140 | ||
|
|
52f526eb38 | ||
|
|
dc05b38165 | ||
|
|
8c3c5ee5e3 | ||
|
|
b46cc6ac0b | ||
|
|
8b925ea626 | ||
|
|
1b180c8342 | ||
|
|
afeb217452 | ||
|
|
c0b3dd3745 | ||
|
|
5f6607bf54 | ||
|
|
a6deca44b5 | ||
|
|
0dbceccbc1 | ||
|
|
48680e10b6 | ||
|
|
b0c88a2a42 | ||
|
|
b9c53a74f9 | ||
|
|
6a4d207db7 | ||
|
|
42c35b6f44 | ||
|
|
9e79d64d62 | ||
|
|
582857f292 | ||
|
|
9bbeb793e5 |
25
.github/workflows/ci.yaml
vendored
25
.github/workflows/ci.yaml
vendored
@@ -115,7 +115,7 @@ jobs:
|
||||
|
||||
cargo-test-linux:
|
||||
name: "cargo test (linux)"
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-22.04-16
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 20
|
||||
@@ -159,7 +159,7 @@ jobs:
|
||||
|
||||
cargo-test-windows:
|
||||
name: "cargo test (windows)"
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-latest-xlarge
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 20
|
||||
@@ -197,6 +197,8 @@ jobs:
|
||||
cache: "npm"
|
||||
cache-dependency-path: playground/package-lock.json
|
||||
- uses: jetli/wasm-pack-action@v0.4.0
|
||||
with:
|
||||
version: v0.13.1
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Test ruff_wasm"
|
||||
run: |
|
||||
@@ -211,7 +213,7 @@ jobs:
|
||||
name: "cargo build (release)"
|
||||
runs-on: macos-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -255,11 +257,11 @@ jobs:
|
||||
NEXTEST_PROFILE: "ci"
|
||||
run: cargo +${{ steps.msrv.outputs.value }} insta test --all-features --unreferenced reject --test-runner nextest
|
||||
|
||||
cargo-fuzz:
|
||||
name: "cargo fuzz"
|
||||
cargo-fuzz-build:
|
||||
name: "cargo fuzz build"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -268,7 +270,6 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: "fuzz -> target"
|
||||
cache-all-crates: "true"
|
||||
- name: "Install cargo-binstall"
|
||||
uses: cargo-bins/cargo-binstall@main
|
||||
with:
|
||||
@@ -279,7 +280,7 @@ jobs:
|
||||
- run: cargo fuzz build -s none
|
||||
|
||||
fuzz-parser:
|
||||
name: "Fuzz the parser"
|
||||
name: "fuzz parser"
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- cargo-test-linux
|
||||
@@ -332,7 +333,7 @@ jobs:
|
||||
|
||||
ecosystem:
|
||||
name: "ecosystem"
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-latest-8
|
||||
needs:
|
||||
- cargo-test-linux
|
||||
- determine_changes
|
||||
@@ -562,12 +563,12 @@ jobs:
|
||||
run: rustup show
|
||||
- name: "Cache rust"
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: "Formatter progress"
|
||||
- name: "Run checks"
|
||||
run: scripts/formatter_ecosystem_checks.sh
|
||||
- name: "Github step summary"
|
||||
run: cat target/progress_projects_stats.txt > $GITHUB_STEP_SUMMARY
|
||||
run: cat target/formatter-ecosystem/stats.txt > $GITHUB_STEP_SUMMARY
|
||||
- name: "Remove checkouts from cache"
|
||||
run: rm -r target/progress_projects
|
||||
run: rm -r target/formatter-ecosystem
|
||||
|
||||
check-ruff-lsp:
|
||||
name: "test ruff-lsp"
|
||||
|
||||
36
.github/workflows/release.yml
vendored
36
.github/workflows/release.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# This file was autogenerated by cargo-dist: https://opensource.axo.dev/cargo-dist/
|
||||
# This file was autogenerated by dist: https://opensource.axo.dev/cargo-dist/
|
||||
#
|
||||
# Copyright 2022-2024, axodotdev
|
||||
# SPDX-License-Identifier: MIT or Apache-2.0
|
||||
@@ -6,7 +6,7 @@
|
||||
# CI that:
|
||||
#
|
||||
# * checks for a Git Tag that looks like a release
|
||||
# * builds artifacts with cargo-dist (archives, installers, hashes)
|
||||
# * builds artifacts with dist (archives, installers, hashes)
|
||||
# * uploads those artifacts to temporary workflow zip
|
||||
# * on success, uploads the artifacts to a GitHub Release
|
||||
#
|
||||
@@ -24,10 +24,10 @@ permissions:
|
||||
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
|
||||
#
|
||||
# If PACKAGE_NAME is specified, then the announcement will be for that
|
||||
# package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
|
||||
# package (erroring out if it doesn't have the given version or isn't dist-able).
|
||||
#
|
||||
# If PACKAGE_NAME isn't specified, then the announcement will be for all
|
||||
# (cargo-dist-able) packages in the workspace with that version (this mode is
|
||||
# (dist-able) packages in the workspace with that version (this mode is
|
||||
# intended for workspaces with only one dist-able package, or with all dist-able
|
||||
# packages versioned/released in lockstep).
|
||||
#
|
||||
@@ -48,7 +48,7 @@ on:
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
# Run 'cargo dist plan' (or host) to determine what tasks we need to do
|
||||
# Run 'dist plan' (or host) to determine what tasks we need to do
|
||||
plan:
|
||||
runs-on: "ubuntu-20.04"
|
||||
outputs:
|
||||
@@ -62,16 +62,16 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install cargo-dist
|
||||
- name: Install dist
|
||||
# we specify bash to get pipefail; it guards against the `curl` command
|
||||
# failing. otherwise `sh` won't catch that `curl` returned non-0
|
||||
shell: bash
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.22.1/cargo-dist-installer.sh | sh"
|
||||
- name: Cache cargo-dist
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.25.2-prerelease.3/cargo-dist-installer.sh | sh"
|
||||
- name: Cache dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/cargo-dist
|
||||
path: ~/.cargo/bin/dist
|
||||
# sure would be cool if github gave us proper conditionals...
|
||||
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
|
||||
# functionality based on whether this is a pull_request, and whether it's from a fork.
|
||||
@@ -79,8 +79,8 @@ jobs:
|
||||
# but also really annoying to build CI around when it needs secrets to work right.)
|
||||
- id: plan
|
||||
run: |
|
||||
cargo dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --output-format=json > plan-dist-manifest.json
|
||||
echo "cargo dist ran successfully"
|
||||
dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --output-format=json > plan-dist-manifest.json
|
||||
echo "dist ran successfully"
|
||||
cat plan-dist-manifest.json
|
||||
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
- name: "Upload dist-manifest.json"
|
||||
@@ -124,12 +124,12 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install cached cargo-dist
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/cargo-dist
|
||||
- run: chmod +x ~/.cargo/bin/dist
|
||||
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
|
||||
- name: Fetch local artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -140,8 +140,8 @@ jobs:
|
||||
- id: cargo-dist
|
||||
shell: bash
|
||||
run: |
|
||||
cargo dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
|
||||
echo "cargo dist ran successfully"
|
||||
dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
|
||||
echo "dist ran successfully"
|
||||
|
||||
# Parse out what we just built and upload it to scratch storage
|
||||
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
|
||||
@@ -174,12 +174,12 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install cached cargo-dist
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/cargo-dist
|
||||
- run: chmod +x ~/.cargo/bin/dist
|
||||
# Fetch artifacts from scratch-storage
|
||||
- name: Fetch artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -191,7 +191,7 @@ jobs:
|
||||
- id: host
|
||||
shell: bash
|
||||
run: |
|
||||
cargo dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
|
||||
dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
|
||||
echo "artifacts uploaded and released successfully"
|
||||
cat dist-manifest.json
|
||||
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
67
Cargo.lock
generated
67
Cargo.lock
generated
@@ -212,6 +212,12 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "boxcar"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f839cdf7e2d3198ac6ca003fd8ebc61715755f41c1cad15ff13df67531e00ed"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.11.0"
|
||||
@@ -1935,18 +1941,6 @@ version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a"
|
||||
|
||||
[[package]]
|
||||
name = "pep440_rs"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0c29f9c43de378b4e4e0cd7dbcce0e5cfb80443de8c05620368b2948bc936a1"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"regex",
|
||||
"serde",
|
||||
"unicode-width 0.1.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pep440_rs"
|
||||
version = "0.7.2"
|
||||
@@ -1956,22 +1950,29 @@ dependencies = [
|
||||
"serde",
|
||||
"unicode-width 0.2.0",
|
||||
"unscanny",
|
||||
"version-ranges",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pep508_rs"
|
||||
version = "0.3.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "910c513bea0f4f833122321c0f20e8c704e01de98692f6989c2ec21f43d88b1e"
|
||||
checksum = "8c2feee999fa547bacab06a4881bacc74688858b92fa8ef1e206c748b0a76048"
|
||||
dependencies = [
|
||||
"boxcar",
|
||||
"indexmap",
|
||||
"itertools 0.13.0",
|
||||
"once_cell",
|
||||
"pep440_rs 0.4.0",
|
||||
"pep440_rs",
|
||||
"regex",
|
||||
"rustc-hash 2.0.0",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"thiserror 1.0.67",
|
||||
"tracing",
|
||||
"unicode-width 0.1.13",
|
||||
"unicode-width 0.2.0",
|
||||
"url",
|
||||
"urlencoding",
|
||||
"version-ranges",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2135,14 +2136,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyproject-toml"
|
||||
version = "0.9.0"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95c3dd745f99aa3c554b7bb00859f7d18c2f1d6afd749ccc86d60b61e702abd9"
|
||||
checksum = "643af57c3f36ba90a8b53e972727d8092f7408a9ebfbaf4c3d2c17b07c58d835"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"pep440_rs 0.4.0",
|
||||
"pep440_rs",
|
||||
"pep508_rs",
|
||||
"serde",
|
||||
"thiserror 1.0.67",
|
||||
"toml",
|
||||
]
|
||||
|
||||
@@ -2370,7 +2372,7 @@ dependencies = [
|
||||
"glob",
|
||||
"insta",
|
||||
"notify",
|
||||
"pep440_rs 0.7.2",
|
||||
"pep440_rs",
|
||||
"rayon",
|
||||
"red_knot_python_semantic",
|
||||
"red_knot_vendored",
|
||||
@@ -2674,7 +2676,7 @@ dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"tracing",
|
||||
"unicode-width 0.1.13",
|
||||
"unicode-width 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2729,7 +2731,7 @@ dependencies = [
|
||||
"natord",
|
||||
"path-absolutize",
|
||||
"pathdiff",
|
||||
"pep440_rs 0.7.2",
|
||||
"pep440_rs",
|
||||
"pyproject-toml",
|
||||
"quick-junit",
|
||||
"regex",
|
||||
@@ -2760,7 +2762,7 @@ dependencies = [
|
||||
"toml",
|
||||
"typed-arena",
|
||||
"unicode-normalization",
|
||||
"unicode-width 0.1.13",
|
||||
"unicode-width 0.2.0",
|
||||
"unicode_names2",
|
||||
"url",
|
||||
]
|
||||
@@ -3060,7 +3062,7 @@ dependencies = [
|
||||
"matchit",
|
||||
"path-absolutize",
|
||||
"path-slash",
|
||||
"pep440_rs 0.7.2",
|
||||
"pep440_rs",
|
||||
"regex",
|
||||
"ruff_cache",
|
||||
"ruff_formatter",
|
||||
@@ -3960,6 +3962,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
@@ -4007,6 +4015,15 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "version-ranges"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8d079415ceb2be83fc355adbadafe401307d5c309c7e6ade6638e6f9f42f42d"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
|
||||
22
Cargo.toml
22
Cargo.toml
@@ -111,7 +111,7 @@ pathdiff = { version = "0.2.1" }
|
||||
pep440_rs = { version = "0.7.1" }
|
||||
pretty_assertions = "1.3.0"
|
||||
proc-macro2 = { version = "1.0.79" }
|
||||
pyproject-toml = { version = "0.9.0" }
|
||||
pyproject-toml = { version = "0.13.4" }
|
||||
quick-junit = { version = "0.5.0" }
|
||||
quote = { version = "1.0.23" }
|
||||
rand = { version = "0.8.5" }
|
||||
@@ -151,7 +151,7 @@ tracing-tree = { version = "0.4.0" }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unic-ucd-category = { version = "0.9" }
|
||||
unicode-ident = { version = "1.0.12" }
|
||||
unicode-width = { version = "0.1.11" }
|
||||
unicode-width = { version = "0.2.0" }
|
||||
unicode_names2 = { version = "1.2.2" }
|
||||
unicode-normalization = { version = "0.1.23" }
|
||||
ureq = { version = "2.9.6" }
|
||||
@@ -248,10 +248,10 @@ debug = 1
|
||||
[profile.dist]
|
||||
inherits = "release"
|
||||
|
||||
# Config for 'cargo dist'
|
||||
# Config for 'dist'
|
||||
[workspace.metadata.dist]
|
||||
# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
|
||||
cargo-dist-version = "0.22.1"
|
||||
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
|
||||
cargo-dist-version = "0.25.2-prerelease.3"
|
||||
# CI backends to support
|
||||
ci = "github"
|
||||
# The installers to generate for each app
|
||||
@@ -282,13 +282,13 @@ targets = [
|
||||
]
|
||||
# Whether to auto-include files like READMEs, LICENSEs, and CHANGELOGs (default true)
|
||||
auto-includes = false
|
||||
# Whether cargo-dist should create a GitHub Release or use an existing draft
|
||||
# Whether dist should create a Github Release or use an existing draft
|
||||
create-release = true
|
||||
# Which actions to run on pull requests
|
||||
pr-run-mode = "skip"
|
||||
# Whether CI should trigger releases with dispatches instead of tag pushes
|
||||
dispatch-releases = true
|
||||
# Which phase cargo-dist should use to create the GitHub release
|
||||
# Which phase dist should use to create the GitHub release
|
||||
github-release = "announce"
|
||||
# Whether CI should include auto-generated code to build local artifacts
|
||||
build-local-artifacts = false
|
||||
@@ -297,14 +297,10 @@ local-artifacts-jobs = ["./build-binaries", "./build-docker"]
|
||||
# Publish jobs to run in CI
|
||||
publish-jobs = ["./publish-pypi", "./publish-wasm"]
|
||||
# Post-announce jobs to run in CI
|
||||
post-announce-jobs = [
|
||||
"./notify-dependents",
|
||||
"./publish-docs",
|
||||
"./publish-playground",
|
||||
]
|
||||
post-announce-jobs = ["./notify-dependents", "./publish-docs", "./publish-playground"]
|
||||
# Custom permissions for GitHub Jobs
|
||||
github-custom-job-permissions = { "build-docker" = { packages = "write", contents = "read" }, "publish-wasm" = { contents = "read", id-token = "write", packages = "write" } }
|
||||
# Whether to install an updater program
|
||||
install-updater = false
|
||||
# Path that installers should place binaries in
|
||||
install-path = "CARGO_HOME"
|
||||
install-path = ["$XDG_BIN_HOME/", "$XDG_DATA_HOME/../bin", "~/.local/bin"]
|
||||
|
||||
@@ -238,8 +238,8 @@ exclude = [
|
||||
line-length = 88
|
||||
indent-width = 4
|
||||
|
||||
# Assume Python 3.8
|
||||
target-version = "py38"
|
||||
# Assume Python 3.9
|
||||
target-version = "py39"
|
||||
|
||||
[lint]
|
||||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
||||
|
||||
@@ -58,7 +58,9 @@ reveal_type(c >= d) # revealed: Literal[True]
|
||||
#### Results with Ambiguity
|
||||
|
||||
```py
|
||||
def bool_instance() -> bool: ...
|
||||
def bool_instance() -> bool:
|
||||
return True
|
||||
|
||||
def int_instance() -> int:
|
||||
return 42
|
||||
|
||||
@@ -134,23 +136,158 @@ reveal_type(c >= c) # revealed: Literal[True]
|
||||
|
||||
#### Non Boolean Rich Comparisons
|
||||
|
||||
Rich comparison methods defined in a class affect tuple comparisons as well. Proper type inference
|
||||
should be possible even in cases where these methods return non-boolean types.
|
||||
|
||||
Note: Tuples use lexicographic comparisons. If the `==` result for all paired elements in the tuple
|
||||
is True, the comparison then considers the tuple’s length. Regardless of the return type of the
|
||||
dunder methods, the final result can still be a boolean value.
|
||||
|
||||
(+cpython: For tuples, `==` and `!=` always produce boolean results, regardless of the return type
|
||||
of the dunder methods.)
|
||||
|
||||
```py
|
||||
from __future__ import annotations
|
||||
|
||||
class A:
|
||||
def __eq__(self, o) -> str: ...
|
||||
def __ne__(self, o) -> int: ...
|
||||
def __lt__(self, o) -> float: ...
|
||||
def __le__(self, o) -> object: ...
|
||||
def __gt__(self, o) -> tuple: ...
|
||||
def __ge__(self, o) -> list: ...
|
||||
def __eq__(self, o: object) -> str:
|
||||
return "hello"
|
||||
|
||||
def __ne__(self, o: object) -> bytes:
|
||||
return b"world"
|
||||
|
||||
def __lt__(self, o: A) -> float:
|
||||
return 3.14
|
||||
|
||||
def __le__(self, o: A) -> complex:
|
||||
return complex(0.5, -0.5)
|
||||
|
||||
def __gt__(self, o: A) -> tuple:
|
||||
return (1, 2, 3)
|
||||
|
||||
def __ge__(self, o: A) -> list:
|
||||
return [1, 2, 3]
|
||||
|
||||
a = (A(), A())
|
||||
|
||||
reveal_type(a == a) # revealed: bool
|
||||
reveal_type(a != a) # revealed: bool
|
||||
reveal_type(a < a) # revealed: float | Literal[False]
|
||||
reveal_type(a <= a) # revealed: complex | Literal[True]
|
||||
reveal_type(a > a) # revealed: tuple | Literal[False]
|
||||
reveal_type(a >= a) # revealed: list | Literal[True]
|
||||
|
||||
# If lexicographic comparison is finished before comparing A()
|
||||
b = ("1_foo", A())
|
||||
c = ("2_bar", A())
|
||||
|
||||
reveal_type(b == c) # revealed: Literal[False]
|
||||
reveal_type(b != c) # revealed: Literal[True]
|
||||
reveal_type(b < c) # revealed: Literal[True]
|
||||
reveal_type(b <= c) # revealed: Literal[True]
|
||||
reveal_type(b > c) # revealed: Literal[False]
|
||||
reveal_type(b >= c) # revealed: Literal[False]
|
||||
|
||||
class B:
|
||||
def __lt__(self, o: B) -> set:
|
||||
return set()
|
||||
|
||||
reveal_type((A(), B()) < (A(), B())) # revealed: float | set | Literal[False]
|
||||
```
|
||||
|
||||
#### Special Handling of Eq and NotEq in Lexicographic Comparisons
|
||||
|
||||
> Example: `(int_instance(), "foo") == (int_instance(), "bar")`
|
||||
|
||||
`Eq` and `NotEq` have unique behavior compared to other operators in lexicographic comparisons.
|
||||
Specifically, for `Eq`, if any non-equal pair exists within the tuples being compared, we can
|
||||
immediately conclude that the tuples are not equal. Conversely, for `NotEq`, if any non-equal pair
|
||||
exists, we can determine that the tuples are unequal.
|
||||
|
||||
In contrast, with operators like `<` and `>`, the comparison must consider each pair of elements
|
||||
sequentially, and the final outcome might remain ambiguous until all pairs are compared.
|
||||
|
||||
```py
|
||||
def str_instance() -> str:
|
||||
return "hello"
|
||||
|
||||
def int_instance() -> int:
|
||||
return 42
|
||||
|
||||
reveal_type("foo" == "bar") # revealed: Literal[False]
|
||||
reveal_type(("foo",) == ("bar",)) # revealed: Literal[False]
|
||||
reveal_type((4, "foo") == (4, "bar")) # revealed: Literal[False]
|
||||
reveal_type((int_instance(), "foo") == (int_instance(), "bar")) # revealed: Literal[False]
|
||||
|
||||
a = (str_instance(), int_instance(), "foo")
|
||||
|
||||
reveal_type(a == a) # revealed: bool
|
||||
reveal_type(a != a) # revealed: bool
|
||||
reveal_type(a < a) # revealed: bool
|
||||
reveal_type(a <= a) # revealed: bool
|
||||
reveal_type(a > a) # revealed: bool
|
||||
reveal_type(a >= a) # revealed: bool
|
||||
|
||||
b = (str_instance(), int_instance(), "bar")
|
||||
|
||||
reveal_type(a == b) # revealed: Literal[False]
|
||||
reveal_type(a != b) # revealed: Literal[True]
|
||||
reveal_type(a < b) # revealed: bool
|
||||
reveal_type(a <= b) # revealed: bool
|
||||
reveal_type(a > b) # revealed: bool
|
||||
reveal_type(a >= b) # revealed: bool
|
||||
|
||||
c = (str_instance(), int_instance(), "foo", "different_length")
|
||||
reveal_type(a == c) # revealed: Literal[False]
|
||||
reveal_type(a != c) # revealed: Literal[True]
|
||||
reveal_type(a < c) # revealed: bool
|
||||
reveal_type(a <= c) # revealed: bool
|
||||
reveal_type(a > c) # revealed: bool
|
||||
reveal_type(a >= c) # revealed: bool
|
||||
```
|
||||
|
||||
#### Error Propagation
|
||||
|
||||
Errors occurring within a tuple comparison should propagate outward. However, if the tuple
|
||||
comparison can clearly conclude before encountering an error, the error should not be raised.
|
||||
|
||||
```py
|
||||
def int_instance() -> int:
|
||||
return 42
|
||||
|
||||
def str_instance() -> str:
|
||||
return "hello"
|
||||
|
||||
class A: ...
|
||||
|
||||
# error: [unsupported-operator] "Operator `<` is not supported for types `A` and `A`"
|
||||
A() < A()
|
||||
# error: [unsupported-operator] "Operator `<=` is not supported for types `A` and `A`"
|
||||
A() <= A()
|
||||
# error: [unsupported-operator] "Operator `>` is not supported for types `A` and `A`"
|
||||
A() > A()
|
||||
# error: [unsupported-operator] "Operator `>=` is not supported for types `A` and `A`"
|
||||
A() >= A()
|
||||
|
||||
a = (0, int_instance(), A())
|
||||
|
||||
# error: [unsupported-operator] "Operator `<` is not supported for types `A` and `A`, in comparing `tuple[Literal[0], int, A]` with `tuple[Literal[0], int, A]`"
|
||||
reveal_type(a < a) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `<=` is not supported for types `A` and `A`, in comparing `tuple[Literal[0], int, A]` with `tuple[Literal[0], int, A]`"
|
||||
reveal_type(a <= a) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `>` is not supported for types `A` and `A`, in comparing `tuple[Literal[0], int, A]` with `tuple[Literal[0], int, A]`"
|
||||
reveal_type(a > a) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `>=` is not supported for types `A` and `A`, in comparing `tuple[Literal[0], int, A]` with `tuple[Literal[0], int, A]`"
|
||||
reveal_type(a >= a) # revealed: Unknown
|
||||
|
||||
# Comparison between `a` and `b` should only involve the first elements, `Literal[0]` and `Literal[99999]`,
|
||||
# and should terminate immediately.
|
||||
b = (99999, int_instance(), A())
|
||||
|
||||
reveal_type(a < b) # revealed: Literal[True]
|
||||
reveal_type(a <= b) # revealed: Literal[True]
|
||||
reveal_type(a > b) # revealed: Literal[False]
|
||||
reveal_type(a >= b) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
### Membership Test Comparisons
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
def bool_instance() -> bool:
|
||||
return True
|
||||
|
||||
class A: ...
|
||||
|
||||
a = 1 in 7 # error: "Operator `in` is not supported for types `Literal[1]` and `Literal[7]`"
|
||||
reveal_type(a) # revealed: bool
|
||||
|
||||
@@ -33,4 +35,8 @@ reveal_type(e) # revealed: bool
|
||||
f = (1, 2) < (1, "hello")
|
||||
# TODO: should be Unknown, once operand type check is implemented
|
||||
reveal_type(f) # revealed: bool
|
||||
|
||||
# error: [unsupported-operator] "Operator `<` is not supported for types `A` and `A`, in comparing `tuple[bool, A]` with `tuple[bool, A]`"
|
||||
g = (bool_instance(), A()) < (bool_instance(), A())
|
||||
reveal_type(g) # revealed: Unknown
|
||||
```
|
||||
|
||||
@@ -58,8 +58,7 @@ reveal_type(sys.version_info >= (3, 9, 1, "final", 0)) # revealed: bool
|
||||
# emitting a lint diagnostic of some kind warning them about the probable error?
|
||||
reveal_type(sys.version_info >= (3, 9, 1, "final", 0, 5)) # revealed: bool
|
||||
|
||||
# TODO: this should be `Literal[False]`; see #14279
|
||||
reveal_type(sys.version_info == (3, 9, 1, "finallllll", 0)) # revealed: bool
|
||||
reveal_type(sys.version_info == (3, 8, 1, "finallllll", 0)) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
## Imports and aliases
|
||||
|
||||
@@ -31,7 +31,7 @@ use crate::{Db, FxOrderSet};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
pub(crate) struct UnionBuilder<'db> {
|
||||
elements: Vec<Type<'db>>,
|
||||
elements: SmallVec<[Type<'db>; 1]>,
|
||||
db: &'db dyn Db,
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ impl<'db> UnionBuilder<'db> {
|
||||
pub(crate) fn new(db: &'db dyn Db) -> Self {
|
||||
Self {
|
||||
db,
|
||||
elements: vec![],
|
||||
elements: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,13 +48,22 @@ impl<'db> UnionBuilder<'db> {
|
||||
match ty {
|
||||
Type::Union(union) => {
|
||||
let new_elements = union.elements(self.db);
|
||||
self.elements.reserve(new_elements.len());
|
||||
for element in new_elements {
|
||||
self = self.add(*element);
|
||||
if self.elements.is_empty() {
|
||||
self.elements.extend_from_slice(new_elements);
|
||||
} else {
|
||||
self.elements.reserve(new_elements.len());
|
||||
for element in new_elements {
|
||||
self = self.add(*element);
|
||||
}
|
||||
}
|
||||
}
|
||||
Type::Never => {}
|
||||
_ => {
|
||||
if self.elements.is_empty() {
|
||||
self.elements.push(ty);
|
||||
return self;
|
||||
}
|
||||
|
||||
let bool_pair = if let Type::BooleanLiteral(b) = ty {
|
||||
Some(Type::BooleanLiteral(!b))
|
||||
} else {
|
||||
|
||||
@@ -3643,16 +3643,16 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
let lhs_elements = lhs.elements(self.db);
|
||||
let rhs_elements = rhs.elements(self.db);
|
||||
|
||||
let mut lexicographic_type_comparison =
|
||||
|op| self.infer_lexicographic_type_comparison(lhs_elements, op, rhs_elements);
|
||||
let mut tuple_rich_comparison =
|
||||
|op| self.infer_tuple_rich_comparison(lhs_elements, op, rhs_elements);
|
||||
|
||||
match op {
|
||||
ast::CmpOp::Eq => lexicographic_type_comparison(RichCompareOperator::Eq),
|
||||
ast::CmpOp::NotEq => lexicographic_type_comparison(RichCompareOperator::Ne),
|
||||
ast::CmpOp::Lt => lexicographic_type_comparison(RichCompareOperator::Lt),
|
||||
ast::CmpOp::LtE => lexicographic_type_comparison(RichCompareOperator::Le),
|
||||
ast::CmpOp::Gt => lexicographic_type_comparison(RichCompareOperator::Gt),
|
||||
ast::CmpOp::GtE => lexicographic_type_comparison(RichCompareOperator::Ge),
|
||||
ast::CmpOp::Eq => tuple_rich_comparison(RichCompareOperator::Eq),
|
||||
ast::CmpOp::NotEq => tuple_rich_comparison(RichCompareOperator::Ne),
|
||||
ast::CmpOp::Lt => tuple_rich_comparison(RichCompareOperator::Lt),
|
||||
ast::CmpOp::LtE => tuple_rich_comparison(RichCompareOperator::Le),
|
||||
ast::CmpOp::Gt => tuple_rich_comparison(RichCompareOperator::Gt),
|
||||
ast::CmpOp::GtE => tuple_rich_comparison(RichCompareOperator::Ge),
|
||||
ast::CmpOp::In | ast::CmpOp::NotIn => {
|
||||
let mut eq_count = 0usize;
|
||||
let mut not_eq_count = 0usize;
|
||||
@@ -3685,8 +3685,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
ast::CmpOp::Is | ast::CmpOp::IsNot => {
|
||||
// - `[ast::CmpOp::Is]`: returns `false` if the elements are definitely unequal, otherwise `bool`
|
||||
// - `[ast::CmpOp::IsNot]`: returns `true` if the elements are definitely unequal, otherwise `bool`
|
||||
let eq_result = lexicographic_type_comparison(RichCompareOperator::Eq)
|
||||
.expect(
|
||||
let eq_result = tuple_rich_comparison(RichCompareOperator::Eq).expect(
|
||||
"infer_binary_type_comparison should never return None for `CmpOp::Eq`",
|
||||
);
|
||||
|
||||
@@ -3751,53 +3750,80 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs lexicographic comparison between two slices of types.
|
||||
/// Simulates rich comparison between tuples and returns the inferred result.
|
||||
/// This performs a lexicographic comparison, returning a union of all possible return types that could result from the comparison.
|
||||
///
|
||||
/// For lexicographic comparison, elements from both slices are compared pairwise using
|
||||
/// `infer_binary_type_comparison`. If a conclusive result cannot be determined as a `BooleanLiteral`,
|
||||
/// it returns `bool`. Returns `None` if the comparison is not supported.
|
||||
fn infer_lexicographic_type_comparison(
|
||||
/// basically it's based on cpython's `tuple_richcompare`
|
||||
/// see `<https://github.com/python/cpython/blob/9d6366b60d01305fc5e45100e0cd13e358aa397d/Objects/tupleobject.c#L637>`
|
||||
fn infer_tuple_rich_comparison(
|
||||
&mut self,
|
||||
left: &[Type<'db>],
|
||||
op: RichCompareOperator,
|
||||
right: &[Type<'db>],
|
||||
) -> Result<Type<'db>, CompareUnsupportedError<'db>> {
|
||||
// Compare paired elements from left and right slices
|
||||
for (l_ty, r_ty) in left.iter().copied().zip(right.iter().copied()) {
|
||||
let eq_result = self
|
||||
let left_iter = left.iter().copied();
|
||||
let right_iter = right.iter().copied();
|
||||
|
||||
let mut builder = UnionBuilder::new(self.db);
|
||||
|
||||
for (l_ty, r_ty) in left_iter.zip(right_iter) {
|
||||
let pairwise_eq_result = self
|
||||
.infer_binary_type_comparison(l_ty, ast::CmpOp::Eq, r_ty)
|
||||
.expect("infer_binary_type_comparison should never return None for `CmpOp::Eq`");
|
||||
|
||||
match eq_result {
|
||||
match pairwise_eq_result {
|
||||
// If propagation is required, return the result as is
|
||||
Type::Todo => return Ok(Type::Todo),
|
||||
ty => match ty.bool(self.db) {
|
||||
// Types are equal, continue to the next pair
|
||||
// - AlwaysTrue : Continue to the next pair for lexicographic comparison
|
||||
Truthiness::AlwaysTrue => continue,
|
||||
// Types are not equal, perform the specified comparison and return the result
|
||||
Truthiness::AlwaysFalse => {
|
||||
return self.infer_binary_type_comparison(l_ty, op.into(), r_ty)
|
||||
// - AlwaysFalse:
|
||||
// Lexicographic comparisons will always terminate with this pair.
|
||||
// Complete the comparison and return the result.
|
||||
// - Ambiguous:
|
||||
// Lexicographic comparisons might continue to the next pair (if eq_result is true),
|
||||
// or terminate here (if eq_result is false).
|
||||
// To account for cases where the comparison terminates here, add the pairwise comparison result to the union builder.
|
||||
eq_truthiness @ (Truthiness::AlwaysFalse | Truthiness::Ambiguous) => {
|
||||
let pairwise_compare_result = match op {
|
||||
RichCompareOperator::Lt
|
||||
| RichCompareOperator::Le
|
||||
| RichCompareOperator::Gt
|
||||
| RichCompareOperator::Ge => {
|
||||
self.infer_binary_type_comparison(l_ty, op.into(), r_ty)?
|
||||
}
|
||||
// For `==` and `!=`, we already figure out the result from `pairwise_eq_result`
|
||||
// NOTE: The CPython implementation does not account for non-boolean return types
|
||||
// or cases where `!=` is not the negation of `==`, we also do not consider these cases.
|
||||
RichCompareOperator::Eq => Type::BooleanLiteral(false),
|
||||
RichCompareOperator::Ne => Type::BooleanLiteral(true),
|
||||
};
|
||||
|
||||
builder = builder.add(pairwise_compare_result);
|
||||
|
||||
if eq_truthiness.is_ambiguous() {
|
||||
continue;
|
||||
}
|
||||
|
||||
return Ok(builder.build());
|
||||
}
|
||||
// If the intermediate result is ambiguous, we cannot determine the final result as BooleanLiteral.
|
||||
// In this case, we simply return a bool instance.
|
||||
Truthiness::Ambiguous => return Ok(KnownClass::Bool.to_instance(self.db)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, the lengths of the two slices may be different, but the prefix of
|
||||
// left and right slices is entirely identical.
|
||||
// We return a comparison of the slice lengths based on the operator.
|
||||
// if no more items to compare, we just compare sizes
|
||||
let (left_len, right_len) = (left.len(), right.len());
|
||||
|
||||
Ok(Type::BooleanLiteral(match op {
|
||||
builder = builder.add(Type::BooleanLiteral(match op {
|
||||
RichCompareOperator::Eq => left_len == right_len,
|
||||
RichCompareOperator::Ne => left_len != right_len,
|
||||
RichCompareOperator::Lt => left_len < right_len,
|
||||
RichCompareOperator::Le => left_len <= right_len,
|
||||
RichCompareOperator::Gt => left_len > right_len,
|
||||
RichCompareOperator::Ge => left_len >= right_len,
|
||||
}))
|
||||
}));
|
||||
|
||||
Ok(builder.build())
|
||||
}
|
||||
|
||||
fn infer_subscript_expression(&mut self, subscript: &ast::ExprSubscript) -> Type<'db> {
|
||||
|
||||
@@ -840,7 +840,7 @@ fn stdin_multiple_parse_error() {
|
||||
|
||||
#[test]
|
||||
fn parse_error_not_included() {
|
||||
// Select any rule except for `E999`, syntax error should still be shown.
|
||||
// Parse errors are always shown
|
||||
let mut cmd = RuffCheck::default().args(["--select=I"]).build();
|
||||
assert_cmd_snapshot!(cmd
|
||||
.pass_stdin("foo =\n"), @r"
|
||||
@@ -859,27 +859,6 @@ fn parse_error_not_included() {
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deprecated_parse_error_selection() {
|
||||
let mut cmd = RuffCheck::default().args(["--select=E999"]).build();
|
||||
assert_cmd_snapshot!(cmd
|
||||
.pass_stdin("foo =\n"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:6: SyntaxError: Expected an expression
|
||||
|
|
||||
1 | foo =
|
||||
| ^
|
||||
|
|
||||
|
||||
Found 1 error.
|
||||
|
||||
----- stderr -----
|
||||
warning: Rule `E999` is deprecated and will be removed in a future release. Syntax errors will always be shown regardless of whether this rule is selected or not.
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_output_preview() {
|
||||
let mut cmd = RuffCheck::default().args(["--preview"]).build();
|
||||
@@ -1250,6 +1229,68 @@ fn removed_indirect() {
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removed_ignore_direct() {
|
||||
let mut cmd = RuffCheck::default().args(["--ignore", "UP027"]).build();
|
||||
assert_cmd_snapshot!(cmd, @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
warning: The following rules have been removed and ignoring them has no effect:
|
||||
- UP027
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removed_ignore_multiple_direct() {
|
||||
let mut cmd = RuffCheck::default()
|
||||
.args(["--ignore", "UP027", "--ignore", "PLR1706"])
|
||||
.build();
|
||||
assert_cmd_snapshot!(cmd, @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
warning: The following rules have been removed and ignoring them has no effect:
|
||||
- PLR1706
|
||||
- UP027
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removed_ignore_remapped_direct() {
|
||||
let mut cmd = RuffCheck::default().args(["--ignore", "PGH001"]).build();
|
||||
assert_cmd_snapshot!(cmd, @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
warning: `PGH001` has been remapped to `S307`.
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removed_ignore_indirect() {
|
||||
// `PLR170` includes removed rules but should not select or warn
|
||||
// since it is not a "direct" selection
|
||||
let mut cmd = RuffCheck::default().args(["--ignore", "PLR170"]).build();
|
||||
assert_cmd_snapshot!(cmd, @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redirect_direct() {
|
||||
// Selection of a redirected rule directly should use the new rule and warn
|
||||
|
||||
@@ -88,7 +88,6 @@ linter.rules.enabled = [
|
||||
ambiguous-class-name (E742),
|
||||
ambiguous-function-name (E743),
|
||||
io-error (E902),
|
||||
syntax-error (E999),
|
||||
unused-import (F401),
|
||||
import-shadowed-by-loop-var (F402),
|
||||
undefined-local-with-import-star (F403),
|
||||
@@ -150,7 +149,6 @@ linter.rules.should_fix = [
|
||||
ambiguous-class-name (E742),
|
||||
ambiguous-function-name (E743),
|
||||
io-error (E902),
|
||||
syntax-error (E999),
|
||||
unused-import (F401),
|
||||
import-shadowed-by-loop-var (F402),
|
||||
undefined-local-with-import-star (F403),
|
||||
|
||||
@@ -71,7 +71,7 @@ class Foo:
|
||||
def foo(self: "Foo", a: int, b: int) -> int:
|
||||
pass
|
||||
|
||||
# ANN101
|
||||
# OK
|
||||
def foo(self, a: int, b: int) -> int:
|
||||
pass
|
||||
|
||||
@@ -125,12 +125,12 @@ class Foo:
|
||||
def foo(cls: Type["Foo"], a: int, b: int) -> int:
|
||||
pass
|
||||
|
||||
# ANN102
|
||||
# OK
|
||||
@classmethod
|
||||
def foo(cls, a: int, b: int) -> int:
|
||||
pass
|
||||
|
||||
# ANN101
|
||||
# OK
|
||||
def foo(self, /, a: int, b: int) -> int:
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import abc
|
||||
from abc import abstractmethod
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def _patch_something(mocker): # OK simple
|
||||
mocker.patch("some.thing")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def _patch_something(mocker): # OK with return
|
||||
if something:
|
||||
return
|
||||
mocker.patch("some.thing")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def _activate_context(): # OK with yield
|
||||
with context:
|
||||
yield
|
||||
|
||||
|
||||
class BaseTest:
|
||||
@pytest.fixture()
|
||||
@abc.abstractmethod
|
||||
def my_fixture(): # OK abstract with import abc
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class BaseTest:
|
||||
@pytest.fixture()
|
||||
@abstractmethod
|
||||
def my_fixture(): # OK abstract with from import
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def my_fixture(): # OK ignoring yield from
|
||||
yield from some_generator()
|
||||
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def my_fixture(): # OK ignoring yield value
|
||||
yield 1
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def patch_something(mocker): # Error simple
|
||||
mocker.patch("some.thing")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def activate_context(): # Error with yield
|
||||
with context:
|
||||
yield
|
||||
@@ -1,57 +0,0 @@
|
||||
import abc
|
||||
from abc import abstractmethod
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def my_fixture(mocker): # OK with return
|
||||
return 0
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def activate_context(): # OK with yield
|
||||
with get_context() as context:
|
||||
yield context
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def _any_fixture(mocker): # Ok nested function
|
||||
def nested_function():
|
||||
return 1
|
||||
|
||||
mocker.patch("...", nested_function)
|
||||
|
||||
|
||||
class BaseTest:
|
||||
@pytest.fixture()
|
||||
@abc.abstractmethod
|
||||
def _my_fixture(): # OK abstract with import abc
|
||||
return NotImplemented
|
||||
|
||||
|
||||
class BaseTest:
|
||||
@pytest.fixture()
|
||||
@abstractmethod
|
||||
def _my_fixture(): # OK abstract with from import
|
||||
return NotImplemented
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def _my_fixture(mocker): # Error with return
|
||||
return 0
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def _activate_context(): # Error with yield
|
||||
with get_context() as context:
|
||||
yield context
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def _activate_context(): # Error with conditional yield from
|
||||
if some_condition:
|
||||
with get_context() as context:
|
||||
yield context
|
||||
else:
|
||||
yield from other_context()
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Tests to determine first-party import classification.
|
||||
|
||||
For typing-only import detection tests, see `TCH002.py`.
|
||||
For typing-only import detection tests, see `TC002.py`.
|
||||
"""
|
||||
|
||||
|
||||
@@ -2,49 +2,49 @@
|
||||
|
||||
|
||||
def f():
|
||||
import pandas as pd # TCH002
|
||||
import pandas as pd # TC002
|
||||
|
||||
x: pd.DataFrame
|
||||
|
||||
|
||||
def f():
|
||||
from pandas import DataFrame # TCH002
|
||||
from pandas import DataFrame # TC002
|
||||
|
||||
x: DataFrame
|
||||
|
||||
|
||||
def f():
|
||||
from pandas import DataFrame as df # TCH002
|
||||
from pandas import DataFrame as df # TC002
|
||||
|
||||
x: df
|
||||
|
||||
|
||||
def f():
|
||||
import pandas as pd # TCH002
|
||||
import pandas as pd # TC002
|
||||
|
||||
x: pd.DataFrame = 1
|
||||
|
||||
|
||||
def f():
|
||||
from pandas import DataFrame # TCH002
|
||||
from pandas import DataFrame # TC002
|
||||
|
||||
x: DataFrame = 2
|
||||
|
||||
|
||||
def f():
|
||||
from pandas import DataFrame as df # TCH002
|
||||
from pandas import DataFrame as df # TC002
|
||||
|
||||
x: df = 3
|
||||
|
||||
|
||||
def f():
|
||||
import pandas as pd # TCH002
|
||||
import pandas as pd # TC002
|
||||
|
||||
x: "pd.DataFrame" = 1
|
||||
|
||||
|
||||
def f():
|
||||
import pandas as pd # TCH002
|
||||
import pandas as pd # TC002
|
||||
|
||||
x = dict["pd.DataFrame", "pd.DataFrame"]
|
||||
|
||||
@@ -153,13 +153,13 @@ def f():
|
||||
|
||||
|
||||
def f():
|
||||
from pandas import DataFrame # noqa: TCH002
|
||||
from pandas import DataFrame # noqa: TC002
|
||||
|
||||
x: DataFrame = 2
|
||||
|
||||
|
||||
def f():
|
||||
from pandas import ( # noqa: TCH002
|
||||
from pandas import ( # noqa: TC002
|
||||
DataFrame,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Tests to determine standard library import classification.
|
||||
|
||||
For typing-only import detection tests, see `TCH002.py`.
|
||||
For typing-only import detection tests, see `TC002.py`.
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass # TCH005
|
||||
pass # TC005
|
||||
|
||||
|
||||
if False:
|
||||
pass # TCH005
|
||||
pass # TC005
|
||||
|
||||
if 0:
|
||||
pass # TCH005
|
||||
pass # TC005
|
||||
|
||||
|
||||
def example():
|
||||
if TYPE_CHECKING:
|
||||
pass # TCH005
|
||||
pass # TC005
|
||||
return
|
||||
|
||||
|
||||
class Test:
|
||||
if TYPE_CHECKING:
|
||||
pass # TCH005
|
||||
pass # TC005
|
||||
x = 2
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ if 0:
|
||||
from typing_extensions import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass # TCH005
|
||||
pass # TC005
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/11368
|
||||
if TYPE_CHECKING:
|
||||
18
crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TC010_1.py
vendored
Normal file
18
crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TC010_1.py
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TypeVar
|
||||
|
||||
|
||||
x: "int" | str # TC010
|
||||
x: ("int" | str) | "bool" # TC010
|
||||
|
||||
|
||||
def func():
|
||||
x: "int" | str # OK
|
||||
|
||||
|
||||
z: list[str, str | "int"] = [] # TC010
|
||||
|
||||
type A = Value["int" | str] # OK
|
||||
|
||||
OldS = TypeVar('OldS', int | 'str', str) # TC010
|
||||
16
crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TC010_2.py
vendored
Normal file
16
crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TC010_2.py
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
from typing import TypeVar
|
||||
|
||||
|
||||
x: "int" | str # TC010
|
||||
x: ("int" | str) | "bool" # TC010
|
||||
|
||||
|
||||
def func():
|
||||
x: "int" | str # OK
|
||||
|
||||
|
||||
z: list[str, str | "int"] = [] # TC010
|
||||
|
||||
type A = Value["int" | str] # OK
|
||||
|
||||
OldS = TypeVar('OldS', int | 'str', str) # TC010
|
||||
@@ -1,18 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TypeVar
|
||||
|
||||
|
||||
x: "int" | str # TCH010
|
||||
x: ("int" | str) | "bool" # TCH010
|
||||
|
||||
|
||||
def func():
|
||||
x: "int" | str # OK
|
||||
|
||||
|
||||
z: list[str, str | "int"] = [] # TCH010
|
||||
|
||||
type A = Value["int" | str] # OK
|
||||
|
||||
OldS = TypeVar('OldS', int | 'str', str) # TCH010
|
||||
@@ -1,16 +0,0 @@
|
||||
from typing import TypeVar
|
||||
|
||||
|
||||
x: "int" | str # TCH010
|
||||
x: ("int" | str) | "bool" # TCH010
|
||||
|
||||
|
||||
def func():
|
||||
x: "int" | str # OK
|
||||
|
||||
|
||||
z: list[str, str | "int"] = [] # TCH010
|
||||
|
||||
type A = Value["int" | str] # OK
|
||||
|
||||
OldS = TypeVar('OldS', int | 'str', str) # TCH010
|
||||
@@ -7,10 +7,10 @@ import pydantic
|
||||
from pydantic import BaseModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import datetime # TCH004
|
||||
from array import array # TCH004
|
||||
import datetime # TC004
|
||||
from array import array # TC004
|
||||
|
||||
import pandas # TCH004
|
||||
import pandas # TC004
|
||||
import pyproj
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import geopandas as gpd # TCH002
|
||||
import geopandas as gpd # TC002
|
||||
import pydantic
|
||||
import pyproj # TCH002
|
||||
import pyproj # TC002
|
||||
from pydantic import BaseModel
|
||||
|
||||
import numpy
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import pathlib
|
||||
from uuid import UUID # TCH003
|
||||
from uuid import UUID # TC003
|
||||
|
||||
import pydantic
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -9,10 +9,10 @@ from attrs import frozen
|
||||
import numpy
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import datetime # TCH004
|
||||
from array import array # TCH004
|
||||
import datetime # TC004
|
||||
from array import array # TC004
|
||||
|
||||
import pandas # TCH004
|
||||
import pandas # TC004
|
||||
import pyproj
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import pandas
|
||||
import pyproj
|
||||
from attrs import frozen
|
||||
|
||||
import numpy # TCH002
|
||||
import numpy # TC002
|
||||
|
||||
|
||||
@attrs.define(auto_attribs=True)
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
import datetime
|
||||
from array import array
|
||||
from dataclasses import dataclass
|
||||
from uuid import UUID # TCH003
|
||||
from uuid import UUID # TC003
|
||||
from collections.abc import Sequence
|
||||
from pydantic import validate_call
|
||||
|
||||
|
||||
8
crates/ruff_linter/resources/test/fixtures/pycodestyle/E402_3.py
vendored
Normal file
8
crates/ruff_linter/resources/test/fixtures/pycodestyle/E402_3.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import pytest
|
||||
|
||||
pytest.importorskip("foo.bar")
|
||||
|
||||
import re
|
||||
from sys import version
|
||||
|
||||
from numpy import *
|
||||
@@ -1,18 +0,0 @@
|
||||
# Should change
|
||||
foo, bar, baz = [fn(x) for x in items]
|
||||
|
||||
foo, bar, baz =[fn(x) for x in items]
|
||||
|
||||
foo, bar, baz = [fn(x) for x in items]
|
||||
|
||||
foo, bar, baz = [[i for i in fn(x)] for x in items]
|
||||
|
||||
foo, bar, baz = [
|
||||
fn(x)
|
||||
for x in items
|
||||
]
|
||||
|
||||
# Should not change
|
||||
foo = [fn(x) for x in items]
|
||||
|
||||
x, = [await foo for foo in bar]
|
||||
6
crates/ruff_linter/resources/test/fixtures/ruff/pyproject_toml/pep639/pyproject.toml
vendored
Normal file
6
crates/ruff_linter/resources/test/fixtures/ruff/pyproject_toml/pep639/pyproject.toml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
[project]
|
||||
name = "hello-world"
|
||||
version = "0.1.0"
|
||||
# PEP 639 - passing
|
||||
license = "MIT OR GPL-2.0-or-later OR (FSFUL AND BSD-2-Clause)"
|
||||
license-files = ["LICEN[CS]E*", "AUTHORS*", "licenses/LICENSE.MIT"]
|
||||
@@ -1,5 +1,3 @@
|
||||
# license-files is wrong here
|
||||
# https://github.com/PyO3/maturin/issues/1615
|
||||
[build-system]
|
||||
requires = [ "maturin>=0.14", "numpy", "wheel", "patchelf",]
|
||||
build-backend = "maturin"
|
||||
@@ -27,10 +27,8 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
Rule::MissingReturnTypeStaticMethod,
|
||||
Rule::MissingReturnTypeUndocumentedPublicFunction,
|
||||
Rule::MissingTypeArgs,
|
||||
Rule::MissingTypeCls,
|
||||
Rule::MissingTypeFunctionArgument,
|
||||
Rule::MissingTypeKwargs,
|
||||
Rule::MissingTypeSelf,
|
||||
]);
|
||||
let enforce_stubs = checker.source_type.is_stub() && checker.enabled(Rule::DocstringInStub);
|
||||
let enforce_stubs_and_runtime = checker.enabled(Rule::IterMethodReturnIterable);
|
||||
|
||||
@@ -180,8 +180,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::RedundantNumericUnion) {
|
||||
flake8_pyi::rules::redundant_numeric_union(checker, parameters);
|
||||
}
|
||||
if checker.enabled(Rule::PrePep570PositionalArgument) {
|
||||
flake8_pyi::rules::pre_pep570_positional_argument(checker, function_def);
|
||||
if checker.enabled(Rule::Pep484StylePositionalOnlyParameter) {
|
||||
flake8_pyi::rules::pep_484_positional_parameter(checker, function_def);
|
||||
}
|
||||
if checker.enabled(Rule::DunderFunctionName) {
|
||||
if let Some(diagnostic) = pep8_naming::rules::dunder_function_name(
|
||||
@@ -293,8 +293,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
Rule::PytestFixtureIncorrectParenthesesStyle,
|
||||
Rule::PytestFixturePositionalArgs,
|
||||
Rule::PytestExtraneousScopeFunction,
|
||||
Rule::PytestMissingFixtureNameUnderscore,
|
||||
Rule::PytestIncorrectFixtureNameUnderscore,
|
||||
Rule::PytestFixtureParamWithoutValue,
|
||||
Rule::PytestDeprecatedYieldFixture,
|
||||
Rule::PytestFixtureFinalizerCallback,
|
||||
@@ -304,7 +302,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
]) {
|
||||
flake8_pytest_style::rules::fixture(
|
||||
checker,
|
||||
stmt,
|
||||
name,
|
||||
parameters,
|
||||
returns.as_deref(),
|
||||
@@ -594,18 +591,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::NonAsciiImportName) {
|
||||
pylint::rules::non_ascii_module_import(checker, alias);
|
||||
}
|
||||
// TODO(charlie): Remove when stabilizing A004.
|
||||
if let Some(asname) = &alias.asname {
|
||||
if checker.settings.preview.is_disabled()
|
||||
&& checker.enabled(Rule::BuiltinVariableShadowing)
|
||||
{
|
||||
flake8_builtins::rules::builtin_variable_shadowing(
|
||||
checker,
|
||||
asname,
|
||||
asname.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::Debugger) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_debugger::rules::debugger_import(stmt, None, &alias.name)
|
||||
@@ -915,19 +901,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
stmt.range(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
// TODO(charlie): Remove when stabilizing A004.
|
||||
if let Some(asname) = &alias.asname {
|
||||
if checker.settings.preview.is_disabled()
|
||||
&& checker.enabled(Rule::BuiltinVariableShadowing)
|
||||
{
|
||||
flake8_builtins::rules::builtin_variable_shadowing(
|
||||
checker,
|
||||
asname,
|
||||
asname.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::RelativeImports) {
|
||||
if let Some(diagnostic) = flake8_tidy_imports::rules::banned_relative_import(
|
||||
@@ -1560,9 +1533,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker, stmt, targets, value,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::UnpackedListComprehension) {
|
||||
pyupgrade::rules::unpacked_list_comprehension(checker, targets, value);
|
||||
}
|
||||
if checker.enabled(Rule::PandasDfVariableName) {
|
||||
if let Some(diagnostic) = pandas_vet::rules::assignment_to_df(targets) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
||||
@@ -453,6 +453,8 @@ impl<'a> Checker<'a> {
|
||||
|
||||
impl<'a> Visitor<'a> for Checker<'a> {
|
||||
fn visit_stmt(&mut self, stmt: &'a Stmt) {
|
||||
let in_preview = self.settings.preview.is_enabled();
|
||||
|
||||
// Step 0: Pre-processing
|
||||
self.semantic.push_node(stmt);
|
||||
|
||||
@@ -504,7 +506,8 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
||||
|| helpers::in_nested_block(self.semantic.current_statements())
|
||||
|| imports::is_matplotlib_activation(stmt, self.semantic())
|
||||
|| imports::is_sys_path_modification(stmt, self.semantic())
|
||||
|| imports::is_os_environ_modification(stmt, self.semantic()))
|
||||
|| imports::is_os_environ_modification(stmt, self.semantic())
|
||||
|| (in_preview && imports::is_pytest_importorskip(stmt, self.semantic())))
|
||||
{
|
||||
self.semantic.flags |= SemanticModelFlags::IMPORT_BOUNDARY;
|
||||
}
|
||||
|
||||
@@ -126,7 +126,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pycodestyle, "E742") => (RuleGroup::Stable, rules::pycodestyle::rules::AmbiguousClassName),
|
||||
(Pycodestyle, "E743") => (RuleGroup::Stable, rules::pycodestyle::rules::AmbiguousFunctionName),
|
||||
(Pycodestyle, "E902") => (RuleGroup::Stable, rules::pycodestyle::rules::IOError),
|
||||
(Pycodestyle, "E999") => (RuleGroup::Deprecated, rules::pycodestyle::rules::SyntaxError),
|
||||
#[allow(deprecated)]
|
||||
(Pycodestyle, "E999") => (RuleGroup::Removed, rules::pycodestyle::rules::SyntaxError),
|
||||
|
||||
// pycodestyle warnings
|
||||
(Pycodestyle, "W191") => (RuleGroup::Stable, rules::pycodestyle::rules::TabIndentation),
|
||||
@@ -187,7 +188,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "C0131") => (RuleGroup::Stable, rules::pylint::rules::TypeBivariance),
|
||||
(Pylint, "C0132") => (RuleGroup::Stable, rules::pylint::rules::TypeParamNameMismatch),
|
||||
(Pylint, "C0205") => (RuleGroup::Stable, rules::pylint::rules::SingleStringSlots),
|
||||
(Pylint, "C0206") => (RuleGroup::Preview, rules::pylint::rules::DictIndexMissingItems),
|
||||
(Pylint, "C0206") => (RuleGroup::Stable, rules::pylint::rules::DictIndexMissingItems),
|
||||
(Pylint, "C0208") => (RuleGroup::Stable, rules::pylint::rules::IterationOverSet),
|
||||
(Pylint, "C0414") => (RuleGroup::Stable, rules::pylint::rules::UselessImportAlias),
|
||||
(Pylint, "C0415") => (RuleGroup::Preview, rules::pylint::rules::ImportOutsideTopLevel),
|
||||
@@ -312,8 +313,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Builtins, "001") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinVariableShadowing),
|
||||
(Flake8Builtins, "002") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinArgumentShadowing),
|
||||
(Flake8Builtins, "003") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinAttributeShadowing),
|
||||
// TODO(charlie): When stabilizing, remove preview gating for A001's treatment of imports.
|
||||
(Flake8Builtins, "004") => (RuleGroup::Preview, rules::flake8_builtins::rules::BuiltinImportShadowing),
|
||||
(Flake8Builtins, "004") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinImportShadowing),
|
||||
(Flake8Builtins, "005") => (RuleGroup::Preview, rules::flake8_builtins::rules::BuiltinModuleShadowing),
|
||||
(Flake8Builtins, "006") => (RuleGroup::Preview, rules::flake8_builtins::rules::BuiltinLambdaArgumentShadowing),
|
||||
|
||||
@@ -352,7 +352,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Bugbear, "033") => (RuleGroup::Stable, rules::flake8_bugbear::rules::DuplicateValue),
|
||||
(Flake8Bugbear, "034") => (RuleGroup::Stable, rules::flake8_bugbear::rules::ReSubPositionalArgs),
|
||||
(Flake8Bugbear, "035") => (RuleGroup::Stable, rules::flake8_bugbear::rules::StaticKeyDictComprehension),
|
||||
(Flake8Bugbear, "039") => (RuleGroup::Preview, rules::flake8_bugbear::rules::MutableContextvarDefault),
|
||||
(Flake8Bugbear, "039") => (RuleGroup::Stable, rules::flake8_bugbear::rules::MutableContextvarDefault),
|
||||
(Flake8Bugbear, "901") => (RuleGroup::Preview, rules::flake8_bugbear::rules::ReturnInGenerator),
|
||||
(Flake8Bugbear, "904") => (RuleGroup::Stable, rules::flake8_bugbear::rules::RaiseWithoutFromInsideExcept),
|
||||
(Flake8Bugbear, "905") => (RuleGroup::Stable, rules::flake8_bugbear::rules::ZipWithoutExplicitStrict),
|
||||
@@ -428,8 +428,10 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Annotations, "001") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingTypeFunctionArgument),
|
||||
(Flake8Annotations, "002") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingTypeArgs),
|
||||
(Flake8Annotations, "003") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingTypeKwargs),
|
||||
(Flake8Annotations, "101") => (RuleGroup::Deprecated, rules::flake8_annotations::rules::MissingTypeSelf),
|
||||
(Flake8Annotations, "102") => (RuleGroup::Deprecated, rules::flake8_annotations::rules::MissingTypeCls),
|
||||
#[allow(deprecated)]
|
||||
(Flake8Annotations, "101") => (RuleGroup::Removed, rules::flake8_annotations::rules::MissingTypeSelf),
|
||||
#[allow(deprecated)]
|
||||
(Flake8Annotations, "102") => (RuleGroup::Removed, rules::flake8_annotations::rules::MissingTypeCls),
|
||||
(Flake8Annotations, "201") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingReturnTypeUndocumentedPublicFunction),
|
||||
(Flake8Annotations, "202") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingReturnTypePrivateFunction),
|
||||
(Flake8Annotations, "204") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingReturnTypeSpecialMethod),
|
||||
@@ -513,7 +515,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pyupgrade, "024") => (RuleGroup::Stable, rules::pyupgrade::rules::OSErrorAlias),
|
||||
(Pyupgrade, "025") => (RuleGroup::Stable, rules::pyupgrade::rules::UnicodeKindPrefix),
|
||||
(Pyupgrade, "026") => (RuleGroup::Stable, rules::pyupgrade::rules::DeprecatedMockImport),
|
||||
(Pyupgrade, "027") => (RuleGroup::Deprecated, rules::pyupgrade::rules::UnpackedListComprehension),
|
||||
(Pyupgrade, "027") => (RuleGroup::Removed, rules::pyupgrade::rules::UnpackedListComprehension),
|
||||
(Pyupgrade, "028") => (RuleGroup::Stable, rules::pyupgrade::rules::YieldInForLoop),
|
||||
(Pyupgrade, "029") => (RuleGroup::Stable, rules::pyupgrade::rules::UnnecessaryBuiltinImport),
|
||||
(Pyupgrade, "030") => (RuleGroup::Stable, rules::pyupgrade::rules::FormatLiterals),
|
||||
@@ -529,7 +531,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pyupgrade, "040") => (RuleGroup::Stable, rules::pyupgrade::rules::NonPEP695TypeAlias),
|
||||
(Pyupgrade, "041") => (RuleGroup::Stable, rules::pyupgrade::rules::TimeoutErrorAlias),
|
||||
(Pyupgrade, "042") => (RuleGroup::Preview, rules::pyupgrade::rules::ReplaceStrEnum),
|
||||
(Pyupgrade, "043") => (RuleGroup::Preview, rules::pyupgrade::rules::UnnecessaryDefaultTypeArgs),
|
||||
(Pyupgrade, "043") => (RuleGroup::Stable, rules::pyupgrade::rules::UnnecessaryDefaultTypeArgs),
|
||||
(Pyupgrade, "044") => (RuleGroup::Preview, rules::pyupgrade::rules::NonPEP646Unpack),
|
||||
|
||||
// pydocstyle
|
||||
@@ -788,16 +790,18 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Pyi, "059") => (RuleGroup::Preview, rules::flake8_pyi::rules::GenericNotLastBaseClass),
|
||||
(Flake8Pyi, "061") => (RuleGroup::Preview, rules::flake8_pyi::rules::RedundantNoneLiteral),
|
||||
(Flake8Pyi, "062") => (RuleGroup::Stable, rules::flake8_pyi::rules::DuplicateLiteralMember),
|
||||
(Flake8Pyi, "063") => (RuleGroup::Preview, rules::flake8_pyi::rules::PrePep570PositionalArgument),
|
||||
(Flake8Pyi, "064") => (RuleGroup::Preview, rules::flake8_pyi::rules::RedundantFinalLiteral),
|
||||
(Flake8Pyi, "066") => (RuleGroup::Preview, rules::flake8_pyi::rules::BadVersionInfoOrder),
|
||||
(Flake8Pyi, "063") => (RuleGroup::Stable, rules::flake8_pyi::rules::Pep484StylePositionalOnlyParameter),
|
||||
(Flake8Pyi, "064") => (RuleGroup::Stable, rules::flake8_pyi::rules::RedundantFinalLiteral),
|
||||
(Flake8Pyi, "066") => (RuleGroup::Stable, rules::flake8_pyi::rules::BadVersionInfoOrder),
|
||||
|
||||
// flake8-pytest-style
|
||||
(Flake8PytestStyle, "001") => (RuleGroup::Stable, rules::flake8_pytest_style::rules::PytestFixtureIncorrectParenthesesStyle),
|
||||
(Flake8PytestStyle, "002") => (RuleGroup::Stable, rules::flake8_pytest_style::rules::PytestFixturePositionalArgs),
|
||||
(Flake8PytestStyle, "003") => (RuleGroup::Stable, rules::flake8_pytest_style::rules::PytestExtraneousScopeFunction),
|
||||
(Flake8PytestStyle, "004") => (RuleGroup::Deprecated, rules::flake8_pytest_style::rules::PytestMissingFixtureNameUnderscore),
|
||||
(Flake8PytestStyle, "005") => (RuleGroup::Deprecated, rules::flake8_pytest_style::rules::PytestIncorrectFixtureNameUnderscore),
|
||||
#[allow(deprecated)]
|
||||
(Flake8PytestStyle, "004") => (RuleGroup::Removed, rules::flake8_pytest_style::rules::PytestMissingFixtureNameUnderscore),
|
||||
#[allow(deprecated)]
|
||||
(Flake8PytestStyle, "005") => (RuleGroup::Removed, rules::flake8_pytest_style::rules::PytestIncorrectFixtureNameUnderscore),
|
||||
(Flake8PytestStyle, "006") => (RuleGroup::Stable, rules::flake8_pytest_style::rules::PytestParametrizeNamesWrongType),
|
||||
(Flake8PytestStyle, "007") => (RuleGroup::Stable, rules::flake8_pytest_style::rules::PytestParametrizeValuesWrongType),
|
||||
(Flake8PytestStyle, "008") => (RuleGroup::Stable, rules::flake8_pytest_style::rules::PytestPatchWithLambda),
|
||||
@@ -924,8 +928,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Numpy, "201") => (RuleGroup::Stable, rules::numpy::rules::Numpy2Deprecation),
|
||||
|
||||
// fastapi
|
||||
(FastApi, "001") => (RuleGroup::Preview, rules::fastapi::rules::FastApiRedundantResponseModel),
|
||||
(FastApi, "002") => (RuleGroup::Preview, rules::fastapi::rules::FastApiNonAnnotatedDependency),
|
||||
(FastApi, "001") => (RuleGroup::Stable, rules::fastapi::rules::FastApiRedundantResponseModel),
|
||||
(FastApi, "002") => (RuleGroup::Stable, rules::fastapi::rules::FastApiNonAnnotatedDependency),
|
||||
(FastApi, "003") => (RuleGroup::Preview, rules::fastapi::rules::FastApiUnusedPathParameter),
|
||||
|
||||
// pydoclint
|
||||
@@ -955,15 +959,15 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Ruff, "018") => (RuleGroup::Stable, rules::ruff::rules::AssignmentInAssert),
|
||||
(Ruff, "019") => (RuleGroup::Stable, rules::ruff::rules::UnnecessaryKeyCheck),
|
||||
(Ruff, "020") => (RuleGroup::Stable, rules::ruff::rules::NeverUnion),
|
||||
(Ruff, "021") => (RuleGroup::Preview, rules::ruff::rules::ParenthesizeChainedOperators),
|
||||
(Ruff, "022") => (RuleGroup::Preview, rules::ruff::rules::UnsortedDunderAll),
|
||||
(Ruff, "023") => (RuleGroup::Preview, rules::ruff::rules::UnsortedDunderSlots),
|
||||
(Ruff, "021") => (RuleGroup::Stable, rules::ruff::rules::ParenthesizeChainedOperators),
|
||||
(Ruff, "022") => (RuleGroup::Stable, rules::ruff::rules::UnsortedDunderAll),
|
||||
(Ruff, "023") => (RuleGroup::Stable, rules::ruff::rules::UnsortedDunderSlots),
|
||||
(Ruff, "024") => (RuleGroup::Stable, rules::ruff::rules::MutableFromkeysValue),
|
||||
(Ruff, "026") => (RuleGroup::Stable, rules::ruff::rules::DefaultFactoryKwarg),
|
||||
(Ruff, "027") => (RuleGroup::Preview, rules::ruff::rules::MissingFStringSyntax),
|
||||
(Ruff, "028") => (RuleGroup::Preview, rules::ruff::rules::InvalidFormatterSuppressionComment),
|
||||
(Ruff, "029") => (RuleGroup::Preview, rules::ruff::rules::UnusedAsync),
|
||||
(Ruff, "030") => (RuleGroup::Preview, rules::ruff::rules::AssertWithPrintMessage),
|
||||
(Ruff, "030") => (RuleGroup::Stable, rules::ruff::rules::AssertWithPrintMessage),
|
||||
(Ruff, "031") => (RuleGroup::Preview, rules::ruff::rules::IncorrectlyParenthesizedTupleInSubscript),
|
||||
(Ruff, "032") => (RuleGroup::Preview, rules::ruff::rules::DecimalFromFloatLiteral),
|
||||
(Ruff, "033") => (RuleGroup::Preview, rules::ruff::rules::PostInitDefault),
|
||||
|
||||
@@ -155,7 +155,7 @@ pub enum Linter {
|
||||
#[prefix = "TID"]
|
||||
Flake8TidyImports,
|
||||
/// [flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
|
||||
#[prefix = "TCH"]
|
||||
#[prefix = "TC"]
|
||||
Flake8TypeChecking,
|
||||
/// [flake8-gettext](https://pypi.org/project/flake8-gettext/)
|
||||
#[prefix = "INT"]
|
||||
|
||||
@@ -87,8 +87,8 @@ static REDIRECTS: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(
|
||||
("PDV90", "PD90"),
|
||||
("PDV901", "PD901"),
|
||||
// TODO(charlie): Remove by 2023-04-01.
|
||||
("TYP", "TCH"),
|
||||
("TYP001", "TCH001"),
|
||||
("TYP", "TC"),
|
||||
("TYP001", "TC001"),
|
||||
// TODO(charlie): Remove by 2023-06-01.
|
||||
("RUF004", "B026"),
|
||||
("PIE802", "C419"),
|
||||
@@ -98,7 +98,6 @@ static REDIRECTS: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(
|
||||
("T003", "FIX003"),
|
||||
("T004", "FIX004"),
|
||||
("RUF011", "B035"),
|
||||
("TCH006", "TCH010"),
|
||||
("TRY200", "B904"),
|
||||
("PGH001", "S307"),
|
||||
("PGH002", "G010"),
|
||||
@@ -126,5 +125,14 @@ static REDIRECTS: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(
|
||||
("RUF025", "C420"),
|
||||
// See: https://github.com/astral-sh/ruff/issues/13492
|
||||
("TRY302", "TRY203"),
|
||||
// TCH renamed to TC to harmonize with flake8 plugin
|
||||
("TCH", "TC"),
|
||||
("TCH001", "TC001"),
|
||||
("TCH002", "TC002"),
|
||||
("TCH003", "TC003"),
|
||||
("TCH004", "TC004"),
|
||||
("TCH005", "TC005"),
|
||||
("TCH006", "TC010"),
|
||||
("TCH010", "TC010"),
|
||||
])
|
||||
});
|
||||
|
||||
@@ -25,4 +25,20 @@ mod tests {
|
||||
assert_messages!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// FAST002 autofixes use `typing_extensions` on Python 3.8,
|
||||
// since `typing.Annotated` was added in Python 3.9
|
||||
#[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002.py"))]
|
||||
fn rules_py38(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}_py38", rule_code.as_ref(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("fastapi").join(path).as_path(),
|
||||
&settings::LinterSettings {
|
||||
target_version: settings::types::PythonVersion::Py38,
|
||||
..settings::LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
)?;
|
||||
assert_messages!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,18 @@ use crate::rules::fastapi::rules::is_fastapi_route;
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
/// ## What it does
|
||||
/// Identifies FastAPI routes with deprecated uses of `Depends`.
|
||||
/// Identifies FastAPI routes with deprecated uses of `Depends` or similar.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// The FastAPI documentation recommends the use of `Annotated` for defining
|
||||
/// route dependencies and parameters, rather than using `Depends` directly
|
||||
/// with a default value.
|
||||
/// The [FastAPI documentation] recommends the use of [`typing.Annotated`] for
|
||||
/// defining route dependencies and parameters, rather than using `Depends`,
|
||||
/// `Query` or similar as a default value for a parameter. Using this approach
|
||||
/// everywhere helps ensure consistency and clarity in defining dependencies
|
||||
/// and parameters.
|
||||
///
|
||||
/// This approach is also suggested for various route parameters, including Body and Cookie, as it helps ensure consistency and clarity in defining dependencies and parameters.
|
||||
/// `Annotated` was added to the `typing` module in Python 3.9; however,
|
||||
/// the third-party [`typing_extensions`] package provides a backport that can be
|
||||
/// used on older versions of Python.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
@@ -55,9 +59,14 @@ use crate::settings::types::PythonVersion;
|
||||
/// async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
|
||||
/// return commons
|
||||
/// ```
|
||||
|
||||
///
|
||||
/// [fastAPI documentation]: https://fastapi.tiangolo.com/tutorial/query-params-str-validations/?h=annotated#advantages-of-annotated
|
||||
/// [typing.Annotated]: https://docs.python.org/3/library/typing.html#typing.Annotated
|
||||
/// [typing_extensions]: https://typing-extensions.readthedocs.io/en/stable/
|
||||
#[violation]
|
||||
pub struct FastApiNonAnnotatedDependency;
|
||||
pub struct FastApiNonAnnotatedDependency {
|
||||
py_version: PythonVersion,
|
||||
}
|
||||
|
||||
impl Violation for FastApiNonAnnotatedDependency {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
@@ -68,11 +77,16 @@ impl Violation for FastApiNonAnnotatedDependency {
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
Some("Replace with `Annotated`".to_string())
|
||||
let title = if self.py_version >= PythonVersion::Py39 {
|
||||
"Replace with `typing.Annotated`"
|
||||
} else {
|
||||
"Replace with `typing_extensions.Annotated`"
|
||||
};
|
||||
Some(title.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// RUF103
|
||||
/// FAST002
|
||||
pub(crate) fn fastapi_non_annotated_dependency(
|
||||
checker: &mut Checker,
|
||||
function_def: &ast::StmtFunctionDef,
|
||||
@@ -135,7 +149,12 @@ fn create_diagnostic(
|
||||
parameter: &ast::ParameterWithDefault,
|
||||
safe_to_update: bool,
|
||||
) {
|
||||
let mut diagnostic = Diagnostic::new(FastApiNonAnnotatedDependency, parameter.range);
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
FastApiNonAnnotatedDependency {
|
||||
py_version: checker.settings.target_version,
|
||||
},
|
||||
parameter.range,
|
||||
);
|
||||
|
||||
if safe_to_update {
|
||||
if let (Some(annotation), Some(default)) =
|
||||
|
||||
@@ -73,7 +73,7 @@ impl AlwaysFixableViolation for FastApiRedundantResponseModel {
|
||||
}
|
||||
}
|
||||
|
||||
/// RUF102
|
||||
/// FAST001
|
||||
pub(crate) fn fastapi_redundant_response_model(
|
||||
checker: &mut Checker,
|
||||
function_def: &StmtFunctionDef,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/fastapi/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
FAST002.py:24:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
|
|
||||
@@ -11,7 +10,7 @@ FAST002.py:24:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
25 | some_security_param: str = Security(get_oauth2_user),
|
||||
26 | ):
|
||||
|
|
||||
= help: Replace with `Annotated`
|
||||
= help: Replace with `typing.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
@@ -40,7 +39,7 @@ FAST002.py:25:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
26 | ):
|
||||
27 | pass
|
||||
|
|
||||
= help: Replace with `Annotated`
|
||||
= help: Replace with `typing.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
@@ -69,7 +68,7 @@ FAST002.py:32:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
33 | some_path_param: str = Path(),
|
||||
34 | some_body_param: str = Body("foo"),
|
||||
|
|
||||
= help: Replace with `Annotated`
|
||||
= help: Replace with `typing.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
@@ -98,7 +97,7 @@ FAST002.py:33:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
34 | some_body_param: str = Body("foo"),
|
||||
35 | some_cookie_param: str = Cookie(),
|
||||
|
|
||||
= help: Replace with `Annotated`
|
||||
= help: Replace with `typing.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
@@ -127,7 +126,7 @@ FAST002.py:34:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
35 | some_cookie_param: str = Cookie(),
|
||||
36 | some_header_param: int = Header(default=5),
|
||||
|
|
||||
= help: Replace with `Annotated`
|
||||
= help: Replace with `typing.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
@@ -156,7 +155,7 @@ FAST002.py:35:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
36 | some_header_param: int = Header(default=5),
|
||||
37 | some_file_param: UploadFile = File(),
|
||||
|
|
||||
= help: Replace with `Annotated`
|
||||
= help: Replace with `typing.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
@@ -185,7 +184,7 @@ FAST002.py:36:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
37 | some_file_param: UploadFile = File(),
|
||||
38 | some_form_param: str = Form(),
|
||||
|
|
||||
= help: Replace with `Annotated`
|
||||
= help: Replace with `typing.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
@@ -214,7 +213,7 @@ FAST002.py:37:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
38 | some_form_param: str = Form(),
|
||||
39 | ):
|
||||
|
|
||||
= help: Replace with `Annotated`
|
||||
= help: Replace with `typing.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
@@ -243,7 +242,7 @@ FAST002.py:38:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
39 | ):
|
||||
40 | # do stuff
|
||||
|
|
||||
= help: Replace with `Annotated`
|
||||
= help: Replace with `typing.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
@@ -272,7 +271,7 @@ FAST002.py:47:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
48 | ):
|
||||
49 | pass
|
||||
|
|
||||
= help: Replace with `Annotated`
|
||||
= help: Replace with `typing.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
@@ -301,7 +300,7 @@ FAST002.py:53:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
54 | skip: int = 0,
|
||||
55 | limit: int = 10,
|
||||
|
|
||||
= help: Replace with `Annotated`
|
||||
= help: Replace with `typing.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
@@ -330,4 +329,4 @@ FAST002.py:67:5: FAST002 FastAPI dependency without `Annotated`
|
||||
68 | ):
|
||||
69 | pass
|
||||
|
|
||||
= help: Replace with `Annotated`
|
||||
= help: Replace with `typing.Annotated`
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/fastapi/mod.rs
|
||||
---
|
||||
FAST002.py:24:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
|
|
||||
22 | @app.get("/items/")
|
||||
23 | def get_items(
|
||||
24 | current_user: User = Depends(get_current_user),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FAST002
|
||||
25 | some_security_param: str = Security(get_oauth2_user),
|
||||
26 | ):
|
||||
|
|
||||
= help: Replace with `typing_extensions.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
13 13 | )
|
||||
14 14 | from pydantic import BaseModel
|
||||
15 |+from typing_extensions import Annotated
|
||||
15 16 |
|
||||
16 17 | app = FastAPI()
|
||||
17 18 | router = APIRouter()
|
||||
--------------------------------------------------------------------------------
|
||||
21 22 |
|
||||
22 23 | @app.get("/items/")
|
||||
23 24 | def get_items(
|
||||
24 |- current_user: User = Depends(get_current_user),
|
||||
25 |+ current_user: Annotated[User, Depends(get_current_user)],
|
||||
25 26 | some_security_param: str = Security(get_oauth2_user),
|
||||
26 27 | ):
|
||||
27 28 | pass
|
||||
|
||||
FAST002.py:25:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
|
|
||||
23 | def get_items(
|
||||
24 | current_user: User = Depends(get_current_user),
|
||||
25 | some_security_param: str = Security(get_oauth2_user),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FAST002
|
||||
26 | ):
|
||||
27 | pass
|
||||
|
|
||||
= help: Replace with `typing_extensions.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
13 13 | )
|
||||
14 14 | from pydantic import BaseModel
|
||||
15 |+from typing_extensions import Annotated
|
||||
15 16 |
|
||||
16 17 | app = FastAPI()
|
||||
17 18 | router = APIRouter()
|
||||
--------------------------------------------------------------------------------
|
||||
22 23 | @app.get("/items/")
|
||||
23 24 | def get_items(
|
||||
24 25 | current_user: User = Depends(get_current_user),
|
||||
25 |- some_security_param: str = Security(get_oauth2_user),
|
||||
26 |+ some_security_param: Annotated[str, Security(get_oauth2_user)],
|
||||
26 27 | ):
|
||||
27 28 | pass
|
||||
28 29 |
|
||||
|
||||
FAST002.py:32:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
|
|
||||
30 | @app.post("/stuff/")
|
||||
31 | def do_stuff(
|
||||
32 | some_query_param: str | None = Query(default=None),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FAST002
|
||||
33 | some_path_param: str = Path(),
|
||||
34 | some_body_param: str = Body("foo"),
|
||||
|
|
||||
= help: Replace with `typing_extensions.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
13 13 | )
|
||||
14 14 | from pydantic import BaseModel
|
||||
15 |+from typing_extensions import Annotated
|
||||
15 16 |
|
||||
16 17 | app = FastAPI()
|
||||
17 18 | router = APIRouter()
|
||||
--------------------------------------------------------------------------------
|
||||
29 30 |
|
||||
30 31 | @app.post("/stuff/")
|
||||
31 32 | def do_stuff(
|
||||
32 |- some_query_param: str | None = Query(default=None),
|
||||
33 |+ some_query_param: Annotated[str | None, Query(default=None)],
|
||||
33 34 | some_path_param: str = Path(),
|
||||
34 35 | some_body_param: str = Body("foo"),
|
||||
35 36 | some_cookie_param: str = Cookie(),
|
||||
|
||||
FAST002.py:33:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
|
|
||||
31 | def do_stuff(
|
||||
32 | some_query_param: str | None = Query(default=None),
|
||||
33 | some_path_param: str = Path(),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FAST002
|
||||
34 | some_body_param: str = Body("foo"),
|
||||
35 | some_cookie_param: str = Cookie(),
|
||||
|
|
||||
= help: Replace with `typing_extensions.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
13 13 | )
|
||||
14 14 | from pydantic import BaseModel
|
||||
15 |+from typing_extensions import Annotated
|
||||
15 16 |
|
||||
16 17 | app = FastAPI()
|
||||
17 18 | router = APIRouter()
|
||||
--------------------------------------------------------------------------------
|
||||
30 31 | @app.post("/stuff/")
|
||||
31 32 | def do_stuff(
|
||||
32 33 | some_query_param: str | None = Query(default=None),
|
||||
33 |- some_path_param: str = Path(),
|
||||
34 |+ some_path_param: Annotated[str, Path()],
|
||||
34 35 | some_body_param: str = Body("foo"),
|
||||
35 36 | some_cookie_param: str = Cookie(),
|
||||
36 37 | some_header_param: int = Header(default=5),
|
||||
|
||||
FAST002.py:34:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
|
|
||||
32 | some_query_param: str | None = Query(default=None),
|
||||
33 | some_path_param: str = Path(),
|
||||
34 | some_body_param: str = Body("foo"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FAST002
|
||||
35 | some_cookie_param: str = Cookie(),
|
||||
36 | some_header_param: int = Header(default=5),
|
||||
|
|
||||
= help: Replace with `typing_extensions.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
13 13 | )
|
||||
14 14 | from pydantic import BaseModel
|
||||
15 |+from typing_extensions import Annotated
|
||||
15 16 |
|
||||
16 17 | app = FastAPI()
|
||||
17 18 | router = APIRouter()
|
||||
--------------------------------------------------------------------------------
|
||||
31 32 | def do_stuff(
|
||||
32 33 | some_query_param: str | None = Query(default=None),
|
||||
33 34 | some_path_param: str = Path(),
|
||||
34 |- some_body_param: str = Body("foo"),
|
||||
35 |+ some_body_param: Annotated[str, Body("foo")],
|
||||
35 36 | some_cookie_param: str = Cookie(),
|
||||
36 37 | some_header_param: int = Header(default=5),
|
||||
37 38 | some_file_param: UploadFile = File(),
|
||||
|
||||
FAST002.py:35:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
|
|
||||
33 | some_path_param: str = Path(),
|
||||
34 | some_body_param: str = Body("foo"),
|
||||
35 | some_cookie_param: str = Cookie(),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FAST002
|
||||
36 | some_header_param: int = Header(default=5),
|
||||
37 | some_file_param: UploadFile = File(),
|
||||
|
|
||||
= help: Replace with `typing_extensions.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
13 13 | )
|
||||
14 14 | from pydantic import BaseModel
|
||||
15 |+from typing_extensions import Annotated
|
||||
15 16 |
|
||||
16 17 | app = FastAPI()
|
||||
17 18 | router = APIRouter()
|
||||
--------------------------------------------------------------------------------
|
||||
32 33 | some_query_param: str | None = Query(default=None),
|
||||
33 34 | some_path_param: str = Path(),
|
||||
34 35 | some_body_param: str = Body("foo"),
|
||||
35 |- some_cookie_param: str = Cookie(),
|
||||
36 |+ some_cookie_param: Annotated[str, Cookie()],
|
||||
36 37 | some_header_param: int = Header(default=5),
|
||||
37 38 | some_file_param: UploadFile = File(),
|
||||
38 39 | some_form_param: str = Form(),
|
||||
|
||||
FAST002.py:36:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
|
|
||||
34 | some_body_param: str = Body("foo"),
|
||||
35 | some_cookie_param: str = Cookie(),
|
||||
36 | some_header_param: int = Header(default=5),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FAST002
|
||||
37 | some_file_param: UploadFile = File(),
|
||||
38 | some_form_param: str = Form(),
|
||||
|
|
||||
= help: Replace with `typing_extensions.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
13 13 | )
|
||||
14 14 | from pydantic import BaseModel
|
||||
15 |+from typing_extensions import Annotated
|
||||
15 16 |
|
||||
16 17 | app = FastAPI()
|
||||
17 18 | router = APIRouter()
|
||||
--------------------------------------------------------------------------------
|
||||
33 34 | some_path_param: str = Path(),
|
||||
34 35 | some_body_param: str = Body("foo"),
|
||||
35 36 | some_cookie_param: str = Cookie(),
|
||||
36 |- some_header_param: int = Header(default=5),
|
||||
37 |+ some_header_param: Annotated[int, Header(default=5)],
|
||||
37 38 | some_file_param: UploadFile = File(),
|
||||
38 39 | some_form_param: str = Form(),
|
||||
39 40 | ):
|
||||
|
||||
FAST002.py:37:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
|
|
||||
35 | some_cookie_param: str = Cookie(),
|
||||
36 | some_header_param: int = Header(default=5),
|
||||
37 | some_file_param: UploadFile = File(),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FAST002
|
||||
38 | some_form_param: str = Form(),
|
||||
39 | ):
|
||||
|
|
||||
= help: Replace with `typing_extensions.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
13 13 | )
|
||||
14 14 | from pydantic import BaseModel
|
||||
15 |+from typing_extensions import Annotated
|
||||
15 16 |
|
||||
16 17 | app = FastAPI()
|
||||
17 18 | router = APIRouter()
|
||||
--------------------------------------------------------------------------------
|
||||
34 35 | some_body_param: str = Body("foo"),
|
||||
35 36 | some_cookie_param: str = Cookie(),
|
||||
36 37 | some_header_param: int = Header(default=5),
|
||||
37 |- some_file_param: UploadFile = File(),
|
||||
38 |+ some_file_param: Annotated[UploadFile, File()],
|
||||
38 39 | some_form_param: str = Form(),
|
||||
39 40 | ):
|
||||
40 41 | # do stuff
|
||||
|
||||
FAST002.py:38:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
|
|
||||
36 | some_header_param: int = Header(default=5),
|
||||
37 | some_file_param: UploadFile = File(),
|
||||
38 | some_form_param: str = Form(),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FAST002
|
||||
39 | ):
|
||||
40 | # do stuff
|
||||
|
|
||||
= help: Replace with `typing_extensions.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
13 13 | )
|
||||
14 14 | from pydantic import BaseModel
|
||||
15 |+from typing_extensions import Annotated
|
||||
15 16 |
|
||||
16 17 | app = FastAPI()
|
||||
17 18 | router = APIRouter()
|
||||
--------------------------------------------------------------------------------
|
||||
35 36 | some_cookie_param: str = Cookie(),
|
||||
36 37 | some_header_param: int = Header(default=5),
|
||||
37 38 | some_file_param: UploadFile = File(),
|
||||
38 |- some_form_param: str = Form(),
|
||||
39 |+ some_form_param: Annotated[str, Form()],
|
||||
39 40 | ):
|
||||
40 41 | # do stuff
|
||||
41 42 | pass
|
||||
|
||||
FAST002.py:47:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
|
|
||||
45 | skip: int,
|
||||
46 | limit: int,
|
||||
47 | current_user: User = Depends(get_current_user),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FAST002
|
||||
48 | ):
|
||||
49 | pass
|
||||
|
|
||||
= help: Replace with `typing_extensions.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
13 13 | )
|
||||
14 14 | from pydantic import BaseModel
|
||||
15 |+from typing_extensions import Annotated
|
||||
15 16 |
|
||||
16 17 | app = FastAPI()
|
||||
17 18 | router = APIRouter()
|
||||
--------------------------------------------------------------------------------
|
||||
44 45 | def get_users(
|
||||
45 46 | skip: int,
|
||||
46 47 | limit: int,
|
||||
47 |- current_user: User = Depends(get_current_user),
|
||||
48 |+ current_user: Annotated[User, Depends(get_current_user)],
|
||||
48 49 | ):
|
||||
49 50 | pass
|
||||
50 51 |
|
||||
|
||||
FAST002.py:53:5: FAST002 [*] FastAPI dependency without `Annotated`
|
||||
|
|
||||
51 | @app.get("/users/")
|
||||
52 | def get_users(
|
||||
53 | current_user: User = Depends(get_current_user),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FAST002
|
||||
54 | skip: int = 0,
|
||||
55 | limit: int = 10,
|
||||
|
|
||||
= help: Replace with `typing_extensions.Annotated`
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | Security,
|
||||
13 13 | )
|
||||
14 14 | from pydantic import BaseModel
|
||||
15 |+from typing_extensions import Annotated
|
||||
15 16 |
|
||||
16 17 | app = FastAPI()
|
||||
17 18 | router = APIRouter()
|
||||
--------------------------------------------------------------------------------
|
||||
50 51 |
|
||||
51 52 | @app.get("/users/")
|
||||
52 53 | def get_users(
|
||||
53 |- current_user: User = Depends(get_current_user),
|
||||
54 |+ current_user: Annotated[User, Depends(get_current_user)],
|
||||
54 55 | skip: int = 0,
|
||||
55 56 | limit: int = 10,
|
||||
56 57 | ):
|
||||
|
||||
FAST002.py:67:5: FAST002 FastAPI dependency without `Annotated`
|
||||
|
|
||||
65 | skip: int = 0,
|
||||
66 | limit: int = 10,
|
||||
67 | current_user: User = Depends(get_current_user),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FAST002
|
||||
68 | ):
|
||||
69 | pass
|
||||
|
|
||||
= help: Replace with `typing_extensions.Annotated`
|
||||
@@ -24,8 +24,6 @@ mod tests {
|
||||
Rule::MissingTypeFunctionArgument,
|
||||
Rule::MissingTypeArgs,
|
||||
Rule::MissingTypeKwargs,
|
||||
Rule::MissingTypeSelf,
|
||||
Rule::MissingTypeCls,
|
||||
Rule::MissingReturnTypeUndocumentedPublicFunction,
|
||||
Rule::MissingReturnTypePrivateFunction,
|
||||
Rule::MissingReturnTypeSpecialMethod,
|
||||
@@ -52,8 +50,6 @@ mod tests {
|
||||
Rule::MissingTypeFunctionArgument,
|
||||
Rule::MissingTypeArgs,
|
||||
Rule::MissingTypeKwargs,
|
||||
Rule::MissingTypeSelf,
|
||||
Rule::MissingTypeCls,
|
||||
Rule::MissingReturnTypeUndocumentedPublicFunction,
|
||||
Rule::MissingReturnTypePrivateFunction,
|
||||
Rule::MissingReturnTypeSpecialMethod,
|
||||
@@ -80,8 +76,6 @@ mod tests {
|
||||
Rule::MissingTypeFunctionArgument,
|
||||
Rule::MissingTypeArgs,
|
||||
Rule::MissingTypeKwargs,
|
||||
Rule::MissingTypeSelf,
|
||||
Rule::MissingTypeCls,
|
||||
])
|
||||
},
|
||||
)?;
|
||||
@@ -161,8 +155,6 @@ mod tests {
|
||||
Rule::MissingTypeFunctionArgument,
|
||||
Rule::MissingTypeArgs,
|
||||
Rule::MissingTypeKwargs,
|
||||
Rule::MissingTypeSelf,
|
||||
Rule::MissingTypeCls,
|
||||
Rule::MissingReturnTypeUndocumentedPublicFunction,
|
||||
Rule::MissingReturnTypePrivateFunction,
|
||||
Rule::MissingReturnTypeSpecialMethod,
|
||||
|
||||
@@ -110,9 +110,8 @@ impl Violation for MissingTypeKwargs {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## Deprecation
|
||||
/// This rule is commonly disabled because type checkers can infer this type without annotation.
|
||||
/// It will be removed in a future release.
|
||||
/// ## Removed
|
||||
/// This rule has been removed because type checkers can infer this type without annotation.
|
||||
///
|
||||
/// ## What it does
|
||||
/// Checks that instance method `self` arguments have type annotations.
|
||||
@@ -139,21 +138,22 @@ impl Violation for MissingTypeKwargs {
|
||||
/// def bar(self: "Foo"): ...
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct MissingTypeSelf {
|
||||
name: String,
|
||||
}
|
||||
#[deprecated(note = "ANN101 has been removed")]
|
||||
pub struct MissingTypeSelf;
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl Violation for MissingTypeSelf {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name } = self;
|
||||
format!("Missing type annotation for `{name}` in method")
|
||||
unreachable!("ANN101 has been removed");
|
||||
}
|
||||
|
||||
fn message_formats() -> &'static [&'static str] {
|
||||
&["Missing type annotation for `{name}` in method"]
|
||||
}
|
||||
}
|
||||
|
||||
/// ## Deprecation
|
||||
/// This rule is commonly disabled because type checkers can infer this type without annotation.
|
||||
/// It will be removed in a future release.
|
||||
/// ## Removed
|
||||
/// This rule has been removed because type checkers can infer this type without annotation.
|
||||
///
|
||||
/// ## What it does
|
||||
/// Checks that class method `cls` arguments have type annotations.
|
||||
@@ -182,15 +182,17 @@ impl Violation for MissingTypeSelf {
|
||||
/// def bar(cls: Type["Foo"]): ...
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct MissingTypeCls {
|
||||
name: String,
|
||||
}
|
||||
#[deprecated(note = "ANN102 has been removed")]
|
||||
pub struct MissingTypeCls;
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl Violation for MissingTypeCls {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name } = self;
|
||||
format!("Missing type annotation for `{name}` in classmethod")
|
||||
unreachable!("ANN102 has been removed")
|
||||
}
|
||||
|
||||
fn message_formats() -> &'static [&'static str] {
|
||||
&["Missing type annotation for `{name}` in classmethod"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,7 +596,6 @@ pub(crate) fn definition(
|
||||
// Keep track of whether we've seen any typed arguments or return values.
|
||||
let mut has_any_typed_arg = false; // Any argument has been typed?
|
||||
let mut has_typed_return = false; // Return value has been typed?
|
||||
let mut has_typed_self_or_cls = false; // Has a typed `self` or `cls` argument?
|
||||
|
||||
// Temporary storage for diagnostics; we emit them at the end
|
||||
// unless configured to suppress ANN* for declarations that are fully untyped.
|
||||
@@ -697,43 +698,6 @@ pub(crate) fn definition(
|
||||
}
|
||||
}
|
||||
|
||||
// ANN101, ANN102
|
||||
if is_method && !visibility::is_staticmethod(decorator_list, checker.semantic()) {
|
||||
if let Some(ParameterWithDefault {
|
||||
parameter,
|
||||
default: _,
|
||||
range: _,
|
||||
}) = parameters
|
||||
.posonlyargs
|
||||
.first()
|
||||
.or_else(|| parameters.args.first())
|
||||
{
|
||||
if parameter.annotation.is_none() {
|
||||
if visibility::is_classmethod(decorator_list, checker.semantic()) {
|
||||
if checker.enabled(Rule::MissingTypeCls) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
MissingTypeCls {
|
||||
name: parameter.name.to_string(),
|
||||
},
|
||||
parameter.range(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if checker.enabled(Rule::MissingTypeSelf) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
MissingTypeSelf {
|
||||
name: parameter.name.to_string(),
|
||||
},
|
||||
parameter.range(),
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
has_typed_self_or_cls = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ANN201, ANN202, ANN401
|
||||
if let Some(expr) = &returns {
|
||||
has_typed_return = true;
|
||||
@@ -927,13 +891,25 @@ pub(crate) fn definition(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !checker.settings.flake8_annotations.ignore_fully_untyped {
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
// If settings say so, don't report any of the
|
||||
// diagnostics gathered here if there were no type annotations at all.
|
||||
if checker.settings.flake8_annotations.ignore_fully_untyped
|
||||
&& !(has_any_typed_arg || has_typed_self_or_cls || has_typed_return)
|
||||
if has_any_typed_arg
|
||||
|| has_typed_return
|
||||
|| (is_method
|
||||
&& !visibility::is_staticmethod(decorator_list, checker.semantic())
|
||||
&& parameters
|
||||
.posonlyargs
|
||||
.first()
|
||||
.or_else(|| parameters.args.first())
|
||||
.is_some_and(|first_param| first_param.parameter.annotation.is_some()))
|
||||
{
|
||||
vec![]
|
||||
} else {
|
||||
diagnostics
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_annotations/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
annotation_presence.py:5:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
@@ -158,14 +157,6 @@ annotation_presence.py:65:39: ANN401 Dynamically typed expressions (typing.Any)
|
||||
66 | pass
|
||||
|
|
||||
|
||||
annotation_presence.py:75:13: ANN101 Missing type annotation for `self` in method
|
||||
|
|
||||
74 | # ANN101
|
||||
75 | def foo(self, a: int, b: int) -> int:
|
||||
| ^^^^ ANN101
|
||||
76 | pass
|
||||
|
|
||||
|
||||
annotation_presence.py:79:29: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
||||
|
|
||||
78 | # ANN401
|
||||
@@ -214,23 +205,6 @@ annotation_presence.py:95:59: ANN401 Dynamically typed expressions (typing.Any)
|
||||
96 | pass
|
||||
|
|
||||
|
||||
annotation_presence.py:130:13: ANN102 Missing type annotation for `cls` in classmethod
|
||||
|
|
||||
128 | # ANN102
|
||||
129 | @classmethod
|
||||
130 | def foo(cls, a: int, b: int) -> int:
|
||||
| ^^^ ANN102
|
||||
131 | pass
|
||||
|
|
||||
|
||||
annotation_presence.py:134:13: ANN101 Missing type annotation for `self` in method
|
||||
|
|
||||
133 | # ANN101
|
||||
134 | def foo(self, /, a: int, b: int) -> int:
|
||||
| ^^^^ ANN101
|
||||
135 | pass
|
||||
|
|
||||
|
||||
annotation_presence.py:149:10: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
||||
|
|
||||
148 | # ANN401
|
||||
@@ -324,12 +298,3 @@ annotation_presence.py:165:9: ANN204 [*] Missing return type annotation for spec
|
||||
165 |- def __init__(self):
|
||||
165 |+ def __init__(self) -> None:
|
||||
166 166 | print(f"{self.attr=}")
|
||||
|
||||
annotation_presence.py:165:18: ANN101 Missing type annotation for `self` in method
|
||||
|
|
||||
163 | # Regression test for: https://github.com/astral-sh/ruff/issues/7711
|
||||
164 | class Class:
|
||||
165 | def __init__(self):
|
||||
| ^^^^ ANN101
|
||||
166 | print(f"{self.attr=}")
|
||||
|
|
||||
|
||||
@@ -19,9 +19,9 @@ use crate::checkers::ast::Checker;
|
||||
/// the `ContextVar`. If the object is modified, those modifications will persist
|
||||
/// across calls, which can lead to unexpected behavior.
|
||||
///
|
||||
/// Instead, prefer to use immutable data structures; or, take `None` as a
|
||||
/// default, and initialize a new mutable object inside for each call using the
|
||||
/// `.set()` method.
|
||||
/// Instead, prefer to use immutable data structures. Alternatively, take
|
||||
/// `None` as a default, and initialize a new mutable object inside for each
|
||||
/// call using the `.set()` method.
|
||||
///
|
||||
/// Types outside the standard library can be marked as immutable with the
|
||||
/// [`lint.flake8-bugbear.extend-immutable-calls`] configuration option.
|
||||
|
||||
@@ -16,8 +16,31 @@ use crate::rules::flake8_builtins::helpers::shadows_builtin;
|
||||
/// Builtins can be marked as exceptions to this rule via the
|
||||
/// [`lint.flake8-builtins.builtins-ignorelist`] configuration option.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// from rich import print
|
||||
///
|
||||
/// print("Some message")
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// from rich import print as rich_print
|
||||
///
|
||||
/// rich_print("Some message")
|
||||
/// ```
|
||||
///
|
||||
/// or:
|
||||
/// ```python
|
||||
/// import rich
|
||||
///
|
||||
/// rich.print("Some message")
|
||||
/// ```
|
||||
///
|
||||
/// ## Options
|
||||
/// - `lint.flake8-builtins.builtins-ignorelist`
|
||||
/// - `target-version`
|
||||
///
|
||||
#[violation]
|
||||
pub struct BuiltinImportShadowing {
|
||||
name: String,
|
||||
|
||||
@@ -2,32 +2,6 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
A001.py:1:16: A001 Variable `sum` is shadowing a Python builtin
|
||||
|
|
||||
1 | import some as sum
|
||||
| ^^^ A001
|
||||
2 | from some import other as int
|
||||
3 | from directory import new as dir
|
||||
|
|
||||
|
||||
A001.py:2:27: A001 Variable `int` is shadowing a Python builtin
|
||||
|
|
||||
1 | import some as sum
|
||||
2 | from some import other as int
|
||||
| ^^^ A001
|
||||
3 | from directory import new as dir
|
||||
|
|
||||
|
||||
A001.py:3:30: A001 Variable `dir` is shadowing a Python builtin
|
||||
|
|
||||
1 | import some as sum
|
||||
2 | from some import other as int
|
||||
3 | from directory import new as dir
|
||||
| ^^^ A001
|
||||
4 |
|
||||
5 | print = 1
|
||||
|
|
||||
|
||||
A001.py:5:1: A001 Variable `print` is shadowing a Python builtin
|
||||
|
|
||||
3 | from directory import new as dir
|
||||
|
||||
@@ -2,22 +2,6 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
A001.py:1:16: A001 Variable `sum` is shadowing a Python builtin
|
||||
|
|
||||
1 | import some as sum
|
||||
| ^^^ A001
|
||||
2 | from some import other as int
|
||||
3 | from directory import new as dir
|
||||
|
|
||||
|
||||
A001.py:2:27: A001 Variable `int` is shadowing a Python builtin
|
||||
|
|
||||
1 | import some as sum
|
||||
2 | from some import other as int
|
||||
| ^^^ A001
|
||||
3 | from directory import new as dir
|
||||
|
|
||||
|
||||
A001.py:5:1: A001 Variable `print` is shadowing a Python builtin
|
||||
|
|
||||
3 | from directory import new as dir
|
||||
|
||||
@@ -66,8 +66,8 @@ mod tests {
|
||||
#[test_case(Rule::PatchVersionComparison, Path::new("PYI004.pyi"))]
|
||||
#[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.py"))]
|
||||
#[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.pyi"))]
|
||||
#[test_case(Rule::PrePep570PositionalArgument, Path::new("PYI063.py"))]
|
||||
#[test_case(Rule::PrePep570PositionalArgument, Path::new("PYI063.pyi"))]
|
||||
#[test_case(Rule::Pep484StylePositionalOnlyParameter, Path::new("PYI063.py"))]
|
||||
#[test_case(Rule::Pep484StylePositionalOnlyParameter, Path::new("PYI063.pyi"))]
|
||||
#[test_case(Rule::RedundantFinalLiteral, Path::new("PYI064.py"))]
|
||||
#[test_case(Rule::RedundantFinalLiteral, Path::new("PYI064.pyi"))]
|
||||
#[test_case(Rule::RedundantLiteralUnion, Path::new("PYI051.py"))]
|
||||
|
||||
@@ -16,8 +16,9 @@ use crate::registry::Rule;
|
||||
/// Comparing `sys.version_info` with `==` or `<=` has unexpected behavior
|
||||
/// and can lead to bugs.
|
||||
///
|
||||
/// For example, `sys.version_info > (3, 8)` will also match `3.8.10`,
|
||||
/// while `sys.version_info <= (3, 8)` will _not_ match `3.8.10`:
|
||||
/// For example, `sys.version_info > (3, 8, 1)` will resolve to `True` if your
|
||||
/// Python version is 3.8.1; meanwhile, `sys.version_info <= (3, 8)` will _not_
|
||||
/// resolve to `True` if your Python version is 3.8.10:
|
||||
///
|
||||
/// ```python
|
||||
/// >>> import sys
|
||||
@@ -61,16 +62,20 @@ impl Violation for BadVersionInfoComparison {
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for if-else statements with `sys.version_info` comparisons that use
|
||||
/// `<` comparators.
|
||||
/// Checks for code that branches on `sys.version_info` comparisons where
|
||||
/// branches corresponding to older Python versions come before branches
|
||||
/// corresponding to newer Python versions.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// As a convention, branches that correspond to newer Python versions should
|
||||
/// come first when using `sys.version_info` comparisons. This makes it easier
|
||||
/// to understand the desired behavior, which typically corresponds to the
|
||||
/// latest Python versions.
|
||||
/// come first. This makes it easier to understand the desired behavior, which
|
||||
/// typically corresponds to the latest Python versions.
|
||||
///
|
||||
/// In [preview], this rule will also flag non-stub files.
|
||||
/// This rule enforces the convention by checking for `if` tests that compare
|
||||
/// `sys.version_info` with `<` rather than `>=`.
|
||||
///
|
||||
/// By default, this rule only applies to stub files.
|
||||
/// In [preview], it will also flag this anti-pattern in non-stub files.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
@@ -101,7 +106,7 @@ pub struct BadVersionInfoOrder;
|
||||
impl Violation for BadVersionInfoOrder {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
"Use `>=` when using `if`-`else` with `sys.version_info` comparisons".to_string()
|
||||
"Put branches for newer Python versions first when branching on `sys.version_info` comparisons".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,19 @@ use crate::checkers::ast::Checker;
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the presence of [PEP 484]-style positional-only arguments.
|
||||
/// Checks for the presence of [PEP 484]-style positional-only parameters.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Historically, [PEP 484] recommended prefixing positional-only arguments
|
||||
/// with a double underscore (`__`). However, [PEP 570] introduced a dedicated
|
||||
/// syntax for positional-only arguments, which should be preferred.
|
||||
/// Historically, [PEP 484] recommended prefixing parameter names with double
|
||||
/// underscores (`__`) to indicate to a type checker that they were
|
||||
/// positional-only. However, [PEP 570] (introduced in Python 3.8) introduced
|
||||
/// dedicated syntax for positional-only arguments. If a forward slash (`/`) is
|
||||
/// present in a function signature on Python 3.8+, all parameters prior to the
|
||||
/// slash are interpreted as positional-only.
|
||||
///
|
||||
/// The new syntax should be preferred as it is more widely used, more concise
|
||||
/// and more readable. It is also respected by Python at runtime, whereas the
|
||||
/// old-style syntax was only understood by type checkers.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
@@ -27,15 +34,18 @@ use crate::settings::types::PythonVersion;
|
||||
/// def foo(x: int, /) -> None: ...
|
||||
/// ```
|
||||
///
|
||||
/// ## Options
|
||||
/// - `target-version`
|
||||
///
|
||||
/// [PEP 484]: https://peps.python.org/pep-0484/#positional-only-arguments
|
||||
/// [PEP 570]: https://peps.python.org/pep-0570
|
||||
#[violation]
|
||||
pub struct PrePep570PositionalArgument;
|
||||
pub struct Pep484StylePositionalOnlyParameter;
|
||||
|
||||
impl Violation for PrePep570PositionalArgument {
|
||||
impl Violation for Pep484StylePositionalOnlyParameter {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
"Use PEP 570 syntax for positional-only arguments".to_string()
|
||||
"Use PEP 570 syntax for positional-only parameters".to_string()
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
@@ -44,7 +54,7 @@ impl Violation for PrePep570PositionalArgument {
|
||||
}
|
||||
|
||||
/// PYI063
|
||||
pub(crate) fn pre_pep570_positional_argument(
|
||||
pub(crate) fn pep_484_positional_parameter(
|
||||
checker: &mut Checker,
|
||||
function_def: &ast::StmtFunctionDef,
|
||||
) {
|
||||
@@ -79,18 +89,18 @@ pub(crate) fn pre_pep570_positional_argument(
|
||||
));
|
||||
|
||||
if let Some(arg) = function_def.parameters.args.get(skip) {
|
||||
if is_pre_pep570_positional_only(arg) {
|
||||
if is_old_style_positional_only(arg) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
PrePep570PositionalArgument,
|
||||
Pep484StylePositionalOnlyParameter,
|
||||
arg.identifier(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the [`ParameterWithDefault`] is an old-style positional-only argument (i.e.,
|
||||
/// Returns `true` if the [`ParameterWithDefault`] is an old-style positional-only parameter (i.e.,
|
||||
/// its name starts with `__` and does not end with `__`).
|
||||
fn is_pre_pep570_positional_only(arg: &ParameterWithDefault) -> bool {
|
||||
fn is_old_style_positional_only(arg: &ParameterWithDefault) -> bool {
|
||||
let arg_name = &arg.parameter.name;
|
||||
arg_name.starts_with("__") && !arg_name.ends_with("__")
|
||||
}
|
||||
|
||||
@@ -11,18 +11,25 @@ use crate::Locator;
|
||||
/// Checks for redundant `Final[Literal[...]]` annotations.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// A `Final[Literal[...]]` annotation can be replaced with `Final`; the literal
|
||||
/// use is unnecessary.
|
||||
/// All constant variables annotated as `Final` are understood as implicitly
|
||||
/// having `Literal` types by a type checker. As such, a `Final[Literal[...]]`
|
||||
/// annotation can often be replaced with a bare `Final`, annotation, which
|
||||
/// will have the same meaning to the type checker while being more concise and
|
||||
/// more readable.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```pyi
|
||||
/// from typing import Final, Literal
|
||||
///
|
||||
/// x: Final[Literal[42]]
|
||||
/// y: Final[Literal[42]] = 42
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```pyi
|
||||
/// from typing import Final, Literal
|
||||
///
|
||||
/// x: Final = 42
|
||||
/// y: Final = 42
|
||||
/// ```
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
PYI063.py:6:9: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.py:6:9: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
4 | from typing import Self
|
||||
5 |
|
||||
@@ -13,7 +12,7 @@ PYI063.py:6:9: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.py:7:14: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.py:7:14: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
6 | def bad(__x: int) -> None: ... # PYI063
|
||||
7 | def also_bad(__x: int, __y: str) -> None: ... # PYI063
|
||||
@@ -22,7 +21,7 @@ PYI063.py:7:14: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.py:8:15: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.py:8:15: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
6 | def bad(__x: int) -> None: ... # PYI063
|
||||
7 | def also_bad(__x: int, __y: str) -> None: ... # PYI063
|
||||
@@ -33,7 +32,7 @@ PYI063.py:8:15: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.py:24:14: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.py:24:14: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
22 | def bad(__self) -> None: ... # PYI063
|
||||
23 | @staticmethod
|
||||
@@ -44,7 +43,7 @@ PYI063.py:24:14: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.py:25:22: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.py:25:22: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
23 | @staticmethod
|
||||
24 | def bad2(__self) -> None: ... # PYI063
|
||||
@@ -55,7 +54,7 @@ PYI063.py:25:22: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.py:26:25: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.py:26:25: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
24 | def bad2(__self) -> None: ... # PYI063
|
||||
25 | def bad3(__self, __x: int) -> None: ... # PYI063
|
||||
@@ -66,7 +65,7 @@ PYI063.py:26:25: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.py:28:25: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.py:28:25: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
26 | def still_bad(self, __x_: int) -> None: ... # PYI063
|
||||
27 | @staticmethod
|
||||
@@ -77,7 +76,7 @@ PYI063.py:28:25: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.py:30:23: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.py:30:23: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
28 | def this_is_bad_too(__x: int) -> None: ... # PYI063
|
||||
29 | @classmethod
|
||||
@@ -88,7 +87,7 @@ PYI063.py:30:23: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.py:52:23: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.py:52:23: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
50 | class Metaclass(type):
|
||||
51 | @classmethod
|
||||
@@ -99,7 +98,7 @@ PYI063.py:52:23: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.py:56:26: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.py:56:26: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
54 | class Metaclass2(type):
|
||||
55 | @classmethod
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
PYI063.pyi:6:9: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.pyi:6:9: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
4 | from typing import Self
|
||||
5 |
|
||||
@@ -13,7 +12,7 @@ PYI063.pyi:6:9: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.pyi:7:14: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.pyi:7:14: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
6 | def bad(__x: int) -> None: ... # PYI063
|
||||
7 | def also_bad(__x: int, __y: str) -> None: ... # PYI063
|
||||
@@ -22,7 +21,7 @@ PYI063.pyi:7:14: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.pyi:8:15: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.pyi:8:15: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
6 | def bad(__x: int) -> None: ... # PYI063
|
||||
7 | def also_bad(__x: int, __y: str) -> None: ... # PYI063
|
||||
@@ -33,7 +32,7 @@ PYI063.pyi:8:15: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.pyi:24:14: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.pyi:24:14: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
22 | def bad(__self) -> None: ... # PYI063
|
||||
23 | @staticmethod
|
||||
@@ -44,7 +43,7 @@ PYI063.pyi:24:14: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.pyi:25:22: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.pyi:25:22: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
23 | @staticmethod
|
||||
24 | def bad2(__self) -> None: ... # PYI063
|
||||
@@ -55,7 +54,7 @@ PYI063.pyi:25:22: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.pyi:26:25: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.pyi:26:25: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
24 | def bad2(__self) -> None: ... # PYI063
|
||||
25 | def bad3(__self, __x: int) -> None: ... # PYI063
|
||||
@@ -66,7 +65,7 @@ PYI063.pyi:26:25: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.pyi:28:25: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.pyi:28:25: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
26 | def still_bad(self, __x_: int) -> None: ... # PYI063 # TODO: doesn't get raised here
|
||||
27 | @staticmethod
|
||||
@@ -77,7 +76,7 @@ PYI063.pyi:28:25: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.pyi:30:23: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.pyi:30:23: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
28 | def this_is_bad_too(__x: int) -> None: ... # PYI063
|
||||
29 | @classmethod
|
||||
@@ -88,7 +87,7 @@ PYI063.pyi:30:23: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.pyi:52:23: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.pyi:52:23: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
50 | class Metaclass(type):
|
||||
51 | @classmethod
|
||||
@@ -99,7 +98,7 @@ PYI063.pyi:52:23: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
|
|
||||
= help: Add `/` to function signature
|
||||
|
||||
PYI063.pyi:56:26: PYI063 Use PEP 570 syntax for positional-only arguments
|
||||
PYI063.pyi:56:26: PYI063 Use PEP 570 syntax for positional-only parameters
|
||||
|
|
||||
54 | class Metaclass2(type):
|
||||
55 | @classmethod
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
PYI066.pyi:3:4: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` comparisons
|
||||
PYI066.pyi:3:4: PYI066 Put branches for newer Python versions first when branching on `sys.version_info` comparisons
|
||||
|
|
||||
1 | import sys
|
||||
2 |
|
||||
@@ -12,7 +11,7 @@ PYI066.pyi:3:4: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` c
|
||||
5 | else:
|
||||
|
|
||||
|
||||
PYI066.pyi:8:4: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` comparisons
|
||||
PYI066.pyi:8:4: PYI066 Put branches for newer Python versions first when branching on `sys.version_info` comparisons
|
||||
|
|
||||
6 | def foo(x, *, bar=True): ...
|
||||
7 |
|
||||
@@ -22,7 +21,7 @@ PYI066.pyi:8:4: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` c
|
||||
10 | elif sys.version_info < (3, 9): # Y066 When using if/else with sys.version_info, put the code for new Python versions first, e.g. "if sys.version_info >= (3, 9)"
|
||||
|
|
||||
|
||||
PYI066.pyi:10:6: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` comparisons
|
||||
PYI066.pyi:10:6: PYI066 Put branches for newer Python versions first when branching on `sys.version_info` comparisons
|
||||
|
|
||||
8 | if sys.version_info < (3, 8): # Y066 When using if/else with sys.version_info, put the code for new Python versions first, e.g. "if sys.version_info >= (3, 8)"
|
||||
9 | def bar(x): ...
|
||||
@@ -32,7 +31,7 @@ PYI066.pyi:10:6: PYI066 Use `>=` when using `if`-`else` with `sys.version_info`
|
||||
12 | elif sys.version_info < (3, 11): # Y066 When using if/else with sys.version_info, put the code for new Python versions first, e.g. "if sys.version_info >= (3, 10)"
|
||||
|
|
||||
|
||||
PYI066.pyi:12:6: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` comparisons
|
||||
PYI066.pyi:12:6: PYI066 Put branches for newer Python versions first when branching on `sys.version_info` comparisons
|
||||
|
|
||||
10 | elif sys.version_info < (3, 9): # Y066 When using if/else with sys.version_info, put the code for new Python versions first, e.g. "if sys.version_info >= (3, 9)"
|
||||
11 | def bar(x, *, bar=True): ...
|
||||
@@ -42,7 +41,7 @@ PYI066.pyi:12:6: PYI066 Use `>=` when using `if`-`else` with `sys.version_info`
|
||||
14 | else:
|
||||
|
|
||||
|
||||
PYI066.pyi:20:6: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` comparisons
|
||||
PYI066.pyi:20:6: PYI066 Put branches for newer Python versions first when branching on `sys.version_info` comparisons
|
||||
|
|
||||
18 | if sys.version_info >= (3, 5):
|
||||
19 | ...
|
||||
|
||||
@@ -45,18 +45,6 @@ mod tests {
|
||||
Settings::default(),
|
||||
"PT003"
|
||||
)]
|
||||
#[test_case(
|
||||
Rule::PytestMissingFixtureNameUnderscore,
|
||||
Path::new("PT004.py"),
|
||||
Settings::default(),
|
||||
"PT004"
|
||||
)]
|
||||
#[test_case(
|
||||
Rule::PytestIncorrectFixtureNameUnderscore,
|
||||
Path::new("PT005.py"),
|
||||
Settings::default(),
|
||||
"PT005"
|
||||
)]
|
||||
#[test_case(
|
||||
Rule::PytestParametrizeNamesWrongType,
|
||||
Path::new("PT006.py"),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Violation};
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
use ruff_python_ast::name::UnqualifiedName;
|
||||
use ruff_python_ast::visitor;
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
@@ -167,8 +166,8 @@ impl AlwaysFixableViolation for PytestExtraneousScopeFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## Deprecation
|
||||
/// Marking fixtures that do not return a value with an underscore
|
||||
/// ## Removal
|
||||
/// This rule has been removed because marking fixtures that do not return a value with an underscore
|
||||
/// isn't a practice recommended by the pytest community.
|
||||
///
|
||||
/// ## What it does
|
||||
@@ -216,20 +215,22 @@ impl AlwaysFixableViolation for PytestExtraneousScopeFunction {
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: `@pytest.fixture` functions](https://docs.pytest.org/en/latest/reference/reference.html#pytest-fixture)
|
||||
#[violation]
|
||||
pub struct PytestMissingFixtureNameUnderscore {
|
||||
function: String,
|
||||
}
|
||||
#[deprecated(note = "PT004 has been removed")]
|
||||
pub struct PytestMissingFixtureNameUnderscore;
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl Violation for PytestMissingFixtureNameUnderscore {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PytestMissingFixtureNameUnderscore { function } = self;
|
||||
format!("Fixture `{function}` does not return anything, add leading underscore")
|
||||
unreachable!("PT004 has been removed");
|
||||
}
|
||||
|
||||
fn message_formats() -> &'static [&'static str] {
|
||||
&["Fixture `{function}` does not return anything, add leading underscore"]
|
||||
}
|
||||
}
|
||||
|
||||
/// ## Deprecation
|
||||
/// Marking fixtures that do not return a value with an underscore
|
||||
/// ## Removal
|
||||
/// This rule has been removed because marking fixtures that do not return a value with an underscore
|
||||
/// isn't a practice recommended by the pytest community.
|
||||
///
|
||||
/// ## What it does
|
||||
@@ -279,15 +280,17 @@ impl Violation for PytestMissingFixtureNameUnderscore {
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: `@pytest.fixture` functions](https://docs.pytest.org/en/latest/reference/reference.html#pytest-fixture)
|
||||
#[violation]
|
||||
pub struct PytestIncorrectFixtureNameUnderscore {
|
||||
function: String,
|
||||
}
|
||||
#[deprecated(note = "PT005 has been removed")]
|
||||
pub struct PytestIncorrectFixtureNameUnderscore;
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl Violation for PytestIncorrectFixtureNameUnderscore {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PytestIncorrectFixtureNameUnderscore { function } = self;
|
||||
format!("Fixture `{function}` returns a value, remove leading underscore")
|
||||
unreachable!("PT005 has been removed");
|
||||
}
|
||||
|
||||
fn message_formats() -> &'static [&'static str] {
|
||||
&["Fixture `{function}` returns a value, remove leading underscore"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -749,43 +752,14 @@ fn check_fixture_decorator(checker: &mut Checker, func_name: &str, decorator: &D
|
||||
}
|
||||
}
|
||||
|
||||
/// PT004, PT005, PT022
|
||||
fn check_fixture_returns(
|
||||
checker: &mut Checker,
|
||||
stmt: &Stmt,
|
||||
name: &str,
|
||||
body: &[Stmt],
|
||||
returns: Option<&Expr>,
|
||||
) {
|
||||
/// PT022
|
||||
fn check_fixture_returns(checker: &mut Checker, name: &str, body: &[Stmt], returns: Option<&Expr>) {
|
||||
let mut visitor = SkipFunctionsVisitor::default();
|
||||
|
||||
for stmt in body {
|
||||
visitor.visit_stmt(stmt);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::PytestIncorrectFixtureNameUnderscore)
|
||||
&& visitor.has_return_with_value
|
||||
&& name.starts_with('_')
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
PytestIncorrectFixtureNameUnderscore {
|
||||
function: name.to_string(),
|
||||
},
|
||||
stmt.identifier(),
|
||||
));
|
||||
} else if checker.enabled(Rule::PytestMissingFixtureNameUnderscore)
|
||||
&& !visitor.has_return_with_value
|
||||
&& !visitor.has_yield_from
|
||||
&& !name.starts_with('_')
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
PytestMissingFixtureNameUnderscore {
|
||||
function: name.to_string(),
|
||||
},
|
||||
stmt.identifier(),
|
||||
));
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::PytestUselessYieldFixture) {
|
||||
let Some(stmt) = body.last() else {
|
||||
return;
|
||||
@@ -904,7 +878,6 @@ fn check_fixture_marks(checker: &mut Checker, decorators: &[Decorator]) {
|
||||
|
||||
pub(crate) fn fixture(
|
||||
checker: &mut Checker,
|
||||
stmt: &Stmt,
|
||||
name: &str,
|
||||
parameters: &Parameters,
|
||||
returns: Option<&Expr>,
|
||||
@@ -924,12 +897,10 @@ pub(crate) fn fixture(
|
||||
check_fixture_decorator_name(checker, decorator);
|
||||
}
|
||||
|
||||
if (checker.enabled(Rule::PytestMissingFixtureNameUnderscore)
|
||||
|| checker.enabled(Rule::PytestIncorrectFixtureNameUnderscore)
|
||||
|| checker.enabled(Rule::PytestUselessYieldFixture))
|
||||
if checker.enabled(Rule::PytestUselessYieldFixture)
|
||||
&& !is_abstract(decorators, checker.semantic())
|
||||
{
|
||||
check_fixture_returns(checker, stmt, name, body, returns);
|
||||
check_fixture_returns(checker, name, body, returns);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::PytestFixtureFinalizerCallback) {
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pytest_style/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
PT004.py:51:5: PT004 Fixture `patch_something` does not return anything, add leading underscore
|
||||
|
|
||||
50 | @pytest.fixture()
|
||||
51 | def patch_something(mocker): # Error simple
|
||||
| ^^^^^^^^^^^^^^^ PT004
|
||||
52 | mocker.patch("some.thing")
|
||||
|
|
||||
|
||||
PT004.py:56:5: PT004 Fixture `activate_context` does not return anything, add leading underscore
|
||||
|
|
||||
55 | @pytest.fixture()
|
||||
56 | def activate_context(): # Error with yield
|
||||
| ^^^^^^^^^^^^^^^^ PT004
|
||||
57 | with context:
|
||||
58 | yield
|
||||
|
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pytest_style/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
PT005.py:41:5: PT005 Fixture `_my_fixture` returns a value, remove leading underscore
|
||||
|
|
||||
40 | @pytest.fixture()
|
||||
41 | def _my_fixture(mocker): # Error with return
|
||||
| ^^^^^^^^^^^ PT005
|
||||
42 | return 0
|
||||
|
|
||||
|
||||
PT005.py:46:5: PT005 Fixture `_activate_context` returns a value, remove leading underscore
|
||||
|
|
||||
45 | @pytest.fixture()
|
||||
46 | def _activate_context(): # Error with yield
|
||||
| ^^^^^^^^^^^^^^^^^ PT005
|
||||
47 | with get_context() as context:
|
||||
48 | yield context
|
||||
|
|
||||
|
||||
PT005.py:52:5: PT005 Fixture `_activate_context` returns a value, remove leading underscore
|
||||
|
|
||||
51 | @pytest.fixture()
|
||||
52 | def _activate_context(): # Error with conditional yield from
|
||||
| ^^^^^^^^^^^^^^^^^ PT005
|
||||
53 | if some_condition:
|
||||
54 | with get_context() as context:
|
||||
|
|
||||
@@ -16,29 +16,29 @@ mod tests {
|
||||
use crate::test::{test_path, test_snippet};
|
||||
use crate::{assert_messages, settings};
|
||||
|
||||
#[test_case(Rule::EmptyTypeCheckingBlock, Path::new("TCH005.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_1.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_10.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_11.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_12.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_13.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_14.pyi"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_15.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_16.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_17.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_2.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_3.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_4.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_5.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_6.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_7.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_8.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_9.py"))]
|
||||
#[test_case(Rule::EmptyTypeCheckingBlock, Path::new("TC005.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_1.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_10.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_11.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_12.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_13.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_14.pyi"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_15.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_16.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_17.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_2.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_3.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_4.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_5.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_6.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_7.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_8.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TC004_9.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("quote.py"))]
|
||||
#[test_case(Rule::RuntimeStringUnion, Path::new("TCH010_1.py"))]
|
||||
#[test_case(Rule::RuntimeStringUnion, Path::new("TCH010_2.py"))]
|
||||
#[test_case(Rule::TypingOnlyFirstPartyImport, Path::new("TCH001.py"))]
|
||||
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("TCH003.py"))]
|
||||
#[test_case(Rule::RuntimeStringUnion, Path::new("TC010_1.py"))]
|
||||
#[test_case(Rule::RuntimeStringUnion, Path::new("TC010_2.py"))]
|
||||
#[test_case(Rule::TypingOnlyFirstPartyImport, Path::new("TC001.py"))]
|
||||
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("TC003.py"))]
|
||||
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("init_var.py"))]
|
||||
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("kw_only.py"))]
|
||||
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("snapshot.py"))]
|
||||
@@ -46,7 +46,7 @@ mod tests {
|
||||
Rule::TypingOnlyStandardLibraryImport,
|
||||
Path::new("singledispatchmethod.py")
|
||||
)]
|
||||
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("TCH002.py"))]
|
||||
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("TC002.py"))]
|
||||
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("quote.py"))]
|
||||
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("singledispatch.py"))]
|
||||
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("strict.py"))]
|
||||
|
||||
@@ -46,7 +46,7 @@ impl AlwaysFixableViolation for EmptyTypeCheckingBlock {
|
||||
}
|
||||
}
|
||||
|
||||
/// TCH005
|
||||
/// TC005
|
||||
pub(crate) fn empty_type_checking_block(checker: &mut Checker, stmt: &ast::StmtIf) {
|
||||
if !typing::is_type_checking_block(stmt, checker.semantic()) {
|
||||
return;
|
||||
|
||||
@@ -96,7 +96,7 @@ enum Action {
|
||||
Ignore,
|
||||
}
|
||||
|
||||
/// TCH004
|
||||
/// TC004
|
||||
pub(crate) fn runtime_import_in_type_checking_block(
|
||||
checker: &Checker,
|
||||
scope: &Scope,
|
||||
|
||||
@@ -51,7 +51,7 @@ impl Violation for RuntimeStringUnion {
|
||||
}
|
||||
}
|
||||
|
||||
/// TCH006
|
||||
/// TC006
|
||||
pub(crate) fn runtime_string_union(checker: &mut Checker, expr: &Expr) {
|
||||
if !checker.semantic().in_type_definition() {
|
||||
return;
|
||||
|
||||
@@ -243,7 +243,7 @@ impl Violation for TypingOnlyStandardLibraryImport {
|
||||
}
|
||||
}
|
||||
|
||||
/// TCH001, TCH002, TCH003
|
||||
/// TC001, TC002, TC003
|
||||
pub(crate) fn typing_only_runtime_import(
|
||||
checker: &Checker,
|
||||
scope: &Scope,
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
TCH005.py:4:5: TCH005 [*] Found empty type-checking block
|
||||
TC005.py:4:5: TC005 [*] Found empty type-checking block
|
||||
|
|
||||
3 | if TYPE_CHECKING:
|
||||
4 | pass # TCH005
|
||||
| ^^^^ TCH005
|
||||
4 | pass # TC005
|
||||
| ^^^^ TC005
|
||||
|
|
||||
= help: Delete empty type-checking block
|
||||
|
||||
@@ -14,55 +14,55 @@ TCH005.py:4:5: TCH005 [*] Found empty type-checking block
|
||||
1 1 | from typing import TYPE_CHECKING, List
|
||||
2 2 |
|
||||
3 |-if TYPE_CHECKING:
|
||||
4 |- pass # TCH005
|
||||
4 |- pass # TC005
|
||||
5 3 |
|
||||
6 4 |
|
||||
7 5 | if False:
|
||||
|
||||
TCH005.py:8:5: TCH005 [*] Found empty type-checking block
|
||||
TC005.py:8:5: TC005 [*] Found empty type-checking block
|
||||
|
|
||||
7 | if False:
|
||||
8 | pass # TCH005
|
||||
| ^^^^ TCH005
|
||||
8 | pass # TC005
|
||||
| ^^^^ TC005
|
||||
9 |
|
||||
10 | if 0:
|
||||
|
|
||||
= help: Delete empty type-checking block
|
||||
|
||||
ℹ Safe fix
|
||||
4 4 | pass # TCH005
|
||||
4 4 | pass # TC005
|
||||
5 5 |
|
||||
6 6 |
|
||||
7 |-if False:
|
||||
8 |- pass # TCH005
|
||||
8 |- pass # TC005
|
||||
9 7 |
|
||||
10 8 | if 0:
|
||||
11 9 | pass # TCH005
|
||||
11 9 | pass # TC005
|
||||
|
||||
TCH005.py:11:5: TCH005 [*] Found empty type-checking block
|
||||
TC005.py:11:5: TC005 [*] Found empty type-checking block
|
||||
|
|
||||
10 | if 0:
|
||||
11 | pass # TCH005
|
||||
| ^^^^ TCH005
|
||||
11 | pass # TC005
|
||||
| ^^^^ TC005
|
||||
|
|
||||
= help: Delete empty type-checking block
|
||||
|
||||
ℹ Safe fix
|
||||
7 7 | if False:
|
||||
8 8 | pass # TCH005
|
||||
8 8 | pass # TC005
|
||||
9 9 |
|
||||
10 |-if 0:
|
||||
11 |- pass # TCH005
|
||||
11 |- pass # TC005
|
||||
12 10 |
|
||||
13 11 |
|
||||
14 12 | def example():
|
||||
|
||||
TCH005.py:16:9: TCH005 [*] Found empty type-checking block
|
||||
TC005.py:16:9: TC005 [*] Found empty type-checking block
|
||||
|
|
||||
14 | def example():
|
||||
15 | if TYPE_CHECKING:
|
||||
16 | pass # TCH005
|
||||
| ^^^^ TCH005
|
||||
16 | pass # TC005
|
||||
| ^^^^ TC005
|
||||
17 | return
|
||||
|
|
||||
= help: Delete empty type-checking block
|
||||
@@ -72,17 +72,17 @@ TCH005.py:16:9: TCH005 [*] Found empty type-checking block
|
||||
13 13 |
|
||||
14 14 | def example():
|
||||
15 |- if TYPE_CHECKING:
|
||||
16 |- pass # TCH005
|
||||
16 |- pass # TC005
|
||||
17 15 | return
|
||||
18 16 |
|
||||
19 17 |
|
||||
|
||||
TCH005.py:22:9: TCH005 [*] Found empty type-checking block
|
||||
TC005.py:22:9: TC005 [*] Found empty type-checking block
|
||||
|
|
||||
20 | class Test:
|
||||
21 | if TYPE_CHECKING:
|
||||
22 | pass # TCH005
|
||||
| ^^^^ TCH005
|
||||
22 | pass # TC005
|
||||
| ^^^^ TC005
|
||||
23 | x = 2
|
||||
|
|
||||
= help: Delete empty type-checking block
|
||||
@@ -92,16 +92,16 @@ TCH005.py:22:9: TCH005 [*] Found empty type-checking block
|
||||
19 19 |
|
||||
20 20 | class Test:
|
||||
21 |- if TYPE_CHECKING:
|
||||
22 |- pass # TCH005
|
||||
22 |- pass # TC005
|
||||
23 21 | x = 2
|
||||
24 22 |
|
||||
25 23 |
|
||||
|
||||
TCH005.py:45:5: TCH005 [*] Found empty type-checking block
|
||||
TC005.py:45:5: TC005 [*] Found empty type-checking block
|
||||
|
|
||||
44 | if TYPE_CHECKING:
|
||||
45 | pass # TCH005
|
||||
| ^^^^ TCH005
|
||||
45 | pass # TC005
|
||||
| ^^^^ TC005
|
||||
46 |
|
||||
47 | # https://github.com/astral-sh/ruff/issues/11368
|
||||
|
|
||||
@@ -112,7 +112,7 @@ TCH005.py:45:5: TCH005 [*] Found empty type-checking block
|
||||
42 42 | from typing_extensions import TYPE_CHECKING
|
||||
43 43 |
|
||||
44 |-if TYPE_CHECKING:
|
||||
45 |- pass # TCH005
|
||||
45 |- pass # TC005
|
||||
46 44 |
|
||||
47 45 | # https://github.com/astral-sh/ruff/issues/11368
|
||||
48 46 | if TYPE_CHECKING:
|
||||
@@ -2,11 +2,11 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
exempt_modules.py:14:12: TCH002 [*] Move third-party import `flask` into a type-checking block
|
||||
exempt_modules.py:14:12: TC002 [*] Move third-party import `flask` into a type-checking block
|
||||
|
|
||||
13 | def f():
|
||||
14 | import flask
|
||||
| ^^^^^ TCH002
|
||||
| ^^^^^ TC002
|
||||
15 |
|
||||
16 | x: flask
|
||||
|
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
<filename>:5:5: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
<filename>:5:5: TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
|
|
||||
4 | from pandas import (
|
||||
5 | DataFrame, # DataFrame
|
||||
| ^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^ TC002
|
||||
6 | Series, # Series
|
||||
7 | )
|
||||
|
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
<filename>:7:5: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
<filename>:7:5: TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
|
|
||||
6 | from pandas import (
|
||||
7 | DataFrame, # DataFrame
|
||||
| ^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^ TC002
|
||||
8 | Series, # Series
|
||||
9 | )
|
||||
|
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
<filename>:7:5: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
<filename>:7:5: TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
|
|
||||
6 | from pandas import (
|
||||
7 | DataFrame, # DataFrame
|
||||
| ^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^ TC002
|
||||
8 | Series, # Series
|
||||
9 | )
|
||||
|
|
||||
@@ -30,12 +30,12 @@ snapshot_kind: text
|
||||
11 13 | def f(x: DataFrame, y: Series):
|
||||
12 14 | pass
|
||||
|
||||
<filename>:8:5: TCH002 [*] Move third-party import `pandas.Series` into a type-checking block
|
||||
<filename>:8:5: TC002 [*] Move third-party import `pandas.Series` into a type-checking block
|
||||
|
|
||||
6 | from pandas import (
|
||||
7 | DataFrame, # DataFrame
|
||||
8 | Series, # Series
|
||||
| ^^^^^^ TCH002
|
||||
| ^^^^^^ TC002
|
||||
9 | )
|
||||
|
|
||||
= help: Move into type-checking block
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
<filename>:6:8: TCH003 [*] Move standard library import `os` into a type-checking block
|
||||
<filename>:6:8: TC003 [*] Move standard library import `os` into a type-checking block
|
||||
|
|
||||
4 | from typing import TYPE_CHECKING
|
||||
5 |
|
||||
6 | import os, pandas
|
||||
| ^^ TCH003
|
||||
| ^^ TC003
|
||||
7 |
|
||||
8 | def f(x: os, y: pandas):
|
||||
|
|
||||
@@ -26,12 +26,12 @@ snapshot_kind: text
|
||||
8 11 | def f(x: os, y: pandas):
|
||||
9 12 | pass
|
||||
|
||||
<filename>:6:12: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
||||
<filename>:6:12: TC002 [*] Move third-party import `pandas` into a type-checking block
|
||||
|
|
||||
4 | from typing import TYPE_CHECKING
|
||||
5 |
|
||||
6 | import os, pandas
|
||||
| ^^^^^^ TCH002
|
||||
| ^^^^^^ TC002
|
||||
7 |
|
||||
8 | def f(x: os, y: pandas):
|
||||
|
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
<filename>:6:8: TCH003 [*] Move standard library import `os` into a type-checking block
|
||||
<filename>:6:8: TC003 [*] Move standard library import `os` into a type-checking block
|
||||
|
|
||||
4 | from typing import TYPE_CHECKING
|
||||
5 |
|
||||
6 | import os, sys
|
||||
| ^^ TCH003
|
||||
| ^^ TC003
|
||||
7 |
|
||||
8 | def f(x: os, y: sys):
|
||||
|
|
||||
@@ -25,12 +25,12 @@ snapshot_kind: text
|
||||
8 10 | def f(x: os, y: sys):
|
||||
9 11 | pass
|
||||
|
||||
<filename>:6:12: TCH003 [*] Move standard library import `sys` into a type-checking block
|
||||
<filename>:6:12: TC003 [*] Move standard library import `sys` into a type-checking block
|
||||
|
|
||||
4 | from typing import TYPE_CHECKING
|
||||
5 |
|
||||
6 | import os, sys
|
||||
| ^^^ TCH003
|
||||
| ^^^ TC003
|
||||
7 |
|
||||
8 | def f(x: os, y: sys):
|
||||
|
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
<filename>:4:18: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
||||
<filename>:4:18: TC002 [*] Move third-party import `pandas` into a type-checking block
|
||||
|
|
||||
2 | from __future__ import annotations
|
||||
3 |
|
||||
4 | import pandas as pd
|
||||
| ^^ TCH002
|
||||
| ^^ TC002
|
||||
5 |
|
||||
6 | def f(x: pd.DataFrame):
|
||||
|
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
quote.py:57:28: TCH004 [*] Quote references to `pandas.DataFrame`. Import is in a type-checking block.
|
||||
quote.py:57:28: TC004 [*] Quote references to `pandas.DataFrame`. Import is in a type-checking block.
|
||||
|
|
||||
56 | if TYPE_CHECKING:
|
||||
57 | from pandas import DataFrame
|
||||
| ^^^^^^^^^ TCH004
|
||||
| ^^^^^^^^^ TC004
|
||||
58 |
|
||||
59 | def func(value: DataFrame):
|
||||
|
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
quote.py:2:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
quote.py:2:24: TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
|
|
||||
1 | def f():
|
||||
2 | from pandas import DataFrame
|
||||
| ^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^ TC002
|
||||
3 |
|
||||
4 | def baz() -> DataFrame:
|
||||
|
|
||||
@@ -26,11 +26,11 @@ quote.py:2:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type
|
||||
6 9 |
|
||||
7 10 |
|
||||
|
||||
quote.py:9:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
quote.py:9:24: TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
|
|
||||
8 | def f():
|
||||
9 | from pandas import DataFrame
|
||||
| ^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^ TC002
|
||||
10 |
|
||||
11 | def baz() -> DataFrame[int]:
|
||||
|
|
||||
@@ -56,11 +56,11 @@ quote.py:9:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type
|
||||
13 16 |
|
||||
14 17 |
|
||||
|
||||
quote.py:16:22: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
||||
quote.py:16:22: TC002 [*] Move third-party import `pandas` into a type-checking block
|
||||
|
|
||||
15 | def f():
|
||||
16 | import pandas as pd
|
||||
| ^^ TCH002
|
||||
| ^^ TC002
|
||||
17 |
|
||||
18 | def baz() -> pd.DataFrame:
|
||||
|
|
||||
@@ -86,11 +86,11 @@ quote.py:16:22: TCH002 [*] Move third-party import `pandas` into a type-checking
|
||||
20 23 |
|
||||
21 24 |
|
||||
|
||||
quote.py:23:22: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
||||
quote.py:23:22: TC002 [*] Move third-party import `pandas` into a type-checking block
|
||||
|
|
||||
22 | def f():
|
||||
23 | import pandas as pd
|
||||
| ^^ TCH002
|
||||
| ^^ TC002
|
||||
24 |
|
||||
25 | def baz() -> pd.DataFrame.Extra:
|
||||
|
|
||||
@@ -116,11 +116,11 @@ quote.py:23:22: TCH002 [*] Move third-party import `pandas` into a type-checking
|
||||
27 30 |
|
||||
28 31 |
|
||||
|
||||
quote.py:30:22: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
||||
quote.py:30:22: TC002 [*] Move third-party import `pandas` into a type-checking block
|
||||
|
|
||||
29 | def f():
|
||||
30 | import pandas as pd
|
||||
| ^^ TCH002
|
||||
| ^^ TC002
|
||||
31 |
|
||||
32 | def baz() -> pd.DataFrame | int:
|
||||
|
|
||||
@@ -146,11 +146,11 @@ quote.py:30:22: TCH002 [*] Move third-party import `pandas` into a type-checking
|
||||
34 37 |
|
||||
35 38 |
|
||||
|
||||
quote.py:38:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
quote.py:38:24: TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
|
|
||||
37 | def f():
|
||||
38 | from pandas import DataFrame
|
||||
| ^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^ TC002
|
||||
39 |
|
||||
40 | def baz() -> DataFrame():
|
||||
|
|
||||
@@ -176,12 +176,12 @@ quote.py:38:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a typ
|
||||
42 45 |
|
||||
43 46 |
|
||||
|
||||
quote.py:47:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
quote.py:47:24: TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
|
|
||||
45 | from typing import Literal
|
||||
46 |
|
||||
47 | from pandas import DataFrame
|
||||
| ^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^ TC002
|
||||
48 |
|
||||
49 | def baz() -> DataFrame[Literal["int"]]:
|
||||
|
|
||||
@@ -207,11 +207,11 @@ quote.py:47:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a typ
|
||||
51 54 |
|
||||
52 55 |
|
||||
|
||||
quote.py:64:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
quote.py:64:24: TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
|
|
||||
63 | def f():
|
||||
64 | from pandas import DataFrame, Series
|
||||
| ^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^ TC002
|
||||
65 |
|
||||
66 | def baz() -> DataFrame | Series:
|
||||
|
|
||||
@@ -237,11 +237,11 @@ quote.py:64:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a typ
|
||||
68 71 |
|
||||
69 72 |
|
||||
|
||||
quote.py:64:35: TCH002 [*] Move third-party import `pandas.Series` into a type-checking block
|
||||
quote.py:64:35: TC002 [*] Move third-party import `pandas.Series` into a type-checking block
|
||||
|
|
||||
63 | def f():
|
||||
64 | from pandas import DataFrame, Series
|
||||
| ^^^^^^ TCH002
|
||||
| ^^^^^^ TC002
|
||||
65 |
|
||||
66 | def baz() -> DataFrame | Series:
|
||||
|
|
||||
@@ -267,11 +267,11 @@ quote.py:64:35: TCH002 [*] Move third-party import `pandas.Series` into a type-c
|
||||
68 71 |
|
||||
69 72 |
|
||||
|
||||
quote.py:71:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
quote.py:71:24: TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
|
|
||||
70 | def f():
|
||||
71 | from pandas import DataFrame, Series
|
||||
| ^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^ TC002
|
||||
72 |
|
||||
73 | def baz() -> (
|
||||
|
|
||||
@@ -310,11 +310,11 @@ quote.py:71:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a typ
|
||||
86 86 |
|
||||
87 87 |
|
||||
|
||||
quote.py:71:35: TCH002 [*] Move third-party import `pandas.Series` into a type-checking block
|
||||
quote.py:71:35: TC002 [*] Move third-party import `pandas.Series` into a type-checking block
|
||||
|
|
||||
70 | def f():
|
||||
71 | from pandas import DataFrame, Series
|
||||
| ^^^^^^ TCH002
|
||||
| ^^^^^^ TC002
|
||||
72 |
|
||||
73 | def baz() -> (
|
||||
|
|
||||
@@ -353,11 +353,11 @@ quote.py:71:35: TCH002 [*] Move third-party import `pandas.Series` into a type-c
|
||||
86 86 |
|
||||
87 87 |
|
||||
|
||||
quote.py:89:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
quote.py:89:24: TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
||||
|
|
||||
88 | def f():
|
||||
89 | from pandas import DataFrame, Series
|
||||
| ^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^ TC002
|
||||
90 |
|
||||
91 | def func(self) -> DataFrame | list[Series]:
|
||||
|
|
||||
@@ -383,11 +383,11 @@ quote.py:89:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a typ
|
||||
93 96 |
|
||||
94 97 |
|
||||
|
||||
quote.py:89:35: TCH002 [*] Move third-party import `pandas.Series` into a type-checking block
|
||||
quote.py:89:35: TC002 [*] Move third-party import `pandas.Series` into a type-checking block
|
||||
|
|
||||
88 | def f():
|
||||
89 | from pandas import DataFrame, Series
|
||||
| ^^^^^^ TCH002
|
||||
| ^^^^^^ TC002
|
||||
90 |
|
||||
91 | def func(self) -> DataFrame | list[Series]:
|
||||
|
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
quote2.py:2:44: TCH002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
quote2.py:2:44: TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
|
|
||||
1 | def f():
|
||||
2 | from django.contrib.auth.models import AbstractBaseUser
|
||||
| ^^^^^^^^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^^^^^^^^ TC002
|
||||
3 |
|
||||
4 | def test_remove_inner_quotes_double(self, user: AbstractBaseUser["int"]):
|
||||
|
|
||||
@@ -26,11 +26,11 @@ quote2.py:2:44: TCH002 [*] Move third-party import `django.contrib.auth.models.A
|
||||
6 9 |
|
||||
7 10 |
|
||||
|
||||
quote2.py:9:44: TCH002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
quote2.py:9:44: TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
|
|
||||
8 | def f():
|
||||
9 | from django.contrib.auth.models import AbstractBaseUser
|
||||
| ^^^^^^^^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^^^^^^^^ TC002
|
||||
10 |
|
||||
11 | def test_remove_inner_quotes_single(self, user: AbstractBaseUser['int']):
|
||||
|
|
||||
@@ -56,11 +56,11 @@ quote2.py:9:44: TCH002 [*] Move third-party import `django.contrib.auth.models.A
|
||||
13 16 |
|
||||
14 17 |
|
||||
|
||||
quote2.py:16:44: TCH002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
quote2.py:16:44: TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
|
|
||||
15 | def f():
|
||||
16 | from django.contrib.auth.models import AbstractBaseUser
|
||||
| ^^^^^^^^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^^^^^^^^ TC002
|
||||
17 |
|
||||
18 | def test_remove_inner_quotes_mixed(self, user: AbstractBaseUser['int', "str"]):
|
||||
|
|
||||
@@ -86,12 +86,12 @@ quote2.py:16:44: TCH002 [*] Move third-party import `django.contrib.auth.models.
|
||||
20 23 |
|
||||
21 24 |
|
||||
|
||||
quote2.py:25:44: TCH002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
quote2.py:25:44: TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
|
|
||||
23 | from typing import Annotated, Literal
|
||||
24 |
|
||||
25 | from django.contrib.auth.models import AbstractBaseUser
|
||||
| ^^^^^^^^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^^^^^^^^ TC002
|
||||
26 |
|
||||
27 | def test_literal_annotation_args_contain_quotes(self, type1: AbstractBaseUser[Literal["user", "admin"], Annotated["int", "1", 2]]):
|
||||
|
|
||||
@@ -117,12 +117,12 @@ quote2.py:25:44: TCH002 [*] Move third-party import `django.contrib.auth.models.
|
||||
29 32 |
|
||||
30 33 |
|
||||
|
||||
quote2.py:34:44: TCH002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
quote2.py:34:44: TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
|
|
||||
32 | from typing import Literal
|
||||
33 |
|
||||
34 | from django.contrib.auth.models import AbstractBaseUser
|
||||
| ^^^^^^^^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^^^^^^^^ TC002
|
||||
35 |
|
||||
36 | def test_union_contain_inner_quotes(self, type1: AbstractBaseUser["int" | Literal["int"]]):
|
||||
|
|
||||
@@ -148,12 +148,12 @@ quote2.py:34:44: TCH002 [*] Move third-party import `django.contrib.auth.models.
|
||||
38 41 |
|
||||
39 42 |
|
||||
|
||||
quote2.py:43:44: TCH002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
quote2.py:43:44: TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
|
|
||||
41 | from typing import Literal
|
||||
42 |
|
||||
43 | from django.contrib.auth.models import AbstractBaseUser
|
||||
| ^^^^^^^^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^^^^^^^^ TC002
|
||||
44 |
|
||||
45 | def test_inner_literal_mixed_quotes(user: AbstractBaseUser[Literal['user', "admin"]]):
|
||||
|
|
||||
@@ -179,12 +179,12 @@ quote2.py:43:44: TCH002 [*] Move third-party import `django.contrib.auth.models.
|
||||
47 50 |
|
||||
48 51 |
|
||||
|
||||
quote2.py:52:44: TCH002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
quote2.py:52:44: TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
|
|
||||
50 | from typing import Literal
|
||||
51 |
|
||||
52 | from django.contrib.auth.models import AbstractBaseUser
|
||||
| ^^^^^^^^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^^^^^^^^ TC002
|
||||
53 |
|
||||
54 | def test_inner_literal_single_quote(user: AbstractBaseUser[Literal['int'], str]):
|
||||
|
|
||||
@@ -210,12 +210,12 @@ quote2.py:52:44: TCH002 [*] Move third-party import `django.contrib.auth.models.
|
||||
56 59 |
|
||||
57 60 |
|
||||
|
||||
quote2.py:61:44: TCH002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
quote2.py:61:44: TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
|
|
||||
59 | from typing import Literal
|
||||
60 |
|
||||
61 | from django.contrib.auth.models import AbstractBaseUser
|
||||
| ^^^^^^^^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^^^^^^^^ TC002
|
||||
62 |
|
||||
63 | def test_mixed_quotes_literal(user: AbstractBaseUser[Literal['user'], "int"]):
|
||||
|
|
||||
@@ -241,12 +241,12 @@ quote2.py:61:44: TCH002 [*] Move third-party import `django.contrib.auth.models.
|
||||
65 68 |
|
||||
66 69 |
|
||||
|
||||
quote2.py:70:44: TCH002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
quote2.py:70:44: TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
|
|
||||
68 | from typing import Annotated, Literal
|
||||
69 |
|
||||
70 | from django.contrib.auth.models import AbstractBaseUser
|
||||
| ^^^^^^^^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^^^^^^^^ TC002
|
||||
71 |
|
||||
72 | def test_annotated_literal_mixed_quotes(user: AbstractBaseUser[Annotated[str, "max_length=20", Literal['user', "admin"]]]):
|
||||
|
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
---
|
||||
quote3.py:4:44: TCH002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
quote3.py:4:44: TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
|
|
||||
2 | from typing import Literal, Union
|
||||
3 |
|
||||
4 | from django.contrib.auth.models import AbstractBaseUser
|
||||
| ^^^^^^^^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^^^^^^^^ TC002
|
||||
5 |
|
||||
6 | def test_union_literal_mixed_quotes(user: AbstractBaseUser[Union[Literal['active', "inactive"], str]]):
|
||||
|
|
||||
@@ -28,12 +28,12 @@ quote3.py:4:44: TCH002 [*] Move third-party import `django.contrib.auth.models.A
|
||||
8 11 |
|
||||
9 12 |
|
||||
|
||||
quote3.py:13:44: TCH002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
quote3.py:13:44: TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
|
|
||||
11 | from typing import Callable, Literal
|
||||
12 |
|
||||
13 | from django.contrib.auth.models import AbstractBaseUser
|
||||
| ^^^^^^^^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^^^^^^^^ TC002
|
||||
14 |
|
||||
15 | def test_callable_literal_mixed_quotes(callable_fn: AbstractBaseUser[Callable[["int", Literal['admin', "user"]], 'bool']]):
|
||||
|
|
||||
@@ -59,12 +59,12 @@ quote3.py:13:44: TCH002 [*] Move third-party import `django.contrib.auth.models.
|
||||
17 20 |
|
||||
18 21 |
|
||||
|
||||
quote3.py:22:44: TCH002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
quote3.py:22:44: TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
|
||||
|
|
||||
20 | from typing import Annotated, Callable, Literal
|
||||
21 |
|
||||
22 | from django.contrib.auth.models import AbstractBaseUser
|
||||
| ^^^^^^^^^^^^^^^^ TCH002
|
||||
| ^^^^^^^^^^^^^^^^ TC002
|
||||
23 |
|
||||
24 | def test_callable_annotated_literal(callable_fn: AbstractBaseUser[Callable[[int, Annotated[str, Literal['active', "inactive"]]], bool]]):
|
||||
|
|
||||
@@ -90,12 +90,12 @@ quote3.py:22:44: TCH002 [*] Move third-party import `django.contrib.auth.models.
|
||||
26 29 |
|
||||
27 30 |
|
||||
|
||||
quote3.py:31:37: TCH002 [*] Move third-party import `django.contrib.auth.models` into a type-checking block
|
||||
quote3.py:31:37: TC002 [*] Move third-party import `django.contrib.auth.models` into a type-checking block
|
||||
|
|
||||
29 | from typing import literal
|
||||
30 |
|
||||
31 | from django.contrib.auth import models
|
||||
| ^^^^^^ TCH002
|
||||
| ^^^^^^ TC002
|
||||
32 |
|
||||
33 | def test_attribute(arg: models.AbstractBaseUser["int"]):
|
||||
|
|
||||
@@ -121,12 +121,12 @@ quote3.py:31:37: TCH002 [*] Move third-party import `django.contrib.auth.models`
|
||||
35 38 |
|
||||
36 39 |
|
||||
|
||||
quote3.py:40:37: TCH002 [*] Move third-party import `django.contrib.auth.models` into a type-checking block
|
||||
quote3.py:40:37: TC002 [*] Move third-party import `django.contrib.auth.models` into a type-checking block
|
||||
|
|
||||
38 | from typing import Literal
|
||||
39 |
|
||||
40 | from django.contrib.auth import models
|
||||
| ^^^^^^ TCH002
|
||||
| ^^^^^^ TC002
|
||||
41 |
|
||||
42 | def test_attribute_typing_literal(arg: models.AbstractBaseUser[Literal["admin"]]):
|
||||
|
|
||||
@@ -152,23 +152,23 @@ quote3.py:40:37: TCH002 [*] Move third-party import `django.contrib.auth.models`
|
||||
44 47 |
|
||||
45 48 |
|
||||
|
||||
quote3.py:59:29: TCH002 Move third-party import `third_party.Type` into a type-checking block
|
||||
quote3.py:59:29: TC002 Move third-party import `third_party.Type` into a type-checking block
|
||||
|
|
||||
57 | def f():
|
||||
58 | from typing import Literal
|
||||
59 | from third_party import Type
|
||||
| ^^^^ TCH002
|
||||
| ^^^^ TC002
|
||||
60 |
|
||||
61 | def test_string_contains_opposite_quote_do_not_fix(self, type1: Type[Literal["'"]], type2: Type[Literal["\'"]]):
|
||||
|
|
||||
= help: Move into type-checking block
|
||||
|
||||
quote3.py:67:29: TCH002 Move third-party import `third_party.Type` into a type-checking block
|
||||
quote3.py:67:29: TC002 Move third-party import `third_party.Type` into a type-checking block
|
||||
|
|
||||
65 | def f():
|
||||
66 | from typing import Literal
|
||||
67 | from third_party import Type
|
||||
| ^^^^ TCH002
|
||||
| ^^^^ TC002
|
||||
68 |
|
||||
69 | def test_quote_contains_backslash(self, type1: Type[Literal["\n"]], type2: Type[Literal["\""]]):
|
||||
|
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
TCH004_1.py:4:26: TCH004 [*] Move import `datetime.datetime` out of type-checking block. Import is used for more than type hinting.
|
||||
TC004_1.py:4:26: TC004 [*] Move import `datetime.datetime` out of type-checking block. Import is used for more than type hinting.
|
||||
|
|
||||
3 | if TYPE_CHECKING:
|
||||
4 | from datetime import datetime
|
||||
| ^^^^^^^^ TCH004
|
||||
| ^^^^^^^^ TC004
|
||||
5 | x = datetime
|
||||
|
|
||||
= help: Move out of type-checking block
|
||||
@@ -2,11 +2,11 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
TCH004_11.py:4:24: TCH004 [*] Move import `typing.List` out of type-checking block. Import is used for more than type hinting.
|
||||
TC004_11.py:4:24: TC004 [*] Move import `typing.List` out of type-checking block. Import is used for more than type hinting.
|
||||
|
|
||||
3 | if TYPE_CHECKING:
|
||||
4 | from typing import List
|
||||
| ^^^^ TCH004
|
||||
| ^^^^ TC004
|
||||
5 |
|
||||
6 | __all__ = ("List",)
|
||||
|
|
||||
@@ -2,11 +2,11 @@
|
||||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
TCH004_12.py:6:33: TCH004 [*] Move import `collections.abc.Callable` out of type-checking block. Import is used for more than type hinting.
|
||||
TC004_12.py:6:33: TC004 [*] Move import `collections.abc.Callable` out of type-checking block. Import is used for more than type hinting.
|
||||
|
|
||||
5 | if TYPE_CHECKING:
|
||||
6 | from collections.abc import Callable
|
||||
| ^^^^^^^^ TCH004
|
||||
| ^^^^^^^^ TC004
|
||||
7 |
|
||||
8 | AnyCallable: TypeAlias = Callable[..., Any]
|
||||
|
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user