Compare commits
159 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
ecab623fb2 | ||
|
|
42f152108a | ||
|
|
594b7b04d3 | ||
|
|
b5b4917d7f | ||
|
|
0084e94f78 | ||
|
|
566c959add | ||
|
|
8bcfc198b8 | ||
|
|
c534bfaf01 | ||
|
|
5e1b2eef57 | ||
|
|
98681b9356 | ||
|
|
3ed537e9f1 | ||
|
|
53efc82989 | ||
|
|
666f488f1b | ||
|
|
aef2fad0c5 | ||
|
|
df66946b89 | ||
|
|
efb23b01af | ||
|
|
e5818d89fd | ||
|
|
a7d48ffd40 | ||
|
|
77f8fa6906 | ||
|
|
7c7f8d1a17 | ||
|
|
761031f729 | ||
|
|
792ec3e96e | ||
|
|
c7107a5a90 | ||
|
|
e0f3a064b9 | ||
|
|
e2e21508dc | ||
|
|
2c0c5ff4e7 | ||
|
|
adf4f1e3f4 | ||
|
|
3dbbb76654 | ||
|
|
b72120f7ef | ||
|
|
33713a7e2a | ||
|
|
5364256190 | ||
|
|
81c97e9e94 | ||
|
|
294f863523 | ||
|
|
4628180fac | ||
|
|
de32247f30 | ||
|
|
209ea06592 | ||
|
|
88bfc32dfc | ||
|
|
66d233134f | ||
|
|
15cb41c1f9 | ||
|
|
eddb9ad38d | ||
|
|
b19ddca69b | ||
|
|
747c39a26a | ||
|
|
dd15656deb | ||
|
|
adf095e889 | ||
|
|
bfd65c4215 | ||
|
|
0631e72187 | ||
|
|
bab688b76c | ||
|
|
7e277667d1 | ||
|
|
d379f3826f | ||
|
|
6f9265d78d | ||
|
|
0c6d652b5f | ||
|
|
03fe560164 | ||
|
|
68343e7edf | ||
|
|
a57e291311 | ||
|
|
f317a71682 | ||
|
|
35bfcff24d | ||
|
|
474b00568a | ||
|
|
3b23d3c041 | ||
|
|
3f4875313f | ||
|
|
8327f262ff | ||
|
|
8c4a9d8808 | ||
|
|
907e7f7705 | ||
|
|
7e8915d76e | ||
|
|
680297aef8 | ||
|
|
314a6e58ed | ||
|
|
918fc2c773 | ||
|
|
e642874cf1 | ||
|
|
aec225d825 | ||
|
|
d24c891a4b | ||
|
|
859f9ec21a | ||
|
|
3410041b4c | ||
|
|
f2ce5e561a | ||
|
|
f495c6d4ae | ||
|
|
768bb24cdf | ||
|
|
492d676736 | ||
|
|
ddc1417f22 | ||
|
|
040aa7463b | ||
|
|
09d457aa52 | ||
|
|
438ef334d3 | ||
|
|
6cc502781f | ||
|
|
e2a1d1a8eb | ||
|
|
040b482cf7 | ||
|
|
03dfbf21eb | ||
|
|
e3c78d8203 | ||
|
|
a9b3caf181 | ||
|
|
629258241f | ||
|
|
762c44527e |
@@ -7,10 +7,6 @@ serial = { max-threads = 1 }
|
|||||||
filter = 'binary(file_watching)'
|
filter = 'binary(file_watching)'
|
||||||
test-group = 'serial'
|
test-group = 'serial'
|
||||||
|
|
||||||
[[profile.default.overrides]]
|
|
||||||
filter = 'binary(e2e)'
|
|
||||||
test-group = 'serial'
|
|
||||||
|
|
||||||
[profile.ci]
|
[profile.ci]
|
||||||
# Print out output for failing tests as soon as they fail, and also at the end
|
# Print out output for failing tests as soon as they fail, and also at the end
|
||||||
# of the run (for easy scrollability).
|
# of the run (for easy scrollability).
|
||||||
|
|||||||
7
.github/renovate.json5
vendored
7
.github/renovate.json5
vendored
@@ -2,12 +2,11 @@
|
|||||||
$schema: "https://docs.renovatebot.com/renovate-schema.json",
|
$schema: "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
dependencyDashboard: true,
|
dependencyDashboard: true,
|
||||||
suppressNotifications: ["prEditedNotification"],
|
suppressNotifications: ["prEditedNotification"],
|
||||||
extends: ["config:recommended"],
|
extends: ["github>astral-sh/renovate-config"],
|
||||||
labels: ["internal"],
|
labels: ["internal"],
|
||||||
schedule: ["before 4am on Monday"],
|
schedule: ["before 4am on Monday"],
|
||||||
semanticCommits: "disabled",
|
semanticCommits: "disabled",
|
||||||
separateMajorMinor: false,
|
separateMajorMinor: false,
|
||||||
prHourlyLimit: 10,
|
|
||||||
enabledManagers: ["github-actions", "pre-commit", "cargo", "pep621", "pip_requirements", "npm"],
|
enabledManagers: ["github-actions", "pre-commit", "cargo", "pep621", "pip_requirements", "npm"],
|
||||||
cargo: {
|
cargo: {
|
||||||
// See https://docs.renovatebot.com/configuration-options/#rangestrategy
|
// See https://docs.renovatebot.com/configuration-options/#rangestrategy
|
||||||
@@ -16,7 +15,7 @@
|
|||||||
pep621: {
|
pep621: {
|
||||||
// The default for this package manager is to only search for `pyproject.toml` files
|
// 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
|
// 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: {
|
pip_requirements: {
|
||||||
// The default for this package manager is to run on all requirements.txt files:
|
// The default for this package manager is to run on all requirements.txt files:
|
||||||
@@ -34,7 +33,7 @@
|
|||||||
npm: {
|
npm: {
|
||||||
// The default for this package manager is to only search for `package.json` files
|
// 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
|
// found at the repository root: https://docs.renovatebot.com/modules/manager/npm/#file-matching
|
||||||
fileMatch: ["^playground/.*package\\.json$"],
|
managerFilePatterns: ["^playground/.*package\\.json$"],
|
||||||
},
|
},
|
||||||
"pre-commit": {
|
"pre-commit": {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|||||||
16
.github/workflows/build-binaries.yml
vendored
16
.github/workflows/build-binaries.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
- name: "Prep README.md"
|
- name: "Prep README.md"
|
||||||
@@ -72,7 +72,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
architecture: x64
|
architecture: x64
|
||||||
@@ -114,7 +114,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
architecture: arm64
|
architecture: arm64
|
||||||
@@ -170,7 +170,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
architecture: ${{ matrix.platform.arch }}
|
architecture: ${{ matrix.platform.arch }}
|
||||||
@@ -223,7 +223,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
architecture: x64
|
architecture: x64
|
||||||
@@ -300,7 +300,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
- name: "Prep README.md"
|
- name: "Prep README.md"
|
||||||
@@ -365,7 +365,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
architecture: x64
|
architecture: x64
|
||||||
@@ -431,7 +431,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
- name: "Prep README.md"
|
- name: "Prep README.md"
|
||||||
|
|||||||
118
.github/workflows/ci.yaml
vendored
118
.github/workflows/ci.yaml
vendored
@@ -230,7 +230,7 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
@@ -252,7 +252,7 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
shared-key: ruff-linux-debug
|
shared-key: ruff-linux-debug
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
@@ -261,15 +261,15 @@ jobs:
|
|||||||
- name: "Install mold"
|
- name: "Install mold"
|
||||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||||
- name: "Install cargo nextest"
|
- name: "Install cargo nextest"
|
||||||
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
|
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||||
with:
|
with:
|
||||||
tool: cargo-nextest
|
tool: cargo-nextest
|
||||||
- name: "Install cargo insta"
|
- name: "Install cargo insta"
|
||||||
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
|
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||||
with:
|
with:
|
||||||
tool: cargo-insta
|
tool: cargo-insta
|
||||||
- name: "Install uv"
|
- name: "Install uv"
|
||||||
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
with:
|
with:
|
||||||
enable-cache: "true"
|
enable-cache: "true"
|
||||||
- name: ty mdtests (GitHub annotations)
|
- name: ty mdtests (GitHub annotations)
|
||||||
@@ -284,6 +284,10 @@ jobs:
|
|||||||
run: cargo insta test --all-features --unreferenced reject --test-runner nextest
|
run: cargo insta test --all-features --unreferenced reject --test-runner nextest
|
||||||
- name: Dogfood ty on py-fuzzer
|
- name: Dogfood ty on py-fuzzer
|
||||||
run: uv run --project=./python/py-fuzzer cargo run -p ty check --project=./python/py-fuzzer
|
run: uv run --project=./python/py-fuzzer cargo run -p ty check --project=./python/py-fuzzer
|
||||||
|
- name: Dogfood ty on the scripts directory
|
||||||
|
run: uv run --project=./scripts cargo run -p ty check --project=./scripts
|
||||||
|
- name: Dogfood ty on ty_benchmark
|
||||||
|
run: uv run --project=./scripts/ty_benchmark cargo run -p ty check --project=./scripts/ty_benchmark
|
||||||
# Check for broken links in the documentation.
|
# Check for broken links in the documentation.
|
||||||
- run: cargo doc --all --no-deps
|
- run: cargo doc --all --no-deps
|
||||||
env:
|
env:
|
||||||
@@ -311,7 +315,7 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
@@ -319,11 +323,11 @@ jobs:
|
|||||||
- name: "Install mold"
|
- name: "Install mold"
|
||||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||||
- name: "Install cargo nextest"
|
- name: "Install cargo nextest"
|
||||||
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
|
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||||
with:
|
with:
|
||||||
tool: cargo-nextest
|
tool: cargo-nextest
|
||||||
- name: "Install uv"
|
- name: "Install uv"
|
||||||
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
with:
|
with:
|
||||||
enable-cache: "true"
|
enable-cache: "true"
|
||||||
- name: "Run tests"
|
- name: "Run tests"
|
||||||
@@ -346,17 +350,17 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup show
|
run: rustup show
|
||||||
- name: "Install cargo nextest"
|
- name: "Install cargo nextest"
|
||||||
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
|
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||||
with:
|
with:
|
||||||
tool: cargo-nextest
|
tool: cargo-nextest
|
||||||
- name: "Install uv"
|
- name: "Install uv"
|
||||||
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
with:
|
with:
|
||||||
enable-cache: "true"
|
enable-cache: "true"
|
||||||
- name: "Run tests"
|
- name: "Run tests"
|
||||||
@@ -374,7 +378,7 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
@@ -411,7 +415,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
file: "Cargo.toml"
|
file: "Cargo.toml"
|
||||||
field: "workspace.package.rust-version"
|
field: "workspace.package.rust-version"
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
@@ -435,7 +439,7 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
workspaces: "fuzz -> target"
|
workspaces: "fuzz -> target"
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
@@ -444,7 +448,7 @@ jobs:
|
|||||||
- name: "Install mold"
|
- name: "Install mold"
|
||||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||||
- name: "Install cargo-binstall"
|
- 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"
|
- name: "Install cargo-fuzz"
|
||||||
# Download the latest version from quick install and not the github releases because github releases only has MUSL targets.
|
# 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
|
run: cargo binstall cargo-fuzz --force --disable-strategies crate-meta-data --no-confirm
|
||||||
@@ -462,8 +466,8 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- 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:
|
with:
|
||||||
shared-key: ruff-linux-debug
|
shared-key: ruff-linux-debug
|
||||||
save-if: false
|
save-if: false
|
||||||
@@ -494,10 +498,10 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup component add rustfmt
|
run: rustup component add rustfmt
|
||||||
# Run all code generation scripts, and verify that the current output is
|
# Run all code generation scripts, and verify that the current output is
|
||||||
@@ -532,7 +536,7 @@ jobs:
|
|||||||
ref: ${{ github.event.pull_request.base.ref }}
|
ref: ${{ github.event.pull_request.base.ref }}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
activate-environment: true
|
activate-environment: true
|
||||||
@@ -543,7 +547,7 @@ jobs:
|
|||||||
- name: "Install mold"
|
- name: "Install mold"
|
||||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
shared-key: ruff-linux-debug
|
shared-key: ruff-linux-debug
|
||||||
save-if: false
|
save-if: false
|
||||||
@@ -638,8 +642,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- 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:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
@@ -684,7 +688,7 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
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 binstall --no-confirm cargo-shear
|
||||||
- run: cargo shear
|
- run: cargo shear
|
||||||
|
|
||||||
@@ -697,8 +701,8 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- 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:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
@@ -719,11 +723,11 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
architecture: x64
|
architecture: x64
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "Prep README.md"
|
- name: "Prep README.md"
|
||||||
@@ -748,8 +752,8 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- 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:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
@@ -781,7 +785,7 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "Add SSH key"
|
- name: "Add SSH key"
|
||||||
@@ -792,7 +796,7 @@ jobs:
|
|||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup show
|
run: rustup show
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
with:
|
with:
|
||||||
python-version: 3.13
|
python-version: 3.13
|
||||||
activate-environment: true
|
activate-environment: true
|
||||||
@@ -825,7 +829,7 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
@@ -853,7 +857,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
shared-key: ruff-linux-debug
|
shared-key: ruff-linux-debug
|
||||||
save-if: false
|
save-if: false
|
||||||
@@ -871,7 +875,7 @@ jobs:
|
|||||||
repository: "astral-sh/ruff-lsp"
|
repository: "astral-sh/ruff-lsp"
|
||||||
path: ruff-lsp
|
path: ruff-lsp
|
||||||
|
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
# installation fails on 3.13 and newer
|
# installation fails on 3.13 and newer
|
||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
@@ -904,7 +908,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup target add wasm32-unknown-unknown
|
run: rustup target add wasm32-unknown-unknown
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
@@ -914,7 +918,7 @@ jobs:
|
|||||||
cache-dependency-path: playground/package-lock.json
|
cache-dependency-path: playground/package-lock.json
|
||||||
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
|
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
|
||||||
- name: "Install Node dependencies"
|
- name: "Install Node dependencies"
|
||||||
run: npm ci
|
run: npm ci --ignore-scripts
|
||||||
working-directory: playground
|
working-directory: playground
|
||||||
- name: "Build playgrounds"
|
- name: "Build playgrounds"
|
||||||
run: npm run dev:wasm
|
run: npm run dev:wasm
|
||||||
@@ -938,22 +942,25 @@ jobs:
|
|||||||
needs.determine_changes.outputs.linter == 'true'
|
needs.determine_changes.outputs.linter == 'true'
|
||||||
)
|
)
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
|
permissions:
|
||||||
|
contents: read # required for actions/checkout
|
||||||
|
id-token: write # required for OIDC authentication with CodSpeed
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout Branch"
|
- name: "Checkout Branch"
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
|
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup show
|
run: rustup show
|
||||||
|
|
||||||
- name: "Install codspeed"
|
- name: "Install codspeed"
|
||||||
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
|
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||||
with:
|
with:
|
||||||
tool: cargo-codspeed
|
tool: cargo-codspeed
|
||||||
|
|
||||||
@@ -961,11 +968,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
|
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"
|
- name: "Run benchmarks"
|
||||||
uses: CodSpeedHQ/action@6a8e2b874c338bf81cc5e8be715ada75908d3871 # v4.3.4
|
uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1
|
||||||
with:
|
with:
|
||||||
mode: instrumentation
|
mode: simulation
|
||||||
run: cargo codspeed run
|
run: cargo codspeed run
|
||||||
token: ${{ secrets.CODSPEED_TOKEN }}
|
|
||||||
|
|
||||||
benchmarks-instrumented-ty:
|
benchmarks-instrumented-ty:
|
||||||
name: "benchmarks instrumented (ty)"
|
name: "benchmarks instrumented (ty)"
|
||||||
@@ -978,22 +984,25 @@ jobs:
|
|||||||
needs.determine_changes.outputs.ty == 'true'
|
needs.determine_changes.outputs.ty == 'true'
|
||||||
)
|
)
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
|
permissions:
|
||||||
|
contents: read # required for actions/checkout
|
||||||
|
id-token: write # required for OIDC authentication with CodSpeed
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout Branch"
|
- name: "Checkout Branch"
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
|
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup show
|
run: rustup show
|
||||||
|
|
||||||
- name: "Install codspeed"
|
- name: "Install codspeed"
|
||||||
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
|
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||||
with:
|
with:
|
||||||
tool: cargo-codspeed
|
tool: cargo-codspeed
|
||||||
|
|
||||||
@@ -1001,11 +1010,10 @@ jobs:
|
|||||||
run: cargo codspeed build --features "codspeed,instrumented" --profile profiling --no-default-features -p ruff_benchmark --bench ty
|
run: cargo codspeed build --features "codspeed,instrumented" --profile profiling --no-default-features -p ruff_benchmark --bench ty
|
||||||
|
|
||||||
- name: "Run benchmarks"
|
- name: "Run benchmarks"
|
||||||
uses: CodSpeedHQ/action@6a8e2b874c338bf81cc5e8be715ada75908d3871 # v4.3.4
|
uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1
|
||||||
with:
|
with:
|
||||||
mode: instrumentation
|
mode: simulation
|
||||||
run: cargo codspeed run
|
run: cargo codspeed run
|
||||||
token: ${{ secrets.CODSPEED_TOKEN }}
|
|
||||||
|
|
||||||
benchmarks-walltime:
|
benchmarks-walltime:
|
||||||
name: "benchmarks walltime (${{ matrix.benchmarks }})"
|
name: "benchmarks walltime (${{ matrix.benchmarks }})"
|
||||||
@@ -1013,6 +1021,9 @@ jobs:
|
|||||||
needs: determine_changes
|
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') }}
|
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
|
timeout-minutes: 20
|
||||||
|
permissions:
|
||||||
|
contents: read # required for actions/checkout
|
||||||
|
id-token: write # required for OIDC authentication with CodSpeed
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
benchmarks:
|
benchmarks:
|
||||||
@@ -1024,16 +1035,16 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
|
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup show
|
run: rustup show
|
||||||
|
|
||||||
- name: "Install codspeed"
|
- name: "Install codspeed"
|
||||||
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
|
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||||
with:
|
with:
|
||||||
tool: cargo-codspeed
|
tool: cargo-codspeed
|
||||||
|
|
||||||
@@ -1041,7 +1052,7 @@ jobs:
|
|||||||
run: cargo codspeed build --features "codspeed,walltime" --profile profiling --no-default-features -p ruff_benchmark
|
run: cargo codspeed build --features "codspeed,walltime" --profile profiling --no-default-features -p ruff_benchmark
|
||||||
|
|
||||||
- name: "Run benchmarks"
|
- name: "Run benchmarks"
|
||||||
uses: CodSpeedHQ/action@6a8e2b874c338bf81cc5e8be715ada75908d3871 # v4.3.4
|
uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1
|
||||||
env:
|
env:
|
||||||
# enabling walltime flamegraphs adds ~6 minutes to the CI time, and they don't
|
# 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
|
# appear to provide much useful insight for our walltime benchmarks right now
|
||||||
@@ -1050,4 +1061,3 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
mode: walltime
|
mode: walltime
|
||||||
run: cargo codspeed run --bench ty_walltime "${{ matrix.benchmarks }}"
|
run: cargo codspeed run --bench ty_walltime "${{ matrix.benchmarks }}"
|
||||||
token: ${{ secrets.CODSPEED_TOKEN }}
|
|
||||||
|
|||||||
4
.github/workflows/daily_fuzz.yaml
vendored
4
.github/workflows/daily_fuzz.yaml
vendored
@@ -34,12 +34,12 @@ jobs:
|
|||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup show
|
run: rustup show
|
||||||
- name: "Install mold"
|
- name: "Install mold"
|
||||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
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
|
- name: Build ruff
|
||||||
# A debug build means the script runs slower once it gets started,
|
# 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
|
# but this is outweighed by the fact that a release build takes *much* longer to compile in CI
|
||||||
|
|||||||
8
.github/workflows/mypy_primer.yaml
vendored
8
.github/workflows/mypy_primer.yaml
vendored
@@ -43,9 +43,9 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install the latest version of uv
|
- name: Install the latest version of uv
|
||||||
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
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:
|
with:
|
||||||
workspaces: "ruff"
|
workspaces: "ruff"
|
||||||
|
|
||||||
@@ -81,9 +81,9 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install the latest version of uv
|
- name: Install the latest version of uv
|
||||||
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
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:
|
with:
|
||||||
workspaces: "ruff"
|
workspaces: "ruff"
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/publish-docs.yml
vendored
4
.github/workflows/publish-docs.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
ref: ${{ inputs.ref }}
|
ref: ${{ inputs.ref }}
|
||||||
persist-credentials: true
|
persist-credentials: true
|
||||||
|
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: 3.12
|
python-version: 3.12
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup show
|
run: rustup show
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: "Install Insiders dependencies"
|
- name: "Install Insiders dependencies"
|
||||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||||
|
|||||||
2
.github/workflows/publish-playground.yml
vendored
2
.github/workflows/publish-playground.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
|||||||
package-manager-cache: false
|
package-manager-cache: false
|
||||||
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
|
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
|
||||||
- name: "Install Node dependencies"
|
- name: "Install Node dependencies"
|
||||||
run: npm ci
|
run: npm ci --ignore-scripts
|
||||||
working-directory: playground
|
working-directory: playground
|
||||||
- name: "Run TypeScript checks"
|
- name: "Run TypeScript checks"
|
||||||
run: npm run check
|
run: npm run check
|
||||||
|
|||||||
2
.github/workflows/publish-pypi.yml
vendored
2
.github/workflows/publish-pypi.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: "Install uv"
|
- name: "Install uv"
|
||||||
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||||
with:
|
with:
|
||||||
pattern: wheels-*
|
pattern: wheels-*
|
||||||
|
|||||||
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
|
package-manager-cache: false
|
||||||
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
|
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
|
||||||
- name: "Install Node dependencies"
|
- name: "Install Node dependencies"
|
||||||
run: npm ci
|
run: npm ci --ignore-scripts
|
||||||
working-directory: playground
|
working-directory: playground
|
||||||
- name: "Run TypeScript checks"
|
- name: "Run TypeScript checks"
|
||||||
run: npm run check
|
run: npm run check
|
||||||
|
|||||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -123,7 +123,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
|
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -174,7 +174,7 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
val: ${{ steps.host.outputs.manifest }}
|
val: ${{ steps.host.outputs.manifest }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
@@ -250,7 +250,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|||||||
12
.github/workflows/sync_typeshed.yaml
vendored
12
.github/workflows/sync_typeshed.yaml
vendored
@@ -77,7 +77,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
git config --global user.name typeshedbot
|
git config --global user.name typeshedbot
|
||||||
git config --global user.email '<>'
|
git config --global user.email '<>'
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
- name: Sync typeshed stubs
|
- name: Sync typeshed stubs
|
||||||
run: |
|
run: |
|
||||||
rm -rf "ruff/${VENDORED_TYPESHED}"
|
rm -rf "ruff/${VENDORED_TYPESHED}"
|
||||||
@@ -131,7 +131,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: true
|
persist-credentials: true
|
||||||
ref: ${{ env.UPSTREAM_BRANCH}}
|
ref: ${{ env.UPSTREAM_BRANCH}}
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
- name: Setup git
|
- name: Setup git
|
||||||
run: |
|
run: |
|
||||||
git config --global user.name typeshedbot
|
git config --global user.name typeshedbot
|
||||||
@@ -170,7 +170,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: true
|
persist-credentials: true
|
||||||
ref: ${{ env.UPSTREAM_BRANCH}}
|
ref: ${{ env.UPSTREAM_BRANCH}}
|
||||||
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
- name: Setup git
|
- name: Setup git
|
||||||
run: |
|
run: |
|
||||||
git config --global user.name typeshedbot
|
git config --global user.name typeshedbot
|
||||||
@@ -198,7 +198,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
rm "${VENDORED_TYPESHED}/pyproject.toml"
|
rm "${VENDORED_TYPESHED}/pyproject.toml"
|
||||||
git commit -am "Remove pyproject.toml file"
|
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"
|
- name: "Install Rust toolchain"
|
||||||
if: ${{ success() }}
|
if: ${{ success() }}
|
||||||
run: rustup show
|
run: rustup show
|
||||||
@@ -207,12 +207,12 @@ jobs:
|
|||||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||||
- name: "Install cargo nextest"
|
- name: "Install cargo nextest"
|
||||||
if: ${{ success() }}
|
if: ${{ success() }}
|
||||||
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
|
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||||
with:
|
with:
|
||||||
tool: cargo-nextest
|
tool: cargo-nextest
|
||||||
- name: "Install cargo insta"
|
- name: "Install cargo insta"
|
||||||
if: ${{ success() }}
|
if: ${{ success() }}
|
||||||
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
|
uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60
|
||||||
with:
|
with:
|
||||||
tool: cargo-insta
|
tool: cargo-insta
|
||||||
- name: Update snapshots
|
- name: Update snapshots
|
||||||
|
|||||||
6
.github/workflows/ty-ecosystem-analyzer.yaml
vendored
6
.github/workflows/ty-ecosystem-analyzer.yaml
vendored
@@ -33,11 +33,11 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install the latest version of uv
|
- name: Install the latest version of uv
|
||||||
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
with:
|
with:
|
||||||
enable-cache: true # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
|
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:
|
with:
|
||||||
workspaces: "ruff"
|
workspaces: "ruff"
|
||||||
lookup-only: false # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
|
lookup-only: false # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@e26ebfb78d372b8b091e1cb1d6fc522e135474c1"
|
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@55df3c868f3fa9ab34cff0498dd6106722aac205"
|
||||||
|
|
||||||
ecosystem-analyzer \
|
ecosystem-analyzer \
|
||||||
--repository ruff \
|
--repository ruff \
|
||||||
|
|||||||
6
.github/workflows/ty-ecosystem-report.yaml
vendored
6
.github/workflows/ty-ecosystem-report.yaml
vendored
@@ -29,11 +29,11 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install the latest version of uv
|
- name: Install the latest version of uv
|
||||||
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
|
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
with:
|
with:
|
||||||
enable-cache: true # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
|
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:
|
with:
|
||||||
workspaces: "ruff"
|
workspaces: "ruff"
|
||||||
lookup-only: false # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
|
lookup-only: false # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
|
||||||
@@ -52,7 +52,7 @@ jobs:
|
|||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@e26ebfb78d372b8b091e1cb1d6fc522e135474c1"
|
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@55df3c868f3fa9ab34cff0498dd6106722aac205"
|
||||||
|
|
||||||
ecosystem-analyzer \
|
ecosystem-analyzer \
|
||||||
--verbose \
|
--verbose \
|
||||||
|
|||||||
2
.github/workflows/typing_conformance.yaml
vendored
2
.github/workflows/typing_conformance.yaml
vendored
@@ -45,7 +45,7 @@ jobs:
|
|||||||
path: typing
|
path: typing
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
workspaces: "ruff"
|
workspaces: "ruff"
|
||||||
|
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -5,5 +5,6 @@
|
|||||||
"rust-analyzer.check.command": "clippy",
|
"rust-analyzer.check.command": "clippy",
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"**/*.snap": true
|
"**/*.snap": true
|
||||||
}
|
},
|
||||||
|
"ty.diagnosticMode": "openFilesOnly"
|
||||||
}
|
}
|
||||||
|
|||||||
64
CHANGELOG.md
64
CHANGELOG.md
@@ -1,5 +1,69 @@
|
|||||||
# Changelog
|
# 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.
|
||||||
|
|
||||||
|
### Preview features
|
||||||
|
|
||||||
|
- \[`flake8-bandit`\] Handle string literal bindings in suspicious-url-open-usage (`S310`) ([#21469](https://github.com/astral-sh/ruff/pull/21469))
|
||||||
|
- \[`pylint`\] Fix `PLR1708` false positives on nested functions ([#21177](https://github.com/astral-sh/ruff/pull/21177))
|
||||||
|
- \[`pylint`\] Fix suppression for empty dict without tuple key annotation (`PLE1141`) ([#21290](https://github.com/astral-sh/ruff/pull/21290))
|
||||||
|
- \[`ruff`\] Add rule `RUF066` to detect unnecessary class properties ([#21535](https://github.com/astral-sh/ruff/pull/21535))
|
||||||
|
- \[`ruff`\] Catch more dummy variable uses (`RUF052`) ([#19799](https://github.com/astral-sh/ruff/pull/19799))
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- [server] Set severity for non-rule diagnostics ([#21559](https://github.com/astral-sh/ruff/pull/21559))
|
||||||
|
- \[`flake8-implicit-str-concat`\] Avoid invalid fix in (`ISC003`) ([#21517](https://github.com/astral-sh/ruff/pull/21517))
|
||||||
|
- \[`parser`\] Fix panic when parsing IPython escape command expressions ([#21480](https://github.com/astral-sh/ruff/pull/21480))
|
||||||
|
|
||||||
|
### CLI
|
||||||
|
|
||||||
|
- Show partial fixability indicator in statistics output ([#21513](https://github.com/astral-sh/ruff/pull/21513))
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
|
||||||
|
- [@mikeleppane](https://github.com/mikeleppane)
|
||||||
|
- [@senekor](https://github.com/senekor)
|
||||||
|
- [@ShaharNaveh](https://github.com/ShaharNaveh)
|
||||||
|
- [@JumboBear](https://github.com/JumboBear)
|
||||||
|
- [@prakhar1144](https://github.com/prakhar1144)
|
||||||
|
- [@tsvikas](https://github.com/tsvikas)
|
||||||
|
- [@danparizher](https://github.com/danparizher)
|
||||||
|
- [@chirizxc](https://github.com/chirizxc)
|
||||||
|
- [@AlexWaygood](https://github.com/AlexWaygood)
|
||||||
|
- [@MichaReiser](https://github.com/MichaReiser)
|
||||||
|
|
||||||
## 0.14.6
|
## 0.14.6
|
||||||
|
|
||||||
Released on 2025-11-21.
|
Released on 2025-11-21.
|
||||||
|
|||||||
73
Cargo.lock
generated
73
Cargo.lock
generated
@@ -442,9 +442,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.51"
|
version = "4.5.53"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
|
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -452,9 +452,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.51"
|
version = "4.5.53"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
|
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -1016,7 +1016,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"option-ext",
|
"option-ext",
|
||||||
"redox_users",
|
"redox_users",
|
||||||
"windows-sys 0.61.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1108,7 +1108,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.61.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1255,7 +1255,7 @@ checksum = "ac7bb8710e1f09672102be7ddf39f764d8440ae74a9f4e30aaa4820dcdffa4af"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"get-size-derive2",
|
"get-size-derive2",
|
||||||
"hashbrown 0.16.0",
|
"hashbrown 0.16.1",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
@@ -1353,9 +1353,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.16.0"
|
version = "0.16.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
]
|
]
|
||||||
@@ -1564,12 +1564,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.12.0"
|
version = "2.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
|
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.16.0",
|
"hashbrown 0.16.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
@@ -1763,7 +1763,7 @@ dependencies = [
|
|||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"portable-atomic-util",
|
"portable-atomic-util",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"windows-sys 0.61.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2859,7 +2859,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.14.6"
|
version = "0.14.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argfile",
|
"argfile",
|
||||||
@@ -3117,17 +3117,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff_linter"
|
name = "ruff_linter"
|
||||||
version = "0.14.6"
|
version = "0.14.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"clap",
|
"clap",
|
||||||
"colored 3.0.0",
|
"colored 3.0.0",
|
||||||
|
"compact_str",
|
||||||
"fern",
|
"fern",
|
||||||
"glob",
|
"glob",
|
||||||
"globset",
|
"globset",
|
||||||
"hashbrown 0.16.0",
|
"hashbrown 0.16.1",
|
||||||
"imperative",
|
"imperative",
|
||||||
"insta",
|
"insta",
|
||||||
"is-macro",
|
"is-macro",
|
||||||
@@ -3472,7 +3473,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff_wasm"
|
name = "ruff_wasm"
|
||||||
version = "0.14.6"
|
version = "0.14.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"console_log",
|
"console_log",
|
||||||
@@ -3570,7 +3571,7 @@ dependencies = [
|
|||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.61.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3588,7 +3589,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "salsa"
|
name = "salsa"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=a885bb4c4c192741b8a17418fef81a71e33d111e#a885bb4c4c192741b8a17418fef81a71e33d111e"
|
source = "git+https://github.com/salsa-rs/salsa.git?rev=59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0#59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"boxcar",
|
"boxcar",
|
||||||
"compact_str",
|
"compact_str",
|
||||||
@@ -3612,12 +3613,12 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "salsa-macro-rules"
|
name = "salsa-macro-rules"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=a885bb4c4c192741b8a17418fef81a71e33d111e#a885bb4c4c192741b8a17418fef81a71e33d111e"
|
source = "git+https://github.com/salsa-rs/salsa.git?rev=59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0#59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "salsa-macros"
|
name = "salsa-macros"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=a885bb4c4c192741b8a17418fef81a71e33d111e#a885bb4c4c192741b8a17418fef81a71e33d111e"
|
source = "git+https://github.com/salsa-rs/salsa.git?rev=59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0#59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -3935,9 +3936,9 @@ checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.110"
|
version = "2.0.111"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
|
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -3971,7 +3972,7 @@ dependencies = [
|
|||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.61.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4216,9 +4217,9 @@ checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.41"
|
version = "0.1.43"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
@@ -4228,9 +4229,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-attributes"
|
name = "tracing-attributes"
|
||||||
version = "0.1.30"
|
version = "0.1.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -4239,9 +4240,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.34"
|
version = "0.1.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"valuable",
|
"valuable",
|
||||||
@@ -4283,9 +4284,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-subscriber"
|
name = "tracing-subscriber"
|
||||||
version = "0.3.20"
|
version = "0.3.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"matchers",
|
"matchers",
|
||||||
@@ -4462,7 +4463,7 @@ dependencies = [
|
|||||||
"drop_bomb",
|
"drop_bomb",
|
||||||
"get-size2",
|
"get-size2",
|
||||||
"glob",
|
"glob",
|
||||||
"hashbrown 0.16.0",
|
"hashbrown 0.16.1",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"indoc",
|
"indoc",
|
||||||
"insta",
|
"insta",
|
||||||
@@ -4474,6 +4475,7 @@ dependencies = [
|
|||||||
"quickcheck_macros",
|
"quickcheck_macros",
|
||||||
"ruff_annotate_snippets",
|
"ruff_annotate_snippets",
|
||||||
"ruff_db",
|
"ruff_db",
|
||||||
|
"ruff_diagnostics",
|
||||||
"ruff_index",
|
"ruff_index",
|
||||||
"ruff_macros",
|
"ruff_macros",
|
||||||
"ruff_memory_usage",
|
"ruff_memory_usage",
|
||||||
@@ -4519,6 +4521,7 @@ dependencies = [
|
|||||||
"lsp-types",
|
"lsp-types",
|
||||||
"regex",
|
"regex",
|
||||||
"ruff_db",
|
"ruff_db",
|
||||||
|
"ruff_diagnostics",
|
||||||
"ruff_macros",
|
"ruff_macros",
|
||||||
"ruff_notebook",
|
"ruff_notebook",
|
||||||
"ruff_python_ast",
|
"ruff_python_ast",
|
||||||
@@ -4559,6 +4562,7 @@ dependencies = [
|
|||||||
"path-slash",
|
"path-slash",
|
||||||
"regex",
|
"regex",
|
||||||
"ruff_db",
|
"ruff_db",
|
||||||
|
"ruff_diagnostics",
|
||||||
"ruff_index",
|
"ruff_index",
|
||||||
"ruff_notebook",
|
"ruff_notebook",
|
||||||
"ruff_python_ast",
|
"ruff_python_ast",
|
||||||
@@ -4600,6 +4604,7 @@ dependencies = [
|
|||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"ruff_db",
|
"ruff_db",
|
||||||
|
"ruff_diagnostics",
|
||||||
"ruff_notebook",
|
"ruff_notebook",
|
||||||
"ruff_python_formatter",
|
"ruff_python_formatter",
|
||||||
"ruff_source_file",
|
"ruff_source_file",
|
||||||
@@ -5020,7 +5025,7 @@ version = "0.1.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.61.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ regex-automata = { version = "0.4.9" }
|
|||||||
rustc-hash = { version = "2.0.0" }
|
rustc-hash = { version = "2.0.0" }
|
||||||
rustc-stable-hash = { version = "0.1.2" }
|
rustc-stable-hash = { version = "0.1.2" }
|
||||||
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
|
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
|
||||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "a885bb4c4c192741b8a17418fef81a71e33d111e", default-features = false, features = [
|
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0", default-features = false, features = [
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"macros",
|
"macros",
|
||||||
"salsa_unstable",
|
"salsa_unstable",
|
||||||
|
|||||||
@@ -147,8 +147,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
|
|||||||
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
|
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
|
||||||
|
|
||||||
# For a specific version.
|
# For a specific version.
|
||||||
curl -LsSf https://astral.sh/ruff/0.14.6/install.sh | sh
|
curl -LsSf https://astral.sh/ruff/0.14.8/install.sh | sh
|
||||||
powershell -c "irm https://astral.sh/ruff/0.14.6/install.ps1 | iex"
|
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),
|
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
|
```yaml
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.14.6
|
rev: v0.14.8
|
||||||
hooks:
|
hooks:
|
||||||
# Run the linter.
|
# Run the linter.
|
||||||
- id: ruff-check
|
- id: ruff-check
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.14.6"
|
version = "0.14.8"
|
||||||
publish = true
|
publish = true
|
||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
|
|||||||
@@ -34,9 +34,21 @@ struct ExpandedStatistics<'a> {
|
|||||||
code: Option<&'a SecondaryCode>,
|
code: Option<&'a SecondaryCode>,
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
count: usize,
|
count: usize,
|
||||||
fixable: bool,
|
#[serde(rename = "fixable")]
|
||||||
|
all_fixable: bool,
|
||||||
|
fixable_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExpandedStatistics<'_> {
|
||||||
|
fn any_fixable(&self) -> bool {
|
||||||
|
self.fixable_count > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accumulator type for grouping diagnostics by code.
|
||||||
|
/// Format: (`code`, `representative_diagnostic`, `total_count`, `fixable_count`)
|
||||||
|
type DiagnosticGroup<'a> = (Option<&'a SecondaryCode>, &'a Diagnostic, usize, usize);
|
||||||
|
|
||||||
pub(crate) struct Printer {
|
pub(crate) struct Printer {
|
||||||
format: OutputFormat,
|
format: OutputFormat,
|
||||||
log_level: LogLevel,
|
log_level: LogLevel,
|
||||||
@@ -133,7 +145,7 @@ impl Printer {
|
|||||||
if fixables.applicable > 0 {
|
if fixables.applicable > 0 {
|
||||||
writeln!(
|
writeln!(
|
||||||
writer,
|
writer,
|
||||||
"{fix_prefix} {} fixable with the --fix option.",
|
"{fix_prefix} {} fixable with the `--fix` option.",
|
||||||
fixables.applicable
|
fixables.applicable
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -256,35 +268,41 @@ impl Printer {
|
|||||||
diagnostics: &Diagnostics,
|
diagnostics: &Diagnostics,
|
||||||
writer: &mut dyn Write,
|
writer: &mut dyn Write,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let required_applicability = self.unsafe_fixes.required_applicability();
|
||||||
let statistics: Vec<ExpandedStatistics> = diagnostics
|
let statistics: Vec<ExpandedStatistics> = diagnostics
|
||||||
.inner
|
.inner
|
||||||
.iter()
|
.iter()
|
||||||
.map(|message| (message.secondary_code(), message))
|
.sorted_by_key(|diagnostic| diagnostic.secondary_code())
|
||||||
.sorted_by_key(|(code, message)| (*code, message.fixable()))
|
.fold(vec![], |mut acc: Vec<DiagnosticGroup>, diagnostic| {
|
||||||
.fold(
|
let is_fixable = diagnostic
|
||||||
vec![],
|
.fix()
|
||||||
|mut acc: Vec<((Option<&SecondaryCode>, &Diagnostic), usize)>, (code, message)| {
|
.is_some_and(|fix| fix.applies(required_applicability));
|
||||||
if let Some(((prev_code, _prev_message), count)) = acc.last_mut() {
|
let code = diagnostic.secondary_code();
|
||||||
if *prev_code == code {
|
|
||||||
*count += 1;
|
if let Some((prev_code, _prev_message, count, fixable_count)) = acc.last_mut() {
|
||||||
return acc;
|
if *prev_code == code {
|
||||||
|
*count += 1;
|
||||||
|
if is_fixable {
|
||||||
|
*fixable_count += 1;
|
||||||
}
|
}
|
||||||
|
return acc;
|
||||||
}
|
}
|
||||||
acc.push(((code, message), 1));
|
}
|
||||||
acc
|
acc.push((code, diagnostic, 1, usize::from(is_fixable)));
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
.iter()
|
||||||
|
.map(
|
||||||
|
|&(code, message, count, fixable_count)| ExpandedStatistics {
|
||||||
|
code,
|
||||||
|
name: message.name(),
|
||||||
|
count,
|
||||||
|
// Backward compatibility: `fixable` is true only when all violations are fixable.
|
||||||
|
// See: https://github.com/astral-sh/ruff/pull/21513
|
||||||
|
all_fixable: fixable_count == count,
|
||||||
|
fixable_count,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.iter()
|
|
||||||
.map(|&((code, message), count)| ExpandedStatistics {
|
|
||||||
code,
|
|
||||||
name: message.name(),
|
|
||||||
count,
|
|
||||||
fixable: if let Some(fix) = message.fix() {
|
|
||||||
fix.applies(self.unsafe_fixes.required_applicability())
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.sorted_by_key(|statistic| Reverse(statistic.count))
|
.sorted_by_key(|statistic| Reverse(statistic.count))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -308,13 +326,14 @@ impl Printer {
|
|||||||
.map(|statistic| statistic.code.map_or(0, |s| s.len()))
|
.map(|statistic| statistic.code.map_or(0, |s| s.len()))
|
||||||
.max()
|
.max()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let any_fixable = statistics.iter().any(|statistic| statistic.fixable);
|
let any_fixable = statistics.iter().any(ExpandedStatistics::any_fixable);
|
||||||
|
|
||||||
let fixable = format!("[{}] ", "*".cyan());
|
let all_fixable = format!("[{}] ", "*".cyan());
|
||||||
|
let partially_fixable = format!("[{}] ", "-".cyan());
|
||||||
let unfixable = "[ ] ";
|
let unfixable = "[ ] ";
|
||||||
|
|
||||||
// By default, we mimic Flake8's `--statistics` format.
|
// By default, we mimic Flake8's `--statistics` format.
|
||||||
for statistic in statistics {
|
for statistic in &statistics {
|
||||||
writeln!(
|
writeln!(
|
||||||
writer,
|
writer,
|
||||||
"{:>count_width$}\t{:<code_width$}\t{}{}",
|
"{:>count_width$}\t{:<code_width$}\t{}{}",
|
||||||
@@ -326,8 +345,10 @@ impl Printer {
|
|||||||
.red()
|
.red()
|
||||||
.bold(),
|
.bold(),
|
||||||
if any_fixable {
|
if any_fixable {
|
||||||
if statistic.fixable {
|
if statistic.all_fixable {
|
||||||
&fixable
|
&all_fixable
|
||||||
|
} else if statistic.any_fixable() {
|
||||||
|
&partially_fixable
|
||||||
} else {
|
} else {
|
||||||
unfixable
|
unfixable
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1043,7 +1043,7 @@ def mvce(keys, values):
|
|||||||
----- stdout -----
|
----- stdout -----
|
||||||
1 C416 [*] unnecessary-comprehension
|
1 C416 [*] unnecessary-comprehension
|
||||||
Found 1 error.
|
Found 1 error.
|
||||||
[*] 1 fixable with the --fix option.
|
[*] 1 fixable with the `--fix` option.
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
");
|
");
|
||||||
@@ -1073,7 +1073,8 @@ def mvce(keys, values):
|
|||||||
"code": "C416",
|
"code": "C416",
|
||||||
"name": "unnecessary-comprehension",
|
"name": "unnecessary-comprehension",
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"fixable": false
|
"fixable": false,
|
||||||
|
"fixable_count": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1106,7 +1107,8 @@ def mvce(keys, values):
|
|||||||
"code": "C416",
|
"code": "C416",
|
||||||
"name": "unnecessary-comprehension",
|
"name": "unnecessary-comprehension",
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"fixable": true
|
"fixable": true,
|
||||||
|
"fixable_count": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1114,6 +1116,54 @@ def mvce(keys, values):
|
|||||||
"#);
|
"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn show_statistics_json_partial_fix() {
|
||||||
|
let mut cmd = RuffCheck::default()
|
||||||
|
.args([
|
||||||
|
"--select",
|
||||||
|
"UP035",
|
||||||
|
"--statistics",
|
||||||
|
"--output-format",
|
||||||
|
"json",
|
||||||
|
])
|
||||||
|
.build();
|
||||||
|
assert_cmd_snapshot!(cmd
|
||||||
|
.pass_stdin("from typing import List, AsyncGenerator"), @r#"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"code": "UP035",
|
||||||
|
"name": "deprecated-import",
|
||||||
|
"count": 2,
|
||||||
|
"fixable": false,
|
||||||
|
"fixable_count": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn show_statistics_partial_fix() {
|
||||||
|
let mut cmd = RuffCheck::default()
|
||||||
|
.args(["--select", "UP035", "--statistics"])
|
||||||
|
.build();
|
||||||
|
assert_cmd_snapshot!(cmd
|
||||||
|
.pass_stdin("from typing import List, AsyncGenerator"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
2 UP035 [-] deprecated-import
|
||||||
|
Found 2 errors.
|
||||||
|
[*] 1 fixable with the `--fix` option.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn show_statistics_syntax_errors() {
|
fn show_statistics_syntax_errors() {
|
||||||
let mut cmd = RuffCheck::default()
|
let mut cmd = RuffCheck::default()
|
||||||
@@ -1810,7 +1860,7 @@ fn check_no_hint_for_hidden_unsafe_fixes_when_disabled() {
|
|||||||
--> -:1:1
|
--> -:1:1
|
||||||
|
|
||||||
Found 2 errors.
|
Found 2 errors.
|
||||||
[*] 1 fixable with the --fix option.
|
[*] 1 fixable with the `--fix` option.
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
");
|
");
|
||||||
@@ -1853,7 +1903,7 @@ fn check_shows_unsafe_fixes_with_opt_in() {
|
|||||||
--> -:1:1
|
--> -:1:1
|
||||||
|
|
||||||
Found 2 errors.
|
Found 2 errors.
|
||||||
[*] 2 fixable with the --fix option.
|
[*] 2 fixable with the `--fix` option.
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
");
|
");
|
||||||
|
|||||||
@@ -59,8 +59,6 @@ divan = { workspace = true, optional = true }
|
|||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
codspeed-criterion-compat = { workspace = true, default-features = false, optional = true }
|
codspeed-criterion-compat = { workspace = true, default-features = false, optional = true }
|
||||||
criterion = { workspace = true, default-features = false, optional = true }
|
criterion = { workspace = true, default-features = false, optional = true }
|
||||||
rayon = { workspace = true }
|
|
||||||
rustc-hash = { workspace = true }
|
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
@@ -88,3 +86,7 @@ mimalloc = { workspace = true }
|
|||||||
|
|
||||||
[target.'cfg(all(not(target_os = "windows"), not(target_os = "openbsd"), any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64", target_arch = "riscv64")))'.dev-dependencies]
|
[target.'cfg(all(not(target_os = "windows"), not(target_os = "openbsd"), any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64", target_arch = "riscv64")))'.dev-dependencies]
|
||||||
tikv-jemallocator = { workspace = true }
|
tikv-jemallocator = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rustc-hash = { workspace = true }
|
||||||
|
rayon = { workspace = true }
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ use criterion::{
|
|||||||
use ruff_benchmark::{
|
use ruff_benchmark::{
|
||||||
LARGE_DATASET, NUMPY_CTYPESLIB, NUMPY_GLOBALS, PYDANTIC_TYPES, TestCase, UNICODE_PYPINYIN,
|
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")]
|
#[cfg(target_os = "windows")]
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ static COLOUR_SCIENCE: Benchmark = Benchmark::new(
|
|||||||
max_dep_date: "2025-06-17",
|
max_dep_date: "2025-06-17",
|
||||||
python_version: PythonVersion::PY310,
|
python_version: PythonVersion::PY310,
|
||||||
},
|
},
|
||||||
600,
|
1070,
|
||||||
);
|
);
|
||||||
|
|
||||||
static FREQTRADE: Benchmark = Benchmark::new(
|
static FREQTRADE: Benchmark = Benchmark::new(
|
||||||
@@ -223,7 +223,7 @@ static STATIC_FRAME: Benchmark = Benchmark::new(
|
|||||||
max_dep_date: "2025-08-09",
|
max_dep_date: "2025-08-09",
|
||||||
python_version: PythonVersion::PY311,
|
python_version: PythonVersion::PY311,
|
||||||
},
|
},
|
||||||
900,
|
950,
|
||||||
);
|
);
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
|
|||||||
@@ -354,6 +354,13 @@ impl Diagnostic {
|
|||||||
Arc::make_mut(&mut self.inner).fix = Some(fix);
|
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.
|
/// Remove the fix for this diagnostic.
|
||||||
pub fn remove_fix(&mut self) {
|
pub fn remove_fix(&mut self) {
|
||||||
Arc::make_mut(&mut self.inner).fix = None;
|
Arc::make_mut(&mut self.inner).fix = None;
|
||||||
|
|||||||
@@ -21,7 +21,11 @@ use crate::source::source_text;
|
|||||||
/// reflected in the changed AST offsets.
|
/// reflected in the changed AST offsets.
|
||||||
/// The other reason is that Ruff's AST doesn't implement `Eq` which Salsa requires
|
/// The other reason is that Ruff's AST doesn't implement `Eq` which Salsa requires
|
||||||
/// for determining if a query result is unchanged.
|
/// 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 {
|
pub fn parsed_module(db: &dyn Db, file: File) -> ParsedModule {
|
||||||
let _span = tracing::trace_span!("parsed_module", ?file).entered();
|
let _span = tracing::trace_span!("parsed_module", ?file).entered();
|
||||||
|
|
||||||
@@ -92,14 +96,9 @@ impl ParsedModule {
|
|||||||
self.inner.store(None);
|
self.inner.store(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the pointer address of this [`ParsedModule`].
|
/// Returns the file to which this module belongs.
|
||||||
///
|
pub fn file(&self) -> File {
|
||||||
/// The pointer uniquely identifies the module within the current Salsa revision,
|
self.file
|
||||||
/// 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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
impl<P: AsRef<SystemPath>> FromIterator<P> for SystemPathBuf {
|
||||||
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
|
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
|
||||||
let mut buf = SystemPathBuf::new();
|
let mut buf = SystemPathBuf::new();
|
||||||
|
|||||||
@@ -149,6 +149,10 @@ impl Fix {
|
|||||||
&self.edits
|
&self.edits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_edits(self) -> Vec<Edit> {
|
||||||
|
self.edits
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the [`Applicability`] of the [`Fix`].
|
/// Return the [`Applicability`] of the [`Fix`].
|
||||||
pub fn applicability(&self) -> Applicability {
|
pub fn applicability(&self) -> Applicability {
|
||||||
self.applicability
|
self.applicability
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ impl ModuleImports {
|
|||||||
// Resolve the imports.
|
// Resolve the imports.
|
||||||
let mut resolved_imports = ModuleImports::default();
|
let mut resolved_imports = ModuleImports::default();
|
||||||
for import in imports {
|
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() {
|
if let Some(path) = resolved.as_system_path() {
|
||||||
resolved_imports.insert(path.to_path_buf());
|
resolved_imports.insert(path.to_path_buf());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
use ruff_db::files::FilePath;
|
use ruff_db::files::{File, FilePath, system_path_to_file};
|
||||||
use ty_python_semantic::{ModuleName, resolve_module, resolve_real_module};
|
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::ModuleDb;
|
||||||
use crate::collector::CollectedImport;
|
use crate::collector::CollectedImport;
|
||||||
@@ -7,12 +11,15 @@ use crate::collector::CollectedImport;
|
|||||||
/// Collect all imports for a given Python file.
|
/// Collect all imports for a given Python file.
|
||||||
pub(crate) struct Resolver<'a> {
|
pub(crate) struct Resolver<'a> {
|
||||||
db: &'a ModuleDb,
|
db: &'a ModuleDb,
|
||||||
|
file: Option<File>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Resolver<'a> {
|
impl<'a> Resolver<'a> {
|
||||||
/// Initialize a [`Resolver`] with a given [`ModuleDb`].
|
/// Initialize a [`Resolver`] with a given [`ModuleDb`].
|
||||||
pub(crate) fn new(db: &'a ModuleDb) -> Self {
|
pub(crate) fn new(db: &'a ModuleDb, path: &SystemPath) -> Self {
|
||||||
Self { db }
|
// 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`].
|
/// Resolve the [`CollectedImport`] into a [`FilePath`].
|
||||||
@@ -70,13 +77,21 @@ impl<'a> Resolver<'a> {
|
|||||||
|
|
||||||
/// Resolves a module name to a module.
|
/// Resolves a module name to a module.
|
||||||
pub(crate) fn resolve_module(&self, module_name: &ModuleName) -> Option<&'a FilePath> {
|
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))
|
Some(module.file(self.db)?.path(self.db))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves a module name to a module (stubs not allowed).
|
/// Resolves a module name to a module (stubs not allowed).
|
||||||
fn resolve_real_module(&self, module_name: &ModuleName) -> Option<&'a FilePath> {
|
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))
|
Some(module.file(self.db)?.path(self.db))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ruff_linter"
|
name = "ruff_linter"
|
||||||
version = "0.14.6"
|
version = "0.14.8"
|
||||||
publish = false
|
publish = false
|
||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
@@ -35,6 +35,7 @@ anyhow = { workspace = true }
|
|||||||
bitflags = { workspace = true }
|
bitflags = { workspace = true }
|
||||||
clap = { workspace = true, features = ["derive", "string"], optional = true }
|
clap = { workspace = true, features = ["derive", "string"], optional = true }
|
||||||
colored = { workspace = true }
|
colored = { workspace = true }
|
||||||
|
compact_str = { workspace = true }
|
||||||
fern = { workspace = true }
|
fern = { workspace = true }
|
||||||
glob = { workspace = true }
|
glob = { workspace = true }
|
||||||
globset = { workspace = true }
|
globset = { workspace = true }
|
||||||
|
|||||||
@@ -45,3 +45,22 @@ urllib.request.urlopen(urllib.request.Request(url))
|
|||||||
# https://github.com/astral-sh/ruff/issues/15522
|
# https://github.com/astral-sh/ruff/issues/15522
|
||||||
map(urllib.request.urlopen, [])
|
map(urllib.request.urlopen, [])
|
||||||
foo = urllib.request.urlopen
|
foo = urllib.request.urlopen
|
||||||
|
|
||||||
|
# https://github.com/astral-sh/ruff/issues/21462
|
||||||
|
path = "https://example.com/data.csv"
|
||||||
|
urllib.request.urlretrieve(path, "data.csv")
|
||||||
|
url = "https://example.com/api"
|
||||||
|
urllib.request.Request(url)
|
||||||
|
|
||||||
|
# Test resolved f-strings and concatenated string literals
|
||||||
|
fstring_url = f"https://example.com/data.csv"
|
||||||
|
urllib.request.urlopen(fstring_url)
|
||||||
|
urllib.request.Request(fstring_url)
|
||||||
|
|
||||||
|
concatenated_url = "https://" + "example.com/data.csv"
|
||||||
|
urllib.request.urlopen(concatenated_url)
|
||||||
|
urllib.request.Request(concatenated_url)
|
||||||
|
|
||||||
|
nested_concatenated = "http://" + "example.com" + "/data.csv"
|
||||||
|
urllib.request.urlopen(nested_concatenated)
|
||||||
|
urllib.request.Request(nested_concatenated)
|
||||||
|
|||||||
@@ -52,16 +52,16 @@ def not_broken5():
|
|||||||
yield inner()
|
yield inner()
|
||||||
|
|
||||||
|
|
||||||
def not_broken6():
|
def broken3():
|
||||||
return (yield from [])
|
return (yield from [])
|
||||||
|
|
||||||
|
|
||||||
def not_broken7():
|
def broken4():
|
||||||
x = yield from []
|
x = yield from []
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
def not_broken8():
|
def broken5():
|
||||||
x = None
|
x = None
|
||||||
|
|
||||||
def inner(ex):
|
def inner(ex):
|
||||||
@@ -76,3 +76,13 @@ class NotBroken9(object):
|
|||||||
def __await__(self):
|
def __await__(self):
|
||||||
yield from function()
|
yield from function()
|
||||||
return 42
|
return 42
|
||||||
|
|
||||||
|
|
||||||
|
async def broken6():
|
||||||
|
yield 1
|
||||||
|
return foo()
|
||||||
|
|
||||||
|
|
||||||
|
async def broken7():
|
||||||
|
yield 1
|
||||||
|
return [1, 2, 3]
|
||||||
|
|||||||
@@ -208,3 +208,17 @@ _ = t"b {f"c" f"d {t"e" t"f"} g"} h"
|
|||||||
_ = f"b {t"abc" \
|
_ = f"b {t"abc" \
|
||||||
t"def"} g"
|
t"def"} g"
|
||||||
|
|
||||||
|
|
||||||
|
# Explicit concatenation with either operand being
|
||||||
|
# a string literal that wraps across multiple lines (in parentheses)
|
||||||
|
# reports diagnostic - no autofix.
|
||||||
|
# See https://github.com/astral-sh/ruff/issues/19757
|
||||||
|
_ = "abc" + (
|
||||||
|
"def"
|
||||||
|
"ghi"
|
||||||
|
)
|
||||||
|
|
||||||
|
_ = (
|
||||||
|
"abc"
|
||||||
|
"def"
|
||||||
|
) + "ghi"
|
||||||
|
|||||||
@@ -216,3 +216,15 @@ def get_items_list():
|
|||||||
|
|
||||||
def get_items_set():
|
def get_items_set():
|
||||||
return tuple({item for item in items}) or None # OK
|
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
|
# https://github.com/astral-sh/ruff/issues/7127
|
||||||
def f(a: "'' and 'b'"): ...
|
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
|
# Valid yield scope
|
||||||
yield 3
|
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
|
||||||
|
|||||||
@@ -30,3 +30,23 @@ for a, b in d_tuple:
|
|||||||
pass
|
pass
|
||||||
for a, b in d_tuple_annotated:
|
for a, b in d_tuple_annotated:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Empty dict cases
|
||||||
|
empty_dict = {}
|
||||||
|
empty_dict["x"] = 1
|
||||||
|
for k, v in empty_dict:
|
||||||
|
pass
|
||||||
|
|
||||||
|
empty_dict_annotated_tuple_keys: dict[tuple[int, str], bool] = {}
|
||||||
|
for k, v in empty_dict_annotated_tuple_keys:
|
||||||
|
pass
|
||||||
|
|
||||||
|
empty_dict_unannotated = {}
|
||||||
|
empty_dict_unannotated[("x", "y")] = True
|
||||||
|
for k, v in empty_dict_unannotated:
|
||||||
|
pass
|
||||||
|
|
||||||
|
empty_dict_annotated_str_keys: dict[str, int] = {}
|
||||||
|
empty_dict_annotated_str_keys["x"] = 1
|
||||||
|
for k, v in empty_dict_annotated_str_keys:
|
||||||
|
pass
|
||||||
|
|||||||
@@ -129,3 +129,26 @@ def generator_with_lambda():
|
|||||||
yield 1
|
yield 1
|
||||||
func = lambda x: x # Just a regular lambda
|
func = lambda x: x # Just a regular lambda
|
||||||
yield 2
|
yield 2
|
||||||
|
|
||||||
|
# See: https://github.com/astral-sh/ruff/issues/21162
|
||||||
|
def foo():
|
||||||
|
def g():
|
||||||
|
yield 1
|
||||||
|
raise StopIteration # Should not trigger
|
||||||
|
|
||||||
|
|
||||||
|
def foo():
|
||||||
|
def g():
|
||||||
|
raise StopIteration # Should not trigger
|
||||||
|
yield 1
|
||||||
|
|
||||||
|
# https://github.com/astral-sh/ruff/pull/21177#pullrequestreview-3430209718
|
||||||
|
def foo():
|
||||||
|
yield 1
|
||||||
|
class C:
|
||||||
|
raise StopIteration # Should trigger
|
||||||
|
yield C
|
||||||
|
|
||||||
|
# https://github.com/astral-sh/ruff/pull/21177#discussion_r2539702728
|
||||||
|
def foo():
|
||||||
|
raise StopIteration((yield 1)) # Should trigger
|
||||||
109
crates/ruff_linter/resources/test/fixtures/ruff/RUF052_1.py
vendored
Normal file
109
crates/ruff_linter/resources/test/fixtures/ruff/RUF052_1.py
vendored
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# Correct usage in loop and comprehension
|
||||||
|
def process_data():
|
||||||
|
return 42
|
||||||
|
def test_correct_dummy_usage():
|
||||||
|
my_list = [{"foo": 1}, {"foo": 2}]
|
||||||
|
|
||||||
|
# Should NOT detect - dummy variable is not used
|
||||||
|
[process_data() for _ in my_list] # OK: `_` is ignored by rule
|
||||||
|
|
||||||
|
# Should NOT detect - dummy variable is not used
|
||||||
|
[item["foo"] for item in my_list] # OK: not a dummy variable name
|
||||||
|
|
||||||
|
# Should NOT detect - dummy variable is not used
|
||||||
|
[42 for _unused in my_list] # OK: `_unused` is not accessed
|
||||||
|
|
||||||
|
# Regular For Loops
|
||||||
|
def test_for_loops():
|
||||||
|
my_list = [{"foo": 1}, {"foo": 2}]
|
||||||
|
|
||||||
|
# Should detect used dummy variable
|
||||||
|
for _item in my_list:
|
||||||
|
print(_item["foo"]) # RUF052: Local dummy variable `_item` is accessed
|
||||||
|
|
||||||
|
# Should detect used dummy variable
|
||||||
|
for _index, _value in enumerate(my_list):
|
||||||
|
result = _index + _value["foo"] # RUF052: Both `_index` and `_value` are accessed
|
||||||
|
|
||||||
|
# List Comprehensions
|
||||||
|
def test_list_comprehensions():
|
||||||
|
my_list = [{"foo": 1}, {"foo": 2}]
|
||||||
|
|
||||||
|
# Should detect used dummy variable
|
||||||
|
result = [_item["foo"] for _item in my_list] # RUF052: Local dummy variable `_item` is accessed
|
||||||
|
|
||||||
|
# Should detect used dummy variable in nested comprehension
|
||||||
|
nested = [[_item["foo"] for _item in _sublist] for _sublist in [my_list, my_list]]
|
||||||
|
# RUF052: Both `_item` and `_sublist` are accessed
|
||||||
|
|
||||||
|
# Should detect with conditions
|
||||||
|
filtered = [_item["foo"] for _item in my_list if _item["foo"] > 0]
|
||||||
|
# RUF052: Local dummy variable `_item` is accessed
|
||||||
|
|
||||||
|
# Dict Comprehensions
|
||||||
|
def test_dict_comprehensions():
|
||||||
|
my_list = [{"key": "a", "value": 1}, {"key": "b", "value": 2}]
|
||||||
|
|
||||||
|
# Should detect used dummy variable
|
||||||
|
result = {_item["key"]: _item["value"] for _item in my_list}
|
||||||
|
# RUF052: Local dummy variable `_item` is accessed
|
||||||
|
|
||||||
|
# Should detect with enumerate
|
||||||
|
indexed = {_index: _item["value"] for _index, _item in enumerate(my_list)}
|
||||||
|
# RUF052: Both `_index` and `_item` are accessed
|
||||||
|
|
||||||
|
# Should detect in nested dict comprehension
|
||||||
|
nested = {_outer: {_inner["key"]: _inner["value"] for _inner in sublist}
|
||||||
|
for _outer, sublist in enumerate([my_list])}
|
||||||
|
# RUF052: `_outer`, `_inner` are accessed
|
||||||
|
|
||||||
|
# Set Comprehensions
|
||||||
|
def test_set_comprehensions():
|
||||||
|
my_list = [{"foo": 1}, {"foo": 2}, {"foo": 1}] # Note: duplicate values
|
||||||
|
|
||||||
|
# Should detect used dummy variable
|
||||||
|
unique_values = {_item["foo"] for _item in my_list}
|
||||||
|
# RUF052: Local dummy variable `_item` is accessed
|
||||||
|
|
||||||
|
# Should detect with conditions
|
||||||
|
filtered_set = {_item["foo"] for _item in my_list if _item["foo"] > 0}
|
||||||
|
# RUF052: Local dummy variable `_item` is accessed
|
||||||
|
|
||||||
|
# Should detect with complex expression
|
||||||
|
processed = {_item["foo"] * 2 for _item in my_list}
|
||||||
|
# RUF052: Local dummy variable `_item` is accessed
|
||||||
|
|
||||||
|
# Generator Expressions
|
||||||
|
def test_generator_expressions():
|
||||||
|
my_list = [{"foo": 1}, {"foo": 2}]
|
||||||
|
|
||||||
|
# Should detect used dummy variable
|
||||||
|
gen = (_item["foo"] for _item in my_list)
|
||||||
|
# RUF052: Local dummy variable `_item` is accessed
|
||||||
|
|
||||||
|
# Should detect when passed to function
|
||||||
|
total = sum(_item["foo"] for _item in my_list)
|
||||||
|
# RUF052: Local dummy variable `_item` is accessed
|
||||||
|
|
||||||
|
# Should detect with multiple generators
|
||||||
|
pairs = ((_x, _y) for _x in range(3) for _y in range(3) if _x != _y)
|
||||||
|
# RUF052: Both `_x` and `_y` are accessed
|
||||||
|
|
||||||
|
# Should detect in nested generator
|
||||||
|
nested_gen = (sum(_inner["foo"] for _inner in sublist) for _sublist in [my_list] for sublist in _sublist)
|
||||||
|
# RUF052: `_inner` and `_sublist` are accessed
|
||||||
|
|
||||||
|
# Complex Examples with Multiple Comprehension Types
|
||||||
|
def test_mixed_comprehensions():
|
||||||
|
data = [{"items": [1, 2, 3]}, {"items": [4, 5, 6]}]
|
||||||
|
|
||||||
|
# Should detect in mixed comprehensions
|
||||||
|
result = [
|
||||||
|
{_key: [_val * 2 for _val in _record["items"]] for _key in ["doubled"]}
|
||||||
|
for _record in data
|
||||||
|
]
|
||||||
|
# RUF052: `_key`, `_val`, and `_record` are all accessed
|
||||||
|
|
||||||
|
# Should detect in generator passed to list constructor
|
||||||
|
gen_list = list(_item["items"][0] for _item in data)
|
||||||
|
# RUF052: Local dummy variable `_item` is accessed
|
||||||
70
crates/ruff_linter/resources/test/fixtures/ruff/RUF066.py
vendored
Normal file
70
crates/ruff_linter/resources/test/fixtures/ruff/RUF066.py
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import abc
|
||||||
|
import typing
|
||||||
|
|
||||||
|
|
||||||
|
class User: # Test normal class properties
|
||||||
|
@property
|
||||||
|
def name(self): # ERROR: No return
|
||||||
|
f"{self.first_name} {self.last_name}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def age(self): # OK: Returning something
|
||||||
|
return 100
|
||||||
|
|
||||||
|
def method(self): # OK: Not a property
|
||||||
|
x = 1
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nested(self): # ERROR: Property itself doesn't return
|
||||||
|
def inner():
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stub(self): ... # OK: A stub; doesn't return anything
|
||||||
|
|
||||||
|
|
||||||
|
class UserMeta(metaclass=abc.ABCMeta): # Test properies inside of an ABC class
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def abstr_prop1(self): ... # OK: Abstract methods doesn't need to return anything
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def abstr_prop2(self): # OK: Abstract methods doesn't need to return anything
|
||||||
|
"""
|
||||||
|
A cool docstring
|
||||||
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def prop1(self): # OK: Returning a value
|
||||||
|
return 1
|
||||||
|
|
||||||
|
@property
|
||||||
|
def prop2(self): # ERROR: Not returning something (even when we are inside an ABC)
|
||||||
|
50
|
||||||
|
|
||||||
|
def method(self): # OK: Not a property
|
||||||
|
x = 1
|
||||||
|
|
||||||
|
|
||||||
|
def func(): # OK: Not a property
|
||||||
|
x = 1
|
||||||
|
|
||||||
|
|
||||||
|
class Proto(typing.Protocol): # Tests for a Protocol class
|
||||||
|
@property
|
||||||
|
def prop1(self) -> int: ... # OK: A stub property
|
||||||
|
|
||||||
|
|
||||||
|
class File: # Extra tests for things like yield/yield from/raise
|
||||||
|
@property
|
||||||
|
def stream1(self): # OK: Yields something
|
||||||
|
yield
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stream2(self): # OK: Yields from something
|
||||||
|
yield from self.stream1
|
||||||
|
|
||||||
|
@property
|
||||||
|
def children(self): # OK: Raises
|
||||||
|
raise ValueError("File does not have children")
|
||||||
@@ -3,3 +3,5 @@ def func():
|
|||||||
|
|
||||||
# Top-level await
|
# Top-level await
|
||||||
await 1
|
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
|
||||||
@@ -17,7 +17,7 @@ crates/ruff_linter/resources/test/project/examples/docs/docs/file.py:8:5: F841 [
|
|||||||
crates/ruff_linter/resources/test/project/project/file.py:1:8: F401 [*] `os` imported but unused
|
crates/ruff_linter/resources/test/project/project/file.py:1:8: F401 [*] `os` imported but unused
|
||||||
crates/ruff_linter/resources/test/project/project/import_file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
crates/ruff_linter/resources/test/project/project/import_file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
||||||
Found 7 errors.
|
Found 7 errors.
|
||||||
[*] 7 potentially fixable with the --fix option.
|
[*] 7 potentially fixable with the `--fix` option.
|
||||||
```
|
```
|
||||||
|
|
||||||
Running from the project directory itself should exhibit the same behavior:
|
Running from the project directory itself should exhibit the same behavior:
|
||||||
@@ -32,7 +32,7 @@ examples/docs/docs/file.py:8:5: F841 [*] Local variable `x` is assigned to but n
|
|||||||
project/file.py:1:8: F401 [*] `os` imported but unused
|
project/file.py:1:8: F401 [*] `os` imported but unused
|
||||||
project/import_file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
project/import_file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
||||||
Found 7 errors.
|
Found 7 errors.
|
||||||
[*] 7 potentially fixable with the --fix option.
|
[*] 7 potentially fixable with the `--fix` option.
|
||||||
```
|
```
|
||||||
|
|
||||||
Running from the sub-package directory should exhibit the same behavior, but omit the top-level
|
Running from the sub-package directory should exhibit the same behavior, but omit the top-level
|
||||||
@@ -43,7 +43,7 @@ files:
|
|||||||
docs/file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
docs/file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
||||||
docs/file.py:8:5: F841 [*] Local variable `x` is assigned to but never used
|
docs/file.py:8:5: F841 [*] Local variable `x` is assigned to but never used
|
||||||
Found 2 errors.
|
Found 2 errors.
|
||||||
[*] 2 potentially fixable with the --fix option.
|
[*] 2 potentially fixable with the `--fix` option.
|
||||||
```
|
```
|
||||||
|
|
||||||
`--config` should force Ruff to use the specified `pyproject.toml` for all files, and resolve
|
`--config` should force Ruff to use the specified `pyproject.toml` for all files, and resolve
|
||||||
@@ -61,7 +61,7 @@ crates/ruff_linter/resources/test/project/examples/docs/docs/file.py:4:27: F401
|
|||||||
crates/ruff_linter/resources/test/project/examples/excluded/script.py:1:8: F401 [*] `os` imported but unused
|
crates/ruff_linter/resources/test/project/examples/excluded/script.py:1:8: F401 [*] `os` imported but unused
|
||||||
crates/ruff_linter/resources/test/project/project/file.py:1:8: F401 [*] `os` imported but unused
|
crates/ruff_linter/resources/test/project/project/file.py:1:8: F401 [*] `os` imported but unused
|
||||||
Found 9 errors.
|
Found 9 errors.
|
||||||
[*] 9 potentially fixable with the --fix option.
|
[*] 9 potentially fixable with the `--fix` option.
|
||||||
```
|
```
|
||||||
|
|
||||||
Running from a parent directory should "ignore" the `exclude` (hence, `concepts/file.py` gets
|
Running from a parent directory should "ignore" the `exclude` (hence, `concepts/file.py` gets
|
||||||
@@ -74,7 +74,7 @@ docs/docs/file.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
|||||||
docs/docs/file.py:8:5: F841 [*] Local variable `x` is assigned to but never used
|
docs/docs/file.py:8:5: F841 [*] Local variable `x` is assigned to but never used
|
||||||
excluded/script.py:5:5: F841 [*] Local variable `x` is assigned to but never used
|
excluded/script.py:5:5: F841 [*] Local variable `x` is assigned to but never used
|
||||||
Found 4 errors.
|
Found 4 errors.
|
||||||
[*] 4 potentially fixable with the --fix option.
|
[*] 4 potentially fixable with the `--fix` option.
|
||||||
```
|
```
|
||||||
|
|
||||||
Passing an excluded directory directly should report errors in the contained files:
|
Passing an excluded directory directly should report errors in the contained files:
|
||||||
@@ -83,7 +83,7 @@ Passing an excluded directory directly should report errors in the contained fil
|
|||||||
∴ cargo run -p ruff -- check crates/ruff_linter/resources/test/project/examples/excluded/
|
∴ cargo run -p ruff -- check crates/ruff_linter/resources/test/project/examples/excluded/
|
||||||
crates/ruff_linter/resources/test/project/examples/excluded/script.py:1:8: F401 [*] `os` imported but unused
|
crates/ruff_linter/resources/test/project/examples/excluded/script.py:1:8: F401 [*] `os` imported but unused
|
||||||
Found 1 error.
|
Found 1 error.
|
||||||
[*] 1 potentially fixable with the --fix option.
|
[*] 1 potentially fixable with the `--fix` option.
|
||||||
```
|
```
|
||||||
|
|
||||||
Unless we `--force-exclude`:
|
Unless we `--force-exclude`:
|
||||||
|
|||||||
@@ -131,6 +131,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
|||||||
if checker.is_rule_enabled(Rule::GeneratorReturnFromIterMethod) {
|
if checker.is_rule_enabled(Rule::GeneratorReturnFromIterMethod) {
|
||||||
flake8_pyi::rules::bad_generator_return_type(function_def, checker);
|
flake8_pyi::rules::bad_generator_return_type(function_def, checker);
|
||||||
}
|
}
|
||||||
|
if checker.is_rule_enabled(Rule::StopIterationReturn) {
|
||||||
|
pylint::rules::stop_iteration_return(checker, function_def);
|
||||||
|
}
|
||||||
if checker.source_type.is_stub() {
|
if checker.source_type.is_stub() {
|
||||||
if checker.is_rule_enabled(Rule::StrOrReprDefinedInStub) {
|
if checker.is_rule_enabled(Rule::StrOrReprDefinedInStub) {
|
||||||
flake8_pyi::rules::str_or_repr_defined_in_stub(checker, stmt);
|
flake8_pyi::rules::str_or_repr_defined_in_stub(checker, stmt);
|
||||||
@@ -344,6 +347,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
|||||||
if checker.is_rule_enabled(Rule::InvalidArgumentName) {
|
if checker.is_rule_enabled(Rule::InvalidArgumentName) {
|
||||||
pep8_naming::rules::invalid_argument_name_function(checker, function_def);
|
pep8_naming::rules::invalid_argument_name_function(checker, function_def);
|
||||||
}
|
}
|
||||||
|
if checker.is_rule_enabled(Rule::PropertyWithoutReturn) {
|
||||||
|
ruff::rules::property_without_return(checker, function_def);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Stmt::Return(_) => {
|
Stmt::Return(_) => {
|
||||||
if checker.is_rule_enabled(Rule::ReturnInInit) {
|
if checker.is_rule_enabled(Rule::ReturnInInit) {
|
||||||
@@ -950,9 +956,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
|||||||
if checker.is_rule_enabled(Rule::MisplacedBareRaise) {
|
if checker.is_rule_enabled(Rule::MisplacedBareRaise) {
|
||||||
pylint::rules::misplaced_bare_raise(checker, raise);
|
pylint::rules::misplaced_bare_raise(checker, raise);
|
||||||
}
|
}
|
||||||
if checker.is_rule_enabled(Rule::StopIterationReturn) {
|
|
||||||
pylint::rules::stop_iteration_return(checker, raise);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Stmt::AugAssign(aug_assign @ ast::StmtAugAssign { target, .. }) => {
|
Stmt::AugAssign(aug_assign @ ast::StmtAugAssign { target, .. }) => {
|
||||||
if checker.is_rule_enabled(Rule::GlobalStatement) {
|
if checker.is_rule_enabled(Rule::GlobalStatement) {
|
||||||
|
|||||||
@@ -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::identifier::Identifier;
|
||||||
use ruff_python_ast::name::QualifiedName;
|
use ruff_python_ast::name::QualifiedName;
|
||||||
use ruff_python_ast::str::Quote;
|
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::visitor::{Visitor, walk_except_handler, walk_pattern};
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, AnyParameterRef, ArgOrKeyword, Comprehension, ElifElseClause, ExceptHandler, Expr,
|
self as ast, AnyParameterRef, ArgOrKeyword, Comprehension, ElifElseClause, ExceptHandler, Expr,
|
||||||
@@ -48,7 +49,7 @@ use ruff_python_parser::semantic_errors::{
|
|||||||
SemanticSyntaxChecker, SemanticSyntaxContext, SemanticSyntaxError, SemanticSyntaxErrorKind,
|
SemanticSyntaxChecker, SemanticSyntaxContext, SemanticSyntaxError, SemanticSyntaxErrorKind,
|
||||||
};
|
};
|
||||||
use ruff_python_parser::typing::{AnnotationKind, ParsedAnnotation, parse_type_annotation};
|
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::all::{DunderAllDefinition, DunderAllFlags};
|
||||||
use ruff_python_semantic::analyze::{imports, typing};
|
use ruff_python_semantic::analyze::{imports, typing};
|
||||||
use ruff_python_semantic::{
|
use ruff_python_semantic::{
|
||||||
@@ -68,6 +69,7 @@ use crate::noqa::NoqaMapping;
|
|||||||
use crate::package::PackageRoot;
|
use crate::package::PackageRoot;
|
||||||
use crate::preview::is_undefined_export_in_dunder_init_enabled;
|
use crate::preview::is_undefined_export_in_dunder_init_enabled;
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
|
use crate::rules::flake8_bugbear::rules::ReturnInGenerator;
|
||||||
use crate::rules::pyflakes::rules::{
|
use crate::rules::pyflakes::rules::{
|
||||||
LateFutureImport, MultipleStarredExpressions, ReturnOutsideFunction,
|
LateFutureImport, MultipleStarredExpressions, ReturnOutsideFunction,
|
||||||
UndefinedLocalWithNestedImportStarUsage, YieldOutsideFunction,
|
UndefinedLocalWithNestedImportStarUsage, YieldOutsideFunction,
|
||||||
@@ -728,6 +730,12 @@ impl SemanticSyntaxContext for Checker<'_> {
|
|||||||
self.report_diagnostic(NonlocalWithoutBinding { name }, error.range);
|
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::ReboundComprehensionVariable
|
||||||
| SemanticSyntaxErrorKind::DuplicateTypeParameter
|
| SemanticSyntaxErrorKind::DuplicateTypeParameter
|
||||||
| SemanticSyntaxErrorKind::MultipleCaseAssignment(_)
|
| SemanticSyntaxErrorKind::MultipleCaseAssignment(_)
|
||||||
@@ -746,6 +754,7 @@ impl SemanticSyntaxContext for Checker<'_> {
|
|||||||
| SemanticSyntaxErrorKind::LoadBeforeNonlocalDeclaration { .. }
|
| SemanticSyntaxErrorKind::LoadBeforeNonlocalDeclaration { .. }
|
||||||
| SemanticSyntaxErrorKind::NonlocalAndGlobal(_)
|
| SemanticSyntaxErrorKind::NonlocalAndGlobal(_)
|
||||||
| SemanticSyntaxErrorKind::AnnotatedGlobal(_)
|
| SemanticSyntaxErrorKind::AnnotatedGlobal(_)
|
||||||
|
| SemanticSyntaxErrorKind::TypeParameterDefaultOrder(_)
|
||||||
| SemanticSyntaxErrorKind::AnnotatedNonlocal(_) => {
|
| SemanticSyntaxErrorKind::AnnotatedNonlocal(_) => {
|
||||||
self.semantic_errors.borrow_mut().push(error);
|
self.semantic_errors.borrow_mut().push(error);
|
||||||
}
|
}
|
||||||
@@ -779,6 +788,10 @@ impl SemanticSyntaxContext for Checker<'_> {
|
|||||||
match scope.kind {
|
match scope.kind {
|
||||||
ScopeKind::Class(_) => return false,
|
ScopeKind::Class(_) => return false,
|
||||||
ScopeKind::Function(_) | ScopeKind::Lambda(_) => return true,
|
ScopeKind::Function(_) | ScopeKind::Lambda(_) => return true,
|
||||||
|
ScopeKind::Generator {
|
||||||
|
kind: GeneratorKind::Generator,
|
||||||
|
..
|
||||||
|
} => return true,
|
||||||
ScopeKind::Generator { .. }
|
ScopeKind::Generator { .. }
|
||||||
| ScopeKind::Module
|
| ScopeKind::Module
|
||||||
| ScopeKind::Type
|
| ScopeKind::Type
|
||||||
@@ -828,14 +841,19 @@ impl SemanticSyntaxContext for Checker<'_> {
|
|||||||
self.source_type.is_ipynb()
|
self.source_type.is_ipynb()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn in_generator_scope(&self) -> bool {
|
fn in_generator_context(&self) -> bool {
|
||||||
matches!(
|
for scope in self.semantic.current_scopes() {
|
||||||
&self.semantic.current_scope().kind,
|
if matches!(
|
||||||
ScopeKind::Generator {
|
scope.kind,
|
||||||
kind: GeneratorKind::Generator,
|
ScopeKind::Generator {
|
||||||
..
|
kind: GeneratorKind::Generator,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn in_loop_context(&self) -> bool {
|
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_codegen::Stylist;
|
||||||
use ruff_python_index::Indexer;
|
use ruff_python_index::Indexer;
|
||||||
use ruff_python_parser::{TokenKind, Tokens};
|
|
||||||
use ruff_source_file::LineRanges;
|
use ruff_source_file::LineRanges;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ use std::path::Path;
|
|||||||
|
|
||||||
use ruff_notebook::CellOffsets;
|
use ruff_notebook::CellOffsets;
|
||||||
use ruff_python_ast::PySourceType;
|
use ruff_python_ast::PySourceType;
|
||||||
|
use ruff_python_ast::token::Tokens;
|
||||||
use ruff_python_codegen::Stylist;
|
use ruff_python_codegen::Stylist;
|
||||||
use ruff_python_index::Indexer;
|
use ruff_python_index::Indexer;
|
||||||
use ruff_python_parser::Tokens;
|
|
||||||
|
|
||||||
use crate::Locator;
|
use crate::Locator;
|
||||||
use crate::directives::TodoComment;
|
use crate::directives::TodoComment;
|
||||||
|
|||||||
@@ -1058,6 +1058,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
|||||||
(Ruff, "063") => rules::ruff::rules::AccessAnnotationsFromClassDict,
|
(Ruff, "063") => rules::ruff::rules::AccessAnnotationsFromClassDict,
|
||||||
(Ruff, "064") => rules::ruff::rules::NonOctalPermissions,
|
(Ruff, "064") => rules::ruff::rules::NonOctalPermissions,
|
||||||
(Ruff, "065") => rules::ruff::rules::LoggingEagerConversion,
|
(Ruff, "065") => rules::ruff::rules::LoggingEagerConversion,
|
||||||
|
(Ruff, "066") => rules::ruff::rules::PropertyWithoutReturn,
|
||||||
|
|
||||||
(Ruff, "100") => rules::ruff::rules::UnusedNOQA,
|
(Ruff, "100") => rules::ruff::rules::UnusedNOQA,
|
||||||
(Ruff, "101") => rules::ruff::rules::RedirectedNOQA,
|
(Ruff, "101") => rules::ruff::rules::RedirectedNOQA,
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||||
use ruff_python_index::Indexer;
|
use ruff_python_index::Indexer;
|
||||||
use ruff_python_parser::{TokenKind, Tokens};
|
|
||||||
use ruff_python_trivia::CommentRanges;
|
use ruff_python_trivia::CommentRanges;
|
||||||
use ruff_source_file::LineRanges;
|
use ruff_source_file::LineRanges;
|
||||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use std::iter::FusedIterator;
|
|||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
|
|
||||||
use ruff_python_ast::statement_visitor::{StatementVisitor, walk_stmt};
|
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_ast::{self as ast, Stmt, Suite};
|
||||||
use ruff_python_parser::{Token, TokenKind, Tokens};
|
|
||||||
use ruff_source_file::UniversalNewlineIterator;
|
use ruff_source_file::UniversalNewlineIterator;
|
||||||
use ruff_text_size::{Ranged, TextSize};
|
use ruff_text_size::{Ranged, TextSize};
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ use anyhow::Result;
|
|||||||
use libcst_native as cst;
|
use libcst_native as cst;
|
||||||
|
|
||||||
use ruff_diagnostics::Edit;
|
use ruff_diagnostics::Edit;
|
||||||
|
use ruff_python_ast::token::Tokens;
|
||||||
use ruff_python_ast::{self as ast, Expr, ModModule, Stmt};
|
use ruff_python_ast::{self as ast, Expr, ModModule, Stmt};
|
||||||
use ruff_python_codegen::Stylist;
|
use ruff_python_codegen::Stylist;
|
||||||
use ruff_python_importer::Insertion;
|
use ruff_python_importer::Insertion;
|
||||||
use ruff_python_parser::{Parsed, Tokens};
|
use ruff_python_parser::Parsed;
|
||||||
use ruff_python_semantic::{
|
use ruff_python_semantic::{
|
||||||
ImportedName, MemberNameImport, ModuleNameImport, NameImport, SemanticModel,
|
ImportedName, MemberNameImport, ModuleNameImport, NameImport, SemanticModel,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ pub mod rule_selector;
|
|||||||
pub mod rules;
|
pub mod rules;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod source_kind;
|
pub mod source_kind;
|
||||||
|
pub mod suppression;
|
||||||
mod text_helpers;
|
mod text_helpers;
|
||||||
pub mod upstream_categories;
|
pub mod upstream_categories;
|
||||||
mod violation;
|
mod violation;
|
||||||
|
|||||||
@@ -1043,6 +1043,7 @@ mod tests {
|
|||||||
Rule::YieldFromInAsyncFunction,
|
Rule::YieldFromInAsyncFunction,
|
||||||
Path::new("yield_from_in_async_function.py")
|
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<()> {
|
fn test_syntax_errors(rule: Rule, path: &Path) -> Result<()> {
|
||||||
let snapshot = path.to_string_lossy().to_string();
|
let snapshot = path.to_string_lossy().to_string();
|
||||||
let path = Path::new("resources/test/fixtures/syntax_errors").join(path);
|
let path = Path::new("resources/test/fixtures/syntax_errors").join(path);
|
||||||
|
|||||||
@@ -279,3 +279,10 @@ pub(crate) const fn is_extended_snmp_api_path_detection_enabled(settings: &Linte
|
|||||||
pub(crate) const fn is_enumerate_for_loop_int_index_enabled(settings: &LinterSettings) -> bool {
|
pub(crate) const fn is_enumerate_for_loop_int_index_enabled(settings: &LinterSettings) -> bool {
|
||||||
settings.preview.is_enabled()
|
settings.preview.is_enabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/astral-sh/ruff/pull/21469
|
||||||
|
pub(crate) const fn is_s310_resolve_string_literal_bindings_enabled(
|
||||||
|
settings: &LinterSettings,
|
||||||
|
) -> bool {
|
||||||
|
settings.preview.is_enabled()
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ mod tests {
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use test_case::test_case;
|
use test_case::test_case;
|
||||||
|
|
||||||
use crate::assert_diagnostics;
|
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
use crate::settings::LinterSettings;
|
use crate::settings::LinterSettings;
|
||||||
use crate::settings::types::PreviewMode;
|
use crate::settings::types::PreviewMode;
|
||||||
use crate::test::test_path;
|
use crate::test::test_path;
|
||||||
|
use crate::{assert_diagnostics, assert_diagnostics_diff};
|
||||||
|
|
||||||
#[test_case(Rule::Assert, Path::new("S101.py"))]
|
#[test_case(Rule::Assert, Path::new("S101.py"))]
|
||||||
#[test_case(Rule::BadFilePermissions, Path::new("S103.py"))]
|
#[test_case(Rule::BadFilePermissions, Path::new("S103.py"))]
|
||||||
@@ -112,14 +112,19 @@ mod tests {
|
|||||||
rule_code.noqa_code(),
|
rule_code.noqa_code(),
|
||||||
path.to_string_lossy()
|
path.to_string_lossy()
|
||||||
);
|
);
|
||||||
let diagnostics = test_path(
|
|
||||||
|
assert_diagnostics_diff!(
|
||||||
|
snapshot,
|
||||||
Path::new("flake8_bandit").join(path).as_path(),
|
Path::new("flake8_bandit").join(path).as_path(),
|
||||||
|
&LinterSettings {
|
||||||
|
preview: PreviewMode::Disabled,
|
||||||
|
..LinterSettings::for_rule(rule_code)
|
||||||
|
},
|
||||||
&LinterSettings {
|
&LinterSettings {
|
||||||
preview: PreviewMode::Enabled,
|
preview: PreviewMode::Enabled,
|
||||||
..LinterSettings::for_rule(rule_code)
|
..LinterSettings::for_rule(rule_code)
|
||||||
},
|
}
|
||||||
)?;
|
);
|
||||||
assert_diagnostics!(snapshot, diagnostics);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,16 @@
|
|||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::{self as ast, Arguments, Decorator, Expr, ExprCall, Operator};
|
use ruff_python_ast::{self as ast, Arguments, Decorator, Expr, ExprCall, Operator};
|
||||||
|
use ruff_python_semantic::SemanticModel;
|
||||||
|
use ruff_python_semantic::analyze::typing::find_binding_value;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::Violation;
|
use crate::Violation;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::preview::is_suspicious_function_reference_enabled;
|
use crate::preview::{
|
||||||
|
is_s310_resolve_string_literal_bindings_enabled, is_suspicious_function_reference_enabled,
|
||||||
|
};
|
||||||
|
use crate::settings::LinterSettings;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for calls to `pickle` functions or modules that wrap them.
|
/// Checks for calls to `pickle` functions or modules that wrap them.
|
||||||
@@ -1016,6 +1021,25 @@ fn suspicious_function(
|
|||||||
|| has_prefix(chars.skip_while(|c| c.is_whitespace()), "https://")
|
|| has_prefix(chars.skip_while(|c| c.is_whitespace()), "https://")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves `expr` to its binding and checks if the resolved expression starts with an HTTP or HTTPS prefix.
|
||||||
|
fn expression_starts_with_http_prefix(
|
||||||
|
expr: &Expr,
|
||||||
|
semantic: &SemanticModel,
|
||||||
|
settings: &LinterSettings,
|
||||||
|
) -> bool {
|
||||||
|
let resolved_expression = if is_s310_resolve_string_literal_bindings_enabled(settings)
|
||||||
|
&& let Some(name_expr) = expr.as_name_expr()
|
||||||
|
&& let Some(binding_id) = semantic.only_binding(name_expr)
|
||||||
|
&& let Some(value) = find_binding_value(semantic.binding(binding_id), semantic)
|
||||||
|
{
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
expr
|
||||||
|
};
|
||||||
|
|
||||||
|
leading_chars(resolved_expression).is_some_and(has_http_prefix)
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the leading characters for an expression, if it's a string literal, f-string, or
|
/// Return the leading characters for an expression, if it's a string literal, f-string, or
|
||||||
/// string concatenation.
|
/// string concatenation.
|
||||||
fn leading_chars(expr: &Expr) -> Option<impl Iterator<Item = char> + Clone + '_> {
|
fn leading_chars(expr: &Expr) -> Option<impl Iterator<Item = char> + Clone + '_> {
|
||||||
@@ -1139,17 +1163,19 @@ fn suspicious_function(
|
|||||||
// URLOpen (`Request`)
|
// URLOpen (`Request`)
|
||||||
["urllib", "request", "Request"] | ["six", "moves", "urllib", "request", "Request"] => {
|
["urllib", "request", "Request"] | ["six", "moves", "urllib", "request", "Request"] => {
|
||||||
if let Some(arguments) = arguments {
|
if let Some(arguments) = arguments {
|
||||||
// If the `url` argument is a string literal or an f-string, allow `http` and `https` schemes.
|
// If the `url` argument is a string literal (including resolved bindings), allow `http` and `https` schemes.
|
||||||
if arguments.args.iter().all(|arg| !arg.is_starred_expr())
|
if arguments.args.iter().all(|arg| !arg.is_starred_expr())
|
||||||
&& arguments
|
&& arguments
|
||||||
.keywords
|
.keywords
|
||||||
.iter()
|
.iter()
|
||||||
.all(|keyword| keyword.arg.is_some())
|
.all(|keyword| keyword.arg.is_some())
|
||||||
{
|
{
|
||||||
if arguments
|
if let Some(url_expr) = arguments.find_argument_value("url", 0)
|
||||||
.find_argument_value("url", 0)
|
&& expression_starts_with_http_prefix(
|
||||||
.and_then(leading_chars)
|
url_expr,
|
||||||
.is_some_and(has_http_prefix)
|
checker.semantic(),
|
||||||
|
checker.settings(),
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1186,19 +1212,25 @@ fn suspicious_function(
|
|||||||
name.segments() == ["urllib", "request", "Request"]
|
name.segments() == ["urllib", "request", "Request"]
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
if arguments
|
if let Some(url_expr) = arguments.find_argument_value("url", 0)
|
||||||
.find_argument_value("url", 0)
|
&& expression_starts_with_http_prefix(
|
||||||
.and_then(leading_chars)
|
url_expr,
|
||||||
.is_some_and(has_http_prefix)
|
checker.semantic(),
|
||||||
|
checker.settings(),
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the `url` argument is a string literal, allow `http` and `https` schemes.
|
// If the `url` argument is a string literal (including resolved bindings), allow `http` and `https` schemes.
|
||||||
Some(expr) => {
|
Some(expr) => {
|
||||||
if leading_chars(expr).is_some_and(has_http_prefix) {
|
if expression_starts_with_http_prefix(
|
||||||
|
expr,
|
||||||
|
checker.semantic(),
|
||||||
|
checker.settings(),
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,3 +254,84 @@ S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom sch
|
|||||||
42 | urllib.request.urlopen(urllib.request.Request(url))
|
42 | urllib.request.urlopen(urllib.request.Request(url))
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
|
|
||||||
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
|
--> S310.py:51:1
|
||||||
|
|
|
||||||
|
49 | # https://github.com/astral-sh/ruff/issues/21462
|
||||||
|
50 | path = "https://example.com/data.csv"
|
||||||
|
51 | urllib.request.urlretrieve(path, "data.csv")
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
52 | url = "https://example.com/api"
|
||||||
|
53 | urllib.request.Request(url)
|
||||||
|
|
|
||||||
|
|
||||||
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
|
--> S310.py:53:1
|
||||||
|
|
|
||||||
|
51 | urllib.request.urlretrieve(path, "data.csv")
|
||||||
|
52 | url = "https://example.com/api"
|
||||||
|
53 | urllib.request.Request(url)
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
54 |
|
||||||
|
55 | # Test resolved f-strings and concatenated string literals
|
||||||
|
|
|
||||||
|
|
||||||
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
|
--> S310.py:57:1
|
||||||
|
|
|
||||||
|
55 | # Test resolved f-strings and concatenated string literals
|
||||||
|
56 | fstring_url = f"https://example.com/data.csv"
|
||||||
|
57 | urllib.request.urlopen(fstring_url)
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
58 | urllib.request.Request(fstring_url)
|
||||||
|
|
|
||||||
|
|
||||||
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
|
--> S310.py:58:1
|
||||||
|
|
|
||||||
|
56 | fstring_url = f"https://example.com/data.csv"
|
||||||
|
57 | urllib.request.urlopen(fstring_url)
|
||||||
|
58 | urllib.request.Request(fstring_url)
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
59 |
|
||||||
|
60 | concatenated_url = "https://" + "example.com/data.csv"
|
||||||
|
|
|
||||||
|
|
||||||
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
|
--> S310.py:61:1
|
||||||
|
|
|
||||||
|
60 | concatenated_url = "https://" + "example.com/data.csv"
|
||||||
|
61 | urllib.request.urlopen(concatenated_url)
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
62 | urllib.request.Request(concatenated_url)
|
||||||
|
|
|
||||||
|
|
||||||
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
|
--> S310.py:62:1
|
||||||
|
|
|
||||||
|
60 | concatenated_url = "https://" + "example.com/data.csv"
|
||||||
|
61 | urllib.request.urlopen(concatenated_url)
|
||||||
|
62 | urllib.request.Request(concatenated_url)
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
63 |
|
||||||
|
64 | nested_concatenated = "http://" + "example.com" + "/data.csv"
|
||||||
|
|
|
||||||
|
|
||||||
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
|
--> S310.py:65:1
|
||||||
|
|
|
||||||
|
64 | nested_concatenated = "http://" + "example.com" + "/data.csv"
|
||||||
|
65 | urllib.request.urlopen(nested_concatenated)
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
66 | urllib.request.Request(nested_concatenated)
|
||||||
|
|
|
||||||
|
|
||||||
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
|
--> S310.py:66:1
|
||||||
|
|
|
||||||
|
64 | nested_concatenated = "http://" + "example.com" + "/data.csv"
|
||||||
|
65 | urllib.request.urlopen(nested_concatenated)
|
||||||
|
66 | urllib.request.Request(nested_concatenated)
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
---
|
---
|
||||||
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
||||||
---
|
---
|
||||||
S301 `pickle` and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue
|
--- Linter settings ---
|
||||||
--> S301.py:3:1
|
-linter.preview = disabled
|
||||||
|
|
+linter.preview = enabled
|
||||||
1 | import pickle
|
|
||||||
2 |
|
|
||||||
3 | pickle.loads()
|
|
||||||
| ^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
|
|
||||||
|
--- Summary ---
|
||||||
|
Removed: 0
|
||||||
|
Added: 2
|
||||||
|
|
||||||
|
--- Added ---
|
||||||
S301 `pickle` and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue
|
S301 `pickle` and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue
|
||||||
--> S301.py:7:5
|
--> S301.py:7:5
|
||||||
|
|
|
|
||||||
@@ -19,6 +19,7 @@ S301 `pickle` and modules that wrap it can be unsafe when used to deserialize un
|
|||||||
8 | foo = pickle.load
|
8 | foo = pickle.load
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S301 `pickle` and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue
|
S301 `pickle` and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue
|
||||||
--> S301.py:8:7
|
--> S301.py:8:7
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -1,24 +1,15 @@
|
|||||||
---
|
---
|
||||||
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
||||||
---
|
---
|
||||||
S307 Use of possibly insecure function; consider using `ast.literal_eval`
|
--- Linter settings ---
|
||||||
--> S307.py:3:7
|
-linter.preview = disabled
|
||||||
|
|
+linter.preview = enabled
|
||||||
1 | import os
|
|
||||||
2 |
|
|
||||||
3 | print(eval("1+1")) # S307
|
|
||||||
| ^^^^^^^^^^^
|
|
||||||
4 | print(eval("os.getcwd()")) # S307
|
|
||||||
|
|
|
||||||
|
|
||||||
S307 Use of possibly insecure function; consider using `ast.literal_eval`
|
--- Summary ---
|
||||||
--> S307.py:4:7
|
Removed: 0
|
||||||
|
|
Added: 2
|
||||||
3 | print(eval("1+1")) # S307
|
|
||||||
4 | print(eval("os.getcwd()")) # S307
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
|
|
||||||
|
--- Added ---
|
||||||
S307 Use of possibly insecure function; consider using `ast.literal_eval`
|
S307 Use of possibly insecure function; consider using `ast.literal_eval`
|
||||||
--> S307.py:16:5
|
--> S307.py:16:5
|
||||||
|
|
|
|
||||||
@@ -28,6 +19,7 @@ S307 Use of possibly insecure function; consider using `ast.literal_eval`
|
|||||||
17 | foo = eval
|
17 | foo = eval
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S307 Use of possibly insecure function; consider using `ast.literal_eval`
|
S307 Use of possibly insecure function; consider using `ast.literal_eval`
|
||||||
--> S307.py:17:7
|
--> S307.py:17:7
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -1,60 +1,37 @@
|
|||||||
---
|
---
|
||||||
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
||||||
---
|
---
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
--- Linter settings ---
|
||||||
--> S308.py:6:5
|
-linter.preview = disabled
|
||||||
|
|
+linter.preview = enabled
|
||||||
4 | def bad_func():
|
|
||||||
5 | inject = "harmful_input"
|
|
||||||
6 | mark_safe(inject)
|
|
||||||
| ^^^^^^^^^^^^^^^^^
|
|
||||||
7 | mark_safe("I will add" + inject + "to my string")
|
|
||||||
8 | mark_safe("I will add %s to my string" % inject)
|
|
||||||
|
|
|
||||||
|
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
--- Summary ---
|
||||||
--> S308.py:7:5
|
Removed: 2
|
||||||
|
|
Added: 4
|
||||||
5 | inject = "harmful_input"
|
|
||||||
6 | mark_safe(inject)
|
|
||||||
7 | mark_safe("I will add" + inject + "to my string")
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
8 | mark_safe("I will add %s to my string" % inject)
|
|
||||||
9 | mark_safe("I will add {} to my string".format(inject))
|
|
||||||
|
|
|
||||||
|
|
||||||
|
--- Removed ---
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
||||||
--> S308.py:8:5
|
--> S308.py:16:1
|
||||||
|
|
|
|
||||||
6 | mark_safe(inject)
|
16 | @mark_safe
|
||||||
7 | mark_safe("I will add" + inject + "to my string")
|
| ^^^^^^^^^^
|
||||||
8 | mark_safe("I will add %s to my string" % inject)
|
17 | def some_func():
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
18 | return '<script>alert("evil!")</script>'
|
||||||
9 | mark_safe("I will add {} to my string".format(inject))
|
|
||||||
10 | mark_safe(f"I will add {inject} to my string")
|
|
||||||
|
|
|
|
||||||
|
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
|
||||||
--> S308.py:9:5
|
|
||||||
|
|
|
||||||
7 | mark_safe("I will add" + inject + "to my string")
|
|
||||||
8 | mark_safe("I will add %s to my string" % inject)
|
|
||||||
9 | mark_safe("I will add {} to my string".format(inject))
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
10 | mark_safe(f"I will add {inject} to my string")
|
|
||||||
|
|
|
||||||
|
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
||||||
--> S308.py:10:5
|
--> S308.py:36:1
|
||||||
|
|
|
|
||||||
8 | mark_safe("I will add %s to my string" % inject)
|
36 | @mark_safe
|
||||||
9 | mark_safe("I will add {} to my string".format(inject))
|
| ^^^^^^^^^^
|
||||||
10 | mark_safe(f"I will add {inject} to my string")
|
37 | def some_func():
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
38 | return '<script>alert("evil!")</script>'
|
||||||
11 |
|
|
||||||
12 | def good_func():
|
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- Added ---
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
||||||
--> S308.py:16:2
|
--> S308.py:16:2
|
||||||
|
|
|
|
||||||
@@ -64,59 +41,6 @@ S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
|||||||
18 | return '<script>alert("evil!")</script>'
|
18 | return '<script>alert("evil!")</script>'
|
||||||
|
|
|
|
||||||
|
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
|
||||||
--> S308.py:26:5
|
|
||||||
|
|
|
||||||
24 | def bad_func():
|
|
||||||
25 | inject = "harmful_input"
|
|
||||||
26 | mark_safe(inject)
|
|
||||||
| ^^^^^^^^^^^^^^^^^
|
|
||||||
27 | mark_safe("I will add" + inject + "to my string")
|
|
||||||
28 | mark_safe("I will add %s to my string" % inject)
|
|
||||||
|
|
|
||||||
|
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
|
||||||
--> S308.py:27:5
|
|
||||||
|
|
|
||||||
25 | inject = "harmful_input"
|
|
||||||
26 | mark_safe(inject)
|
|
||||||
27 | mark_safe("I will add" + inject + "to my string")
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
28 | mark_safe("I will add %s to my string" % inject)
|
|
||||||
29 | mark_safe("I will add {} to my string".format(inject))
|
|
||||||
|
|
|
||||||
|
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
|
||||||
--> S308.py:28:5
|
|
||||||
|
|
|
||||||
26 | mark_safe(inject)
|
|
||||||
27 | mark_safe("I will add" + inject + "to my string")
|
|
||||||
28 | mark_safe("I will add %s to my string" % inject)
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
29 | mark_safe("I will add {} to my string".format(inject))
|
|
||||||
30 | mark_safe(f"I will add {inject} to my string")
|
|
||||||
|
|
|
||||||
|
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
|
||||||
--> S308.py:29:5
|
|
||||||
|
|
|
||||||
27 | mark_safe("I will add" + inject + "to my string")
|
|
||||||
28 | mark_safe("I will add %s to my string" % inject)
|
|
||||||
29 | mark_safe("I will add {} to my string".format(inject))
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
30 | mark_safe(f"I will add {inject} to my string")
|
|
||||||
|
|
|
||||||
|
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
|
||||||
--> S308.py:30:5
|
|
||||||
|
|
|
||||||
28 | mark_safe("I will add %s to my string" % inject)
|
|
||||||
29 | mark_safe("I will add {} to my string".format(inject))
|
|
||||||
30 | mark_safe(f"I will add {inject} to my string")
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
31 |
|
|
||||||
32 | def good_func():
|
|
||||||
|
|
|
||||||
|
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
||||||
--> S308.py:36:2
|
--> S308.py:36:2
|
||||||
@@ -127,6 +51,7 @@ S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
|||||||
38 | return '<script>alert("evil!")</script>'
|
38 | return '<script>alert("evil!")</script>'
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
||||||
--> S308.py:42:5
|
--> S308.py:42:5
|
||||||
|
|
|
|
||||||
@@ -136,6 +61,7 @@ S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
|||||||
43 | foo = mark_safe
|
43 | foo = mark_safe
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
|
||||||
--> S308.py:43:7
|
--> S308.py:43:7
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -1,260 +1,106 @@
|
|||||||
---
|
---
|
||||||
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
||||||
---
|
---
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
--- Linter settings ---
|
||||||
--> S310.py:6:1
|
-linter.preview = disabled
|
||||||
|
|
+linter.preview = enabled
|
||||||
4 | urllib.request.urlopen(url=f'http://www.google.com')
|
|
||||||
5 | urllib.request.urlopen(url='http://' + 'www' + '.google.com')
|
|
||||||
6 | urllib.request.urlopen(url='http://www.google.com', **kwargs)
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
7 | urllib.request.urlopen(url=f'http://www.google.com', **kwargs)
|
|
||||||
8 | urllib.request.urlopen('http://www.google.com')
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
--- Summary ---
|
||||||
--> S310.py:7:1
|
Removed: 8
|
||||||
|
|
Added: 2
|
||||||
5 | urllib.request.urlopen(url='http://' + 'www' + '.google.com')
|
|
||||||
6 | urllib.request.urlopen(url='http://www.google.com', **kwargs)
|
|
||||||
7 | urllib.request.urlopen(url=f'http://www.google.com', **kwargs)
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
8 | urllib.request.urlopen('http://www.google.com')
|
|
||||||
9 | urllib.request.urlopen(f'http://www.google.com')
|
|
||||||
|
|
|
||||||
|
|
||||||
|
--- Removed ---
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
--> S310.py:10:1
|
--> S310.py:51:1
|
||||||
|
|
|
|
||||||
8 | urllib.request.urlopen('http://www.google.com')
|
49 | # https://github.com/astral-sh/ruff/issues/21462
|
||||||
9 | urllib.request.urlopen(f'http://www.google.com')
|
50 | path = "https://example.com/data.csv"
|
||||||
10 | urllib.request.urlopen('file:///foo/bar/baz')
|
51 | urllib.request.urlretrieve(path, "data.csv")
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
11 | urllib.request.urlopen(url)
|
52 | url = "https://example.com/api"
|
||||||
|
53 | urllib.request.Request(url)
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
--> S310.py:11:1
|
--> S310.py:53:1
|
||||||
|
|
|
|
||||||
9 | urllib.request.urlopen(f'http://www.google.com')
|
51 | urllib.request.urlretrieve(path, "data.csv")
|
||||||
10 | urllib.request.urlopen('file:///foo/bar/baz')
|
52 | url = "https://example.com/api"
|
||||||
11 | urllib.request.urlopen(url)
|
53 | urllib.request.Request(url)
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
12 |
|
54 |
|
||||||
13 | urllib.request.Request(url='http://www.google.com')
|
55 | # Test resolved f-strings and concatenated string literals
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
--> S310.py:16:1
|
--> S310.py:57:1
|
||||||
|
|
|
|
||||||
14 | urllib.request.Request(url=f'http://www.google.com')
|
55 | # Test resolved f-strings and concatenated string literals
|
||||||
15 | urllib.request.Request(url='http://' + 'www' + '.google.com')
|
56 | fstring_url = f"https://example.com/data.csv"
|
||||||
16 | urllib.request.Request(url='http://www.google.com', **kwargs)
|
57 | urllib.request.urlopen(fstring_url)
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
17 | urllib.request.Request(url=f'http://www.google.com', **kwargs)
|
58 | urllib.request.Request(fstring_url)
|
||||||
18 | urllib.request.Request('http://www.google.com')
|
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
--> S310.py:17:1
|
--> S310.py:58:1
|
||||||
|
|
|
|
||||||
15 | urllib.request.Request(url='http://' + 'www' + '.google.com')
|
56 | fstring_url = f"https://example.com/data.csv"
|
||||||
16 | urllib.request.Request(url='http://www.google.com', **kwargs)
|
57 | urllib.request.urlopen(fstring_url)
|
||||||
17 | urllib.request.Request(url=f'http://www.google.com', **kwargs)
|
58 | urllib.request.Request(fstring_url)
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
18 | urllib.request.Request('http://www.google.com')
|
59 |
|
||||||
19 | urllib.request.Request(f'http://www.google.com')
|
60 | concatenated_url = "https://" + "example.com/data.csv"
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
--> S310.py:20:1
|
--> S310.py:61:1
|
||||||
|
|
|
|
||||||
18 | urllib.request.Request('http://www.google.com')
|
60 | concatenated_url = "https://" + "example.com/data.csv"
|
||||||
19 | urllib.request.Request(f'http://www.google.com')
|
61 | urllib.request.urlopen(concatenated_url)
|
||||||
20 | urllib.request.Request('file:///foo/bar/baz')
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
62 | urllib.request.Request(concatenated_url)
|
||||||
21 | urllib.request.Request(url)
|
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
--> S310.py:21:1
|
--> S310.py:62:1
|
||||||
|
|
|
|
||||||
19 | urllib.request.Request(f'http://www.google.com')
|
60 | concatenated_url = "https://" + "example.com/data.csv"
|
||||||
20 | urllib.request.Request('file:///foo/bar/baz')
|
61 | urllib.request.urlopen(concatenated_url)
|
||||||
21 | urllib.request.Request(url)
|
62 | urllib.request.Request(concatenated_url)
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
22 |
|
63 |
|
||||||
23 | urllib.request.URLopener().open(fullurl='http://www.google.com')
|
64 | nested_concatenated = "http://" + "example.com" + "/data.csv"
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
--> S310.py:23:1
|
--> S310.py:65:1
|
||||||
|
|
|
|
||||||
21 | urllib.request.Request(url)
|
64 | nested_concatenated = "http://" + "example.com" + "/data.csv"
|
||||||
22 |
|
65 | urllib.request.urlopen(nested_concatenated)
|
||||||
23 | urllib.request.URLopener().open(fullurl='http://www.google.com')
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
66 | urllib.request.Request(nested_concatenated)
|
||||||
24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com')
|
|
||||||
25 | urllib.request.URLopener().open(fullurl='http://' + 'www' + '.google.com')
|
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
--> S310.py:24:1
|
--> S310.py:66:1
|
||||||
|
|
|
|
||||||
23 | urllib.request.URLopener().open(fullurl='http://www.google.com')
|
64 | nested_concatenated = "http://" + "example.com" + "/data.csv"
|
||||||
24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com')
|
65 | urllib.request.urlopen(nested_concatenated)
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
66 | urllib.request.Request(nested_concatenated)
|
||||||
25 | urllib.request.URLopener().open(fullurl='http://' + 'www' + '.google.com')
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
26 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs)
|
|
||||||
|
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:25:1
|
|
||||||
|
|
|
||||||
23 | urllib.request.URLopener().open(fullurl='http://www.google.com')
|
|
||||||
24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com')
|
|
||||||
25 | urllib.request.URLopener().open(fullurl='http://' + 'www' + '.google.com')
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
26 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs)
|
|
||||||
27 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs)
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:26:1
|
|
||||||
|
|
|
||||||
24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com')
|
|
||||||
25 | urllib.request.URLopener().open(fullurl='http://' + 'www' + '.google.com')
|
|
||||||
26 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs)
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
27 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs)
|
|
||||||
28 | urllib.request.URLopener().open('http://www.google.com')
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:27:1
|
|
||||||
|
|
|
||||||
25 | urllib.request.URLopener().open(fullurl='http://' + 'www' + '.google.com')
|
|
||||||
26 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs)
|
|
||||||
27 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs)
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
28 | urllib.request.URLopener().open('http://www.google.com')
|
|
||||||
29 | urllib.request.URLopener().open(f'http://www.google.com')
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:28:1
|
|
||||||
|
|
|
||||||
26 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs)
|
|
||||||
27 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs)
|
|
||||||
28 | urllib.request.URLopener().open('http://www.google.com')
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
29 | urllib.request.URLopener().open(f'http://www.google.com')
|
|
||||||
30 | urllib.request.URLopener().open('http://' + 'www' + '.google.com')
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:29:1
|
|
||||||
|
|
|
||||||
27 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs)
|
|
||||||
28 | urllib.request.URLopener().open('http://www.google.com')
|
|
||||||
29 | urllib.request.URLopener().open(f'http://www.google.com')
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
30 | urllib.request.URLopener().open('http://' + 'www' + '.google.com')
|
|
||||||
31 | urllib.request.URLopener().open('file:///foo/bar/baz')
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:30:1
|
|
||||||
|
|
|
||||||
28 | urllib.request.URLopener().open('http://www.google.com')
|
|
||||||
29 | urllib.request.URLopener().open(f'http://www.google.com')
|
|
||||||
30 | urllib.request.URLopener().open('http://' + 'www' + '.google.com')
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
31 | urllib.request.URLopener().open('file:///foo/bar/baz')
|
|
||||||
32 | urllib.request.URLopener().open(url)
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:31:1
|
|
||||||
|
|
|
||||||
29 | urllib.request.URLopener().open(f'http://www.google.com')
|
|
||||||
30 | urllib.request.URLopener().open('http://' + 'www' + '.google.com')
|
|
||||||
31 | urllib.request.URLopener().open('file:///foo/bar/baz')
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
32 | urllib.request.URLopener().open(url)
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:32:1
|
|
||||||
|
|
|
||||||
30 | urllib.request.URLopener().open('http://' + 'www' + '.google.com')
|
|
||||||
31 | urllib.request.URLopener().open('file:///foo/bar/baz')
|
|
||||||
32 | urllib.request.URLopener().open(url)
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
33 |
|
|
||||||
34 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'))
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:37:1
|
|
||||||
|
|
|
||||||
35 | urllib.request.urlopen(url=urllib.request.Request(f'http://www.google.com'))
|
|
||||||
36 | urllib.request.urlopen(url=urllib.request.Request('http://' + 'www' + '.google.com'))
|
|
||||||
37 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'), **kwargs)
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
38 | urllib.request.urlopen(url=urllib.request.Request(f'http://www.google.com'), **kwargs)
|
|
||||||
39 | urllib.request.urlopen(urllib.request.Request('http://www.google.com'))
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:38:1
|
|
||||||
|
|
|
||||||
36 | urllib.request.urlopen(url=urllib.request.Request('http://' + 'www' + '.google.com'))
|
|
||||||
37 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'), **kwargs)
|
|
||||||
38 | urllib.request.urlopen(url=urllib.request.Request(f'http://www.google.com'), **kwargs)
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
39 | urllib.request.urlopen(urllib.request.Request('http://www.google.com'))
|
|
||||||
40 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com'))
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:41:1
|
|
||||||
|
|
|
||||||
39 | urllib.request.urlopen(urllib.request.Request('http://www.google.com'))
|
|
||||||
40 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com'))
|
|
||||||
41 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz'))
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
42 | urllib.request.urlopen(urllib.request.Request(url))
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:41:24
|
|
||||||
|
|
|
||||||
39 | urllib.request.urlopen(urllib.request.Request('http://www.google.com'))
|
|
||||||
40 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com'))
|
|
||||||
41 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz'))
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
42 | urllib.request.urlopen(urllib.request.Request(url))
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:42:1
|
|
||||||
|
|
|
||||||
40 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com'))
|
|
||||||
41 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz'))
|
|
||||||
42 | urllib.request.urlopen(urllib.request.Request(url))
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
|
||||||
--> S310.py:42:24
|
|
||||||
|
|
|
||||||
40 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com'))
|
|
||||||
41 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz'))
|
|
||||||
42 | urllib.request.urlopen(urllib.request.Request(url))
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
|
|
||||||
|
--- Added ---
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
--> S310.py:46:5
|
--> S310.py:46:5
|
||||||
|
|
|
|
||||||
@@ -264,6 +110,7 @@ S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom sch
|
|||||||
47 | foo = urllib.request.urlopen
|
47 | foo = urllib.request.urlopen
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||||
--> S310.py:47:7
|
--> S310.py:47:7
|
||||||
|
|
|
|
||||||
@@ -271,4 +118,6 @@ S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom sch
|
|||||||
46 | map(urllib.request.urlopen, [])
|
46 | map(urllib.request.urlopen, [])
|
||||||
47 | foo = urllib.request.urlopen
|
47 | foo = urllib.request.urlopen
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
48 |
|
||||||
|
49 | # https://github.com/astral-sh/ruff/issues/21462
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -1,103 +1,15 @@
|
|||||||
---
|
---
|
||||||
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
||||||
---
|
---
|
||||||
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
--- Linter settings ---
|
||||||
--> S311.py:10:1
|
-linter.preview = disabled
|
||||||
|
|
+linter.preview = enabled
|
||||||
9 | # Errors
|
|
||||||
10 | random.Random()
|
|
||||||
| ^^^^^^^^^^^^^^^
|
|
||||||
11 | random.random()
|
|
||||||
12 | random.randrange()
|
|
||||||
|
|
|
||||||
|
|
||||||
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
--- Summary ---
|
||||||
--> S311.py:11:1
|
Removed: 0
|
||||||
|
|
Added: 2
|
||||||
9 | # Errors
|
|
||||||
10 | random.Random()
|
|
||||||
11 | random.random()
|
|
||||||
| ^^^^^^^^^^^^^^^
|
|
||||||
12 | random.randrange()
|
|
||||||
13 | random.randint()
|
|
||||||
|
|
|
||||||
|
|
||||||
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
|
||||||
--> S311.py:12:1
|
|
||||||
|
|
|
||||||
10 | random.Random()
|
|
||||||
11 | random.random()
|
|
||||||
12 | random.randrange()
|
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
|
||||||
13 | random.randint()
|
|
||||||
14 | random.choice()
|
|
||||||
|
|
|
||||||
|
|
||||||
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
|
||||||
--> S311.py:13:1
|
|
||||||
|
|
|
||||||
11 | random.random()
|
|
||||||
12 | random.randrange()
|
|
||||||
13 | random.randint()
|
|
||||||
| ^^^^^^^^^^^^^^^^
|
|
||||||
14 | random.choice()
|
|
||||||
15 | random.choices()
|
|
||||||
|
|
|
||||||
|
|
||||||
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
|
||||||
--> S311.py:14:1
|
|
||||||
|
|
|
||||||
12 | random.randrange()
|
|
||||||
13 | random.randint()
|
|
||||||
14 | random.choice()
|
|
||||||
| ^^^^^^^^^^^^^^^
|
|
||||||
15 | random.choices()
|
|
||||||
16 | random.uniform()
|
|
||||||
|
|
|
||||||
|
|
||||||
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
|
||||||
--> S311.py:15:1
|
|
||||||
|
|
|
||||||
13 | random.randint()
|
|
||||||
14 | random.choice()
|
|
||||||
15 | random.choices()
|
|
||||||
| ^^^^^^^^^^^^^^^^
|
|
||||||
16 | random.uniform()
|
|
||||||
17 | random.triangular()
|
|
||||||
|
|
|
||||||
|
|
||||||
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
|
||||||
--> S311.py:16:1
|
|
||||||
|
|
|
||||||
14 | random.choice()
|
|
||||||
15 | random.choices()
|
|
||||||
16 | random.uniform()
|
|
||||||
| ^^^^^^^^^^^^^^^^
|
|
||||||
17 | random.triangular()
|
|
||||||
18 | random.randbytes()
|
|
||||||
|
|
|
||||||
|
|
||||||
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
|
||||||
--> S311.py:17:1
|
|
||||||
|
|
|
||||||
15 | random.choices()
|
|
||||||
16 | random.uniform()
|
|
||||||
17 | random.triangular()
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^
|
|
||||||
18 | random.randbytes()
|
|
||||||
|
|
|
||||||
|
|
||||||
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
|
||||||
--> S311.py:18:1
|
|
||||||
|
|
|
||||||
16 | random.uniform()
|
|
||||||
17 | random.triangular()
|
|
||||||
18 | random.randbytes()
|
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
|
||||||
19 |
|
|
||||||
20 | # Unrelated
|
|
||||||
|
|
|
||||||
|
|
||||||
|
--- Added ---
|
||||||
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
||||||
--> S311.py:26:5
|
--> S311.py:26:5
|
||||||
|
|
|
|
||||||
@@ -107,6 +19,7 @@ S311 Standard pseudo-random generators are not suitable for cryptographic purpos
|
|||||||
27 | foo = random.randrange
|
27 | foo = random.randrange
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
S311 Standard pseudo-random generators are not suitable for cryptographic purposes
|
||||||
--> S311.py:27:7
|
--> S311.py:27:7
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
---
|
---
|
||||||
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
||||||
---
|
---
|
||||||
S312 Telnet is considered insecure. Use SSH or some other encrypted protocol.
|
--- Linter settings ---
|
||||||
--> S312.py:3:1
|
-linter.preview = disabled
|
||||||
|
|
+linter.preview = enabled
|
||||||
1 | from telnetlib import Telnet
|
|
||||||
2 |
|
|
||||||
3 | Telnet("localhost", 23)
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
|
|
||||||
|
--- Summary ---
|
||||||
|
Removed: 0
|
||||||
|
Added: 3
|
||||||
|
|
||||||
|
--- Added ---
|
||||||
S312 Telnet is considered insecure. Use SSH or some other encrypted protocol.
|
S312 Telnet is considered insecure. Use SSH or some other encrypted protocol.
|
||||||
--> S312.py:7:5
|
--> S312.py:7:5
|
||||||
|
|
|
|
||||||
@@ -19,6 +19,7 @@ S312 Telnet is considered insecure. Use SSH or some other encrypted protocol.
|
|||||||
8 | foo = Telnet
|
8 | foo = Telnet
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S312 Telnet is considered insecure. Use SSH or some other encrypted protocol.
|
S312 Telnet is considered insecure. Use SSH or some other encrypted protocol.
|
||||||
--> S312.py:8:7
|
--> S312.py:8:7
|
||||||
|
|
|
|
||||||
@@ -30,6 +31,7 @@ S312 Telnet is considered insecure. Use SSH or some other encrypted protocol.
|
|||||||
10 | import telnetlib
|
10 | import telnetlib
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S312 Telnet is considered insecure. Use SSH or some other encrypted protocol.
|
S312 Telnet is considered insecure. Use SSH or some other encrypted protocol.
|
||||||
--> S312.py:11:5
|
--> S312.py:11:5
|
||||||
|
|
|
|
||||||
@@ -39,13 +41,3 @@ S312 Telnet is considered insecure. Use SSH or some other encrypted protocol.
|
|||||||
12 |
|
12 |
|
||||||
13 | from typing import Annotated
|
13 | from typing import Annotated
|
||||||
|
|
|
|
||||||
|
|
||||||
S312 Telnet is considered insecure. Use SSH or some other encrypted protocol.
|
|
||||||
--> S312.py:14:24
|
|
||||||
|
|
|
||||||
13 | from typing import Annotated
|
|
||||||
14 | foo: Annotated[Telnet, telnetlib.Telnet()]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
|
||||||
15 |
|
|
||||||
16 | def _() -> Telnet: ...
|
|
||||||
|
|
|
||||||
|
|||||||
@@ -1,26 +1,15 @@
|
|||||||
---
|
---
|
||||||
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
||||||
---
|
---
|
||||||
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
--- Linter settings ---
|
||||||
--> S508.py:3:25
|
-linter.preview = disabled
|
||||||
|
|
+linter.preview = enabled
|
||||||
1 | from pysnmp.hlapi import CommunityData
|
|
||||||
2 |
|
|
||||||
3 | CommunityData("public", mpModel=0) # S508
|
|
||||||
| ^^^^^^^^^
|
|
||||||
4 | CommunityData("public", mpModel=1) # S508
|
|
||||||
|
|
|
||||||
|
|
||||||
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
--- Summary ---
|
||||||
--> S508.py:4:25
|
Removed: 0
|
||||||
|
|
Added: 8
|
||||||
3 | CommunityData("public", mpModel=0) # S508
|
|
||||||
4 | CommunityData("public", mpModel=1) # S508
|
|
||||||
| ^^^^^^^^^
|
|
||||||
5 |
|
|
||||||
6 | CommunityData("public", mpModel=2) # OK
|
|
||||||
|
|
|
||||||
|
|
||||||
|
--- Added ---
|
||||||
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
||||||
--> S508.py:18:46
|
--> S508.py:18:46
|
||||||
|
|
|
|
||||||
@@ -32,6 +21,7 @@ S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
|||||||
20 | pysnmp.hlapi.v1arch.asyncio.CommunityData("public", mpModel=0) # S508
|
20 | pysnmp.hlapi.v1arch.asyncio.CommunityData("public", mpModel=0) # S508
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
||||||
--> S508.py:19:58
|
--> S508.py:19:58
|
||||||
|
|
|
|
||||||
@@ -42,6 +32,7 @@ S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
|||||||
21 | pysnmp.hlapi.v1arch.CommunityData("public", mpModel=0) # S508
|
21 | pysnmp.hlapi.v1arch.CommunityData("public", mpModel=0) # S508
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
||||||
--> S508.py:20:53
|
--> S508.py:20:53
|
||||||
|
|
|
|
||||||
@@ -53,6 +44,7 @@ S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
|||||||
22 | pysnmp.hlapi.v3arch.asyncio.auth.CommunityData("public", mpModel=0) # S508
|
22 | pysnmp.hlapi.v3arch.asyncio.auth.CommunityData("public", mpModel=0) # S508
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
||||||
--> S508.py:21:45
|
--> S508.py:21:45
|
||||||
|
|
|
|
||||||
@@ -64,6 +56,7 @@ S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
|||||||
23 | pysnmp.hlapi.v3arch.asyncio.CommunityData("public", mpModel=0) # S508
|
23 | pysnmp.hlapi.v3arch.asyncio.CommunityData("public", mpModel=0) # S508
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
||||||
--> S508.py:22:58
|
--> S508.py:22:58
|
||||||
|
|
|
|
||||||
@@ -75,6 +68,7 @@ S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
|||||||
24 | pysnmp.hlapi.v3arch.CommunityData("public", mpModel=0) # S508
|
24 | pysnmp.hlapi.v3arch.CommunityData("public", mpModel=0) # S508
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
||||||
--> S508.py:23:53
|
--> S508.py:23:53
|
||||||
|
|
|
|
||||||
@@ -86,6 +80,7 @@ S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
|||||||
25 | pysnmp.hlapi.auth.CommunityData("public", mpModel=0) # S508
|
25 | pysnmp.hlapi.auth.CommunityData("public", mpModel=0) # S508
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
||||||
--> S508.py:24:45
|
--> S508.py:24:45
|
||||||
|
|
|
|
||||||
@@ -96,6 +91,7 @@ S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
|||||||
25 | pysnmp.hlapi.auth.CommunityData("public", mpModel=0) # S508
|
25 | pysnmp.hlapi.auth.CommunityData("public", mpModel=0) # S508
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
|
||||||
--> S508.py:25:43
|
--> S508.py:25:43
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -1,24 +1,15 @@
|
|||||||
---
|
---
|
||||||
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
||||||
---
|
---
|
||||||
S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure.
|
--- Linter settings ---
|
||||||
--> S509.py:4:12
|
-linter.preview = disabled
|
||||||
|
|
+linter.preview = enabled
|
||||||
4 | insecure = UsmUserData("securityName") # S509
|
|
||||||
| ^^^^^^^^^^^
|
|
||||||
5 | auth_no_priv = UsmUserData("securityName", "authName") # S509
|
|
||||||
|
|
|
||||||
|
|
||||||
S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure.
|
--- Summary ---
|
||||||
--> S509.py:5:16
|
Removed: 0
|
||||||
|
|
Added: 4
|
||||||
4 | insecure = UsmUserData("securityName") # S509
|
|
||||||
5 | auth_no_priv = UsmUserData("securityName", "authName") # S509
|
|
||||||
| ^^^^^^^^^^^
|
|
||||||
6 |
|
|
||||||
7 | less_insecure = UsmUserData("securityName", "authName", "privName") # OK
|
|
||||||
|
|
|
||||||
|
|
||||||
|
--- Added ---
|
||||||
S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure.
|
S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure.
|
||||||
--> S509.py:15:1
|
--> S509.py:15:1
|
||||||
|
|
|
|
||||||
@@ -30,6 +21,7 @@ S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv`
|
|||||||
17 | pysnmp.hlapi.v3arch.asyncio.auth.UsmUserData("user") # S509
|
17 | pysnmp.hlapi.v3arch.asyncio.auth.UsmUserData("user") # S509
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure.
|
S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure.
|
||||||
--> S509.py:16:1
|
--> S509.py:16:1
|
||||||
|
|
|
|
||||||
@@ -40,6 +32,7 @@ S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv`
|
|||||||
18 | pysnmp.hlapi.auth.UsmUserData("user") # S509
|
18 | pysnmp.hlapi.auth.UsmUserData("user") # S509
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure.
|
S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure.
|
||||||
--> S509.py:17:1
|
--> S509.py:17:1
|
||||||
|
|
|
|
||||||
@@ -50,6 +43,7 @@ S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv`
|
|||||||
18 | pysnmp.hlapi.auth.UsmUserData("user") # S509
|
18 | pysnmp.hlapi.auth.UsmUserData("user") # S509
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure.
|
S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure.
|
||||||
--> S509.py:18:1
|
--> S509.py:18:1
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
|||||||
/// keyword-only argument, to force callers to be explicit when providing
|
/// keyword-only argument, to force callers to be explicit when providing
|
||||||
/// the argument.
|
/// the argument.
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override],
|
||||||
|
/// since changing the signature of a subclass method that overrides a
|
||||||
|
/// superclass method may cause type checkers to complain about a violation of
|
||||||
|
/// the Liskov Substitution Principle.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// from math import ceil, floor
|
/// from math import ceil, floor
|
||||||
@@ -89,6 +94,8 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
|||||||
/// ## References
|
/// ## References
|
||||||
/// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls)
|
/// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls)
|
||||||
/// - [_How to Avoid “The Boolean Trap”_ by Adam Johnson](https://adamj.eu/tech/2021/07/10/python-type-hints-how-to-avoid-the-boolean-trap/)
|
/// - [_How to Avoid “The Boolean Trap”_ by Adam Johnson](https://adamj.eu/tech/2021/07/10/python-type-hints-how-to-avoid-the-boolean-trap/)
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.127")]
|
#[violation_metadata(stable_since = "v0.0.127")]
|
||||||
pub(crate) struct BooleanDefaultValuePositionalArgument;
|
pub(crate) struct BooleanDefaultValuePositionalArgument;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
|||||||
/// the argument.
|
/// the argument.
|
||||||
///
|
///
|
||||||
/// Dunder methods that define operators are exempt from this rule, as are
|
/// Dunder methods that define operators are exempt from this rule, as are
|
||||||
/// setters and `@override` definitions.
|
/// setters and [`@override`][override] definitions.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
@@ -93,6 +93,8 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
|||||||
/// ## References
|
/// ## References
|
||||||
/// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls)
|
/// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls)
|
||||||
/// - [_How to Avoid “The Boolean Trap”_ by Adam Johnson](https://adamj.eu/tech/2021/07/10/python-type-hints-how-to-avoid-the-boolean-trap/)
|
/// - [_How to Avoid “The Boolean Trap”_ by Adam Johnson](https://adamj.eu/tech/2021/07/10/python-type-hints-how-to-avoid-the-boolean-trap/)
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.127")]
|
#[violation_metadata(stable_since = "v0.0.127")]
|
||||||
pub(crate) struct BooleanTypeHintPositionalArgument;
|
pub(crate) struct BooleanTypeHintPositionalArgument;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::statement_visitor;
|
use ruff_python_ast::visitor::{Visitor, walk_expr, walk_stmt};
|
||||||
use ruff_python_ast::statement_visitor::StatementVisitor;
|
|
||||||
use ruff_python_ast::{self as ast, Expr, Stmt, StmtFunctionDef};
|
use ruff_python_ast::{self as ast, Expr, Stmt, StmtFunctionDef};
|
||||||
use ruff_text_size::TextRange;
|
use ruff_text_size::TextRange;
|
||||||
|
|
||||||
@@ -96,6 +95,11 @@ pub(crate) fn return_in_generator(checker: &Checker, function_def: &StmtFunction
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Async functions are flagged by the `ReturnInGenerator` semantic syntax error.
|
||||||
|
if function_def.is_async {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mut visitor = ReturnInGeneratorVisitor::default();
|
let mut visitor = ReturnInGeneratorVisitor::default();
|
||||||
visitor.visit_body(&function_def.body);
|
visitor.visit_body(&function_def.body);
|
||||||
|
|
||||||
@@ -112,15 +116,9 @@ struct ReturnInGeneratorVisitor {
|
|||||||
has_yield: bool,
|
has_yield: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StatementVisitor<'_> for ReturnInGeneratorVisitor {
|
impl Visitor<'_> for ReturnInGeneratorVisitor {
|
||||||
fn visit_stmt(&mut self, stmt: &Stmt) {
|
fn visit_stmt(&mut self, stmt: &Stmt) {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Expr(ast::StmtExpr { value, .. }) => match **value {
|
|
||||||
Expr::Yield(_) | Expr::YieldFrom(_) => {
|
|
||||||
self.has_yield = true;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
Stmt::FunctionDef(_) => {
|
Stmt::FunctionDef(_) => {
|
||||||
// Do not recurse into nested functions; they're evaluated separately.
|
// Do not recurse into nested functions; they're evaluated separately.
|
||||||
}
|
}
|
||||||
@@ -130,8 +128,19 @@ impl StatementVisitor<'_> for ReturnInGeneratorVisitor {
|
|||||||
node_index: _,
|
node_index: _,
|
||||||
}) => {
|
}) => {
|
||||||
self.return_ = Some(*range);
|
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 |
|
37 |
|
||||||
38 | yield from not_broken()
|
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]
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ use crate::rules::flake8_builtins::helpers::shadows_builtin;
|
|||||||
/// non-obvious errors, as readers may mistake the argument for the
|
/// non-obvious errors, as readers may mistake the argument for the
|
||||||
/// builtin and vice versa.
|
/// builtin and vice versa.
|
||||||
///
|
///
|
||||||
|
/// Function definitions decorated with [`@override`][override] or
|
||||||
|
/// [`@overload`][overload] are exempt from this rule by default.
|
||||||
/// Builtins can be marked as exceptions to this rule via the
|
/// Builtins can be marked as exceptions to this rule via the
|
||||||
/// [`lint.flake8-builtins.ignorelist`] configuration option.
|
/// [`lint.flake8-builtins.ignorelist`] configuration option.
|
||||||
///
|
///
|
||||||
@@ -48,6 +50,9 @@ use crate::rules::flake8_builtins::helpers::shadows_builtin;
|
|||||||
/// ## References
|
/// ## References
|
||||||
/// - [_Is it bad practice to use a built-in function name as an attribute or method identifier?_](https://stackoverflow.com/questions/9109333/is-it-bad-practice-to-use-a-built-in-function-name-as-an-attribute-or-method-ide)
|
/// - [_Is it bad practice to use a built-in function name as an attribute or method identifier?_](https://stackoverflow.com/questions/9109333/is-it-bad-practice-to-use-a-built-in-function-name-as-an-attribute-or-method-ide)
|
||||||
/// - [_Why is it a bad idea to name a variable `id` in Python?_](https://stackoverflow.com/questions/77552/id-is-a-bad-variable-name-in-python)
|
/// - [_Why is it a bad idea to name a variable `id` in Python?_](https://stackoverflow.com/questions/77552/id-is-a-bad-variable-name-in-python)
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
|
/// [overload]: https://docs.python.org/3/library/typing.html#typing.overload
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.48")]
|
#[violation_metadata(stable_since = "v0.0.48")]
|
||||||
pub(crate) struct BuiltinArgumentShadowing {
|
pub(crate) struct BuiltinArgumentShadowing {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
|
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||||
use ruff_python_index::Indexer;
|
use ruff_python_index::Indexer;
|
||||||
use ruff_python_parser::{TokenKind, Tokens};
|
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::Locator;
|
use crate::Locator;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use ruff_python_ast as ast;
|
|||||||
use ruff_python_ast::ExprGenerator;
|
use ruff_python_ast::ExprGenerator;
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
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 ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use ruff_python_ast as ast;
|
|||||||
use ruff_python_ast::ExprGenerator;
|
use ruff_python_ast::ExprGenerator;
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
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 ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
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 ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
|
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr, Operator};
|
use ruff_python_ast::{self as ast, Expr, Operator};
|
||||||
use ruff_python_trivia::is_python_whitespace;
|
use ruff_python_trivia::is_python_whitespace;
|
||||||
use ruff_source_file::LineRanges;
|
use ruff_source_file::LineRanges;
|
||||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::AlwaysFixableViolation;
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::{Edit, Fix};
|
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for string literals that are explicitly concatenated (using the
|
/// Checks for string literals that are explicitly concatenated (using the
|
||||||
@@ -36,14 +36,16 @@ use crate::{Edit, Fix};
|
|||||||
#[violation_metadata(stable_since = "v0.0.201")]
|
#[violation_metadata(stable_since = "v0.0.201")]
|
||||||
pub(crate) struct ExplicitStringConcatenation;
|
pub(crate) struct ExplicitStringConcatenation;
|
||||||
|
|
||||||
impl AlwaysFixableViolation for ExplicitStringConcatenation {
|
impl Violation for ExplicitStringConcatenation {
|
||||||
|
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||||
|
|
||||||
#[derive_message_formats]
|
#[derive_message_formats]
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
"Explicitly concatenated string should be implicitly concatenated".to_string()
|
"Explicitly concatenated string should be implicitly concatenated".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fix_title(&self) -> String {
|
fn fix_title(&self) -> Option<String> {
|
||||||
"Remove redundant '+' operator to implicitly concatenate".to_string()
|
Some("Remove redundant '+' operator to implicitly concatenate".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,9 +84,27 @@ pub(crate) fn explicit(checker: &Checker, expr: &Expr) {
|
|||||||
.locator()
|
.locator()
|
||||||
.contains_line_break(TextRange::new(left.end(), right.start()))
|
.contains_line_break(TextRange::new(left.end(), right.start()))
|
||||||
{
|
{
|
||||||
checker
|
let mut diagnostic =
|
||||||
.report_diagnostic(ExplicitStringConcatenation, expr.range())
|
checker.report_diagnostic(ExplicitStringConcatenation, expr.range());
|
||||||
.set_fix(generate_fix(checker, bin_op));
|
|
||||||
|
let is_parenthesized = |expr: &Expr| {
|
||||||
|
parenthesized_range(
|
||||||
|
expr.into(),
|
||||||
|
bin_op.into(),
|
||||||
|
checker.comment_ranges(),
|
||||||
|
checker.source(),
|
||||||
|
)
|
||||||
|
.is_some()
|
||||||
|
};
|
||||||
|
// If either `left` or `right` is parenthesized, generating
|
||||||
|
// a fix would be too involved. Just report the diagnostic.
|
||||||
|
// Currently, attempting `generate_fix` would result in
|
||||||
|
// an invalid code. See: #19757
|
||||||
|
if is_parenthesized(left) || is_parenthesized(right) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostic.set_fix(generate_fix(checker, bin_op));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use std::borrow::Cow;
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::StringFlags;
|
use ruff_python_ast::StringFlags;
|
||||||
|
use ruff_python_ast::token::{Token, TokenKind, Tokens};
|
||||||
use ruff_python_index::Indexer;
|
use ruff_python_index::Indexer;
|
||||||
use ruff_python_parser::{Token, TokenKind, Tokens};
|
|
||||||
use ruff_source_file::LineRanges;
|
use ruff_source_file::LineRanges;
|
||||||
use ruff_text_size::{Ranged, TextLen, TextRange};
|
use ruff_text_size::{Ranged, TextLen, TextRange};
|
||||||
|
|
||||||
|
|||||||
@@ -357,3 +357,33 @@ help: Remove redundant '+' operator to implicitly concatenate
|
|||||||
203 | )
|
203 | )
|
||||||
204 |
|
204 |
|
||||||
205 | # nested examples with both t and f-strings
|
205 | # nested examples with both t and f-strings
|
||||||
|
|
||||||
|
ISC003 Explicitly concatenated string should be implicitly concatenated
|
||||||
|
--> ISC.py:216:5
|
||||||
|
|
|
||||||
|
214 | # reports diagnostic - no autofix.
|
||||||
|
215 | # See https://github.com/astral-sh/ruff/issues/19757
|
||||||
|
216 | _ = "abc" + (
|
||||||
|
| _____^
|
||||||
|
217 | | "def"
|
||||||
|
218 | | "ghi"
|
||||||
|
219 | | )
|
||||||
|
| |_^
|
||||||
|
220 |
|
||||||
|
221 | _ = (
|
||||||
|
|
|
||||||
|
help: Remove redundant '+' operator to implicitly concatenate
|
||||||
|
|
||||||
|
ISC003 Explicitly concatenated string should be implicitly concatenated
|
||||||
|
--> ISC.py:221:5
|
||||||
|
|
|
||||||
|
219 | )
|
||||||
|
220 |
|
||||||
|
221 | _ = (
|
||||||
|
| _____^
|
||||||
|
222 | | "abc"
|
||||||
|
223 | | "def"
|
||||||
|
224 | | ) + "ghi"
|
||||||
|
| |_________^
|
||||||
|
|
|
||||||
|
help: Remove redundant '+' operator to implicitly concatenate
|
||||||
|
|||||||
@@ -89,3 +89,24 @@ ISC002 Implicitly concatenated string literals over multiple lines
|
|||||||
209 | | t"def"} g"
|
209 | | t"def"} g"
|
||||||
| |__________^
|
| |__________^
|
||||||
|
|
|
|
||||||
|
|
||||||
|
ISC002 Implicitly concatenated string literals over multiple lines
|
||||||
|
--> ISC.py:217:5
|
||||||
|
|
|
||||||
|
215 | # See https://github.com/astral-sh/ruff/issues/19757
|
||||||
|
216 | _ = "abc" + (
|
||||||
|
217 | / "def"
|
||||||
|
218 | | "ghi"
|
||||||
|
| |_________^
|
||||||
|
219 | )
|
||||||
|
|
|
||||||
|
|
||||||
|
ISC002 Implicitly concatenated string literals over multiple lines
|
||||||
|
--> ISC.py:222:5
|
||||||
|
|
|
||||||
|
221 | _ = (
|
||||||
|
222 | / "abc"
|
||||||
|
223 | | "def"
|
||||||
|
| |_________^
|
||||||
|
224 | ) + "ghi"
|
||||||
|
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::map_subscript;
|
|
||||||
use ruff_python_ast::whitespace::trailing_comment_start_offset;
|
use ruff_python_ast::whitespace::trailing_comment_start_offset;
|
||||||
use ruff_python_ast::{Expr, ExprStringLiteral, Stmt, StmtExpr};
|
use ruff_python_ast::{Expr, ExprStringLiteral, Stmt, StmtExpr};
|
||||||
use ruff_python_semantic::{ScopeKind, SemanticModel};
|
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
@@ -101,7 +99,7 @@ pub(crate) fn unnecessary_placeholder(checker: &Checker, body: &[Stmt]) {
|
|||||||
// Ellipses are significant in protocol methods and abstract methods.
|
// Ellipses are significant in protocol methods and abstract methods.
|
||||||
// Specifically, Pyright uses the presence of an ellipsis to indicate that
|
// Specifically, Pyright uses the presence of an ellipsis to indicate that
|
||||||
// a method is a stub, rather than a default implementation.
|
// a method is a stub, rather than a default implementation.
|
||||||
if in_protocol_or_abstract_method(checker.semantic()) {
|
if checker.semantic().in_protocol_or_abstract_method() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Placeholder::Ellipsis
|
Placeholder::Ellipsis
|
||||||
@@ -163,21 +161,3 @@ impl std::fmt::Display for Placeholder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if the [`SemanticModel`] is in a `typing.Protocol` subclass or an abstract
|
|
||||||
/// method.
|
|
||||||
fn in_protocol_or_abstract_method(semantic: &SemanticModel) -> bool {
|
|
||||||
semantic.current_scopes().any(|scope| match scope.kind {
|
|
||||||
ScopeKind::Class(class_def) => class_def
|
|
||||||
.bases()
|
|
||||||
.iter()
|
|
||||||
.any(|base| semantic.match_typing_expr(map_subscript(base), "Protocol")),
|
|
||||||
ScopeKind::Function(function_def) => {
|
|
||||||
ruff_python_semantic::analyze::visibility::is_abstract(
|
|
||||||
&function_def.decorator_list,
|
|
||||||
semantic,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
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_ast::{self as ast, Expr};
|
||||||
use ruff_python_parser::{TokenKind, Tokens};
|
|
||||||
use ruff_text_size::{Ranged, TextLen, TextSize};
|
use ruff_text_size::{Ranged, TextLen, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ use ruff_diagnostics::Applicability;
|
|||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::{is_const_false, is_const_true};
|
use ruff_python_ast::helpers::{is_const_false, is_const_true};
|
||||||
use ruff_python_ast::stmt_if::elif_else_range;
|
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::visitor::Visitor;
|
||||||
use ruff_python_ast::whitespace::indentation;
|
use ruff_python_ast::whitespace::indentation;
|
||||||
use ruff_python_ast::{self as ast, Decorator, ElifElseClause, Expr, Stmt};
|
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::SemanticModel;
|
||||||
use ruff_python_semantic::analyze::visibility::is_property;
|
use ruff_python_semantic::analyze::visibility::is_property;
|
||||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer, is_python_whitespace};
|
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
|
208 | # https://github.com/astral-sh/ruff/issues/21136
|
||||||
209 | def get_items():
|
209 | def get_items():
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
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 |
|
156 |
|
||||||
157 |
|
157 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
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
|
||||||
|
|||||||
@@ -60,6 +60,16 @@ impl Violation for UnusedFunctionArgument {
|
|||||||
/// prefixed with an underscore, or some other value that adheres to the
|
/// prefixed with an underscore, or some other value that adheres to the
|
||||||
/// [`lint.dummy-variable-rgx`] pattern.
|
/// [`lint.dummy-variable-rgx`] pattern.
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override].
|
||||||
|
/// Removing a parameter from a subclass method (or changing a parameter's
|
||||||
|
/// name) may cause type checkers to complain about a violation of the Liskov
|
||||||
|
/// Substitution Principle if it means that the method now incompatibly
|
||||||
|
/// overrides a method defined on a superclass. Explicitly decorating an
|
||||||
|
/// overriding method with `@override` signals to Ruff that the method is
|
||||||
|
/// intended to override a superclass method and that a type checker will
|
||||||
|
/// enforce that it does so; Ruff therefore knows that it should not enforce
|
||||||
|
/// rules about unused arguments on such methods.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// class Class:
|
/// class Class:
|
||||||
@@ -76,6 +86,8 @@ impl Violation for UnusedFunctionArgument {
|
|||||||
///
|
///
|
||||||
/// ## Options
|
/// ## Options
|
||||||
/// - `lint.dummy-variable-rgx`
|
/// - `lint.dummy-variable-rgx`
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.168")]
|
#[violation_metadata(stable_since = "v0.0.168")]
|
||||||
pub(crate) struct UnusedMethodArgument {
|
pub(crate) struct UnusedMethodArgument {
|
||||||
@@ -101,6 +113,16 @@ impl Violation for UnusedMethodArgument {
|
|||||||
/// prefixed with an underscore, or some other value that adheres to the
|
/// prefixed with an underscore, or some other value that adheres to the
|
||||||
/// [`lint.dummy-variable-rgx`] pattern.
|
/// [`lint.dummy-variable-rgx`] pattern.
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override].
|
||||||
|
/// Removing a parameter from a subclass method (or changing a parameter's
|
||||||
|
/// name) may cause type checkers to complain about a violation of the Liskov
|
||||||
|
/// Substitution Principle if it means that the method now incompatibly
|
||||||
|
/// overrides a method defined on a superclass. Explicitly decorating an
|
||||||
|
/// overriding method with `@override` signals to Ruff that the method is
|
||||||
|
/// intended to override a superclass method and that a type checker will
|
||||||
|
/// enforce that it does so; Ruff therefore knows that it should not enforce
|
||||||
|
/// rules about unused arguments on such methods.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// class Class:
|
/// class Class:
|
||||||
@@ -119,6 +141,8 @@ impl Violation for UnusedMethodArgument {
|
|||||||
///
|
///
|
||||||
/// ## Options
|
/// ## Options
|
||||||
/// - `lint.dummy-variable-rgx`
|
/// - `lint.dummy-variable-rgx`
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.168")]
|
#[violation_metadata(stable_since = "v0.0.168")]
|
||||||
pub(crate) struct UnusedClassMethodArgument {
|
pub(crate) struct UnusedClassMethodArgument {
|
||||||
@@ -144,6 +168,16 @@ impl Violation for UnusedClassMethodArgument {
|
|||||||
/// prefixed with an underscore, or some other value that adheres to the
|
/// prefixed with an underscore, or some other value that adheres to the
|
||||||
/// [`lint.dummy-variable-rgx`] pattern.
|
/// [`lint.dummy-variable-rgx`] pattern.
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override].
|
||||||
|
/// Removing a parameter from a subclass method (or changing a parameter's
|
||||||
|
/// name) may cause type checkers to complain about a violation of the Liskov
|
||||||
|
/// Substitution Principle if it means that the method now incompatibly
|
||||||
|
/// overrides a method defined on a superclass. Explicitly decorating an
|
||||||
|
/// overriding method with `@override` signals to Ruff that the method is
|
||||||
|
/// intended to override a superclass method, and that a type checker will
|
||||||
|
/// enforce that it does so; Ruff therefore knows that it should not enforce
|
||||||
|
/// rules about unused arguments on such methods.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// class Class:
|
/// class Class:
|
||||||
@@ -162,6 +196,8 @@ impl Violation for UnusedClassMethodArgument {
|
|||||||
///
|
///
|
||||||
/// ## Options
|
/// ## Options
|
||||||
/// - `lint.dummy-variable-rgx`
|
/// - `lint.dummy-variable-rgx`
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.168")]
|
#[violation_metadata(stable_since = "v0.0.168")]
|
||||||
pub(crate) struct UnusedStaticMethodArgument {
|
pub(crate) struct UnusedStaticMethodArgument {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ pub(crate) fn check_os_pathlib_single_arg_calls(
|
|||||||
fn_argument: &str,
|
fn_argument: &str,
|
||||||
fix_enabled: bool,
|
fix_enabled: bool,
|
||||||
violation: impl Violation,
|
violation: impl Violation,
|
||||||
applicability: Option<Applicability>,
|
applicability: Applicability,
|
||||||
) {
|
) {
|
||||||
if call.arguments.len() != 1 {
|
if call.arguments.len() != 1 {
|
||||||
return;
|
return;
|
||||||
@@ -91,18 +91,14 @@ pub(crate) fn check_os_pathlib_single_arg_calls(
|
|||||||
|
|
||||||
let edit = Edit::range_replacement(replacement, range);
|
let edit = Edit::range_replacement(replacement, range);
|
||||||
|
|
||||||
let fix = match applicability {
|
let applicability = match applicability {
|
||||||
Some(Applicability::Unsafe) => Fix::unsafe_edits(edit, [import_edit]),
|
Applicability::DisplayOnly => Applicability::DisplayOnly,
|
||||||
_ => {
|
_ if checker.comment_ranges().intersects(range) => Applicability::Unsafe,
|
||||||
let applicability = if checker.comment_ranges().intersects(range) {
|
_ => applicability,
|
||||||
Applicability::Unsafe
|
|
||||||
} else {
|
|
||||||
Applicability::Safe
|
|
||||||
};
|
|
||||||
Fix::applicable_edits(edit, [import_edit], applicability)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let fix = Fix::applicable_edits(edit, [import_edit], applicability);
|
||||||
|
|
||||||
Ok(fix)
|
Ok(fix)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -138,6 +134,7 @@ pub(crate) fn is_file_descriptor(expr: &Expr, semantic: &SemanticModel) -> bool
|
|||||||
typing::is_int(binding, semantic)
|
typing::is_int(binding, semantic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
pub(crate) fn check_os_pathlib_two_arg_calls(
|
pub(crate) fn check_os_pathlib_two_arg_calls(
|
||||||
checker: &Checker,
|
checker: &Checker,
|
||||||
call: &ExprCall,
|
call: &ExprCall,
|
||||||
@@ -146,6 +143,7 @@ pub(crate) fn check_os_pathlib_two_arg_calls(
|
|||||||
second_arg: &str,
|
second_arg: &str,
|
||||||
fix_enabled: bool,
|
fix_enabled: bool,
|
||||||
violation: impl Violation,
|
violation: impl Violation,
|
||||||
|
applicability: Applicability,
|
||||||
) {
|
) {
|
||||||
let range = call.range();
|
let range = call.range();
|
||||||
let mut diagnostic = checker.report_diagnostic(violation, call.func.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})")
|
format!("{binding}({path_code}).{attr}({second_code})")
|
||||||
};
|
};
|
||||||
|
|
||||||
let applicability = if checker.comment_ranges().intersects(range) {
|
let applicability = match applicability {
|
||||||
Applicability::Unsafe
|
Applicability::DisplayOnly => Applicability::DisplayOnly,
|
||||||
} else {
|
_ if checker.comment_ranges().intersects(range) => Applicability::Unsafe,
|
||||||
Applicability::Safe
|
_ => applicability,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Fix::applicable_edits(
|
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)
|
.find_argument_value(name, position)
|
||||||
.is_some_and(|expr| !expr.is_none_literal_expr())
|
.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_diagnostics::{Applicability, Edit, Fix};
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::ExprCall;
|
use ruff_python_ast::ExprCall;
|
||||||
use ruff_text_size::Ranged;
|
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
|
/// ## What it does
|
||||||
/// Checks for uses of `os.getcwd` and `os.getcwdb`.
|
/// Checks for uses of `os.getcwd` and `os.getcwdb`.
|
||||||
///
|
///
|
||||||
@@ -37,6 +39,8 @@ use ruff_text_size::Ranged;
|
|||||||
///
|
///
|
||||||
/// ## Fix Safety
|
/// ## Fix Safety
|
||||||
/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression.
|
/// This rule's fix is 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
|
/// ## References
|
||||||
/// - [Python documentation: `Path.cwd`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.cwd)
|
/// - [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(),
|
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
|
Applicability::Unsafe
|
||||||
} else {
|
} else {
|
||||||
Applicability::Safe
|
Applicability::Safe
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ use crate::{FixAvailability, Violation};
|
|||||||
/// behaviors is required, there's no existing `pathlib` alternative. See CPython issue
|
/// behaviors is required, there's no existing `pathlib` alternative. See CPython issue
|
||||||
/// [#69200](https://github.com/python/cpython/issues/69200).
|
/// [#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
|
/// ## References
|
||||||
/// - [Python documentation: `Path.resolve`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.resolve)
|
/// - [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)
|
/// - [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",
|
"path",
|
||||||
is_fix_os_path_abspath_enabled(checker.settings()),
|
is_fix_os_path_abspath_enabled(checker.settings()),
|
||||||
OsPathAbspath,
|
OsPathAbspath,
|
||||||
Some(Applicability::Unsafe),
|
Applicability::Unsafe,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,6 @@ pub(crate) fn os_path_basename(checker: &Checker, call: &ExprCall, segments: &[&
|
|||||||
"p",
|
"p",
|
||||||
is_fix_os_path_basename_enabled(checker.settings()),
|
is_fix_os_path_basename_enabled(checker.settings()),
|
||||||
OsPathBasename,
|
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`
|
/// As a result, code relying on the exact string returned by `os.path.dirname`
|
||||||
/// may behave differently after the fix.
|
/// 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
|
/// ## Known issues
|
||||||
/// While using `pathlib` can improve the readability and type safety of your code,
|
/// 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,
|
/// 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",
|
"p",
|
||||||
is_fix_os_path_dirname_enabled(checker.settings()),
|
is_fix_os_path_dirname_enabled(checker.settings()),
|
||||||
OsPathDirname,
|
OsPathDirname,
|
||||||
Some(Applicability::Unsafe),
|
Applicability::Unsafe,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use ruff_diagnostics::Applicability;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::ExprCall;
|
use ruff_python_ast::ExprCall;
|
||||||
|
|
||||||
@@ -72,6 +73,6 @@ pub(crate) fn os_path_exists(checker: &Checker, call: &ExprCall, segments: &[&st
|
|||||||
"path",
|
"path",
|
||||||
is_fix_os_path_exists_enabled(checker.settings()),
|
is_fix_os_path_exists_enabled(checker.settings()),
|
||||||
OsPathExists,
|
OsPathExists,
|
||||||
None,
|
Applicability::Safe,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ use crate::{FixAvailability, Violation};
|
|||||||
/// directory can't be resolved: `os.path.expanduser` returns the
|
/// directory can't be resolved: `os.path.expanduser` returns the
|
||||||
/// input unchanged, while `Path.expanduser` raises `RuntimeError`.
|
/// 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
|
/// ## References
|
||||||
/// - [Python documentation: `Path.expanduser`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.expanduser)
|
/// - [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)
|
/// - [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",
|
"path",
|
||||||
is_fix_os_path_expanduser_enabled(checker.settings()),
|
is_fix_os_path_expanduser_enabled(checker.settings()),
|
||||||
OsPathExpanduser,
|
OsPathExpanduser,
|
||||||
Some(Applicability::Unsafe),
|
Applicability::Unsafe,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use ruff_diagnostics::Applicability;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::ExprCall;
|
use ruff_python_ast::ExprCall;
|
||||||
|
|
||||||
@@ -75,6 +76,6 @@ pub(crate) fn os_path_getatime(checker: &Checker, call: &ExprCall, segments: &[&
|
|||||||
"filename",
|
"filename",
|
||||||
is_fix_os_path_getatime_enabled(checker.settings()),
|
is_fix_os_path_getatime_enabled(checker.settings()),
|
||||||
OsPathGetatime,
|
OsPathGetatime,
|
||||||
None,
|
Applicability::Safe,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use ruff_diagnostics::Applicability;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::ExprCall;
|
use ruff_python_ast::ExprCall;
|
||||||
|
|
||||||
@@ -76,6 +77,6 @@ pub(crate) fn os_path_getctime(checker: &Checker, call: &ExprCall, segments: &[&
|
|||||||
"filename",
|
"filename",
|
||||||
is_fix_os_path_getctime_enabled(checker.settings()),
|
is_fix_os_path_getctime_enabled(checker.settings()),
|
||||||
OsPathGetctime,
|
OsPathGetctime,
|
||||||
None,
|
Applicability::Safe,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use ruff_diagnostics::Applicability;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::ExprCall;
|
use ruff_python_ast::ExprCall;
|
||||||
|
|
||||||
@@ -76,6 +77,6 @@ pub(crate) fn os_path_getmtime(checker: &Checker, call: &ExprCall, segments: &[&
|
|||||||
"filename",
|
"filename",
|
||||||
is_fix_os_path_getmtime_enabled(checker.settings()),
|
is_fix_os_path_getmtime_enabled(checker.settings()),
|
||||||
OsPathGetmtime,
|
OsPathGetmtime,
|
||||||
None,
|
Applicability::Safe,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use ruff_diagnostics::Applicability;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::ExprCall;
|
use ruff_python_ast::ExprCall;
|
||||||
|
|
||||||
@@ -76,6 +77,6 @@ pub(crate) fn os_path_getsize(checker: &Checker, call: &ExprCall, segments: &[&s
|
|||||||
"filename",
|
"filename",
|
||||||
is_fix_os_path_getsize_enabled(checker.settings()),
|
is_fix_os_path_getsize_enabled(checker.settings()),
|
||||||
OsPathGetsize,
|
OsPathGetsize,
|
||||||
None,
|
Applicability::Safe,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use ruff_diagnostics::Applicability;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::ExprCall;
|
use ruff_python_ast::ExprCall;
|
||||||
|
|
||||||
@@ -71,6 +72,6 @@ pub(crate) fn os_path_isabs(checker: &Checker, call: &ExprCall, segments: &[&str
|
|||||||
"s",
|
"s",
|
||||||
is_fix_os_path_isabs_enabled(checker.settings()),
|
is_fix_os_path_isabs_enabled(checker.settings()),
|
||||||
OsPathIsabs,
|
OsPathIsabs,
|
||||||
None,
|
Applicability::Safe,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user