Compare commits
323 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b78141f6b | ||
|
|
5235977abc | ||
|
|
01d3d4bbd2 | ||
|
|
ac4a4da50e | ||
|
|
a6d269f263 | ||
|
|
f17282d615 | ||
|
|
6950c93934 | ||
|
|
ae75b303f0 | ||
|
|
20240fc3d9 | ||
|
|
f990d9dcc5 | ||
|
|
5c48414093 | ||
|
|
bcf745c5ba | ||
|
|
2f125f4019 | ||
|
|
6ab3fc60f4 | ||
|
|
222ca98a41 | ||
|
|
ec609f5c3b | ||
|
|
b9060ea2bd | ||
|
|
b56a799417 | ||
|
|
780d153ae8 | ||
|
|
7cc205b5d6 | ||
|
|
e1df2b1400 | ||
|
|
2a6d7cd71c | ||
|
|
2b5fb70482 | ||
|
|
8c048b463c | ||
|
|
19abee086b | ||
|
|
1ed5d7e437 | ||
|
|
3f032cf09d | ||
|
|
775326790e | ||
|
|
7b0fb1a3b4 | ||
|
|
c2a3e97b7f | ||
|
|
805b2eb0b7 | ||
|
|
0c7ea800af | ||
|
|
c67029ded9 | ||
|
|
a70afa7de7 | ||
|
|
d1b8fe6af2 | ||
|
|
913b9d1fcf | ||
|
|
f9e82f2578 | ||
|
|
79ae1840af | ||
|
|
8938b2d555 | ||
|
|
33434fcb9c | ||
|
|
8a3a269eef | ||
|
|
d31eb87877 | ||
|
|
72245960a1 | ||
|
|
e6b00f0c4e | ||
|
|
f952bef1ad | ||
|
|
dc223fd3ca | ||
|
|
209aaa5add | ||
|
|
ff37d7af23 | ||
|
|
c65f47d7c4 | ||
|
|
d1d06960f0 | ||
|
|
576e0c7b80 | ||
|
|
1fba98681e | ||
|
|
95e61987d1 | ||
|
|
a0721912a4 | ||
|
|
694bf7f5b8 | ||
|
|
466719247b | ||
|
|
3fa4440d87 | ||
|
|
14e06f9f8b | ||
|
|
e7a2e0f437 | ||
|
|
67b43ab72a | ||
|
|
5ae4667fd5 | ||
|
|
d8a6109b69 | ||
|
|
fcacd3cd95 | ||
|
|
42c071d302 | ||
|
|
c14896b42c | ||
|
|
935094c2ff | ||
|
|
2c41c54e0c | ||
|
|
d6daa61563 | ||
|
|
cb6788ab5f | ||
|
|
e82160a83a | ||
|
|
26b1dd0ca2 | ||
|
|
fcfd6ad129 | ||
|
|
6a0cebdf7b | ||
|
|
0a5dfcb26a | ||
|
|
3ff1f003f4 | ||
|
|
ebdc4afc33 | ||
|
|
03ee6033f9 | ||
|
|
a401989b7a | ||
|
|
4cd4b37e74 | ||
|
|
602b4b3519 | ||
|
|
c4fdbf8903 | ||
|
|
5d939222db | ||
|
|
b2498c576f | ||
|
|
c89d2f835e | ||
|
|
211d8e170d | ||
|
|
b92be59ffe | ||
|
|
b030c70dda | ||
|
|
10ba79489a | ||
|
|
ea3cbcc362 | ||
|
|
b8f45c93b4 | ||
|
|
621718784a | ||
|
|
fcbf5c3fae | ||
|
|
c68686b1de | ||
|
|
583411a29f | ||
|
|
6d94aa89e3 | ||
|
|
8d5d34c6d1 | ||
|
|
edadd7814f | ||
|
|
3180f9978a | ||
|
|
bdff4a66ac | ||
|
|
ab26f2dc9d | ||
|
|
0099f9720f | ||
|
|
d9fdcebfc1 | ||
|
|
b7038cee13 | ||
|
|
be740106e0 | ||
|
|
63d892f1e4 | ||
|
|
28aad95414 | ||
|
|
5f4bce6d2b | ||
|
|
4ea4fd1984 | ||
|
|
d4027d8b65 | ||
|
|
9bf168c0a4 | ||
|
|
59148344be | ||
|
|
0945803427 | ||
|
|
b7294b48e7 | ||
|
|
be31d71849 | ||
|
|
46c3b3af94 | ||
|
|
3d34d9298d | ||
|
|
1156c65be1 | ||
|
|
1a53996f53 | ||
|
|
4bd395a850 | ||
|
|
bb4f3dedf4 | ||
|
|
01470d9045 | ||
|
|
0b471197dc | ||
|
|
399eb84d5e | ||
|
|
35cd57d0fc | ||
|
|
2b2812c4f2 | ||
|
|
9d0ffd33ca | ||
|
|
e209b5fc5f | ||
|
|
c1286d61df | ||
|
|
6c1ff6a85f | ||
|
|
06bcb85f81 | ||
|
|
d7a4999915 | ||
|
|
d4e54cff05 | ||
|
|
e1b6f6e57e | ||
|
|
50053f60f3 | ||
|
|
a4f73ea8c7 | ||
|
|
04a95cb9ee | ||
|
|
f9b3f10456 | ||
|
|
f47a517e79 | ||
|
|
ea31229be0 | ||
|
|
0854543328 | ||
|
|
0bc3d99298 | ||
|
|
0cd453bdf0 | ||
|
|
84a5584888 | ||
|
|
6146b75dd0 | ||
|
|
236074fdde | ||
|
|
e323bb015b | ||
|
|
80fa3f2bfa | ||
|
|
1846d90bbd | ||
|
|
0106bce02f | ||
|
|
2695d0561a | ||
|
|
5f715417e0 | ||
|
|
f7c2d25205 | ||
|
|
6e096f216a | ||
|
|
d0ad4be20e | ||
|
|
6425fe8c12 | ||
|
|
68db74b3c5 | ||
|
|
9646bc7d7f | ||
|
|
5756829344 | ||
|
|
cb45b25879 | ||
|
|
0911ce4cbc | ||
|
|
51f04ee6ef | ||
|
|
dbeadd99a8 | ||
|
|
79b35fc3cc | ||
|
|
9f16ae354e | ||
|
|
9741f788c7 | ||
|
|
901060fa96 | ||
|
|
f069eb9e3d | ||
|
|
fe72bde23c | ||
|
|
1268ddca92 | ||
|
|
af433ac14d | ||
|
|
ccca11839a | ||
|
|
12e45498e8 | ||
|
|
33a7ed058f | ||
|
|
52deeb36ee | ||
|
|
0f610f2cf7 | ||
|
|
741e180e2d | ||
|
|
b6a382eeaf | ||
|
|
050350527c | ||
|
|
c9b39e31fc | ||
|
|
28a5e607b4 | ||
|
|
09c50c311c | ||
|
|
252506f8ed | ||
|
|
f4572fe40b | ||
|
|
8c9215489e | ||
|
|
dcd2bfaab7 | ||
|
|
f0e173d9fd | ||
|
|
f4f1b1d0ee | ||
|
|
edc6c4058f | ||
|
|
4233f6ec91 | ||
|
|
fcdc7bdd33 | ||
|
|
86ced3516b | ||
|
|
6943beee66 | ||
|
|
85f094f592 | ||
|
|
17d938f078 | ||
|
|
5cedf0f724 | ||
|
|
38297c08b4 | ||
|
|
30e90838d0 | ||
|
|
040fb9cef4 | ||
|
|
8961d8eb6f | ||
|
|
31bddef98f | ||
|
|
a59d252246 | ||
|
|
5b9d4f18ae | ||
|
|
c6a760e298 | ||
|
|
3644695bf2 | ||
|
|
b1d01b1950 | ||
|
|
4e84e8a8e2 | ||
|
|
a256fdb9f4 | ||
|
|
7479dfd815 | ||
|
|
ba4c0a21fa | ||
|
|
73e179ffab | ||
|
|
3cbaaa4795 | ||
|
|
2681c0e633 | ||
|
|
f3bdd2e7be | ||
|
|
652c644c2a | ||
|
|
04d273bcc7 | ||
|
|
154439728a | ||
|
|
1ddc577204 | ||
|
|
74effb40b9 | ||
|
|
6c3724ab98 | ||
|
|
3b8121379d | ||
|
|
5ba47c3302 | ||
|
|
b613460fe5 | ||
|
|
daadd24bde | ||
|
|
9308e939f4 | ||
|
|
04c9348de0 | ||
|
|
2d3766d928 | ||
|
|
550b643e33 | ||
|
|
cbe344f4d5 | ||
|
|
063431cb0f | ||
|
|
c6e5fed658 | ||
|
|
f73b398776 | ||
|
|
55c4020ba9 | ||
|
|
d70f899f71 | ||
|
|
19c4b7bee6 | ||
|
|
3238743a7b | ||
|
|
f22c269ccf | ||
|
|
8ca3977602 | ||
|
|
6db05d8cc6 | ||
|
|
fc63c6f2e2 | ||
|
|
f7f5bc9085 | ||
|
|
6b85430a14 | ||
|
|
a68c865010 | ||
|
|
fe7f2e2e4d | ||
|
|
0a3cf8ba11 | ||
|
|
bf5b463c0d | ||
|
|
6aa9900c03 | ||
|
|
9e21414294 | ||
|
|
bb4e674415 | ||
|
|
b42ff08612 | ||
|
|
03fb62c174 | ||
|
|
2dfc645ea9 | ||
|
|
fe8e2bb237 | ||
|
|
a9ed8d5391 | ||
|
|
41a681531d | ||
|
|
837e70677b | ||
|
|
7ebe372122 | ||
|
|
625849b846 | ||
|
|
32f1edc555 | ||
|
|
2f35099f81 | ||
|
|
ce8fd31a8f | ||
|
|
fdb241cad2 | ||
|
|
ab303f4e09 | ||
|
|
15cb21a6f4 | ||
|
|
2e2ba2cb16 | ||
|
|
d4c0a41b00 | ||
|
|
8702b5a40a | ||
|
|
bab818e801 | ||
|
|
a3aa841fc9 | ||
|
|
fdd894145b | ||
|
|
85f67b2ee3 | ||
|
|
e9c6f16c56 | ||
|
|
d3b18345c5 | ||
|
|
0e4d174551 | ||
|
|
73efbeb581 | ||
|
|
2fb312bb2b | ||
|
|
e8e66f3824 | ||
|
|
a8d080c825 | ||
|
|
ddd541b198 | ||
|
|
3090aec97d | ||
|
|
14c6419bc1 | ||
|
|
3bc29d6c0c | ||
|
|
67c5086aba | ||
|
|
cd82b83f89 | ||
|
|
39fb2cc732 | ||
|
|
9c732c7946 | ||
|
|
2332ea5753 | ||
|
|
6b1062ccc3 | ||
|
|
39fa38cb35 | ||
|
|
ddf7de7e86 | ||
|
|
e5101e8eac | ||
|
|
d9c3f8e249 | ||
|
|
7e0d018b35 | ||
|
|
4b05ca1198 | ||
|
|
f0465bf106 | ||
|
|
8134ec25f0 | ||
|
|
6049aabe27 | ||
|
|
badade3ccc | ||
|
|
fa26860296 | ||
|
|
140e0acf54 | ||
|
|
c711db11ce | ||
|
|
1fe6954150 | ||
|
|
2414469ac3 | ||
|
|
838ba1ca3d | ||
|
|
8f3f8d3e0b | ||
|
|
8ba9eb83af | ||
|
|
2c6efc2f5f | ||
|
|
d6930ca991 | ||
|
|
f70c286e6a | ||
|
|
dcff515ad8 | ||
|
|
b9e387013f | ||
|
|
a69451ff46 | ||
|
|
01b372a75c | ||
|
|
cd2e7fa72a | ||
|
|
fdf0b999cd | ||
|
|
45b5fa573f | ||
|
|
a0258f2205 | ||
|
|
0a68636de3 | ||
|
|
2f53781a77 | ||
|
|
7e7be05ddf | ||
|
|
f5afa8198c | ||
|
|
eeabfd6d18 | ||
|
|
490301f9fe | ||
|
|
f5be3d8e5b |
@@ -1,6 +1,6 @@
|
||||
[alias]
|
||||
dev = "run --package ruff_dev --bin ruff_dev"
|
||||
benchmark = "bench -p ruff_benchmark --"
|
||||
benchmark = "bench -p ruff_benchmark --bench linter --bench formatter --"
|
||||
|
||||
[target.'cfg(all())']
|
||||
rustflags = [
|
||||
|
||||
46
.devcontainer/devcontainer.json
Normal file
46
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,46 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/rust
|
||||
{
|
||||
"name": "Ruff",
|
||||
"image": "mcr.microsoft.com/devcontainers/rust:0-1-bullseye",
|
||||
"mounts": [
|
||||
{
|
||||
"source": "devcontainer-cargo-cache-${devcontainerId}",
|
||||
"target": "/usr/local/cargo",
|
||||
"type": "volume"
|
||||
}
|
||||
],
|
||||
"customizations": {
|
||||
"codespaces": {
|
||||
"openFiles": [
|
||||
"CONTRIBUTING.md"
|
||||
]
|
||||
},
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"rust-lang.rust-analyzer",
|
||||
"serayuzgur.crates",
|
||||
"tamasfe.even-better-toml",
|
||||
"Swellaby.vscode-rust-test-adapter",
|
||||
"charliermarsh.ruff"
|
||||
],
|
||||
"settings": {
|
||||
"rust-analyzer.updates.askBeforeDownload": false
|
||||
}
|
||||
}
|
||||
},
|
||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/python": {
|
||||
"installTools": false
|
||||
}
|
||||
},
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
"postCreateCommand": ".devcontainer/post-create.sh"
|
||||
// Configure tool-specific properties.
|
||||
// "customizations": {},
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
}
|
||||
8
.devcontainer/post-create.sh
Executable file
8
.devcontainer/post-create.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
rustup default < rust-toolchain
|
||||
rustup component add clippy rustfmt
|
||||
cargo install cargo-insta
|
||||
cargo fetch
|
||||
|
||||
pip install maturin pre-commit
|
||||
@@ -14,4 +14,7 @@ indent_size = 2
|
||||
indent_size = 4
|
||||
|
||||
[*.snap]
|
||||
trim_trailing_whitespace = false
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.md]
|
||||
max_line_length = 100
|
||||
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<!--
|
||||
Thank you for contributing to Ruff! To help us out with reviewing, please consider the following:
|
||||
|
||||
- Does this pull request include a summary of the change? (See below.)
|
||||
- Does this pull request include a descriptive title?
|
||||
- Does this pull request include references to any relevant issues?
|
||||
-->
|
||||
|
||||
## Summary
|
||||
|
||||
<!-- What's the purpose of the change? What does it do, and why? -->
|
||||
|
||||
## Test Plan
|
||||
|
||||
<!-- How was it tested? -->
|
||||
5
.github/release.yml
vendored
5
.github/release.yml
vendored
@@ -1,5 +1,9 @@
|
||||
# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#configuring-automatically-generated-release-notes
|
||||
changelog:
|
||||
exclude:
|
||||
labels:
|
||||
- internal
|
||||
- documentation
|
||||
categories:
|
||||
- title: Breaking Changes
|
||||
labels:
|
||||
@@ -11,6 +15,7 @@ changelog:
|
||||
- title: Settings
|
||||
labels:
|
||||
- configuration
|
||||
- cli
|
||||
- title: Bug Fixes
|
||||
labels:
|
||||
- bug
|
||||
|
||||
52
.github/workflows/ci.yaml
vendored
52
.github/workflows/ci.yaml
vendored
@@ -15,6 +15,8 @@ env:
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
PACKAGE_NAME: ruff
|
||||
PYTHON_VERSION: "3.7" # to build abi3 wheels
|
||||
|
||||
jobs:
|
||||
cargo-fmt:
|
||||
@@ -85,6 +87,22 @@ jobs:
|
||||
name: ruff
|
||||
path: target/debug/ruff
|
||||
|
||||
cargo-fuzz:
|
||||
runs-on: ubuntu-latest
|
||||
name: "cargo fuzz"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: "fuzz -> target"
|
||||
- name: "Install cargo-fuzz"
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-fuzz@0.11
|
||||
- run: cargo fuzz build -s none
|
||||
|
||||
cargo-test-wasm:
|
||||
runs-on: ubuntu-latest
|
||||
name: "cargo test (wasm)"
|
||||
@@ -181,18 +199,30 @@ jobs:
|
||||
- name: "Install cargo-udeps"
|
||||
uses: taiki-e/install-action@cargo-udeps
|
||||
- name: "Run cargo-udeps"
|
||||
run: cargo +nightly-2023-03-30 udeps
|
||||
|
||||
|
||||
python-package:
|
||||
name: "python package"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
manylinux: auto
|
||||
args: --out dist
|
||||
- name: "Test wheel"
|
||||
run: |
|
||||
unused_dependencies=$(cargo +nightly-2023-03-30 udeps > unused.txt && cat unused.txt | cut -d $'\n' -f 2-)
|
||||
if [ -z "$unused_dependencies" ]; then
|
||||
echo "No unused dependencies found" > $GITHUB_STEP_SUMMARY
|
||||
exit 0
|
||||
else
|
||||
echo "Found unused dependencies" > $GITHUB_STEP_SUMMARY
|
||||
echo '```console' >> $GITHUB_STEP_SUMMARY
|
||||
echo "$unused_dependencies" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
|
||||
pre-commit:
|
||||
name: "pre-commit"
|
||||
|
||||
4
.github/workflows/docs.yaml
vendored
4
.github/workflows/docs.yaml
vendored
@@ -1,9 +1,9 @@
|
||||
name: mkdocs
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [ published ]
|
||||
|
||||
jobs:
|
||||
mkdocs:
|
||||
|
||||
2
.github/workflows/flake8-to-ruff.yaml
vendored
2
.github/workflows/flake8-to-ruff.yaml
vendored
@@ -52,7 +52,7 @@ jobs:
|
||||
- name: "Build wheels - universal2"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
args: --release --universal2 --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
args: --release --target universal2-apple-darwin --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
- name: "Install built wheel - universal2"
|
||||
run: |
|
||||
pip install dist/${{ env.CRATE_NAME }}-*universal2.whl --force-reinstall
|
||||
|
||||
4
.github/workflows/playground.yaml
vendored
4
.github/workflows/playground.yaml
vendored
@@ -2,8 +2,8 @@ name: "[Playground] Release"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
release:
|
||||
types: [ published ]
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
|
||||
80
.github/workflows/release.yaml
vendored
80
.github/workflows/release.yaml
vendored
@@ -1,9 +1,9 @@
|
||||
name: "[ruff] Release"
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [ published ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -18,6 +18,32 @@ env:
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
|
||||
jobs:
|
||||
sdist:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build sdist"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
command: sdist
|
||||
args: --out dist
|
||||
- name: "Test sdist"
|
||||
run: |
|
||||
rustup default $(cat rust-toolchain)
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.tar.gz --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Upload sdist"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
|
||||
macos-x86_64:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
@@ -32,7 +58,7 @@ jobs:
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --out dist --sdist
|
||||
args: --release --out dist
|
||||
- name: "Test wheel - x86_64"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
@@ -69,7 +95,7 @@ jobs:
|
||||
- name: "Build wheels - universal2"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
args: --release --universal2 --out dist
|
||||
args: --release --target universal2-apple-darwin --out dist
|
||||
- name: "Test wheel - universal2"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*universal2.whl --force-reinstall
|
||||
@@ -369,26 +395,48 @@ jobs:
|
||||
- musllinux
|
||||
- musllinux-cross
|
||||
if: "startsWith(github.ref, 'refs/tags/')"
|
||||
environment:
|
||||
name: release
|
||||
permissions:
|
||||
# For pypi trusted publishing
|
||||
id-token: write
|
||||
# For GitHub release publishing
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
- uses: actions/setup-python@v4
|
||||
path: wheels
|
||||
- name: "Publish to PyPi"
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.RUFF_TOKEN }}
|
||||
run: |
|
||||
pip install --upgrade twine
|
||||
twine upload --skip-existing *
|
||||
- name: "Update pre-commit mirror"
|
||||
run: |
|
||||
curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.RUFF_PRE_COMMIT_PAT }}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/charliermarsh/ruff-pre-commit/dispatches --data '{"event_type": "pypi_release"}'
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
skip-existing: true
|
||||
packages-dir: wheels
|
||||
verbose: true
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: binaries
|
||||
- name: Release
|
||||
- name: "Publish to GitHub"
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: binaries/*
|
||||
|
||||
# After the release has been published, we update downstream repositories
|
||||
# This is separate because if this fails the release is still fine, we just need to do some manual workflow triggers
|
||||
update-dependents:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: release
|
||||
steps:
|
||||
- name: "Update pre-commit mirror"
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
github-token: ${{ secrets.RUFF_PRE_COMMIT_PAT }}
|
||||
script: |
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: 'astral-sh',
|
||||
repo: 'ruff-pre-commit',
|
||||
workflow_id: 'main.yml',
|
||||
ref: 'main',
|
||||
})
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,10 +1,11 @@
|
||||
# Local cache
|
||||
.ruff_cache
|
||||
crates/ruff/resources/test/cpython
|
||||
mkdocs.yml
|
||||
.overrides
|
||||
ruff-old
|
||||
github_search*.jsonl
|
||||
schemastore
|
||||
.venv*
|
||||
scratch.py
|
||||
|
||||
###
|
||||
# Rust.gitignore
|
||||
|
||||
14
.markdownlint.yaml
Normal file
14
.markdownlint.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
# default to true for all rules
|
||||
default: true
|
||||
|
||||
# MD033/no-inline-html
|
||||
MD033: false
|
||||
|
||||
# MD041/first-line-h1
|
||||
MD041: false
|
||||
|
||||
# MD013/line-length
|
||||
MD013:
|
||||
line_length: 100
|
||||
code_blocks: false
|
||||
ignore_code_blocks: true
|
||||
@@ -1,4 +1,12 @@
|
||||
fail_fast: true
|
||||
|
||||
exclude: |
|
||||
(?x)^(
|
||||
crates/ruff/resources/.*|
|
||||
crates/ruff_python_formatter/resources/.*|
|
||||
crates/ruff_python_formatter/src/snapshots/.*
|
||||
)$
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/abravalheri/validate-pyproject
|
||||
rev: v0.12.1
|
||||
@@ -17,14 +25,9 @@ repos:
|
||||
rev: v0.33.0
|
||||
hooks:
|
||||
- id: markdownlint-fix
|
||||
args:
|
||||
- --disable
|
||||
- MD013 # line-length
|
||||
- MD033 # no-inline-html
|
||||
- --
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.14.8
|
||||
rev: v1.14.12
|
||||
hooks:
|
||||
- id: typos
|
||||
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.0.268
|
||||
|
||||
### The `keep-runtime-typing` setting has been removed ([#4427](https://github.com/charliermarsh/ruff/pull/4427))
|
||||
|
||||
Enabling the `keep-runtime-typing` option, located under the `pyupgrade` section, is equivalent
|
||||
to ignoring the `UP006` and `UP007` rules via Ruff's standard `ignore` mechanism. As there's no
|
||||
need for a dedicated setting to disable these rules, the `keep-runtime-typing` option has been
|
||||
removed.
|
||||
|
||||
## 0.0.267
|
||||
|
||||
### `update-check` is no longer a valid configuration option ([#4313](https://github.com/charliermarsh/ruff/pull/4313))
|
||||
@@ -77,7 +86,8 @@ the intention of adding a stable public API in the future.
|
||||
### `select`, `extend-select`, `ignore`, and `extend-ignore` have new semantics ([#2312](https://github.com/charliermarsh/ruff/pull/2312))
|
||||
|
||||
Previously, the interplay between `select` and its related options could lead to unexpected
|
||||
behavior. For example, `ruff --select E501 --ignore ALL` and `ruff --select E501 --extend-ignore ALL` behaved differently. (See [#2312](https://github.com/charliermarsh/ruff/pull/2312) for more
|
||||
behavior. For example, `ruff --select E501 --ignore ALL` and `ruff --select E501 --extend-ignore ALL`
|
||||
behaved differently. (See [#2312](https://github.com/charliermarsh/ruff/pull/2312) for more
|
||||
examples.)
|
||||
|
||||
When Ruff determines the enabled rule set, it has to reconcile `select` and `ignore` from a variety
|
||||
|
||||
@@ -8,6 +8,7 @@ Welcome! We're happy to have you here. Thank you in advance for your contributio
|
||||
- [Project Structure](#project-structure)
|
||||
- [Example: Adding a new lint rule](#example-adding-a-new-lint-rule)
|
||||
- [Rule naming convention](#rule-naming-convention)
|
||||
- [Rule testing: fixtures and snapshots](#rule-testing-fixtures-and-snapshots)
|
||||
- [Example: Adding a new configuration option](#example-adding-a-new-configuration-option)
|
||||
- [MkDocs](#mkdocs)
|
||||
- [Release Process](#release-process)
|
||||
@@ -93,9 +94,11 @@ At time of writing, the repository includes the following crates:
|
||||
|
||||
- `crates/ruff`: library crate containing all lint rules and the core logic for running them.
|
||||
- `crates/ruff_cli`: binary crate containing Ruff's command-line interface.
|
||||
- `crates/ruff_dev`: binary crate containing utilities used in the development of Ruff itself (e.g., `cargo dev generate-all`).
|
||||
- `crates/ruff_dev`: binary crate containing utilities used in the development of Ruff itself (e.g.,
|
||||
`cargo dev generate-all`).
|
||||
- `crates/ruff_macros`: library crate containing macros used by Ruff.
|
||||
- `crates/ruff_python`: library crate implementing Python-specific functionality (e.g., lists of standard library modules by versionb).
|
||||
- `crates/ruff_python`: library crate implementing Python-specific functionality (e.g., lists of
|
||||
standard library modules by version).
|
||||
- `crates/flake8_to_ruff`: binary crate for generating Ruff configuration from Flake8 configuration.
|
||||
|
||||
### Example: Adding a new lint rule
|
||||
@@ -103,14 +106,20 @@ At time of writing, the repository includes the following crates:
|
||||
At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
|
||||
1. Determine a name for the new rule as per our [rule naming convention](#rule-naming-convention).
|
||||
|
||||
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
|
||||
|
||||
1. In that file, define a violation struct. You can grep for `#[violation]` to see examples.
|
||||
1. Map the violation struct to a rule code in `crates/ruff/src/registry.rs` (e.g., `E402`).
|
||||
1. Define the logic for triggering the violation in `crates/ruff/src/checkers/ast.rs` (for AST-based
|
||||
checks), `crates/ruff/src/checkers/tokens.rs` (for token-based checks), `crates/ruff/src/checkers/lines.rs`
|
||||
(for text-based checks), or `crates/ruff/src/checkers/filesystem.rs` (for filesystem-based
|
||||
checks).
|
||||
1. Add a test fixture.
|
||||
|
||||
1. Map the violation struct to a rule code in `crates/ruff/src/codes.rs` (e.g., `E402`).
|
||||
|
||||
1. Define the logic for triggering the violation in `crates/ruff/src/checkers/ast/mod.rs` (for
|
||||
AST-based checks), `crates/ruff/src/checkers/tokens.rs` (for token-based checks),
|
||||
`crates/ruff/src/checkers/lines.rs` (for text-based checks), or
|
||||
`crates/ruff/src/checkers/filesystem.rs` (for filesystem-based checks).
|
||||
|
||||
1. Add proper [testing](#rule-testing-fixtures-and-snapshots) for your rule.
|
||||
|
||||
1. Update the generated files (documentation and generated code).
|
||||
|
||||
To define the violation, start by creating a dedicated file for your rule under the appropriate
|
||||
@@ -125,18 +134,8 @@ collecting diagnostics as it goes.
|
||||
If you need to inspect the AST, you can run `cargo dev print-ast` with a Python file. Grep
|
||||
for the `Check::new` invocations to understand how other, similar rules are implemented.
|
||||
|
||||
To add a test fixture, create a file under `crates/ruff/resources/test/fixtures/[linter]`, named to match
|
||||
the code you defined earlier (e.g., `crates/ruff/resources/test/fixtures/pycodestyle/E402.py`). This file should
|
||||
contain a variety of violations and non-violations designed to evaluate and demonstrate the behavior
|
||||
of your lint rule.
|
||||
|
||||
Run `cargo dev generate-all` to generate the code for your new fixture. Then run Ruff
|
||||
locally with (e.g.) `cargo run -p ruff_cli -- check crates/ruff/resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402`.
|
||||
|
||||
Once you're satisfied with the output, codify the behavior as a snapshot test by adding a new
|
||||
`test_case` macro in the relevant `crates/ruff/src/[linter]/mod.rs` file. Then, run `cargo test`.
|
||||
Your test will fail, but you'll be prompted to follow-up with `cargo insta review`. Accept the
|
||||
generated snapshot, then commit the snapshot file alongside the rest of your changes.
|
||||
Once you're satisfied with your code, add tests for your rule. See [rule testing](#rule-testing-fixtures-and-snapshots)
|
||||
for more details.
|
||||
|
||||
Finally, regenerate the documentation and generated code with `cargo dev generate-all`.
|
||||
|
||||
@@ -148,12 +147,44 @@ This implies that rule names:
|
||||
|
||||
- should state the bad thing being checked for
|
||||
|
||||
- should not contain instructions on what you what you should use instead
|
||||
- should not contain instructions on what you should use instead
|
||||
(these belong in the rule documentation and the `autofix_title` for rules that have autofix)
|
||||
|
||||
When re-implementing rules from other linters, this convention is given more importance than
|
||||
preserving the original rule name.
|
||||
|
||||
#### Rule testing: fixtures and snapshots
|
||||
|
||||
To test rules, Ruff uses snapshots of Ruff's output for a given file (fixture). Generally, there
|
||||
will be one file per rule (e.g., `E402.py`), and each file will contain all necessary examples of
|
||||
both violations and non-violations. `cargo insta review` will generate a snapshot file containing
|
||||
Ruff's output for each fixture, which you can then commit alongside your changes.
|
||||
|
||||
Once you've completed the code for the rule itself, you can define tests with the following steps:
|
||||
|
||||
1. Add a Python file to `crates/ruff/resources/test/fixtures/[linter]` that contains the code you
|
||||
want to test. The file name should match the rule name (e.g., `E402.py`), and it should include
|
||||
examples of both violations and non-violations.
|
||||
|
||||
1. Run Ruff locally against your file and verify the output is as expected. Once you're satisfied
|
||||
with the output (you see the violations you expect, and no others), proceed to the next step.
|
||||
For example, if you're adding a new rule named `E402`, you would run:
|
||||
|
||||
```shell
|
||||
cargo run -p ruff_cli -- check crates/ruff/resources/test/fixtures/pycodestyle/E402.py --no-cache
|
||||
```
|
||||
|
||||
1. Add the test to the relevant `crates/ruff/src/rules/[linter]/mod.rs` file. If you're contributing
|
||||
a rule to a pre-existing set, you should be able to find a similar example to pattern-match
|
||||
against. If you're adding a new linter, you'll need to create a new `mod.rs` file (see,
|
||||
e.g., `crates/ruff/src/rules/flake8_bugbear/mod.rs`)
|
||||
|
||||
1. Run `cargo test`. Your test will fail, but you'll be prompted to follow-up
|
||||
with `cargo insta review`. Run `cargo insta review`, review and accept the generated snapshot,
|
||||
then commit the snapshot file alongside the rest of your changes.
|
||||
|
||||
1. Run `cargo test` again to ensure that your test passes.
|
||||
|
||||
### Example: Adding a new configuration option
|
||||
|
||||
Ruff's user-facing settings live in a few different places.
|
||||
@@ -184,6 +215,8 @@ Finally, regenerate the documentation and generated code with `cargo dev generat
|
||||
|
||||
To preview any changes to the documentation locally:
|
||||
|
||||
1. Install the [Rust toolchain](https://www.rust-lang.org/tools/install).
|
||||
|
||||
1. Install MkDocs and Material for MkDocs with:
|
||||
|
||||
```shell
|
||||
|
||||
739
Cargo.lock
generated
739
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
17
Cargo.toml
17
Cargo.toml
@@ -3,7 +3,7 @@ members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
rust-version = "1.69"
|
||||
rust-version = "1.70"
|
||||
homepage = "https://beta.ruff.rs/docs/"
|
||||
documentation = "https://beta.ruff.rs/docs/"
|
||||
repository = "https://github.com/charliermarsh/ruff"
|
||||
@@ -11,7 +11,7 @@ authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = { version = "1.0.69" }
|
||||
bitflags = { version = "2.2.1" }
|
||||
bitflags = { version = "2.3.1" }
|
||||
chrono = { version = "0.4.23", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.1.8", features = ["derive"] }
|
||||
colored = { version = "2.0.0" }
|
||||
@@ -24,17 +24,21 @@ is-macro = { version = "0.2.2" }
|
||||
itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "80e4c1399f95e5beb532fdd1e209ad2dbb470438" }
|
||||
log = { version = "0.4.17" }
|
||||
memchr = "2.5.0"
|
||||
nohash-hasher = { version = "0.2.0" }
|
||||
num-bigint = { version = "0.4.3" }
|
||||
num-traits = { version = "0.2.15" }
|
||||
once_cell = { version = "1.17.1" }
|
||||
path-absolutize = { version = "3.0.14" }
|
||||
proc-macro2 = { version = "1.0.51" }
|
||||
quote = { version = "1.0.23" }
|
||||
regex = { version = "1.7.1" }
|
||||
ruff_text_size = { git = "https://github.com/RustPython/Parser.git", rev = "947fb53d0b41fec465db3d8e725bdb2eec1299ec" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "f3e4d3409253660bd4fa7f3d24d3db747e7dca61" }
|
||||
rustpython-literal = { git = "https://github.com/RustPython/Parser.git", rev = "947fb53d0b41fec465db3d8e725bdb2eec1299ec" }
|
||||
rustpython-parser = { git = "https://github.com/RustPython/Parser.git", rev = "947fb53d0b41fec465db3d8e725bdb2eec1299ec" , default-features = false}
|
||||
ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" }
|
||||
rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd", default-features = false, features = ["all-nodes-with-ranges"]}
|
||||
rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" }
|
||||
rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" }
|
||||
rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd", default-features = false, features = ["full-lexer", "all-nodes-with-ranges"] }
|
||||
schemars = { version = "0.8.12" }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = { version = "1.0.93", features = ["preserve_order"] }
|
||||
@@ -45,7 +49,6 @@ strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
syn = { version = "2.0.15" }
|
||||
test-case = { version = "3.0.0" }
|
||||
textwrap = { version = "0.16.0" }
|
||||
toml = { version = "0.7.2" }
|
||||
|
||||
[profile.release]
|
||||
|
||||
56
LICENSE
56
LICENSE
@@ -354,6 +354,37 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-todos, licensed as follows:
|
||||
"""
|
||||
Copyright (c) 2019 EclecticIQ. All rights reserved.
|
||||
Copyright (c) 2020 Gram <gram@orsinium.dev>. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
|
||||
- flake8-unused-arguments, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
@@ -783,6 +814,31 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-async, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Cooper Lees
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-type-checking, licensed as follows:
|
||||
"""
|
||||
Copyright (c) 2021, Sondre Lillebø Gundersen
|
||||
|
||||
52
README.md
52
README.md
@@ -24,17 +24,18 @@ An extremely fast Python linter, written in Rust.
|
||||
<i>Linting the CPython codebase from scratch.</i>
|
||||
</p>
|
||||
|
||||
- ⚡️ 10-100x faster than existing linters
|
||||
- 🐍 Installable via `pip`
|
||||
- 🛠️ `pyproject.toml` support
|
||||
- 🤝 Python 3.11 compatibility
|
||||
- 📦 Built-in caching, to avoid re-analyzing unchanged files
|
||||
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
- 📏 Over [500 built-in rules](https://beta.ruff.rs/docs/rules/)
|
||||
- ⚖️ [Near-parity](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) with the built-in Flake8 rule set
|
||||
- 🔌 Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
|
||||
- ⌨️ First-party editor integrations for [VS Code](https://github.com/charliermarsh/ruff-vscode) and [more](https://github.com/charliermarsh/ruff-lsp)
|
||||
- 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](https://beta.ruff.rs/docs/configuration/#pyprojecttoml-discovery)
|
||||
- ⚡️ 10-100x faster than existing linters
|
||||
- 🐍 Installable via `pip`
|
||||
- 🛠️ `pyproject.toml` support
|
||||
- 🤝 Python 3.11 compatibility
|
||||
- 📦 Built-in caching, to avoid re-analyzing unchanged files
|
||||
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
- 📏 Over [500 built-in rules](https://beta.ruff.rs/docs/rules/)
|
||||
- ⚖️ [Near-parity](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) with the
|
||||
built-in Flake8 rule set
|
||||
- 🔌 Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
|
||||
- ⌨️ First-party editor integrations for [VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://github.com/astral-sh/ruff-lsp)
|
||||
- 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](https://beta.ruff.rs/docs/configuration/#pyprojecttoml-discovery)
|
||||
|
||||
Ruff aims to be orders of magnitude faster than alternative tools while integrating more
|
||||
functionality behind a single, common interface.
|
||||
@@ -84,7 +85,8 @@ of [Conda](https://docs.conda.io/en/latest/):
|
||||
[**Timothy Crosley**](https://twitter.com/timothycrosley/status/1606420868514877440),
|
||||
creator of [isort](https://github.com/PyCQA/isort):
|
||||
|
||||
> Just switched my first project to Ruff. Only one downside so far: it's so fast I couldn't believe it was working till I intentionally introduced some errors.
|
||||
> Just switched my first project to Ruff. Only one downside so far: it's so fast I couldn't believe
|
||||
> it was working till I intentionally introduced some errors.
|
||||
|
||||
[**Tim Abbott**](https://github.com/charliermarsh/ruff/issues/465#issuecomment-1317400028), lead
|
||||
developer of [Zulip](https://github.com/zulip/zulip):
|
||||
@@ -135,15 +137,15 @@ ruff check path/to/code/to/file.py # Lint `file.py`
|
||||
Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
|
||||
```yaml
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.0.267'
|
||||
rev: v0.0.272
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
|
||||
Ruff can also be used as a [VS Code extension](https://github.com/charliermarsh/ruff-vscode) or
|
||||
alongside any other editor through the [Ruff LSP](https://github.com/charliermarsh/ruff-lsp).
|
||||
Ruff can also be used as a [VS Code extension](https://github.com/astral-sh/ruff-vscode) or
|
||||
alongside any other editor through the [Ruff LSP](https://github.com/astral-sh/ruff-lsp).
|
||||
|
||||
Ruff can also be used as a [GitHub Action](https://github.com/features/actions) via
|
||||
[`ruff-action`](https://github.com/chartboost/ruff-action):
|
||||
@@ -242,6 +244,8 @@ stylistic rules made obsolete by the use of an autoformatter, like
|
||||
If you're just getting started with Ruff, **the default rule set is a great place to start**: it
|
||||
catches a wide variety of common errors (like unused imports) with zero configuration.
|
||||
|
||||
<!-- End section: Rules -->
|
||||
|
||||
Beyond the defaults, Ruff re-implements some of the most popular Flake8 plugins and related code
|
||||
quality tools, including:
|
||||
|
||||
@@ -249,6 +253,7 @@ quality tools, including:
|
||||
- [eradicate](https://pypi.org/project/eradicate/)
|
||||
- [flake8-2020](https://pypi.org/project/flake8-2020/)
|
||||
- [flake8-annotations](https://pypi.org/project/flake8-annotations/)
|
||||
- [flake8-async](https://pypi.org/project/flake8-async)
|
||||
- [flake8-bandit](https://pypi.org/project/flake8-bandit/) ([#1646](https://github.com/charliermarsh/ruff/issues/1646))
|
||||
- [flake8-blind-except](https://pypi.org/project/flake8-blind-except/)
|
||||
- [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap/)
|
||||
@@ -263,6 +268,7 @@ quality tools, including:
|
||||
- [flake8-eradicate](https://pypi.org/project/flake8-eradicate/)
|
||||
- [flake8-errmsg](https://pypi.org/project/flake8-errmsg/)
|
||||
- [flake8-executable](https://pypi.org/project/flake8-executable/)
|
||||
- [flake8-future-annotations](https://pypi.org/project/flake8-future-annotations/)
|
||||
- [flake8-gettext](https://pypi.org/project/flake8-gettext/)
|
||||
- [flake8-implicit-str-concat](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
- [flake8-import-conventions](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
@@ -279,6 +285,7 @@ quality tools, including:
|
||||
- [flake8-simplify](https://pypi.org/project/flake8-simplify/)
|
||||
- [flake8-super](https://pypi.org/project/flake8-super/)
|
||||
- [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/)
|
||||
- [flake8-todos](https://pypi.org/project/flake8-todos/)
|
||||
- [flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
|
||||
- [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/)
|
||||
- [flynt](https://pypi.org/project/flynt/) ([#2102](https://github.com/charliermarsh/ruff/issues/2102))
|
||||
@@ -288,12 +295,11 @@ quality tools, including:
|
||||
- [pep8-naming](https://pypi.org/project/pep8-naming/)
|
||||
- [pydocstyle](https://pypi.org/project/pydocstyle/)
|
||||
- [pygrep-hooks](https://github.com/pre-commit/pygrep-hooks)
|
||||
- [pylint-airflow](https://pypi.org/project/pylint-airflow/)
|
||||
- [pyupgrade](https://pypi.org/project/pyupgrade/)
|
||||
- [tryceratops](https://pypi.org/project/tryceratops/)
|
||||
- [yesqa](https://pypi.org/project/yesqa/)
|
||||
|
||||
<!-- End section: Rules -->
|
||||
|
||||
For a complete enumeration of the supported rules, see [_Rules_](https://beta.ruff.rs/docs/rules/).
|
||||
|
||||
## Contributing
|
||||
@@ -349,7 +355,9 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Gradio](https://github.com/gradio-app/gradio)
|
||||
- [Great Expectations](https://github.com/great-expectations/great_expectations)
|
||||
- Hugging Face ([Transformers](https://github.com/huggingface/transformers), [Datasets](https://github.com/huggingface/datasets), [Diffusers](https://github.com/huggingface/diffusers))
|
||||
- Hugging Face ([Transformers](https://github.com/huggingface/transformers),
|
||||
[Datasets](https://github.com/huggingface/datasets),
|
||||
[Diffusers](https://github.com/huggingface/diffusers))
|
||||
- [Hatch](https://github.com/pypa/hatch)
|
||||
- [Home Assistant](https://github.com/home-assistant/core)
|
||||
- [Ibis](https://github.com/ibis-project/ibis)
|
||||
@@ -361,7 +369,9 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- Modern Treasury ([Python SDK](https://github.com/Modern-Treasury/modern-treasury-python-sdk))
|
||||
- Mozilla ([Firefox](https://github.com/mozilla/gecko-dev))
|
||||
- [MegaLinter](https://github.com/oxsecurity/megalinter)
|
||||
- Microsoft ([Semantic Kernel](https://github.com/microsoft/semantic-kernel), [ONNX Runtime](https://github.com/microsoft/onnxruntime))
|
||||
- Microsoft ([Semantic Kernel](https://github.com/microsoft/semantic-kernel),
|
||||
[ONNX Runtime](https://github.com/microsoft/onnxruntime),
|
||||
[LightGBM](https://github.com/microsoft/LightGBM))
|
||||
- Netflix ([Dispatch](https://github.com/Netflix/dispatch))
|
||||
- [Neon](https://github.com/neondatabase/neon)
|
||||
- [ONNX](https://github.com/onnx/onnx)
|
||||
@@ -385,7 +395,7 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [SciPy](https://github.com/scipy/scipy)
|
||||
- [Sphinx](https://github.com/sphinx-doc/sphinx)
|
||||
- [Stable Baselines3](https://github.com/DLR-RM/stable-baselines3)
|
||||
- [Starlite](https://github.com/starlite-api/starlite)
|
||||
- [Litestar](https://litestar.dev/)
|
||||
- [The Algorithms](https://github.com/TheAlgorithms/Python)
|
||||
- [Vega-Altair](https://github.com/altair-viz/altair)
|
||||
- WordPress ([Openverse](https://github.com/WordPress/openverse))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[files]
|
||||
extend-exclude = ["snapshots", "black"]
|
||||
extend-exclude = ["resources", "snapshots"]
|
||||
|
||||
[default.extend-words]
|
||||
trivias = "trivias"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.267"
|
||||
version = "0.0.272"
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ requires-python = ">=3.7"
|
||||
repository = "https://github.com/charliermarsh/ruff#subdirectory=crates/flake8_to_ruff"
|
||||
|
||||
[build-system]
|
||||
requires = ["maturin>=0.15.1,<0.16"]
|
||||
requires = ["maturin>=1.0,<2.0"]
|
||||
build-backend = "maturin"
|
||||
|
||||
[tool.maturin]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.267"
|
||||
version = "0.0.272"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
@@ -17,11 +17,13 @@ name = "ruff"
|
||||
ruff_cache = { path = "../ruff_cache" }
|
||||
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
|
||||
ruff_macros = { path = "../ruff_macros" }
|
||||
ruff_newlines = { path = "../ruff_newlines" }
|
||||
ruff_python_ast = { path = "../ruff_python_ast", features = ["serde"] }
|
||||
ruff_python_semantic = { path = "../ruff_python_semantic" }
|
||||
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
|
||||
ruff_rustpython = { path = "../ruff_rustpython" }
|
||||
ruff_text_size = { workspace = true }
|
||||
ruff_textwrap = { path = "../ruff_textwrap" }
|
||||
|
||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||
anyhow = { workspace = true }
|
||||
@@ -41,8 +43,8 @@ libcst = { workspace = true }
|
||||
log = { workspace = true }
|
||||
natord = { version = "1.0.9" }
|
||||
nohash-hasher = { workspace = true }
|
||||
num-bigint = { version = "0.4.3" }
|
||||
num-traits = { version = "0.2.15" }
|
||||
num-bigint = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
path-absolutize = { workspace = true, features = [
|
||||
"once_cell_cache",
|
||||
@@ -50,11 +52,12 @@ path-absolutize = { workspace = true, features = [
|
||||
] }
|
||||
pathdiff = { version = "0.2.1" }
|
||||
pep440_rs = { version = "0.3.1", features = ["serde"] }
|
||||
pyproject-toml = { version = "0.6.0" }
|
||||
quick-junit = { version = "0.3.2" }
|
||||
regex = { workspace = true }
|
||||
result-like = { version = "0.4.6" }
|
||||
rustc-hash = { workspace = true }
|
||||
rustpython-common = { workspace = true }
|
||||
rustpython-format = { workspace = true }
|
||||
rustpython-parser = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
semver = { version = "1.0.16" }
|
||||
@@ -65,11 +68,11 @@ shellexpand = { workspace = true }
|
||||
smallvec = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
textwrap = { workspace = true }
|
||||
thiserror = { version = "1.0.38" }
|
||||
toml = { workspace = true }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unicode-width = { version = "0.1.10" }
|
||||
unicode_names2 = { version = "0.6.0", git = "https://github.com/youknowone/unicode_names2.git", rev = "4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde" }
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { workspace = true }
|
||||
@@ -81,6 +84,4 @@ colored = { workspace = true, features = ["no-color"] }
|
||||
[features]
|
||||
default = []
|
||||
schemars = ["dep:schemars"]
|
||||
logical_lines = []
|
||||
jupyter_notebook = []
|
||||
ecosystem_ci = []
|
||||
|
||||
16
crates/ruff/resources/test/fixtures/airflow/AIR001.py
vendored
Normal file
16
crates/ruff/resources/test/fixtures/airflow/AIR001.py
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
from airflow.operators import PythonOperator
|
||||
|
||||
|
||||
def my_callable():
|
||||
pass
|
||||
|
||||
|
||||
my_task = PythonOperator(task_id="my_task", callable=my_callable)
|
||||
my_task_2 = PythonOperator(callable=my_callable, task_id="my_task_2")
|
||||
|
||||
incorrect_name = PythonOperator(task_id="my_task")
|
||||
incorrect_name_2 = PythonOperator(callable=my_callable, task_id="my_task_2")
|
||||
|
||||
from my_module import MyClass
|
||||
|
||||
incorrect_name = MyClass(task_id="my_task")
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Any, Type
|
||||
from typing_extensions import override
|
||||
|
||||
# Error
|
||||
def foo(a, b):
|
||||
@@ -94,6 +95,31 @@ class Foo:
|
||||
def foo(self: "Foo", a: int, *params: str, **options: Any) -> int:
|
||||
pass
|
||||
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: Any, *params: str, **options: str) -> int:
|
||||
pass
|
||||
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: int, *params: str, **options: str) -> Any:
|
||||
pass
|
||||
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: int, *params: Any, **options: Any) -> int:
|
||||
pass
|
||||
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: int, *params: Any, **options: str) -> int:
|
||||
pass
|
||||
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: int, *params: str, **options: Any) -> int:
|
||||
pass
|
||||
|
||||
# OK
|
||||
@classmethod
|
||||
def foo(cls: Type["Foo"], a: int, b: int) -> int:
|
||||
|
||||
23
crates/ruff/resources/test/fixtures/flake8_async/ASYNC100.py
vendored
Normal file
23
crates/ruff/resources/test/fixtures/flake8_async/ASYNC100.py
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import urllib.request
|
||||
import requests
|
||||
import httpx
|
||||
|
||||
|
||||
async def foo():
|
||||
urllib.request.urlopen("http://example.com/foo/bar").read()
|
||||
|
||||
|
||||
async def foo():
|
||||
requests.get()
|
||||
|
||||
|
||||
async def foo():
|
||||
httpx.get()
|
||||
|
||||
|
||||
async def foo():
|
||||
requests.post()
|
||||
|
||||
|
||||
async def foo():
|
||||
httpx.post()
|
||||
31
crates/ruff/resources/test/fixtures/flake8_async/ASYNC101.py
vendored
Normal file
31
crates/ruff/resources/test/fixtures/flake8_async/ASYNC101.py
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
|
||||
async def foo():
|
||||
open("foo")
|
||||
|
||||
|
||||
async def foo():
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
async def foo():
|
||||
subprocess.run("foo")
|
||||
|
||||
|
||||
async def foo():
|
||||
subprocess.call("foo")
|
||||
|
||||
|
||||
async def foo():
|
||||
subprocess.foo(0)
|
||||
|
||||
|
||||
async def foo():
|
||||
os.wait4(10)
|
||||
|
||||
|
||||
async def foo():
|
||||
os.wait(12)
|
||||
13
crates/ruff/resources/test/fixtures/flake8_async/ASYNC102.py
vendored
Normal file
13
crates/ruff/resources/test/fixtures/flake8_async/ASYNC102.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import os
|
||||
|
||||
|
||||
async def foo():
|
||||
os.popen()
|
||||
|
||||
|
||||
async def foo():
|
||||
os.spawnl()
|
||||
|
||||
|
||||
async def foo():
|
||||
os.fspath("foo")
|
||||
3
crates/ruff/resources/test/fixtures/flake8_bandit/S601.py
vendored
Normal file
3
crates/ruff/resources/test/fixtures/flake8_bandit/S601.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import paramiko
|
||||
|
||||
paramiko.exec_command('something; really; unsafe')
|
||||
@@ -74,8 +74,8 @@ def query40():
|
||||
|
||||
def query41():
|
||||
return (
|
||||
"SELECT *"
|
||||
"FROM table"
|
||||
"SELECT * "
|
||||
"FROM table "
|
||||
f"WHERE var = {var}"
|
||||
)
|
||||
|
||||
@@ -84,7 +84,7 @@ query42 = cursor.execute("SELECT * FROM table WHERE var = %s" % var)
|
||||
query43 = cursor.execute(f"SELECT * FROM table WHERE var = {var}")
|
||||
query44 = cursor.execute("SELECT * FROM table WHERE var = {}".format(var))
|
||||
query45 = cursor.executemany("SELECT * FROM table WHERE var = %s" % var, [])
|
||||
|
||||
|
||||
# # pass
|
||||
query = "SELECT * FROM table WHERE id = 1"
|
||||
query = "DELETE FROM table WHERE id = 1"
|
||||
@@ -93,3 +93,12 @@ query = "UPDATE table SET id = 1"
|
||||
cursor.execute('SELECT * FROM table WHERE id = %s', var)
|
||||
cursor.execute('SELECT * FROM table WHERE id = 1')
|
||||
cursor.executemany('SELECT * FROM table WHERE id = %s', [var, var2])
|
||||
|
||||
# # INSERT without INTO (e.g. MySQL and derivatives)
|
||||
query = "INSERT table VALUES (%s)" % (var,)
|
||||
|
||||
# # REPLACE (e.g. MySQL and derivatives, SQLite)
|
||||
query = "REPLACE INTO table VALUES (%s)" % (var,)
|
||||
query = "REPLACE table VALUES (%s)" % (var,)
|
||||
|
||||
query = "Deselect something that is not SQL even though it has a ' from ' somewhere in %s." % "there"
|
||||
|
||||
8
crates/ruff/resources/test/fixtures/flake8_bandit/S609.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/flake8_bandit/S609.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
os.popen("chmod +w foo*")
|
||||
subprocess.Popen("/bin/chown root: *", shell=True)
|
||||
subprocess.Popen(["/usr/local/bin/rsync", "*", "some_where:"], shell=True)
|
||||
subprocess.Popen("/usr/local/bin/rsync * no_injection_here:")
|
||||
os.system("tar cf foo.tar bar/*")
|
||||
@@ -57,12 +57,16 @@ dict.fromkeys(("world",), True)
|
||||
{}.deploy(True, False)
|
||||
getattr(someobj, attrname, False)
|
||||
mylist.index(True)
|
||||
bool(False)
|
||||
int(True)
|
||||
str(int(False))
|
||||
cfg.get("hello", True)
|
||||
cfg.getint("hello", True)
|
||||
cfg.getfloat("hello", True)
|
||||
cfg.getboolean("hello", True)
|
||||
os.set_blocking(0, False)
|
||||
g_action.set_enabled(True)
|
||||
settings.set_enable_developer_extras(True)
|
||||
|
||||
|
||||
class Registry:
|
||||
@@ -80,3 +84,6 @@ class Registry:
|
||||
# FBT001: Boolean positional arg in function definition
|
||||
def foo(self, value: bool) -> None:
|
||||
pass
|
||||
|
||||
def foo(self) -> None:
|
||||
object.__setattr__(self, "flag", True)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import collections
|
||||
import datetime as dt
|
||||
from decimal import Decimal
|
||||
from fractions import Fraction
|
||||
import logging
|
||||
import operator
|
||||
from pathlib import Path
|
||||
@@ -158,12 +159,37 @@ def float_infinity_literal(value=float("1e999")):
|
||||
pass
|
||||
|
||||
|
||||
# But don't allow standard floats
|
||||
def float_int_is_wrong(value=float(3)):
|
||||
# Allow standard floats
|
||||
def float_int_okay(value=float(3)):
|
||||
pass
|
||||
|
||||
|
||||
def float_str_not_inf_or_nan_is_wrong(value=float("3.14")):
|
||||
def float_str_not_inf_or_nan_okay(value=float("3.14")):
|
||||
pass
|
||||
|
||||
|
||||
# Allow immutable str() value
|
||||
def str_okay(value=str("foo")):
|
||||
pass
|
||||
|
||||
|
||||
# Allow immutable bool() value
|
||||
def bool_okay(value=bool("bar")):
|
||||
pass
|
||||
|
||||
|
||||
# Allow immutable int() value
|
||||
def int_okay(value=int("12")):
|
||||
pass
|
||||
|
||||
|
||||
# Allow immutable complex() value
|
||||
def complex_okay(value=complex(1,2)):
|
||||
pass
|
||||
|
||||
|
||||
# Allow immutable Fraction() value
|
||||
def fraction_okay(value=Fraction(1,2)):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@@ -73,7 +73,18 @@ def f():
|
||||
|
||||
|
||||
def f():
|
||||
# Fixable.
|
||||
# Unfixable.
|
||||
for foo, bar, baz in (["1", "2", "3"],):
|
||||
if foo or baz:
|
||||
break
|
||||
else:
|
||||
bar = 1
|
||||
|
||||
print(bar)
|
||||
|
||||
|
||||
def f():
|
||||
# Unfixable (false negative) due to usage of `bar` outside of loop.
|
||||
for foo, bar, baz in (["1", "2", "3"],):
|
||||
if foo or baz:
|
||||
break
|
||||
@@ -85,4 +96,4 @@ def f():
|
||||
# Unfixable due to trailing underscore (`_line_` wouldn't be considered an ignorable
|
||||
# variable name).
|
||||
for line_ in range(self.header_lines):
|
||||
fp.readline()
|
||||
fp.readline()
|
||||
|
||||
@@ -120,3 +120,11 @@ class AbstractClass(ABC):
|
||||
@abstractmethod
|
||||
def empty_1(self, foo: Union[str, int, list, float]):
|
||||
...
|
||||
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Foo(ABC): # noqa: B024
|
||||
...
|
||||
|
||||
11
crates/ruff/resources/test/fixtures/flake8_bugbear/B033.py
vendored
Normal file
11
crates/ruff/resources/test/fixtures/flake8_bugbear/B033.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
###
|
||||
# Errors.
|
||||
###
|
||||
incorrect_set = {"value1", 23, 5, "value1"}
|
||||
incorrect_set = {1, 1}
|
||||
|
||||
###
|
||||
# Non-errors.
|
||||
###
|
||||
correct_set = {"value1", 23, 5}
|
||||
correct_set = {5, "5"}
|
||||
@@ -1,3 +1,6 @@
|
||||
from itertools import count, cycle, repeat
|
||||
|
||||
# Errors
|
||||
zip()
|
||||
zip(range(3))
|
||||
zip("a", "b")
|
||||
@@ -5,6 +8,18 @@ zip("a", "b", *zip("c"))
|
||||
zip(zip("a"), strict=False)
|
||||
zip(zip("a", strict=True))
|
||||
|
||||
# OK
|
||||
zip(range(3), strict=True)
|
||||
zip("a", "b", strict=False)
|
||||
zip("a", "b", "c", strict=True)
|
||||
|
||||
# OK (infinite iterators).
|
||||
zip([1, 2, 3], cycle("ABCDEF"))
|
||||
zip([1, 2, 3], count())
|
||||
zip([1, 2, 3], repeat(1))
|
||||
zip([1, 2, 3], repeat(1, None))
|
||||
zip([1, 2, 3], repeat(1, times=None))
|
||||
|
||||
# Errors (limited iterators).
|
||||
zip([1, 2, 3], repeat(1, 1))
|
||||
zip([1, 2, 3], repeat(1, times=4))
|
||||
|
||||
@@ -631,3 +631,11 @@ result = function(
|
||||
the_first_one = next(
|
||||
(i for i in range(10) if i // 2 == 0) # COM812 fix should include the final bracket
|
||||
)
|
||||
|
||||
foo = namedtuple(
|
||||
name="foo",
|
||||
status="bar",
|
||||
message="sfdsdfsdgs fsdfsdf output!dsfdfsdjkg ghfskdjghkdssd sd fsdf s\n"[
|
||||
:20
|
||||
],
|
||||
)
|
||||
|
||||
@@ -9,6 +9,10 @@ def f_a_short():
|
||||
raise RuntimeError("Error")
|
||||
|
||||
|
||||
def f_a_empty():
|
||||
raise RuntimeError("")
|
||||
|
||||
|
||||
def f_b():
|
||||
example = "example"
|
||||
raise RuntimeError(f"This is an {example} exception")
|
||||
|
||||
8
crates/ruff/resources/test/fixtures/flake8_fixme/T00.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/flake8_fixme/T00.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# TODO: todo
|
||||
# todo: todo
|
||||
# XXX: xxx
|
||||
# xxx: xxx
|
||||
# HACK: hack
|
||||
# hack: hack
|
||||
# FIXME: fixme
|
||||
# fixme: fixme
|
||||
7
crates/ruff/resources/test/fixtures/flake8_future_annotations/edge_case.py
vendored
Normal file
7
crates/ruff/resources/test/fixtures/flake8_future_annotations/edge_case.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
from typing import List
|
||||
import typing as t
|
||||
|
||||
|
||||
def main(_: List[int]) -> None:
|
||||
a_list: t.List[str] = []
|
||||
a_list.append("hello")
|
||||
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/from_typing_import.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/from_typing_import.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
from typing import List
|
||||
|
||||
|
||||
def main() -> None:
|
||||
a_list: List[str] = []
|
||||
a_list.append("hello")
|
||||
8
crates/ruff/resources/test/fixtures/flake8_future_annotations/from_typing_import_many.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/flake8_future_annotations/from_typing_import_many.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
from typing import Dict, List, Optional, Set, Union, cast
|
||||
|
||||
|
||||
def main() -> None:
|
||||
a_list: List[Optional[str]] = []
|
||||
a_list.append("hello")
|
||||
a_dict = cast(Dict[int | None, Union[int, Set[bool]]], {})
|
||||
a_dict[1] = {True, False}
|
||||
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/import_typing.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/import_typing.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import typing
|
||||
|
||||
|
||||
def main() -> None:
|
||||
a_list: typing.List[str] = []
|
||||
a_list.append("hello")
|
||||
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/import_typing_as.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/import_typing_as.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import typing as t
|
||||
|
||||
|
||||
def main() -> None:
|
||||
a_list: t.List[str] = []
|
||||
a_list.append("hello")
|
||||
@@ -0,0 +1,7 @@
|
||||
def main() -> None:
|
||||
a_list: list[str] = []
|
||||
a_list.append("hello")
|
||||
|
||||
|
||||
def hello(y: dict[str, int]) -> None:
|
||||
del y
|
||||
7
crates/ruff/resources/test/fixtures/flake8_future_annotations/no_future_import_uses_union.py
vendored
Normal file
7
crates/ruff/resources/test/fixtures/flake8_future_annotations/no_future_import_uses_union.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
def main() -> None:
|
||||
a_list: list[str] | None = []
|
||||
a_list.append("hello")
|
||||
|
||||
|
||||
def hello(y: dict[str, int] | None) -> None:
|
||||
del y
|
||||
@@ -0,0 +1,8 @@
|
||||
def main() -> None:
|
||||
a_list: list[str | None] = []
|
||||
a_list.append("hello")
|
||||
|
||||
|
||||
def hello(y: dict[str | None, int]) -> None:
|
||||
z: tuple[str, str | None, str] = tuple(y)
|
||||
del z
|
||||
3
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_no_types.py
vendored
Normal file
3
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_no_types.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
def main() -> str:
|
||||
a_str = "hello"
|
||||
return a_str
|
||||
10
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_non_simplifiable_types.py
vendored
Normal file
10
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_non_simplifiable_types.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class Stuff(NamedTuple):
|
||||
x: int
|
||||
|
||||
|
||||
def main() -> None:
|
||||
a_list = Stuff(5)
|
||||
print(a_list)
|
||||
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_uses_future.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_uses_future.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def main() -> None:
|
||||
a_list: list[str] = []
|
||||
a_list.append("hello")
|
||||
8
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_variable_name.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_variable_name.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import typing
|
||||
|
||||
IRRELEVANT = typing.TypeVar
|
||||
|
||||
|
||||
def main() -> None:
|
||||
List: list[str] = []
|
||||
List.append("hello")
|
||||
65
crates/ruff/resources/test/fixtures/flake8_pyi/PYI013.py
vendored
Normal file
65
crates/ruff/resources/test/fixtures/flake8_pyi/PYI013.py
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
class OneAttributeClass:
|
||||
value: int
|
||||
...
|
||||
|
||||
|
||||
class OneAttributeClass2:
|
||||
...
|
||||
value: int
|
||||
|
||||
|
||||
class TwoEllipsesClass:
|
||||
...
|
||||
...
|
||||
|
||||
|
||||
class DocstringClass:
|
||||
"""
|
||||
My body only contains an ellipsis.
|
||||
"""
|
||||
|
||||
...
|
||||
|
||||
|
||||
class NonEmptyChild(Exception):
|
||||
value: int
|
||||
...
|
||||
|
||||
|
||||
class NonEmptyChild2(Exception):
|
||||
...
|
||||
value: int
|
||||
|
||||
|
||||
class NonEmptyWithInit:
|
||||
value: int
|
||||
...
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
|
||||
class EmptyClass:
|
||||
...
|
||||
|
||||
|
||||
class EmptyEllipsis:
|
||||
...
|
||||
|
||||
|
||||
class Dog:
|
||||
eyes: int = 2
|
||||
|
||||
|
||||
class WithInit:
|
||||
value: int = 0
|
||||
|
||||
def __init__():
|
||||
...
|
||||
|
||||
|
||||
def function():
|
||||
...
|
||||
|
||||
|
||||
...
|
||||
56
crates/ruff/resources/test/fixtures/flake8_pyi/PYI013.pyi
vendored
Normal file
56
crates/ruff/resources/test/fixtures/flake8_pyi/PYI013.pyi
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
# Violations of PYI013
|
||||
|
||||
class OneAttributeClass:
|
||||
value: int
|
||||
... # Error
|
||||
|
||||
class OneAttributeClass2:
|
||||
... # Error
|
||||
value: int
|
||||
|
||||
class MyClass:
|
||||
...
|
||||
value: int
|
||||
|
||||
class TwoEllipsesClass:
|
||||
...
|
||||
... # Error
|
||||
|
||||
class DocstringClass:
|
||||
"""
|
||||
My body only contains an ellipsis.
|
||||
"""
|
||||
|
||||
... # Error
|
||||
|
||||
class NonEmptyChild(Exception):
|
||||
value: int
|
||||
... # Error
|
||||
|
||||
class NonEmptyChild2(Exception):
|
||||
... # Error
|
||||
value: int
|
||||
|
||||
class NonEmptyWithInit:
|
||||
value: int
|
||||
... # Error
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
# Not violations
|
||||
|
||||
class EmptyClass: ...
|
||||
class EmptyEllipsis: ...
|
||||
|
||||
class Dog:
|
||||
eyes: int = 2
|
||||
|
||||
class WithInit:
|
||||
value: int = 0
|
||||
|
||||
def __init__(): ...
|
||||
|
||||
def function(): ...
|
||||
|
||||
...
|
||||
9
crates/ruff/resources/test/fixtures/flake8_pyi/PYI024.py
vendored
Normal file
9
crates/ruff/resources/test/fixtures/flake8_pyi/PYI024.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import collections
|
||||
|
||||
person: collections.namedtuple # OK
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
person: namedtuple # OK
|
||||
|
||||
person = namedtuple("Person", ["name", "age"]) # OK
|
||||
11
crates/ruff/resources/test/fixtures/flake8_pyi/PYI024.pyi
vendored
Normal file
11
crates/ruff/resources/test/fixtures/flake8_pyi/PYI024.pyi
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import collections
|
||||
|
||||
person: collections.namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
person: namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
|
||||
|
||||
person = namedtuple(
|
||||
"Person", ["name", "age"]
|
||||
) # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
|
||||
19
crates/ruff/resources/test/fixtures/flake8_pyi/PYI025.py
vendored
Normal file
19
crates/ruff/resources/test/fixtures/flake8_pyi/PYI025.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
from collections.abc import Set as AbstractSet # Ok
|
||||
|
||||
|
||||
from collections.abc import Set # Ok
|
||||
|
||||
|
||||
from collections.abc import (
|
||||
Container,
|
||||
Sized,
|
||||
Set, # Ok
|
||||
ValuesView
|
||||
)
|
||||
|
||||
from collections.abc import (
|
||||
Container,
|
||||
Sized,
|
||||
Set as AbstractSet, # Ok
|
||||
ValuesView
|
||||
)
|
||||
19
crates/ruff/resources/test/fixtures/flake8_pyi/PYI025.pyi
vendored
Normal file
19
crates/ruff/resources/test/fixtures/flake8_pyi/PYI025.pyi
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
from collections.abc import Set as AbstractSet # Ok
|
||||
|
||||
|
||||
from collections.abc import Set # PYI025
|
||||
|
||||
|
||||
from collections.abc import (
|
||||
Container,
|
||||
Sized,
|
||||
Set, # PYI025
|
||||
ValuesView
|
||||
)
|
||||
|
||||
from collections.abc import (
|
||||
Container,
|
||||
Sized,
|
||||
Set as AbstractSet,
|
||||
ValuesView # Ok
|
||||
)
|
||||
57
crates/ruff/resources/test/fixtures/flake8_pyi/PYI029.py
vendored
Normal file
57
crates/ruff/resources/test/fixtures/flake8_pyi/PYI029.py
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
import builtins
|
||||
from abc import abstractmethod
|
||||
|
||||
|
||||
def __repr__(self) -> str:
|
||||
...
|
||||
|
||||
|
||||
def __str__(self) -> builtins.str:
|
||||
...
|
||||
|
||||
|
||||
def __repr__(self, /, foo) -> str:
|
||||
...
|
||||
|
||||
|
||||
def __repr__(self, *, foo) -> str:
|
||||
...
|
||||
|
||||
|
||||
class ShouldRemoveSingle:
|
||||
def __str__(self) -> builtins.str:
|
||||
...
|
||||
|
||||
|
||||
class ShouldRemove:
|
||||
def __repr__(self) -> str:
|
||||
...
|
||||
|
||||
def __str__(self) -> builtins.str:
|
||||
...
|
||||
|
||||
|
||||
class NoReturnSpecified:
|
||||
def __str__(self):
|
||||
...
|
||||
|
||||
def __repr__(self):
|
||||
...
|
||||
|
||||
|
||||
class NonMatchingArgs:
|
||||
def __str__(self, *, extra) -> builtins.str:
|
||||
...
|
||||
|
||||
def __repr__(self, /, extra) -> str:
|
||||
...
|
||||
|
||||
|
||||
class MatchingArgsButAbstract:
|
||||
@abstractmethod
|
||||
def __str__(self) -> builtins.str:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def __repr__(self) -> str:
|
||||
...
|
||||
28
crates/ruff/resources/test/fixtures/flake8_pyi/PYI029.pyi
vendored
Normal file
28
crates/ruff/resources/test/fixtures/flake8_pyi/PYI029.pyi
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import builtins
|
||||
from abc import abstractmethod
|
||||
|
||||
def __repr__(self) -> str: ...
|
||||
def __str__(self) -> builtins.str: ...
|
||||
def __repr__(self, /, foo) -> str: ...
|
||||
def __repr__(self, *, foo) -> str: ...
|
||||
|
||||
class ShouldRemoveSingle:
|
||||
def __str__(self) -> builtins.str: ... # Error: PYI029
|
||||
|
||||
class ShouldRemove:
|
||||
def __repr__(self) -> str: ... # Error: PYI029
|
||||
def __str__(self) -> builtins.str: ... # Error: PYI029
|
||||
|
||||
class NoReturnSpecified:
|
||||
def __str__(self): ...
|
||||
def __repr__(self): ...
|
||||
|
||||
class NonMatchingArgs:
|
||||
def __str__(self, *, extra) -> builtins.str: ...
|
||||
def __repr__(self, /, extra) -> str: ...
|
||||
|
||||
class MatchingArgsButAbstract:
|
||||
@abstractmethod
|
||||
def __str__(self) -> builtins.str: ...
|
||||
@abstractmethod
|
||||
def __repr__(self) -> str: ...
|
||||
24
crates/ruff/resources/test/fixtures/flake8_pyi/PYI032.py
vendored
Normal file
24
crates/ruff/resources/test/fixtures/flake8_pyi/PYI032.py
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
from typing import Any
|
||||
import typing
|
||||
|
||||
|
||||
class Bad:
|
||||
def __eq__(self, other: Any) -> bool: ... # Fine because not a stub file
|
||||
def __ne__(self, other: typing.Any) -> typing.Any: ... # Fine because not a stub file
|
||||
|
||||
|
||||
class Good:
|
||||
def __eq__(self, other: object) -> bool: ...
|
||||
|
||||
def __ne__(self, obj: object) -> int: ...
|
||||
|
||||
|
||||
class WeirdButFine:
|
||||
def __eq__(self, other: Any, strange_extra_arg: list[str]) -> Any: ...
|
||||
def __ne__(self, *, kw_only_other: Any) -> bool: ...
|
||||
|
||||
|
||||
class Unannotated:
|
||||
def __eq__(self) -> Any: ...
|
||||
def __ne__(self) -> bool: ...
|
||||
|
||||
24
crates/ruff/resources/test/fixtures/flake8_pyi/PYI032.pyi
vendored
Normal file
24
crates/ruff/resources/test/fixtures/flake8_pyi/PYI032.pyi
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
from typing import Any
|
||||
import typing
|
||||
|
||||
|
||||
class Bad:
|
||||
def __eq__(self, other: Any) -> bool: ... # Y032
|
||||
def __ne__(self, other: typing.Any) -> typing.Any: ... # Y032
|
||||
|
||||
|
||||
class Good:
|
||||
def __eq__(self, other: object) -> bool: ...
|
||||
|
||||
def __ne__(self, obj: object) -> int: ...
|
||||
|
||||
|
||||
class WeirdButFine:
|
||||
def __eq__(self, other: Any, strange_extra_arg: list[str]) -> Any: ...
|
||||
def __ne__(self, *, kw_only_other: Any) -> bool: ...
|
||||
|
||||
|
||||
class Unannotated:
|
||||
def __eq__(self) -> Any: ...
|
||||
def __ne__(self) -> bool: ...
|
||||
|
||||
280
crates/ruff/resources/test/fixtures/flake8_pyi/PYI034.py
vendored
Normal file
280
crates/ruff/resources/test/fixtures/flake8_pyi/PYI034.py
vendored
Normal file
@@ -0,0 +1,280 @@
|
||||
# flags: --extend-ignore=Y023
|
||||
|
||||
import abc
|
||||
import builtins
|
||||
import collections.abc
|
||||
import typing
|
||||
from abc import abstractmethod
|
||||
from collections.abc import AsyncIterable, AsyncIterator, Iterable, Iterator
|
||||
from typing import Any, overload
|
||||
|
||||
import typing_extensions
|
||||
from _typeshed import Self
|
||||
from typing_extensions import final
|
||||
|
||||
|
||||
class Bad(
|
||||
object
|
||||
): # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
|
||||
def __new__(cls, *args: Any, **kwargs: Any) -> Bad:
|
||||
... # Y034 "__new__" methods usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__new__", e.g. "def __new__(cls, *args: Any, **kwargs: Any) -> Self: ..."
|
||||
|
||||
def __repr__(self) -> str:
|
||||
... # Y029 Defining __repr__ or __str__ in a stub is almost always redundant
|
||||
|
||||
def __str__(self) -> builtins.str:
|
||||
... # Y029 Defining __repr__ or __str__ in a stub is almost always redundant
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
... # Y032 Prefer "object" to "Any" for the second parameter in "__eq__" methods
|
||||
|
||||
def __ne__(self, other: typing.Any) -> typing.Any:
|
||||
... # Y032 Prefer "object" to "Any" for the second parameter in "__ne__" methods
|
||||
|
||||
def __enter__(self) -> Bad:
|
||||
... # Y034 "__enter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__enter__", e.g. "def __enter__(self) -> Self: ..."
|
||||
|
||||
async def __aenter__(self) -> Bad:
|
||||
... # Y034 "__aenter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__aenter__", e.g. "async def __aenter__(self) -> Self: ..."
|
||||
|
||||
def __iadd__(self, other: Bad) -> Bad:
|
||||
... # Y034 "__iadd__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__iadd__", e.g. "def __iadd__(self, other: Bad) -> Self: ..."
|
||||
|
||||
|
||||
class AlsoBad(int, builtins.object):
|
||||
... # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
|
||||
|
||||
|
||||
class Good:
|
||||
def __new__(cls: type[Self], *args: Any, **kwargs: Any) -> Self:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def __str__(self) -> str:
|
||||
...
|
||||
|
||||
@abc.abstractmethod
|
||||
def __repr__(self) -> str:
|
||||
...
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
...
|
||||
|
||||
def __ne__(self, obj: object) -> int:
|
||||
...
|
||||
|
||||
def __enter__(self: Self) -> Self:
|
||||
...
|
||||
|
||||
async def __aenter__(self: Self) -> Self:
|
||||
...
|
||||
|
||||
def __ior__(self: Self, other: Self) -> Self:
|
||||
...
|
||||
|
||||
|
||||
class Fine:
|
||||
@overload
|
||||
def __new__(cls, foo: int) -> FineSubclass:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __new__(cls, *args: Any, **kwargs: Any) -> Fine:
|
||||
...
|
||||
|
||||
@abc.abstractmethod
|
||||
def __str__(self) -> str:
|
||||
...
|
||||
|
||||
@abc.abstractmethod
|
||||
def __repr__(self) -> str:
|
||||
...
|
||||
|
||||
def __eq__(self, other: Any, strange_extra_arg: list[str]) -> Any:
|
||||
...
|
||||
|
||||
def __ne__(self, *, kw_only_other: Any) -> bool:
|
||||
...
|
||||
|
||||
def __enter__(self) -> None:
|
||||
...
|
||||
|
||||
async def __aenter__(self) -> bool:
|
||||
...
|
||||
|
||||
|
||||
class FineSubclass(Fine):
|
||||
...
|
||||
|
||||
|
||||
class StrangeButAcceptable(str):
|
||||
@typing_extensions.overload
|
||||
def __new__(cls, foo: int) -> StrangeButAcceptableSubclass:
|
||||
...
|
||||
|
||||
@typing_extensions.overload
|
||||
def __new__(cls, *args: Any, **kwargs: Any) -> StrangeButAcceptable:
|
||||
...
|
||||
|
||||
def __str__(self) -> StrangeButAcceptable:
|
||||
...
|
||||
|
||||
def __repr__(self) -> StrangeButAcceptable:
|
||||
...
|
||||
|
||||
|
||||
class StrangeButAcceptableSubclass(StrangeButAcceptable):
|
||||
...
|
||||
|
||||
|
||||
class FineAndDandy:
|
||||
def __str__(self, weird_extra_arg) -> str:
|
||||
...
|
||||
|
||||
def __repr__(self, weird_extra_arg_with_default=...) -> str:
|
||||
...
|
||||
|
||||
|
||||
@final
|
||||
class WillNotBeSubclassed:
|
||||
def __new__(cls, *args: Any, **kwargs: Any) -> WillNotBeSubclassed:
|
||||
...
|
||||
|
||||
def __enter__(self) -> WillNotBeSubclassed:
|
||||
...
|
||||
|
||||
async def __aenter__(self) -> WillNotBeSubclassed:
|
||||
...
|
||||
|
||||
|
||||
# we don't emit an error for these; out of scope for a linter
|
||||
class InvalidButPluginDoesNotCrash:
|
||||
def __new__() -> InvalidButPluginDoesNotCrash:
|
||||
...
|
||||
|
||||
def __enter__() -> InvalidButPluginDoesNotCrash:
|
||||
...
|
||||
|
||||
async def __aenter__() -> InvalidButPluginDoesNotCrash:
|
||||
...
|
||||
|
||||
|
||||
class BadIterator1(Iterator[int]):
|
||||
def __iter__(self) -> Iterator[int]:
|
||||
... # Y034 "__iter__" methods in classes like "BadIterator1" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator1.__iter__", e.g. "def __iter__(self) -> Self: ..."
|
||||
|
||||
|
||||
class BadIterator2(
|
||||
typing.Iterator[int]
|
||||
): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
|
||||
def __iter__(self) -> Iterator[int]:
|
||||
... # Y034 "__iter__" methods in classes like "BadIterator2" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator2.__iter__", e.g. "def __iter__(self) -> Self: ..."
|
||||
|
||||
|
||||
class BadIterator3(
|
||||
typing.Iterator[int]
|
||||
): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
|
||||
def __iter__(self) -> collections.abc.Iterator[int]:
|
||||
... # Y034 "__iter__" methods in classes like "BadIterator3" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator3.__iter__", e.g. "def __iter__(self) -> Self: ..."
|
||||
|
||||
|
||||
class BadIterator4(Iterator[int]):
|
||||
# Note: *Iterable*, not *Iterator*, returned!
|
||||
def __iter__(self) -> Iterable[int]:
|
||||
... # Y034 "__iter__" methods in classes like "BadIterator4" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator4.__iter__", e.g. "def __iter__(self) -> Self: ..."
|
||||
|
||||
|
||||
class IteratorReturningIterable:
|
||||
def __iter__(self) -> Iterable[str]:
|
||||
... # Y045 "__iter__" methods should return an Iterator, not an Iterable
|
||||
|
||||
|
||||
class BadAsyncIterator(collections.abc.AsyncIterator[str]):
|
||||
def __aiter__(self) -> typing.AsyncIterator[str]:
|
||||
... # Y034 "__aiter__" methods in classes like "BadAsyncIterator" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadAsyncIterator.__aiter__", e.g. "def __aiter__(self) -> Self: ..." # Y022 Use "collections.abc.AsyncIterator[T]" instead of "typing.AsyncIterator[T]" (PEP 585 syntax)
|
||||
|
||||
|
||||
class AsyncIteratorReturningAsyncIterable:
|
||||
def __aiter__(self) -> AsyncIterable[str]:
|
||||
... # Y045 "__aiter__" methods should return an AsyncIterator, not an AsyncIterable
|
||||
|
||||
|
||||
class Abstract(Iterator[str]):
|
||||
@abstractmethod
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def __enter__(self) -> Abstract:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def __aenter__(self) -> Abstract:
|
||||
...
|
||||
|
||||
|
||||
class GoodIterator(Iterator[str]):
|
||||
def __iter__(self: Self) -> Self:
|
||||
...
|
||||
|
||||
|
||||
class GoodAsyncIterator(AsyncIterator[int]):
|
||||
def __aiter__(self: Self) -> Self:
|
||||
...
|
||||
|
||||
|
||||
class DoesNotInheritFromIterator:
|
||||
def __iter__(self) -> DoesNotInheritFromIterator:
|
||||
...
|
||||
|
||||
|
||||
class Unannotated:
|
||||
def __new__(cls, *args, **kwargs):
|
||||
...
|
||||
|
||||
def __iter__(self):
|
||||
...
|
||||
|
||||
def __aiter__(self):
|
||||
...
|
||||
|
||||
async def __aenter__(self):
|
||||
...
|
||||
|
||||
def __repr__(self):
|
||||
...
|
||||
|
||||
def __str__(self):
|
||||
...
|
||||
|
||||
def __eq__(self):
|
||||
...
|
||||
|
||||
def __ne__(self):
|
||||
...
|
||||
|
||||
def __iadd__(self):
|
||||
...
|
||||
|
||||
def __ior__(self):
|
||||
...
|
||||
|
||||
|
||||
def __repr__(self) -> str:
|
||||
...
|
||||
|
||||
|
||||
def __str__(self) -> str:
|
||||
...
|
||||
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
...
|
||||
|
||||
|
||||
def __ne__(self, other: Any) -> bool:
|
||||
...
|
||||
|
||||
|
||||
def __imul__(self, other: Any) -> list[str]:
|
||||
...
|
||||
188
crates/ruff/resources/test/fixtures/flake8_pyi/PYI034.pyi
vendored
Normal file
188
crates/ruff/resources/test/fixtures/flake8_pyi/PYI034.pyi
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
# flags: --extend-ignore=Y023
|
||||
|
||||
import abc
|
||||
import builtins
|
||||
import collections.abc
|
||||
import typing
|
||||
from abc import abstractmethod
|
||||
from collections.abc import AsyncIterable, AsyncIterator, Iterable, Iterator
|
||||
from typing import Any, overload
|
||||
|
||||
import typing_extensions
|
||||
from _typeshed import Self
|
||||
from typing_extensions import final
|
||||
|
||||
class Bad(
|
||||
object
|
||||
): # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
|
||||
def __new__(
|
||||
cls, *args: Any, **kwargs: Any
|
||||
) -> Bad: ... # Y034 "__new__" methods usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__new__", e.g. "def __new__(cls, *args: Any, **kwargs: Any) -> Self: ..."
|
||||
def __repr__(
|
||||
self,
|
||||
) -> str: ... # Y029 Defining __repr__ or __str__ in a stub is almost always redundant
|
||||
def __str__(
|
||||
self,
|
||||
) -> builtins.str: ... # Y029 Defining __repr__ or __str__ in a stub is almost always redundant
|
||||
def __eq__(
|
||||
self, other: Any
|
||||
) -> bool: ... # Y032 Prefer "object" to "Any" for the second parameter in "__eq__" methods
|
||||
def __ne__(
|
||||
self, other: typing.Any
|
||||
) -> typing.Any: ... # Y032 Prefer "object" to "Any" for the second parameter in "__ne__" methods
|
||||
def __enter__(
|
||||
self,
|
||||
) -> Bad: ... # Y034 "__enter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__enter__", e.g. "def __enter__(self) -> Self: ..."
|
||||
async def __aenter__(
|
||||
self,
|
||||
) -> Bad: ... # Y034 "__aenter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__aenter__", e.g. "async def __aenter__(self) -> Self: ..."
|
||||
def __iadd__(
|
||||
self, other: Bad
|
||||
) -> Bad: ... # Y034 "__iadd__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__iadd__", e.g. "def __iadd__(self, other: Bad) -> Self: ..."
|
||||
|
||||
class AlsoBad(
|
||||
int, builtins.object
|
||||
): ... # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
|
||||
|
||||
class Good:
|
||||
def __new__(cls: type[Self], *args: Any, **kwargs: Any) -> Self: ...
|
||||
@abstractmethod
|
||||
def __str__(self) -> str: ...
|
||||
@abc.abstractmethod
|
||||
def __repr__(self) -> str: ...
|
||||
def __eq__(self, other: object) -> bool: ...
|
||||
def __ne__(self, obj: object) -> int: ...
|
||||
def __enter__(self: Self) -> Self: ...
|
||||
async def __aenter__(self: Self) -> Self: ...
|
||||
def __ior__(self: Self, other: Self) -> Self: ...
|
||||
|
||||
class Fine:
|
||||
@overload
|
||||
def __new__(cls, foo: int) -> FineSubclass: ...
|
||||
@overload
|
||||
def __new__(cls, *args: Any, **kwargs: Any) -> Fine: ...
|
||||
@abc.abstractmethod
|
||||
def __str__(self) -> str: ...
|
||||
@abc.abstractmethod
|
||||
def __repr__(self) -> str: ...
|
||||
def __eq__(self, other: Any, strange_extra_arg: list[str]) -> Any: ...
|
||||
def __ne__(self, *, kw_only_other: Any) -> bool: ...
|
||||
def __enter__(self) -> None: ...
|
||||
async def __aenter__(self) -> bool: ...
|
||||
|
||||
class FineSubclass(Fine): ...
|
||||
|
||||
class StrangeButAcceptable(str):
|
||||
@typing_extensions.overload
|
||||
def __new__(cls, foo: int) -> StrangeButAcceptableSubclass: ...
|
||||
@typing_extensions.overload
|
||||
def __new__(cls, *args: Any, **kwargs: Any) -> StrangeButAcceptable: ...
|
||||
def __str__(self) -> StrangeButAcceptable: ...
|
||||
def __repr__(self) -> StrangeButAcceptable: ...
|
||||
|
||||
class StrangeButAcceptableSubclass(StrangeButAcceptable): ...
|
||||
|
||||
class FineAndDandy:
|
||||
def __str__(self, weird_extra_arg) -> str: ...
|
||||
def __repr__(self, weird_extra_arg_with_default=...) -> str: ...
|
||||
|
||||
@final
|
||||
class WillNotBeSubclassed:
|
||||
def __new__(cls, *args: Any, **kwargs: Any) -> WillNotBeSubclassed: ...
|
||||
def __enter__(self) -> WillNotBeSubclassed: ...
|
||||
async def __aenter__(self) -> WillNotBeSubclassed: ...
|
||||
|
||||
# we don't emit an error for these; out of scope for a linter
|
||||
class InvalidButPluginDoesNotCrash:
|
||||
def __new__() -> InvalidButPluginDoesNotCrash: ...
|
||||
def __enter__() -> InvalidButPluginDoesNotCrash: ...
|
||||
async def __aenter__() -> InvalidButPluginDoesNotCrash: ...
|
||||
|
||||
class BadIterator1(Iterator[int]):
|
||||
def __iter__(
|
||||
self,
|
||||
) -> Iterator[
|
||||
int
|
||||
]: ... # Y034 "__iter__" methods in classes like "BadIterator1" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator1.__iter__", e.g. "def __iter__(self) -> Self: ..."
|
||||
|
||||
class BadIterator2(
|
||||
typing.Iterator[int]
|
||||
): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
|
||||
def __iter__(
|
||||
self,
|
||||
) -> Iterator[
|
||||
int
|
||||
]: ... # Y034 "__iter__" methods in classes like "BadIterator2" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator2.__iter__", e.g. "def __iter__(self) -> Self: ..."
|
||||
|
||||
class BadIterator3(
|
||||
typing.Iterator[int]
|
||||
): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
|
||||
def __iter__(
|
||||
self,
|
||||
) -> collections.abc.Iterator[
|
||||
int
|
||||
]: ... # Y034 "__iter__" methods in classes like "BadIterator3" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator3.__iter__", e.g. "def __iter__(self) -> Self: ..."
|
||||
|
||||
class BadIterator4(Iterator[int]):
|
||||
# Note: *Iterable*, not *Iterator*, returned!
|
||||
def __iter__(
|
||||
self,
|
||||
) -> Iterable[
|
||||
int
|
||||
]: ... # Y034 "__iter__" methods in classes like "BadIterator4" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator4.__iter__", e.g. "def __iter__(self) -> Self: ..."
|
||||
|
||||
class IteratorReturningIterable:
|
||||
def __iter__(
|
||||
self,
|
||||
) -> Iterable[
|
||||
str
|
||||
]: ... # Y045 "__iter__" methods should return an Iterator, not an Iterable
|
||||
|
||||
class BadAsyncIterator(collections.abc.AsyncIterator[str]):
|
||||
def __aiter__(
|
||||
self,
|
||||
) -> typing.AsyncIterator[
|
||||
str
|
||||
]: ... # Y034 "__aiter__" methods in classes like "BadAsyncIterator" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadAsyncIterator.__aiter__", e.g. "def __aiter__(self) -> Self: ..." # Y022 Use "collections.abc.AsyncIterator[T]" instead of "typing.AsyncIterator[T]" (PEP 585 syntax)
|
||||
|
||||
class AsyncIteratorReturningAsyncIterable:
|
||||
def __aiter__(
|
||||
self,
|
||||
) -> AsyncIterable[
|
||||
str
|
||||
]: ... # Y045 "__aiter__" methods should return an AsyncIterator, not an AsyncIterable
|
||||
|
||||
class Abstract(Iterator[str]):
|
||||
@abstractmethod
|
||||
def __iter__(self) -> Iterator[str]: ...
|
||||
@abstractmethod
|
||||
def __enter__(self) -> Abstract: ...
|
||||
@abstractmethod
|
||||
async def __aenter__(self) -> Abstract: ...
|
||||
|
||||
class GoodIterator(Iterator[str]):
|
||||
def __iter__(self: Self) -> Self: ...
|
||||
|
||||
class GoodAsyncIterator(AsyncIterator[int]):
|
||||
def __aiter__(self: Self) -> Self: ...
|
||||
|
||||
class DoesNotInheritFromIterator:
|
||||
def __iter__(self) -> DoesNotInheritFromIterator: ...
|
||||
|
||||
class Unannotated:
|
||||
def __new__(cls, *args, **kwargs): ...
|
||||
def __iter__(self): ...
|
||||
def __aiter__(self): ...
|
||||
async def __aenter__(self): ...
|
||||
def __repr__(self): ...
|
||||
def __str__(self): ...
|
||||
def __eq__(self): ...
|
||||
def __ne__(self): ...
|
||||
def __iadd__(self): ...
|
||||
def __ior__(self): ...
|
||||
|
||||
def __repr__(self) -> str: ...
|
||||
def __str__(self) -> str: ...
|
||||
def __eq__(self, other: Any) -> bool: ...
|
||||
def __ne__(self, other: Any) -> bool: ...
|
||||
def __imul__(self, other: Any) -> list[str]: ...
|
||||
15
crates/ruff/resources/test/fixtures/flake8_pyi/PYI035.py
vendored
Normal file
15
crates/ruff/resources/test/fixtures/flake8_pyi/PYI035.py
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
__all__: list[str]
|
||||
|
||||
__all__: list[str] = ["foo"]
|
||||
|
||||
|
||||
class Foo:
|
||||
__all__: list[str]
|
||||
__match_args__: tuple[str, ...]
|
||||
__slots__: tuple[str, ...]
|
||||
|
||||
|
||||
class Bar:
|
||||
__all__: list[str] = ["foo"]
|
||||
__match_args__: tuple[str, ...] = (1,)
|
||||
__slots__: tuple[str, ...] = "foo"
|
||||
13
crates/ruff/resources/test/fixtures/flake8_pyi/PYI035.pyi
vendored
Normal file
13
crates/ruff/resources/test/fixtures/flake8_pyi/PYI035.pyi
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
__all__: list[str] # Error: PYI035
|
||||
|
||||
__all__: list[str] = ["foo"]
|
||||
|
||||
class Foo:
|
||||
__all__: list[str]
|
||||
__match_args__: tuple[str, ...] # Error: PYI035
|
||||
__slots__: tuple[str, ...] # Error: PYI035
|
||||
|
||||
class Bar:
|
||||
__all__: list[str] = ["foo"]
|
||||
__match_args__: tuple[str, ...] = (1,)
|
||||
__slots__: tuple[str, ...] = "foo"
|
||||
85
crates/ruff/resources/test/fixtures/flake8_pyi/PYI045.py
vendored
Normal file
85
crates/ruff/resources/test/fixtures/flake8_pyi/PYI045.py
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
import collections.abc
|
||||
import typing
|
||||
from collections.abc import Iterator, Iterable
|
||||
|
||||
|
||||
class NoReturn:
|
||||
def __iter__(self):
|
||||
...
|
||||
|
||||
|
||||
class TypingIterableTReturn:
|
||||
def __iter__(self) -> typing.Iterable[int]:
|
||||
...
|
||||
|
||||
def not_iter(self) -> typing.Iterable[int]:
|
||||
...
|
||||
|
||||
|
||||
class TypingIterableReturn:
|
||||
def __iter__(self) -> typing.Iterable:
|
||||
...
|
||||
|
||||
def not_iter(self) -> typing.Iterable:
|
||||
...
|
||||
|
||||
|
||||
class CollectionsIterableTReturn:
|
||||
def __iter__(self) -> collections.abc.Iterable[int]:
|
||||
...
|
||||
|
||||
def not_iter(self) -> collections.abc.Iterable[int]:
|
||||
...
|
||||
|
||||
|
||||
class CollectionsIterableReturn:
|
||||
def __iter__(self) -> collections.abc.Iterable:
|
||||
...
|
||||
|
||||
def not_iter(self) -> collections.abc.Iterable:
|
||||
...
|
||||
|
||||
|
||||
class IterableReturn:
|
||||
def __iter__(self) -> Iterable:
|
||||
...
|
||||
|
||||
|
||||
class IteratorReturn:
|
||||
def __iter__(self) -> Iterator:
|
||||
...
|
||||
|
||||
|
||||
class IteratorTReturn:
|
||||
def __iter__(self) -> Iterator[int]:
|
||||
...
|
||||
|
||||
|
||||
class TypingIteratorReturn:
|
||||
def __iter__(self) -> typing.Iterator:
|
||||
...
|
||||
|
||||
|
||||
class TypingIteratorTReturn:
|
||||
def __iter__(self) -> typing.Iterator[int]:
|
||||
...
|
||||
|
||||
|
||||
class CollectionsIteratorReturn:
|
||||
def __iter__(self) -> collections.abc.Iterator:
|
||||
...
|
||||
|
||||
|
||||
class CollectionsIteratorTReturn:
|
||||
def __iter__(self) -> collections.abc.Iterator[int]:
|
||||
...
|
||||
|
||||
|
||||
class TypingAsyncIterableTReturn:
|
||||
def __aiter__(self) -> typing.AsyncIterable[int]:
|
||||
...
|
||||
|
||||
|
||||
class TypingAsyncIterableReturn:
|
||||
def __aiter__(self) -> typing.AsyncIterable:
|
||||
...
|
||||
49
crates/ruff/resources/test/fixtures/flake8_pyi/PYI045.pyi
vendored
Normal file
49
crates/ruff/resources/test/fixtures/flake8_pyi/PYI045.pyi
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
import collections.abc
|
||||
import typing
|
||||
from collections.abc import Iterator, Iterable
|
||||
|
||||
class NoReturn:
|
||||
def __iter__(self): ...
|
||||
|
||||
class TypingIterableTReturn:
|
||||
def __iter__(self) -> typing.Iterable[int]: ... # Error: PYI045
|
||||
def not_iter(self) -> typing.Iterable[int]: ...
|
||||
|
||||
class TypingIterableReturn:
|
||||
def __iter__(self) -> typing.Iterable: ... # Error: PYI045
|
||||
def not_iter(self) -> typing.Iterable: ...
|
||||
|
||||
class CollectionsIterableTReturn:
|
||||
def __iter__(self) -> collections.abc.Iterable[int]: ... # Error: PYI045
|
||||
def not_iter(self) -> collections.abc.Iterable[int]: ...
|
||||
|
||||
class CollectionsIterableReturn:
|
||||
def __iter__(self) -> collections.abc.Iterable: ... # Error: PYI045
|
||||
def not_iter(self) -> collections.abc.Iterable: ...
|
||||
|
||||
class IterableReturn:
|
||||
def __iter__(self) -> Iterable: ... # Error: PYI045
|
||||
|
||||
class IteratorReturn:
|
||||
def __iter__(self) -> Iterator: ...
|
||||
|
||||
class IteratorTReturn:
|
||||
def __iter__(self) -> Iterator[int]: ...
|
||||
|
||||
class TypingIteratorReturn:
|
||||
def __iter__(self) -> typing.Iterator: ...
|
||||
|
||||
class TypingIteratorTReturn:
|
||||
def __iter__(self) -> typing.Iterator[int]: ...
|
||||
|
||||
class CollectionsIteratorReturn:
|
||||
def __iter__(self) -> collections.abc.Iterator: ...
|
||||
|
||||
class CollectionsIteratorTReturn:
|
||||
def __iter__(self) -> collections.abc.Iterator[int]: ...
|
||||
|
||||
class TypingAsyncIterableTReturn:
|
||||
def __aiter__(self) -> typing.AsyncIterable[int]: ... # Error: PYI045
|
||||
|
||||
class TypingAsyncIterableReturn:
|
||||
def __aiter__(self) -> typing.AsyncIterable: ... # Error: PYI045
|
||||
19
crates/ruff/resources/test/fixtures/flake8_pyi/PYI048.py
vendored
Normal file
19
crates/ruff/resources/test/fixtures/flake8_pyi/PYI048.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
def bar(): # OK
|
||||
...
|
||||
|
||||
|
||||
def oof(): # OK, docstrings are handled by another rule
|
||||
"""oof"""
|
||||
print("foo")
|
||||
|
||||
|
||||
def foo(): # Ok not in Stub file
|
||||
"""foo"""
|
||||
print("foo")
|
||||
print("foo")
|
||||
|
||||
|
||||
def buzz(): # Ok not in Stub file
|
||||
print("fizz")
|
||||
print("buzz")
|
||||
print("test")
|
||||
20
crates/ruff/resources/test/fixtures/flake8_pyi/PYI048.pyi
vendored
Normal file
20
crates/ruff/resources/test/fixtures/flake8_pyi/PYI048.pyi
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
def bar():
|
||||
... # OK
|
||||
|
||||
|
||||
def oof(): # OK, docstrings are handled by another rule
|
||||
"""oof"""
|
||||
print("foo")
|
||||
|
||||
|
||||
|
||||
def foo(): # ERROR PYI048
|
||||
"""foo"""
|
||||
print("foo")
|
||||
print("foo")
|
||||
|
||||
|
||||
def buzz(): # ERROR PYI048
|
||||
print("fizz")
|
||||
print("buzz")
|
||||
print("test")
|
||||
32
crates/ruff/resources/test/fixtures/flake8_pyi/PYI050.py
vendored
Normal file
32
crates/ruff/resources/test/fixtures/flake8_pyi/PYI050.py
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
from typing import NoReturn, Never
|
||||
import typing_extensions
|
||||
|
||||
|
||||
def foo(arg):
|
||||
...
|
||||
|
||||
|
||||
def foo_int(arg: int):
|
||||
...
|
||||
|
||||
|
||||
def foo_no_return(arg: NoReturn):
|
||||
...
|
||||
|
||||
|
||||
def foo_no_return_typing_extensions(
|
||||
arg: typing_extensions.NoReturn,
|
||||
):
|
||||
...
|
||||
|
||||
|
||||
def foo_no_return_kwarg(arg: int, *, arg2: NoReturn):
|
||||
...
|
||||
|
||||
|
||||
def foo_no_return_pos_only(arg: int, /, arg2: NoReturn):
|
||||
...
|
||||
|
||||
|
||||
def foo_never(arg: Never):
|
||||
...
|
||||
12
crates/ruff/resources/test/fixtures/flake8_pyi/PYI050.pyi
vendored
Normal file
12
crates/ruff/resources/test/fixtures/flake8_pyi/PYI050.pyi
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
from typing import NoReturn, Never
|
||||
import typing_extensions
|
||||
|
||||
def foo(arg): ...
|
||||
def foo_int(arg: int): ...
|
||||
def foo_no_return(arg: NoReturn): ... # Error: PYI050
|
||||
def foo_no_return_typing_extensions(
|
||||
arg: typing_extensions.NoReturn,
|
||||
): ... # Error: PYI050
|
||||
def foo_no_return_kwarg(arg: int, *, arg2: NoReturn): ... # Error: PYI050
|
||||
def foo_no_return_pos_only(arg: int, /, arg2: NoReturn): ... # Error: PYI050
|
||||
def foo_never(arg: Never): ...
|
||||
93
crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.py
vendored
Normal file
93
crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.py
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
import builtins
|
||||
import typing
|
||||
from typing import TypeAlias, Final
|
||||
|
||||
field1: int
|
||||
field2: int = ...
|
||||
field3 = ... # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
field4: int = 0
|
||||
field41: int = 0xFFFFFFFF
|
||||
field42: int = 1234567890
|
||||
field43: int = -0xFFFFFFFF
|
||||
field44: int = -1234567890
|
||||
field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
|
||||
field6 = 0 # Y052 Need type annotation for "field6"
|
||||
field7 = b"" # Y052 Need type annotation for "field7"
|
||||
field71 = "foo" # Y052 Need type annotation for "field71"
|
||||
field72: str = "foo"
|
||||
field8 = False # Y052 Need type annotation for "field8"
|
||||
field81 = -1 # Y052 Need type annotation for "field81"
|
||||
field82: float = -98.43
|
||||
field83 = -42j # Y052 Need type annotation for "field83"
|
||||
field84 = 5 + 42j # Y052 Need type annotation for "field84"
|
||||
field85 = -5 - 42j # Y052 Need type annotation for "field85"
|
||||
field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None"
|
||||
Field95: TypeAlias = None
|
||||
Field96: TypeAlias = int | None
|
||||
Field97: TypeAlias = None | typing.SupportsInt | builtins.str | float | bool
|
||||
field19 = [1, 2, 3] # Y052 Need type annotation for "field19"
|
||||
field191: list[int] = [1, 2, 3]
|
||||
field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
|
||||
field201: tuple[int, ...] = (1, 2, 3)
|
||||
field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
|
||||
field211: set[int] = {1, 2, 3}
|
||||
field212 = {"foo": "bar"} # Y052 Need type annotation for "field212"
|
||||
field213: dict[str, str] = {"foo": "bar"}
|
||||
field22: Final = {"foo": 5}
|
||||
field221: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Y015 Only simple default values are allowed for assignments
|
||||
field223: list[int] = [*range(10)] # Y015 Only simple default values are allowed for assignments
|
||||
field224: list[int] = list(range(10)) # Y015 Only simple default values are allowed for assignments
|
||||
field225: list[object] = [{}, 1, 2] # Y015 Only simple default values are allowed for assignments
|
||||
field226: tuple[str | tuple[str, ...], ...] = ("foo", ("foo", "bar")) # Y015 Only simple default values are allowed for assignments
|
||||
field227: dict[str, object] = {"foo": {"foo": "bar"}} # Y015 Only simple default values are allowed for assignments
|
||||
field228: dict[str, list[object]] = {"foo": []} # Y015 Only simple default values are allowed for assignments
|
||||
# When parsed, this case results in `None` being placed in the `.keys` list for the `ast.Dict` node
|
||||
field229: dict[int, int] = {1: 2, **{3: 4}} # Y015 Only simple default values are allowed for assignments
|
||||
field23 = "foo" + "bar" # Y015 Only simple default values are allowed for assignments
|
||||
field24 = b"foo" + b"bar" # Y015 Only simple default values are allowed for assignments
|
||||
field25 = 5 * 5 # Y015 Only simple default values are allowed for assignments
|
||||
|
||||
# We shouldn't emit Y015 within functions
|
||||
def f():
|
||||
field26: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
|
||||
# We shouldn't emit Y015 for __slots__ or __match_args__
|
||||
class Class1:
|
||||
__slots__ = (
|
||||
'_one',
|
||||
'_two',
|
||||
'_three',
|
||||
'_four',
|
||||
'_five',
|
||||
'_six',
|
||||
'_seven',
|
||||
'_eight',
|
||||
'_nine',
|
||||
'_ten',
|
||||
'_eleven',
|
||||
)
|
||||
|
||||
__match_args__ = (
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
'four',
|
||||
'five',
|
||||
'six',
|
||||
'seven',
|
||||
'eight',
|
||||
'nine',
|
||||
'ten',
|
||||
'eleven',
|
||||
)
|
||||
|
||||
# We shouldn't emit Y015 for __all__
|
||||
__all__ = ["Class1"]
|
||||
|
||||
# Ignore the following for PYI015
|
||||
field26 = typing.Sequence[int]
|
||||
field27 = list[str]
|
||||
field28 = builtins.str
|
||||
field29 = str
|
||||
field30 = str | bytes | None
|
||||
100
crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.pyi
vendored
Normal file
100
crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.pyi
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
import builtins
|
||||
import typing
|
||||
from typing import TypeAlias, Final, NewType, TypeVar, TypeVarTuple, ParamSpec
|
||||
|
||||
# We shouldn't emit Y015 for simple default values
|
||||
field1: int
|
||||
field2: int = ...
|
||||
field3 = ... # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
field4: int = 0
|
||||
field41: int = 0xFFFFFFFF
|
||||
field42: int = 1234567890
|
||||
field43: int = -0xFFFFFFFF
|
||||
field44: int = -1234567890
|
||||
field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
|
||||
field6 = 0 # Y052 Need type annotation for "field6"
|
||||
field7 = b"" # Y052 Need type annotation for "field7"
|
||||
field71 = "foo" # Y052 Need type annotation for "field71"
|
||||
field72: str = "foo"
|
||||
field8 = False # Y052 Need type annotation for "field8"
|
||||
field81 = -1 # Y052 Need type annotation for "field81"
|
||||
field82: float = -98.43
|
||||
field83 = -42j # Y052 Need type annotation for "field83"
|
||||
field84 = 5 + 42j # Y052 Need type annotation for "field84"
|
||||
field85 = -5 - 42j # Y052 Need type annotation for "field85"
|
||||
field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None"
|
||||
Field95: TypeAlias = None
|
||||
Field96: TypeAlias = int | None
|
||||
Field97: TypeAlias = None | typing.SupportsInt | builtins.str | float | bool
|
||||
Field98 = NewType('MyInt', int)
|
||||
Field99 = TypeVar('Field99')
|
||||
Field100 = TypeVarTuple('Field100')
|
||||
Field101 = ParamSpec('Field101')
|
||||
field19 = [1, 2, 3] # Y052 Need type annotation for "field19"
|
||||
field191: list[int] = [1, 2, 3]
|
||||
field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
|
||||
field201: tuple[int, ...] = (1, 2, 3)
|
||||
field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
|
||||
field211: set[int] = {1, 2, 3}
|
||||
field212 = {"foo": "bar"} # Y052 Need type annotation for "field212"
|
||||
field213: dict[str, str] = {"foo": "bar"}
|
||||
field22: Final = {"foo": 5}
|
||||
|
||||
# We *should* emit Y015 for more complex default values
|
||||
field221: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Y015 Only simple default values are allowed for assignments
|
||||
field223: list[int] = [*range(10)] # Y015 Only simple default values are allowed for assignments
|
||||
field224: list[int] = list(range(10)) # Y015 Only simple default values are allowed for assignments
|
||||
field225: list[object] = [{}, 1, 2] # Y015 Only simple default values are allowed for assignments
|
||||
field226: tuple[str | tuple[str, ...], ...] = ("foo", ("foo", "bar")) # Y015 Only simple default values are allowed for assignments
|
||||
field227: dict[str, object] = {"foo": {"foo": "bar"}} # Y015 Only simple default values are allowed for assignments
|
||||
field228: dict[str, list[object]] = {"foo": []} # Y015 Only simple default values are allowed for assignments
|
||||
# When parsed, this case results in `None` being placed in the `.keys` list for the `ast.Dict` node
|
||||
field229: dict[int, int] = {1: 2, **{3: 4}} # Y015 Only simple default values are allowed for assignments
|
||||
field23 = "foo" + "bar" # Y015 Only simple default values are allowed for assignments
|
||||
field24 = b"foo" + b"bar" # Y015 Only simple default values are allowed for assignments
|
||||
field25 = 5 * 5 # Y015 Only simple default values are allowed for assignments
|
||||
|
||||
# We shouldn't emit Y015 within functions
|
||||
def f():
|
||||
field26: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
|
||||
# We shouldn't emit Y015 for __slots__ or __match_args__
|
||||
class Class1:
|
||||
__slots__ = (
|
||||
'_one',
|
||||
'_two',
|
||||
'_three',
|
||||
'_four',
|
||||
'_five',
|
||||
'_six',
|
||||
'_seven',
|
||||
'_eight',
|
||||
'_nine',
|
||||
'_ten',
|
||||
'_eleven',
|
||||
)
|
||||
|
||||
__match_args__ = (
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
'four',
|
||||
'five',
|
||||
'six',
|
||||
'seven',
|
||||
'eight',
|
||||
'nine',
|
||||
'ten',
|
||||
'eleven',
|
||||
)
|
||||
|
||||
# We shouldn't emit Y015 for __all__
|
||||
__all__ = ["Class1"]
|
||||
|
||||
# Ignore the following for PYI015
|
||||
field26 = typing.Sequence[int]
|
||||
field27 = list[str]
|
||||
field28 = builtins.str
|
||||
field29 = str
|
||||
field30 = str | bytes | None
|
||||
38
crates/ruff/resources/test/fixtures/flake8_pyi/PYI053.py
vendored
Normal file
38
crates/ruff/resources/test/fixtures/flake8_pyi/PYI053.py
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
def f1(x: str = "50 character stringggggggggggggggggggggggggggggggg") -> None:
|
||||
...
|
||||
|
||||
|
||||
def f2(x: str = "51 character stringgggggggggggggggggggggggggggggggg") -> None:
|
||||
...
|
||||
|
||||
|
||||
def f3(x: str = "50 character stringggggggggggggggggggggggggggggg\U0001f600") -> None:
|
||||
...
|
||||
|
||||
|
||||
def f4(x: str = "51 character stringgggggggggggggggggggggggggggggg\U0001f600") -> None:
|
||||
...
|
||||
|
||||
|
||||
def f5(x: bytes = b"50 character byte stringgggggggggggggggggggggggggg") -> None:
|
||||
...
|
||||
|
||||
|
||||
def f6(x: bytes = b"51 character byte stringgggggggggggggggggggggggggg") -> None:
|
||||
...
|
||||
|
||||
|
||||
def f7(x: bytes = b"50 character byte stringggggggggggggggggggggggggg\xff") -> None:
|
||||
...
|
||||
|
||||
|
||||
def f8(x: bytes = b"50 character byte stringgggggggggggggggggggggggggg\xff") -> None:
|
||||
...
|
||||
|
||||
|
||||
foo: str = "50 character stringggggggggggggggggggggggggggggggg"
|
||||
bar: str = "51 character stringgggggggggggggggggggggggggggggggg"
|
||||
|
||||
baz: bytes = b"50 character byte stringgggggggggggggggggggggggggg"
|
||||
|
||||
qux: bytes = b"51 character byte stringggggggggggggggggggggggggggg\xff"
|
||||
30
crates/ruff/resources/test/fixtures/flake8_pyi/PYI053.pyi
vendored
Normal file
30
crates/ruff/resources/test/fixtures/flake8_pyi/PYI053.pyi
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
def f1(x: str = "50 character stringggggggggggggggggggggggggggggggg") -> None: ... # OK
|
||||
def f2(
|
||||
x: str = "51 character stringgggggggggggggggggggggggggggggggg", # Error: PYI053
|
||||
) -> None: ...
|
||||
def f3(
|
||||
x: str = "50 character stringgggggggggggggggggggggggggggggg\U0001f600", # OK
|
||||
) -> None: ...
|
||||
def f4(
|
||||
x: str = "51 character stringggggggggggggggggggggggggggggggg\U0001f600", # Error: PYI053
|
||||
) -> None: ...
|
||||
def f5(
|
||||
x: bytes = b"50 character byte stringgggggggggggggggggggggggggg", # OK
|
||||
) -> None: ...
|
||||
def f6(
|
||||
x: bytes = b"51 character byte stringgggggggggggggggggggggggggg", # Error: PYI053
|
||||
) -> None: ...
|
||||
def f7(
|
||||
x: bytes = b"50 character byte stringggggggggggggggggggggggggg\xff", # OK
|
||||
) -> None: ...
|
||||
def f8(
|
||||
x: bytes = b"51 character byte stringgggggggggggggggggggggggggg\xff", # Error: PYI053
|
||||
) -> None: ...
|
||||
|
||||
foo: str = "50 character stringggggggggggggggggggggggggggggggg" # OK
|
||||
|
||||
bar: str = "51 character stringgggggggggggggggggggggggggggggggg" # Error: PYI053
|
||||
|
||||
baz: bytes = b"50 character byte stringgggggggggggggggggggggggggg" # OK
|
||||
|
||||
qux: bytes = b"51 character byte stringggggggggggggggggggggggggggg\xff" # Error: PYI053
|
||||
20
crates/ruff/resources/test/fixtures/flake8_pyi/PYI054.py
vendored
Normal file
20
crates/ruff/resources/test/fixtures/flake8_pyi/PYI054.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
field01: int = 0xFFFFFFFF
|
||||
field02: int = 0xFFFFFFFFF
|
||||
field03: int = -0xFFFFFFFF
|
||||
field04: int = -0xFFFFFFFFF
|
||||
|
||||
field05: int = 1234567890
|
||||
field06: int = 12_456_890
|
||||
field07: int = 12345678901
|
||||
field08: int = -1234567801
|
||||
field09: int = -234_567_890
|
||||
|
||||
field10: float = 123.456789
|
||||
field11: float = 123.4567890
|
||||
field12: float = -123.456789
|
||||
field13: float = -123.567_890
|
||||
|
||||
field14: complex = 1e1234567j
|
||||
field15: complex = 1e12345678j
|
||||
field16: complex = -1e1234567j
|
||||
field17: complex = 1e123456789j
|
||||
20
crates/ruff/resources/test/fixtures/flake8_pyi/PYI054.pyi
vendored
Normal file
20
crates/ruff/resources/test/fixtures/flake8_pyi/PYI054.pyi
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
field01: int = 0xFFFFFFFF
|
||||
field02: int = 0xFFFFFFFFF # Error: PYI054
|
||||
field03: int = -0xFFFFFFFF
|
||||
field04: int = -0xFFFFFFFFF # Error: PYI054
|
||||
|
||||
field05: int = 1234567890
|
||||
field06: int = 12_456_890
|
||||
field07: int = 12345678901 # Error: PYI054
|
||||
field08: int = -1234567801
|
||||
field09: int = -234_567_890 # Error: PYI054
|
||||
|
||||
field10: float = 123.456789
|
||||
field11: float = 123.4567890 # Error: PYI054
|
||||
field12: float = -123.456789
|
||||
field13: float = -123.567_890 # Error: PYI054
|
||||
|
||||
field14: complex = 1e1234567j
|
||||
field15: complex = 1e12345678j # Error: PYI054
|
||||
field16: complex = -1e1234567j
|
||||
field17: complex = 1e123456789j # Error: PYI054
|
||||
@@ -39,3 +39,8 @@ def test_error():
|
||||
message
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
assert something # OK
|
||||
assert something and something_else # Error
|
||||
assert something and something_else and something_third # Error
|
||||
|
||||
@@ -272,3 +272,34 @@ def str_to_bool(val):
|
||||
if isinstance(val, bool):
|
||||
return some_obj
|
||||
return val
|
||||
|
||||
|
||||
# Mixed assignments
|
||||
def function_assignment(x):
|
||||
def f(): ...
|
||||
|
||||
return f
|
||||
|
||||
|
||||
def class_assignment(x):
|
||||
class Foo: ...
|
||||
|
||||
return Foo
|
||||
|
||||
|
||||
def mixed_function_assignment(x):
|
||||
if x:
|
||||
def f(): ...
|
||||
else:
|
||||
f = 42
|
||||
|
||||
return f
|
||||
|
||||
|
||||
def mixed_class_assignment(x):
|
||||
if x:
|
||||
class Foo: ...
|
||||
else:
|
||||
Foo = 42
|
||||
|
||||
return Foo
|
||||
|
||||
@@ -86,9 +86,16 @@ while x > 0:
|
||||
):
|
||||
print("Bad module!")
|
||||
|
||||
# SIM102
|
||||
if node.module:
|
||||
if node.module == "multiprocessing" or node.module.startswith(
|
||||
# SIM102 (auto-fixable)
|
||||
if node.module012345678:
|
||||
if node.module == "multiprocß9💣2ℝ" or node.module.startswith(
|
||||
"multiprocessing."
|
||||
):
|
||||
print("Bad module!")
|
||||
|
||||
# SIM102 (not auto-fixable)
|
||||
if node.module0123456789:
|
||||
if node.module == "multiprocß9💣2ℝ" or node.module.startswith(
|
||||
"multiprocessing."
|
||||
):
|
||||
print("Bad module!")
|
||||
|
||||
@@ -80,17 +80,25 @@ else:
|
||||
|
||||
# SIM108
|
||||
if a:
|
||||
b = cccccccccccccccccccccccccccccccccccc
|
||||
b = "cccccccccccccccccccccccccccccccccß"
|
||||
else:
|
||||
b = ddddddddddddddddddddddddddddddddddddd
|
||||
b = "ddddddddddddddddddddddddddddddddd💣"
|
||||
|
||||
|
||||
# OK (too long)
|
||||
if True:
|
||||
if a:
|
||||
b = cccccccccccccccccccccccccccccccccccc
|
||||
b = ccccccccccccccccccccccccccccccccccc
|
||||
else:
|
||||
b = ddddddddddddddddddddddddddddddddddddd
|
||||
b = ddddddddddddddddddddddddddddddddddd
|
||||
|
||||
|
||||
# OK (too long with tabs)
|
||||
if True:
|
||||
if a:
|
||||
b = ccccccccccccccccccccccccccccccccccc
|
||||
else:
|
||||
b = ddddddddddddddddddddddddddddddddddd
|
||||
|
||||
|
||||
# SIM108 (without fix due to trailing comment)
|
||||
|
||||
@@ -155,3 +155,19 @@ def f():
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
# SIM110
|
||||
for x in "012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ":
|
||||
if x.isdigit():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# OK (too long)
|
||||
for x in "012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9":
|
||||
if x.isdigit():
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -171,3 +171,19 @@ def f():
|
||||
if x > y:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
# SIM111
|
||||
for x in "012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9":
|
||||
if x.isdigit():
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
# OK (too long)
|
||||
for x in "012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß90":
|
||||
if x.isdigit():
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -90,3 +90,13 @@ with (
|
||||
D() as d,
|
||||
):
|
||||
print("hello")
|
||||
|
||||
# SIM117 (auto-fixable)
|
||||
with A("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as a:
|
||||
with B("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as b:
|
||||
print("hello")
|
||||
|
||||
# SIM117 (not auto-fixable too long)
|
||||
with A("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ890") as a:
|
||||
with B("01ß9💣2ℝ8901ß9💣2ℝ8901ß9💣2ℝ89") as b:
|
||||
print("hello")
|
||||
@@ -14,7 +14,7 @@ if key not in a_dict:
|
||||
else:
|
||||
var = a_dict[key]
|
||||
|
||||
# SIM401 (default with a complex expression)
|
||||
# OK (default contains effect)
|
||||
if key in a_dict:
|
||||
var = a_dict[key]
|
||||
else:
|
||||
@@ -36,12 +36,18 @@ else:
|
||||
if key in a_dict:
|
||||
vars[idx] = a_dict[key]
|
||||
else:
|
||||
vars[idx] = "default"
|
||||
vars[idx] = "defaultß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789"
|
||||
|
||||
###
|
||||
# Negative cases
|
||||
###
|
||||
|
||||
# OK (too long)
|
||||
if key in a_dict:
|
||||
vars[idx] = a_dict[key]
|
||||
else:
|
||||
vars[idx] = "defaultß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß"
|
||||
|
||||
# OK (false negative)
|
||||
if not key in a_dict:
|
||||
var = "default"
|
||||
|
||||
9
crates/ruff/resources/test/fixtures/flake8_todos/TD001.py
vendored
Normal file
9
crates/ruff/resources/test/fixtures/flake8_todos/TD001.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# T001 - accepted
|
||||
# TODO (evanrittenhouse): this is a valid TODO
|
||||
# SOME_OTHER_TAG: this is impossible to determine
|
||||
# this is not a TODO
|
||||
|
||||
# T001 - errors
|
||||
# XXX (evanrittenhouse): this is not fine
|
||||
# FIXME (evanrittenhouse): this is not fine
|
||||
# foo # XXX: this isn't fine either
|
||||
8
crates/ruff/resources/test/fixtures/flake8_todos/TD002.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/flake8_todos/TD002.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# T002 - accepted
|
||||
# TODO (evanrittenhouse): this has an author
|
||||
# TODO(evanrittenhouse): this also has an author
|
||||
# T002 - errors
|
||||
# TODO: this has no author
|
||||
# FIXME: neither does this
|
||||
# TODO : and neither does this
|
||||
# foo # TODO: this doesn't either
|
||||
31
crates/ruff/resources/test/fixtures/flake8_todos/TD003.py
vendored
Normal file
31
crates/ruff/resources/test/fixtures/flake8_todos/TD003.py
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# TDO003 - accepted
|
||||
# TODO: this comment has a link
|
||||
# https://github.com/charliermarsh/ruff/issues/3870
|
||||
|
||||
# TODO: this comment has an issue
|
||||
# TDO-3870
|
||||
|
||||
# TDO003 - errors
|
||||
# TODO: this comment has no
|
||||
# link after it
|
||||
|
||||
# TODO: here's a TODO with no link after it
|
||||
def foo(x):
|
||||
return x
|
||||
|
||||
# TODO: here's a TODO on the last line with no link
|
||||
# Here's more content.
|
||||
# TDO-3870
|
||||
|
||||
# TODO: here's a TODO on the last line with no link
|
||||
# Here's more content, with a space.
|
||||
|
||||
# TDO-3870
|
||||
|
||||
# TODO: here's a TODO without an issue link
|
||||
# TODO: followed by a new TODO with an issue link
|
||||
# TDO-3870
|
||||
|
||||
# foo # TODO: no link!
|
||||
|
||||
# TODO: here's a TODO on the last line with no link
|
||||
8
crates/ruff/resources/test/fixtures/flake8_todos/TD004.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/flake8_todos/TD004.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# T004 - accepted
|
||||
# TODO(evanrittenhouse): this has a colon
|
||||
# T004 - errors
|
||||
# TODO this has no colon
|
||||
# TODO(evanrittenhouse 😀) this has no colon
|
||||
# FIXME add a colon
|
||||
# foo # TODO add a colon
|
||||
# TODO this has a colon but it doesn't terminate the tag, so this should throw. https://www.google.com
|
||||
7
crates/ruff/resources/test/fixtures/flake8_todos/TD005.py
vendored
Normal file
7
crates/ruff/resources/test/fixtures/flake8_todos/TD005.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# T005 - accepted
|
||||
# TODO(evanrittenhouse): this has text, while the errors do not
|
||||
# T005 - errors
|
||||
# TODO(evanrittenhouse):
|
||||
# TODO(evanrittenhouse)
|
||||
# FIXME
|
||||
# foo # TODO
|
||||
6
crates/ruff/resources/test/fixtures/flake8_todos/TD006.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_todos/TD006.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# TDO006 - accepted
|
||||
# TODO (evanrittenhouse): this is a valid TODO
|
||||
# TDO006 - error
|
||||
# ToDo (evanrittenhouse): invalid capitalization
|
||||
# todo (evanrittenhouse): another invalid capitalization
|
||||
# foo # todo: invalid capitalization
|
||||
10
crates/ruff/resources/test/fixtures/flake8_todos/TD007.py
vendored
Normal file
10
crates/ruff/resources/test/fixtures/flake8_todos/TD007.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# T007 - accepted
|
||||
# TODO(evanrittenhouse): this has a space after a colon
|
||||
# TODO: so does this
|
||||
# T007 - errors
|
||||
# TODO(evanrittenhouse):this has no space after a colon
|
||||
# TODO (evanrittenhouse):this doesn't either
|
||||
# TODO:neither does this
|
||||
# FIXME:and lastly neither does this
|
||||
# foo # TODO:this is really the last one
|
||||
# TODO this colon doesn't terminate the tag, so don't check it. https://www.google.com
|
||||
@@ -146,3 +146,21 @@ def f():
|
||||
import pandas as pd
|
||||
|
||||
x = dict[pd.DataFrame, pd.DataFrame]
|
||||
|
||||
|
||||
def f():
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def f():
|
||||
from pandas import DataFrame # noqa: TCH002
|
||||
|
||||
x: DataFrame = 2
|
||||
|
||||
|
||||
def f():
|
||||
from pandas import ( # noqa: TCH002
|
||||
DataFrame,
|
||||
)
|
||||
|
||||
x: DataFrame = 2
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
|
||||
def f():
|
||||
# Even in strict mode, this shouldn't rase an error, since `pkg` is used at runtime,
|
||||
# Even in strict mode, this shouldn't raise an error, since `pkg` is used at runtime,
|
||||
# and implicitly imports `pkg.bar`.
|
||||
import pkg
|
||||
import pkg.bar
|
||||
@@ -12,7 +12,7 @@ def f():
|
||||
|
||||
|
||||
def f():
|
||||
# Even in strict mode, this shouldn't rase an error, since `pkg.bar` is used at
|
||||
# Even in strict mode, this shouldn't raise an error, since `pkg.bar` is used at
|
||||
# runtime, and implicitly imports `pkg`.
|
||||
import pkg
|
||||
import pkg.bar
|
||||
@@ -22,7 +22,7 @@ def f():
|
||||
|
||||
|
||||
def f():
|
||||
# In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
|
||||
# In un-strict mode, this shouldn't raise an error, since `pkg` is used at runtime.
|
||||
import pkg
|
||||
from pkg import A
|
||||
|
||||
@@ -31,7 +31,7 @@ def f():
|
||||
|
||||
|
||||
def f():
|
||||
# In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
|
||||
# In un-strict mode, this shouldn't raise an error, since `pkg` is used at runtime.
|
||||
from pkg import A, B
|
||||
|
||||
def test(value: A):
|
||||
@@ -39,7 +39,7 @@ def f():
|
||||
|
||||
|
||||
def f():
|
||||
# Even in strict mode, this shouldn't rase an error, since `pkg.baz` is used at
|
||||
# Even in strict mode, this shouldn't raise an error, since `pkg.baz` is used at
|
||||
# runtime, and implicitly imports `pkg.bar`.
|
||||
import pkg.bar
|
||||
import pkg.baz
|
||||
@@ -49,9 +49,56 @@ def f():
|
||||
|
||||
|
||||
def f():
|
||||
# In un-strict mode, this _should_ rase an error, since `pkg` is used at runtime.
|
||||
# In un-strict mode, this _should_ raise an error, since `pkg.bar` isn't used at runtime
|
||||
import pkg
|
||||
from pkg.bar import A
|
||||
|
||||
def test(value: A):
|
||||
return pkg.B()
|
||||
|
||||
|
||||
def f():
|
||||
# In un-strict mode, this shouldn't raise an error, since `pkg.bar` is used at runtime.
|
||||
import pkg
|
||||
import pkg.bar as B
|
||||
|
||||
def test(value: pkg.A):
|
||||
return B()
|
||||
|
||||
|
||||
def f():
|
||||
# In un-strict mode, this shouldn't raise an error, since `pkg.foo.bar` is used at runtime.
|
||||
import pkg.foo as F
|
||||
import pkg.foo.bar as B
|
||||
|
||||
def test(value: F.Foo):
|
||||
return B()
|
||||
|
||||
|
||||
def f():
|
||||
# In un-strict mode, this shouldn't raise an error, since `pkg.foo.bar` is used at runtime.
|
||||
import pkg
|
||||
import pkg.foo.bar as B
|
||||
|
||||
def test(value: pkg.A):
|
||||
return B()
|
||||
|
||||
|
||||
def f():
|
||||
# In un-strict mode, this _should_ raise an error, since `pkg` isn't used at runtime.
|
||||
# Note that `pkg` is a prefix of `pkgfoo` which are both different modules. This is
|
||||
# testing the implementation.
|
||||
import pkg
|
||||
import pkgfoo.bar as B
|
||||
|
||||
def test(value: pkg.A):
|
||||
return B()
|
||||
|
||||
|
||||
def f():
|
||||
# In un-strict mode, this shouldn't raise an error, since `pkg` is used at runtime.
|
||||
import pkg.bar as B
|
||||
import pkg.foo as F
|
||||
|
||||
def test(value: F.Foo):
|
||||
return B.Bar()
|
||||
|
||||
@@ -2,3 +2,7 @@ import a
|
||||
# Don't take this comment into account when determining whether the next import can fit on one line.
|
||||
from b import c
|
||||
from d import e # Do take this comment into account when determining whether the next import can fit on one line.
|
||||
# The next import fits on one line.
|
||||
from f import g # 012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ
|
||||
# The next import doesn't fit on one line.
|
||||
from h import i # 012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9012ß9💣2ℝ9
|
||||
|
||||
4
crates/ruff/resources/test/fixtures/isort/required_imports/off.py
vendored
Normal file
4
crates/ruff/resources/test/fixtures/isort/required_imports/off.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# isort: off
|
||||
|
||||
x = 1
|
||||
# isort: on
|
||||
@@ -6,7 +6,16 @@ import f
|
||||
import c
|
||||
import d
|
||||
|
||||
# isort: split
|
||||
# isort: split
|
||||
|
||||
import a
|
||||
import b
|
||||
|
||||
if True:
|
||||
import C
|
||||
import A
|
||||
|
||||
# isort: split
|
||||
|
||||
import D
|
||||
import B
|
||||
|
||||
@@ -40,3 +40,27 @@ def start():
|
||||
#: E117 W191
|
||||
def start():
|
||||
print()
|
||||
#: E112
|
||||
if False: #
|
||||
print()
|
||||
#:
|
||||
if False:
|
||||
print()
|
||||
#:
|
||||
if False: #
|
||||
print()
|
||||
#:
|
||||
if False:
|
||||
print()
|
||||
|
||||
print()
|
||||
#:
|
||||
if False:
|
||||
print()
|
||||
if False:
|
||||
|
||||
print()
|
||||
#:
|
||||
if False:
|
||||
|
||||
print()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user