Compare commits
122 Commits
0.14.7
...
gankra/scr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9526fe0a5 | ||
|
|
e733a87bd7 | ||
|
|
0ab8521171 | ||
|
|
0ccd84136a | ||
|
|
3981a23ee9 | ||
|
|
385dd2770b | ||
|
|
7519f6c27b | ||
|
|
4686111681 | ||
|
|
4364ffbdd3 | ||
|
|
b845e81c4a | ||
|
|
c99e10eedc | ||
|
|
a364195335 | ||
|
|
dfd6ed0524 | ||
|
|
ac882f7e63 | ||
|
|
857fd4f683 | ||
|
|
285d6410d3 | ||
|
|
cbff09b9af | ||
|
|
6e0e49eda8 | ||
|
|
ef45c97dab | ||
|
|
9714c589e1 | ||
|
|
b2fb421ddd | ||
|
|
2f05ffa2c8 | ||
|
|
b623189560 | ||
|
|
f29436ca9e | ||
|
|
e42cdf8495 | ||
|
|
71a7a03ad4 | ||
|
|
48f7f42784 | ||
|
|
3deb7e1b90 | ||
|
|
5df8a959f5 | ||
|
|
6f03afe318 | ||
|
|
1951f1bbb8 | ||
|
|
10de342991 | ||
|
|
3511b7a06b | ||
|
|
f3e5713d90 | ||
|
|
a9de6b5c3e | ||
|
|
06415b1877 | ||
|
|
518d11b33f | ||
|
|
da94b99248 | ||
|
|
3c2cf49f60 | ||
|
|
fdcb5a7e73 | ||
|
|
6a025d1925 | ||
|
|
f054e7edf8 | ||
|
|
e154efa229 | ||
|
|
32f400a457 | ||
|
|
2a38395bc8 | ||
|
|
8c72b296c9 | ||
|
|
086f1e0b89 | ||
|
|
5da45f8ec7 | ||
|
|
62f20b1e86 | ||
|
|
cccb0bbaa4 | ||
|
|
9d4f1c6ae2 | ||
|
|
326025d45f | ||
|
|
3aefe85b32 | ||
|
|
b8ecc83a54 | ||
|
|
6491932757 | ||
|
|
a9f2bb41bd | ||
|
|
e2b72fbf99 | ||
|
|
14fce0d440 | ||
|
|
8ebecb2a88 | ||
|
|
45ac30a4d7 | ||
|
|
0280949000 | ||
|
|
c722f498fe | ||
|
|
1f4f8d9950 | ||
|
|
4488e9d47d | ||
|
|
b08f0b2caa | ||
|
|
d6e472f297 | ||
|
|
45842cc034 | ||
|
|
cd079bd92e | ||
|
|
5756b3809c | ||
|
|
92c5f62ec0 | ||
|
|
21e5a57296 | ||
|
|
f4e4229683 | ||
|
|
e6ddeed386 | ||
|
|
c5b8d551df | ||
|
|
f68080b55e | ||
|
|
abaa49f552 | ||
|
|
7b0aab1696 | ||
|
|
2250fa6f98 | ||
|
|
392a8e4e50 | ||
|
|
515de2d062 | ||
|
|
508c0a0861 | ||
|
|
0d2792517d | ||
|
|
05d053376b | ||
|
|
ac2552b11b | ||
|
|
644096ea8a | ||
|
|
015ab9e576 | ||
|
|
cf4196466c | ||
|
|
2182c750db | ||
|
|
72304b01eb | ||
|
|
ec854c7199 | ||
|
|
edc6ed5077 | ||
|
|
f052bd644c | ||
|
|
bc44dc2afb | ||
|
|
52f59c5c39 | ||
|
|
53299cbff4 | ||
|
|
3738ab1c46 | ||
|
|
b4f618e180 | ||
|
|
a561e6659d | ||
|
|
0e651b50b7 | ||
|
|
116fd7c7af | ||
|
|
5358ddae88 | ||
|
|
3a11e714c6 | ||
|
|
a2096ee2cb | ||
|
|
2e229aa8cb | ||
|
|
c2773b4c6f | ||
|
|
bc6517a807 | ||
|
|
4686c36079 | ||
|
|
a6cbc138d2 | ||
|
|
846df40a6e | ||
|
|
c61e885527 | ||
|
|
13af584428 | ||
|
|
984480a586 | ||
|
|
aef056954b | ||
|
|
5265af4eee | ||
|
|
5b32908920 | ||
|
|
d8d1464d96 | ||
|
|
e7beb7e1f4 | ||
|
|
b02e8212c9 | ||
|
|
69ace00210 | ||
|
|
d40590c8f9 | ||
|
|
b2387f4eab | ||
|
|
8795d9f0cb |
@@ -7,10 +7,6 @@ serial = { max-threads = 1 }
|
||||
filter = 'binary(file_watching)'
|
||||
test-group = 'serial'
|
||||
|
||||
[[profile.default.overrides]]
|
||||
filter = 'binary(e2e)'
|
||||
test-group = 'serial'
|
||||
|
||||
[profile.ci]
|
||||
# Print out output for failing tests as soon as they fail, and also at the end
|
||||
# of the run (for easy scrollability).
|
||||
|
||||
15
.github/renovate.json5
vendored
15
.github/renovate.json5
vendored
@@ -2,12 +2,11 @@
|
||||
$schema: "https://docs.renovatebot.com/renovate-schema.json",
|
||||
dependencyDashboard: true,
|
||||
suppressNotifications: ["prEditedNotification"],
|
||||
extends: ["config:recommended"],
|
||||
extends: ["github>astral-sh/renovate-config"],
|
||||
labels: ["internal"],
|
||||
schedule: ["before 4am on Monday"],
|
||||
semanticCommits: "disabled",
|
||||
separateMajorMinor: false,
|
||||
prHourlyLimit: 10,
|
||||
enabledManagers: ["github-actions", "pre-commit", "cargo", "pep621", "pip_requirements", "npm"],
|
||||
cargo: {
|
||||
// See https://docs.renovatebot.com/configuration-options/#rangestrategy
|
||||
@@ -16,7 +15,7 @@
|
||||
pep621: {
|
||||
// The default for this package manager is to only search for `pyproject.toml` files
|
||||
// found at the repository root: https://docs.renovatebot.com/modules/manager/pep621/#file-matching
|
||||
fileMatch: ["^(python|scripts)/.*pyproject\\.toml$"],
|
||||
managerFilePatterns: ["^(python|scripts)/.*pyproject\\.toml$"],
|
||||
},
|
||||
pip_requirements: {
|
||||
// The default for this package manager is to run on all requirements.txt files:
|
||||
@@ -34,7 +33,7 @@
|
||||
npm: {
|
||||
// The default for this package manager is to only search for `package.json` files
|
||||
// found at the repository root: https://docs.renovatebot.com/modules/manager/npm/#file-matching
|
||||
fileMatch: ["^playground/.*package\\.json$"],
|
||||
managerFilePatterns: ["^playground/.*package\\.json$"],
|
||||
},
|
||||
"pre-commit": {
|
||||
enabled: true,
|
||||
@@ -76,14 +75,6 @@
|
||||
matchManagers: ["cargo"],
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
// `mkdocs-material` requires a manual update to keep the version in sync
|
||||
// with `mkdocs-material-insider`.
|
||||
// See: https://squidfunk.github.io/mkdocs-material/insiders/upgrade/
|
||||
matchManagers: ["pip_requirements"],
|
||||
matchPackageNames: ["mkdocs-material"],
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
groupName: "pre-commit dependencies",
|
||||
matchManagers: ["pre-commit"],
|
||||
|
||||
16
.github/workflows/build-binaries.yml
vendored
16
.github/workflows/build-binaries.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Prep README.md"
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -114,7 +114,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: arm64
|
||||
@@ -170,7 +170,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: ${{ matrix.platform.arch }}
|
||||
@@ -223,7 +223,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -300,7 +300,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Prep README.md"
|
||||
@@ -365,7 +365,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -431,7 +431,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Prep README.md"
|
||||
|
||||
107
.github/workflows/ci.yaml
vendored
107
.github/workflows/ci.yaml
vendored
@@ -24,6 +24,8 @@ env:
|
||||
PACKAGE_NAME: ruff
|
||||
PYTHON_VERSION: "3.14"
|
||||
NEXTEST_PROFILE: ci
|
||||
# Enable mdtests that require external dependencies
|
||||
MDTEST_EXTERNAL: "1"
|
||||
|
||||
jobs:
|
||||
determine_changes:
|
||||
@@ -230,7 +232,7 @@ jobs:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -252,7 +254,7 @@ jobs:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
shared-key: ruff-linux-debug
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
@@ -261,11 +263,11 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@f79fe7514db78f0a7bdba3cb6dd9c1baa7d046d9 # v2.62.56
|
||||
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@f79fe7514db78f0a7bdba3cb6dd9c1baa7d046d9 # v2.62.56
|
||||
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: "Install uv"
|
||||
@@ -315,7 +317,7 @@ jobs:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -323,7 +325,7 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@f79fe7514db78f0a7bdba3cb6dd9c1baa7d046d9 # v2.62.56
|
||||
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install uv"
|
||||
@@ -350,13 +352,13 @@ jobs:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@f79fe7514db78f0a7bdba3cb6dd9c1baa7d046d9 # v2.62.56
|
||||
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install uv"
|
||||
@@ -378,7 +380,7 @@ jobs:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -415,7 +417,7 @@ jobs:
|
||||
with:
|
||||
file: "Cargo.toml"
|
||||
field: "workspace.package.rust-version"
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -439,7 +441,7 @@ jobs:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
workspaces: "fuzz -> target"
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
@@ -448,7 +450,7 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||
- name: "Install cargo-binstall"
|
||||
uses: cargo-bins/cargo-binstall@ae04fb5e853ae6cd3ad7de4a1d554a8b646d12aa # v1.15.11
|
||||
uses: cargo-bins/cargo-binstall@3fc81674af4165a753833a94cae9f91d8849049f # v1.16.2
|
||||
- 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
|
||||
@@ -467,7 +469,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
shared-key: ruff-linux-debug
|
||||
save-if: false
|
||||
@@ -498,7 +500,7 @@ jobs:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||
@@ -547,7 +549,7 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
shared-key: ruff-linux-debug
|
||||
save-if: false
|
||||
@@ -643,7 +645,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -688,7 +690,7 @@ jobs:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: cargo-bins/cargo-binstall@ae04fb5e853ae6cd3ad7de4a1d554a8b646d12aa # v1.15.11
|
||||
- uses: cargo-bins/cargo-binstall@3fc81674af4165a753833a94cae9f91d8849049f # v1.16.2
|
||||
- run: cargo binstall --no-confirm cargo-shear
|
||||
- run: cargo shear
|
||||
|
||||
@@ -702,7 +704,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -723,11 +725,11 @@ jobs:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: "Prep README.md"
|
||||
@@ -753,7 +755,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
@@ -779,20 +781,13 @@ jobs:
|
||||
name: "mkdocs"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: "Add SSH key"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: Install uv
|
||||
@@ -800,11 +795,7 @@ jobs:
|
||||
with:
|
||||
python-version: 3.13
|
||||
activate-environment: true
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: uv pip install -r docs/requirements-insiders.txt
|
||||
- name: "Install dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: uv pip install -r docs/requirements.txt
|
||||
- name: "Update README File"
|
||||
run: python scripts/transform_readme.py --target mkdocs
|
||||
@@ -812,12 +803,8 @@ jobs:
|
||||
run: python scripts/generate_mkdocs.py
|
||||
- name: "Check docs formatting"
|
||||
run: python scripts/check_docs_formatted.py
|
||||
- name: "Build Insiders docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.insiders.yml
|
||||
- name: "Build docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.public.yml
|
||||
run: mkdocs build --strict -f mkdocs.yml
|
||||
|
||||
check-formatter-instability-and-black-similarity:
|
||||
name: "formatter instabilities and black similarity"
|
||||
@@ -829,7 +816,7 @@ jobs:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -857,7 +844,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
shared-key: ruff-linux-debug
|
||||
save-if: false
|
||||
@@ -875,7 +862,7 @@ jobs:
|
||||
repository: "astral-sh/ruff-lsp"
|
||||
path: ruff-lsp
|
||||
|
||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
# installation fails on 3.13 and newer
|
||||
python-version: "3.12"
|
||||
@@ -908,7 +895,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup target add wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
@@ -918,7 +905,7 @@ jobs:
|
||||
cache-dependency-path: playground/package-lock.json
|
||||
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
|
||||
- name: "Install Node dependencies"
|
||||
run: npm ci
|
||||
run: npm ci --ignore-scripts
|
||||
working-directory: playground
|
||||
- name: "Build playgrounds"
|
||||
run: npm run dev:wasm
|
||||
@@ -942,13 +929,16 @@ jobs:
|
||||
needs.determine_changes.outputs.linter == 'true'
|
||||
)
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read # required for actions/checkout
|
||||
id-token: write # required for OIDC authentication with CodSpeed
|
||||
steps:
|
||||
- name: "Checkout Branch"
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||
@@ -957,7 +947,7 @@ jobs:
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@f79fe7514db78f0a7bdba3cb6dd9c1baa7d046d9 # v2.62.56
|
||||
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
@@ -965,11 +955,10 @@ jobs:
|
||||
run: cargo codspeed build --features "codspeed,instrumented" --profile profiling --no-default-features -p ruff_benchmark --bench formatter --bench lexer --bench linter --bench parser
|
||||
|
||||
- name: "Run benchmarks"
|
||||
uses: CodSpeedHQ/action@6a8e2b874c338bf81cc5e8be715ada75908d3871 # v4.3.4
|
||||
uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1
|
||||
with:
|
||||
mode: instrumentation
|
||||
mode: simulation
|
||||
run: cargo codspeed run
|
||||
token: ${{ secrets.CODSPEED_TOKEN }}
|
||||
|
||||
benchmarks-instrumented-ty:
|
||||
name: "benchmarks instrumented (ty)"
|
||||
@@ -982,13 +971,16 @@ jobs:
|
||||
needs.determine_changes.outputs.ty == 'true'
|
||||
)
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read # required for actions/checkout
|
||||
id-token: write # required for OIDC authentication with CodSpeed
|
||||
steps:
|
||||
- name: "Checkout Branch"
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||
@@ -997,7 +989,7 @@ jobs:
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@f79fe7514db78f0a7bdba3cb6dd9c1baa7d046d9 # v2.62.56
|
||||
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
@@ -1005,11 +997,10 @@ jobs:
|
||||
run: cargo codspeed build --features "codspeed,instrumented" --profile profiling --no-default-features -p ruff_benchmark --bench ty
|
||||
|
||||
- name: "Run benchmarks"
|
||||
uses: CodSpeedHQ/action@6a8e2b874c338bf81cc5e8be715ada75908d3871 # v4.3.4
|
||||
uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1
|
||||
with:
|
||||
mode: instrumentation
|
||||
mode: simulation
|
||||
run: cargo codspeed run
|
||||
token: ${{ secrets.CODSPEED_TOKEN }}
|
||||
|
||||
benchmarks-walltime:
|
||||
name: "benchmarks walltime (${{ matrix.benchmarks }})"
|
||||
@@ -1017,6 +1008,9 @@ jobs:
|
||||
needs: determine_changes
|
||||
if: ${{ github.repository == 'astral-sh/ruff' && !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.ty == 'true' || github.ref == 'refs/heads/main') }}
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read # required for actions/checkout
|
||||
id-token: write # required for OIDC authentication with CodSpeed
|
||||
strategy:
|
||||
matrix:
|
||||
benchmarks:
|
||||
@@ -1028,7 +1022,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||
@@ -1037,7 +1031,7 @@ jobs:
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@f79fe7514db78f0a7bdba3cb6dd9c1baa7d046d9 # v2.62.56
|
||||
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
@@ -1045,7 +1039,7 @@ jobs:
|
||||
run: cargo codspeed build --features "codspeed,walltime" --profile profiling --no-default-features -p ruff_benchmark
|
||||
|
||||
- name: "Run benchmarks"
|
||||
uses: CodSpeedHQ/action@6a8e2b874c338bf81cc5e8be715ada75908d3871 # v4.3.4
|
||||
uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1
|
||||
env:
|
||||
# enabling walltime flamegraphs adds ~6 minutes to the CI time, and they don't
|
||||
# appear to provide much useful insight for our walltime benchmarks right now
|
||||
@@ -1054,4 +1048,3 @@ jobs:
|
||||
with:
|
||||
mode: walltime
|
||||
run: cargo codspeed run --bench ty_walltime "${{ matrix.benchmarks }}"
|
||||
token: ${{ secrets.CODSPEED_TOKEN }}
|
||||
|
||||
2
.github/workflows/daily_fuzz.yaml
vendored
2
.github/workflows/daily_fuzz.yaml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
- name: Build ruff
|
||||
# A debug build means the script runs slower once it gets started,
|
||||
# but this is outweighed by the fact that a release build takes *much* longer to compile in CI
|
||||
|
||||
4
.github/workflows/mypy_primer.yaml
vendored
4
.github/workflows/mypy_primer.yaml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
workspaces: "ruff"
|
||||
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
workspaces: "ruff"
|
||||
|
||||
|
||||
24
.github/workflows/publish-docs.yml
vendored
24
.github/workflows/publish-docs.yml
vendored
@@ -20,15 +20,13 @@ on:
|
||||
jobs:
|
||||
mkdocs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
persist-credentials: true
|
||||
|
||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: 3.12
|
||||
|
||||
@@ -59,23 +57,12 @@ jobs:
|
||||
echo "branch_name=update-docs-$branch_display_name-$timestamp" >> "$GITHUB_ENV"
|
||||
echo "timestamp=$timestamp" >> "$GITHUB_ENV"
|
||||
|
||||
- name: "Add SSH key"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
|
||||
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: pip install -r docs/requirements-insiders.txt
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
|
||||
- name: "Install dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: pip install -r docs/requirements.txt
|
||||
|
||||
- name: "Copy README File"
|
||||
@@ -83,13 +70,8 @@ jobs:
|
||||
python scripts/transform_readme.py --target mkdocs
|
||||
python scripts/generate_mkdocs.py
|
||||
|
||||
- name: "Build Insiders docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.insiders.yml
|
||||
|
||||
- name: "Build docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.public.yml
|
||||
run: mkdocs build --strict -f mkdocs.yml
|
||||
|
||||
- name: "Clone docs repo"
|
||||
run: git clone https://${{ secrets.ASTRAL_DOCS_PAT }}@github.com/astral-sh/docs.git astral-docs
|
||||
|
||||
2
.github/workflows/publish-playground.yml
vendored
2
.github/workflows/publish-playground.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
package-manager-cache: false
|
||||
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
|
||||
- name: "Install Node dependencies"
|
||||
run: npm ci
|
||||
run: npm ci --ignore-scripts
|
||||
working-directory: playground
|
||||
- name: "Run TypeScript checks"
|
||||
run: npm run check
|
||||
|
||||
2
.github/workflows/publish-ty-playground.yml
vendored
2
.github/workflows/publish-ty-playground.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
package-manager-cache: false
|
||||
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
|
||||
- name: "Install Node dependencies"
|
||||
run: npm ci
|
||||
run: npm ci --ignore-scripts
|
||||
working-directory: playground
|
||||
- name: "Run TypeScript checks"
|
||||
run: npm run check
|
||||
|
||||
6
.github/workflows/sync_typeshed.yaml
vendored
6
.github/workflows/sync_typeshed.yaml
vendored
@@ -198,7 +198,7 @@ jobs:
|
||||
run: |
|
||||
rm "${VENDORED_TYPESHED}/pyproject.toml"
|
||||
git commit -am "Remove pyproject.toml file"
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
- name: "Install Rust toolchain"
|
||||
if: ${{ success() }}
|
||||
run: rustup show
|
||||
@@ -207,12 +207,12 @@ jobs:
|
||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||
- name: "Install cargo nextest"
|
||||
if: ${{ success() }}
|
||||
uses: taiki-e/install-action@f79fe7514db78f0a7bdba3cb6dd9c1baa7d046d9 # v2.62.56
|
||||
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
if: ${{ success() }}
|
||||
uses: taiki-e/install-action@f79fe7514db78f0a7bdba3cb6dd9c1baa7d046d9 # v2.62.56
|
||||
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: Update snapshots
|
||||
|
||||
2
.github/workflows/ty-ecosystem-analyzer.yaml
vendored
2
.github/workflows/ty-ecosystem-analyzer.yaml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
with:
|
||||
enable-cache: true # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
|
||||
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
workspaces: "ruff"
|
||||
lookup-only: false # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
|
||||
|
||||
2
.github/workflows/ty-ecosystem-report.yaml
vendored
2
.github/workflows/ty-ecosystem-report.yaml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
with:
|
||||
enable-cache: true # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
|
||||
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
workspaces: "ruff"
|
||||
lookup-only: false # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
|
||||
|
||||
2
.github/workflows/typing_conformance.yaml
vendored
2
.github/workflows/typing_conformance.yaml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
path: typing
|
||||
persist-credentials: false
|
||||
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
workspaces: "ruff"
|
||||
|
||||
|
||||
29
CHANGELOG.md
29
CHANGELOG.md
@@ -1,5 +1,34 @@
|
||||
# Changelog
|
||||
|
||||
## 0.14.8
|
||||
|
||||
Released on 2025-12-04.
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`flake8-bugbear`\] Catch `yield` expressions within other statements (`B901`) ([#21200](https://github.com/astral-sh/ruff/pull/21200))
|
||||
- \[`flake8-use-pathlib`\] Mark fixes unsafe for return type changes (`PTH104`, `PTH105`, `PTH109`, `PTH115`) ([#21440](https://github.com/astral-sh/ruff/pull/21440))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Fix syntax error false positives for `await` outside functions ([#21763](https://github.com/astral-sh/ruff/pull/21763))
|
||||
- \[`flake8-simplify`\] Fix truthiness assumption for non-iterable arguments in tuple/list/set calls (`SIM222`, `SIM223`) ([#21479](https://github.com/astral-sh/ruff/pull/21479))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Suggest using `--output-file` option in GitLab integration ([#21706](https://github.com/astral-sh/ruff/pull/21706))
|
||||
|
||||
### Other changes
|
||||
|
||||
- [syntax-error] Default type parameter followed by non-default type parameter ([#21657](https://github.com/astral-sh/ruff/pull/21657))
|
||||
|
||||
### Contributors
|
||||
|
||||
- [@kieran-ryan](https://github.com/kieran-ryan)
|
||||
- [@11happy](https://github.com/11happy)
|
||||
- [@danparizher](https://github.com/danparizher)
|
||||
- [@ntBre](https://github.com/ntBre)
|
||||
|
||||
## 0.14.7
|
||||
|
||||
Released on 2025-11-28.
|
||||
|
||||
@@ -331,13 +331,6 @@ you addressed them.
|
||||
|
||||
## MkDocs
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> The documentation uses Material for MkDocs Insiders, which is closed-source software.
|
||||
> This means only members of the Astral organization can preview the documentation exactly as it
|
||||
> will appear in production.
|
||||
> Outside contributors can still preview the documentation, but there will be some differences. Consult [the Material for MkDocs documentation](https://squidfunk.github.io/mkdocs-material/insiders/benefits/#features) for which features are exclusively available in the insiders version.
|
||||
|
||||
To preview any changes to the documentation locally:
|
||||
|
||||
1. Install the [Rust toolchain](https://www.rust-lang.org/tools/install).
|
||||
@@ -351,11 +344,7 @@ To preview any changes to the documentation locally:
|
||||
1. Run the development server with:
|
||||
|
||||
```shell
|
||||
# For contributors.
|
||||
uvx --with-requirements docs/requirements.txt -- mkdocs serve -f mkdocs.public.yml
|
||||
|
||||
# For members of the Astral org, which has access to MkDocs Insiders via sponsorship.
|
||||
uvx --with-requirements docs/requirements-insiders.txt -- mkdocs serve -f mkdocs.insiders.yml
|
||||
uvx --with-requirements docs/requirements.txt -- mkdocs serve -f mkdocs.yml
|
||||
```
|
||||
|
||||
The documentation should then be available locally at
|
||||
|
||||
40
Cargo.lock
generated
40
Cargo.lock
generated
@@ -1108,7 +1108,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1763,7 +1763,7 @@ dependencies = [
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde_core",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2859,7 +2859,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.14.7"
|
||||
version = "0.14.8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argfile",
|
||||
@@ -3117,13 +3117,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.14.7"
|
||||
version = "0.14.8"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"anyhow",
|
||||
"bitflags 2.10.0",
|
||||
"clap",
|
||||
"colored 3.0.0",
|
||||
"compact_str",
|
||||
"fern",
|
||||
"glob",
|
||||
"globset",
|
||||
@@ -3472,7 +3473,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_wasm"
|
||||
version = "0.14.7"
|
||||
version = "0.14.8"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
@@ -3570,7 +3571,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3588,7 +3589,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
[[package]]
|
||||
name = "salsa"
|
||||
version = "0.24.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=17bc55d699565e5a1cb1bd42363b905af2f9f3e7#17bc55d699565e5a1cb1bd42363b905af2f9f3e7"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0#59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0"
|
||||
dependencies = [
|
||||
"boxcar",
|
||||
"compact_str",
|
||||
@@ -3612,12 +3613,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "salsa-macro-rules"
|
||||
version = "0.24.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=17bc55d699565e5a1cb1bd42363b905af2f9f3e7#17bc55d699565e5a1cb1bd42363b905af2f9f3e7"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0#59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0"
|
||||
|
||||
[[package]]
|
||||
name = "salsa-macros"
|
||||
version = "0.24.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=17bc55d699565e5a1cb1bd42363b905af2f9f3e7#17bc55d699565e5a1cb1bd42363b905af2f9f3e7"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0#59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3971,7 +3972,7 @@ dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4216,9 +4217,9 @@ checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
@@ -4228,9 +4229,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.30"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4239,9 +4240,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.34"
|
||||
version = "0.1.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
@@ -4283,9 +4284,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.20"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"matchers",
|
||||
@@ -4556,6 +4557,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"camino",
|
||||
"colored 3.0.0",
|
||||
"dunce",
|
||||
"insta",
|
||||
"memchr",
|
||||
"path-slash",
|
||||
@@ -5024,7 +5026,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -146,7 +146,7 @@ regex-automata = { version = "0.4.9" }
|
||||
rustc-hash = { version = "2.0.0" }
|
||||
rustc-stable-hash = { version = "0.1.2" }
|
||||
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "17bc55d699565e5a1cb1bd42363b905af2f9f3e7", default-features = false, features = [
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0", default-features = false, features = [
|
||||
"compact_str",
|
||||
"macros",
|
||||
"salsa_unstable",
|
||||
@@ -272,6 +272,12 @@ large_stack_arrays = "allow"
|
||||
lto = "fat"
|
||||
codegen-units = 16
|
||||
|
||||
# Profile to build a minimally sized binary for ruff/ty
|
||||
[profile.minimal-size]
|
||||
inherits = "release"
|
||||
opt-level = "z"
|
||||
codegen-units = 1
|
||||
|
||||
# Some crates don't change as much but benefit more from
|
||||
# more expensive optimization passes, so we selectively
|
||||
# decrease codegen-units in some cases.
|
||||
|
||||
@@ -147,8 +147,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.14.7/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.14.7/install.ps1 | iex"
|
||||
curl -LsSf https://astral.sh/ruff/0.14.8/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.14.8/install.ps1 | iex"
|
||||
```
|
||||
|
||||
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
||||
@@ -181,7 +181,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.14.7
|
||||
rev: v0.14.8
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff-check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.14.7"
|
||||
version = "0.14.8"
|
||||
publish = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -6,7 +6,8 @@ use criterion::{
|
||||
use ruff_benchmark::{
|
||||
LARGE_DATASET, NUMPY_CTYPESLIB, NUMPY_GLOBALS, PYDANTIC_TYPES, TestCase, UNICODE_PYPINYIN,
|
||||
};
|
||||
use ruff_python_parser::{Mode, TokenKind, lexer};
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_python_parser::{Mode, lexer};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[global_allocator]
|
||||
|
||||
@@ -166,28 +166,8 @@ impl Diagnostic {
|
||||
/// Returns the primary message for this diagnostic.
|
||||
///
|
||||
/// A diagnostic always has a message, but it may be empty.
|
||||
///
|
||||
/// NOTE: At present, this routine will return the first primary
|
||||
/// annotation's message as the primary message when the main diagnostic
|
||||
/// message is empty. This is meant to facilitate an incremental migration
|
||||
/// in ty over to the new diagnostic data model. (The old data model
|
||||
/// didn't distinguish between messages on the entire diagnostic and
|
||||
/// messages attached to a particular span.)
|
||||
pub fn primary_message(&self) -> &str {
|
||||
if !self.inner.message.as_str().is_empty() {
|
||||
return self.inner.message.as_str();
|
||||
}
|
||||
// FIXME: As a special case, while we're migrating ty
|
||||
// to the new diagnostic data model, we'll look for a primary
|
||||
// message from the primary annotation. This is because most
|
||||
// ty diagnostics are created with an empty diagnostic
|
||||
// message and instead attach the message to the annotation.
|
||||
// Fixing this will require touching basically every diagnostic
|
||||
// in ty, so we do it this way for now to match the old
|
||||
// semantics. ---AG
|
||||
self.primary_annotation()
|
||||
.and_then(|ann| ann.get_message())
|
||||
.unwrap_or_default()
|
||||
self.inner.message.as_str()
|
||||
}
|
||||
|
||||
/// Introspects this diagnostic and returns what kind of "primary" message
|
||||
@@ -199,18 +179,6 @@ impl Diagnostic {
|
||||
/// contains *essential* information or context for understanding the
|
||||
/// diagnostic.
|
||||
///
|
||||
/// The reason why we don't just always return both the main diagnostic
|
||||
/// message and the primary annotation message is because this was written
|
||||
/// in the midst of an incremental migration of ty over to the new
|
||||
/// diagnostic data model. At time of writing, diagnostics were still
|
||||
/// constructed in the old model where the main diagnostic message and the
|
||||
/// primary annotation message were not distinguished from each other. So
|
||||
/// for now, we carefully return what kind of messages this diagnostic
|
||||
/// contains. In effect, if this diagnostic has a non-empty main message
|
||||
/// *and* a non-empty primary annotation message, then the diagnostic is
|
||||
/// 100% using the new diagnostic data model and we can format things
|
||||
/// appropriately.
|
||||
///
|
||||
/// The type returned implements the `std::fmt::Display` trait. In most
|
||||
/// cases, just converting it to a string (or printing it) will do what
|
||||
/// you want.
|
||||
@@ -224,11 +192,10 @@ impl Diagnostic {
|
||||
.primary_annotation()
|
||||
.and_then(|ann| ann.get_message())
|
||||
.unwrap_or_default();
|
||||
match (main.is_empty(), annotation.is_empty()) {
|
||||
(false, true) => ConciseMessage::MainDiagnostic(main),
|
||||
(true, false) => ConciseMessage::PrimaryAnnotation(annotation),
|
||||
(false, false) => ConciseMessage::Both { main, annotation },
|
||||
(true, true) => ConciseMessage::Empty,
|
||||
if annotation.is_empty() {
|
||||
ConciseMessage::MainDiagnostic(main)
|
||||
} else {
|
||||
ConciseMessage::Both { main, annotation }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +321,13 @@ impl Diagnostic {
|
||||
Arc::make_mut(&mut self.inner).fix = Some(fix);
|
||||
}
|
||||
|
||||
/// If `fix` is `Some`, set the fix for this diagnostic.
|
||||
pub fn set_optional_fix(&mut self, fix: Option<Fix>) {
|
||||
if let Some(fix) = fix {
|
||||
self.set_fix(fix);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the fix for this diagnostic.
|
||||
pub fn remove_fix(&mut self) {
|
||||
Arc::make_mut(&mut self.inner).fix = None;
|
||||
@@ -686,18 +660,6 @@ impl SubDiagnostic {
|
||||
/// contains *essential* information or context for understanding the
|
||||
/// diagnostic.
|
||||
///
|
||||
/// The reason why we don't just always return both the main diagnostic
|
||||
/// message and the primary annotation message is because this was written
|
||||
/// in the midst of an incremental migration of ty over to the new
|
||||
/// diagnostic data model. At time of writing, diagnostics were still
|
||||
/// constructed in the old model where the main diagnostic message and the
|
||||
/// primary annotation message were not distinguished from each other. So
|
||||
/// for now, we carefully return what kind of messages this diagnostic
|
||||
/// contains. In effect, if this diagnostic has a non-empty main message
|
||||
/// *and* a non-empty primary annotation message, then the diagnostic is
|
||||
/// 100% using the new diagnostic data model and we can format things
|
||||
/// appropriately.
|
||||
///
|
||||
/// The type returned implements the `std::fmt::Display` trait. In most
|
||||
/// cases, just converting it to a string (or printing it) will do what
|
||||
/// you want.
|
||||
@@ -707,11 +669,10 @@ impl SubDiagnostic {
|
||||
.primary_annotation()
|
||||
.and_then(|ann| ann.get_message())
|
||||
.unwrap_or_default();
|
||||
match (main.is_empty(), annotation.is_empty()) {
|
||||
(false, true) => ConciseMessage::MainDiagnostic(main),
|
||||
(true, false) => ConciseMessage::PrimaryAnnotation(annotation),
|
||||
(false, false) => ConciseMessage::Both { main, annotation },
|
||||
(true, true) => ConciseMessage::Empty,
|
||||
if annotation.is_empty() {
|
||||
ConciseMessage::MainDiagnostic(main)
|
||||
} else {
|
||||
ConciseMessage::Both { main, annotation }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -881,6 +842,10 @@ impl Annotation {
|
||||
pub fn hide_snippet(&mut self, yes: bool) {
|
||||
self.hide_snippet = yes;
|
||||
}
|
||||
|
||||
pub fn is_primary(&self) -> bool {
|
||||
self.is_primary
|
||||
}
|
||||
}
|
||||
|
||||
/// Tags that can be associated with an annotation.
|
||||
@@ -1501,28 +1466,10 @@ pub enum DiagnosticFormat {
|
||||
pub enum ConciseMessage<'a> {
|
||||
/// A diagnostic contains a non-empty main message and an empty
|
||||
/// primary annotation message.
|
||||
///
|
||||
/// This strongly suggests that the diagnostic is using the
|
||||
/// "new" data model.
|
||||
MainDiagnostic(&'a str),
|
||||
/// A diagnostic contains an empty main message and a non-empty
|
||||
/// primary annotation message.
|
||||
///
|
||||
/// This strongly suggests that the diagnostic is using the
|
||||
/// "old" data model.
|
||||
PrimaryAnnotation(&'a str),
|
||||
/// A diagnostic contains a non-empty main message and a non-empty
|
||||
/// primary annotation message.
|
||||
///
|
||||
/// This strongly suggests that the diagnostic is using the
|
||||
/// "new" data model.
|
||||
Both { main: &'a str, annotation: &'a str },
|
||||
/// A diagnostic contains an empty main message and an empty
|
||||
/// primary annotation message.
|
||||
///
|
||||
/// This indicates that the diagnostic is probably using the old
|
||||
/// model.
|
||||
Empty,
|
||||
/// A custom concise message has been provided.
|
||||
Custom(&'a str),
|
||||
}
|
||||
@@ -1533,13 +1480,9 @@ impl std::fmt::Display for ConciseMessage<'_> {
|
||||
ConciseMessage::MainDiagnostic(main) => {
|
||||
write!(f, "{main}")
|
||||
}
|
||||
ConciseMessage::PrimaryAnnotation(annotation) => {
|
||||
write!(f, "{annotation}")
|
||||
}
|
||||
ConciseMessage::Both { main, annotation } => {
|
||||
write!(f, "{main}: {annotation}")
|
||||
}
|
||||
ConciseMessage::Empty => Ok(()),
|
||||
ConciseMessage::Custom(message) => {
|
||||
write!(f, "{message}")
|
||||
}
|
||||
|
||||
@@ -21,7 +21,11 @@ use crate::source::source_text;
|
||||
/// reflected in the changed AST offsets.
|
||||
/// The other reason is that Ruff's AST doesn't implement `Eq` which Salsa requires
|
||||
/// for determining if a query result is unchanged.
|
||||
#[salsa::tracked(returns(ref), no_eq, heap_size=ruff_memory_usage::heap_size)]
|
||||
///
|
||||
/// The LRU capacity of 200 was picked without any empirical evidence that it's optimal,
|
||||
/// instead it's a wild guess that it should be unlikely that incremental changes involve
|
||||
/// more than 200 modules. Parsed ASTs within the same revision are never evicted by Salsa.
|
||||
#[salsa::tracked(returns(ref), no_eq, heap_size=ruff_memory_usage::heap_size, lru=200)]
|
||||
pub fn parsed_module(db: &dyn Db, file: File) -> ParsedModule {
|
||||
let _span = tracing::trace_span!("parsed_module", ?file).entered();
|
||||
|
||||
@@ -92,14 +96,9 @@ impl ParsedModule {
|
||||
self.inner.store(None);
|
||||
}
|
||||
|
||||
/// Returns the pointer address of this [`ParsedModule`].
|
||||
///
|
||||
/// The pointer uniquely identifies the module within the current Salsa revision,
|
||||
/// regardless of whether particular [`ParsedModuleRef`] instances are garbage collected.
|
||||
pub fn addr(&self) -> usize {
|
||||
// Note that the outer `Arc` in `inner` is stable across garbage collection, while the inner
|
||||
// `Arc` within the `ArcSwap` may change.
|
||||
Arc::as_ptr(&self.inner).addr()
|
||||
/// Returns the file to which this module belongs.
|
||||
pub fn file(&self) -> File {
|
||||
self.file
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -667,6 +667,13 @@ impl Deref for SystemPathBuf {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for SystemPathBuf {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.0.as_std_path()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: AsRef<SystemPath>> FromIterator<P> for SystemPathBuf {
|
||||
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
|
||||
let mut buf = SystemPathBuf::new();
|
||||
|
||||
@@ -149,6 +149,10 @@ impl Fix {
|
||||
&self.edits
|
||||
}
|
||||
|
||||
pub fn into_edits(self) -> Vec<Edit> {
|
||||
self.edits
|
||||
}
|
||||
|
||||
/// Return the [`Applicability`] of the [`Fix`].
|
||||
pub fn applicability(&self) -> Applicability {
|
||||
self.applicability
|
||||
|
||||
@@ -49,7 +49,7 @@ impl ModuleImports {
|
||||
// Resolve the imports.
|
||||
let mut resolved_imports = ModuleImports::default();
|
||||
for import in imports {
|
||||
for resolved in Resolver::new(db).resolve(import) {
|
||||
for resolved in Resolver::new(db, path).resolve(import) {
|
||||
if let Some(path) = resolved.as_system_path() {
|
||||
resolved_imports.insert(path.to_path_buf());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use ruff_db::files::FilePath;
|
||||
use ty_python_semantic::{ModuleName, resolve_module, resolve_real_module};
|
||||
use ruff_db::files::{File, FilePath, system_path_to_file};
|
||||
use ruff_db::system::SystemPath;
|
||||
use ty_python_semantic::{
|
||||
ModuleName, resolve_module, resolve_module_confident, resolve_real_module,
|
||||
resolve_real_module_confident,
|
||||
};
|
||||
|
||||
use crate::ModuleDb;
|
||||
use crate::collector::CollectedImport;
|
||||
@@ -7,12 +11,15 @@ use crate::collector::CollectedImport;
|
||||
/// Collect all imports for a given Python file.
|
||||
pub(crate) struct Resolver<'a> {
|
||||
db: &'a ModuleDb,
|
||||
file: Option<File>,
|
||||
}
|
||||
|
||||
impl<'a> Resolver<'a> {
|
||||
/// Initialize a [`Resolver`] with a given [`ModuleDb`].
|
||||
pub(crate) fn new(db: &'a ModuleDb) -> Self {
|
||||
Self { db }
|
||||
pub(crate) fn new(db: &'a ModuleDb, path: &SystemPath) -> Self {
|
||||
// If we know the importing file we can potentially resolve more imports
|
||||
let file = system_path_to_file(db, path).ok();
|
||||
Self { db, file }
|
||||
}
|
||||
|
||||
/// Resolve the [`CollectedImport`] into a [`FilePath`].
|
||||
@@ -70,13 +77,21 @@ impl<'a> Resolver<'a> {
|
||||
|
||||
/// Resolves a module name to a module.
|
||||
pub(crate) fn resolve_module(&self, module_name: &ModuleName) -> Option<&'a FilePath> {
|
||||
let module = resolve_module(self.db, module_name)?;
|
||||
let module = if let Some(file) = self.file {
|
||||
resolve_module(self.db, file, module_name)?
|
||||
} else {
|
||||
resolve_module_confident(self.db, module_name)?
|
||||
};
|
||||
Some(module.file(self.db)?.path(self.db))
|
||||
}
|
||||
|
||||
/// Resolves a module name to a module (stubs not allowed).
|
||||
fn resolve_real_module(&self, module_name: &ModuleName) -> Option<&'a FilePath> {
|
||||
let module = resolve_real_module(self.db, module_name)?;
|
||||
let module = if let Some(file) = self.file {
|
||||
resolve_real_module(self.db, file, module_name)?
|
||||
} else {
|
||||
resolve_real_module_confident(self.db, module_name)?
|
||||
};
|
||||
Some(module.file(self.db)?.path(self.db))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.14.7"
|
||||
version = "0.14.8"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -35,6 +35,7 @@ anyhow = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive", "string"], optional = true }
|
||||
colored = { workspace = true }
|
||||
compact_str = { workspace = true }
|
||||
fern = { workspace = true }
|
||||
glob = { workspace = true }
|
||||
globset = { workspace = true }
|
||||
|
||||
@@ -28,9 +28,11 @@ yaml.load("{}", SafeLoader)
|
||||
yaml.load("{}", yaml.SafeLoader)
|
||||
yaml.load("{}", CSafeLoader)
|
||||
yaml.load("{}", yaml.CSafeLoader)
|
||||
yaml.load("{}", yaml.cyaml.CSafeLoader)
|
||||
yaml.load("{}", NewSafeLoader)
|
||||
yaml.load("{}", Loader=SafeLoader)
|
||||
yaml.load("{}", Loader=yaml.SafeLoader)
|
||||
yaml.load("{}", Loader=CSafeLoader)
|
||||
yaml.load("{}", Loader=yaml.CSafeLoader)
|
||||
yaml.load("{}", Loader=yaml.cyaml.CSafeLoader)
|
||||
yaml.load("{}", Loader=NewSafeLoader)
|
||||
|
||||
@@ -52,16 +52,16 @@ def not_broken5():
|
||||
yield inner()
|
||||
|
||||
|
||||
def not_broken6():
|
||||
def broken3():
|
||||
return (yield from [])
|
||||
|
||||
|
||||
def not_broken7():
|
||||
def broken4():
|
||||
x = yield from []
|
||||
return x
|
||||
|
||||
|
||||
def not_broken8():
|
||||
def broken5():
|
||||
x = None
|
||||
|
||||
def inner(ex):
|
||||
@@ -76,3 +76,13 @@ class NotBroken9(object):
|
||||
def __await__(self):
|
||||
yield from function()
|
||||
return 42
|
||||
|
||||
|
||||
async def broken6():
|
||||
yield 1
|
||||
return foo()
|
||||
|
||||
|
||||
async def broken7():
|
||||
yield 1
|
||||
return [1, 2, 3]
|
||||
|
||||
@@ -216,3 +216,15 @@ def get_items_list():
|
||||
|
||||
def get_items_set():
|
||||
return tuple({item for item in items}) or None # OK
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/21473
|
||||
tuple("") or True # SIM222
|
||||
tuple(t"") or True # OK
|
||||
tuple(0) or True # OK
|
||||
tuple(1) or True # OK
|
||||
tuple(False) or True # OK
|
||||
tuple(None) or True # OK
|
||||
tuple(...) or True # OK
|
||||
tuple(lambda x: x) or True # OK
|
||||
tuple(x for x in range(0)) or True # OK
|
||||
|
||||
@@ -157,3 +157,15 @@ print(f"{1}{''}" and "bar")
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/7127
|
||||
def f(a: "'' and 'b'"): ...
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/21473
|
||||
tuple("") and False # SIM223
|
||||
tuple(t"") and False # OK
|
||||
tuple(0) and False # OK
|
||||
tuple(1) and False # OK
|
||||
tuple(False) and False # OK
|
||||
tuple(None) and False # OK
|
||||
tuple(...) and False # OK
|
||||
tuple(lambda x: x) and False # OK
|
||||
tuple(x for x in range(0)) and False # OK
|
||||
|
||||
@@ -17,3 +17,24 @@ def _():
|
||||
|
||||
# Valid yield scope
|
||||
yield 3
|
||||
|
||||
|
||||
# await is valid in any generator, sync or async
|
||||
(await cor async for cor in f()) # ok
|
||||
(await cor for cor in f()) # ok
|
||||
|
||||
# but not in comprehensions
|
||||
[await cor async for cor in f()] # F704
|
||||
{await cor async for cor in f()} # F704
|
||||
{await cor: 1 async for cor in f()} # F704
|
||||
[await cor for cor in f()] # F704
|
||||
{await cor for cor in f()} # F704
|
||||
{await cor: 1 for cor in f()} # F704
|
||||
|
||||
# or in the iterator of an async generator, which is evaluated in the parent
|
||||
# scope
|
||||
(cor async for cor in await f()) # F704
|
||||
(await cor async for cor in [await c for c in f()]) # F704
|
||||
|
||||
# this is also okay because the comprehension is within the generator scope
|
||||
([await c for c in cor] async for cor in f()) # ok
|
||||
|
||||
@@ -3,3 +3,5 @@ def func():
|
||||
|
||||
# Top-level await
|
||||
await 1
|
||||
|
||||
([await c for c in cor] async for cor in func()) # ok
|
||||
|
||||
24
crates/ruff_linter/resources/test/fixtures/syntax_errors/return_in_generator.py
vendored
Normal file
24
crates/ruff_linter/resources/test/fixtures/syntax_errors/return_in_generator.py
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
async def gen():
|
||||
yield 1
|
||||
return 42
|
||||
|
||||
def gen(): # B901 but not a syntax error - not an async generator
|
||||
yield 1
|
||||
return 42
|
||||
|
||||
async def gen(): # ok - no value in return
|
||||
yield 1
|
||||
return
|
||||
|
||||
async def gen():
|
||||
yield 1
|
||||
return foo()
|
||||
|
||||
async def gen():
|
||||
yield 1
|
||||
return [1, 2, 3]
|
||||
|
||||
async def gen():
|
||||
if True:
|
||||
yield 1
|
||||
return 10
|
||||
@@ -35,6 +35,7 @@ use ruff_python_ast::helpers::{collect_import_from_member, is_docstring_stmt, to
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
use ruff_python_ast::name::QualifiedName;
|
||||
use ruff_python_ast::str::Quote;
|
||||
use ruff_python_ast::token::Tokens;
|
||||
use ruff_python_ast::visitor::{Visitor, walk_except_handler, walk_pattern};
|
||||
use ruff_python_ast::{
|
||||
self as ast, AnyParameterRef, ArgOrKeyword, Comprehension, ElifElseClause, ExceptHandler, Expr,
|
||||
@@ -48,7 +49,7 @@ use ruff_python_parser::semantic_errors::{
|
||||
SemanticSyntaxChecker, SemanticSyntaxContext, SemanticSyntaxError, SemanticSyntaxErrorKind,
|
||||
};
|
||||
use ruff_python_parser::typing::{AnnotationKind, ParsedAnnotation, parse_type_annotation};
|
||||
use ruff_python_parser::{ParseError, Parsed, Tokens};
|
||||
use ruff_python_parser::{ParseError, Parsed};
|
||||
use ruff_python_semantic::all::{DunderAllDefinition, DunderAllFlags};
|
||||
use ruff_python_semantic::analyze::{imports, typing};
|
||||
use ruff_python_semantic::{
|
||||
@@ -68,6 +69,7 @@ use crate::noqa::NoqaMapping;
|
||||
use crate::package::PackageRoot;
|
||||
use crate::preview::is_undefined_export_in_dunder_init_enabled;
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::flake8_bugbear::rules::ReturnInGenerator;
|
||||
use crate::rules::pyflakes::rules::{
|
||||
LateFutureImport, MultipleStarredExpressions, ReturnOutsideFunction,
|
||||
UndefinedLocalWithNestedImportStarUsage, YieldOutsideFunction,
|
||||
@@ -728,6 +730,12 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
self.report_diagnostic(NonlocalWithoutBinding { name }, error.range);
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::ReturnInGenerator => {
|
||||
// B901
|
||||
if self.is_rule_enabled(Rule::ReturnInGenerator) {
|
||||
self.report_diagnostic(ReturnInGenerator, error.range);
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::ReboundComprehensionVariable
|
||||
| SemanticSyntaxErrorKind::DuplicateTypeParameter
|
||||
| SemanticSyntaxErrorKind::MultipleCaseAssignment(_)
|
||||
@@ -746,6 +754,7 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
| SemanticSyntaxErrorKind::LoadBeforeNonlocalDeclaration { .. }
|
||||
| SemanticSyntaxErrorKind::NonlocalAndGlobal(_)
|
||||
| SemanticSyntaxErrorKind::AnnotatedGlobal(_)
|
||||
| SemanticSyntaxErrorKind::TypeParameterDefaultOrder(_)
|
||||
| SemanticSyntaxErrorKind::AnnotatedNonlocal(_) => {
|
||||
self.semantic_errors.borrow_mut().push(error);
|
||||
}
|
||||
@@ -779,6 +788,10 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
match scope.kind {
|
||||
ScopeKind::Class(_) => return false,
|
||||
ScopeKind::Function(_) | ScopeKind::Lambda(_) => return true,
|
||||
ScopeKind::Generator {
|
||||
kind: GeneratorKind::Generator,
|
||||
..
|
||||
} => return true,
|
||||
ScopeKind::Generator { .. }
|
||||
| ScopeKind::Module
|
||||
| ScopeKind::Type
|
||||
@@ -828,14 +841,19 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
self.source_type.is_ipynb()
|
||||
}
|
||||
|
||||
fn in_generator_scope(&self) -> bool {
|
||||
matches!(
|
||||
&self.semantic.current_scope().kind,
|
||||
ScopeKind::Generator {
|
||||
kind: GeneratorKind::Generator,
|
||||
..
|
||||
fn in_generator_context(&self) -> bool {
|
||||
for scope in self.semantic.current_scopes() {
|
||||
if matches!(
|
||||
scope.kind,
|
||||
ScopeKind::Generator {
|
||||
kind: GeneratorKind::Generator,
|
||||
..
|
||||
}
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
)
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn in_loop_context(&self) -> bool {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::{TokenKind, Tokens};
|
||||
use ruff_source_file::LineRanges;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ use std::path::Path;
|
||||
|
||||
use ruff_notebook::CellOffsets;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_ast::token::Tokens;
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::Tokens;
|
||||
|
||||
use crate::Locator;
|
||||
use crate::directives::TodoComment;
|
||||
|
||||
@@ -5,8 +5,8 @@ use std::str::FromStr;
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::{TokenKind, Tokens};
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
use ruff_source_file::LineRanges;
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
|
||||
@@ -5,8 +5,8 @@ use std::iter::FusedIterator;
|
||||
use std::slice::Iter;
|
||||
|
||||
use ruff_python_ast::statement_visitor::{StatementVisitor, walk_stmt};
|
||||
use ruff_python_ast::token::{Token, TokenKind, Tokens};
|
||||
use ruff_python_ast::{self as ast, Stmt, Suite};
|
||||
use ruff_python_parser::{Token, TokenKind, Tokens};
|
||||
use ruff_source_file::UniversalNewlineIterator;
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
|
||||
|
||||
@@ -9,10 +9,11 @@ use anyhow::Result;
|
||||
use libcst_native as cst;
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_python_ast::token::Tokens;
|
||||
use ruff_python_ast::{self as ast, Expr, ModModule, Stmt};
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_importer::Insertion;
|
||||
use ruff_python_parser::{Parsed, Tokens};
|
||||
use ruff_python_parser::Parsed;
|
||||
use ruff_python_semantic::{
|
||||
ImportedName, MemberNameImport, ModuleNameImport, NameImport, SemanticModel,
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ pub mod rule_selector;
|
||||
pub mod rules;
|
||||
pub mod settings;
|
||||
pub mod source_kind;
|
||||
pub mod suppression;
|
||||
mod text_helpers;
|
||||
pub mod upstream_categories;
|
||||
mod violation;
|
||||
|
||||
@@ -1043,6 +1043,7 @@ mod tests {
|
||||
Rule::YieldFromInAsyncFunction,
|
||||
Path::new("yield_from_in_async_function.py")
|
||||
)]
|
||||
#[test_case(Rule::ReturnInGenerator, Path::new("return_in_generator.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);
|
||||
|
||||
@@ -75,6 +75,7 @@ pub(crate) fn unsafe_yaml_load(checker: &Checker, call: &ast::ExprCall) {
|
||||
qualified_name.segments(),
|
||||
["yaml", "SafeLoader" | "CSafeLoader"]
|
||||
| ["yaml", "loader", "SafeLoader" | "CSafeLoader"]
|
||||
| ["yaml", "cyaml", "CSafeLoader"]
|
||||
)
|
||||
})
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::statement_visitor;
|
||||
use ruff_python_ast::statement_visitor::StatementVisitor;
|
||||
use ruff_python_ast::visitor::{Visitor, walk_expr, walk_stmt};
|
||||
use ruff_python_ast::{self as ast, Expr, Stmt, StmtFunctionDef};
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
@@ -96,6 +95,11 @@ pub(crate) fn return_in_generator(checker: &Checker, function_def: &StmtFunction
|
||||
return;
|
||||
}
|
||||
|
||||
// Async functions are flagged by the `ReturnInGenerator` semantic syntax error.
|
||||
if function_def.is_async {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut visitor = ReturnInGeneratorVisitor::default();
|
||||
visitor.visit_body(&function_def.body);
|
||||
|
||||
@@ -112,15 +116,9 @@ struct ReturnInGeneratorVisitor {
|
||||
has_yield: bool,
|
||||
}
|
||||
|
||||
impl StatementVisitor<'_> for ReturnInGeneratorVisitor {
|
||||
impl Visitor<'_> for ReturnInGeneratorVisitor {
|
||||
fn visit_stmt(&mut self, stmt: &Stmt) {
|
||||
match stmt {
|
||||
Stmt::Expr(ast::StmtExpr { value, .. }) => match **value {
|
||||
Expr::Yield(_) | Expr::YieldFrom(_) => {
|
||||
self.has_yield = true;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Stmt::FunctionDef(_) => {
|
||||
// Do not recurse into nested functions; they're evaluated separately.
|
||||
}
|
||||
@@ -130,8 +128,19 @@ impl StatementVisitor<'_> for ReturnInGeneratorVisitor {
|
||||
node_index: _,
|
||||
}) => {
|
||||
self.return_ = Some(*range);
|
||||
walk_stmt(self, stmt);
|
||||
}
|
||||
_ => statement_visitor::walk_stmt(self, stmt),
|
||||
_ => walk_stmt(self, stmt),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &Expr) {
|
||||
match expr {
|
||||
Expr::Lambda(_) => {}
|
||||
Expr::Yield(_) | Expr::YieldFrom(_) => {
|
||||
self.has_yield = true;
|
||||
}
|
||||
_ => walk_expr(self, expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,3 +21,46 @@ B901 Using `yield` and `return {value}` in a generator function can lead to conf
|
||||
37 |
|
||||
38 | yield from not_broken()
|
||||
|
|
||||
|
||||
B901 Using `yield` and `return {value}` in a generator function can lead to confusing behavior
|
||||
--> B901.py:56:5
|
||||
|
|
||||
55 | def broken3():
|
||||
56 | return (yield from [])
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
B901 Using `yield` and `return {value}` in a generator function can lead to confusing behavior
|
||||
--> B901.py:61:5
|
||||
|
|
||||
59 | def broken4():
|
||||
60 | x = yield from []
|
||||
61 | return x
|
||||
| ^^^^^^^^
|
||||
|
|
||||
|
||||
B901 Using `yield` and `return {value}` in a generator function can lead to confusing behavior
|
||||
--> B901.py:72:5
|
||||
|
|
||||
71 | inner((yield from []))
|
||||
72 | return x
|
||||
| ^^^^^^^^
|
||||
|
|
||||
|
||||
B901 Using `yield` and `return {value}` in a generator function can lead to confusing behavior
|
||||
--> B901.py:83:5
|
||||
|
|
||||
81 | async def broken6():
|
||||
82 | yield 1
|
||||
83 | return foo()
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
B901 Using `yield` and `return {value}` in a generator function can lead to confusing behavior
|
||||
--> B901.py:88:5
|
||||
|
|
||||
86 | async def broken7():
|
||||
87 | yield 1
|
||||
88 | return [1, 2, 3]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::{TokenKind, Tokens};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::Locator;
|
||||
|
||||
@@ -3,7 +3,7 @@ use ruff_python_ast as ast;
|
||||
use ruff_python_ast::ExprGenerator;
|
||||
use ruff_python_ast::comparable::ComparableExpr;
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
@@ -3,7 +3,7 @@ use ruff_python_ast as ast;
|
||||
use ruff_python_ast::ExprGenerator;
|
||||
use ruff_python_ast::comparable::ComparableExpr;
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::borrow::Cow;
|
||||
use itertools::Itertools;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::StringFlags;
|
||||
use ruff_python_ast::token::{Token, TokenKind, Tokens};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::{Token, TokenKind, Tokens};
|
||||
use ruff_source_file::LineRanges;
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_python_parser::{TokenKind, Tokens};
|
||||
use ruff_text_size::{Ranged, TextLen, TextSize};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
@@ -4,10 +4,10 @@ use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::helpers::{is_const_false, is_const_true};
|
||||
use ruff_python_ast::stmt_if::elif_else_range;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
use ruff_python_ast::whitespace::indentation;
|
||||
use ruff_python_ast::{self as ast, Decorator, ElifElseClause, Expr, Stmt};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_python_semantic::analyze::visibility::is_property;
|
||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer, is_python_whitespace};
|
||||
|
||||
@@ -1144,3 +1144,23 @@ help: Replace with `(i for i in range(1))`
|
||||
208 | # https://github.com/astral-sh/ruff/issues/21136
|
||||
209 | def get_items():
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM222 [*] Use `True` instead of `... or True`
|
||||
--> SIM222.py:222:1
|
||||
|
|
||||
221 | # https://github.com/astral-sh/ruff/issues/21473
|
||||
222 | tuple("") or True # SIM222
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
223 | tuple(t"") or True # OK
|
||||
224 | tuple(0) or True # OK
|
||||
|
|
||||
help: Replace with `True`
|
||||
219 |
|
||||
220 |
|
||||
221 | # https://github.com/astral-sh/ruff/issues/21473
|
||||
- tuple("") or True # SIM222
|
||||
222 + True # SIM222
|
||||
223 | tuple(t"") or True # OK
|
||||
224 | tuple(0) or True # OK
|
||||
225 | tuple(1) or True # OK
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
@@ -1025,3 +1025,23 @@ help: Replace with `f"{''}{''}"`
|
||||
156 |
|
||||
157 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM223 [*] Use `tuple("")` instead of `tuple("") and ...`
|
||||
--> SIM223.py:163:1
|
||||
|
|
||||
162 | # https://github.com/astral-sh/ruff/issues/21473
|
||||
163 | tuple("") and False # SIM223
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
164 | tuple(t"") and False # OK
|
||||
165 | tuple(0) and False # OK
|
||||
|
|
||||
help: Replace with `tuple("")`
|
||||
160 |
|
||||
161 |
|
||||
162 | # https://github.com/astral-sh/ruff/issues/21473
|
||||
- tuple("") and False # SIM223
|
||||
163 + tuple("") # SIM223
|
||||
164 | tuple(t"") and False # OK
|
||||
165 | tuple(0) and False # OK
|
||||
166 | tuple(1) and False # OK
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
@@ -57,7 +57,7 @@ pub(crate) fn check_os_pathlib_single_arg_calls(
|
||||
fn_argument: &str,
|
||||
fix_enabled: bool,
|
||||
violation: impl Violation,
|
||||
applicability: Option<Applicability>,
|
||||
applicability: Applicability,
|
||||
) {
|
||||
if call.arguments.len() != 1 {
|
||||
return;
|
||||
@@ -91,18 +91,14 @@ pub(crate) fn check_os_pathlib_single_arg_calls(
|
||||
|
||||
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)
|
||||
}
|
||||
let applicability = match applicability {
|
||||
Applicability::DisplayOnly => Applicability::DisplayOnly,
|
||||
_ if checker.comment_ranges().intersects(range) => Applicability::Unsafe,
|
||||
_ => applicability,
|
||||
};
|
||||
|
||||
let fix = Fix::applicable_edits(edit, [import_edit], applicability);
|
||||
|
||||
Ok(fix)
|
||||
});
|
||||
}
|
||||
@@ -138,6 +134,7 @@ pub(crate) fn is_file_descriptor(expr: &Expr, semantic: &SemanticModel) -> bool
|
||||
typing::is_int(binding, semantic)
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub(crate) fn check_os_pathlib_two_arg_calls(
|
||||
checker: &Checker,
|
||||
call: &ExprCall,
|
||||
@@ -146,6 +143,7 @@ pub(crate) fn check_os_pathlib_two_arg_calls(
|
||||
second_arg: &str,
|
||||
fix_enabled: bool,
|
||||
violation: impl Violation,
|
||||
applicability: Applicability,
|
||||
) {
|
||||
let range = call.range();
|
||||
let mut diagnostic = checker.report_diagnostic(violation, call.func.range());
|
||||
@@ -174,10 +172,10 @@ pub(crate) fn check_os_pathlib_two_arg_calls(
|
||||
format!("{binding}({path_code}).{attr}({second_code})")
|
||||
};
|
||||
|
||||
let applicability = if checker.comment_ranges().intersects(range) {
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
let applicability = match applicability {
|
||||
Applicability::DisplayOnly => Applicability::DisplayOnly,
|
||||
_ if checker.comment_ranges().intersects(range) => Applicability::Unsafe,
|
||||
_ => applicability,
|
||||
};
|
||||
|
||||
Ok(Fix::applicable_edits(
|
||||
@@ -209,3 +207,9 @@ pub(crate) fn is_argument_non_default(arguments: &Arguments, name: &str, positio
|
||||
.find_argument_value(name, position)
|
||||
.is_some_and(|expr| !expr.is_none_literal_expr())
|
||||
}
|
||||
|
||||
/// Returns `true` if the given call is a top-level expression in its statement.
|
||||
/// This means the call's return value is not used, so return type changes don't matter.
|
||||
pub(crate) fn is_top_level_expression_call(checker: &Checker) -> bool {
|
||||
checker.semantic().current_expression_parent().is_none()
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::preview::is_fix_os_getcwd_enabled;
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_diagnostics::{Applicability, Edit, Fix};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::preview::is_fix_os_getcwd_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::is_top_level_expression_call;
|
||||
use crate::{FixAvailability, Violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.getcwd` and `os.getcwdb`.
|
||||
///
|
||||
@@ -37,6 +39,8 @@ use ruff_text_size::Ranged;
|
||||
///
|
||||
/// ## Fix Safety
|
||||
/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression.
|
||||
/// Additionally, the fix is marked as unsafe when the return value is used because the type changes
|
||||
/// from `str` or `bytes` to a `Path` object.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `Path.cwd`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.cwd)
|
||||
@@ -83,7 +87,10 @@ pub(crate) fn os_getcwd(checker: &Checker, call: &ExprCall, segments: &[&str]) {
|
||||
checker.semantic(),
|
||||
)?;
|
||||
|
||||
let applicability = if checker.comment_ranges().intersects(range) {
|
||||
// Unsafe when the fix would delete comments or change a used return value
|
||||
let applicability = if checker.comment_ranges().intersects(range)
|
||||
|| !is_top_level_expression_call(checker)
|
||||
{
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
|
||||
@@ -45,6 +45,10 @@ use crate::{FixAvailability, Violation};
|
||||
/// behaviors is required, there's no existing `pathlib` alternative. See CPython issue
|
||||
/// [#69200](https://github.com/python/cpython/issues/69200).
|
||||
///
|
||||
/// Additionally, the fix is marked as unsafe because `os.path.abspath()` returns `str` or `bytes` (`AnyStr`),
|
||||
/// while `Path.resolve()` returns a `Path` object. This change in return type can break code that uses
|
||||
/// the return value.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `Path.resolve`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.resolve)
|
||||
/// - [Python documentation: `os.path.abspath`](https://docs.python.org/3/library/os.path.html#os.path.abspath)
|
||||
@@ -85,6 +89,6 @@ pub(crate) fn os_path_abspath(checker: &Checker, call: &ExprCall, segments: &[&s
|
||||
"path",
|
||||
is_fix_os_path_abspath_enabled(checker.settings()),
|
||||
OsPathAbspath,
|
||||
Some(Applicability::Unsafe),
|
||||
Applicability::Unsafe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -82,6 +82,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),
|
||||
Applicability::Unsafe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,10 @@ use crate::{FixAvailability, Violation};
|
||||
/// As a result, code relying on the exact string returned by `os.path.dirname`
|
||||
/// may behave differently after the fix.
|
||||
///
|
||||
/// Additionally, the fix is marked as unsafe because `os.path.dirname()` returns `str` or `bytes` (`AnyStr`),
|
||||
/// while `Path.parent` returns a `Path` object. This change in return type can break code that uses
|
||||
/// the return value.
|
||||
///
|
||||
/// ## Known issues
|
||||
/// While using `pathlib` can improve the readability and type safety of your code,
|
||||
/// it can be less performant than the lower-level alternatives that work directly with strings,
|
||||
@@ -82,6 +86,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),
|
||||
Applicability::Unsafe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
@@ -72,6 +73,6 @@ pub(crate) fn os_path_exists(checker: &Checker, call: &ExprCall, segments: &[&st
|
||||
"path",
|
||||
is_fix_os_path_exists_enabled(checker.settings()),
|
||||
OsPathExists,
|
||||
None,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,10 @@ use crate::{FixAvailability, Violation};
|
||||
/// directory can't be resolved: `os.path.expanduser` returns the
|
||||
/// input unchanged, while `Path.expanduser` raises `RuntimeError`.
|
||||
///
|
||||
/// Additionally, the fix is marked as unsafe because `os.path.expanduser()` returns `str` or `bytes` (`AnyStr`),
|
||||
/// while `Path.expanduser()` returns a `Path` object. This change in return type can break code that uses
|
||||
/// the return value.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `Path.expanduser`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.expanduser)
|
||||
/// - [Python documentation: `os.path.expanduser`](https://docs.python.org/3/library/os.path.html#os.path.expanduser)
|
||||
@@ -76,6 +80,6 @@ pub(crate) fn os_path_expanduser(checker: &Checker, call: &ExprCall, segments: &
|
||||
"path",
|
||||
is_fix_os_path_expanduser_enabled(checker.settings()),
|
||||
OsPathExpanduser,
|
||||
Some(Applicability::Unsafe),
|
||||
Applicability::Unsafe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
@@ -75,6 +76,6 @@ pub(crate) fn os_path_getatime(checker: &Checker, call: &ExprCall, segments: &[&
|
||||
"filename",
|
||||
is_fix_os_path_getatime_enabled(checker.settings()),
|
||||
OsPathGetatime,
|
||||
None,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
@@ -76,6 +77,6 @@ pub(crate) fn os_path_getctime(checker: &Checker, call: &ExprCall, segments: &[&
|
||||
"filename",
|
||||
is_fix_os_path_getctime_enabled(checker.settings()),
|
||||
OsPathGetctime,
|
||||
None,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
@@ -76,6 +77,6 @@ pub(crate) fn os_path_getmtime(checker: &Checker, call: &ExprCall, segments: &[&
|
||||
"filename",
|
||||
is_fix_os_path_getmtime_enabled(checker.settings()),
|
||||
OsPathGetmtime,
|
||||
None,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
@@ -76,6 +77,6 @@ pub(crate) fn os_path_getsize(checker: &Checker, call: &ExprCall, segments: &[&s
|
||||
"filename",
|
||||
is_fix_os_path_getsize_enabled(checker.settings()),
|
||||
OsPathGetsize,
|
||||
None,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
@@ -71,6 +72,6 @@ pub(crate) fn os_path_isabs(checker: &Checker, call: &ExprCall, segments: &[&str
|
||||
"s",
|
||||
is_fix_os_path_isabs_enabled(checker.settings()),
|
||||
OsPathIsabs,
|
||||
None,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
@@ -73,6 +74,6 @@ pub(crate) fn os_path_isdir(checker: &Checker, call: &ExprCall, segments: &[&str
|
||||
"s",
|
||||
is_fix_os_path_isdir_enabled(checker.settings()),
|
||||
OsPathIsdir,
|
||||
None,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
@@ -73,6 +74,6 @@ pub(crate) fn os_path_isfile(checker: &Checker, call: &ExprCall, segments: &[&st
|
||||
"path",
|
||||
is_fix_os_path_isfile_enabled(checker.settings()),
|
||||
OsPathIsfile,
|
||||
None,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
@@ -73,6 +74,6 @@ pub(crate) fn os_path_islink(checker: &Checker, call: &ExprCall, segments: &[&st
|
||||
"path",
|
||||
is_fix_os_path_islink_enabled(checker.settings()),
|
||||
OsPathIslink,
|
||||
None,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
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_samefile_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::{
|
||||
check_os_pathlib_two_arg_calls, has_unknown_keywords_or_starred_expr,
|
||||
};
|
||||
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.samefile`.
|
||||
@@ -79,5 +81,6 @@ pub(crate) fn os_path_samefile(checker: &Checker, call: &ExprCall, segments: &[&
|
||||
"f2",
|
||||
fix_enabled,
|
||||
OsPathSamefile,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::{ExprCall, PythonVersion};
|
||||
|
||||
@@ -5,6 +6,7 @@ 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,
|
||||
is_top_level_expression_call,
|
||||
};
|
||||
use crate::{FixAvailability, Violation};
|
||||
|
||||
@@ -38,6 +40,8 @@ use crate::{FixAvailability, Violation};
|
||||
///
|
||||
/// ## Fix Safety
|
||||
/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression.
|
||||
/// Additionally, the fix is marked as unsafe when the return value is used because the type changes
|
||||
/// from `str` or `bytes` (`AnyStr`) to a `Path` object.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `Path.readlink`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.readline)
|
||||
@@ -82,6 +86,13 @@ pub(crate) fn os_readlink(checker: &Checker, call: &ExprCall, segments: &[&str])
|
||||
return;
|
||||
}
|
||||
|
||||
let applicability = if !is_top_level_expression_call(checker) {
|
||||
// Unsafe because the return type changes (str/bytes -> Path)
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
};
|
||||
|
||||
check_os_pathlib_single_arg_calls(
|
||||
checker,
|
||||
call,
|
||||
@@ -89,6 +100,6 @@ pub(crate) fn os_readlink(checker: &Checker, call: &ExprCall, segments: &[&str])
|
||||
"path",
|
||||
is_fix_os_readlink_enabled(checker.settings()),
|
||||
OsReadlink,
|
||||
None,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
@@ -84,6 +85,6 @@ pub(crate) fn os_remove(checker: &Checker, call: &ExprCall, segments: &[&str]) {
|
||||
"path",
|
||||
is_fix_os_remove_enabled(checker.settings()),
|
||||
OsRemove,
|
||||
None,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
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_rename_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::{
|
||||
check_os_pathlib_two_arg_calls, has_unknown_keywords_or_starred_expr,
|
||||
is_keyword_only_argument_non_default,
|
||||
is_keyword_only_argument_non_default, is_top_level_expression_call,
|
||||
};
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.rename`.
|
||||
@@ -38,6 +40,8 @@ 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.
|
||||
/// Additionally, the fix is marked as unsafe when the return value is used because the type changes
|
||||
/// from `None` to a `Path` object.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `Path.rename`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.rename)
|
||||
@@ -87,5 +91,22 @@ pub(crate) fn os_rename(checker: &Checker, call: &ExprCall, segments: &[&str]) {
|
||||
&["src", "dst", "src_dir_fd", "dst_dir_fd"],
|
||||
);
|
||||
|
||||
check_os_pathlib_two_arg_calls(checker, call, "rename", "src", "dst", fix_enabled, OsRename);
|
||||
// Unsafe when the fix would delete comments or change a used return value
|
||||
let applicability = if !is_top_level_expression_call(checker) {
|
||||
// Unsafe because the return type changes (None -> Path)
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
};
|
||||
|
||||
check_os_pathlib_two_arg_calls(
|
||||
checker,
|
||||
call,
|
||||
"rename",
|
||||
"src",
|
||||
"dst",
|
||||
fix_enabled,
|
||||
OsRename,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
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_replace_enabled;
|
||||
use crate::rules::flake8_use_pathlib::helpers::{
|
||||
check_os_pathlib_two_arg_calls, has_unknown_keywords_or_starred_expr,
|
||||
is_keyword_only_argument_non_default,
|
||||
is_keyword_only_argument_non_default, is_top_level_expression_call,
|
||||
};
|
||||
use crate::{FixAvailability, Violation};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `os.replace`.
|
||||
@@ -41,6 +43,8 @@ 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.
|
||||
/// Additionally, the fix is marked as unsafe when the return value is used because the type changes
|
||||
/// from `None` to a `Path` object.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `Path.replace`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.replace)
|
||||
@@ -90,6 +94,14 @@ pub(crate) fn os_replace(checker: &Checker, call: &ExprCall, segments: &[&str])
|
||||
&["src", "dst", "src_dir_fd", "dst_dir_fd"],
|
||||
);
|
||||
|
||||
// Unsafe when the fix would delete comments or change a used return value
|
||||
let applicability = if !is_top_level_expression_call(checker) {
|
||||
// Unsafe because the return type changes (None -> Path)
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
};
|
||||
|
||||
check_os_pathlib_two_arg_calls(
|
||||
checker,
|
||||
call,
|
||||
@@ -98,5 +110,6 @@ pub(crate) fn os_replace(checker: &Checker, call: &ExprCall, segments: &[&str])
|
||||
"dst",
|
||||
fix_enabled,
|
||||
OsReplace,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
@@ -84,6 +85,6 @@ pub(crate) fn os_rmdir(checker: &Checker, call: &ExprCall, segments: &[&str]) {
|
||||
"path",
|
||||
is_fix_os_rmdir_enabled(checker.settings()),
|
||||
OsRmdir,
|
||||
None,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use ruff_diagnostics::Applicability;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::ExprCall;
|
||||
|
||||
@@ -84,6 +85,6 @@ pub(crate) fn os_unlink(checker: &Checker, call: &ExprCall, segments: &[&str]) {
|
||||
"path",
|
||||
is_fix_os_unlink_enabled(checker.settings()),
|
||||
OsUnlink,
|
||||
None,
|
||||
Applicability::Safe,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_python_ast::token::Tokens;
|
||||
use ruff_python_ast::{self as ast, Stmt};
|
||||
use ruff_python_parser::Tokens;
|
||||
use ruff_source_file::LineRanges;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_python_ast::Stmt;
|
||||
use ruff_python_parser::{TokenKind, Tokens};
|
||||
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||
use ruff_python_trivia::PythonWhitespace;
|
||||
use ruff_source_file::UniversalNewlines;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
@@ -11,8 +11,8 @@ use comments::Comment;
|
||||
use normalize::normalize_imports;
|
||||
use order::order_imports;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_ast::token::Tokens;
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_parser::Tokens;
|
||||
use settings::Settings;
|
||||
use types::EitherImport::{Import, ImportFrom};
|
||||
use types::{AliasData, ImportBlock, TrailingComma};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use itertools::{EitherOrBoth, Itertools};
|
||||
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::token::Tokens;
|
||||
use ruff_python_ast::whitespace::trailing_lines_end;
|
||||
use ruff_python_ast::{PySourceType, PythonVersion, Stmt};
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::Tokens;
|
||||
use ruff_python_trivia::{PythonWhitespace, leading_indentation, textwrap::indent};
|
||||
use ruff_source_file::{LineRanges, UniversalNewlines};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
|
||||
/// Returns `true` if the name should be considered "ambiguous".
|
||||
pub(super) fn is_ambiguous_name(name: &str) -> bool {
|
||||
|
||||
@@ -8,10 +8,10 @@ use itertools::Itertools;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_notebook::CellOffsets;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_ast::token::TokenIterWithContext;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_python_ast::token::Tokens;
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_parser::TokenIterWithContext;
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_parser::Tokens;
|
||||
use ruff_python_trivia::PythonWhitespace;
|
||||
use ruff_source_file::{LineRanges, UniversalNewlines};
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_notebook::CellOffsets;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_ast::token::{TokenIterWithContext, TokenKind, Tokens};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::{TokenIterWithContext, TokenKind, Tokens};
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
|
||||
use crate::Locator;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::AlwaysFixableViolation;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use crate::Violation;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::Edit;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::LintContext;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::LintContext;
|
||||
|
||||
@@ -9,7 +9,7 @@ pub(crate) use missing_whitespace::*;
|
||||
pub(crate) use missing_whitespace_after_keyword::*;
|
||||
pub(crate) use missing_whitespace_around_operator::*;
|
||||
pub(crate) use redundant_backslash::*;
|
||||
use ruff_python_parser::{TokenKind, Tokens};
|
||||
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||
use ruff_python_trivia::is_python_whitespace;
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
pub(crate) use space_around_operator::*;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_source_file::LineRanges;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::LintContext;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::checkers::ast::LintContext;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_python_trivia::PythonWhitespace;
|
||||
use ruff_source_file::LineRanges;
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_ast::token::TokenKind;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::checkers::ast::LintContext;
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::iter::Peekable;
|
||||
use itertools::Itertools;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_notebook::CellOffsets;
|
||||
use ruff_python_parser::{Token, TokenKind, Tokens};
|
||||
use ruff_python_ast::token::{Token, TokenKind, Tokens};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::{AlwaysFixableViolation, Edit, Fix, checkers::ast::LintContext};
|
||||
|
||||
@@ -2,8 +2,8 @@ use anyhow::{Error, bail};
|
||||
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::helpers;
|
||||
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||
use ruff_python_ast::{CmpOp, Expr};
|
||||
use ruff_python_parser::{TokenKind, Tokens};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
@@ -3,8 +3,8 @@ use itertools::Itertools;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::helpers::contains_effect;
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||
use ruff_python_ast::{self as ast, Stmt};
|
||||
use ruff_python_parser::{TokenKind, Tokens};
|
||||
use ruff_python_semantic::Binding;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
|
||||
@@ -37,3 +37,88 @@ F704 `await` statement outside of a function
|
||||
12 |
|
||||
13 | def _():
|
||||
|
|
||||
|
||||
F704 `await` statement outside of a function
|
||||
--> F704.py:27:2
|
||||
|
|
||||
26 | # but not in comprehensions
|
||||
27 | [await cor async for cor in f()] # F704
|
||||
| ^^^^^^^^^
|
||||
28 | {await cor async for cor in f()} # F704
|
||||
29 | {await cor: 1 async for cor in f()} # F704
|
||||
|
|
||||
|
||||
F704 `await` statement outside of a function
|
||||
--> F704.py:28:2
|
||||
|
|
||||
26 | # but not in comprehensions
|
||||
27 | [await cor async for cor in f()] # F704
|
||||
28 | {await cor async for cor in f()} # F704
|
||||
| ^^^^^^^^^
|
||||
29 | {await cor: 1 async for cor in f()} # F704
|
||||
30 | [await cor for cor in f()] # F704
|
||||
|
|
||||
|
||||
F704 `await` statement outside of a function
|
||||
--> F704.py:29:2
|
||||
|
|
||||
27 | [await cor async for cor in f()] # F704
|
||||
28 | {await cor async for cor in f()} # F704
|
||||
29 | {await cor: 1 async for cor in f()} # F704
|
||||
| ^^^^^^^^^
|
||||
30 | [await cor for cor in f()] # F704
|
||||
31 | {await cor for cor in f()} # F704
|
||||
|
|
||||
|
||||
F704 `await` statement outside of a function
|
||||
--> F704.py:30:2
|
||||
|
|
||||
28 | {await cor async for cor in f()} # F704
|
||||
29 | {await cor: 1 async for cor in f()} # F704
|
||||
30 | [await cor for cor in f()] # F704
|
||||
| ^^^^^^^^^
|
||||
31 | {await cor for cor in f()} # F704
|
||||
32 | {await cor: 1 for cor in f()} # F704
|
||||
|
|
||||
|
||||
F704 `await` statement outside of a function
|
||||
--> F704.py:31:2
|
||||
|
|
||||
29 | {await cor: 1 async for cor in f()} # F704
|
||||
30 | [await cor for cor in f()] # F704
|
||||
31 | {await cor for cor in f()} # F704
|
||||
| ^^^^^^^^^
|
||||
32 | {await cor: 1 for cor in f()} # F704
|
||||
|
|
||||
|
||||
F704 `await` statement outside of a function
|
||||
--> F704.py:32:2
|
||||
|
|
||||
30 | [await cor for cor in f()] # F704
|
||||
31 | {await cor for cor in f()} # F704
|
||||
32 | {await cor: 1 for cor in f()} # F704
|
||||
| ^^^^^^^^^
|
||||
33 |
|
||||
34 | # or in the iterator of an async generator, which is evaluated in the parent
|
||||
|
|
||||
|
||||
F704 `await` statement outside of a function
|
||||
--> F704.py:36:23
|
||||
|
|
||||
34 | # or in the iterator of an async generator, which is evaluated in the parent
|
||||
35 | # scope
|
||||
36 | (cor async for cor in await f()) # F704
|
||||
| ^^^^^^^^^
|
||||
37 | (await cor async for cor in [await c for c in f()]) # F704
|
||||
|
|
||||
|
||||
F704 `await` statement outside of a function
|
||||
--> F704.py:37:30
|
||||
|
|
||||
35 | # scope
|
||||
36 | (cor async for cor in await f()) # F704
|
||||
37 | (await cor async for cor in [await c for c in f()]) # F704
|
||||
| ^^^^^^^
|
||||
38 |
|
||||
39 | # this is also okay because the comprehension is within the generator scope
|
||||
|
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_parser::{Token, TokenKind};
|
||||
use ruff_python_ast::token::{Token, TokenKind};
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
|
||||
use crate::Locator;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_python_ast::StmtImportFrom;
|
||||
use ruff_python_parser::{TokenKind, Tokens};
|
||||
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::Locator;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user