Compare commits
4 Commits
deps/parse
...
4404_fix_e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2abdc2540a | ||
|
|
1e36145972 | ||
|
|
bd2c50be86 | ||
|
|
51bc758d18 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -4,4 +4,3 @@ crates/ruff/resources/test/fixtures/isort/line_ending_crlf.py text eol=crlf
|
||||
crates/ruff/resources/test/fixtures/pycodestyle/W605_1.py text eol=crlf
|
||||
|
||||
ruff.schema.json linguist-generated=true text=auto eol=lf
|
||||
*.md.snap linguist-language=Markdown
|
||||
|
||||
71
.github/workflows/ci.yaml
vendored
71
.github/workflows/ci.yaml
vendored
@@ -16,7 +16,7 @@ env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
PACKAGE_NAME: ruff
|
||||
PYTHON_VERSION: "3.11" # to build abi3 wheels
|
||||
PYTHON_VERSION: "3.7" # to build abi3 wheels
|
||||
|
||||
jobs:
|
||||
cargo-fmt:
|
||||
@@ -31,6 +31,17 @@ jobs:
|
||||
cargo-clippy:
|
||||
name: "cargo clippy"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
run: |
|
||||
rustup component add clippy
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
|
||||
cargo-clippy-wasm:
|
||||
name: "cargo clippy (wasm)"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -38,10 +49,7 @@ jobs:
|
||||
rustup component add clippy
|
||||
rustup target add wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Clippy"
|
||||
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
- name: "Clippy (wasm)"
|
||||
run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features -- -D warnings
|
||||
- run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features -- -D warnings
|
||||
|
||||
cargo-test:
|
||||
strategy:
|
||||
@@ -54,21 +62,20 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
# cargo insta 1.30.0 fails for some reason (https://github.com/mitsuhiko/insta/issues/392)
|
||||
- run: cargo install cargo-insta@=1.29.0
|
||||
- run: cargo install cargo-insta
|
||||
- run: pip install black[d]==23.1.0
|
||||
- name: "Run tests (Ubuntu)"
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: cargo insta test --all --all-features --unreferenced reject
|
||||
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
|
||||
# We can't reject unreferenced snapshots on windows because flake8_executable can't run on windows
|
||||
run: cargo insta test --all --all-features
|
||||
run: |
|
||||
cargo insta test --all --all-features
|
||||
git diff --exit-code
|
||||
- run: cargo test --package ruff_cli --test black_compatibility_test -- --ignored
|
||||
# TODO: Skipped as it's currently broken. The resource were moved from the
|
||||
# ruff_cli to ruff crate, but this test was not updated.
|
||||
if: false
|
||||
# Check for broken links in the documentation.
|
||||
- run: cargo doc --all --no-deps
|
||||
env:
|
||||
@@ -142,7 +149,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
python-version: "3.11"
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
name: Download Ruff binary
|
||||
@@ -210,10 +217,11 @@ jobs:
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
manylinux: auto
|
||||
args: --out dist
|
||||
- name: "Test wheel"
|
||||
run: |
|
||||
pip install --force-reinstall --find-links dist ${{ env.PACKAGE_NAME }}
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Remove wheels from cache"
|
||||
@@ -226,7 +234,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
python-version: "3.11"
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -250,24 +258,13 @@ jobs:
|
||||
docs:
|
||||
name: "mkdocs"
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
- name: "Add SSH key"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
uses: webfactory/ssh-agent@v0.8.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: pip install -r docs/requirements-insiders.txt
|
||||
- name: "Install dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: pip install -r docs/requirements.txt
|
||||
- name: "Update README File"
|
||||
run: python scripts/transform_readme.py --target mkdocs
|
||||
@@ -275,23 +272,5 @@ jobs:
|
||||
run: python scripts/generate_mkdocs.py
|
||||
- name: "Check docs formatting"
|
||||
run: python scripts/check_docs_formatted.py
|
||||
- name: "Build Insiders docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.insiders.yml
|
||||
- name: "Build docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.generated.yml
|
||||
|
||||
check-formatter-stability:
|
||||
name: "Check formatter stability"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Cache rust"
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: "Clone CPython 3.10"
|
||||
run: git clone --branch 3.10 --depth 1 https://github.com/python/cpython.git crates/ruff/resources/test/cpython
|
||||
- name: "Check stability"
|
||||
run: cargo run --bin ruff_dev -- format-dev --stability-check crates/ruff/resources/test/cpython
|
||||
run: mkdocs build --strict
|
||||
|
||||
20
.github/workflows/docs.yaml
vendored
20
.github/workflows/docs.yaml
vendored
@@ -10,34 +10,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
|
||||
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
- name: "Add SSH key"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
uses: webfactory/ssh-agent@v0.8.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: pip install -r docs/requirements-insiders.txt
|
||||
- name: "Install dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: pip install -r docs/requirements.txt
|
||||
run: |
|
||||
pip install -r docs/requirements.txt
|
||||
- name: "Copy README File"
|
||||
run: |
|
||||
python scripts/transform_readme.py --target mkdocs
|
||||
python scripts/generate_mkdocs.py
|
||||
- name: "Build Insiders docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.insiders.yml
|
||||
- name: "Build docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.generated.yml
|
||||
mkdocs build --strict
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
|
||||
2
.github/workflows/flake8-to-ruff.yaml
vendored
2
.github/workflows/flake8-to-ruff.yaml
vendored
@@ -9,7 +9,7 @@ concurrency:
|
||||
env:
|
||||
PACKAGE_NAME: flake8-to-ruff
|
||||
CRATE_NAME: flake8_to_ruff
|
||||
PYTHON_VERSION: "3.11"
|
||||
PYTHON_VERSION: "3.7" # to build abi3 wheels
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
95
.github/workflows/release.yaml
vendored
95
.github/workflows/release.yaml
vendored
@@ -2,17 +2,8 @@ name: "[ruff] Release"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "The version to tag, without the leading 'v'. If omitted, will initiate a dry run (no uploads)."
|
||||
type: string
|
||||
sha:
|
||||
description: "Optionally, the full sha of the commit to be released"
|
||||
type: string
|
||||
pull_request:
|
||||
paths:
|
||||
# When we change pyproject.toml, we want to ensure that the maturin builds still work
|
||||
- pyproject.toml
|
||||
release:
|
||||
types: [ published ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -20,7 +11,7 @@ concurrency:
|
||||
|
||||
env:
|
||||
PACKAGE_NAME: ruff
|
||||
PYTHON_VERSION: "3.11"
|
||||
PYTHON_VERSION: "3.7" # to build abi3 wheels
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -392,39 +383,8 @@ jobs:
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
validate-tag:
|
||||
name: Validate tag
|
||||
runs-on: ubuntu-latest
|
||||
# If you don't set an input tag, it's a dry run (no uploads).
|
||||
if: ${{ inputs.tag }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check tag consistency
|
||||
run: |
|
||||
version=$(grep "version = " pyproject.toml | sed -e 's/version = "\(.*\)"/\1/g')
|
||||
if [ "${{ inputs.tag }}" != "${version}" ]; then
|
||||
echo "The input tag does not match the version from pyproject.toml:" >&2
|
||||
echo "${{ inputs.tag }}" >&2
|
||||
echo "${version}" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "Releasing ${version}"
|
||||
fi
|
||||
- name: Check SHA consistency
|
||||
if: ${{ inputs.sha }}
|
||||
run: |
|
||||
git_sha=$(git rev-parse HEAD)
|
||||
if [ "${{ inputs.sha }}" != "${git_sha}" ]; then
|
||||
echo "The specified sha does not match the git checkout" >&2
|
||||
echo "${{ inputs.sha }}" >&2
|
||||
echo "${git_sha}" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "Releasing ${git_sha}"
|
||||
fi
|
||||
|
||||
upload-release:
|
||||
name: Upload to PyPI
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- macos-universal
|
||||
@@ -434,56 +394,25 @@ jobs:
|
||||
- linux-cross
|
||||
- musllinux
|
||||
- musllinux-cross
|
||||
- validate-tag
|
||||
# If you don't set an input tag, it's a dry run (no uploads).
|
||||
if: ${{ inputs.tag }}
|
||||
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
|
||||
path: wheels
|
||||
- name: Publish to PyPi
|
||||
- name: "Publish to PyPi"
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
skip-existing: true
|
||||
packages-dir: wheels
|
||||
verbose: true
|
||||
|
||||
tag-release:
|
||||
name: Tag release
|
||||
runs-on: ubuntu-latest
|
||||
needs: upload-release
|
||||
# If you don't set an input tag, it's a dry run (no uploads).
|
||||
if: ${{ inputs.tag }}
|
||||
permissions:
|
||||
# For git tag
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: git tag
|
||||
run: |
|
||||
git config user.email "hey@astral.sh"
|
||||
git config user.name "Ruff Release CI"
|
||||
git tag -m "v${{ inputs.tag }}" "v${{ inputs.tag }}"
|
||||
# If there is duplicate tag, this will fail. The publish to pypi action will have been a noop (due to skip
|
||||
# existing), so we make a non-destructive exit here
|
||||
git push --tags
|
||||
|
||||
publish-release:
|
||||
name: Publish to GitHub
|
||||
runs-on: ubuntu-latest
|
||||
needs: tag-release
|
||||
# If you don't set an input tag, it's a dry run (no uploads).
|
||||
if: ${{ inputs.tag }}
|
||||
permissions:
|
||||
# For GitHub release publishing
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
@@ -491,16 +420,14 @@ jobs:
|
||||
- name: "Publish to GitHub"
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: binaries/*
|
||||
tag_name: v${{ inputs.tag }}
|
||||
|
||||
# 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: Update dependents
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: publish-release
|
||||
needs: release
|
||||
steps:
|
||||
- name: "Update pre-commit mirror"
|
||||
uses: actions/github-script@v6
|
||||
|
||||
17
.gitignore
vendored
17
.gitignore
vendored
@@ -1,24 +1,15 @@
|
||||
# Benchmarking cpython (CONTRIBUTING.md)
|
||||
crates/ruff/resources/test/cpython
|
||||
# generate_mkdocs.py
|
||||
mkdocs.generated.yml
|
||||
# check_ecosystem.py
|
||||
mkdocs.yml
|
||||
.overrides
|
||||
ruff-old
|
||||
github_search*.jsonl
|
||||
# update_schemastore.py
|
||||
schemastore
|
||||
# `maturin develop` and ecosystem_all_check.sh
|
||||
.venv*
|
||||
# Formatter debugging (crates/ruff_python_formatter/README.md)
|
||||
scratch.*
|
||||
# Created by `perf` (CONTRIBUTING.md)
|
||||
scratch.py
|
||||
perf.data
|
||||
perf.data.old
|
||||
# Created by `flamegraph` (CONTRIBUTING.md)
|
||||
flamegraph.svg
|
||||
# Additional target directories that don't invalidate the main compile cache when changing linker settings,
|
||||
# e.g. `CARGO_TARGET_DIR=target-maturin maturin build --release --strip` or
|
||||
# `CARGO_TARGET_DIR=target-llvm-lines RUSTFLAGS="-Csymbol-mangling-version=v0" cargo llvm-lines -p ruff --lib`
|
||||
# Additional target directories that don't invalidate the main compile cache when changing linker settings
|
||||
/target*
|
||||
|
||||
###
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
# default to true for all rules
|
||||
default: true
|
||||
|
||||
# MD007/unordered-list-indent
|
||||
MD007:
|
||||
indent: 4
|
||||
|
||||
# MD033/no-inline-html
|
||||
MD033: false
|
||||
|
||||
@@ -12,4 +8,7 @@ MD033: false
|
||||
MD041: false
|
||||
|
||||
# MD013/line-length
|
||||
MD013: false
|
||||
MD013:
|
||||
line_length: 100
|
||||
code_blocks: false
|
||||
ignore_code_blocks: true
|
||||
|
||||
@@ -4,11 +4,8 @@ exclude: |
|
||||
(?x)^(
|
||||
crates/ruff/resources/.*|
|
||||
crates/ruff/src/rules/.*/snapshots/.*|
|
||||
crates/ruff_cli/resources/.*|
|
||||
crates/ruff_python_formatter/resources/.*|
|
||||
crates/ruff_python_formatter/tests/snapshots/.*|
|
||||
crates/ruff_python_resolver/resources/.*|
|
||||
crates/ruff_python_resolver/tests/snapshots/.*
|
||||
crates/ruff_python_formatter/src/snapshots/.*
|
||||
)$
|
||||
|
||||
repos:
|
||||
@@ -22,7 +19,6 @@ repos:
|
||||
hooks:
|
||||
- id: mdformat
|
||||
additional_dependencies:
|
||||
- mdformat-mkdocs
|
||||
- mdformat-black
|
||||
- black==23.1.0 # Must be the latest version of Black
|
||||
|
||||
|
||||
@@ -1,67 +1,5 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.0.277
|
||||
|
||||
### `.ipynb_checkpoints`, `.pyenv`, `.pytest_cache`, and `.vscode` are now excluded by default ([#5513](https://github.com/astral-sh/ruff/pull/5513))
|
||||
|
||||
Ruff maintains a list of default exclusions, which now consists of the following patterns:
|
||||
|
||||
- `.bzr`
|
||||
- `.direnv`
|
||||
- `.eggs`
|
||||
- `.git`
|
||||
- `.git-rewrite`
|
||||
- `.hg`
|
||||
- `.ipynb_checkpoints`
|
||||
- `.mypy_cache`
|
||||
- `.nox`
|
||||
- `.pants.d`
|
||||
- `.pyenv`
|
||||
- `.pytest_cache`
|
||||
- `.pytype`
|
||||
- `.ruff_cache`
|
||||
- `.svn`
|
||||
- `.tox`
|
||||
- `.venv`
|
||||
- `.vscode`
|
||||
- `__pypackages__`
|
||||
- `_build`
|
||||
- `buck-out`
|
||||
- `build`
|
||||
- `dist`
|
||||
- `node_modules`
|
||||
- `venv`
|
||||
|
||||
Previously, the `.ipynb_checkpoints`, `.pyenv`, `.pytest_cache`, and `.vscode` directories were not
|
||||
excluded by default. This change brings Ruff's default exclusions in line with other tools like
|
||||
Black.
|
||||
|
||||
## 0.0.276
|
||||
|
||||
### The `keep-runtime-typing` setting has been reinstated ([#5470](https://github.com/astral-sh/ruff/pull/5470))
|
||||
|
||||
The `keep-runtime-typing` setting has been reinstated with revised semantics. This setting was
|
||||
removed in [#4427](https://github.com/astral-sh/ruff/pull/4427), as it was equivalent to ignoring
|
||||
the `UP006` and `UP007` rules via Ruff's standard `ignore` mechanism.
|
||||
|
||||
Taking `UP006` (rewrite `List[int]` to `list[int]`) as an example, the setting now behaves as
|
||||
follows:
|
||||
|
||||
- On Python 3.7 and Python 3.8, setting `keep-runtime-typing = true` will cause Ruff to ignore
|
||||
`UP006` violations, even if `from __future__ import annotations` is present in the file.
|
||||
While such annotations are valid in Python 3.7 and Python 3.8 when combined with
|
||||
`from __future__ import annotations`, they aren't supported by libraries like Pydantic and
|
||||
FastAPI, which rely on runtime type checking.
|
||||
- On Python 3.9 and above, the setting has no effect, as `list[int]` is a valid type annotation,
|
||||
and libraries like Pydantic and FastAPI support it without issue.
|
||||
|
||||
In short: `keep-runtime-typing` can be used to ensure that Ruff doesn't introduce type annotations
|
||||
that are not supported at runtime by the current Python version, which are unsupported by libraries
|
||||
like Pydantic and FastAPI.
|
||||
|
||||
Note that this is not a breaking change, but is included here to complement the previous removal
|
||||
of `keep-runtime-typing`.
|
||||
|
||||
## 0.0.268
|
||||
|
||||
### The `keep-runtime-typing` setting has been removed ([#4427](https://github.com/astral-sh/ruff/pull/4427))
|
||||
@@ -203,25 +141,25 @@ 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:
|
||||
of silently ignoring them. For example, the following will now fail:
|
||||
|
||||
```console
|
||||
ruff --clean --respect-gitignore
|
||||
```
|
||||
```console
|
||||
ruff --clean --respect-gitignore
|
||||
```
|
||||
|
||||
(the `clean` command doesn't support `--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`.
|
||||
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.)
|
||||
(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`.
|
||||
This is no longer supported; instead, use `--format text`.
|
||||
|
||||
## 0.0.226
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
- [Scope](#scope)
|
||||
- [Enforcement](#enforcement)
|
||||
- [Enforcement Guidelines](#enforcement-guidelines)
|
||||
- [1. Correction](#1-correction)
|
||||
- [2. Warning](#2-warning)
|
||||
- [3. Temporary Ban](#3-temporary-ban)
|
||||
- [4. Permanent Ban](#4-permanent-ban)
|
||||
- [1. Correction](#1-correction)
|
||||
- [2. Warning](#2-warning)
|
||||
- [3. Temporary Ban](#3-temporary-ban)
|
||||
- [4. Permanent Ban](#4-permanent-ban)
|
||||
- [Attribution](#attribution)
|
||||
|
||||
## Our Pledge
|
||||
@@ -33,20 +33,20 @@ community include:
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
|
||||
473
CONTRIBUTING.md
473
CONTRIBUTING.md
@@ -3,29 +3,16 @@
|
||||
Welcome! We're happy to have you here. Thank you in advance for your contribution to Ruff.
|
||||
|
||||
- [The Basics](#the-basics)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Development](#development)
|
||||
- [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)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Development](#development)
|
||||
- [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)
|
||||
- [Creating a new release](#creating-a-new-release)
|
||||
- [Ecosystem CI](#ecosystem-ci)
|
||||
- [Benchmarking and Profiling](#benchmarking-and-profiling)
|
||||
- [CPython Benchmark](#cpython-benchmark)
|
||||
- [Microbenchmarks](#microbenchmarks)
|
||||
- [Benchmark-driven Development](#benchmark-driven-development)
|
||||
- [PR Summary](#pr-summary)
|
||||
- [Tips](#tips)
|
||||
- [Profiling Projects](#profiling-projects)
|
||||
- [Linux](#linux)
|
||||
- [Mac](#mac)
|
||||
- [`cargo dev`](#cargo-dev)
|
||||
- [Subsystems](#subsystems)
|
||||
- [Compilation Pipeline](#compilation-pipeline)
|
||||
- [Benchmarks](#benchmarks)
|
||||
|
||||
## The Basics
|
||||
|
||||
@@ -36,10 +23,7 @@ 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/astral-sh/ruff/issues) outlining your proposed change.
|
||||
You can also join us on [**Discord**](https://discord.gg/c9MhzV8aU5) to discuss your idea with the
|
||||
community. We've labeled [beginner-friendly tasks](https://github.com/astral-sh/ruff/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
|
||||
in the issue tracker, along with [bugs](https://github.com/astral-sh/ruff/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
|
||||
and [improvements](https://github.com/astral-sh/ruff/issues?q=is%3Aissue+is%3Aopen+label%3Aaccepted)
|
||||
that are ready for contributions.
|
||||
community.
|
||||
|
||||
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
|
||||
@@ -50,8 +34,6 @@ As a concrete example: consider taking on one of the rules from the [`flake8-pyi
|
||||
plugin, and looking to the originating [Python source](https://github.com/PyCQA/flake8-pyi) for
|
||||
guidance.
|
||||
|
||||
If you have suggestions on how we might improve the contributing documentation, [let us know](https://github.com/astral-sh/ruff/discussions/5693)!
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Ruff is written in Rust. You'll need to install the
|
||||
@@ -110,56 +92,48 @@ The vast majority of the code, including all lint rules, lives in the `ruff` cra
|
||||
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.
|
||||
If you're working on a rule, this is the crate for you.
|
||||
- `crates/ruff_benchmark`: binary crate for running micro-benchmarks.
|
||||
- `crates/ruff_cache`: library crate for caching lint results.
|
||||
- `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`), see the [`cargo dev`](#cargo-dev) section below.
|
||||
- `crates/ruff_diagnostics`: library crate for the rule-independent abstractions in the lint
|
||||
diagnostics APIs.
|
||||
- `crates/ruff_formatter`: library crate for language agnostic code formatting logic based on an
|
||||
intermediate representation. The backend for `ruff_python_formatter`.
|
||||
`cargo dev generate-all`).
|
||||
- `crates/ruff_diagnostics`: library crate for the lint diagnostics APIs.
|
||||
- `crates/ruff_formatter`: library crate for generic code formatting logic based on an intermediate
|
||||
representation.
|
||||
- `crates/ruff_index`: library crate inspired by `rustc_index`.
|
||||
- `crates/ruff_macros`: proc macro crate containing macros used by Ruff.
|
||||
- `crates/ruff_python_ast`: library crate containing Python-specific AST types and utilities. Note
|
||||
that the AST schema itself is defined in the
|
||||
[rustpython-ast](https://github.com/astral-sh/RustPython-Parser) crate.
|
||||
- `crates/ruff_python_formatter`: library crate implementing the Python formatter. Emits an
|
||||
intermediate representation for each node, which `ruff_formatter` prints based on the configured
|
||||
line length.
|
||||
- `crates/ruff_macros`: library crate containing macros used by Ruff.
|
||||
- `crates/ruff_python_ast`: library crate containing Python-specific AST types and utilities.
|
||||
- `crates/ruff_python_formatter`: library crate containing Python-specific code formatting logic.
|
||||
- `crates/ruff_python_semantic`: library crate containing Python-specific semantic analysis logic,
|
||||
including Ruff's semantic model. Used to resolve queries like "What import does this variable
|
||||
refer to?"
|
||||
- `crates/ruff_python_stdlib`: library crate containing Python-specific standard library data, e.g.
|
||||
the names of all built-in exceptions and which standard library types are immutable.
|
||||
including Ruff's semantic model.
|
||||
- `crates/ruff_python_stdlib`: library crate containing Python-specific standard library data.
|
||||
- `crates/ruff_python_whitespace`: library crate containing Python-specific whitespace analysis
|
||||
logic (indentation and newlines).
|
||||
logic.
|
||||
- `crates/ruff_rustpython`: library crate containing `RustPython`-specific utilities.
|
||||
- `crates/ruff_testing_macros`: library crate containing macros used for testing Ruff.
|
||||
- `crates/ruff_textwrap`: library crate to indent and dedent Python source code.
|
||||
- `crates/ruff_wasm`: library crate for exposing Ruff as a WebAssembly module. Powers the
|
||||
[Ruff Playground](https://play.ruff.rs/).
|
||||
- `crates/ruff_wasm`: library crate for exposing Ruff as a WebAssembly module.
|
||||
|
||||
### Example: Adding a new lint rule
|
||||
|
||||
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)
|
||||
(e.g., `AssertFalse`, as in, "allow `assert False`").
|
||||
(e.g., `AssertFalse`, as in, "allow `assert False`").
|
||||
|
||||
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/assert_false.rs`).
|
||||
|
||||
1. In that file, define a violation struct (e.g., `pub struct AssertFalse`). You can grep for
|
||||
`#[violation]` to see examples.
|
||||
`#[violation]` to see examples.
|
||||
|
||||
1. In that file, define a function that adds the violation to the diagnostic list as appropriate
|
||||
(e.g., `pub(crate) fn assert_false`) based on whatever inputs are required for the rule (e.g.,
|
||||
an `ast::StmtAssert` node).
|
||||
(e.g., `pub(crate) fn assert_false`) based on whatever inputs are required for the rule (e.g.,
|
||||
an `ast::StmtAssert` node).
|
||||
|
||||
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).
|
||||
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. Map the violation struct to a rule code in `crates/ruff/src/codes.rs` (e.g., `B011`).
|
||||
|
||||
@@ -192,13 +166,13 @@ suppression comment would be framed as "allow `assert False`".
|
||||
As such, rule names should...
|
||||
|
||||
- Highlight the pattern that is being linted against, rather than the preferred alternative.
|
||||
For example, `AssertFalse` guards against `assert False` statements.
|
||||
For example, `AssertFalse` guards against `assert False` statements.
|
||||
|
||||
- _Not_ contain instructions on how to fix the violation, which instead belong in the rule
|
||||
documentation and the `autofix_title`.
|
||||
documentation and the `autofix_title`.
|
||||
|
||||
- _Not_ contain a redundant prefix, like `Disallow` or `Banned`, which are already implied by the
|
||||
convention.
|
||||
convention.
|
||||
|
||||
When re-implementing rules from other linters, we prioritize adhering to this convention over
|
||||
preserving the original rule name.
|
||||
@@ -213,25 +187,25 @@ 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.
|
||||
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:
|
||||
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
|
||||
```
|
||||
```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`)
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -269,25 +243,21 @@ To preview any changes to the documentation locally:
|
||||
|
||||
1. Install MkDocs and Material for MkDocs with:
|
||||
|
||||
```shell
|
||||
pip install -r docs/requirements.txt
|
||||
```
|
||||
```shell
|
||||
pip install -r docs/requirements.txt
|
||||
```
|
||||
|
||||
1. Generate the MkDocs site with:
|
||||
|
||||
```shell
|
||||
python scripts/generate_mkdocs.py
|
||||
```
|
||||
```shell
|
||||
python scripts/generate_mkdocs.py
|
||||
```
|
||||
|
||||
1. Run the development server with:
|
||||
|
||||
```shell
|
||||
# For contributors.
|
||||
mkdocs serve -f mkdocs.generated.yml
|
||||
|
||||
# For members of the Astral org, which has access to MkDocs Insiders via sponsorship.
|
||||
mkdocs serve -f mkdocs.insiders.yml
|
||||
```
|
||||
```shell
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
The documentation should then be available locally at
|
||||
[http://127.0.0.1:8000/docs/](http://127.0.0.1:8000/docs/).
|
||||
@@ -301,27 +271,6 @@ 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).
|
||||
|
||||
### Creating a new release
|
||||
|
||||
1. Update the version with `rg 0.0.269 --files-with-matches | xargs sed -i 's/0.0.269/0.0.270/g'`
|
||||
1. Update `BREAKING_CHANGES.md`
|
||||
1. Create a PR with the version and `BREAKING_CHANGES.md` updated
|
||||
1. Merge the PR
|
||||
1. Run the release workflow with the version number (without starting `v`) as input. Make sure
|
||||
main has your merged PR as last commit
|
||||
1. The release workflow will do the following:
|
||||
1. Build all the assets. If this fails (even though we tested in step 4), we haven’t tagged or
|
||||
uploaded anything, you can restart after pushing a fix.
|
||||
1. Upload to PyPI.
|
||||
1. Create and push the Git tag (as extracted from `pyproject.toml`). We create the Git tag only
|
||||
after building the wheels and uploading to PyPI, since we can't delete or modify the tag ([#4468](https://github.com/charliermarsh/ruff/issues/4468)).
|
||||
1. Attach artifacts to draft GitHub release
|
||||
1. Trigger downstream repositories. This can fail non-catastrophically, as we can run any
|
||||
downstream jobs manually if needed.
|
||||
1. Create release notes in GitHub UI and promote from draft.
|
||||
1. If needed, [update the schemastore](https://github.com/charliermarsh/ruff/blob/main/scripts/update_schemastore.py)
|
||||
1. If needed, update the `ruff-lsp` and `ruff-vscode` repositories.
|
||||
|
||||
## Ecosystem CI
|
||||
|
||||
GitHub Actions will run your changes against a number of real-world projects from GitHub and
|
||||
@@ -336,15 +285,7 @@ downloading the [`known-github-tomls.json`](https://github.com/akx/ruff-usage-ag
|
||||
as `github_search.jsonl` and following the instructions in [scripts/Dockerfile.ecosystem](https://github.com/astral-sh/ruff/blob/main/scripts/Dockerfile.ecosystem).
|
||||
Note that this check will take a while to run.
|
||||
|
||||
## Benchmarking and Profiling
|
||||
|
||||
We have several ways of benchmarking and profiling Ruff:
|
||||
|
||||
- Our main performance benchmark comparing Ruff with other tools on the CPython codebase
|
||||
- Microbenchmarks which the linter or the formatter on individual files. There run on pull requests.
|
||||
- Profiling the linter on either the microbenchmarks or entire projects
|
||||
|
||||
### CPython Benchmark
|
||||
## 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.
|
||||
@@ -356,18 +297,22 @@ git clone --branch 3.10 https://github.com/python/cpython.git crates/ruff/resour
|
||||
To benchmark the release build:
|
||||
|
||||
```shell
|
||||
cargo build --release && hyperfine --warmup 10 \
|
||||
"./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache -e" \
|
||||
"./target/release/ruff ./crates/ruff/resources/test/cpython/ -e"
|
||||
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'
|
||||
@@ -419,16 +364,9 @@ Summary
|
||||
159.43 ± 2.48 times faster than 'pycodestyle crates/ruff/resources/test/cpython'
|
||||
```
|
||||
|
||||
To benchmark a subset of rules, e.g. `LineTooLong` and `DocLineTooLong`:
|
||||
|
||||
```shell
|
||||
cargo build --release && hyperfine --warmup 10 \
|
||||
"./target/release/ruff ./crates/ruff/resources/test/cpython/ --no-cache -e --select W505,E501"
|
||||
```
|
||||
|
||||
You can run `poetry install` from `./scripts/benchmarks` to create a working environment for the
|
||||
above. All reported benchmarks were computed using the versions specified by
|
||||
`./scripts/benchmarks/pyproject.toml` on Python 3.11.
|
||||
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:
|
||||
|
||||
@@ -469,288 +407,3 @@ 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
|
||||
```
|
||||
|
||||
### Microbenchmarks
|
||||
|
||||
The `ruff_benchmark` crate benchmarks the linter and the formatter on individual files.
|
||||
|
||||
You can run the benchmarks with
|
||||
|
||||
```shell
|
||||
cargo benchmark
|
||||
```
|
||||
|
||||
#### Benchmark-driven Development
|
||||
|
||||
Ruff uses [Criterion.rs](https://bheisler.github.io/criterion.rs/book/) for benchmarks. You can use
|
||||
`--save-baseline=<name>` to store an initial baseline benchmark (e.g. on `main`) and then use
|
||||
`--benchmark=<name>` to compare against that benchmark. Criterion will print a message telling you
|
||||
if the benchmark improved/regressed compared to that baseline.
|
||||
|
||||
```shell
|
||||
# Run once on your "baseline" code
|
||||
cargo benchmark --save-baseline=main
|
||||
|
||||
# Then iterate with
|
||||
cargo benchmark --baseline=main
|
||||
```
|
||||
|
||||
#### PR Summary
|
||||
|
||||
You can use `--save-baseline` and `critcmp` to get a pretty comparison between two recordings.
|
||||
This is useful to illustrate the improvements of a PR.
|
||||
|
||||
```shell
|
||||
# On main
|
||||
cargo benchmark --save-baseline=main
|
||||
|
||||
# After applying your changes
|
||||
cargo benchmark --save-baseline=pr
|
||||
|
||||
critcmp main pr
|
||||
```
|
||||
|
||||
You must install [`critcmp`](https://github.com/BurntSushi/critcmp) for the comparison.
|
||||
|
||||
```bash
|
||||
cargo install critcmp
|
||||
```
|
||||
|
||||
#### Tips
|
||||
|
||||
- Use `cargo benchmark <filter>` to only run specific benchmarks. For example: `cargo benchmark linter/pydantic`
|
||||
to only run the pydantic tests.
|
||||
- Use `cargo benchmark --quiet` for a more cleaned up output (without statistical relevance)
|
||||
- Use `cargo benchmark --quick` to get faster results (more prone to noise)
|
||||
|
||||
### Profiling Projects
|
||||
|
||||
You can either use the microbenchmarks from above or a project directory for benchmarking. There
|
||||
are a lot of profiling tools out there,
|
||||
[The Rust Performance Book](https://nnethercote.github.io/perf-book/profiling.html) lists some
|
||||
examples.
|
||||
|
||||
#### Linux
|
||||
|
||||
Install `perf` and build `ruff_benchmark` with the `release-debug` profile and then run it with perf
|
||||
|
||||
```shell
|
||||
cargo bench -p ruff_benchmark --no-run --profile=release-debug && perf record --call-graph dwarf -F 9999 cargo bench -p ruff_benchmark --profile=release-debug -- --profile-time=1
|
||||
```
|
||||
|
||||
You can also use the `ruff_dev` launcher to run `ruff check` multiple times on a repository to
|
||||
gather enough samples for a good flamegraph (change the 999, the sample rate, and the 30, the number
|
||||
of checks, to your liking)
|
||||
|
||||
```shell
|
||||
cargo build --bin ruff_dev --profile=release-debug
|
||||
perf record -g -F 999 target/release-debug/ruff_dev repeat --repeat 30 --exit-zero --no-cache path/to/cpython > /dev/null
|
||||
```
|
||||
|
||||
Then convert the recorded profile
|
||||
|
||||
```shell
|
||||
perf script -F +pid > /tmp/test.perf
|
||||
```
|
||||
|
||||
You can now view the converted file with [firefox profiler](https://profiler.firefox.com/), with a
|
||||
more in-depth guide [here](https://profiler.firefox.com/docs/#/./guide-perf-profiling)
|
||||
|
||||
An alternative is to convert the perf data to `flamegraph.svg` using
|
||||
[flamegraph](https://github.com/flamegraph-rs/flamegraph) (`cargo install flamegraph`):
|
||||
|
||||
```shell
|
||||
flamegraph --perfdata perf.data
|
||||
```
|
||||
|
||||
#### Mac
|
||||
|
||||
Install [`cargo-instruments`](https://crates.io/crates/cargo-instruments):
|
||||
|
||||
```shell
|
||||
cargo install cargo-instruments
|
||||
```
|
||||
|
||||
Then run the profiler with
|
||||
|
||||
```shell
|
||||
cargo instruments -t time --bench linter --profile release-debug -p ruff_benchmark -- --profile-time=1
|
||||
```
|
||||
|
||||
- `-t`: Specifies what to profile. Useful options are `time` to profile the wall time and `alloc`
|
||||
for profiling the allocations.
|
||||
- You may want to pass an additional filter to run a single test file
|
||||
|
||||
Otherwise, follow the instructions from the linux section.
|
||||
|
||||
## `cargo dev`
|
||||
|
||||
`cargo dev` is a shortcut for `cargo run --package ruff_dev --bin ruff_dev`. You can run some useful
|
||||
utils with it:
|
||||
|
||||
- `cargo dev print-ast <file>`: Print the AST of a python file using the
|
||||
[RustPython parser](https://github.com/astral-sh/RustPython-Parser/tree/main/parser) that is
|
||||
mainly used in Ruff. For `if True: pass # comment`, you can see the syntax tree, the byte offsets
|
||||
for start and stop of each node and also how the `:` token, the comment and whitespace are not
|
||||
represented anymore:
|
||||
|
||||
```text
|
||||
[
|
||||
If(
|
||||
StmtIf {
|
||||
range: 0..13,
|
||||
test: Constant(
|
||||
ExprConstant {
|
||||
range: 3..7,
|
||||
value: Bool(
|
||||
true,
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
),
|
||||
body: [
|
||||
Pass(
|
||||
StmtPass {
|
||||
range: 9..13,
|
||||
},
|
||||
),
|
||||
],
|
||||
orelse: [],
|
||||
},
|
||||
),
|
||||
]
|
||||
```
|
||||
|
||||
- `cargo dev print-tokens <file>`: Print the tokens that the AST is built upon. Again for
|
||||
`if True: pass # comment`:
|
||||
|
||||
```text
|
||||
0 If 2
|
||||
3 True 7
|
||||
7 Colon 8
|
||||
9 Pass 13
|
||||
14 Comment(
|
||||
"# comment",
|
||||
) 23
|
||||
23 Newline 24
|
||||
```
|
||||
|
||||
- `cargo dev print-cst <file>`: Print the CST of a python file using
|
||||
[LibCST](https://github.com/Instagram/LibCST), which is used in addition to the RustPython parser
|
||||
in Ruff. E.g. for `if True: pass # comment` everything including the whitespace is represented:
|
||||
|
||||
```text
|
||||
Module {
|
||||
body: [
|
||||
Compound(
|
||||
If(
|
||||
If {
|
||||
test: Name(
|
||||
Name {
|
||||
value: "True",
|
||||
lpar: [],
|
||||
rpar: [],
|
||||
},
|
||||
),
|
||||
body: SimpleStatementSuite(
|
||||
SimpleStatementSuite {
|
||||
body: [
|
||||
Pass(
|
||||
Pass {
|
||||
semicolon: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
leading_whitespace: SimpleWhitespace(
|
||||
" ",
|
||||
),
|
||||
trailing_whitespace: TrailingWhitespace {
|
||||
whitespace: SimpleWhitespace(
|
||||
" ",
|
||||
),
|
||||
comment: Some(
|
||||
Comment(
|
||||
"# comment",
|
||||
),
|
||||
),
|
||||
newline: Newline(
|
||||
None,
|
||||
Real,
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
orelse: None,
|
||||
leading_lines: [],
|
||||
whitespace_before_test: SimpleWhitespace(
|
||||
" ",
|
||||
),
|
||||
whitespace_after_test: SimpleWhitespace(
|
||||
"",
|
||||
),
|
||||
is_elif: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
header: [],
|
||||
footer: [],
|
||||
default_indent: " ",
|
||||
default_newline: "\n",
|
||||
has_trailing_newline: true,
|
||||
encoding: "utf-8",
|
||||
}
|
||||
```
|
||||
|
||||
- `cargo dev generate-all`: Update `ruff.schema.json`, `docs/configuration.md` and `docs/rules`.
|
||||
You can also set `RUFF_UPDATE_SCHEMA=1` to update `ruff.schema.json` during `cargo test`.
|
||||
- `cargo dev generate-cli-help`, `cargo dev generate-docs` and `cargo dev generate-json-schema`:
|
||||
Update just `docs/configuration.md`, `docs/rules` and `ruff.schema.json` respectively.
|
||||
- `cargo dev generate-options`: Generate a markdown-compatible table of all `pyproject.toml`
|
||||
options. Used for <https://beta.ruff.rs/docs/settings/>
|
||||
- `cargo dev generate-rules-table`: Generate a markdown-compatible table of all rules. Used for <https://beta.ruff.rs/docs/rules/>
|
||||
- `cargo dev round-trip <python file or jupyter notebook>`: Read a Python file or Jupyter Notebook,
|
||||
parse it, serialize the parsed representation and write it back. Used to check how good our
|
||||
representation is so that fixes don't rewrite irrelevant parts of a file.
|
||||
- `cargo dev format_dev`: See ruff_python_formatter README.md
|
||||
|
||||
## Subsystems
|
||||
|
||||
### Compilation Pipeline
|
||||
|
||||
If we view Ruff as a compiler, in which the inputs are paths to Python files and the outputs are
|
||||
diagnostics, then our current compilation pipeline proceeds as follows:
|
||||
|
||||
1. **File discovery**: Given paths like `foo/`, locate all Python files in any specified subdirectories, taking into account our hierarchical settings system and any `exclude` options.
|
||||
|
||||
1. **Package resolution**: Determine the “package root” for every file by traversing over its parent directories and looking for `__init__.py` files.
|
||||
|
||||
1. **Cache initialization**: For every “package root”, initialize an empty cache.
|
||||
|
||||
1. **Analysis**: For every file, in parallel:
|
||||
|
||||
1. **Cache read**: If the file is cached (i.e., its modification timestamp hasn't changed since it was last analyzed), short-circuit, and return the cached diagnostics.
|
||||
|
||||
1. **Tokenization**: Run the lexer over the file to generate a token stream.
|
||||
|
||||
1. **Indexing**: Extract metadata from the token stream, such as: comment ranges, `# noqa` locations, `# isort: off` locations, “doc lines”, etc.
|
||||
|
||||
1. **Token-based rule evaluation**: Run any lint rules that are based on the contents of the token stream (e.g., commented-out code).
|
||||
|
||||
1. **Filesystem-based rule evaluation**: Run any lint rules that are based on the contents of the filesystem (e.g., lack of `__init__.py` file in a package).
|
||||
|
||||
1. **Logical line-based rule evaluation**: Run any lint rules that are based on logical lines (e.g., stylistic rules).
|
||||
|
||||
1. **Parsing**: Run the parser over the token stream to produce an AST. (This consumes the token stream, so anything that relies on the token stream needs to happen before parsing.)
|
||||
|
||||
1. **AST-based rule evaluation**: Run any lint rules that are based on the AST. This includes the vast majority of lint rules. As part of this step, we also build the semantic model for the current file as we traverse over the AST. Some lint rules are evaluated eagerly, as we iterate over the AST, while others are evaluated in a deferred manner (e.g., unused imports, since we can’t determine whether an import is unused until we’ve finished analyzing the entire file), after we’ve finished the initial traversal.
|
||||
|
||||
1. **Import-based rule evaluation**: Run any lint rules that are based on the module’s imports (e.g., import sorting). These could, in theory, be included in the AST-based rule evaluation phase — they’re just separated for simplicity.
|
||||
|
||||
1. **Physical line-based rule evaluation**: Run any lint rules that are based on physical lines (e.g., line-length).
|
||||
|
||||
1. **Suppression enforcement**: Remove any violations that are suppressed via `# noqa` directives or `per-file-ignores`.
|
||||
|
||||
1. **Cache write**: Write the generated diagnostics to the package cache using the file as a key.
|
||||
|
||||
1. **Reporting**: Print diagnostics in the specified format (text, JSON, etc.), to the specified output channel (stdout, a file, etc.).
|
||||
|
||||
748
Cargo.lock
generated
748
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
23
Cargo.toml
23
Cargo.toml
@@ -21,9 +21,10 @@ filetime = { version = "0.2.20" }
|
||||
glob = { version = "0.3.1" }
|
||||
globset = { version = "0.4.10" }
|
||||
ignore = { version = "0.4.20" }
|
||||
insta = { version = "1.30.0" }
|
||||
insta = { version = "1.28.0" }
|
||||
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" }
|
||||
@@ -35,6 +36,11 @@ proc-macro2 = { version = "1.0.51" }
|
||||
quote = { version = "1.0.23" }
|
||||
regex = { version = "1.7.1" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "8d74eee75031b68d2204219963fae54a3f31a394" }
|
||||
rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "8d74eee75031b68d2204219963fae54a3f31a394" , default-features = false, features = ["all-nodes-with-ranges", "num-bigint"]}
|
||||
rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "8d74eee75031b68d2204219963fae54a3f31a394", default-features = false, features = ["num-bigint"] }
|
||||
rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "8d74eee75031b68d2204219963fae54a3f31a394" }
|
||||
rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "8d74eee75031b68d2204219963fae54a3f31a394" , default-features = false, features = ["full-lexer", "all-nodes-with-ranges", "num-bigint"] }
|
||||
schemars = { version = "0.8.12" }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = { version = "1.0.93" }
|
||||
@@ -45,25 +51,10 @@ 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" }
|
||||
thiserror = { version = "1.0.43" }
|
||||
toml = { version = "0.7.2" }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
# v1.0.1
|
||||
libcst = { git = "https://github.com/Instagram/LibCST.git", rev = "3cacca1a1029f05707e50703b49fe3dd860aa839", default-features = false }
|
||||
|
||||
# Please tag the RustPython version every time you update its revision here and in fuzz/Cargo.toml
|
||||
# Tagging the version ensures that older ruff versions continue to build from source even when we rebase our RustPython fork.
|
||||
# Note: As of tag v0.0.8 we are cherry-picking commits instead of rebasing so the tag is not necessary
|
||||
ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "126652b684910c29a7bcc32293d4ca0f81454e34" }
|
||||
rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "126652b684910c29a7bcc32293d4ca0f81454e34" , default-features = false, features = ["num-bigint"]}
|
||||
rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "126652b684910c29a7bcc32293d4ca0f81454e34", default-features = false, features = ["num-bigint"] }
|
||||
rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "126652b684910c29a7bcc32293d4ca0f81454e34", default-features = false }
|
||||
rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "126652b684910c29a7bcc32293d4ca0f81454e34" , default-features = false, features = ["full-lexer", "num-bigint"] }
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
|
||||
[profile.dev.package.insta]
|
||||
opt-level = 3
|
||||
|
||||
26
LICENSE
26
LICENSE
@@ -1224,32 +1224,6 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- Pyright, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Pyright - A static type checker for the Python language
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
- rust-analyzer/text-size, licensed under the MIT license:
|
||||
"""
|
||||
Permission is hereby granted, free of charge, to any
|
||||
|
||||
35
README.md
35
README.md
@@ -14,9 +14,9 @@ An extremely fast Python linter, written in Rust.
|
||||
|
||||
<p align="center">
|
||||
<picture align="center">
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/1309177/232603514-c95e9b0f-6b31-43de-9a80-9e844173fd6a.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/1309177/232603516-4fb4892d-585c-4b20-b810-3db9161831e4.svg">
|
||||
<img alt="Shows a bar chart with benchmark results." src="https://user-images.githubusercontent.com/1309177/232603516-4fb4892d-585c-4b20-b810-3db9161831e4.svg">
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/1309177/212613422-7faaf278-706b-4294-ad92-236ffcab3430.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/1309177/212613257-5f4bca12-6d6b-4c79-9bac-51a4c6d08928.svg">
|
||||
<img alt="Shows a bar chart with benchmark results." src="https://user-images.githubusercontent.com/1309177/212613257-5f4bca12-6d6b-4c79-9bac-51a4c6d08928.svg">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
@@ -32,10 +32,9 @@ An extremely fast Python linter, written in Rust.
|
||||
- 🔧 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
|
||||
built-in Flake8 rule set
|
||||
- 🔌 Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
|
||||
- ⌨️ First-party [editor integrations](https://beta.ruff.rs/docs/editor-integrations/) for
|
||||
[VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://github.com/astral-sh/ruff-lsp)
|
||||
- ⌨️ 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
|
||||
@@ -140,7 +139,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.0.278
|
||||
rev: v0.0.272
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -331,11 +330,9 @@ We're grateful to the maintainers of these tools for their work, and for all
|
||||
the value they've provided to the Python community.
|
||||
|
||||
Ruff's autoformatter is built on a fork of Rome's [`rome_formatter`](https://github.com/rome/tools/tree/main/crates/rome_formatter),
|
||||
and again draws on both API and implementation details from [Rome](https://github.com/rome/tools),
|
||||
and again draws on both the APIs and implementation details of [Rome](https://github.com/rome/tools),
|
||||
[Prettier](https://github.com/prettier/prettier), and [Black](https://github.com/psf/black).
|
||||
|
||||
Ruff's import resolver is based on the import resolution algorithm from [Pyright](https://github.com/microsoft/pyright).
|
||||
|
||||
Ruff is also influenced by a number of tools outside the Python ecosystem, like
|
||||
[Clippy](https://github.com/rust-lang/rust-clippy) and [ESLint](https://github.com/eslint/eslint).
|
||||
|
||||
@@ -348,7 +345,6 @@ Ruff is released under the MIT license.
|
||||
Ruff is used by a number of major open-source projects and companies, including:
|
||||
|
||||
- Amazon ([AWS SAM](https://github.com/aws/serverless-application-model))
|
||||
- Anthropic ([Python SDK](https://github.com/anthropics/anthropic-sdk-python))
|
||||
- [Apache Airflow](https://github.com/apache/airflow)
|
||||
- AstraZeneca ([Magnus](https://github.com/AstraZeneca/magnus-core))
|
||||
- Benchling ([Refac](https://github.com/benchling/refac))
|
||||
@@ -358,30 +354,26 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [DVC](https://github.com/iterative/dvc)
|
||||
- [Dagger](https://github.com/dagger/dagger)
|
||||
- [Dagster](https://github.com/dagster-io/dagster)
|
||||
- Databricks ([MLflow](https://github.com/mlflow/mlflow))
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Gradio](https://github.com/gradio-app/gradio)
|
||||
- [Great Expectations](https://github.com/great-expectations/great_expectations)
|
||||
- [HTTPX](https://github.com/encode/httpx)
|
||||
- Hugging Face ([Transformers](https://github.com/huggingface/transformers),
|
||||
[Datasets](https://github.com/huggingface/datasets),
|
||||
[Diffusers](https://github.com/huggingface/diffusers))
|
||||
[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)
|
||||
- ING Bank ([popmon](https://github.com/ing-bank/popmon), [probatus](https://github.com/ing-bank/probatus))
|
||||
- [Ibis](https://github.com/ibis-project/ibis)
|
||||
- [Jupyter](https://github.com/jupyter-server/jupyter_server)
|
||||
- [LangChain](https://github.com/hwchase17/langchain)
|
||||
- [LlamaIndex](https://github.com/jerryjliu/llama_index)
|
||||
- Matrix ([Synapse](https://github.com/matrix-org/synapse))
|
||||
- [MegaLinter](https://github.com/oxsecurity/megalinter)
|
||||
- Meltano ([Meltano CLI](https://github.com/meltano/meltano), [Singer SDK](https://github.com/meltano/sdk))
|
||||
- Microsoft ([Semantic Kernel](https://github.com/microsoft/semantic-kernel),
|
||||
[ONNX Runtime](https://github.com/microsoft/onnxruntime),
|
||||
[LightGBM](https://github.com/microsoft/LightGBM))
|
||||
- Modern Treasury ([Python SDK](https://github.com/Modern-Treasury/modern-treasury-python-sdk))
|
||||
- Mozilla ([Firefox](https://github.com/mozilla/gecko-dev))
|
||||
- [Mypy](https://github.com/python/mypy)
|
||||
- [MegaLinter](https://github.com/oxsecurity/megalinter)
|
||||
- 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)
|
||||
@@ -417,7 +409,6 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [featuretools](https://github.com/alteryx/featuretools)
|
||||
- [meson-python](https://github.com/mesonbuild/meson-python)
|
||||
- [nox](https://github.com/wntrblm/nox)
|
||||
- [pip](https://github.com/pypa/pip)
|
||||
|
||||
### Show Your Support
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
extend-exclude = ["resources", "snapshots"]
|
||||
|
||||
[default.extend-words]
|
||||
trivias = "trivias"
|
||||
hel = "hel"
|
||||
whos = "whos"
|
||||
spawnve = "spawnve"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.278"
|
||||
version = "0.0.272"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
|
||||
@@ -82,12 +82,12 @@ flake8-to-ruff path/to/.flake8 --plugin flake8-builtins --plugin flake8-quotes
|
||||
## Limitations
|
||||
|
||||
1. Ruff only supports a subset of the Flake configuration options. `flake8-to-ruff` will warn on and
|
||||
ignore unsupported options in the `.flake8` file (or equivalent). (Similarly, Ruff has a few
|
||||
configuration options that don't exist in Flake8.)
|
||||
ignore unsupported options in the `.flake8` file (or equivalent). (Similarly, Ruff has a few
|
||||
configuration options that don't exist in Flake8.)
|
||||
1. Ruff will omit any rule codes that are unimplemented or unsupported by Ruff, including rule
|
||||
codes from unsupported plugins. (See the
|
||||
[documentation](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) for the complete
|
||||
list of supported plugins.)
|
||||
codes from unsupported plugins. (See the
|
||||
[documentation](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) for the complete
|
||||
list of supported plugins.)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.278"
|
||||
version = "0.0.272"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -17,7 +17,6 @@ name = "ruff"
|
||||
[dependencies]
|
||||
ruff_cache = { path = "../ruff_cache" }
|
||||
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
|
||||
ruff_index = { path = "../ruff_index" }
|
||||
ruff_macros = { path = "../ruff_macros" }
|
||||
ruff_python_whitespace = { path = "../ruff_python_whitespace" }
|
||||
ruff_python_ast = { path = "../ruff_python_ast", features = ["serde"] }
|
||||
@@ -43,7 +42,6 @@ is-macro = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
libcst = { workspace = true }
|
||||
log = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
natord = { version = "1.0.9" }
|
||||
nohash-hasher = { workspace = true }
|
||||
num-bigint = { workspace = true }
|
||||
@@ -73,12 +71,11 @@ shellexpand = { workspace = true }
|
||||
smallvec = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
thiserror = { version = "1.0.43" }
|
||||
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" }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { workspace = true }
|
||||
@@ -90,5 +87,4 @@ colored = { workspace = true, features = ["no-color"] }
|
||||
[features]
|
||||
default = []
|
||||
schemars = ["dep:schemars"]
|
||||
# Enables the UnreachableCode rule
|
||||
unreachable-code = []
|
||||
jupyter_notebook = []
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
def func():
|
||||
assert True
|
||||
|
||||
def func():
|
||||
assert False
|
||||
|
||||
def func():
|
||||
assert True, "oops"
|
||||
|
||||
def func():
|
||||
assert False, "oops"
|
||||
@@ -1,41 +0,0 @@
|
||||
def func():
|
||||
async for i in range(5):
|
||||
print(i)
|
||||
|
||||
def func():
|
||||
async for i in range(20):
|
||||
print(i)
|
||||
else:
|
||||
return 0
|
||||
|
||||
def func():
|
||||
async for i in range(10):
|
||||
if i == 5:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def func():
|
||||
async for i in range(111):
|
||||
if i == 5:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
return 2
|
||||
|
||||
def func():
|
||||
async for i in range(12):
|
||||
continue
|
||||
|
||||
def func():
|
||||
async for i in range(1110):
|
||||
if True:
|
||||
continue
|
||||
|
||||
def func():
|
||||
async for i in range(13):
|
||||
break
|
||||
|
||||
def func():
|
||||
async for i in range(1110):
|
||||
if True:
|
||||
break
|
||||
@@ -1,41 +0,0 @@
|
||||
def func():
|
||||
for i in range(5):
|
||||
print(i)
|
||||
|
||||
def func():
|
||||
for i in range(20):
|
||||
print(i)
|
||||
else:
|
||||
return 0
|
||||
|
||||
def func():
|
||||
for i in range(10):
|
||||
if i == 5:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def func():
|
||||
for i in range(111):
|
||||
if i == 5:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
return 2
|
||||
|
||||
def func():
|
||||
for i in range(12):
|
||||
continue
|
||||
|
||||
def func():
|
||||
for i in range(1110):
|
||||
if True:
|
||||
continue
|
||||
|
||||
def func():
|
||||
for i in range(13):
|
||||
break
|
||||
|
||||
def func():
|
||||
for i in range(1110):
|
||||
if True:
|
||||
break
|
||||
@@ -1,108 +0,0 @@
|
||||
def func():
|
||||
if False:
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def func():
|
||||
if True:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def func():
|
||||
if False:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
def func():
|
||||
if True:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def func():
|
||||
if False:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
return "unreachable"
|
||||
|
||||
def func():
|
||||
if True:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
return "unreachable"
|
||||
|
||||
def func():
|
||||
if True:
|
||||
if True:
|
||||
return 1
|
||||
return 2
|
||||
else:
|
||||
return 3
|
||||
return "unreachable2"
|
||||
|
||||
def func():
|
||||
if False:
|
||||
return 0
|
||||
|
||||
def func():
|
||||
if True:
|
||||
return 1
|
||||
|
||||
def func():
|
||||
if True:
|
||||
return 1
|
||||
elif False:
|
||||
return 2
|
||||
else:
|
||||
return 0
|
||||
|
||||
def func():
|
||||
if False:
|
||||
return 1
|
||||
elif True:
|
||||
return 2
|
||||
else:
|
||||
return 0
|
||||
|
||||
def func():
|
||||
if True:
|
||||
if False:
|
||||
return 0
|
||||
elif True:
|
||||
return 1
|
||||
else:
|
||||
return 2
|
||||
return 3
|
||||
elif True:
|
||||
return 4
|
||||
else:
|
||||
return 5
|
||||
return 6
|
||||
|
||||
def func():
|
||||
if False:
|
||||
return "unreached"
|
||||
elif False:
|
||||
return "also unreached"
|
||||
return "reached"
|
||||
|
||||
# Test case found in the Bokeh repository that trigger a false positive.
|
||||
def func(self, obj: BytesRep) -> bytes:
|
||||
data = obj["data"]
|
||||
|
||||
if isinstance(data, str):
|
||||
return base64.b64decode(data)
|
||||
elif isinstance(data, Buffer):
|
||||
buffer = data
|
||||
else:
|
||||
id = data["id"]
|
||||
|
||||
if id in self._buffers:
|
||||
buffer = self._buffers[id]
|
||||
else:
|
||||
self.error(f"can't resolve buffer '{id}'")
|
||||
|
||||
return buffer.data
|
||||
@@ -1,131 +0,0 @@
|
||||
def func(status):
|
||||
match status:
|
||||
case _:
|
||||
return 0
|
||||
return "unreachable"
|
||||
|
||||
def func(status):
|
||||
match status:
|
||||
case 1:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def func(status):
|
||||
match status:
|
||||
case 1:
|
||||
return 1
|
||||
case _:
|
||||
return 0
|
||||
|
||||
def func(status):
|
||||
match status:
|
||||
case 1 | 2 | 3:
|
||||
return 5
|
||||
return 6
|
||||
|
||||
def func(status):
|
||||
match status:
|
||||
case 1 | 2 | 3:
|
||||
return 5
|
||||
case _:
|
||||
return 10
|
||||
return 0
|
||||
|
||||
def func(status):
|
||||
match status:
|
||||
case 0:
|
||||
return 0
|
||||
case 1:
|
||||
return 1
|
||||
case 1:
|
||||
return "1 again"
|
||||
case _:
|
||||
return 3
|
||||
|
||||
def func(status):
|
||||
i = 0
|
||||
match status, i:
|
||||
case _, _:
|
||||
return 0
|
||||
|
||||
def func(status):
|
||||
i = 0
|
||||
match status, i:
|
||||
case _, 0:
|
||||
return 0
|
||||
case _, 2:
|
||||
return 0
|
||||
|
||||
def func(point):
|
||||
match point:
|
||||
case (0, 0):
|
||||
print("Origin")
|
||||
case _:
|
||||
raise ValueError("oops")
|
||||
|
||||
def func(point):
|
||||
match point:
|
||||
case (0, 0):
|
||||
print("Origin")
|
||||
case (0, y):
|
||||
print(f"Y={y}")
|
||||
case (x, 0):
|
||||
print(f"X={x}")
|
||||
case (x, y):
|
||||
print(f"X={x}, Y={y}")
|
||||
case _:
|
||||
raise ValueError("Not a point")
|
||||
|
||||
def where_is(point):
|
||||
class Point:
|
||||
x: int
|
||||
y: int
|
||||
|
||||
match point:
|
||||
case Point(x=0, y=0):
|
||||
print("Origin")
|
||||
case Point(x=0, y=y):
|
||||
print(f"Y={y}")
|
||||
case Point(x=x, y=0):
|
||||
print(f"X={x}")
|
||||
case Point():
|
||||
print("Somewhere else")
|
||||
case _:
|
||||
print("Not a point")
|
||||
|
||||
def func(points):
|
||||
match points:
|
||||
case []:
|
||||
print("No points")
|
||||
case [Point(0, 0)]:
|
||||
print("The origin")
|
||||
case [Point(x, y)]:
|
||||
print(f"Single point {x}, {y}")
|
||||
case [Point(0, y1), Point(0, y2)]:
|
||||
print(f"Two on the Y axis at {y1}, {y2}")
|
||||
case _:
|
||||
print("Something else")
|
||||
|
||||
def func(point):
|
||||
match point:
|
||||
case Point(x, y) if x == y:
|
||||
print(f"Y=X at {x}")
|
||||
case Point(x, y):
|
||||
print(f"Not on the diagonal")
|
||||
|
||||
def func():
|
||||
from enum import Enum
|
||||
class Color(Enum):
|
||||
RED = 'red'
|
||||
GREEN = 'green'
|
||||
BLUE = 'blue'
|
||||
|
||||
color = Color(input("Enter your choice of 'red', 'blue' or 'green': "))
|
||||
|
||||
match color:
|
||||
case Color.RED:
|
||||
print("I see red!")
|
||||
case Color.GREEN:
|
||||
print("Grass is green")
|
||||
case Color.BLUE:
|
||||
print("I'm feeling the blues :(")
|
||||
@@ -1,5 +0,0 @@
|
||||
def func():
|
||||
raise Exception
|
||||
|
||||
def func():
|
||||
raise "a glass!"
|
||||
@@ -1,23 +0,0 @@
|
||||
def func():
|
||||
pass
|
||||
|
||||
def func():
|
||||
pass
|
||||
|
||||
def func():
|
||||
return
|
||||
|
||||
def func():
|
||||
return 1
|
||||
|
||||
def func():
|
||||
return 1
|
||||
return "unreachable"
|
||||
|
||||
def func():
|
||||
i = 0
|
||||
|
||||
def func():
|
||||
i = 0
|
||||
i += 2
|
||||
return i
|
||||
@@ -1,41 +0,0 @@
|
||||
def func():
|
||||
try:
|
||||
...
|
||||
except Exception:
|
||||
...
|
||||
except OtherException as e:
|
||||
...
|
||||
else:
|
||||
...
|
||||
finally:
|
||||
...
|
||||
|
||||
def func():
|
||||
try:
|
||||
...
|
||||
except Exception:
|
||||
...
|
||||
|
||||
def func():
|
||||
try:
|
||||
...
|
||||
except Exception:
|
||||
...
|
||||
except OtherException as e:
|
||||
...
|
||||
|
||||
def func():
|
||||
try:
|
||||
...
|
||||
except Exception:
|
||||
...
|
||||
except OtherException as e:
|
||||
...
|
||||
else:
|
||||
...
|
||||
|
||||
def func():
|
||||
try:
|
||||
...
|
||||
finally:
|
||||
...
|
||||
@@ -1,121 +0,0 @@
|
||||
def func():
|
||||
while False:
|
||||
return "unreachable"
|
||||
return 1
|
||||
|
||||
def func():
|
||||
while False:
|
||||
return "unreachable"
|
||||
else:
|
||||
return 1
|
||||
|
||||
def func():
|
||||
while False:
|
||||
return "unreachable"
|
||||
else:
|
||||
return 1
|
||||
return "also unreachable"
|
||||
|
||||
def func():
|
||||
while True:
|
||||
return 1
|
||||
return "unreachable"
|
||||
|
||||
def func():
|
||||
while True:
|
||||
return 1
|
||||
else:
|
||||
return "unreachable"
|
||||
|
||||
def func():
|
||||
while True:
|
||||
return 1
|
||||
else:
|
||||
return "unreachable"
|
||||
return "also unreachable"
|
||||
|
||||
def func():
|
||||
i = 0
|
||||
while False:
|
||||
i += 1
|
||||
return i
|
||||
|
||||
def func():
|
||||
i = 0
|
||||
while True:
|
||||
i += 1
|
||||
return i
|
||||
|
||||
def func():
|
||||
while True:
|
||||
pass
|
||||
return 1
|
||||
|
||||
def func():
|
||||
i = 0
|
||||
while True:
|
||||
if True:
|
||||
print("ok")
|
||||
i += 1
|
||||
return i
|
||||
|
||||
def func():
|
||||
i = 0
|
||||
while True:
|
||||
if False:
|
||||
print("ok")
|
||||
i += 1
|
||||
return i
|
||||
|
||||
def func():
|
||||
while True:
|
||||
if True:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def func():
|
||||
while True:
|
||||
continue
|
||||
|
||||
def func():
|
||||
while False:
|
||||
continue
|
||||
|
||||
def func():
|
||||
while True:
|
||||
break
|
||||
|
||||
def func():
|
||||
while False:
|
||||
break
|
||||
|
||||
def func():
|
||||
while True:
|
||||
if True:
|
||||
continue
|
||||
|
||||
def func():
|
||||
while True:
|
||||
if True:
|
||||
break
|
||||
|
||||
'''
|
||||
TODO: because `try` statements aren't handled this triggers a false positive as
|
||||
the last statement is reached, but the rules thinks it isn't (it doesn't
|
||||
see/process the break statement).
|
||||
|
||||
# Test case found in the Bokeh repository that trigger a false positive.
|
||||
def bokeh2(self, host: str = DEFAULT_HOST, port: int = DEFAULT_PORT) -> None:
|
||||
self.stop_serving = False
|
||||
while True:
|
||||
try:
|
||||
self.server = HTTPServer((host, port), HtmlOnlyHandler)
|
||||
self.host = host
|
||||
self.port = port
|
||||
break
|
||||
except OSError:
|
||||
log.debug(f"port {port} is in use, trying to next one")
|
||||
port += 1
|
||||
|
||||
self.thread = threading.Thread(target=self._run_web_server)
|
||||
'''
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Annotated, Any, Optional, Type, Union
|
||||
from typing import Any, Type
|
||||
from typing_extensions import override
|
||||
|
||||
# Error
|
||||
@@ -95,27 +95,27 @@ class Foo:
|
||||
def foo(self: "Foo", a: int, *params: str, **options: Any) -> int:
|
||||
pass
|
||||
|
||||
# OK
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: Any, *params: str, **options: str) -> int:
|
||||
pass
|
||||
|
||||
# OK
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: int, *params: str, **options: str) -> Any:
|
||||
pass
|
||||
|
||||
# OK
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: int, *params: Any, **options: Any) -> int:
|
||||
pass
|
||||
|
||||
# OK
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: int, *params: Any, **options: str) -> int:
|
||||
pass
|
||||
|
||||
# OK
|
||||
# ANN401
|
||||
@override
|
||||
def foo(self: "Foo", a: int, *params: str, **options: Any) -> int:
|
||||
pass
|
||||
@@ -137,18 +137,3 @@ class Foo:
|
||||
|
||||
# OK
|
||||
def f(*args: *tuple[int]) -> None: ...
|
||||
def f(a: object) -> None: ...
|
||||
def f(a: str | bytes) -> None: ...
|
||||
def f(a: Union[str, bytes]) -> None: ...
|
||||
def f(a: Optional[str]) -> None: ...
|
||||
def f(a: Annotated[str, ...]) -> None: ...
|
||||
def f(a: "Union[str, bytes]") -> None: ...
|
||||
def f(a: int + int) -> None: ...
|
||||
|
||||
# ANN401
|
||||
def f(a: Any | int) -> None: ...
|
||||
def f(a: int | Any) -> None: ...
|
||||
def f(a: Union[str, bytes, Any]) -> None: ...
|
||||
def f(a: Optional[Any]) -> None: ...
|
||||
def f(a: Annotated[Any, ...]) -> None: ...
|
||||
def f(a: "Union[str, bytes, Any]") -> None: ...
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import os
|
||||
|
||||
print(eval("1+1")) # S307
|
||||
print(eval("os.getcwd()")) # S307
|
||||
|
||||
|
||||
class Class(object):
|
||||
def eval(self):
|
||||
print("hi")
|
||||
|
||||
def foo(self):
|
||||
self.eval() # OK
|
||||
@@ -177,9 +177,6 @@ def str_okay(value=str("foo")):
|
||||
def bool_okay(value=bool("bar")):
|
||||
pass
|
||||
|
||||
# Allow immutable bytes() value
|
||||
def bytes_okay(value=bytes(1)):
|
||||
pass
|
||||
|
||||
# Allow immutable int() value
|
||||
def int_okay(value=int("12")):
|
||||
|
||||
@@ -23,10 +23,6 @@ class Foobar(unittest.TestCase):
|
||||
with self.assertRaises(Exception):
|
||||
raise Exception("Evil I say!")
|
||||
|
||||
def also_evil_raises(self) -> None:
|
||||
with self.assertRaises(BaseException):
|
||||
raise Exception("Evil I say!")
|
||||
|
||||
def context_manager_raises(self) -> None:
|
||||
with self.assertRaises(Exception) as ex:
|
||||
raise Exception("Context manager is good")
|
||||
@@ -45,9 +41,6 @@ def test_pytest_raises():
|
||||
with pytest.raises(Exception):
|
||||
raise ValueError("Hello")
|
||||
|
||||
with pytest.raises(Exception), pytest.raises(ValueError):
|
||||
raise ValueError("Hello")
|
||||
|
||||
with pytest.raises(Exception, "hello"):
|
||||
raise ValueError("This is fine")
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import re
|
||||
from re import sub
|
||||
|
||||
# B034
|
||||
re.sub("a", "b", "aaa", re.IGNORECASE)
|
||||
re.sub("a", "b", "aaa", 5)
|
||||
re.sub("a", "b", "aaa", 5, re.IGNORECASE)
|
||||
re.subn("a", "b", "aaa", re.IGNORECASE)
|
||||
re.subn("a", "b", "aaa", 5)
|
||||
re.subn("a", "b", "aaa", 5, re.IGNORECASE)
|
||||
re.split(" ", "a a a a", re.I)
|
||||
re.split(" ", "a a a a", 2)
|
||||
re.split(" ", "a a a a", 2, re.I)
|
||||
sub("a", "b", "aaa", re.IGNORECASE)
|
||||
|
||||
# OK
|
||||
re.sub("a", "b", "aaa")
|
||||
re.sub("a", "b", "aaa", flags=re.IGNORECASE)
|
||||
re.sub("a", "b", "aaa", count=5)
|
||||
re.sub("a", "b", "aaa", count=5, flags=re.IGNORECASE)
|
||||
re.subn("a", "b", "aaa")
|
||||
re.subn("a", "b", "aaa", flags=re.IGNORECASE)
|
||||
re.subn("a", "b", "aaa", count=5)
|
||||
re.subn("a", "b", "aaa", count=5, flags=re.IGNORECASE)
|
||||
re.split(" ", "a a a a", flags=re.I)
|
||||
re.split(" ", "a a a a", maxsplit=2)
|
||||
re.split(" ", "a a a a", maxsplit=2, flags=re.I)
|
||||
@@ -14,10 +14,9 @@ except AssertionError:
|
||||
except Exception as err:
|
||||
assert err
|
||||
raise Exception("No cause here...")
|
||||
except BaseException as err:
|
||||
raise err
|
||||
except BaseException as err:
|
||||
raise some_other_err
|
||||
except BaseException as base_err:
|
||||
# Might use this instead of bare raise with the `.with_traceback()` method
|
||||
raise base_err
|
||||
finally:
|
||||
raise Exception("Nothing to chain from, so no warning here")
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@ set(reversed(x))
|
||||
sorted(list(x))
|
||||
sorted(tuple(x))
|
||||
sorted(sorted(x))
|
||||
sorted(sorted(x, key=foo, reverse=False), reverse=False, key=foo)
|
||||
sorted(sorted(x, reverse=True), reverse=True)
|
||||
sorted(sorted(x, key=lambda y: y))
|
||||
sorted(reversed(x))
|
||||
sorted(list(x), key=lambda y: y)
|
||||
tuple(
|
||||
@@ -22,9 +21,3 @@ tuple(
|
||||
"o"]
|
||||
)
|
||||
)
|
||||
|
||||
# Nested sorts with differing keyword arguments. Not flagged.
|
||||
sorted(sorted(x, key=lambda y: y))
|
||||
sorted(sorted(x, key=lambda y: y), key=lambda x: x)
|
||||
sorted(sorted(x), reverse=True)
|
||||
sorted(sorted(x, reverse=False), reverse=True)
|
||||
|
||||
@@ -25,15 +25,10 @@ map(lambda x=2, y=1: x + y, nums, nums)
|
||||
set(map(lambda x, y: x, nums, nums))
|
||||
|
||||
|
||||
def func(arg1: int, arg2: int = 4):
|
||||
def myfunc(arg1: int, arg2: int = 4):
|
||||
return 2 * arg1 + arg2
|
||||
|
||||
|
||||
# Non-error: `func` is not a lambda.
|
||||
list(map(func, nums))
|
||||
list(map(myfunc, nums))
|
||||
|
||||
# False positive: need to preserve the late-binding of `x` in the inner lambda.
|
||||
map(lambda x: lambda: x, range(4))
|
||||
|
||||
# Error: the `x` is overridden by the inner lambda.
|
||||
map(lambda x: lambda x: x, range(4))
|
||||
[x for x in nums]
|
||||
|
||||
@@ -19,6 +19,3 @@ from datetime import datetime
|
||||
|
||||
# no args unqualified
|
||||
datetime(2000, 1, 1, 0, 0, 0)
|
||||
|
||||
# uses `astimezone` method
|
||||
datetime(2000, 1, 1, 0, 0, 0).astimezone()
|
||||
|
||||
@@ -7,6 +7,3 @@ from datetime import datetime
|
||||
|
||||
# unqualified
|
||||
datetime.today()
|
||||
|
||||
# uses `astimezone` method
|
||||
datetime.today().astimezone()
|
||||
|
||||
@@ -7,6 +7,3 @@ from datetime import datetime
|
||||
|
||||
# unqualified
|
||||
datetime.utcnow()
|
||||
|
||||
# uses `astimezone` method
|
||||
datetime.utcnow().astimezone()
|
||||
|
||||
@@ -7,6 +7,3 @@ from datetime import datetime
|
||||
|
||||
# unqualified
|
||||
datetime.utcfromtimestamp(1234)
|
||||
|
||||
# uses `astimezone` method
|
||||
datetime.utcfromtimestamp(1234).astimezone()
|
||||
|
||||
@@ -16,6 +16,3 @@ from datetime import datetime
|
||||
|
||||
# no args unqualified
|
||||
datetime.now()
|
||||
|
||||
# uses `astimezone` method
|
||||
datetime.now().astimezone()
|
||||
|
||||
@@ -16,6 +16,3 @@ from datetime import datetime
|
||||
|
||||
# no args unqualified
|
||||
datetime.fromtimestamp(1234)
|
||||
|
||||
# uses `astimezone` method
|
||||
datetime.fromtimestamp(1234).astimezone()
|
||||
|
||||
@@ -111,19 +111,3 @@ class PerfectlyFine(models.Model):
|
||||
@property
|
||||
def random_property(self):
|
||||
return "%s" % self
|
||||
|
||||
|
||||
class MultipleConsecutiveFields(models.Model):
|
||||
"""Model that contains multiple out-of-order field definitions in a row."""
|
||||
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
|
||||
first_name = models.CharField(max_length=32)
|
||||
last_name = models.CharField(max_length=32)
|
||||
|
||||
def get_absolute_url(self):
|
||||
pass
|
||||
|
||||
middle_name = models.CharField(max_length=32)
|
||||
|
||||
@@ -5,18 +5,15 @@ import matplotlib.pyplot # unconventional
|
||||
import numpy # unconventional
|
||||
import pandas # unconventional
|
||||
import seaborn # unconventional
|
||||
import tkinter # unconventional
|
||||
|
||||
import altair as altr # unconventional
|
||||
import matplotlib.pyplot as plot # unconventional
|
||||
import numpy as nmp # unconventional
|
||||
import pandas as pdas # unconventional
|
||||
import seaborn as sbrn # unconventional
|
||||
import tkinter as tkr # unconventional
|
||||
|
||||
import altair as alt # conventional
|
||||
import matplotlib.pyplot as plt # conventional
|
||||
import numpy as np # conventional
|
||||
import pandas as pd # conventional
|
||||
import seaborn as sns # conventional
|
||||
import tkinter as tk # conventional
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import sys
|
||||
|
||||
if sys.version == 'Python 2.7.10': ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
if 'linux' == sys.platform: ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
if hasattr(sys, 'maxint'): ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
if sys.maxsize == 42: ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
@@ -1,6 +0,0 @@
|
||||
import sys
|
||||
|
||||
if sys.version == 'Python 2.7.10': ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
if 'linux' == sys.platform: ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
if hasattr(sys, 'maxint'): ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
if sys.maxsize == 42: ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
@@ -1,31 +0,0 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] == 2: ...
|
||||
if sys.version_info[0] == True: ... # Y003 Unrecognized sys.version_info check # E712 comparison to True should be 'if cond is True:' or 'if cond:'
|
||||
if sys.version_info[0.0] == 2: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[False] == 2: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[0j] == 2: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[0] == (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[0] == '2': ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[1:] >= (7, 11): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[::-1] < (11, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:3] >= (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:True] >= (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:1] == (2,): ...
|
||||
if sys.version_info[:1] == (True,): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:1] == (2, 7): ... # Y005 Version comparison must be against a length-1 tuple
|
||||
if sys.version_info[:2] == (2, 7): ...
|
||||
if sys.version_info[:2] == (2,): ... # Y005 Version comparison must be against a length-2 tuple
|
||||
if sys.version_info[:2] == "lol": ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:2.0] >= (3, 9): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:2j] >= (3, 9): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:, :] >= (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info < [3, 0]: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info < ('3', '0'): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info >= (3, 4, 3): ... # Y004 Version comparison must use only major and minor version
|
||||
if sys.version_info == (3, 4): ... # Y006 Use only < and >= for version comparisons
|
||||
if sys.version_info > (3, 0): ... # Y006 Use only < and >= for version comparisons
|
||||
if sys.version_info <= (3, 0): ... # Y006 Use only < and >= for version comparisons
|
||||
if sys.version_info < (3, 5): ...
|
||||
if sys.version_info >= (3, 5): ...
|
||||
if (2, 7) <= sys.version_info < (3, 5): ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
@@ -1,31 +0,0 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] == 2: ...
|
||||
if sys.version_info[0] == True: ... # Y003 Unrecognized sys.version_info check # E712 comparison to True should be 'if cond is True:' or 'if cond:'
|
||||
if sys.version_info[0.0] == 2: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[False] == 2: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[0j] == 2: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[0] == (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[0] == '2': ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[1:] >= (7, 11): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[::-1] < (11, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:3] >= (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:True] >= (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:1] == (2,): ...
|
||||
if sys.version_info[:1] == (True,): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:1] == (2, 7): ... # Y005 Version comparison must be against a length-1 tuple
|
||||
if sys.version_info[:2] == (2, 7): ...
|
||||
if sys.version_info[:2] == (2,): ... # Y005 Version comparison must be against a length-2 tuple
|
||||
if sys.version_info[:2] == "lol": ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:2.0] >= (3, 9): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:2j] >= (3, 9): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info[:, :] >= (2, 7): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info < [3, 0]: ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info < ('3', '0'): ... # Y003 Unrecognized sys.version_info check
|
||||
if sys.version_info >= (3, 4, 3): ... # Y004 Version comparison must use only major and minor version
|
||||
if sys.version_info == (3, 4): ... # Y006 Use only < and >= for version comparisons
|
||||
if sys.version_info > (3, 0): ... # Y006 Use only < and >= for version comparisons
|
||||
if sys.version_info <= (3, 0): ... # Y006 Use only < and >= for version comparisons
|
||||
if sys.version_info < (3, 5): ...
|
||||
if sys.version_info >= (3, 5): ...
|
||||
if (2, 7) <= sys.version_info < (3, 5): ... # Y002 If test must be a simple comparison against sys.platform or sys.version_info
|
||||
@@ -1,15 +0,0 @@
|
||||
import sys
|
||||
from sys import version_info
|
||||
|
||||
if sys.version_info >= (3, 4, 3): ... # PYI004
|
||||
if sys.version_info < (3, 4, 3): ... # PYI004
|
||||
if sys.version_info == (3, 4, 3): ... # PYI004
|
||||
if sys.version_info != (3, 4, 3): ... # PYI004
|
||||
|
||||
if sys.version_info[0] == 2: ...
|
||||
if version_info[0] == 2: ...
|
||||
if sys.version_info < (3, 5): ...
|
||||
if version_info >= (3, 5): ...
|
||||
if sys.version_info[:2] == (2, 7): ...
|
||||
if sys.version_info[:1] == (2,): ...
|
||||
if sys.platform == 'linux': ...
|
||||
@@ -1,15 +0,0 @@
|
||||
import sys
|
||||
from sys import version_info
|
||||
|
||||
if sys.version_info >= (3, 4, 3): ... # PYI004
|
||||
if sys.version_info < (3, 4, 3): ... # PYI004
|
||||
if sys.version_info == (3, 4, 3): ... # PYI004
|
||||
if sys.version_info != (3, 4, 3): ... # PYI004
|
||||
|
||||
if sys.version_info[0] == 2: ...
|
||||
if version_info[0] == 2: ...
|
||||
if sys.version_info < (3, 5): ...
|
||||
if version_info >= (3, 5): ...
|
||||
if sys.version_info[:2] == (2, 7): ...
|
||||
if sys.version_info[:1] == (2,): ...
|
||||
if sys.platform == 'linux': ...
|
||||
@@ -1,14 +0,0 @@
|
||||
import sys
|
||||
from sys import platform, version_info
|
||||
|
||||
if sys.version_info[:1] == (2, 7): ... # Y005
|
||||
if sys.version_info[:2] == (2,): ... # Y005
|
||||
|
||||
|
||||
if sys.version_info[0] == 2: ...
|
||||
if version_info[0] == 2: ...
|
||||
if sys.version_info < (3, 5): ...
|
||||
if version_info >= (3, 5): ...
|
||||
if sys.version_info[:2] == (2, 7): ...
|
||||
if sys.version_info[:1] == (2,): ...
|
||||
if platform == 'linux': ...
|
||||
@@ -1,14 +0,0 @@
|
||||
import sys
|
||||
from sys import platform, version_info
|
||||
|
||||
if sys.version_info[:1] == (2, 7): ... # Y005
|
||||
if sys.version_info[:2] == (2,): ... # Y005
|
||||
|
||||
|
||||
if sys.version_info[0] == 2: ...
|
||||
if version_info[0] == 2: ...
|
||||
if sys.version_info < (3, 5): ...
|
||||
if version_info >= (3, 5): ...
|
||||
if sys.version_info[:2] == (2, 7): ...
|
||||
if sys.version_info[:1] == (2,): ...
|
||||
if platform == 'linux': ...
|
||||
@@ -91,4 +91,3 @@ field27 = list[str]
|
||||
field28 = builtins.str
|
||||
field29 = str
|
||||
field30 = str | bytes | None
|
||||
field31: typing.Final = field30
|
||||
|
||||
@@ -98,4 +98,3 @@ field27 = list[str]
|
||||
field28 = builtins.str
|
||||
field29 = str
|
||||
field30 = str | bytes | None
|
||||
field31: typing.Final = field30
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import typing
|
||||
|
||||
# Shouldn't affect non-union field types.
|
||||
field1: str
|
||||
|
||||
@@ -32,45 +30,3 @@ field10: (str | int) | str # PYI016: Duplicate union member `str`
|
||||
|
||||
# Should emit for nested unions.
|
||||
field11: dict[int | int, str]
|
||||
|
||||
# Should emit for unions with more than two cases
|
||||
field12: int | int | int # Error
|
||||
field13: int | int | int | int # Error
|
||||
|
||||
# Should emit for unions with more than two cases, even if not directly adjacent
|
||||
field14: int | int | str | int # Error
|
||||
|
||||
# Should emit for duplicate literal types; also covered by PYI030
|
||||
field15: typing.Literal[1] | typing.Literal[1] # Error
|
||||
|
||||
# Shouldn't emit if in new parent type
|
||||
field16: int | dict[int, str] # OK
|
||||
|
||||
# Shouldn't emit if not in a union parent
|
||||
field17: dict[int, int] # OK
|
||||
|
||||
# Should emit in cases with newlines
|
||||
field18: typing.Union[
|
||||
set[
|
||||
int # foo
|
||||
],
|
||||
set[
|
||||
int # bar
|
||||
],
|
||||
] # Error, newline and comment will not be emitted in message
|
||||
|
||||
# Should emit in cases with `typing.Union` instead of `|`
|
||||
field19: typing.Union[int, int] # Error
|
||||
|
||||
# Should emit in cases with nested `typing.Union`
|
||||
field20: typing.Union[int, typing.Union[int, str]] # Error
|
||||
|
||||
# Should emit in cases with mixed `typing.Union` and `|`
|
||||
field21: typing.Union[int, int | str] # Error
|
||||
|
||||
# Should emit only once in cases with multiple nested `typing.Union`
|
||||
field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
||||
|
||||
# Should emit in cases with newlines
|
||||
field23: set[ # foo
|
||||
int] | set[int]
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
from typing import Literal
|
||||
# Shouldn't emit for any cases in the non-stub file for compatibility with flake8-pyi.
|
||||
# Note that this rule could be applied here in the future.
|
||||
|
||||
field1: Literal[1] # OK
|
||||
field2: Literal[1] | Literal[2] # OK
|
||||
|
||||
def func1(arg1: Literal[1] | Literal[2]): # OK
|
||||
print(arg1)
|
||||
|
||||
|
||||
def func2() -> Literal[1] | Literal[2]: # OK
|
||||
return "my Literal[1]ing"
|
||||
|
||||
|
||||
field3: Literal[1] | Literal[2] | str # OK
|
||||
field4: str | Literal[1] | Literal[2] # OK
|
||||
field5: Literal[1] | str | Literal[2] # OK
|
||||
field6: Literal[1] | bool | Literal[2] | str # OK
|
||||
field7 = Literal[1] | Literal[2] # OK
|
||||
field8: Literal[1] | (Literal[2] | str) # OK
|
||||
field9: Literal[1] | (Literal[2] | str) # OK
|
||||
field10: (Literal[1] | str) | Literal[2] # OK
|
||||
field11: dict[Literal[1] | Literal[2], str] # OK
|
||||
@@ -1,86 +0,0 @@
|
||||
import typing
|
||||
import typing_extensions
|
||||
from typing import Literal
|
||||
|
||||
# Shouldn't affect non-union field types.
|
||||
field1: Literal[1] # OK
|
||||
|
||||
# Should emit for duplicate field types.
|
||||
field2: Literal[1] | Literal[2] # Error
|
||||
|
||||
# Should emit for union types in arguments.
|
||||
def func1(arg1: Literal[1] | Literal[2]): # Error
|
||||
print(arg1)
|
||||
|
||||
|
||||
# Should emit for unions in return types.
|
||||
def func2() -> Literal[1] | Literal[2]: # Error
|
||||
return "my Literal[1]ing"
|
||||
|
||||
|
||||
# Should emit in longer unions, even if not directly adjacent.
|
||||
field3: Literal[1] | Literal[2] | str # Error
|
||||
field4: str | Literal[1] | Literal[2] # Error
|
||||
field5: Literal[1] | str | Literal[2] # Error
|
||||
field6: Literal[1] | bool | Literal[2] | str # Error
|
||||
|
||||
# Should emit for non-type unions.
|
||||
field7 = Literal[1] | Literal[2] # Error
|
||||
|
||||
# Should emit for parenthesized unions.
|
||||
field8: Literal[1] | (Literal[2] | str) # Error
|
||||
|
||||
# Should handle user parentheses when fixing.
|
||||
field9: Literal[1] | (Literal[2] | str) # Error
|
||||
field10: (Literal[1] | str) | Literal[2] # Error
|
||||
|
||||
# Should emit for union in generic parent type.
|
||||
field11: dict[Literal[1] | Literal[2], str] # Error
|
||||
|
||||
# Should emit for unions with more than two cases
|
||||
field12: Literal[1] | Literal[2] | Literal[3] # Error
|
||||
field13: Literal[1] | Literal[2] | Literal[3] | Literal[4] # Error
|
||||
|
||||
# Should emit for unions with more than two cases, even if not directly adjacent
|
||||
field14: Literal[1] | Literal[2] | str | Literal[3] # Error
|
||||
|
||||
# Should emit for unions with mixed literal internal types
|
||||
field15: Literal[1] | Literal["foo"] | Literal[True] # Error
|
||||
|
||||
# Shouldn't emit for duplicate field types with same value; covered by Y016
|
||||
field16: Literal[1] | Literal[1] # OK
|
||||
|
||||
# Shouldn't emit if in new parent type
|
||||
field17: Literal[1] | dict[Literal[2], str] # OK
|
||||
|
||||
# Shouldn't emit if not in a union parent
|
||||
field18: dict[Literal[1], Literal[2]] # OK
|
||||
|
||||
# Should respect name of literal type used
|
||||
field19: typing.Literal[1] | typing.Literal[2] # Error
|
||||
|
||||
# Should emit in cases with newlines
|
||||
field20: typing.Union[
|
||||
Literal[
|
||||
1 # test
|
||||
],
|
||||
Literal[2],
|
||||
] # Error, newline and comment will not be emitted in message
|
||||
|
||||
# Should handle multiple unions with multiple members
|
||||
field21: Literal[1, 2] | Literal[3, 4] # Error
|
||||
|
||||
# Should emit in cases with `typing.Union` instead of `|`
|
||||
field22: typing.Union[Literal[1], Literal[2]] # Error
|
||||
|
||||
# Should emit in cases with `typing_extensions.Literal`
|
||||
field23: typing_extensions.Literal[1] | typing_extensions.Literal[2] # Error
|
||||
|
||||
# Should emit in cases with nested `typing.Union`
|
||||
field24: typing.Union[Literal[1], typing.Union[Literal[2], str]] # Error
|
||||
|
||||
# Should emit in cases with mixed `typing.Union` and `|`
|
||||
field25: typing.Union[Literal[1], Literal[2] | str] # Error
|
||||
|
||||
# Should emit only once in cases with multiple nested `typing.Union`
|
||||
field24: typing.Union[Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]]] # Error
|
||||
@@ -1,75 +0,0 @@
|
||||
import builtins
|
||||
import types
|
||||
import typing
|
||||
from collections.abc import Awaitable
|
||||
from types import TracebackType
|
||||
from typing import Any, Type
|
||||
|
||||
import _typeshed
|
||||
import typing_extensions
|
||||
from _typeshed import Unused
|
||||
|
||||
class GoodOne:
|
||||
def __exit__(self, *args: object) -> None: ...
|
||||
async def __aexit__(self, *args) -> str: ...
|
||||
|
||||
class GoodTwo:
|
||||
def __exit__(self, typ: type[builtins.BaseException] | None, *args: builtins.object) -> bool | None: ...
|
||||
async def __aexit__(self, /, typ: Type[BaseException] | None, *args: object, **kwargs) -> bool: ...
|
||||
|
||||
class GoodThree:
|
||||
def __exit__(self, __typ: typing.Type[BaseException] | None, exc: BaseException | None, *args: object) -> None: ...
|
||||
async def __aexit__(self, typ: typing_extensions.Type[BaseException] | None, __exc: BaseException | None, *args: object) -> None: ...
|
||||
|
||||
class GoodFour:
|
||||
def __exit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None) -> None: ...
|
||||
async def __aexit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None, *args: list[None]) -> None: ...
|
||||
|
||||
class GoodFive:
|
||||
def __exit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None, weird_extra_arg: int = ..., *args: int, **kwargs: str) -> None: ...
|
||||
async def __aexit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None) -> Awaitable[None]: ...
|
||||
|
||||
class GoodSix:
|
||||
def __exit__(self, typ: object, exc: builtins.object, tb: object) -> None: ...
|
||||
async def __aexit__(self, typ: object, exc: object, tb: builtins.object) -> None: ...
|
||||
|
||||
class GoodSeven:
|
||||
def __exit__(self, *args: Unused) -> bool: ...
|
||||
async def __aexit__(self, typ: Type[BaseException] | None, *args: _typeshed.Unused) -> Awaitable[None]: ...
|
||||
|
||||
class GoodEight:
|
||||
def __exit__(self, __typ: typing.Type[BaseException] | None, exc: BaseException | None, *args: _typeshed.Unused) -> bool: ...
|
||||
async def __aexit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None, weird_extra_arg: int = ..., *args: Unused, **kwargs: Unused) -> Awaitable[None]: ...
|
||||
|
||||
class GoodNine:
|
||||
def __exit__(self, __typ: typing.Union[typing.Type[BaseException] , None], exc: typing.Union[BaseException , None], *args: _typeshed.Unused) -> bool: ...
|
||||
async def __aexit__(self, typ: typing.Union[typing.Type[BaseException], None], exc: typing.Union[BaseException , None], tb: typing.Union[TracebackType , None], weird_extra_arg: int = ..., *args: Unused, **kwargs: Unused) -> Awaitable[None]: ...
|
||||
|
||||
class GoodTen:
|
||||
def __exit__(self, __typ: typing.Optional[typing.Type[BaseException]], exc: typing.Optional[BaseException], *args: _typeshed.Unused) -> bool: ...
|
||||
async def __aexit__(self, typ: typing.Optional[typing.Type[BaseException]], exc: typing.Optional[BaseException], tb: typing.Optional[TracebackType], weird_extra_arg: int = ..., *args: Unused, **kwargs: Unused) -> Awaitable[None]: ...
|
||||
|
||||
|
||||
class BadOne:
|
||||
def __exit__(self, *args: Any) -> None: ... # PYI036: Bad star-args annotation
|
||||
async def __aexit__(self) -> None: ... # PYI036: Missing args
|
||||
|
||||
class BadTwo:
|
||||
def __exit__(self, typ, exc, tb, weird_extra_arg) -> None: ... # PYI036: Extra arg must have default
|
||||
async def __aexit__(self, typ, exc, tb, *, weird_extra_arg) -> None: ...# PYI036: Extra arg must have default
|
||||
|
||||
class BadThree:
|
||||
def __exit__(self, typ: type[BaseException], exc: BaseException | None, tb: TracebackType | None) -> None: ... # PYI036: First arg has bad annotation
|
||||
async def __aexit__(self, __typ: type[BaseException] | None, __exc: BaseException, __tb: TracebackType) -> bool | None: ... # PYI036: Second arg has bad annotation
|
||||
|
||||
class BadFour:
|
||||
def __exit__(self, typ: typing.Optional[type[BaseException]], exc: typing.Union[BaseException, None], tb: TracebackType) -> None: ... # PYI036: Third arg has bad annotation
|
||||
async def __aexit__(self, __typ: type[BaseException] | None, __exc: BaseException | None, __tb: typing.Union[TracebackType, None, int]) -> bool | None: ... # PYI036: Third arg has bad annotation
|
||||
|
||||
class BadFive:
|
||||
def __exit__(self, typ: BaseException | None, *args: list[str]) -> bool: ... # PYI036: Bad star-args annotation
|
||||
async def __aexit__(self, /, typ: type[BaseException] | None, *args: Any) -> Awaitable[None]: ... # PYI036: Bad star-args annotation
|
||||
|
||||
class BadSix:
|
||||
def __exit__(self, typ, exc, tb, weird_extra_arg, extra_arg2 = None) -> None: ... # PYI036: Extra arg must have default
|
||||
async def __aexit__(self, typ, exc, tb, *, weird_extra_arg) -> None: ... # PYI036: kwargs must have default
|
||||
@@ -1,75 +0,0 @@
|
||||
import builtins
|
||||
import types
|
||||
import typing
|
||||
from collections.abc import Awaitable
|
||||
from types import TracebackType
|
||||
from typing import Any, Type
|
||||
|
||||
import _typeshed
|
||||
import typing_extensions
|
||||
from _typeshed import Unused
|
||||
|
||||
class GoodOne:
|
||||
def __exit__(self, *args: object) -> None: ...
|
||||
async def __aexit__(self, *args) -> str: ...
|
||||
|
||||
class GoodTwo:
|
||||
def __exit__(self, typ: type[builtins.BaseException] | None, *args: builtins.object) -> bool | None: ...
|
||||
async def __aexit__(self, /, typ: Type[BaseException] | None, *args: object, **kwargs) -> bool: ...
|
||||
|
||||
class GoodThree:
|
||||
def __exit__(self, __typ: typing.Type[BaseException] | None, exc: BaseException | None, *args: object) -> None: ...
|
||||
async def __aexit__(self, typ: typing_extensions.Type[BaseException] | None, __exc: BaseException | None, *args: object) -> None: ...
|
||||
|
||||
class GoodFour:
|
||||
def __exit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None) -> None: ...
|
||||
async def __aexit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None, *args: list[None]) -> None: ...
|
||||
|
||||
class GoodFive:
|
||||
def __exit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None, weird_extra_arg: int = ..., *args: int, **kwargs: str) -> None: ...
|
||||
async def __aexit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None) -> Awaitable[None]: ...
|
||||
|
||||
class GoodSix:
|
||||
def __exit__(self, typ: object, exc: builtins.object, tb: object) -> None: ...
|
||||
async def __aexit__(self, typ: object, exc: object, tb: builtins.object) -> None: ...
|
||||
|
||||
class GoodSeven:
|
||||
def __exit__(self, *args: Unused) -> bool: ...
|
||||
async def __aexit__(self, typ: Type[BaseException] | None, *args: _typeshed.Unused) -> Awaitable[None]: ...
|
||||
|
||||
class GoodEight:
|
||||
def __exit__(self, __typ: typing.Type[BaseException] | None, exc: BaseException | None, *args: _typeshed.Unused) -> bool: ...
|
||||
async def __aexit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None, weird_extra_arg: int = ..., *args: Unused, **kwargs: Unused) -> Awaitable[None]: ...
|
||||
|
||||
class GoodNine:
|
||||
def __exit__(self, __typ: typing.Union[typing.Type[BaseException] , None], exc: typing.Union[BaseException , None], *args: _typeshed.Unused) -> bool: ...
|
||||
async def __aexit__(self, typ: typing.Union[typing.Type[BaseException], None], exc: typing.Union[BaseException , None], tb: typing.Union[TracebackType , None], weird_extra_arg: int = ..., *args: Unused, **kwargs: Unused) -> Awaitable[None]: ...
|
||||
|
||||
class GoodTen:
|
||||
def __exit__(self, __typ: typing.Optional[typing.Type[BaseException]], exc: typing.Optional[BaseException], *args: _typeshed.Unused) -> bool: ...
|
||||
async def __aexit__(self, typ: typing.Optional[typing.Type[BaseException]], exc: typing.Optional[BaseException], tb: typing.Optional[TracebackType], weird_extra_arg: int = ..., *args: Unused, **kwargs: Unused) -> Awaitable[None]: ...
|
||||
|
||||
|
||||
class BadOne:
|
||||
def __exit__(self, *args: Any) -> None: ... # PYI036: Bad star-args annotation
|
||||
async def __aexit__(self) -> None: ... # PYI036: Missing args
|
||||
|
||||
class BadTwo:
|
||||
def __exit__(self, typ, exc, tb, weird_extra_arg) -> None: ... # PYI036: Extra arg must have default
|
||||
async def __aexit__(self, typ, exc, tb, *, weird_extra_arg1, weird_extra_arg2) -> None: ...# PYI036: kwargs must have default
|
||||
|
||||
class BadThree:
|
||||
def __exit__(self, typ: type[BaseException], exc: BaseException | None, tb: TracebackType | None) -> None: ... # PYI036: First arg has bad annotation
|
||||
async def __aexit__(self, __typ: type[BaseException] | None, __exc: BaseException, __tb: TracebackType) -> bool | None: ... # PYI036: Second arg has bad annotation
|
||||
|
||||
class BadFour:
|
||||
def __exit__(self, typ: typing.Optional[type[BaseException]], exc: typing.Union[BaseException, None], tb: TracebackType) -> None: ... # PYI036: Third arg has bad annotation
|
||||
async def __aexit__(self, __typ: type[BaseException] | None, __exc: BaseException | None, __tb: typing.Union[TracebackType, None, int]) -> bool | None: ... # PYI036: Third arg has bad annotation
|
||||
|
||||
class BadFive:
|
||||
def __exit__(self, typ: BaseException | None, *args: list[str]) -> bool: ... # PYI036: Bad star-args annotation
|
||||
async def __aexit__(self, /, typ: type[BaseException] | None, *args: Any) -> Awaitable[None]: ... # PYI036: Bad star-args annotation
|
||||
|
||||
class BadSix:
|
||||
def __exit__(self, typ, exc, tb, weird_extra_arg, extra_arg2 = None) -> None: ... # PYI036: Extra arg must have default
|
||||
async def __aexit__(self, typ, exc, tb, *, weird_extra_arg) -> None: ... # PYI036: kwargs must have default
|
||||
@@ -1,47 +0,0 @@
|
||||
from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
from typing_extensions import (
|
||||
TypeAlias,
|
||||
)
|
||||
|
||||
TA0: TypeAlias = int
|
||||
TA1: TypeAlias = int | float | bool
|
||||
TA2: TypeAlias = Union[int, float, bool]
|
||||
|
||||
|
||||
def good1(arg: int) -> int | bool:
|
||||
...
|
||||
|
||||
|
||||
def good2(arg: int, arg2: int | bool) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f0(arg1: float | int) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f2(arg1: int, /, arg2: int | int | float) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f3(arg1: int, *args: Union[int | int | float]) -> None:
|
||||
...
|
||||
|
||||
|
||||
async def f4(**kwargs: int | int | float) -> None:
|
||||
...
|
||||
|
||||
|
||||
class Foo:
|
||||
def good(self, arg: int) -> None:
|
||||
...
|
||||
|
||||
def bad(self, arg: int | float | complex) -> None:
|
||||
...
|
||||
@@ -1,39 +0,0 @@
|
||||
from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
from typing_extensions import (
|
||||
TypeAlias,
|
||||
)
|
||||
|
||||
# Type aliases not flagged
|
||||
TA0: TypeAlias = int
|
||||
TA1: TypeAlias = int | float | bool
|
||||
TA2: TypeAlias = Union[int, float, bool]
|
||||
|
||||
|
||||
def good1(arg: int) -> int | bool: ...
|
||||
|
||||
|
||||
def good2(arg: int, arg2: int | bool) -> None: ...
|
||||
|
||||
|
||||
def f0(arg1: float | int) -> None: ... # PYI041
|
||||
|
||||
|
||||
def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: ... # PYI041
|
||||
|
||||
|
||||
def f2(arg1: int, /, arg2: int | int | float) -> None: ... # PYI041
|
||||
|
||||
|
||||
def f3(arg1: int, *args: Union[int | int | float]) -> None: ... # PYI041
|
||||
|
||||
|
||||
async def f4(**kwargs: int | int | float) -> None: ... # PYI041
|
||||
|
||||
|
||||
class Foo:
|
||||
def good(self, arg: int) -> None: ...
|
||||
|
||||
def bad(self, arg: int | float | complex) -> None: ... # PYI041
|
||||
@@ -36,11 +36,3 @@ bar: str = "51 character stringgggggggggggggggggggggggggggggggg"
|
||||
baz: bytes = b"50 character byte stringgggggggggggggggggggggggggg"
|
||||
|
||||
qux: bytes = b"51 character byte stringggggggggggggggggggggggggggg\xff"
|
||||
|
||||
|
||||
class Demo:
|
||||
"""Docstrings are excluded from this rule. Some padding."""
|
||||
|
||||
|
||||
def func() -> None:
|
||||
"""Docstrings are excluded from this rule. Some padding."""
|
||||
|
||||
@@ -28,9 +28,3 @@ bar: str = "51 character stringgggggggggggggggggggggggggggggggg" # Error: PYI05
|
||||
baz: bytes = b"50 character byte stringgggggggggggggggggggggggggg" # OK
|
||||
|
||||
qux: bytes = b"51 character byte stringggggggggggggggggggggggggggg\xff" # Error: PYI053
|
||||
|
||||
class Demo:
|
||||
"""Docstrings are excluded from this rule. Some padding.""" # OK
|
||||
|
||||
def func() -> None:
|
||||
"""Docstrings are excluded from this rule. Some padding.""" # OK
|
||||
|
||||
@@ -21,13 +21,6 @@ def test_error():
|
||||
assert something and something_else == """error
|
||||
message
|
||||
"""
|
||||
assert (
|
||||
something
|
||||
and something_else
|
||||
== """error
|
||||
message
|
||||
"""
|
||||
)
|
||||
|
||||
# recursive case
|
||||
assert not (a or not (b or c))
|
||||
@@ -38,6 +31,14 @@ message
|
||||
assert not (something or something_else and something_third), "with message"
|
||||
# detected, but no autofix for mixed conditions (e.g. `a or b and c`)
|
||||
assert not (something or something_else and something_third)
|
||||
# detected, but no autofix for parenthesized conditions
|
||||
assert (
|
||||
something
|
||||
and something_else
|
||||
== """error
|
||||
message
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
assert something # OK
|
||||
|
||||
@@ -29,26 +29,6 @@ raise TypeError(
|
||||
# Hello, world!
|
||||
)
|
||||
|
||||
# OK
|
||||
raise AssertionError
|
||||
|
||||
# OK
|
||||
raise AttributeError("test message")
|
||||
|
||||
|
||||
def return_error():
|
||||
return ValueError("Something")
|
||||
|
||||
|
||||
# OK
|
||||
raise return_error()
|
||||
|
||||
|
||||
class Class:
|
||||
@staticmethod
|
||||
def error():
|
||||
return ValueError("Something")
|
||||
|
||||
|
||||
# OK
|
||||
raise Class.error()
|
||||
|
||||
@@ -4,10 +4,3 @@ class Bad(str): # SLOT000
|
||||
|
||||
class Good(str): # Ok
|
||||
__slots__ = ["foo"]
|
||||
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Fine(str, Enum): # Ok
|
||||
__slots__ = ["foo"]
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
# T002 - accepted
|
||||
# TODO (evanrittenhouse): this has an author
|
||||
# TODO(evanrittenhouse): this has an author
|
||||
# TODO (evanrittenhouse) and more: this has an author
|
||||
# TODO(evanrittenhouse) and more: this has an author
|
||||
# TODO@mayrholu: this has an author
|
||||
# TODO @mayrholu: this has an author
|
||||
# TODO@mayrholu and more: this has an author
|
||||
# TODO @mayrholu and more: this has an author
|
||||
# TODO(evanrittenhouse): this also has an author
|
||||
# T002 - errors
|
||||
# TODO: this has no author
|
||||
# FIXME: neither does this
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import A
|
||||
import B
|
||||
import b
|
||||
import C
|
||||
import d
|
||||
import E
|
||||
import f
|
||||
from g import a, B, c
|
||||
from h import A, b, C
|
||||
@@ -26,9 +26,3 @@ def f():
|
||||
import os # isort:skip
|
||||
import collections
|
||||
import abc
|
||||
|
||||
|
||||
def f():
|
||||
import sys; import os # isort:skip
|
||||
import sys; import os # isort:skip # isort:skip
|
||||
import sys; import os
|
||||
|
||||
@@ -19,13 +19,3 @@ if True:
|
||||
|
||||
import D
|
||||
import B
|
||||
|
||||
|
||||
import e
|
||||
import f
|
||||
|
||||
# isort: split
|
||||
# isort: split
|
||||
|
||||
import d
|
||||
import c
|
||||
|
||||
@@ -34,4 +34,4 @@
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4cec6161-f594-446c-ab65-37395bbb3127",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import math\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"_ = math.pi"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff)",
|
||||
"language": "python",
|
||||
"name": "ruff"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
# Do this (new version)
|
||||
from numpy.random import default_rng
|
||||
|
||||
rng = default_rng()
|
||||
vals = rng.standard_normal(10)
|
||||
more_vals = rng.standard_normal(10)
|
||||
@@ -8,13 +7,11 @@ numbers = rng.integers(high, size=5)
|
||||
|
||||
# instead of this (legacy version)
|
||||
from numpy import random
|
||||
|
||||
vals = random.standard_normal(10)
|
||||
more_vals = random.standard_normal(10)
|
||||
numbers = random.integers(high, size=5)
|
||||
|
||||
import numpy
|
||||
|
||||
numpy.random.seed()
|
||||
numpy.random.get_state()
|
||||
numpy.random.set_state()
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import numpy as np
|
||||
|
||||
np.round_(np.random.rand(5, 5), 2)
|
||||
np.product(np.random.rand(5, 5))
|
||||
np.cumproduct(np.random.rand(5, 5))
|
||||
np.sometrue(np.random.rand(5, 5))
|
||||
np.alltrue(np.random.rand(5, 5))
|
||||
|
||||
from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
|
||||
round_(np.random.rand(5, 5), 2)
|
||||
product(np.random.rand(5, 5))
|
||||
cumproduct(np.random.rand(5, 5))
|
||||
sometrue(np.random.rand(5, 5))
|
||||
alltrue(np.random.rand(5, 5))
|
||||
@@ -4,9 +4,7 @@ x = pd.DataFrame()
|
||||
|
||||
x.drop(["a"], axis=1, inplace=True)
|
||||
|
||||
x.y.drop(["a"], axis=1, inplace=True)
|
||||
|
||||
x["y"].drop(["a"], axis=1, inplace=True)
|
||||
x.drop(["a"], axis=1, inplace=True)
|
||||
|
||||
x.drop(
|
||||
inplace=True,
|
||||
@@ -25,7 +23,6 @@ x.drop(["a"], axis=1, **kwargs, inplace=True)
|
||||
x.drop(["a"], axis=1, inplace=True, **kwargs)
|
||||
f(x.drop(["a"], axis=1, inplace=True))
|
||||
|
||||
x.apply(lambda x: x.sort_values("a", inplace=True))
|
||||
x.apply(lambda x: x.sort_values('a', inplace=True))
|
||||
import torch
|
||||
|
||||
torch.m.ReLU(inplace=True) # safe because this isn't a pandas call
|
||||
torch.m.ReLU(inplace=True) # safe because this isn't a pandas call
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import pandas as pd
|
||||
|
||||
|
||||
data = pd.Series(range(1000))
|
||||
|
||||
# PD101
|
||||
data.nunique() <= 1
|
||||
data.nunique(dropna=True) <= 1
|
||||
data.nunique(dropna=False) <= 1
|
||||
data.nunique() == 1
|
||||
data.nunique(dropna=True) == 1
|
||||
data.nunique(dropna=False) == 1
|
||||
data.nunique() != 1
|
||||
data.nunique(dropna=True) != 1
|
||||
data.nunique(dropna=False) != 1
|
||||
data.nunique() > 1
|
||||
data.dropna().nunique() == 1
|
||||
data[data.notnull()].nunique() == 1
|
||||
|
||||
# No violation of this rule
|
||||
data.nunique() == 0 # empty
|
||||
data.nunique() >= 1 # not-empty
|
||||
data.nunique() < 1 # empty
|
||||
data.nunique() == 2 # not constant
|
||||
data.unique() == 1 # not `nunique`
|
||||
|
||||
{"hello": "world"}.nunique() == 1 # no pd.Series
|
||||
@@ -1,20 +0,0 @@
|
||||
import pandas as pd
|
||||
|
||||
# Errors.
|
||||
df = pd.read_table("data.csv", sep=",")
|
||||
df = pd.read_table("data.csv", sep=",", header=0)
|
||||
filename = "data.csv"
|
||||
df = pd.read_table(filename, sep=",")
|
||||
df = pd.read_table(filename, sep=",", header=0)
|
||||
|
||||
# Non-errors.
|
||||
df = pd.read_csv("data.csv")
|
||||
df = pd.read_table("data.tsv")
|
||||
df = pd.read_table("data.tsv", sep="\t")
|
||||
df = pd.read_table("data.tsv", sep=",,")
|
||||
df = pd.read_table("data.tsv", sep=", ")
|
||||
df = pd.read_table("data.tsv", sep=" ,")
|
||||
df = pd.read_table("data.tsv", sep=" , ")
|
||||
not_pd.read_table("data.csv", sep=",")
|
||||
data = read_table("data.csv", sep=",")
|
||||
data = read_table
|
||||
@@ -1,4 +1,4 @@
|
||||
import abc
|
||||
from abc import ABCMeta
|
||||
|
||||
import pydantic
|
||||
|
||||
@@ -19,10 +19,6 @@ class Class:
|
||||
def class_method(cls):
|
||||
pass
|
||||
|
||||
@abc.abstractclassmethod
|
||||
def abstract_class_method(cls):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def static_method(x):
|
||||
return x
|
||||
@@ -45,7 +41,7 @@ class Class:
|
||||
...
|
||||
|
||||
|
||||
class MetaClass(abc.ABCMeta):
|
||||
class MetaClass(ABCMeta):
|
||||
def bad_method(self):
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import abc
|
||||
from abc import ABCMeta
|
||||
|
||||
import pydantic
|
||||
|
||||
@@ -34,23 +34,6 @@ class Class:
|
||||
def stillBad(cls, my_field: str) -> str:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def badAllowed(cls):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def stillBad(cls):
|
||||
pass
|
||||
|
||||
@abc.abstractclassmethod
|
||||
def badAllowed(cls):
|
||||
pass
|
||||
|
||||
@abc.abstractclassmethod
|
||||
def stillBad(cls):
|
||||
pass
|
||||
|
||||
|
||||
class PosOnlyClass:
|
||||
def badAllowed(this, blah, /, self, something: str):
|
||||
pass
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
foo_tuple = (1, 2, 3)
|
||||
foo_list = [1, 2, 3]
|
||||
foo_set = {1, 2, 3}
|
||||
foo_dict = {1: 2, 3: 4}
|
||||
foo_int = 123
|
||||
|
||||
for i in list(foo_tuple): # PERF101
|
||||
pass
|
||||
|
||||
for i in list(foo_list): # PERF101
|
||||
pass
|
||||
|
||||
for i in list(foo_set): # PERF101
|
||||
pass
|
||||
|
||||
for i in list((1, 2, 3)): # PERF101
|
||||
pass
|
||||
|
||||
for i in list([1, 2, 3]): # PERF101
|
||||
pass
|
||||
|
||||
for i in list({1, 2, 3}): # PERF101
|
||||
pass
|
||||
|
||||
for i in list(
|
||||
{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
}
|
||||
):
|
||||
pass
|
||||
|
||||
for i in list( # Comment
|
||||
{1, 2, 3}
|
||||
): # PERF101
|
||||
pass
|
||||
|
||||
for i in list(foo_dict): # Ok
|
||||
pass
|
||||
|
||||
for i in list(1): # Ok
|
||||
pass
|
||||
|
||||
for i in list(foo_int): # Ok
|
||||
pass
|
||||
|
||||
|
||||
import itertools
|
||||
|
||||
for i in itertools.product(foo_int): # Ok
|
||||
pass
|
||||
@@ -1,101 +1,71 @@
|
||||
some_dict = {"a": 12, "b": 32, "c": 44}
|
||||
|
||||
|
||||
def f():
|
||||
for _, value in some_dict.items(): # PERF102
|
||||
print(value)
|
||||
for _, value in some_dict.items(): # PERF102
|
||||
print(value)
|
||||
|
||||
|
||||
def f():
|
||||
for key, _ in some_dict.items(): # PERF102
|
||||
print(key)
|
||||
for key, _ in some_dict.items(): # PERF102
|
||||
print(key)
|
||||
|
||||
|
||||
def f():
|
||||
for weird_arg_name, _ in some_dict.items(): # PERF102
|
||||
print(weird_arg_name)
|
||||
for weird_arg_name, _ in some_dict.items(): # PERF102
|
||||
print(weird_arg_name)
|
||||
|
||||
|
||||
def f():
|
||||
for name, (_, _) in some_dict.items(): # PERF102
|
||||
print(name)
|
||||
for name, (_, _) in some_dict.items(): # PERF102
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
for name, (value1, _) in some_dict.items(): # OK
|
||||
print(name, value1)
|
||||
for name, (value1, _) in some_dict.items(): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
for (key1, _), (_, _) in some_dict.items(): # PERF102
|
||||
print(key1)
|
||||
for (key1, _), (_, _) in some_dict.items(): # PERF102
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
for (_, (_, _)), (value, _) in some_dict.items(): # PERF102
|
||||
print(value)
|
||||
for (_, (_, _)), (value, _) in some_dict.items(): # PERF102
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
for (_, key2), (value1, _) in some_dict.items(): # OK
|
||||
print(key2, value1)
|
||||
for (_, key2), (value1, _) in some_dict.items(): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
for ((_, key2), (value1, _)) in some_dict.items(): # OK
|
||||
print(key2, value1)
|
||||
for ((_, key2), (value1, _)) in some_dict.items(): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
for ((_, key2), (_, _)) in some_dict.items(): # PERF102
|
||||
print(key2)
|
||||
for ((_, key2), (_, _)) in some_dict.items(): # PERF102
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
for (_, _, _, variants), (r_language, _, _, _) in some_dict.items(): # OK
|
||||
print(variants, r_language)
|
||||
for (_, _, _, variants), (r_language, _, _, _) in some_dict.items(): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
for (_, _, (_, variants)), (_, (_, (r_language, _))) in some_dict.items(): # OK
|
||||
print(variants, r_language)
|
||||
for (_, _, (_, variants)), (_, (_, (r_language, _))) in some_dict.items(): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
for key, value in some_dict.items(): # OK
|
||||
print(key, value)
|
||||
for key, value in some_dict.items(): # OK
|
||||
print(key, value)
|
||||
|
||||
|
||||
def f():
|
||||
for _, value in some_dict.items(12): # OK
|
||||
print(value)
|
||||
for _, value in some_dict.items(12): # OK
|
||||
print(value)
|
||||
|
||||
|
||||
def f():
|
||||
for key in some_dict.keys(): # OK
|
||||
print(key)
|
||||
for key in some_dict.keys(): # OK
|
||||
print(key)
|
||||
|
||||
|
||||
def f():
|
||||
for value in some_dict.values(): # OK
|
||||
print(value)
|
||||
for value in some_dict.values(): # OK
|
||||
print(value)
|
||||
|
||||
|
||||
def f():
|
||||
for name, (_, _) in (some_function()).items(): # PERF102
|
||||
print(name)
|
||||
for name, (_, _) in (some_function()).items(): # PERF102
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
for name, (_, _) in (some_function().some_attribute).items(): # PERF102
|
||||
print(name)
|
||||
|
||||
|
||||
def f():
|
||||
for name, unused_value in some_dict.items(): # PERF102
|
||||
print(name)
|
||||
|
||||
|
||||
def f():
|
||||
for unused_name, value in some_dict.items(): # PERF102
|
||||
print(value)
|
||||
for name, (_, _) in (some_function().some_attribute).items(): # PERF102
|
||||
pass
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
for i in range(10):
|
||||
try: # PERF203
|
||||
print(f"{i}")
|
||||
except:
|
||||
print("error")
|
||||
|
||||
try:
|
||||
for i in range(10):
|
||||
print(f"{i}")
|
||||
except:
|
||||
print("error")
|
||||
|
||||
i = 0
|
||||
while i < 10: # PERF203
|
||||
try:
|
||||
print(f"{i}")
|
||||
except:
|
||||
print("error")
|
||||
|
||||
i += 1
|
||||
|
||||
try:
|
||||
i = 0
|
||||
while i < 10:
|
||||
print(f"{i}")
|
||||
i += 1
|
||||
except:
|
||||
print("error")
|
||||
@@ -1,47 +0,0 @@
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
if i % 2:
|
||||
result.append(i) # PERF401
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
result.append(i * i) # PERF401
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
if i % 2:
|
||||
result.append(i) # PERF401
|
||||
elif i % 2:
|
||||
result.append(i) # PERF401
|
||||
else:
|
||||
result.append(i) # PERF401
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
result.append(i) # OK
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = {}
|
||||
for i in items:
|
||||
result[i].append(i) # OK
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
if i not in result:
|
||||
result.append(i) # OK
|
||||
@@ -1,26 +0,0 @@
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
result.append(i) # PERF402
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
result.insert(0, i) # PERF402
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = []
|
||||
for i in items:
|
||||
result.append(i * i) # OK
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = {}
|
||||
for i in items:
|
||||
result[i].append(i * i) # OK
|
||||
@@ -63,6 +63,3 @@ class Foo:
|
||||
#: E702:2:4
|
||||
while 1:
|
||||
1;...
|
||||
#: E703:2:1
|
||||
0\
|
||||
;
|
||||
|
||||
@@ -36,4 +36,3 @@ if (True) == TrueElement or x == TrueElement:
|
||||
assert (not foo) in bar
|
||||
assert {"x": not foo} in bar
|
||||
assert [42, not foo] in bar
|
||||
assert not (re.search(r"^.:\\Users\\[^\\]*\\Downloads\\.*") is None)
|
||||
|
||||
@@ -36,4 +36,3 @@ if (True) == TrueElement or x == TrueElement:
|
||||
assert (not foo) in bar
|
||||
assert {"x": not foo} in bar
|
||||
assert [42, not foo] in bar
|
||||
assert not (re.search(r"^.:\\Users\\[^\\]*\\Downloads\\.*") is None)
|
||||
|
||||
@@ -1,135 +1,51 @@
|
||||
def scope():
|
||||
# E731
|
||||
#: E731
|
||||
f = lambda x: 2 * x
|
||||
#: E731
|
||||
f = lambda x: 2 * x
|
||||
#: E731
|
||||
while False:
|
||||
this = lambda y, z: 2 * x
|
||||
#: E731
|
||||
f = lambda: (yield 1)
|
||||
#: E731
|
||||
f = lambda: (yield from g())
|
||||
#: E731
|
||||
class F:
|
||||
f = lambda x: 2 * x
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
f = lambda x: 2 * x
|
||||
f = object()
|
||||
f.method = lambda: "Method"
|
||||
f = {}
|
||||
f["a"] = lambda x: x**2
|
||||
f = []
|
||||
f.append(lambda x: x**2)
|
||||
f = g = lambda x: x**2
|
||||
lambda: "no-op"
|
||||
|
||||
# Annotated
|
||||
from typing import Callable, ParamSpec
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
while False:
|
||||
this = lambda y, z: 2 * x
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
f = lambda: (yield 1)
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
f = lambda: (yield from g())
|
||||
|
||||
|
||||
def scope():
|
||||
# OK
|
||||
f = object()
|
||||
f.method = lambda: "Method"
|
||||
|
||||
|
||||
def scope():
|
||||
# OK
|
||||
f = {}
|
||||
f["a"] = lambda x: x**2
|
||||
|
||||
|
||||
def scope():
|
||||
# OK
|
||||
f = []
|
||||
f.append(lambda x: x**2)
|
||||
|
||||
|
||||
def scope():
|
||||
# OK
|
||||
f = g = lambda x: x**2
|
||||
|
||||
|
||||
def scope():
|
||||
# OK
|
||||
lambda: "no-op"
|
||||
|
||||
|
||||
class Scope:
|
||||
# E731
|
||||
f = lambda x: 2 * x
|
||||
|
||||
|
||||
class Scope:
|
||||
from typing import Callable
|
||||
|
||||
# E731
|
||||
f: Callable[[int], int] = lambda x: 2 * x
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
from typing import Callable
|
||||
|
||||
x: Callable[[int], int]
|
||||
if True:
|
||||
x = lambda: 1
|
||||
else:
|
||||
x = lambda: 2
|
||||
return x
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
|
||||
from typing import Callable, ParamSpec
|
||||
|
||||
# ParamSpec cannot be used in this context, so do not preserve the annotation.
|
||||
P = ParamSpec("P")
|
||||
f: Callable[P, int] = lambda *args: len(args)
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
|
||||
from typing import Callable
|
||||
|
||||
f: Callable[[], None] = lambda: None
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
|
||||
from typing import Callable
|
||||
|
||||
f: Callable[..., None] = lambda a, b: None
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
|
||||
from typing import Callable
|
||||
|
||||
f: Callable[[int], int] = lambda x: 2 * x
|
||||
P = ParamSpec("P")
|
||||
|
||||
# ParamSpec cannot be used in this context, so do not preserve the annotation.
|
||||
f: Callable[P, int] = lambda *args: len(args)
|
||||
f: Callable[[], None] = lambda: None
|
||||
f: Callable[..., None] = lambda a, b: None
|
||||
f: Callable[[int], int] = lambda x: 2 * x
|
||||
|
||||
# Let's use the `Callable` type from `collections.abc` instead.
|
||||
def scope():
|
||||
# E731
|
||||
from collections.abc import Callable
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
f: Callable[[str, int], str] = lambda a, b: a * b
|
||||
f: Callable[[str, int], str] = lambda a, b: a * b
|
||||
f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
|
||||
f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
|
||||
# Override `Callable`
|
||||
class Callable:
|
||||
pass
|
||||
|
||||
|
||||
def scope():
|
||||
# E731
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
|
||||
# Do not copy the annotation from here on out.
|
||||
f: Callable[[str, int], str] = lambda a, b: a * b
|
||||
|
||||
@@ -19,14 +19,6 @@ with \_ somewhere
|
||||
in the middle
|
||||
"""
|
||||
|
||||
#: W605:1:38
|
||||
value = 'new line\nand invalid escape \_ here'
|
||||
|
||||
|
||||
def f():
|
||||
#: W605:1:11
|
||||
return'\.png$'
|
||||
|
||||
#: Okay
|
||||
regex = r'\.png$'
|
||||
regex = '\\.png$'
|
||||
|
||||
@@ -19,11 +19,6 @@ with \_ somewhere
|
||||
in the middle
|
||||
"""
|
||||
|
||||
|
||||
def f():
|
||||
#: W605:1:11
|
||||
return'\.png$'
|
||||
|
||||
#: Okay
|
||||
regex = r'\.png$'
|
||||
regex = '\\.png$'
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
def double_quotes_backslash():
|
||||
"""Sum\\mary."""
|
||||
|
||||
|
||||
def double_quotes_backslash_raw():
|
||||
r"""Sum\mary."""
|
||||
|
||||
|
||||
def double_quotes_backslash_uppercase():
|
||||
R"""Sum\\mary."""
|
||||
|
||||
|
||||
def make_unique_pod_id(pod_id: str) -> str | None:
|
||||
r"""
|
||||
Generate a unique Pod name.
|
||||
|
||||
Kubernetes pod names must consist of one or more lowercase
|
||||
rfc1035/rfc1123 labels separated by '.' with a maximum length of 253
|
||||
characters.
|
||||
|
||||
Name must pass the following regex for validation
|
||||
``^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$``
|
||||
|
||||
For more details, see:
|
||||
https://github.com/kubernetes/kubernetes/blob/release-1.1/docs/design/identifiers.md
|
||||
|
||||
:param pod_id: requested pod name
|
||||
:return: ``str`` valid Pod name of appropriate length
|
||||
"""
|
||||
@@ -1,25 +0,0 @@
|
||||
def f(a: int, b: int) -> int:
|
||||
"""Showcase function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
a : int
|
||||
_description_
|
||||
b : int
|
||||
_description_
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
_description
|
||||
"""
|
||||
return b - a
|
||||
|
||||
|
||||
def f() -> int:
|
||||
"""Showcase function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
Returns
|
||||
-------
|
||||
"""
|
||||
@@ -513,19 +513,3 @@ def implicit_string_concatenation():
|
||||
A value of some sort.
|
||||
|
||||
""""Extra content"
|
||||
|
||||
|
||||
def replace_equals_with_dash():
|
||||
"""Equal length equals should be replaced with dashes.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
"""
|
||||
|
||||
|
||||
def replace_equals_with_dash2():
|
||||
"""Here, the length of equals is not the same.
|
||||
|
||||
Parameters
|
||||
===========
|
||||
"""
|
||||
|
||||
@@ -37,10 +37,7 @@ f"{{test}}"
|
||||
f'{{ 40 }}'
|
||||
f"{{a {{x}}"
|
||||
f"{{{{x}}}}"
|
||||
""f""
|
||||
''f""
|
||||
(""f""r"")
|
||||
|
||||
# To be fixed
|
||||
# Error: f-string: single '}' is not allowed at line 41 column 8
|
||||
# f"\{{x}}"
|
||||
# f"\{{x}}"
|
||||
|
||||
@@ -48,8 +48,3 @@ x = {
|
||||
|
||||
x = {"a": 1, "a": 1}
|
||||
x = {"a": 1, "b": 2, "a": 1}
|
||||
|
||||
x = {
|
||||
('a', 'b'): 'asdf',
|
||||
('a', 'b'): 'qwer',
|
||||
}
|
||||
|
||||
@@ -80,8 +80,3 @@ def multiple_assignment():
|
||||
global CONSTANT # [global-statement]
|
||||
CONSTANT = 1
|
||||
CONSTANT = 2
|
||||
|
||||
|
||||
def no_assignment():
|
||||
"""Shouldn't warn"""
|
||||
global CONSTANT
|
||||
|
||||
@@ -36,6 +36,3 @@ for item in set(("apples", "lemons", "water")): # set constructor is fine
|
||||
|
||||
for number in {i for i in range(10)}: # set comprehensions are fine
|
||||
print(number)
|
||||
|
||||
for item in {*numbers_set, 4, 5, 6}: # set unpacking is fine
|
||||
print(f"I like {item}.")
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# Errors.
|
||||
foo == "a" or foo == "b"
|
||||
|
||||
foo != "a" and foo != "b"
|
||||
|
||||
foo == "a" or foo == "b" or foo == "c"
|
||||
|
||||
foo != "a" and foo != "b" and foo != "c"
|
||||
|
||||
foo == a or foo == "b" or foo == 3 # Mixed types.
|
||||
|
||||
# False negatives (the current implementation doesn't support Yoda conditions).
|
||||
"a" == foo or "b" == foo or "c" == foo
|
||||
|
||||
"a" != foo and "b" != foo and "c" != foo
|
||||
|
||||
"a" == foo or foo == "b" or "c" == foo
|
||||
|
||||
# OK
|
||||
foo == "a" and foo == "b" and foo == "c" # `and` mixed with `==`.
|
||||
|
||||
foo != "a" or foo != "b" or foo != "c" # `or` mixed with `!=`.
|
||||
|
||||
foo == a or foo == b() or foo == c # Call expression.
|
||||
|
||||
foo != a or foo() != b or foo != c # Call expression.
|
||||
|
||||
foo in {"a", "b", "c"} # Uses membership test already.
|
||||
|
||||
foo not in {"a", "b", "c"} # Uses membership test already.
|
||||
|
||||
foo == "a" # Single comparison.
|
||||
|
||||
foo != "a" # Single comparison.
|
||||
@@ -1,35 +0,0 @@
|
||||
# Errors.
|
||||
class Foo:
|
||||
__slots__ = "bar"
|
||||
|
||||
def __init__(self, bar):
|
||||
self.bar = bar
|
||||
|
||||
|
||||
class Foo:
|
||||
__slots__: str = "bar"
|
||||
|
||||
def __init__(self, bar):
|
||||
self.bar = bar
|
||||
|
||||
|
||||
class Foo:
|
||||
__slots__: str = f"bar"
|
||||
|
||||
def __init__(self, bar):
|
||||
self.bar = bar
|
||||
|
||||
|
||||
# Non-errors.
|
||||
class Foo:
|
||||
__slots__ = ("bar",)
|
||||
|
||||
def __init__(self, bar):
|
||||
self.bar = bar
|
||||
|
||||
|
||||
class Foo:
|
||||
__slots__: tuple[str, ...] = ("bar",)
|
||||
|
||||
def __init__(self, bar):
|
||||
self.bar = bar
|
||||
@@ -1,37 +0,0 @@
|
||||
from typing import ParamSpec, TypeVar
|
||||
|
||||
# Errors.
|
||||
|
||||
T = TypeVar("T", covariant=True, contravariant=True)
|
||||
T = TypeVar(name="T", covariant=True, contravariant=True)
|
||||
|
||||
T = ParamSpec("T", covariant=True, contravariant=True)
|
||||
T = ParamSpec(name="T", covariant=True, contravariant=True)
|
||||
|
||||
# Non-errors.
|
||||
|
||||
T = TypeVar("T")
|
||||
T = TypeVar("T", covariant=False)
|
||||
T = TypeVar("T", contravariant=False)
|
||||
T = TypeVar("T", covariant=False, contravariant=False)
|
||||
T = TypeVar("T", covariant=True)
|
||||
T = TypeVar("T", covariant=True, contravariant=False)
|
||||
T = TypeVar(name="T", covariant=True, contravariant=False)
|
||||
T = TypeVar(name="T", covariant=True)
|
||||
T = TypeVar("T", contravariant=True)
|
||||
T = TypeVar("T", covariant=False, contravariant=True)
|
||||
T = TypeVar(name="T", covariant=False, contravariant=True)
|
||||
T = TypeVar(name="T", contravariant=True)
|
||||
|
||||
T = ParamSpec("T")
|
||||
T = ParamSpec("T", covariant=False)
|
||||
T = ParamSpec("T", contravariant=False)
|
||||
T = ParamSpec("T", covariant=False, contravariant=False)
|
||||
T = ParamSpec("T", covariant=True)
|
||||
T = ParamSpec("T", covariant=True, contravariant=False)
|
||||
T = ParamSpec(name="T", covariant=True, contravariant=False)
|
||||
T = ParamSpec(name="T", covariant=True)
|
||||
T = ParamSpec("T", contravariant=True)
|
||||
T = ParamSpec("T", covariant=False, contravariant=True)
|
||||
T = ParamSpec(name="T", covariant=False, contravariant=True)
|
||||
T = ParamSpec(name="T", contravariant=True)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user