Compare commits
97 Commits
david/fix-
...
david/gene
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1e64a0da4 | ||
|
|
65982a1e14 | ||
|
|
57d1f7132d | ||
|
|
7a75702237 | ||
|
|
9ca632c84f | ||
|
|
64fe7d30a3 | ||
|
|
beeeb8d5c5 | ||
|
|
b6fca52855 | ||
|
|
ac7f882c78 | ||
|
|
aef0a107a8 | ||
|
|
512395f4e6 | ||
|
|
5dec37fbaf | ||
|
|
4bda9dad68 | ||
|
|
9d972d0583 | ||
|
|
1bbb553d6f | ||
|
|
bb4c51afb2 | ||
|
|
3dbdd2b883 | ||
|
|
d8e43bf9f7 | ||
|
|
ee448eab2d | ||
|
|
307b7df027 | ||
|
|
e139104aba | ||
|
|
9bb9b54168 | ||
|
|
262f2767ca | ||
|
|
1de9dac9d5 | ||
|
|
1cf6c2439f | ||
|
|
2b51ec6531 | ||
|
|
b85c995927 | ||
|
|
fd7eb1e22f | ||
|
|
4de7d653bd | ||
|
|
9cb37db510 | ||
|
|
ed06fb5ce2 | ||
|
|
54df73c9f7 | ||
|
|
d7524ea6d4 | ||
|
|
bf66178959 | ||
|
|
9cdac2d6fb | ||
|
|
79706a2e26 | ||
|
|
25853e2377 | ||
|
|
61f906d8e7 | ||
|
|
08a561fc05 | ||
|
|
aa5d665d52 | ||
|
|
d55edb3d74 | ||
|
|
ab86ae1760 | ||
|
|
916968d0ff | ||
|
|
deb3d3d150 | ||
|
|
982a0a2a7c | ||
|
|
f893b19930 | ||
|
|
c96ebe3936 | ||
|
|
22ca5dd890 | ||
|
|
f7995f4aef | ||
|
|
480fb278d0 | ||
|
|
b2f364d9cb | ||
|
|
aa82137d9f | ||
|
|
adfe2438e9 | ||
|
|
3247991429 | ||
|
|
08fcf7e106 | ||
|
|
2467c4352e | ||
|
|
a27c64811e | ||
|
|
5d52902e18 | ||
|
|
eb6154f792 | ||
|
|
fdfb51b595 | ||
|
|
7ee863b6d7 | ||
|
|
8ade6c4eaf | ||
|
|
9e45bfa9fd | ||
|
|
7509d376eb | ||
|
|
a24a4b55ee | ||
|
|
888a22e849 | ||
|
|
08c1d3660c | ||
|
|
670fffef37 | ||
|
|
de63f408b9 | ||
|
|
555b9f78d6 | ||
|
|
c6516e9b60 | ||
|
|
1aaa0847ab | ||
|
|
b49aa35074 | ||
|
|
1e34f3f20a | ||
|
|
77b2cee223 | ||
|
|
200349c6e8 | ||
|
|
0d4f7dde99 | ||
|
|
cb1ba0d4c2 | ||
|
|
cda376afe0 | ||
|
|
b14fc96141 | ||
|
|
c452a2cb79 | ||
|
|
4c3e1930f6 | ||
|
|
5d7c17c20a | ||
|
|
c402bf8ae2 | ||
|
|
6bc33a041f | ||
|
|
0a0eaf5a9b | ||
|
|
8e52027a88 | ||
|
|
78db56e362 | ||
|
|
046893c186 | ||
|
|
9cea752934 | ||
|
|
3b913ce652 | ||
|
|
aee9350df1 | ||
|
|
4e97b97a76 | ||
|
|
00214fc60c | ||
|
|
ec5584219e | ||
|
|
d5e48a0f80 | ||
|
|
f40a0b3800 |
30
.github/workflows/ci.yaml
vendored
30
.github/workflows/ci.yaml
vendored
@@ -259,6 +259,10 @@ jobs:
|
||||
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
|
||||
with:
|
||||
enable-cache: "true"
|
||||
- name: ty mdtests (GitHub annotations)
|
||||
if: ${{ needs.determine_changes.outputs.ty == 'true' }}
|
||||
env:
|
||||
@@ -317,6 +321,10 @@ jobs:
|
||||
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
|
||||
with:
|
||||
enable-cache: "true"
|
||||
- name: "Run tests"
|
||||
shell: bash
|
||||
env:
|
||||
@@ -340,6 +348,10 @@ jobs:
|
||||
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
|
||||
with:
|
||||
enable-cache: "true"
|
||||
- name: "Run tests"
|
||||
shell: bash
|
||||
env:
|
||||
@@ -441,9 +453,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo-binstall"
|
||||
uses: cargo-bins/cargo-binstall@2bb61346d075e720d4c3da92f23b6d612d5a7543 # v1.15.3
|
||||
with:
|
||||
tool: cargo-fuzz@0.11.2
|
||||
uses: cargo-bins/cargo-binstall@837578dfb436769f1e6669b2e23ffea9d9d2da8f # v1.15.4
|
||||
- name: "Install cargo-fuzz"
|
||||
# Download the latest version from quick install and not the github releases because github releases only has MUSL targets.
|
||||
run: cargo binstall cargo-fuzz --force --disable-strategies crate-meta-data --no-confirm
|
||||
@@ -463,7 +473,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
- uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
name: Download Ruff binary to test
|
||||
id: download-cached-binary
|
||||
@@ -664,7 +674,7 @@ jobs:
|
||||
branch: ${{ github.event.pull_request.base.ref }}
|
||||
workflow: "ci.yaml"
|
||||
check_artifacts: true
|
||||
- uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
- uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
- name: Fuzz
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
@@ -694,7 +704,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: cargo-bins/cargo-binstall@2bb61346d075e720d4c3da92f23b6d612d5a7543 # v1.15.3
|
||||
- uses: cargo-bins/cargo-binstall@837578dfb436769f1e6669b2e23ffea9d9d2da8f # v1.15.4
|
||||
- run: cargo binstall --no-confirm cargo-shear
|
||||
- run: cargo shear
|
||||
|
||||
@@ -734,7 +744,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
- uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
@@ -777,7 +787,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: uv pip install -r docs/requirements-insiders.txt --system
|
||||
@@ -909,7 +919,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
||||
- uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
- uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
@@ -942,7 +952,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
||||
- uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
- uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
|
||||
2
.github/workflows/daily_fuzz.yaml
vendored
2
.github/workflows/daily_fuzz.yaml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
- uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
|
||||
4
.github/workflows/mypy_primer.yaml
vendored
4
.github/workflows/mypy_primer.yaml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
|
||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
||||
with:
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
|
||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
||||
with:
|
||||
|
||||
2
.github/workflows/publish-pypi.yml
vendored
2
.github/workflows/publish-pypi.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
pattern: wheels-*
|
||||
|
||||
6
.github/workflows/sync_typeshed.yaml
vendored
6
.github/workflows/sync_typeshed.yaml
vendored
@@ -65,7 +65,7 @@ jobs:
|
||||
run: |
|
||||
git config --global user.name typeshedbot
|
||||
git config --global user.email '<>'
|
||||
- uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
- uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
- name: Sync typeshed stubs
|
||||
run: |
|
||||
rm -rf "ruff/${VENDORED_TYPESHED}"
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ env.UPSTREAM_BRANCH}}
|
||||
- uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
- uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
- name: Setup git
|
||||
run: |
|
||||
git config --global user.name typeshedbot
|
||||
@@ -155,7 +155,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ env.UPSTREAM_BRANCH}}
|
||||
- uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
- uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
- name: Setup git
|
||||
run: |
|
||||
git config --global user.name typeshedbot
|
||||
|
||||
5
.github/workflows/ty-ecosystem-analyzer.yaml
vendored
5
.github/workflows/ty-ecosystem-analyzer.yaml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
|
||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
||||
with:
|
||||
@@ -64,11 +64,12 @@ jobs:
|
||||
|
||||
cd ..
|
||||
|
||||
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@27dd66d9e397d986ef9c631119ee09556eab8af9"
|
||||
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@1f560d07d672effae250e3d271da53d96c5260ff"
|
||||
|
||||
ecosystem-analyzer \
|
||||
--repository ruff \
|
||||
diff \
|
||||
--profile=release \
|
||||
--projects-old ruff/projects_old.txt \
|
||||
--projects-new ruff/projects_new.txt \
|
||||
--old old_commit \
|
||||
|
||||
2
.github/workflows/ty-ecosystem-report.yaml
vendored
2
.github/workflows/ty-ecosystem-report.yaml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b # v6.6.0
|
||||
uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
||||
|
||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
||||
with:
|
||||
|
||||
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,5 +1,33 @@
|
||||
# Changelog
|
||||
|
||||
## 0.12.12
|
||||
|
||||
### Preview features
|
||||
|
||||
- Show fixes by default ([#19919](https://github.com/astral-sh/ruff/pull/19919))
|
||||
- \[`airflow`\] Convert `DatasetOrTimeSchedule(datasets=...)` to `AssetOrTimeSchedule(assets=...)` (`AIR311`) ([#20202](https://github.com/astral-sh/ruff/pull/20202))
|
||||
- \[`airflow`\] Improve the `AIR002` error message ([#20173](https://github.com/astral-sh/ruff/pull/20173))
|
||||
- \[`airflow`\] Move `airflow.operators.postgres_operator.Mapping` from `AIR302` to `AIR301` ([#20172](https://github.com/astral-sh/ruff/pull/20172))
|
||||
- \[`flake8-async`\] Implement `blocking-input` rule (`ASYNC250`) ([#20122](https://github.com/astral-sh/ruff/pull/20122))
|
||||
- \[`flake8-use-pathlib`\] Make `PTH119` and `PTH120` fixes unsafe because they can change behavior ([#20118](https://github.com/astral-sh/ruff/pull/20118))
|
||||
- \[`pylint`\] Add U+061C to `PLE2502` ([#20106](https://github.com/astral-sh/ruff/pull/20106))
|
||||
- \[`ruff`\] Fix false negative for empty f-strings in `deque` calls (`RUF037`) ([#20109](https://github.com/astral-sh/ruff/pull/20109))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Less confidently mark f-strings as empty when inferring truthiness ([#20152](https://github.com/astral-sh/ruff/pull/20152))
|
||||
- \[`fastapi`\] Fix false positive for paths with spaces around parameters (`FAST003`) ([#20077](https://github.com/astral-sh/ruff/pull/20077))
|
||||
- \[`flake8-comprehensions`\] Skip `C417` when lambda contains `yield`/`yield from` ([#20201](https://github.com/astral-sh/ruff/pull/20201))
|
||||
- \[`perflint`\] Handle tuples in dictionary comprehensions (`PERF403`) ([#19934](https://github.com/astral-sh/ruff/pull/19934))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`pycodestyle`\] Preserve return type annotation for `ParamSpec` (`E731`) ([#20108](https://github.com/astral-sh/ruff/pull/20108))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Add fix safety sections to docs ([#17490](https://github.com/astral-sh/ruff/pull/17490),[#17499](https://github.com/astral-sh/ruff/pull/17499))
|
||||
|
||||
## 0.12.11
|
||||
|
||||
### Preview features
|
||||
|
||||
223
Cargo.lock
generated
223
Cargo.lock
generated
@@ -257,9 +257,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.3"
|
||||
version = "2.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d"
|
||||
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
@@ -295,7 +295,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata 0.4.10",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -408,9 +408,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.46"
|
||||
version = "4.5.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57"
|
||||
checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -418,9 +418,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.46"
|
||||
version = "4.5.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41"
|
||||
checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -461,9 +461,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.45"
|
||||
version = "4.5.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6"
|
||||
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -603,7 +603,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -612,7 +612,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.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1035,7 +1035,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1231,8 +1231,8 @@ dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr",
|
||||
"log",
|
||||
"regex-automata 0.4.10",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1241,7 +1241,7 @@ version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"ignore",
|
||||
"walkdir",
|
||||
]
|
||||
@@ -1459,7 +1459,7 @@ dependencies = [
|
||||
"globset",
|
||||
"log",
|
||||
"memchr",
|
||||
"regex-automata 0.4.10",
|
||||
"regex-automata",
|
||||
"same-file",
|
||||
"walkdir",
|
||||
"winapi-util",
|
||||
@@ -1521,7 +1521,7 @@ version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"inotify-sys",
|
||||
"libc",
|
||||
]
|
||||
@@ -1537,9 +1537,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.43.1"
|
||||
version = "1.43.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "154934ea70c58054b556dd430b99a98c2a7ff5309ac9891597e339b5c28f4371"
|
||||
checksum = "46fdb647ebde000f43b5b53f773c30cf9b0cb4300453208713fa38b2c70935a0"
|
||||
dependencies = [
|
||||
"console 0.15.11",
|
||||
"globset",
|
||||
@@ -1617,7 +1617,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1681,7 +1681,7 @@ dependencies = [
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1728,9 +1728,9 @@ checksum = "a037eddb7d28de1d0fc42411f501b53b75838d313908078d6698d064f3029b24"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.77"
|
||||
version = "0.3.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||
checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
@@ -1770,9 +1770,9 @@ checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||
|
||||
[[package]]
|
||||
name = "libcst"
|
||||
version = "1.8.2"
|
||||
version = "1.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae28ddc5b90c3e3146a21d051ca095cbc8d932ad8714cf65ddf71a9abb35684c"
|
||||
checksum = "052ef5d9fc958a51aeebdf3713573b36c6fd6eed0bf0e60e204d2c0f8cf19b9f"
|
||||
dependencies = [
|
||||
"annotate-snippets",
|
||||
"libcst_derive",
|
||||
@@ -1785,9 +1785,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libcst_derive"
|
||||
version = "1.8.2"
|
||||
version = "1.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc2de5c2f62bcf8a4f7290b1854388b262c4b68f1db1a3ee3ef6d4c1319b00a3"
|
||||
checksum = "a91a751afee92cbdd59d4bc6754c7672712eec2d30a308f23de4e3287b2929cb"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -1809,7 +1809,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -1850,9 +1850,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
|
||||
[[package]]
|
||||
name = "lsp-server"
|
||||
@@ -1913,11 +1913,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata 0.1.10",
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2014,7 +2014,7 @@ version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
@@ -2026,7 +2026,7 @@ version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
@@ -2054,7 +2054,7 @@ version = "8.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"fsevent-sys",
|
||||
"inotify",
|
||||
"kqueue",
|
||||
@@ -2074,12 +2074,11 @@ checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
version = "0.50.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
|
||||
dependencies = [
|
||||
"overload",
|
||||
"winapi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2154,12 +2153,6 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.4"
|
||||
@@ -2666,7 +2659,7 @@ version = "0.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2688,17 +2681,8 @@ checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.10",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
dependencies = [
|
||||
"regex-syntax 0.6.29",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2709,7 +2693,7 @@ checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2718,12 +2702,6 @@ version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
@@ -2743,13 +2721,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.12.11"
|
||||
version = "0.12.12"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argfile",
|
||||
"assert_fs",
|
||||
"bincode 2.0.1",
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"cachedir",
|
||||
"clap",
|
||||
"clap_complete_command",
|
||||
@@ -2999,11 +2977,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.12.11"
|
||||
version = "0.12.12"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"anyhow",
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"clap",
|
||||
"colored 3.0.0",
|
||||
"fern",
|
||||
@@ -3108,7 +3086,7 @@ name = "ruff_python_ast"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"compact_str",
|
||||
"get-size2",
|
||||
"is-macro",
|
||||
@@ -3196,7 +3174,7 @@ dependencies = [
|
||||
name = "ruff_python_literal"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"itertools 0.14.0",
|
||||
"ruff_python_ast",
|
||||
"unic-ucd-category",
|
||||
@@ -3207,7 +3185,7 @@ name = "ruff_python_parser"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"bstr",
|
||||
"compact_str",
|
||||
"get-size2",
|
||||
@@ -3232,7 +3210,7 @@ dependencies = [
|
||||
name = "ruff_python_semantic"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"insta",
|
||||
"is-macro",
|
||||
"ruff_cache",
|
||||
@@ -3253,7 +3231,7 @@ dependencies = [
|
||||
name = "ruff_python_stdlib"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
@@ -3337,7 +3315,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_wasm"
|
||||
version = "0.12.11"
|
||||
version = "0.12.12"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
@@ -3430,11 +3408,11 @@ version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3827,7 +3805,7 @@ dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4161,15 +4139,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.19"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
@@ -4240,7 +4218,8 @@ dependencies = [
|
||||
name = "ty_ide"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"camino",
|
||||
"get-size2",
|
||||
"insta",
|
||||
"itertools 0.14.0",
|
||||
@@ -4278,7 +4257,7 @@ dependencies = [
|
||||
"pep440_rs",
|
||||
"rayon",
|
||||
"regex",
|
||||
"regex-automata 0.4.10",
|
||||
"regex-automata",
|
||||
"ruff_cache",
|
||||
"ruff_db",
|
||||
"ruff_macros",
|
||||
@@ -4304,7 +4283,7 @@ name = "ty_python_semantic"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"bitvec",
|
||||
"camino",
|
||||
"colored 3.0.0",
|
||||
@@ -4357,7 +4336,7 @@ name = "ty_server"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"crossbeam",
|
||||
"dunce",
|
||||
"insta",
|
||||
@@ -4400,7 +4379,7 @@ name = "ty_test"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
"camino",
|
||||
"colored 3.0.0",
|
||||
"insta",
|
||||
@@ -4759,21 +4738,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.100"
|
||||
version = "0.2.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||
checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.100"
|
||||
version = "0.2.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||
checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
@@ -4785,9 +4765,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.50"
|
||||
version = "0.4.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
|
||||
checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@@ -4798,9 +4778,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.100"
|
||||
version = "0.2.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||
checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -4808,9 +4788,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.100"
|
||||
version = "0.2.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4821,18 +4801,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.100"
|
||||
version = "0.2.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||
checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.50"
|
||||
version = "0.3.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3"
|
||||
checksum = "80cc7f8a4114fdaa0c58383caf973fc126cf004eba25c9dc639bccd3880d55ad"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"minicov",
|
||||
@@ -4843,9 +4823,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.50"
|
||||
version = "0.3.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b"
|
||||
checksum = "c5ada2ab788d46d4bda04c9d567702a79c8ced14f51f221646a16ed39d0e6a5d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4854,9 +4834,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.77"
|
||||
version = "0.3.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
|
||||
checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -4892,37 +4872,15 @@ dependencies = [
|
||||
"glob",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
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.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.2"
|
||||
@@ -4982,6 +4940,15 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
@@ -5150,7 +5117,7 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"bitflags 2.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
10
Cargo.toml
10
Cargo.toml
@@ -115,7 +115,7 @@ jiff = { version = "0.2.0" }
|
||||
js-sys = { version = "0.3.69" }
|
||||
jod-thread = { version = "1.0.0" }
|
||||
libc = { version = "0.2.153" }
|
||||
libcst = { version = "1.1.0", default-features = false }
|
||||
libcst = { version = "1.8.4", default-features = false }
|
||||
log = { version = "0.4.17" }
|
||||
lsp-server = { version = "0.7.6" }
|
||||
lsp-types = { git = "https://github.com/astral-sh/lsp-types.git", rev = "3512a9f", features = [
|
||||
@@ -251,6 +251,14 @@ rest_pat_in_fully_bound_structs = "warn"
|
||||
redundant_clone = "warn"
|
||||
debug_assert_with_mut_call = "warn"
|
||||
unused_peekable = "warn"
|
||||
# This lint sometimes flags code whose `if` and `else`
|
||||
# bodies could be flipped when a `!` operator is removed.
|
||||
# While perhaps sometimes a good idea, it is also often
|
||||
# not a good idea due to other factors impacting
|
||||
# readability. For example, if flipping the bodies results
|
||||
# in the `if` being an order of magnitude bigger than the
|
||||
# `else`, then some might consider that harder to read.
|
||||
if_not_else = "allow"
|
||||
|
||||
# Diagnostics are not actionable: Enable once https://github.com/rust-lang/rust-clippy/issues/13774 is resolved.
|
||||
large_stack_arrays = "allow"
|
||||
|
||||
@@ -148,8 +148,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.12.11/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.12.11/install.ps1 | iex"
|
||||
curl -LsSf https://astral.sh/ruff/0.12.12/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.12.12/install.ps1 | iex"
|
||||
```
|
||||
|
||||
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
||||
@@ -182,7 +182,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.12.11
|
||||
rev: v0.12.12
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff-check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.12.11"
|
||||
version = "0.12.12"
|
||||
publish = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -1489,6 +1489,8 @@ fn deprecated_direct() {
|
||||
|
||||
#[test]
|
||||
fn deprecated_multiple_direct() {
|
||||
// Multiple deprecated rules selected by exact code should be included
|
||||
// but a warning should be displayed
|
||||
let mut cmd = RuffCheck::default()
|
||||
.args(["--select", "RUF920", "--select", "RUF921"])
|
||||
.build();
|
||||
@@ -1516,16 +1518,10 @@ fn deprecated_indirect() {
|
||||
// since it is not a "direct" selection
|
||||
let mut cmd = RuffCheck::default().args(["--select", "RUF92"]).build();
|
||||
assert_cmd_snapshot!(cmd, @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
RUF920 Hey this is a deprecated test rule.
|
||||
--> -:1:1
|
||||
|
||||
RUF921 Hey this is another deprecated test rule.
|
||||
--> -:1:1
|
||||
|
||||
Found 2 errors.
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -2155,16 +2151,10 @@ extend-safe-fixes = ["RUF9"]
|
||||
RUF903 Hey this is a stable test rule with a display only fix.
|
||||
--> -:1:1
|
||||
|
||||
RUF920 Hey this is a deprecated test rule.
|
||||
--> -:1:1
|
||||
|
||||
RUF921 Hey this is another deprecated test rule.
|
||||
--> -:1:1
|
||||
|
||||
RUF950 Hey this is a test rule that was redirected from another.
|
||||
--> -:1:1
|
||||
|
||||
Found 7 errors.
|
||||
Found 5 errors.
|
||||
[*] 1 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option).
|
||||
|
||||
----- stderr -----
|
||||
|
||||
@@ -5780,28 +5780,6 @@ match 42: # invalid-syntax
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn future_annotations_preview_warning() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(["--config", "lint.future-annotations = true"])
|
||||
.args(["--select", "F"])
|
||||
.arg("--no-preview")
|
||||
.arg("-")
|
||||
.pass_stdin("1"),
|
||||
@r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
warning: The `lint.future-annotations` setting will have no effect because `preview` is disabled
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn up045_nested_optional_flatten_all() {
|
||||
let contents = "\
|
||||
|
||||
@@ -55,6 +55,10 @@ either a redundant alias or, if already present in the file, an `__all__` entry.
|
||||
to remove third-party and standard library imports -- the fix is unsafe because the module's
|
||||
interface changes.
|
||||
|
||||
See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc)
|
||||
for more details on how Ruff
|
||||
determines whether an import is first or third-party.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
@@ -83,11 +87,6 @@ else:
|
||||
print("numpy is not installed")
|
||||
```
|
||||
|
||||
## Preview
|
||||
When [preview](https://docs.astral.sh/ruff/preview/) is enabled,
|
||||
the criterion for determining whether an import is first-party
|
||||
is stricter, which could affect the suggested fix. See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) for more details.
|
||||
|
||||
## Options
|
||||
- `lint.ignore-init-module-imports`
|
||||
- `lint.pyflakes.allowed-unused-imports`
|
||||
|
||||
@@ -95,7 +95,7 @@ exit_code: 1
|
||||
"rules": [
|
||||
{
|
||||
"fullDescription": {
|
||||
"text": "## What it does\nChecks for unused imports.\n\n## Why is this bad?\nUnused imports add a performance overhead at runtime, and risk creating\nimport cycles. They also increase the cognitive load of reading the code.\n\nIf an import statement is used to check for the availability or existence\nof a module, consider using `importlib.util.find_spec` instead.\n\nIf an import statement is used to re-export a symbol as part of a module's\npublic interface, consider using a \"redundant\" import alias, which\ninstructs Ruff (and other tools) to respect the re-export, and avoid\nmarking it as unused, as in:\n\n```python\nfrom module import member as member\n```\n\nAlternatively, you can use `__all__` to declare a symbol as part of the module's\ninterface, as in:\n\n```python\n# __init__.py\nimport some_module\n\n__all__ = [\"some_module\"]\n```\n\n## Fix safety\n\nFixes to remove unused imports are safe, except in `__init__.py` files.\n\nApplying fixes to `__init__.py` files is currently in preview. The fix offered depends on the\ntype of the unused import. Ruff will suggest a safe fix to export first-party imports with\neither a redundant alias or, if already present in the file, an `__all__` entry. If multiple\n`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix\nto remove third-party and standard library imports -- the fix is unsafe because the module's\ninterface changes.\n\n## Example\n\n```python\nimport numpy as np # unused import\n\n\ndef area(radius):\n return 3.14 * radius**2\n```\n\nUse instead:\n\n```python\ndef area(radius):\n return 3.14 * radius**2\n```\n\nTo check the availability of a module, use `importlib.util.find_spec`:\n\n```python\nfrom importlib.util import find_spec\n\nif find_spec(\"numpy\") is not None:\n print(\"numpy is installed\")\nelse:\n print(\"numpy is not installed\")\n```\n\n## Preview\nWhen [preview](https://docs.astral.sh/ruff/preview/) is enabled,\nthe criterion for determining whether an import is first-party\nis stricter, which could affect the suggested fix. See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) for more details.\n\n## Options\n- `lint.ignore-init-module-imports`\n- `lint.pyflakes.allowed-unused-imports`\n\n## References\n- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)\n- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)\n- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)\n"
|
||||
"text": "## What it does\nChecks for unused imports.\n\n## Why is this bad?\nUnused imports add a performance overhead at runtime, and risk creating\nimport cycles. They also increase the cognitive load of reading the code.\n\nIf an import statement is used to check for the availability or existence\nof a module, consider using `importlib.util.find_spec` instead.\n\nIf an import statement is used to re-export a symbol as part of a module's\npublic interface, consider using a \"redundant\" import alias, which\ninstructs Ruff (and other tools) to respect the re-export, and avoid\nmarking it as unused, as in:\n\n```python\nfrom module import member as member\n```\n\nAlternatively, you can use `__all__` to declare a symbol as part of the module's\ninterface, as in:\n\n```python\n# __init__.py\nimport some_module\n\n__all__ = [\"some_module\"]\n```\n\n## Fix safety\n\nFixes to remove unused imports are safe, except in `__init__.py` files.\n\nApplying fixes to `__init__.py` files is currently in preview. The fix offered depends on the\ntype of the unused import. Ruff will suggest a safe fix to export first-party imports with\neither a redundant alias or, if already present in the file, an `__all__` entry. If multiple\n`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix\nto remove third-party and standard library imports -- the fix is unsafe because the module's\ninterface changes.\n\nSee [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc)\nfor more details on how Ruff\ndetermines whether an import is first or third-party.\n\n## Example\n\n```python\nimport numpy as np # unused import\n\n\ndef area(radius):\n return 3.14 * radius**2\n```\n\nUse instead:\n\n```python\ndef area(radius):\n return 3.14 * radius**2\n```\n\nTo check the availability of a module, use `importlib.util.find_spec`:\n\n```python\nfrom importlib.util import find_spec\n\nif find_spec(\"numpy\") is not None:\n print(\"numpy is installed\")\nelse:\n print(\"numpy is not installed\")\n```\n\n## Options\n- `lint.ignore-init-module-imports`\n- `lint.pyflakes.allowed-unused-imports`\n\n## References\n- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)\n- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)\n- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)\n"
|
||||
},
|
||||
"help": {
|
||||
"text": "`{name}` imported but unused; consider using `importlib.util.find_spec` to test for availability"
|
||||
|
||||
@@ -454,24 +454,26 @@ impl Diagnostic {
|
||||
|
||||
/// Computes the start source location for the message.
|
||||
///
|
||||
/// Panics if the diagnostic has no primary span, if its file is not a `SourceFile`, or if the
|
||||
/// span has no range.
|
||||
pub fn expect_ruff_start_location(&self) -> LineColumn {
|
||||
self.expect_primary_span()
|
||||
.expect_ruff_file()
|
||||
.to_source_code()
|
||||
.line_column(self.expect_range().start())
|
||||
/// Returns None if the diagnostic has no primary span, if its file is not a `SourceFile`,
|
||||
/// or if the span has no range.
|
||||
pub fn ruff_start_location(&self) -> Option<LineColumn> {
|
||||
Some(
|
||||
self.ruff_source_file()?
|
||||
.to_source_code()
|
||||
.line_column(self.range()?.start()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Computes the end source location for the message.
|
||||
///
|
||||
/// Panics if the diagnostic has no primary span, if its file is not a `SourceFile`, or if the
|
||||
/// span has no range.
|
||||
pub fn expect_ruff_end_location(&self) -> LineColumn {
|
||||
self.expect_primary_span()
|
||||
.expect_ruff_file()
|
||||
.to_source_code()
|
||||
.line_column(self.expect_range().end())
|
||||
/// Returns None if the diagnostic has no primary span, if its file is not a `SourceFile`,
|
||||
/// or if the span has no range.
|
||||
pub fn ruff_end_location(&self) -> Option<LineColumn> {
|
||||
Some(
|
||||
self.ruff_source_file()?
|
||||
.to_source_code()
|
||||
.line_column(self.range()?.end()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the [`SourceFile`] which the message belongs to.
|
||||
@@ -501,13 +503,18 @@ impl Diagnostic {
|
||||
|
||||
/// Returns the ordering of diagnostics based on the start of their ranges, if they have any.
|
||||
///
|
||||
/// Panics if either diagnostic has no primary span, if the span has no range, or if its file is
|
||||
/// not a `SourceFile`.
|
||||
/// Panics if either diagnostic has no primary span, or if its file is not a `SourceFile`.
|
||||
pub fn ruff_start_ordering(&self, other: &Self) -> std::cmp::Ordering {
|
||||
(self.expect_ruff_source_file(), self.expect_range().start()).cmp(&(
|
||||
let a = (
|
||||
self.expect_ruff_source_file(),
|
||||
self.range().map(|r| r.start()),
|
||||
);
|
||||
let b = (
|
||||
other.expect_ruff_source_file(),
|
||||
other.expect_range().start(),
|
||||
))
|
||||
other.range().map(|r| r.start()),
|
||||
);
|
||||
|
||||
a.cmp(&b)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1444,7 +1451,7 @@ pub enum DiagnosticFormat {
|
||||
Junit,
|
||||
/// Print diagnostics in the JSON format used by GitLab [Code Quality] reports.
|
||||
///
|
||||
/// [Code Quality]: https://docs.gitlab.com/ee/ci/testing/code_quality.html#implement-a-custom-tool
|
||||
/// [Code Quality]: https://docs.gitlab.com/ci/testing/code_quality/#code-quality-report-format
|
||||
#[cfg(feature = "serde")]
|
||||
Gitlab,
|
||||
}
|
||||
|
||||
@@ -81,14 +81,19 @@ impl IndentStyle {
|
||||
pub const fn is_space(&self) -> bool {
|
||||
matches!(self, IndentStyle::Space)
|
||||
}
|
||||
|
||||
/// Returns the string representation of the indent style.
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
IndentStyle::Tab => "tab",
|
||||
IndentStyle::Space => "space",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for IndentStyle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
IndentStyle::Tab => std::write!(f, "tab"),
|
||||
IndentStyle::Space => std::write!(f, "space"),
|
||||
}
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -139,4 +139,16 @@ impl LineEnding {
|
||||
LineEnding::CarriageReturn => "\r",
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the string used to configure this line ending.
|
||||
///
|
||||
/// See [`LineEnding::as_str`] for the actual string representation of the line ending.
|
||||
#[inline]
|
||||
pub const fn as_setting_str(&self) -> &'static str {
|
||||
match self {
|
||||
LineEnding::LineFeed => "lf",
|
||||
LineEnding::CarriageReturnLineFeed => "crlf",
|
||||
LineEnding::CarriageReturn => "cr",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.12.11"
|
||||
version = "0.12.12"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -12,6 +12,7 @@ from airflow import (
|
||||
from airflow.api_connexion.security import requires_access
|
||||
from airflow.contrib.aws_athena_hook import AWSAthenaHook
|
||||
from airflow.datasets import DatasetAliasEvent
|
||||
from airflow.operators.postgres_operator import Mapping
|
||||
from airflow.operators.subdag import SubDagOperator
|
||||
from airflow.secrets.cache import SecretCache
|
||||
from airflow.secrets.local_filesystem import LocalFilesystemBackend
|
||||
@@ -52,6 +53,8 @@ DatasetAliasEvent()
|
||||
# airflow.operators.subdag.*
|
||||
SubDagOperator()
|
||||
|
||||
# airflow.operators.postgres_operator
|
||||
Mapping()
|
||||
|
||||
# airflow.secrets
|
||||
# get_connection
|
||||
|
||||
@@ -70,7 +70,7 @@ from airflow.timetables.datasets import DatasetOrTimeSchedule
|
||||
from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
|
||||
# airflow.timetables.datasets
|
||||
DatasetOrTimeSchedule()
|
||||
DatasetOrTimeSchedule(datasets=[])
|
||||
|
||||
# airflow.utils.dag_parsing_context
|
||||
get_parsing_context()
|
||||
|
||||
@@ -75,3 +75,7 @@ list(map(lambda x, y: x, [(1, 2), (3, 4)]))
|
||||
_ = t"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
_ = t"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
|
||||
|
||||
# See https://github.com/astral-sh/ruff/issues/20198
|
||||
# No error: lambda contains `yield`, so map() should not be rewritten
|
||||
map(lambda x: (yield x), [1, 2, 3])
|
||||
|
||||
@@ -141,3 +141,133 @@ class ExampleWithKeywords:
|
||||
|
||||
def method3(self):
|
||||
super(ExampleWithKeywords, self).some_method() # Should be fixed - no keywords
|
||||
|
||||
# See: https://github.com/astral-sh/ruff/issues/19357
|
||||
# Must be detected
|
||||
class ParentD:
|
||||
def f(self):
|
||||
print("D")
|
||||
|
||||
class ChildD1(ParentD):
|
||||
def f(self):
|
||||
if False: __class__ # Python injects __class__ into scope
|
||||
builtins.super(ChildD1, self).f()
|
||||
|
||||
class ChildD2(ParentD):
|
||||
def f(self):
|
||||
if False: super # Python injects __class__ into scope
|
||||
builtins.super(ChildD2, self).f()
|
||||
|
||||
class ChildD3(ParentD):
|
||||
def f(self):
|
||||
builtins.super(ChildD3, self).f()
|
||||
super # Python injects __class__ into scope
|
||||
|
||||
import builtins as builtins_alias
|
||||
class ChildD4(ParentD):
|
||||
def f(self):
|
||||
builtins_alias.super(ChildD4, self).f()
|
||||
super # Python injects __class__ into scope
|
||||
|
||||
class ChildD5(ParentD):
|
||||
def f(self):
|
||||
super = 1
|
||||
super # Python injects __class__ into scope
|
||||
builtins.super(ChildD5, self).f()
|
||||
|
||||
class ChildD6(ParentD):
|
||||
def f(self):
|
||||
super: "Any"
|
||||
__class__ # Python injects __class__ into scope
|
||||
builtins.super(ChildD6, self).f()
|
||||
|
||||
class ChildD7(ParentD):
|
||||
def f(self):
|
||||
def x():
|
||||
__class__ # Python injects __class__ into scope
|
||||
builtins.super(ChildD7, self).f()
|
||||
|
||||
class ChildD8(ParentD):
|
||||
def f(self):
|
||||
def x():
|
||||
super = 1
|
||||
super # Python injects __class__ into scope
|
||||
builtins.super(ChildD8, self).f()
|
||||
|
||||
class ChildD9(ParentD):
|
||||
def f(self):
|
||||
def x():
|
||||
__class__ = 1
|
||||
__class__ # Python injects __class__ into scope
|
||||
builtins.super(ChildD9, self).f()
|
||||
|
||||
class ChildD10(ParentD):
|
||||
def f(self):
|
||||
def x():
|
||||
__class__ = 1
|
||||
super # Python injects __class__ into scope
|
||||
builtins.super(ChildD10, self).f()
|
||||
|
||||
|
||||
# Must be ignored
|
||||
class ParentI:
|
||||
def f(self):
|
||||
print("I")
|
||||
|
||||
class ChildI1(ParentI):
|
||||
def f(self):
|
||||
builtins.super(ChildI1, self).f() # no __class__ in the local scope
|
||||
|
||||
|
||||
class ChildI2(ParentI):
|
||||
def b(self):
|
||||
x = __class__
|
||||
if False: super
|
||||
|
||||
def f(self):
|
||||
self.b()
|
||||
builtins.super(ChildI2, self).f() # no __class__ in the local scope
|
||||
|
||||
class ChildI3(ParentI):
|
||||
def f(self):
|
||||
if False: super
|
||||
def x(_):
|
||||
builtins.super(ChildI3, self).f() # no __class__ in the local scope
|
||||
x(None)
|
||||
|
||||
class ChildI4(ParentI):
|
||||
def f(self):
|
||||
super: "str"
|
||||
builtins.super(ChildI4, self).f() # no __class__ in the local scope
|
||||
|
||||
class ChildI5(ParentI):
|
||||
def f(self):
|
||||
super = 1
|
||||
__class__ = 3
|
||||
builtins.super(ChildI5, self).f() # no __class__ in the local scope
|
||||
|
||||
class ChildI6(ParentI):
|
||||
def f(self):
|
||||
__class__ = None
|
||||
__class__
|
||||
builtins.super(ChildI6, self).f() # no __class__ in the local scope
|
||||
|
||||
class ChildI7(ParentI):
|
||||
def f(self):
|
||||
__class__ = None
|
||||
super
|
||||
builtins.super(ChildI7, self).f()
|
||||
|
||||
class ChildI8(ParentI):
|
||||
def f(self):
|
||||
__class__: "Any"
|
||||
super
|
||||
builtins.super(ChildI8, self).f()
|
||||
|
||||
class ChildI9(ParentI):
|
||||
def f(self):
|
||||
class A:
|
||||
def foo(self):
|
||||
if False: super
|
||||
if False: __class__
|
||||
builtins.super(ChildI9, self).f()
|
||||
|
||||
59
crates/ruff_linter/resources/test/fixtures/pyupgrade/UP043.pyi
vendored
Normal file
59
crates/ruff_linter/resources/test/fixtures/pyupgrade/UP043.pyi
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
from collections.abc import Generator, AsyncGenerator
|
||||
|
||||
|
||||
def func() -> Generator[int, None, None]:
|
||||
yield 42
|
||||
|
||||
|
||||
def func() -> Generator[int, None]:
|
||||
yield 42
|
||||
|
||||
|
||||
def func() -> Generator[int]:
|
||||
yield 42
|
||||
|
||||
|
||||
def func() -> Generator[int, int, int]:
|
||||
foo = yield 42
|
||||
return foo
|
||||
|
||||
|
||||
def func() -> Generator[int, int, None]:
|
||||
_ = yield 42
|
||||
return None
|
||||
|
||||
|
||||
def func() -> Generator[int, None, int]:
|
||||
yield 42
|
||||
return 42
|
||||
|
||||
|
||||
async def func() -> AsyncGenerator[int, None]:
|
||||
yield 42
|
||||
|
||||
|
||||
async def func() -> AsyncGenerator[int]:
|
||||
yield 42
|
||||
|
||||
|
||||
async def func() -> AsyncGenerator[int, int]:
|
||||
foo = yield 42
|
||||
return foo
|
||||
|
||||
|
||||
from typing import Generator, AsyncGenerator
|
||||
|
||||
|
||||
def func() -> Generator[str, None, None]:
|
||||
yield "hello"
|
||||
|
||||
|
||||
async def func() -> AsyncGenerator[str, None]:
|
||||
yield "hello"
|
||||
|
||||
|
||||
async def func() -> AsyncGenerator[ # type: ignore
|
||||
str,
|
||||
None
|
||||
]:
|
||||
yield "hello"
|
||||
@@ -42,3 +42,7 @@ b"a" in bytes("a", "utf-8")
|
||||
1 in set(set([1]))
|
||||
'' in {""}
|
||||
frozenset() in {frozenset()}
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/20238
|
||||
"b" in f"" "" # Error
|
||||
"b" in f"" "x" # OK
|
||||
|
||||
1
crates/ruff_linter/resources/test/fixtures/syntax_errors/yield_from_in_async_function.py
vendored
Normal file
1
crates/ruff_linter/resources/test/fixtures/syntax_errors/yield_from_in_async_function.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
async def f(): yield from x # error
|
||||
@@ -8,7 +8,7 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::{
|
||||
is_assert_raises_exception_call_enabled, is_optional_as_none_in_union_enabled,
|
||||
is_optional_as_none_in_union_enabled, is_unnecessary_default_type_args_stubs_enabled,
|
||||
};
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::{
|
||||
@@ -142,7 +142,10 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
}
|
||||
|
||||
if checker.is_rule_enabled(Rule::UnnecessaryDefaultTypeArgs) {
|
||||
if checker.target_version() >= PythonVersion::PY313 {
|
||||
if checker.target_version() >= PythonVersion::PY313
|
||||
|| is_unnecessary_default_type_args_stubs_enabled(checker.settings())
|
||||
&& checker.semantic().in_stub_file()
|
||||
{
|
||||
pyupgrade::rules::unnecessary_default_type_args(checker, expr);
|
||||
}
|
||||
}
|
||||
@@ -1292,9 +1295,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
if checker.is_rule_enabled(Rule::NonOctalPermissions) {
|
||||
ruff::rules::non_octal_permissions(checker, call);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::AssertRaisesException)
|
||||
&& is_assert_raises_exception_call_enabled(checker.settings())
|
||||
{
|
||||
if checker.is_rule_enabled(Rule::AssertRaisesException) {
|
||||
flake8_bugbear::rules::assert_raises_exception_call(checker, call);
|
||||
}
|
||||
}
|
||||
@@ -1319,13 +1320,10 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
pylint::rules::yield_in_init(checker, expr);
|
||||
}
|
||||
}
|
||||
Expr::YieldFrom(yield_from) => {
|
||||
Expr::YieldFrom(_) => {
|
||||
if checker.is_rule_enabled(Rule::YieldInInit) {
|
||||
pylint::rules::yield_in_init(checker, expr);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::YieldFromInAsyncFunction) {
|
||||
pylint::rules::yield_from_in_async_function(checker, yield_from);
|
||||
}
|
||||
}
|
||||
Expr::FString(f_string_expr @ ast::ExprFString { value, .. }) => {
|
||||
if checker.is_rule_enabled(Rule::FStringMissingPlaceholders) {
|
||||
|
||||
@@ -71,7 +71,9 @@ use crate::registry::Rule;
|
||||
use crate::rules::pyflakes::rules::{
|
||||
LateFutureImport, ReturnOutsideFunction, YieldOutsideFunction,
|
||||
};
|
||||
use crate::rules::pylint::rules::{AwaitOutsideAsync, LoadBeforeGlobalDeclaration};
|
||||
use crate::rules::pylint::rules::{
|
||||
AwaitOutsideAsync, LoadBeforeGlobalDeclaration, YieldFromInAsyncFunction,
|
||||
};
|
||||
use crate::rules::{flake8_pyi, flake8_type_checking, pyflakes, pyupgrade};
|
||||
use crate::settings::rule_table::RuleTable;
|
||||
use crate::settings::{LinterSettings, TargetVersion, flags};
|
||||
@@ -668,6 +670,12 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
self.report_diagnostic(AwaitOutsideAsync, error.range);
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::YieldFromInAsyncFunction => {
|
||||
// PLE1700
|
||||
if self.is_rule_enabled(Rule::YieldFromInAsyncFunction) {
|
||||
self.report_diagnostic(YieldFromInAsyncFunction, error.range);
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::ReboundComprehensionVariable
|
||||
| SemanticSyntaxErrorKind::DuplicateTypeParameter
|
||||
| SemanticSyntaxErrorKind::MultipleCaseAssignment(_)
|
||||
|
||||
@@ -334,7 +334,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Async, "109") => (RuleGroup::Stable, rules::flake8_async::rules::AsyncFunctionWithTimeout),
|
||||
(Flake8Async, "110") => (RuleGroup::Stable, rules::flake8_async::rules::AsyncBusyWait),
|
||||
(Flake8Async, "115") => (RuleGroup::Stable, rules::flake8_async::rules::AsyncZeroSleep),
|
||||
(Flake8Async, "116") => (RuleGroup::Preview, rules::flake8_async::rules::LongSleepNotForever),
|
||||
(Flake8Async, "116") => (RuleGroup::Stable, rules::flake8_async::rules::LongSleepNotForever),
|
||||
(Flake8Async, "210") => (RuleGroup::Stable, rules::flake8_async::rules::BlockingHttpCallInAsyncFunction),
|
||||
(Flake8Async, "212") => (RuleGroup::Preview, rules::flake8_async::rules::BlockingHttpCallHttpxInAsyncFunction),
|
||||
(Flake8Async, "220") => (RuleGroup::Stable, rules::flake8_async::rules::CreateSubprocessInAsyncFunction),
|
||||
@@ -563,7 +563,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pyupgrade, "035") => (RuleGroup::Stable, rules::pyupgrade::rules::DeprecatedImport),
|
||||
(Pyupgrade, "036") => (RuleGroup::Stable, rules::pyupgrade::rules::OutdatedVersionBlock),
|
||||
(Pyupgrade, "037") => (RuleGroup::Stable, rules::pyupgrade::rules::QuotedAnnotation),
|
||||
(Pyupgrade, "038") => (RuleGroup::Deprecated, rules::pyupgrade::rules::NonPEP604Isinstance),
|
||||
(Pyupgrade, "038") => (RuleGroup::Removed, rules::pyupgrade::rules::NonPEP604Isinstance),
|
||||
(Pyupgrade, "039") => (RuleGroup::Stable, rules::pyupgrade::rules::UnnecessaryClassParentheses),
|
||||
(Pyupgrade, "040") => (RuleGroup::Stable, rules::pyupgrade::rules::NonPEP695TypeAlias),
|
||||
(Pyupgrade, "041") => (RuleGroup::Stable, rules::pyupgrade::rules::TimeoutErrorAlias),
|
||||
@@ -574,7 +574,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pyupgrade, "046") => (RuleGroup::Stable, rules::pyupgrade::rules::NonPEP695GenericClass),
|
||||
(Pyupgrade, "047") => (RuleGroup::Stable, rules::pyupgrade::rules::NonPEP695GenericFunction),
|
||||
(Pyupgrade, "049") => (RuleGroup::Stable, rules::pyupgrade::rules::PrivateTypeParameter),
|
||||
(Pyupgrade, "050") => (RuleGroup::Preview, rules::pyupgrade::rules::UselessClassMetaclassType),
|
||||
(Pyupgrade, "050") => (RuleGroup::Stable, rules::pyupgrade::rules::UselessClassMetaclassType),
|
||||
|
||||
// pydocstyle
|
||||
(Pydocstyle, "100") => (RuleGroup::Stable, rules::pydocstyle::rules::UndocumentedPublicModule),
|
||||
@@ -773,7 +773,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(PandasVet, "013") => (RuleGroup::Stable, rules::pandas_vet::rules::PandasUseOfDotStack),
|
||||
(PandasVet, "015") => (RuleGroup::Stable, rules::pandas_vet::rules::PandasUseOfPdMerge),
|
||||
(PandasVet, "101") => (RuleGroup::Stable, rules::pandas_vet::rules::PandasNuniqueConstantSeriesCheck),
|
||||
(PandasVet, "901") => (RuleGroup::Deprecated, rules::pandas_vet::rules::PandasDfVariableName),
|
||||
(PandasVet, "901") => (RuleGroup::Removed, rules::pandas_vet::rules::PandasDfVariableName),
|
||||
|
||||
// flake8-errmsg
|
||||
(Flake8ErrMsg, "101") => (RuleGroup::Stable, rules::flake8_errmsg::rules::RawStringInException),
|
||||
@@ -830,8 +830,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Pyi, "056") => (RuleGroup::Stable, rules::flake8_pyi::rules::UnsupportedMethodCallOnAll),
|
||||
(Flake8Pyi, "058") => (RuleGroup::Stable, rules::flake8_pyi::rules::GeneratorReturnFromIterMethod),
|
||||
(Flake8Pyi, "057") => (RuleGroup::Stable, rules::flake8_pyi::rules::ByteStringUsage),
|
||||
(Flake8Pyi, "059") => (RuleGroup::Preview, rules::flake8_pyi::rules::GenericNotLastBaseClass),
|
||||
(Flake8Pyi, "061") => (RuleGroup::Preview, rules::flake8_pyi::rules::RedundantNoneLiteral),
|
||||
(Flake8Pyi, "059") => (RuleGroup::Stable, rules::flake8_pyi::rules::GenericNotLastBaseClass),
|
||||
(Flake8Pyi, "061") => (RuleGroup::Stable, rules::flake8_pyi::rules::RedundantNoneLiteral),
|
||||
(Flake8Pyi, "062") => (RuleGroup::Stable, rules::flake8_pyi::rules::DuplicateLiteralMember),
|
||||
(Flake8Pyi, "063") => (RuleGroup::Stable, rules::flake8_pyi::rules::Pep484StylePositionalOnlyParameter),
|
||||
(Flake8Pyi, "064") => (RuleGroup::Stable, rules::flake8_pyi::rules::RedundantFinalLiteral),
|
||||
@@ -956,7 +956,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8UsePathlib, "207") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::Glob),
|
||||
(Flake8UsePathlib, "208") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsListdir),
|
||||
(Flake8UsePathlib, "210") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::InvalidPathlibWithSuffix),
|
||||
(Flake8UsePathlib, "211") => (RuleGroup::Preview, rules::flake8_use_pathlib::rules::OsSymlink),
|
||||
(Flake8UsePathlib, "211") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsSymlink),
|
||||
|
||||
// flake8-logging-format
|
||||
(Flake8LoggingFormat, "001") => (RuleGroup::Stable, rules::flake8_logging_format::violations::LoggingStringFormat),
|
||||
@@ -1032,7 +1032,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Ruff, "039") => (RuleGroup::Preview, rules::ruff::rules::UnrawRePattern),
|
||||
(Ruff, "040") => (RuleGroup::Stable, rules::ruff::rules::InvalidAssertMessageLiteralArgument),
|
||||
(Ruff, "041") => (RuleGroup::Stable, rules::ruff::rules::UnnecessaryNestedLiteral),
|
||||
(Ruff, "043") => (RuleGroup::Preview, rules::ruff::rules::PytestRaisesAmbiguousPattern),
|
||||
(Ruff, "043") => (RuleGroup::Stable, rules::ruff::rules::PytestRaisesAmbiguousPattern),
|
||||
(Ruff, "045") => (RuleGroup::Preview, rules::ruff::rules::ImplicitClassVarInDataclass),
|
||||
(Ruff, "046") => (RuleGroup::Stable, rules::ruff::rules::UnnecessaryCastToInt),
|
||||
(Ruff, "047") => (RuleGroup::Preview, rules::ruff::rules::NeedlessElse),
|
||||
@@ -1046,7 +1046,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Ruff, "056") => (RuleGroup::Preview, rules::ruff::rules::FalsyDictGetFallback),
|
||||
(Ruff, "057") => (RuleGroup::Stable, rules::ruff::rules::UnnecessaryRound),
|
||||
(Ruff, "058") => (RuleGroup::Stable, rules::ruff::rules::StarmapZip),
|
||||
(Ruff, "059") => (RuleGroup::Preview, rules::ruff::rules::UnusedUnpackedVariable),
|
||||
(Ruff, "059") => (RuleGroup::Stable, rules::ruff::rules::UnusedUnpackedVariable),
|
||||
(Ruff, "060") => (RuleGroup::Preview, rules::ruff::rules::InEmptyCollection),
|
||||
(Ruff, "061") => (RuleGroup::Preview, rules::ruff::rules::LegacyFormPytestRaises),
|
||||
(Ruff, "063") => (RuleGroup::Preview, rules::ruff::rules::AccessAnnotationsFromClassDict),
|
||||
@@ -1106,11 +1106,11 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
|
||||
// airflow
|
||||
(Airflow, "001") => (RuleGroup::Stable, rules::airflow::rules::AirflowVariableNameTaskIdMismatch),
|
||||
(Airflow, "002") => (RuleGroup::Preview, rules::airflow::rules::AirflowDagNoScheduleArgument),
|
||||
(Airflow, "301") => (RuleGroup::Preview, rules::airflow::rules::Airflow3Removal),
|
||||
(Airflow, "302") => (RuleGroup::Preview, rules::airflow::rules::Airflow3MovedToProvider),
|
||||
(Airflow, "311") => (RuleGroup::Preview, rules::airflow::rules::Airflow3SuggestedUpdate),
|
||||
(Airflow, "312") => (RuleGroup::Preview, rules::airflow::rules::Airflow3SuggestedToMoveToProvider),
|
||||
(Airflow, "002") => (RuleGroup::Stable, rules::airflow::rules::AirflowDagNoScheduleArgument),
|
||||
(Airflow, "301") => (RuleGroup::Stable, rules::airflow::rules::Airflow3Removal),
|
||||
(Airflow, "302") => (RuleGroup::Stable, rules::airflow::rules::Airflow3MovedToProvider),
|
||||
(Airflow, "311") => (RuleGroup::Stable, rules::airflow::rules::Airflow3SuggestedUpdate),
|
||||
(Airflow, "312") => (RuleGroup::Stable, rules::airflow::rules::Airflow3SuggestedToMoveToProvider),
|
||||
|
||||
// perflint
|
||||
(Perflint, "101") => (RuleGroup::Stable, rules::perflint::rules::UnnecessaryListCast),
|
||||
@@ -1137,7 +1137,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Refurb, "105") => (RuleGroup::Stable, rules::refurb::rules::PrintEmptyString),
|
||||
(Refurb, "110") => (RuleGroup::Preview, rules::refurb::rules::IfExpInsteadOfOrOperator),
|
||||
(Refurb, "113") => (RuleGroup::Preview, rules::refurb::rules::RepeatedAppend),
|
||||
(Refurb, "116") => (RuleGroup::Preview, rules::refurb::rules::FStringNumberFormat),
|
||||
(Refurb, "116") => (RuleGroup::Stable, rules::refurb::rules::FStringNumberFormat),
|
||||
(Refurb, "118") => (RuleGroup::Preview, rules::refurb::rules::ReimplementedOperator),
|
||||
(Refurb, "122") => (RuleGroup::Stable, rules::refurb::rules::ForLoopWrites),
|
||||
(Refurb, "129") => (RuleGroup::Stable, rules::refurb::rules::ReadlinesInFor),
|
||||
|
||||
@@ -1231,6 +1231,10 @@ mod tests {
|
||||
)]
|
||||
#[test_case(Rule::AwaitOutsideAsync, Path::new("await_outside_async_function.py"))]
|
||||
#[test_case(Rule::AwaitOutsideAsync, Path::new("async_comprehension.py"))]
|
||||
#[test_case(
|
||||
Rule::YieldFromInAsyncFunction,
|
||||
Path::new("yield_from_in_async_function.py")
|
||||
)]
|
||||
fn test_syntax_errors(rule: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = path.to_string_lossy().to_string();
|
||||
let path = Path::new("resources/test/fixtures/syntax_errors").join(path);
|
||||
|
||||
@@ -19,7 +19,7 @@ impl Emitter for GithubEmitter {
|
||||
context: &EmitterContext,
|
||||
) -> anyhow::Result<()> {
|
||||
for diagnostic in diagnostics {
|
||||
let source_location = diagnostic.expect_ruff_start_location();
|
||||
let source_location = diagnostic.ruff_start_location().unwrap_or_default();
|
||||
let filename = diagnostic.expect_ruff_filename();
|
||||
let location = if context.is_notebook(&filename) {
|
||||
// We can't give a reasonable location for the structured formats,
|
||||
@@ -29,7 +29,7 @@ impl Emitter for GithubEmitter {
|
||||
source_location
|
||||
};
|
||||
|
||||
let end_location = diagnostic.expect_ruff_end_location();
|
||||
let end_location = diagnostic.ruff_end_location().unwrap_or_default();
|
||||
|
||||
write!(
|
||||
writer,
|
||||
|
||||
@@ -105,7 +105,7 @@ fn group_diagnostics_by_filename(
|
||||
.or_insert_with(Vec::new)
|
||||
.push(MessageWithLocation {
|
||||
message: diagnostic,
|
||||
start_location: diagnostic.expect_ruff_start_location(),
|
||||
start_location: diagnostic.ruff_start_location().unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
grouped_messages
|
||||
|
||||
@@ -158,8 +158,8 @@ struct SarifResult<'a> {
|
||||
impl<'a> SarifResult<'a> {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn from_message(message: &'a Diagnostic) -> Result<Self> {
|
||||
let start_location = message.expect_ruff_start_location();
|
||||
let end_location = message.expect_ruff_end_location();
|
||||
let start_location = message.ruff_start_location().unwrap_or_default();
|
||||
let end_location = message.ruff_end_location().unwrap_or_default();
|
||||
let path = normalize_path(&*message.expect_ruff_filename());
|
||||
Ok(Self {
|
||||
code: RuleCode::from(message),
|
||||
@@ -178,8 +178,8 @@ impl<'a> SarifResult<'a> {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[expect(clippy::unnecessary_wraps)]
|
||||
fn from_message(message: &'a Diagnostic) -> Result<Self> {
|
||||
let start_location = message.expect_ruff_start_location();
|
||||
let end_location = message.expect_ruff_end_location();
|
||||
let start_location = message.ruff_start_location().unwrap_or_default();
|
||||
let end_location = message.ruff_end_location().unwrap_or_default();
|
||||
let path = normalize_path(&*message.expect_ruff_filename());
|
||||
Ok(Self {
|
||||
code: RuleCode::from(message),
|
||||
|
||||
@@ -81,7 +81,7 @@ expression: value
|
||||
"rules": [
|
||||
{
|
||||
"fullDescription": {
|
||||
"text": "## What it does\nChecks for unused imports.\n\n## Why is this bad?\nUnused imports add a performance overhead at runtime, and risk creating\nimport cycles. They also increase the cognitive load of reading the code.\n\nIf an import statement is used to check for the availability or existence\nof a module, consider using `importlib.util.find_spec` instead.\n\nIf an import statement is used to re-export a symbol as part of a module's\npublic interface, consider using a \"redundant\" import alias, which\ninstructs Ruff (and other tools) to respect the re-export, and avoid\nmarking it as unused, as in:\n\n```python\nfrom module import member as member\n```\n\nAlternatively, you can use `__all__` to declare a symbol as part of the module's\ninterface, as in:\n\n```python\n# __init__.py\nimport some_module\n\n__all__ = [\"some_module\"]\n```\n\n## Fix safety\n\nFixes to remove unused imports are safe, except in `__init__.py` files.\n\nApplying fixes to `__init__.py` files is currently in preview. The fix offered depends on the\ntype of the unused import. Ruff will suggest a safe fix to export first-party imports with\neither a redundant alias or, if already present in the file, an `__all__` entry. If multiple\n`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix\nto remove third-party and standard library imports -- the fix is unsafe because the module's\ninterface changes.\n\n## Example\n\n```python\nimport numpy as np # unused import\n\n\ndef area(radius):\n return 3.14 * radius**2\n```\n\nUse instead:\n\n```python\ndef area(radius):\n return 3.14 * radius**2\n```\n\nTo check the availability of a module, use `importlib.util.find_spec`:\n\n```python\nfrom importlib.util import find_spec\n\nif find_spec(\"numpy\") is not None:\n print(\"numpy is installed\")\nelse:\n print(\"numpy is not installed\")\n```\n\n## Preview\nWhen [preview](https://docs.astral.sh/ruff/preview/) is enabled,\nthe criterion for determining whether an import is first-party\nis stricter, which could affect the suggested fix. See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) for more details.\n\n## Options\n- `lint.ignore-init-module-imports`\n- `lint.pyflakes.allowed-unused-imports`\n\n## References\n- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)\n- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)\n- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)\n"
|
||||
"text": "## What it does\nChecks for unused imports.\n\n## Why is this bad?\nUnused imports add a performance overhead at runtime, and risk creating\nimport cycles. They also increase the cognitive load of reading the code.\n\nIf an import statement is used to check for the availability or existence\nof a module, consider using `importlib.util.find_spec` instead.\n\nIf an import statement is used to re-export a symbol as part of a module's\npublic interface, consider using a \"redundant\" import alias, which\ninstructs Ruff (and other tools) to respect the re-export, and avoid\nmarking it as unused, as in:\n\n```python\nfrom module import member as member\n```\n\nAlternatively, you can use `__all__` to declare a symbol as part of the module's\ninterface, as in:\n\n```python\n# __init__.py\nimport some_module\n\n__all__ = [\"some_module\"]\n```\n\n## Fix safety\n\nFixes to remove unused imports are safe, except in `__init__.py` files.\n\nApplying fixes to `__init__.py` files is currently in preview. The fix offered depends on the\ntype of the unused import. Ruff will suggest a safe fix to export first-party imports with\neither a redundant alias or, if already present in the file, an `__all__` entry. If multiple\n`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix\nto remove third-party and standard library imports -- the fix is unsafe because the module's\ninterface changes.\n\nSee [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc)\nfor more details on how Ruff\ndetermines whether an import is first or third-party.\n\n## Example\n\n```python\nimport numpy as np # unused import\n\n\ndef area(radius):\n return 3.14 * radius**2\n```\n\nUse instead:\n\n```python\ndef area(radius):\n return 3.14 * radius**2\n```\n\nTo check the availability of a module, use `importlib.util.find_spec`:\n\n```python\nfrom importlib.util import find_spec\n\nif find_spec(\"numpy\") is not None:\n print(\"numpy is installed\")\nelse:\n print(\"numpy is not installed\")\n```\n\n## Options\n- `lint.ignore-init-module-imports`\n- `lint.pyflakes.allowed-unused-imports`\n\n## References\n- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)\n- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)\n- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)\n"
|
||||
},
|
||||
"help": {
|
||||
"text": "`{name}` imported but unused; consider using `importlib.util.find_spec` to test for availability"
|
||||
@@ -119,7 +119,7 @@ expression: value
|
||||
},
|
||||
{
|
||||
"fullDescription": {
|
||||
"text": "## What it does\nChecks for the presence of unused variables in function scopes.\n\n## Why is this bad?\nA variable that is defined but not used is likely a mistake, and should\nbe removed to avoid confusion.\n\nIf a variable is intentionally defined-but-not-used, it should be\nprefixed with an underscore, or some other value that adheres to the\n[`lint.dummy-variable-rgx`] pattern.\n\n## Example\n```python\ndef foo():\n x = 1\n y = 2\n return x\n```\n\nUse instead:\n```python\ndef foo():\n x = 1\n return x\n```\n\n## Fix safety\n\nThis rule's fix is marked as unsafe because removing an unused variable assignment may\ndelete comments that are attached to the assignment.\n\n## Options\n- `lint.dummy-variable-rgx`\n"
|
||||
"text": "## What it does\nChecks for the presence of unused variables in function scopes.\n\n## Why is this bad?\nA variable that is defined but not used is likely a mistake, and should\nbe removed to avoid confusion.\n\nIf a variable is intentionally defined-but-not-used, it should be\nprefixed with an underscore, or some other value that adheres to the\n[`lint.dummy-variable-rgx`] pattern.\n\n## Example\n```python\ndef foo():\n x = 1\n y = 2\n return x\n```\n\nUse instead:\n```python\ndef foo():\n x = 1\n return x\n```\n\n## Fix safety\n\nThis rule's fix is marked as unsafe because removing an unused variable assignment may\ndelete comments that are attached to the assignment.\n\n## See also\n\nThis rule does not apply to bindings in unpacked assignments (e.g. `x, y = 1, 2`). See\n[`unused-unpacked-variable`][RUF059] for this case.\n\n## Options\n- `lint.dummy-variable-rgx`\n\n[RUF059]: https://docs.astral.sh/ruff/rules/unused-unpacked-variable/\n"
|
||||
},
|
||||
"help": {
|
||||
"text": "Local variable `{name}` is assigned to but never used"
|
||||
|
||||
@@ -11,11 +11,6 @@ pub(crate) const fn is_py314_support_enabled(settings: &LinterSettings) -> bool
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/16565
|
||||
pub(crate) const fn is_full_path_match_source_strategy_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// Rule-specific behavior
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/15541
|
||||
@@ -200,35 +195,11 @@ pub(crate) const fn is_allow_nested_roots_enabled(settings: &LinterSettings) ->
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/18208
|
||||
pub(crate) const fn is_multiple_with_statements_fix_safe_enabled(
|
||||
settings: &LinterSettings,
|
||||
) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/18400
|
||||
pub(crate) const fn is_ignore_init_files_in_useless_alias_enabled(
|
||||
settings: &LinterSettings,
|
||||
) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/18572
|
||||
pub(crate) const fn is_optional_as_none_in_union_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/18547
|
||||
pub(crate) const fn is_invalid_async_mock_access_check_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/18867
|
||||
pub(crate) const fn is_raise_exception_byte_string_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/18683
|
||||
pub(crate) const fn is_safe_super_call_with_parameters_fix_enabled(
|
||||
settings: &LinterSettings,
|
||||
@@ -236,27 +207,14 @@ pub(crate) const fn is_safe_super_call_with_parameters_fix_enabled(
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/19063
|
||||
pub(crate) const fn is_assert_raises_exception_call_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/19100
|
||||
pub(crate) const fn is_add_future_annotations_imports_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/19390
|
||||
pub(crate) const fn is_trailing_comma_type_params_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/19851
|
||||
pub(crate) const fn is_maxsplit_without_separator_fix_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/20106
|
||||
pub(crate) const fn is_bidi_forbid_arabic_letter_mark_enabled(settings: &LinterSettings) -> bool {
|
||||
// https://github.com/astral-sh/ruff/pull/20027
|
||||
pub(crate) const fn is_unnecessary_default_type_args_stubs_enabled(
|
||||
settings: &LinterSettings,
|
||||
) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
@@ -214,10 +214,8 @@ impl RuleSelector {
|
||||
RuleGroup::Preview => {
|
||||
preview_enabled && (self.is_exact() || !preview_require_explicit)
|
||||
}
|
||||
// Deprecated rules are excluded in preview mode and with 'All' option unless explicitly selected
|
||||
RuleGroup::Deprecated => {
|
||||
(!preview_enabled || self.is_exact()) && !matches!(self, RuleSelector::All)
|
||||
}
|
||||
// Deprecated rules are excluded by default unless explicitly selected
|
||||
RuleGroup::Deprecated => !preview_enabled && self.is_exact(),
|
||||
// Removed rules are included if explicitly selected but will error downstream
|
||||
RuleGroup::Removed => self.is_exact(),
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ pub(crate) enum Replacement {
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum ProviderReplacement {
|
||||
None,
|
||||
AutoImport {
|
||||
module: &'static str,
|
||||
name: &'static str,
|
||||
|
||||
@@ -46,7 +46,7 @@ pub(crate) struct AirflowDagNoScheduleArgument;
|
||||
impl Violation for AirflowDagNoScheduleArgument {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
"DAG should have an explicit `schedule` argument".to_string()
|
||||
"`DAG` or `@dag` should have an explicit `schedule` argument".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@ use ruff_text_size::TextRange;
|
||||
use crate::{FixAvailability, Violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of Airflow functions and values that have been moved to it providers.
|
||||
/// (e.g., apache-airflow-providers-fab)
|
||||
/// Checks for uses of Airflow functions and values that have been moved to its providers
|
||||
/// (e.g., `apache-airflow-providers-fab`).
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Airflow 3.0 moved various deprecated functions, members, and other
|
||||
/// values to its providers. The user needs to install the corresponding provider and replace
|
||||
/// the original usage with the one in the provider
|
||||
/// the original usage with the one in the provider.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
@@ -50,9 +50,6 @@ impl Violation for Airflow3MovedToProvider<'_> {
|
||||
replacement,
|
||||
} = self;
|
||||
match replacement {
|
||||
ProviderReplacement::None => {
|
||||
format!("`{deprecated}` is removed in Airflow 3.0")
|
||||
}
|
||||
ProviderReplacement::AutoImport {
|
||||
name: _,
|
||||
module: _,
|
||||
@@ -85,7 +82,6 @@ impl Violation for Airflow3MovedToProvider<'_> {
|
||||
provider,
|
||||
version,
|
||||
} => Some((module, name.as_str(), provider, version)),
|
||||
ProviderReplacement::None => None,
|
||||
} {
|
||||
Some(format!(
|
||||
"Install `apache-airflow-providers-{provider}>={version}` and use `{name}` from `{module}` instead."
|
||||
@@ -1020,7 +1016,6 @@ fn check_names_moved_to_provider(checker: &Checker, expr: &Expr, ranged: TextRan
|
||||
provider: "postgres",
|
||||
version: "1.0.0",
|
||||
},
|
||||
["airflow", "operators", "postgres_operator", "Mapping"] => ProviderReplacement::None,
|
||||
|
||||
// apache-airflow-providers-presto
|
||||
["airflow", "hooks", "presto_hook", "PrestoHook"] => ProviderReplacement::AutoImport {
|
||||
@@ -1209,16 +1204,6 @@ fn check_names_moved_to_provider(checker: &Checker, expr: &Expr, ranged: TextRan
|
||||
ProviderReplacement::SourceModuleMovedToProvider { module, name, .. } => {
|
||||
(module, name.as_str())
|
||||
}
|
||||
ProviderReplacement::None => {
|
||||
checker.report_diagnostic(
|
||||
Airflow3MovedToProvider {
|
||||
deprecated: qualified_name,
|
||||
replacement,
|
||||
},
|
||||
ranged,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if is_guarded_by_try_except(expr, module, name, checker.semantic()) {
|
||||
|
||||
@@ -23,7 +23,7 @@ use ruff_text_size::TextRange;
|
||||
/// ## Why is this bad?
|
||||
/// Airflow 3.0 removed various deprecated functions, members, and other
|
||||
/// values. Some have more modern replacements. Others are considered too niche
|
||||
/// and not worth to be maintained in Airflow.
|
||||
/// and not worth continued maintenance in Airflow.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
@@ -704,6 +704,7 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
["airflow", "operators", "subdag", ..] => {
|
||||
Replacement::Message("The whole `airflow.subdag` module has been removed.")
|
||||
}
|
||||
["airflow", "operators", "postgres_operator", "Mapping"] => Replacement::None,
|
||||
["airflow", "operators", "python", "get_current_context"] => Replacement::AutoImport {
|
||||
module: "airflow.sdk",
|
||||
name: "get_current_context",
|
||||
|
||||
@@ -65,9 +65,6 @@ impl Violation for Airflow3SuggestedToMoveToProvider<'_> {
|
||||
replacement,
|
||||
} = self;
|
||||
match replacement {
|
||||
ProviderReplacement::None => {
|
||||
format!("`{deprecated}` is removed in Airflow 3.0")
|
||||
}
|
||||
ProviderReplacement::AutoImport {
|
||||
name: _,
|
||||
module: _,
|
||||
@@ -91,7 +88,6 @@ impl Violation for Airflow3SuggestedToMoveToProvider<'_> {
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
let Airflow3SuggestedToMoveToProvider { replacement, .. } = self;
|
||||
match replacement {
|
||||
ProviderReplacement::None => None,
|
||||
ProviderReplacement::AutoImport {
|
||||
module,
|
||||
name,
|
||||
@@ -319,16 +315,6 @@ fn check_names_moved_to_provider(checker: &Checker, expr: &Expr, ranged: TextRan
|
||||
ProviderReplacement::SourceModuleMovedToProvider { module, name, .. } => {
|
||||
(module, name.as_str())
|
||||
}
|
||||
ProviderReplacement::None => {
|
||||
checker.report_diagnostic(
|
||||
Airflow3SuggestedToMoveToProvider {
|
||||
deprecated: qualified_name,
|
||||
replacement: replacement.clone(),
|
||||
},
|
||||
ranged.range(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if is_guarded_by_try_except(expr, module, name, checker.semantic()) {
|
||||
|
||||
@@ -17,9 +17,9 @@ use ruff_text_size::TextRange;
|
||||
/// ## Why is this bad?
|
||||
/// Airflow 3.0 removed various deprecated functions, members, and other
|
||||
/// values. Some have more modern replacements. Others are considered too niche
|
||||
/// and not worth to be maintained in Airflow.
|
||||
/// and not worth continued maintenance in Airflow.
|
||||
/// Even though these symbols still work fine on Airflow 3.0, they are expected to be removed in a future version.
|
||||
/// The user is suggested to replace the original usage with the new ones.
|
||||
/// Where available, users should replace the removed functionality with the new alternatives.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
@@ -157,6 +157,9 @@ fn check_call_arguments(checker: &Checker, qualified_name: &QualifiedName, argum
|
||||
["airflow", .., "DAG" | "dag"] => {
|
||||
diagnostic_for_argument(checker, arguments, "sla_miss_callback", None);
|
||||
}
|
||||
["airflow", "timetables", "datasets", "DatasetOrTimeSchedule"] => {
|
||||
diagnostic_for_argument(checker, arguments, "datasets", Some("assets"));
|
||||
}
|
||||
segments => {
|
||||
if is_airflow_builtin_or_provider(segments, "operators", "Operator") {
|
||||
diagnostic_for_argument(checker, arguments, "sla", None);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/airflow/mod.rs
|
||||
---
|
||||
AIR002 DAG should have an explicit `schedule` argument
|
||||
AIR002 `DAG` or `@dag` should have an explicit `schedule` argument
|
||||
--> AIR002.py:4:1
|
||||
|
|
||||
2 | from airflow.timetables.simple import NullTimetable
|
||||
@@ -12,7 +12,7 @@ AIR002 DAG should have an explicit `schedule` argument
|
||||
6 | DAG(dag_id="class_schedule", schedule="@hourly")
|
||||
|
|
||||
|
||||
AIR002 DAG should have an explicit `schedule` argument
|
||||
AIR002 `DAG` or `@dag` should have an explicit `schedule` argument
|
||||
--> AIR002.py:13:2
|
||||
|
|
||||
13 | @dag()
|
||||
|
||||
@@ -2,350 +2,362 @@
|
||||
source: crates/ruff_linter/src/rules/airflow/mod.rs
|
||||
---
|
||||
AIR301 `airflow.PY36` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:39:1
|
||||
--> AIR301_names.py:40:1
|
||||
|
|
||||
38 | # airflow root
|
||||
39 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
39 | # airflow root
|
||||
40 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^
|
||||
40 |
|
||||
41 | # airflow.api_connexion.security
|
||||
41 |
|
||||
42 | # airflow.api_connexion.security
|
||||
|
|
||||
help: Use `sys.version_info` instead
|
||||
|
||||
AIR301 `airflow.PY37` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:39:7
|
||||
--> AIR301_names.py:40:7
|
||||
|
|
||||
38 | # airflow root
|
||||
39 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
39 | # airflow root
|
||||
40 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^
|
||||
40 |
|
||||
41 | # airflow.api_connexion.security
|
||||
41 |
|
||||
42 | # airflow.api_connexion.security
|
||||
|
|
||||
help: Use `sys.version_info` instead
|
||||
|
||||
AIR301 `airflow.PY38` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:39:13
|
||||
--> AIR301_names.py:40:13
|
||||
|
|
||||
38 | # airflow root
|
||||
39 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
39 | # airflow root
|
||||
40 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^
|
||||
40 |
|
||||
41 | # airflow.api_connexion.security
|
||||
41 |
|
||||
42 | # airflow.api_connexion.security
|
||||
|
|
||||
help: Use `sys.version_info` instead
|
||||
|
||||
AIR301 `airflow.PY39` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:39:19
|
||||
--> AIR301_names.py:40:19
|
||||
|
|
||||
38 | # airflow root
|
||||
39 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
39 | # airflow root
|
||||
40 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^
|
||||
40 |
|
||||
41 | # airflow.api_connexion.security
|
||||
41 |
|
||||
42 | # airflow.api_connexion.security
|
||||
|
|
||||
help: Use `sys.version_info` instead
|
||||
|
||||
AIR301 `airflow.PY310` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:39:25
|
||||
--> AIR301_names.py:40:25
|
||||
|
|
||||
38 | # airflow root
|
||||
39 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
39 | # airflow root
|
||||
40 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^^
|
||||
40 |
|
||||
41 | # airflow.api_connexion.security
|
||||
41 |
|
||||
42 | # airflow.api_connexion.security
|
||||
|
|
||||
help: Use `sys.version_info` instead
|
||||
|
||||
AIR301 `airflow.PY311` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:39:32
|
||||
--> AIR301_names.py:40:32
|
||||
|
|
||||
38 | # airflow root
|
||||
39 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
39 | # airflow root
|
||||
40 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^^
|
||||
40 |
|
||||
41 | # airflow.api_connexion.security
|
||||
41 |
|
||||
42 | # airflow.api_connexion.security
|
||||
|
|
||||
help: Use `sys.version_info` instead
|
||||
|
||||
AIR301 `airflow.PY312` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:39:39
|
||||
--> AIR301_names.py:40:39
|
||||
|
|
||||
38 | # airflow root
|
||||
39 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
39 | # airflow root
|
||||
40 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
| ^^^^^
|
||||
40 |
|
||||
41 | # airflow.api_connexion.security
|
||||
41 |
|
||||
42 | # airflow.api_connexion.security
|
||||
|
|
||||
help: Use `sys.version_info` instead
|
||||
|
||||
AIR301 `airflow.api_connexion.security.requires_access` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:42:1
|
||||
--> AIR301_names.py:43:1
|
||||
|
|
||||
41 | # airflow.api_connexion.security
|
||||
42 | requires_access
|
||||
42 | # airflow.api_connexion.security
|
||||
43 | requires_access
|
||||
| ^^^^^^^^^^^^^^^
|
||||
43 |
|
||||
44 | # airflow.contrib.*
|
||||
44 |
|
||||
45 | # airflow.contrib.*
|
||||
|
|
||||
help: Use `airflow.api_fastapi.core_api.security.requires_access_*` instead
|
||||
|
||||
AIR301 `airflow.contrib.aws_athena_hook.AWSAthenaHook` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:45:1
|
||||
--> AIR301_names.py:46:1
|
||||
|
|
||||
44 | # airflow.contrib.*
|
||||
45 | AWSAthenaHook()
|
||||
45 | # airflow.contrib.*
|
||||
46 | AWSAthenaHook()
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: The whole `airflow.contrib` module has been removed.
|
||||
|
||||
AIR301 `airflow.datasets.DatasetAliasEvent` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:49:1
|
||||
--> AIR301_names.py:50:1
|
||||
|
|
||||
48 | # airflow.datasets
|
||||
49 | DatasetAliasEvent()
|
||||
49 | # airflow.datasets
|
||||
50 | DatasetAliasEvent()
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
AIR301 `airflow.operators.subdag.SubDagOperator` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:53:1
|
||||
--> AIR301_names.py:54:1
|
||||
|
|
||||
52 | # airflow.operators.subdag.*
|
||||
53 | SubDagOperator()
|
||||
53 | # airflow.operators.subdag.*
|
||||
54 | SubDagOperator()
|
||||
| ^^^^^^^^^^^^^^
|
||||
55 |
|
||||
56 | # airflow.operators.postgres_operator
|
||||
|
|
||||
help: The whole `airflow.subdag` module has been removed.
|
||||
|
||||
AIR301 [*] `airflow.secrets.cache.SecretCache` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:61:1
|
||||
AIR301 `airflow.operators.postgres_operator.Mapping` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:57:1
|
||||
|
|
||||
60 | # airflow.secrets.cache
|
||||
61 | SecretCache()
|
||||
56 | # airflow.operators.postgres_operator
|
||||
57 | Mapping()
|
||||
| ^^^^^^^
|
||||
58 |
|
||||
59 | # airflow.secrets
|
||||
|
|
||||
|
||||
AIR301 [*] `airflow.secrets.cache.SecretCache` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:64:1
|
||||
|
|
||||
63 | # airflow.secrets.cache
|
||||
64 | SecretCache()
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
help: Use `SecretCache` from `airflow.sdk` instead.
|
||||
13 | from airflow.contrib.aws_athena_hook import AWSAthenaHook
|
||||
14 | from airflow.datasets import DatasetAliasEvent
|
||||
15 | from airflow.operators.subdag import SubDagOperator
|
||||
15 | from airflow.operators.postgres_operator import Mapping
|
||||
16 | from airflow.operators.subdag import SubDagOperator
|
||||
- from airflow.secrets.cache import SecretCache
|
||||
16 | from airflow.secrets.local_filesystem import LocalFilesystemBackend
|
||||
17 | from airflow.triggers.external_task import TaskStateTrigger
|
||||
18 | from airflow.utils import dates
|
||||
17 | from airflow.secrets.local_filesystem import LocalFilesystemBackend
|
||||
18 | from airflow.triggers.external_task import TaskStateTrigger
|
||||
19 | from airflow.utils import dates
|
||||
--------------------------------------------------------------------------------
|
||||
33 | from airflow.utils.trigger_rule import TriggerRule
|
||||
34 | from airflow.www.auth import has_access, has_access_dataset
|
||||
35 | from airflow.www.utils import get_sensitive_variables_fields, should_hide_value_for_key
|
||||
36 + from airflow.sdk import SecretCache
|
||||
37 |
|
||||
38 | # airflow root
|
||||
39 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
34 | from airflow.utils.trigger_rule import TriggerRule
|
||||
35 | from airflow.www.auth import has_access, has_access_dataset
|
||||
36 | from airflow.www.utils import get_sensitive_variables_fields, should_hide_value_for_key
|
||||
37 + from airflow.sdk import SecretCache
|
||||
38 |
|
||||
39 | # airflow root
|
||||
40 | PY36, PY37, PY38, PY39, PY310, PY311, PY312
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
AIR301 `airflow.triggers.external_task.TaskStateTrigger` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:65:1
|
||||
|
|
||||
64 | # airflow.triggers.external_task
|
||||
65 | TaskStateTrigger()
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
66 |
|
||||
67 | # airflow.utils.date
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.date_range` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:68:1
|
||||
|
|
||||
67 | # airflow.utils.date
|
||||
68 | dates.date_range
|
||||
67 | # airflow.triggers.external_task
|
||||
68 | TaskStateTrigger()
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
69 | dates.days_ago
|
||||
69 |
|
||||
70 | # airflow.utils.date
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.days_ago` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:69:1
|
||||
|
|
||||
67 | # airflow.utils.date
|
||||
68 | dates.date_range
|
||||
69 | dates.days_ago
|
||||
| ^^^^^^^^^^^^^^
|
||||
70 |
|
||||
71 | date_range
|
||||
|
|
||||
help: Use `pendulum.today('UTC').add(days=-N, ...)` instead
|
||||
|
||||
AIR301 `airflow.utils.dates.date_range` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:71:1
|
||||
|
|
||||
69 | dates.days_ago
|
||||
70 |
|
||||
71 | date_range
|
||||
| ^^^^^^^^^^
|
||||
72 | days_ago
|
||||
73 | infer_time_unit
|
||||
70 | # airflow.utils.date
|
||||
71 | dates.date_range
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
72 | dates.days_ago
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.days_ago` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:72:1
|
||||
|
|
||||
71 | date_range
|
||||
72 | days_ago
|
||||
70 | # airflow.utils.date
|
||||
71 | dates.date_range
|
||||
72 | dates.days_ago
|
||||
| ^^^^^^^^^^^^^^
|
||||
73 |
|
||||
74 | date_range
|
||||
|
|
||||
help: Use `pendulum.today('UTC').add(days=-N, ...)` instead
|
||||
|
||||
AIR301 `airflow.utils.dates.date_range` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:74:1
|
||||
|
|
||||
72 | dates.days_ago
|
||||
73 |
|
||||
74 | date_range
|
||||
| ^^^^^^^^^^
|
||||
75 | days_ago
|
||||
76 | infer_time_unit
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.days_ago` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:75:1
|
||||
|
|
||||
74 | date_range
|
||||
75 | days_ago
|
||||
| ^^^^^^^^
|
||||
73 | infer_time_unit
|
||||
74 | parse_execution_date
|
||||
76 | infer_time_unit
|
||||
77 | parse_execution_date
|
||||
|
|
||||
help: Use `pendulum.today('UTC').add(days=-N, ...)` instead
|
||||
|
||||
AIR301 `airflow.utils.dates.infer_time_unit` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:73:1
|
||||
--> AIR301_names.py:76:1
|
||||
|
|
||||
71 | date_range
|
||||
72 | days_ago
|
||||
73 | infer_time_unit
|
||||
74 | date_range
|
||||
75 | days_ago
|
||||
76 | infer_time_unit
|
||||
| ^^^^^^^^^^^^^^^
|
||||
74 | parse_execution_date
|
||||
75 | round_time
|
||||
77 | parse_execution_date
|
||||
78 | round_time
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.parse_execution_date` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:74:1
|
||||
--> AIR301_names.py:77:1
|
||||
|
|
||||
72 | days_ago
|
||||
73 | infer_time_unit
|
||||
74 | parse_execution_date
|
||||
75 | days_ago
|
||||
76 | infer_time_unit
|
||||
77 | parse_execution_date
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
75 | round_time
|
||||
76 | scale_time_units
|
||||
78 | round_time
|
||||
79 | scale_time_units
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.round_time` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:75:1
|
||||
--> AIR301_names.py:78:1
|
||||
|
|
||||
73 | infer_time_unit
|
||||
74 | parse_execution_date
|
||||
75 | round_time
|
||||
76 | infer_time_unit
|
||||
77 | parse_execution_date
|
||||
78 | round_time
|
||||
| ^^^^^^^^^^
|
||||
76 | scale_time_units
|
||||
79 | scale_time_units
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.scale_time_units` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:76:1
|
||||
--> AIR301_names.py:79:1
|
||||
|
|
||||
74 | parse_execution_date
|
||||
75 | round_time
|
||||
76 | scale_time_units
|
||||
77 | parse_execution_date
|
||||
78 | round_time
|
||||
79 | scale_time_units
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
77 |
|
||||
78 | # This one was not deprecated.
|
||||
80 |
|
||||
81 | # This one was not deprecated.
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dag_cycle_tester.test_cycle` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:83:1
|
||||
--> AIR301_names.py:86:1
|
||||
|
|
||||
82 | # airflow.utils.dag_cycle_tester
|
||||
83 | test_cycle
|
||||
85 | # airflow.utils.dag_cycle_tester
|
||||
86 | test_cycle
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.db.create_session` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:87:1
|
||||
--> AIR301_names.py:90:1
|
||||
|
|
||||
86 | # airflow.utils.db
|
||||
87 | create_session
|
||||
89 | # airflow.utils.db
|
||||
90 | create_session
|
||||
| ^^^^^^^^^^^^^^
|
||||
88 |
|
||||
89 | # airflow.utils.decorators
|
||||
91 |
|
||||
92 | # airflow.utils.decorators
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.decorators.apply_defaults` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:90:1
|
||||
--> AIR301_names.py:93:1
|
||||
|
|
||||
89 | # airflow.utils.decorators
|
||||
90 | apply_defaults
|
||||
92 | # airflow.utils.decorators
|
||||
93 | apply_defaults
|
||||
| ^^^^^^^^^^^^^^
|
||||
91 |
|
||||
92 | # airflow.utils.file
|
||||
94 |
|
||||
95 | # airflow.utils.file
|
||||
|
|
||||
help: `apply_defaults` is now unconditionally done and can be safely removed.
|
||||
|
||||
AIR301 `airflow.utils.file.mkdirs` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:93:1
|
||||
--> AIR301_names.py:96:1
|
||||
|
|
||||
92 | # airflow.utils.file
|
||||
93 | mkdirs
|
||||
95 | # airflow.utils.file
|
||||
96 | mkdirs
|
||||
| ^^^^^^
|
||||
|
|
||||
help: Use `pathlib.Path({path}).mkdir` instead
|
||||
|
||||
AIR301 `airflow.utils.state.SHUTDOWN` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:97:1
|
||||
|
|
||||
96 | # airflow.utils.state
|
||||
97 | SHUTDOWN
|
||||
| ^^^^^^^^
|
||||
98 | terminating_states
|
||||
|
|
||||
--> AIR301_names.py:100:1
|
||||
|
|
||||
99 | # airflow.utils.state
|
||||
100 | SHUTDOWN
|
||||
| ^^^^^^^^
|
||||
101 | terminating_states
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.state.terminating_states` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:98:1
|
||||
--> AIR301_names.py:101:1
|
||||
|
|
||||
96 | # airflow.utils.state
|
||||
97 | SHUTDOWN
|
||||
98 | terminating_states
|
||||
99 | # airflow.utils.state
|
||||
100 | SHUTDOWN
|
||||
101 | terminating_states
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
99 |
|
||||
100 | # airflow.utils.trigger_rule
|
||||
102 |
|
||||
103 | # airflow.utils.trigger_rule
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.trigger_rule.TriggerRule.DUMMY` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:101:1
|
||||
--> AIR301_names.py:104:1
|
||||
|
|
||||
100 | # airflow.utils.trigger_rule
|
||||
101 | TriggerRule.DUMMY
|
||||
103 | # airflow.utils.trigger_rule
|
||||
104 | TriggerRule.DUMMY
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
102 | TriggerRule.NONE_FAILED_OR_SKIPPED
|
||||
105 | TriggerRule.NONE_FAILED_OR_SKIPPED
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.trigger_rule.TriggerRule.NONE_FAILED_OR_SKIPPED` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:102:1
|
||||
--> AIR301_names.py:105:1
|
||||
|
|
||||
100 | # airflow.utils.trigger_rule
|
||||
101 | TriggerRule.DUMMY
|
||||
102 | TriggerRule.NONE_FAILED_OR_SKIPPED
|
||||
103 | # airflow.utils.trigger_rule
|
||||
104 | TriggerRule.DUMMY
|
||||
105 | TriggerRule.NONE_FAILED_OR_SKIPPED
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
AIR301 `airflow.www.auth.has_access` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:106:1
|
||||
--> AIR301_names.py:109:1
|
||||
|
|
||||
105 | # airflow.www.auth
|
||||
106 | has_access
|
||||
108 | # airflow.www.auth
|
||||
109 | has_access
|
||||
| ^^^^^^^^^^
|
||||
107 | has_access_dataset
|
||||
110 | has_access_dataset
|
||||
|
|
||||
|
||||
AIR301 `airflow.www.auth.has_access_dataset` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:107:1
|
||||
--> AIR301_names.py:110:1
|
||||
|
|
||||
105 | # airflow.www.auth
|
||||
106 | has_access
|
||||
107 | has_access_dataset
|
||||
108 | # airflow.www.auth
|
||||
109 | has_access
|
||||
110 | has_access_dataset
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
108 |
|
||||
109 | # airflow.www.utils
|
||||
111 |
|
||||
112 | # airflow.www.utils
|
||||
|
|
||||
|
||||
AIR301 `airflow.www.utils.get_sensitive_variables_fields` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:110:1
|
||||
--> AIR301_names.py:113:1
|
||||
|
|
||||
109 | # airflow.www.utils
|
||||
110 | get_sensitive_variables_fields
|
||||
112 | # airflow.www.utils
|
||||
113 | get_sensitive_variables_fields
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
111 | should_hide_value_for_key
|
||||
114 | should_hide_value_for_key
|
||||
|
|
||||
|
||||
AIR301 `airflow.www.utils.should_hide_value_for_key` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:111:1
|
||||
--> AIR301_names.py:114:1
|
||||
|
|
||||
109 | # airflow.www.utils
|
||||
110 | get_sensitive_variables_fields
|
||||
111 | should_hide_value_for_key
|
||||
112 | # airflow.www.utils
|
||||
113 | get_sensitive_variables_fields
|
||||
114 | should_hide_value_for_key
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
@@ -20,11 +20,3 @@ help: Install `apache-airflow-providers-postgres>=1.0.0` and use `PostgresHook`
|
||||
6 | PostgresHook()
|
||||
7 | Mapping()
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
AIR302 `airflow.operators.postgres_operator.Mapping` is removed in Airflow 3.0
|
||||
--> AIR302_postgres.py:7:1
|
||||
|
|
||||
6 | PostgresHook()
|
||||
7 | Mapping()
|
||||
| ^^^^^^^
|
||||
|
|
||||
|
||||
@@ -558,7 +558,7 @@ AIR311 [*] `airflow.timetables.datasets.DatasetOrTimeSchedule` is removed in Air
|
||||
--> AIR311_names.py:73:1
|
||||
|
|
||||
72 | # airflow.timetables.datasets
|
||||
73 | DatasetOrTimeSchedule()
|
||||
73 | DatasetOrTimeSchedule(datasets=[])
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
74 |
|
||||
75 | # airflow.utils.dag_parsing_context
|
||||
@@ -570,12 +570,31 @@ help: Use `AssetOrTimeSchedule` from `airflow.timetables.assets` instead.
|
||||
71 + from airflow.timetables.assets import AssetOrTimeSchedule
|
||||
72 |
|
||||
73 | # airflow.timetables.datasets
|
||||
- DatasetOrTimeSchedule()
|
||||
74 + AssetOrTimeSchedule()
|
||||
- DatasetOrTimeSchedule(datasets=[])
|
||||
74 + AssetOrTimeSchedule(datasets=[])
|
||||
75 |
|
||||
76 | # airflow.utils.dag_parsing_context
|
||||
77 | get_parsing_context()
|
||||
|
||||
AIR311 [*] `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:73:23
|
||||
|
|
||||
72 | # airflow.timetables.datasets
|
||||
73 | DatasetOrTimeSchedule(datasets=[])
|
||||
| ^^^^^^^^
|
||||
74 |
|
||||
75 | # airflow.utils.dag_parsing_context
|
||||
|
|
||||
help: Use `assets` instead
|
||||
70 | from airflow.utils.dag_parsing_context import get_parsing_context
|
||||
71 |
|
||||
72 | # airflow.timetables.datasets
|
||||
- DatasetOrTimeSchedule(datasets=[])
|
||||
73 + DatasetOrTimeSchedule(assets=[])
|
||||
74 |
|
||||
75 | # airflow.utils.dag_parsing_context
|
||||
76 | get_parsing_context()
|
||||
|
||||
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:76:1
|
||||
|
|
||||
@@ -593,7 +612,7 @@ help: Use `get_parsing_context` from `airflow.sdk` instead.
|
||||
70 + from airflow.sdk import get_parsing_context
|
||||
71 |
|
||||
72 | # airflow.timetables.datasets
|
||||
73 | DatasetOrTimeSchedule()
|
||||
73 | DatasetOrTimeSchedule(datasets=[])
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
AIR311 [*] `airflow.decorators.base.DecoratedMappedOperator` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
||||
|
||||
@@ -79,7 +79,6 @@ impl Violation for FastApiUnusedPathParameter {
|
||||
function_name,
|
||||
is_positional,
|
||||
} = self;
|
||||
#[expect(clippy::if_not_else)]
|
||||
if !is_positional {
|
||||
format!(
|
||||
"Parameter `{arg_name}` appears in route path, but not in `{function_name}` signature"
|
||||
|
||||
@@ -16,8 +16,6 @@ mod tests {
|
||||
use crate::settings::LinterSettings;
|
||||
use crate::test::test_path;
|
||||
|
||||
use crate::settings::types::PreviewMode;
|
||||
|
||||
use ruff_python_ast::PythonVersion;
|
||||
|
||||
#[test_case(Rule::AbstractBaseClassWithoutAbstractMethod, Path::new("B024.py"))]
|
||||
@@ -177,23 +175,4 @@ mod tests {
|
||||
assert_diagnostics!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::AssertRaisesException, Path::new("B017_0.py"))]
|
||||
#[test_case(Rule::AssertRaisesException, Path::new("B017_1.py"))]
|
||||
fn rules_preview(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
rule_code.noqa_code(),
|
||||
path.to_string_lossy()
|
||||
);
|
||||
let diagnostics = test_path(
|
||||
Path::new("flake8_bugbear").join(path).as_path(),
|
||||
&LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
..LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
)?;
|
||||
assert_diagnostics!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,41 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
|
||||
---
|
||||
B017 Do not assert blind exception: `Exception`
|
||||
--> B017_1.py:20:9
|
||||
|
|
||||
18 | class Foobar(unittest.TestCase):
|
||||
19 | def call_form_raises(self) -> None:
|
||||
20 | self.assertRaises(Exception, something_else)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
21 | self.assertRaises(BaseException, something_else)
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `BaseException`
|
||||
--> B017_1.py:21:9
|
||||
|
|
||||
19 | def call_form_raises(self) -> None:
|
||||
20 | self.assertRaises(Exception, something_else)
|
||||
21 | self.assertRaises(BaseException, something_else)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `Exception`
|
||||
--> B017_1.py:25:5
|
||||
|
|
||||
24 | def test_pytest_call_form() -> None:
|
||||
25 | pytest.raises(Exception, something_else)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
26 | pytest.raises(BaseException, something_else)
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `BaseException`
|
||||
--> B017_1.py:26:5
|
||||
|
|
||||
24 | def test_pytest_call_form() -> None:
|
||||
25 | pytest.raises(Exception, something_else)
|
||||
26 | pytest.raises(BaseException, something_else)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
27 |
|
||||
28 | pytest.raises(Exception, something_else, match="hello")
|
||||
|
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
|
||||
---
|
||||
B017 Do not assert blind exception: `Exception`
|
||||
--> B017_0.py:23:14
|
||||
|
|
||||
21 | class Foobar(unittest.TestCase):
|
||||
22 | def evil_raises(self) -> None:
|
||||
23 | with self.assertRaises(Exception):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
24 | raise Exception("Evil I say!")
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `BaseException`
|
||||
--> B017_0.py:27:14
|
||||
|
|
||||
26 | def also_evil_raises(self) -> None:
|
||||
27 | with self.assertRaises(BaseException):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
28 | raise Exception("Evil I say!")
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `Exception`
|
||||
--> B017_0.py:45:10
|
||||
|
|
||||
44 | def test_pytest_raises():
|
||||
45 | with pytest.raises(Exception):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
46 | raise ValueError("Hello")
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `Exception`
|
||||
--> B017_0.py:48:10
|
||||
|
|
||||
46 | raise ValueError("Hello")
|
||||
47 |
|
||||
48 | with pytest.raises(Exception), pytest.raises(ValueError):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
49 | raise ValueError("Hello")
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `Exception`
|
||||
--> B017_0.py:57:36
|
||||
|
|
||||
55 | raise ValueError("This is also fine")
|
||||
56 |
|
||||
57 | with contextlib.nullcontext(), pytest.raises(Exception):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
58 | raise ValueError("Multiple context managers")
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `Exception`
|
||||
--> B017_0.py:62:10
|
||||
|
|
||||
61 | def test_pytest_raises_keyword():
|
||||
62 | with pytest.raises(expected_exception=Exception):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
63 | raise ValueError("Should be flagged")
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `Exception`
|
||||
--> B017_0.py:68:18
|
||||
|
|
||||
66 | class TestKwargs(unittest.TestCase):
|
||||
67 | def test_method(self):
|
||||
68 | with self.assertRaises(exception=Exception):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
69 | raise ValueError("Should be flagged")
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `BaseException`
|
||||
--> B017_0.py:71:18
|
||||
|
|
||||
69 | raise ValueError("Should be flagged")
|
||||
70 |
|
||||
71 | with self.assertRaises(exception=BaseException):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
72 | raise ValueError("Should be flagged")
|
||||
|
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
|
||||
---
|
||||
B017 Do not assert blind exception: `Exception`
|
||||
--> B017_1.py:20:9
|
||||
|
|
||||
18 | class Foobar(unittest.TestCase):
|
||||
19 | def call_form_raises(self) -> None:
|
||||
20 | self.assertRaises(Exception, something_else)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
21 | self.assertRaises(BaseException, something_else)
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `BaseException`
|
||||
--> B017_1.py:21:9
|
||||
|
|
||||
19 | def call_form_raises(self) -> None:
|
||||
20 | self.assertRaises(Exception, something_else)
|
||||
21 | self.assertRaises(BaseException, something_else)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `Exception`
|
||||
--> B017_1.py:25:5
|
||||
|
|
||||
24 | def test_pytest_call_form() -> None:
|
||||
25 | pytest.raises(Exception, something_else)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
26 | pytest.raises(BaseException, something_else)
|
||||
|
|
||||
|
||||
B017 Do not assert blind exception: `BaseException`
|
||||
--> B017_1.py:26:5
|
||||
|
|
||||
24 | def test_pytest_call_form() -> None:
|
||||
25 | pytest.raises(Exception, something_else)
|
||||
26 | pytest.raises(BaseException, something_else)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
27 |
|
||||
28 | pytest.raises(Exception, something_else, match="hello")
|
||||
|
|
||||
@@ -10,7 +10,7 @@ mod tests {
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::test::test_path;
|
||||
use crate::{assert_diagnostics, assert_diagnostics_diff, settings};
|
||||
use crate::{assert_diagnostics, settings};
|
||||
|
||||
#[test_case(Path::new("COM81.py"))]
|
||||
#[test_case(Path::new("COM81_syntax_error.py"))]
|
||||
@@ -27,28 +27,4 @@ mod tests {
|
||||
assert_diagnostics!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Path::new("COM81.py"))]
|
||||
#[test_case(Path::new("COM81_syntax_error.py"))]
|
||||
fn preview_rules(path: &Path) -> Result<()> {
|
||||
let snapshot = format!("preview_diff__{}", path.to_string_lossy());
|
||||
let rules = vec![
|
||||
Rule::MissingTrailingComma,
|
||||
Rule::TrailingCommaOnBareTuple,
|
||||
Rule::ProhibitedTrailingComma,
|
||||
];
|
||||
let settings_before = settings::LinterSettings::for_rules(rules.clone());
|
||||
let settings_after = settings::LinterSettings {
|
||||
preview: crate::settings::types::PreviewMode::Enabled,
|
||||
..settings::LinterSettings::for_rules(rules)
|
||||
};
|
||||
|
||||
assert_diagnostics_diff!(
|
||||
snapshot,
|
||||
Path::new("flake8_commas").join(path).as_path(),
|
||||
&settings_before,
|
||||
&settings_after
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::Locator;
|
||||
use crate::checkers::ast::LintContext;
|
||||
use crate::preview::is_trailing_comma_type_params_enabled;
|
||||
use crate::settings::LinterSettings;
|
||||
use crate::{AlwaysFixableViolation, Violation};
|
||||
use crate::{Edit, Fix};
|
||||
|
||||
@@ -298,7 +296,7 @@ pub(crate) fn trailing_commas(
|
||||
}
|
||||
|
||||
// Update the comma context stack.
|
||||
let context = update_context(token, prev, prev_prev, &mut stack, lint_context.settings());
|
||||
let context = update_context(token, prev, prev_prev, &mut stack);
|
||||
|
||||
check_token(token, prev, prev_prev, context, locator, lint_context);
|
||||
|
||||
@@ -417,7 +415,6 @@ fn update_context(
|
||||
prev: SimpleToken,
|
||||
prev_prev: SimpleToken,
|
||||
stack: &mut Vec<Context>,
|
||||
settings: &LinterSettings,
|
||||
) -> Context {
|
||||
let new_context = match token.ty {
|
||||
TokenType::OpeningBracket => match (prev.ty, prev_prev.ty) {
|
||||
@@ -427,19 +424,11 @@ fn update_context(
|
||||
}
|
||||
_ => Context::new(ContextType::Tuple),
|
||||
},
|
||||
TokenType::OpeningSquareBracket if is_trailing_comma_type_params_enabled(settings) => {
|
||||
match (prev.ty, prev_prev.ty) {
|
||||
(TokenType::Named, TokenType::Def | TokenType::Class | TokenType::Type) => {
|
||||
Context::new(ContextType::TypeParameters)
|
||||
}
|
||||
(TokenType::ClosingBracket | TokenType::Named | TokenType::String, _) => {
|
||||
Context::new(ContextType::Subscript)
|
||||
}
|
||||
_ => Context::new(ContextType::List),
|
||||
TokenType::OpeningSquareBracket => match (prev.ty, prev_prev.ty) {
|
||||
(TokenType::Named, TokenType::Def | TokenType::Class | TokenType::Type) => {
|
||||
Context::new(ContextType::TypeParameters)
|
||||
}
|
||||
}
|
||||
TokenType::OpeningSquareBracket => match prev.ty {
|
||||
TokenType::ClosingBracket | TokenType::Named | TokenType::String => {
|
||||
(TokenType::ClosingBracket | TokenType::Named | TokenType::String, _) => {
|
||||
Context::new(ContextType::Subscript)
|
||||
}
|
||||
_ => Context::new(ContextType::List),
|
||||
|
||||
@@ -939,3 +939,111 @@ help: Add trailing comma
|
||||
644 | )
|
||||
645 |
|
||||
646 | assert False, f"<- This is not a trailing comma"
|
||||
|
||||
COM812 [*] Trailing comma missing
|
||||
--> COM81.py:655:6
|
||||
|
|
||||
654 | type X[
|
||||
655 | T
|
||||
| ^
|
||||
656 | ] = T
|
||||
657 | def f[
|
||||
|
|
||||
help: Add trailing comma
|
||||
652 | }"""
|
||||
653 |
|
||||
654 | type X[
|
||||
- T
|
||||
655 + T,
|
||||
656 | ] = T
|
||||
657 | def f[
|
||||
658 | T
|
||||
|
||||
COM812 [*] Trailing comma missing
|
||||
--> COM81.py:658:6
|
||||
|
|
||||
656 | ] = T
|
||||
657 | def f[
|
||||
658 | T
|
||||
| ^
|
||||
659 | ](): pass
|
||||
660 | class C[
|
||||
|
|
||||
help: Add trailing comma
|
||||
655 | T
|
||||
656 | ] = T
|
||||
657 | def f[
|
||||
- T
|
||||
658 + T,
|
||||
659 | ](): pass
|
||||
660 | class C[
|
||||
661 | T
|
||||
|
||||
COM812 [*] Trailing comma missing
|
||||
--> COM81.py:661:6
|
||||
|
|
||||
659 | ](): pass
|
||||
660 | class C[
|
||||
661 | T
|
||||
| ^
|
||||
662 | ]: pass
|
||||
|
|
||||
help: Add trailing comma
|
||||
658 | T
|
||||
659 | ](): pass
|
||||
660 | class C[
|
||||
- T
|
||||
661 + T,
|
||||
662 | ]: pass
|
||||
663 |
|
||||
664 | type X[T,] = T
|
||||
|
||||
COM819 [*] Trailing comma prohibited
|
||||
--> COM81.py:664:9
|
||||
|
|
||||
662 | ]: pass
|
||||
663 |
|
||||
664 | type X[T,] = T
|
||||
| ^
|
||||
665 | def f[T,](): pass
|
||||
666 | class C[T,]: pass
|
||||
|
|
||||
help: Remove trailing comma
|
||||
661 | T
|
||||
662 | ]: pass
|
||||
663 |
|
||||
- type X[T,] = T
|
||||
664 + type X[T] = T
|
||||
665 | def f[T,](): pass
|
||||
666 | class C[T,]: pass
|
||||
|
||||
COM819 [*] Trailing comma prohibited
|
||||
--> COM81.py:665:8
|
||||
|
|
||||
664 | type X[T,] = T
|
||||
665 | def f[T,](): pass
|
||||
| ^
|
||||
666 | class C[T,]: pass
|
||||
|
|
||||
help: Remove trailing comma
|
||||
662 | ]: pass
|
||||
663 |
|
||||
664 | type X[T,] = T
|
||||
- def f[T,](): pass
|
||||
665 + def f[T](): pass
|
||||
666 | class C[T,]: pass
|
||||
|
||||
COM819 [*] Trailing comma prohibited
|
||||
--> COM81.py:666:10
|
||||
|
|
||||
664 | type X[T,] = T
|
||||
665 | def f[T,](): pass
|
||||
666 | class C[T,]: pass
|
||||
| ^
|
||||
|
|
||||
help: Remove trailing comma
|
||||
663 |
|
||||
664 | type X[T,] = T
|
||||
665 | def f[T,](): pass
|
||||
- class C[T,]: pass
|
||||
666 + class C[T]: pass
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_commas/mod.rs
|
||||
---
|
||||
--- Linter settings ---
|
||||
-linter.preview = disabled
|
||||
+linter.preview = enabled
|
||||
|
||||
--- Summary ---
|
||||
Removed: 0
|
||||
Added: 6
|
||||
|
||||
--- Added ---
|
||||
COM812 [*] Trailing comma missing
|
||||
--> COM81.py:655:6
|
||||
|
|
||||
654 | type X[
|
||||
655 | T
|
||||
| ^
|
||||
656 | ] = T
|
||||
657 | def f[
|
||||
|
|
||||
help: Add trailing comma
|
||||
652 | }"""
|
||||
653 |
|
||||
654 | type X[
|
||||
- T
|
||||
655 + T,
|
||||
656 | ] = T
|
||||
657 | def f[
|
||||
658 | T
|
||||
|
||||
|
||||
COM812 [*] Trailing comma missing
|
||||
--> COM81.py:658:6
|
||||
|
|
||||
656 | ] = T
|
||||
657 | def f[
|
||||
658 | T
|
||||
| ^
|
||||
659 | ](): pass
|
||||
660 | class C[
|
||||
|
|
||||
help: Add trailing comma
|
||||
655 | T
|
||||
656 | ] = T
|
||||
657 | def f[
|
||||
- T
|
||||
658 + T,
|
||||
659 | ](): pass
|
||||
660 | class C[
|
||||
661 | T
|
||||
|
||||
|
||||
COM812 [*] Trailing comma missing
|
||||
--> COM81.py:661:6
|
||||
|
|
||||
659 | ](): pass
|
||||
660 | class C[
|
||||
661 | T
|
||||
| ^
|
||||
662 | ]: pass
|
||||
|
|
||||
help: Add trailing comma
|
||||
658 | T
|
||||
659 | ](): pass
|
||||
660 | class C[
|
||||
- T
|
||||
661 + T,
|
||||
662 | ]: pass
|
||||
663 |
|
||||
664 | type X[T,] = T
|
||||
|
||||
|
||||
COM819 [*] Trailing comma prohibited
|
||||
--> COM81.py:664:9
|
||||
|
|
||||
662 | ]: pass
|
||||
663 |
|
||||
664 | type X[T,] = T
|
||||
| ^
|
||||
665 | def f[T,](): pass
|
||||
666 | class C[T,]: pass
|
||||
|
|
||||
help: Remove trailing comma
|
||||
661 | T
|
||||
662 | ]: pass
|
||||
663 |
|
||||
- type X[T,] = T
|
||||
664 + type X[T] = T
|
||||
665 | def f[T,](): pass
|
||||
666 | class C[T,]: pass
|
||||
|
||||
|
||||
COM819 [*] Trailing comma prohibited
|
||||
--> COM81.py:665:8
|
||||
|
|
||||
664 | type X[T,] = T
|
||||
665 | def f[T,](): pass
|
||||
| ^
|
||||
666 | class C[T,]: pass
|
||||
|
|
||||
help: Remove trailing comma
|
||||
662 | ]: pass
|
||||
663 |
|
||||
664 | type X[T,] = T
|
||||
- def f[T,](): pass
|
||||
665 + def f[T](): pass
|
||||
666 | class C[T,]: pass
|
||||
|
||||
|
||||
COM819 [*] Trailing comma prohibited
|
||||
--> COM81.py:666:10
|
||||
|
|
||||
664 | type X[T,] = T
|
||||
665 | def f[T,](): pass
|
||||
666 | class C[T,]: pass
|
||||
| ^
|
||||
|
|
||||
help: Remove trailing comma
|
||||
663 |
|
||||
664 | type X[T,] = T
|
||||
665 | def f[T,](): pass
|
||||
- class C[T,]: pass
|
||||
666 + class C[T]: pass
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_commas/mod.rs
|
||||
---
|
||||
--- Linter settings ---
|
||||
-linter.preview = disabled
|
||||
+linter.preview = enabled
|
||||
|
||||
--- Summary ---
|
||||
Removed: 0
|
||||
Added: 0
|
||||
@@ -122,6 +122,13 @@ pub(crate) fn unnecessary_map(checker: &Checker, call: &ast::ExprCall) {
|
||||
}
|
||||
};
|
||||
|
||||
// If the lambda body contains a `yield` or `yield from`, rewriting `map(lambda ...)` to a
|
||||
// generator expression or any comprehension is invalid Python syntax
|
||||
// (e.g., `yield` is not allowed inside generator or comprehension expressions). In such cases, skip.
|
||||
if lambda_contains_yield(&lambda.body) {
|
||||
return;
|
||||
}
|
||||
|
||||
for iterable in iterables {
|
||||
// For example, (x+1 for x in (c:=a)) is invalid syntax
|
||||
// so we can't suggest it.
|
||||
@@ -183,6 +190,13 @@ fn map_lambda_and_iterables<'a>(
|
||||
Some((lambda, iterables))
|
||||
}
|
||||
|
||||
/// Returns true if the expression tree contains a `yield` or `yield from` expression.
|
||||
fn lambda_contains_yield(expr: &Expr) -> bool {
|
||||
any_over_expr(expr, &|expr| {
|
||||
matches!(expr, Expr::Yield(_) | Expr::YieldFrom(_))
|
||||
})
|
||||
}
|
||||
|
||||
/// A lambda as the first argument to `map()` has the "expected" arity when:
|
||||
///
|
||||
/// * It has exactly one parameter
|
||||
|
||||
@@ -342,6 +342,7 @@ help: Replace `map()` with a set comprehension
|
||||
75 + _ = t"{ {x % 2 == 0 for x in nums} }"
|
||||
76 | _ = t"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
77 |
|
||||
78 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
C417 [*] Unnecessary `map()` usage (rewrite using a dict comprehension)
|
||||
@@ -359,4 +360,6 @@ help: Replace `map()` with a dict comprehension
|
||||
- _ = t"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
76 + _ = t"{ {v: v**2 for v in nums} }"
|
||||
77 |
|
||||
78 |
|
||||
79 | # See https://github.com/astral-sh/ruff/issues/20198
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
@@ -9,7 +9,6 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::types::PreviewMode;
|
||||
use crate::test::test_path;
|
||||
use crate::{assert_diagnostics, settings};
|
||||
|
||||
@@ -47,15 +46,14 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preview_string_exception() -> Result<()> {
|
||||
fn string_exception() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("flake8_errmsg/EM101_byte_string.py"),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
..settings::LinterSettings::for_rule(Rule::RawStringInException)
|
||||
},
|
||||
)?;
|
||||
assert_diagnostics!("preview", diagnostics);
|
||||
assert_diagnostics!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,16 +7,12 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::Locator;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_raise_exception_byte_string_enabled;
|
||||
use crate::registry::Rule;
|
||||
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of string literals in exception constructors.
|
||||
///
|
||||
/// In [preview], this rule checks for byte string literals in
|
||||
/// exception constructors.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Python includes the `raise` in the default traceback (and formatters
|
||||
/// like Rich and IPython do too).
|
||||
@@ -51,8 +47,6 @@ use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
/// raise RuntimeError(msg)
|
||||
/// RuntimeError: 'Some value' is incorrect
|
||||
/// ```
|
||||
///
|
||||
/// [preview]: https://docs.astral.sh/ruff/preview/
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct RawStringInException;
|
||||
|
||||
@@ -218,9 +212,7 @@ pub(crate) fn string_in_exception(checker: &Checker, stmt: &Stmt, exc: &Expr) {
|
||||
// Check for byte string literals.
|
||||
Expr::BytesLiteral(ast::ExprBytesLiteral { value: bytes, .. }) => {
|
||||
if checker.settings().rules.enabled(Rule::RawStringInException) {
|
||||
if bytes.len() >= checker.settings().flake8_errmsg.max_string_length
|
||||
&& is_raise_exception_byte_string_enabled(checker.settings())
|
||||
{
|
||||
if bytes.len() >= checker.settings().flake8_errmsg.max_string_length {
|
||||
let mut diagnostic =
|
||||
checker.report_diagnostic(RawStringInException, first.range());
|
||||
if let Some(indentation) = whitespace::indentation(checker.source(), stmt) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
use ruff_python_ast::{self as ast, ParameterWithDefault};
|
||||
use ruff_python_semantic::analyze::function_type;
|
||||
|
||||
use crate::Violation;
|
||||
@@ -85,16 +85,9 @@ pub(crate) fn pep_484_positional_parameter(checker: &Checker, function_def: &ast
|
||||
function_type::FunctionType::Method | function_type::FunctionType::ClassMethod
|
||||
));
|
||||
|
||||
if let Some(arg) = function_def.parameters.args.get(skip) {
|
||||
if is_old_style_positional_only(arg) {
|
||||
checker.report_diagnostic(Pep484StylePositionalOnlyParameter, arg.identifier());
|
||||
if let Some(param) = function_def.parameters.args.get(skip) {
|
||||
if param.uses_pep_484_positional_only_convention() {
|
||||
checker.report_diagnostic(Pep484StylePositionalOnlyParameter, param.identifier());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the [`ParameterWithDefault`] is an old-style positional-only parameter (i.e.,
|
||||
/// its name starts with `__` and does not end with `__`).
|
||||
fn is_old_style_positional_only(param: &ParameterWithDefault) -> bool {
|
||||
let arg_name = param.name();
|
||||
arg_name.starts_with("__") && !arg_name.ends_with("__")
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::MultipleWithStatements, Path::new("SIM117.py"))]
|
||||
#[test_case(Rule::SplitStaticString, Path::new("SIM905.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
|
||||
@@ -10,7 +10,6 @@ use super::fix_with;
|
||||
use crate::Fix;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::fits;
|
||||
use crate::preview::is_multiple_with_statements_fix_safe_enabled;
|
||||
use crate::{FixAvailability, Violation};
|
||||
|
||||
/// ## What it does
|
||||
@@ -45,15 +44,8 @@ use crate::{FixAvailability, Violation};
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
///
|
||||
/// This fix is marked as always unsafe unless [preview] mode is enabled, in which case it is always
|
||||
/// marked as safe. Note that the fix is unavailable if it would remove comments (in either case).
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: The `with` statement](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement)
|
||||
///
|
||||
/// [preview]: https://docs.astral.sh/ruff/preview/
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct MultipleWithStatements;
|
||||
|
||||
@@ -195,11 +187,7 @@ pub(crate) fn multiple_with_statements(
|
||||
checker.settings().tab_size,
|
||||
)
|
||||
}) {
|
||||
if is_multiple_with_statements_fix_safe_enabled(checker.settings()) {
|
||||
Ok(Some(Fix::safe_edit(edit)))
|
||||
} else {
|
||||
Ok(Some(Fix::unsafe_edit(edit)))
|
||||
}
|
||||
Ok(Some(Fix::safe_edit(edit)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ help: Combine `with` statements
|
||||
4 |
|
||||
5 | # SIM117
|
||||
6 | with A():
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:7:1
|
||||
@@ -46,7 +45,6 @@ help: Combine `with` statements
|
||||
10 |
|
||||
11 | # SIM117
|
||||
12 | with A() as a:
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:13:1
|
||||
@@ -84,7 +82,6 @@ help: Combine `with` statements
|
||||
22 |
|
||||
23 | # OK
|
||||
24 | with A() as a:
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:47:1
|
||||
@@ -107,7 +104,6 @@ help: Combine `with` statements
|
||||
49 |
|
||||
50 | while True:
|
||||
51 | # SIM117
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:53:5
|
||||
@@ -144,7 +140,6 @@ help: Combine `with` statements
|
||||
64 | "this for some reason")
|
||||
65 |
|
||||
66 | # SIM117
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:68:1
|
||||
@@ -171,7 +166,6 @@ help: Combine `with` statements
|
||||
73 |
|
||||
74 | # SIM117
|
||||
75 | with A() as a:
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:76:1
|
||||
@@ -203,7 +197,6 @@ help: Combine `with` statements
|
||||
81 |
|
||||
82 | # SIM117
|
||||
83 | with (
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:84:1
|
||||
@@ -237,7 +230,6 @@ help: Combine `with` statements
|
||||
90 |
|
||||
91 | # SIM117 (auto-fixable)
|
||||
92 | with A("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as a:
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:95:1
|
||||
@@ -260,7 +252,6 @@ help: Combine `with` statements
|
||||
97 |
|
||||
98 | # SIM117 (not auto-fixable too long)
|
||||
99 | with A("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ890") as a:
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:100:1
|
||||
@@ -319,7 +310,6 @@ help: Combine `with` statements
|
||||
136 |
|
||||
137 | # Allow cascading for some statements.
|
||||
138 | import anyio
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:163:1
|
||||
@@ -339,4 +329,3 @@ help: Combine `with` statements
|
||||
- pass
|
||||
163 + async with asyncio.timeout(1), A(), B():
|
||||
164 + pass
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
@@ -1,331 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs
|
||||
---
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:2:1
|
||||
|
|
||||
1 | # SIM117
|
||||
2 | / with A() as a:
|
||||
3 | | with B() as b:
|
||||
| |__________________^
|
||||
4 | print("hello")
|
||||
|
|
||||
help: Combine `with` statements
|
||||
1 | # SIM117
|
||||
- with A() as a:
|
||||
- with B() as b:
|
||||
- print("hello")
|
||||
2 + with A() as a, B() as b:
|
||||
3 + print("hello")
|
||||
4 |
|
||||
5 | # SIM117
|
||||
6 | with A():
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:7:1
|
||||
|
|
||||
6 | # SIM117
|
||||
7 | / with A():
|
||||
8 | | with B():
|
||||
| |_____________^
|
||||
9 | with C():
|
||||
10 | print("hello")
|
||||
|
|
||||
help: Combine `with` statements
|
||||
4 | print("hello")
|
||||
5 |
|
||||
6 | # SIM117
|
||||
- with A():
|
||||
- with B():
|
||||
- with C():
|
||||
- print("hello")
|
||||
7 + with A(), B():
|
||||
8 + with C():
|
||||
9 + print("hello")
|
||||
10 |
|
||||
11 | # SIM117
|
||||
12 | with A() as a:
|
||||
|
||||
SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:13:1
|
||||
|
|
||||
12 | # SIM117
|
||||
13 | / with A() as a:
|
||||
14 | | # Unfixable due to placement of this comment.
|
||||
15 | | with B() as b:
|
||||
| |__________________^
|
||||
16 | print("hello")
|
||||
|
|
||||
help: Combine `with` statements
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:19:1
|
||||
|
|
||||
18 | # SIM117
|
||||
19 | / with A() as a:
|
||||
20 | | with B() as b:
|
||||
| |__________________^
|
||||
21 | # Fixable due to placement of this comment.
|
||||
22 | print("hello")
|
||||
|
|
||||
help: Combine `with` statements
|
||||
16 | print("hello")
|
||||
17 |
|
||||
18 | # SIM117
|
||||
- with A() as a:
|
||||
- with B() as b:
|
||||
- # Fixable due to placement of this comment.
|
||||
- print("hello")
|
||||
19 + with A() as a, B() as b:
|
||||
20 + # Fixable due to placement of this comment.
|
||||
21 + print("hello")
|
||||
22 |
|
||||
23 | # OK
|
||||
24 | with A() as a:
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:47:1
|
||||
|
|
||||
46 | # SIM117
|
||||
47 | / async with A() as a:
|
||||
48 | | async with B() as b:
|
||||
| |________________________^
|
||||
49 | print("hello")
|
||||
|
|
||||
help: Combine `with` statements
|
||||
44 | print("hello")
|
||||
45 |
|
||||
46 | # SIM117
|
||||
- async with A() as a:
|
||||
- async with B() as b:
|
||||
- print("hello")
|
||||
47 + async with A() as a, B() as b:
|
||||
48 + print("hello")
|
||||
49 |
|
||||
50 | while True:
|
||||
51 | # SIM117
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:53:5
|
||||
|
|
||||
51 | while True:
|
||||
52 | # SIM117
|
||||
53 | / with A() as a:
|
||||
54 | | with B() as b:
|
||||
| |______________________^
|
||||
55 | """this
|
||||
56 | is valid"""
|
||||
|
|
||||
help: Combine `with` statements
|
||||
50 |
|
||||
51 | while True:
|
||||
52 | # SIM117
|
||||
- with A() as a:
|
||||
- with B() as b:
|
||||
- """this
|
||||
53 + with A() as a, B() as b:
|
||||
54 + """this
|
||||
55 | is valid"""
|
||||
56 |
|
||||
- """the indentation on
|
||||
57 + """the indentation on
|
||||
58 | this line is significant"""
|
||||
59 |
|
||||
- "this is" \
|
||||
60 + "this is" \
|
||||
61 | "allowed too"
|
||||
62 |
|
||||
- ("so is"
|
||||
63 + ("so is"
|
||||
64 | "this for some reason")
|
||||
65 |
|
||||
66 | # SIM117
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:68:1
|
||||
|
|
||||
67 | # SIM117
|
||||
68 | / with (
|
||||
69 | | A() as a,
|
||||
70 | | B() as b,
|
||||
71 | | ):
|
||||
72 | | with C() as c:
|
||||
| |__________________^
|
||||
73 | print("hello")
|
||||
|
|
||||
help: Combine `with` statements
|
||||
67 | # SIM117
|
||||
68 | with (
|
||||
69 | A() as a,
|
||||
- B() as b,
|
||||
70 + B() as b,C() as c
|
||||
71 | ):
|
||||
- with C() as c:
|
||||
- print("hello")
|
||||
72 + print("hello")
|
||||
73 |
|
||||
74 | # SIM117
|
||||
75 | with A() as a:
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:76:1
|
||||
|
|
||||
75 | # SIM117
|
||||
76 | / with A() as a:
|
||||
77 | | with (
|
||||
78 | | B() as b,
|
||||
79 | | C() as c,
|
||||
80 | | ):
|
||||
| |______^
|
||||
81 | print("hello")
|
||||
|
|
||||
help: Combine `with` statements
|
||||
73 | print("hello")
|
||||
74 |
|
||||
75 | # SIM117
|
||||
- with A() as a:
|
||||
- with (
|
||||
- B() as b,
|
||||
- C() as c,
|
||||
- ):
|
||||
- print("hello")
|
||||
76 + with (
|
||||
77 + A() as a, B() as b,
|
||||
78 + C() as c,
|
||||
79 + ):
|
||||
80 + print("hello")
|
||||
81 |
|
||||
82 | # SIM117
|
||||
83 | with (
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:84:1
|
||||
|
|
||||
83 | # SIM117
|
||||
84 | / with (
|
||||
85 | | A() as a,
|
||||
86 | | B() as b,
|
||||
87 | | ):
|
||||
88 | | with (
|
||||
89 | | C() as c,
|
||||
90 | | D() as d,
|
||||
91 | | ):
|
||||
| |______^
|
||||
92 | print("hello")
|
||||
|
|
||||
help: Combine `with` statements
|
||||
83 | # SIM117
|
||||
84 | with (
|
||||
85 | A() as a,
|
||||
- B() as b,
|
||||
86 + B() as b,C() as c,
|
||||
87 + D() as d,
|
||||
88 | ):
|
||||
- with (
|
||||
- C() as c,
|
||||
- D() as d,
|
||||
- ):
|
||||
- print("hello")
|
||||
89 + print("hello")
|
||||
90 |
|
||||
91 | # SIM117 (auto-fixable)
|
||||
92 | with A("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as a:
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:95:1
|
||||
|
|
||||
94 | # SIM117 (auto-fixable)
|
||||
95 | / with A("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as a:
|
||||
96 | | with B("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as b:
|
||||
| |__________________________________________________^
|
||||
97 | print("hello")
|
||||
|
|
||||
help: Combine `with` statements
|
||||
92 | print("hello")
|
||||
93 |
|
||||
94 | # SIM117 (auto-fixable)
|
||||
- with A("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as a:
|
||||
- with B("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as b:
|
||||
- print("hello")
|
||||
95 + with A("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as a, B("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as b:
|
||||
96 + print("hello")
|
||||
97 |
|
||||
98 | # SIM117 (not auto-fixable too long)
|
||||
99 | with A("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ890") as a:
|
||||
|
||||
SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:100:1
|
||||
|
|
||||
99 | # SIM117 (not auto-fixable too long)
|
||||
100 | / with A("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ890") as a:
|
||||
101 | | with B("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as b:
|
||||
| |__________________________________________________^
|
||||
102 | print("hello")
|
||||
|
|
||||
help: Combine `with` statements
|
||||
|
||||
SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:106:5
|
||||
|
|
||||
104 | # From issue #3025.
|
||||
105 | async def main():
|
||||
106 | / async with A() as a: # SIM117.
|
||||
107 | | async with B() as b:
|
||||
| |____________________________^
|
||||
108 | print("async-inside!")
|
||||
|
|
||||
help: Combine `with` statements
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:126:1
|
||||
|
|
||||
125 | # SIM117
|
||||
126 | / with A() as a:
|
||||
127 | | with B() as b:
|
||||
| |__________________^
|
||||
128 | type ListOrSet[T] = list[T] | set[T]
|
||||
|
|
||||
help: Combine `with` statements
|
||||
123 | f(b2, c2, d2)
|
||||
124 |
|
||||
125 | # SIM117
|
||||
- with A() as a:
|
||||
- with B() as b:
|
||||
- type ListOrSet[T] = list[T] | set[T]
|
||||
126 + with A() as a, B() as b:
|
||||
127 + type ListOrSet[T] = list[T] | set[T]
|
||||
128 |
|
||||
- class ClassA[T: str]:
|
||||
- def method1(self) -> T:
|
||||
- ...
|
||||
129 + class ClassA[T: str]:
|
||||
130 + def method1(self) -> T:
|
||||
131 + ...
|
||||
132 |
|
||||
- f" something { my_dict["key"] } something else "
|
||||
133 + f" something { my_dict["key"] } something else "
|
||||
134 |
|
||||
- f"foo {f"bar {x}"} baz"
|
||||
135 + f"foo {f"bar {x}"} baz"
|
||||
136 |
|
||||
137 | # Allow cascading for some statements.
|
||||
138 | import anyio
|
||||
|
||||
SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
|
||||
--> SIM117.py:163:1
|
||||
|
|
||||
162 | # Do not suppress combination, if a context manager is already combined with another.
|
||||
163 | / async with asyncio.timeout(1), A():
|
||||
164 | | async with B():
|
||||
| |___________________^
|
||||
165 | pass
|
||||
|
|
||||
help: Combine `with` statements
|
||||
160 | pass
|
||||
161 |
|
||||
162 | # Do not suppress combination, if a context manager is already combined with another.
|
||||
- async with asyncio.timeout(1), A():
|
||||
- async with B():
|
||||
- pass
|
||||
163 + async with asyncio.timeout(1), A(), B():
|
||||
164 + pass
|
||||
@@ -66,7 +66,7 @@ impl TypingReference {
|
||||
}
|
||||
|
||||
// prefer `from __future__ import annotations` to quoting
|
||||
if settings.future_annotations()
|
||||
if settings.future_annotations
|
||||
&& !reference.in_typing_only_annotation()
|
||||
&& reference.in_runtime_evaluated_annotation()
|
||||
{
|
||||
|
||||
@@ -14,7 +14,6 @@ mod tests {
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::registry::{Linter, Rule};
|
||||
use crate::settings::types::PreviewMode;
|
||||
use crate::test::{test_path, test_snippet};
|
||||
use crate::{assert_diagnostics, settings};
|
||||
|
||||
@@ -86,7 +85,6 @@ mod tests {
|
||||
Path::new("flake8_type_checking").join(path).as_path(),
|
||||
&settings::LinterSettings {
|
||||
future_annotations: true,
|
||||
preview: PreviewMode::Enabled,
|
||||
// also enable quoting annotations to check the interaction. the future import
|
||||
// should take precedence.
|
||||
flake8_type_checking: super::settings::Settings {
|
||||
|
||||
@@ -11,12 +11,10 @@ use crate::checkers::ast::{Checker, DiagnosticGuard};
|
||||
use crate::codes::Rule;
|
||||
use crate::fix;
|
||||
use crate::importer::ImportedMembers;
|
||||
use crate::preview::is_full_path_match_source_strategy_enabled;
|
||||
use crate::rules::flake8_type_checking::helpers::{
|
||||
TypingReference, filter_contained, quote_annotation,
|
||||
};
|
||||
use crate::rules::flake8_type_checking::imports::ImportBinding;
|
||||
use crate::rules::isort::categorize::MatchSourceStrategy;
|
||||
use crate::rules::isort::{ImportSection, ImportType, categorize};
|
||||
use crate::{Fix, FixAvailability, Violation};
|
||||
|
||||
@@ -40,6 +38,13 @@ use crate::{Fix, FixAvailability, Violation};
|
||||
/// [`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them
|
||||
/// as such.
|
||||
///
|
||||
/// If [`lint.future-annotations`] is set to `true`, `from __future__ import
|
||||
/// annotations` will be added if doing so would enable an import to be
|
||||
/// moved into an `if TYPE_CHECKING:` block. This takes precedence over the
|
||||
/// [`lint.flake8-type-checking.quote-annotations`] setting described above if
|
||||
/// both settings are enabled.
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// from __future__ import annotations
|
||||
@@ -65,18 +70,6 @@ use crate::{Fix, FixAvailability, Violation};
|
||||
/// return len(sized)
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// ## Preview
|
||||
/// When [preview](https://docs.astral.sh/ruff/preview/) is enabled,
|
||||
/// the criterion for determining whether an import is first-party
|
||||
/// is stricter, which could affect whether this lint is triggered vs [`TC001`](https://docs.astral.sh/ruff/rules/typing-only-third-party-import/). See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) for more details.
|
||||
///
|
||||
/// If [`lint.future-annotations`] is set to `true`, `from __future__ import
|
||||
/// annotations` will be added if doing so would enable an import to be moved into an `if
|
||||
/// TYPE_CHECKING:` block. This takes precedence over the
|
||||
/// [`lint.flake8-type-checking.quote-annotations`] setting described above if both settings are
|
||||
/// enabled.
|
||||
///
|
||||
/// ## Options
|
||||
/// - `lint.flake8-type-checking.quote-annotations`
|
||||
/// - `lint.flake8-type-checking.runtime-evaluated-base-classes`
|
||||
@@ -128,6 +121,12 @@ impl Violation for TypingOnlyFirstPartyImport {
|
||||
/// [`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them
|
||||
/// as such.
|
||||
///
|
||||
/// If [`lint.future-annotations`] is set to `true`, `from __future__ import
|
||||
/// annotations` will be added if doing so would enable an import to be
|
||||
/// moved into an `if TYPE_CHECKING:` block. This takes precedence over the
|
||||
/// [`lint.flake8-type-checking.quote-annotations`] setting described above if
|
||||
/// both settings are enabled.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// from __future__ import annotations
|
||||
@@ -153,17 +152,6 @@ impl Violation for TypingOnlyFirstPartyImport {
|
||||
/// return len(df)
|
||||
/// ```
|
||||
///
|
||||
/// ## Preview
|
||||
/// When [preview](https://docs.astral.sh/ruff/preview/) is enabled,
|
||||
/// the criterion for determining whether an import is first-party
|
||||
/// is stricter, which could affect whether this lint is triggered vs [`TC001`](https://docs.astral.sh/ruff/rules/typing-only-first-party-import/). See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) for more details.
|
||||
///
|
||||
/// If [`lint.future-annotations`] is set to `true`, `from __future__ import
|
||||
/// annotations` will be added if doing so would enable an import to be moved into an `if
|
||||
/// TYPE_CHECKING:` block. This takes precedence over the
|
||||
/// [`lint.flake8-type-checking.quote-annotations`] setting described above if both settings are
|
||||
/// enabled.
|
||||
///
|
||||
/// ## Options
|
||||
/// - `lint.flake8-type-checking.quote-annotations`
|
||||
/// - `lint.flake8-type-checking.runtime-evaluated-base-classes`
|
||||
@@ -215,6 +203,12 @@ impl Violation for TypingOnlyThirdPartyImport {
|
||||
/// [`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them
|
||||
/// as such.
|
||||
///
|
||||
/// If [`lint.future-annotations`] is set to `true`, `from __future__ import
|
||||
/// annotations` will be added if doing so would enable an import to be
|
||||
/// moved into an `if TYPE_CHECKING:` block. This takes precedence over the
|
||||
/// [`lint.flake8-type-checking.quote-annotations`] setting described above if
|
||||
/// both settings are enabled.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// from __future__ import annotations
|
||||
@@ -240,15 +234,6 @@ impl Violation for TypingOnlyThirdPartyImport {
|
||||
/// return str(path)
|
||||
/// ```
|
||||
///
|
||||
/// ## Preview
|
||||
///
|
||||
/// When [preview](https://docs.astral.sh/ruff/preview/) is enabled, if
|
||||
/// [`lint.future-annotations`] is set to `true`, `from __future__ import
|
||||
/// annotations` will be added if doing so would enable an import to be moved into an `if
|
||||
/// TYPE_CHECKING:` block. This takes precedence over the
|
||||
/// [`lint.flake8-type-checking.quote-annotations`] setting described above if both settings are
|
||||
/// enabled.
|
||||
///
|
||||
/// ## Options
|
||||
/// - `lint.flake8-type-checking.quote-annotations`
|
||||
/// - `lint.flake8-type-checking.runtime-evaluated-base-classes`
|
||||
@@ -297,7 +282,7 @@ pub(crate) fn typing_only_runtime_import(
|
||||
|
||||
// If we can't add a `__future__` import and in un-strict mode, don't flag typing-only
|
||||
// imports that are implicitly loaded by way of a valid runtime import.
|
||||
if !checker.settings().future_annotations()
|
||||
if !checker.settings().future_annotations
|
||||
&& !checker.settings().flake8_type_checking.strict
|
||||
&& runtime_imports
|
||||
.iter()
|
||||
@@ -347,13 +332,6 @@ pub(crate) fn typing_only_runtime_import(
|
||||
let source_name = import.source_name().join(".");
|
||||
|
||||
// Categorize the import, using coarse-grained categorization.
|
||||
let match_source_strategy =
|
||||
if is_full_path_match_source_strategy_enabled(checker.settings()) {
|
||||
MatchSourceStrategy::FullPath
|
||||
} else {
|
||||
MatchSourceStrategy::Root
|
||||
};
|
||||
|
||||
let import_type = match categorize(
|
||||
&source_name,
|
||||
qualified_name.is_unresolved_import(),
|
||||
@@ -365,7 +343,6 @@ pub(crate) fn typing_only_runtime_import(
|
||||
checker.settings().isort.no_sections,
|
||||
&checker.settings().isort.section_order,
|
||||
&checker.settings().isort.default_section,
|
||||
match_source_strategy,
|
||||
) {
|
||||
ImportSection::Known(ImportType::LocalFolder | ImportType::FirstParty) => {
|
||||
ImportType::FirstParty
|
||||
|
||||
@@ -60,6 +60,7 @@ pub(crate) fn check_os_pathlib_single_arg_calls(
|
||||
fn_argument: &str,
|
||||
fix_enabled: bool,
|
||||
violation: impl Violation,
|
||||
applicability: Option<Applicability>,
|
||||
) {
|
||||
if call.arguments.len() != 1 {
|
||||
return;
|
||||
@@ -74,33 +75,39 @@ pub(crate) fn check_os_pathlib_single_arg_calls(
|
||||
|
||||
let mut diagnostic = checker.report_diagnostic(violation, call.func.range());
|
||||
|
||||
if fix_enabled {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("pathlib", "Path"),
|
||||
call.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
|
||||
let applicability = if checker.comment_ranges().intersects(range) {
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
};
|
||||
|
||||
let replacement = if is_pathlib_path_call(checker, arg) {
|
||||
format!("{arg_code}.{attr}")
|
||||
} else {
|
||||
format!("{binding}({arg_code}).{attr}")
|
||||
};
|
||||
|
||||
Ok(Fix::applicable_edits(
|
||||
Edit::range_replacement(replacement, range),
|
||||
[import_edit],
|
||||
applicability,
|
||||
))
|
||||
});
|
||||
if !fix_enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("pathlib", "Path"),
|
||||
call.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
|
||||
let replacement = if is_pathlib_path_call(checker, arg) {
|
||||
format!("{arg_code}.{attr}")
|
||||
} else {
|
||||
format!("{binding}({arg_code}).{attr}")
|
||||
};
|
||||
|
||||
let edit = Edit::range_replacement(replacement, range);
|
||||
|
||||
let fix = match applicability {
|
||||
Some(Applicability::Unsafe) => Fix::unsafe_edits(edit, [import_edit]),
|
||||
_ => {
|
||||
let applicability = if checker.comment_ranges().intersects(range) {
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
};
|
||||
Fix::applicable_edits(edit, [import_edit], applicability)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(fix)
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn get_name_expr(expr: &Expr) -> Option<&ast::ExprName> {
|
||||
|
||||
@@ -129,7 +129,6 @@ mod tests {
|
||||
#[test_case(Rule::OsPathGetatime, Path::new("PTH203.py"))]
|
||||
#[test_case(Rule::OsPathGetmtime, Path::new("PTH204.py"))]
|
||||
#[test_case(Rule::OsPathGetctime, Path::new("PTH205.py"))]
|
||||
#[test_case(Rule::OsSymlink, Path::new("PTH211.py"))]
|
||||
fn preview_flake8_use_pathlib(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_path_basename_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.path.basename`.
|
||||
@@ -34,7 +36,16 @@ use ruff_python_ast::ExprCall;
|
||||
/// especially on older versions of Python.
|
||||
///
|
||||
/// ## Fix Safety
|
||||
/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression.
|
||||
/// This rule's fix is always marked as unsafe because the replacement is not always semantically
|
||||
/// equivalent to the original code. In particular, `pathlib` performs path normalization,
|
||||
/// which can alter the result compared to `os.path.basename`. For example, this normalization:
|
||||
///
|
||||
/// - Collapses consecutive slashes (e.g., `"a//b"` → `"a/b"`).
|
||||
/// - Removes trailing slashes (e.g., `"a/b/"` → `"a/b"`).
|
||||
/// - Eliminates `"."` (e.g., `"a/./b"` → `"a/b"`).
|
||||
///
|
||||
/// As a result, code relying on the exact string returned by `os.path.basename`
|
||||
/// may behave differently after the fix.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `PurePath.name`](https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.name)
|
||||
@@ -62,6 +73,7 @@ pub(crate) fn os_path_basename(checker: &Checker, call: &ExprCall, segments: &[&
|
||||
if segments != ["os", "path", "basename"] {
|
||||
return;
|
||||
}
|
||||
|
||||
check_os_pathlib_single_arg_calls(
|
||||
checker,
|
||||
call,
|
||||
@@ -69,5 +81,6 @@ pub(crate) fn os_path_basename(checker: &Checker, call: &ExprCall, segments: &[&
|
||||
"p",
|
||||
is_fix_os_path_basename_enabled(checker.settings()),
|
||||
OsPathBasename,
|
||||
Some(Applicability::Unsafe),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_path_dirname_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.path.dirname`.
|
||||
@@ -29,7 +31,16 @@ use ruff_python_ast::ExprCall;
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix Safety
|
||||
/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression.
|
||||
/// This rule's fix is always marked as unsafe because the replacement is not always semantically
|
||||
/// equivalent to the original code. In particular, `pathlib` performs path normalization,
|
||||
/// which can alter the result compared to `os.path.dirname`. For example, this normalization:
|
||||
///
|
||||
/// - Collapses consecutive slashes (e.g., `"a//b"` → `"a/b"`).
|
||||
/// - Removes trailing slashes (e.g., `"a/b/"` → `"a/b"`).
|
||||
/// - Eliminates `"."` (e.g., `"a/./b"` → `"a/b"`).
|
||||
///
|
||||
/// As a result, code relying on the exact string returned by `os.path.dirname`
|
||||
/// may behave differently after the fix.
|
||||
///
|
||||
/// ## Known issues
|
||||
/// While using `pathlib` can improve the readability and type safety of your code,
|
||||
@@ -62,6 +73,7 @@ pub(crate) fn os_path_dirname(checker: &Checker, call: &ExprCall, segments: &[&s
|
||||
if segments != ["os", "path", "dirname"] {
|
||||
return;
|
||||
}
|
||||
|
||||
check_os_pathlib_single_arg_calls(
|
||||
checker,
|
||||
call,
|
||||
@@ -69,5 +81,6 @@ pub(crate) fn os_path_dirname(checker: &Checker, call: &ExprCall, segments: &[&s
|
||||
"p",
|
||||
is_fix_os_path_dirname_enabled(checker.settings()),
|
||||
OsPathDirname,
|
||||
Some(Applicability::Unsafe),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_path_exists_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.path.exists`.
|
||||
@@ -62,6 +63,7 @@ pub(crate) fn os_path_exists(checker: &Checker, call: &ExprCall, segments: &[&st
|
||||
if segments != ["os", "path", "exists"] {
|
||||
return;
|
||||
}
|
||||
|
||||
check_os_pathlib_single_arg_calls(
|
||||
checker,
|
||||
call,
|
||||
@@ -69,5 +71,6 @@ pub(crate) fn os_path_exists(checker: &Checker, call: &ExprCall, segments: &[&st
|
||||
"path",
|
||||
is_fix_os_path_exists_enabled(checker.settings()),
|
||||
OsPathExists,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_path_expanduser_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.path.expanduser`.
|
||||
@@ -62,6 +63,7 @@ pub(crate) fn os_path_expanduser(checker: &Checker, call: &ExprCall, segments: &
|
||||
if segments != ["os", "path", "expanduser"] {
|
||||
return;
|
||||
}
|
||||
|
||||
check_os_pathlib_single_arg_calls(
|
||||
checker,
|
||||
call,
|
||||
@@ -69,5 +71,6 @@ pub(crate) fn os_path_expanduser(checker: &Checker, call: &ExprCall, segments: &
|
||||
"path",
|
||||
is_fix_os_path_expanduser_enabled(checker.settings()),
|
||||
OsPathExpanduser,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_path_getatime_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.path.getatime`.
|
||||
@@ -65,6 +66,7 @@ pub(crate) fn os_path_getatime(checker: &Checker, call: &ExprCall, segments: &[&
|
||||
if segments != ["os", "path", "getatime"] {
|
||||
return;
|
||||
}
|
||||
|
||||
check_os_pathlib_single_arg_calls(
|
||||
checker,
|
||||
call,
|
||||
@@ -72,5 +74,6 @@ pub(crate) fn os_path_getatime(checker: &Checker, call: &ExprCall, segments: &[&
|
||||
"filename",
|
||||
is_fix_os_path_getatime_enabled(checker.settings()),
|
||||
OsPathGetatime,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_path_getctime_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.path.getctime`.
|
||||
@@ -66,6 +67,7 @@ pub(crate) fn os_path_getctime(checker: &Checker, call: &ExprCall, segments: &[&
|
||||
if segments != ["os", "path", "getctime"] {
|
||||
return;
|
||||
}
|
||||
|
||||
check_os_pathlib_single_arg_calls(
|
||||
checker,
|
||||
call,
|
||||
@@ -73,5 +75,6 @@ pub(crate) fn os_path_getctime(checker: &Checker, call: &ExprCall, segments: &[&
|
||||
"filename",
|
||||
is_fix_os_path_getctime_enabled(checker.settings()),
|
||||
OsPathGetctime,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_path_getmtime_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.path.getmtime`.
|
||||
@@ -66,6 +67,7 @@ pub(crate) fn os_path_getmtime(checker: &Checker, call: &ExprCall, segments: &[&
|
||||
if segments != ["os", "path", "getmtime"] {
|
||||
return;
|
||||
}
|
||||
|
||||
check_os_pathlib_single_arg_calls(
|
||||
checker,
|
||||
call,
|
||||
@@ -73,5 +75,6 @@ pub(crate) fn os_path_getmtime(checker: &Checker, call: &ExprCall, segments: &[&
|
||||
"filename",
|
||||
is_fix_os_path_getmtime_enabled(checker.settings()),
|
||||
OsPathGetmtime,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_path_getsize_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.path.getsize`.
|
||||
@@ -66,6 +67,7 @@ pub(crate) fn os_path_getsize(checker: &Checker, call: &ExprCall, segments: &[&s
|
||||
if segments != ["os", "path", "getsize"] {
|
||||
return;
|
||||
}
|
||||
|
||||
check_os_pathlib_single_arg_calls(
|
||||
checker,
|
||||
call,
|
||||
@@ -73,5 +75,6 @@ pub(crate) fn os_path_getsize(checker: &Checker, call: &ExprCall, segments: &[&s
|
||||
"filename",
|
||||
is_fix_os_path_getsize_enabled(checker.settings()),
|
||||
OsPathGetsize,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_path_isabs_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.path.isabs`.
|
||||
@@ -61,6 +62,7 @@ pub(crate) fn os_path_isabs(checker: &Checker, call: &ExprCall, segments: &[&str
|
||||
if segments != ["os", "path", "isabs"] {
|
||||
return;
|
||||
}
|
||||
|
||||
check_os_pathlib_single_arg_calls(
|
||||
checker,
|
||||
call,
|
||||
@@ -68,5 +70,6 @@ pub(crate) fn os_path_isabs(checker: &Checker, call: &ExprCall, segments: &[&str
|
||||
"s",
|
||||
is_fix_os_path_isabs_enabled(checker.settings()),
|
||||
OsPathIsabs,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_path_isdir_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.path.isdir`.
|
||||
@@ -71,5 +72,6 @@ pub(crate) fn os_path_isdir(checker: &Checker, call: &ExprCall, segments: &[&str
|
||||
"s",
|
||||
is_fix_os_path_isdir_enabled(checker.settings()),
|
||||
OsPathIsdir,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_path_isfile_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.path.isfile`.
|
||||
@@ -71,5 +72,6 @@ pub(crate) fn os_path_isfile(checker: &Checker, call: &ExprCall, segments: &[&st
|
||||
"path",
|
||||
is_fix_os_path_isfile_enabled(checker.settings()),
|
||||
OsPathIsfile,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_path_islink_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.path.islink`.
|
||||
@@ -71,5 +72,6 @@ pub(crate) fn os_path_islink(checker: &Checker, call: &ExprCall, segments: &[&st
|
||||
"path",
|
||||
is_fix_os_path_islink_enabled(checker.settings()),
|
||||
OsPathIslink,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::{ExprCall, PythonVersion};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_readlink_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::{
|
||||
check_os_pathlib_single_arg_calls, is_keyword_only_argument_non_default,
|
||||
};
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::{ExprCall, PythonVersion};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.readlink`.
|
||||
@@ -87,5 +88,6 @@ pub(crate) fn os_readlink(checker: &Checker, call: &ExprCall, segments: &[&str])
|
||||
"path",
|
||||
is_fix_os_readlink_enabled(checker.settings()),
|
||||
OsReadlink,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_remove_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::{
|
||||
check_os_pathlib_single_arg_calls, is_keyword_only_argument_non_default,
|
||||
};
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.remove`.
|
||||
@@ -82,5 +83,6 @@ pub(crate) fn os_remove(checker: &Checker, call: &ExprCall, segments: &[&str]) {
|
||||
"path",
|
||||
is_fix_os_remove_enabled(checker.settings()),
|
||||
OsRemove,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_rmdir_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::{
|
||||
check_os_pathlib_single_arg_calls, is_keyword_only_argument_non_default,
|
||||
};
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.rmdir`.
|
||||
@@ -82,5 +83,6 @@ pub(crate) fn os_rmdir(checker: &Checker, call: &ExprCall, segments: &[&str]) {
|
||||
"path",
|
||||
is_fix_os_rmdir_enabled(checker.settings()),
|
||||
OsRmdir,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_fix_os_unlink_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::{
|
||||
check_os_pathlib_single_arg_calls, is_keyword_only_argument_non_default,
|
||||
};
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.unlink`.
|
||||
@@ -82,5 +83,6 @@ pub(crate) fn os_unlink(checker: &Checker, call: &ExprCall, segments: &[&str]) {
|
||||
"path",
|
||||
is_fix_os_unlink_enabled(checker.settings()),
|
||||
OsUnlink,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs
|
||||
---
|
||||
PTH211 [*] `os.symlink` should be replaced by `Path.symlink_to`
|
||||
--> PTH211.py:5:1
|
||||
|
|
||||
5 | os.symlink("usr/bin/python", "tmp/python")
|
||||
| ^^^^^^^^^^
|
||||
6 | os.symlink(b"usr/bin/python", b"tmp/python")
|
||||
7 | Path("tmp/python").symlink_to("usr/bin/python") # Ok
|
||||
|
|
||||
help: Replace with `Path(...).symlink_to(...)`
|
||||
2 | from pathlib import Path
|
||||
3 |
|
||||
4 |
|
||||
- os.symlink("usr/bin/python", "tmp/python")
|
||||
5 + Path("tmp/python").symlink_to("usr/bin/python")
|
||||
6 | os.symlink(b"usr/bin/python", b"tmp/python")
|
||||
7 | Path("tmp/python").symlink_to("usr/bin/python") # Ok
|
||||
8 |
|
||||
|
||||
PTH211 [*] `os.symlink` should be replaced by `Path.symlink_to`
|
||||
--> PTH211.py:6:1
|
||||
|
|
||||
5 | os.symlink("usr/bin/python", "tmp/python")
|
||||
6 | os.symlink(b"usr/bin/python", b"tmp/python")
|
||||
| ^^^^^^^^^^
|
||||
7 | Path("tmp/python").symlink_to("usr/bin/python") # Ok
|
||||
|
|
||||
help: Replace with `Path(...).symlink_to(...)`
|
||||
3 |
|
||||
4 |
|
||||
5 | os.symlink("usr/bin/python", "tmp/python")
|
||||
- os.symlink(b"usr/bin/python", b"tmp/python")
|
||||
6 + Path(b"tmp/python").symlink_to(b"usr/bin/python")
|
||||
7 | Path("tmp/python").symlink_to("usr/bin/python") # Ok
|
||||
8 |
|
||||
9 | os.symlink("usr/bin/python", "tmp/python", target_is_directory=True)
|
||||
|
||||
PTH211 [*] `os.symlink` should be replaced by `Path.symlink_to`
|
||||
--> PTH211.py:9:1
|
||||
|
|
||||
7 | Path("tmp/python").symlink_to("usr/bin/python") # Ok
|
||||
8 |
|
||||
9 | os.symlink("usr/bin/python", "tmp/python", target_is_directory=True)
|
||||
| ^^^^^^^^^^
|
||||
10 | os.symlink(b"usr/bin/python", b"tmp/python", target_is_directory=True)
|
||||
11 | Path("tmp/python").symlink_to("usr/bin/python", target_is_directory=True) # Ok
|
||||
|
|
||||
help: Replace with `Path(...).symlink_to(...)`
|
||||
6 | os.symlink(b"usr/bin/python", b"tmp/python")
|
||||
7 | Path("tmp/python").symlink_to("usr/bin/python") # Ok
|
||||
8 |
|
||||
- os.symlink("usr/bin/python", "tmp/python", target_is_directory=True)
|
||||
9 + Path("tmp/python").symlink_to("usr/bin/python", target_is_directory=True)
|
||||
10 | os.symlink(b"usr/bin/python", b"tmp/python", target_is_directory=True)
|
||||
11 | Path("tmp/python").symlink_to("usr/bin/python", target_is_directory=True) # Ok
|
||||
12 |
|
||||
|
||||
PTH211 [*] `os.symlink` should be replaced by `Path.symlink_to`
|
||||
--> PTH211.py:10:1
|
||||
|
|
||||
9 | os.symlink("usr/bin/python", "tmp/python", target_is_directory=True)
|
||||
10 | os.symlink(b"usr/bin/python", b"tmp/python", target_is_directory=True)
|
||||
| ^^^^^^^^^^
|
||||
11 | Path("tmp/python").symlink_to("usr/bin/python", target_is_directory=True) # Ok
|
||||
|
|
||||
help: Replace with `Path(...).symlink_to(...)`
|
||||
7 | Path("tmp/python").symlink_to("usr/bin/python") # Ok
|
||||
8 |
|
||||
9 | os.symlink("usr/bin/python", "tmp/python", target_is_directory=True)
|
||||
- os.symlink(b"usr/bin/python", b"tmp/python", target_is_directory=True)
|
||||
10 + Path(b"tmp/python").symlink_to(b"usr/bin/python", target_is_directory=True)
|
||||
11 | Path("tmp/python").symlink_to("usr/bin/python", target_is_directory=True) # Ok
|
||||
12 |
|
||||
13 | fd = os.open(".", os.O_RDONLY)
|
||||
|
||||
PTH211 `os.symlink` should be replaced by `Path.symlink_to`
|
||||
--> PTH211.py:17:1
|
||||
|
|
||||
15 | os.close(fd)
|
||||
16 |
|
||||
17 | os.symlink(src="usr/bin/python", dst="tmp/python", unknown=True)
|
||||
| ^^^^^^^^^^
|
||||
18 | os.symlink("usr/bin/python", dst="tmp/python", target_is_directory=False)
|
||||
|
|
||||
help: Replace with `Path(...).symlink_to(...)`
|
||||
|
||||
PTH211 [*] `os.symlink` should be replaced by `Path.symlink_to`
|
||||
--> PTH211.py:18:1
|
||||
|
|
||||
17 | os.symlink(src="usr/bin/python", dst="tmp/python", unknown=True)
|
||||
18 | os.symlink("usr/bin/python", dst="tmp/python", target_is_directory=False)
|
||||
| ^^^^^^^^^^
|
||||
19 |
|
||||
20 | os.symlink(src="usr/bin/python", dst="tmp/python", dir_fd=None)
|
||||
|
|
||||
help: Replace with `Path(...).symlink_to(...)`
|
||||
15 | os.close(fd)
|
||||
16 |
|
||||
17 | os.symlink(src="usr/bin/python", dst="tmp/python", unknown=True)
|
||||
- os.symlink("usr/bin/python", dst="tmp/python", target_is_directory=False)
|
||||
18 + Path("tmp/python").symlink_to("usr/bin/python")
|
||||
19 |
|
||||
20 | os.symlink(src="usr/bin/python", dst="tmp/python", dir_fd=None)
|
||||
21 |
|
||||
|
||||
PTH211 [*] `os.symlink` should be replaced by `Path.symlink_to`
|
||||
--> PTH211.py:20:1
|
||||
|
|
||||
18 | os.symlink("usr/bin/python", dst="tmp/python", target_is_directory=False)
|
||||
19 |
|
||||
20 | os.symlink(src="usr/bin/python", dst="tmp/python", dir_fd=None)
|
||||
| ^^^^^^^^^^
|
||||
21 |
|
||||
22 | os.symlink("usr/bin/python", dst="tmp/python", target_is_directory= True )
|
||||
|
|
||||
help: Replace with `Path(...).symlink_to(...)`
|
||||
17 | os.symlink(src="usr/bin/python", dst="tmp/python", unknown=True)
|
||||
18 | os.symlink("usr/bin/python", dst="tmp/python", target_is_directory=False)
|
||||
19 |
|
||||
- os.symlink(src="usr/bin/python", dst="tmp/python", dir_fd=None)
|
||||
20 + Path("tmp/python").symlink_to("usr/bin/python")
|
||||
21 |
|
||||
22 | os.symlink("usr/bin/python", dst="tmp/python", target_is_directory= True )
|
||||
23 | os.symlink("usr/bin/python", dst="tmp/python", target_is_directory="nonboolean")
|
||||
|
||||
PTH211 [*] `os.symlink` should be replaced by `Path.symlink_to`
|
||||
--> PTH211.py:22:1
|
||||
|
|
||||
20 | os.symlink(src="usr/bin/python", dst="tmp/python", dir_fd=None)
|
||||
21 |
|
||||
22 | os.symlink("usr/bin/python", dst="tmp/python", target_is_directory= True )
|
||||
| ^^^^^^^^^^
|
||||
23 | os.symlink("usr/bin/python", dst="tmp/python", target_is_directory="nonboolean")
|
||||
|
|
||||
help: Replace with `Path(...).symlink_to(...)`
|
||||
19 |
|
||||
20 | os.symlink(src="usr/bin/python", dst="tmp/python", dir_fd=None)
|
||||
21 |
|
||||
- os.symlink("usr/bin/python", dst="tmp/python", target_is_directory= True )
|
||||
22 + Path("tmp/python").symlink_to("usr/bin/python", target_is_directory=True)
|
||||
23 | os.symlink("usr/bin/python", dst="tmp/python", target_is_directory="nonboolean")
|
||||
|
||||
PTH211 `os.symlink` should be replaced by `Path.symlink_to`
|
||||
--> PTH211.py:23:1
|
||||
|
|
||||
22 | os.symlink("usr/bin/python", dst="tmp/python", target_is_directory= True )
|
||||
23 | os.symlink("usr/bin/python", dst="tmp/python", target_is_directory="nonboolean")
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: Replace with `Path(...).symlink_to(...)`
|
||||
@@ -466,6 +466,7 @@ help: Replace with `Path(...).name`
|
||||
30 | os.path.dirname(p)
|
||||
31 | os.path.samefile(p)
|
||||
32 | os.path.splitext(p)
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
PTH120 [*] `os.path.dirname()` should be replaced by `Path.parent`
|
||||
--> full_name.py:29:1
|
||||
@@ -493,6 +494,7 @@ help: Replace with `Path(...).parent`
|
||||
31 | os.path.samefile(p)
|
||||
32 | os.path.splitext(p)
|
||||
33 | with open(p) as fp:
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
PTH121 `os.path.samefile()` should be replaced by `Path.samefile()`
|
||||
--> full_name.py:30:1
|
||||
|
||||
@@ -466,6 +466,7 @@ help: Replace with `Path(...).name`
|
||||
30 | foo_p.dirname(p)
|
||||
31 | foo_p.samefile(p)
|
||||
32 | foo_p.splitext(p)
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
PTH120 [*] `os.path.dirname()` should be replaced by `Path.parent`
|
||||
--> import_as.py:29:1
|
||||
@@ -492,6 +493,7 @@ help: Replace with `Path(...).parent`
|
||||
30 + pathlib.Path(p).parent
|
||||
31 | foo_p.samefile(p)
|
||||
32 | foo_p.splitext(p)
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
PTH121 `os.path.samefile()` should be replaced by `Path.samefile()`
|
||||
--> import_as.py:30:1
|
||||
|
||||
@@ -480,6 +480,7 @@ help: Replace with `Path(...).name`
|
||||
32 | dirname(p)
|
||||
33 | samefile(p)
|
||||
34 | splitext(p)
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
PTH120 [*] `os.path.dirname()` should be replaced by `Path.parent`
|
||||
--> import_from.py:31:1
|
||||
@@ -508,6 +509,7 @@ help: Replace with `Path(...).parent`
|
||||
33 | samefile(p)
|
||||
34 | splitext(p)
|
||||
35 | with open(p) as fp:
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
PTH121 `os.path.samefile()` should be replaced by `Path.samefile()`
|
||||
--> import_from.py:32:1
|
||||
|
||||
@@ -480,6 +480,7 @@ help: Replace with `Path(...).name`
|
||||
37 | xdirname(p)
|
||||
38 | xsamefile(p)
|
||||
39 | xsplitext(p)
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
PTH120 [*] `os.path.dirname()` should be replaced by `Path.parent`
|
||||
--> import_from_as.py:36:1
|
||||
@@ -507,6 +508,7 @@ help: Replace with `Path(...).parent`
|
||||
37 + pathlib.Path(p).parent
|
||||
38 | xsamefile(p)
|
||||
39 | xsplitext(p)
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
PTH121 `os.path.samefile()` should be replaced by `Path.samefile()`
|
||||
--> import_from_as.py:37:1
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::fs;
|
||||
use std::iter;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -101,7 +100,6 @@ pub(crate) fn categorize<'a>(
|
||||
no_sections: bool,
|
||||
section_order: &'a [ImportSection],
|
||||
default_section: &'a ImportSection,
|
||||
match_source_strategy: MatchSourceStrategy,
|
||||
) -> &'a ImportSection {
|
||||
let module_base = module_name.split('.').next().unwrap();
|
||||
let (mut import_type, mut reason) = {
|
||||
@@ -129,7 +127,7 @@ pub(crate) fn categorize<'a>(
|
||||
&ImportSection::Known(ImportType::FirstParty),
|
||||
Reason::SamePackage,
|
||||
)
|
||||
} else if let Some(src) = match_sources(src, module_name, match_source_strategy) {
|
||||
} else if let Some(src) = match_sources(src, module_name) {
|
||||
(
|
||||
&ImportSection::Known(ImportType::FirstParty),
|
||||
Reason::SourceMatch(src),
|
||||
@@ -161,61 +159,29 @@ fn same_package(package: Option<PackageRoot<'_>>, module_base: &str) -> bool {
|
||||
/// Returns the source path with respect to which the module `name`
|
||||
/// should be considered first party, or `None` if no path is found.
|
||||
///
|
||||
/// The [`MatchSourceStrategy`] is the criterion used to decide whether
|
||||
/// the module path matches a given source directory.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// - The module named `foo` will match `[SRC]` if `[SRC]/foo` is a directory,
|
||||
/// no matter the strategy.
|
||||
/// - The module named `foo` will match `[SRC]` if `[SRC]/foo` is a directory
|
||||
///
|
||||
/// - With `match_source_strategy == MatchSourceStrategy::Root`, the module
|
||||
/// named `foo.baz` will match `[SRC]` if `[SRC]/foo` is a
|
||||
/// directory or `[SRC]/foo.py` exists.
|
||||
///
|
||||
/// - With `match_source_stratgy == MatchSourceStrategy::FullPath`, the module
|
||||
/// named `foo.baz` will match `[SRC]` only if `[SRC]/foo/baz` is a directory,
|
||||
/// or `[SRC]/foo/baz.py` exists or `[SRC]/foo/baz.pyi` exists.
|
||||
fn match_sources<'a>(
|
||||
paths: &'a [PathBuf],
|
||||
name: &str,
|
||||
match_source_strategy: MatchSourceStrategy,
|
||||
) -> Option<&'a Path> {
|
||||
match match_source_strategy {
|
||||
MatchSourceStrategy::Root => {
|
||||
let base = name.split('.').next()?;
|
||||
for path in paths {
|
||||
if let Ok(metadata) = fs::metadata(path.join(base)) {
|
||||
if metadata.is_dir() {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
if let Ok(metadata) = fs::metadata(path.join(format!("{base}.py"))) {
|
||||
if metadata.is_file() {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
/// - The module named `foo.baz` will match `[SRC]` only if `[SRC]/foo/baz`
|
||||
/// is a directory, or `[SRC]/foo/baz.py` exists,
|
||||
/// or `[SRC]/foo/baz.pyi` exists.
|
||||
fn match_sources<'a>(paths: &'a [PathBuf], name: &str) -> Option<&'a Path> {
|
||||
let relative_path: PathBuf = name.split('.').collect();
|
||||
relative_path.components().next()?;
|
||||
for root in paths {
|
||||
let candidate = root.join(&relative_path);
|
||||
if candidate.is_dir() {
|
||||
return Some(root);
|
||||
}
|
||||
MatchSourceStrategy::FullPath => {
|
||||
let relative_path: PathBuf = name.split('.').collect();
|
||||
relative_path.components().next()?;
|
||||
for root in paths {
|
||||
let candidate = root.join(&relative_path);
|
||||
if candidate.is_dir() {
|
||||
return Some(root);
|
||||
}
|
||||
if ["py", "pyi"]
|
||||
.into_iter()
|
||||
.any(|extension| candidate.with_extension(extension).is_file())
|
||||
{
|
||||
return Some(root);
|
||||
}
|
||||
}
|
||||
None
|
||||
if ["py", "pyi"]
|
||||
.into_iter()
|
||||
.any(|extension| candidate.with_extension(extension).is_file())
|
||||
{
|
||||
return Some(root);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
@@ -229,7 +195,6 @@ pub(crate) fn categorize_imports<'a>(
|
||||
no_sections: bool,
|
||||
section_order: &'a [ImportSection],
|
||||
default_section: &'a ImportSection,
|
||||
match_source_strategy: MatchSourceStrategy,
|
||||
) -> BTreeMap<&'a ImportSection, ImportBlock<'a>> {
|
||||
let mut block_by_type: BTreeMap<&ImportSection, ImportBlock> = BTreeMap::default();
|
||||
// Categorize `Stmt::Import`.
|
||||
@@ -245,7 +210,6 @@ pub(crate) fn categorize_imports<'a>(
|
||||
no_sections,
|
||||
section_order,
|
||||
default_section,
|
||||
match_source_strategy,
|
||||
);
|
||||
block_by_type
|
||||
.entry(import_type)
|
||||
@@ -266,7 +230,6 @@ pub(crate) fn categorize_imports<'a>(
|
||||
no_sections,
|
||||
section_order,
|
||||
default_section,
|
||||
match_source_strategy,
|
||||
);
|
||||
block_by_type
|
||||
.entry(classification)
|
||||
@@ -287,7 +250,6 @@ pub(crate) fn categorize_imports<'a>(
|
||||
no_sections,
|
||||
section_order,
|
||||
default_section,
|
||||
match_source_strategy,
|
||||
);
|
||||
block_by_type
|
||||
.entry(classification)
|
||||
@@ -308,7 +270,6 @@ pub(crate) fn categorize_imports<'a>(
|
||||
no_sections,
|
||||
section_order,
|
||||
default_section,
|
||||
match_source_strategy,
|
||||
);
|
||||
block_by_type
|
||||
.entry(classification)
|
||||
@@ -463,25 +424,9 @@ impl fmt::Display for KnownModules {
|
||||
}
|
||||
}
|
||||
|
||||
/// Rule to determine whether a module path matches
|
||||
/// a relative path from a source directory.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum MatchSourceStrategy {
|
||||
/// Matches if first term in module path is found in file system
|
||||
///
|
||||
/// # Example
|
||||
/// Module is `foo.bar.baz` and `[SRC]/foo` exists
|
||||
Root,
|
||||
/// Matches only if full module path is reflected in file system
|
||||
///
|
||||
/// # Example
|
||||
/// Module is `foo.bar.baz` and `[SRC]/foo/bar/baz` exists
|
||||
FullPath,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::rules::isort::categorize::{MatchSourceStrategy, match_sources};
|
||||
use crate::rules::isort::categorize::match_sources;
|
||||
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -522,49 +467,17 @@ mod tests {
|
||||
|
||||
let paths = vec![project_dir.clone()];
|
||||
|
||||
// Test with Root strategy
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage", MatchSourceStrategy::Root),
|
||||
match_sources(&paths, "mypackage"),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage.module1", MatchSourceStrategy::Root),
|
||||
match_sources(&paths, "mypackage.module1"),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage.nonexistent", MatchSourceStrategy::Root),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "nonexistent", MatchSourceStrategy::Root),
|
||||
None
|
||||
);
|
||||
|
||||
// Test with FullPath strategy
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage", MatchSourceStrategy::FullPath),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage.module1", MatchSourceStrategy::FullPath),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
// Differs in behavior from [`MatchSourceStrategy::Root`]
|
||||
assert_eq!(
|
||||
match_sources(
|
||||
&paths,
|
||||
"mypackage.nonexistent",
|
||||
MatchSourceStrategy::FullPath
|
||||
),
|
||||
None
|
||||
);
|
||||
assert_eq!(match_sources(&paths, "mypackage.nonexistent",), None);
|
||||
}
|
||||
|
||||
/// Tests a src-based Python package layout:
|
||||
@@ -588,39 +501,12 @@ mod tests {
|
||||
|
||||
let paths = vec![src_dir.clone()];
|
||||
|
||||
// Test with Root strategy
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage", MatchSourceStrategy::Root),
|
||||
match_sources(&paths, "mypackage.module1"),
|
||||
Some(src_dir.as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage.module1", MatchSourceStrategy::Root),
|
||||
Some(src_dir.as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage.nonexistent", MatchSourceStrategy::Root),
|
||||
Some(src_dir.as_path())
|
||||
);
|
||||
|
||||
// Test with FullPath strategy
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage.module1", MatchSourceStrategy::FullPath),
|
||||
Some(src_dir.as_path())
|
||||
);
|
||||
|
||||
// Differs in behavior from [`MatchSourceStrategy::Root`]
|
||||
assert_eq!(
|
||||
match_sources(
|
||||
&paths,
|
||||
"mypackage.nonexistent",
|
||||
MatchSourceStrategy::FullPath
|
||||
),
|
||||
None
|
||||
);
|
||||
assert_eq!(match_sources(&paths, "mypackage.nonexistent"), None);
|
||||
}
|
||||
|
||||
/// Tests a nested package layout:
|
||||
@@ -647,35 +533,13 @@ mod tests {
|
||||
|
||||
let paths = vec![project_dir.clone()];
|
||||
|
||||
// Test with Root strategy
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage", MatchSourceStrategy::Root),
|
||||
match_sources(&paths, "mypackage.subpackage.module2"),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage.subpackage", MatchSourceStrategy::Root),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
// Test with FullPath strategy
|
||||
|
||||
assert_eq!(
|
||||
match_sources(
|
||||
&paths,
|
||||
"mypackage.subpackage.module2",
|
||||
MatchSourceStrategy::FullPath
|
||||
),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
// Differs in behavior from [`MatchSourceStrategy::Root`]
|
||||
assert_eq!(
|
||||
match_sources(
|
||||
&paths,
|
||||
"mypackage.subpackage.nonexistent",
|
||||
MatchSourceStrategy::FullPath
|
||||
),
|
||||
match_sources(&paths, "mypackage.subpackage.nonexistent"),
|
||||
None
|
||||
);
|
||||
}
|
||||
@@ -699,52 +563,17 @@ mod tests {
|
||||
create_file(project_dir.join("namespace/package1/module1.py"));
|
||||
|
||||
let paths = vec![project_dir.clone()];
|
||||
// Test with Root strategy
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "namespace", MatchSourceStrategy::Root),
|
||||
match_sources(&paths, "namespace.package1"),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "namespace.package1", MatchSourceStrategy::Root),
|
||||
match_sources(&paths, "namespace.package1.module1"),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
match_sources(
|
||||
&paths,
|
||||
"namespace.package2.module1",
|
||||
MatchSourceStrategy::Root
|
||||
),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
// Test with FullPath strategy
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "namespace.package1", MatchSourceStrategy::FullPath),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
match_sources(
|
||||
&paths,
|
||||
"namespace.package1.module1",
|
||||
MatchSourceStrategy::FullPath
|
||||
),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
// Differs in behavior from [`MatchSourceStrategy::Root`]
|
||||
assert_eq!(
|
||||
match_sources(
|
||||
&paths,
|
||||
"namespace.package2.module1",
|
||||
MatchSourceStrategy::FullPath
|
||||
),
|
||||
None
|
||||
);
|
||||
assert_eq!(match_sources(&paths, "namespace.package2.module1"), None);
|
||||
}
|
||||
|
||||
/// Tests a package with type stubs (.pyi files):
|
||||
@@ -764,12 +593,11 @@ mod tests {
|
||||
create_file(project_dir.join("mypackage/__init__.py"));
|
||||
create_file(project_dir.join("mypackage/module1.pyi")); // Only create .pyi file, not .py
|
||||
|
||||
// Test with FullPath strategy
|
||||
let paths = vec![project_dir.clone()];
|
||||
|
||||
// Module "mypackage.module1" should match project_dir using .pyi file
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage.module1", MatchSourceStrategy::FullPath),
|
||||
match_sources(&paths, "mypackage.module1"),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
}
|
||||
@@ -796,30 +624,17 @@ mod tests {
|
||||
create_file(project_dir.join("mypackage/feature/__init__.py"));
|
||||
create_file(project_dir.join("mypackage/feature/submodule.py"));
|
||||
|
||||
// Test with Root strategy
|
||||
let paths = vec![project_dir.clone()];
|
||||
|
||||
// Module "mypackage.feature" should match project_dir (matches the file first)
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage.feature", MatchSourceStrategy::Root),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
// Test with FullPath strategy
|
||||
|
||||
// Module "mypackage.feature" should match project_dir
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage.feature", MatchSourceStrategy::FullPath),
|
||||
match_sources(&paths, "mypackage.feature"),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
|
||||
// Module "mypackage.feature.submodule" should match project_dir
|
||||
assert_eq!(
|
||||
match_sources(
|
||||
&paths,
|
||||
"mypackage.feature.submodule",
|
||||
MatchSourceStrategy::FullPath
|
||||
),
|
||||
match_sources(&paths, "mypackage.feature.submodule"),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
}
|
||||
@@ -857,13 +672,13 @@ mod tests {
|
||||
|
||||
// Module "package1" should match project1_dir
|
||||
assert_eq!(
|
||||
match_sources(&paths, "package1", MatchSourceStrategy::Root),
|
||||
match_sources(&paths, "package1"),
|
||||
Some(project1_dir.as_path())
|
||||
);
|
||||
|
||||
// Module "package2" should match project2_dir
|
||||
assert_eq!(
|
||||
match_sources(&paths, "package2", MatchSourceStrategy::Root),
|
||||
match_sources(&paths, "package2"),
|
||||
Some(project2_dir.as_path())
|
||||
);
|
||||
|
||||
@@ -872,7 +687,7 @@ mod tests {
|
||||
|
||||
// Module "package1" should still match project1_dir
|
||||
assert_eq!(
|
||||
match_sources(&paths_reversed, "package1", MatchSourceStrategy::Root),
|
||||
match_sources(&paths_reversed, "package1"),
|
||||
Some(project1_dir.as_path())
|
||||
);
|
||||
}
|
||||
@@ -885,8 +700,7 @@ mod tests {
|
||||
///
|
||||
/// In theory this should never happen since we expect
|
||||
/// module names to have been normalized by the time we
|
||||
/// call `match_sources`. But it is worth noting that the
|
||||
/// behavior is different depending on the [`MatchSourceStrategy`]
|
||||
/// call `match_sources`.
|
||||
#[test]
|
||||
fn test_empty_module_name() {
|
||||
let temp_dir = tempdir().unwrap();
|
||||
@@ -894,16 +708,9 @@ mod tests {
|
||||
|
||||
create_dir(project_dir.join("mypackage"));
|
||||
|
||||
let paths = vec![project_dir.clone()];
|
||||
let paths = vec![project_dir];
|
||||
|
||||
assert_eq!(
|
||||
match_sources(&paths, "", MatchSourceStrategy::Root),
|
||||
Some(project_dir.as_path())
|
||||
);
|
||||
assert_eq!(
|
||||
match_sources(&paths, "", MatchSourceStrategy::FullPath),
|
||||
None
|
||||
);
|
||||
assert_eq!(match_sources(&paths, ""), None);
|
||||
}
|
||||
|
||||
/// Tests behavior with an empty list of source paths
|
||||
@@ -911,14 +718,6 @@ mod tests {
|
||||
fn test_empty_paths() {
|
||||
let paths: Vec<PathBuf> = vec![];
|
||||
|
||||
// Empty paths should return None
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage", MatchSourceStrategy::Root),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
match_sources(&paths, "mypackage", MatchSourceStrategy::FullPath),
|
||||
None
|
||||
);
|
||||
assert_eq!(match_sources(&paths, "mypackage"), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ use std::path::PathBuf;
|
||||
use annotate::annotate_imports;
|
||||
use block::{Block, Trailer};
|
||||
pub(crate) use categorize::categorize;
|
||||
use categorize::categorize_imports;
|
||||
pub use categorize::{ImportSection, ImportType};
|
||||
use categorize::{MatchSourceStrategy, categorize_imports};
|
||||
use comments::Comment;
|
||||
use normalize::normalize_imports;
|
||||
use order::order_imports;
|
||||
@@ -76,7 +76,6 @@ pub(crate) fn format_imports(
|
||||
source_type: PySourceType,
|
||||
target_version: PythonVersion,
|
||||
settings: &Settings,
|
||||
match_source_strategy: MatchSourceStrategy,
|
||||
tokens: &Tokens,
|
||||
) -> String {
|
||||
let trailer = &block.trailer;
|
||||
@@ -104,7 +103,6 @@ pub(crate) fn format_imports(
|
||||
package,
|
||||
target_version,
|
||||
settings,
|
||||
match_source_strategy,
|
||||
);
|
||||
|
||||
if !block_output.is_empty() && !output.is_empty() {
|
||||
@@ -161,7 +159,6 @@ fn format_import_block(
|
||||
package: Option<PackageRoot<'_>>,
|
||||
target_version: PythonVersion,
|
||||
settings: &Settings,
|
||||
match_source_strategy: MatchSourceStrategy,
|
||||
) -> String {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum LineInsertion {
|
||||
@@ -182,7 +179,6 @@ fn format_import_block(
|
||||
settings.no_sections,
|
||||
&settings.section_order,
|
||||
&settings.default_section,
|
||||
match_source_strategy,
|
||||
);
|
||||
|
||||
let mut output = String::new();
|
||||
|
||||
@@ -14,9 +14,7 @@ use crate::Locator;
|
||||
use crate::checkers::ast::LintContext;
|
||||
use crate::line_width::LineWidthBuilder;
|
||||
use crate::package::PackageRoot;
|
||||
use crate::preview::is_full_path_match_source_strategy_enabled;
|
||||
use crate::rules::isort::block::Block;
|
||||
use crate::rules::isort::categorize::MatchSourceStrategy;
|
||||
use crate::rules::isort::{comments, format_imports};
|
||||
use crate::settings::LinterSettings;
|
||||
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
@@ -40,12 +38,6 @@ use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
/// import pandas
|
||||
/// ```
|
||||
///
|
||||
/// ## Preview
|
||||
/// When [`preview`](https://docs.astral.sh/ruff/preview/) mode is enabled, Ruff applies a stricter criterion
|
||||
/// for determining whether an import should be classified as first-party.
|
||||
/// Specifically, for an import of the form `import foo.bar.baz`, Ruff will
|
||||
/// check that `foo/bar`, relative to a [user-specified `src`](https://docs.astral.sh/ruff/settings/#src) directory, contains either
|
||||
/// the directory `baz` or else a file with the name `baz.py` or `baz.pyi`.
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct UnsortedImports;
|
||||
|
||||
@@ -129,12 +121,6 @@ pub(crate) fn organize_imports(
|
||||
trailing_lines_end(block.imports.last().unwrap(), locator.contents())
|
||||
};
|
||||
|
||||
let match_source_strategy = if is_full_path_match_source_strategy_enabled(settings) {
|
||||
MatchSourceStrategy::FullPath
|
||||
} else {
|
||||
MatchSourceStrategy::Root
|
||||
};
|
||||
|
||||
// Generate the sorted import block.
|
||||
let expected = format_imports(
|
||||
block,
|
||||
@@ -148,7 +134,6 @@ pub(crate) fn organize_imports(
|
||||
source_type,
|
||||
target_version,
|
||||
&settings.isort,
|
||||
match_source_strategy,
|
||||
tokens,
|
||||
);
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::{Violation, checkers::ast::Checker};
|
||||
|
||||
/// ## Deprecated
|
||||
/// ## Removed
|
||||
///
|
||||
/// This rule has been deprecated as it's highly opinionated and overly strict in most cases.
|
||||
/// This rule has been removed as it's highly opinionated and overly strict in most cases.
|
||||
///
|
||||
/// ## What it does
|
||||
/// Checks for assignments to the variable `df`.
|
||||
|
||||
@@ -141,7 +141,6 @@ impl Violation for InvalidFirstArgumentNameForClassMethod {
|
||||
#[derive_message_formats]
|
||||
// The first string below is what shows up in the documentation
|
||||
// in the rule table, and it is the more common case.
|
||||
#[expect(clippy::if_not_else)]
|
||||
fn message(&self) -> String {
|
||||
if !self.is_new {
|
||||
"First argument of a class method should be named `cls`".to_string()
|
||||
|
||||
@@ -18,8 +18,8 @@ use crate::rules::pep8_naming::helpers;
|
||||
/// > (Let’s hope that these variables are meant for use inside one module
|
||||
/// > only.) The conventions are about the same as those for functions.
|
||||
/// >
|
||||
/// > Modules that are designed for use via from M import * should use the
|
||||
/// > __all__ mechanism to prevent exporting globals, or use the older
|
||||
/// > Modules that are designed for use via `from M import *` should use the
|
||||
/// > `__all__` mechanism to prevent exporting globals, or use the older
|
||||
/// > convention of prefixing such globals with an underscore (which you might
|
||||
/// > want to do to indicate these globals are “module non-public”).
|
||||
/// >
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user