Compare commits

...

19 Commits

Author SHA1 Message Date
Charlie Marsh
cc2110449c Run cargo dev commands 2022-12-24 15:06:12 -05:00
Charlie Marsh
f6ca49e05f Bump version to 0.0.193 2022-12-24 14:56:26 -05:00
Charlie Marsh
9a7331b2e2 Annotate RUF100 messages with unmatched, disabled, and unknown codes (#1365) 2022-12-24 14:55:55 -05:00
Edgar R. M
4888afd423 Generate JSON schema for Ruff options (#1329) 2022-12-24 14:10:22 -05:00
Sawbez
0dc523b081 Add autofix for W605 [InvalidEscapeSequence] (#1361) 2022-12-24 13:46:28 -05:00
Harutaka Kawamura
63772e335d Fix B025 location (#1360) 2022-12-24 12:22:11 -05:00
Reiner Gerecke
7f4ff1e38f Fix false-positive in RET504 when referencing globals (#1358) 2022-12-24 12:02:57 -05:00
Reiner Gerecke
32ebc1d227 Don't trigger E721 when comparing with None (#1356) 2022-12-24 04:45:40 -05:00
Sawbez
4ded155dc0 Add autofix for W292 [NoNewLineAtEndOfFile] (#1354) 2022-12-23 23:14:17 -05:00
Harutaka Kawamura
201e1250de Update RustPython to use the correct BinOp location (#1355) 2022-12-23 22:58:39 -05:00
Reiner Gerecke
102b049a32 Add cache-dir to command-line and pyproject.toml (#1351) 2022-12-23 22:58:29 -05:00
Charlie Marsh
74f49eda64 Bump compatibility to 3.11 (#1352) 2022-12-23 12:12:11 -05:00
Reiner Gerecke
9da3e2cca1 Implement "native literals" check from pyupgrade (#1350) 2022-12-23 11:40:32 -05:00
Charlie Marsh
e290050821 Avoid enabling all EM checks at once (#1349) 2022-12-23 08:25:07 -05:00
Charlie Marsh
bc9ed0a4ef Tweak LSP docs 2022-12-22 22:16:32 -05:00
Charlie Marsh
20b9b44973 Link to ruff-lsp docs 2022-12-22 21:53:11 -05:00
Charlie Marsh
6e5a553235 Update Neovim instructions 2022-12-22 21:52:30 -05:00
Charlie Marsh
2a08a63f17 Add a link to the PyCharm plugin (#1345) 2022-12-22 21:50:24 -05:00
Colin Delahunty
d4290e6721 Update CONTRIBUTING.md (#1344) 2022-12-22 21:04:19 -05:00
85 changed files with 2736 additions and 767 deletions

View File

@@ -42,6 +42,9 @@ jobs:
- run: ./target/release/ruff_dev generate-check-code-prefix
- run: git diff --quiet src/checks_gen.rs || echo "::error file=src/checks_gen.rs::This file is outdated. You may have to rerun 'cargo dev generate-check-code-prefix'."
- run: git diff --exit-code -- README.md src/checks_gen.rs
- run: ./target/release/ruff_dev generate-json-schema
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. You may have to rerun 'cargo dev generate-json-schema'."
- run: git diff --exit-code -- ruff.schema.json
cargo_fmt:
name: "cargo fmt"
@@ -131,7 +134,7 @@ jobs:
override: true
- uses: actions/setup-python@v4
with:
python-version: "3.10"
python-version: "3.11"
- run: pip install maturin
- uses: actions/cache@v3
env:

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.192
rev: v0.0.193
hooks:
- id: ruff

View File

@@ -48,8 +48,8 @@ prior to merging.
There are four phases to adding a new lint rule:
1. Define the rule in `src/checks.rs`.
2. Define the _logic_ for triggering the rule in `src/check_ast.rs` (for AST-based checks),
`src/check_tokens.rs` (for token-based checks), or `src/check_lines.rs` (for text-based checks).
2. Define the _logic_ for triggering the rule in `src/checkers/ast.rs` (for AST-based checks),
`src/checkers/tokens.rs` (for token-based checks), or `src/checkers/lines.rs` (for text-based checks).
3. Add a test fixture.
4. Update the generated files (documentation and generated code).
@@ -105,7 +105,8 @@ You may also want to add the new configuration option to the `flake8-to-ruff` to
responsible for converting `flake8` configuration files to Ruff's TOML format. This logic
lives in `flake8_to_ruff/src/converter.rs`.
To update the documentation for supported configuration options, run `cargo dev generate-options`.
Run `cargo dev generate-options` to update the documentation for supported configuration options,
and `cargo dev generate-json-schema` to update the JSON schema for `tool.ruff` in `pyproject.toml`.
## Release process

66
Cargo.lock generated
View File

@@ -644,6 +644,12 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "dyn-clone"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60"
[[package]]
name = "either"
version = "1.8.0"
@@ -724,7 +730,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.192-dev.0"
version = "0.0.193-dev.0"
dependencies = [
"anyhow",
"clap 4.0.29",
@@ -1847,7 +1853,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.192"
version = "0.0.193"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1888,6 +1894,7 @@ dependencies = [
"rustpython-ast",
"rustpython-common",
"rustpython-parser",
"schemars",
"serde",
"serde_json",
"shellexpand",
@@ -1904,7 +1911,7 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.192"
version = "0.0.193"
dependencies = [
"anyhow",
"clap 4.0.29",
@@ -1916,13 +1923,15 @@ dependencies = [
"rustpython-ast",
"rustpython-common",
"rustpython-parser",
"schemars",
"serde_json",
"strum",
"strum_macros",
]
[[package]]
name = "ruff_macros"
version = "0.0.192"
version = "0.0.193"
dependencies = [
"proc-macro2",
"quote",
@@ -1965,7 +1974,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
dependencies = [
"num-bigint",
"rustpython-common",
@@ -1975,7 +1984,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.0.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
dependencies = [
"ascii",
"cfg-if 1.0.0",
@@ -1998,7 +2007,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
dependencies = [
"bincode",
"bitflags",
@@ -2015,7 +2024,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
dependencies = [
"ahash",
"anyhow",
@@ -2058,6 +2067,30 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "schemars"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307"
dependencies = [
"dyn-clone",
"schemars_derive",
"serde",
"serde_json",
]
[[package]]
name = "schemars_derive"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@@ -2107,10 +2140,21 @@ dependencies = [
]
[[package]]
name = "serde_json"
version = "1.0.89"
name = "serde_derive_internals"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
dependencies = [
"itoa",
"ryu",

View File

@@ -6,7 +6,7 @@ members = [
[package]
name = "ruff"
version = "0.0.192"
version = "0.0.193"
edition = "2021"
rust-version = "1.65.0"
@@ -43,11 +43,12 @@ quick-junit = { version = "0.3.2" }
rayon = { version = "1.5.3" }
regex = { version = "1.6.0" }
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
ruff_macros = { version = "0.0.192", path = "ruff_macros" }
ruff_macros = { version = "0.0.193", path = "ruff_macros" }
rustc-hash = { version = "1.1.0" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "1b6cb170e925a43d605b3fed9f6b878e63e47744" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "1b6cb170e925a43d605b3fed9f6b878e63e47744" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "1b6cb170e925a43d605b3fed9f6b878e63e47744" }
schemars = { version = "0.8.11" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
shellexpand = { version = "3.0.0" }

333
README.md
View File

@@ -17,7 +17,7 @@ An extremely fast Python linter, written in Rust.
- ⚡️ 10-100x faster than existing linters
- 🐍 Installable via `pip`
- 🤝 Python 3.10 compatibility
- 🤝 Python 3.11 compatibility
- 🛠️ `pyproject.toml` support
- 📦 Built-in caching, to avoid re-analyzing unchanged files
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
@@ -162,7 +162,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
```yaml
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.0.192'
rev: 'v0.0.193'
hooks:
- id: ruff
# Respect `exclude` and `extend-exclude` settings.
@@ -318,6 +318,8 @@ Options:
Show violations with source code
--respect-gitignore
Respect file exclusions via `.gitignore` and other standard ignore files
--force-exclude
Enforce exclusions, even for paths passed to Ruff directly on the command-line
--show-files
See the files Ruff will be run against with the current settings
--show-settings
@@ -336,6 +338,8 @@ Options:
The name of the file when passing it through stdin
--explain <EXPLAIN>
Explain a rule
--cache-dir <CACHE_DIR>
Path to the cache directory
-h, --help
Print help information
-V, --version
@@ -536,8 +540,8 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
| E743 | AmbiguousFunctionName | Ambiguous function name: `...` | |
| E902 | IOError | IOError: `...` | |
| E999 | SyntaxError | SyntaxError: `...` | |
| W292 | NoNewLineAtEndOfFile | No newline at end of file | |
| W605 | InvalidEscapeSequence | Invalid escape sequence: '\c' | |
| W292 | NoNewLineAtEndOfFile | No newline at end of file | 🛠 |
| W605 | InvalidEscapeSequence | Invalid escape sequence: '\c' | 🛠 |
### mccabe (C90)
@@ -629,6 +633,7 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
| UP015 | RedundantOpenModes | Unnecessary open mode parameters | 🛠 |
| UP016 | RemoveSixCompat | Unnecessary `six` compatibility usage | 🛠 |
| UP017 | DatetimeTimezoneUTC | Use `datetime.UTC` alias | 🛠 |
| UP018 | NativeLiterals | Unnecessary call to `str` and `bytes` | 🛠 |
### pep8-naming (N)
@@ -952,7 +957,7 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI.
| RUF001 | AmbiguousUnicodeCharacterString | String contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
| RUF002 | AmbiguousUnicodeCharacterDocstring | Docstring contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
| RUF003 | AmbiguousUnicodeCharacterComment | Comment contains ambiguous unicode character '𝐁' (did you mean 'B'?) | |
| RUF100 | UnusedNOQA | Unused `noqa` directive | 🛠 |
| RUF100 | UnusedNOQA | Unused blanket `noqa` directive | 🛠 |
<!-- End auto-generated sections. -->
@@ -1018,19 +1023,17 @@ local configs = require 'lspconfig.configs'
if not configs.ruff_lsp then
configs.ruff_lsp = {
default_config = {
cmd = { "ruff-lsp" },
filetypes = {'python'},
root_dir = require('lspconfig').util.find_git_ancestor,
settings = {
ruff_lsp = {
-- Any extra CLI arguments for `ruff` go here.
args = {}
cmd = { 'ruff-lsp' },
filetypes = { 'python' },
root_dir = require('lspconfig').util.find_git_ancestor,
init_options = {
settings = {
args = {}
}
}
}
}
}
end
require('lspconfig').ruff_lsp.setup {
on_attach = on_attach,
}
@@ -1040,6 +1043,8 @@ Upon successful installation, you should see Ruff's diagnostics surfaced directl
![Code Actions available in Neovim](https://user-images.githubusercontent.com/1309177/208278707-25fa37e4-079d-4597-ad35-b95dba066960.png)
To use `ruff-lsp` with other editors, including Sublime Text and Helix, see the [`ruff-lsp` documentation](https://github.com/charliermarsh/ruff-lsp#installation-and-usage).
### Language Server Protocol (Unofficial)
Ruff is also available as the [`python-lsp-ruff`](https://github.com/python-lsp/python-lsp-ruff)
@@ -1078,24 +1083,15 @@ require'lspconfig'.pylsp.setup {
}
```
### PyCharm
Ruff can be installed as an [External Tool](https://www.jetbrains.com/help/pycharm/configuring-third-party-tools.html)
in PyCharm. Open the Preferences pane, then navigate to "Tools", then "External Tools". From there,
add a new tool with the following configuration:
![Install Ruff as an External Tool](https://user-images.githubusercontent.com/1309177/193155720-336e43f0-1a8d-46b4-bc12-e60f9ae01f7e.png)
Ruff should then appear as a runnable action:
![Ruff as a runnable action](https://user-images.githubusercontent.com/1309177/193156026-732b0aaf-3dd9-4549-9b4d-2de6d2168a33.png)
### Vim & Neovim
Ruff can be integrated into any editor that supports the Language Server Protocol via [`ruff-lsp`](https://github.com/charliermarsh/ruff-lsp)
(see: [Language Server Protocol](#language-server-protocol-official)).
(see: [Language Server Protocol](#language-server-protocol-official)), including Vim and Neovim.
Ruff is also available as part of the [coc-pyright](https://github.com/fannheyward/coc-pyright)
It's recommended that you use [`ruff-lsp`](https://github.com/charliermarsh/ruff-lsp), the
officially supported LSP server for Ruff.
However, Ruff is also available as part of the [coc-pyright](https://github.com/fannheyward/coc-pyright)
extension for `coc.nvim`.
<details>
@@ -1112,7 +1108,6 @@ tools:
format-command: 'ruff --stdin-filename ${INPUT} --config ~/myconfigs/linters/ruff.toml --fix --exit-zero --quiet -'
format-stdin: true
```
</details>
<details>
@@ -1149,9 +1144,26 @@ null_ls.setup({
}
})
```
</details>
### PyCharm (External Tool)
Ruff can be installed as an [External Tool](https://www.jetbrains.com/help/pycharm/configuring-third-party-tools.html)
in PyCharm. Open the Preferences pane, then navigate to "Tools", then "External Tools". From there,
add a new tool with the following configuration:
![Install Ruff as an External Tool](https://user-images.githubusercontent.com/1309177/193155720-336e43f0-1a8d-46b4-bc12-e60f9ae01f7e.png)
Ruff should then appear as a runnable action:
![Ruff as a runnable action](https://user-images.githubusercontent.com/1309177/193156026-732b0aaf-3dd9-4549-9b4d-2de6d2168a33.png)
### PyCharm (Unofficial)
Ruff is also available as the [Ruff](https://plugins.jetbrains.com/plugin/20574-ruff) plugin on the
IntelliJ Marketplace (maintained by @koxudaxi).
### GitHub Actions
GitHub Actions has everything you need to run Ruff out-of-the-box:
@@ -1167,7 +1179,7 @@ jobs:
- name: Install Python
uses: actions/setup-python@v4
with:
python-version: "3.10"
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
@@ -1225,7 +1237,7 @@ natively, including:
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (17/33)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (18/33)
- [`yesqa`](https://github.com/asottile/yesqa)
Note that, in some cases, Ruff uses different error code prefixes than would be found in the
@@ -1569,8 +1581,8 @@ Summary
#### [`allowed-confusables`](#allowed-confusables)
A list of allowed "confusable" Unicode characters to ignore when enforcing `RUF001`,
`RUF002`, and `RUF003`.
A list of allowed "confusable" Unicode characters to ignore when
enforcing `RUF001`, `RUF002`, and `RUF003`.
**Default value**: `[]`
@@ -1587,11 +1599,37 @@ allowed-confusables = ["", "ρ", ""]
---
#### [`cache-dir`](#cache-dir)
A path to the cache directory.
By default, Ruff stores cache results in a `.ruff_cache` directory in
the current project root.
However, Ruff will also respect the `RUFF_CACHE_DIR` environment
variable, which takes precedence over that default.
This setting will override even the `RUFF_CACHE_DIR` environment
variable, if set.
**Default value**: `.ruff_cache`
**Type**: `PathBuf`
**Example usage**:
```toml
[tool.ruff]
cache-dir = "~/.cache/ruff"
```
---
#### [`dummy-variable-rgx`](#dummy-variable-rgx)
A regular expression used to identify "dummy" variables, or those which should be
ignored when evaluating (e.g.) unused-variable checks. The default expression matches
`_`, `__`, and `_var`, but not `_var_`.
A regular expression used to identify "dummy" variables, or those which
should be ignored when evaluating (e.g.) unused-variable checks. The
default expression matches `_`, `__`, and `_var`, but not `_var_`.
**Default value**: `"^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"`
@@ -1613,15 +1651,16 @@ A list of file patterns to exclude from linting.
Exclusions are based on globs, and can be either:
- Single-path patterns, like `.mypy_cache` (to exclude any directory named `.mypy_cache` in the
tree), `foo.py` (to exclude any file named `foo.py`), or `foo_*.py` (to exclude any file matching
`foo_*.py` ).
- Relative patterns, like `directory/foo.py` (to exclude that specific file) or `directory/*.py`
(to exclude any Python files in `directory`). Note that these paths are relative to the
project root (e.g., the directory containing your `pyproject.toml`).
- Single-path patterns, like `.mypy_cache` (to exclude any directory
named `.mypy_cache` in the tree), `foo.py` (to exclude any file named
`foo.py`), or `foo_*.py` (to exclude any file matching `foo_*.py` ).
- Relative patterns, like `directory/foo.py` (to exclude that specific
file) or `directory/*.py` (to exclude any Python files in
`directory`). Note that these paths are relative to the project root
(e.g., the directory containing your `pyproject.toml`).
Note that you'll typically want to use [`extend-exclude`](#extend-exclude) to modify
the excluded paths.
Note that you'll typically want to use
[`extend-exclude`](#extend-exclude) to modify the excluded paths.
**Default value**: `[".bzr", ".direnv", ".eggs", ".git", ".hg", ".mypy_cache", ".nox", ".pants.d", ".ruff_cache", ".svn", ".tox", ".venv", "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", "venv"]`
@@ -1638,12 +1677,13 @@ exclude = [".venv"]
#### [`extend`](#extend)
A path to a local `pyproject.toml` file to merge into this configuration. User home
directory and environment variables will be expanded.
A path to a local `pyproject.toml` file to merge into this
configuration. User home directory and environment variables will be
expanded.
To resolve the current `pyproject.toml` file, Ruff will first resolve this base
configuration file, then merge in any properties defined in the current configuration
file.
To resolve the current `pyproject.toml` file, Ruff will first resolve
this base configuration file, then merge in any properties defined
in the current configuration file.
**Default value**: `None`
@@ -1663,7 +1703,8 @@ line-length = 100
#### [`extend-exclude`](#extend-exclude)
A list of file patterns to omit from linting, in addition to those specified by `exclude`.
A list of file patterns to omit from linting, in addition to those
specified by `exclude`.
**Default value**: `[]`
@@ -1681,7 +1722,8 @@ extend-exclude = ["tests", "src/bad.py"]
#### [`extend-ignore`](#extend-ignore)
A list of check code prefixes to ignore, in addition to those specified by `ignore`.
A list of check code prefixes to ignore, in addition to those specified
by `ignore`.
**Default value**: `[]`
@@ -1699,7 +1741,8 @@ extend-ignore = ["F841"]
#### [`extend-select`](#extend-select)
A list of check code prefixes to enable, in addition to those specified by `select`.
A list of check code prefixes to enable, in addition to those specified
by `select`.
**Default value**: `[]`
@@ -1717,9 +1760,10 @@ extend-select = ["B", "Q"]
#### [`external`](#external)
A list of check codes that are unsupported by Ruff, but should be preserved when (e.g.)
validating `# noqa` directives. Useful for retaining `# noqa` directives that cover plugins not
yet implemented in Ruff.
A list of check codes that are unsupported by Ruff, but should be
preserved when (e.g.) validating `# noqa` directives. Useful for
retaining `# noqa` directives that cover plugins not yet implemented
in Ruff.
**Default value**: `[]`
@@ -1774,14 +1818,16 @@ fixable = ["E", "F"]
#### [`force-exclude`](#force-exclude)
Whether to enforce `exclude` and `extend-exclude` patterns, even for paths that are
passed to Ruff explicitly. Typically, Ruff will lint any paths passed in directly, even
if they would typically be excluded. Setting `force-exclude = true` will cause Ruff to
Whether to enforce `exclude` and `extend-exclude` patterns, even for
paths that are passed to Ruff explicitly. Typically, Ruff will lint
any paths passed in directly, even if they would typically be
excluded. Setting `force-exclude = true` will cause Ruff to
respect these exclusions unequivocally.
This is useful for [`pre-commit`](https://pre-commit.com/), which explicitly passes all
changed files to the [`ruff-pre-commit`](https://github.com/charliermarsh/ruff-pre-commit)
plugin, regardless of whether they're marked as excluded by Ruff's own settings.
plugin, regardless of whether they're marked as excluded by Ruff's own
settings.
**Default value**: `false`
@@ -1798,9 +1844,10 @@ force-exclude = true
#### [`format`](#format)
The style in which violation messages should be formatted: `"text"` (default),
`"grouped"` (group messages by file), `"json"` (machine-readable), `"junit"`
(machine-readable XML), or `"github"` (GitHub Actions annotations).
The style in which violation messages should be formatted: `"text"`
(default), `"grouped"` (group messages by file), `"json"`
(machine-readable), `"junit"` (machine-readable XML), or `"github"`
(GitHub Actions annotations).
**Default value**: `"text"`
@@ -1818,11 +1865,13 @@ format = "grouped"
#### [`ignore`](#ignore)
A list of check code prefixes to ignore. Prefixes can specify exact checks (like
`F841`), entire categories (like `F`), or anything in between.
A list of check code prefixes to ignore. Prefixes can specify exact
checks (like `F841`), entire categories (like `F`), or anything in
between.
When breaking ties between enabled and disabled checks (via `select` and `ignore`,
respectively), more specific prefixes override less specific prefixes.
When breaking ties between enabled and disabled checks (via `select` and
`ignore`, respectively), more specific prefixes override less
specific prefixes.
**Default value**: `[]`
@@ -1840,10 +1889,11 @@ ignore = ["F841"]
#### [`ignore-init-module-imports`](#ignore-init-module-imports)
Avoid automatically removing unused imports in `__init__.py` files. Such imports will
still be +flagged, but with a dedicated message suggesting that the import is either
added to the module' +`__all__` symbol, or re-exported with a redundant alias (e.g.,
`import os as os`).
Avoid automatically removing unused imports in `__init__.py` files. Such
imports will still be +flagged, but with a dedicated message
suggesting that the import is either added to the module' +`__all__`
symbol, or re-exported with a redundant alias (e.g., `import os as
os`).
**Default value**: `false`
@@ -1860,7 +1910,8 @@ ignore-init-module-imports = true
#### [`line-length`](#line-length)
The line length to use when enforcing long-lines violations (like E501).
The line length to use when enforcing long-lines violations (like
`E501`).
**Default value**: `88`
@@ -1878,8 +1929,8 @@ line-length = 120
#### [`per-file-ignores`](#per-file-ignores)
A list of mappings from file pattern to check code prefixes to exclude, when considering
any matching files.
A list of mappings from file pattern to check code prefixes to exclude,
when considering any matching files.
**Default value**: `{}`
@@ -1899,8 +1950,9 @@ any matching files.
#### [`respect-gitignore`](#respect-gitignore)
Whether to automatically exclude files that are ignored by `.ignore`, `.gitignore`,
`.git/info/exclude`, and global `gitignore` files. Enabled by default.
Whether to automatically exclude files that are ignored by `.ignore`,
`.gitignore`, `.git/info/exclude`, and global `gitignore` files.
Enabled by default.
**Default value**: `true`
@@ -1917,11 +1969,13 @@ respect_gitignore = false
#### [`select`](#select)
A list of check code prefixes to enable. Prefixes can specify exact checks (like
`F841`), entire categories (like `F`), or anything in between.
A list of check code prefixes to enable. Prefixes can specify exact
checks (like `F841`), entire categories (like `F`), or anything in
between.
When breaking ties between enabled and disabled checks (via `select` and `ignore`,
respectively), more specific prefixes override less specific prefixes.
When breaking ties between enabled and disabled checks (via `select` and
`ignore`, respectively), more specific prefixes override less
specific prefixes.
**Default value**: `["E", "F"]`
@@ -1939,8 +1993,8 @@ select = ["E", "F", "B", "Q"]
#### [`show-source`](#show-source)
Whether to show source code snippets when reporting lint error violations (overridden by
the `--show-source` command-line flag).
Whether to show source code snippets when reporting lint error
violations (overridden by the `--show-source` command-line flag).
**Default value**: `false`
@@ -1958,7 +2012,8 @@ show-source = true
#### [`src`](#src)
The source code paths to consider, e.g., when resolving first- vs. third-party imports.
The source code paths to consider, e.g., when resolving first- vs.
third-party imports.
As an example: given a Python package structure like:
@@ -1972,13 +2027,15 @@ my_package/
bar.py
```
The `src` directory should be included in `source` (e.g., `source = ["src"]`), such that
when resolving imports, `my_package.foo` is considered a first-party import.
The `src` directory should be included in `source` (e.g., `source =
["src"]`), such that when resolving imports, `my_package.foo` is
considered a first-party import.
This field supports globs. For example, if you have a series of Python packages in
a `python_modules` directory, `src = ["python_modules/*"]` would expand to incorporate
all of the packages in that directory. User home directory and environment variables
will also be expanded.
This field supports globs. For example, if you have a series of Python
packages in a `python_modules` directory, `src =
["python_modules/*"]` would expand to incorporate all of the
packages in that directory. User home directory and environment
variables will also be expanded.
**Default value**: `["."]`
@@ -1996,9 +2053,10 @@ src = ["src", "test"]
#### [`target-version`](#target-version)
The Python version to target, e.g., when considering automatic code upgrades, like
rewriting type annotations. Note that the target version will _not_ be inferred from the
_current_ Python version, and instead must be specified explicitly (as seen below).
The Python version to target, e.g., when considering automatic code
upgrades, like rewriting type annotations. Note that the target
version will _not_ be inferred from the _current_ Python version,
and instead must be specified explicitly (as seen below).
**Default value**: `"py310"`
@@ -2036,7 +2094,8 @@ unfixable = ["F401"]
#### [`allow-star-arg-any`](#allow-star-arg-any)
Whether to suppress `ANN401` for dynamically typed `*args` and `**kwargs` arguments.
Whether to suppress `ANN401` for dynamically typed `*args` and
`**kwargs` arguments.
**Default value**: `false`
@@ -2053,8 +2112,8 @@ allow-star-arg-any = true
#### [`mypy-init-return`](#mypy-init-return)
Whether to allow the omission of a return type hint for `__init__` if at least one
argument is annotated.
Whether to allow the omission of a return type hint for `__init__` if at
least one argument is annotated.
**Default value**: `false`
@@ -2071,8 +2130,8 @@ mypy-init-return = true
#### [`suppress-dummy-args`](#suppress-dummy-args)
Whether to suppress `ANN000`-level errors for arguments matching the "dummy" variable
regex (like `_`).
Whether to suppress `ANN000`-level errors for arguments matching the
"dummy" variable regex (like `_`).
**Default value**: `false`
@@ -2089,11 +2148,12 @@ suppress-dummy-args = true
#### [`suppress-none-returning`](#suppress-none-returning)
Whether to suppress `ANN200`-level errors for functions that meet either of the
following criteria:
Whether to suppress `ANN200`-level errors for functions that meet either
of the following criteria:
- Contain no `return` statement.
- Explicit `return` statement(s) all return `None` (explicitly or implicitly).
- Explicit `return` statement(s) all return `None` (explicitly or
implicitly).
**Default value**: `false`
@@ -2112,8 +2172,8 @@ suppress-none-returning = true
#### [`extend-immutable-calls`](#extend-immutable-calls)
Additional callable functions to consider "immutable" when evaluating, e.g.,
`no-mutable-default-argument` checks (`B006`).
Additional callable functions to consider "immutable" when evaluating,
e.g., `no-mutable-default-argument` checks (`B006`).
**Default value**: `[]`
@@ -2152,7 +2212,8 @@ max-string-length = 20
#### [`aliases`](#aliases)
The conventional aliases for imports. These aliases can be extended by the `extend_aliases` option.
The conventional aliases for imports. These aliases can be extended by
the `extend_aliases` option.
**Default value**: `{"altair": "alt", "matplotlib.pyplot": "plt", "numpy": "np", "pandas": "pd", "seaborn": "sns"}`
@@ -2174,7 +2235,8 @@ seaborn = "sns"
#### [`extend-aliases`](#extend-aliases)
A mapping of modules to their conventional import aliases. These aliases will be added to the `aliases` mapping.
A mapping of modules to their conventional import aliases. These aliases
will be added to the `aliases` mapping.
**Default value**: `{}`
@@ -2194,8 +2256,8 @@ A mapping of modules to their conventional import aliases. These aliases will be
#### [`avoid-escape`](#avoid-escape)
Whether to avoid using single quotes if a string contains single quotes, or vice-versa
with double quotes, as per [PEP8](https://peps.python.org/pep-0008/#string-quotes).
Whether to avoid using single quotes if a string contains single quotes,
or vice-versa with double quotes, as per [PEP8](https://peps.python.org/pep-0008/#string-quotes).
This minimizes the need to escape quotation marks within strings.
**Default value**: `true`
@@ -2214,7 +2276,8 @@ avoid-escape = false
#### [`docstring-quotes`](#docstring-quotes)
Quote style to prefer for docstrings (either "single" (`'`) or "double" (`"`)).
Quote style to prefer for docstrings (either "single" (`'`) or "double"
(`"`)).
**Default value**: `"double"`
@@ -2231,7 +2294,8 @@ docstring-quotes = "single"
#### [`inline-quotes`](#inline-quotes)
Quote style to prefer for inline strings (either "single" (`'`) or "double" (`"`)).
Quote style to prefer for inline strings (either "single" (`'`) or
"double" (`"`)).
**Default value**: `"double"`
@@ -2248,7 +2312,8 @@ inline-quotes = "single"
#### [`multiline-quotes`](#multiline-quotes)
Quote style to prefer for multiline strings (either "single" (`'`) or "double" (`"`)).
Quote style to prefer for multiline strings (either "single" (`'`) or
"double" (`"`)).
**Default value**: `"double"`
@@ -2267,8 +2332,8 @@ multiline-quotes = "single"
#### [`ban-relative-imports`](#ban-relative-imports)
Whether to ban all relative imports (`"all"`), or only those imports that extend into
the parent module and beyond (`"parents"`).
Whether to ban all relative imports (`"all"`), or only those imports
that extend into the parent module and beyond (`"parents"`).
**Default value**: `"parents"`
@@ -2325,8 +2390,8 @@ combine-as-imports = true
#### [`extra-standard-library`](#extra-standard-library)
A list of modules to consider standard-library, in addition to those known to Ruff in
advance.
A list of modules to consider standard-library, in addition to those
known to Ruff in advance.
**Default value**: `[]`
@@ -2343,9 +2408,10 @@ extra-standard-library = ["path"]
#### [`force-wrap-aliases`](#force-wrap-aliases)
Force `import from` statements with multiple members and at least one alias (e.g.,
`import A as B`) to wrap such that every line contains exactly one member. For example,
this formatting would be retained, rather than condensing to a single line:
Force `import from` statements with multiple members and at least one
alias (e.g., `import A as B`) to wrap such that every line contains
exactly one member. For example, this formatting would be retained,
rather than condensing to a single line:
```py
from .utils import (
@@ -2354,9 +2420,10 @@ from .utils import (
)
```
Note that this setting is only effective when combined with `combine-as-imports = true`.
When `combine-as-imports` isn't enabled, every aliased `import from` will be given its
own line, in which case, wrapping is not necessary.
Note that this setting is only effective when combined with
`combine-as-imports = true`. When `combine-as-imports` isn't
enabled, every aliased `import from` will be given its own line, in
which case, wrapping is not necessary.
**Default value**: `false`
@@ -2374,8 +2441,8 @@ combine-as-imports = true
#### [`known-first-party`](#known-first-party)
A list of modules to consider first-party, regardless of whether they can be identified
as such via introspection of the local filesystem.
A list of modules to consider first-party, regardless of whether they
can be identified as such via introspection of the local filesystem.
**Default value**: `[]`
@@ -2392,8 +2459,8 @@ known-first-party = ["src"]
#### [`known-third-party`](#known-third-party)
A list of modules to consider third-party, regardless of whether they can be identified
as such via introspection of the local filesystem.
A list of modules to consider third-party, regardless of whether they
can be identified as such via introspection of the local filesystem.
**Default value**: `[]`
@@ -2432,9 +2499,10 @@ max-complexity = 5
#### [`classmethod-decorators`](#classmethod-decorators)
A list of decorators that, when applied to a method, indicate that the method should be
treated as a class method. For example, Ruff will expect that any method decorated by a
decorator in this list takes a `cls` argument as its first argument.
A list of decorators that, when applied to a method, indicate that the
method should be treated as a class method. For example, Ruff will
expect that any method decorated by a decorator in this list takes a
`cls` argument as its first argument.
**Default value**: `["classmethod"]`
@@ -2469,9 +2537,10 @@ ignore-names = ["callMethod"]
#### [`staticmethod-decorators`](#staticmethod-decorators)
A list of decorators that, when applied to a method, indicate that the method should be
treated as a static method. For example, Ruff will expect that any method decorated by a
decorator in this list has no `self` or `cls` argument.
A list of decorators that, when applied to a method, indicate that the
method should be treated as a static method. For example, Ruff will
expect that any method decorated by a decorator in this list has no
`self` or `cls` argument.
**Default value**: `["staticmethod"]`
@@ -2491,7 +2560,11 @@ staticmethod-decorators = ["staticmethod", "stcmthd"]
#### [`keep-runtime-typing`](#keep-runtime-typing)
Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604 (`Optional[str]` -> `str | None`) rewrites even if a file imports `from __future__ import annotations`. Note that this setting is only applicable when the target Python version is below 3.9 and 3.10 respectively.
Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604
(`Optional[str]` -> `str | None`) rewrites even if a file imports `from
__future__ import annotations`. Note that this setting is only
applicable when the target Python version is below 3.9 and 3.10
respectively.
**Default value**: `false`

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.192"
version = "0.0.193"
dependencies = [
"anyhow",
"clap",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.192"
version = "0.0.193"
dependencies = [
"anyhow",
"bincode",
@@ -2028,7 +2028,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
dependencies = [
"num-bigint",
"rustpython-common",
@@ -2038,7 +2038,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.0.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
dependencies = [
"ascii",
"cfg-if 1.0.0",
@@ -2061,7 +2061,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
dependencies = [
"bincode",
"bitflags",
@@ -2078,7 +2078,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
source = "git+https://github.com/RustPython/RustPython.git?rev=1b6cb170e925a43d605b3fed9f6b878e63e47744#1b6cb170e925a43d605b3fed9f6b878e63e47744"
dependencies = [
"ahash",
"anyhow",

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.192-dev.0"
version = "0.0.193-dev.0"
edition = "2021"
[lib]

View File

@@ -12,6 +12,7 @@ classifiers = [
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Quality Assurance",

View File

@@ -67,10 +67,7 @@ pub fn convert(
}
let from_codes = plugin::infer_plugins_from_codes(&referenced_codes);
if !from_codes.is_empty() {
eprintln!(
"Inferred plugins from referenced check codes: {:#?}",
from_codes
);
eprintln!("Inferred plugins from referenced check codes: {from_codes:#?}");
}
from_options.into_iter().chain(from_codes).collect()
}),
@@ -315,6 +312,7 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
cache_dir: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
@@ -369,6 +367,7 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
cache_dir: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
@@ -423,6 +422,7 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
cache_dir: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
@@ -477,6 +477,7 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
cache_dir: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
@@ -531,6 +532,7 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
cache_dir: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
@@ -629,6 +631,7 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
cache_dir: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
@@ -684,6 +687,7 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
cache_dir: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,

View File

@@ -12,6 +12,7 @@ classifiers = [
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Quality Assurance",

View File

@@ -237,3 +237,15 @@ def close(self):
any_failed = True
report(traceback.format_exc())
return any_failed
def global_assignment():
global X
X = 1
return X
def nonlocal_assignment():
X = 1
def inner():
nonlocal X
X = 1
return X

View File

@@ -15,7 +15,7 @@ import types
if type(res) is not types.ListType:
pass
#: E721
assert type(res) == type(False) or type(res) == type(None)
assert type(res) == type(False)
#: E721
assert type(res) == type([])
#: E721
@@ -52,3 +52,5 @@ if isinstance(res, types.MethodType):
pass
if type(a) != type(b) or type(a) == type(ccc):
pass
assert type(res) == type(None)

View File

@@ -1,2 +1,2 @@
def fn() -> None:
pass
print("Newline present (no W292)")

View File

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,25 @@
# These remain unchanged
str(1)
str(*a)
str("foo", *a)
str(**k)
str("foo", **k)
str("foo", encoding="UTF-8")
str("foo"
"bar")
bytes("foo", encoding="UTF-8")
bytes(*a)
bytes("foo", *a)
bytes("foo", **a)
bytes(b"foo"
b"bar")
# These become string or byte literals
str()
str("foo")
str("""
foo""")
bytes()
bytes(b"foo")
bytes(b"""
foo""")

View File

@@ -15,8 +15,8 @@ def f() -> None:
# Invalid
d = 1 # noqa: F841, E501
# Invalid (and unimplemented)
d = 1 # noqa: F841, W191
# Invalid (and unimplemented or not enabled)
d = 1 # noqa: F841, W191, F821
# Invalid (but external)
d = 1 # noqa: F841, V101

1194
ruff.schema.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_dev"
version = "0.0.192"
version = "0.0.193"
edition = "2021"
[dependencies]
@@ -11,8 +11,10 @@ itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
once_cell = { version = "1.16.0" }
ruff = { path = ".." }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "1b6cb170e925a43d605b3fed9f6b878e63e47744" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "1b6cb170e925a43d605b3fed9f6b878e63e47744" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "1b6cb170e925a43d605b3fed9f6b878e63e47744" }
schemars = { version = "0.8.11" }
serde_json = {version="1.0.91"}
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }

View File

@@ -65,7 +65,8 @@ pub fn main(cli: &Cli) -> Result<()> {
.derive("Ord")
.derive("Clone")
.derive("Serialize")
.derive("Deserialize");
.derive("Deserialize")
.derive("JsonSchema");
for prefix in prefix_to_codes.keys() {
gen = gen.push_variant(Variant::new(prefix.to_string()));
}
@@ -138,8 +139,7 @@ pub fn main(cli: &Cli) -> Result<()> {
_ => panic!("Invalid prefix: {prefix}"),
};
gen = gen.line(format!(
"CheckCodePrefix::{prefix} => SuffixLength::{},",
specificity
"CheckCodePrefix::{prefix} => SuffixLength::{specificity},"
));
}
gen.line("}");
@@ -152,6 +152,8 @@ pub fn main(cli: &Cli) -> Result<()> {
output.push('\n');
output.push_str("use colored::Colorize;");
output.push('\n');
output.push_str("use schemars::JsonSchema;");
output.push('\n');
output.push_str("use serde::{Deserialize, Serialize};");
output.push('\n');
output.push_str("use strum_macros::{AsRefStr, EnumString};");

View File

@@ -0,0 +1,30 @@
use std::fs;
use std::path::PathBuf;
use anyhow::Result;
use clap::Args;
use ruff::settings::options::Options;
use schemars::schema_for;
#[derive(Args)]
pub struct Cli {
/// Write the generated table to stdout (rather than to `ruff.schema.json`).
#[arg(long)]
dry_run: bool,
}
pub fn main(cli: &Cli) -> Result<()> {
let schema = schema_for!(Options);
let schema_string = serde_json::to_string_pretty(&schema).unwrap();
if cli.dry_run {
println!("{schema_string}");
} else {
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("Failed to find root directory")
.join("ruff.schema.json");
fs::write(file, schema_string.as_bytes())?;
}
Ok(())
}

View File

@@ -12,6 +12,7 @@
)]
pub mod generate_check_code_prefix;
pub mod generate_json_schema;
pub mod generate_options;
pub mod generate_rules_table;
pub mod generate_source_code;

View File

@@ -14,8 +14,8 @@
use anyhow::Result;
use clap::{Parser, Subcommand};
use ruff_dev::{
generate_check_code_prefix, generate_options, generate_rules_table, generate_source_code,
print_ast, print_cst, print_tokens,
generate_check_code_prefix, generate_json_schema, generate_options, generate_rules_table,
generate_source_code, print_ast, print_cst, print_tokens,
};
#[derive(Parser)]
@@ -30,6 +30,8 @@ struct Cli {
enum Commands {
/// Generate the `CheckCodePrefix` enum.
GenerateCheckCodePrefix(generate_check_code_prefix::Cli),
/// Generate JSON schema for the TOML configuration file.
GenerateJSONSchema(generate_json_schema::Cli),
/// Generate a Markdown-compatible table of supported lint rules.
GenerateRulesTable(generate_rules_table::Cli),
/// Generate a Markdown-compatible listing of configuration options.
@@ -48,6 +50,7 @@ fn main() -> Result<()> {
let cli = Cli::parse();
match &cli.command {
Commands::GenerateCheckCodePrefix(args) => generate_check_code_prefix::main(args)?,
Commands::GenerateJSONSchema(args) => generate_json_schema::main(args)?,
Commands::GenerateRulesTable(args) => generate_rules_table::main(args)?,
Commands::GenerateSourceCode(args) => generate_source_code::main(args)?,
Commands::GenerateOptions(args) => generate_options::main(args)?,

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_macros"
version = "0.0.192"
version = "0.0.193"
edition = "2021"
[lib]

View File

@@ -13,13 +13,14 @@
use quote::{quote, quote_spanned};
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::token::Comma;
use syn::{
parse_macro_input, AngleBracketedGenericArguments, Attribute, Data, DataStruct, DeriveInput,
Field, Fields, Lit, LitStr, Path, PathArguments, PathSegment, Token, Type, TypePath,
};
#[proc_macro_derive(ConfigurationOptions, attributes(option, option_group))]
#[proc_macro_derive(ConfigurationOptions, attributes(option, doc, option_group))]
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
@@ -39,11 +40,28 @@ fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
let mut output = vec![];
for field in fields.named.iter() {
if let Some(attr) = field.attrs.iter().find(|a| a.path.is_ident("option")) {
output.push(handle_option(field, attr)?);
let docs: Vec<&Attribute> = field
.attrs
.iter()
.filter(|attr| attr.path.is_ident("doc"))
.collect();
if docs.is_empty() {
return Err(syn::Error::new(
field.span(),
"Missing documentation for field",
));
}
if let Some(attr) = field.attrs.iter().find(|attr| attr.path.is_ident("option")) {
output.push(handle_option(field, attr, docs)?);
};
if field.attrs.iter().any(|a| a.path.is_ident("option_group")) {
if field
.attrs
.iter()
.any(|attr| attr.path.is_ident("option_group"))
{
output.push(handle_option_group(field)?);
};
}
@@ -70,8 +88,10 @@ fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
/// deriving `ConfigurationOptions`, create code that calls retrieves options
/// from that group: `Foobar::get_available_options()`
fn handle_option_group(field: &Field) -> syn::Result<proc_macro2::TokenStream> {
// unwrap is safe because we're only going over named fields
let ident = field.ident.as_ref().unwrap();
let ident = field
.ident
.as_ref()
.expect("Expected to handle named fields");
match &field.ty {
Type::Path(TypePath {
@@ -103,17 +123,49 @@ fn handle_option_group(field: &Field) -> syn::Result<proc_macro2::TokenStream> {
}
}
/// Parse a `doc` attribute into it a string literal.
fn parse_doc(doc: &Attribute) -> syn::Result<String> {
let doc = doc
.parse_meta()
.map_err(|e| syn::Error::new(doc.span(), e))?;
match doc {
syn::Meta::NameValue(syn::MetaNameValue {
lit: Lit::Str(lit_str),
..
}) => Ok(lit_str.value()),
_ => Err(syn::Error::new(doc.span(), "Expected doc attribute.")),
}
}
/// Parse an `#[option(doc="...", default="...", value_type="...",
/// example="...")]` attribute and return data in the form of an `OptionField`.
fn handle_option(field: &Field, attr: &Attribute) -> syn::Result<proc_macro2::TokenStream> {
// unwrap is safe because we're only going over named fields
let ident = field.ident.as_ref().unwrap();
fn handle_option(
field: &Field,
attr: &Attribute,
docs: Vec<&Attribute>,
) -> syn::Result<proc_macro2::TokenStream> {
// Convert the list of `doc` attributes into a single string.
let doc = textwrap::dedent(
&docs
.into_iter()
.map(parse_doc)
.collect::<syn::Result<Vec<_>>>()?
.join("\n"),
)
.trim_matches('\n')
.to_string();
let ident = field
.ident
.as_ref()
.expect("Expected to handle named fields");
let FieldAttributes {
doc,
default,
value_type,
example,
..
} = attr.parse_args::<FieldAttributes>()?;
let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span());
@@ -130,7 +182,6 @@ fn handle_option(field: &Field, attr: &Attribute) -> syn::Result<proc_macro2::To
#[derive(Debug)]
struct FieldAttributes {
doc: String,
default: String,
value_type: String,
example: String,
@@ -138,8 +189,6 @@ struct FieldAttributes {
impl Parse for FieldAttributes {
fn parse(input: ParseStream) -> syn::Result<Self> {
let doc = _parse_key_value(input, "doc")?;
input.parse::<Comma>()?;
let default = _parse_key_value(input, "default")?;
input.parse::<Comma>()?;
let value_type = _parse_key_value(input, "value_type")?;
@@ -150,7 +199,6 @@ impl Parse for FieldAttributes {
}
Ok(FieldAttributes {
doc: textwrap::dedent(&doc).trim_matches('\n').to_string(),
default,
value_type,
example: textwrap::dedent(&example).trim_matches('\n').to_string(),

View File

@@ -3,7 +3,7 @@ use std::fs;
use std::fs::{create_dir_all, File, Metadata};
use std::hash::{Hash, Hasher};
use std::io::Write;
use std::path::Path;
use std::path::{Path, PathBuf};
use anyhow::Result;
use filetime::FileTime;
@@ -36,8 +36,12 @@ struct CheckResult {
messages: Vec<Message>,
}
fn cache_dir() -> &'static Path {
Path::new(CACHE_DIR.as_ref().map_or(".ruff_cache", String::as_str))
/// Return the cache directory for a given project root. Defers to the
/// `RUFF_CACHE_DIR` environment variable, if set.
pub fn cache_dir(project_root: &Path) -> PathBuf {
CACHE_DIR
.as_ref()
.map_or_else(|| project_root.join(".ruff_cache"), PathBuf::from)
}
fn content_dir() -> &'static Path {
@@ -53,10 +57,8 @@ fn cache_key<P: AsRef<Path>>(path: P, settings: &Settings, autofix: fixer::Mode)
hasher.finish()
}
/// Initialize the cache directory.
pub fn init() -> Result<()> {
let path = cache_dir();
/// Initialize the cache at the specified `Path`.
pub fn init(path: &Path) -> Result<()> {
// Create the cache directories.
create_dir_all(path.join(content_dir()))?;
@@ -75,15 +77,15 @@ pub fn init() -> Result<()> {
Ok(())
}
fn write_sync(key: u64, value: &[u8]) -> Result<(), std::io::Error> {
fn write_sync(cache_dir: &Path, key: u64, value: &[u8]) -> Result<(), std::io::Error> {
fs::write(
cache_dir().join(content_dir()).join(format!("{key:x}")),
cache_dir.join(content_dir()).join(format!("{key:x}")),
value,
)
}
fn read_sync(key: u64) -> Result<Vec<u8>, std::io::Error> {
fs::read(cache_dir().join(content_dir()).join(format!("{key:x}")))
fn read_sync(cache_dir: &Path, key: u64) -> Result<Vec<u8>, std::io::Error> {
fs::read(cache_dir.join(content_dir()).join(format!("{key:x}")))
}
/// Get a value from the cache.
@@ -98,7 +100,7 @@ pub fn get<P: AsRef<Path>>(
return None;
};
let encoded = read_sync(cache_key(path, settings, autofix)).ok()?;
let encoded = read_sync(&settings.cache_dir, cache_key(path, settings, autofix)).ok()?;
let (mtime, messages) = match bincode::deserialize::<CheckResult>(&encoded[..]) {
Ok(CheckResult {
metadata: CacheMetadata { mtime },
@@ -135,6 +137,7 @@ pub fn set<P: AsRef<Path>>(
messages,
};
if let Err(e) = write_sync(
&settings.cache_dir,
cache_key(path, settings, autofix),
&bincode::serialize(&check_result).unwrap(),
) {

View File

@@ -1067,17 +1067,11 @@ where
}
}
if self.settings.enabled.contains(&CheckCode::EM101)
| self.settings.enabled.contains(&CheckCode::EM102)
| self.settings.enabled.contains(&CheckCode::EM103)
|| self.settings.enabled.contains(&CheckCode::EM102)
|| self.settings.enabled.contains(&CheckCode::EM103)
{
if let Some(exc) = exc {
self.add_checks(
flake8_errmsg::checks::check_string_in_exception(
exc,
self.settings.flake8_errmsg.max_string_length,
)
.into_iter(),
);
flake8_errmsg::plugins::string_in_exception(self, exc);
}
}
}
@@ -1157,7 +1151,7 @@ where
if self.settings.enabled.contains(&CheckCode::B014)
|| self.settings.enabled.contains(&CheckCode::B025)
{
flake8_bugbear::plugins::duplicate_exceptions(self, stmt, handlers);
flake8_bugbear::plugins::duplicate_exceptions(self, handlers);
}
if self.settings.enabled.contains(&CheckCode::B013) {
flake8_bugbear::plugins::redundant_tuple_in_exception_handler(self, handlers);
@@ -1650,6 +1644,9 @@ where
if self.settings.enabled.contains(&CheckCode::UP016) {
pyupgrade::plugins::remove_six_compat(self, expr);
}
if self.settings.enabled.contains(&CheckCode::UP018) {
pyupgrade::plugins::native_literals(self, expr, func, args, keywords);
}
// flake8-super
if self.settings.enabled.contains(&CheckCode::UP008) {

View File

@@ -55,7 +55,11 @@ pub fn check_lines(
}
if enforce_no_newline_at_end_of_file {
if let Some(check) = no_newline_at_end_of_file(contents) {
if let Some(check) = no_newline_at_end_of_file(
contents,
matches!(autofix, flags::Autofix::Enabled)
&& settings.fixable.contains(&CheckCode::W292),
) {
checks.push(check);
}
}

View File

@@ -1,11 +1,13 @@
//! `NoQA` enforcement and validation.
use std::str::FromStr;
use nohash_hasher::IntMap;
use rustpython_parser::ast::Location;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checks::{Check, CheckCode, CheckKind, CODE_REDIRECTS};
use crate::checks::{Check, CheckCode, CheckKind, UnusedCodes, CODE_REDIRECTS};
use crate::noqa;
use crate::noqa::{is_file_exempt, Directive};
use crate::settings::{flags, Settings};
@@ -98,18 +100,29 @@ pub fn check_noqa(
}
}
Directive::Codes(spaces, start, end, codes) => {
let mut invalid_codes = vec![];
let mut disabled_codes = vec![];
let mut unknown_codes = vec![];
let mut unmatched_codes = vec![];
let mut valid_codes = vec![];
let mut self_ignore = false;
for code in codes {
let code = CODE_REDIRECTS.get(code).map_or(code, AsRef::as_ref);
if code == CheckCode::RUF100.as_ref() {
self_ignore = true;
break;
}
if matches.contains(&code) || settings.external.contains(code) {
valid_codes.push(code);
} else {
if matches.contains(&code) || settings.external.contains(code) {
valid_codes.push(code);
if let Ok(check_code) = CheckCode::from_str(code) {
if settings.enabled.contains(&check_code) {
unmatched_codes.push(code);
} else {
disabled_codes.push(code);
}
} else {
invalid_codes.push(code);
unknown_codes.push(code);
}
}
}
@@ -118,14 +131,25 @@ pub fn check_noqa(
continue;
}
if !invalid_codes.is_empty() {
if !(disabled_codes.is_empty()
&& unknown_codes.is_empty()
&& unmatched_codes.is_empty())
{
let mut check = Check::new(
CheckKind::UnusedNOQA(Some(
invalid_codes
CheckKind::UnusedNOQA(Some(UnusedCodes {
disabled: disabled_codes
.iter()
.map(|code| (*code).to_string())
.collect(),
)),
unknown: unknown_codes
.iter()
.map(|code| (*code).to_string())
.collect(),
unmatched: unmatched_codes
.iter()
.map(|code| (*code).to_string())
.collect(),
})),
Range {
location: Location::new(row + 1, start),
end_location: Location::new(row + 1, end),

View File

@@ -89,7 +89,11 @@ pub fn check_tokens(
if enforce_invalid_escape_sequence {
if matches!(tok, Tok::String { .. }) {
checks.extend(pycodestyle::checks::invalid_escape_sequence(
locator, start, end,
locator,
start,
end,
matches!(autofix, flags::Autofix::Enabled)
&& settings.fixable.contains(&CheckCode::W605),
));
}
}

View File

@@ -1,5 +1,4 @@
use std::fmt;
use std::str::FromStr;
use itertools::Itertools;
use once_cell::sync::Lazy;
@@ -226,6 +225,7 @@ pub enum CheckCode {
UP015,
UP016,
UP017,
UP018,
// pydocstyle
D100,
D101,
@@ -635,6 +635,13 @@ impl fmt::Display for Branch {
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct UnusedCodes {
pub unknown: Vec<String>,
pub disabled: Vec<String>,
pub unmatched: Vec<String>,
}
#[derive(AsRefStr, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum CheckKind {
// pycodestyle errors
@@ -829,6 +836,7 @@ pub enum CheckKind {
RedundantOpenModes,
RemoveSixCompat,
DatetimeTimezoneUTC,
NativeLiterals,
// pydocstyle
BlankLineAfterLastSection(String),
BlankLineAfterSection(String),
@@ -941,7 +949,7 @@ pub enum CheckKind {
AmbiguousUnicodeCharacterString(char, char),
AmbiguousUnicodeCharacterDocstring(char, char),
AmbiguousUnicodeCharacterComment(char, char),
UnusedNOQA(Option<Vec<String>>),
UnusedNOQA(Option<UnusedCodes>),
// flake8-datetimez
CallDatetimeWithoutTzinfo,
CallDatetimeToday,
@@ -1203,6 +1211,7 @@ impl CheckCode {
CheckCode::UP015 => CheckKind::RedundantOpenModes,
CheckCode::UP016 => CheckKind::RemoveSixCompat,
CheckCode::UP017 => CheckKind::DatetimeTimezoneUTC,
CheckCode::UP018 => CheckKind::NativeLiterals,
// pydocstyle
CheckCode::D100 => CheckKind::PublicModule,
CheckCode::D101 => CheckKind::PublicClass,
@@ -1621,6 +1630,7 @@ impl CheckCode {
CheckCode::UP015 => CheckCategory::Pyupgrade,
CheckCode::UP016 => CheckCategory::Pyupgrade,
CheckCode::UP017 => CheckCategory::Pyupgrade,
CheckCode::UP018 => CheckCategory::Pyupgrade,
CheckCode::W292 => CheckCategory::Pycodestyle,
CheckCode::W605 => CheckCategory::Pycodestyle,
CheckCode::YTT101 => CheckCategory::Flake82020,
@@ -1642,9 +1652,9 @@ impl CheckKind {
pub fn code(&self) -> &'static CheckCode {
match self {
// pycodestyle errors
CheckKind::AmbiguousClassName(_) => &CheckCode::E742,
CheckKind::AmbiguousFunctionName(_) => &CheckCode::E743,
CheckKind::AmbiguousVariableName(_) => &CheckCode::E741,
CheckKind::AmbiguousClassName(..) => &CheckCode::E742,
CheckKind::AmbiguousFunctionName(..) => &CheckCode::E743,
CheckKind::AmbiguousVariableName(..) => &CheckCode::E741,
CheckKind::AssertTuple => &CheckCode::F631,
CheckKind::BreakOutsideLoop => &CheckCode::F701,
CheckKind::ContinueOutsideLoop => &CheckCode::F702,
@@ -1653,14 +1663,14 @@ impl CheckKind {
CheckKind::DoNotUseBareExcept => &CheckCode::E722,
CheckKind::DuplicateArgumentName => &CheckCode::F831,
CheckKind::FStringMissingPlaceholders => &CheckCode::F541,
CheckKind::ForwardAnnotationSyntaxError(_) => &CheckCode::F722,
CheckKind::FutureFeatureNotDefined(_) => &CheckCode::F407,
CheckKind::IOError(_) => &CheckCode::E902,
CheckKind::ForwardAnnotationSyntaxError(..) => &CheckCode::F722,
CheckKind::FutureFeatureNotDefined(..) => &CheckCode::F407,
CheckKind::IOError(..) => &CheckCode::E902,
CheckKind::IfTuple => &CheckCode::F634,
CheckKind::ImportShadowedByLoopVar(..) => &CheckCode::F402,
CheckKind::ImportStarNotPermitted(_) => &CheckCode::F406,
CheckKind::ImportStarNotPermitted(..) => &CheckCode::F406,
CheckKind::ImportStarUsage(..) => &CheckCode::F405,
CheckKind::ImportStarUsed(_) => &CheckCode::F403,
CheckKind::ImportStarUsed(..) => &CheckCode::F403,
CheckKind::InvalidPrintSyntax => &CheckCode::F633,
CheckKind::IsLiteral => &CheckCode::F632,
CheckKind::LateFutureImport => &CheckCode::F404,
@@ -1668,42 +1678,42 @@ impl CheckKind {
CheckKind::MultipleImportsOnOneLine => &CheckCode::E401,
CheckKind::ModuleImportNotAtTopOfFile => &CheckCode::E402,
CheckKind::MultiValueRepeatedKeyLiteral => &CheckCode::F601,
CheckKind::MultiValueRepeatedKeyVariable(_) => &CheckCode::F602,
CheckKind::NoneComparison(_) => &CheckCode::E711,
CheckKind::MultiValueRepeatedKeyVariable(..) => &CheckCode::F602,
CheckKind::NoneComparison(..) => &CheckCode::E711,
CheckKind::NotInTest => &CheckCode::E713,
CheckKind::NotIsTest => &CheckCode::E714,
CheckKind::PercentFormatExpectedMapping => &CheckCode::F502,
CheckKind::PercentFormatExpectedSequence => &CheckCode::F503,
CheckKind::PercentFormatExtraNamedArguments(_) => &CheckCode::F504,
CheckKind::PercentFormatInvalidFormat(_) => &CheckCode::F501,
CheckKind::PercentFormatMissingArgument(_) => &CheckCode::F505,
CheckKind::PercentFormatExtraNamedArguments(..) => &CheckCode::F504,
CheckKind::PercentFormatInvalidFormat(..) => &CheckCode::F501,
CheckKind::PercentFormatMissingArgument(..) => &CheckCode::F505,
CheckKind::PercentFormatMixedPositionalAndNamed => &CheckCode::F506,
CheckKind::PercentFormatPositionalCountMismatch(..) => &CheckCode::F507,
CheckKind::PercentFormatStarRequiresSequence => &CheckCode::F508,
CheckKind::PercentFormatUnsupportedFormatCharacter(_) => &CheckCode::F509,
CheckKind::PercentFormatUnsupportedFormatCharacter(..) => &CheckCode::F509,
CheckKind::RaiseNotImplemented => &CheckCode::F901,
CheckKind::ReturnOutsideFunction => &CheckCode::F706,
CheckKind::StringDotFormatExtraNamedArguments(_) => &CheckCode::F522,
CheckKind::StringDotFormatExtraPositionalArguments(_) => &CheckCode::F523,
CheckKind::StringDotFormatInvalidFormat(_) => &CheckCode::F521,
CheckKind::StringDotFormatMissingArguments(_) => &CheckCode::F524,
CheckKind::StringDotFormatExtraNamedArguments(..) => &CheckCode::F522,
CheckKind::StringDotFormatExtraPositionalArguments(..) => &CheckCode::F523,
CheckKind::StringDotFormatInvalidFormat(..) => &CheckCode::F521,
CheckKind::StringDotFormatMissingArguments(..) => &CheckCode::F524,
CheckKind::StringDotFormatMixingAutomatic => &CheckCode::F525,
CheckKind::SyntaxError(_) => &CheckCode::E999,
CheckKind::SyntaxError(..) => &CheckCode::E999,
CheckKind::ExpressionsInStarAssignment => &CheckCode::F621,
CheckKind::TrueFalseComparison(..) => &CheckCode::E712,
CheckKind::TwoStarredExpressions => &CheckCode::F622,
CheckKind::TypeComparison => &CheckCode::E721,
CheckKind::UndefinedExport(_) => &CheckCode::F822,
CheckKind::UndefinedLocal(_) => &CheckCode::F823,
CheckKind::UndefinedExport(..) => &CheckCode::F822,
CheckKind::UndefinedLocal(..) => &CheckCode::F823,
CheckKind::RedefinedWhileUnused(..) => &CheckCode::F811,
CheckKind::UndefinedName(_) => &CheckCode::F821,
CheckKind::UndefinedName(..) => &CheckCode::F821,
CheckKind::UnusedImport(..) => &CheckCode::F401,
CheckKind::UnusedVariable(_) => &CheckCode::F841,
CheckKind::UnusedAnnotation(_) => &CheckCode::F842,
CheckKind::YieldOutsideFunction(_) => &CheckCode::F704,
CheckKind::UnusedVariable(..) => &CheckCode::F841,
CheckKind::UnusedAnnotation(..) => &CheckCode::F842,
CheckKind::YieldOutsideFunction(..) => &CheckCode::F704,
// pycodestyle warnings
CheckKind::NoNewLineAtEndOfFile => &CheckCode::W292,
CheckKind::InvalidEscapeSequence(_) => &CheckCode::W605,
CheckKind::InvalidEscapeSequence(..) => &CheckCode::W605,
// pylint
CheckKind::AwaitOutsideAsync => &CheckCode::PLE1142,
CheckKind::ConsiderMergingIsinstance(..) => &CheckCode::PLR1701,
@@ -1712,96 +1722,96 @@ impl CheckKind {
CheckKind::MisplacedComparisonConstant(..) => &CheckCode::PLC2201,
CheckKind::PropertyWithParameters => &CheckCode::PLR0206,
CheckKind::UnnecessaryDirectLambdaCall => &CheckCode::PLC3002,
CheckKind::UseSysExit(_) => &CheckCode::PLR1722,
CheckKind::UseSysExit(..) => &CheckCode::PLR1722,
CheckKind::NonlocalWithoutBinding(..) => &CheckCode::PLE0117,
CheckKind::UsedPriorGlobalDeclaration(..) => &CheckCode::PLE0118,
CheckKind::UselessElseOnLoop => &CheckCode::PLW0120,
CheckKind::UselessImportAlias => &CheckCode::PLC0414,
// flake8-builtins
CheckKind::BuiltinVariableShadowing(_) => &CheckCode::A001,
CheckKind::BuiltinArgumentShadowing(_) => &CheckCode::A002,
CheckKind::BuiltinAttributeShadowing(_) => &CheckCode::A003,
CheckKind::BuiltinVariableShadowing(..) => &CheckCode::A001,
CheckKind::BuiltinArgumentShadowing(..) => &CheckCode::A002,
CheckKind::BuiltinAttributeShadowing(..) => &CheckCode::A003,
// flake8-bugbear
CheckKind::AbstractBaseClassWithoutAbstractMethod(_) => &CheckCode::B024,
CheckKind::AbstractBaseClassWithoutAbstractMethod(..) => &CheckCode::B024,
CheckKind::AssignmentToOsEnviron => &CheckCode::B003,
CheckKind::CachedInstanceMethod => &CheckCode::B019,
CheckKind::CannotRaiseLiteral => &CheckCode::B016,
CheckKind::DoNotAssertFalse => &CheckCode::B011,
CheckKind::DuplicateHandlerException(_) => &CheckCode::B014,
CheckKind::DuplicateTryBlockException(_) => &CheckCode::B025,
CheckKind::EmptyMethodWithoutAbstractDecorator(_) => &CheckCode::B027,
CheckKind::DuplicateHandlerException(..) => &CheckCode::B014,
CheckKind::DuplicateTryBlockException(..) => &CheckCode::B025,
CheckKind::EmptyMethodWithoutAbstractDecorator(..) => &CheckCode::B027,
CheckKind::FStringDocstring => &CheckCode::B021,
CheckKind::FunctionCallArgumentDefault(_) => &CheckCode::B008,
CheckKind::FunctionUsesLoopVariable(_) => &CheckCode::B023,
CheckKind::FunctionCallArgumentDefault(..) => &CheckCode::B008,
CheckKind::FunctionUsesLoopVariable(..) => &CheckCode::B023,
CheckKind::GetAttrWithConstant => &CheckCode::B009,
CheckKind::JumpStatementInFinally(_) => &CheckCode::B012,
CheckKind::LoopVariableOverridesIterator(_) => &CheckCode::B020,
CheckKind::JumpStatementInFinally(..) => &CheckCode::B012,
CheckKind::LoopVariableOverridesIterator(..) => &CheckCode::B020,
CheckKind::MutableArgumentDefault => &CheckCode::B006,
CheckKind::NoAssertRaisesException => &CheckCode::B017,
CheckKind::RaiseWithoutFromInsideExcept => &CheckCode::B904,
CheckKind::ZipWithoutExplicitStrict => &CheckCode::B905,
CheckKind::RedundantTupleInExceptionHandler(_) => &CheckCode::B013,
CheckKind::RedundantTupleInExceptionHandler(..) => &CheckCode::B013,
CheckKind::SetAttrWithConstant => &CheckCode::B010,
CheckKind::StarArgUnpackingAfterKeywordArg => &CheckCode::B026,
CheckKind::StripWithMultiCharacters => &CheckCode::B005,
CheckKind::UnaryPrefixIncrement => &CheckCode::B002,
CheckKind::UnreliableCallableCheck => &CheckCode::B004,
CheckKind::UnusedLoopControlVariable(_) => &CheckCode::B007,
CheckKind::UnusedLoopControlVariable(..) => &CheckCode::B007,
CheckKind::UselessComparison => &CheckCode::B015,
CheckKind::UselessContextlibSuppress => &CheckCode::B022,
CheckKind::UselessExpression => &CheckCode::B018,
// flake8-blind-except
CheckKind::BlindExcept(_) => &CheckCode::BLE001,
CheckKind::BlindExcept(..) => &CheckCode::BLE001,
// flake8-comprehensions
CheckKind::UnnecessaryGeneratorList => &CheckCode::C400,
CheckKind::UnnecessaryGeneratorSet => &CheckCode::C401,
CheckKind::UnnecessaryGeneratorDict => &CheckCode::C402,
CheckKind::UnnecessaryListComprehensionSet => &CheckCode::C403,
CheckKind::UnnecessaryListComprehensionDict => &CheckCode::C404,
CheckKind::UnnecessaryLiteralSet(_) => &CheckCode::C405,
CheckKind::UnnecessaryLiteralDict(_) => &CheckCode::C406,
CheckKind::UnnecessaryCollectionCall(_) => &CheckCode::C408,
CheckKind::UnnecessaryLiteralSet(..) => &CheckCode::C405,
CheckKind::UnnecessaryLiteralDict(..) => &CheckCode::C406,
CheckKind::UnnecessaryCollectionCall(..) => &CheckCode::C408,
CheckKind::UnnecessaryLiteralWithinTupleCall(..) => &CheckCode::C409,
CheckKind::UnnecessaryLiteralWithinListCall(..) => &CheckCode::C410,
CheckKind::UnnecessaryListCall => &CheckCode::C411,
CheckKind::UnnecessaryCallAroundSorted(_) => &CheckCode::C413,
CheckKind::UnnecessaryCallAroundSorted(..) => &CheckCode::C413,
CheckKind::UnnecessaryDoubleCastOrProcess(..) => &CheckCode::C414,
CheckKind::UnnecessarySubscriptReversal(_) => &CheckCode::C415,
CheckKind::UnnecessarySubscriptReversal(..) => &CheckCode::C415,
CheckKind::UnnecessaryComprehension(..) => &CheckCode::C416,
CheckKind::UnnecessaryMap(_) => &CheckCode::C417,
CheckKind::UnnecessaryMap(..) => &CheckCode::C417,
// flake8-debugger
CheckKind::Debugger(_) => &CheckCode::T100,
CheckKind::Debugger(..) => &CheckCode::T100,
// flake8-tidy-imports
CheckKind::BannedRelativeImport(_) => &CheckCode::TID252,
CheckKind::BannedRelativeImport(..) => &CheckCode::TID252,
// flake8-return
CheckKind::UnnecessaryReturnNone => &CheckCode::RET501,
CheckKind::ImplicitReturnValue => &CheckCode::RET502,
CheckKind::ImplicitReturn => &CheckCode::RET503,
CheckKind::UnnecessaryAssign => &CheckCode::RET504,
CheckKind::SuperfluousElseReturn(_) => &CheckCode::RET505,
CheckKind::SuperfluousElseRaise(_) => &CheckCode::RET506,
CheckKind::SuperfluousElseContinue(_) => &CheckCode::RET507,
CheckKind::SuperfluousElseBreak(_) => &CheckCode::RET508,
CheckKind::SuperfluousElseReturn(..) => &CheckCode::RET505,
CheckKind::SuperfluousElseRaise(..) => &CheckCode::RET506,
CheckKind::SuperfluousElseContinue(..) => &CheckCode::RET507,
CheckKind::SuperfluousElseBreak(..) => &CheckCode::RET508,
// flake8-print
CheckKind::PrintFound => &CheckCode::T201,
CheckKind::PPrintFound => &CheckCode::T203,
// flake8-quotes
CheckKind::BadQuotesInlineString(_) => &CheckCode::Q000,
CheckKind::BadQuotesMultilineString(_) => &CheckCode::Q001,
CheckKind::BadQuotesDocstring(_) => &CheckCode::Q002,
CheckKind::BadQuotesInlineString(..) => &CheckCode::Q000,
CheckKind::BadQuotesMultilineString(..) => &CheckCode::Q001,
CheckKind::BadQuotesDocstring(..) => &CheckCode::Q002,
CheckKind::AvoidQuoteEscape => &CheckCode::Q003,
// flake8-annotations
CheckKind::MissingTypeFunctionArgument(_) => &CheckCode::ANN001,
CheckKind::MissingTypeArgs(_) => &CheckCode::ANN002,
CheckKind::MissingTypeKwargs(_) => &CheckCode::ANN003,
CheckKind::MissingTypeSelf(_) => &CheckCode::ANN101,
CheckKind::MissingTypeCls(_) => &CheckCode::ANN102,
CheckKind::MissingReturnTypePublicFunction(_) => &CheckCode::ANN201,
CheckKind::MissingReturnTypePrivateFunction(_) => &CheckCode::ANN202,
CheckKind::MissingReturnTypeSpecialMethod(_) => &CheckCode::ANN204,
CheckKind::MissingReturnTypeStaticMethod(_) => &CheckCode::ANN205,
CheckKind::MissingReturnTypeClassMethod(_) => &CheckCode::ANN206,
CheckKind::DynamicallyTypedExpression(_) => &CheckCode::ANN401,
CheckKind::MissingTypeFunctionArgument(..) => &CheckCode::ANN001,
CheckKind::MissingTypeArgs(..) => &CheckCode::ANN002,
CheckKind::MissingTypeKwargs(..) => &CheckCode::ANN003,
CheckKind::MissingTypeSelf(..) => &CheckCode::ANN101,
CheckKind::MissingTypeCls(..) => &CheckCode::ANN102,
CheckKind::MissingReturnTypePublicFunction(..) => &CheckCode::ANN201,
CheckKind::MissingReturnTypePrivateFunction(..) => &CheckCode::ANN202,
CheckKind::MissingReturnTypeSpecialMethod(..) => &CheckCode::ANN204,
CheckKind::MissingReturnTypeStaticMethod(..) => &CheckCode::ANN205,
CheckKind::MissingReturnTypeClassMethod(..) => &CheckCode::ANN206,
CheckKind::DynamicallyTypedExpression(..) => &CheckCode::ANN401,
// flake8-2020
CheckKind::SysVersionSlice3Referenced => &CheckCode::YTT101,
CheckKind::SysVersion2Referenced => &CheckCode::YTT102,
@@ -1816,29 +1826,30 @@ impl CheckKind {
// flake8-simplify
CheckKind::KeyInDict(..) => &CheckCode::SIM118,
// pyupgrade
CheckKind::TypeOfPrimitive(_) => &CheckCode::UP003,
CheckKind::TypeOfPrimitive(..) => &CheckCode::UP003,
CheckKind::UselessMetaclassType => &CheckCode::UP001,
CheckKind::DeprecatedUnittestAlias(..) => &CheckCode::UP005,
CheckKind::UsePEP585Annotation(_) => &CheckCode::UP006,
CheckKind::UsePEP585Annotation(..) => &CheckCode::UP006,
CheckKind::UsePEP604Annotation => &CheckCode::UP007,
CheckKind::UselessObjectInheritance(_) => &CheckCode::UP004,
CheckKind::UselessObjectInheritance(..) => &CheckCode::UP004,
CheckKind::SuperCallWithParameters => &CheckCode::UP008,
CheckKind::PEP3120UnnecessaryCodingComment => &CheckCode::UP009,
CheckKind::UnnecessaryFutureImport(_) => &CheckCode::UP010,
CheckKind::UnnecessaryFutureImport(..) => &CheckCode::UP010,
CheckKind::UnnecessaryLRUCacheParams => &CheckCode::UP011,
CheckKind::UnnecessaryEncodeUTF8 => &CheckCode::UP012,
CheckKind::ConvertTypedDictFunctionalToClass(_) => &CheckCode::UP013,
CheckKind::ConvertNamedTupleFunctionalToClass(_) => &CheckCode::UP014,
CheckKind::ConvertTypedDictFunctionalToClass(..) => &CheckCode::UP013,
CheckKind::ConvertNamedTupleFunctionalToClass(..) => &CheckCode::UP014,
CheckKind::RedundantOpenModes => &CheckCode::UP015,
CheckKind::RemoveSixCompat => &CheckCode::UP016,
CheckKind::DatetimeTimezoneUTC => &CheckCode::UP017,
CheckKind::NativeLiterals => &CheckCode::UP018,
// pydocstyle
CheckKind::BlankLineAfterLastSection(_) => &CheckCode::D413,
CheckKind::BlankLineAfterSection(_) => &CheckCode::D410,
CheckKind::BlankLineBeforeSection(_) => &CheckCode::D411,
CheckKind::CapitalizeSectionName(_) => &CheckCode::D405,
CheckKind::DashedUnderlineAfterSection(_) => &CheckCode::D407,
CheckKind::DocumentAllArguments(_) => &CheckCode::D417,
CheckKind::BlankLineAfterLastSection(..) => &CheckCode::D413,
CheckKind::BlankLineAfterSection(..) => &CheckCode::D410,
CheckKind::BlankLineBeforeSection(..) => &CheckCode::D411,
CheckKind::CapitalizeSectionName(..) => &CheckCode::D405,
CheckKind::DashedUnderlineAfterSection(..) => &CheckCode::D407,
CheckKind::DocumentAllArguments(..) => &CheckCode::D417,
CheckKind::EndsInPeriod => &CheckCode::D400,
CheckKind::EndsInPunctuation => &CheckCode::D415,
CheckKind::FirstLineCapitalized => &CheckCode::D403,
@@ -1848,21 +1859,21 @@ impl CheckKind {
CheckKind::MultiLineSummaryFirstLine => &CheckCode::D212,
CheckKind::MultiLineSummarySecondLine => &CheckCode::D213,
CheckKind::NewLineAfterLastParagraph => &CheckCode::D209,
CheckKind::NewLineAfterSectionName(_) => &CheckCode::D406,
CheckKind::NoBlankLineAfterFunction(_) => &CheckCode::D202,
CheckKind::NewLineAfterSectionName(..) => &CheckCode::D406,
CheckKind::NoBlankLineAfterFunction(..) => &CheckCode::D202,
CheckKind::BlankLineAfterSummary => &CheckCode::D205,
CheckKind::NoBlankLineBeforeClass(_) => &CheckCode::D211,
CheckKind::NoBlankLineBeforeFunction(_) => &CheckCode::D201,
CheckKind::NoBlankLinesBetweenHeaderAndContent(_) => &CheckCode::D412,
CheckKind::NoBlankLineBeforeClass(..) => &CheckCode::D211,
CheckKind::NoBlankLineBeforeFunction(..) => &CheckCode::D201,
CheckKind::NoBlankLinesBetweenHeaderAndContent(..) => &CheckCode::D412,
CheckKind::NoOverIndentation => &CheckCode::D208,
CheckKind::NoSignature => &CheckCode::D402,
CheckKind::NoSurroundingWhitespace => &CheckCode::D210,
CheckKind::NoThisPrefix => &CheckCode::D404,
CheckKind::NoUnderIndentation => &CheckCode::D207,
CheckKind::NonEmpty => &CheckCode::D419,
CheckKind::NonEmptySection(_) => &CheckCode::D414,
CheckKind::OneBlankLineAfterClass(_) => &CheckCode::D204,
CheckKind::OneBlankLineBeforeClass(_) => &CheckCode::D203,
CheckKind::NonEmptySection(..) => &CheckCode::D414,
CheckKind::OneBlankLineAfterClass(..) => &CheckCode::D204,
CheckKind::OneBlankLineBeforeClass(..) => &CheckCode::D203,
CheckKind::PublicClass => &CheckCode::D101,
CheckKind::PublicFunction => &CheckCode::D103,
CheckKind::PublicInit => &CheckCode::D107,
@@ -1870,18 +1881,18 @@ impl CheckKind {
CheckKind::PublicModule => &CheckCode::D100,
CheckKind::PublicNestedClass => &CheckCode::D106,
CheckKind::PublicPackage => &CheckCode::D104,
CheckKind::SectionNameEndsInColon(_) => &CheckCode::D416,
CheckKind::SectionNotOverIndented(_) => &CheckCode::D214,
CheckKind::SectionUnderlineAfterName(_) => &CheckCode::D408,
CheckKind::SectionUnderlineMatchesSectionLength(_) => &CheckCode::D409,
CheckKind::SectionUnderlineNotOverIndented(_) => &CheckCode::D215,
CheckKind::SectionNameEndsInColon(..) => &CheckCode::D416,
CheckKind::SectionNotOverIndented(..) => &CheckCode::D214,
CheckKind::SectionUnderlineAfterName(..) => &CheckCode::D408,
CheckKind::SectionUnderlineMatchesSectionLength(..) => &CheckCode::D409,
CheckKind::SectionUnderlineNotOverIndented(..) => &CheckCode::D215,
CheckKind::SkipDocstring => &CheckCode::D418,
CheckKind::UsesRPrefixForBackslashedContent => &CheckCode::D301,
CheckKind::UsesTripleQuotes => &CheckCode::D300,
// pep8-naming
CheckKind::InvalidClassName(_) => &CheckCode::N801,
CheckKind::InvalidFunctionName(_) => &CheckCode::N802,
CheckKind::InvalidArgumentName(_) => &CheckCode::N803,
CheckKind::InvalidClassName(..) => &CheckCode::N801,
CheckKind::InvalidFunctionName(..) => &CheckCode::N802,
CheckKind::InvalidArgumentName(..) => &CheckCode::N803,
CheckKind::InvalidFirstArgumentNameForClassMethod => &CheckCode::N804,
CheckKind::InvalidFirstArgumentNameForMethod => &CheckCode::N805,
CheckKind::NonLowercaseVariableInFunction(..) => &CheckCode::N806,
@@ -1954,7 +1965,7 @@ impl CheckKind {
CheckKind::AmbiguousUnicodeCharacterString(..) => &CheckCode::RUF001,
CheckKind::AmbiguousUnicodeCharacterDocstring(..) => &CheckCode::RUF002,
CheckKind::AmbiguousUnicodeCharacterComment(..) => &CheckCode::RUF003,
CheckKind::UnusedNOQA(_) => &CheckCode::RUF100,
CheckKind::UnusedNOQA(..) => &CheckCode::RUF100,
}
}
@@ -2555,6 +2566,7 @@ impl CheckKind {
CheckKind::RedundantOpenModes => "Unnecessary open mode parameters".to_string(),
CheckKind::RemoveSixCompat => "Unnecessary `six` compatibility usage".to_string(),
CheckKind::DatetimeTimezoneUTC => "Use `datetime.UTC` alias".to_string(),
CheckKind::NativeLiterals => "Unnecessary call to `str` and `bytes`".to_string(),
CheckKind::ConvertTypedDictFunctionalToClass(name) => {
format!("Convert `{name}` from `TypedDict` functional to class syntax")
}
@@ -2599,13 +2611,13 @@ impl CheckKind {
CheckKind::NoBlankLineAfterFunction(num_lines) => {
format!("No blank lines allowed after function docstring (found {num_lines})")
}
CheckKind::NoBlankLineBeforeClass(_) => {
CheckKind::NoBlankLineBeforeClass(..) => {
"No blank lines allowed before class docstring".to_string()
}
CheckKind::OneBlankLineBeforeClass(_) => {
CheckKind::OneBlankLineBeforeClass(..) => {
"1 blank line required before class docstring".to_string()
}
CheckKind::OneBlankLineAfterClass(_) => {
CheckKind::OneBlankLineAfterClass(..) => {
"1 blank line required after class docstring".to_string()
}
CheckKind::PublicModule => "Missing docstring in public module".to_string(),
@@ -2851,19 +2863,44 @@ impl CheckKind {
)
}
CheckKind::UnusedNOQA(codes) => match codes {
None => "Unused `noqa` directive".to_string(),
None => "Unused blanket `noqa` directive".to_string(),
Some(codes) => {
let codes = codes
.iter()
.map(|code| {
if CheckCode::from_str(code).is_ok() {
code.to_string()
} else {
format!("{code} (not implemented)")
}
})
.join(", ");
format!("Unused `noqa` directive for: {codes}")
let mut codes_by_reason = vec![];
if !codes.unmatched.is_empty() {
codes_by_reason.push(format!(
"unused: {}",
codes
.unmatched
.iter()
.map(|code| format!("`{code}`"))
.join(", ")
));
}
if !codes.disabled.is_empty() {
codes_by_reason.push(format!(
"non-enabled: {}",
codes
.disabled
.iter()
.map(|code| format!("`{code}`"))
.join(", ")
));
}
if !codes.unknown.is_empty() {
codes_by_reason.push(format!(
"unknown: {}",
codes
.unknown
.iter()
.map(|code| format!("`{code}`"))
.join(", ")
));
}
if codes_by_reason.is_empty() {
"Unused `noqa` directive".to_string()
} else {
format!("Unused `noqa` directive ({})", codes_by_reason.join("; "))
}
}
},
// flake8-datetimez
@@ -2956,6 +2993,7 @@ impl CheckKind {
| CheckKind::ConvertNamedTupleFunctionalToClass(..)
| CheckKind::ConvertTypedDictFunctionalToClass(..)
| CheckKind::DashedUnderlineAfterSection(..)
| CheckKind::DatetimeTimezoneUTC
| CheckKind::DeprecatedUnittestAlias(..)
| CheckKind::DoNotAssertFalse
| CheckKind::DoNotAssignLambda
@@ -2965,16 +3003,19 @@ impl CheckKind {
| CheckKind::GetAttrWithConstant
| CheckKind::ImplicitReturn
| CheckKind::ImplicitReturnValue
| CheckKind::InvalidEscapeSequence(..)
| CheckKind::IsLiteral
| CheckKind::KeyInDict(..)
| CheckKind::MisplacedComparisonConstant(..)
| CheckKind::MissingReturnTypeSpecialMethod(..)
| CheckKind::NativeLiterals
| CheckKind::NewLineAfterLastParagraph
| CheckKind::NewLineAfterSectionName(..)
| CheckKind::NoBlankLineAfterFunction(..)
| CheckKind::NoBlankLineBeforeClass(..)
| CheckKind::NoBlankLineBeforeFunction(..)
| CheckKind::NoBlankLinesBetweenHeaderAndContent(..)
| CheckKind::NoNewLineAtEndOfFile
| CheckKind::NoOverIndentation
| CheckKind::NoSurroundingWhitespace
| CheckKind::NoUnderIndentation
@@ -2991,7 +3032,6 @@ impl CheckKind {
| CheckKind::RedundantOpenModes
| CheckKind::RedundantTupleInExceptionHandler(..)
| CheckKind::RemoveSixCompat
| CheckKind::DatetimeTimezoneUTC
| CheckKind::SectionNameEndsInColon(..)
| CheckKind::SectionNotOverIndented(..)
| CheckKind::SectionUnderlineAfterName(..)

View File

@@ -1,6 +1,7 @@
//! File automatically generated by `examples/generate_check_code_prefix.rs`.
use colored::Colorize;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use strum_macros::{AsRefStr, EnumString};
@@ -8,7 +9,17 @@ use crate::checks::CheckCode;
use crate::one_time_warning;
#[derive(
EnumString, AsRefStr, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize,
EnumString,
AsRefStr,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Serialize,
Deserialize,
JsonSchema,
)]
pub enum CheckCodePrefix {
A,
@@ -513,6 +524,7 @@ pub enum CheckCodePrefix {
UP015,
UP016,
UP017,
UP018,
W,
W2,
W29,
@@ -2088,6 +2100,7 @@ impl CheckCodePrefix {
CheckCode::UP015,
CheckCode::UP016,
CheckCode::UP017,
CheckCode::UP018,
]
}
CheckCodePrefix::U0 => {
@@ -2114,6 +2127,7 @@ impl CheckCodePrefix {
CheckCode::UP015,
CheckCode::UP016,
CheckCode::UP017,
CheckCode::UP018,
]
}
CheckCodePrefix::U00 => {
@@ -2222,6 +2236,7 @@ impl CheckCodePrefix {
CheckCode::UP015,
CheckCode::UP016,
CheckCode::UP017,
CheckCode::UP018,
]
}
CheckCodePrefix::U010 => {
@@ -2313,6 +2328,7 @@ impl CheckCodePrefix {
CheckCode::UP015,
CheckCode::UP016,
CheckCode::UP017,
CheckCode::UP018,
],
CheckCodePrefix::UP0 => vec![
CheckCode::UP001,
@@ -2331,6 +2347,7 @@ impl CheckCodePrefix {
CheckCode::UP015,
CheckCode::UP016,
CheckCode::UP017,
CheckCode::UP018,
],
CheckCodePrefix::UP00 => vec![
CheckCode::UP001,
@@ -2359,6 +2376,7 @@ impl CheckCodePrefix {
CheckCode::UP015,
CheckCode::UP016,
CheckCode::UP017,
CheckCode::UP018,
],
CheckCodePrefix::UP010 => vec![CheckCode::UP010],
CheckCodePrefix::UP011 => vec![CheckCode::UP011],
@@ -2368,6 +2386,7 @@ impl CheckCodePrefix {
CheckCodePrefix::UP015 => vec![CheckCode::UP015],
CheckCodePrefix::UP016 => vec![CheckCode::UP016],
CheckCodePrefix::UP017 => vec![CheckCode::UP017],
CheckCodePrefix::UP018 => vec![CheckCode::UP018],
CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605],
CheckCodePrefix::W2 => vec![CheckCode::W292],
CheckCodePrefix::W29 => vec![CheckCode::W292],
@@ -2923,6 +2942,7 @@ impl CheckCodePrefix {
CheckCodePrefix::UP015 => SuffixLength::Three,
CheckCodePrefix::UP016 => SuffixLength::Three,
CheckCodePrefix::UP017 => SuffixLength::Three,
CheckCodePrefix::UP018 => SuffixLength::Three,
CheckCodePrefix::W => SuffixLength::Zero,
CheckCodePrefix::W2 => SuffixLength::One,
CheckCodePrefix::W29 => SuffixLength::Two,

View File

@@ -133,6 +133,9 @@ pub struct Cli {
/// Generate shell completion
#[arg(long, hide = true, value_name = "SHELL")]
pub generate_shell_completion: Option<clap_complete_command::Shell>,
/// Path to the cache directory.
#[arg(long)]
pub cache_dir: Option<PathBuf>,
}
impl Cli {
@@ -180,6 +183,7 @@ impl Cli {
fix: resolve_bool_arg(self.fix, self.no_fix),
format: self.format,
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
cache_dir: self.cache_dir,
},
)
}
@@ -238,6 +242,7 @@ pub struct Overrides {
pub fix: Option<bool>,
pub format: Option<SerializationFormat>,
pub force_exclude: Option<bool>,
pub cache_dir: Option<PathBuf>,
}
/// Map the CLI settings to a `LogLevel`.

View File

@@ -20,7 +20,7 @@ use crate::message::Message;
use crate::resolver::{FileDiscovery, PyprojectDiscovery};
use crate::settings::flags;
use crate::settings::types::SerializationFormat;
use crate::{packages, resolver};
use crate::{cache, packages, resolver};
/// Run the linter over a collection of files.
pub fn run(
@@ -47,6 +47,30 @@ pub fn run(
.collect::<Vec<_>>(),
);
// Initialize the cache.
if matches!(cache, flags::Cache::Enabled) {
match &pyproject_strategy {
PyprojectDiscovery::Fixed(settings) => {
if let Err(e) = cache::init(&settings.cache_dir) {
error!(
"Failed to initialize cache at {}: {e:?}",
settings.cache_dir.to_string_lossy()
);
}
}
PyprojectDiscovery::Hierarchical(default) => {
for settings in std::iter::once(default).chain(resolver.iter()) {
if let Err(e) = cache::init(&settings.cache_dir) {
error!(
"Failed to initialize cache at {}: {e:?}",
settings.cache_dir.to_string_lossy()
);
}
}
}
}
};
let start = Instant::now();
let mut diagnostics: Diagnostics = par_iter(&paths)
.map(|entry| {

View File

@@ -1,51 +1,53 @@
//! Settings for the `flake-annotations` plugin.
use ruff_macros::ConfigurationOptions;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
)]
#[serde(
deny_unknown_fields,
rename_all = "kebab-case",
rename = "Flake8AnnotationsOptions"
)]
pub struct Options {
#[option(
doc = r#"
Whether to allow the omission of a return type hint for `__init__` if at least one
argument is annotated.
"#,
default = "false",
value_type = "bool",
example = "mypy-init-return = true"
)]
/// Whether to allow the omission of a return type hint for `__init__` if at
/// least one argument is annotated.
pub mypy_init_return: Option<bool>,
#[option(
doc = r#"
Whether to suppress `ANN000`-level errors for arguments matching the "dummy" variable
regex (like `_`).
"#,
default = "false",
value_type = "bool",
example = "suppress-dummy-args = true"
)]
/// Whether to suppress `ANN000`-level errors for arguments matching the
/// "dummy" variable regex (like `_`).
pub suppress_dummy_args: Option<bool>,
#[option(
doc = r#"
Whether to suppress `ANN200`-level errors for functions that meet either of the
following criteria:
- Contain no `return` statement.
- Explicit `return` statement(s) all return `None` (explicitly or implicitly).
"#,
default = "false",
value_type = "bool",
example = "suppress-none-returning = true"
)]
/// Whether to suppress `ANN200`-level errors for functions that meet either
/// of the following criteria:
///
/// - Contain no `return` statement.
/// - Explicit `return` statement(s) all return `None` (explicitly or
/// implicitly).
pub suppress_none_returning: Option<bool>,
#[option(
doc = "Whether to suppress `ANN401` for dynamically typed `*args` and `**kwargs` \
arguments.",
default = "false",
value_type = "bool",
example = "allow-star-arg-any = true"
)]
/// Whether to suppress `ANN401` for dynamically typed `*args` and
/// `**kwargs` arguments.
pub allow_star_arg_any: Option<bool>,
}

View File

@@ -1,8 +1,6 @@
use itertools::Itertools;
use rustc_hash::FxHashSet;
use rustpython_ast::{
Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Location, Stmt,
};
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Location};
use crate::ast::helpers;
use crate::ast::types::Range;
@@ -26,17 +24,17 @@ fn duplicate_handler_exceptions<'a>(
checker: &mut Checker,
expr: &'a Expr,
elts: &'a [Expr],
) -> FxHashSet<Vec<&'a str>> {
let mut seen: FxHashSet<Vec<&str>> = FxHashSet::default();
) -> FxHashMap<Vec<&'a str>, &'a Expr> {
let mut seen: FxHashMap<Vec<&str>, &Expr> = FxHashMap::default();
let mut duplicates: FxHashSet<Vec<&str>> = FxHashSet::default();
let mut unique_elts: Vec<&Expr> = Vec::default();
for type_ in elts {
let call_path = helpers::collect_call_paths(type_);
if !call_path.is_empty() {
if seen.contains(&call_path) {
if seen.contains_key(&call_path) {
duplicates.insert(call_path);
} else {
seen.insert(call_path);
seen.entry(call_path).or_insert(type_);
unique_elts.push(type_);
}
}
@@ -77,9 +75,9 @@ fn duplicate_handler_exceptions<'a>(
seen
}
pub fn duplicate_exceptions(checker: &mut Checker, stmt: &Stmt, handlers: &[Excepthandler]) {
pub fn duplicate_exceptions(checker: &mut Checker, handlers: &[Excepthandler]) {
let mut seen: FxHashSet<Vec<&str>> = FxHashSet::default();
let mut duplicates: FxHashSet<Vec<&str>> = FxHashSet::default();
let mut duplicates: FxHashMap<Vec<&str>, Vec<&Expr>> = FxHashMap::default();
for handler in handlers {
let ExcepthandlerKind::ExceptHandler { type_: Some(type_), .. } = &handler.node else {
continue;
@@ -89,16 +87,16 @@ pub fn duplicate_exceptions(checker: &mut Checker, stmt: &Stmt, handlers: &[Exce
let call_path = helpers::collect_call_paths(type_);
if !call_path.is_empty() {
if seen.contains(&call_path) {
duplicates.insert(call_path);
duplicates.entry(call_path).or_default().push(type_);
} else {
seen.insert(call_path);
}
}
}
ExprKind::Tuple { elts, .. } => {
for name in duplicate_handler_exceptions(checker, type_, elts) {
for (name, expr) in duplicate_handler_exceptions(checker, type_, elts) {
if seen.contains(&name) {
duplicates.insert(name);
duplicates.entry(name).or_default().push(expr);
} else {
seen.insert(name);
}
@@ -109,11 +107,13 @@ pub fn duplicate_exceptions(checker: &mut Checker, stmt: &Stmt, handlers: &[Exce
}
if checker.settings.enabled.contains(&CheckCode::B025) {
for duplicate in duplicates.into_iter().sorted() {
checker.add_check(Check::new(
CheckKind::DuplicateTryBlockException(duplicate.join(".")),
Range::from_located(stmt),
));
for (name, exprs) in duplicates {
for expr in exprs {
checker.add_check(Check::new(
CheckKind::DuplicateTryBlockException(name.join(".")),
Range::from_located(expr),
));
}
}
}
}

View File

@@ -1,16 +1,19 @@
//! Settings for the `flake8-bugbear` plugin.
use ruff_macros::ConfigurationOptions;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
)]
#[serde(
deny_unknown_fields,
rename_all = "kebab-case",
rename = "Flake8BugbearOptions"
)]
pub struct Options {
#[option(
doc = r#"
Additional callable functions to consider "immutable" when evaluating, e.g.,
`no-mutable-default-argument` checks (`B006`).
"#,
default = r#"[]"#,
value_type = "Vec<String>",
example = r#"
@@ -18,6 +21,8 @@ pub struct Options {
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]
"#
)]
/// Additional callable functions to consider "immutable" when evaluating,
/// e.g., `no-mutable-default-argument` checks (`B006`).
pub extend_immutable_calls: Option<Vec<String>>,
}

View File

@@ -5,37 +5,37 @@ expression: checks
- kind:
DuplicateTryBlockException: ValueError
location:
row: 15
column: 0
row: 19
column: 7
end_location:
row: 20
column: 9
row: 19
column: 17
fix: ~
- kind:
DuplicateTryBlockException: pickle.PickleError
location:
row: 22
column: 0
row: 28
column: 7
end_location:
row: 29
column: 9
fix: ~
- kind:
DuplicateTryBlockException: TypeError
location:
row: 31
column: 0
end_location:
row: 38
column: 9
row: 28
column: 25
fix: ~
- kind:
DuplicateTryBlockException: ValueError
location:
row: 31
column: 0
row: 35
column: 7
end_location:
row: 38
column: 9
row: 35
column: 17
fix: ~
- kind:
DuplicateTryBlockException: TypeError
location:
row: 37
column: 17
end_location:
row: 37
column: 26
fix: ~

View File

@@ -1,46 +0,0 @@
use rustpython_ast::{Constant, Expr, ExprKind};
use crate::ast::types::Range;
use crate::checks::{Check, CheckKind};
pub fn check_string_in_exception(exc: &Expr, max_string_length: usize) -> Vec<Check> {
let mut checks = vec![];
if let ExprKind::Call { args, .. } = &exc.node {
if let Some(first) = args.first() {
match &first.node {
// Check for string literals
ExprKind::Constant {
value: Constant::Str(string),
..
} => {
if string.len() > max_string_length {
checks.push(Check::new(
CheckKind::RawStringInException,
Range::from_located(first),
));
}
}
// Check for f-strings
ExprKind::JoinedStr { .. } => checks.push(Check::new(
CheckKind::FStringInException,
Range::from_located(first),
)),
// Check for .format() calls
ExprKind::Call { func, .. } => {
if let ExprKind::Attribute { value, attr, .. } = &func.node {
if attr == "format" && matches!(value.node, ExprKind::Constant { .. }) {
checks.push(Check::new(
CheckKind::DotFormatInException,
Range::from_located(first),
));
}
}
}
_ => {}
}
}
}
checks
}

View File

@@ -1,4 +1,4 @@
pub mod checks;
pub mod plugins;
pub mod settings;
#[cfg(test)]

View File

@@ -0,0 +1,52 @@
use rustpython_ast::{Constant, Expr, ExprKind};
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckCode, CheckKind};
/// EM101, EM102, EM103
pub fn string_in_exception(checker: &mut Checker, exc: &Expr) {
if let ExprKind::Call { args, .. } = &exc.node {
if let Some(first) = args.first() {
match &first.node {
// Check for string literals
ExprKind::Constant {
value: Constant::Str(string),
..
} => {
if checker.settings.enabled.contains(&CheckCode::EM101) {
if string.len() > checker.settings.flake8_errmsg.max_string_length {
checker.add_check(Check::new(
CheckKind::RawStringInException,
Range::from_located(first),
));
}
}
}
// Check for f-strings
ExprKind::JoinedStr { .. } => {
if checker.settings.enabled.contains(&CheckCode::EM102) {
checker.add_check(Check::new(
CheckKind::FStringInException,
Range::from_located(first),
));
}
}
// Check for .format() calls
ExprKind::Call { func, .. } => {
if checker.settings.enabled.contains(&CheckCode::EM103) {
if let ExprKind::Attribute { value, attr, .. } = &func.node {
if attr == "format" && matches!(value.node, ExprKind::Constant { .. }) {
checker.add_check(Check::new(
CheckKind::DotFormatInException,
Range::from_located(first),
));
}
}
}
}
_ => {}
}
}
}
}

View File

@@ -1,19 +1,24 @@
//! Settings for the `flake8-errmsg` plugin.
use ruff_macros::ConfigurationOptions;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
)]
#[serde(
deny_unknown_fields,
rename_all = "kebab-case",
rename = "Flake8ErrMsgOptions"
)]
pub struct Options {
#[option(
doc = r#"
Maximum string length for string literals in exception messages.
"#,
default = "0",
value_type = "usize",
example = "max-string-length = 20"
)]
/// Maximum string length for string literals in exception messages.
pub max_string_length: Option<usize>,
}

View File

@@ -5,6 +5,7 @@ use std::hash::{Hash, Hasher};
use itertools::Itertools;
use ruff_macros::ConfigurationOptions;
use rustc_hash::FxHashMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[
@@ -15,12 +16,16 @@ const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[
("seaborn", "sns"),
];
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
)]
#[serde(
deny_unknown_fields,
rename_all = "kebab-case",
rename = "Flake8ImportConventionsOptions"
)]
pub struct Options {
#[option(
doc = "The conventional aliases for imports. These aliases can be extended by the \
`extend_aliases` option.",
default = r#"{"altair": "alt", "matplotlib.pyplot": "plt", "numpy": "np", "pandas": "pd", "seaborn": "sns"}"#,
value_type = "FxHashMap<String, String>",
example = r#"
@@ -32,10 +37,10 @@ pub struct Options {
seaborn = "sns"
"#
)]
/// The conventional aliases for imports. These aliases can be extended by
/// the `extend_aliases` option.
pub aliases: Option<FxHashMap<String, String>>,
#[option(
doc = "A mapping of modules to their conventional import aliases. These aliases will be \
added to the `aliases` mapping.",
default = r#"{}"#,
value_type = "FxHashMap<String, String>",
example = r#"
@@ -43,6 +48,8 @@ pub struct Options {
"dask.dataframe" = "dd"
"#
)]
/// A mapping of modules to their conventional import aliases. These aliases
/// will be added to the `aliases` mapping.
pub extend_aliases: Option<FxHashMap<String, String>>,
}

View File

@@ -1,57 +1,56 @@
//! Settings for the `flake8-quotes` plugin.
use ruff_macros::ConfigurationOptions;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub enum Quote {
Single,
Double,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
)]
#[serde(
deny_unknown_fields,
rename_all = "kebab-case",
rename = "Flake8QuotesOptions"
)]
pub struct Options {
#[option(
doc = r#"
Quote style to prefer for inline strings (either "single" (`'`) or "double" (`"`)).
"#,
default = r#""double""#,
value_type = "Quote",
example = r#"
inline-quotes = "single"
"#
)]
/// Quote style to prefer for inline strings (either "single" (`'`) or
/// "double" (`"`)).
pub inline_quotes: Option<Quote>,
#[option(
doc = r#"
Quote style to prefer for multiline strings (either "single" (`'`) or "double" (`"`)).
"#,
default = r#""double""#,
value_type = "Quote",
example = r#"
multiline-quotes = "single"
"#
)]
/// Quote style to prefer for multiline strings (either "single" (`'`) or
/// "double" (`"`)).
pub multiline_quotes: Option<Quote>,
#[option(
doc = r#"
Quote style to prefer for docstrings (either "single" (`'`) or "double" (`"`)).
"#,
default = r#""double""#,
value_type = "Quote",
example = r#"
docstring-quotes = "single"
"#
)]
/// Quote style to prefer for docstrings (either "single" (`'`) or "double"
/// (`"`)).
pub docstring_quotes: Option<Quote>,
#[option(
doc = r#"
Whether to avoid using single quotes if a string contains single quotes, or vice-versa
with double quotes, as per [PEP8](https://peps.python.org/pep-0008/#string-quotes).
This minimizes the need to escape quotation marks within strings.
"#,
default = r#"true"#,
value_type = "bool",
example = r#"
@@ -59,6 +58,9 @@ pub struct Options {
avoid-escape = false
"#
)]
/// Whether to avoid using single quotes if a string contains single quotes,
/// or vice-versa with double quotes, as per [PEP8](https://peps.python.org/pep-0008/#string-quotes).
/// This minimizes the need to escape quotation marks within strings.
pub avoid_escape: Option<bool>,
}

View File

@@ -203,6 +203,10 @@ fn unnecessary_assign(checker: &mut Checker, stack: &Stack, expr: &Expr) {
return;
}
if stack.non_locals.contains(id.as_str()) {
return;
}
checker.add_check(Check::new(
CheckKind::UnnecessaryAssign,
Range::from_located(expr),

View File

@@ -1,4 +1,4 @@
use rustc_hash::FxHashMap;
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{Expr, ExprKind, Location, Stmt, StmtKind};
use crate::ast::visitor;
@@ -10,6 +10,7 @@ pub struct Stack<'a> {
pub ifs: Vec<&'a Stmt>,
pub elifs: Vec<&'a Stmt>,
pub refs: FxHashMap<&'a str, Vec<Location>>,
pub non_locals: FxHashSet<&'a str>,
pub assigns: FxHashMap<&'a str, Vec<Location>>,
pub loops: Vec<(Location, Location)>,
pub tries: Vec<(Location, Location)>,
@@ -48,6 +49,11 @@ impl<'a> ReturnVisitor<'a> {
impl<'a> Visitor<'a> for ReturnVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
match &stmt.node {
StmtKind::Global { names } | StmtKind::Nonlocal { names } => {
self.stack
.non_locals
.extend(names.iter().map(std::string::String::as_str));
}
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
// Don't recurse.
}

View File

@@ -1,23 +1,26 @@
//! Settings for the `flake8-tidy-imports` plugin.
use ruff_macros::ConfigurationOptions;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub enum Strictness {
Parents,
All,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
)]
#[serde(
deny_unknown_fields,
rename_all = "kebab-case",
rename = "Flake8TidyImportsOptions"
)]
pub struct Options {
#[option(
doc = r#"
Whether to ban all relative imports (`"all"`), or only those imports that extend into
the parent module and beyond (`"parents"`).
"#,
default = r#""parents""#,
value_type = "Strictness",
example = r#"
@@ -25,6 +28,8 @@ pub struct Options {
ban-relative-imports = "all"
"#
)]
/// Whether to ban all relative imports (`"all"`), or only those imports
/// that extend into the parent module and beyond (`"parents"`).
pub ban_relative_imports: Option<Strictness>,
}

View File

@@ -1,19 +1,24 @@
//! Settings for the `flake8-unused-arguments` plugin.
use ruff_macros::ConfigurationOptions;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
)]
#[serde(
deny_unknown_fields,
rename_all = "kebab-case",
rename = "Flake8UnusedArgumentsOptions"
)]
pub struct Options {
#[option(
doc = r#"
Whether to allow unused variadic arguments, like `*args` and `**kwargs`.
"#,
default = "false",
value_type = "bool",
example = "ignore-variadic-names = true"
)]
/// Whether to allow unused variadic arguments, like `*args` and `**kwargs`.
pub ignore_variadic_names: Option<bool>,
}

View File

@@ -3,40 +3,19 @@
use std::collections::BTreeSet;
use ruff_macros::ConfigurationOptions;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
)]
#[serde(
deny_unknown_fields,
rename_all = "kebab-case",
rename = "IsortOptions"
)]
pub struct Options {
#[option(
doc = r#"
Combines as imports on the same line. See isort's [`combine-as-imports`](https://pycqa.github.io/isort/docs/configuration/options.html#combine-as-imports)
option.
"#,
default = r#"false"#,
value_type = "bool",
example = r#"
combine-as-imports = true
"#
)]
pub combine_as_imports: Option<bool>,
#[option(
doc = r#"
Force `import from` statements with multiple members and at least one alias (e.g.,
`import A as B`) to wrap such that every line contains exactly one member. For example,
this formatting would be retained, rather than condensing to a single line:
```py
from .utils import (
test_directory as test_directory,
test_id as test_id
)
```
Note that this setting is only effective when combined with `combine-as-imports = true`.
When `combine-as-imports` isn't enabled, every aliased `import from` will be given its
own line, in which case, wrapping is not necessary.
"#,
default = r#"false"#,
value_type = "bool",
example = r#"
@@ -44,42 +23,62 @@ pub struct Options {
combine-as-imports = true
"#
)]
/// Force `import from` statements with multiple members and at least one
/// alias (e.g., `import A as B`) to wrap such that every line contains
/// exactly one member. For example, this formatting would be retained,
/// rather than condensing to a single line:
///
/// ```py
/// from .utils import (
/// test_directory as test_directory,
/// test_id as test_id
/// )
/// ```
///
/// Note that this setting is only effective when combined with
/// `combine-as-imports = true`. When `combine-as-imports` isn't
/// enabled, every aliased `import from` will be given its own line, in
/// which case, wrapping is not necessary.
pub force_wrap_aliases: Option<bool>,
#[option(
doc = r#"
A list of modules to consider first-party, regardless of whether they can be identified
as such via introspection of the local filesystem.
"#,
default = r#"false"#,
value_type = "bool",
example = r#"
combine-as-imports = true
"#
)]
/// Combines as imports on the same line. See isort's [`combine-as-imports`](https://pycqa.github.io/isort/docs/configuration/options.html#combine-as-imports)
/// option.
pub combine_as_imports: Option<bool>,
#[option(
default = r#"[]"#,
value_type = "Vec<String>",
example = r#"
known-first-party = ["src"]
"#
)]
/// A list of modules to consider first-party, regardless of whether they
/// can be identified as such via introspection of the local filesystem.
pub known_first_party: Option<Vec<String>>,
#[option(
doc = r#"
A list of modules to consider third-party, regardless of whether they can be identified
as such via introspection of the local filesystem.
"#,
default = r#"[]"#,
value_type = "Vec<String>",
example = r#"
known-third-party = ["src"]
"#
)]
/// A list of modules to consider third-party, regardless of whether they
/// can be identified as such via introspection of the local filesystem.
pub known_third_party: Option<Vec<String>>,
#[option(
doc = r#"
A list of modules to consider standard-library, in addition to those known to Ruff in
advance.
"#,
default = r#"[]"#,
value_type = "Vec<String>",
example = r#"
extra-standard-library = ["path"]
"#
)]
/// A list of modules to consider standard-library, in addition to those
/// known to Ruff in advance.
pub extra_standard_library: Option<Vec<String>>,
}

View File

@@ -18,6 +18,7 @@ use std::sync::mpsc::channel;
use ::ruff::autofix::fixer;
use ::ruff::cli::{extract_log_level, Cli, Overrides};
use ::ruff::commands;
use ::ruff::logging::{set_up_logging, LogLevel};
use ::ruff::printer::Printer;
use ::ruff::resolver::{resolve_settings, FileDiscovery, PyprojectDiscovery, Relativity};
@@ -26,7 +27,6 @@ use ::ruff::settings::types::SerializationFormat;
use ::ruff::settings::{pyproject, Settings};
#[cfg(feature = "update-informer")]
use ::ruff::updates;
use ::ruff::{cache, commands};
use anyhow::Result;
use clap::{CommandFactory, Parser};
use colored::Colorize;
@@ -123,6 +123,7 @@ fn inner_main() -> Result<ExitCode> {
} else {
fixer::Mode::None
};
let cache = !cli.no_cache;
if let Some(code) = cli.explain {
commands::explain(&code, &format)?;
@@ -137,13 +138,6 @@ fn inner_main() -> Result<ExitCode> {
return Ok(ExitCode::SUCCESS);
}
// Initialize the cache.
let mut cache_enabled: bool = !cli.no_cache;
if cache_enabled && cache::init().is_err() {
eprintln!("Unable to initialize cache; disabling...");
cache_enabled = false;
}
let printer = Printer::new(&format, &log_level);
if cli.watch {
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
@@ -168,7 +162,7 @@ fn inner_main() -> Result<ExitCode> {
&pyproject_strategy,
&file_strategy,
&overrides,
cache_enabled.into(),
cache.into(),
fixer::Mode::None,
)?;
printer.write_continuously(&messages)?;
@@ -198,7 +192,7 @@ fn inner_main() -> Result<ExitCode> {
&pyproject_strategy,
&file_strategy,
&overrides,
cache_enabled.into(),
cache.into(),
fixer::Mode::None,
)?;
printer.write_continuously(&messages)?;
@@ -237,7 +231,7 @@ fn inner_main() -> Result<ExitCode> {
&pyproject_strategy,
&file_strategy,
&overrides,
cache_enabled.into(),
cache.into(),
autofix,
)?
};

View File

@@ -1,13 +1,19 @@
//! Settings for the `mccabe` plugin.
use ruff_macros::ConfigurationOptions;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
)]
#[serde(
deny_unknown_fields,
rename_all = "kebab-case",
rename = "McCabeOptions"
)]
pub struct Options {
#[option(
doc = "The maximum McCabe complexity to allow before triggering `C901` errors.",
default = "10",
value_type = "usize",
example = r#"
@@ -15,6 +21,7 @@ pub struct Options {
max-complexity = 5
"#
)]
/// The maximum McCabe complexity to allow before triggering `C901` errors.
pub max_complexity: Option<usize>,
}

View File

@@ -1,6 +1,7 @@
//! Settings for the `pep8-naming` plugin.
use ruff_macros::ConfigurationOptions;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
const IGNORE_NAMES: [&str; 12] = [
@@ -22,26 +23,25 @@ const CLASSMETHOD_DECORATORS: [&str; 1] = ["classmethod"];
const STATICMETHOD_DECORATORS: [&str; 1] = ["staticmethod"];
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
)]
#[serde(
deny_unknown_fields,
rename_all = "kebab-case",
rename = "Pep8NamingOptions"
)]
pub struct Options {
#[option(
doc = r#"
A list of names to ignore when considering `pep8-naming` violations.
"#,
default = r#"["setUp", "tearDown", "setUpClass", "tearDownClass", "setUpModule", "tearDownModule", "asyncSetUp", "asyncTearDown", "setUpTestData", "failureException", "longMessage", "maxDiff"]"#,
value_type = "Vec<String>",
example = r#"
ignore-names = ["callMethod"]
"#
)]
/// A list of names to ignore when considering `pep8-naming` violations.
pub ignore_names: Option<Vec<String>>,
#[option(
doc = r#"
A list of decorators that, when applied to a method, indicate that the method should be
treated as a class method. For example, Ruff will expect that any method decorated by a
decorator in this list takes a `cls` argument as its first argument.
"#,
default = r#"["classmethod"]"#,
value_type = "Vec<String>",
example = r#"
@@ -49,13 +49,12 @@ pub struct Options {
classmethod-decorators = ["classmethod", "pydantic.validator"]
"#
)]
/// A list of decorators that, when applied to a method, indicate that the
/// method should be treated as a class method. For example, Ruff will
/// expect that any method decorated by a decorator in this list takes a
/// `cls` argument as its first argument.
pub classmethod_decorators: Option<Vec<String>>,
#[option(
doc = r#"
A list of decorators that, when applied to a method, indicate that the method should be
treated as a static method. For example, Ruff will expect that any method decorated by a
decorator in this list has no `self` or `cls` argument.
"#,
default = r#"["staticmethod"]"#,
value_type = "Vec<String>",
example = r#"
@@ -63,6 +62,10 @@ pub struct Options {
staticmethod-decorators = ["staticmethod", "stcmthd"]
"#
)]
/// A list of decorators that, when applied to a method, indicate that the
/// method should be treated as a static method. For example, Ruff will
/// expect that any method decorated by a decorator in this list has no
/// `self` or `cls` argument.
pub staticmethod_decorators: Option<Vec<String>>,
}

View File

@@ -1,10 +1,11 @@
use itertools::izip;
use once_cell::sync::Lazy;
use regex::Regex;
use rustpython_ast::{Located, Location, Stmt, StmtKind};
use rustpython_ast::{Constant, Located, Location, Stmt, StmtKind};
use rustpython_parser::ast::{Cmpop, Expr, ExprKind};
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checks::{Check, CheckKind};
use crate::source_code_locator::SourceCodeLocator;
@@ -54,7 +55,14 @@ pub fn type_comparison(ops: &[Cmpop], comparators: &[Expr], location: Range) ->
if id == "type" {
if let Some(arg) = args.first() {
// Allow comparison for types which are not obvious.
if !matches!(arg.node, ExprKind::Name { .. }) {
if !matches!(
arg.node,
ExprKind::Name { .. }
| ExprKind::Constant {
value: Constant::None,
kind: None
}
) {
checks.push(Check::new(CheckKind::TypeComparison, location));
}
}
@@ -134,18 +142,24 @@ pub fn ambiguous_function_name(name: &str, location: Range) -> Option<Check> {
}
/// W292
pub fn no_newline_at_end_of_file(contents: &str) -> Option<Check> {
pub fn no_newline_at_end_of_file(contents: &str, autofix: bool) -> Option<Check> {
if !contents.ends_with('\n') {
// Note: if `lines.last()` is `None`, then `contents` is empty (and so we don't
// want to raise W292 anyway).
if let Some(line) = contents.lines().last() {
return Some(Check::new(
// Both locations are at the end of the file (and thus the same).
let location = Location::new(contents.lines().count(), line.len());
let mut check = Check::new(
CheckKind::NoNewLineAtEndOfFile,
Range {
location: Location::new(contents.lines().count(), line.len() + 1),
end_location: Location::new(contents.lines().count(), line.len() + 1),
location,
end_location: location,
},
));
);
if autofix {
check.amend(Fix::insertion("\n".to_string(), location));
}
return Some(check);
}
}
None
@@ -174,6 +188,7 @@ pub fn invalid_escape_sequence(
locator: &SourceCodeLocator,
start: Location,
end: Location,
autofix: bool,
) -> Vec<Check> {
let mut checks = vec![];
@@ -221,13 +236,17 @@ pub fn invalid_escape_sequence(
};
let location = Location::new(start.row() + row_offset, col);
let end_location = Location::new(location.row(), location.column() + 2);
checks.push(Check::new(
let mut check = Check::new(
CheckKind::InvalidEscapeSequence(next_char),
Range {
location,
end_location,
},
));
);
if autofix {
check.amend(Fix::insertion(r"\".to_string(), location));
}
checks.push(check);
}
}
}

View File

@@ -31,6 +31,8 @@ mod tests {
#[test_case(CheckCode::W292, Path::new("W292_0.py"))]
#[test_case(CheckCode::W292, Path::new("W292_1.py"))]
#[test_case(CheckCode::W292, Path::new("W292_2.py"))]
#[test_case(CheckCode::W292, Path::new("W292_3.py"))]
#[test_case(CheckCode::W292, Path::new("W292_4.py"))]
#[test_case(CheckCode::W605, Path::new("W605_0.py"))]
#[test_case(CheckCode::W605, Path::new("W605_1.py"))]
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {

View File

@@ -42,14 +42,6 @@ expression: checks
row: 18
column: 31
fix: ~
- kind: TypeComparison
location:
row: 18
column: 35
end_location:
row: 18
column: 58
fix: ~
- kind: TypeComparison
location:
row: 20

View File

@@ -5,9 +5,16 @@ expression: checks
- kind: NoNewLineAtEndOfFile
location:
row: 2
column: 9
column: 8
end_location:
row: 2
column: 9
fix: ~
column: 8
fix:
content: "\n"
location:
row: 2
column: 8
end_location:
row: 2
column: 8

View File

@@ -0,0 +1,6 @@
---
source: src/pycodestyle/mod.rs
expression: checks
---
[]

View File

@@ -0,0 +1,20 @@
---
source: src/pycodestyle/mod.rs
expression: checks
---
- kind: NoNewLineAtEndOfFile
location:
row: 1
column: 1
end_location:
row: 1
column: 1
fix:
content: "\n"
location:
row: 1
column: 1
end_location:
row: 1
column: 1

View File

@@ -10,7 +10,14 @@ expression: checks
end_location:
row: 2
column: 11
fix: ~
fix:
content: "\\"
location:
row: 2
column: 9
end_location:
row: 2
column: 9
- kind:
InvalidEscapeSequence: "."
location:
@@ -19,7 +26,14 @@ expression: checks
end_location:
row: 6
column: 2
fix: ~
fix:
content: "\\"
location:
row: 6
column: 0
end_location:
row: 6
column: 0
- kind:
InvalidEscapeSequence: _
location:
@@ -28,7 +42,14 @@ expression: checks
end_location:
row: 11
column: 7
fix: ~
fix:
content: "\\"
location:
row: 11
column: 5
end_location:
row: 11
column: 5
- kind:
InvalidEscapeSequence: _
location:
@@ -37,5 +58,12 @@ expression: checks
end_location:
row: 18
column: 7
fix: ~
fix:
content: "\\"
location:
row: 18
column: 5
end_location:
row: 18
column: 5

View File

@@ -10,7 +10,14 @@ expression: checks
end_location:
row: 2
column: 11
fix: ~
fix:
content: "\\"
location:
row: 2
column: 9
end_location:
row: 2
column: 9
- kind:
InvalidEscapeSequence: "."
location:
@@ -19,7 +26,14 @@ expression: checks
end_location:
row: 6
column: 2
fix: ~
fix:
content: "\\"
location:
row: 6
column: 0
end_location:
row: 6
column: 0
- kind:
InvalidEscapeSequence: _
location:
@@ -28,7 +42,14 @@ expression: checks
end_location:
row: 11
column: 7
fix: ~
fix:
content: "\\"
location:
row: 11
column: 5
end_location:
row: 11
column: 5
- kind:
InvalidEscapeSequence: _
location:
@@ -37,5 +58,12 @@ expression: checks
end_location:
row: 18
column: 7
fix: ~
fix:
content: "\\"
location:
row: 18
column: 5
end_location:
row: 18
column: 5

View File

@@ -6,7 +6,7 @@ expression: checks
PercentFormatInvalidFormat: incomplete format
location:
row: 1
column: 9
column: 0
end_location:
row: 1
column: 25

View File

@@ -5,7 +5,7 @@ expression: checks
- kind: PercentFormatExpectedMapping
location:
row: 6
column: 10
column: 0
end_location:
row: 6
column: 19
@@ -13,7 +13,7 @@ expression: checks
- kind: PercentFormatExpectedMapping
location:
row: 7
column: 10
column: 0
end_location:
row: 7
column: 20
@@ -21,7 +21,7 @@ expression: checks
- kind: PercentFormatExpectedMapping
location:
row: 8
column: 10
column: 0
end_location:
row: 8
column: 19
@@ -29,7 +29,7 @@ expression: checks
- kind: PercentFormatExpectedMapping
location:
row: 9
column: 10
column: 0
end_location:
row: 9
column: 22
@@ -37,7 +37,7 @@ expression: checks
- kind: PercentFormatExpectedMapping
location:
row: 11
column: 10
column: 0
end_location:
row: 11
column: 37
@@ -45,7 +45,7 @@ expression: checks
- kind: PercentFormatExpectedMapping
location:
row: 12
column: 10
column: 0
end_location:
row: 12
column: 37
@@ -53,7 +53,7 @@ expression: checks
- kind: PercentFormatExpectedMapping
location:
row: 13
column: 10
column: 0
end_location:
row: 13
column: 37

View File

@@ -5,7 +5,7 @@ expression: checks
- kind: PercentFormatExpectedMapping
location:
row: 9
column: 10
column: 0
end_location:
row: 9
column: 21

View File

@@ -5,7 +5,7 @@ expression: checks
- kind: PercentFormatExpectedSequence
location:
row: 17
column: 8
column: 0
end_location:
row: 17
column: 24
@@ -13,7 +13,7 @@ expression: checks
- kind: PercentFormatExpectedSequence
location:
row: 18
column: 8
column: 0
end_location:
row: 18
column: 28
@@ -21,7 +21,7 @@ expression: checks
- kind: PercentFormatExpectedSequence
location:
row: 23
column: 8
column: 0
end_location:
row: 23
column: 42

View File

@@ -5,7 +5,7 @@ expression: checks
- kind: PercentFormatExpectedSequence
location:
row: 10
column: 8
column: 0
end_location:
row: 10
column: 20

View File

@@ -7,7 +7,7 @@ expression: checks
- b
location:
row: 3
column: 14
column: 0
end_location:
row: 3
column: 34
@@ -24,7 +24,7 @@ expression: checks
- b
location:
row: 8
column: 8
column: 0
end_location:
row: 8
column: 29
@@ -41,7 +41,7 @@ expression: checks
- b
location:
row: 9
column: 8
column: 0
end_location:
row: 9
column: 29

View File

@@ -7,7 +7,7 @@ expression: checks
- baz
location:
row: 8
column: 10
column: 0
end_location:
row: 8
column: 32

View File

@@ -7,7 +7,7 @@ expression: checks
- bar
location:
row: 7
column: 10
column: 0
end_location:
row: 7
column: 14

View File

@@ -5,7 +5,7 @@ expression: checks
- kind: PercentFormatMixedPositionalAndNamed
location:
row: 2
column: 13
column: 0
end_location:
row: 2
column: 29
@@ -13,7 +13,7 @@ expression: checks
- kind: PercentFormatMixedPositionalAndNamed
location:
row: 3
column: 13
column: 0
end_location:
row: 3
column: 29
@@ -21,7 +21,7 @@ expression: checks
- kind: PercentFormatMixedPositionalAndNamed
location:
row: 11
column: 11
column: 0
end_location:
row: 11
column: 27

View File

@@ -8,7 +8,7 @@ expression: checks
- 1
location:
row: 5
column: 8
column: 0
end_location:
row: 5
column: 14
@@ -19,7 +19,7 @@ expression: checks
- 3
location:
row: 6
column: 8
column: 0
end_location:
row: 6
column: 19

View File

@@ -5,7 +5,7 @@ expression: checks
- kind: PercentFormatStarRequiresSequence
location:
row: 11
column: 11
column: 0
end_location:
row: 11
column: 27

View File

@@ -6,7 +6,7 @@ expression: checks
PercentFormatUnsupportedFormatCharacter: j
location:
row: 4
column: 5
column: 0
end_location:
row: 4
column: 11

View File

@@ -37,6 +37,7 @@ mod tests {
#[test_case(CheckCode::UP014, Path::new("UP014.py"); "UP014")]
#[test_case(CheckCode::UP015, Path::new("UP015.py"); "UP015")]
#[test_case(CheckCode::UP016, Path::new("UP016.py"); "UP016")]
#[test_case(CheckCode::UP018, Path::new("UP018.py"); "UP018")]
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
let mut checks = test_path(

View File

@@ -2,6 +2,7 @@ pub use convert_named_tuple_functional_to_class::convert_named_tuple_functional_
pub use convert_typed_dict_functional_to_class::convert_typed_dict_functional_to_class;
pub use datetime_utc_alias::datetime_utc_alias;
pub use deprecated_unittest_alias::deprecated_unittest_alias;
pub use native_literals::native_literals;
pub use redundant_open_modes::redundant_open_modes;
pub use remove_six_compat::remove_six_compat;
pub use super_call_with_parameters::super_call_with_parameters;
@@ -18,6 +19,7 @@ mod convert_named_tuple_functional_to_class;
mod convert_typed_dict_functional_to_class;
mod datetime_utc_alias;
mod deprecated_unittest_alias;
mod native_literals;
mod redundant_open_modes;
mod remove_six_compat;
mod super_call_with_parameters;

View File

@@ -0,0 +1,73 @@
use rustpython_ast::{Constant, Expr, ExprKind, Keyword};
use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckCode, CheckKind};
/// UP018
pub fn native_literals(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let ExprKind::Name { id, .. } = &func.node else { return; };
if (id == "str" || id == "bytes")
&& keywords.is_empty()
&& args.len() <= 1
&& checker.is_builtin(id)
{
let Some(arg) = args.get(0) else {
let mut check = Check::new(CheckKind::NativeLiterals, Range::from_located(expr));
if checker.patch(&CheckCode::UP018) {
check.amend(Fix::replacement(
format!("{}\"\"", if id == "bytes" { "b" } else { "" }),
expr.location,
expr.end_location.unwrap(),
));
}
checker.add_check(check);
return;
};
if !matches!(
&arg.node,
ExprKind::Constant {
value: Constant::Str(_) | Constant::Bytes(_),
..
}
) {
return;
}
// rust-python merges adjacent string/bytes literals into one node, but we can't
// safely remove the outer call in this situation. We're following pyupgrade
// here and skip.
let arg_code = checker
.locator
.slice_source_code_range(&Range::from_located(arg));
if lexer::make_tokenizer(&arg_code)
.flatten()
.filter(|(_, tok, _)| matches!(tok, Tok::String { .. } | Tok::Bytes { .. }))
.count()
> 1
{
return;
}
let mut check = Check::new(CheckKind::NativeLiterals, Range::from_located(expr));
if checker.patch(&CheckCode::UP018) {
check.amend(Fix::replacement(
arg_code.to_string(),
expr.location,
expr.end_location.unwrap(),
));
}
checker.add_check(check);
}
}

View File

@@ -1,15 +1,19 @@
//! Settings for the `pyupgrade` plugin.
use ruff_macros::ConfigurationOptions;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
)]
#[serde(
deny_unknown_fields,
rename_all = "kebab-case",
rename = "PyUpgradeOptions"
)]
pub struct Options {
#[option(
doc = r#"
Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604 (`Optional[str]` -> `str | None`) rewrites even if a file imports `from __future__ import annotations`. Note that this setting is only applicable when the target Python version is below 3.9 and 3.10 respectively.
"#,
default = r#"false"#,
value_type = "bool",
example = r#"
@@ -17,6 +21,11 @@ pub struct Options {
keep-runtime-typing = true
"#
)]
/// Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604
/// (`Optional[str]` -> `str | None`) rewrites even if a file imports `from
/// __future__ import annotations`. Note that this setting is only
/// applicable when the target Python version is below 3.9 and 3.10
/// respectively.
pub keep_runtime_typing: Option<bool>,
}

View File

@@ -0,0 +1,95 @@
---
source: src/pyupgrade/mod.rs
expression: checks
---
- kind: NativeLiterals
location:
row: 18
column: 0
end_location:
row: 18
column: 5
fix:
content: "\"\""
location:
row: 18
column: 0
end_location:
row: 18
column: 5
- kind: NativeLiterals
location:
row: 19
column: 0
end_location:
row: 19
column: 10
fix:
content: "\"foo\""
location:
row: 19
column: 0
end_location:
row: 19
column: 10
- kind: NativeLiterals
location:
row: 20
column: 0
end_location:
row: 21
column: 7
fix:
content: "\"\"\"\nfoo\"\"\""
location:
row: 20
column: 0
end_location:
row: 21
column: 7
- kind: NativeLiterals
location:
row: 22
column: 0
end_location:
row: 22
column: 7
fix:
content: "b\"\""
location:
row: 22
column: 0
end_location:
row: 22
column: 7
- kind: NativeLiterals
location:
row: 23
column: 0
end_location:
row: 23
column: 13
fix:
content: "b\"foo\""
location:
row: 23
column: 0
end_location:
row: 23
column: 13
- kind: NativeLiterals
location:
row: 24
column: 0
end_location:
row: 25
column: 7
fix:
content: "b\"\"\"\nfoo\"\"\""
location:
row: 24
column: 0
end_location:
row: 25
column: 7

View File

@@ -86,6 +86,11 @@ impl Resolver {
.unwrap_or(default),
}
}
/// Return an iterator over the resolved `Settings` in this `Resolver`.
pub fn iter(&self) -> impl Iterator<Item = &Settings> {
self.settings.values()
}
}
/// Recursively resolve a `Configuration` from a `pyproject.toml` file at the

View File

@@ -20,7 +20,10 @@ expression: checks
column: 17
- kind:
UnusedNOQA:
- E501
unknown: []
disabled: []
unmatched:
- E501
location:
row: 13
column: 11
@@ -37,8 +40,11 @@ expression: checks
column: 23
- kind:
UnusedNOQA:
- F841
- E501
unknown: []
disabled: []
unmatched:
- F841
- E501
location:
row: 16
column: 11
@@ -55,14 +61,18 @@ expression: checks
column: 29
- kind:
UnusedNOQA:
- F841
- W191
unknown:
- W191
disabled:
- F821
unmatched:
- F841
location:
row: 19
column: 11
end_location:
row: 19
column: 29
column: 35
fix:
content: ""
location:
@@ -70,11 +80,14 @@ expression: checks
column: 9
end_location:
row: 19
column: 29
column: 35
- kind:
UnusedNOQA:
- F841
- V101
unknown:
- V101
disabled: []
unmatched:
- F841
location:
row: 22
column: 11
@@ -91,7 +104,10 @@ expression: checks
column: 29
- kind:
UnusedNOQA:
- E501
unknown: []
disabled: []
unmatched:
- E501
location:
row: 26
column: 9
@@ -117,7 +133,10 @@ expression: checks
fix: ~
- kind:
UnusedNOQA:
- E501
unknown: []
disabled: []
unmatched:
- E501
location:
row: 29
column: 32
@@ -134,7 +153,10 @@ expression: checks
column: 44
- kind:
UnusedNOQA:
- F841
unknown: []
disabled: []
unmatched:
- F841
location:
row: 55
column: 5
@@ -151,7 +173,10 @@ expression: checks
column: 23
- kind:
UnusedNOQA:
- E501
unknown: []
disabled: []
unmatched:
- E501
location:
row: 63
column: 5

View File

@@ -46,6 +46,7 @@ pub struct Configuration {
pub src: Option<Vec<PathBuf>>,
pub target_version: Option<PythonVersion>,
pub unfixable: Option<Vec<CheckCodePrefix>>,
pub cache_dir: Option<PathBuf>,
// Plugins
pub flake8_annotations: Option<flake8_annotations::settings::Options>,
pub flake8_bugbear: Option<flake8_bugbear::settings::Options>,
@@ -130,6 +131,14 @@ impl Configuration {
.transpose()?,
target_version: options.target_version,
unfixable: options.unfixable,
cache_dir: options
.cache_dir
.map(|dir| {
let dir = shellexpand::full(&dir);
dir.map(|dir| PathBuf::from(dir.as_ref()))
})
.transpose()
.map_err(|e| anyhow!("Invalid `cache-dir` value: {e}"))?,
// Plugins
flake8_annotations: options.flake8_annotations,
flake8_bugbear: options.flake8_bugbear,
@@ -184,6 +193,7 @@ impl Configuration {
src: self.src.or(config.src),
target_version: self.target_version.or(config.target_version),
unfixable: self.unfixable.or(config.unfixable),
cache_dir: self.cache_dir.or(config.cache_dir),
// Plugins
flake8_annotations: self.flake8_annotations.or(config.flake8_annotations),
flake8_bugbear: self.flake8_bugbear.or(config.flake8_bugbear),
@@ -254,6 +264,9 @@ impl Configuration {
if let Some(unfixable) = overrides.unfixable {
self.unfixable = Some(unfixable);
}
if let Some(cache_dir) = overrides.cache_dir {
self.cache_dir = Some(cache_dir);
}
// Special-case: `extend_ignore` and `extend_select` are parallel arrays, so
// push an empty array if only one of the two is provided.
match (overrides.extend_ignore, overrides.extend_select) {

View File

@@ -13,6 +13,7 @@ use path_absolutize::path_dedot;
use regex::Regex;
use rustc_hash::FxHashSet;
use crate::cache::cache_dir;
use crate::checks::CheckCode;
use crate::checks_gen::{CheckCodePrefix, SuffixLength, CATEGORIES};
use crate::settings::configuration::Configuration;
@@ -49,6 +50,7 @@ pub struct Settings {
pub show_source: bool,
pub src: Vec<PathBuf>,
pub target_version: PythonVersion,
pub cache_dir: PathBuf,
// Plugins
pub flake8_annotations: flake8_annotations::settings::Settings,
pub flake8_bugbear: flake8_bugbear::settings::Settings,
@@ -140,6 +142,7 @@ impl Settings {
.unwrap_or_else(|| vec![project_root.to_path_buf()]),
target_version: config.target_version.unwrap_or(PythonVersion::Py310),
show_source: config.show_source.unwrap_or_default(),
cache_dir: config.cache_dir.unwrap_or_else(|| cache_dir(project_root)),
// Plugins
flake8_annotations: config
.flake8_annotations
@@ -209,6 +212,7 @@ impl Settings {
show_source: false,
src: vec![path_dedot::CWD.clone()],
target_version: PythonVersion::Py310,
cache_dir: cache_dir(path_dedot::CWD.as_path()),
flake8_annotations: flake8_annotations::settings::Settings::default(),
flake8_bugbear: flake8_bugbear::settings::Settings::default(),
flake8_errmsg: flake8_errmsg::settings::Settings::default(),
@@ -242,6 +246,7 @@ impl Settings {
show_source: false,
src: vec![path_dedot::CWD.clone()],
target_version: PythonVersion::Py310,
cache_dir: cache_dir(path_dedot::CWD.as_path()),
flake8_annotations: flake8_annotations::settings::Settings::default(),
flake8_bugbear: flake8_bugbear::settings::Settings::default(),
flake8_errmsg: flake8_errmsg::settings::Settings::default(),

View File

@@ -2,6 +2,7 @@
use ruff_macros::ConfigurationOptions;
use rustc_hash::FxHashMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::checks_gen::CheckCodePrefix;
@@ -11,14 +12,12 @@ use crate::{
flake8_tidy_imports, flake8_unused_arguments, isort, mccabe, pep8_naming, pyupgrade,
};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct Options {
#[option(
doc = r#"
A list of allowed "confusable" Unicode characters to ignore when enforcing `RUF001`,
`RUF002`, and `RUF003`.
"#,
default = r#"[]"#,
value_type = "Vec<char>",
example = r#"
@@ -27,13 +26,10 @@ pub struct Options {
allowed-confusables = ["", "ρ", ""]
"#
)]
/// A list of allowed "confusable" Unicode characters to ignore when
/// enforcing `RUF001`, `RUF002`, and `RUF003`.
pub allowed_confusables: Option<Vec<char>>,
#[option(
doc = r#"
A regular expression used to identify "dummy" variables, or those which should be
ignored when evaluating (e.g.) unused-variable checks. The default expression matches
`_`, `__`, and `_var`, but not `_var_`.
"#,
default = r#""^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$""#,
value_type = "Regex",
example = r#"
@@ -41,39 +37,33 @@ pub struct Options {
dummy-variable-rgx = "^_$"
"#
)]
/// A regular expression used to identify "dummy" variables, or those which
/// should be ignored when evaluating (e.g.) unused-variable checks. The
/// default expression matches `_`, `__`, and `_var`, but not `_var_`.
pub dummy_variable_rgx: Option<String>,
#[option(
doc = r#"
A list of file patterns to exclude from linting.
Exclusions are based on globs, and can be either:
- Single-path patterns, like `.mypy_cache` (to exclude any directory named `.mypy_cache` in the
tree), `foo.py` (to exclude any file named `foo.py`), or `foo_*.py` (to exclude any file matching
`foo_*.py` ).
- Relative patterns, like `directory/foo.py` (to exclude that specific file) or `directory/*.py`
(to exclude any Python files in `directory`). Note that these paths are relative to the
project root (e.g., the directory containing your `pyproject.toml`).
Note that you'll typically want to use [`extend-exclude`](#extend-exclude) to modify
the excluded paths.
"#,
default = r#"[".bzr", ".direnv", ".eggs", ".git", ".hg", ".mypy_cache", ".nox", ".pants.d", ".ruff_cache", ".svn", ".tox", ".venv", "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", "venv"]"#,
value_type = "Vec<FilePattern>",
example = r#"
exclude = [".venv"]
"#
)]
/// A list of file patterns to exclude from linting.
///
/// Exclusions are based on globs, and can be either:
///
/// - Single-path patterns, like `.mypy_cache` (to exclude any directory
/// named `.mypy_cache` in the tree), `foo.py` (to exclude any file named
/// `foo.py`), or `foo_*.py` (to exclude any file matching `foo_*.py` ).
/// - Relative patterns, like `directory/foo.py` (to exclude that specific
/// file) or `directory/*.py` (to exclude any Python files in
/// `directory`). Note that these paths are relative to the project root
/// (e.g., the directory containing your `pyproject.toml`).
///
/// Note that you'll typically want to use
/// [`extend-exclude`](#extend-exclude) to modify the excluded paths.
pub exclude: Option<Vec<String>>,
#[option(
doc = r#"
A path to a local `pyproject.toml` file to merge into this configuration. User home
directory and environment variables will be expanded.
To resolve the current `pyproject.toml` file, Ruff will first resolve this base
configuration file, then merge in any properties defined in the current configuration
file.
"#,
default = r#"None"#,
value_type = "Path",
example = r#"
@@ -83,10 +73,15 @@ pub struct Options {
line-length = 100
"#
)]
/// A path to a local `pyproject.toml` file to merge into this
/// configuration. User home directory and environment variables will be
/// expanded.
///
/// To resolve the current `pyproject.toml` file, Ruff will first resolve
/// this base configuration file, then merge in any properties defined
/// in the current configuration file.
pub extend: Option<String>,
#[option(
doc = "A list of file patterns to omit from linting, in addition to those specified by \
`exclude`.",
default = "[]",
value_type = "Vec<FilePattern>",
example = r#"
@@ -94,10 +89,10 @@ pub struct Options {
extend-exclude = ["tests", "src/bad.py"]
"#
)]
/// A list of file patterns to omit from linting, in addition to those
/// specified by `exclude`.
pub extend_exclude: Option<Vec<String>>,
#[option(
doc = "A list of check code prefixes to ignore, in addition to those specified by \
`ignore`.",
default = "[]",
value_type = "Vec<CheckCodePrefix>",
example = r#"
@@ -105,10 +100,10 @@ pub struct Options {
extend-ignore = ["F841"]
"#
)]
/// A list of check code prefixes to ignore, in addition to those specified
/// by `ignore`.
pub extend_ignore: Option<Vec<CheckCodePrefix>>,
#[option(
doc = "A list of check code prefixes to enable, in addition to those specified by \
`select`.",
default = "[]",
value_type = "Vec<CheckCodePrefix>",
example = r#"
@@ -116,13 +111,10 @@ pub struct Options {
extend-select = ["B", "Q"]
"#
)]
/// A list of check code prefixes to enable, in addition to those specified
/// by `select`.
pub extend_select: Option<Vec<CheckCodePrefix>>,
#[option(
doc = r#"
A list of check codes that are unsupported by Ruff, but should be preserved when (e.g.)
validating `# noqa` directives. Useful for retaining `# noqa` directives that cover plugins not
yet implemented in Ruff.
"#,
default = "[]",
value_type = "Vec<String>",
example = r#"
@@ -131,19 +123,16 @@ pub struct Options {
external = ["V101"]
"#
)]
/// A list of check codes that are unsupported by Ruff, but should be
/// preserved when (e.g.) validating `# noqa` directives. Useful for
/// retaining `# noqa` directives that cover plugins not yet implemented
/// in Ruff.
pub external: Option<Vec<String>>,
#[option(
doc = r#"
Enable autofix behavior by-default when running `ruff` (overridden
by the `--fix` and `--no-fix` command-line flags).
"#,
default = "false",
value_type = "bool",
example = "fix = true"
)]
#[option(default = "false", value_type = "bool", example = "fix = true")]
/// Enable autofix behavior by-default when running `ruff` (overridden
/// by the `--fix` and `--no-fix` command-line flags).
pub fix: Option<bool>,
#[option(
doc = "A list of check code prefixes to consider autofix-able.",
default = r#"["A", "ANN", "ARG", "B", "BLE", "C", "D", "E", "ERA", "F", "FBT", "I", "ICN", "N", "PGH", "PLC", "PLE", "PLR", "PLW", "Q", "RET", "RUF", "S", "T", "TID", "UP", "W", "YTT"]"#,
value_type = "Vec<CheckCodePrefix>",
example = r#"
@@ -151,13 +140,9 @@ pub struct Options {
fixable = ["E", "F"]
"#
)]
/// A list of check code prefixes to consider autofix-able.
pub fixable: Option<Vec<CheckCodePrefix>>,
#[option(
doc = r#"
The style in which violation messages should be formatted: `"text"` (default),
`"grouped"` (group messages by file), `"json"` (machine-readable), `"junit"`
(machine-readable XML), or `"github"` (GitHub Actions annotations).
"#,
default = r#""text""#,
value_type = "SerializationType",
example = r#"
@@ -165,33 +150,30 @@ pub struct Options {
format = "grouped"
"#
)]
/// The style in which violation messages should be formatted: `"text"`
/// (default), `"grouped"` (group messages by file), `"json"`
/// (machine-readable), `"junit"` (machine-readable XML), or `"github"`
/// (GitHub Actions annotations).
pub format: Option<SerializationFormat>,
#[option(
doc = r#"
Whether to enforce `exclude` and `extend-exclude` patterns, even for paths that are
passed to Ruff explicitly. Typically, Ruff will lint any paths passed in directly, even
if they would typically be excluded. Setting `force-exclude = true` will cause Ruff to
respect these exclusions unequivocally.
This is useful for [`pre-commit`](https://pre-commit.com/), which explicitly passes all
changed files to the [`ruff-pre-commit`](https://github.com/charliermarsh/ruff-pre-commit)
plugin, regardless of whether they're marked as excluded by Ruff's own settings.
"#,
default = r#"false"#,
value_type = "bool",
example = r#"
force-exclude = true
"#
)]
/// Whether to enforce `exclude` and `extend-exclude` patterns, even for
/// paths that are passed to Ruff explicitly. Typically, Ruff will lint
/// any paths passed in directly, even if they would typically be
/// excluded. Setting `force-exclude = true` will cause Ruff to
/// respect these exclusions unequivocally.
///
/// This is useful for [`pre-commit`](https://pre-commit.com/), which explicitly passes all
/// changed files to the [`ruff-pre-commit`](https://github.com/charliermarsh/ruff-pre-commit)
/// plugin, regardless of whether they're marked as excluded by Ruff's own
/// settings.
pub force_exclude: Option<bool>,
#[option(
doc = r"
A list of check code prefixes to ignore. Prefixes can specify exact checks (like
`F841`), entire categories (like `F`), or anything in between.
When breaking ties between enabled and disabled checks (via `select` and `ignore`,
respectively), more specific prefixes override less specific prefixes.
",
default = "[]",
value_type = "Vec<CheckCodePrefix>",
example = r#"
@@ -199,23 +181,28 @@ pub struct Options {
ignore = ["F841"]
"#
)]
/// A list of check code prefixes to ignore. Prefixes can specify exact
/// checks (like `F841`), entire categories (like `F`), or anything in
/// between.
///
/// When breaking ties between enabled and disabled checks (via `select` and
/// `ignore`, respectively), more specific prefixes override less
/// specific prefixes.
pub ignore: Option<Vec<CheckCodePrefix>>,
#[option(
doc = r#"
Avoid automatically removing unused imports in `__init__.py` files. Such imports will
still be +flagged, but with a dedicated message suggesting that the import is either
added to the module' +`__all__` symbol, or re-exported with a redundant alias (e.g.,
`import os as os`).
"#,
default = "false",
value_type = "bool",
example = r#"
ignore-init-module-imports = true
"#
)]
/// Avoid automatically removing unused imports in `__init__.py` files. Such
/// imports will still be +flagged, but with a dedicated message
/// suggesting that the import is either added to the module' +`__all__`
/// symbol, or re-exported with a redundant alias (e.g., `import os as
/// os`).
pub ignore_init_module_imports: Option<bool>,
#[option(
doc = "The line length to use when enforcing long-lines violations (like E501).",
default = "88",
value_type = "usize",
example = r#"
@@ -223,27 +210,21 @@ pub struct Options {
line-length = 120
"#
)]
/// The line length to use when enforcing long-lines violations (like
/// `E501`).
pub line_length: Option<usize>,
#[option(
doc = r#"
Whether to automatically exclude files that are ignored by `.ignore`, `.gitignore`,
`.git/info/exclude`, and global `gitignore` files. Enabled by default.
"#,
default = "true",
value_type = "bool",
example = r#"
respect_gitignore = false
"#
)]
/// Whether to automatically exclude files that are ignored by `.ignore`,
/// `.gitignore`, `.git/info/exclude`, and global `gitignore` files.
/// Enabled by default.
pub respect_gitignore: Option<bool>,
#[option(
doc = r#"
A list of check code prefixes to enable. Prefixes can specify exact checks (like
`F841`), entire categories (like `F`), or anything in between.
When breaking ties between enabled and disabled checks (via `select` and `ignore`,
respectively), more specific prefixes override less specific prefixes.
"#,
default = r#"["E", "F"]"#,
value_type = "Vec<CheckCodePrefix>",
example = r#"
@@ -251,12 +232,15 @@ pub struct Options {
select = ["E", "F", "B", "Q"]
"#
)]
/// A list of check code prefixes to enable. Prefixes can specify exact
/// checks (like `F841`), entire categories (like `F`), or anything in
/// between.
///
/// When breaking ties between enabled and disabled checks (via `select` and
/// `ignore`, respectively), more specific prefixes override less
/// specific prefixes.
pub select: Option<Vec<CheckCodePrefix>>,
#[option(
doc = r#"
Whether to show source code snippets when reporting lint error violations (overridden by
the `--show-source` command-line flag).
"#,
default = "false",
value_type = "bool",
example = r#"
@@ -264,31 +248,10 @@ pub struct Options {
show-source = true
"#
)]
/// Whether to show source code snippets when reporting lint error
/// violations (overridden by the `--show-source` command-line flag).
pub show_source: Option<bool>,
#[option(
doc = r#"
The source code paths to consider, e.g., when resolving first- vs. third-party imports.
As an example: given a Python package structure like:
```text
my_package/
pyproject.toml
src/
my_package/
__init__.py
foo.py
bar.py
```
The `src` directory should be included in `source` (e.g., `source = ["src"]`), such that
when resolving imports, `my_package.foo` is considered a first-party import.
This field supports globs. For example, if you have a series of Python packages in
a `python_modules` directory, `src = ["python_modules/*"]` would expand to incorporate
all of the packages in that directory. User home directory and environment variables
will also be expanded.
"#,
default = r#"["."]"#,
value_type = "Vec<PathBuf>",
example = r#"
@@ -296,13 +259,32 @@ pub struct Options {
src = ["src", "test"]
"#
)]
/// The source code paths to consider, e.g., when resolving first- vs.
/// third-party imports.
///
/// As an example: given a Python package structure like:
///
/// ```text
/// my_package/
/// pyproject.toml
/// src/
/// my_package/
/// __init__.py
/// foo.py
/// bar.py
/// ```
///
/// The `src` directory should be included in `source` (e.g., `source =
/// ["src"]`), such that when resolving imports, `my_package.foo` is
/// considered a first-party import.
///
/// This field supports globs. For example, if you have a series of Python
/// packages in a `python_modules` directory, `src =
/// ["python_modules/*"]` would expand to incorporate all of the
/// packages in that directory. User home directory and environment
/// variables will also be expanded.
pub src: Option<Vec<String>>,
#[option(
doc = r#"
The Python version to target, e.g., when considering automatic code upgrades, like
rewriting type annotations. Note that the target version will _not_ be inferred from the
_current_ Python version, and instead must be specified explicitly (as seen below).
"#,
default = r#""py310""#,
value_type = "PythonVersion",
example = r#"
@@ -310,9 +292,12 @@ pub struct Options {
target-version = "py37"
"#
)]
/// The Python version to target, e.g., when considering automatic code
/// upgrades, like rewriting type annotations. Note that the target
/// version will _not_ be inferred from the _current_ Python version,
/// and instead must be specified explicitly (as seen below).
pub target_version: Option<PythonVersion>,
#[option(
doc = "A list of check code prefixes to consider un-autofix-able.",
default = "[]",
value_type = "Vec<CheckCodePrefix>",
example = r#"
@@ -320,36 +305,60 @@ pub struct Options {
unfixable = ["F401"]
"#
)]
/// A list of check code prefixes to consider un-autofix-able.
pub unfixable: Option<Vec<CheckCodePrefix>>,
// Plugins
#[option(
default = ".ruff_cache",
value_type = "PathBuf",
example = r#"cache-dir = "~/.cache/ruff""#
)]
/// A path to the cache directory.
///
/// By default, Ruff stores cache results in a `.ruff_cache` directory in
/// the current project root.
///
/// However, Ruff will also respect the `RUFF_CACHE_DIR` environment
/// variable, which takes precedence over that default.
///
/// This setting will override even the `RUFF_CACHE_DIR` environment
/// variable, if set.
pub cache_dir: Option<String>,
/// Plugins
#[option_group]
/// Options for the `flake8-annotations` plugin.
pub flake8_annotations: Option<flake8_annotations::settings::Options>,
#[option_group]
/// Options for the `flake8-bugbear` plugin.
pub flake8_bugbear: Option<flake8_bugbear::settings::Options>,
#[option_group]
/// Options for the `flake8-errmsg` plugin.
pub flake8_errmsg: Option<flake8_errmsg::settings::Options>,
#[option_group]
/// Options for the `flake8-quotes` plugin.
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
#[option_group]
/// Options for the `flake8-tidy-imports` plugin.
pub flake8_tidy_imports: Option<flake8_tidy_imports::settings::Options>,
#[option_group]
/// Options for the `flake8-import-conventions` plugin.
pub flake8_import_conventions: Option<flake8_import_conventions::settings::Options>,
#[option_group]
/// Options for the `flake8-unused-arguments` plugin.
pub flake8_unused_arguments: Option<flake8_unused_arguments::settings::Options>,
#[option_group]
/// Options for the `isort` plugin.
pub isort: Option<isort::settings::Options>,
#[option_group]
/// Options for the `mccabe` plugin.
pub mccabe: Option<mccabe::settings::Options>,
#[option_group]
/// Options for the `pep8-naming` plugin.
pub pep8_naming: Option<pep8_naming::settings::Options>,
#[option_group]
/// Options for the `pyupgrade` plugin.
pub pyupgrade: Option<pyupgrade::settings::Options>,
// Tables are required to go last.
#[option(
doc = r#"
A list of mappings from file pattern to check code prefixes to exclude, when considering
any matching files.
"#,
default = "{}",
value_type = "HashMap<String, Vec<CheckCodePrefix>>",
example = r#"
@@ -359,5 +368,7 @@ pub struct Options {
"path/to/file.py" = ["E402"]
"#
)]
/// A list of mappings from file pattern to check code prefixes to exclude,
/// when considering any matching files.
pub per_file_ignores: Option<FxHashMap<String, Vec<CheckCodePrefix>>>,
}

View File

@@ -141,6 +141,7 @@ mod tests {
src: None,
target_version: None,
unfixable: None,
cache_dir: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
@@ -189,6 +190,7 @@ line-length = 79
src: None,
target_version: None,
unfixable: None,
cache_dir: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
@@ -237,6 +239,7 @@ exclude = ["foo.py"]
src: None,
target_version: None,
unfixable: None,
cache_dir: None,
flake8_annotations: None,
flake8_errmsg: None,
flake8_bugbear: None,
@@ -285,6 +288,7 @@ select = ["E501"]
src: None,
target_version: None,
unfixable: None,
cache_dir: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
@@ -334,6 +338,7 @@ ignore = ["E501"]
src: None,
target_version: None,
unfixable: None,
cache_dir: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
@@ -415,6 +420,7 @@ other-attribute = 1
format: None,
force_exclude: None,
unfixable: None,
cache_dir: None,
per_file_ignores: Some(FxHashMap::from_iter([(
"__init__.py".to_string(),
vec![CheckCodePrefix::F401]

View File

@@ -7,13 +7,16 @@ use anyhow::{anyhow, bail, Result};
use clap::ValueEnum;
use globset::{Glob, GlobSetBuilder};
use rustc_hash::FxHashSet;
use schemars::JsonSchema;
use serde::{de, Deserialize, Deserializer, Serialize};
use crate::checks::CheckCode;
use crate::checks_gen::CheckCodePrefix;
use crate::fs;
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, Hash)]
#[derive(
Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema,
)]
#[serde(rename_all = "lowercase")]
pub enum PythonVersion {
Py33,
@@ -142,7 +145,7 @@ impl FromStr for PatternPrefixPair {
}
}
#[derive(Clone, Copy, ValueEnum, PartialEq, Eq, Serialize, Deserialize, Debug)]
#[derive(Clone, Copy, ValueEnum, PartialEq, Eq, Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum SerializationFormat {
Text,