Compare commits

..

3 Commits

Author SHA1 Message Date
Charlie Marsh
cd1732767e Update docs 2023-06-06 16:59:55 -04:00
Tom Kuson
89c1dc39f7 Tweak code 2023-06-06 21:49:48 +01:00
Tom Kuson
ad4ae49ea2 Change set flagging logic 2023-06-06 21:40:51 +01:00
1761 changed files with 38301 additions and 62210 deletions

9
.github/CODEOWNERS vendored
View File

@@ -1,9 +0,0 @@
# GitHub code owners file. For more info: https://help.github.com/articles/about-codeowners/
#
# - Comment lines begin with `#` character.
# - Each line is a file pattern followed by one or more owners.
# - The '*' pattern is global owners.
# - Order is important. The last matching pattern has the most precedence.
# Jupyter
/crates/ruff/src/jupyter/ @dhruvmanila

View File

@@ -14,7 +14,7 @@ jobs:
name: "Run | ${{ matrix.os }}"
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
@@ -29,7 +29,10 @@ jobs:
- uses: Swatinem/rust-cache@v2
- name: "PR - Build benchmarks"
run: cargo bench -p ruff_benchmark --no-run
uses: actions-rs/cargo@v1
with:
command: bench
args: -p ruff_benchmark --no-run
- name: "PR - Run benchmarks"
run: cargo benchmark --save-baseline=pr
@@ -44,7 +47,10 @@ jobs:
run: rustup show
- name: "Main - Build benchmarks"
run: cargo bench -p ruff_benchmark --no-run
uses: actions-rs/cargo@v1
with:
command: bench
args: -p ruff_benchmark --no-run
- name: "Main - Run benchmarks"
run: cargo benchmark --save-baseline=main
@@ -72,10 +78,11 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- name: "Install cargo-binstall"
uses: taiki-e/install-action@cargo-binstall
- name: "Install critcmp"
uses: taiki-e/install-action@v2
with:
tool: critcmp
run: cargo binstall critcmp -y
- name: "Linux | Download PR benchmark results"
uses: actions/download-artifact@v3

View File

@@ -2,7 +2,7 @@ name: CI
on:
push:
branches: [ main ]
branches: [main]
pull_request:
workflow_dispatch:
@@ -54,7 +54,7 @@ jobs:
cargo-test:
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
name: "cargo test | ${{ matrix.os }}"
steps:
@@ -76,9 +76,6 @@ jobs:
cargo insta test --all --all-features
git diff --exit-code
- run: cargo test --package ruff_cli --test black_compatibility_test -- --ignored
# 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:
@@ -90,22 +87,6 @@ jobs:
name: ruff
path: target/debug/ruff
cargo-fuzz:
runs-on: ubuntu-latest
name: "cargo fuzz"
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@v2
with:
workspaces: "fuzz -> target"
- name: "Install cargo-fuzz"
uses: taiki-e/install-action@v2
with:
tool: cargo-fuzz@0.11
- run: cargo fuzz build -s none
cargo-test-wasm:
runs-on: ubuntu-latest
name: "cargo test (wasm)"
@@ -197,12 +178,12 @@ jobs:
- uses: actions/checkout@v3
- name: "Install nightly Rust toolchain"
# Only pinned to make caching work, update freely
run: rustup toolchain install nightly-2023-06-08
run: rustup toolchain install nightly-2023-03-30
- uses: Swatinem/rust-cache@v2
- name: "Install cargo-udeps"
uses: taiki-e/install-action@cargo-udeps
- name: "Run cargo-udeps"
run: cargo +nightly-2023-06-08 udeps
run: cargo +nightly-2023-03-30 udeps
python-package:
@@ -214,20 +195,18 @@ jobs:
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- uses: Swatinem/rust-cache@v2
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
uses: PyO3/maturin-action@v1
with:
manylinux: auto
args: --out dist
- name: "Test wheel"
run: |
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"
run: rm -rf target/wheels
pre-commit:
name: "pre-commit"
@@ -245,8 +224,8 @@ jobs:
- name: "Cache pre-commit"
uses: actions/cache@v3
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
path: ~/.cache/pre-commit
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
- name: "Run pre-commit"
run: |
echo '```console' > $GITHUB_STEP_SUMMARY

View File

@@ -66,7 +66,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
target: [ x64, x86 ]
target: [x64, x86]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
@@ -94,7 +94,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
target: [ x86_64, i686 ]
target: [x86_64, i686]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
@@ -121,7 +121,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
target: [ aarch64, armv7, s390x, ppc64le, ppc64 ]
target: [aarch64, armv7, s390x, ppc64le, ppc64]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4

View File

@@ -2,8 +2,8 @@ name: PR Check Comment
on:
workflow_run:
workflows: [ CI, Benchmark ]
types: [ completed ]
workflows: [CI, Benchmark]
types: [completed]
workflow_dispatch:
inputs:
workflow_run_id:

View File

@@ -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
push:
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 }}
@@ -43,7 +34,6 @@ jobs:
args: --out dist
- name: "Test sdist"
run: |
rustup default $(cat rust-toolchain)
pip install dist/${{ env.PACKAGE_NAME }}-*.tar.gz --force-reinstall
ruff --help
python -m ruff --help
@@ -230,7 +220,7 @@ jobs:
platform:
- target: aarch64-unknown-linux-gnu
arch: aarch64
# see https://github.com/astral-sh/ruff/issues/3791
# see https://github.com/charliermarsh/ruff/issues/3791
# and https://github.com/gnzlbg/jemallocator/issues/170#issuecomment-1503228963
maturin_docker_options: -e JEMALLOC_SYS_WITH_LG_PAGE=16
- target: armv7-unknown-linux-gnueabihf
@@ -392,39 +382,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,9 +393,7 @@ 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:
@@ -447,60 +404,27 @@ jobs:
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
path: binaries
- name: "Publish to GitHub"
- name: Release
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

15
.gitignore vendored
View File

@@ -1,26 +1,11 @@
# Benchmarking cpython (CONTRIBUTING.md)
crates/ruff/resources/test/cpython
# generate_mkdocs.py
mkdocs.yml
.overrides
# check_ecosystem.py
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.py
# Created by `perf` (CONTRIBUTING.md)
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`
/target*
###
# Rust.gitignore

View File

@@ -3,8 +3,6 @@ fail_fast: true
exclude: |
(?x)^(
crates/ruff/resources/.*|
crates/ruff/src/rules/.*/snapshots/.*|
crates/ruff_cli/resources/.*|
crates/ruff_python_formatter/resources/.*|
crates/ruff_python_formatter/src/snapshots/.*
)$
@@ -39,19 +37,29 @@ repos:
name: cargo fmt
entry: cargo fmt --
language: system
types: [ rust ]
pass_filenames: false # This makes it a lot faster
types: [rust]
- id: clippy
name: clippy
entry: cargo clippy --workspace --all-targets --all-features -- -D warnings
language: system
pass_filenames: false
- id: ruff
name: ruff
entry: cargo run --bin ruff -- check --no-cache --force-exclude --fix --exit-non-zero-on-fix
entry: cargo run -p ruff_cli -- check --no-cache --force-exclude --fix --exit-non-zero-on-fix
language: system
types_or: [ python, pyi ]
types_or: [python, pyi]
require_serial: true
exclude: |
(?x)^(
crates/ruff/resources/.*|
crates/ruff_python_formatter/resources/.*
)$
- id: dev-generate-all
name: dev-generate-all
entry: cargo dev generate-all
language: system
pass_filenames: false
exclude: target
# Black
- repo: https://github.com/psf/black
@@ -60,4 +68,4 @@ repos:
- id: black
ci:
skip: [ cargo-fmt, dev-generate-all ]
skip: [cargo-fmt, clippy, dev-generate-all]

View File

@@ -2,7 +2,7 @@
## 0.0.268
### The `keep-runtime-typing` setting has been removed ([#4427](https://github.com/astral-sh/ruff/pull/4427))
### The `keep-runtime-typing` setting has been removed ([#4427](https://github.com/charliermarsh/ruff/pull/4427))
Enabling the `keep-runtime-typing` option, located under the `pyupgrade` section, is equivalent
to ignoring the `UP006` and `UP007` rules via Ruff's standard `ignore` mechanism. As there's no
@@ -11,9 +11,9 @@ removed.
## 0.0.267
### `update-check` is no longer a valid configuration option ([#4313](https://github.com/astral-sh/ruff/pull/4313))
### `update-check` is no longer a valid configuration option ([#4313](https://github.com/charliermarsh/ruff/pull/4313))
The `update-check` functionality was deprecated in [#2530](https://github.com/astral-sh/ruff/pull/2530),
The `update-check` functionality was deprecated in [#2530](https://github.com/charliermarsh/ruff/pull/2530),
in that the behavior itself was removed, and Ruff was changed to warn when that option was enabled.
Now, Ruff will throw an error when `update-check` is provided via a configuration file (e.g.,
@@ -22,7 +22,7 @@ this option from their configuration.
## 0.0.265
### `--fix-only` now exits with a zero exit code, unless `--exit-non-zero-on-fix` is specified ([#4146](https://github.com/astral-sh/ruff/pull/4146))
### `--fix-only` now exits with a zero exit code, unless `--exit-non-zero-on-fix` is specified ([#4146](https://github.com/charliermarsh/ruff/pull/4146))
Previously, `--fix-only` would exit with a non-zero exit code if any fixes were applied. This
behavior was inconsistent with `--fix`, and further, meant that `--exit-non-zero-on-fix` was
@@ -33,7 +33,7 @@ in which case it will exit with a non-zero exit code if any fixes were applied.
## 0.0.260
### Fixes are now represented as a list of edits ([#3709](https://github.com/astral-sh/ruff/pull/3709))
### Fixes are now represented as a list of edits ([#3709](https://github.com/charliermarsh/ruff/pull/3709))
Previously, Ruff represented each fix as a single edit, which prohibited Ruff from automatically
fixing violations that required multiple edits across a file. As such, Ruff now represents each
@@ -68,14 +68,14 @@ The updated representation instead includes a list of edits:
## 0.0.246
### `multiple-statements-on-one-line-def` (`E704`) was removed ([#2773](https://github.com/astral-sh/ruff/pull/2773))
### `multiple-statements-on-one-line-def` (`E704`) was removed ([#2773](https://github.com/charliermarsh/ruff/pull/2773))
This rule was introduced in v0.0.245. However, it turns out that pycodestyle and Flake8 ignore this
rule by default, as it is not part of PEP 8. As such, we've removed it from Ruff.
## 0.0.245
### Ruff's public `check` method was removed ([#2709](https://github.com/astral-sh/ruff/pull/2709))
### Ruff's public `check` method was removed ([#2709](https://github.com/charliermarsh/ruff/pull/2709))
Previously, Ruff exposed a `check` method as a public Rust API. This method was used by few,
if any clients, and was not well documented or supported. As such, it has been removed, with
@@ -83,11 +83,11 @@ the intention of adding a stable public API in the future.
## 0.0.238
### `select`, `extend-select`, `ignore`, and `extend-ignore` have new semantics ([#2312](https://github.com/astral-sh/ruff/pull/2312))
### `select`, `extend-select`, `ignore`, and `extend-ignore` have new semantics ([#2312](https://github.com/charliermarsh/ruff/pull/2312))
Previously, the interplay between `select` and its related options could lead to unexpected
behavior. For example, `ruff --select E501 --ignore ALL` and `ruff --select E501 --extend-ignore ALL`
behaved differently. (See [#2312](https://github.com/astral-sh/ruff/pull/2312) for more
behaved differently. (See [#2312](https://github.com/charliermarsh/ruff/pull/2312) for more
examples.)
When Ruff determines the enabled rule set, it has to reconcile `select` and `ignore` from a variety
@@ -113,14 +113,14 @@ ignore = ["F401"]
Running `ruff --select F` would previously have enabled all `F` rules, apart from `F401`. Now, it
will enable all `F` rules, including `F401`, as the command line's `--select` resets the resolution.
### `remove-six-compat` (`UP016`) has been removed ([#2332](https://github.com/astral-sh/ruff/pull/2332))
### `remove-six-compat` (`UP016`) has been removed ([#2332](https://github.com/charliermarsh/ruff/pull/2332))
The `remove-six-compat` rule has been removed. This rule was only useful for one-time Python 2-to-3
upgrades.
## 0.0.237
### `--explain`, `--clean`, and `--generate-shell-completion` are now subcommands ([#2190](https://github.com/astral-sh/ruff/pull/2190))
### `--explain`, `--clean`, and `--generate-shell-completion` are now subcommands ([#2190](https://github.com/charliermarsh/ruff/pull/2190))
`--explain`, `--clean`, and `--generate-shell-completion` are now implemented as subcommands:
@@ -163,14 +163,14 @@ no change in behavior. However, please note the following exceptions:
## 0.0.226
### `misplaced-comparison-constant` (`PLC2201`) was deprecated in favor of `SIM300` ([#1980](https://github.com/astral-sh/ruff/pull/1980))
### `misplaced-comparison-constant` (`PLC2201`) was deprecated in favor of `SIM300` ([#1980](https://github.com/charliermarsh/ruff/pull/1980))
These two rules contain (nearly) identical logic. To deduplicate the rule set, we've upgraded
`SIM300` to handle a few more cases, and deprecated `PLC2201` in favor of `SIM300`.
## 0.0.225
### `@functools.cache` rewrites have been moved to a standalone rule (`UP033`) ([#1938](https://github.com/astral-sh/ruff/pull/1938))
### `@functools.cache` rewrites have been moved to a standalone rule (`UP033`) ([#1938](https://github.com/charliermarsh/ruff/pull/1938))
Previously, `UP011` handled both `@functools.lru_cache()`-to-`@functools.lru_cache` conversions,
_and_ `@functools.lru_cache(maxsize=None)`-to-`@functools.cache` conversions. The latter has been
@@ -179,7 +179,7 @@ to reflect the change in rule code.
## 0.0.222
### `--max-complexity` has been removed from the CLI ([#1877](https://github.com/astral-sh/ruff/pull/1877))
### `--max-complexity` has been removed from the CLI ([#1877](https://github.com/charliermarsh/ruff/pull/1877))
The McCabe plugin's `--max-complexity` setting has been removed from the CLI, for consistency with
the treatment of other, similar settings.
@@ -194,7 +194,7 @@ max-complexity = 10
## 0.0.181
### Files excluded by `.gitignore` are now ignored ([#1234](https://github.com/astral-sh/ruff/pull/1234))
### Files excluded by `.gitignore` are now ignored ([#1234](https://github.com/charliermarsh/ruff/pull/1234))
Ruff will now avoid checking files that are excluded by `.ignore`, `.gitignore`,
`.git/info/exclude`, and global `gitignore` files. This behavior is powered by the [`ignore`](https://docs.rs/ignore/latest/ignore/struct.WalkBuilder.html#ignore-rules)
@@ -207,7 +207,7 @@ default.
## 0.0.178
### Configuration files are now resolved hierarchically ([#1190](https://github.com/astral-sh/ruff/pull/1190))
### Configuration files are now resolved hierarchically ([#1190](https://github.com/charliermarsh/ruff/pull/1190))
`pyproject.toml` files are now resolved hierarchically, such that for each Python file, we find
the first `pyproject.toml` file in its path, and use that to determine its lint settings.

View File

@@ -12,7 +12,7 @@ Welcome! We're happy to have you here. Thank you in advance for your contributio
- [Example: Adding a new configuration option](#example-adding-a-new-configuration-option)
- [MkDocs](#mkdocs)
- [Release Process](#release-process)
- [Benchmarks](#benchmarking-and-profiling)
- [Benchmarks](#benchmarks)
## The Basics
@@ -21,18 +21,18 @@ Ruff welcomes contributions in the form of Pull Requests.
For small changes (e.g., bug fixes), feel free to submit a PR.
For larger changes (e.g., new lint rules, new functionality, new configuration options), consider
creating an [**issue**](https://github.com/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.
creating an [**issue**](https://github.com/charliermarsh/ruff/issues) outlining your proposed
change. You can also join us on [**Discord**](https://discord.gg/c9MhzV8aU5) to discuss your idea with
the 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
pattern-match against the examples in the existing codebase. Many lint rules are inspired by
existing Python plugins, which can be used as a reference implementation.
As a concrete example: consider taking on one of the rules from the [`flake8-pyi`](https://github.com/astral-sh/ruff/issues/848)
plugin, and looking to the originating [Python source](https://github.com/PyCQA/flake8-pyi) for
guidance.
As a concrete example: consider taking on one of the rules from the [`flake8-pyi`](https://github.com/charliermarsh/ruff/issues/848)
plugin, and looking to the originating [Python source](https://github.com/PyCQA/flake8-pyi)
for guidance.
### Prerequisites
@@ -45,12 +45,6 @@ You'll also need [Insta](https://insta.rs/docs/) to update snapshot tests:
cargo install cargo-insta
```
and pre-commit to run some validation checks:
```shell
pipx install pre-commit # or `pip install pre-commit` if you have a virtualenv
```
### Development
After cloning the repository, run Ruff locally with:
@@ -63,9 +57,9 @@ Prior to opening a pull request, ensure that your code has been auto-formatted,
and that it passes both the lint and test validation checks:
```shell
cargo clippy --workspace --all-targets --all-features -- -D warnings # Rust linting
RUFF_UPDATE_SCHEMA=1 cargo test # Rust testing and updating ruff.schema.json
pre-commit run --all-files --show-diff-on-failure # Rust and Python formatting, Markdown and Python linting, etc.
cargo fmt # Auto-formatting...
cargo clippy --fix --workspace --all-targets --all-features # Linting...
cargo test # Testing...
```
These checks will run on GitHub Actions when you open your Pull Request, but running them locally
@@ -78,6 +72,13 @@ after running `cargo test` like so:
cargo insta review
```
If you have `pre-commit` [installed](https://pre-commit.com/#installation) then you can use it to
assist with formatting and linting. The following command will run the `pre-commit` hooks:
```shell
pre-commit run --all-files
```
Your Pull Request will be reviewed by a maintainer, which may involve a few rounds of iteration
prior to merging.
@@ -92,89 +93,64 @@ 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.
- `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`).
- `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`: 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.
- `crates/ruff_python_stdlib`: library crate containing Python-specific standard library data.
- `crates/ruff_python_whitespace`: library crate containing Python-specific whitespace analysis
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.
- `crates/ruff_python`: library crate implementing Python-specific functionality (e.g., lists of
standard library modules by version).
- `crates/flake8_to_ruff`: binary crate for generating Ruff configuration from Flake8 configuration.
### Example: Adding a new lint rule
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`").
1. Determine a name for the new rule as per our [rule naming convention](#rule-naming-convention).
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/assert_false.rs`).
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
1. In that file, define a violation struct (e.g., `pub struct AssertFalse`). You can grep for
`#[violation]` to see examples.
1. In that file, define a violation struct. You can grep for `#[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).
1. Map the violation struct to a rule code in `crates/ruff/src/codes.rs` (e.g., `E402`).
1. Define the logic for triggering the violation in `crates/ruff/src/checkers/ast/mod.rs` (for
AST-based checks), `crates/ruff/src/checkers/tokens.rs` (for token-based checks),
`crates/ruff/src/checkers/lines.rs` (for text-based checks), or
`crates/ruff/src/checkers/filesystem.rs` (for filesystem-based checks).
1. Map the violation struct to a rule code in `crates/ruff/src/codes.rs` (e.g., `B011`).
1. Add proper [testing](#rule-testing-fixtures-and-snapshots) for your rule.
1. Update the generated files (documentation and generated code).
To trigger the violation, you'll likely want to augment the logic in `crates/ruff/src/checkers/ast.rs`
to call your new function at the appropriate time and with the appropriate inputs. The `Checker`
defined therein is a Python AST visitor, which iterates over the AST, building up a semantic model,
and calling out to lint rule analyzer functions as it goes.
To define the violation, start by creating a dedicated file for your rule under the appropriate
rule linter (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`). That file should
contain a struct defined via `#[violation]`, along with a function that creates the violation
based on any required inputs.
To trigger the violation, you'll likely want to augment the logic in `crates/ruff/src/checkers/ast.rs`,
which defines the Python AST visitor, responsible for iterating over the abstract syntax tree and
collecting diagnostics as it goes.
If you need to inspect the AST, you can run `cargo dev print-ast` with a Python file. Grep
for the `Diagnostic::new` invocations to understand how other, similar rules are implemented.
for the `Check::new` invocations to understand how other, similar rules are implemented.
Once you're satisfied with your code, add tests for your rule. See [rule testing](#rule-testing-fixtures-and-snapshots)
for more details.
Finally, regenerate the documentation and other generated assets (like our JSON Schema) with:
`cargo dev generate-all`.
Finally, regenerate the documentation and generated code with `cargo dev generate-all`.
#### Rule naming convention
Like Clippy, Ruff's rule names should make grammatical and logical sense when read as "allow
${rule}" or "allow ${rule} items", as in the context of suppression comments.
The rule name should make sense when read as "allow _rule-name_" or "allow _rule-name_ items".
For example, `AssertFalse` fits this convention: it flags `assert False` statements, and so a
suppression comment would be framed as "allow `assert False`".
This implies that rule names:
As such, rule names should...
- should state the bad thing being checked for
- Highlight the pattern that is being linted against, rather than the preferred alternative.
For example, `AssertFalse` guards against `assert False` statements.
- should not contain instructions on what you should use instead
(these belong in the rule documentation and the `autofix_title` for rules that have autofix)
- _Not_ contain instructions on how to fix the violation, which instead belong in the rule
documentation and the `autofix_title`.
- _Not_ contain a redundant prefix, like `Disallow` or `Banned`, which are already implied by the
convention.
When re-implementing rules from other linters, we prioritize adhering to this convention over
When re-implementing rules from other linters, this convention is given more importance than
preserving the original rule name.
#### Rule testing: fixtures and snapshots
@@ -271,28 +247,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 havent tagged or
uploaded anything, you can restart after pushing a fix
1. Upload to pypi
1. Create and push the git tag (from pyproject.toml). We create the git tag only here
because we can't change it ([#4468](https://github.com/charliermarsh/ruff/issues/4468)), so
we want to make sure everything up to and including publishing to pypi worked.
1. Attach artifacts to draft GitHub release
1. Trigger downstream repositories. This can fail without causing fallout, it is possible (if
inconvenient) to trigger the downstream jobs manually
1. Create release notes in GitHub UI and promote from draft to proper release(<https://github.com/charliermarsh/ruff/releases/new>)
1. If needed, [update the schemastore](https://github.com/charliermarsh/ruff/blob/main/scripts/update_schemastore.py)
1. If needed, update ruff-lsp and ruff-vscode
## Ecosystem CI
GitHub Actions will run your changes against a number of real-world projects from GitHub and
@@ -304,18 +258,10 @@ python scripts/check_ecosystem.py path/to/your/ruff path/to/older/ruff
You can also run the Ecosystem CI check in a Docker container across a larger set of projects by
downloading the [`known-github-tomls.json`](https://github.com/akx/ruff-usage-aggregate/blob/master/data/known-github-tomls.jsonl)
as `github_search.jsonl` and following the instructions in [scripts/Dockerfile.ecosystem](https://github.com/astral-sh/ruff/blob/main/scripts/Dockerfile.ecosystem).
as `github_search.jsonl` and following the instructions in [scripts/Dockerfile.ecosystem](https://github.com/charliermarsh/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.
@@ -394,9 +340,9 @@ Summary
159.43 ± 2.48 times faster than 'pycodestyle crates/ruff/resources/test/cpython'
```
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:
@@ -437,116 +383,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 -g -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.

178
Cargo.lock generated
View File

@@ -171,12 +171,6 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "bincode"
version = "1.3.3"
@@ -262,8 +256,7 @@ dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"time 0.1.45",
"time",
"wasm-bindgen",
"winapi",
]
@@ -562,41 +555,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "darling"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.18",
]
[[package]]
name = "darling_macro"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
dependencies = [
"darling_core",
"quote",
"syn 2.0.18",
]
[[package]]
name = "diff"
version = "0.1.13"
@@ -733,7 +691,7 @@ dependencies = [
[[package]]
name = "flake8-to-ruff"
version = "0.0.275"
version = "0.0.271"
dependencies = [
"anyhow",
"clap",
@@ -856,12 +814,6 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hexf-parse"
version = "0.2.1"
@@ -891,12 +843,6 @@ dependencies = [
"cc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.3.0"
@@ -1456,7 +1402,6 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
dependencies = [
"phf_macros",
"phf_shared",
]
@@ -1480,19 +1425,6 @@ dependencies = [
"rand",
]
[[package]]
name = "phf_macros"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "phf_shared"
version = "0.11.1"
@@ -1793,7 +1725,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.275"
version = "0.0.271"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1820,7 +1752,6 @@ dependencies = [
"path-absolutize",
"pathdiff",
"pep440_rs",
"phf",
"pretty_assertions",
"pyproject-toml",
"quick-junit",
@@ -1829,10 +1760,10 @@ dependencies = [
"ruff_cache",
"ruff_diagnostics",
"ruff_macros",
"ruff_newlines",
"ruff_python_ast",
"ruff_python_semantic",
"ruff_python_stdlib",
"ruff_python_whitespace",
"ruff_rustpython",
"ruff_text_size",
"ruff_textwrap",
@@ -1843,7 +1774,6 @@ dependencies = [
"semver",
"serde",
"serde_json",
"serde_with",
"shellexpand",
"similar",
"smallvec",
@@ -1880,7 +1810,6 @@ name = "ruff_cache"
version = "0.0.0"
dependencies = [
"filetime",
"glob",
"globset",
"itertools",
"regex",
@@ -1889,7 +1818,7 @@ dependencies = [
[[package]]
name = "ruff_cli"
version = "0.0.275"
version = "0.0.271"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1908,7 +1837,6 @@ dependencies = [
"glob",
"ignore",
"itertools",
"itoa",
"log",
"mimalloc",
"notify",
@@ -1943,20 +1871,17 @@ dependencies = [
"clap",
"itertools",
"libcst",
"log",
"once_cell",
"pretty_assertions",
"regex",
"ruff",
"ruff_cli",
"ruff_diagnostics",
"ruff_python_formatter",
"ruff_textwrap",
"rustpython-format",
"rustpython-parser",
"schemars",
"serde_json",
"similar",
"strum",
"strum_macros",
]
@@ -2005,6 +1930,14 @@ dependencies = [
"syn 2.0.18",
]
[[package]]
name = "ruff_newlines"
version = "0.0.0"
dependencies = [
"memchr",
"ruff_text_size",
]
[[package]]
name = "ruff_python_ast"
version = "0.0.0"
@@ -2019,7 +1952,7 @@ dependencies = [
"num-bigint",
"num-traits",
"once_cell",
"ruff_python_whitespace",
"ruff_newlines",
"ruff_text_size",
"rustc-hash",
"rustpython-ast",
@@ -2041,8 +1974,8 @@ dependencies = [
"itertools",
"once_cell",
"ruff_formatter",
"ruff_newlines",
"ruff_python_ast",
"ruff_python_whitespace",
"ruff_testing_macros",
"ruff_text_size",
"rustc-hash",
@@ -2076,14 +2009,6 @@ dependencies = [
"rustc-hash",
]
[[package]]
name = "ruff_python_whitespace"
version = "0.0.0"
dependencies = [
"memchr",
"ruff_text_size",
]
[[package]]
name = "ruff_rustpython"
version = "0.0.0"
@@ -2105,7 +2030,7 @@ dependencies = [
[[package]]
name = "ruff_text_size"
version = "0.0.0"
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=f60e204b73b95bdb6ce87ccd0de34081b4a17c11#f60e204b73b95bdb6ce87ccd0de34081b4a17c11"
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd"
dependencies = [
"schemars",
"serde",
@@ -2115,7 +2040,7 @@ dependencies = [
name = "ruff_textwrap"
version = "0.0.0"
dependencies = [
"ruff_python_whitespace",
"ruff_newlines",
"ruff_text_size",
]
@@ -2183,7 +2108,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.2.0"
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=f60e204b73b95bdb6ce87ccd0de34081b4a17c11#f60e204b73b95bdb6ce87ccd0de34081b4a17c11"
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd"
dependencies = [
"is-macro",
"num-bigint",
@@ -2194,7 +2119,7 @@ dependencies = [
[[package]]
name = "rustpython-format"
version = "0.2.0"
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=f60e204b73b95bdb6ce87ccd0de34081b4a17c11#f60e204b73b95bdb6ce87ccd0de34081b4a17c11"
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd"
dependencies = [
"bitflags 2.3.1",
"itertools",
@@ -2206,7 +2131,7 @@ dependencies = [
[[package]]
name = "rustpython-literal"
version = "0.2.0"
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=f60e204b73b95bdb6ce87ccd0de34081b4a17c11#f60e204b73b95bdb6ce87ccd0de34081b4a17c11"
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd"
dependencies = [
"hexf-parse",
"is-macro",
@@ -2218,7 +2143,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.2.0"
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=f60e204b73b95bdb6ce87ccd0de34081b4a17c11#f60e204b73b95bdb6ce87ccd0de34081b4a17c11"
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd"
dependencies = [
"anyhow",
"is-macro",
@@ -2241,10 +2166,9 @@ dependencies = [
[[package]]
name = "rustpython-parser-core"
version = "0.2.0"
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=f60e204b73b95bdb6ce87ccd0de34081b4a17c11#f60e204b73b95bdb6ce87ccd0de34081b4a17c11"
source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd"
dependencies = [
"is-macro",
"memchr",
"ruff_text_size",
]
@@ -2369,6 +2293,7 @@ version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
@@ -2383,34 +2308,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_with"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513"
dependencies = [
"base64 0.21.2",
"chrono",
"hex",
"indexmap",
"serde",
"serde_json",
"serde_with_macros",
"time 0.3.21",
]
[[package]]
name = "serde_with_macros"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.18",
]
[[package]]
name = "shellexpand"
version = "3.1.0"
@@ -2637,33 +2534,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "time"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc"
dependencies = [
"itoa",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
[[package]]
name = "time-macros"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
dependencies = [
"time-core",
]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
@@ -2882,7 +2752,7 @@ version = "2.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d"
dependencies = [
"base64 0.13.1",
"base64",
"flate2",
"log",
"once_cell",

View File

@@ -1,15 +1,13 @@
[workspace]
members = ["crates/*"]
resolver = "2"
[workspace.package]
edition = "2021"
rust-version = "1.70"
homepage = "https://beta.ruff.rs/docs"
documentation = "https://beta.ruff.rs/docs"
repository = "https://github.com/astral-sh/ruff"
homepage = "https://beta.ruff.rs/docs/"
documentation = "https://beta.ruff.rs/docs/"
repository = "https://github.com/charliermarsh/ruff"
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
license = "MIT"
[workspace.dependencies]
anyhow = { version = "1.0.69" }
@@ -24,6 +22,7 @@ ignore = { version = "0.4.20" }
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,11 +34,16 @@ 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 = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" }
rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd", default-features = false, features = ["all-nodes-with-ranges"]}
rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" }
rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" }
rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd", default-features = false, features = ["full-lexer", "all-nodes-with-ranges"] }
schemars = { version = "0.8.12" }
serde = { version = "1.0.152", features = ["derive"] }
serde_json = { version = "1.0.93" }
serde_json = { version = "1.0.93", features = ["preserve_order"] }
shellexpand = { version = "3.0.0" }
similar = { version = "2.2.1", features = ["inline"] }
similar = { version = "2.2.1" }
smallvec = { version = "1.10.0" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }
@@ -47,22 +51,8 @@ syn = { version = "2.0.15" }
test-case = { version = "3.0.0" }
toml = { version = "0.7.2" }
# v0.0.1
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "80e4c1399f95e5beb532fdd1e209ad2dbb470438" }
# v0.0.3
ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "f60e204b73b95bdb6ce87ccd0de34081b4a17c11" }
# v0.0.3
rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "f60e204b73b95bdb6ce87ccd0de34081b4a17c11" , default-features = false, features = ["all-nodes-with-ranges", "num-bigint"]}
# v0.0.3
rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "f60e204b73b95bdb6ce87ccd0de34081b4a17c11", default-features = false, features = ["num-bigint"] }
# v0.0.3
rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "f60e204b73b95bdb6ce87ccd0de34081b4a17c11", default-features = false }
# v0.0.3
rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "f60e204b73b95bdb6ce87ccd0de34081b4a17c11" , default-features = false, features = ["full-lexer", "all-nodes-with-ranges", "num-bigint"] }
[profile.release]
lto = "fat"
codegen-units = 1
[profile.dev.package.insta]
opt-level = 3

48
LICENSE
View File

@@ -354,29 +354,6 @@ are:
SOFTWARE.
"""
- flake8-slots, licensed as follows:
"""
Copyright (c) 2021 Dominic Davis-Foster
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
"""
- flake8-todos, licensed as follows:
"""
Copyright (c) 2019 EclecticIQ. All rights reserved.
@@ -1199,31 +1176,6 @@ are:
- flake8-django, licensed under the GPL license.
- perflint, licensed as follows:
"""
MIT License
Copyright (c) 2022 Anthony Shaw
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

View File

@@ -2,11 +2,11 @@
# Ruff
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/charliermarsh/ruff)
[![image](https://img.shields.io/pypi/v/ruff.svg)](https://pypi.python.org/pypi/ruff)
[![image](https://img.shields.io/pypi/l/ruff.svg)](https://pypi.python.org/pypi/ruff)
[![image](https://img.shields.io/pypi/pyversions/ruff.svg)](https://pypi.python.org/pypi/ruff)
[![Actions status](https://github.com/astral-sh/ruff/workflows/CI/badge.svg)](https://github.com/astral-sh/ruff/actions)
[![Actions status](https://github.com/charliermarsh/ruff/workflows/CI/badge.svg)](https://github.com/charliermarsh/ruff/actions)
[**Discord**](https://discord.gg/c9MhzV8aU5) | [**Docs**](https://beta.ruff.rs/docs/) | [**Playground**](https://play.ruff.rs/)
@@ -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>
@@ -88,7 +88,7 @@ creator of [isort](https://github.com/PyCQA/isort):
> Just switched my first project to Ruff. Only one downside so far: it's so fast I couldn't believe
> it was working till I intentionally introduced some errors.
[**Tim Abbott**](https://github.com/astral-sh/ruff/issues/465#issuecomment-1317400028), lead
[**Tim Abbott**](https://github.com/charliermarsh/ruff/issues/465#issuecomment-1317400028), lead
developer of [Zulip](https://github.com/zulip/zulip):
> This is just ridiculously fast... `ruff` is amazing.
@@ -139,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.275
rev: v0.0.271
hooks:
- id: ruff
```
@@ -254,14 +254,13 @@ quality tools, including:
- [flake8-2020](https://pypi.org/project/flake8-2020/)
- [flake8-annotations](https://pypi.org/project/flake8-annotations/)
- [flake8-async](https://pypi.org/project/flake8-async)
- [flake8-bandit](https://pypi.org/project/flake8-bandit/) ([#1646](https://github.com/astral-sh/ruff/issues/1646))
- [flake8-bandit](https://pypi.org/project/flake8-bandit/) ([#1646](https://github.com/charliermarsh/ruff/issues/1646))
- [flake8-blind-except](https://pypi.org/project/flake8-blind-except/)
- [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap/)
- [flake8-bugbear](https://pypi.org/project/flake8-bugbear/)
- [flake8-builtins](https://pypi.org/project/flake8-builtins/)
- [flake8-commas](https://pypi.org/project/flake8-commas/)
- [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/)
- [flake8-copyright](https://pypi.org/project/flake8-copyright/)
- [flake8-datetimez](https://pypi.org/project/flake8-datetimez/)
- [flake8-debugger](https://pypi.org/project/flake8-debugger/)
- [flake8-django](https://pypi.org/project/flake8-django/)
@@ -284,13 +283,12 @@ quality tools, including:
- [flake8-return](https://pypi.org/project/flake8-return/)
- [flake8-self](https://pypi.org/project/flake8-self/)
- [flake8-simplify](https://pypi.org/project/flake8-simplify/)
- [flake8-slots](https://pypi.org/project/flake8-slots/)
- [flake8-super](https://pypi.org/project/flake8-super/)
- [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/)
- [flake8-todos](https://pypi.org/project/flake8-todos/)
- [flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
- [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/)
- [flynt](https://pypi.org/project/flynt/) ([#2102](https://github.com/astral-sh/ruff/issues/2102))
- [flynt](https://pypi.org/project/flynt/) ([#2102](https://github.com/charliermarsh/ruff/issues/2102))
- [isort](https://pypi.org/project/isort/)
- [mccabe](https://pypi.org/project/mccabe/)
- [pandas-vet](https://pypi.org/project/pandas-vet/)
@@ -313,8 +311,8 @@ You can also join us on [**Discord**](https://discord.gg/c9MhzV8aU5).
## Support
Having trouble? Check out the existing issues on [**GitHub**](https://github.com/astral-sh/ruff/issues),
or feel free to [**open a new one**](https://github.com/astral-sh/ruff/issues/new).
Having trouble? Check out the existing issues on [**GitHub**](https://github.com/charliermarsh/ruff/issues),
or feel free to [**open a new one**](https://github.com/charliermarsh/ruff/issues/new).
You can also ask for help on [**Discord**](https://discord.gg/c9MhzV8aU5).
@@ -336,7 +334,7 @@ and again draws on both the APIs and implementation details of [Rome](https://gi
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).
Ruff is the beneficiary of a large number of [contributors](https://github.com/astral-sh/ruff/graphs/contributors).
Ruff is the beneficiary of a large number of [contributors](https://github.com/charliermarsh/ruff/graphs/contributors).
Ruff is released under the MIT license.
@@ -415,21 +413,21 @@ Ruff is used by a number of major open-source projects and companies, including:
If you're using Ruff, consider adding the Ruff badge to project's `README.md`:
```md
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/charliermarsh/ruff)
```
...or `README.rst`:
```rst
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
:target: https://github.com/astral-sh/ruff
:target: https://github.com/charliermarsh/ruff
:alt: Ruff
```
...or, as HTML:
```html
<a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json" alt="Ruff" style="max-width:100%;"></a>
<a href="https://github.com/charliermarsh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json" alt="Ruff" style="max-width:100%;"></a>
```
## License

View File

@@ -8,4 +8,3 @@ whos = "whos"
spawnve = "spawnve"
ned = "ned"
poit = "poit"
BA = "BA" # acronym for "Bad Allowed", used in testing.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -1,16 +1,8 @@
[package]
name = "flake8-to-ruff"
version = "0.0.275"
description = """
Convert Flake8 configuration files to Ruff configuration files.
"""
authors = { workspace = true }
version = "0.0.271"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
documentation = { workspace = true }
repository = { workspace = true }
license = { workspace = true }
[dependencies]
ruff = { path = "../ruff", default-features = false }

View File

@@ -1,7 +1,7 @@
# flake8-to-ruff
Convert existing Flake8 configuration files (`setup.cfg`, `tox.ini`, or `.flake8`) for use with
[Ruff](https://github.com/astral-sh/ruff).
[Ruff](https://github.com/charliermarsh/ruff).
Generates a Ruff-compatible `pyproject.toml` section.
@@ -96,4 +96,4 @@ MIT
## Contributing
Contributions are welcome and hugely appreciated. To get started, check out the
[contributing guidelines](https://github.com/astral-sh/ruff/blob/main/CONTRIBUTING.md).
[contributing guidelines](https://github.com/charliermarsh/ruff/blob/main/CONTRIBUTING.md).

View File

@@ -23,7 +23,7 @@ description = "Convert existing Flake8 configuration to Ruff."
requires-python = ">=3.7"
[project.urls]
repository = "https://github.com/astral-sh/ruff#subdirectory=crates/flake8_to_ruff"
repository = "https://github.com/charliermarsh/ruff#subdirectory=crates/flake8_to_ruff"
[build-system]
requires = ["maturin>=1.0,<2.0"]

View File

@@ -1,15 +1,14 @@
[package]
name = "ruff"
version = "0.0.275"
publish = false
authors = { workspace = true }
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
documentation = { workspace = true }
repository = { workspace = true }
license = { workspace = true }
version = "0.0.271"
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
documentation.workspace = true
homepage.workspace = true
repository.workspace = true
readme = "README.md"
license = "MIT"
[lib]
name = "ruff"
@@ -18,7 +17,7 @@ name = "ruff"
ruff_cache = { path = "../ruff_cache" }
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
ruff_macros = { path = "../ruff_macros" }
ruff_python_whitespace = { path = "../ruff_python_whitespace" }
ruff_newlines = { path = "../ruff_newlines" }
ruff_python_ast = { path = "../ruff_python_ast", features = ["serde"] }
ruff_python_semantic = { path = "../ruff_python_semantic" }
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
@@ -53,7 +52,6 @@ path-absolutize = { workspace = true, features = [
] }
pathdiff = { version = "0.2.1" }
pep440_rs = { version = "0.3.1", features = ["serde"] }
phf = { version = "0.11", features = ["macros"] }
pyproject-toml = { version = "0.6.0" }
quick-junit = { version = "0.3.2" }
regex = { workspace = true }
@@ -65,8 +63,7 @@ schemars = { workspace = true, optional = true }
semver = { version = "1.0.16" }
serde = { workspace = true }
serde_json = { workspace = true }
serde_with = { version = "3.0.0" }
similar = { workspace = true }
similar = { workspace = true, features = ["inline"] }
shellexpand = { workspace = true }
smallvec = { workspace = true }
strum = { workspace = true }

View File

@@ -149,7 +149,7 @@ for group in groupby(items, key=lambda p: p[1]):
collect_shop_items("Joe", group[1])
# https://github.com/astral-sh/ruff/issues/4050
# https://github.com/charliermarsh/ruff/issues/4050
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
if _section == "greens":
for item in section_items:

View File

@@ -1,6 +1,3 @@
from itertools import count, cycle, repeat
# Errors
zip()
zip(range(3))
zip("a", "b")
@@ -8,18 +5,6 @@ zip("a", "b", *zip("c"))
zip(zip("a"), strict=False)
zip(zip("a", strict=True))
# OK
zip(range(3), strict=True)
zip("a", "b", strict=False)
zip("a", "b", "c", strict=True)
# OK (infinite iterators).
zip([1, 2, 3], cycle("ABCDEF"))
zip([1, 2, 3], count())
zip([1, 2, 3], repeat(1))
zip([1, 2, 3], repeat(1, None))
zip([1, 2, 3], repeat(1, times=None))
# Errors (limited iterators).
zip([1, 2, 3], repeat(1, 1))
zip([1, 2, 3], repeat(1, times=4))

View File

@@ -1,6 +1,6 @@
class MyClass:
ImportError = 4
id: int
id = 5
dir = "/"
def __init__(self):
@@ -10,10 +10,3 @@ class MyClass:
def str(self):
pass
from typing import TypedDict
class MyClass(TypedDict):
id: int

View File

@@ -1,20 +1,13 @@
x = set(x for x in range(3))
x = set(x for x in range(3))
y = f"{set(a if a < 6 else 0 for a in range(3))}"
_ = "{}".format(set(a if a < 6 else 0 for a in range(3)))
print(f"Hello {set(a for a in range(3))} World")
x = set(
x for x in range(3)
)
y = f'{set(a if a < 6 else 0 for a in range(3))}'
_ = '{}'.format(set(a if a < 6 else 0 for a in range(3)))
print(f'Hello {set(a for a in range(3))} World')
def set(*args, **kwargs):
return None
def f(x):
return x
print(f'Hello {set(a for a in "abc")} World')
print(f"Hello {set(a for a in 'abc')} World")
print(f"Hello {set(f(a) for a in 'abc')} World")
print(f"{set(a for a in 'abc') - set(a for a in 'ab')}")
print(f"{ set(a for a in 'abc') - set(a for a in 'ab') }")
# The fix generated for this diagnostic is incorrect, as we add additional space
# around the set comprehension.
print(f"{ {set(a for a in 'abc')} }")
set(x for x in range(3))

View File

@@ -5,14 +5,3 @@ dict(
dict(((x, x) for x in range(3)), z=3)
y = f'{dict((x, x) for x in range(3))}'
print(f'Hello {dict((x, x) for x in range(3))} World')
print(f"Hello {dict((x, x) for x in 'abc')} World")
print(f'Hello {dict((x, x) for x in "abc")} World')
print(f'Hello {dict((x,x) for x in "abc")} World')
f'{dict((x, x) for x in range(3)) | dict((x, x) for x in range(3))}'
f'{ dict((x, x) for x in range(3)) | dict((x, x) for x in range(3)) }'
def f(x):
return x
print(f'Hello {dict((x,f(x)) for x in "abc")} World')

View File

@@ -2,14 +2,3 @@ s = set([x for x in range(3)])
s = set(
[x for x in range(3)]
)
s = f"{set([x for x in 'ab'])}"
s = f'{set([x for x in "ab"])}'
def f(x):
return x
s = f"{set([f(x) for x in 'ab'])}"
s = f"{ set([x for x in 'ab']) | set([x for x in 'ab']) }"
s = f"{set([x for x in 'ab']) | set([x for x in 'ab'])}"

View File

@@ -1,13 +1,2 @@
dict([(i, i) for i in range(3)])
dict([(i, i) for i in range(3)], z=4)
def f(x):
return x
f'{dict([(s,s) for s in "ab"])}'
f"{dict([(s,s) for s in 'ab'])}"
f"{dict([(s, s) for s in 'ab'])}"
f"{dict([(s,f(s)) for s in 'ab'])}"
f'{dict([(s,s) for s in "ab"]) | dict([(s,s) for s in "ab"])}'
f'{ dict([(s,s) for s in "ab"]) | dict([(s,s) for s in "ab"]) }'

View File

@@ -16,11 +16,3 @@ set(
set(
[1,]
)
f"{set([1,2,3])}"
f"{set(['a', 'b'])}"
f'{set(["a", "b"])}'
f"{set(['a', 'b']) - set(['a'])}"
f"{ set(['a', 'b']) - set(['a']) }"
f"a {set(['a', 'b']) - set(['a'])} b"
f"a { set(['a', 'b']) - set(['a']) } b"

View File

@@ -10,13 +10,3 @@ def list():
a = list()
f"{dict(x='y')}"
f'{dict(x="y")}'
f"{dict()}"
f"a {dict()} b"
f"{dict(x='y') | dict(y='z')}"
f"{ dict(x='y') | dict(y='z') }"
f"a {dict(x='y') | dict(y='z')} b"
f"a { dict(x='y') | dict(y='z') } b"

View File

@@ -34,19 +34,3 @@ _ = (
b"abc"
b"def"
)
_ = """a""" """b"""
_ = """a
b""" """c
d"""
_ = f"""a""" f"""b"""
_ = f"a" "b"
_ = """a""" "b"
_ = 'a' "b"
_ = rf"a" rf"b"

View File

@@ -1,19 +1,19 @@
def f():
from collections.abc import Set as AbstractSet # Ok
from collections.abc import Set as AbstractSet # Ok
def f():
from collections.abc import Container, Sized, Set as AbstractSet, ValuesView # Ok
from collections.abc import Set # Ok
def f():
from collections.abc import Set # PYI025
from collections.abc import (
Container,
Sized,
Set, # Ok
ValuesView
)
def f():
from collections.abc import Container, Sized, Set, ValuesView # PYI025
GLOBAL: Set[int] = set()
class Class:
member: Set[int]
from collections.abc import (
Container,
Sized,
Set as AbstractSet, # Ok
ValuesView
)

View File

@@ -1,50 +1,19 @@
def f():
from collections.abc import Set as AbstractSet # Ok
from collections.abc import Set as AbstractSet # Ok
def f():
from collections.abc import Container, Sized, Set as AbstractSet, ValuesView # Ok
def f():
from collections.abc import Set # PYI025
from collections.abc import Set # PYI025
def f():
from collections.abc import Container, Sized, Set, ValuesView # PYI025
def f():
"""Test: local symbol renaming."""
if True:
from collections.abc import Set
else:
Set = 1
from collections.abc import (
Container,
Sized,
Set, # PYI025
ValuesView
)
x: Set = set()
x: Set
del Set
def f():
print(Set)
def Set():
pass
print(Set)
from collections.abc import Set
def f():
"""Test: global symbol renaming."""
global Set
Set = 1
print(Set)
def f():
"""Test: nonlocal symbol renaming."""
from collections.abc import Set
def g():
nonlocal Set
Set = 1
print(Set)
from collections.abc import (
Container,
Sized,
Set as AbstractSet,
ValuesView # Ok
)

View File

@@ -1,7 +0,0 @@
# Bad import.
from __future__ import annotations # Not PYI044 (not a stubfile).
# Good imports.
from __future__ import Something
import sys
from socket import AF_INET

View File

@@ -1,7 +0,0 @@
# Bad import.
from __future__ import annotations # PYI044.
# Good imports.
from __future__ import Something
import sys
from socket import AF_INET

View File

@@ -1,32 +0,0 @@
from typing import NoReturn, Never
import typing_extensions
def foo(arg):
...
def foo_int(arg: int):
...
def foo_no_return(arg: NoReturn):
...
def foo_no_return_typing_extensions(
arg: typing_extensions.NoReturn,
):
...
def foo_no_return_kwarg(arg: int, *, arg2: NoReturn):
...
def foo_no_return_pos_only(arg: int, /, arg2: NoReturn):
...
def foo_never(arg: Never):
...

View File

@@ -1,12 +0,0 @@
from typing import NoReturn, Never
import typing_extensions
def foo(arg): ...
def foo_int(arg: int): ...
def foo_no_return(arg: NoReturn): ... # Error: PYI050
def foo_no_return_typing_extensions(
arg: typing_extensions.NoReturn,
): ... # Error: PYI050
def foo_no_return_kwarg(arg: int, *, arg2: NoReturn): ... # Error: PYI050
def foo_no_return_pos_only(arg: int, /, arg2: NoReturn): ... # Error: PYI050
def foo_never(arg: Never): ...

View File

@@ -1,25 +1,17 @@
import pytest
# OK
def f():
pytest.fail("this is a failure")
def test_xxx():
pytest.fail("this is a failure") # Test OK arg
def f():
pytest.fail(msg="this is a failure")
def test_xxx():
pytest.fail(msg="this is a failure") # Test OK kwarg
def f():
pytest.fail(reason="this is a failure")
# Errors
def f():
def test_xxx(): # Error
pytest.fail()
pytest.fail("")
pytest.fail(f"")
pytest.fail(msg="")
pytest.fail(msg=f"")
pytest.fail(reason="")
pytest.fail(reason=f"")

View File

@@ -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

View File

@@ -79,7 +79,7 @@ def x():
return a
# Ignore unpacking
# ignore unpacking
def x():
b, a = [1, 2]
return a
@@ -109,8 +109,7 @@ def x():
# Considered OK, since functions can have side effects.
def x():
a = 1
b = 2
b, a = 1, 2
print(b)
return a
@@ -277,25 +276,20 @@ def str_to_bool(val):
# Mixed assignments
def function_assignment(x):
def f():
...
def f(): ...
return f
def class_assignment(x):
class Foo:
...
class Foo: ...
return Foo
def mixed_function_assignment(x):
if x:
def f():
...
def f(): ...
else:
f = 42
@@ -304,56 +298,8 @@ def mixed_function_assignment(x):
def mixed_class_assignment(x):
if x:
class Foo:
...
class Foo: ...
else:
Foo = 42
return Foo
# `with` statements
def foo():
with open("foo.txt", "r") as f:
x = f.read()
return x # RET504
def foo():
with open("foo.txt", "r") as f:
x = f.read()
print(x)
return x
def foo():
with open("foo.txt", "r") as f:
x = f.read()
print(x)
return x
# Autofix cases
def foo():
a = 1
b=a
return b # RET504
def foo():
a = 1
b =a
return b # RET504
def foo():
a = 1
b= a
return b # RET504
def foo():
a = 1 # Comment
return a

View File

@@ -53,9 +53,6 @@ class Foo(metaclass=BazMeta):
def __really_private_func(self, arg):
super().__really_private_func(arg)
def __eq__(self, other):
return self._private_thing == other._private_thing
foo = Foo()

View File

@@ -171,17 +171,3 @@ def f():
if x.isdigit():
return True
return False
async def f():
# OK
for x in iterable:
if await check(x):
return True
return False
async def f():
# SIM110
for x in iterable:
if check(x):
return True
return False

View File

@@ -33,17 +33,17 @@ with A() as a:
print("hello")
a()
# OK, can't merge async with and with.
# OK
async with A() as a:
with B() as b:
print("hello")
# OK, can't merge async with and with.
# OK
with A() as a:
async with B() as b:
print("hello")
# SIM117
# OK
async with A() as a:
async with B() as b:
print("hello")
@@ -99,25 +99,4 @@ with A("01ß9💣28901ß9💣28901ß9💣289") as a:
# SIM117 (not auto-fixable too long)
with A("01ß9💣28901ß9💣28901ß9💣2890") as a:
with B("01ß9💣28901ß9💣28901ß9💣289") as b:
print("hello")
# From issue #3025.
async def main():
async with A() as a: # SIM117.
async with B() as b:
print("async-inside!")
return 0
# OK. Can't merge across different kinds of with statements.
with a as a2:
async with b as b2:
with c as c2:
async with d as d2:
f(a2, b2, c2, d2)
# OK. Can't merge across different kinds of with statements.
async with b as b2:
with c as c2:
async with d as d2:
f(b2, c2, d2)
print("hello")

View File

@@ -1,6 +0,0 @@
class Bad(str): # SLOT000
pass
class Good(str): # Ok
__slots__ = ["foo"]

View File

@@ -1,21 +0,0 @@
class Bad(tuple): # SLOT001
pass
class Good(tuple): # Ok
__slots__ = ("foo",)
from typing import Tuple
class Bad(Tuple): # SLOT001
pass
class Bad(Tuple[str, int, float]): # SLOT001
pass
class Good(Tuple[str, int, float]): # OK
__slots__ = ("foo",)

View File

@@ -1,14 +0,0 @@
from collections import namedtuple
from typing import NamedTuple
class Bad(namedtuple("foo", ["str", "int"])): # SLOT002
pass
class Good(namedtuple("foo", ["str", "int"])): # OK
__slots__ = ("foo",)
class Good(NamedTuple): # Ok
pass

View File

@@ -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

View File

@@ -1,6 +1,6 @@
# TDO003 - accepted
# TODO: this comment has a link
# https://github.com/astral-sh/ruff/issues/3870
# https://github.com/charliermarsh/ruff/issues/3870
# TODO: this comment has an issue
# TDO-3870

View File

@@ -164,11 +164,3 @@ def f():
)
x: DataFrame = 2
def f():
global Member
from module import Member
x: Member = 1

View File

@@ -1,37 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "1",
"metadata": {},
"outputs": [],
"source": [
"import math\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
}

View File

@@ -1,38 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "1",
"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
}

View File

@@ -1,8 +0,0 @@
{
"execution_count": null,
"cell_type": "code",
"id": "1",
"metadata": {},
"outputs": [],
"source": ["def foo():\n", " pass\n", "\n", "%timeit foo()"]
}

View File

@@ -1,6 +0,0 @@
{
"cell_type": "markdown",
"id": "1",
"metadata": {},
"source": ["This is a markdown cell\n", "Some more content"]
}

View File

@@ -1,8 +0,0 @@
{
"execution_count": null,
"cell_type": "code",
"id": "1",
"metadata": {},
"outputs": [],
"source": ["def foo():\n", " pass"]
}

View File

@@ -1,8 +0,0 @@
{
"execution_count": null,
"cell_type": "code",
"id": "1",
"metadata": {},
"outputs": [],
"source": "%timeit print('hello world')"
}

View File

@@ -1,51 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "0c7535f6-43cb-423f-bfe1-d263b8f55da0",
"metadata": {},
"outputs": [],
"source": [
"from pathlib import Path\n",
"import random\n",
"import math"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c066fa1a-5682-47af-8c17-5afec3cf4ad0",
"metadata": {},
"outputs": [],
"source": [
"from typing import Any\n",
"import collections\n",
"# Newline should be added here\n",
"def foo():\n",
" pass"
]
}
],
"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
}

View File

@@ -1,53 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "663ba955-baca-4f34-9ebb-840d2573ae3f",
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"import random\n",
"from pathlib import Path"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d0adfe23-8aea-47e9-bf67-d856cfcb96ea",
"metadata": {},
"outputs": [],
"source": [
"import collections\n",
"from typing import Any\n",
"\n",
"\n",
"# Newline should be added here\n",
"def foo():\n",
" pass"
]
}
],
"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
}

View File

@@ -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
}

View File

@@ -3,16 +3,6 @@
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2023-03-08T23:01:09.782916Z",
"start_time": "2023-03-08T23:01:09.705831Z"
},
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
@@ -29,26 +19,32 @@
" print(f\"cell one: {y}\")\n",
"\n",
"unused_variable()"
]
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"start_time": "2023-03-08T23:01:09.705831Z",
"end_time": "2023-03-08T23:01:09.782916Z"
}
}
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's do another mistake"
]
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2023-03-08T23:01:09.915760Z",
"start_time": "2023-03-08T23:01:09.733809Z"
},
"collapsed": true,
"jupyter": {
"outputs_hidden": true
"ExecuteTime": {
"start_time": "2023-03-08T23:01:09.733809Z",
"end_time": "2023-03-08T23:01:09.915760Z"
}
},
"outputs": [
@@ -66,66 +62,27 @@
"\n",
"mutable_argument()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's create an empty cell"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Multi-line empty cell!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(\"after empty cells\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python (ruff)",
"display_name": "Python 3",
"language": "python",
"name": "ruff"
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.3"
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
"nbformat_minor": 0
}

View File

@@ -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

View File

@@ -1,11 +0,0 @@
class badAllowed:
pass
class stillBad:
pass
class BAD_ALLOWED:
pass
class STILL_BAD:
pass

View File

@@ -1,14 +0,0 @@
import unittest
def badAllowed():
pass
def stillBad():
pass
class Test(unittest.TestCase):
def badAllowed(self):
return super().tearDown()
def stillBad(self):
return super().tearDown()

View File

@@ -1,12 +0,0 @@
def func(_, a, badAllowed):
return _, a, badAllowed
def func(_, a, stillBad):
return _, a, stillBad
class Class:
def method(self, _, a, badAllowed):
return _, a, badAllowed
def method(self, _, a, stillBad):
return _, a, stillBad

View File

@@ -1,22 +0,0 @@
from abc import ABCMeta
class Class:
def __init_subclass__(self, default_name, **kwargs):
...
@classmethod
def badAllowed(self, x, /, other):
...
@classmethod
def stillBad(self, x, /, other):
...
class MetaClass(ABCMeta):
def badAllowed(self):
pass
def stillBad(self):
pass

View File

@@ -1,59 +0,0 @@
import abc
import pydantic
class Class:
def badAllowed(this):
pass
def stillBad(this):
pass
if False:
def badAllowed(this):
pass
def stillBad(this):
pass
@pydantic.validator
def badAllowed(cls, my_field: str) -> str:
pass
@pydantic.validator
def stillBad(cls, my_field: str) -> str:
pass
@pydantic.validator("my_field")
def badAllowed(cls, my_field: str) -> str:
pass
@pydantic.validator("my_field")
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
def stillBad(this, blah, /, self, something: str):
pass

View File

@@ -1,6 +0,0 @@
def assign():
badAllowed = 0
stillBad = 0
BAD_ALLOWED = 0
STILL_BAD = 0

View File

@@ -1,13 +0,0 @@
def __badAllowed__():
pass
def __stillBad__():
pass
def nested():
def __badAllowed__():
pass
def __stillBad__():
pass

View File

@@ -1,5 +0,0 @@
import mod.BAD_ALLOWED as badAllowed
import mod.STILL_BAD as stillBad
from mod import BAD_ALLOWED as badAllowed
from mod import STILL_BAD as stillBad

View File

@@ -1,5 +0,0 @@
import mod.badallowed as badAllowed
import mod.stillbad as stillBad
from mod import badallowed as BadAllowed
from mod import stillbad as StillBad

View File

@@ -1,8 +0,0 @@
import mod.BadAllowed as badallowed
import mod.stillBad as stillbad
from mod import BadAllowed as badallowed
from mod import StillBad as stillbad
from mod import BadAllowed as bad_allowed
from mod import StillBad as still_bad

View File

@@ -1,8 +0,0 @@
import mod.BadAllowed as BADALLOWED
import mod.StillBad as STILLBAD
from mod import BadAllowed as BADALLOWED
from mod import StillBad as STILLBAD
from mod import BadAllowed as BAD_ALLOWED
from mod import StillBad as STILL_BAD

View File

@@ -1,19 +0,0 @@
class C:
badAllowed = 0
stillBad = 0
_badAllowed = 0
_stillBad = 0
bad_Allowed = 0
still_Bad = 0
class D(TypedDict):
badAllowed: bool
stillBad: bool
_badAllowed: list
_stillBad: list
bad_Allowed: set
still_Bad: set

View File

@@ -1,8 +0,0 @@
badAllowed = 0
stillBad = 0
_badAllowed = 0
_stillBad = 0
bad_Allowed = 0
still_Bad = 0

View File

@@ -1,5 +0,0 @@
import mod.BadAllowed as BA
import mod.StillBad as SB
from mod import BadAllowed as BA
from mod import StillBad as SB

View File

@@ -1,11 +0,0 @@
class BadAllowed(Exception):
pass
class StillBad(Exception):
pass
class BadAllowed(AnotherError):
pass
class StillBad(AnotherError):
pass

View File

@@ -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

View File

@@ -1,71 +0,0 @@
some_dict = {"a": 12, "b": 32, "c": 44}
for _, value in some_dict.items(): # PERF102
print(value)
for key, _ in some_dict.items(): # PERF102
print(key)
for weird_arg_name, _ in some_dict.items(): # PERF102
print(weird_arg_name)
for name, (_, _) in some_dict.items(): # PERF102
pass
for name, (value1, _) in some_dict.items(): # OK
pass
for (key1, _), (_, _) in some_dict.items(): # PERF102
pass
for (_, (_, _)), (value, _) in some_dict.items(): # PERF102
pass
for (_, key2), (value1, _) in some_dict.items(): # OK
pass
for ((_, key2), (value1, _)) in some_dict.items(): # OK
pass
for ((_, key2), (_, _)) in some_dict.items(): # PERF102
pass
for (_, _, _, variants), (r_language, _, _, _) in some_dict.items(): # OK
pass
for (_, _, (_, variants)), (_, (_, (r_language, _))) in some_dict.items(): # OK
pass
for key, value in some_dict.items(): # OK
print(key, value)
for _, value in some_dict.items(12): # OK
print(value)
for key in some_dict.keys(): # OK
print(key)
for value in some_dict.values(): # OK
print(value)
for name, (_, _) in (some_function()).items(): # PERF102
pass
for name, (_, _) in (some_function().some_attribute).items(): # PERF102
pass

View File

@@ -60,9 +60,3 @@ match *0, 1, *2:
#:
class Foo:
match: Optional[Match] = None
#: E702:2:4
while 1:
1;...
#: E703:2:1
0\
;

View File

@@ -1,32 +0,0 @@
"""Test that runtime typing references are properly attributed to scoped imports."""
from __future__ import annotations
from typing import TYPE_CHECKING, cast
if TYPE_CHECKING:
from threading import Thread
def fn(thread: Thread):
from threading import Thread
# The `Thread` on the left-hand side should resolve to the `Thread` imported at the
# top level.
x: Thread
def fn(thread: Thread):
from threading import Thread
# The `Thread` on the left-hand side should resolve to the `Thread` imported at the
# top level.
cast("Thread", thread)
def fn(thread: Thread):
from threading import Thread
# The `Thread` on the right-hand side should resolve to the`Thread` imported within
# `fn`.
cast(Thread, thread)

View File

@@ -1,11 +0,0 @@
"""Test that straight `__future__` imports are considered unused."""
def f():
import __future__
def f():
import __future__
print(__future__.absolute_import)

View File

@@ -7,5 +7,3 @@ hidden = {"a": "!"}
"%(a)s" % {"a": 1, r"b": "!"} # F504 ("b" not used)
"%(a)s" % {'a': 1, u"b": "!"} # F504 ("b" not used)
'' % {'a''b' : ''} # F504 ("ab" not used)

View File

@@ -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}}"

View File

@@ -19,9 +19,3 @@ if ("123" != x) is 3:
if "123" != (x is 3):
pass
{2 is
not ''}
{2 is
not ''}

View File

@@ -126,22 +126,3 @@ def f(x: int):
def f():
if any((key := (value := x)) for x in ["ok"]):
print(key)
def f() -> None:
is_connected = False
class Foo:
@property
def is_connected(self):
nonlocal is_connected
return is_connected
def do_thing(self):
# This should resolve to the `is_connected` in the function scope.
nonlocal is_connected
print(is_connected)
obj = Foo()
obj.do_thing()

View File

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

View File

@@ -108,42 +108,3 @@ def f():
def f():
toplevel = tt = 1
def f(provided: int) -> int:
match provided:
case [_, *x]:
pass
def f(provided: int) -> int:
match provided:
case x:
pass
def f(provided: int) -> int:
match provided:
case Foo(bar) as x:
pass
def f(provided: int) -> int:
match provided:
case {"foo": 0, **x}:
pass
def f(provided: int) -> int:
match provided:
case {**x}:
pass
global CONSTANT
def f() -> None:
global CONSTANT
CONSTANT = 1
CONSTANT = 2

View File

@@ -1,3 +1,4 @@
if True:
import foo1; x = 1
import foo2; x = 1
@@ -10,6 +11,7 @@ if True:
import foo4 \
; x = 1
if True:
x = 1; import foo5
@@ -18,10 +20,12 @@ if True:
x = 1; \
import foo6
if True:
x = 1 \
; import foo7
if True:
x = 1; import foo8; x = 1
x = 1; import foo9; x = 1
@@ -36,27 +40,12 @@ if True:
;import foo11 \
;x = 1
if True:
x = 1; \
\
import foo12
if True:
x = 1; \
\
import foo13
if True:
x = 1; \
# \
import foo14
# Continuation, but not as the last content in the file.
x = 1; \
import foo15
import foo12
# Continuation, followed by end-of-file. (Removing `import foo` would cause a syntax
# error.)
x = 1; \
import foo16
import foo13

View File

@@ -1,45 +0,0 @@
# Errors.
foo == foo
foo != foo
foo > foo
foo >= foo
foo < foo
foo <= foo
foo is foo
foo is not foo
foo in foo
foo not in foo
# Non-errors.
"foo" == "foo" # This is flagged by `comparison-of-constant` instead.
foo == "foo"
foo == bar
foo != bar
foo > bar
foo >= bar
foo < bar
foo <= bar
foo is bar
foo is not bar
foo in bar
foo not in bar

View File

@@ -73,10 +73,3 @@ def override_class():
pass
CLASS()
def multiple_assignment():
"""Should warn on every assignment."""
global CONSTANT # [global-statement]
CONSTANT = 1
CONSTANT = 2

View File

@@ -14,12 +14,6 @@ __all__ = (x for x in ["Hello", "world"]) # [invalid-all-format]
__all__ = {x for x in ["Hello", "world"]} # [invalid-all-format]
__all__ = foo # [invalid-all-format]
__all__ = foo.bar # [invalid-all-format]
__all__ = foo["bar"] # [invalid-all-format]
__all__ = ["Hello"]
__all__ = ("Hello",)
@@ -35,8 +29,3 @@ __all__ = list({"Hello", "world"})
__all__ = list(["Hello"]) + list(["world"])
__all__ = tuple(["Hello"]) + ("world",)
__all__ = __all__ + ["Hello"]
__all__ = __all__ + multiprocessing.__all__

View File

@@ -31,21 +31,6 @@ with None as i:
with None as j: # ok
pass
# Async with -> with, variable reused
async with None as i:
with None as i: # error
pass
# Async with -> with, different variable
async with None as i:
with None as j: # ok
pass
# Async for -> for, variable reused
async for i in []:
for i in []: # error
pass
# For -> for -> for, doubly nested variable reuse
for i in []:
for j in []:

View File

@@ -1,8 +1,53 @@
[tool.ruff]
allowed-confusables = ["", "ρ", ""]
line-length = 88
extend-exclude = [
"excluded_file.py",
"migrations",
"with_excluded_file/other_excluded_file.py",
]
external = ["V101"]
per-file-ignores = { "__init__.py" = ["F401"] }
[tool.ruff.flake8-bugbear]
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]
[tool.ruff.flake8-builtins]
builtins-ignorelist = ["id", "dir"]
[tool.ruff.flake8-quotes]
inline-quotes = "single"
multiline-quotes = "double"
docstring-quotes = "double"
avoid-escape = true
[tool.ruff.mccabe]
max-complexity = 10
[tool.ruff.pep8-naming]
classmethod-decorators = ["pydantic.validator"]
[tool.ruff.flake8-tidy-imports]
ban-relative-imports = "parents"
[tool.ruff.flake8-tidy-imports.banned-api]
"cgi".msg = "The cgi module is deprecated."
"typing.TypedDict".msg = "Use typing_extensions.TypedDict instead."
[tool.ruff.flake8-errmsg]
max-string-length = 20
[tool.ruff.flake8-import-conventions.aliases]
pandas = "pd"
[tool.ruff.flake8-import-conventions.extend-aliases]
"dask.dataframe" = "dd"
[tool.ruff.flake8-pytest-style]
fixture-parentheses = false
parametrize-names-type = "csv"
parametrize-values-type = "tuple"
parametrize-values-row-type = "list"
raises-require-match-for = ["Exception", "TypeError", "KeyError"]
raises-extend-require-match-for = ["requests.RequestException"]
mark-parentheses = false

View File

@@ -134,19 +134,6 @@ class A(
...
class A(object, object):
...
@decorator()
class A(object):
...
@decorator() # class A(object):
class A(object):
...
object = A

View File

@@ -21,6 +21,10 @@ x = "foo {0}" \
"\N{snowman} {0}".format(1)
'{' '0}'.format(1)
# These will not change because we are waiting for libcst to fix this issue:
# https://github.com/Instagram/LibCST/issues/846
print(
'foo{0}'
'bar{1}'.format(1, 2)
@@ -30,5 +34,3 @@ print(
'foo{0}' # ohai\n"
'bar{1}'.format(1, 2)
)
'{' '0}'.format(1)

View File

@@ -13,5 +13,3 @@ f"{0}".format(a)
f"{0}".format(1)
print(f"{0}".format(1))
''.format(1)

View File

@@ -48,12 +48,3 @@ if True: from collections import (
# OK
from a import b
# Ok: `typing_extensions` contains backported improvements.
from typing_extensions import SupportsIndex
# Ok: `typing_extensions` contains backported improvements.
from typing_extensions import NamedTuple
# Ok: `typing_extensions` supports `frozen_default` (backported from 3.12).
from typing_extensions import dataclass_transform

View File

@@ -1,43 +0,0 @@
# Errors
class A():
pass
class A() \
:
pass
class A \
():
pass
@decorator()
class A():
pass
@decorator
class A():
pass
# OK
class A:
pass
class A(A):
pass
class A(metaclass=type):
pass
@decorator()
class A:
pass
@decorator
class A:
pass

View File

@@ -5,11 +5,12 @@ from typing import ClassVar, Sequence
KNOWINGLY_MUTABLE_DEFAULT = []
@dataclass
@dataclass()
class A:
mutable_default: list[int] = []
immutable_annotation: typing.Sequence[int] = []
without_annotation = []
ignored_via_comment: list[int] = [] # noqa: RUF008
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
perfectly_fine: list[int] = field(default_factory=list)
class_variable: typing.ClassVar[list[int]] = []
@@ -20,6 +21,7 @@ class B:
mutable_default: list[int] = []
immutable_annotation: Sequence[int] = []
without_annotation = []
ignored_via_comment: list[int] = [] # noqa: RUF008
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
perfectly_fine: list[int] = field(default_factory=list)
class_variable: ClassVar[list[int]] = []

View File

@@ -28,9 +28,3 @@ def ascii(arg):
f"{ascii(bla)}" # OK
(
f"Member of tuple mismatches type at index {i}. Expected {of_shape_i}. Got "
" intermediary content "
f" that flows {repr(obj)} of type {type(obj)}.{additional_message}" # RUF010
)

View File

@@ -1,13 +0,0 @@
data = ["some", "Data"]
# Ok
{value: value.upper() for value in data}
{value.lower(): value.upper() for value in data}
{v: v*v for v in range(10)}
{(0, "a", v): v*v for v in range(10)} # Tuple with variable
# Errors
{"key": value.upper() for value in data}
{True: value.upper() for value in data}
{0: value.upper() for value in data}
{(1, "a"): value.upper() for value in data} # constant tuple

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