Compare commits
2 Commits
david/stab
...
micha/more
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f78c03c5dc | ||
|
|
1fa5e1fbbc |
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,9 +1,8 @@
|
||||
<!--
|
||||
Thank you for contributing to Ruff/ty! To help us out with reviewing, please consider the following:
|
||||
Thank you for contributing to Ruff! To help us out with reviewing, please consider the following:
|
||||
|
||||
- Does this pull request include a summary of the change? (See below.)
|
||||
- Does this pull request include a descriptive title? (Please prefix with `[ty]` for ty pull
|
||||
requests.)
|
||||
- Does this pull request include a descriptive title?
|
||||
- Does this pull request include references to any relevant issues?
|
||||
-->
|
||||
|
||||
|
||||
12
.github/workflows/build-docker.yml
vendored
12
.github/workflows/build-docker.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ matrix.platform }}
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
type=pep440,pattern={{ version }},value=${{ fromJson(inputs.plan).announcement_tag }}
|
||||
type=pep440,pattern={{ major }}.{{ minor }},value=${{ fromJson(inputs.plan).announcement_tag }}
|
||||
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -169,7 +169,7 @@ jobs:
|
||||
steps:
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -231,7 +231,7 @@ jobs:
|
||||
${{ env.TAG_PATTERNS }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
@@ -276,7 +276,7 @@ jobs:
|
||||
type=pep440,pattern={{ version }},value=${{ fromJson(inputs.plan).announcement_tag }}
|
||||
type=pep440,pattern={{ major }}.{{ minor }},value=${{ fromJson(inputs.plan).announcement_tag }}
|
||||
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
|
||||
20
.github/workflows/ci.yaml
vendored
20
.github/workflows/ci.yaml
vendored
@@ -239,11 +239,11 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: ty mdtests (GitHub annotations)
|
||||
@@ -297,11 +297,11 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: "Run tests"
|
||||
@@ -324,7 +324,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Run tests"
|
||||
@@ -407,11 +407,11 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: "Run tests"
|
||||
@@ -437,7 +437,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo-binstall"
|
||||
uses: cargo-bins/cargo-binstall@13f9d60d5358393bf14644dba56d9f123bc5d595 # v1.12.4
|
||||
uses: cargo-bins/cargo-binstall@63aaa5c1932cebabc34eceda9d92a70215dcead6 # v1.12.3
|
||||
with:
|
||||
tool: cargo-fuzz@0.11.2
|
||||
- name: "Install cargo-fuzz"
|
||||
@@ -692,7 +692,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: cargo-bins/cargo-binstall@13f9d60d5358393bf14644dba56d9f123bc5d595 # v1.12.4
|
||||
- uses: cargo-bins/cargo-binstall@63aaa5c1932cebabc34eceda9d92a70215dcead6 # v1.12.3
|
||||
- run: cargo binstall --no-confirm cargo-shear
|
||||
- run: cargo shear
|
||||
|
||||
@@ -908,7 +908,7 @@ jobs:
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
|
||||
2
.github/workflows/mypy_primer.yaml
vendored
2
.github/workflows/mypy_primer.yaml
vendored
@@ -69,7 +69,7 @@ jobs:
|
||||
echo "Project selector: $PRIMER_SELECTOR"
|
||||
# Allow the exit code to be 0 or 1, only fail for actual mypy_primer crashes/bugs
|
||||
uvx \
|
||||
--from="git+https://github.com/hauntsaninja/mypy_primer@968b2b61c05f84462d6fcc78d2f5205bbb8b98c2" \
|
||||
--from="git+https://github.com/hauntsaninja/mypy_primer@4b15cf3b07db69db67bbfaebfffb2a8a28040933" \
|
||||
mypy_primer \
|
||||
--repo ruff \
|
||||
--type-checker ty \
|
||||
|
||||
@@ -5,7 +5,7 @@ exclude: |
|
||||
.github/workflows/release.yml|
|
||||
crates/ty_vendored/vendor/.*|
|
||||
crates/ty_project/resources/.*|
|
||||
crates/ty/docs/(configuration|rules|cli).md|
|
||||
crates/ty/docs/rules.md|
|
||||
crates/ruff_benchmark/resources/.*|
|
||||
crates/ruff_linter/resources/.*|
|
||||
crates/ruff_linter/src/rules/.*/snapshots/.*|
|
||||
@@ -80,7 +80,7 @@ repos:
|
||||
pass_filenames: false # This makes it a lot faster
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.11.9
|
||||
rev: v0.11.8
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
- id: ruff
|
||||
@@ -98,7 +98,7 @@ repos:
|
||||
# zizmor detects security vulnerabilities in GitHub Actions workflows.
|
||||
# Additional configuration for the tool is found in `.github/zizmor.yml`
|
||||
- repo: https://github.com/woodruffw/zizmor-pre-commit
|
||||
rev: v1.7.0
|
||||
rev: v1.6.0
|
||||
hooks:
|
||||
- id: zizmor
|
||||
|
||||
|
||||
32
CHANGELOG.md
32
CHANGELOG.md
@@ -1,37 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 0.11.9
|
||||
|
||||
### Preview features
|
||||
|
||||
- Default to latest supported Python version for version-related syntax errors ([#17529](https://github.com/astral-sh/ruff/pull/17529))
|
||||
- Implement deferred annotations for Python 3.14 ([#17658](https://github.com/astral-sh/ruff/pull/17658))
|
||||
- \[`airflow`\] Fix `SQLTableCheckOperator` typo (`AIR302`) ([#17946](https://github.com/astral-sh/ruff/pull/17946))
|
||||
- \[`airflow`\] Remove `airflow.utils.dag_parsing_context.get_parsing_context` (`AIR301`) ([#17852](https://github.com/astral-sh/ruff/pull/17852))
|
||||
- \[`airflow`\] Skip attribute check in try catch block (`AIR301`) ([#17790](https://github.com/astral-sh/ruff/pull/17790))
|
||||
- \[`flake8-bandit`\] Mark tuples of string literals as trusted input in `S603` ([#17801](https://github.com/astral-sh/ruff/pull/17801))
|
||||
- \[`isort`\] Check full module path against project root(s) when categorizing first-party imports ([#16565](https://github.com/astral-sh/ruff/pull/16565))
|
||||
- \[`ruff`\] Add new rule `in-empty-collection` (`RUF060`) ([#16480](https://github.com/astral-sh/ruff/pull/16480))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Fix missing `combine` call for `lint.typing-extensions` setting ([#17823](https://github.com/astral-sh/ruff/pull/17823))
|
||||
- \[`flake8-async`\] Fix module name in `ASYNC110`, `ASYNC115`, and `ASYNC116` fixes ([#17774](https://github.com/astral-sh/ruff/pull/17774))
|
||||
- \[`pyupgrade`\] Add spaces between tokens as necessary to avoid syntax errors in `UP018` autofix ([#17648](https://github.com/astral-sh/ruff/pull/17648))
|
||||
- \[`refurb`\] Fix false positive for float and complex numbers in `FURB116` ([#17661](https://github.com/astral-sh/ruff/pull/17661))
|
||||
- [parser] Flag single unparenthesized generator expr with trailing comma in arguments. ([#17893](https://github.com/astral-sh/ruff/pull/17893))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Add instructions on how to upgrade to a newer Rust version ([#17928](https://github.com/astral-sh/ruff/pull/17928))
|
||||
- Update code of conduct email address ([#17875](https://github.com/astral-sh/ruff/pull/17875))
|
||||
- Add fix safety sections to `PLC2801`, `PLR1722`, and `RUF013` ([#17825](https://github.com/astral-sh/ruff/pull/17825), [#17826](https://github.com/astral-sh/ruff/pull/17826), [#17759](https://github.com/astral-sh/ruff/pull/17759))
|
||||
- Add link to `check-typed-exception` from `S110` and `S112` ([#17786](https://github.com/astral-sh/ruff/pull/17786))
|
||||
|
||||
### Other changes
|
||||
|
||||
- Allow passing a virtual environment to `ruff analyze graph` ([#17743](https://github.com/astral-sh/ruff/pull/17743))
|
||||
|
||||
## 0.11.8
|
||||
|
||||
### Preview features
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
|
||||
Welcome! We're happy to have you here. Thank you in advance for your contribution to Ruff.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> This guide is for Ruff. If you're looking to contribute to ty, please see [the ty contributing
|
||||
> guide](https://github.com/astral-sh/ruff/blob/main/crates/ty/CONTRIBUTING.md).
|
||||
|
||||
## The Basics
|
||||
|
||||
Ruff welcomes contributions in the form of pull requests.
|
||||
|
||||
104
Cargo.lock
generated
104
Cargo.lock
generated
@@ -334,9 +334,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.38"
|
||||
version = "4.5.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
|
||||
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -344,9 +344,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.38"
|
||||
version = "4.5.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
|
||||
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -409,7 +409,7 @@ version = "4.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c41dc435a7b98e4608224bbf65282309f5403719df9113621b30f8b6f74e2f4"
|
||||
dependencies = [
|
||||
"nix 0.29.0",
|
||||
"nix",
|
||||
"terminfo",
|
||||
"thiserror 2.0.12",
|
||||
"which",
|
||||
@@ -478,7 +478,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -487,7 +487,7 @@ version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -681,11 +681,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.4.7"
|
||||
version = "3.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73"
|
||||
checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c"
|
||||
dependencies = [
|
||||
"nix 0.30.1",
|
||||
"nix",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -1057,9 +1057,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@@ -1539,9 +1539,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.13"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806"
|
||||
checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"jiff-tzdb-platform",
|
||||
@@ -1554,9 +1554,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.13"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48"
|
||||
checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1771,15 +1771,6 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5cab8f2cadc416a82d2e783a1946388b31654d391d1c7d92cc1f03e295b1deb"
|
||||
dependencies = [
|
||||
"unicode-id",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
@@ -1880,18 +1871,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
@@ -2470,7 +2449,7 @@ version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2570,7 +2549,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.11.9"
|
||||
version = "0.11.8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argfile",
|
||||
@@ -2604,7 +2583,6 @@ dependencies = [
|
||||
"ruff_linter",
|
||||
"ruff_macros",
|
||||
"ruff_notebook",
|
||||
"ruff_options_metadata",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_formatter",
|
||||
"ruff_python_parser",
|
||||
@@ -2723,7 +2701,6 @@ dependencies = [
|
||||
"indoc",
|
||||
"itertools 0.14.0",
|
||||
"libcst",
|
||||
"markdown",
|
||||
"pretty_assertions",
|
||||
"rayon",
|
||||
"regex",
|
||||
@@ -2732,7 +2709,6 @@ dependencies = [
|
||||
"ruff_formatter",
|
||||
"ruff_linter",
|
||||
"ruff_notebook",
|
||||
"ruff_options_metadata",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_codegen",
|
||||
"ruff_python_formatter",
|
||||
@@ -2749,7 +2725,6 @@ dependencies = [
|
||||
"tracing",
|
||||
"tracing-indicatif",
|
||||
"tracing-subscriber",
|
||||
"ty",
|
||||
"ty_project",
|
||||
"url",
|
||||
]
|
||||
@@ -2811,7 +2786,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.11.9"
|
||||
version = "0.11.8"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"anyhow",
|
||||
@@ -2901,13 +2876,6 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_options_metadata"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_ast"
|
||||
version = "0.0.0"
|
||||
@@ -3146,11 +3114,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_wasm"
|
||||
version = "0.11.9"
|
||||
version = "0.11.8"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.2",
|
||||
"js-sys",
|
||||
"log",
|
||||
"ruff_formatter",
|
||||
@@ -3194,7 +3162,6 @@ dependencies = [
|
||||
"ruff_graph",
|
||||
"ruff_linter",
|
||||
"ruff_macros",
|
||||
"ruff_options_metadata",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_formatter",
|
||||
"ruff_python_semantic",
|
||||
@@ -3231,12 +3198,6 @@ version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-stable-hash"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "781442f29170c5c93b7185ad559492601acdc71d5bb0706f5868094f45cfcd08"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.44"
|
||||
@@ -3278,7 +3239,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
[[package]]
|
||||
name = "salsa"
|
||||
version = "0.21.1"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=7edce6e248f35c8114b4b021cdb474a3fb2813b3#7edce6e248f35c8114b4b021cdb474a3fb2813b3"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=af69cc11146352ec2eb6d972537e99c473ac3748#af69cc11146352ec2eb6d972537e99c473ac3748"
|
||||
dependencies = [
|
||||
"boxcar",
|
||||
"compact_str",
|
||||
@@ -3301,12 +3262,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "salsa-macro-rules"
|
||||
version = "0.21.1"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=7edce6e248f35c8114b4b021cdb474a3fb2813b3#7edce6e248f35c8114b4b021cdb474a3fb2813b3"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=af69cc11146352ec2eb6d972537e99c473ac3748#af69cc11146352ec2eb6d972537e99c473ac3748"
|
||||
|
||||
[[package]]
|
||||
name = "salsa-macros"
|
||||
version = "0.21.1"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=7edce6e248f35c8114b4b021cdb474a3fb2813b3#7edce6e248f35c8114b4b021cdb474a3fb2813b3"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=af69cc11146352ec2eb6d972537e99c473ac3748#af69cc11146352ec2eb6d972537e99c473ac3748"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -3643,7 +3604,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.2",
|
||||
"once_cell",
|
||||
"rustix 1.0.2",
|
||||
"windows-sys 0.59.0",
|
||||
@@ -4002,7 +3963,6 @@ dependencies = [
|
||||
"crossbeam",
|
||||
"ctrlc",
|
||||
"filetime",
|
||||
"indicatif",
|
||||
"insta",
|
||||
"insta-cmd",
|
||||
"jiff",
|
||||
@@ -4055,7 +4015,6 @@ dependencies = [
|
||||
"ruff_cache",
|
||||
"ruff_db",
|
||||
"ruff_macros",
|
||||
"ruff_options_metadata",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_formatter",
|
||||
"ruff_text_size",
|
||||
@@ -4159,7 +4118,6 @@ dependencies = [
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustc-stable-hash",
|
||||
"salsa",
|
||||
"serde",
|
||||
"smallvec",
|
||||
@@ -4187,7 +4145,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.2",
|
||||
"js-sys",
|
||||
"log",
|
||||
"ruff_db",
|
||||
@@ -4263,12 +4221,6 @@ dependencies = [
|
||||
"unic-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-id"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
@@ -4372,7 +4324,7 @@ version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.2",
|
||||
"js-sys",
|
||||
"rand 0.9.1",
|
||||
"uuid-macro-internal",
|
||||
@@ -4645,7 +4597,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -23,7 +23,6 @@ ruff_index = { path = "crates/ruff_index" }
|
||||
ruff_linter = { path = "crates/ruff_linter" }
|
||||
ruff_macros = { path = "crates/ruff_macros" }
|
||||
ruff_notebook = { path = "crates/ruff_notebook" }
|
||||
ruff_options_metadata = { path = "crates/ruff_options_metadata" }
|
||||
ruff_python_ast = { path = "crates/ruff_python_ast" }
|
||||
ruff_python_codegen = { path = "crates/ruff_python_codegen" }
|
||||
ruff_python_formatter = { path = "crates/ruff_python_formatter" }
|
||||
@@ -38,7 +37,6 @@ ruff_source_file = { path = "crates/ruff_source_file" }
|
||||
ruff_text_size = { path = "crates/ruff_text_size" }
|
||||
ruff_workspace = { path = "crates/ruff_workspace" }
|
||||
|
||||
ty = { path = "crates/ty" }
|
||||
ty_ide = { path = "crates/ty_ide" }
|
||||
ty_project = { path = "crates/ty_project", default-features = false }
|
||||
ty_python_semantic = { path = "crates/ty_python_semantic" }
|
||||
@@ -125,9 +123,8 @@ rand = { version = "0.9.0" }
|
||||
rayon = { version = "1.10.0" }
|
||||
regex = { version = "1.10.2" }
|
||||
rustc-hash = { version = "2.0.0" }
|
||||
rustc-stable-hash = { version = "0.1.2" }
|
||||
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "7edce6e248f35c8114b4b021cdb474a3fb2813b3" }
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "af69cc11146352ec2eb6d972537e99c473ac3748" }
|
||||
schemars = { version = "0.8.16" }
|
||||
seahash = { version = "4.1.0" }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
@@ -185,7 +182,7 @@ wild = { version = "2" }
|
||||
zip = { version = "0.6.6", default-features = false }
|
||||
|
||||
[workspace.metadata.cargo-shear]
|
||||
ignored = ["getrandom", "ruff_options_metadata"]
|
||||
ignored = ["getrandom"]
|
||||
|
||||
|
||||
[workspace.lints.rust]
|
||||
|
||||
@@ -149,8 +149,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
|
||||
|
||||
# For a specific version.
|
||||
curl -LsSf https://astral.sh/ruff/0.11.9/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.11.9/install.ps1 | iex"
|
||||
curl -LsSf https://astral.sh/ruff/0.11.8/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.11.8/install.ps1 | iex"
|
||||
```
|
||||
|
||||
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
||||
@@ -183,7 +183,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.11.9
|
||||
rev: v0.11.8
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
@@ -255,7 +255,7 @@ indent-width = 4
|
||||
target-version = "py39"
|
||||
|
||||
[lint]
|
||||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
||||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
||||
select = ["E4", "E7", "E9", "F"]
|
||||
ignore = []
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.11.9"
|
||||
version = "0.11.8"
|
||||
publish = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -20,7 +20,6 @@ ruff_graph = { workspace = true, features = ["serde", "clap"] }
|
||||
ruff_linter = { workspace = true, features = ["clap"] }
|
||||
ruff_macros = { workspace = true }
|
||||
ruff_notebook = { workspace = true }
|
||||
ruff_options_metadata = { workspace = true, features = ["serde"] }
|
||||
ruff_python_ast = { workspace = true }
|
||||
ruff_python_formatter = { workspace = true }
|
||||
ruff_python_parser = { workspace = true }
|
||||
@@ -84,7 +83,7 @@ dist = true
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
mimalloc = { workspace = true }
|
||||
|
||||
[target.'cfg(all(not(target_os = "windows"), not(target_os = "openbsd"), not(target_os = "aix"), not(target_os = "android"), any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))'.dependencies]
|
||||
[target.'cfg(all(not(target_os = "windows"), not(target_os = "openbsd"), not(target_os = "aix"), any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))'.dependencies]
|
||||
tikv-jemallocator = { workspace = true }
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -22,12 +22,12 @@ use ruff_linter::settings::types::{
|
||||
PythonVersion, UnsafeFixes,
|
||||
};
|
||||
use ruff_linter::{RuleParser, RuleSelector, RuleSelectorParser};
|
||||
use ruff_options_metadata::{OptionEntry, OptionsMetadata};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_source_file::{LineIndex, OneIndexed, PositionEncoding};
|
||||
use ruff_text_size::TextRange;
|
||||
use ruff_workspace::configuration::{Configuration, RuleSelection};
|
||||
use ruff_workspace::options::{Options, PycodestyleOptions};
|
||||
use ruff_workspace::options_base::{OptionEntry, OptionsMetadata};
|
||||
use ruff_workspace::resolver::ConfigurationTransformer;
|
||||
use rustc_hash::FxHashMap;
|
||||
use toml;
|
||||
|
||||
@@ -2,8 +2,10 @@ use clap::builder::{PossibleValue, TypedValueParser, ValueParserFactory};
|
||||
use itertools::Itertools;
|
||||
use std::str::FromStr;
|
||||
|
||||
use ruff_options_metadata::{OptionField, OptionSet, OptionsMetadata, Visit};
|
||||
use ruff_workspace::options::Options;
|
||||
use ruff_workspace::{
|
||||
options::Options,
|
||||
options_base::{OptionField, OptionSet, OptionsMetadata, Visit},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
struct CollectOptionsVisitor {
|
||||
|
||||
@@ -2,8 +2,8 @@ use anyhow::{anyhow, Result};
|
||||
|
||||
use crate::args::HelpFormat;
|
||||
|
||||
use ruff_options_metadata::OptionsMetadata;
|
||||
use ruff_workspace::options::Options;
|
||||
use ruff_workspace::options_base::OptionsMetadata;
|
||||
|
||||
#[expect(clippy::print_stdout)]
|
||||
pub(crate) fn config(key: Option<&str>, format: HelpFormat) -> Result<()> {
|
||||
|
||||
@@ -15,7 +15,6 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "openbsd"),
|
||||
not(target_os = "aix"),
|
||||
not(target_os = "android"),
|
||||
any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
|
||||
@@ -301,62 +301,6 @@ fn benchmark_many_string_assignments(criterion: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn benchmark_many_tuple_assignments(criterion: &mut Criterion) {
|
||||
setup_rayon();
|
||||
|
||||
criterion.bench_function("ty_micro[many_tuple_assignments]", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
// This is a micro benchmark, but it is effectively identical to a code sample
|
||||
// observed in https://github.com/astral-sh/ty/issues/362
|
||||
setup_micro_case(
|
||||
r#"
|
||||
def flag() -> bool:
|
||||
return True
|
||||
|
||||
t = ()
|
||||
if flag():
|
||||
t += (1,)
|
||||
if flag():
|
||||
t += (2,)
|
||||
if flag():
|
||||
t += (3,)
|
||||
if flag():
|
||||
t += (4,)
|
||||
if flag():
|
||||
t += (5,)
|
||||
if flag():
|
||||
t += (6,)
|
||||
if flag():
|
||||
t += (7,)
|
||||
if flag():
|
||||
t += (8,)
|
||||
if flag():
|
||||
t += (9,)
|
||||
if flag():
|
||||
t += (10,)
|
||||
if flag():
|
||||
t += (11,)
|
||||
|
||||
# Perform some kind of operation on the union type
|
||||
print(1 in t)
|
||||
"#,
|
||||
)
|
||||
},
|
||||
|case| {
|
||||
let Case { db, .. } = case;
|
||||
let result = db.check().unwrap();
|
||||
assert_eq!(result.len(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(check_file, benchmark_cold, benchmark_incremental);
|
||||
criterion_group!(
|
||||
micro,
|
||||
benchmark_many_string_assignments,
|
||||
benchmark_many_tuple_assignments
|
||||
);
|
||||
criterion_group!(micro, benchmark_many_string_assignments);
|
||||
criterion_main!(check_file, micro);
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::{fmt::Formatter, sync::Arc};
|
||||
|
||||
use render::{FileResolver, Input};
|
||||
use ruff_source_file::{SourceCode, SourceFile};
|
||||
use thiserror::Error;
|
||||
|
||||
use ruff_annotate_snippets::Level as AnnotateLevel;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
@@ -612,19 +613,40 @@ impl DiagnosticId {
|
||||
code.split_once(':').map(|(_, rest)| rest)
|
||||
}
|
||||
|
||||
/// Returns a concise description of this diagnostic ID.
|
||||
/// Returns `true` if this `DiagnosticId` matches the given name.
|
||||
///
|
||||
/// Note that this doesn't include the lint's category. It
|
||||
/// only includes the lint's name.
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use ruff_db::diagnostic::DiagnosticId;
|
||||
///
|
||||
/// assert!(DiagnosticId::Io.matches("io"));
|
||||
/// assert!(DiagnosticId::lint("test").matches("lint:test"));
|
||||
/// assert!(!DiagnosticId::lint("test").matches("test"));
|
||||
/// ```
|
||||
pub fn matches(&self, expected_name: &str) -> bool {
|
||||
match self.as_str() {
|
||||
Ok(id) => id == expected_name,
|
||||
Err(DiagnosticAsStrError::Category { category, name }) => expected_name
|
||||
.strip_prefix(category)
|
||||
.and_then(|prefix| prefix.strip_prefix(":"))
|
||||
.is_some_and(|rest| rest == name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> Result<&str, DiagnosticAsStrError> {
|
||||
Ok(match self {
|
||||
DiagnosticId::Panic => "panic",
|
||||
DiagnosticId::Io => "io",
|
||||
DiagnosticId::InvalidSyntax => "invalid-syntax",
|
||||
DiagnosticId::Lint(name) => name.as_str(),
|
||||
DiagnosticId::Lint(name) => {
|
||||
return Err(DiagnosticAsStrError::Category {
|
||||
category: "lint",
|
||||
name: name.as_str(),
|
||||
})
|
||||
}
|
||||
DiagnosticId::RevealedType => "revealed-type",
|
||||
DiagnosticId::UnknownRule => "unknown-rule",
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_invalid_syntax(&self) -> bool {
|
||||
@@ -632,9 +654,26 @@ impl DiagnosticId {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Error)]
|
||||
pub enum DiagnosticAsStrError {
|
||||
/// The id can't be converted to a string because it belongs to a sub-category.
|
||||
#[error("id from a sub-category: {category}:{name}")]
|
||||
Category {
|
||||
/// The id's category.
|
||||
category: &'static str,
|
||||
/// The diagnostic id in this category.
|
||||
name: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DiagnosticId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
match self.as_str() {
|
||||
Ok(name) => f.write_str(name),
|
||||
Err(DiagnosticAsStrError::Category { category, name }) => {
|
||||
write!(f, "{category}:{name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -140,6 +140,7 @@ impl std::fmt::Display for DisplayDiagnostic<'_> {
|
||||
/// both.)
|
||||
#[derive(Debug)]
|
||||
struct Resolved<'a> {
|
||||
id: String,
|
||||
diagnostics: Vec<ResolvedDiagnostic<'a>>,
|
||||
}
|
||||
|
||||
@@ -151,12 +152,14 @@ impl<'a> Resolved<'a> {
|
||||
for sub in &diag.inner.subs {
|
||||
diagnostics.push(ResolvedDiagnostic::from_sub_diagnostic(resolver, sub));
|
||||
}
|
||||
Resolved { diagnostics }
|
||||
let id = diag.inner.id.to_string();
|
||||
Resolved { id, diagnostics }
|
||||
}
|
||||
|
||||
/// Creates a value that is amenable to rendering directly.
|
||||
fn to_renderable(&self, context: usize) -> Renderable<'_> {
|
||||
Renderable {
|
||||
id: &self.id,
|
||||
diagnostics: self
|
||||
.diagnostics
|
||||
.iter()
|
||||
@@ -174,7 +177,6 @@ impl<'a> Resolved<'a> {
|
||||
#[derive(Debug)]
|
||||
struct ResolvedDiagnostic<'a> {
|
||||
severity: Severity,
|
||||
id: Option<String>,
|
||||
message: String,
|
||||
annotations: Vec<ResolvedAnnotation<'a>>,
|
||||
}
|
||||
@@ -195,11 +197,20 @@ impl<'a> ResolvedDiagnostic<'a> {
|
||||
ResolvedAnnotation::new(path, &diagnostic_source, ann)
|
||||
})
|
||||
.collect();
|
||||
let id = Some(diag.inner.id.to_string());
|
||||
let message = diag.inner.message.as_str().to_string();
|
||||
let message = if diag.inner.message.as_str().is_empty() {
|
||||
diag.inner.id.to_string()
|
||||
} else {
|
||||
// TODO: See the comment on `Renderable::id` for
|
||||
// a plausible better idea than smushing the ID
|
||||
// into the diagnostic message.
|
||||
format!(
|
||||
"{id}: {message}",
|
||||
id = diag.inner.id,
|
||||
message = diag.inner.message.as_str(),
|
||||
)
|
||||
};
|
||||
ResolvedDiagnostic {
|
||||
severity: diag.inner.severity,
|
||||
id,
|
||||
message,
|
||||
annotations,
|
||||
}
|
||||
@@ -222,7 +233,6 @@ impl<'a> ResolvedDiagnostic<'a> {
|
||||
.collect();
|
||||
ResolvedDiagnostic {
|
||||
severity: diag.inner.severity,
|
||||
id: None,
|
||||
message: diag.inner.message.as_str().to_string(),
|
||||
annotations,
|
||||
}
|
||||
@@ -289,7 +299,6 @@ impl<'a> ResolvedDiagnostic<'a> {
|
||||
.sort_by(|snips1, snips2| snips1.has_primary.cmp(&snips2.has_primary).reverse());
|
||||
RenderableDiagnostic {
|
||||
severity: self.severity,
|
||||
id: self.id.as_deref(),
|
||||
message: &self.message,
|
||||
snippets_by_input,
|
||||
}
|
||||
@@ -369,6 +378,20 @@ impl<'a> ResolvedAnnotation<'a> {
|
||||
/// renderable value. This is usually the lifetime of `Resolved`.
|
||||
#[derive(Debug)]
|
||||
struct Renderable<'r> {
|
||||
// TODO: This is currently unused in the rendering logic below. I'm not
|
||||
// 100% sure yet where I want to put it, but I like what `rustc` does:
|
||||
//
|
||||
// error[E0599]: no method named `sub_builder` <..snip..>
|
||||
//
|
||||
// I believe in order to do this, we'll need to patch it in to
|
||||
// `ruff_annotate_snippets` though. We leave it here for now with that plan
|
||||
// in mind.
|
||||
//
|
||||
// (At time of writing, 2025-03-13, we currently render the diagnostic
|
||||
// ID into the main message of the parent diagnostic. We don't use this
|
||||
// specific field to do that though.)
|
||||
#[expect(dead_code)]
|
||||
id: &'r str,
|
||||
diagnostics: Vec<RenderableDiagnostic<'r>>,
|
||||
}
|
||||
|
||||
@@ -377,12 +400,6 @@ struct Renderable<'r> {
|
||||
struct RenderableDiagnostic<'r> {
|
||||
/// The severity of the diagnostic.
|
||||
severity: Severity,
|
||||
/// The ID of the diagnostic. The ID can usually be used on the CLI or in a
|
||||
/// config file to change the severity of a lint.
|
||||
///
|
||||
/// An ID is always present for top-level diagnostics and always absent for
|
||||
/// sub-diagnostics.
|
||||
id: Option<&'r str>,
|
||||
/// The message emitted with the diagnostic, before any snippets are
|
||||
/// rendered.
|
||||
message: &'r str,
|
||||
@@ -403,11 +420,7 @@ impl RenderableDiagnostic<'_> {
|
||||
.iter()
|
||||
.map(|snippet| snippet.to_annotate(path))
|
||||
});
|
||||
let mut message = level.title(self.message);
|
||||
if let Some(id) = self.id {
|
||||
message = message.id(id);
|
||||
}
|
||||
message.snippets(snippets)
|
||||
level.title(self.message).snippets(snippets)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -786,7 +799,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
3 | canary
|
||||
@@ -810,7 +823,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
warning[test-diagnostic]: main diagnostic message
|
||||
warning: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
3 | canary
|
||||
@@ -830,7 +843,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
info[test-diagnostic]: main diagnostic message
|
||||
info: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
3 | canary
|
||||
@@ -857,7 +870,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:1:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -876,7 +889,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:1:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -897,7 +910,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> non-ascii:5:1
|
||||
|
|
||||
3 | ΔΔΔΔΔΔΔΔΔΔΔΔ
|
||||
@@ -916,7 +929,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> non-ascii:2:2
|
||||
|
|
||||
1 | ☃☃☃☃☃☃☃☃☃☃☃☃
|
||||
@@ -940,7 +953,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
4 | dog
|
||||
@@ -957,7 +970,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
5 | elephant
|
||||
@@ -972,7 +985,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:1:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -989,7 +1002,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:11:1
|
||||
|
|
||||
9 | inchworm
|
||||
@@ -1006,7 +1019,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1039,7 +1052,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:1:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1083,7 +1096,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:1:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1108,7 +1121,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:1:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1136,7 +1149,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:1:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1164,7 +1177,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:1:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1189,7 +1202,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:1:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1220,7 +1233,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:1:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1258,7 +1271,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> spacey-animals:8:1
|
||||
|
|
||||
7 | dog
|
||||
@@ -1275,7 +1288,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> spacey-animals:12:1
|
||||
|
|
||||
11 | gorilla
|
||||
@@ -1293,7 +1306,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> spacey-animals:13:1
|
||||
|
|
||||
11 | gorilla
|
||||
@@ -1333,7 +1346,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> spacey-animals:3:1
|
||||
|
|
||||
3 | beetle
|
||||
@@ -1362,7 +1375,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:3:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1399,7 +1412,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:3:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1436,7 +1449,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:3:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1464,7 +1477,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:3:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1500,7 +1513,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:3:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1539,7 +1552,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:3:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1587,7 +1600,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:3:1
|
||||
|
|
||||
1 | aardvark
|
||||
@@ -1623,7 +1636,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
3 | canary
|
||||
@@ -1646,7 +1659,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
3 | canary
|
||||
@@ -1666,7 +1679,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
3 | canary
|
||||
@@ -1686,7 +1699,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:4
|
||||
|
|
||||
3 | canary
|
||||
@@ -1708,7 +1721,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:4
|
||||
|
|
||||
3 | canary
|
||||
@@ -1740,7 +1753,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:4:1
|
||||
|
|
||||
2 | beetle
|
||||
@@ -1769,7 +1782,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:4:1
|
||||
|
|
||||
2 | beetle
|
||||
@@ -1800,7 +1813,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
3 | canary
|
||||
@@ -1835,7 +1848,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
3 | canary
|
||||
@@ -1863,7 +1876,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
3 | canary
|
||||
@@ -1895,7 +1908,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:3
|
||||
|
|
||||
3 | canary
|
||||
@@ -1917,7 +1930,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:3
|
||||
|
|
||||
3 | canary
|
||||
@@ -1950,7 +1963,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:8:1
|
||||
|
|
||||
6 | finch
|
||||
@@ -1990,7 +2003,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:5:1
|
||||
|
|
||||
5 | elephant
|
||||
@@ -2034,7 +2047,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> fruits:1:1
|
||||
|
|
||||
1 | apple
|
||||
@@ -2069,7 +2082,7 @@ watermelon
|
||||
insta::assert_snapshot!(
|
||||
env.render(&diag),
|
||||
@r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
error: lint:test-diagnostic: main diagnostic message
|
||||
--> animals:11:1
|
||||
|
|
||||
11 | kangaroo
|
||||
|
||||
@@ -277,7 +277,7 @@ impl std::panic::RefUnwindSafe for Files {}
|
||||
#[salsa::input]
|
||||
pub struct File {
|
||||
/// The path of the file (immutable).
|
||||
#[returns(ref)]
|
||||
#[return_ref]
|
||||
pub path: FilePath,
|
||||
|
||||
/// The unix permissions of the file. Only supported on unix systems. Always `None` on Windows
|
||||
|
||||
@@ -19,8 +19,8 @@ use crate::Db;
|
||||
#[salsa::input(debug)]
|
||||
pub struct FileRoot {
|
||||
/// The path of a root is guaranteed to never change.
|
||||
#[returns(deref)]
|
||||
pub path: SystemPathBuf,
|
||||
#[return_ref]
|
||||
path_buf: SystemPathBuf,
|
||||
|
||||
/// The kind of the root at the time of its creation.
|
||||
kind_at_time_of_creation: FileRootKind,
|
||||
@@ -32,6 +32,10 @@ pub struct FileRoot {
|
||||
}
|
||||
|
||||
impl FileRoot {
|
||||
pub fn path(self, db: &dyn Db) -> &SystemPath {
|
||||
self.path_buf(db)
|
||||
}
|
||||
|
||||
pub fn durability(self, db: &dyn Db) -> salsa::Durability {
|
||||
self.kind_at_time_of_creation(db).durability()
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::Db;
|
||||
/// reflected in the changed AST offsets.
|
||||
/// The other reason is that Ruff's AST doesn't implement `Eq` which Sala requires
|
||||
/// for determining if a query result is unchanged.
|
||||
#[salsa::tracked(returns(ref), no_eq)]
|
||||
#[salsa::tracked(return_ref, no_eq)]
|
||||
pub fn parsed_module(db: &dyn Db, file: File) -> ParsedModule {
|
||||
let _span = tracing::trace_span!("parsed_module", ?file).entered();
|
||||
|
||||
|
||||
@@ -11,14 +11,12 @@ repository = { workspace = true }
|
||||
license = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
ty = { workspace = true }
|
||||
ty_project = { workspace = true, features = ["schemars"] }
|
||||
ruff = { workspace = true }
|
||||
ruff_diagnostics = { workspace = true }
|
||||
ruff_formatter = { workspace = true }
|
||||
ruff_linter = { workspace = true, features = ["schemars"] }
|
||||
ruff_notebook = { workspace = true }
|
||||
ruff_options_metadata = { workspace = true }
|
||||
ruff_python_ast = { workspace = true }
|
||||
ruff_python_codegen = { workspace = true }
|
||||
ruff_python_formatter = { workspace = true }
|
||||
@@ -33,7 +31,6 @@ imara-diff = { workspace = true }
|
||||
indicatif = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
libcst = { workspace = true }
|
||||
markdown = { version = "1.0.0" }
|
||||
pretty_assertions = { workspace = true }
|
||||
rayon = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{
|
||||
generate_cli_help, generate_docs, generate_json_schema, generate_ty_cli_reference,
|
||||
generate_ty_options, generate_ty_rules, generate_ty_schema,
|
||||
generate_cli_help, generate_docs, generate_json_schema, generate_ty_rules, generate_ty_schema,
|
||||
};
|
||||
|
||||
pub(crate) const REGENERATE_ALL_COMMAND: &str = "cargo dev generate-all";
|
||||
@@ -41,8 +40,6 @@ pub(crate) fn main(args: &Args) -> Result<()> {
|
||||
generate_docs::main(&generate_docs::Args {
|
||||
dry_run: args.mode.is_dry_run(),
|
||||
})?;
|
||||
generate_ty_options::main(&generate_ty_options::Args { mode: args.mode })?;
|
||||
generate_ty_rules::main(&generate_ty_rules::Args { mode: args.mode })?;
|
||||
generate_ty_cli_reference::main(&generate_ty_cli_reference::Args { mode: args.mode })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Generate CLI help.
|
||||
#![allow(clippy::print_stdout)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, str};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Generate Markdown documentation for applicable rules.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Write as _;
|
||||
@@ -12,8 +13,8 @@ use strum::IntoEnumIterator;
|
||||
|
||||
use ruff_diagnostics::FixAvailability;
|
||||
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
|
||||
use ruff_options_metadata::{OptionEntry, OptionsMetadata};
|
||||
use ruff_workspace::options::Options;
|
||||
use ruff_workspace::options_base::{OptionEntry, OptionsMetadata};
|
||||
|
||||
use crate::ROOT_DIR;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
use itertools::Itertools;
|
||||
use std::fmt::Write;
|
||||
|
||||
use ruff_options_metadata::{OptionField, OptionSet, OptionsMetadata, Visit};
|
||||
use ruff_python_trivia::textwrap;
|
||||
use ruff_workspace::options::Options;
|
||||
use ruff_workspace::options_base::{OptionField, OptionSet, OptionsMetadata, Visit};
|
||||
|
||||
pub(crate) fn generate() -> String {
|
||||
let mut output = String::new();
|
||||
|
||||
@@ -11,8 +11,8 @@ use strum::IntoEnumIterator;
|
||||
use ruff_diagnostics::FixAvailability;
|
||||
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
|
||||
use ruff_linter::upstream_categories::UpstreamCategoryAndPrefix;
|
||||
use ruff_options_metadata::OptionsMetadata;
|
||||
use ruff_workspace::options::Options;
|
||||
use ruff_workspace::options_base::OptionsMetadata;
|
||||
|
||||
const FIX_SYMBOL: &str = "🛠️";
|
||||
const PREVIEW_SYMBOL: &str = "🧪";
|
||||
|
||||
@@ -1,334 +0,0 @@
|
||||
//! Generate a Markdown-compatible reference for the ty command-line interface.
|
||||
use std::cmp::max;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use clap::{Command, CommandFactory};
|
||||
use itertools::Itertools;
|
||||
use pretty_assertions::StrComparison;
|
||||
|
||||
use crate::generate_all::{Mode, REGENERATE_ALL_COMMAND};
|
||||
use crate::ROOT_DIR;
|
||||
|
||||
use ty::Cli;
|
||||
|
||||
const SHOW_HIDDEN_COMMANDS: &[&str] = &["generate-shell-completion"];
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub(crate) struct Args {
|
||||
#[arg(long, default_value_t, value_enum)]
|
||||
pub(crate) mode: Mode,
|
||||
}
|
||||
|
||||
pub(crate) fn main(args: &Args) -> Result<()> {
|
||||
let reference_string = generate();
|
||||
let filename = "crates/ty/docs/cli.md";
|
||||
let reference_path = PathBuf::from(ROOT_DIR).join(filename);
|
||||
|
||||
match args.mode {
|
||||
Mode::DryRun => {
|
||||
println!("{reference_string}");
|
||||
}
|
||||
Mode::Check => {
|
||||
match std::fs::read_to_string(reference_path) {
|
||||
Ok(current) => {
|
||||
if current == reference_string {
|
||||
println!("Up-to-date: {filename}");
|
||||
} else {
|
||||
let comparison = StrComparison::new(¤t, &reference_string);
|
||||
bail!("{filename} changed, please run `{REGENERATE_ALL_COMMAND}`:\n{comparison}");
|
||||
}
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
bail!("{filename} not found, please run `{REGENERATE_ALL_COMMAND}`");
|
||||
}
|
||||
Err(err) => {
|
||||
bail!("{filename} changed, please run `{REGENERATE_ALL_COMMAND}`:\n{err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Mode::Write => match std::fs::read_to_string(&reference_path) {
|
||||
Ok(current) => {
|
||||
if current == reference_string {
|
||||
println!("Up-to-date: {filename}");
|
||||
} else {
|
||||
println!("Updating: {filename}");
|
||||
std::fs::write(reference_path, reference_string.as_bytes())?;
|
||||
}
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
println!("Updating: {filename}");
|
||||
std::fs::write(reference_path, reference_string.as_bytes())?;
|
||||
}
|
||||
Err(err) => {
|
||||
bail!("{filename} changed, please run `cargo dev generate-cli-reference`:\n{err}");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate() -> String {
|
||||
let mut output = String::new();
|
||||
|
||||
let mut ty = Cli::command();
|
||||
|
||||
// It is very important to build the command before beginning inspection or subcommands
|
||||
// will be missing all of the propagated options.
|
||||
ty.build();
|
||||
|
||||
let mut parents = Vec::new();
|
||||
|
||||
output.push_str("# CLI Reference\n\n");
|
||||
generate_command(&mut output, &ty, &mut parents);
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
#[allow(clippy::format_push_string)]
|
||||
fn generate_command<'a>(output: &mut String, command: &'a Command, parents: &mut Vec<&'a Command>) {
|
||||
if command.is_hide_set() && !SHOW_HIDDEN_COMMANDS.contains(&command.get_name()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate the command header.
|
||||
let name = if parents.is_empty() {
|
||||
command.get_name().to_string()
|
||||
} else {
|
||||
format!(
|
||||
"{} {}",
|
||||
parents.iter().map(|cmd| cmd.get_name()).join(" "),
|
||||
command.get_name()
|
||||
)
|
||||
};
|
||||
|
||||
// Display the top-level `ty` command at the same level as its children
|
||||
let level = max(2, parents.len() + 1);
|
||||
output.push_str(&format!("{} {name}\n\n", "#".repeat(level)));
|
||||
|
||||
// Display the command description.
|
||||
if let Some(about) = command.get_long_about().or_else(|| command.get_about()) {
|
||||
output.push_str(&about.to_string());
|
||||
output.push_str("\n\n");
|
||||
}
|
||||
|
||||
// Display the usage
|
||||
{
|
||||
// This appears to be the simplest way to get rendered usage from Clap,
|
||||
// it is complicated to render it manually. It's annoying that it
|
||||
// requires a mutable reference but it doesn't really matter.
|
||||
let mut command = command.clone();
|
||||
output.push_str("<h3 class=\"cli-reference\">Usage</h3>\n\n");
|
||||
output.push_str(&format!(
|
||||
"```\n{}\n```",
|
||||
command
|
||||
.render_usage()
|
||||
.to_string()
|
||||
.trim_start_matches("Usage: "),
|
||||
));
|
||||
output.push_str("\n\n");
|
||||
}
|
||||
|
||||
if command.get_name() == "help" {
|
||||
return;
|
||||
}
|
||||
|
||||
// Display a list of child commands
|
||||
let mut subcommands = command.get_subcommands().peekable();
|
||||
let has_subcommands = subcommands.peek().is_some();
|
||||
if has_subcommands {
|
||||
output.push_str("<h3 class=\"cli-reference\">Commands</h3>\n\n");
|
||||
output.push_str("<dl class=\"cli-reference\">");
|
||||
|
||||
for subcommand in subcommands {
|
||||
if subcommand.is_hide_set() {
|
||||
continue;
|
||||
}
|
||||
let subcommand_name = format!("{name} {}", subcommand.get_name());
|
||||
output.push_str(&format!(
|
||||
"<dt><a href=\"#{}\"><code>{subcommand_name}</code></a></dt>",
|
||||
subcommand_name.replace(' ', "-")
|
||||
));
|
||||
if let Some(about) = subcommand.get_about() {
|
||||
output.push_str(&format!(
|
||||
"<dd>{}</dd>\n",
|
||||
markdown::to_html(&about.to_string())
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
output.push_str("</dl>\n\n");
|
||||
}
|
||||
|
||||
// Do not display options for commands with children
|
||||
if !has_subcommands {
|
||||
let name_key = name.replace(' ', "-");
|
||||
|
||||
// Display positional arguments
|
||||
let mut arguments = command
|
||||
.get_positionals()
|
||||
.filter(|arg| !arg.is_hide_set())
|
||||
.peekable();
|
||||
|
||||
if arguments.peek().is_some() {
|
||||
output.push_str("<h3 class=\"cli-reference\">Arguments</h3>\n\n");
|
||||
output.push_str("<dl class=\"cli-reference\">");
|
||||
|
||||
for arg in arguments {
|
||||
let id = format!("{name_key}--{}", arg.get_id());
|
||||
output.push_str(&format!("<dt id=\"{id}\">"));
|
||||
output.push_str(&format!(
|
||||
"<a href=\"#{id}\"><code>{}</code></a>",
|
||||
arg.get_id().to_string().to_uppercase(),
|
||||
));
|
||||
output.push_str("</dt>");
|
||||
if let Some(help) = arg.get_long_help().or_else(|| arg.get_help()) {
|
||||
output.push_str("<dd>");
|
||||
output.push_str(&format!("{}\n", markdown::to_html(&help.to_string())));
|
||||
output.push_str("</dd>");
|
||||
}
|
||||
}
|
||||
|
||||
output.push_str("</dl>\n\n");
|
||||
}
|
||||
|
||||
// Display options and flags
|
||||
let mut options = command
|
||||
.get_arguments()
|
||||
.filter(|arg| !arg.is_positional())
|
||||
.filter(|arg| !arg.is_hide_set())
|
||||
.sorted_by_key(|arg| arg.get_id())
|
||||
.peekable();
|
||||
|
||||
if options.peek().is_some() {
|
||||
output.push_str("<h3 class=\"cli-reference\">Options</h3>\n\n");
|
||||
output.push_str("<dl class=\"cli-reference\">");
|
||||
for opt in options {
|
||||
let Some(long) = opt.get_long() else { continue };
|
||||
let id = format!("{name_key}--{long}");
|
||||
|
||||
output.push_str(&format!("<dt id=\"{id}\">"));
|
||||
output.push_str(&format!("<a href=\"#{id}\"><code>--{long}</code></a>"));
|
||||
for long_alias in opt.get_all_aliases().into_iter().flatten() {
|
||||
output.push_str(&format!(", <code>--{long_alias}</code>"));
|
||||
}
|
||||
if let Some(short) = opt.get_short() {
|
||||
output.push_str(&format!(", <code>-{short}</code>"));
|
||||
}
|
||||
for short_alias in opt.get_all_short_aliases().into_iter().flatten() {
|
||||
output.push_str(&format!(", <code>-{short_alias}</code>"));
|
||||
}
|
||||
|
||||
// Re-implements private `Arg::is_takes_value_set` used in `Command::get_opts`
|
||||
if opt
|
||||
.get_num_args()
|
||||
.unwrap_or_else(|| 1.into())
|
||||
.takes_values()
|
||||
{
|
||||
if let Some(values) = opt.get_value_names() {
|
||||
for value in values {
|
||||
output.push_str(&format!(
|
||||
" <i>{}</i>",
|
||||
value.to_lowercase().replace('_', "-")
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
output.push_str("</dt>");
|
||||
if let Some(help) = opt.get_long_help().or_else(|| opt.get_help()) {
|
||||
output.push_str("<dd>");
|
||||
output.push_str(&format!("{}\n", markdown::to_html(&help.to_string())));
|
||||
emit_env_option(opt, output);
|
||||
emit_default_option(opt, output);
|
||||
emit_possible_options(opt, output);
|
||||
output.push_str("</dd>");
|
||||
}
|
||||
}
|
||||
|
||||
output.push_str("</dl>");
|
||||
}
|
||||
|
||||
output.push_str("\n\n");
|
||||
}
|
||||
|
||||
parents.push(command);
|
||||
|
||||
// Recurse to all of the subcommands.
|
||||
for subcommand in command.get_subcommands() {
|
||||
generate_command(output, subcommand, parents);
|
||||
}
|
||||
|
||||
parents.pop();
|
||||
}
|
||||
|
||||
fn emit_env_option(opt: &clap::Arg, output: &mut String) {
|
||||
if opt.is_hide_env_set() {
|
||||
return;
|
||||
}
|
||||
if let Some(env) = opt.get_env() {
|
||||
output.push_str(&markdown::to_html(&format!(
|
||||
"May also be set with the `{}` environment variable.",
|
||||
env.to_string_lossy()
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_default_option(opt: &clap::Arg, output: &mut String) {
|
||||
if opt.is_hide_default_value_set() || !opt.get_num_args().expect("built").takes_values() {
|
||||
return;
|
||||
}
|
||||
|
||||
let values = opt.get_default_values();
|
||||
if !values.is_empty() {
|
||||
let value = format!(
|
||||
"\n[default: {}]",
|
||||
opt.get_default_values()
|
||||
.iter()
|
||||
.map(|s| s.to_string_lossy())
|
||||
.join(",")
|
||||
);
|
||||
output.push_str(&markdown::to_html(&value));
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_possible_options(opt: &clap::Arg, output: &mut String) {
|
||||
if opt.is_hide_possible_values_set() {
|
||||
return;
|
||||
}
|
||||
|
||||
let values = opt.get_possible_values();
|
||||
if !values.is_empty() {
|
||||
let value = format!(
|
||||
"\nPossible values:\n{}",
|
||||
values
|
||||
.into_iter()
|
||||
.filter(|value| !value.is_hide_set())
|
||||
.map(|value| {
|
||||
let name = value.get_name();
|
||||
value.get_help().map_or_else(
|
||||
|| format!(" - `{name}`"),
|
||||
|help| format!(" - `{name}`: {help}"),
|
||||
)
|
||||
})
|
||||
.collect_vec()
|
||||
.join("\n"),
|
||||
);
|
||||
output.push_str(&markdown::to_html(&value));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::generate_all::Mode;
|
||||
|
||||
use super::{main, Args};
|
||||
|
||||
#[test]
|
||||
fn ty_cli_reference_is_up_to_date() -> Result<()> {
|
||||
main(&Args { mode: Mode::Check })
|
||||
}
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
//! Generate a Markdown-compatible listing of configuration options for `pyproject.toml`.
|
||||
|
||||
use anyhow::bail;
|
||||
use itertools::Itertools;
|
||||
use pretty_assertions::StrComparison;
|
||||
use std::{fmt::Write, path::PathBuf};
|
||||
|
||||
use ruff_options_metadata::{OptionField, OptionSet, OptionsMetadata, Visit};
|
||||
use ty_project::metadata::Options;
|
||||
|
||||
use crate::{
|
||||
generate_all::{Mode, REGENERATE_ALL_COMMAND},
|
||||
ROOT_DIR,
|
||||
};
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub(crate) struct Args {
|
||||
/// Write the generated table to stdout (rather than to `crates/ty/docs/configuration.md`).
|
||||
#[arg(long, default_value_t, value_enum)]
|
||||
pub(crate) mode: Mode,
|
||||
}
|
||||
|
||||
pub(crate) fn main(args: &Args) -> anyhow::Result<()> {
|
||||
let mut output = String::new();
|
||||
let file_name = "crates/ty/docs/configuration.md";
|
||||
let markdown_path = PathBuf::from(ROOT_DIR).join(file_name);
|
||||
|
||||
generate_set(
|
||||
&mut output,
|
||||
Set::Toplevel(Options::metadata()),
|
||||
&mut Vec::new(),
|
||||
);
|
||||
|
||||
match args.mode {
|
||||
Mode::DryRun => {
|
||||
println!("{output}");
|
||||
}
|
||||
Mode::Check => {
|
||||
let current = std::fs::read_to_string(&markdown_path)?;
|
||||
if output == current {
|
||||
println!("Up-to-date: {file_name}",);
|
||||
} else {
|
||||
let comparison = StrComparison::new(¤t, &output);
|
||||
bail!("{file_name} changed, please run `{REGENERATE_ALL_COMMAND}`:\n{comparison}",);
|
||||
}
|
||||
}
|
||||
Mode::Write => {
|
||||
let current = std::fs::read_to_string(&markdown_path)?;
|
||||
if current == output {
|
||||
println!("Up-to-date: {file_name}",);
|
||||
} else {
|
||||
println!("Updating: {file_name}",);
|
||||
std::fs::write(markdown_path, output.as_bytes())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_set(output: &mut String, set: Set, parents: &mut Vec<Set>) {
|
||||
match &set {
|
||||
Set::Toplevel(_) => {
|
||||
output.push_str("# Configuration\n");
|
||||
}
|
||||
Set::Named { name, .. } => {
|
||||
let title = parents
|
||||
.iter()
|
||||
.filter_map(|set| set.name())
|
||||
.chain(std::iter::once(name.as_str()))
|
||||
.join(".");
|
||||
writeln!(output, "## `{title}`\n",).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(documentation) = set.metadata().documentation() {
|
||||
output.push_str(documentation);
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
let mut visitor = CollectOptionsVisitor::default();
|
||||
set.metadata().record(&mut visitor);
|
||||
|
||||
let (mut fields, mut sets) = (visitor.fields, visitor.groups);
|
||||
|
||||
fields.sort_unstable_by(|(name, _), (name2, _)| name.cmp(name2));
|
||||
sets.sort_unstable_by(|(name, _), (name2, _)| name.cmp(name2));
|
||||
|
||||
parents.push(set);
|
||||
|
||||
// Generate the fields.
|
||||
for (name, field) in &fields {
|
||||
emit_field(output, name, field, parents.as_slice());
|
||||
output.push_str("---\n\n");
|
||||
}
|
||||
|
||||
// Generate all the sub-sets.
|
||||
for (set_name, sub_set) in &sets {
|
||||
generate_set(
|
||||
output,
|
||||
Set::Named {
|
||||
name: set_name.to_string(),
|
||||
set: *sub_set,
|
||||
},
|
||||
parents,
|
||||
);
|
||||
}
|
||||
|
||||
parents.pop();
|
||||
}
|
||||
|
||||
enum Set {
|
||||
Toplevel(OptionSet),
|
||||
Named { name: String, set: OptionSet },
|
||||
}
|
||||
|
||||
impl Set {
|
||||
fn name(&self) -> Option<&str> {
|
||||
match self {
|
||||
Set::Toplevel(_) => None,
|
||||
Set::Named { name, .. } => Some(name),
|
||||
}
|
||||
}
|
||||
|
||||
fn metadata(&self) -> &OptionSet {
|
||||
match self {
|
||||
Set::Toplevel(set) => set,
|
||||
Set::Named { set, .. } => set,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_field(output: &mut String, name: &str, field: &OptionField, parents: &[Set]) {
|
||||
let header_level = if parents.is_empty() { "###" } else { "####" };
|
||||
|
||||
let _ = writeln!(output, "{header_level} `{name}`");
|
||||
|
||||
output.push('\n');
|
||||
|
||||
if let Some(deprecated) = &field.deprecated {
|
||||
output.push_str("> [!WARN] \"Deprecated\"\n");
|
||||
output.push_str("> This option has been deprecated");
|
||||
|
||||
if let Some(since) = deprecated.since {
|
||||
write!(output, " in {since}").unwrap();
|
||||
}
|
||||
|
||||
output.push('.');
|
||||
|
||||
if let Some(message) = deprecated.message {
|
||||
writeln!(output, " {message}").unwrap();
|
||||
}
|
||||
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
output.push_str(field.doc);
|
||||
output.push_str("\n\n");
|
||||
let _ = writeln!(output, "**Default value**: `{}`", field.default);
|
||||
output.push('\n');
|
||||
let _ = writeln!(output, "**Type**: `{}`", field.value_type);
|
||||
output.push('\n');
|
||||
output.push_str("**Example usage** (`pyproject.toml`):\n\n");
|
||||
output.push_str(&format_example(
|
||||
&format_header(
|
||||
field.scope,
|
||||
field.example,
|
||||
parents,
|
||||
ConfigurationFile::PyprojectToml,
|
||||
),
|
||||
field.example,
|
||||
));
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
fn format_example(header: &str, content: &str) -> String {
|
||||
if header.is_empty() {
|
||||
format!("```toml\n{content}\n```\n",)
|
||||
} else {
|
||||
format!("```toml\n{header}\n{content}\n```\n",)
|
||||
}
|
||||
}
|
||||
|
||||
/// Format the TOML header for the example usage for a given option.
|
||||
///
|
||||
/// For example: `[tool.ruff.format]` or `[tool.ruff.lint.isort]`.
|
||||
fn format_header(
|
||||
scope: Option<&str>,
|
||||
example: &str,
|
||||
parents: &[Set],
|
||||
configuration: ConfigurationFile,
|
||||
) -> String {
|
||||
let tool_parent = match configuration {
|
||||
ConfigurationFile::PyprojectToml => Some("tool.ty"),
|
||||
ConfigurationFile::TyToml => None,
|
||||
};
|
||||
|
||||
let header = tool_parent
|
||||
.into_iter()
|
||||
.chain(parents.iter().filter_map(|parent| parent.name()))
|
||||
.chain(scope)
|
||||
.join(".");
|
||||
|
||||
// Ex) `[[tool.ty.xx]]`
|
||||
if example.starts_with(&format!("[[{header}")) {
|
||||
return String::new();
|
||||
}
|
||||
// Ex) `[tool.ty.rules]`
|
||||
if example.starts_with(&format!("[{header}")) {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
if header.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!("[{header}]")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CollectOptionsVisitor {
|
||||
groups: Vec<(String, OptionSet)>,
|
||||
fields: Vec<(String, OptionField)>,
|
||||
}
|
||||
|
||||
impl Visit for CollectOptionsVisitor {
|
||||
fn record_set(&mut self, name: &str, group: OptionSet) {
|
||||
self.groups.push((name.to_owned(), group));
|
||||
}
|
||||
|
||||
fn record_field(&mut self, name: &str, field: OptionField) {
|
||||
self.fields.push((name.to_owned(), field));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum ConfigurationFile {
|
||||
PyprojectToml,
|
||||
#[expect(dead_code)]
|
||||
TyToml,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::generate_all::Mode;
|
||||
|
||||
use super::{main, Args};
|
||||
|
||||
#[test]
|
||||
fn ty_configuration_markdown_up_to_date() -> Result<()> {
|
||||
main(&Args { mode: Mode::Check })?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Generates the rules table for ty
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write as _;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
//!
|
||||
//! Within the ruff repository you can run it with `cargo dev`.
|
||||
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
use ruff::{args::GlobalConfigArgs, check};
|
||||
@@ -17,8 +15,6 @@ mod generate_docs;
|
||||
mod generate_json_schema;
|
||||
mod generate_options;
|
||||
mod generate_rules_table;
|
||||
mod generate_ty_cli_reference;
|
||||
mod generate_ty_options;
|
||||
mod generate_ty_rules;
|
||||
mod generate_ty_schema;
|
||||
mod print_ast;
|
||||
@@ -52,7 +48,6 @@ enum Command {
|
||||
GenerateTyRules(generate_ty_rules::Args),
|
||||
/// Generate a Markdown-compatible listing of configuration options.
|
||||
GenerateOptions,
|
||||
GenerateTyOptions(generate_ty_options::Args),
|
||||
/// Generate CLI help.
|
||||
GenerateCliHelp(generate_cli_help::Args),
|
||||
/// Generate Markdown docs.
|
||||
@@ -97,7 +92,6 @@ fn main() -> Result<ExitCode> {
|
||||
Command::GenerateRulesTable => println!("{}", generate_rules_table::generate()),
|
||||
Command::GenerateTyRules(args) => generate_ty_rules::main(&args)?,
|
||||
Command::GenerateOptions => println!("{}", generate_options::generate()),
|
||||
Command::GenerateTyOptions(args) => generate_ty_options::main(&args)?,
|
||||
Command::GenerateCliHelp(args) => generate_cli_help::main(&args)?,
|
||||
Command::GenerateDocs(args) => generate_docs::main(&args)?,
|
||||
Command::PrintAST(args) => print_ast::main(&args)?,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Print the AST for a given Python file.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Print the `LibCST` CST for a given Python file.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Print the token stream for a given Python file.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Run round-trip source code generation on a given Python or Jupyter notebook file.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -92,7 +92,7 @@ impl std::fmt::Display for IndentStyle {
|
||||
}
|
||||
}
|
||||
|
||||
/// The visual width of an indentation.
|
||||
/// The visual width of a indentation.
|
||||
///
|
||||
/// Determines the visual width of a tab character (`\t`) and the number of
|
||||
/// spaces per indent when using [`IndentStyle::Space`].
|
||||
@@ -207,7 +207,7 @@ pub trait FormatOptions {
|
||||
/// What's the max width of a line. Defaults to 80.
|
||||
fn line_width(&self) -> LineWidth;
|
||||
|
||||
/// Derives the print options from these format options
|
||||
/// Derives the print options from the these format options
|
||||
fn as_print_options(&self) -> PrinterOptions;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,8 +88,8 @@ impl Db for ModuleDb {
|
||||
!file.path(self).is_vendored_path()
|
||||
}
|
||||
|
||||
fn rule_selection(&self) -> &RuleSelection {
|
||||
&self.rule_selection
|
||||
fn rule_selection(&self) -> Arc<RuleSelection> {
|
||||
self.rule_selection.clone()
|
||||
}
|
||||
|
||||
fn lint_registry(&self) -> &LintRegistry {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.11.9"
|
||||
version = "0.11.8"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -24,6 +24,9 @@ from airflow.contrib.aws_athena_hook import AWSAthenaHook
|
||||
from airflow.datasets import DatasetAliasEvent
|
||||
from airflow.hooks.base_hook import BaseHook
|
||||
from airflow.operators.subdag import SubDagOperator
|
||||
from airflow.providers.mysql.datasets import mysql
|
||||
from airflow.providers.postgres.datasets import postgres
|
||||
from airflow.providers.trino.datasets import trino
|
||||
from airflow.secrets.local_filesystem import LocalFilesystemBackend
|
||||
from airflow.sensors.base_sensor_operator import BaseSensorOperator
|
||||
from airflow.triggers.external_task import TaskStateTrigger
|
||||
@@ -75,6 +78,14 @@ BaseHook()
|
||||
# airflow.operators.subdag.*
|
||||
SubDagOperator()
|
||||
|
||||
# airflow.providers.mysql
|
||||
mysql.sanitize_uri
|
||||
|
||||
# airflow.providers.postgres
|
||||
postgres.sanitize_uri
|
||||
|
||||
# airflow.providers.trino
|
||||
trino.sanitize_uri
|
||||
|
||||
# airflow.secrets
|
||||
# get_connection
|
||||
@@ -144,18 +155,3 @@ should_hide_value_for_key
|
||||
from airflow.operators.python import get_current_context
|
||||
|
||||
get_current_context()
|
||||
|
||||
# airflow.providers.mysql
|
||||
from airflow.providers.mysql.datasets.mysql import sanitize_uri
|
||||
|
||||
sanitize_uri
|
||||
|
||||
# airflow.providers.postgres
|
||||
from airflow.providers.postgres.datasets.postgres import sanitize_uri
|
||||
|
||||
sanitize_uri
|
||||
|
||||
# airflow.providers.trino
|
||||
from airflow.providers.trino.datasets.trino import sanitize_uri
|
||||
|
||||
sanitize_uri
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
try:
|
||||
from airflow.assets.manager import AssetManager
|
||||
from airflow.sdk import Asset
|
||||
except ModuleNotFoundError:
|
||||
from airflow.datasets.manager import DatasetManager as AssetManager
|
||||
from airflow.datasets import Dataset as Asset
|
||||
|
||||
AssetManager()
|
||||
Asset
|
||||
|
||||
try:
|
||||
from airflow.sdk import Asset
|
||||
except ModuleNotFoundError:
|
||||
from airflow import datasets
|
||||
|
||||
Asset = datasets.Dataset
|
||||
|
||||
asset = Asset()
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
try:
|
||||
from airflow.providers.http.operators.http import HttpOperator
|
||||
except ModuleNotFoundError:
|
||||
from airflow.operators.http_operator import SimpleHttpOperator as HttpOperator
|
||||
|
||||
HttpOperator()
|
||||
@@ -13,10 +13,6 @@ from airflow.decorators import dag, setup, task, task_group, teardown
|
||||
from airflow.io.path import ObjectStoragePath
|
||||
from airflow.io.storage import attach
|
||||
from airflow.models import DAG as DAGFromModel
|
||||
from airflow.models import (
|
||||
Connection,
|
||||
Variable,
|
||||
)
|
||||
from airflow.models.baseoperator import chain, chain_linear, cross_downstream
|
||||
from airflow.models.baseoperatorlink import BaseOperatorLink
|
||||
from airflow.models.dag import DAG as DAGFromDag
|
||||
@@ -46,9 +42,7 @@ ObjectStoragePath()
|
||||
attach()
|
||||
|
||||
# airflow.models
|
||||
Connection()
|
||||
DAGFromModel()
|
||||
Variable()
|
||||
|
||||
# airflow.models.baseoperator
|
||||
chain()
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
try:
|
||||
from airflow.sdk import Asset
|
||||
except ModuleNotFoundError:
|
||||
from airflow.datasets import Dataset as Asset
|
||||
|
||||
Asset()
|
||||
@@ -1,8 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
try:
|
||||
from airflow.providers.standard.triggers.file import FileTrigger
|
||||
except ModuleNotFoundError:
|
||||
from airflow.triggers.file import FileTrigger
|
||||
|
||||
FileTrigger()
|
||||
@@ -166,6 +166,3 @@ query60 = f"""
|
||||
foo
|
||||
FROM ({user_input}) raw
|
||||
"""
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/17967
|
||||
query61 = f"SELECT * FROM table" # skip expressionless f-strings
|
||||
|
||||
@@ -25,11 +25,3 @@ warnings.warn(
|
||||
# some comments here
|
||||
source = None # no trailing comma
|
||||
)
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18011
|
||||
warnings.warn("test", skip_file_prefixes=(os.path.dirname(__file__),))
|
||||
# trigger diagnostic if `skip_file_prefixes` is present and set to the default value
|
||||
warnings.warn("test", skip_file_prefixes=())
|
||||
|
||||
_my_prefixes = ("this","that")
|
||||
warnings.warn("test", skip_file_prefixes = _my_prefixes)
|
||||
|
||||
@@ -28,14 +28,3 @@ abc(a=1, **{'a': c}, **{'b': c}) # PIE804
|
||||
# Some values need to be parenthesized.
|
||||
abc(foo=1, **{'bar': (bar := 1)}) # PIE804
|
||||
abc(foo=1, **{'bar': (yield 1)}) # PIE804
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18036
|
||||
# The autofix for this is unsafe due to the comments inside the dictionary.
|
||||
foo(
|
||||
**{
|
||||
# Comment 1
|
||||
"x": 1.0,
|
||||
# Comment 2
|
||||
"y": 2.0,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -12,38 +12,3 @@ def test_xxx(_fixture): # Error arg
|
||||
|
||||
def test_xxx(*, _fixture): # Error kwonly
|
||||
pass
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/17599
|
||||
|
||||
@pytest.mark.parametrize("_foo", [1, 2, 3])
|
||||
def test_thingy(_foo): # Ok defined in parametrize
|
||||
pass
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"_test_input,_expected",
|
||||
[("3+5", 8), ("2+4", 6), pytest.param("6*9", 42, marks=pytest.mark.xfail)],
|
||||
)
|
||||
def test_eval(_test_input, _expected): # OK defined in parametrize
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.parametrize("_foo", [1, 2, 3])
|
||||
def test_thingy2(_foo, _bar): # Error _bar is not defined in parametrize
|
||||
pass
|
||||
|
||||
@pytest.mark.parametrize(["_foo", "_bar"], [1, 2, 3])
|
||||
def test_thingy3(_foo, _bar): # OK defined in parametrize
|
||||
pass
|
||||
|
||||
@pytest.mark.parametrize(("_foo"), [1, 2, 3])
|
||||
def test_thingy4(_foo, _bar): # Error _bar is not defined in parametrize
|
||||
pass
|
||||
|
||||
@pytest.mark.parametrize([" _foo", " _bar "], [1, 2, 3])
|
||||
def test_thingy5(_foo, _bar): # OK defined in parametrize
|
||||
pass
|
||||
|
||||
x = "other"
|
||||
@pytest.mark.parametrize(x, [1, 2, 3])
|
||||
def test_thingy5(_foo, _bar): # known false negative, we don't try to resolve variables
|
||||
pass
|
||||
|
||||
@@ -31,7 +31,7 @@ no_sep = None
|
||||
|
||||
" a*a a*a a ".split("*", -1) # [" a", "a a", "a a "]
|
||||
"".split() # []
|
||||
"""
|
||||
"""
|
||||
""".split() # []
|
||||
" ".split() # []
|
||||
"/abc/".split() # ["/abc/"]
|
||||
@@ -73,7 +73,7 @@ r"\n " "\n".split() # [r"\n"]
|
||||
|
||||
# negatives
|
||||
|
||||
# invalid values should not cause panic
|
||||
# invalid values should not cause panic
|
||||
"a,b,c,d".split(maxsplit="hello")
|
||||
"a,b,c,d".split(maxsplit=-"hello")
|
||||
|
||||
@@ -106,27 +106,3 @@ b"TesT.WwW.ExamplE.CoM".split(b".")
|
||||
'''itemC'''
|
||||
"'itemD'"
|
||||
""".split()
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18042
|
||||
print("a,b".rsplit(","))
|
||||
print("a,b,c".rsplit(",", 1))
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18069
|
||||
|
||||
print("".split(maxsplit=0))
|
||||
print("".split(sep=None, maxsplit=0))
|
||||
print(" ".split(maxsplit=0))
|
||||
print(" ".split(sep=None, maxsplit=0))
|
||||
print(" x ".split(maxsplit=0))
|
||||
print(" x ".split(sep=None, maxsplit=0))
|
||||
print(" x ".split(maxsplit=0))
|
||||
print(" x ".split(sep=None, maxsplit=0))
|
||||
print("".rsplit(maxsplit=0))
|
||||
print("".rsplit(sep=None, maxsplit=0))
|
||||
print(" ".rsplit(maxsplit=0))
|
||||
print(" ".rsplit(sep=None, maxsplit=0))
|
||||
print(" x ".rsplit(maxsplit=0))
|
||||
print(" x ".rsplit(maxsplit=0))
|
||||
print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
print(" x ".rsplit(maxsplit=0))
|
||||
print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
@@ -9,7 +9,3 @@ extensions_dir = "./extensions"
|
||||
glob.glob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp"))
|
||||
list(glob.iglob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp")))
|
||||
search("*.png")
|
||||
|
||||
# if `dir_fd` is set, suppress the diagnostic
|
||||
glob.glob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp"), dir_fd=1)
|
||||
list(glob.iglob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp"), dir_fd=1))
|
||||
|
||||
@@ -87,20 +87,3 @@ def bar(x: int):
|
||||
os.rename("src", "dst", src_dir_fd=3, dst_dir_fd=4)
|
||||
os.rename("src", "dst", src_dir_fd=3)
|
||||
os.rename("src", "dst", dst_dir_fd=4)
|
||||
|
||||
# if `dir_fd` is set, suppress the diagnostic
|
||||
os.readlink(p, dir_fd=1)
|
||||
os.stat(p, dir_fd=2)
|
||||
os.unlink(p, dir_fd=3)
|
||||
os.remove(p, dir_fd=4)
|
||||
os.rmdir(p, dir_fd=5)
|
||||
os.mkdir(p, dir_fd=6)
|
||||
os.chmod(p, dir_fd=7)
|
||||
# `chmod` can also receive a file descriptor in the first argument
|
||||
os.chmod(8)
|
||||
os.chmod(x)
|
||||
|
||||
# if `src_dir_fd` or `dst_dir_fd` are set, suppress the diagnostic
|
||||
os.replace("src", "dst", src_dir_fd=1, dst_dir_fd=2)
|
||||
os.replace("src", "dst", src_dir_fd=1)
|
||||
os.replace("src", "dst", dst_dir_fd=2)
|
||||
|
||||
@@ -144,14 +144,14 @@ def f():
|
||||
|
||||
def f():
|
||||
# make sure that `tmp` is not deleted
|
||||
tmp = 1; result = [] # comment should be protected
|
||||
tmp = 1; result = [] # commment should be protected
|
||||
for i in range(10):
|
||||
result.append(i + 1) # PERF401
|
||||
|
||||
|
||||
def f():
|
||||
# make sure that `tmp` is not deleted
|
||||
result = []; tmp = 1 # comment should be protected
|
||||
result = []; tmp = 1 # commment should be protected
|
||||
for i in range(10):
|
||||
result.append(i + 1) # PERF401
|
||||
|
||||
|
||||
@@ -6,16 +6,14 @@ from .mmap import error
|
||||
raise error
|
||||
|
||||
# Testing the modules
|
||||
import socket, mmap, select, resource
|
||||
import socket, mmap, select
|
||||
raise socket.error
|
||||
raise mmap.error
|
||||
raise select.error
|
||||
raise resource.error
|
||||
|
||||
raise socket.error()
|
||||
raise mmap.error(1)
|
||||
raise select.error(1, 2)
|
||||
raise resource.error(1, "strerror", "filename")
|
||||
|
||||
raise socket.error(
|
||||
1,
|
||||
@@ -32,9 +30,6 @@ raise error(1)
|
||||
from select import error
|
||||
raise error(1, 2)
|
||||
|
||||
from resource import error
|
||||
raise error(1, "strerror", "filename")
|
||||
|
||||
# Testing the names
|
||||
raise EnvironmentError
|
||||
raise IOError
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import datetime
|
||||
import sys
|
||||
|
||||
num = 1337
|
||||
|
||||
def return_num() -> int:
|
||||
@@ -13,7 +10,6 @@ print(bin(num)[2:]) # FURB116
|
||||
print(oct(1337)[2:]) # FURB116
|
||||
print(hex(1337)[2:]) # FURB116
|
||||
print(bin(1337)[2:]) # FURB116
|
||||
print(bin(+1337)[2:]) # FURB116
|
||||
|
||||
print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
print(bin(int(f"{num}"))[2:]) # FURB116 (no autofix)
|
||||
@@ -26,19 +22,3 @@ print(hex(0x1337)[3:])
|
||||
# float and complex numbers should be ignored
|
||||
print(bin(1.0)[2:])
|
||||
print(bin(3.14j)[2:])
|
||||
|
||||
d = datetime.datetime.now(tz=datetime.UTC)
|
||||
# autofix is display-only
|
||||
print(bin(d)[2:])
|
||||
# no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
print(bin(len("xyz").numerator)[2:])
|
||||
|
||||
# autofix is display-only
|
||||
print(bin({0: 1}[0].numerator)[2:])
|
||||
# no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
print(bin(ord("\\").numerator)[2:])
|
||||
print(hex(sys
|
||||
.maxunicode)[2:])
|
||||
|
||||
# for negatives numbers autofix is display-only
|
||||
print(bin(-1)[2:])
|
||||
|
||||
@@ -19,9 +19,6 @@ b'c' in b""
|
||||
b"a" in bytearray()
|
||||
b"a" in bytes()
|
||||
1 in frozenset()
|
||||
1 in set(set())
|
||||
2 in frozenset([])
|
||||
'' in set("")
|
||||
|
||||
# OK
|
||||
1 in [2]
|
||||
@@ -38,7 +35,3 @@ b'c' in b"x"
|
||||
b"a" in bytearray([2])
|
||||
b"a" in bytes("a", "utf-8")
|
||||
1 in frozenset("c")
|
||||
1 in set(set((1,2)))
|
||||
1 in set(set([1]))
|
||||
'' in {""}
|
||||
frozenset() in {frozenset()}
|
||||
|
||||
@@ -1116,95 +1116,6 @@ mod tests {
|
||||
PythonVersion::PY310,
|
||||
"InvalidStarExpression"
|
||||
)]
|
||||
#[test_case(
|
||||
"irrefutable_case_pattern_wildcard",
|
||||
"
|
||||
match value:
|
||||
case _:
|
||||
pass
|
||||
case 1:
|
||||
pass
|
||||
",
|
||||
PythonVersion::PY310,
|
||||
"IrrefutableCasePattern"
|
||||
)]
|
||||
#[test_case(
|
||||
"irrefutable_case_pattern_capture",
|
||||
"
|
||||
match value:
|
||||
case irrefutable:
|
||||
pass
|
||||
case 1:
|
||||
pass
|
||||
",
|
||||
PythonVersion::PY310,
|
||||
"IrrefutableCasePattern"
|
||||
)]
|
||||
#[test_case(
|
||||
"single_starred_assignment",
|
||||
"*a = [1, 2, 3, 4]",
|
||||
PythonVersion::PY310,
|
||||
"SingleStarredAssignment"
|
||||
)]
|
||||
#[test_case(
|
||||
"write_to_debug",
|
||||
"
|
||||
__debug__ = False
|
||||
",
|
||||
PythonVersion::PY310,
|
||||
"WriteToDebug"
|
||||
)]
|
||||
#[test_case(
|
||||
"write_to_debug_in_function_param",
|
||||
"
|
||||
def process(__debug__):
|
||||
pass
|
||||
",
|
||||
PythonVersion::PY310,
|
||||
"WriteToDebug"
|
||||
)]
|
||||
#[test_case(
|
||||
"write_to_debug_class_type_param",
|
||||
"
|
||||
class Generic[__debug__]:
|
||||
pass
|
||||
",
|
||||
PythonVersion::PY312,
|
||||
"WriteToDebug"
|
||||
)]
|
||||
#[test_case(
|
||||
"invalid_expression_yield_in_type_param",
|
||||
"
|
||||
type X[T: (yield 1)] = int
|
||||
",
|
||||
PythonVersion::PY312,
|
||||
"InvalidExpression"
|
||||
)]
|
||||
#[test_case(
|
||||
"invalid_expression_yield_in_type_alias",
|
||||
"
|
||||
type Y = (yield 1)
|
||||
",
|
||||
PythonVersion::PY312,
|
||||
"InvalidExpression"
|
||||
)]
|
||||
#[test_case(
|
||||
"invalid_expression_walrus_in_return_annotation",
|
||||
"
|
||||
def f[T](x: int) -> (y := 3): return x
|
||||
",
|
||||
PythonVersion::PY312,
|
||||
"InvalidExpression"
|
||||
)]
|
||||
#[test_case(
|
||||
"invalid_expression_yield_from_in_base_class",
|
||||
"
|
||||
class C[T]((yield from [object])):
|
||||
pass
|
||||
",
|
||||
PythonVersion::PY312,
|
||||
"InvalidExpression"
|
||||
)]
|
||||
fn test_semantic_errors(
|
||||
name: &str,
|
||||
contents: &str,
|
||||
|
||||
@@ -8,20 +8,13 @@ use ruff_python_semantic::SemanticModel;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum Replacement {
|
||||
// There's no replacement or suggestion other than removal
|
||||
None,
|
||||
// The attribute name of a class has been changed.
|
||||
AttrName(&'static str),
|
||||
// Additional information. Used when there's replacement but they're not direct mapping.
|
||||
Name(&'static str),
|
||||
Message(&'static str),
|
||||
// Symbols updated in Airflow 3 with replacement
|
||||
// e.g., `airflow.datasets.Dataset` to `airflow.sdk.Asset`
|
||||
AutoImport {
|
||||
module: &'static str,
|
||||
name: &'static str,
|
||||
},
|
||||
// Symbols updated in Airflow 3 with only module changed. Used when we want to match multiple names.
|
||||
// e.g., `airflow.configuration.as_dict | get` to `airflow.configuration.conf.as_dict | get`
|
||||
SourceModuleMoved {
|
||||
module: &'static str,
|
||||
name: String,
|
||||
@@ -52,8 +45,7 @@ pub(crate) enum ProviderReplacement {
|
||||
|
||||
pub(crate) fn is_guarded_by_try_except(
|
||||
expr: &Expr,
|
||||
module: &str,
|
||||
name: &str,
|
||||
replacement: &Replacement,
|
||||
semantic: &SemanticModel,
|
||||
) -> bool {
|
||||
match expr {
|
||||
@@ -71,7 +63,7 @@ pub(crate) fn is_guarded_by_try_except(
|
||||
if !suspended_exceptions.contains(Exceptions::ATTRIBUTE_ERROR) {
|
||||
return false;
|
||||
}
|
||||
try_block_contains_undeprecated_attribute(try_node, module, name, semantic)
|
||||
try_block_contains_undeprecated_attribute(try_node, replacement, semantic)
|
||||
}
|
||||
Expr::Name(ExprName { id, .. }) => {
|
||||
let Some(binding_id) = semantic.lookup_symbol(id.as_str()) else {
|
||||
@@ -97,7 +89,7 @@ pub(crate) fn is_guarded_by_try_except(
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try_block_contains_undeprecated_import(try_node, module, name)
|
||||
try_block_contains_undeprecated_import(try_node, replacement)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
@@ -108,10 +100,12 @@ pub(crate) fn is_guarded_by_try_except(
|
||||
/// member is being accessed from the non-deprecated location?
|
||||
fn try_block_contains_undeprecated_attribute(
|
||||
try_node: &StmtTry,
|
||||
module: &str,
|
||||
name: &str,
|
||||
replacement: &Replacement,
|
||||
semantic: &SemanticModel,
|
||||
) -> bool {
|
||||
let Replacement::AutoImport { module, name } = replacement else {
|
||||
return false;
|
||||
};
|
||||
let undeprecated_qualified_name = {
|
||||
let mut builder = QualifiedNameBuilder::default();
|
||||
for part in module.split('.') {
|
||||
@@ -128,7 +122,10 @@ fn try_block_contains_undeprecated_attribute(
|
||||
/// Given an [`ast::StmtTry`] node, does the `try` branch of that node
|
||||
/// contain any [`ast::StmtImportFrom`] nodes that indicate the airflow
|
||||
/// member is being imported from the non-deprecated location?
|
||||
fn try_block_contains_undeprecated_import(try_node: &StmtTry, module: &str, name: &str) -> bool {
|
||||
fn try_block_contains_undeprecated_import(try_node: &StmtTry, replacement: &Replacement) -> bool {
|
||||
let Replacement::AutoImport { module, name } = replacement else {
|
||||
return false;
|
||||
};
|
||||
let mut import_searcher = ImportSearcher::new(module, name);
|
||||
import_searcher.visit_body(&try_node.body);
|
||||
import_searcher.found_import
|
||||
|
||||
@@ -46,12 +46,9 @@ mod tests {
|
||||
#[test_case(Rule::Airflow3MovedToProvider, Path::new("AIR302_sqlite.py"))]
|
||||
#[test_case(Rule::Airflow3MovedToProvider, Path::new("AIR302_zendesk.py"))]
|
||||
#[test_case(Rule::Airflow3MovedToProvider, Path::new("AIR302_standard.py"))]
|
||||
#[test_case(Rule::Airflow3MovedToProvider, Path::new("AIR302_try.py"))]
|
||||
#[test_case(Rule::Airflow3SuggestedUpdate, Path::new("AIR311_args.py"))]
|
||||
#[test_case(Rule::Airflow3SuggestedUpdate, Path::new("AIR311_names.py"))]
|
||||
#[test_case(Rule::Airflow3SuggestedUpdate, Path::new("AIR311_try.py"))]
|
||||
#[test_case(Rule::Airflow3SuggestedToMoveToProvider, Path::new("AIR312.py"))]
|
||||
#[test_case(Rule::Airflow3SuggestedToMoveToProvider, Path::new("AIR312_try.py"))]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::rules::airflow::helpers::{is_guarded_by_try_except, ProviderReplacement};
|
||||
use crate::rules::airflow::helpers::ProviderReplacement;
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::{Expr, ExprAttribute};
|
||||
@@ -937,17 +937,13 @@ fn check_names_moved_to_provider(checker: &Checker, expr: &Expr, ranged: TextRan
|
||||
ranged.range(),
|
||||
);
|
||||
|
||||
let semantic = checker.semantic();
|
||||
if let Some((module, name)) = match &replacement {
|
||||
ProviderReplacement::AutoImport { module, name, .. } => Some((module, *name)),
|
||||
ProviderReplacement::SourceModuleMovedToProvider { module, name, .. } => {
|
||||
Some((module, name.as_str()))
|
||||
}
|
||||
_ => None,
|
||||
} {
|
||||
if is_guarded_by_try_except(expr, module, name, semantic) {
|
||||
return;
|
||||
}
|
||||
if let ProviderReplacement::AutoImport {
|
||||
module,
|
||||
name,
|
||||
provider: _,
|
||||
version: _,
|
||||
} = replacement
|
||||
{
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import_from(module, name),
|
||||
@@ -958,5 +954,6 @@ fn check_names_moved_to_provider(checker: &Checker, expr: &Expr, ranged: TextRan
|
||||
Ok(Fix::safe_edits(import_edit, [replacement_edit]))
|
||||
});
|
||||
}
|
||||
|
||||
checker.report_diagnostic(diagnostic);
|
||||
}
|
||||
|
||||
@@ -57,27 +57,28 @@ impl Violation for Airflow3Removal {
|
||||
} = self;
|
||||
match replacement {
|
||||
Replacement::None
|
||||
| Replacement::AttrName(_)
|
||||
| Replacement::Message(_)
|
||||
| Replacement::Name(_)
|
||||
| Replacement::AutoImport { module: _, name: _ }
|
||||
| Replacement::SourceModuleMoved { module: _, name: _ } => {
|
||||
format!("`{deprecated}` is removed in Airflow 3.0")
|
||||
}
|
||||
Replacement::Message(message) => {
|
||||
format!("`{deprecated}` is removed in Airflow 3.0; {message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
let Airflow3Removal { replacement, .. } = self;
|
||||
match replacement {
|
||||
Replacement::None => None,
|
||||
Replacement::AttrName(name) => Some(format!("Use `{name}` instead")),
|
||||
Replacement::Message(message) => Some((*message).to_string()),
|
||||
Replacement::Name(name) => Some(format!("Use `{name}` instead")),
|
||||
Replacement::AutoImport { module, name } => {
|
||||
Some(format!("Use `{module}.{name}` instead"))
|
||||
}
|
||||
Replacement::SourceModuleMoved { module, name } => {
|
||||
Some(format!("Use `{module}.{name}` instead"))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,22 +278,22 @@ fn check_class_attribute(checker: &Checker, attribute_expr: &ExprAttribute) {
|
||||
|
||||
let replacement = match *qualname.segments() {
|
||||
["airflow", "providers_manager", "ProvidersManager"] => match attr.as_str() {
|
||||
"dataset_factories" => Replacement::AttrName("asset_factories"),
|
||||
"dataset_uri_handlers" => Replacement::AttrName("asset_uri_handlers"),
|
||||
"dataset_factories" => Replacement::Name("asset_factories"),
|
||||
"dataset_uri_handlers" => Replacement::Name("asset_uri_handlers"),
|
||||
"dataset_to_openlineage_converters" => {
|
||||
Replacement::AttrName("asset_to_openlineage_converters")
|
||||
Replacement::Name("asset_to_openlineage_converters")
|
||||
}
|
||||
_ => return,
|
||||
},
|
||||
["airflow", "lineage", "hook", "DatasetLineageInfo"] => match attr.as_str() {
|
||||
"dataset" => Replacement::AttrName("asset"),
|
||||
"dataset" => Replacement::Name("asset"),
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Create the `Fix` first to avoid cloning `Replacement`.
|
||||
let fix = if let Replacement::AttrName(name) = replacement {
|
||||
let fix = if let Replacement::Name(name) = replacement {
|
||||
Some(Fix::safe_edit(Edit::range_replacement(
|
||||
name.to_string(),
|
||||
attr.range(),
|
||||
@@ -465,52 +466,52 @@ fn check_method(checker: &Checker, call_expr: &ExprCall) {
|
||||
|
||||
let replacement = match qualname.segments() {
|
||||
["airflow", "datasets", "manager", "DatasetManager"] => match attr.as_str() {
|
||||
"register_dataset_change" => Replacement::AttrName("register_asset_change"),
|
||||
"create_datasets" => Replacement::AttrName("create_assets"),
|
||||
"notify_dataset_created" => Replacement::AttrName("notify_asset_created"),
|
||||
"notify_dataset_changed" => Replacement::AttrName("notify_asset_changed"),
|
||||
"notify_dataset_alias_created" => Replacement::AttrName("notify_asset_alias_created"),
|
||||
"register_dataset_change" => Replacement::Name("register_asset_change"),
|
||||
"create_datasets" => Replacement::Name("create_assets"),
|
||||
"notify_dataset_created" => Replacement::Name("notify_asset_created"),
|
||||
"notify_dataset_changed" => Replacement::Name("notify_asset_changed"),
|
||||
"notify_dataset_alias_created" => Replacement::Name("notify_asset_alias_created"),
|
||||
_ => return,
|
||||
},
|
||||
["airflow", "lineage", "hook", "HookLineageCollector"] => match attr.as_str() {
|
||||
"create_dataset" => Replacement::AttrName("create_asset"),
|
||||
"add_input_dataset" => Replacement::AttrName("add_input_asset"),
|
||||
"add_output_dataset" => Replacement::AttrName("add_output_asset"),
|
||||
"collected_datasets" => Replacement::AttrName("collected_assets"),
|
||||
"create_dataset" => Replacement::Name("create_asset"),
|
||||
"add_input_dataset" => Replacement::Name("add_input_asset"),
|
||||
"add_output_dataset" => Replacement::Name("add_output_asset"),
|
||||
"collected_datasets" => Replacement::Name("collected_assets"),
|
||||
_ => return,
|
||||
},
|
||||
["airflow", "providers", "amazon", "auth_manager", "aws_auth_manager", "AwsAuthManager"] => {
|
||||
match attr.as_str() {
|
||||
"is_authorized_dataset" => Replacement::AttrName("is_authorized_asset"),
|
||||
"is_authorized_dataset" => Replacement::Name("is_authorized_asset"),
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
["airflow", "providers_manager", "ProvidersManager"] => match attr.as_str() {
|
||||
"initialize_providers_dataset_uri_resources" => {
|
||||
Replacement::AttrName("initialize_providers_asset_uri_resources")
|
||||
Replacement::Name("initialize_providers_asset_uri_resources")
|
||||
}
|
||||
_ => return,
|
||||
},
|
||||
["airflow", "secrets", "local_filesystem", "LocalFilesystemBackend"] => match attr.as_str()
|
||||
{
|
||||
"get_connections" => Replacement::AttrName("get_connection"),
|
||||
"get_connections" => Replacement::Name("get_connection"),
|
||||
_ => return,
|
||||
},
|
||||
["airflow", "datasets", ..] | ["airflow", "Dataset"] => match attr.as_str() {
|
||||
"iter_datasets" => Replacement::AttrName("iter_assets"),
|
||||
"iter_dataset_aliases" => Replacement::AttrName("iter_asset_aliases"),
|
||||
"iter_datasets" => Replacement::Name("iter_assets"),
|
||||
"iter_dataset_aliases" => Replacement::Name("iter_asset_aliases"),
|
||||
_ => return,
|
||||
},
|
||||
segments => {
|
||||
if is_airflow_secret_backend(segments) {
|
||||
match attr.as_str() {
|
||||
"get_conn_uri" => Replacement::AttrName("get_conn_value"),
|
||||
"get_connections" => Replacement::AttrName("get_connection"),
|
||||
"get_conn_uri" => Replacement::Name("get_conn_value"),
|
||||
"get_connections" => Replacement::Name("get_connection"),
|
||||
_ => return,
|
||||
}
|
||||
} else if is_airflow_hook(segments) {
|
||||
match attr.as_str() {
|
||||
"get_connections" => Replacement::AttrName("get_connection"),
|
||||
"get_connections" => Replacement::Name("get_connection"),
|
||||
_ => return,
|
||||
}
|
||||
} else {
|
||||
@@ -519,7 +520,7 @@ fn check_method(checker: &Checker, call_expr: &ExprCall) {
|
||||
}
|
||||
};
|
||||
// Create the `Fix` first to avoid cloning `Replacement`.
|
||||
let fix = if let Replacement::AttrName(name) = replacement {
|
||||
let fix = if let Replacement::Name(name) = replacement {
|
||||
Some(Fix::safe_edit(Edit::range_replacement(
|
||||
name.to_string(),
|
||||
attr.range(),
|
||||
@@ -565,12 +566,12 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
let replacement = match qualified_name.segments() {
|
||||
// airflow.PY\d{1,2}
|
||||
["airflow", "PY36" | "PY37" | "PY38" | "PY39" | "PY310" | "PY311" | "PY312"] => {
|
||||
Replacement::Message("Use `sys.version_info` instead")
|
||||
Replacement::Name("sys.version_info")
|
||||
}
|
||||
|
||||
// airflow.api_connexion.security
|
||||
["airflow", "api_connexion", "security", "requires_access"] => {
|
||||
Replacement::Message("Use `airflow.api_connexion.security.requires_access_*` instead")
|
||||
Replacement::Name("airflow.api_connexion.security.requires_access_*")
|
||||
}
|
||||
["airflow", "api_connexion", "security", "requires_access_dataset"] => {
|
||||
Replacement::AutoImport {
|
||||
@@ -625,10 +626,9 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
["airflow", "datasets", "DatasetAliasEvent"] => Replacement::None,
|
||||
|
||||
// airflow.hooks
|
||||
["airflow", "hooks", "base_hook", "BaseHook"] => Replacement::AutoImport {
|
||||
module: "airflow.hooks.base",
|
||||
name: "BaseHook",
|
||||
},
|
||||
["airflow", "hooks", "base_hook", "BaseHook"] => {
|
||||
Replacement::Name("airflow.hooks.base.BaseHook")
|
||||
}
|
||||
|
||||
// airflow.lineage.hook
|
||||
["airflow", "lineage", "hook", "DatasetLineageInfo"] => Replacement::AutoImport {
|
||||
@@ -664,10 +664,9 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
},
|
||||
|
||||
// airflow.notifications
|
||||
["airflow", "notifications", "basenotifier", "BaseNotifier"] => Replacement::AutoImport {
|
||||
module: "airflow.sdk",
|
||||
name: "BaseNotifier",
|
||||
},
|
||||
["airflow", "notifications", "basenotifier", "BaseNotifier"] => {
|
||||
Replacement::Name("airflow.sdk.BaseNotifier")
|
||||
}
|
||||
|
||||
// airflow.operators
|
||||
["airflow", "operators", "subdag", ..] => {
|
||||
@@ -692,10 +691,7 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
|
||||
// airflow.sensors
|
||||
["airflow", "sensors", "base_sensor_operator", "BaseSensorOperator"] => {
|
||||
Replacement::AutoImport {
|
||||
module: "airflow.sdk.bases.sensor",
|
||||
name: "BaseSensorOperator",
|
||||
}
|
||||
Replacement::Name("airflow.sdk.bases.sensor.BaseSensorOperator")
|
||||
}
|
||||
|
||||
// airflow.timetables
|
||||
@@ -724,36 +720,23 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
|
||||
// airflow.utils.dates
|
||||
["dates", "date_range"] => Replacement::None,
|
||||
["dates", "days_ago"] => {
|
||||
Replacement::Message("Use `pendulum.today('UTC').add(days=-N, ...)` instead")
|
||||
}
|
||||
["dates", "days_ago"] => Replacement::Name("pendulum.today('UTC').add(days=-N, ...)"),
|
||||
["dates", "parse_execution_date" | "round_time" | "scale_time_units" | "infer_time_unit"] => {
|
||||
Replacement::None
|
||||
}
|
||||
|
||||
// airflow.utils.file
|
||||
["file", "TemporaryDirectory"] => Replacement::AutoImport {
|
||||
module: "tempfile",
|
||||
name: "TemporaryDirectory",
|
||||
},
|
||||
["file", "mkdirs"] => Replacement::Message("Use `pathlib.Path({path}).mkdir` instead"),
|
||||
["file", "TemporaryDirectory"] => Replacement::Name("tempfile.TemporaryDirectory"),
|
||||
["file", "mkdirs"] => Replacement::Name("pathlib.Path({path}).mkdir"),
|
||||
|
||||
// airflow.utils.helpers
|
||||
["helpers", "chain"] => Replacement::AutoImport {
|
||||
module: "airflow.sdk",
|
||||
name: "chain",
|
||||
},
|
||||
["helpers", "cross_downstream"] => Replacement::AutoImport {
|
||||
module: "airflow.sdk",
|
||||
name: "cross_downstream",
|
||||
},
|
||||
["helpers", "chain"] => Replacement::Name("airflow.sdk.chain"),
|
||||
["helpers", "cross_downstream"] => Replacement::Name("airflow.sdk.cross_downstream"),
|
||||
|
||||
// TODO: update it as SourceModuleMoved
|
||||
// airflow.utils.log.secrets_masker
|
||||
["log", "secrets_masker"] => Replacement::AutoImport {
|
||||
module: "airflow.sdk.execution_time",
|
||||
name: "secrets_masker",
|
||||
},
|
||||
["log", "secrets_masker"] => {
|
||||
Replacement::Name("airflow.sdk.execution_time.secrets_masker")
|
||||
}
|
||||
|
||||
// airflow.utils.state
|
||||
["state", "SHUTDOWN" | "terminating_states"] => Replacement::None,
|
||||
@@ -768,20 +751,18 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
// airflow.www
|
||||
// TODO: www has been removed
|
||||
["airflow", "www", "auth", "has_access"] => {
|
||||
Replacement::Message("Use `airflow.www.auth.has_access_*` instead")
|
||||
Replacement::Name("airflow.www.auth.has_access_*")
|
||||
}
|
||||
["airflow", "www", "auth", "has_access_dataset"] => Replacement::AutoImport {
|
||||
module: "airflow.www.auth",
|
||||
name: "has_access_asset",
|
||||
},
|
||||
["airflow", "www", "utils", "get_sensitive_variables_fields"] => Replacement::AutoImport {
|
||||
module: "airflow.utils.log.secrets_masker",
|
||||
name: "get_sensitive_variables_fields",
|
||||
},
|
||||
["airflow", "www", "utils", "should_hide_value_for_key"] => Replacement::AutoImport {
|
||||
module: "airflow.utils.log.secrets_masker",
|
||||
name: "should_hide_value_for_key",
|
||||
},
|
||||
["airflow", "www", "utils", "get_sensitive_variables_fields"] => {
|
||||
Replacement::Name("airflow.utils.log.secrets_masker.get_sensitive_variables_fields")
|
||||
}
|
||||
["airflow", "www", "utils", "should_hide_value_for_key"] => {
|
||||
Replacement::Name("airflow.utils.log.secrets_masker.should_hide_value_for_key")
|
||||
}
|
||||
|
||||
// airflow.providers.amazon
|
||||
["airflow", "providers", "amazon", "aws", "datasets", "s3", rest] => match *rest {
|
||||
@@ -793,10 +774,9 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
module: "airflow.providers.amazon.aws.assets.s3",
|
||||
name: "convert_asset_to_openlineage",
|
||||
},
|
||||
"sanitize_uri" => Replacement::AutoImport {
|
||||
module: "airflow.providers.amazon.aws.assets.s3",
|
||||
name: "sanitize_uri",
|
||||
},
|
||||
"sanitize_uri" => {
|
||||
Replacement::Name("airflow.providers.amazon.aws.assets.s3.sanitize_uri")
|
||||
}
|
||||
_ => return,
|
||||
},
|
||||
["airflow", "providers", "amazon", "aws", "auth_manager", "avp", "entities", "AvpEntities", "DATASET"] => {
|
||||
@@ -817,10 +797,9 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
module: "airflow.providers.common.io.assets.file",
|
||||
name: "convert_asset_to_openlineage",
|
||||
},
|
||||
"sanitize_uri" => Replacement::AutoImport {
|
||||
module: "airflow.providers.common.io.assets.file",
|
||||
name: "sanitize_uri",
|
||||
},
|
||||
"sanitize_uri" => {
|
||||
Replacement::Name("airflow.providers.common.io.assets.file.sanitize_uri")
|
||||
}
|
||||
_ => return,
|
||||
},
|
||||
|
||||
@@ -847,28 +826,20 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
module: "airflow.providers.google.assets.gcs",
|
||||
name: "convert_asset_to_openlineage",
|
||||
},
|
||||
["gcs", "sanitize_uri"] => Replacement::AutoImport {
|
||||
module: "airflow.providers.google.assets.gcs",
|
||||
name: "sanitize_uri",
|
||||
},
|
||||
|
||||
["gcs", "sanitize_uri"] => {
|
||||
Replacement::Name("airflow.providers.google.assets.gcs.sanitize_uri")
|
||||
}
|
||||
_ => return,
|
||||
},
|
||||
|
||||
// airflow.providers.mysql
|
||||
["airflow", "providers", "mysql", "datasets", "mysql", "sanitize_uri"] => {
|
||||
Replacement::AutoImport {
|
||||
module: "airflow.providers.mysql.assets.mysql",
|
||||
name: "sanitize_uri",
|
||||
}
|
||||
Replacement::Name("airflow.providers.mysql.assets.mysql.sanitize_uri")
|
||||
}
|
||||
|
||||
// airflow.providers.postgres
|
||||
["airflow", "providers", "postgres", "datasets", "postgres", "sanitize_uri"] => {
|
||||
Replacement::AutoImport {
|
||||
module: "airflow.providers.postgres.assets.postgres",
|
||||
name: "sanitize_uri",
|
||||
}
|
||||
Replacement::Name("airflow.providers.postgres.assets.postgres.sanitize_uri")
|
||||
}
|
||||
|
||||
// airflow.providers.openlineage
|
||||
@@ -888,15 +859,16 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
|
||||
// airflow.providers.trino
|
||||
["airflow", "providers", "trino", "datasets", "trino", "sanitize_uri"] => {
|
||||
Replacement::AutoImport {
|
||||
module: "airflow.providers.trino.assets.trino",
|
||||
name: "sanitize_uri",
|
||||
}
|
||||
Replacement::Name("airflow.providers.trino.assets.trino.sanitize_uri")
|
||||
}
|
||||
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if is_guarded_by_try_except(expr, &replacement, semantic) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
Airflow3Removal {
|
||||
deprecated: qualified_name.to_string(),
|
||||
@@ -904,15 +876,8 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
},
|
||||
range,
|
||||
);
|
||||
let semantic = checker.semantic();
|
||||
if let Some((module, name)) = match &replacement {
|
||||
Replacement::AutoImport { module, name } => Some((module, *name)),
|
||||
Replacement::SourceModuleMoved { module, name } => Some((module, name.as_str())),
|
||||
_ => None,
|
||||
} {
|
||||
if is_guarded_by_try_except(expr, module, name, semantic) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Replacement::AutoImport { module, name } = replacement {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import_from(module, name),
|
||||
@@ -981,7 +946,7 @@ fn diagnostic_for_argument(
|
||||
Airflow3Removal {
|
||||
deprecated: deprecated.to_string(),
|
||||
replacement: match replacement {
|
||||
Some(name) => Replacement::AttrName(name),
|
||||
Some(name) => Replacement::Name(name),
|
||||
None => Replacement::None,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::importer::ImportRequest;
|
||||
|
||||
use crate::rules::airflow::helpers::{is_guarded_by_try_except, ProviderReplacement};
|
||||
use crate::rules::airflow::helpers::ProviderReplacement;
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::{Expr, ExprAttribute};
|
||||
@@ -279,17 +279,13 @@ fn check_names_moved_to_provider(checker: &Checker, expr: &Expr, ranged: TextRan
|
||||
ranged.range(),
|
||||
);
|
||||
|
||||
let semantic = checker.semantic();
|
||||
if let Some((module, name)) = match &replacement {
|
||||
ProviderReplacement::AutoImport { module, name, .. } => Some((module, *name)),
|
||||
ProviderReplacement::SourceModuleMovedToProvider { module, name, .. } => {
|
||||
Some((module, name.as_str()))
|
||||
}
|
||||
_ => None,
|
||||
} {
|
||||
if is_guarded_by_try_except(expr, module, name, semantic) {
|
||||
return;
|
||||
}
|
||||
if let ProviderReplacement::AutoImport {
|
||||
module,
|
||||
name,
|
||||
provider: _,
|
||||
version: _,
|
||||
} = replacement
|
||||
{
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import_from(module, name),
|
||||
|
||||
@@ -53,8 +53,7 @@ impl Violation for Airflow3SuggestedUpdate {
|
||||
} = self;
|
||||
match replacement {
|
||||
Replacement::None
|
||||
| Replacement::AttrName(_)
|
||||
| Replacement::Message(_)
|
||||
| Replacement::Name(_)
|
||||
| Replacement::AutoImport { module: _, name: _ }
|
||||
| Replacement::SourceModuleMoved { module: _, name: _ } => {
|
||||
format!(
|
||||
@@ -62,21 +61,27 @@ impl Violation for Airflow3SuggestedUpdate {
|
||||
It still works in Airflow 3.0 but is expected to be removed in a future version."
|
||||
)
|
||||
}
|
||||
Replacement::Message(message) => {
|
||||
format!(
|
||||
"`{deprecated}` is removed in Airflow 3.0; \
|
||||
It still works in Airflow 3.0 but is expected to be removed in a future version.; \
|
||||
{message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
let Airflow3SuggestedUpdate { replacement, .. } = self;
|
||||
match replacement {
|
||||
Replacement::None => None,
|
||||
Replacement::AttrName(name) => Some(format!("Use `{name}` instead")),
|
||||
Replacement::Message(message) => Some((*message).to_string()),
|
||||
Replacement::Name(name) => Some(format!("Use `{name}` instead")),
|
||||
Replacement::AutoImport { module, name } => {
|
||||
Some(format!("Use `{module}.{name}` instead"))
|
||||
}
|
||||
Replacement::SourceModuleMoved { module, name } => {
|
||||
Some(format!("Use `{module}.{name}` instead"))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,7 +126,7 @@ fn diagnostic_for_argument(
|
||||
Airflow3SuggestedUpdate {
|
||||
deprecated: deprecated.to_string(),
|
||||
replacement: match replacement {
|
||||
Some(name) => Replacement::AttrName(name),
|
||||
Some(name) => Replacement::Name(name),
|
||||
None => Replacement::None,
|
||||
},
|
||||
},
|
||||
@@ -237,14 +242,6 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
name: "attach".to_string(),
|
||||
},
|
||||
|
||||
// airflow.models
|
||||
["airflow", "models", rest @ ("Connection" | "Variable")] => {
|
||||
Replacement::SourceModuleMoved {
|
||||
module: "airflow.sdk",
|
||||
name: (*rest).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
// airflow.models.baseoperator
|
||||
["airflow", "models", "baseoperator", rest] => match *rest {
|
||||
"chain" | "chain_linear" | "cross_downstream" => Replacement::SourceModuleMoved {
|
||||
@@ -278,6 +275,10 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if is_guarded_by_try_except(expr, &replacement, semantic) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
Airflow3SuggestedUpdate {
|
||||
deprecated: qualified_name.to_string(),
|
||||
@@ -286,15 +287,7 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
range,
|
||||
);
|
||||
|
||||
let semantic = checker.semantic();
|
||||
if let Some((module, name)) = match &replacement {
|
||||
Replacement::AutoImport { module, name } => Some((module, *name)),
|
||||
Replacement::SourceModuleMoved { module, name } => Some((module, name.as_str())),
|
||||
_ => None,
|
||||
} {
|
||||
if is_guarded_by_try_except(expr, module, name, semantic) {
|
||||
return;
|
||||
}
|
||||
if let Replacement::AutoImport { module, name } = replacement {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import_from(module, name),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/airflow/mod.rs
|
||||
---
|
||||
AIR301_airflow_plugin.py:7:5: AIR301 `operators` is removed in Airflow 3.0
|
||||
AIR301_airflow_plugin.py:7:5: AIR301 `operators` is removed in Airflow 3.0; This extension should just be imported as a regular python module.
|
||||
|
|
||||
5 | name = "test_plugin"
|
||||
6 | # --- Invalid extensions start
|
||||
@@ -10,9 +10,8 @@ AIR301_airflow_plugin.py:7:5: AIR301 `operators` is removed in Airflow 3.0
|
||||
8 | sensors = [PluginSensorOperator]
|
||||
9 | hooks = [PluginHook]
|
||||
|
|
||||
= help: This extension should just be imported as a regular python module.
|
||||
|
||||
AIR301_airflow_plugin.py:8:5: AIR301 `sensors` is removed in Airflow 3.0
|
||||
AIR301_airflow_plugin.py:8:5: AIR301 `sensors` is removed in Airflow 3.0; This extension should just be imported as a regular python module.
|
||||
|
|
||||
6 | # --- Invalid extensions start
|
||||
7 | operators = [PluginOperator]
|
||||
@@ -21,9 +20,8 @@ AIR301_airflow_plugin.py:8:5: AIR301 `sensors` is removed in Airflow 3.0
|
||||
9 | hooks = [PluginHook]
|
||||
10 | executors = [PluginExecutor]
|
||||
|
|
||||
= help: This extension should just be imported as a regular python module.
|
||||
|
||||
AIR301_airflow_plugin.py:9:5: AIR301 `hooks` is removed in Airflow 3.0
|
||||
AIR301_airflow_plugin.py:9:5: AIR301 `hooks` is removed in Airflow 3.0; This extension should just be imported as a regular python module.
|
||||
|
|
||||
7 | operators = [PluginOperator]
|
||||
8 | sensors = [PluginSensorOperator]
|
||||
@@ -32,9 +30,8 @@ AIR301_airflow_plugin.py:9:5: AIR301 `hooks` is removed in Airflow 3.0
|
||||
10 | executors = [PluginExecutor]
|
||||
11 | # --- Invalid extensions end
|
||||
|
|
||||
= help: This extension should just be imported as a regular python module.
|
||||
|
||||
AIR301_airflow_plugin.py:10:5: AIR301 `executors` is removed in Airflow 3.0
|
||||
AIR301_airflow_plugin.py:10:5: AIR301 `executors` is removed in Airflow 3.0; This extension should just be imported as a regular python module.
|
||||
|
|
||||
8 | sensors = [PluginSensorOperator]
|
||||
9 | hooks = [PluginHook]
|
||||
@@ -43,4 +40,3 @@ AIR301_airflow_plugin.py:10:5: AIR301 `executors` is removed in Airflow 3.0
|
||||
11 | # --- Invalid extensions end
|
||||
12 | macros = [plugin_macro]
|
||||
|
|
||||
= help: This extension should just be imported as a regular python module.
|
||||
|
||||
@@ -258,11 +258,10 @@ AIR301_args.py:90:16: AIR301 `filename_template` is removed in Airflow 3.0
|
||||
92 | FabAuthManager(None)
|
||||
|
|
||||
|
||||
AIR301_args.py:92:15: AIR301 `appbuilder` is removed in Airflow 3.0
|
||||
AIR301_args.py:92:15: AIR301 `appbuilder` is removed in Airflow 3.0; The constructor takes no parameter now
|
||||
|
|
||||
90 | GCSTaskHandler(filename_template="/tmp/test")
|
||||
91 |
|
||||
92 | FabAuthManager(None)
|
||||
| ^^^^^^ AIR301
|
||||
|
|
||||
= help: The constructor takes no parameter now
|
||||
|
||||
@@ -1,488 +1,448 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/airflow/mod.rs
|
||||
---
|
||||
AIR301_names.py:53:1: AIR301 `airflow.PY36` is removed in Airflow 3.0
|
||||
AIR301_names.py:56:1: AIR301 `airflow.PY36` is removed in Airflow 3.0
|
||||
|
|
||||
52 | # airflow root
|
||||
53 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
55 | # airflow root
|
||||
56 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^ AIR301
|
||||
54 |
|
||||
55 | # airflow.api_connexion.security
|
||||
57 |
|
||||
58 | # airflow.api_connexion.security
|
||||
|
|
||||
= help: Use `sys.version_info` instead
|
||||
|
||||
AIR301_names.py:53:7: AIR301 `airflow.PY37` is removed in Airflow 3.0
|
||||
AIR301_names.py:56:7: AIR301 `airflow.PY37` is removed in Airflow 3.0
|
||||
|
|
||||
52 | # airflow root
|
||||
53 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
55 | # airflow root
|
||||
56 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^ AIR301
|
||||
54 |
|
||||
55 | # airflow.api_connexion.security
|
||||
57 |
|
||||
58 | # airflow.api_connexion.security
|
||||
|
|
||||
= help: Use `sys.version_info` instead
|
||||
|
||||
AIR301_names.py:53:13: AIR301 `airflow.PY38` is removed in Airflow 3.0
|
||||
AIR301_names.py:56:13: AIR301 `airflow.PY38` is removed in Airflow 3.0
|
||||
|
|
||||
52 | # airflow root
|
||||
53 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
55 | # airflow root
|
||||
56 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^ AIR301
|
||||
54 |
|
||||
55 | # airflow.api_connexion.security
|
||||
57 |
|
||||
58 | # airflow.api_connexion.security
|
||||
|
|
||||
= help: Use `sys.version_info` instead
|
||||
|
||||
AIR301_names.py:53:19: AIR301 `airflow.PY39` is removed in Airflow 3.0
|
||||
AIR301_names.py:56:19: AIR301 `airflow.PY39` is removed in Airflow 3.0
|
||||
|
|
||||
52 | # airflow root
|
||||
53 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
55 | # airflow root
|
||||
56 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^ AIR301
|
||||
54 |
|
||||
55 | # airflow.api_connexion.security
|
||||
57 |
|
||||
58 | # airflow.api_connexion.security
|
||||
|
|
||||
= help: Use `sys.version_info` instead
|
||||
|
||||
AIR301_names.py:53:25: AIR301 `airflow.PY310` is removed in Airflow 3.0
|
||||
AIR301_names.py:56:25: AIR301 `airflow.PY310` is removed in Airflow 3.0
|
||||
|
|
||||
52 | # airflow root
|
||||
53 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
55 | # airflow root
|
||||
56 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^^ AIR301
|
||||
54 |
|
||||
55 | # airflow.api_connexion.security
|
||||
57 |
|
||||
58 | # airflow.api_connexion.security
|
||||
|
|
||||
= help: Use `sys.version_info` instead
|
||||
|
||||
AIR301_names.py:53:32: AIR301 `airflow.PY311` is removed in Airflow 3.0
|
||||
AIR301_names.py:56:32: AIR301 `airflow.PY311` is removed in Airflow 3.0
|
||||
|
|
||||
52 | # airflow root
|
||||
53 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
55 | # airflow root
|
||||
56 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^^ AIR301
|
||||
54 |
|
||||
55 | # airflow.api_connexion.security
|
||||
57 |
|
||||
58 | # airflow.api_connexion.security
|
||||
|
|
||||
= help: Use `sys.version_info` instead
|
||||
|
||||
AIR301_names.py:53:39: AIR301 `airflow.PY312` is removed in Airflow 3.0
|
||||
AIR301_names.py:56:39: AIR301 `airflow.PY312` is removed in Airflow 3.0
|
||||
|
|
||||
52 | # airflow root
|
||||
53 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
55 | # airflow root
|
||||
56 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^^ AIR301
|
||||
54 |
|
||||
55 | # airflow.api_connexion.security
|
||||
57 |
|
||||
58 | # airflow.api_connexion.security
|
||||
|
|
||||
= help: Use `sys.version_info` instead
|
||||
|
||||
AIR301_names.py:56:1: AIR301 `airflow.api_connexion.security.requires_access` is removed in Airflow 3.0
|
||||
AIR301_names.py:59:1: AIR301 `airflow.api_connexion.security.requires_access` is removed in Airflow 3.0
|
||||
|
|
||||
55 | # airflow.api_connexion.security
|
||||
56 | requires_access
|
||||
58 | # airflow.api_connexion.security
|
||||
59 | requires_access
|
||||
| ^^^^^^^^^^^^^^^ AIR301
|
||||
|
|
||||
= help: Use `airflow.api_connexion.security.requires_access_*` instead
|
||||
|
||||
AIR301_names.py:60:1: AIR301 `airflow.configuration.get` is removed in Airflow 3.0
|
||||
AIR301_names.py:63:1: AIR301 `airflow.configuration.get` is removed in Airflow 3.0
|
||||
|
|
||||
59 | # airflow.configuration
|
||||
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
62 | # airflow.configuration
|
||||
63 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
| ^^^ AIR301
|
||||
|
|
||||
= help: Use `airflow.configuration.conf.get` instead
|
||||
|
||||
AIR301_names.py:60:6: AIR301 `airflow.configuration.getboolean` is removed in Airflow 3.0
|
||||
AIR301_names.py:63:6: AIR301 `airflow.configuration.getboolean` is removed in Airflow 3.0
|
||||
|
|
||||
59 | # airflow.configuration
|
||||
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
62 | # airflow.configuration
|
||||
63 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
| ^^^^^^^^^^ AIR301
|
||||
|
|
||||
= help: Use `airflow.configuration.conf.getboolean` instead
|
||||
|
||||
AIR301_names.py:60:18: AIR301 `airflow.configuration.getfloat` is removed in Airflow 3.0
|
||||
AIR301_names.py:63:18: AIR301 `airflow.configuration.getfloat` is removed in Airflow 3.0
|
||||
|
|
||||
59 | # airflow.configuration
|
||||
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
62 | # airflow.configuration
|
||||
63 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
| ^^^^^^^^ AIR301
|
||||
|
|
||||
= help: Use `airflow.configuration.conf.getfloat` instead
|
||||
|
||||
AIR301_names.py:60:28: AIR301 `airflow.configuration.getint` is removed in Airflow 3.0
|
||||
AIR301_names.py:63:28: AIR301 `airflow.configuration.getint` is removed in Airflow 3.0
|
||||
|
|
||||
59 | # airflow.configuration
|
||||
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
62 | # airflow.configuration
|
||||
63 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
| ^^^^^^ AIR301
|
||||
|
|
||||
= help: Use `airflow.configuration.conf.getint` instead
|
||||
|
||||
AIR301_names.py:60:36: AIR301 `airflow.configuration.has_option` is removed in Airflow 3.0
|
||||
AIR301_names.py:63:36: AIR301 `airflow.configuration.has_option` is removed in Airflow 3.0
|
||||
|
|
||||
59 | # airflow.configuration
|
||||
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
62 | # airflow.configuration
|
||||
63 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
| ^^^^^^^^^^ AIR301
|
||||
|
|
||||
= help: Use `airflow.configuration.conf.has_option` instead
|
||||
|
||||
AIR301_names.py:60:48: AIR301 `airflow.configuration.remove_option` is removed in Airflow 3.0
|
||||
AIR301_names.py:63:48: AIR301 `airflow.configuration.remove_option` is removed in Airflow 3.0
|
||||
|
|
||||
59 | # airflow.configuration
|
||||
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
62 | # airflow.configuration
|
||||
63 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
| ^^^^^^^^^^^^^ AIR301
|
||||
|
|
||||
= help: Use `airflow.configuration.conf.remove_option` instead
|
||||
|
||||
AIR301_names.py:60:63: AIR301 `airflow.configuration.as_dict` is removed in Airflow 3.0
|
||||
AIR301_names.py:63:63: AIR301 `airflow.configuration.as_dict` is removed in Airflow 3.0
|
||||
|
|
||||
59 | # airflow.configuration
|
||||
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
62 | # airflow.configuration
|
||||
63 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
| ^^^^^^^ AIR301
|
||||
|
|
||||
= help: Use `airflow.configuration.conf.as_dict` instead
|
||||
|
||||
AIR301_names.py:60:72: AIR301 `airflow.configuration.set` is removed in Airflow 3.0
|
||||
AIR301_names.py:63:72: AIR301 `airflow.configuration.set` is removed in Airflow 3.0
|
||||
|
|
||||
59 | # airflow.configuration
|
||||
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
62 | # airflow.configuration
|
||||
63 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
|
||||
| ^^^ AIR301
|
||||
|
|
||||
= help: Use `airflow.configuration.conf.set` instead
|
||||
|
||||
AIR301_names.py:64:1: AIR301 `airflow.contrib.aws_athena_hook.AWSAthenaHook` is removed in Airflow 3.0
|
||||
AIR301_names.py:67:1: AIR301 `airflow.contrib.aws_athena_hook.AWSAthenaHook` is removed in Airflow 3.0; The whole `airflow.contrib` module has been removed.
|
||||
|
|
||||
63 | # airflow.contrib.*
|
||||
64 | AWSAthenaHook()
|
||||
66 | # airflow.contrib.*
|
||||
67 | AWSAthenaHook()
|
||||
| ^^^^^^^^^^^^^ AIR301
|
||||
|
|
||||
= help: The whole `airflow.contrib` module has been removed.
|
||||
|
||||
AIR301_names.py:68:1: AIR301 `airflow.datasets.DatasetAliasEvent` is removed in Airflow 3.0
|
||||
AIR301_names.py:71:1: AIR301 `airflow.datasets.DatasetAliasEvent` is removed in Airflow 3.0
|
||||
|
|
||||
67 | # airflow.datasets
|
||||
68 | DatasetAliasEvent()
|
||||
70 | # airflow.datasets
|
||||
71 | DatasetAliasEvent()
|
||||
| ^^^^^^^^^^^^^^^^^ AIR301
|
||||
|
|
||||
|
||||
AIR301_names.py:72:1: AIR301 `airflow.hooks.base_hook.BaseHook` is removed in Airflow 3.0
|
||||
AIR301_names.py:75:1: AIR301 `airflow.hooks.base_hook.BaseHook` is removed in Airflow 3.0
|
||||
|
|
||||
71 | # airflow.hooks
|
||||
72 | BaseHook()
|
||||
74 | # airflow.hooks
|
||||
75 | BaseHook()
|
||||
| ^^^^^^^^ AIR301
|
||||
|
|
||||
= help: Use `airflow.hooks.base.BaseHook` instead
|
||||
|
||||
AIR301_names.py:76:1: AIR301 `airflow.operators.subdag.SubDagOperator` is removed in Airflow 3.0
|
||||
AIR301_names.py:79:1: AIR301 `airflow.operators.subdag.SubDagOperator` is removed in Airflow 3.0; The whole `airflow.subdag` module has been removed.
|
||||
|
|
||||
75 | # airflow.operators.subdag.*
|
||||
76 | SubDagOperator()
|
||||
78 | # airflow.operators.subdag.*
|
||||
79 | SubDagOperator()
|
||||
| ^^^^^^^^^^^^^^ AIR301
|
||||
80 |
|
||||
81 | # airflow.providers.mysql
|
||||
|
|
||||
= help: The whole `airflow.subdag` module has been removed.
|
||||
|
||||
AIR301_names.py:85:1: AIR301 `airflow.sensors.base_sensor_operator.BaseSensorOperator` is removed in Airflow 3.0
|
||||
AIR301_names.py:82:7: AIR301 `airflow.providers.mysql.datasets.mysql.sanitize_uri` is removed in Airflow 3.0
|
||||
|
|
||||
84 | # airflow.sensors.base_sensor_operator
|
||||
85 | BaseSensorOperator()
|
||||
81 | # airflow.providers.mysql
|
||||
82 | mysql.sanitize_uri
|
||||
| ^^^^^^^^^^^^ AIR301
|
||||
83 |
|
||||
84 | # airflow.providers.postgres
|
||||
|
|
||||
= help: Use `airflow.providers.mysql.assets.mysql.sanitize_uri` instead
|
||||
|
||||
AIR301_names.py:85:10: AIR301 `airflow.providers.postgres.datasets.postgres.sanitize_uri` is removed in Airflow 3.0
|
||||
|
|
||||
84 | # airflow.providers.postgres
|
||||
85 | postgres.sanitize_uri
|
||||
| ^^^^^^^^^^^^ AIR301
|
||||
86 |
|
||||
87 | # airflow.providers.trino
|
||||
|
|
||||
= help: Use `airflow.providers.postgres.assets.postgres.sanitize_uri` instead
|
||||
|
||||
AIR301_names.py:88:7: AIR301 `airflow.providers.trino.datasets.trino.sanitize_uri` is removed in Airflow 3.0
|
||||
|
|
||||
87 | # airflow.providers.trino
|
||||
88 | trino.sanitize_uri
|
||||
| ^^^^^^^^^^^^ AIR301
|
||||
89 |
|
||||
90 | # airflow.secrets
|
||||
|
|
||||
= help: Use `airflow.providers.trino.assets.trino.sanitize_uri` instead
|
||||
|
||||
AIR301_names.py:96:1: AIR301 `airflow.sensors.base_sensor_operator.BaseSensorOperator` is removed in Airflow 3.0
|
||||
|
|
||||
95 | # airflow.sensors.base_sensor_operator
|
||||
96 | BaseSensorOperator()
|
||||
| ^^^^^^^^^^^^^^^^^^ AIR301
|
||||
|
|
||||
= help: Use `airflow.sdk.bases.sensor.BaseSensorOperator` instead
|
||||
|
||||
AIR301_names.py:89:1: AIR301 `airflow.triggers.external_task.TaskStateTrigger` is removed in Airflow 3.0
|
||||
|
|
||||
88 | # airflow.triggers.external_task
|
||||
89 | TaskStateTrigger()
|
||||
| ^^^^^^^^^^^^^^^^ AIR301
|
||||
90 |
|
||||
91 | # airflow.utils.date
|
||||
|
|
||||
|
||||
AIR301_names.py:92:7: AIR301 `airflow.utils.dates.date_range` is removed in Airflow 3.0
|
||||
|
|
||||
91 | # airflow.utils.date
|
||||
92 | dates.date_range
|
||||
| ^^^^^^^^^^ AIR301
|
||||
93 | dates.days_ago
|
||||
|
|
||||
|
||||
AIR301_names.py:93:7: AIR301 `airflow.utils.dates.days_ago` is removed in Airflow 3.0
|
||||
|
|
||||
91 | # airflow.utils.date
|
||||
92 | dates.date_range
|
||||
93 | dates.days_ago
|
||||
| ^^^^^^^^ AIR301
|
||||
94 |
|
||||
95 | date_range
|
||||
|
|
||||
= help: Use `pendulum.today('UTC').add(days=-N, ...)` instead
|
||||
|
||||
AIR301_names.py:95:1: AIR301 `airflow.utils.dates.date_range` is removed in Airflow 3.0
|
||||
|
|
||||
93 | dates.days_ago
|
||||
94 |
|
||||
95 | date_range
|
||||
| ^^^^^^^^^^ AIR301
|
||||
96 | days_ago
|
||||
97 | infer_time_unit
|
||||
|
|
||||
|
||||
AIR301_names.py:96:1: AIR301 `airflow.utils.dates.days_ago` is removed in Airflow 3.0
|
||||
|
|
||||
95 | date_range
|
||||
96 | days_ago
|
||||
| ^^^^^^^^ AIR301
|
||||
97 | infer_time_unit
|
||||
98 | parse_execution_date
|
||||
|
|
||||
= help: Use `pendulum.today('UTC').add(days=-N, ...)` instead
|
||||
|
||||
AIR301_names.py:97:1: AIR301 `airflow.utils.dates.infer_time_unit` is removed in Airflow 3.0
|
||||
|
|
||||
95 | date_range
|
||||
96 | days_ago
|
||||
97 | infer_time_unit
|
||||
| ^^^^^^^^^^^^^^^ AIR301
|
||||
98 | parse_execution_date
|
||||
99 | round_time
|
||||
|
|
||||
|
||||
AIR301_names.py:98:1: AIR301 `airflow.utils.dates.parse_execution_date` is removed in Airflow 3.0
|
||||
AIR301_names.py:100:1: AIR301 `airflow.triggers.external_task.TaskStateTrigger` is removed in Airflow 3.0
|
||||
|
|
||||
96 | days_ago
|
||||
97 | infer_time_unit
|
||||
98 | parse_execution_date
|
||||
| ^^^^^^^^^^^^^^^^^^^^ AIR301
|
||||
99 | round_time
|
||||
100 | scale_time_units
|
||||
|
|
||||
|
||||
AIR301_names.py:99:1: AIR301 `airflow.utils.dates.round_time` is removed in Airflow 3.0
|
||||
|
|
||||
97 | infer_time_unit
|
||||
98 | parse_execution_date
|
||||
99 | round_time
|
||||
| ^^^^^^^^^^ AIR301
|
||||
100 | scale_time_units
|
||||
|
|
||||
|
||||
AIR301_names.py:100:1: AIR301 `airflow.utils.dates.scale_time_units` is removed in Airflow 3.0
|
||||
|
|
||||
98 | parse_execution_date
|
||||
99 | round_time
|
||||
100 | scale_time_units
|
||||
99 | # airflow.triggers.external_task
|
||||
100 | TaskStateTrigger()
|
||||
| ^^^^^^^^^^^^^^^^ AIR301
|
||||
101 |
|
||||
102 | # This one was not deprecated.
|
||||
102 | # airflow.utils.date
|
||||
|
|
||||
|
||||
AIR301_names.py:107:1: AIR301 `airflow.utils.dag_cycle_tester.test_cycle` is removed in Airflow 3.0
|
||||
AIR301_names.py:103:7: AIR301 `airflow.utils.dates.date_range` is removed in Airflow 3.0
|
||||
|
|
||||
106 | # airflow.utils.dag_cycle_tester
|
||||
107 | test_cycle
|
||||
102 | # airflow.utils.date
|
||||
103 | dates.date_range
|
||||
| ^^^^^^^^^^ AIR301
|
||||
104 | dates.days_ago
|
||||
|
|
||||
|
||||
AIR301_names.py:104:7: AIR301 `airflow.utils.dates.days_ago` is removed in Airflow 3.0
|
||||
|
|
||||
102 | # airflow.utils.date
|
||||
103 | dates.date_range
|
||||
104 | dates.days_ago
|
||||
| ^^^^^^^^ AIR301
|
||||
105 |
|
||||
106 | date_range
|
||||
|
|
||||
= help: Use `pendulum.today('UTC').add(days=-N, ...)` instead
|
||||
|
||||
AIR301_names.py:106:1: AIR301 `airflow.utils.dates.date_range` is removed in Airflow 3.0
|
||||
|
|
||||
104 | dates.days_ago
|
||||
105 |
|
||||
106 | date_range
|
||||
| ^^^^^^^^^^ AIR301
|
||||
107 | days_ago
|
||||
108 | infer_time_unit
|
||||
|
|
||||
|
||||
AIR301_names.py:107:1: AIR301 `airflow.utils.dates.days_ago` is removed in Airflow 3.0
|
||||
|
|
||||
106 | date_range
|
||||
107 | days_ago
|
||||
| ^^^^^^^^ AIR301
|
||||
108 | infer_time_unit
|
||||
109 | parse_execution_date
|
||||
|
|
||||
= help: Use `pendulum.today('UTC').add(days=-N, ...)` instead
|
||||
|
||||
AIR301_names.py:108:1: AIR301 `airflow.utils.dates.infer_time_unit` is removed in Airflow 3.0
|
||||
|
|
||||
106 | date_range
|
||||
107 | days_ago
|
||||
108 | infer_time_unit
|
||||
| ^^^^^^^^^^^^^^^ AIR301
|
||||
109 | parse_execution_date
|
||||
110 | round_time
|
||||
|
|
||||
|
||||
AIR301_names.py:109:1: AIR301 `airflow.utils.dates.parse_execution_date` is removed in Airflow 3.0
|
||||
|
|
||||
107 | days_ago
|
||||
108 | infer_time_unit
|
||||
109 | parse_execution_date
|
||||
| ^^^^^^^^^^^^^^^^^^^^ AIR301
|
||||
110 | round_time
|
||||
111 | scale_time_units
|
||||
|
|
||||
|
||||
AIR301_names.py:110:1: AIR301 `airflow.utils.dates.round_time` is removed in Airflow 3.0
|
||||
|
|
||||
108 | infer_time_unit
|
||||
109 | parse_execution_date
|
||||
110 | round_time
|
||||
| ^^^^^^^^^^ AIR301
|
||||
111 | scale_time_units
|
||||
|
|
||||
|
||||
AIR301_names.py:111:1: AIR301 `airflow.utils.dates.scale_time_units` is removed in Airflow 3.0
|
||||
|
|
||||
109 | parse_execution_date
|
||||
110 | round_time
|
||||
111 | scale_time_units
|
||||
| ^^^^^^^^^^^^^^^^ AIR301
|
||||
112 |
|
||||
113 | # This one was not deprecated.
|
||||
|
|
||||
|
||||
AIR301_names.py:118:1: AIR301 `airflow.utils.dag_cycle_tester.test_cycle` is removed in Airflow 3.0
|
||||
|
|
||||
117 | # airflow.utils.dag_cycle_tester
|
||||
118 | test_cycle
|
||||
| ^^^^^^^^^^ AIR301
|
||||
|
|
||||
|
||||
AIR301_names.py:111:1: AIR301 `airflow.utils.db.create_session` is removed in Airflow 3.0
|
||||
AIR301_names.py:122:1: AIR301 `airflow.utils.db.create_session` is removed in Airflow 3.0
|
||||
|
|
||||
110 | # airflow.utils.db
|
||||
111 | create_session
|
||||
121 | # airflow.utils.db
|
||||
122 | create_session
|
||||
| ^^^^^^^^^^^^^^ AIR301
|
||||
112 |
|
||||
113 | # airflow.utils.decorators
|
||||
123 |
|
||||
124 | # airflow.utils.decorators
|
||||
|
|
||||
|
||||
AIR301_names.py:114:1: AIR301 `airflow.utils.decorators.apply_defaults` is removed in Airflow 3.0
|
||||
AIR301_names.py:125:1: AIR301 `airflow.utils.decorators.apply_defaults` is removed in Airflow 3.0; `apply_defaults` is now unconditionally done and can be safely removed.
|
||||
|
|
||||
113 | # airflow.utils.decorators
|
||||
114 | apply_defaults
|
||||
124 | # airflow.utils.decorators
|
||||
125 | apply_defaults
|
||||
| ^^^^^^^^^^^^^^ AIR301
|
||||
115 |
|
||||
116 | # airflow.utils.file
|
||||
126 |
|
||||
127 | # airflow.utils.file
|
||||
|
|
||||
= help: `apply_defaults` is now unconditionally done and can be safely removed.
|
||||
|
||||
AIR301_names.py:117:1: AIR301 `airflow.utils.file.TemporaryDirectory` is removed in Airflow 3.0
|
||||
AIR301_names.py:128:1: AIR301 `airflow.utils.file.TemporaryDirectory` is removed in Airflow 3.0
|
||||
|
|
||||
116 | # airflow.utils.file
|
||||
117 | TemporaryDirectory()
|
||||
127 | # airflow.utils.file
|
||||
128 | TemporaryDirectory()
|
||||
| ^^^^^^^^^^^^^^^^^^ AIR301
|
||||
118 | mkdirs
|
||||
129 | mkdirs
|
||||
|
|
||||
= help: Use `tempfile.TemporaryDirectory` instead
|
||||
|
||||
AIR301_names.py:118:1: AIR301 `airflow.utils.file.mkdirs` is removed in Airflow 3.0
|
||||
AIR301_names.py:129:1: AIR301 `airflow.utils.file.mkdirs` is removed in Airflow 3.0
|
||||
|
|
||||
116 | # airflow.utils.file
|
||||
117 | TemporaryDirectory()
|
||||
118 | mkdirs
|
||||
127 | # airflow.utils.file
|
||||
128 | TemporaryDirectory()
|
||||
129 | mkdirs
|
||||
| ^^^^^^ AIR301
|
||||
119 |
|
||||
120 | # airflow.utils.helpers
|
||||
130 |
|
||||
131 | # airflow.utils.helpers
|
||||
|
|
||||
= help: Use `pathlib.Path({path}).mkdir` instead
|
||||
|
||||
AIR301_names.py:121:1: AIR301 [*] `airflow.utils.helpers.chain` is removed in Airflow 3.0
|
||||
AIR301_names.py:132:1: AIR301 `airflow.utils.helpers.chain` is removed in Airflow 3.0
|
||||
|
|
||||
120 | # airflow.utils.helpers
|
||||
121 | helper_chain
|
||||
131 | # airflow.utils.helpers
|
||||
132 | helper_chain
|
||||
| ^^^^^^^^^^^^ AIR301
|
||||
122 | helper_cross_downstream
|
||||
133 | helper_cross_downstream
|
||||
|
|
||||
= help: Use `airflow.sdk.chain` instead
|
||||
|
||||
ℹ Safe fix
|
||||
48 48 | from airflow.utils.trigger_rule import TriggerRule
|
||||
49 49 | from airflow.www.auth import has_access
|
||||
50 50 | from airflow.www.utils import get_sensitive_variables_fields, should_hide_value_for_key
|
||||
51 |+from airflow.sdk import chain
|
||||
51 52 |
|
||||
52 53 | # airflow root
|
||||
53 54 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
--------------------------------------------------------------------------------
|
||||
118 119 | mkdirs
|
||||
119 120 |
|
||||
120 121 | # airflow.utils.helpers
|
||||
121 |-helper_chain
|
||||
122 |+chain
|
||||
122 123 | helper_cross_downstream
|
||||
123 124 |
|
||||
124 125 | # airflow.utils.log
|
||||
|
||||
AIR301_names.py:122:1: AIR301 [*] `airflow.utils.helpers.cross_downstream` is removed in Airflow 3.0
|
||||
AIR301_names.py:133:1: AIR301 `airflow.utils.helpers.cross_downstream` is removed in Airflow 3.0
|
||||
|
|
||||
120 | # airflow.utils.helpers
|
||||
121 | helper_chain
|
||||
122 | helper_cross_downstream
|
||||
131 | # airflow.utils.helpers
|
||||
132 | helper_chain
|
||||
133 | helper_cross_downstream
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ AIR301
|
||||
123 |
|
||||
124 | # airflow.utils.log
|
||||
134 |
|
||||
135 | # airflow.utils.log
|
||||
|
|
||||
= help: Use `airflow.sdk.cross_downstream` instead
|
||||
|
||||
ℹ Safe fix
|
||||
48 48 | from airflow.utils.trigger_rule import TriggerRule
|
||||
49 49 | from airflow.www.auth import has_access
|
||||
50 50 | from airflow.www.utils import get_sensitive_variables_fields, should_hide_value_for_key
|
||||
51 |+from airflow.sdk import cross_downstream
|
||||
51 52 |
|
||||
52 53 | # airflow root
|
||||
53 54 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
--------------------------------------------------------------------------------
|
||||
119 120 |
|
||||
120 121 | # airflow.utils.helpers
|
||||
121 122 | helper_chain
|
||||
122 |-helper_cross_downstream
|
||||
123 |+cross_downstream
|
||||
123 124 |
|
||||
124 125 | # airflow.utils.log
|
||||
125 126 | secrets_masker
|
||||
|
||||
AIR301_names.py:125:1: AIR301 `airflow.utils.log.secrets_masker` is removed in Airflow 3.0
|
||||
AIR301_names.py:136:1: AIR301 `airflow.utils.log.secrets_masker` is removed in Airflow 3.0
|
||||
|
|
||||
124 | # airflow.utils.log
|
||||
125 | secrets_masker
|
||||
135 | # airflow.utils.log
|
||||
136 | secrets_masker
|
||||
| ^^^^^^^^^^^^^^ AIR301
|
||||
126 |
|
||||
127 | # airflow.utils.state
|
||||
137 |
|
||||
138 | # airflow.utils.state
|
||||
|
|
||||
= help: Use `airflow.sdk.execution_time.secrets_masker` instead
|
||||
|
||||
AIR301_names.py:128:1: AIR301 `airflow.utils.state.SHUTDOWN` is removed in Airflow 3.0
|
||||
AIR301_names.py:139:1: AIR301 `airflow.utils.state.SHUTDOWN` is removed in Airflow 3.0
|
||||
|
|
||||
127 | # airflow.utils.state
|
||||
128 | SHUTDOWN
|
||||
138 | # airflow.utils.state
|
||||
139 | SHUTDOWN
|
||||
| ^^^^^^^^ AIR301
|
||||
129 | terminating_states
|
||||
140 | terminating_states
|
||||
|
|
||||
|
||||
AIR301_names.py:129:1: AIR301 `airflow.utils.state.terminating_states` is removed in Airflow 3.0
|
||||
AIR301_names.py:140:1: AIR301 `airflow.utils.state.terminating_states` is removed in Airflow 3.0
|
||||
|
|
||||
127 | # airflow.utils.state
|
||||
128 | SHUTDOWN
|
||||
129 | terminating_states
|
||||
138 | # airflow.utils.state
|
||||
139 | SHUTDOWN
|
||||
140 | terminating_states
|
||||
| ^^^^^^^^^^^^^^^^^^ AIR301
|
||||
130 |
|
||||
131 | # airflow.utils.trigger_rule
|
||||
141 |
|
||||
142 | # airflow.utils.trigger_rule
|
||||
|
|
||||
|
||||
AIR301_names.py:132:13: AIR301 `airflow.utils.trigger_rule.TriggerRule.DUMMY` is removed in Airflow 3.0
|
||||
AIR301_names.py:143:13: AIR301 `airflow.utils.trigger_rule.TriggerRule.DUMMY` is removed in Airflow 3.0
|
||||
|
|
||||
131 | # airflow.utils.trigger_rule
|
||||
132 | TriggerRule.DUMMY
|
||||
142 | # airflow.utils.trigger_rule
|
||||
143 | TriggerRule.DUMMY
|
||||
| ^^^^^ AIR301
|
||||
133 | TriggerRule.NONE_FAILED_OR_SKIPPED
|
||||
144 | TriggerRule.NONE_FAILED_OR_SKIPPED
|
||||
|
|
||||
|
||||
AIR301_names.py:133:13: AIR301 `airflow.utils.trigger_rule.TriggerRule.NONE_FAILED_OR_SKIPPED` is removed in Airflow 3.0
|
||||
AIR301_names.py:144:13: AIR301 `airflow.utils.trigger_rule.TriggerRule.NONE_FAILED_OR_SKIPPED` is removed in Airflow 3.0
|
||||
|
|
||||
131 | # airflow.utils.trigger_rule
|
||||
132 | TriggerRule.DUMMY
|
||||
133 | TriggerRule.NONE_FAILED_OR_SKIPPED
|
||||
142 | # airflow.utils.trigger_rule
|
||||
143 | TriggerRule.DUMMY
|
||||
144 | TriggerRule.NONE_FAILED_OR_SKIPPED
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ AIR301
|
||||
|
|
||||
|
||||
AIR301_names.py:137:1: AIR301 `airflow.www.auth.has_access` is removed in Airflow 3.0
|
||||
AIR301_names.py:148:1: AIR301 `airflow.www.auth.has_access` is removed in Airflow 3.0
|
||||
|
|
||||
136 | # airflow.www.auth
|
||||
137 | has_access
|
||||
147 | # airflow.www.auth
|
||||
148 | has_access
|
||||
| ^^^^^^^^^^ AIR301
|
||||
138 |
|
||||
139 | # airflow.www.utils
|
||||
149 |
|
||||
150 | # airflow.www.utils
|
||||
|
|
||||
= help: Use `airflow.www.auth.has_access_*` instead
|
||||
|
||||
AIR301_names.py:140:1: AIR301 `airflow.www.utils.get_sensitive_variables_fields` is removed in Airflow 3.0
|
||||
AIR301_names.py:151:1: AIR301 `airflow.www.utils.get_sensitive_variables_fields` is removed in Airflow 3.0
|
||||
|
|
||||
139 | # airflow.www.utils
|
||||
140 | get_sensitive_variables_fields
|
||||
150 | # airflow.www.utils
|
||||
151 | get_sensitive_variables_fields
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR301
|
||||
141 | should_hide_value_for_key
|
||||
152 | should_hide_value_for_key
|
||||
|
|
||||
= help: Use `airflow.utils.log.secrets_masker.get_sensitive_variables_fields` instead
|
||||
|
||||
AIR301_names.py:141:1: AIR301 `airflow.www.utils.should_hide_value_for_key` is removed in Airflow 3.0
|
||||
AIR301_names.py:152:1: AIR301 `airflow.www.utils.should_hide_value_for_key` is removed in Airflow 3.0
|
||||
|
|
||||
139 | # airflow.www.utils
|
||||
140 | get_sensitive_variables_fields
|
||||
141 | should_hide_value_for_key
|
||||
150 | # airflow.www.utils
|
||||
151 | get_sensitive_variables_fields
|
||||
152 | should_hide_value_for_key
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ AIR301
|
||||
142 |
|
||||
143 | # airflow.operators.python
|
||||
153 |
|
||||
154 | # airflow.operators.python
|
||||
|
|
||||
= help: Use `airflow.utils.log.secrets_masker.should_hide_value_for_key` instead
|
||||
|
||||
AIR301_names.py:146:1: AIR301 `airflow.operators.python.get_current_context` is removed in Airflow 3.0
|
||||
AIR301_names.py:157:1: AIR301 `airflow.operators.python.get_current_context` is removed in Airflow 3.0
|
||||
|
|
||||
144 | from airflow.operators.python import get_current_context
|
||||
145 |
|
||||
146 | get_current_context()
|
||||
155 | from airflow.operators.python import get_current_context
|
||||
156 |
|
||||
157 | get_current_context()
|
||||
| ^^^^^^^^^^^^^^^^^^^ AIR301
|
||||
147 |
|
||||
148 | # airflow.providers.mysql
|
||||
|
|
||||
= help: Use `airflow.sdk.get_current_context` instead
|
||||
|
||||
AIR301_names.py:151:1: AIR301 `airflow.providers.mysql.datasets.mysql.sanitize_uri` is removed in Airflow 3.0
|
||||
|
|
||||
149 | from airflow.providers.mysql.datasets.mysql import sanitize_uri
|
||||
150 |
|
||||
151 | sanitize_uri
|
||||
| ^^^^^^^^^^^^ AIR301
|
||||
152 |
|
||||
153 | # airflow.providers.postgres
|
||||
|
|
||||
= help: Use `airflow.providers.mysql.assets.mysql.sanitize_uri` instead
|
||||
|
||||
AIR301_names.py:156:1: AIR301 `airflow.providers.postgres.datasets.postgres.sanitize_uri` is removed in Airflow 3.0
|
||||
|
|
||||
154 | from airflow.providers.postgres.datasets.postgres import sanitize_uri
|
||||
155 |
|
||||
156 | sanitize_uri
|
||||
| ^^^^^^^^^^^^ AIR301
|
||||
157 |
|
||||
158 | # airflow.providers.trino
|
||||
|
|
||||
= help: Use `airflow.providers.postgres.assets.postgres.sanitize_uri` instead
|
||||
|
||||
AIR301_names.py:161:1: AIR301 `airflow.providers.trino.datasets.trino.sanitize_uri` is removed in Airflow 3.0
|
||||
|
|
||||
159 | from airflow.providers.trino.datasets.trino import sanitize_uri
|
||||
160 |
|
||||
161 | sanitize_uri
|
||||
| ^^^^^^^^^^^^ AIR301
|
||||
|
|
||||
= help: Use `airflow.providers.trino.assets.trino.sanitize_uri` instead
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/airflow/mod.rs
|
||||
---
|
||||
|
||||
@@ -1,394 +1,337 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/airflow/mod.rs
|
||||
---
|
||||
AIR311_names.py:27:1: AIR311 [*] `airflow.Dataset` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:23:1: AIR311 [*] `airflow.Dataset` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
26 | # airflow
|
||||
27 | DatasetFromRoot()
|
||||
22 | # airflow
|
||||
23 | DatasetFromRoot()
|
||||
| ^^^^^^^^^^^^^^^ AIR311
|
||||
28 |
|
||||
29 | # airflow.datasets
|
||||
24 |
|
||||
25 | # airflow.datasets
|
||||
|
|
||||
= help: Use `airflow.sdk.Asset` instead
|
||||
|
||||
ℹ Safe fix
|
||||
22 22 | from airflow.models.dag import DAG as DAGFromDag
|
||||
23 23 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
24 24 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
25 |+from airflow.sdk import Asset
|
||||
25 26 |
|
||||
26 27 | # airflow
|
||||
27 |-DatasetFromRoot()
|
||||
28 |+Asset()
|
||||
28 29 |
|
||||
29 30 | # airflow.datasets
|
||||
30 31 | Dataset()
|
||||
18 18 | from airflow.models.dag import DAG as DAGFromDag
|
||||
19 19 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
20 20 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
21 |+from airflow.sdk import Asset
|
||||
21 22 |
|
||||
22 23 | # airflow
|
||||
23 |-DatasetFromRoot()
|
||||
24 |+Asset()
|
||||
24 25 |
|
||||
25 26 | # airflow.datasets
|
||||
26 27 | Dataset()
|
||||
|
||||
AIR311_names.py:30:1: AIR311 [*] `airflow.datasets.Dataset` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:26:1: AIR311 [*] `airflow.datasets.Dataset` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
29 | # airflow.datasets
|
||||
30 | Dataset()
|
||||
25 | # airflow.datasets
|
||||
26 | Dataset()
|
||||
| ^^^^^^^ AIR311
|
||||
31 | DatasetAlias()
|
||||
32 | DatasetAll()
|
||||
27 | DatasetAlias()
|
||||
28 | DatasetAll()
|
||||
|
|
||||
= help: Use `airflow.sdk.Asset` instead
|
||||
|
||||
ℹ Safe fix
|
||||
22 22 | from airflow.models.dag import DAG as DAGFromDag
|
||||
23 23 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
24 24 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
25 |+from airflow.sdk import Asset
|
||||
25 26 |
|
||||
26 27 | # airflow
|
||||
27 28 | DatasetFromRoot()
|
||||
28 29 |
|
||||
29 30 | # airflow.datasets
|
||||
30 |-Dataset()
|
||||
31 |+Asset()
|
||||
31 32 | DatasetAlias()
|
||||
32 33 | DatasetAll()
|
||||
33 34 | DatasetAny()
|
||||
18 18 | from airflow.models.dag import DAG as DAGFromDag
|
||||
19 19 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
20 20 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
21 |+from airflow.sdk import Asset
|
||||
21 22 |
|
||||
22 23 | # airflow
|
||||
23 24 | DatasetFromRoot()
|
||||
24 25 |
|
||||
25 26 | # airflow.datasets
|
||||
26 |-Dataset()
|
||||
27 |+Asset()
|
||||
27 28 | DatasetAlias()
|
||||
28 29 | DatasetAll()
|
||||
29 30 | DatasetAny()
|
||||
|
||||
AIR311_names.py:31:1: AIR311 [*] `airflow.datasets.DatasetAlias` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:27:1: AIR311 [*] `airflow.datasets.DatasetAlias` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
29 | # airflow.datasets
|
||||
30 | Dataset()
|
||||
31 | DatasetAlias()
|
||||
25 | # airflow.datasets
|
||||
26 | Dataset()
|
||||
27 | DatasetAlias()
|
||||
| ^^^^^^^^^^^^ AIR311
|
||||
32 | DatasetAll()
|
||||
33 | DatasetAny()
|
||||
28 | DatasetAll()
|
||||
29 | DatasetAny()
|
||||
|
|
||||
= help: Use `airflow.sdk.AssetAlias` instead
|
||||
|
||||
ℹ Safe fix
|
||||
22 22 | from airflow.models.dag import DAG as DAGFromDag
|
||||
23 23 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
24 24 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
25 |+from airflow.sdk import AssetAlias
|
||||
25 26 |
|
||||
26 27 | # airflow
|
||||
27 28 | DatasetFromRoot()
|
||||
28 29 |
|
||||
29 30 | # airflow.datasets
|
||||
30 31 | Dataset()
|
||||
31 |-DatasetAlias()
|
||||
32 |+AssetAlias()
|
||||
32 33 | DatasetAll()
|
||||
33 34 | DatasetAny()
|
||||
34 35 | Metadata()
|
||||
18 18 | from airflow.models.dag import DAG as DAGFromDag
|
||||
19 19 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
20 20 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
21 |+from airflow.sdk import AssetAlias
|
||||
21 22 |
|
||||
22 23 | # airflow
|
||||
23 24 | DatasetFromRoot()
|
||||
24 25 |
|
||||
25 26 | # airflow.datasets
|
||||
26 27 | Dataset()
|
||||
27 |-DatasetAlias()
|
||||
28 |+AssetAlias()
|
||||
28 29 | DatasetAll()
|
||||
29 30 | DatasetAny()
|
||||
30 31 | Metadata()
|
||||
|
||||
AIR311_names.py:32:1: AIR311 [*] `airflow.datasets.DatasetAll` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:28:1: AIR311 [*] `airflow.datasets.DatasetAll` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
30 | Dataset()
|
||||
31 | DatasetAlias()
|
||||
32 | DatasetAll()
|
||||
26 | Dataset()
|
||||
27 | DatasetAlias()
|
||||
28 | DatasetAll()
|
||||
| ^^^^^^^^^^ AIR311
|
||||
33 | DatasetAny()
|
||||
34 | Metadata()
|
||||
29 | DatasetAny()
|
||||
30 | Metadata()
|
||||
|
|
||||
= help: Use `airflow.sdk.AssetAll` instead
|
||||
|
||||
ℹ Safe fix
|
||||
22 22 | from airflow.models.dag import DAG as DAGFromDag
|
||||
23 23 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
24 24 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
25 |+from airflow.sdk import AssetAll
|
||||
25 26 |
|
||||
26 27 | # airflow
|
||||
27 28 | DatasetFromRoot()
|
||||
18 18 | from airflow.models.dag import DAG as DAGFromDag
|
||||
19 19 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
20 20 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
21 |+from airflow.sdk import AssetAll
|
||||
21 22 |
|
||||
22 23 | # airflow
|
||||
23 24 | DatasetFromRoot()
|
||||
--------------------------------------------------------------------------------
|
||||
29 30 | # airflow.datasets
|
||||
30 31 | Dataset()
|
||||
31 32 | DatasetAlias()
|
||||
32 |-DatasetAll()
|
||||
33 |+AssetAll()
|
||||
33 34 | DatasetAny()
|
||||
34 35 | Metadata()
|
||||
35 36 | expand_alias_to_datasets()
|
||||
25 26 | # airflow.datasets
|
||||
26 27 | Dataset()
|
||||
27 28 | DatasetAlias()
|
||||
28 |-DatasetAll()
|
||||
29 |+AssetAll()
|
||||
29 30 | DatasetAny()
|
||||
30 31 | Metadata()
|
||||
31 32 | expand_alias_to_datasets()
|
||||
|
||||
AIR311_names.py:33:1: AIR311 [*] `airflow.datasets.DatasetAny` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:29:1: AIR311 [*] `airflow.datasets.DatasetAny` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
31 | DatasetAlias()
|
||||
32 | DatasetAll()
|
||||
33 | DatasetAny()
|
||||
27 | DatasetAlias()
|
||||
28 | DatasetAll()
|
||||
29 | DatasetAny()
|
||||
| ^^^^^^^^^^ AIR311
|
||||
34 | Metadata()
|
||||
35 | expand_alias_to_datasets()
|
||||
30 | Metadata()
|
||||
31 | expand_alias_to_datasets()
|
||||
|
|
||||
= help: Use `airflow.sdk.AssetAny` instead
|
||||
|
||||
ℹ Safe fix
|
||||
22 22 | from airflow.models.dag import DAG as DAGFromDag
|
||||
23 23 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
24 24 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
25 |+from airflow.sdk import AssetAny
|
||||
25 26 |
|
||||
26 27 | # airflow
|
||||
27 28 | DatasetFromRoot()
|
||||
18 18 | from airflow.models.dag import DAG as DAGFromDag
|
||||
19 19 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
20 20 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
21 |+from airflow.sdk import AssetAny
|
||||
21 22 |
|
||||
22 23 | # airflow
|
||||
23 24 | DatasetFromRoot()
|
||||
--------------------------------------------------------------------------------
|
||||
30 31 | Dataset()
|
||||
31 32 | DatasetAlias()
|
||||
32 33 | DatasetAll()
|
||||
33 |-DatasetAny()
|
||||
34 |+AssetAny()
|
||||
34 35 | Metadata()
|
||||
35 36 | expand_alias_to_datasets()
|
||||
36 37 |
|
||||
26 27 | Dataset()
|
||||
27 28 | DatasetAlias()
|
||||
28 29 | DatasetAll()
|
||||
29 |-DatasetAny()
|
||||
30 |+AssetAny()
|
||||
30 31 | Metadata()
|
||||
31 32 | expand_alias_to_datasets()
|
||||
32 33 |
|
||||
|
||||
AIR311_names.py:34:1: AIR311 `airflow.datasets.metadata.Metadata` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:30:1: AIR311 `airflow.datasets.metadata.Metadata` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
32 | DatasetAll()
|
||||
33 | DatasetAny()
|
||||
34 | Metadata()
|
||||
28 | DatasetAll()
|
||||
29 | DatasetAny()
|
||||
30 | Metadata()
|
||||
| ^^^^^^^^ AIR311
|
||||
35 | expand_alias_to_datasets()
|
||||
31 | expand_alias_to_datasets()
|
||||
|
|
||||
= help: Use `airflow.sdk.Metadata` instead
|
||||
|
||||
AIR311_names.py:35:1: AIR311 [*] `airflow.datasets.expand_alias_to_datasets` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:31:1: AIR311 [*] `airflow.datasets.expand_alias_to_datasets` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
33 | DatasetAny()
|
||||
34 | Metadata()
|
||||
35 | expand_alias_to_datasets()
|
||||
29 | DatasetAny()
|
||||
30 | Metadata()
|
||||
31 | expand_alias_to_datasets()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ AIR311
|
||||
36 |
|
||||
37 | # airflow.decorators
|
||||
32 |
|
||||
33 | # airflow.decorators
|
||||
|
|
||||
= help: Use `airflow.sdk.expand_alias_to_assets` instead
|
||||
|
||||
ℹ Safe fix
|
||||
22 22 | from airflow.models.dag import DAG as DAGFromDag
|
||||
23 23 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
24 24 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
25 |+from airflow.sdk import expand_alias_to_assets
|
||||
25 26 |
|
||||
26 27 | # airflow
|
||||
27 28 | DatasetFromRoot()
|
||||
18 18 | from airflow.models.dag import DAG as DAGFromDag
|
||||
19 19 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
20 20 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
21 |+from airflow.sdk import expand_alias_to_assets
|
||||
21 22 |
|
||||
22 23 | # airflow
|
||||
23 24 | DatasetFromRoot()
|
||||
--------------------------------------------------------------------------------
|
||||
32 33 | DatasetAll()
|
||||
33 34 | DatasetAny()
|
||||
34 35 | Metadata()
|
||||
35 |-expand_alias_to_datasets()
|
||||
36 |+expand_alias_to_assets()
|
||||
36 37 |
|
||||
37 38 | # airflow.decorators
|
||||
38 39 | dag()
|
||||
28 29 | DatasetAll()
|
||||
29 30 | DatasetAny()
|
||||
30 31 | Metadata()
|
||||
31 |-expand_alias_to_datasets()
|
||||
32 |+expand_alias_to_assets()
|
||||
32 33 |
|
||||
33 34 | # airflow.decorators
|
||||
34 35 | dag()
|
||||
|
||||
AIR311_names.py:38:1: AIR311 `airflow.decorators.dag` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:34:1: AIR311 `airflow.decorators.dag` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
37 | # airflow.decorators
|
||||
38 | dag()
|
||||
33 | # airflow.decorators
|
||||
34 | dag()
|
||||
| ^^^ AIR311
|
||||
39 | task()
|
||||
40 | task_group()
|
||||
35 | task()
|
||||
36 | task_group()
|
||||
|
|
||||
= help: Use `airflow.sdk.dag` instead
|
||||
|
||||
AIR311_names.py:39:1: AIR311 `airflow.decorators.task` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:35:1: AIR311 `airflow.decorators.task` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
37 | # airflow.decorators
|
||||
38 | dag()
|
||||
39 | task()
|
||||
33 | # airflow.decorators
|
||||
34 | dag()
|
||||
35 | task()
|
||||
| ^^^^ AIR311
|
||||
40 | task_group()
|
||||
41 | setup()
|
||||
36 | task_group()
|
||||
37 | setup()
|
||||
|
|
||||
= help: Use `airflow.sdk.task` instead
|
||||
|
||||
AIR311_names.py:40:1: AIR311 `airflow.decorators.task_group` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:36:1: AIR311 `airflow.decorators.task_group` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
38 | dag()
|
||||
39 | task()
|
||||
40 | task_group()
|
||||
34 | dag()
|
||||
35 | task()
|
||||
36 | task_group()
|
||||
| ^^^^^^^^^^ AIR311
|
||||
41 | setup()
|
||||
42 | teardown()
|
||||
37 | setup()
|
||||
38 | teardown()
|
||||
|
|
||||
= help: Use `airflow.sdk.task_group` instead
|
||||
|
||||
AIR311_names.py:41:1: AIR311 `airflow.decorators.setup` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:37:1: AIR311 `airflow.decorators.setup` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
39 | task()
|
||||
40 | task_group()
|
||||
41 | setup()
|
||||
35 | task()
|
||||
36 | task_group()
|
||||
37 | setup()
|
||||
| ^^^^^ AIR311
|
||||
42 | teardown()
|
||||
38 | teardown()
|
||||
|
|
||||
= help: Use `airflow.sdk.setup` instead
|
||||
|
||||
AIR311_names.py:42:1: AIR311 `airflow.decorators.teardown` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:38:1: AIR311 `airflow.decorators.teardown` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
40 | task_group()
|
||||
41 | setup()
|
||||
42 | teardown()
|
||||
36 | task_group()
|
||||
37 | setup()
|
||||
38 | teardown()
|
||||
| ^^^^^^^^ AIR311
|
||||
43 |
|
||||
44 | # airflow.io
|
||||
39 |
|
||||
40 | # airflow.io
|
||||
|
|
||||
= help: Use `airflow.sdk.teardown` instead
|
||||
|
||||
AIR311_names.py:45:1: AIR311 `airflow.io.path.ObjectStoragePath` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:41:1: AIR311 `airflow.io.path.ObjectStoragePath` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
44 | # airflow.io
|
||||
45 | ObjectStoragePath()
|
||||
40 | # airflow.io
|
||||
41 | ObjectStoragePath()
|
||||
| ^^^^^^^^^^^^^^^^^ AIR311
|
||||
46 | attach()
|
||||
42 | attach()
|
||||
|
|
||||
= help: Use `airflow.sdk.ObjectStoragePath` instead
|
||||
|
||||
AIR311_names.py:46:1: AIR311 `airflow.io.storage.attach` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:42:1: AIR311 `airflow.io.storage.attach` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
44 | # airflow.io
|
||||
45 | ObjectStoragePath()
|
||||
46 | attach()
|
||||
40 | # airflow.io
|
||||
41 | ObjectStoragePath()
|
||||
42 | attach()
|
||||
| ^^^^^^ AIR311
|
||||
47 |
|
||||
48 | # airflow.models
|
||||
43 |
|
||||
44 | # airflow.models
|
||||
|
|
||||
= help: Use `airflow.sdk.io.attach` instead
|
||||
|
||||
AIR311_names.py:49:1: AIR311 `airflow.models.Connection` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:45:1: AIR311 `airflow.models.DAG` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
48 | # airflow.models
|
||||
49 | Connection()
|
||||
| ^^^^^^^^^^ AIR311
|
||||
50 | DAGFromModel()
|
||||
51 | Variable()
|
||||
|
|
||||
= help: Use `airflow.sdk.Connection` instead
|
||||
|
||||
AIR311_names.py:50:1: AIR311 [*] `airflow.models.DAG` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
48 | # airflow.models
|
||||
49 | Connection()
|
||||
50 | DAGFromModel()
|
||||
44 | # airflow.models
|
||||
45 | DAGFromModel()
|
||||
| ^^^^^^^^^^^^ AIR311
|
||||
51 | Variable()
|
||||
46 |
|
||||
47 | # airflow.models.baseoperator
|
||||
|
|
||||
= help: Use `airflow.sdk.DAG` instead
|
||||
|
||||
ℹ Safe fix
|
||||
22 22 | from airflow.models.dag import DAG as DAGFromDag
|
||||
23 23 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
24 24 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
25 |+from airflow.sdk import DAG
|
||||
25 26 |
|
||||
26 27 | # airflow
|
||||
27 28 | DatasetFromRoot()
|
||||
--------------------------------------------------------------------------------
|
||||
47 48 |
|
||||
48 49 | # airflow.models
|
||||
49 50 | Connection()
|
||||
50 |-DAGFromModel()
|
||||
51 |+DAG()
|
||||
51 52 | Variable()
|
||||
52 53 |
|
||||
53 54 | # airflow.models.baseoperator
|
||||
|
||||
AIR311_names.py:51:1: AIR311 `airflow.models.Variable` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:48:1: AIR311 `airflow.models.baseoperator.chain` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
49 | Connection()
|
||||
50 | DAGFromModel()
|
||||
51 | Variable()
|
||||
| ^^^^^^^^ AIR311
|
||||
52 |
|
||||
53 | # airflow.models.baseoperator
|
||||
|
|
||||
= help: Use `airflow.sdk.Variable` instead
|
||||
|
||||
AIR311_names.py:54:1: AIR311 `airflow.models.baseoperator.chain` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
53 | # airflow.models.baseoperator
|
||||
54 | chain()
|
||||
47 | # airflow.models.baseoperator
|
||||
48 | chain()
|
||||
| ^^^^^ AIR311
|
||||
55 | chain_linear()
|
||||
56 | cross_downstream()
|
||||
49 | chain_linear()
|
||||
50 | cross_downstream()
|
||||
|
|
||||
= help: Use `airflow.sdk.chain` instead
|
||||
|
||||
AIR311_names.py:55:1: AIR311 `airflow.models.baseoperator.chain_linear` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:49:1: AIR311 `airflow.models.baseoperator.chain_linear` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
53 | # airflow.models.baseoperator
|
||||
54 | chain()
|
||||
55 | chain_linear()
|
||||
47 | # airflow.models.baseoperator
|
||||
48 | chain()
|
||||
49 | chain_linear()
|
||||
| ^^^^^^^^^^^^ AIR311
|
||||
56 | cross_downstream()
|
||||
50 | cross_downstream()
|
||||
|
|
||||
= help: Use `airflow.sdk.chain_linear` instead
|
||||
|
||||
AIR311_names.py:56:1: AIR311 `airflow.models.baseoperator.cross_downstream` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:50:1: AIR311 `airflow.models.baseoperator.cross_downstream` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
54 | chain()
|
||||
55 | chain_linear()
|
||||
56 | cross_downstream()
|
||||
48 | chain()
|
||||
49 | chain_linear()
|
||||
50 | cross_downstream()
|
||||
| ^^^^^^^^^^^^^^^^ AIR311
|
||||
57 |
|
||||
58 | # airflow.models.baseoperatolinker
|
||||
51 |
|
||||
52 | # airflow.models.baseoperatolinker
|
||||
|
|
||||
= help: Use `airflow.sdk.cross_downstream` instead
|
||||
|
||||
AIR311_names.py:62:1: AIR311 [*] `airflow.models.dag.DAG` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:56:1: AIR311 `airflow.models.dag.DAG` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
61 | # airflow.models.dag
|
||||
62 | DAGFromDag()
|
||||
55 | # airflow.models.dag
|
||||
56 | DAGFromDag()
|
||||
| ^^^^^^^^^^ AIR311
|
||||
63 | # airflow.timetables.datasets
|
||||
64 | DatasetOrTimeSchedule()
|
||||
57 | # airflow.timetables.datasets
|
||||
58 | DatasetOrTimeSchedule()
|
||||
|
|
||||
= help: Use `airflow.sdk.DAG` instead
|
||||
|
||||
ℹ Safe fix
|
||||
22 22 | from airflow.models.dag import DAG as DAGFromDag
|
||||
23 23 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
24 24 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
25 |+from airflow.sdk import DAG
|
||||
25 26 |
|
||||
26 27 | # airflow
|
||||
27 28 | DatasetFromRoot()
|
||||
--------------------------------------------------------------------------------
|
||||
59 60 | BaseOperatorLink()
|
||||
60 61 |
|
||||
61 62 | # airflow.models.dag
|
||||
62 |-DAGFromDag()
|
||||
63 |+DAG()
|
||||
63 64 | # airflow.timetables.datasets
|
||||
64 65 | DatasetOrTimeSchedule()
|
||||
65 66 |
|
||||
|
||||
AIR311_names.py:64:1: AIR311 [*] `airflow.timetables.datasets.DatasetOrTimeSchedule` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:58:1: AIR311 [*] `airflow.timetables.datasets.DatasetOrTimeSchedule` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
62 | DAGFromDag()
|
||||
63 | # airflow.timetables.datasets
|
||||
64 | DatasetOrTimeSchedule()
|
||||
56 | DAGFromDag()
|
||||
57 | # airflow.timetables.datasets
|
||||
58 | DatasetOrTimeSchedule()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ AIR311
|
||||
65 |
|
||||
66 | # airflow.utils.dag_parsing_context
|
||||
59 |
|
||||
60 | # airflow.utils.dag_parsing_context
|
||||
|
|
||||
= help: Use `airflow.timetables.assets.AssetOrTimeSchedule` instead
|
||||
|
||||
ℹ Safe fix
|
||||
22 22 | from airflow.models.dag import DAG as DAGFromDag
|
||||
23 23 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
24 24 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
25 |+from airflow.timetables.assets import AssetOrTimeSchedule
|
||||
25 26 |
|
||||
26 27 | # airflow
|
||||
27 28 | DatasetFromRoot()
|
||||
18 18 | from airflow.models.dag import DAG as DAGFromDag
|
||||
19 19 | from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
20 20 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
21 |+from airflow.timetables.assets import AssetOrTimeSchedule
|
||||
21 22 |
|
||||
22 23 | # airflow
|
||||
23 24 | DatasetFromRoot()
|
||||
--------------------------------------------------------------------------------
|
||||
61 62 | # airflow.models.dag
|
||||
62 63 | DAGFromDag()
|
||||
63 64 | # airflow.timetables.datasets
|
||||
64 |-DatasetOrTimeSchedule()
|
||||
65 |+AssetOrTimeSchedule()
|
||||
65 66 |
|
||||
66 67 | # airflow.utils.dag_parsing_context
|
||||
67 68 | get_parsing_context()
|
||||
55 56 | # airflow.models.dag
|
||||
56 57 | DAGFromDag()
|
||||
57 58 | # airflow.timetables.datasets
|
||||
58 |-DatasetOrTimeSchedule()
|
||||
59 |+AssetOrTimeSchedule()
|
||||
59 60 |
|
||||
60 61 | # airflow.utils.dag_parsing_context
|
||||
61 62 | get_parsing_context()
|
||||
|
||||
AIR311_names.py:67:1: AIR311 `airflow.utils.dag_parsing_context.get_parsing_context` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
AIR311_names.py:61:1: AIR311 `airflow.utils.dag_parsing_context.get_parsing_context` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
|
||||
66 | # airflow.utils.dag_parsing_context
|
||||
67 | get_parsing_context()
|
||||
60 | # airflow.utils.dag_parsing_context
|
||||
61 | get_parsing_context()
|
||||
| ^^^^^^^^^^^^^^^^^^^ AIR311
|
||||
|
|
||||
= help: Use `airflow.sdk.get_parsing_context` instead
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/airflow/mod.rs
|
||||
---
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/airflow/mod.rs
|
||||
---
|
||||
|
||||
@@ -100,15 +100,7 @@ pub(crate) fn hardcoded_sql_expression(checker: &Checker, expr: &Expr) {
|
||||
}
|
||||
|
||||
// f"select * from table where val = {val}"
|
||||
Expr::FString(f_string)
|
||||
if f_string
|
||||
.value
|
||||
.f_strings()
|
||||
.any(|fs| fs.elements.iter().any(ast::FStringElement::is_expression)) =>
|
||||
{
|
||||
concatenated_f_string(f_string, checker.locator())
|
||||
}
|
||||
|
||||
Expr::FString(f_string) => concatenated_f_string(f_string, checker.locator()),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
|
||||
@@ -601,6 +601,4 @@ S608.py:164:11: S608 Possible SQL injection vector through string-based query co
|
||||
167 | | FROM ({user_input}) raw
|
||||
168 | | """
|
||||
| |___^ S608
|
||||
169 |
|
||||
170 | # https://github.com/astral-sh/ruff/issues/17967
|
||||
|
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_python_ast::{self as ast};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::{checkers::ast::Checker, fix::edits::add_argument};
|
||||
@@ -60,18 +60,10 @@ pub(crate) fn no_explicit_stacklevel(checker: &Checker, call: &ast::ExprCall) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When prefixes are supplied, stacklevel is implicitly overridden to be `max(2, stacklevel)`.
|
||||
//
|
||||
// Signature as of Python 3.13 (https://docs.python.org/3/library/warnings.html#warnings.warn)
|
||||
// ```text
|
||||
// 0 1 2 3 4
|
||||
// warnings.warn(message, category=None, stacklevel=1, source=None, *, skip_file_prefixes=())
|
||||
// ```
|
||||
if call
|
||||
.arguments
|
||||
.find_argument_value("stacklevel", 2)
|
||||
.is_some()
|
||||
|| is_skip_file_prefixes_param_set(&call.arguments)
|
||||
|| call
|
||||
.arguments
|
||||
.args
|
||||
@@ -98,14 +90,3 @@ pub(crate) fn no_explicit_stacklevel(checker: &Checker, call: &ast::ExprCall) {
|
||||
|
||||
checker.report_diagnostic(diagnostic);
|
||||
}
|
||||
|
||||
/// Returns `true` if `skip_file_prefixes` is set to its non-default value.
|
||||
/// The default value of `skip_file_prefixes` is an empty tuple.
|
||||
fn is_skip_file_prefixes_param_set(arguments: &ast::Arguments) -> bool {
|
||||
arguments
|
||||
.find_keyword("skip_file_prefixes")
|
||||
.is_some_and(|keyword| match &keyword.value {
|
||||
Expr::Tuple(tuple) => !tuple.elts.is_empty(),
|
||||
_ => true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -61,26 +61,3 @@ B028.py:22:1: B028 [*] No explicit `stacklevel` keyword argument found
|
||||
26 |- source = None # no trailing comma
|
||||
26 |+ source = None, stacklevel=2 # no trailing comma
|
||||
27 27 | )
|
||||
28 28 |
|
||||
29 29 | # https://github.com/astral-sh/ruff/issues/18011
|
||||
|
||||
B028.py:32:1: B028 [*] No explicit `stacklevel` keyword argument found
|
||||
|
|
||||
30 | warnings.warn("test", skip_file_prefixes=(os.path.dirname(__file__),))
|
||||
31 | # trigger diagnostic if `skip_file_prefixes` is present and set to the default value
|
||||
32 | warnings.warn("test", skip_file_prefixes=())
|
||||
| ^^^^^^^^^^^^^ B028
|
||||
33 |
|
||||
34 | _my_prefixes = ("this","that")
|
||||
|
|
||||
= help: Set `stacklevel=2`
|
||||
|
||||
ℹ Unsafe fix
|
||||
29 29 | # https://github.com/astral-sh/ruff/issues/18011
|
||||
30 30 | warnings.warn("test", skip_file_prefixes=(os.path.dirname(__file__),))
|
||||
31 31 | # trigger diagnostic if `skip_file_prefixes` is present and set to the default value
|
||||
32 |-warnings.warn("test", skip_file_prefixes=())
|
||||
32 |+warnings.warn("test", skip_file_prefixes=(), stacklevel=2)
|
||||
33 33 |
|
||||
34 34 | _my_prefixes = ("this","that")
|
||||
35 35 | warnings.warn("test", skip_file_prefixes = _my_prefixes)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::{FxBuildHasher, FxHashSet};
|
||||
|
||||
use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
@@ -19,7 +19,6 @@ use crate::fix::edits::{remove_argument, Parentheses};
|
||||
/// arguments directly.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```python
|
||||
/// def foo(bar):
|
||||
/// return bar + 1
|
||||
@@ -29,7 +28,6 @@ use crate::fix::edits::{remove_argument, Parentheses};
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```python
|
||||
/// def foo(bar):
|
||||
/// return bar + 1
|
||||
@@ -38,26 +36,6 @@ use crate::fix::edits::{remove_argument, Parentheses};
|
||||
/// print(foo(bar=2)) # prints 3
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
///
|
||||
/// This rule's fix is marked as unsafe for dictionaries with comments interleaved between
|
||||
/// the items, as comments may be removed.
|
||||
///
|
||||
/// For example, the fix would be marked as unsafe in the following case:
|
||||
///
|
||||
/// ```python
|
||||
/// foo(
|
||||
/// **{
|
||||
/// # comment
|
||||
/// "x": 1.0,
|
||||
/// # comment
|
||||
/// "y": 2.0,
|
||||
/// }
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// as this is converted to `foo(x=1.0, y=2.0)` without any of the comments.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: Dictionary displays](https://docs.python.org/3/reference/expressions.html#dictionary-displays)
|
||||
/// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls)
|
||||
@@ -135,7 +113,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &Checker, call: &ast::ExprCall) {
|
||||
.iter()
|
||||
.all(|kwarg| !duplicate_keywords.contains(kwarg))
|
||||
{
|
||||
let edit = Edit::range_replacement(
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
kwargs
|
||||
.iter()
|
||||
.zip(dict.iter_values())
|
||||
@@ -156,15 +134,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &Checker, call: &ast::ExprCall) {
|
||||
})
|
||||
.join(", "),
|
||||
keyword.range(),
|
||||
);
|
||||
diagnostic.set_fix(Fix::applicable_edit(
|
||||
edit,
|
||||
if checker.comment_ranges().intersects(dict.range()) {
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
},
|
||||
));
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,8 +208,6 @@ PIE804.py:29:12: PIE804 [*] Unnecessary `dict` kwargs
|
||||
29 |-abc(foo=1, **{'bar': (bar := 1)}) # PIE804
|
||||
29 |+abc(foo=1, bar=(bar := 1)) # PIE804
|
||||
30 30 | abc(foo=1, **{'bar': (yield 1)}) # PIE804
|
||||
31 31 |
|
||||
32 32 | # https://github.com/astral-sh/ruff/issues/18036
|
||||
|
||||
PIE804.py:30:12: PIE804 [*] Unnecessary `dict` kwargs
|
||||
|
|
||||
@@ -217,8 +215,6 @@ PIE804.py:30:12: PIE804 [*] Unnecessary `dict` kwargs
|
||||
29 | abc(foo=1, **{'bar': (bar := 1)}) # PIE804
|
||||
30 | abc(foo=1, **{'bar': (yield 1)}) # PIE804
|
||||
| ^^^^^^^^^^^^^^^^^^^^ PIE804
|
||||
31 |
|
||||
32 | # https://github.com/astral-sh/ruff/issues/18036
|
||||
|
|
||||
= help: Remove unnecessary kwargs
|
||||
|
||||
@@ -228,34 +224,3 @@ PIE804.py:30:12: PIE804 [*] Unnecessary `dict` kwargs
|
||||
29 29 | abc(foo=1, **{'bar': (bar := 1)}) # PIE804
|
||||
30 |-abc(foo=1, **{'bar': (yield 1)}) # PIE804
|
||||
30 |+abc(foo=1, bar=(yield 1)) # PIE804
|
||||
31 31 |
|
||||
32 32 | # https://github.com/astral-sh/ruff/issues/18036
|
||||
33 33 | # The autofix for this is unsafe due to the comments inside the dictionary.
|
||||
|
||||
PIE804.py:35:5: PIE804 [*] Unnecessary `dict` kwargs
|
||||
|
|
||||
33 | # The autofix for this is unsafe due to the comments inside the dictionary.
|
||||
34 | foo(
|
||||
35 | / **{
|
||||
36 | | # Comment 1
|
||||
37 | | "x": 1.0,
|
||||
38 | | # Comment 2
|
||||
39 | | "y": 2.0,
|
||||
40 | | }
|
||||
| |_____^ PIE804
|
||||
41 | )
|
||||
|
|
||||
= help: Remove unnecessary kwargs
|
||||
|
||||
ℹ Unsafe fix
|
||||
32 32 | # https://github.com/astral-sh/ruff/issues/18036
|
||||
33 33 | # The autofix for this is unsafe due to the comments inside the dictionary.
|
||||
34 34 | foo(
|
||||
35 |- **{
|
||||
36 |- # Comment 1
|
||||
37 |- "x": 1.0,
|
||||
38 |- # Comment 2
|
||||
39 |- "y": 2.0,
|
||||
40 |- }
|
||||
35 |+ x=1.0, y=2.0
|
||||
41 36 | )
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Violation};
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::helpers::map_callable;
|
||||
use ruff_python_ast::name::UnqualifiedName;
|
||||
use ruff_python_ast::visitor;
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
@@ -12,7 +11,6 @@ use ruff_python_semantic::SemanticModel;
|
||||
use ruff_source_file::LineRanges;
|
||||
use ruff_text_size::Ranged;
|
||||
use ruff_text_size::{TextLen, TextRange};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits;
|
||||
@@ -809,51 +807,10 @@ fn check_fixture_returns(checker: &Checker, name: &str, body: &[Stmt], returns:
|
||||
}
|
||||
|
||||
/// PT019
|
||||
fn check_test_function_args(checker: &Checker, parameters: &Parameters, decorators: &[Decorator]) {
|
||||
let mut named_parametrize = FxHashSet::default();
|
||||
for decorator in decorators.iter().filter(|decorator| {
|
||||
UnqualifiedName::from_expr(map_callable(&decorator.expression))
|
||||
.is_some_and(|name| matches!(name.segments(), ["pytest", "mark", "parametrize"]))
|
||||
}) {
|
||||
let Some(call_expr) = decorator.expression.as_call_expr() else {
|
||||
continue;
|
||||
};
|
||||
let Some(first_arg) = call_expr.arguments.find_argument_value("argnames", 0) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
match first_arg {
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||
named_parametrize.extend(
|
||||
value
|
||||
.to_str()
|
||||
.split(',')
|
||||
.map(str::trim)
|
||||
.filter(|param| !param.is_empty() && param.starts_with('_')),
|
||||
);
|
||||
}
|
||||
|
||||
Expr::Name(_) => return,
|
||||
Expr::List(ast::ExprList { elts, .. }) | Expr::Tuple(ast::ExprTuple { elts, .. })
|
||||
if elts.iter().any(Expr::is_name_expr) =>
|
||||
{
|
||||
return
|
||||
}
|
||||
Expr::List(ast::ExprList { elts, .. }) | Expr::Tuple(ast::ExprTuple { elts, .. }) => {
|
||||
named_parametrize.extend(
|
||||
elts.iter()
|
||||
.filter_map(Expr::as_string_literal_expr)
|
||||
.map(|param| param.value.to_str().trim())
|
||||
.filter(|param| !param.is_empty() && param.starts_with('_')),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_test_function_args(checker: &Checker, parameters: &Parameters) {
|
||||
for parameter in parameters.iter_non_variadic_params() {
|
||||
let name = parameter.name();
|
||||
if name.starts_with('_') && !named_parametrize.contains(name.as_str()) {
|
||||
if name.starts_with('_') {
|
||||
checker.report_diagnostic(Diagnostic::new(
|
||||
PytestFixtureParamWithoutValue {
|
||||
name: name.to_string(),
|
||||
@@ -958,6 +915,6 @@ pub(crate) fn fixture(
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::PytestFixtureParamWithoutValue) && name.starts_with("test_") {
|
||||
check_test_function_args(checker, parameters, decorators);
|
||||
check_test_function_args(checker, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pytest_style/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
PT019.py:9:14: PT019 Fixture `_fixture` without value is injected as parameter, use `@pytest.mark.usefixtures` instead
|
||||
|
|
||||
@@ -14,19 +15,3 @@ PT019.py:13:17: PT019 Fixture `_fixture` without value is injected as parameter,
|
||||
| ^^^^^^^^ PT019
|
||||
14 | pass
|
||||
|
|
||||
|
||||
PT019.py:31:24: PT019 Fixture `_bar` without value is injected as parameter, use `@pytest.mark.usefixtures` instead
|
||||
|
|
||||
30 | @pytest.mark.parametrize("_foo", [1, 2, 3])
|
||||
31 | def test_thingy2(_foo, _bar): # Error _bar is not defined in parametrize
|
||||
| ^^^^ PT019
|
||||
32 | pass
|
||||
|
|
||||
|
||||
PT019.py:39:24: PT019 Fixture `_bar` without value is injected as parameter, use `@pytest.mark.usefixtures` instead
|
||||
|
|
||||
38 | @pytest.mark.parametrize(("_foo"), [1, 2, 3])
|
||||
39 | def test_thingy4(_foo, _bar): # Error _bar is not defined in parametrize
|
||||
| ^^^^ PT019
|
||||
40 | pass
|
||||
|
|
||||
|
||||
@@ -83,7 +83,7 @@ pub(crate) fn split_static_string(
|
||||
let sep_arg = arguments.find_argument_value("sep", 0);
|
||||
let split_replacement = if let Some(sep) = sep_arg {
|
||||
match sep {
|
||||
Expr::NoneLiteral(_) => split_default(str_value, maxsplit_value, direction),
|
||||
Expr::NoneLiteral(_) => split_default(str_value, maxsplit_value),
|
||||
Expr::StringLiteral(sep_value) => {
|
||||
let sep_value_str = sep_value.value.to_str();
|
||||
Some(split_sep(
|
||||
@@ -99,7 +99,7 @@ pub(crate) fn split_static_string(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
split_default(str_value, maxsplit_value, direction)
|
||||
split_default(str_value, maxsplit_value)
|
||||
};
|
||||
|
||||
let mut diagnostic = Diagnostic::new(SplitStaticString, call.range());
|
||||
@@ -144,11 +144,7 @@ fn construct_replacement(elts: &[&str], flags: StringLiteralFlags) -> Expr {
|
||||
})
|
||||
}
|
||||
|
||||
fn split_default(
|
||||
str_value: &StringLiteralValue,
|
||||
max_split: i32,
|
||||
direction: Direction,
|
||||
) -> Option<Expr> {
|
||||
fn split_default(str_value: &StringLiteralValue, max_split: i32) -> Option<Expr> {
|
||||
// From the Python documentation:
|
||||
// > If sep is not specified or is None, a different splitting algorithm is applied: runs of
|
||||
// > consecutive whitespace are regarded as a single separator, and the result will contain
|
||||
@@ -156,7 +152,6 @@ fn split_default(
|
||||
// > Consequently, splitting an empty string or a string consisting of just whitespace with
|
||||
// > a None separator returns [].
|
||||
// https://docs.python.org/3/library/stdtypes.html#str.split
|
||||
let string_val = str_value.to_str();
|
||||
match max_split.cmp(&0) {
|
||||
Ordering::Greater => {
|
||||
// Autofix for `maxsplit` without separator not yet implemented, as
|
||||
@@ -165,30 +160,14 @@ fn split_default(
|
||||
None
|
||||
}
|
||||
Ordering::Equal => {
|
||||
// Behavior for maxsplit = 0 when sep is None:
|
||||
// - If the string is empty or all whitespace, result is [].
|
||||
// - Otherwise:
|
||||
// - " x ".split(maxsplit=0) -> ['x ']
|
||||
// - " x ".rsplit(maxsplit=0) -> [' x']
|
||||
// - "".split(maxsplit=0) -> []
|
||||
// - " ".split(maxsplit=0) -> []
|
||||
let processed_str = if direction == Direction::Left {
|
||||
string_val.trim_start()
|
||||
} else {
|
||||
string_val.trim_end()
|
||||
};
|
||||
let list_items: &[_] = if processed_str.is_empty() {
|
||||
&[]
|
||||
} else {
|
||||
&[processed_str]
|
||||
};
|
||||
let list_items: Vec<&str> = vec![str_value.to_str()];
|
||||
Some(construct_replacement(
|
||||
list_items,
|
||||
&list_items,
|
||||
str_value.first_literal_flags(),
|
||||
))
|
||||
}
|
||||
Ordering::Less => {
|
||||
let list_items: Vec<&str> = string_val.split_whitespace().collect();
|
||||
let list_items: Vec<&str> = str_value.to_str().split_whitespace().collect();
|
||||
Some(construct_replacement(
|
||||
&list_items,
|
||||
str_value.first_literal_flags(),
|
||||
@@ -207,20 +186,12 @@ fn split_sep(
|
||||
let list_items: Vec<&str> = if let Ok(split_n) = usize::try_from(max_split) {
|
||||
match direction {
|
||||
Direction::Left => value.splitn(split_n + 1, sep_value).collect(),
|
||||
Direction::Right => {
|
||||
let mut items: Vec<&str> = value.rsplitn(split_n + 1, sep_value).collect();
|
||||
items.reverse();
|
||||
items
|
||||
}
|
||||
Direction::Right => value.rsplitn(split_n + 1, sep_value).collect(),
|
||||
}
|
||||
} else {
|
||||
match direction {
|
||||
Direction::Left => value.split(sep_value).collect(),
|
||||
Direction::Right => {
|
||||
let mut items: Vec<&str> = value.rsplit(sep_value).collect();
|
||||
items.reverse();
|
||||
items
|
||||
}
|
||||
Direction::Right => value.rsplit(sep_value).collect(),
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -352,7 +352,7 @@ SIM905.py:32:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
32 | " a*a a*a a ".split("*", -1) # [" a", "a a", "a a "]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
33 | "".split() # []
|
||||
34 | """
|
||||
34 | """
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
@@ -363,7 +363,7 @@ SIM905.py:32:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
32 |-" a*a a*a a ".split("*", -1) # [" a", "a a", "a a "]
|
||||
32 |+[" a", "a a", "a a "] # [" a", "a a", "a a "]
|
||||
33 33 | "".split() # []
|
||||
34 34 | """
|
||||
34 34 | """
|
||||
35 35 | """.split() # []
|
||||
|
||||
SIM905.py:33:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
@@ -371,7 +371,7 @@ SIM905.py:33:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
32 | " a*a a*a a ".split("*", -1) # [" a", "a a", "a a "]
|
||||
33 | "".split() # []
|
||||
| ^^^^^^^^^^ SIM905
|
||||
34 | """
|
||||
34 | """
|
||||
35 | """.split() # []
|
||||
|
|
||||
= help: Replace with list literal
|
||||
@@ -382,7 +382,7 @@ SIM905.py:33:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
32 32 | " a*a a*a a ".split("*", -1) # [" a", "a a", "a a "]
|
||||
33 |-"".split() # []
|
||||
33 |+[] # []
|
||||
34 34 | """
|
||||
34 34 | """
|
||||
35 35 | """.split() # []
|
||||
36 36 | " ".split() # []
|
||||
|
||||
@@ -390,7 +390,7 @@ SIM905.py:34:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
32 | " a*a a*a a ".split("*", -1) # [" a", "a a", "a a "]
|
||||
33 | "".split() # []
|
||||
34 | / """
|
||||
34 | / """
|
||||
35 | | """.split() # []
|
||||
| |___________^ SIM905
|
||||
36 | " ".split() # []
|
||||
@@ -402,7 +402,7 @@ SIM905.py:34:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
31 31 |
|
||||
32 32 | " a*a a*a a ".split("*", -1) # [" a", "a a", "a a "]
|
||||
33 33 | "".split() # []
|
||||
34 |-"""
|
||||
34 |-"""
|
||||
35 |-""".split() # []
|
||||
34 |+[] # []
|
||||
36 35 | " ".split() # []
|
||||
@@ -411,7 +411,7 @@ SIM905.py:34:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
||||
SIM905.py:36:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
34 | """
|
||||
34 | """
|
||||
35 | """.split() # []
|
||||
36 | " ".split() # []
|
||||
| ^^^^^^^^^^^^^^^^^ SIM905
|
||||
@@ -422,7 +422,7 @@ SIM905.py:36:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
||||
ℹ Safe fix
|
||||
33 33 | "".split() # []
|
||||
34 34 | """
|
||||
34 34 | """
|
||||
35 35 | """.split() # []
|
||||
36 |-" ".split() # []
|
||||
36 |+[] # []
|
||||
@@ -442,7 +442,7 @@ SIM905.py:37:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
34 34 | """
|
||||
34 34 | """
|
||||
35 35 | """.split() # []
|
||||
36 36 | " ".split() # []
|
||||
37 |-"/abc/".split() # ["/abc/"]
|
||||
@@ -854,8 +854,6 @@ SIM905.py:103:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
107 | | "'itemD'"
|
||||
108 | | """.split()
|
||||
| |___________^ SIM905
|
||||
109 |
|
||||
110 | # https://github.com/astral-sh/ruff/issues/18042
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
@@ -870,393 +868,3 @@ SIM905.py:103:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
107 |-"'itemD'"
|
||||
108 |-""".split()
|
||||
103 |+['"itemA"', "'itemB'", "'''itemC'''", "\"'itemD'\""]
|
||||
109 104 |
|
||||
110 105 | # https://github.com/astral-sh/ruff/issues/18042
|
||||
111 106 | print("a,b".rsplit(","))
|
||||
|
||||
SIM905.py:111:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
110 | # https://github.com/astral-sh/ruff/issues/18042
|
||||
111 | print("a,b".rsplit(","))
|
||||
| ^^^^^^^^^^^^^^^^^ SIM905
|
||||
112 | print("a,b,c".rsplit(",", 1))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
108 108 | """.split()
|
||||
109 109 |
|
||||
110 110 | # https://github.com/astral-sh/ruff/issues/18042
|
||||
111 |-print("a,b".rsplit(","))
|
||||
111 |+print(["a", "b"])
|
||||
112 112 | print("a,b,c".rsplit(",", 1))
|
||||
113 113 |
|
||||
114 114 | # https://github.com/astral-sh/ruff/issues/18069
|
||||
|
||||
SIM905.py:112:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
110 | # https://github.com/astral-sh/ruff/issues/18042
|
||||
111 | print("a,b".rsplit(","))
|
||||
112 | print("a,b,c".rsplit(",", 1))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
113 |
|
||||
114 | # https://github.com/astral-sh/ruff/issues/18069
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
109 109 |
|
||||
110 110 | # https://github.com/astral-sh/ruff/issues/18042
|
||||
111 111 | print("a,b".rsplit(","))
|
||||
112 |-print("a,b,c".rsplit(",", 1))
|
||||
112 |+print(["a,b", "c"])
|
||||
113 113 |
|
||||
114 114 | # https://github.com/astral-sh/ruff/issues/18069
|
||||
115 115 |
|
||||
|
||||
SIM905.py:116:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
114 | # https://github.com/astral-sh/ruff/issues/18069
|
||||
115 |
|
||||
116 | print("".split(maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
117 | print("".split(sep=None, maxsplit=0))
|
||||
118 | print(" ".split(maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
113 113 |
|
||||
114 114 | # https://github.com/astral-sh/ruff/issues/18069
|
||||
115 115 |
|
||||
116 |-print("".split(maxsplit=0))
|
||||
116 |+print([])
|
||||
117 117 | print("".split(sep=None, maxsplit=0))
|
||||
118 118 | print(" ".split(maxsplit=0))
|
||||
119 119 | print(" ".split(sep=None, maxsplit=0))
|
||||
|
||||
SIM905.py:117:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
116 | print("".split(maxsplit=0))
|
||||
117 | print("".split(sep=None, maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
118 | print(" ".split(maxsplit=0))
|
||||
119 | print(" ".split(sep=None, maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
114 114 | # https://github.com/astral-sh/ruff/issues/18069
|
||||
115 115 |
|
||||
116 116 | print("".split(maxsplit=0))
|
||||
117 |-print("".split(sep=None, maxsplit=0))
|
||||
117 |+print([])
|
||||
118 118 | print(" ".split(maxsplit=0))
|
||||
119 119 | print(" ".split(sep=None, maxsplit=0))
|
||||
120 120 | print(" x ".split(maxsplit=0))
|
||||
|
||||
SIM905.py:118:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
116 | print("".split(maxsplit=0))
|
||||
117 | print("".split(sep=None, maxsplit=0))
|
||||
118 | print(" ".split(maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
119 | print(" ".split(sep=None, maxsplit=0))
|
||||
120 | print(" x ".split(maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
115 115 |
|
||||
116 116 | print("".split(maxsplit=0))
|
||||
117 117 | print("".split(sep=None, maxsplit=0))
|
||||
118 |-print(" ".split(maxsplit=0))
|
||||
118 |+print([])
|
||||
119 119 | print(" ".split(sep=None, maxsplit=0))
|
||||
120 120 | print(" x ".split(maxsplit=0))
|
||||
121 121 | print(" x ".split(sep=None, maxsplit=0))
|
||||
|
||||
SIM905.py:119:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
117 | print("".split(sep=None, maxsplit=0))
|
||||
118 | print(" ".split(maxsplit=0))
|
||||
119 | print(" ".split(sep=None, maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
120 | print(" x ".split(maxsplit=0))
|
||||
121 | print(" x ".split(sep=None, maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
116 116 | print("".split(maxsplit=0))
|
||||
117 117 | print("".split(sep=None, maxsplit=0))
|
||||
118 118 | print(" ".split(maxsplit=0))
|
||||
119 |-print(" ".split(sep=None, maxsplit=0))
|
||||
119 |+print([])
|
||||
120 120 | print(" x ".split(maxsplit=0))
|
||||
121 121 | print(" x ".split(sep=None, maxsplit=0))
|
||||
122 122 | print(" x ".split(maxsplit=0))
|
||||
|
||||
SIM905.py:120:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
118 | print(" ".split(maxsplit=0))
|
||||
119 | print(" ".split(sep=None, maxsplit=0))
|
||||
120 | print(" x ".split(maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
121 | print(" x ".split(sep=None, maxsplit=0))
|
||||
122 | print(" x ".split(maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
117 117 | print("".split(sep=None, maxsplit=0))
|
||||
118 118 | print(" ".split(maxsplit=0))
|
||||
119 119 | print(" ".split(sep=None, maxsplit=0))
|
||||
120 |-print(" x ".split(maxsplit=0))
|
||||
120 |+print(["x "])
|
||||
121 121 | print(" x ".split(sep=None, maxsplit=0))
|
||||
122 122 | print(" x ".split(maxsplit=0))
|
||||
123 123 | print(" x ".split(sep=None, maxsplit=0))
|
||||
|
||||
SIM905.py:121:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
119 | print(" ".split(sep=None, maxsplit=0))
|
||||
120 | print(" x ".split(maxsplit=0))
|
||||
121 | print(" x ".split(sep=None, maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
122 | print(" x ".split(maxsplit=0))
|
||||
123 | print(" x ".split(sep=None, maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
118 118 | print(" ".split(maxsplit=0))
|
||||
119 119 | print(" ".split(sep=None, maxsplit=0))
|
||||
120 120 | print(" x ".split(maxsplit=0))
|
||||
121 |-print(" x ".split(sep=None, maxsplit=0))
|
||||
121 |+print(["x "])
|
||||
122 122 | print(" x ".split(maxsplit=0))
|
||||
123 123 | print(" x ".split(sep=None, maxsplit=0))
|
||||
124 124 | print("".rsplit(maxsplit=0))
|
||||
|
||||
SIM905.py:122:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
120 | print(" x ".split(maxsplit=0))
|
||||
121 | print(" x ".split(sep=None, maxsplit=0))
|
||||
122 | print(" x ".split(maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
123 | print(" x ".split(sep=None, maxsplit=0))
|
||||
124 | print("".rsplit(maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
119 119 | print(" ".split(sep=None, maxsplit=0))
|
||||
120 120 | print(" x ".split(maxsplit=0))
|
||||
121 121 | print(" x ".split(sep=None, maxsplit=0))
|
||||
122 |-print(" x ".split(maxsplit=0))
|
||||
122 |+print(["x "])
|
||||
123 123 | print(" x ".split(sep=None, maxsplit=0))
|
||||
124 124 | print("".rsplit(maxsplit=0))
|
||||
125 125 | print("".rsplit(sep=None, maxsplit=0))
|
||||
|
||||
SIM905.py:123:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
121 | print(" x ".split(sep=None, maxsplit=0))
|
||||
122 | print(" x ".split(maxsplit=0))
|
||||
123 | print(" x ".split(sep=None, maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
124 | print("".rsplit(maxsplit=0))
|
||||
125 | print("".rsplit(sep=None, maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
120 120 | print(" x ".split(maxsplit=0))
|
||||
121 121 | print(" x ".split(sep=None, maxsplit=0))
|
||||
122 122 | print(" x ".split(maxsplit=0))
|
||||
123 |-print(" x ".split(sep=None, maxsplit=0))
|
||||
123 |+print(["x "])
|
||||
124 124 | print("".rsplit(maxsplit=0))
|
||||
125 125 | print("".rsplit(sep=None, maxsplit=0))
|
||||
126 126 | print(" ".rsplit(maxsplit=0))
|
||||
|
||||
SIM905.py:124:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
122 | print(" x ".split(maxsplit=0))
|
||||
123 | print(" x ".split(sep=None, maxsplit=0))
|
||||
124 | print("".rsplit(maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
125 | print("".rsplit(sep=None, maxsplit=0))
|
||||
126 | print(" ".rsplit(maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
121 121 | print(" x ".split(sep=None, maxsplit=0))
|
||||
122 122 | print(" x ".split(maxsplit=0))
|
||||
123 123 | print(" x ".split(sep=None, maxsplit=0))
|
||||
124 |-print("".rsplit(maxsplit=0))
|
||||
124 |+print([])
|
||||
125 125 | print("".rsplit(sep=None, maxsplit=0))
|
||||
126 126 | print(" ".rsplit(maxsplit=0))
|
||||
127 127 | print(" ".rsplit(sep=None, maxsplit=0))
|
||||
|
||||
SIM905.py:125:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
123 | print(" x ".split(sep=None, maxsplit=0))
|
||||
124 | print("".rsplit(maxsplit=0))
|
||||
125 | print("".rsplit(sep=None, maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
126 | print(" ".rsplit(maxsplit=0))
|
||||
127 | print(" ".rsplit(sep=None, maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
122 122 | print(" x ".split(maxsplit=0))
|
||||
123 123 | print(" x ".split(sep=None, maxsplit=0))
|
||||
124 124 | print("".rsplit(maxsplit=0))
|
||||
125 |-print("".rsplit(sep=None, maxsplit=0))
|
||||
125 |+print([])
|
||||
126 126 | print(" ".rsplit(maxsplit=0))
|
||||
127 127 | print(" ".rsplit(sep=None, maxsplit=0))
|
||||
128 128 | print(" x ".rsplit(maxsplit=0))
|
||||
|
||||
SIM905.py:126:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
124 | print("".rsplit(maxsplit=0))
|
||||
125 | print("".rsplit(sep=None, maxsplit=0))
|
||||
126 | print(" ".rsplit(maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
127 | print(" ".rsplit(sep=None, maxsplit=0))
|
||||
128 | print(" x ".rsplit(maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
123 123 | print(" x ".split(sep=None, maxsplit=0))
|
||||
124 124 | print("".rsplit(maxsplit=0))
|
||||
125 125 | print("".rsplit(sep=None, maxsplit=0))
|
||||
126 |-print(" ".rsplit(maxsplit=0))
|
||||
126 |+print([])
|
||||
127 127 | print(" ".rsplit(sep=None, maxsplit=0))
|
||||
128 128 | print(" x ".rsplit(maxsplit=0))
|
||||
129 129 | print(" x ".rsplit(maxsplit=0))
|
||||
|
||||
SIM905.py:127:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
125 | print("".rsplit(sep=None, maxsplit=0))
|
||||
126 | print(" ".rsplit(maxsplit=0))
|
||||
127 | print(" ".rsplit(sep=None, maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
128 | print(" x ".rsplit(maxsplit=0))
|
||||
129 | print(" x ".rsplit(maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
124 124 | print("".rsplit(maxsplit=0))
|
||||
125 125 | print("".rsplit(sep=None, maxsplit=0))
|
||||
126 126 | print(" ".rsplit(maxsplit=0))
|
||||
127 |-print(" ".rsplit(sep=None, maxsplit=0))
|
||||
127 |+print([])
|
||||
128 128 | print(" x ".rsplit(maxsplit=0))
|
||||
129 129 | print(" x ".rsplit(maxsplit=0))
|
||||
130 130 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
|
||||
SIM905.py:128:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
126 | print(" ".rsplit(maxsplit=0))
|
||||
127 | print(" ".rsplit(sep=None, maxsplit=0))
|
||||
128 | print(" x ".rsplit(maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
129 | print(" x ".rsplit(maxsplit=0))
|
||||
130 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
125 125 | print("".rsplit(sep=None, maxsplit=0))
|
||||
126 126 | print(" ".rsplit(maxsplit=0))
|
||||
127 127 | print(" ".rsplit(sep=None, maxsplit=0))
|
||||
128 |-print(" x ".rsplit(maxsplit=0))
|
||||
128 |+print([" x"])
|
||||
129 129 | print(" x ".rsplit(maxsplit=0))
|
||||
130 130 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
131 131 | print(" x ".rsplit(maxsplit=0))
|
||||
|
||||
SIM905.py:129:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
127 | print(" ".rsplit(sep=None, maxsplit=0))
|
||||
128 | print(" x ".rsplit(maxsplit=0))
|
||||
129 | print(" x ".rsplit(maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
130 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
131 | print(" x ".rsplit(maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
126 126 | print(" ".rsplit(maxsplit=0))
|
||||
127 127 | print(" ".rsplit(sep=None, maxsplit=0))
|
||||
128 128 | print(" x ".rsplit(maxsplit=0))
|
||||
129 |-print(" x ".rsplit(maxsplit=0))
|
||||
129 |+print([" x"])
|
||||
130 130 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
131 131 | print(" x ".rsplit(maxsplit=0))
|
||||
132 132 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
|
||||
SIM905.py:130:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
128 | print(" x ".rsplit(maxsplit=0))
|
||||
129 | print(" x ".rsplit(maxsplit=0))
|
||||
130 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
131 | print(" x ".rsplit(maxsplit=0))
|
||||
132 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
127 127 | print(" ".rsplit(sep=None, maxsplit=0))
|
||||
128 128 | print(" x ".rsplit(maxsplit=0))
|
||||
129 129 | print(" x ".rsplit(maxsplit=0))
|
||||
130 |-print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
130 |+print([" x"])
|
||||
131 131 | print(" x ".rsplit(maxsplit=0))
|
||||
132 132 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
|
||||
SIM905.py:131:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
129 | print(" x ".rsplit(maxsplit=0))
|
||||
130 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
131 | print(" x ".rsplit(maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
132 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
128 128 | print(" x ".rsplit(maxsplit=0))
|
||||
129 129 | print(" x ".rsplit(maxsplit=0))
|
||||
130 130 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
131 |-print(" x ".rsplit(maxsplit=0))
|
||||
131 |+print([" x"])
|
||||
132 132 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
|
||||
SIM905.py:132:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
130 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
131 | print(" x ".rsplit(maxsplit=0))
|
||||
132 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
129 129 | print(" x ".rsplit(maxsplit=0))
|
||||
130 130 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
131 131 | print(" x ".rsplit(maxsplit=0))
|
||||
132 |-print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
132 |+print([" x"])
|
||||
|
||||
@@ -26,109 +26,41 @@ pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) {
|
||||
// PTH100
|
||||
["os", "path", "abspath"] => OsPathAbspath.into(),
|
||||
// PTH101
|
||||
["os", "chmod"] => {
|
||||
// `dir_fd` is not supported by pathlib, so check if it's set to non-default values.
|
||||
// Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.chmod)
|
||||
// ```text
|
||||
// 0 1 2 3
|
||||
// os.chmod(path, mode, *, dir_fd=None, follow_symlinks=True)
|
||||
// ```
|
||||
if call
|
||||
.arguments
|
||||
.find_argument_value("path", 0)
|
||||
.is_some_and(|expr| is_file_descriptor(expr, checker.semantic()))
|
||||
|| is_argument_non_default(&call.arguments, "dir_fd", 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
OsChmod.into()
|
||||
}
|
||||
["os", "chmod"] => OsChmod.into(),
|
||||
// PTH102
|
||||
["os", "makedirs"] => OsMakedirs.into(),
|
||||
// PTH103
|
||||
["os", "mkdir"] => {
|
||||
// `dir_fd` is not supported by pathlib, so check if it's set to non-default values.
|
||||
// Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.mkdir)
|
||||
// ```text
|
||||
// 0 1 2
|
||||
// os.mkdir(path, mode=0o777, *, dir_fd=None)
|
||||
// ```
|
||||
if is_argument_non_default(&call.arguments, "dir_fd", 2) {
|
||||
return;
|
||||
}
|
||||
OsMkdir.into()
|
||||
}
|
||||
["os", "mkdir"] => OsMkdir.into(),
|
||||
// PTH104
|
||||
["os", "rename"] => {
|
||||
// `src_dir_fd` and `dst_dir_fd` are not supported by pathlib, so check if they are
|
||||
// set to non-default values.
|
||||
// are set to non-default values.
|
||||
// Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.rename)
|
||||
// ```text
|
||||
// 0 1 2 3
|
||||
// os.rename(src, dst, *, src_dir_fd=None, dst_dir_fd=None)
|
||||
// ```
|
||||
if is_argument_non_default(&call.arguments, "src_dir_fd", 2)
|
||||
|| is_argument_non_default(&call.arguments, "dst_dir_fd", 3)
|
||||
if call
|
||||
.arguments
|
||||
.find_argument_value("src_dir_fd", 2)
|
||||
.is_some_and(|expr| !expr.is_none_literal_expr())
|
||||
|| call
|
||||
.arguments
|
||||
.find_argument_value("dst_dir_fd", 3)
|
||||
.is_some_and(|expr| !expr.is_none_literal_expr())
|
||||
{
|
||||
return;
|
||||
}
|
||||
OsRename.into()
|
||||
}
|
||||
// PTH105
|
||||
["os", "replace"] => {
|
||||
// `src_dir_fd` and `dst_dir_fd` are not supported by pathlib, so check if they are
|
||||
// set to non-default values.
|
||||
// Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.replace)
|
||||
// ```text
|
||||
// 0 1 2 3
|
||||
// os.replace(src, dst, *, src_dir_fd=None, dst_dir_fd=None)
|
||||
// ```
|
||||
if is_argument_non_default(&call.arguments, "src_dir_fd", 2)
|
||||
|| is_argument_non_default(&call.arguments, "dst_dir_fd", 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
OsReplace.into()
|
||||
}
|
||||
["os", "replace"] => OsReplace.into(),
|
||||
// PTH106
|
||||
["os", "rmdir"] => {
|
||||
// `dir_fd` is not supported by pathlib, so check if it's set to non-default values.
|
||||
// Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.rmdir)
|
||||
// ```text
|
||||
// 0 1
|
||||
// os.rmdir(path, *, dir_fd=None)
|
||||
// ```
|
||||
if is_argument_non_default(&call.arguments, "dir_fd", 1) {
|
||||
return;
|
||||
}
|
||||
OsRmdir.into()
|
||||
}
|
||||
["os", "rmdir"] => OsRmdir.into(),
|
||||
// PTH107
|
||||
["os", "remove"] => {
|
||||
// `dir_fd` is not supported by pathlib, so check if it's set to non-default values.
|
||||
// Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.remove)
|
||||
// ```text
|
||||
// 0 1
|
||||
// os.remove(path, *, dir_fd=None)
|
||||
// ```
|
||||
if is_argument_non_default(&call.arguments, "dir_fd", 1) {
|
||||
return;
|
||||
}
|
||||
OsRemove.into()
|
||||
}
|
||||
["os", "remove"] => OsRemove.into(),
|
||||
// PTH108
|
||||
["os", "unlink"] => {
|
||||
// `dir_fd` is not supported by pathlib, so check if it's set to non-default values.
|
||||
// Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.unlink)
|
||||
// ```text
|
||||
// 0 1
|
||||
// os.unlink(path, *, dir_fd=None)
|
||||
// ```
|
||||
if is_argument_non_default(&call.arguments, "dir_fd", 1) {
|
||||
return;
|
||||
}
|
||||
OsUnlink.into()
|
||||
}
|
||||
["os", "unlink"] => OsUnlink.into(),
|
||||
// PTH109
|
||||
["os", "getcwd"] => OsGetcwd.into(),
|
||||
["os", "getcwdb"] => OsGetcwd.into(),
|
||||
@@ -144,17 +76,10 @@ pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) {
|
||||
["os", "path", "islink"] => OsPathIslink.into(),
|
||||
// PTH116
|
||||
["os", "stat"] => {
|
||||
// `dir_fd` is not supported by pathlib, so check if it's set to non-default values.
|
||||
// Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.stat)
|
||||
// ```text
|
||||
// 0 1 2
|
||||
// os.stat(path, *, dir_fd=None, follow_symlinks=True)
|
||||
// ```
|
||||
if call
|
||||
.arguments
|
||||
.find_argument_value("path", 0)
|
||||
.find_positional(0)
|
||||
.is_some_and(|expr| is_file_descriptor(expr, checker.semantic()))
|
||||
|| is_argument_non_default(&call.arguments, "dir_fd", 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -223,10 +148,13 @@ pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) {
|
||||
Expr::BooleanLiteral(ExprBooleanLiteral { value: true, .. })
|
||||
)
|
||||
})
|
||||
|| is_argument_non_default(&call.arguments, "opener", 7)
|
||||
|| call
|
||||
.arguments
|
||||
.find_argument_value("file", 0)
|
||||
.find_argument_value("opener", 7)
|
||||
.is_some_and(|expr| !expr.is_none_literal_expr())
|
||||
|| call
|
||||
.arguments
|
||||
.find_positional(0)
|
||||
.is_some_and(|expr| is_file_descriptor(expr, checker.semantic()))
|
||||
{
|
||||
return;
|
||||
@@ -236,53 +164,17 @@ pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) {
|
||||
// PTH124
|
||||
["py", "path", "local"] => PyPath.into(),
|
||||
// PTH207
|
||||
["glob", "glob"] => {
|
||||
// `dir_fd` is not supported by pathlib, so check if it's set to non-default values.
|
||||
// Signature as of Python 3.13 (https://docs.python.org/3/library/glob.html#glob.glob)
|
||||
// ```text
|
||||
// 0 1 2 3 4
|
||||
// glob.glob(pathname, *, root_dir=None, dir_fd=None, recursive=False, include_hidden=False)
|
||||
// ```
|
||||
if is_argument_non_default(&call.arguments, "dir_fd", 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
Glob {
|
||||
function: "glob".to_string(),
|
||||
}
|
||||
.into()
|
||||
["glob", "glob"] => Glob {
|
||||
function: "glob".to_string(),
|
||||
}
|
||||
|
||||
["glob", "iglob"] => {
|
||||
// `dir_fd` is not supported by pathlib, so check if it's set to non-default values.
|
||||
// Signature as of Python 3.13 (https://docs.python.org/3/library/glob.html#glob.iglob)
|
||||
// ```text
|
||||
// 0 1 2 3 4
|
||||
// glob.iglob(pathname, *, root_dir=None, dir_fd=None, recursive=False, include_hidden=False)
|
||||
// ```
|
||||
if is_argument_non_default(&call.arguments, "dir_fd", 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
Glob {
|
||||
function: "iglob".to_string(),
|
||||
}
|
||||
.into()
|
||||
.into(),
|
||||
["glob", "iglob"] => Glob {
|
||||
function: "iglob".to_string(),
|
||||
}
|
||||
.into(),
|
||||
// PTH115
|
||||
// Python 3.9+
|
||||
["os", "readlink"] if checker.target_version() >= PythonVersion::PY39 => {
|
||||
// `dir_fd` is not supported by pathlib, so check if it's set to non-default values.
|
||||
// Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.readlink)
|
||||
// ```text
|
||||
// 0 1
|
||||
// os.readlink(path, *, dir_fd=None)
|
||||
// ```
|
||||
if is_argument_non_default(&call.arguments, "dir_fd", 1) {
|
||||
return;
|
||||
}
|
||||
OsReadlink.into()
|
||||
}
|
||||
["os", "readlink"] if checker.target_version() >= PythonVersion::PY39 => OsReadlink.into(),
|
||||
// PTH208
|
||||
["os", "listdir"] => {
|
||||
if call
|
||||
@@ -332,10 +224,3 @@ fn get_name_expr(expr: &Expr) -> Option<&ast::ExprName> {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if argument `name` is set to a non-default `None` value.
|
||||
fn is_argument_non_default(arguments: &ast::Arguments, name: &str, position: usize) -> bool {
|
||||
arguments
|
||||
.find_argument_value(name, position)
|
||||
.is_some_and(|expr| !expr.is_none_literal_expr())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
PTH207.py:9:1: PTH207 Replace `glob` with `Path.glob` or `Path.rglob`
|
||||
|
|
||||
@@ -25,6 +26,4 @@ PTH207.py:11:1: PTH207 Replace `glob` with `Path.glob` or `Path.rglob`
|
||||
10 | list(glob.iglob(os.path.join(extensions_dir, "ops", "autograd", "*.cpp")))
|
||||
11 | search("*.png")
|
||||
| ^^^^^^ PTH207
|
||||
12 |
|
||||
13 | # if `dir_fd` is set, suppress the diagnostic
|
||||
|
|
||||
|
||||
@@ -93,7 +93,7 @@ PERF401.py:142:9: PERF401 Use a list comprehension to create a transformed list
|
||||
|
||||
PERF401.py:149:9: PERF401 Use a list comprehension to create a transformed list
|
||||
|
|
||||
147 | tmp = 1; result = [] # comment should be protected
|
||||
147 | tmp = 1; result = [] # commment should be protected
|
||||
148 | for i in range(10):
|
||||
149 | result.append(i + 1) # PERF401
|
||||
| ^^^^^^^^^^^^^^^^^^^^ PERF401
|
||||
@@ -102,7 +102,7 @@ PERF401.py:149:9: PERF401 Use a list comprehension to create a transformed list
|
||||
|
||||
PERF401.py:156:9: PERF401 Use a list comprehension to create a transformed list
|
||||
|
|
||||
154 | result = []; tmp = 1 # comment should be protected
|
||||
154 | result = []; tmp = 1 # commment should be protected
|
||||
155 | for i in range(10):
|
||||
156 | result.append(i + 1) # PERF401
|
||||
| ^^^^^^^^^^^^^^^^^^^^ PERF401
|
||||
|
||||
@@ -223,7 +223,7 @@ PERF401.py:142:9: PERF401 [*] Use a list comprehension to create a transformed l
|
||||
|
||||
PERF401.py:149:9: PERF401 [*] Use a list comprehension to create a transformed list
|
||||
|
|
||||
147 | tmp = 1; result = [] # comment should be protected
|
||||
147 | tmp = 1; result = [] # commment should be protected
|
||||
148 | for i in range(10):
|
||||
149 | result.append(i + 1) # PERF401
|
||||
| ^^^^^^^^^^^^^^^^^^^^ PERF401
|
||||
@@ -234,10 +234,10 @@ PERF401.py:149:9: PERF401 [*] Use a list comprehension to create a transformed l
|
||||
144 144 |
|
||||
145 145 | def f():
|
||||
146 146 | # make sure that `tmp` is not deleted
|
||||
147 |- tmp = 1; result = [] # comment should be protected
|
||||
147 |- tmp = 1; result = [] # commment should be protected
|
||||
148 |- for i in range(10):
|
||||
149 |- result.append(i + 1) # PERF401
|
||||
147 |+ tmp = 1 # comment should be protected
|
||||
147 |+ tmp = 1 # commment should be protected
|
||||
148 |+ result = [i + 1 for i in range(10)] # PERF401
|
||||
150 149 |
|
||||
151 150 |
|
||||
@@ -245,7 +245,7 @@ PERF401.py:149:9: PERF401 [*] Use a list comprehension to create a transformed l
|
||||
|
||||
PERF401.py:156:9: PERF401 [*] Use a list comprehension to create a transformed list
|
||||
|
|
||||
154 | result = []; tmp = 1 # comment should be protected
|
||||
154 | result = []; tmp = 1 # commment should be protected
|
||||
155 | for i in range(10):
|
||||
156 | result.append(i + 1) # PERF401
|
||||
| ^^^^^^^^^^^^^^^^^^^^ PERF401
|
||||
@@ -256,10 +256,10 @@ PERF401.py:156:9: PERF401 [*] Use a list comprehension to create a transformed l
|
||||
151 151 |
|
||||
152 152 | def f():
|
||||
153 153 | # make sure that `tmp` is not deleted
|
||||
154 |- result = []; tmp = 1 # comment should be protected
|
||||
154 |- result = []; tmp = 1 # commment should be protected
|
||||
155 |- for i in range(10):
|
||||
156 |- result.append(i + 1) # PERF401
|
||||
154 |+ tmp = 1 # comment should be protected
|
||||
154 |+ tmp = 1 # commment should be protected
|
||||
155 |+ result = [i + 1 for i in range(10)] # PERF401
|
||||
157 156 |
|
||||
158 157 |
|
||||
|
||||
@@ -38,11 +38,6 @@ use crate::checkers::ast::Checker;
|
||||
/// nums.add(num + 5)
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// This fix is always unsafe because it changes the program’s behavior. Replacing the
|
||||
/// original set with a copy during iteration allows code that would previously raise a
|
||||
/// `RuntimeError` to run without error.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `set`](https://docs.python.org/3/library/stdtypes.html#set)
|
||||
#[derive(ViolationMetadata)]
|
||||
|
||||
@@ -21,7 +21,6 @@ pub(crate) enum MinMax {
|
||||
/// readability.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```python
|
||||
/// minimum = min(1, 2, min(3, 4, 5))
|
||||
/// maximum = max(1, 2, max(3, 4, 5))
|
||||
@@ -29,26 +28,12 @@ pub(crate) enum MinMax {
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```python
|
||||
/// minimum = min(1, 2, 3, 4, 5)
|
||||
/// maximum = max(1, 2, 3, 4, 5)
|
||||
/// diff = maximum - minimum
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
///
|
||||
/// This fix is always unsafe and may change the program's behavior for types without full
|
||||
/// equivalence relations, such as float comparisons involving `NaN`.
|
||||
///
|
||||
/// ```python
|
||||
/// print(min(2.0, min(float("nan"), 1.0))) # before fix: 2.0
|
||||
/// print(min(2.0, float("nan"), 1.0)) # after fix: 1.0
|
||||
///
|
||||
/// print(max(1.0, max(float("nan"), 2.0))) # before fix: 1.0
|
||||
/// print(max(1.0, float("nan"), 2.0)) # after fix: 2.0
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `min`](https://docs.python.org/3/library/functions.html#min)
|
||||
/// - [Python documentation: `max`](https://docs.python.org/3/library/functions.html#max)
|
||||
|
||||
@@ -28,17 +28,6 @@ use crate::fix::edits::add_argument;
|
||||
/// Python 3.10 and later, or `locale.getpreferredencoding()` on earlier versions,
|
||||
/// to make the encoding explicit.
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// This fix is always unsafe and may change the program's behavior. It forces
|
||||
/// `encoding="utf-8"` as the default, regardless of the platform’s actual default
|
||||
/// encoding, which may cause `UnicodeDecodeError` on non-UTF-8 systems.
|
||||
/// ```python
|
||||
/// with open("test.txt") as f:
|
||||
/// print(f.read()) # before fix (on UTF-8 systems): 你好,世界!
|
||||
/// with open("test.txt", encoding="utf-8") as f:
|
||||
/// print(f.read()) # after fix (on Windows): UnicodeDecodeError
|
||||
/// ```
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// open("file.txt")
|
||||
|
||||
@@ -12,11 +12,6 @@ use crate::checkers::ast::Checker;
|
||||
/// ## Why is this bad?
|
||||
/// The import alias is redundant and should be removed to avoid confusion.
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// This fix is marked as always unsafe because the user may be intentionally
|
||||
/// re-exporting the import. While statements like `import numpy as numpy`
|
||||
/// appear redundant, they can have semantic meaning in certain contexts.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// import numpy as numpy
|
||||
|
||||
@@ -64,7 +64,7 @@ fn is_alias(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
[
|
||||
"" | "builtins",
|
||||
"EnvironmentError" | "IOError" | "WindowsError"
|
||||
] | ["mmap" | "resource" | "select" | "socket" | "os", "error"]
|
||||
] | ["mmap" | "select" | "socket" | "os", "error"]
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
UP024_2.py:10:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
8 | # Testing the modules
|
||||
9 | import socket, mmap, select, resource
|
||||
9 | import socket, mmap, select
|
||||
10 | raise socket.error
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
11 | raise mmap.error
|
||||
@@ -15,33 +15,32 @@ UP024_2.py:10:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
ℹ Safe fix
|
||||
7 7 |
|
||||
8 8 | # Testing the modules
|
||||
9 9 | import socket, mmap, select, resource
|
||||
9 9 | import socket, mmap, select
|
||||
10 |-raise socket.error
|
||||
10 |+raise OSError
|
||||
11 11 | raise mmap.error
|
||||
12 12 | raise select.error
|
||||
13 13 | raise resource.error
|
||||
13 13 |
|
||||
|
||||
UP024_2.py:11:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
9 | import socket, mmap, select, resource
|
||||
9 | import socket, mmap, select
|
||||
10 | raise socket.error
|
||||
11 | raise mmap.error
|
||||
| ^^^^^^^^^^ UP024
|
||||
12 | raise select.error
|
||||
13 | raise resource.error
|
||||
|
|
||||
= help: Replace `mmap.error` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
8 8 | # Testing the modules
|
||||
9 9 | import socket, mmap, select, resource
|
||||
9 9 | import socket, mmap, select
|
||||
10 10 | raise socket.error
|
||||
11 |-raise mmap.error
|
||||
11 |+raise OSError
|
||||
12 12 | raise select.error
|
||||
13 13 | raise resource.error
|
||||
14 14 |
|
||||
13 13 |
|
||||
14 14 | raise socket.error()
|
||||
|
||||
UP024_2.py:12:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
@@ -49,416 +48,355 @@ UP024_2.py:12:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
11 | raise mmap.error
|
||||
12 | raise select.error
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
13 | raise resource.error
|
||||
13 |
|
||||
14 | raise socket.error()
|
||||
|
|
||||
= help: Replace `select.error` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
9 9 | import socket, mmap, select, resource
|
||||
9 9 | import socket, mmap, select
|
||||
10 10 | raise socket.error
|
||||
11 11 | raise mmap.error
|
||||
12 |-raise select.error
|
||||
12 |+raise OSError
|
||||
13 13 | raise resource.error
|
||||
14 14 |
|
||||
15 15 | raise socket.error()
|
||||
13 13 |
|
||||
14 14 | raise socket.error()
|
||||
15 15 | raise mmap.error(1)
|
||||
|
||||
UP024_2.py:13:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
UP024_2.py:14:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
11 | raise mmap.error
|
||||
12 | raise select.error
|
||||
13 | raise resource.error
|
||||
| ^^^^^^^^^^^^^^ UP024
|
||||
14 |
|
||||
15 | raise socket.error()
|
||||
|
|
||||
= help: Replace `resource.error` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
10 10 | raise socket.error
|
||||
11 11 | raise mmap.error
|
||||
12 12 | raise select.error
|
||||
13 |-raise resource.error
|
||||
13 |+raise OSError
|
||||
14 14 |
|
||||
15 15 | raise socket.error()
|
||||
16 16 | raise mmap.error(1)
|
||||
|
||||
UP024_2.py:15:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
13 | raise resource.error
|
||||
14 |
|
||||
15 | raise socket.error()
|
||||
13 |
|
||||
14 | raise socket.error()
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
16 | raise mmap.error(1)
|
||||
17 | raise select.error(1, 2)
|
||||
15 | raise mmap.error(1)
|
||||
16 | raise select.error(1, 2)
|
||||
|
|
||||
= help: Replace `socket.error` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
11 11 | raise mmap.error
|
||||
12 12 | raise select.error
|
||||
13 13 | raise resource.error
|
||||
14 14 |
|
||||
15 |-raise socket.error()
|
||||
15 |+raise OSError()
|
||||
16 16 | raise mmap.error(1)
|
||||
17 17 | raise select.error(1, 2)
|
||||
18 18 | raise resource.error(1, "strerror", "filename")
|
||||
13 13 |
|
||||
14 |-raise socket.error()
|
||||
14 |+raise OSError()
|
||||
15 15 | raise mmap.error(1)
|
||||
16 16 | raise select.error(1, 2)
|
||||
17 17 |
|
||||
|
||||
UP024_2.py:16:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
UP024_2.py:15:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
15 | raise socket.error()
|
||||
16 | raise mmap.error(1)
|
||||
14 | raise socket.error()
|
||||
15 | raise mmap.error(1)
|
||||
| ^^^^^^^^^^ UP024
|
||||
17 | raise select.error(1, 2)
|
||||
18 | raise resource.error(1, "strerror", "filename")
|
||||
16 | raise select.error(1, 2)
|
||||
|
|
||||
= help: Replace `mmap.error` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
13 13 | raise resource.error
|
||||
14 14 |
|
||||
15 15 | raise socket.error()
|
||||
16 |-raise mmap.error(1)
|
||||
16 |+raise OSError(1)
|
||||
17 17 | raise select.error(1, 2)
|
||||
18 18 | raise resource.error(1, "strerror", "filename")
|
||||
19 19 |
|
||||
12 12 | raise select.error
|
||||
13 13 |
|
||||
14 14 | raise socket.error()
|
||||
15 |-raise mmap.error(1)
|
||||
15 |+raise OSError(1)
|
||||
16 16 | raise select.error(1, 2)
|
||||
17 17 |
|
||||
18 18 | raise socket.error(
|
||||
|
||||
UP024_2.py:17:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
UP024_2.py:16:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
15 | raise socket.error()
|
||||
16 | raise mmap.error(1)
|
||||
17 | raise select.error(1, 2)
|
||||
14 | raise socket.error()
|
||||
15 | raise mmap.error(1)
|
||||
16 | raise select.error(1, 2)
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
18 | raise resource.error(1, "strerror", "filename")
|
||||
17 |
|
||||
18 | raise socket.error(
|
||||
|
|
||||
= help: Replace `select.error` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
14 14 |
|
||||
15 15 | raise socket.error()
|
||||
16 16 | raise mmap.error(1)
|
||||
17 |-raise select.error(1, 2)
|
||||
17 |+raise OSError(1, 2)
|
||||
18 18 | raise resource.error(1, "strerror", "filename")
|
||||
19 19 |
|
||||
20 20 | raise socket.error(
|
||||
13 13 |
|
||||
14 14 | raise socket.error()
|
||||
15 15 | raise mmap.error(1)
|
||||
16 |-raise select.error(1, 2)
|
||||
16 |+raise OSError(1, 2)
|
||||
17 17 |
|
||||
18 18 | raise socket.error(
|
||||
19 19 | 1,
|
||||
|
||||
UP024_2.py:18:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
16 | raise mmap.error(1)
|
||||
17 | raise select.error(1, 2)
|
||||
18 | raise resource.error(1, "strerror", "filename")
|
||||
| ^^^^^^^^^^^^^^ UP024
|
||||
19 |
|
||||
20 | raise socket.error(
|
||||
|
|
||||
= help: Replace `resource.error` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
15 15 | raise socket.error()
|
||||
16 16 | raise mmap.error(1)
|
||||
17 17 | raise select.error(1, 2)
|
||||
18 |-raise resource.error(1, "strerror", "filename")
|
||||
18 |+raise OSError(1, "strerror", "filename")
|
||||
19 19 |
|
||||
20 20 | raise socket.error(
|
||||
21 21 | 1,
|
||||
|
||||
UP024_2.py:20:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
18 | raise resource.error(1, "strerror", "filename")
|
||||
19 |
|
||||
20 | raise socket.error(
|
||||
16 | raise select.error(1, 2)
|
||||
17 |
|
||||
18 | raise socket.error(
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
21 | 1,
|
||||
22 | 2,
|
||||
19 | 1,
|
||||
20 | 2,
|
||||
|
|
||||
= help: Replace `socket.error` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
17 17 | raise select.error(1, 2)
|
||||
18 18 | raise resource.error(1, "strerror", "filename")
|
||||
19 19 |
|
||||
20 |-raise socket.error(
|
||||
20 |+raise OSError(
|
||||
21 21 | 1,
|
||||
22 22 | 2,
|
||||
23 23 | 3,
|
||||
15 15 | raise mmap.error(1)
|
||||
16 16 | raise select.error(1, 2)
|
||||
17 17 |
|
||||
18 |-raise socket.error(
|
||||
18 |+raise OSError(
|
||||
19 19 | 1,
|
||||
20 20 | 2,
|
||||
21 21 | 3,
|
||||
|
||||
UP024_2.py:27:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
UP024_2.py:25:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
26 | from mmap import error
|
||||
27 | raise error
|
||||
24 | from mmap import error
|
||||
25 | raise error
|
||||
| ^^^^^ UP024
|
||||
28 |
|
||||
29 | from socket import error
|
||||
26 |
|
||||
27 | from socket import error
|
||||
|
|
||||
= help: Replace `error` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
24 24 | )
|
||||
25 25 |
|
||||
26 26 | from mmap import error
|
||||
27 |-raise error
|
||||
27 |+raise OSError
|
||||
28 28 |
|
||||
29 29 | from socket import error
|
||||
30 30 | raise error(1)
|
||||
22 22 | )
|
||||
23 23 |
|
||||
24 24 | from mmap import error
|
||||
25 |-raise error
|
||||
25 |+raise OSError
|
||||
26 26 |
|
||||
27 27 | from socket import error
|
||||
28 28 | raise error(1)
|
||||
|
||||
UP024_2.py:30:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
UP024_2.py:28:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
29 | from socket import error
|
||||
30 | raise error(1)
|
||||
27 | from socket import error
|
||||
28 | raise error(1)
|
||||
| ^^^^^ UP024
|
||||
31 |
|
||||
32 | from select import error
|
||||
29 |
|
||||
30 | from select import error
|
||||
|
|
||||
= help: Replace `error` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
27 27 | raise error
|
||||
28 28 |
|
||||
29 29 | from socket import error
|
||||
30 |-raise error(1)
|
||||
30 |+raise OSError(1)
|
||||
31 31 |
|
||||
32 32 | from select import error
|
||||
33 33 | raise error(1, 2)
|
||||
25 25 | raise error
|
||||
26 26 |
|
||||
27 27 | from socket import error
|
||||
28 |-raise error(1)
|
||||
28 |+raise OSError(1)
|
||||
29 29 |
|
||||
30 30 | from select import error
|
||||
31 31 | raise error(1, 2)
|
||||
|
||||
UP024_2.py:33:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
UP024_2.py:31:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
32 | from select import error
|
||||
33 | raise error(1, 2)
|
||||
30 | from select import error
|
||||
31 | raise error(1, 2)
|
||||
| ^^^^^ UP024
|
||||
34 |
|
||||
35 | from resource import error
|
||||
32 |
|
||||
33 | # Testing the names
|
||||
|
|
||||
= help: Replace `error` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
30 30 | raise error(1)
|
||||
31 31 |
|
||||
32 32 | from select import error
|
||||
33 |-raise error(1, 2)
|
||||
33 |+raise OSError(1, 2)
|
||||
34 34 |
|
||||
35 35 | from resource import error
|
||||
36 36 | raise error(1, "strerror", "filename")
|
||||
28 28 | raise error(1)
|
||||
29 29 |
|
||||
30 30 | from select import error
|
||||
31 |-raise error(1, 2)
|
||||
31 |+raise OSError(1, 2)
|
||||
32 32 |
|
||||
33 33 | # Testing the names
|
||||
34 34 | raise EnvironmentError
|
||||
|
||||
UP024_2.py:34:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
33 | # Testing the names
|
||||
34 | raise EnvironmentError
|
||||
| ^^^^^^^^^^^^^^^^ UP024
|
||||
35 | raise IOError
|
||||
36 | raise WindowsError
|
||||
|
|
||||
= help: Replace `EnvironmentError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
31 31 | raise error(1, 2)
|
||||
32 32 |
|
||||
33 33 | # Testing the names
|
||||
34 |-raise EnvironmentError
|
||||
34 |+raise OSError
|
||||
35 35 | raise IOError
|
||||
36 36 | raise WindowsError
|
||||
37 37 |
|
||||
|
||||
UP024_2.py:35:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
33 | # Testing the names
|
||||
34 | raise EnvironmentError
|
||||
35 | raise IOError
|
||||
| ^^^^^^^ UP024
|
||||
36 | raise WindowsError
|
||||
|
|
||||
= help: Replace `IOError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
32 32 |
|
||||
33 33 | # Testing the names
|
||||
34 34 | raise EnvironmentError
|
||||
35 |-raise IOError
|
||||
35 |+raise OSError
|
||||
36 36 | raise WindowsError
|
||||
37 37 |
|
||||
38 38 | raise EnvironmentError()
|
||||
|
||||
UP024_2.py:36:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
35 | from resource import error
|
||||
36 | raise error(1, "strerror", "filename")
|
||||
| ^^^^^ UP024
|
||||
34 | raise EnvironmentError
|
||||
35 | raise IOError
|
||||
36 | raise WindowsError
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
37 |
|
||||
38 | # Testing the names
|
||||
38 | raise EnvironmentError()
|
||||
|
|
||||
= help: Replace `error` with builtin `OSError`
|
||||
= help: Replace `WindowsError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
33 33 | raise error(1, 2)
|
||||
34 34 |
|
||||
35 35 | from resource import error
|
||||
36 |-raise error(1, "strerror", "filename")
|
||||
36 |+raise OSError(1, "strerror", "filename")
|
||||
33 33 | # Testing the names
|
||||
34 34 | raise EnvironmentError
|
||||
35 35 | raise IOError
|
||||
36 |-raise WindowsError
|
||||
36 |+raise OSError
|
||||
37 37 |
|
||||
38 38 | # Testing the names
|
||||
39 39 | raise EnvironmentError
|
||||
38 38 | raise EnvironmentError()
|
||||
39 39 | raise IOError(1)
|
||||
|
||||
UP024_2.py:38:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
36 | raise WindowsError
|
||||
37 |
|
||||
38 | raise EnvironmentError()
|
||||
| ^^^^^^^^^^^^^^^^ UP024
|
||||
39 | raise IOError(1)
|
||||
40 | raise WindowsError(1, 2)
|
||||
|
|
||||
= help: Replace `EnvironmentError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
35 35 | raise IOError
|
||||
36 36 | raise WindowsError
|
||||
37 37 |
|
||||
38 |-raise EnvironmentError()
|
||||
38 |+raise OSError()
|
||||
39 39 | raise IOError(1)
|
||||
40 40 | raise WindowsError(1, 2)
|
||||
41 41 |
|
||||
|
||||
UP024_2.py:39:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
38 | # Testing the names
|
||||
39 | raise EnvironmentError
|
||||
| ^^^^^^^^^^^^^^^^ UP024
|
||||
40 | raise IOError
|
||||
41 | raise WindowsError
|
||||
38 | raise EnvironmentError()
|
||||
39 | raise IOError(1)
|
||||
| ^^^^^^^ UP024
|
||||
40 | raise WindowsError(1, 2)
|
||||
|
|
||||
= help: Replace `EnvironmentError` with builtin `OSError`
|
||||
= help: Replace `IOError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
36 36 | raise error(1, "strerror", "filename")
|
||||
36 36 | raise WindowsError
|
||||
37 37 |
|
||||
38 38 | # Testing the names
|
||||
39 |-raise EnvironmentError
|
||||
39 |+raise OSError
|
||||
40 40 | raise IOError
|
||||
41 41 | raise WindowsError
|
||||
42 42 |
|
||||
38 38 | raise EnvironmentError()
|
||||
39 |-raise IOError(1)
|
||||
39 |+raise OSError(1)
|
||||
40 40 | raise WindowsError(1, 2)
|
||||
41 41 |
|
||||
42 42 | raise EnvironmentError(
|
||||
|
||||
UP024_2.py:40:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
38 | # Testing the names
|
||||
39 | raise EnvironmentError
|
||||
40 | raise IOError
|
||||
| ^^^^^^^ UP024
|
||||
41 | raise WindowsError
|
||||
38 | raise EnvironmentError()
|
||||
39 | raise IOError(1)
|
||||
40 | raise WindowsError(1, 2)
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
41 |
|
||||
42 | raise EnvironmentError(
|
||||
|
|
||||
= help: Replace `IOError` with builtin `OSError`
|
||||
= help: Replace `WindowsError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
37 37 |
|
||||
38 38 | # Testing the names
|
||||
39 39 | raise EnvironmentError
|
||||
40 |-raise IOError
|
||||
40 |+raise OSError
|
||||
41 41 | raise WindowsError
|
||||
42 42 |
|
||||
43 43 | raise EnvironmentError()
|
||||
38 38 | raise EnvironmentError()
|
||||
39 39 | raise IOError(1)
|
||||
40 |-raise WindowsError(1, 2)
|
||||
40 |+raise OSError(1, 2)
|
||||
41 41 |
|
||||
42 42 | raise EnvironmentError(
|
||||
43 43 | 1,
|
||||
|
||||
UP024_2.py:41:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
UP024_2.py:42:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
39 | raise EnvironmentError
|
||||
40 | raise IOError
|
||||
41 | raise WindowsError
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
42 |
|
||||
43 | raise EnvironmentError()
|
||||
|
|
||||
= help: Replace `WindowsError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
38 38 | # Testing the names
|
||||
39 39 | raise EnvironmentError
|
||||
40 40 | raise IOError
|
||||
41 |-raise WindowsError
|
||||
41 |+raise OSError
|
||||
42 42 |
|
||||
43 43 | raise EnvironmentError()
|
||||
44 44 | raise IOError(1)
|
||||
|
||||
UP024_2.py:43:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
41 | raise WindowsError
|
||||
42 |
|
||||
43 | raise EnvironmentError()
|
||||
40 | raise WindowsError(1, 2)
|
||||
41 |
|
||||
42 | raise EnvironmentError(
|
||||
| ^^^^^^^^^^^^^^^^ UP024
|
||||
44 | raise IOError(1)
|
||||
45 | raise WindowsError(1, 2)
|
||||
43 | 1,
|
||||
44 | 2,
|
||||
|
|
||||
= help: Replace `EnvironmentError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
40 40 | raise IOError
|
||||
41 41 | raise WindowsError
|
||||
42 42 |
|
||||
43 |-raise EnvironmentError()
|
||||
43 |+raise OSError()
|
||||
44 44 | raise IOError(1)
|
||||
45 45 | raise WindowsError(1, 2)
|
||||
46 46 |
|
||||
39 39 | raise IOError(1)
|
||||
40 40 | raise WindowsError(1, 2)
|
||||
41 41 |
|
||||
42 |-raise EnvironmentError(
|
||||
42 |+raise OSError(
|
||||
43 43 | 1,
|
||||
44 44 | 2,
|
||||
45 45 | 3,
|
||||
|
||||
UP024_2.py:44:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
UP024_2.py:48:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
43 | raise EnvironmentError()
|
||||
44 | raise IOError(1)
|
||||
| ^^^^^^^ UP024
|
||||
45 | raise WindowsError(1, 2)
|
||||
|
|
||||
= help: Replace `IOError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
41 41 | raise WindowsError
|
||||
42 42 |
|
||||
43 43 | raise EnvironmentError()
|
||||
44 |-raise IOError(1)
|
||||
44 |+raise OSError(1)
|
||||
45 45 | raise WindowsError(1, 2)
|
||||
46 46 |
|
||||
47 47 | raise EnvironmentError(
|
||||
|
||||
UP024_2.py:45:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
43 | raise EnvironmentError()
|
||||
44 | raise IOError(1)
|
||||
45 | raise WindowsError(1, 2)
|
||||
46 | )
|
||||
47 |
|
||||
48 | raise WindowsError
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
46 |
|
||||
47 | raise EnvironmentError(
|
||||
49 | raise EnvironmentError(1)
|
||||
50 | raise IOError(1, 2)
|
||||
|
|
||||
= help: Replace `WindowsError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
42 42 |
|
||||
43 43 | raise EnvironmentError()
|
||||
44 44 | raise IOError(1)
|
||||
45 |-raise WindowsError(1, 2)
|
||||
45 |+raise OSError(1, 2)
|
||||
46 46 |
|
||||
47 47 | raise EnvironmentError(
|
||||
48 48 | 1,
|
||||
45 45 | 3,
|
||||
46 46 | )
|
||||
47 47 |
|
||||
48 |-raise WindowsError
|
||||
48 |+raise OSError
|
||||
49 49 | raise EnvironmentError(1)
|
||||
50 50 | raise IOError(1, 2)
|
||||
|
||||
UP024_2.py:47:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
UP024_2.py:49:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
45 | raise WindowsError(1, 2)
|
||||
46 |
|
||||
47 | raise EnvironmentError(
|
||||
48 | raise WindowsError
|
||||
49 | raise EnvironmentError(1)
|
||||
| ^^^^^^^^^^^^^^^^ UP024
|
||||
48 | 1,
|
||||
49 | 2,
|
||||
50 | raise IOError(1, 2)
|
||||
|
|
||||
= help: Replace `EnvironmentError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
44 44 | raise IOError(1)
|
||||
45 45 | raise WindowsError(1, 2)
|
||||
46 46 |
|
||||
47 |-raise EnvironmentError(
|
||||
47 |+raise OSError(
|
||||
48 48 | 1,
|
||||
49 49 | 2,
|
||||
50 50 | 3,
|
||||
46 46 | )
|
||||
47 47 |
|
||||
48 48 | raise WindowsError
|
||||
49 |-raise EnvironmentError(1)
|
||||
49 |+raise OSError(1)
|
||||
50 50 | raise IOError(1, 2)
|
||||
|
||||
UP024_2.py:53:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
UP024_2.py:50:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
51 | )
|
||||
52 |
|
||||
53 | raise WindowsError
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
54 | raise EnvironmentError(1)
|
||||
55 | raise IOError(1, 2)
|
||||
|
|
||||
= help: Replace `WindowsError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
50 50 | 3,
|
||||
51 51 | )
|
||||
52 52 |
|
||||
53 |-raise WindowsError
|
||||
53 |+raise OSError
|
||||
54 54 | raise EnvironmentError(1)
|
||||
55 55 | raise IOError(1, 2)
|
||||
|
||||
UP024_2.py:54:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
53 | raise WindowsError
|
||||
54 | raise EnvironmentError(1)
|
||||
| ^^^^^^^^^^^^^^^^ UP024
|
||||
55 | raise IOError(1, 2)
|
||||
|
|
||||
= help: Replace `EnvironmentError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
51 51 | )
|
||||
52 52 |
|
||||
53 53 | raise WindowsError
|
||||
54 |-raise EnvironmentError(1)
|
||||
54 |+raise OSError(1)
|
||||
55 55 | raise IOError(1, 2)
|
||||
|
||||
UP024_2.py:55:7: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
53 | raise WindowsError
|
||||
54 | raise EnvironmentError(1)
|
||||
55 | raise IOError(1, 2)
|
||||
48 | raise WindowsError
|
||||
49 | raise EnvironmentError(1)
|
||||
50 | raise IOError(1, 2)
|
||||
| ^^^^^^^ UP024
|
||||
|
|
||||
= help: Replace `IOError` with builtin `OSError`
|
||||
|
||||
ℹ Safe fix
|
||||
52 52 |
|
||||
53 53 | raise WindowsError
|
||||
54 54 | raise EnvironmentError(1)
|
||||
55 |-raise IOError(1, 2)
|
||||
55 |+raise OSError(1, 2)
|
||||
47 47 |
|
||||
48 48 | raise WindowsError
|
||||
49 49 | raise EnvironmentError(1)
|
||||
50 |-raise IOError(1, 2)
|
||||
50 |+raise OSError(1, 2)
|
||||
|
||||
@@ -89,15 +89,4 @@ mod tests {
|
||||
assert_messages!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fstring_number_format_python_311() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("refurb/FURB116.py"),
|
||||
&settings::LinterSettings::for_rule(Rule::FStringNumberFormat)
|
||||
.with_target_version(PythonVersion::PY311),
|
||||
)?;
|
||||
assert_messages!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::{self as ast, Expr, ExprCall, Number, PythonVersion, UnaryOp};
|
||||
use ruff_source_file::find_newline;
|
||||
use ruff_python_ast::{self as ast, Expr, ExprCall, Number};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -25,11 +24,6 @@ use crate::fix::snippet::SourceCodeSnippet;
|
||||
/// ```python
|
||||
/// print(f"{1337:b}")
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// The fix is only marked as safe for integer literals, all other cases
|
||||
/// are display-only, as they may change the runtime behaviour of the program
|
||||
/// or introduce syntax errors.
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct FStringNumberFormat {
|
||||
replacement: Option<SourceCodeSnippet>,
|
||||
@@ -127,23 +121,20 @@ pub(crate) fn fstring_number_format(checker: &Checker, subscript: &ast::ExprSubs
|
||||
return;
|
||||
}
|
||||
|
||||
let maybe_number = if let Some(maybe_number) = arg
|
||||
.as_unary_op_expr()
|
||||
.filter(|unary_expr| unary_expr.op == UnaryOp::UAdd)
|
||||
.map(|unary_expr| &unary_expr.operand)
|
||||
{
|
||||
maybe_number
|
||||
} else {
|
||||
arg
|
||||
};
|
||||
// Generate a replacement, if possible.
|
||||
let replacement = if matches!(
|
||||
arg,
|
||||
Expr::NumberLiteral(_) | Expr::Name(_) | Expr::Attribute(_)
|
||||
) {
|
||||
let inner_source = checker.locator().slice(arg);
|
||||
|
||||
let applicability = if matches!(maybe_number, Expr::NumberLiteral(_)) {
|
||||
Applicability::Safe
|
||||
} else {
|
||||
Applicability::DisplayOnly
|
||||
};
|
||||
let quote = checker.stylist().quote();
|
||||
let shorthand = base.shorthand();
|
||||
|
||||
let replacement = try_create_replacement(checker, arg, base);
|
||||
Some(format!("f{quote}{{{inner_source}:{shorthand}}}{quote}"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
FStringNumberFormat {
|
||||
@@ -154,54 +145,15 @@ pub(crate) fn fstring_number_format(checker: &Checker, subscript: &ast::ExprSubs
|
||||
);
|
||||
|
||||
if let Some(replacement) = replacement {
|
||||
let edit = Edit::range_replacement(replacement, subscript.range());
|
||||
diagnostic.set_fix(Fix::applicable_edit(edit, applicability));
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
replacement,
|
||||
subscript.range(),
|
||||
)));
|
||||
}
|
||||
|
||||
checker.report_diagnostic(diagnostic);
|
||||
}
|
||||
|
||||
/// Generate a replacement, if possible.
|
||||
fn try_create_replacement(checker: &Checker, arg: &Expr, base: Base) -> Option<String> {
|
||||
if !matches!(
|
||||
arg,
|
||||
Expr::NumberLiteral(_) | Expr::Name(_) | Expr::Attribute(_) | Expr::UnaryOp(_)
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let inner_source = checker.locator().slice(arg);
|
||||
|
||||
// On Python 3.11 and earlier, trying to replace an `arg` that contains a backslash
|
||||
// would create a `SyntaxError` in the f-string.
|
||||
if checker.target_version() <= PythonVersion::PY311 && inner_source.contains('\\') {
|
||||
return None;
|
||||
}
|
||||
|
||||
// On Python 3.11 and earlier, trying to replace an `arg` that spans multiple lines
|
||||
// would create a `SyntaxError` in the f-string.
|
||||
if checker.target_version() <= PythonVersion::PY311 && find_newline(inner_source).is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let quote = checker.stylist().quote();
|
||||
let shorthand = base.shorthand();
|
||||
|
||||
// If the `arg` contains double quotes we need to create the f-string with single quotes
|
||||
// to avoid a `SyntaxError` in Python 3.11 and earlier.
|
||||
if checker.target_version() <= PythonVersion::PY311 && inner_source.contains(quote.as_str()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// If the `arg` contains a brace add an space before it to avoid a `SyntaxError`
|
||||
// in the f-string.
|
||||
if inner_source.starts_with('{') {
|
||||
Some(format!("f{quote}{{ {inner_source}:{shorthand}}}{quote}"))
|
||||
} else {
|
||||
Some(format!("f{quote}{{{inner_source}:{shorthand}}}{quote}"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum Base {
|
||||
Hex,
|
||||
|
||||
@@ -1,288 +1,144 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/refurb/mod.rs
|
||||
---
|
||||
FURB116.py:9:7: FURB116 Replace `oct` call with `f"{num:o}"`
|
||||
FURB116.py:6:7: FURB116 [*] Replace `oct` call with `f"{num:o}"`
|
||||
|
|
||||
4 | return num
|
||||
5 |
|
||||
6 | print(oct(num)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^ FURB116
|
||||
7 | print(hex(num)[2:]) # FURB116
|
||||
8 | print(bin(num)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{num:o}"`
|
||||
|
||||
ℹ Safe fix
|
||||
3 3 | def return_num() -> int:
|
||||
4 4 | return num
|
||||
5 5 |
|
||||
6 |-print(oct(num)[2:]) # FURB116
|
||||
6 |+print(f"{num:o}") # FURB116
|
||||
7 7 | print(hex(num)[2:]) # FURB116
|
||||
8 8 | print(bin(num)[2:]) # FURB116
|
||||
9 9 |
|
||||
|
||||
FURB116.py:7:7: FURB116 [*] Replace `hex` call with `f"{num:x}"`
|
||||
|
|
||||
6 | print(oct(num)[2:]) # FURB116
|
||||
7 | print(hex(num)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^ FURB116
|
||||
8 | print(bin(num)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{num:x}"`
|
||||
|
||||
ℹ Safe fix
|
||||
4 4 | return num
|
||||
5 5 |
|
||||
6 6 | print(oct(num)[2:]) # FURB116
|
||||
7 |-print(hex(num)[2:]) # FURB116
|
||||
7 |+print(f"{num:x}") # FURB116
|
||||
8 8 | print(bin(num)[2:]) # FURB116
|
||||
9 9 |
|
||||
10 10 | print(oct(1337)[2:]) # FURB116
|
||||
|
||||
FURB116.py:8:7: FURB116 [*] Replace `bin` call with `f"{num:b}"`
|
||||
|
|
||||
7 | return num
|
||||
8 |
|
||||
9 | print(oct(num)[2:]) # FURB116
|
||||
6 | print(oct(num)[2:]) # FURB116
|
||||
7 | print(hex(num)[2:]) # FURB116
|
||||
8 | print(bin(num)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^ FURB116
|
||||
10 | print(hex(num)[2:]) # FURB116
|
||||
11 | print(bin(num)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{num:o}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
6 6 | def return_num() -> int:
|
||||
7 7 | return num
|
||||
8 8 |
|
||||
9 |-print(oct(num)[2:]) # FURB116
|
||||
9 |+print(f"{num:o}") # FURB116
|
||||
10 10 | print(hex(num)[2:]) # FURB116
|
||||
11 11 | print(bin(num)[2:]) # FURB116
|
||||
12 12 |
|
||||
|
||||
FURB116.py:10:7: FURB116 Replace `hex` call with `f"{num:x}"`
|
||||
|
|
||||
9 | print(oct(num)[2:]) # FURB116
|
||||
10 | print(hex(num)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^ FURB116
|
||||
11 | print(bin(num)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{num:x}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
7 7 | return num
|
||||
8 8 |
|
||||
9 9 | print(oct(num)[2:]) # FURB116
|
||||
10 |-print(hex(num)[2:]) # FURB116
|
||||
10 |+print(f"{num:x}") # FURB116
|
||||
11 11 | print(bin(num)[2:]) # FURB116
|
||||
12 12 |
|
||||
13 13 | print(oct(1337)[2:]) # FURB116
|
||||
|
||||
FURB116.py:11:7: FURB116 Replace `bin` call with `f"{num:b}"`
|
||||
|
|
||||
9 | print(oct(num)[2:]) # FURB116
|
||||
10 | print(hex(num)[2:]) # FURB116
|
||||
11 | print(bin(num)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^ FURB116
|
||||
12 |
|
||||
13 | print(oct(1337)[2:]) # FURB116
|
||||
9 |
|
||||
10 | print(oct(1337)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{num:b}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
8 8 |
|
||||
9 9 | print(oct(num)[2:]) # FURB116
|
||||
10 10 | print(hex(num)[2:]) # FURB116
|
||||
11 |-print(bin(num)[2:]) # FURB116
|
||||
11 |+print(f"{num:b}") # FURB116
|
||||
12 12 |
|
||||
13 13 | print(oct(1337)[2:]) # FURB116
|
||||
14 14 | print(hex(1337)[2:]) # FURB116
|
||||
ℹ Safe fix
|
||||
5 5 |
|
||||
6 6 | print(oct(num)[2:]) # FURB116
|
||||
7 7 | print(hex(num)[2:]) # FURB116
|
||||
8 |-print(bin(num)[2:]) # FURB116
|
||||
8 |+print(f"{num:b}") # FURB116
|
||||
9 9 |
|
||||
10 10 | print(oct(1337)[2:]) # FURB116
|
||||
11 11 | print(hex(1337)[2:]) # FURB116
|
||||
|
||||
FURB116.py:13:7: FURB116 [*] Replace `oct` call with `f"{1337:o}"`
|
||||
FURB116.py:10:7: FURB116 [*] Replace `oct` call with `f"{1337:o}"`
|
||||
|
|
||||
11 | print(bin(num)[2:]) # FURB116
|
||||
12 |
|
||||
13 | print(oct(1337)[2:]) # FURB116
|
||||
8 | print(bin(num)[2:]) # FURB116
|
||||
9 |
|
||||
10 | print(oct(1337)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^^ FURB116
|
||||
14 | print(hex(1337)[2:]) # FURB116
|
||||
15 | print(bin(1337)[2:]) # FURB116
|
||||
11 | print(hex(1337)[2:]) # FURB116
|
||||
12 | print(bin(1337)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{1337:o}"`
|
||||
|
||||
ℹ Safe fix
|
||||
10 10 | print(hex(num)[2:]) # FURB116
|
||||
11 11 | print(bin(num)[2:]) # FURB116
|
||||
12 12 |
|
||||
13 |-print(oct(1337)[2:]) # FURB116
|
||||
13 |+print(f"{1337:o}") # FURB116
|
||||
14 14 | print(hex(1337)[2:]) # FURB116
|
||||
15 15 | print(bin(1337)[2:]) # FURB116
|
||||
16 16 | print(bin(+1337)[2:]) # FURB116
|
||||
7 7 | print(hex(num)[2:]) # FURB116
|
||||
8 8 | print(bin(num)[2:]) # FURB116
|
||||
9 9 |
|
||||
10 |-print(oct(1337)[2:]) # FURB116
|
||||
10 |+print(f"{1337:o}") # FURB116
|
||||
11 11 | print(hex(1337)[2:]) # FURB116
|
||||
12 12 | print(bin(1337)[2:]) # FURB116
|
||||
13 13 |
|
||||
|
||||
FURB116.py:14:7: FURB116 [*] Replace `hex` call with `f"{1337:x}"`
|
||||
FURB116.py:11:7: FURB116 [*] Replace `hex` call with `f"{1337:x}"`
|
||||
|
|
||||
13 | print(oct(1337)[2:]) # FURB116
|
||||
14 | print(hex(1337)[2:]) # FURB116
|
||||
10 | print(oct(1337)[2:]) # FURB116
|
||||
11 | print(hex(1337)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^^ FURB116
|
||||
15 | print(bin(1337)[2:]) # FURB116
|
||||
16 | print(bin(+1337)[2:]) # FURB116
|
||||
12 | print(bin(1337)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{1337:x}"`
|
||||
|
||||
ℹ Safe fix
|
||||
11 11 | print(bin(num)[2:]) # FURB116
|
||||
12 12 |
|
||||
13 13 | print(oct(1337)[2:]) # FURB116
|
||||
14 |-print(hex(1337)[2:]) # FURB116
|
||||
14 |+print(f"{1337:x}") # FURB116
|
||||
15 15 | print(bin(1337)[2:]) # FURB116
|
||||
16 16 | print(bin(+1337)[2:]) # FURB116
|
||||
17 17 |
|
||||
8 8 | print(bin(num)[2:]) # FURB116
|
||||
9 9 |
|
||||
10 10 | print(oct(1337)[2:]) # FURB116
|
||||
11 |-print(hex(1337)[2:]) # FURB116
|
||||
11 |+print(f"{1337:x}") # FURB116
|
||||
12 12 | print(bin(1337)[2:]) # FURB116
|
||||
13 13 |
|
||||
14 14 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
|
||||
FURB116.py:15:7: FURB116 [*] Replace `bin` call with `f"{1337:b}"`
|
||||
FURB116.py:12:7: FURB116 [*] Replace `bin` call with `f"{1337:b}"`
|
||||
|
|
||||
13 | print(oct(1337)[2:]) # FURB116
|
||||
14 | print(hex(1337)[2:]) # FURB116
|
||||
15 | print(bin(1337)[2:]) # FURB116
|
||||
10 | print(oct(1337)[2:]) # FURB116
|
||||
11 | print(hex(1337)[2:]) # FURB116
|
||||
12 | print(bin(1337)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^^ FURB116
|
||||
16 | print(bin(+1337)[2:]) # FURB116
|
||||
13 |
|
||||
14 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
|
|
||||
= help: Replace with `f"{1337:b}"`
|
||||
|
||||
ℹ Safe fix
|
||||
12 12 |
|
||||
13 13 | print(oct(1337)[2:]) # FURB116
|
||||
14 14 | print(hex(1337)[2:]) # FURB116
|
||||
15 |-print(bin(1337)[2:]) # FURB116
|
||||
15 |+print(f"{1337:b}") # FURB116
|
||||
16 16 | print(bin(+1337)[2:]) # FURB116
|
||||
17 17 |
|
||||
18 18 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
9 9 |
|
||||
10 10 | print(oct(1337)[2:]) # FURB116
|
||||
11 11 | print(hex(1337)[2:]) # FURB116
|
||||
12 |-print(bin(1337)[2:]) # FURB116
|
||||
12 |+print(f"{1337:b}") # FURB116
|
||||
13 13 |
|
||||
14 14 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
15 15 | print(bin(int(f"{num}"))[2:]) # FURB116 (no autofix)
|
||||
|
||||
FURB116.py:16:7: FURB116 [*] Replace `bin` call with `f"{+1337:b}"`
|
||||
FURB116.py:14:7: FURB116 Replace `bin` call with f-string
|
||||
|
|
||||
14 | print(hex(1337)[2:]) # FURB116
|
||||
15 | print(bin(1337)[2:]) # FURB116
|
||||
16 | print(bin(+1337)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^^^ FURB116
|
||||
17 |
|
||||
18 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
|
|
||||
= help: Replace with `f"{+1337:b}"`
|
||||
|
||||
ℹ Safe fix
|
||||
13 13 | print(oct(1337)[2:]) # FURB116
|
||||
14 14 | print(hex(1337)[2:]) # FURB116
|
||||
15 15 | print(bin(1337)[2:]) # FURB116
|
||||
16 |-print(bin(+1337)[2:]) # FURB116
|
||||
16 |+print(f"{+1337:b}") # FURB116
|
||||
17 17 |
|
||||
18 18 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
19 19 | print(bin(int(f"{num}"))[2:]) # FURB116 (no autofix)
|
||||
|
||||
FURB116.py:18:7: FURB116 Replace `bin` call with f-string
|
||||
|
|
||||
16 | print(bin(+1337)[2:]) # FURB116
|
||||
17 |
|
||||
18 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
12 | print(bin(1337)[2:]) # FURB116
|
||||
13 |
|
||||
14 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ FURB116
|
||||
19 | print(bin(int(f"{num}"))[2:]) # FURB116 (no autofix)
|
||||
15 | print(bin(int(f"{num}"))[2:]) # FURB116 (no autofix)
|
||||
|
|
||||
= help: Replace with f-string
|
||||
|
||||
FURB116.py:19:7: FURB116 Replace `bin` call with f-string
|
||||
FURB116.py:15:7: FURB116 Replace `bin` call with f-string
|
||||
|
|
||||
18 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
19 | print(bin(int(f"{num}"))[2:]) # FURB116 (no autofix)
|
||||
14 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
15 | print(bin(int(f"{num}"))[2:]) # FURB116 (no autofix)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ FURB116
|
||||
20 |
|
||||
21 | ## invalid
|
||||
16 |
|
||||
17 | ## invalid
|
||||
|
|
||||
= help: Replace with f-string
|
||||
|
||||
FURB116.py:32:7: FURB116 Replace `bin` call with `f"{d:b}"`
|
||||
|
|
||||
30 | d = datetime.datetime.now(tz=datetime.UTC)
|
||||
31 | # autofix is display-only
|
||||
32 | print(bin(d)[2:])
|
||||
| ^^^^^^^^^^ FURB116
|
||||
33 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
34 | print(bin(len("xyz").numerator)[2:])
|
||||
|
|
||||
= help: Replace with `f"{d:b}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
29 29 |
|
||||
30 30 | d = datetime.datetime.now(tz=datetime.UTC)
|
||||
31 31 | # autofix is display-only
|
||||
32 |-print(bin(d)[2:])
|
||||
32 |+print(f"{d:b}")
|
||||
33 33 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
34 34 | print(bin(len("xyz").numerator)[2:])
|
||||
35 35 |
|
||||
|
||||
FURB116.py:34:7: FURB116 Replace `bin` call with `f"{len("xyz").numerator:b}"`
|
||||
|
|
||||
32 | print(bin(d)[2:])
|
||||
33 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
34 | print(bin(len("xyz").numerator)[2:])
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB116
|
||||
35 |
|
||||
36 | # autofix is display-only
|
||||
|
|
||||
= help: Replace with `f"{len("xyz").numerator:b}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
31 31 | # autofix is display-only
|
||||
32 32 | print(bin(d)[2:])
|
||||
33 33 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
34 |-print(bin(len("xyz").numerator)[2:])
|
||||
34 |+print(f"{len("xyz").numerator:b}")
|
||||
35 35 |
|
||||
36 36 | # autofix is display-only
|
||||
37 37 | print(bin({0: 1}[0].numerator)[2:])
|
||||
|
||||
FURB116.py:37:7: FURB116 Replace `bin` call with `f"{ {0: 1}[0].numerator:b}"`
|
||||
|
|
||||
36 | # autofix is display-only
|
||||
37 | print(bin({0: 1}[0].numerator)[2:])
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB116
|
||||
38 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
39 | print(bin(ord("\\").numerator)[2:])
|
||||
|
|
||||
= help: Replace with `f"{ {0: 1}[0].numerator:b}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
34 34 | print(bin(len("xyz").numerator)[2:])
|
||||
35 35 |
|
||||
36 36 | # autofix is display-only
|
||||
37 |-print(bin({0: 1}[0].numerator)[2:])
|
||||
37 |+print(f"{ {0: 1}[0].numerator:b}")
|
||||
38 38 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
39 39 | print(bin(ord("\\").numerator)[2:])
|
||||
40 40 | print(hex(sys
|
||||
|
||||
FURB116.py:39:7: FURB116 Replace `bin` call with `f"{ord("\\").numerator:b}"`
|
||||
|
|
||||
37 | print(bin({0: 1}[0].numerator)[2:])
|
||||
38 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
39 | print(bin(ord("\\").numerator)[2:])
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB116
|
||||
40 | print(hex(sys
|
||||
41 | .maxunicode)[2:])
|
||||
|
|
||||
= help: Replace with `f"{ord("\\").numerator:b}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
36 36 | # autofix is display-only
|
||||
37 37 | print(bin({0: 1}[0].numerator)[2:])
|
||||
38 38 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
39 |-print(bin(ord("\\").numerator)[2:])
|
||||
39 |+print(f"{ord("\\").numerator:b}")
|
||||
40 40 | print(hex(sys
|
||||
41 41 | .maxunicode)[2:])
|
||||
42 42 |
|
||||
|
||||
FURB116.py:40:7: FURB116 Replace `hex` call with f-string
|
||||
|
|
||||
38 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
39 | print(bin(ord("\\").numerator)[2:])
|
||||
40 | print(hex(sys
|
||||
| _______^
|
||||
41 | | .maxunicode)[2:])
|
||||
| |________________^ FURB116
|
||||
42 |
|
||||
43 | # for negatives numbers autofix is display-only
|
||||
|
|
||||
= help: Replace with f-string
|
||||
|
||||
ℹ Display-only fix
|
||||
37 37 | print(bin({0: 1}[0].numerator)[2:])
|
||||
38 38 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
39 39 | print(bin(ord("\\").numerator)[2:])
|
||||
40 |-print(hex(sys
|
||||
41 |-.maxunicode)[2:])
|
||||
40 |+print(f"{sys
|
||||
41 |+.maxunicode:x}")
|
||||
42 42 |
|
||||
43 43 | # for negatives numbers autofix is display-only
|
||||
44 44 | print(bin(-1)[2:])
|
||||
|
||||
FURB116.py:44:7: FURB116 Replace `bin` call with `f"{-1:b}"`
|
||||
|
|
||||
43 | # for negatives numbers autofix is display-only
|
||||
44 | print(bin(-1)[2:])
|
||||
| ^^^^^^^^^^^ FURB116
|
||||
|
|
||||
= help: Replace with `f"{-1:b}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
41 41 | .maxunicode)[2:])
|
||||
42 42 |
|
||||
43 43 | # for negatives numbers autofix is display-only
|
||||
44 |-print(bin(-1)[2:])
|
||||
44 |+print(f"{-1:b}")
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/refurb/mod.rs
|
||||
---
|
||||
FURB116.py:9:7: FURB116 Replace `oct` call with `f"{num:o}"`
|
||||
|
|
||||
7 | return num
|
||||
8 |
|
||||
9 | print(oct(num)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^ FURB116
|
||||
10 | print(hex(num)[2:]) # FURB116
|
||||
11 | print(bin(num)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{num:o}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
6 6 | def return_num() -> int:
|
||||
7 7 | return num
|
||||
8 8 |
|
||||
9 |-print(oct(num)[2:]) # FURB116
|
||||
9 |+print(f"{num:o}") # FURB116
|
||||
10 10 | print(hex(num)[2:]) # FURB116
|
||||
11 11 | print(bin(num)[2:]) # FURB116
|
||||
12 12 |
|
||||
|
||||
FURB116.py:10:7: FURB116 Replace `hex` call with `f"{num:x}"`
|
||||
|
|
||||
9 | print(oct(num)[2:]) # FURB116
|
||||
10 | print(hex(num)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^ FURB116
|
||||
11 | print(bin(num)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{num:x}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
7 7 | return num
|
||||
8 8 |
|
||||
9 9 | print(oct(num)[2:]) # FURB116
|
||||
10 |-print(hex(num)[2:]) # FURB116
|
||||
10 |+print(f"{num:x}") # FURB116
|
||||
11 11 | print(bin(num)[2:]) # FURB116
|
||||
12 12 |
|
||||
13 13 | print(oct(1337)[2:]) # FURB116
|
||||
|
||||
FURB116.py:11:7: FURB116 Replace `bin` call with `f"{num:b}"`
|
||||
|
|
||||
9 | print(oct(num)[2:]) # FURB116
|
||||
10 | print(hex(num)[2:]) # FURB116
|
||||
11 | print(bin(num)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^ FURB116
|
||||
12 |
|
||||
13 | print(oct(1337)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{num:b}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
8 8 |
|
||||
9 9 | print(oct(num)[2:]) # FURB116
|
||||
10 10 | print(hex(num)[2:]) # FURB116
|
||||
11 |-print(bin(num)[2:]) # FURB116
|
||||
11 |+print(f"{num:b}") # FURB116
|
||||
12 12 |
|
||||
13 13 | print(oct(1337)[2:]) # FURB116
|
||||
14 14 | print(hex(1337)[2:]) # FURB116
|
||||
|
||||
FURB116.py:13:7: FURB116 [*] Replace `oct` call with `f"{1337:o}"`
|
||||
|
|
||||
11 | print(bin(num)[2:]) # FURB116
|
||||
12 |
|
||||
13 | print(oct(1337)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^^ FURB116
|
||||
14 | print(hex(1337)[2:]) # FURB116
|
||||
15 | print(bin(1337)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{1337:o}"`
|
||||
|
||||
ℹ Safe fix
|
||||
10 10 | print(hex(num)[2:]) # FURB116
|
||||
11 11 | print(bin(num)[2:]) # FURB116
|
||||
12 12 |
|
||||
13 |-print(oct(1337)[2:]) # FURB116
|
||||
13 |+print(f"{1337:o}") # FURB116
|
||||
14 14 | print(hex(1337)[2:]) # FURB116
|
||||
15 15 | print(bin(1337)[2:]) # FURB116
|
||||
16 16 | print(bin(+1337)[2:]) # FURB116
|
||||
|
||||
FURB116.py:14:7: FURB116 [*] Replace `hex` call with `f"{1337:x}"`
|
||||
|
|
||||
13 | print(oct(1337)[2:]) # FURB116
|
||||
14 | print(hex(1337)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^^ FURB116
|
||||
15 | print(bin(1337)[2:]) # FURB116
|
||||
16 | print(bin(+1337)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{1337:x}"`
|
||||
|
||||
ℹ Safe fix
|
||||
11 11 | print(bin(num)[2:]) # FURB116
|
||||
12 12 |
|
||||
13 13 | print(oct(1337)[2:]) # FURB116
|
||||
14 |-print(hex(1337)[2:]) # FURB116
|
||||
14 |+print(f"{1337:x}") # FURB116
|
||||
15 15 | print(bin(1337)[2:]) # FURB116
|
||||
16 16 | print(bin(+1337)[2:]) # FURB116
|
||||
17 17 |
|
||||
|
||||
FURB116.py:15:7: FURB116 [*] Replace `bin` call with `f"{1337:b}"`
|
||||
|
|
||||
13 | print(oct(1337)[2:]) # FURB116
|
||||
14 | print(hex(1337)[2:]) # FURB116
|
||||
15 | print(bin(1337)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^^ FURB116
|
||||
16 | print(bin(+1337)[2:]) # FURB116
|
||||
|
|
||||
= help: Replace with `f"{1337:b}"`
|
||||
|
||||
ℹ Safe fix
|
||||
12 12 |
|
||||
13 13 | print(oct(1337)[2:]) # FURB116
|
||||
14 14 | print(hex(1337)[2:]) # FURB116
|
||||
15 |-print(bin(1337)[2:]) # FURB116
|
||||
15 |+print(f"{1337:b}") # FURB116
|
||||
16 16 | print(bin(+1337)[2:]) # FURB116
|
||||
17 17 |
|
||||
18 18 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
|
||||
FURB116.py:16:7: FURB116 [*] Replace `bin` call with `f"{+1337:b}"`
|
||||
|
|
||||
14 | print(hex(1337)[2:]) # FURB116
|
||||
15 | print(bin(1337)[2:]) # FURB116
|
||||
16 | print(bin(+1337)[2:]) # FURB116
|
||||
| ^^^^^^^^^^^^^^ FURB116
|
||||
17 |
|
||||
18 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
|
|
||||
= help: Replace with `f"{+1337:b}"`
|
||||
|
||||
ℹ Safe fix
|
||||
13 13 | print(oct(1337)[2:]) # FURB116
|
||||
14 14 | print(hex(1337)[2:]) # FURB116
|
||||
15 15 | print(bin(1337)[2:]) # FURB116
|
||||
16 |-print(bin(+1337)[2:]) # FURB116
|
||||
16 |+print(f"{+1337:b}") # FURB116
|
||||
17 17 |
|
||||
18 18 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
19 19 | print(bin(int(f"{num}"))[2:]) # FURB116 (no autofix)
|
||||
|
||||
FURB116.py:18:7: FURB116 Replace `bin` call with f-string
|
||||
|
|
||||
16 | print(bin(+1337)[2:]) # FURB116
|
||||
17 |
|
||||
18 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ FURB116
|
||||
19 | print(bin(int(f"{num}"))[2:]) # FURB116 (no autofix)
|
||||
|
|
||||
= help: Replace with f-string
|
||||
|
||||
FURB116.py:19:7: FURB116 Replace `bin` call with f-string
|
||||
|
|
||||
18 | print(bin(return_num())[2:]) # FURB116 (no autofix)
|
||||
19 | print(bin(int(f"{num}"))[2:]) # FURB116 (no autofix)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ FURB116
|
||||
20 |
|
||||
21 | ## invalid
|
||||
|
|
||||
= help: Replace with f-string
|
||||
|
||||
FURB116.py:32:7: FURB116 Replace `bin` call with `f"{d:b}"`
|
||||
|
|
||||
30 | d = datetime.datetime.now(tz=datetime.UTC)
|
||||
31 | # autofix is display-only
|
||||
32 | print(bin(d)[2:])
|
||||
| ^^^^^^^^^^ FURB116
|
||||
33 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
34 | print(bin(len("xyz").numerator)[2:])
|
||||
|
|
||||
= help: Replace with `f"{d:b}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
29 29 |
|
||||
30 30 | d = datetime.datetime.now(tz=datetime.UTC)
|
||||
31 31 | # autofix is display-only
|
||||
32 |-print(bin(d)[2:])
|
||||
32 |+print(f"{d:b}")
|
||||
33 33 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
34 34 | print(bin(len("xyz").numerator)[2:])
|
||||
35 35 |
|
||||
|
||||
FURB116.py:34:7: FURB116 Replace `bin` call with f-string
|
||||
|
|
||||
32 | print(bin(d)[2:])
|
||||
33 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
34 | print(bin(len("xyz").numerator)[2:])
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB116
|
||||
35 |
|
||||
36 | # autofix is display-only
|
||||
|
|
||||
= help: Replace with f-string
|
||||
|
||||
FURB116.py:37:7: FURB116 Replace `bin` call with `f"{ {0: 1}[0].numerator:b}"`
|
||||
|
|
||||
36 | # autofix is display-only
|
||||
37 | print(bin({0: 1}[0].numerator)[2:])
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB116
|
||||
38 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
39 | print(bin(ord("\\").numerator)[2:])
|
||||
|
|
||||
= help: Replace with `f"{ {0: 1}[0].numerator:b}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
34 34 | print(bin(len("xyz").numerator)[2:])
|
||||
35 35 |
|
||||
36 36 | # autofix is display-only
|
||||
37 |-print(bin({0: 1}[0].numerator)[2:])
|
||||
37 |+print(f"{ {0: 1}[0].numerator:b}")
|
||||
38 38 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
39 39 | print(bin(ord("\\").numerator)[2:])
|
||||
40 40 | print(hex(sys
|
||||
|
||||
FURB116.py:39:7: FURB116 Replace `bin` call with f-string
|
||||
|
|
||||
37 | print(bin({0: 1}[0].numerator)[2:])
|
||||
38 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
39 | print(bin(ord("\\").numerator)[2:])
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB116
|
||||
40 | print(hex(sys
|
||||
41 | .maxunicode)[2:])
|
||||
|
|
||||
= help: Replace with f-string
|
||||
|
||||
FURB116.py:40:7: FURB116 Replace `hex` call with f-string
|
||||
|
|
||||
38 | # no autofix for Python 3.11 and earlier, as it introduces a syntax error
|
||||
39 | print(bin(ord("\\").numerator)[2:])
|
||||
40 | print(hex(sys
|
||||
| _______^
|
||||
41 | | .maxunicode)[2:])
|
||||
| |________________^ FURB116
|
||||
42 |
|
||||
43 | # for negatives numbers autofix is display-only
|
||||
|
|
||||
= help: Replace with f-string
|
||||
|
||||
FURB116.py:44:7: FURB116 Replace `bin` call with `f"{-1:b}"`
|
||||
|
|
||||
43 | # for negatives numbers autofix is display-only
|
||||
44 | print(bin(-1)[2:])
|
||||
| ^^^^^^^^^^^ FURB116
|
||||
|
|
||||
= help: Replace with `f"{-1:b}"`
|
||||
|
||||
ℹ Display-only fix
|
||||
41 41 | .maxunicode)[2:])
|
||||
42 42 |
|
||||
43 43 | # for negatives numbers autofix is display-only
|
||||
44 |-print(bin(-1)[2:])
|
||||
44 |+print(f"{-1:b}")
|
||||
@@ -1,7 +1,6 @@
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::{self as ast, CmpOp, Expr};
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -49,14 +48,6 @@ pub(crate) fn in_empty_collection(checker: &Checker, compare: &ast::ExprCompare)
|
||||
};
|
||||
|
||||
let semantic = checker.semantic();
|
||||
|
||||
if is_empty(right, semantic) {
|
||||
checker.report_diagnostic(Diagnostic::new(InEmptyCollection, compare.range()));
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
let set_methods = ["set", "frozenset"];
|
||||
let collection_methods = [
|
||||
"list",
|
||||
"tuple",
|
||||
@@ -68,7 +59,7 @@ fn is_empty(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
"str",
|
||||
];
|
||||
|
||||
match expr {
|
||||
let is_empty_collection = match right {
|
||||
Expr::List(ast::ExprList { elts, .. }) => elts.is_empty(),
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.is_empty(),
|
||||
Expr::Set(ast::ExprSet { elts, .. }) => elts.is_empty(),
|
||||
@@ -84,19 +75,15 @@ fn is_empty(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
arguments,
|
||||
range: _,
|
||||
}) => {
|
||||
if arguments.is_empty() {
|
||||
collection_methods
|
||||
arguments.is_empty()
|
||||
&& collection_methods
|
||||
.iter()
|
||||
.any(|s| semantic.match_builtin_expr(func, s))
|
||||
} else if let Some(arg) = arguments.find_positional(0) {
|
||||
set_methods
|
||||
.iter()
|
||||
.any(|s| semantic.match_builtin_expr(func, s))
|
||||
&& is_empty(arg, semantic)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if is_empty_collection {
|
||||
checker.report_diagnostic(Diagnostic::new(InEmptyCollection, compare.range()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,12 +61,6 @@ use super::helpers::{dataclass_kind, DataclassKind};
|
||||
/// foo = Foo() # Prints '1 2'.
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
///
|
||||
/// This fix is always marked as unsafe because, although switching to `InitVar` is usually correct,
|
||||
/// it is incorrect when the parameter is not intended to be part of the public API or when the value
|
||||
/// is meant to be shared across all instances.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: Post-init processing](https://docs.python.org/3/library/dataclasses.html#post-init-processing)
|
||||
/// - [Python documentation: Init-only variables](https://docs.python.org/3/library/dataclasses.html#init-only-variables)
|
||||
|
||||
@@ -29,14 +29,6 @@ use crate::{checkers::ast::Checker, importer::ImportRequest};
|
||||
/// pairwise(letters) # ("A", "B"), ("B", "C"), ("C", "D")
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
///
|
||||
/// The fix is always marked unsafe because it assumes that slicing an object
|
||||
/// (e.g., `obj[1:]`) produces a value with the same type and iteration behavior
|
||||
/// as the original object, which is not guaranteed for user-defined types that
|
||||
/// override `__getitem__` without properly handling slices. Moreover, the fix
|
||||
/// could delete comments.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `itertools.pairwise`](https://docs.python.org/3/library/itertools.html#itertools.pairwise)
|
||||
#[derive(ViolationMetadata)]
|
||||
|
||||
@@ -187,7 +187,6 @@ RUF060.py:20:1: RUF060 Unnecessary membership test on empty collection
|
||||
20 | b"a" in bytes()
|
||||
| ^^^^^^^^^^^^^^^ RUF060
|
||||
21 | 1 in frozenset()
|
||||
22 | 1 in set(set())
|
||||
|
|
||||
|
||||
RUF060.py:21:1: RUF060 Unnecessary membership test on empty collection
|
||||
@@ -196,35 +195,6 @@ RUF060.py:21:1: RUF060 Unnecessary membership test on empty collection
|
||||
20 | b"a" in bytes()
|
||||
21 | 1 in frozenset()
|
||||
| ^^^^^^^^^^^^^^^^ RUF060
|
||||
22 | 1 in set(set())
|
||||
23 | 2 in frozenset([])
|
||||
|
|
||||
|
||||
RUF060.py:22:1: RUF060 Unnecessary membership test on empty collection
|
||||
|
|
||||
20 | b"a" in bytes()
|
||||
21 | 1 in frozenset()
|
||||
22 | 1 in set(set())
|
||||
| ^^^^^^^^^^^^^^^ RUF060
|
||||
23 | 2 in frozenset([])
|
||||
24 | '' in set("")
|
||||
|
|
||||
|
||||
RUF060.py:23:1: RUF060 Unnecessary membership test on empty collection
|
||||
|
|
||||
21 | 1 in frozenset()
|
||||
22 | 1 in set(set())
|
||||
23 | 2 in frozenset([])
|
||||
| ^^^^^^^^^^^^^^^^^^ RUF060
|
||||
24 | '' in set("")
|
||||
|
|
||||
|
||||
RUF060.py:24:1: RUF060 Unnecessary membership test on empty collection
|
||||
|
|
||||
22 | 1 in set(set())
|
||||
23 | 2 in frozenset([])
|
||||
24 | '' in set("")
|
||||
| ^^^^^^^^^^^^^ RUF060
|
||||
25 |
|
||||
26 | # OK
|
||||
22 |
|
||||
23 | # OK
|
||||
|
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/linter.rs
|
||||
---
|
||||
<filename>:2:22: SyntaxError: named expression cannot be used within a generic definition
|
||||
|
|
||||
2 | def f[T](x: int) -> (y := 3): return x
|
||||
| ^^^^^^
|
||||
|
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user