Compare commits

..

2 Commits

Author SHA1 Message Date
Charlie Marsh
7479c943a4 Add warning 2024-05-26 14:48:54 -04:00
Charlie Marsh
e4303e2c68 Add warning 2024-05-26 14:48:51 -04:00
1967 changed files with 27992 additions and 47540 deletions

View File

@@ -1,28 +1,2 @@
[alias]
dev = "run --package ruff_dev --bin ruff_dev"
[target.'cfg(all())']
rustflags = [
# CLIPPY LINT SETTINGS
# This is a workaround to configure lints for the entire workspace, pending the ability to configure this via TOML.
# See: `https://github.com/rust-lang/cargo/issues/5034`
# `https://github.com/EmbarkStudios/rust-ecosystem/issues/22#issuecomment-947011395`
"-Dunsafe_code",
"-Wclippy::pedantic",
# Allowed pedantic lints
"-Wclippy::char_lit_as_u8",
"-Aclippy::collapsible_else_if",
"-Aclippy::collapsible_if",
"-Aclippy::implicit_hasher",
"-Aclippy::match_same_arms",
"-Aclippy::missing_errors_doc",
"-Aclippy::missing_panics_doc",
"-Aclippy::module_name_repetitions",
"-Aclippy::must_use_candidate",
"-Aclippy::similar_names",
"-Aclippy::too_many_lines",
# Disallowed restriction lints
"-Wclippy::print_stdout",
"-Wclippy::print_stderr",
"-Wclippy::dbg_macro",
]

View File

@@ -3,8 +3,8 @@ Thank you for taking the time to report an issue! We're glad to have you involve
If you're filing a bug report, please consider including the following information:
* A minimal code snippet that reproduces the bug.
* The command you invoked (e.g., `ruff /path/to/file.py --fix`), ideally including the `--isolated` flag.
* The current Ruff settings (any relevant sections from your `pyproject.toml`).
* The current Ruff version (`ruff --version`).
- A minimal code snippet that reproduces the bug.
- The command you invoked (e.g., `ruff /path/to/file.py --fix`), ideally including the `--isolated` flag.
- The current Ruff settings (any relevant sections from your `pyproject.toml`).
- The current Ruff version (`ruff --version`).
-->

19
.github/release.yml vendored
View File

@@ -1,19 +0,0 @@
# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#configuring-automatically-generated-release-notes
changelog:
categories:
- title: Breaking Changes
labels:
- breaking
- title: Rules
labels:
- rule
- autofix
- title: Settings
labels:
- configuration
- title: Bug Fixes
labels:
- bug
- title: Other Changes
labels:
- "*"

View File

@@ -4,7 +4,7 @@ on:
push:
branches: [main]
pull_request:
workflow_dispatch:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}
@@ -13,7 +13,6 @@ concurrency:
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_TERM_COLOR: always
RUSTUP_MAX_RETRIES: 10
jobs:
@@ -22,13 +21,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup show
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-11-01
override: true
- uses: Swatinem/rust-cache@v1
- run: cargo build --all
- run: ./target/debug/ruff_dev generate-all
- run: git diff --quiet README.md || echo "::error file=README.md::This file is outdated. Run 'cargo dev generate-all'."
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. Run 'cargo dev generate-all'."
- run: git diff --quiet README.md || echo "::error file=README.md::This file is outdated. Run 'cargo +nightly dev generate-all'."
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. Run 'cargo +nightly dev generate-all'."
- run: git diff --exit-code -- README.md ruff.schema.json
cargo-fmt:
@@ -36,8 +38,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup component add rustfmt
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-11-01
override: true
components: rustfmt
- run: cargo fmt --all --check
cargo_clippy:
@@ -45,80 +51,111 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: |
rustup component add clippy
rustup target add wasm32-unknown-unknown
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-11-01
override: true
components: clippy
target: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v1
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
- run: cargo clippy -p ruff --target wasm32-unknown-unknown --all-features -- -D warnings
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic
- run: cargo clippy -p ruff --target wasm32-unknown-unknown --all-features -- -D warnings -W clippy::pedantic
cargo-test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
name: "cargo test | ${{ matrix.os }}"
name: "cargo test"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup show
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: 1.65.0
override: true
- uses: Swatinem/rust-cache@v1
- run: cargo install cargo-insta
- run: pip install black[d]==22.12.0
- name: "Run tests (Ubuntu)"
if: ${{ matrix.os == 'ubuntu-latest' }}
- name: Run tests
run: |
cargo insta test --all --all-features --delete-unreferenced-snapshots
git diff --exit-code
- name: "Run tests (Windows)"
if: ${{ matrix.os == 'windows-latest' }}
shell: bash
run: |
cargo insta test --all --all-features
cargo insta test --all --delete-unreferenced-snapshots
git diff --exit-code
- run: cargo test --package ruff_cli --test black_compatibility_test -- --ignored
# Check for broken links in the documentation.
- run: cargo doc --all --no-deps
env:
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
RUSTDOCFLAGS: "-D warnings"
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
- run: RUSTDOCFLAGS="-D warnings" cargo doc --all --no-deps
scripts:
name: "test scripts"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup show
- uses: actions-rs/toolchain@v1
with:
profile: minimal
override: true
- uses: Swatinem/rust-cache@v1
- run: ./scripts/add_rule.py --name DoTheThing --code PLC999 --linter pylint
- run: cargo check
- run: |
./scripts/add_plugin.py test --url https://pypi.org/project/-test/0.1.0/ --prefix TST
./scripts/add_rule.py --name FirstRule --code TST001 --linter test
./scripts/add_plugin.py test --url https://pypi.org/project/-test/0.1.0/ --prefix TST
./scripts/add_rule.py --name FirstRule --code TST001 --linter test
- run: cargo check
# TODO(charlie): Re-enable the `wasm-pack` tests.
# See: https://github.com/charliermarsh/ruff/issues/1425
# wasm-pack-test:
# name: "wasm-pack test"
# runs-on: ubuntu-latest
# env:
# WASM_BINDGEN_TEST_TIMEOUT: 60
# steps:
# - uses: actions/checkout@v3
# - uses: actions-rs/toolchain@v1
# with:
# profile: minimal
# toolchain: nightly-2022-11-01
# override: true
# - uses: actions/cache@v3
# env:
# cache-name: cache-cargo
# with:
# path: |
# ~/.cargo/registry
# ~/.cargo/git
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
# - uses: jetli/wasm-pack-action@v0.4.0
# - uses: jetli/wasm-bindgen-action@v0.2.0
# - run: wasm-pack test --node
maturin-build:
name: "maturin build"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup show
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: 1.65.0
override: true
- uses: Swatinem/rust-cache@v1
- uses: actions/setup-python@v4
with:
python-version: "3.11"
- run: pip install maturin
- run: maturin build -b bin
- run: python scripts/transform_readme.py --target pypi
typos:
name: "spell check"
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: crate-ci/typos@master
with:
files: .
- name: Checkout Actions Repository
uses: actions/checkout@v2
- name: Check spelling of file.txt
uses: crate-ci/typos@master
with:
files: .

View File

@@ -1,34 +0,0 @@
name: mkdocs
on:
push:
paths:
- README.md
- mkdocs.template.yml
- .github/workflows/docs.yaml
branches: [main]
workflow_dispatch:
jobs:
mkdocs:
runs-on: ubuntu-latest
env:
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- name: "Install dependencies"
run: |
pip install "mkdocs~=1.4.2" "mkdocs-material~=9.0.6"
- name: "Copy README File"
run: |
python scripts/transform_readme.py --target mkdocs
python scripts/generate_mkdocs.py
mkdocs build --strict
- name: "Deploy to Cloudflare Pages"
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
uses: cloudflare/wrangler-action@2.0.0
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
command: pages publish site --project-name=ruff-docs --branch ${GITHUB_HEAD_REF} --commit-hash ${GITHUB_SHA}

View File

@@ -1,6 +1,7 @@
name: "[flake8-to-ruff] Release"
on: workflow_dispatch
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -9,55 +10,63 @@ concurrency:
env:
PACKAGE_NAME: flake8-to-ruff
CRATE_NAME: flake8_to_ruff
PYTHON_VERSION: "3.7" # to build abi3 wheels
PYTHON_VERSION: "3.11"
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_TERM_COLOR: always
RUSTUP_MAX_RETRIES: 10
jobs:
macos-x86_64:
runs-on: macos-latest
runs-on: macos-12
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: "Install Rust toolchain"
run: rustup show
- name: "Build wheels - x86_64"
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
default: true
- name: Build wheels - x86_64
uses: messense/maturin-action@v1
with:
target: x86_64
args: --release --out dist --sdist -m ./${{ env.CRATE_NAME }}/Cargo.toml
- name: "Install built wheel - x86_64"
- name: Install built wheel - x86_64
run: |
pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
macos-universal:
runs-on: macos-latest
runs-on: macos-14
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: "Install Rust toolchain"
run: rustup show
- name: "Build wheels - universal2"
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
default: true
- name: Build wheels - universal2
uses: messense/maturin-action@v1
with:
args: --release --universal2 --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
- name: "Install built wheel - universal2"
- name: Install built wheel - universal2
run: |
pip install dist/${{ env.CRATE_NAME }}-*universal2.whl --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -73,19 +82,23 @@ jobs:
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: ${{ matrix.target }}
- name: "Install Rust toolchain"
run: rustup show
- name: "Build wheels"
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
default: true
- name: Build wheels
uses: messense/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
- name: "Install built wheel"
- name: Install built wheel
shell: bash
run: |
python -m pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -101,18 +114,18 @@ jobs:
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: "Build wheels"
- name: Build wheels
uses: messense/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
- name: "Install built wheel"
- name: Install built wheel
if: matrix.target == 'x86_64'
run: |
pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -127,13 +140,13 @@ jobs:
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Build wheels"
- name: Build wheels
uses: messense/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: auto
args: --no-default-features --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
- uses: uraimo/run-on-arch-action@v2.5.0
- uses: uraimo/run-on-arch-action@v2.0.5
if: matrix.target != 'ppc64'
name: Install built wheel
with:
@@ -146,8 +159,8 @@ jobs:
pip3 install -U pip
run: |
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -165,13 +178,13 @@ jobs:
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: "Build wheels"
- name: Build wheels
uses: messense/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: musllinux_1_2
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
- name: "Install built wheel"
- name: Install built wheel
if: matrix.target == 'x86_64-unknown-linux-musl'
uses: addnab/docker-run-action@v3
with:
@@ -180,8 +193,8 @@ jobs:
run: |
apk add py3-pip
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links /io/dist/ --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -200,7 +213,7 @@ jobs:
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Build wheels"
- name: Build wheels
uses: messense/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
@@ -216,67 +229,8 @@ jobs:
apk add py3-pip
run: |
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
pypy:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
target: [x86_64, aarch64]
python-version:
- "3.7"
- "3.8"
- "3.9"
exclude:
- os: macos-latest
target: aarch64
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: pypy${{ matrix.python-version }}
- name: "Build wheels"
uses: messense/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist -i pypy${{ matrix.python-version }} -m ./${{ env.CRATE_NAME }}/Cargo.toml
- name: "Install built wheel"
if: matrix.target == 'x86_64'
run: |
pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
release:
name: Release
runs-on: ubuntu-latest
needs:
- macos-universal
- macos-x86_64
- windows
- linux
- linux-cross
- musllinux
- musllinux-cross
- pypy
steps:
- uses: actions/download-artifact@v3
with:
name: wheels
- uses: actions/setup-python@v4
- name: "Publish to PyPi"
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.FLAKE8_TO_RUFF_TOKEN }}
run: |
pip install --upgrade twine
twine upload --skip-existing *

View File

@@ -8,18 +8,21 @@ on:
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_TERM_COLOR: always
RUSTUP_MAX_RETRIES: 10
jobs:
publish:
runs-on: ubuntu-latest
env:
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-11-01
override: true
target: wasm32-unknown-unknown
- uses: actions/setup-node@v3
with:
node-version: 18
@@ -28,7 +31,7 @@ jobs:
- uses: jetli/wasm-pack-action@v0.4.0
- uses: jetli/wasm-bindgen-action@v0.2.0
- name: "Run wasm-pack"
run: wasm-pack build --target web --out-dir ../../playground/src/pkg crates/ruff
run: wasm-pack build --target web --out-dir playground/src/pkg . -- -p ruff
- name: "Install Node dependencies"
run: npm ci
working-directory: playground

View File

@@ -1,7 +1,6 @@
name: "[ruff] Release"
on:
workflow_dispatch:
release:
types: [published]
@@ -14,7 +13,6 @@ env:
PYTHON_VERSION: "3.7" # to build abi3 wheels
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_TERM_COLOR: always
RUSTUP_MAX_RETRIES: 10
jobs:
@@ -26,18 +24,22 @@ jobs:
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels - x86_64"
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
default: true
- name: Build wheels - x86_64
uses: messense/maturin-action@v1
with:
target: x86_64
args: --release --out dist --sdist
- name: "Install built wheel - x86_64"
- name: Install built wheel - x86_64
run: |
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -49,17 +51,21 @@ jobs:
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels - universal2"
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
default: true
- name: Build wheels - universal2
uses: messense/maturin-action@v1
with:
args: --release --universal2 --out dist
- name: "Install built wheel - universal2"
- name: Install built wheel - universal2
run: |
pip install dist/${{ env.PACKAGE_NAME }}-*universal2.whl --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -75,19 +81,23 @@ jobs:
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: ${{ matrix.target }}
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
default: true
- name: Build wheels
uses: messense/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist
- name: "Install built wheel"
- name: Install built wheel
shell: bash
run: |
python -m pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -103,20 +113,18 @@ jobs:
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
- name: Build wheels
uses: messense/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist
- name: "Install built wheel"
- name: Install built wheel
if: matrix.target == 'x86_64'
run: |
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -131,15 +139,13 @@ jobs:
- 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 wheels"
- name: Build wheels
uses: messense/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist
- uses: uraimo/run-on-arch-action@v2.5.0
args: --no-default-features --release --out dist
- uses: uraimo/run-on-arch-action@v2.0.5
if: matrix.target != 'ppc64'
name: Install built wheel
with:
@@ -152,8 +158,8 @@ jobs:
pip3 install -U pip
run: |
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -171,15 +177,13 @@ jobs:
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
- name: Build wheels
uses: messense/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: musllinux_1_2
args: --release --out dist
- name: "Install built wheel"
- name: Install built wheel
if: matrix.target == 'x86_64-unknown-linux-musl'
uses: addnab/docker-run-action@v3
with:
@@ -188,8 +192,8 @@ jobs:
run: |
apk add py3-pip
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links /io/dist/ --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -208,9 +212,7 @@ jobs:
- 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 wheels"
- name: Build wheels
uses: messense/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
@@ -226,8 +228,8 @@ jobs:
apk add py3-pip
run: |
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -250,20 +252,18 @@ jobs:
- uses: actions/setup-python@v4
with:
python-version: pypy${{ matrix.python-version }}
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
- name: Build wheels
uses: messense/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist -i pypy${{ matrix.python-version }}
- name: "Install built wheel"
- name: Install built wheel
if: matrix.target == 'x86_64'
run: |
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
- name: "Upload wheels"
uses: actions/upload-artifact@v3
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
@@ -282,17 +282,17 @@ jobs:
- pypy
if: "startsWith(github.ref, 'refs/tags/')"
steps:
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v2
with:
name: wheels
- uses: actions/setup-python@v4
- name: "Publish to PyPi"
- 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"
- 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"}'

8
.gitignore vendored
View File

@@ -1,9 +1,6 @@
# Local cache
.ruff_cache
crates/ruff/resources/test/cpython
docs/
mkdocs.yml
.overrides
resources/test/cpython
###
# Rust.gitignore
@@ -185,6 +182,3 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
.vimspector.json
# Visual Studio Code
.vscode/

View File

@@ -1,19 +1,14 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.231
hooks:
- id: ruff
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.10.1
hooks:
- id: validate-pyproject
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.33.0
hooks:
- id: markdownlint-fix
args:
- --disable
- MD013 # line-length
- MD033 # no-inline-html
- --
- repo: local
hooks:
- id: cargo-fmt
@@ -21,24 +16,3 @@ repos:
entry: cargo fmt --
language: rust
types: [rust]
- id: clippy
name: clippy
entry: cargo clippy --workspace --all-targets --all-features -- -D warnings
language: rust
pass_filenames: false
- id: ruff
name: ruff
entry: cargo run -- --no-cache --fix
language: rust
types_or: [python, pyi]
require_serial: true
exclude: ^crates/ruff/resources
- id: dev-generate-all
name: dev-generate-all
entry: cargo dev generate-all
language: rust
pass_filenames: false
exclude: target
ci:
skip: [cargo-fmt, clippy, dev-generate-all]

View File

@@ -1,85 +1,5 @@
# Breaking Changes
## 0.0.238
### `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
examples.)
When Ruff determines the enabled rule set, it has to reconcile `select` and `ignore` from a variety
of sources, including the current `pyproject.toml`, any inherited `pyproject.toml` files, and the
CLI.
The new semantics are such that Ruff uses the "highest-priority" `select` as the basis for the rule
set, and then applies any `extend-select`, `ignore`, and `extend-ignore` adjustments. CLI options
are given higher priority than `pyproject.toml` options, and the current `pyproject.toml` file is
given higher priority than any inherited `pyproject.toml` files.
`extend-select` and `extend-ignore` are no longer given "top priority"; instead, they merely append
to the `select` and `ignore` lists, as in Flake8.
This change is largely backwards compatible -- most users should experience no change in behavior.
However, as an example of a breaking change, consider the following:
```toml
[tool.ruff]
ignore = ["F401"]
```
Running `ruff --select F` would previously have enabled all `F` rules, apart from `F401`. Now, it
will enable all `F` rules, including `F401`, as the command line's `--select` resets the resolution.
### `remove-six-compat` (`UP016`) has been removed ([#2332](https://github.com/charliermarsh/ruff/pull/2332))
The `remove-six-compat` rule has been removed. This rule was only useful for one-time Python 2-to-3
upgrades.
## 0.0.237
### `--explain`, `--clean`, and `--generate-shell-completion` are now subcommands ([#2190](https://github.com/charliermarsh/ruff/pull/2190))
`--explain`, `--clean`, and `--generate-shell-completion` are now implemented as subcommands:
```console
ruff . # Still works! And will always work.
ruff check . # New! Also works.
ruff --explain E402 # Still works.
ruff rule E402 # New! Also works. (And preferred.)
# Oops! The command has to come first.
ruff --format json --explain E402 # No longer works.
ruff --explain E402 --format json # Still works!
ruff rule E402 --format json # Works! (And preferred.)
```
This change is largely backwards compatible -- most users should experience
no change in behavior. However, please note the following exceptions:
* Subcommands will now fail when invoked with unsupported arguments, instead
of silently ignoring them. For example, the following will now fail:
```console
ruff --clean --respect-gitignore
```
(the `clean` command doesn't support `--respect-gitignore`.)
* The semantics of `ruff <arg>` have changed slightly when `<arg>` is a valid subcommand.
For example, prior to this release, running `ruff rule` would run `ruff` over a file or
directory called `rule`. Now, `ruff rule` would invoke the `rule` subcommand. This should
only impact projects with files or directories named `rule`, `check`, `explain`, `clean`,
or `generate-shell-completion`.
* Scripts that invoke ruff should supply `--` before any positional arguments.
(The semantics of `ruff -- <arg>` have not changed.)
* `--explain` previously treated `--format grouped` as a synonym for `--format text`.
This is no longer supported; instead, use `--format text`.
## 0.0.226
### `misplaced-comparison-constant` (`PLC2201`) was deprecated in favor of `SIM300` ([#1980](https://github.com/charliermarsh/ruff/pull/1980))

View File

@@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
@@ -115,12 +115,14 @@ the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available [here](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html).
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the [FAQ](https://www.contributor-covenant.org/faq).
Translations are available [here](https://www.contributor-covenant.org/translations).
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@@ -4,14 +4,10 @@ Welcome! We're happy to have you here. Thank you in advance for your contributio
## The basics
Ruff welcomes contributions in the form of Pull Requests.
For small changes (e.g., bug fixes), feel free to submit a PR.
For larger changes (e.g., new lint rules, new functionality, new configuration options), consider
creating an [**issue**](https://github.com/charliermarsh/ruff/issues) outlining your proposed
change. You can also join us on [**Discord**](https://discord.gg/Z8KbeK24) to discuss your idea with
the community.
Ruff welcomes contributions in the form of Pull Requests. For small changes (e.g., bug fixes), feel
free to submit a PR. For larger changes (e.g., new lint rules, new functionality, new configuration
options), consider submitting an [Issue](https://github.com/charliermarsh/ruff/issues) outlining
your proposed change.
If you're looking for a place to start, we recommend implementing a new lint rule (see:
[_Adding a new lint rule_](#example-adding-a-new-lint-rule), which will allow you to learn from and
@@ -20,7 +16,8 @@ existing Python plugins, which can be used as a reference implementation.
As a concrete example: consider taking on one of the rules from the [`tryceratops`](https://github.com/charliermarsh/ruff/issues/2056)
plugin, and looking to the originating [Python source](https://github.com/guilatrova/tryceratops)
for guidance.
for guidance. [`flake8-simplify`](https://github.com/charliermarsh/ruff/issues/998) has a few rules
left too.
### Prerequisites
@@ -38,108 +35,79 @@ cargo install cargo-insta
After cloning the repository, run Ruff locally with:
```shell
cargo run /path/to/file.py --no-cache
cargo run resources/test/fixtures --no-cache
```
Prior to opening a pull request, ensure that your code has been auto-formatted,
and that it passes both the lint and test validation checks:
and that it passes both the lint and test validation checks.
For rustfmt and Clippy, we use [nightly Rust][nightly], as it is stricter than stable Rust.
(However, tests and builds use stable Rust.)
```shell
cargo fmt --all # Auto-formatting...
cargo clippy --fix --workspace --all-targets --all-features # Linting...
cargo test --all # Testing...
cargo +nightly fmt --all # Auto-formatting...
cargo +nightly clippy --fix --workspace --all-targets --all-features -- -W clippy::pedantic # Linting...
cargo test --all # Testing...
```
These checks will run on GitHub Actions when you open your Pull Request, but running them locally
will save you time and expedite the merge process.
Note that many code changes also require updating the snapshot tests, which is done interactively
after running `cargo test` like so:
```shell
cargo insta review
```
If you have `pre-commit` [installed](https://pre-commit.com/#installation) then you can use it to
assist with formatting and linting. The following command will run the `pre-commit` hooks:
```shell
pre-commit run --all-files
```
Your Pull Request will be reviewed by a maintainer, which may involve a few rounds of iteration
prior to merging.
### Project structure
Ruff is structured as a monorepo with a [flat crate structure](https://matklad.github.io/2021/08/22/large-rust-workspaces.html),
such that all crates are contained in a flat `crates` directory.
The vast majority of the code, including all lint rules, lives in the `ruff` crate (located at
`crates/ruff`). As a contributor, that's the crate that'll be most relevant to you.
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_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/flake8_to_ruff`: binary crate for generating Ruff configuration from Flake8 configuration.
### Example: Adding a new lint rule
At a high level, the steps involved in adding a new lint rule are as follows:
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
1. Create a file for your rule (e.g., `src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
2. In that file, define a violation struct. You can grep for `define_violation!` to see examples.
3. Map the violation struct to a rule code in `crates/ruff/src/registry.rs` (e.g., `E402`).
4. 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).
3. Map the violation struct to a rule code in `src/registry.rs` (e.g., `E402`).
4. Define the logic for triggering the violation in `src/checkers/ast.rs` (for AST-based checks),
`src/checkers/tokens.rs` (for token-based checks), `src/checkers/lines.rs` (for text-based
checks), or `src/checkers/filesystem.rs` (for filesystem-based checks).
5. Add a test fixture.
6. Update the generated files (documentation and generated code).
To define the violation, start by creating a dedicated file for your rule under the appropriate
rule linter (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`). That file should
rule linter (e.g., `src/rules/flake8_bugbear/rules/abstract_base_class.rs`). That file should
contain a struct defined via `define_violation!`, along with a function that creates the violation
based on any required inputs. (Many of the existing examples live in `crates/ruff/src/violations.rs`,
but we're looking to place new rules in their own files.)
based on any required inputs. (Many of the existing examples live in `src/violations.rs`, but we're
looking to place new rules in their own files.)
To trigger the violation, you'll likely want to augment the logic in `crates/ruff/src/checkers/ast.rs`,
which defines the Python AST visitor, responsible for iterating over the abstract syntax tree and
To trigger the violation, you'll likely want to augment the logic in `src/checkers/ast.rs`, which
defines the Python AST visitor, responsible for iterating over the abstract syntax tree and
collecting diagnostics as it goes.
If you need to inspect the AST, you can run `cargo dev print-ast` with a Python file. Grep
If you need to inspect the AST, you can run `cargo +nightly 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
To add a test fixture, create a file under `resources/test/fixtures/[linter]`, named to match
the code you defined earlier (e.g., `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 crates/ruff/resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402`.
Run `cargo +nightly dev generate-all` to generate the code for your new fixture. Then run Ruff
locally with (e.g.) `cargo run 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 --all`.
`test_case` macro in the relevant `src/[linter]/mod.rs` file. Then, run `cargo test --all`.
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.
Finally, regenerate the documentation and generated code with `cargo dev generate-all`.
Finally, regenerate the documentation and generated code with `cargo +nightly dev generate-all`.
### Example: Adding a new configuration option
Ruff's user-facing settings live in a few different places.
First, the command-line options are defined via the `Cli` struct in `crates/ruff/src/cli.rs`.
First, the command-line options are defined via the `Cli` struct in `src/cli.rs`.
Second, the `pyproject.toml` options are defined in `crates/ruff/src/settings/options.rs` (via the
`Options` struct), `crates/ruff/src/settings/configuration.rs` (via the `Configuration` struct), and
`crates/ruff/src/settings/mod.rs` (via the `Settings` struct). These represent, respectively: the
schema used to parse the `pyproject.toml` file; an internal, intermediate representation; and the
final, internal representation used to power Ruff.
Second, the `pyproject.toml` options are defined in `src/settings/options.rs` (via the `Options`
struct), `src/settings/configuration.rs` (via the `Configuration` struct), and `src/settings/mod.rs`
(via the `Settings` struct). These represent, respectively: the schema used to parse the
`pyproject.toml` file; an internal, intermediate representation; and the final, internal
representation used to power Ruff.
To add a new configuration option, you'll likely want to modify these latter few files (along with
`cli.rs`, if appropriate). If you want to pattern-match against an existing example, grep for
@@ -147,13 +115,13 @@ To add a new configuration option, you'll likely want to modify these latter few
variables (e.g., `_`).
Note that plugin-specific configuration options are defined in their own modules (e.g.,
`crates/ruff/src/flake8_unused_arguments/settings.rs`).
`src/flake8_unused_arguments/settings.rs`).
You may also want to add the new configuration option to the `flake8-to-ruff` tool, which is
responsible for converting `flake8` configuration files to Ruff's TOML format. This logic
lives in `crates/ruff/src/flake8_to_ruff/converter.rs`.
lives in `flake8_to_ruff/src/converter.rs`.
Finally, regenerate the documentation and generated code with `cargo dev generate-all`.
Finally, regenerate the documentation and generated code with `cargo +nightly dev generate-all`.
## Release process
@@ -164,125 +132,4 @@ them to [PyPI](https://pypi.org/project/ruff/).
Ruff follows the [semver](https://semver.org/) versioning standard. However, as pre-1.0 software,
even patch releases may contain [non-backwards-compatible changes](https://semver.org/#spec-item-4).
## Benchmarks
First, clone [CPython](https://github.com/python/cpython). It's a large and diverse Python codebase,
which makes it a good target for benchmarking.
```shell
git clone --branch 3.10 https://github.com/python/cpython.git crates/ruff/resources/test/cpython
```
To benchmark the release build:
```shell
cargo build --release && hyperfine --ignore-failure --warmup 10 \
"./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache" \
"./target/release/ruff ./crates/ruff/resources/test/cpython/"
Benchmark 1: ./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache
Time (mean ± σ): 293.8 ms ± 3.2 ms [User: 2384.6 ms, System: 90.3 ms]
Range (min … max): 289.9 ms … 301.6 ms 10 runs
Warning: Ignoring non-zero exit code.
Benchmark 2: ./target/release/ruff ./crates/ruff/resources/test/cpython/
Time (mean ± σ): 48.0 ms ± 3.1 ms [User: 65.2 ms, System: 124.7 ms]
Range (min … max): 45.0 ms … 66.7 ms 62 runs
Warning: Ignoring non-zero exit code.
Summary
'./target/release/ruff ./crates/ruff/resources/test/cpython/' ran
6.12 ± 0.41 times faster than './target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache'
```
To benchmark against the ecosystem's existing tools:
```shell
hyperfine --ignore-failure --warmup 5 \
"./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache" \
"pyflakes crates/ruff/resources/test/cpython" \
"autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython" \
"pycodestyle crates/ruff/resources/test/cpython" \
"flake8 crates/ruff/resources/test/cpython"
Benchmark 1: ./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache
Time (mean ± σ): 294.3 ms ± 3.3 ms [User: 2467.5 ms, System: 89.6 ms]
Range (min … max): 291.1 ms … 302.8 ms 10 runs
Warning: Ignoring non-zero exit code.
Benchmark 2: pyflakes crates/ruff/resources/test/cpython
Time (mean ± σ): 15.786 s ± 0.143 s [User: 15.560 s, System: 0.214 s]
Range (min … max): 15.640 s … 16.157 s 10 runs
Warning: Ignoring non-zero exit code.
Benchmark 3: autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython
Time (mean ± σ): 6.175 s ± 0.169 s [User: 54.102 s, System: 1.057 s]
Range (min … max): 5.950 s … 6.391 s 10 runs
Benchmark 4: pycodestyle crates/ruff/resources/test/cpython
Time (mean ± σ): 46.921 s ± 0.508 s [User: 46.699 s, System: 0.202 s]
Range (min … max): 46.171 s … 47.863 s 10 runs
Warning: Ignoring non-zero exit code.
Benchmark 5: flake8 crates/ruff/resources/test/cpython
Time (mean ± σ): 12.260 s ± 0.321 s [User: 102.934 s, System: 1.230 s]
Range (min … max): 11.848 s … 12.933 s 10 runs
Warning: Ignoring non-zero exit code.
Summary
'./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache' ran
20.98 ± 0.62 times faster than 'autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython'
41.66 ± 1.18 times faster than 'flake8 crates/ruff/resources/test/cpython'
53.64 ± 0.77 times faster than 'pyflakes crates/ruff/resources/test/cpython'
159.43 ± 2.48 times faster than 'pycodestyle crates/ruff/resources/test/cpython'
```
You can run `poetry install` from `./scripts` to create a working environment for the above. All
reported benchmarks were computed using the versions specified by `./scripts/pyproject.toml`
on Python 3.11.
To benchmark Pylint, remove the following files from the CPython repository:
```shell
rm Lib/test/bad_coding.py \
Lib/test/bad_coding2.py \
Lib/test/bad_getattr.py \
Lib/test/bad_getattr2.py \
Lib/test/bad_getattr3.py \
Lib/test/badcert.pem \
Lib/test/badkey.pem \
Lib/test/badsyntax_3131.py \
Lib/test/badsyntax_future10.py \
Lib/test/badsyntax_future3.py \
Lib/test/badsyntax_future4.py \
Lib/test/badsyntax_future5.py \
Lib/test/badsyntax_future6.py \
Lib/test/badsyntax_future7.py \
Lib/test/badsyntax_future8.py \
Lib/test/badsyntax_future9.py \
Lib/test/badsyntax_pep3120.py \
Lib/test/test_asyncio/test_runners.py \
Lib/test/test_copy.py \
Lib/test/test_inspect.py \
Lib/test/test_typing.py
```
Then, from `crates/ruff/resources/test/cpython`, run: `time pylint -j 0 -E $(git ls-files '*.py')`. This
will execute Pylint with maximum parallelism and only report errors.
To benchmark Pyupgrade, run the following from `crates/ruff/resources/test/cpython`:
```shell
hyperfine --ignore-failure --warmup 5 --prepare "git reset --hard HEAD" \
"find . -type f -name \"*.py\" | xargs -P 0 pyupgrade --py311-plus"
Benchmark 1: find . -type f -name "*.py" | xargs -P 0 pyupgrade --py311-plus
Time (mean ± σ): 30.119 s ± 0.195 s [User: 28.638 s, System: 0.390 s]
Range (min … max): 29.813 s … 30.356 s 10 runs
```
[nightly]: https://rust-lang.github.io/rustup/concepts/channels.html#working-with-nightly-rust

592
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,85 @@
[workspace]
members = ["crates/*"]
default-members = ["crates/ruff", "crates/ruff_cli"]
members = [
"flake8_to_ruff",
"ruff_dev",
"ruff_cli",
]
default-members = [".", "ruff_cli"]
[package]
name = "ruff"
version = "0.0.231"
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
edition = "2021"
rust-version = "1.65.0"
documentation = "https://github.com/charliermarsh/ruff"
homepage = "https://github.com/charliermarsh/ruff"
repository = "https://github.com/charliermarsh/ruff"
readme = "README.md"
license = "MIT"
[lib]
name = "ruff"
crate-type = ["cdylib", "rlib"]
doctest = false
[dependencies]
anyhow = { version = "1.0.66" }
bitflags = { version = "1.3.2" }
cfg-if = { version = "1.0.0" }
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
clap = { version = "4.0.1", features = ["derive", "env"] }
colored = { version = "2.0.0" }
dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
glob = { version = "0.3.0" }
globset = { version = "0.4.9" }
ignore = { version = "0.4.18" }
imperative = { version = "1.0.3" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
log = { version = "0.4.17" }
natord = { version = "1.0.9" }
nohash-hasher = { version = "0.2.0" }
num-bigint = { version = "0.4.3" }
num-traits = "0.2.15"
once_cell = { version = "1.16.0" }
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
regex = { version = "1.6.0" }
ruff_macros = { version = "0.0.231", path = "ruff_macros" }
rustc-hash = { version = "1.1.0" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
schemars = { version = "0.8.11" }
semver = { version = "1.0.16" }
serde = { version = "1.0.147", features = ["derive"] }
shellexpand = { version = "3.0.0" }
smallvec = { version = "1.10.0" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }
textwrap = { version = "0.16.0" }
thiserror = { version = "1.0" }
titlecase = { version = "2.2.1" }
toml_edit = { version = "0.17.1", features = ["easy"] }
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
# For (future) wasm-pack support
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
getrandom = { version = "0.2.7", features = ["js"] }
console_error_panic_hook = { version = "0.1.7" }
console_log = { version = "0.2.0" }
serde-wasm-bindgen = { version = "0.4" }
js-sys = { version = "0.3.60" }
wasm-bindgen = { version = "0.2.83" }
[dev-dependencies]
insta = { version = "1.19.1", features = ["yaml"] }
test-case = { version = "2.2.2" }
wasm-bindgen-test = { version = "0.3.33" }
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
criterion = { version = "0.4.0" }
[profile.release]
panic = "abort"

1016
LICENSE

File diff suppressed because it is too large Load Diff

1595
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
[files]
extend-exclude = ["snapshots"]

View File

@@ -1,83 +0,0 @@
[package]
name = "ruff"
version = "0.0.243"
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
edition = "2021"
rust-version = "1.65.0"
documentation = "https://github.com/charliermarsh/ruff"
homepage = "https://github.com/charliermarsh/ruff"
repository = "https://github.com/charliermarsh/ruff"
readme = "README.md"
license = "MIT"
[lib]
name = "ruff"
crate-type = ["cdylib", "rlib"]
doctest = false
[dependencies]
anyhow = { version = "1.0.66" }
bisection = { version = "0.1.0" }
bitflags = { version = "1.3.2" }
cfg-if = { version = "1.0.0" }
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
clap = { version = "4.0.1", features = ["derive", "env"] }
colored = { version = "2.0.0" }
dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
glob = { version = "0.3.0" }
globset = { version = "0.4.9" }
ignore = { version = "0.4.18" }
imperative = { version = "1.0.3" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
log = { version = "0.4.17" }
natord = { version = "1.0.9" }
nohash-hasher = { version = "0.2.0" }
num-bigint = { version = "0.4.3" }
num-traits = "0.2.15"
once_cell = { version = "1.16.0" }
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
regex = { version = "1.6.0" }
ruff_macros = { version = "0.0.243", path = "../ruff_macros" }
ruff_python = { version = "0.0.243", path = "../ruff_python" }
rustc-hash = { version = "1.1.0" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" }
schemars = { version = "0.8.11" }
semver = { version = "1.0.16" }
serde = { version = "1.0.147", features = ["derive"] }
shellexpand = { version = "3.0.0" }
smallvec = { version = "1.10.0" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }
textwrap = { version = "0.16.0" }
thiserror = { version = "1.0" }
titlecase = { version = "2.2.1" }
toml = { version = "0.6.0" }
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
# For (future) wasm-pack support
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
getrandom = { version = "0.2.7", features = ["js"] }
console_error_panic_hook = { version = "0.1.7" }
console_log = { version = "0.2.0" }
serde-wasm-bindgen = { version = "0.4" }
js-sys = { version = "0.3.60" }
wasm-bindgen = { version = "0.2.83" }
[target.'cfg(not(target_family = "wasm"))'.dependencies]
is_executable = "1.0.1"
[dev-dependencies]
insta = { version = "1.19.0", features = ["yaml", "redactions"] }
test-case = { version = "2.2.2" }
wasm-bindgen-test = { version = "0.3.33" }
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
criterion = { version = "0.4.0" }
[features]
default = []
logical_lines = []

View File

@@ -1,14 +0,0 @@
try:
pass
except Exception:
pass
try:
pass
except:
pass
try:
pass
except ValueError:
pass

View File

@@ -1,88 +0,0 @@
for i in range(10):
print(i)
print(i) # name no longer defined on Python 3; no warning yet
for i in range(10): # name not used within the loop; B007
print(10)
print(i) # name no longer defined on Python 3; no warning yet
for _ in range(10): # _ is okay for a throw-away variable
print(10)
for i in range(10):
for j in range(10):
for k in range(10): # k not used, i and j used transitively
print(i + j)
def strange_generator():
for i in range(10):
for j in range(10):
for k in range(10):
for l in range(10):
yield i, (j, (k, l))
for i, (j, (k, l)) in strange_generator(): # i, k not used
print(j, l)
FMT = "{foo} {bar}"
for foo, bar in [(1, 2)]:
if foo:
print(FMT.format(**locals()))
for foo, bar in [(1, 2)]:
if foo:
print(FMT.format(**globals()))
for foo, bar in [(1, 2)]:
if foo:
print(FMT.format(**vars()))
for foo, bar in [(1, 2)]:
print(FMT.format(foo=foo, bar=eval("bar")))
def f():
# Fixable.
for foo, bar, baz in (["1", "2", "3"],):
if foo or baz:
break
def f():
# Unfixable due to usage of `bar` outside of loop.
for foo, bar, baz in (["1", "2", "3"],):
if foo or baz:
break
print(bar)
def f():
# Fixable.
for foo, bar, baz in (["1", "2", "3"],):
if foo or baz:
break
bar = 1
def f():
# Fixable.
for foo, bar, baz in (["1", "2", "3"],):
if foo or baz:
break
bar = 1
print(bar)
# Unfixable due to trailing underscore (`_line_` wouldn't be considered an ignorable
# variable name).
for line_ in range(self.header_lines):
fp.readline()

View File

@@ -1,4 +0,0 @@
#!/usr/bin/python
if __name__ == '__main__':
print('I should be executable.')

View File

@@ -1,2 +0,0 @@
if __name__ == '__main__':
print('I should be executable.')

View File

@@ -1,4 +0,0 @@
#!/usr/bin/python
if __name__ == '__main__':
print('I should be executable.')

View File

@@ -1,2 +0,0 @@
if __name__ == '__main__':
print('I should be executable.')

View File

@@ -1,2 +0,0 @@
if __name__ == '__main__':
print('I should be executable.')

View File

@@ -1,4 +0,0 @@
#!/usr/bin/python
if __name__ == '__main__':
print('I should be executable.')

View File

@@ -1,3 +0,0 @@
import logging
logging.info("Hello {}".format("World!"))

View File

@@ -1,3 +0,0 @@
import logging
logging.info("Hello %s" % "World!")

View File

@@ -1,3 +0,0 @@
import logging
logging.info("Hello" + " " + "World!")

View File

@@ -1,4 +0,0 @@
import logging
name = "world"
logging.info(f"Hello {name}")

View File

@@ -1,3 +0,0 @@
import logging
logging.warn("Hello World!")

View File

@@ -1,8 +0,0 @@
import logging
logging.info(
"Hello world!",
extra={
"name": "foobar",
},
)

View File

@@ -1,8 +0,0 @@
import logging
logging.info(
"Hello world!",
extra=dict(
name="foobar",
),
)

View File

@@ -1,21 +0,0 @@
import logging
import sys
# G201
try:
pass
except:
logging.error("Hello World", exc_info=True)
try:
pass
except:
logging.error("Hello World", exc_info=sys.exc_info())
# OK
try:
pass
except:
logging.error("Hello World", exc_info=False)
logging.error("Hello World", exc_info=sys.exc_info())

View File

@@ -1,21 +0,0 @@
import logging
import sys
# G202
try:
pass
except:
logging.exception("Hello World", exc_info=True)
try:
pass
except:
logging.exception("Hello World", exc_info=sys.exc_info())
# OK
try:
pass
except:
logging.exception("Hello World", exc_info=False)
logging.exception("Hello World", exc_info=True)

View File

@@ -1,7 +0,0 @@
import argparse
from pathlib import Path
parser = argparse.ArgumentParser()
parser.add_argument("target_dir", type=Path)
args = parser.parse_args()
parser.error(f"Target directory {args.target_dir} does not exist")

View File

@@ -1,8 +0,0 @@
import logging
logging.info(
"Hello {world}!",
extra=dict(
world="World",
),
)

View File

@@ -1,8 +0,0 @@
import logging
logging.info(
"Hello {world}!",
extra=dict(
world="{}".format("World"),
),
)

View File

@@ -1,3 +0,0 @@
import logging
logging.info("Hello World!")

View File

@@ -1,3 +0,0 @@
import warnings
warnings.warn("Hello World!")

View File

@@ -1,17 +0,0 @@
# error
obj.startswith("foo") or obj.startswith("bar")
# error
obj.endswith("foo") or obj.endswith("bar")
# error
obj.startswith(foo) or obj.startswith(bar)
# error
obj.startswith(foo) or obj.startswith("foo")
# ok
obj.startswith(("foo", "bar"))
# ok
obj.endswith(("foo", "bar"))
# ok
obj.startswith("foo") or obj.endswith("bar")
# ok
obj.startswith("foo") or abc.startswith("bar")

View File

@@ -1,73 +0,0 @@
import pytest
@pytest.fixture()
def ok_no_scope():
...
@pytest.fixture(scope="module")
def ok_other_scope():
...
@pytest.fixture(scope="function")
def error():
...
@pytest.fixture(scope="function", name="my_fixture")
def error_multiple_args():
...
@pytest.fixture(name="my_fixture", scope="function")
def error_multiple_args():
...
@pytest.fixture(name="my_fixture", scope="function", **kwargs)
def error_second_arg():
...
# pytest.fixture does not take positional arguments, however this
# tests the general case as we use a helper function that should
# work for all cases.
@pytest.fixture("my_fixture", scope="function")
def error_arg():
...
@pytest.fixture(
scope="function",
name="my_fixture",
)
def error_multiple_args():
...
@pytest.fixture(
name="my_fixture",
scope="function",
)
def error_multiple_args():
...
@pytest.fixture(
"hello",
name,
*args
,
# another comment ,)
scope=\
"function" # some comment ),
,
name2=name, name3="my_fixture", **kwargs
)
def error_multiple_args():
...

View File

@@ -1,4 +0,0 @@
this_should_be_linted = "double quote string"
this_should_be_linted = u"double quote string"
this_should_be_linted = f"double quote string"
this_should_be_linted = f"double {'quote'} string"

View File

@@ -1,27 +0,0 @@
x = (
"This"
"is"
"not"
)
x = (
"This" \
"is" \
"not"
)
x = (
"This"
"is 'actually'"
"fine"
)
x = (
"This" \
"is 'actually'" \
"fine"
)
if True:
"This can use 'double' quotes"
"But this needs to be changed"

View File

@@ -1,4 +0,0 @@
this_should_be_linted = 'single quote string'
this_should_be_linted = u'double quote string'
this_should_be_linted = f'double quote string'
this_should_be_linted = f'double {"quote"} string'

View File

@@ -1,27 +0,0 @@
x = (
'This'
'is'
'not'
)
x = (
'This' \
'is' \
'not'
)
x = (
'This'
'is "actually"'
'fine'
)
x = (
'This' \
'is "actually"' \
'fine'
)
if True:
'This can use "single" quotes'
'But this needs to be changed'

View File

@@ -1,34 +0,0 @@
try:
y = 6 + "7"
except TypeError:
# RSE102
raise ValueError()
try:
x = 1 / 0
except ZeroDivisionError:
raise
# RSE102
raise TypeError()
# RSE102
raise TypeError ()
# RSE102
raise TypeError \
()
# RSE102
raise TypeError(
)
# RSE102
raise TypeError(
# Hello, world!
)
raise AssertionError
raise AttributeError("test message")

View File

@@ -1,59 +0,0 @@
class BazMeta(type):
_private_count = 1
def __new__(mcs, name, bases, attrs):
if mcs._private_count <= 5:
mcs.some_method()
return super().__new__(mcs, name, bases, attrs)
def some_method():
pass
class Bar:
_private = True
@classmethod
def is_private(cls):
return cls._private
class Foo(metaclass=BazMeta):
def __init__(self):
self.public_thing = "foo"
self._private_thing = "bar"
self.__really_private_thing = "baz"
self.bar = Bar()
def __str__(self):
return "foo"
def get_bar():
if self.bar._private: # SLF001
return None
return self.bar
def public_func(self):
pass
def _private_func(self):
pass
def __really_private_func(self, arg):
pass
foo = Foo()
print(foo.public_thing)
print(foo.public_func())
print(foo.__dict__)
print(foo.__str__())
print(foo._private_thing) # SLF001
print(foo.__really_private_thing) # SLF001
print(foo._private_func()) # SLF001
print(foo.__really_private_func(1)) # SLF001
print(foo.bar._private) # SLF001

View File

@@ -1,28 +0,0 @@
# Errors
"yoda" == compare # SIM300
"yoda" == compare # SIM300
42 == age # SIM300
("a", "b") == compare # SIM300
"yoda" <= compare # SIM300
"yoda" < compare # SIM300
42 > age # SIM300
YODA == age # SIM300
YODA > age # SIM300
YODA >= age # SIM300
JediOrder.YODA == age # SIM300
0 < (number - 100) # SIM300
# OK
compare == "yoda"
age == 42
compare == ("a", "b")
x == y
"yoda" == compare == 1
"yoda" == compare == someothervar
"yoda" == "yoda"
age == YODA
age < YODA
age <= YODA
YODA == YODA
age == JediOrder.YODA
(number - 100) > 0

View File

@@ -1,28 +0,0 @@
"""Tests to determine first-party import classification.
For typing-only import detection tests, see `TCH002.py`.
"""
def f():
import TYP001
x: TYP001
def f():
import TYP001
print(TYP001)
def f():
from . import TYP001
x: TYP001
def f():
from . import TYP001
print(TYP001)

View File

@@ -1,148 +0,0 @@
"""Tests to determine accurate detection of typing-only imports."""
def f():
import pandas as pd # TCH002
x: pd.DataFrame
def f():
from pandas import DataFrame # TCH002
x: DataFrame
def f():
from pandas import DataFrame as df # TCH002
x: df
def f():
import pandas as pd # TCH002
x: pd.DataFrame = 1
def f():
from pandas import DataFrame # TCH002
x: DataFrame = 2
def f():
from pandas import DataFrame as df # TCH002
x: df = 3
def f():
import pandas as pd # TCH002
x: "pd.DataFrame" = 1
def f():
import pandas as pd
x = dict["pd.DataFrame", "pd.DataFrame"] # TCH002
def f():
import pandas as pd
print(pd)
def f():
from pandas import DataFrame
print(DataFrame)
def f():
from pandas import DataFrame
def f():
print(DataFrame)
def f():
from typing import Dict, Any
def example() -> Any:
return 1
x: Dict[int] = 20
def f():
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Dict
x: Dict[int] = 20
def f():
from pathlib import Path
class ImportVisitor(ast.NodeTransformer):
def __init__(self, cwd: Path) -> None:
self.cwd = cwd
origin = Path(spec.origin)
class ExampleClass:
def __init__(self):
self.cwd = Path(pandas.getcwd())
def f():
import pandas
class Migration:
enum = pandas
def f():
import pandas
class Migration:
enum = pandas.EnumClass
def f():
from typing import TYPE_CHECKING
from pandas import y
if TYPE_CHECKING:
_type = x
else:
_type = y
def f():
from typing import TYPE_CHECKING
from pandas import y
if TYPE_CHECKING:
_type = x
elif True:
_type = y
def f():
from typing import cast
import pandas as pd
x = cast(pd.DataFrame, 2)
def f():
import pandas as pd
x = dict[pd.DataFrame, pd.DataFrame]

View File

@@ -1,16 +0,0 @@
"""Tests to determine standard library import classification.
For typing-only import detection tests, see `TCH002.py`.
"""
def f():
import os
x: os
def f():
import os
print(os)

View File

@@ -1,5 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from datetime import datetime
x = datetime

View File

@@ -1,8 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from datetime import date
def example():
return date()

View File

@@ -1,6 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Any
CustomType = Any

View File

@@ -1,11 +0,0 @@
from typing import TYPE_CHECKING, Type
if TYPE_CHECKING:
from typing import Any
def example(*args: Any, **kwargs: Any):
return
my_type: Type[Any] | Any

View File

@@ -1,8 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import List, Sequence, Set
def example(a: List[int], /, b: Sequence[int], *, c: Set[int]):
return

View File

@@ -1,10 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from pandas import DataFrame
def example():
from pandas import DataFrame
x = DataFrame

View File

@@ -1,10 +0,0 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import AsyncIterator, List
class Example:
async def example(self) -> AsyncIterator[List[str]]:
yield 0

View File

@@ -1,7 +0,0 @@
from typing import TYPE_CHECKING
from weakref import WeakKeyDictionary
if TYPE_CHECKING:
from typing import Any
d = WeakKeyDictionary["Any", "Any"]()

View File

@@ -1,16 +0,0 @@
def f():
import pandas as pd
x: pd.DataFrame
def f():
import pandas.core.frame as pd
x: pd.DataFrame
def f():
import flask
x: flask

View File

@@ -1,54 +0,0 @@
def f():
# Even in strict mode, this shouldn't rase an error, since `pkg` is used at runtime,
# and implicitly imports `pkg.bar`.
import pkg
import pkg.bar
def test(value: pkg.bar.A):
return pkg.B()
def f():
# Even in strict mode, this shouldn't rase an error, since `pkg.bar` is used at
# runtime, and implicitly imports `pkg`.
import pkg
import pkg.bar
def test(value: pkg.A):
return pkg.bar.B()
def f():
# In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
import pkg
from pkg import A
def test(value: A):
return pkg.B()
def f():
# In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
from pkg import A, B
def test(value: A):
return B()
def f():
# Even in strict mode, this shouldn't rase an error, since `pkg.baz` is used at
# runtime, and implicitly imports `pkg.bar`.
import pkg.bar
import pkg.baz
def test(value: pkg.bar.A):
return pkg.baz.B()
def f():
# In un-strict mode, this _should_ rase an error, since `pkg` is used at runtime.
import pkg
from pkg.bar import A
def test(value: A):
return pkg.B()

View File

@@ -1,3 +0,0 @@
from pathlib import Path
(Path("") / "").open()

View File

@@ -1,8 +0,0 @@
# office_helper and tests are both first-party,
# but we want tests and experiments to be separated, in that order
from office_helper.core import CoreState
import tests.common.foo as tcf
from tests.common import async_mock_service
from experiments.starry import *
from experiments.weird import varieties
from office_helper.assistants import entity_registry as er

View File

@@ -1,13 +0,0 @@
from __future__ import annotations
from typing import Any
from requests import Session
from my_first_party import my_first_party_object
from . import my_local_folder_object
class Thing(object):
name: str
def __init__(self, name: str):
self.name = name

View File

@@ -1,22 +0,0 @@
from __future__ import annotations
from typing import Any
from requests import Session
from my_first_party import my_first_party_object
from . import my_local_folder_object
def main():
my_local_folder_object.get()

View File

@@ -1,9 +0,0 @@
from __future__ import annotations
from typing import Any
from requests import Session
from my_first_party import my_first_party_object
from . import my_local_folder_object

View File

@@ -1,29 +0,0 @@
from numpy import (
cos,
int8,
int16,
int32,
int64,
sin,
tan,
uint8,
uint16,
uint32,
uint64,
)
if True:
# inside nested block
from numpy import (
cos,
int8,
int16,
int32,
int64,
sin,
tan,
uint8,
uint16,
uint32,
uint64,
)

View File

@@ -1,13 +0,0 @@
from numpy import (
cos,
int8,
int16,
int32,
int64,
sin,
tan,
uint8,
uint16,
uint32,
uint64,
)

View File

@@ -1,5 +0,0 @@
[tool.ruff]
line-length = 88
[tool.ruff.isort]
lines-after-imports = 3

View File

@@ -1,3 +0,0 @@
from .logging import config_logging
from .settings import ENV
from .settings import *

View File

@@ -1,20 +0,0 @@
import pandas as pd
x = pd.DataFrame()
x.drop(["a"], axis=1, inplace=True)
x.drop(["a"], axis=1, inplace=True)
x.drop(
inplace=True,
columns=["a"],
axis=1,
)
if True:
x.drop(
inplace=True,
columns=["a"],
axis=1,
)

View File

@@ -1,40 +0,0 @@
import collections
from collections import namedtuple
from typing import TypeVar
from typing import NewType
from typing import NamedTuple, TypedDict
GLOBAL: str = "foo"
def assign():
global GLOBAL
GLOBAL = "bar"
lower = 0
Camel = 0
CONSTANT = 0
_ = 0
MyObj1 = collections.namedtuple("MyObj1", ["a", "b"])
MyObj2 = namedtuple("MyObj12", ["a", "b"])
T = TypeVar("T")
UserId = NewType("UserId", int)
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
Point2D = TypedDict('Point2D', {'in': int, 'x-y': int})
def aug_assign(rank, world_size):
global CURRENT_PORT
CURRENT_PORT += 1
if CURRENT_PORT > MAX_PORT:
CURRENT_PORT = START_PORT
def loop_assign():
global CURRENT_PORT
for CURRENT_PORT in range(5):
pass

View File

@@ -1,42 +0,0 @@
#: E111
if x > 2:
print(x)
#: E111 E117
if True:
print()
#: E112
if False:
print()
#: E113
print()
print()
#: E114 E116
mimetype = 'application/x-directory'
# 'httpd/unix-directory'
create_date = False
#: E116 E116 E116
def start(self):
if True:
self.master.start()
# try:
# self.master.start()
# except MasterExit:
# self.shutdown()
# finally:
# sys.exit()
#: E115 E115 E115 E115 E115 E115
def start(self):
if True:
# try:
# self.master.start()
# except MasterExit:
# self.shutdown()
# finally:
# sys.exit()
self.master.start()
#: E117
def start():
print()
#: E117 W191
def start():
print()

View File

@@ -1,78 +0,0 @@
#: E201:1:6
spam( ham[1], {eggs: 2})
#: E201:1:10
spam(ham[ 1], {eggs: 2})
#: E201:1:15
spam(ham[1], { eggs: 2})
#: E201:1:6
spam( ham[1], {eggs: 2})
#: E201:1:10
spam(ham[ 1], {eggs: 2})
#: E201:1:15
spam(ham[1], { eggs: 2})
#: Okay
spam(ham[1], {eggs: 2})
#:
#: E202:1:23
spam(ham[1], {eggs: 2} )
#: E202:1:22
spam(ham[1], {eggs: 2 })
#: E202:1:11
spam(ham[1 ], {eggs: 2})
#: E202:1:23
spam(ham[1], {eggs: 2} )
#: E202:1:22
spam(ham[1], {eggs: 2 })
#: E202:1:11
spam(ham[1 ], {eggs: 2})
#: Okay
spam(ham[1], {eggs: 2})
result = func(
arg1='some value',
arg2='another value',
)
result = func(
arg1='some value',
arg2='another value'
)
result = [
item for item in items
if item > 5
]
#:
#: E203:1:10
if x == 4 :
print x, y
x, y = y, x
#: E203:1:10
if x == 4 :
print x, y
x, y = y, x
#: E203:2:15 E702:2:16
if x == 4:
print x, y ; x, y = y, x
#: E203:2:15 E702:2:16
if x == 4:
print x, y ; x, y = y, x
#: E203:3:13
if x == 4:
print x, y
x, y = y , x
#: E203:3:13
if x == 4:
print x, y
x, y = y , x
#: Okay
if x == 4:
print x, y
x, y = y, x
a[b1, :] == a[b1, ...]
b = a[:, b1]
#:

View File

@@ -1,171 +0,0 @@
#: E221
a = 12 + 3
b = 4 + 5
#: E221 E221
x = 1
y = 2
long_variable = 3
#: E221 E221
x[0] = 1
x[1] = 2
long_variable = 3
#: E221 E221
x = f(x) + 1
y = long_variable + 2
z = x[0] + 3
#: E221:3:14
text = """
bar
foo %s""" % rofl
#: Okay
x = 1
y = 2
long_variable = 3
#:
#: E222
a = a + 1
b = b + 10
#: E222 E222
x = -1
y = -2
long_variable = 3
#: E222 E222
x[0] = 1
x[1] = 2
long_variable = 3
#:
#: E223
foobart = 4
a = 3 # aligned with tab
#:
#: E224
a += 1
b += 1000
#:
#: E225
submitted +=1
#: E225
submitted+= 1
#: E225
c =-1
#: E225
x = x /2 - 1
#: E225
c = alpha -4
#: E225
c = alpha- 4
#: E225
z = x **y
#: E225
z = (x + 1) **y
#: E225
z = (x + 1)** y
#: E225
_1kB = _1MB >>10
#: E225
_1kB = _1MB>> 10
#: E225 E225
i=i+ 1
#: E225 E225
i=i +1
#: E225
i = 1and 1
#: E225
i = 1or 0
#: E225
1is 1
#: E225
1in []
#: E225
i = 1 @2
#: E225
i = 1@ 2
#: E225 E226
i=i+1
#: E225 E226
i =i+1
#: E225 E226
i= i+1
#: E225 E226
c = (a +b)*(a - b)
#: E225 E226
c = (a+ b)*(a - b)
#:
#: E226
z = 2//30
#: E226 E226
c = (a+b) * (a-b)
#: E226
norman = True+False
#: E226
x = x*2 - 1
#: E226
x = x/2 - 1
#: E226 E226
hypot2 = x*x + y*y
#: E226
c = (a + b)*(a - b)
#: E226
def halves(n):
return (i//2 for i in range(n))
#: E227
_1kB = _1MB>>10
#: E227
_1MB = _1kB<<10
#: E227
a = b|c
#: E227
b = c&a
#: E227
c = b^a
#: E228
a = b%c
#: E228
msg = fmt%(errno, errmsg)
#: E228
msg = "Error %d occurred"%errno
#:
#: Okay
i = i + 1
submitted += 1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
_1MiB = 2 ** 20
_1TiB = 2**30
foo(bar, key='word', *args, **kwargs)
baz(**kwargs)
negative = -1
spam(-1)
-negative
func1(lambda *args, **kw: (args, kw))
func2(lambda a, b=h[:], c=0: (a, b, c))
if not -5 < x < +5:
print >>sys.stderr, "x is out of range."
print >> sys.stdout, "x is an integer."
x = x / 2 - 1
x = 1 @ 2
if alpha[:-i]:
*a, b = (1, 2, 3)
def squares(n):
return (i**2 for i in range(n))
ENG_PREFIXES = {
-6: "\u03bc", # Greek letter mu
-3: "m",
}
#:

View File

@@ -1,87 +0,0 @@
"""Test: no-blank-line-after-function special-cases around comment handling."""
# OK
def outer():
"""This is a docstring."""
def inner():
return
return inner
# OK
def outer():
"""This is a docstring."""
def inner():
return
return inner
# OK
def outer():
"""This is a docstring."""
# This is a comment.
def inner():
return
return inner
# OK
def outer():
"""This is a docstring."""
# This is a comment.
def inner():
return
return inner
# OK
def outer():
"""This is a docstring."""
# This is a comment.
# Followed by another comment.
def inner():
return
return inner
# D202
def outer():
"""This is a docstring."""
def inner():
return
return inner
# D202
def outer():
"""This is a docstring."""
# This is a comment.
def inner():
return
return inner
# D202
def outer():
"""This is a docstring."""
# This is a comment.
def inner():
return
return inner

View File

@@ -1,6 +0,0 @@
"""Test: explicit re-export of shadowed builtins."""
from concurrent.futures import (
CancelledError as CancelledError,
TimeoutError as TimeoutError,
)

View File

@@ -1,53 +0,0 @@
"""Test: noqa directives."""
from typing_extensions import List, Sequence
# This should ignore both errors.
from typing import ( # noqa: F811
List,
Sequence,
)
# This should ignore both errors.
from typing import ( # noqa
List,
Sequence,
)
# This should ignore both errors.
from typing import (
List, # noqa: F811
Sequence, # noqa: F811
)
# This should ignore both errors.
from typing import (
List, # noqa
Sequence, # noqa
)
# This should ignore the first error.
from typing import (
List, # noqa: F811
Sequence,
)
# This should ignore both errors.
from typing import ( # noqa
List,
Sequence,
)
# This should ignore both errors.
from typing import List, Sequence # noqa: F811
# This should ignore both errors.
from typing import List, Sequence # noqa
def f():
# This should ignore both errors.
from typing import ( # noqa: F811
List,
Sequence,
)

View File

@@ -1,3 +0,0 @@
a = 1
__all__ = list(["a", "b"])

View File

@@ -1,15 +0,0 @@
from __future__ import annotations
# Test case for https://github.com/charliermarsh/ruff/issues/1552
def f():
x = 0
list()[x:]
# Test case for https://github.com/charliermarsh/ruff/issues/2603
def f():
KeyTupleT = tuple[str, ...]
keys_checked: set[KeyTupleT] = set()
return keys_checked

View File

@@ -1,26 +0,0 @@
def f():
from typing import Literal
# OK
x: Literal["foo"]
def f():
from .typical import Literal
# OK
x: Literal["foo"]
def f():
from . import typical
# OK
x: typical.Literal["foo"]
def f():
from .atypical import Literal
# F821
x: Literal["foo"]

View File

@@ -1,33 +0,0 @@
def f():
from typing import Literal
# OK
x: Literal["foo"]
def f():
from ..typical import Literal
# OK
x: Literal["foo"]
def f():
from .. import typical
# OK
x: typical.Literal["foo"]
def f():
from .typical import Literal
# F821
x: Literal["foo"]
def f():
from .atypical import Literal
# F821
x: Literal["foo"]

View File

@@ -1,73 +0,0 @@
# PLE1310
"Hello World".strip("Hello")
# PLE1310
"Hello World".strip("Hello")
# PLE1310
"Hello World".strip(u"Hello")
# PLE1310
"Hello World".strip(r"Hello")
# PLE1310
"Hello World".strip("Hello\t")
# PLE1310
"Hello World".strip(r"Hello\t")
# PLE1310
"Hello World".strip("Hello\\")
# PLE1310
"Hello World".strip(r"Hello\\")
# PLE1310
"Hello World".strip("🤣🤣🤣🤣🙃👀😀")
# PLE1310
"Hello World".strip(
"""
there are a lot of characters to strip
"""
)
# PLE1310
"Hello World".strip("can we get a long " \
"string of characters to strip " \
"please?")
# PLE1310
"Hello World".strip(
"can we get a long "
"string of characters to strip "
"please?"
)
# PLE1310
"Hello World".strip(
"can \t we get a long"
"string \t of characters to strip"
"please?"
)
# PLE1310
"Hello World".strip(
"abc def"
"ghi"
)
# PLE1310
u''.strip('http://')
# PLE1310
u''.lstrip('http://')
# PLE1310
b''.rstrip('http://')
# OK
''.strip('Hi')
# OK
''.strip()

View File

@@ -1,38 +0,0 @@
###
# Errors.
###
def f():
global X
def f():
global X
print(X)
###
# Non-errors.
###
def f():
global X
X = 1
def f():
global X
(X, y) = (1, 2)
def f():
global X
del X
def f():
global X
X += 1

View File

@@ -1,31 +0,0 @@
__all__ = "CONST" # [invalid-all-format]
__all__ = ["Hello"] + {"world"} # [invalid-all-format]
__all__ += {"world"} # [invalid-all-format]
__all__ = {"world"} + ["Hello"] # [invalid-all-format]
__all__ = {"world"} + list(["Hello"]) # [invalid-all-format]
__all__ = list(["Hello"]) + {"world"} # [invalid-all-format]
__all__ = (x for x in ["Hello", "world"]) # [invalid-all-format]
__all__ = {x for x in ["Hello", "world"]} # [invalid-all-format]
__all__ = ["Hello"]
__all__ = ("Hello",)
__all__ = ["Hello"] + ("world",)
__all__ = [x for x in ["Hello", "world"]]
__all__ = list(["Hello", "world"])
__all__ = list({"Hello", "world"})
__all__ = list(["Hello"]) + list(["world"])
__all__ = tuple(["Hello"]) + ("world",)

View File

@@ -1,15 +0,0 @@
__all__ = (
None, # [invalid-all-object]
Fruit,
Worm,
)
__all__ = list([None, "Fruit", "Worm"]) # [invalid-all-object]
class Fruit:
pass
class Worm:
pass

View File

@@ -1,34 +0,0 @@
def f(x, y, z, t, u, v, w, r): # Too many arguments (8/5)
pass
def f(x, y, z, t, u): # OK
pass
def f(x): # OK
pass
def f(x, y, z, _t, _u, _v, _w, r): # OK (underscore-prefixed names are ignored
pass
def f(x, y, z, u=1, v=1, r=1): # Too many arguments (6/5)
pass
def f(x=1, y=1, z=1): # OK
pass
def f(x, y, z, /, u, v, w): # OK
pass
def f(x, y, z, *, u, v, w): # OK
pass
def f(x, y, z, a, b, c, *, u, v, w): # Too many arguments (6/5)
pass

View File

@@ -1,10 +0,0 @@
# Too many args (6/4) for max_args=4
# OK for dummy_variable_rgx ~ "skip_.*"
def f(x, y, z, skip_t, skip_u, skip_v):
pass
# Too many args (6/4) for max_args=4
# Too many args (6/5) for dummy_variable_rgx ~ "skip_.*"
def f(x, y, z, t, u, v):
pass

View File

@@ -1,72 +0,0 @@
"""
Test for too many branches.
Taken from the pylint source 2023-02-03
"""
# pylint: disable=using-constant-test
def wrong(): # [too-many-branches]
""" Has too many branches. """
if 1:
pass
elif 1:
pass
elif 1:
pass
elif 1:
pass
elif 1:
pass
elif 1:
pass
try:
pass
finally:
pass
if 2:
pass
while True:
pass
if 1:
pass
elif 2:
pass
elif 3:
pass
def good():
""" Too many branches only if we take
into consideration the nested functions.
"""
def nested_1():
""" empty """
if 1:
pass
elif 2:
pass
elif 3:
pass
elif 4:
pass
nested_1()
try:
pass
finally:
pass
try:
pass
finally:
pass
if 1:
pass
elif 2:
pass
elif 3:
pass
elif 4:
pass
elif 5:
pass
elif 6:
pass
elif 7:
pass

View File

@@ -1,19 +0,0 @@
def f(x):
if x:
pass
def g(x):
if x:
pass
else:
pass
def h(x):
return
def i(x):
if x:
pass
else:
pass

View File

@@ -1,41 +0,0 @@
"""
https://github.com/PyCQA/pylint/blob/69eca9b3f9856c3033957b769358803ee48e8e47/tests/functional/t/too/too_many_return_statements.py
"""
def stupid_function(arg): # [too-many-return-statements]
if arg == 1:
return 1
if arg == 2:
return 2
if arg == 3:
return 3
if arg == 4:
return 4
if arg == 5:
return 5
if arg == 6:
return 6
if arg == 7:
return 7
if arg == 8:
return 8
if arg == 9:
return 9
if arg == 10:
return 10
return None
def many_yield(text):
"""Not a problem"""
if text:
yield f" line 1: {text}\n"
yield " line 2\n"
yield " line 3\n"
yield " line 4\n"
yield " line 5\n"
else:
yield " line 6\n"
yield " line 7\n"
yield " line 8\n"
yield " line 9\n"
yield " line 10\n"

View File

@@ -1,5 +0,0 @@
def f(x): # Too many return statements (2/1)
if x == 1:
return
if x == 2:
return

View File

@@ -1,56 +0,0 @@
def f(): # OK
return
async def f(): # Too many statements (52/50)
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()
print()

View File

@@ -1,8 +0,0 @@
# Too may statements (2/1) for max_statements=1
def f(x):
pass
def f(x):
def g(x):
pass

View File

@@ -1,15 +0,0 @@
# Valid calls; no errors expected.
'{}'.format(1)
x = ('{0} {1}',)
'{0} {0}'.format(1)
'{0:<{1}}'.format(1, 4)
f"{0}".format(a)
f"{0}".format(1)
print(f"{0}".format(1))

View File

@@ -1,28 +0,0 @@
# These SHOULD change
args = list(range(10))
kwargs = {x: x for x in range(10)}
"{0}".format(*args)
"{0}".format(**kwargs)
"{0}_{1}".format(*args)
"{0}_{1}".format(1, *args)
"{1}_{0}".format(*args)
"{1}_{0}".format(1, *args)
"{0}_{1}".format(1, 2, *args)
"{0}_{1}".format(*args, 1, 2)
"{0}_{1}_{2}".format(1, **kwargs)
"{0}_{1}_{2}".format(1, 2, **kwargs)
"{0}_{1}_{2}".format(1, 2, 3, **kwargs)
"{0}_{1}_{2}".format(1, 2, 3, *args, **kwargs)

View File

@@ -1,50 +0,0 @@
# UP035
from collections import Mapping
from collections import Mapping as MAP
from collections import Mapping, Sequence
from collections import Counter, Mapping
from collections import (Counter, Mapping)
from collections import (Counter,
Mapping)
from collections import Counter, \
Mapping
from collections import Counter, Mapping, Sequence
from collections import Mapping as mapping, Counter
if True:
from collections import Mapping, Counter
if True:
if True:
pass
from collections import Mapping, Counter
if True: from collections import Mapping
import os
from collections import Counter, Mapping
import sys
if True:
from collections import (
Mapping,
Callable,
Bad,
Good,
)
from typing import Callable, Match, Pattern, List
if True: from collections import (
Mapping, Counter)
# OK
from a import b

Some files were not shown because too many files have changed in this diff Show More