Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89afc9db74 | ||
|
|
0f34cdb7a3 | ||
|
|
437b6f23b9 | ||
|
|
0fe2b15676 | ||
|
|
e81efa5a3d | ||
|
|
49559da54e | ||
|
|
b74fd1fe13 | ||
|
|
9c4d24a452 | ||
|
|
7a4449eacb | ||
|
|
ee31fa6109 | ||
|
|
6ffe767252 | ||
|
|
2f894e3951 | ||
|
|
589d923c99 | ||
|
|
c5722d8a4d | ||
|
|
c2d6307e9b | ||
|
|
f44fada446 | ||
|
|
6a6f4651aa | ||
|
|
66ae4db6cd | ||
|
|
801c76037f | ||
|
|
ab825eb28d | ||
|
|
826ef7da67 | ||
|
|
72f5393d3a | ||
|
|
6602f7f489 | ||
|
|
aafddae644 | ||
|
|
749df87de0 | ||
|
|
d67db33f22 | ||
|
|
f0a54716e5 | ||
|
|
c59e1ff0b5 | ||
|
|
ecf858cf16 | ||
|
|
8063aee006 | ||
|
|
f1fee5d240 | ||
|
|
d3155560df |
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.124
|
||||
rev: v0.0.128
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
@@ -98,6 +98,13 @@ _and_ a `pyproject.toml` parameter to `src/pyproject.rs`. If you want to pattern
|
||||
existing example, grep for `dummy_variable_rgx`, which defines a regular expression to match against
|
||||
acceptable unused variables (e.g., `_`).
|
||||
|
||||
If the new plugin's configuration should be cached between runs, you'll need to add it to the
|
||||
`Hash` implementation for `Settings` in `src/settings/mod.rs`.
|
||||
|
||||
You may also want to add the new configuration option to the `flake8-to-ruff` tool, which is
|
||||
responsible for converting `flake8` configuration files to Ruff's TOML format. This logic
|
||||
lives in `flake8_to_ruff/src/converter.rs`.
|
||||
|
||||
## Release process
|
||||
|
||||
As of now, Ruff has an ad hoc release process: releases are cut with high frequency via GitHub
|
||||
|
||||
28
Cargo.lock
generated
28
Cargo.lock
generated
@@ -49,6 +49,16 @@ version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7021ce4924a3f25f802b2cccd1af585e39ea1a363a1aa2e72afe54b67a3a7a7"
|
||||
|
||||
[[package]]
|
||||
name = "annotate-snippets"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3b9d411ecbaf79885c6df4d75fff75858d5995ff25385657a28af47e82f9c36"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
"yansi-term",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.66"
|
||||
@@ -417,7 +427,7 @@ version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5b5db619f3556839cb2223ae86ff3f9a09da2c5013be42bc9af08c9589bf70c"
|
||||
dependencies = [
|
||||
"annotate-snippets",
|
||||
"annotate-snippets 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -930,7 +940,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.124-dev.0"
|
||||
version = "0.0.128-dev.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.22",
|
||||
@@ -2238,8 +2248,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.124"
|
||||
version = "0.0.128"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
"atty",
|
||||
@@ -2287,7 +2298,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.124"
|
||||
version = "0.0.128"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.22",
|
||||
@@ -3312,3 +3323,12 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi-term"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
@@ -6,13 +6,14 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.124"
|
||||
version = "0.0.128"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "ruff"
|
||||
|
||||
[dependencies]
|
||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||
anyhow = { version = "1.0.66" }
|
||||
atty = { version = "0.2.14" }
|
||||
bincode = { version = "1.3.3" }
|
||||
|
||||
49
LICENSE
49
LICENSE
@@ -168,6 +168,30 @@ are:
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-blind-except, licensed as follows:
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Elijah Andrews
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-bugbear, licensed as follows:
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
@@ -218,6 +242,31 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-tidy-imports, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Adam Johnson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-print, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
199
README.md
199
README.md
@@ -44,31 +44,35 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
|
||||
## Table of Contents
|
||||
|
||||
1. [Installation and Usage](#installation-and-usage)
|
||||
2. [Configuration](#configuration)
|
||||
3. [Supported Rules](#supported-rules)
|
||||
1. [Pyflakes](#pyflakes)
|
||||
2. [pycodestyle](#pycodestyle)
|
||||
3. [isort](#isort)
|
||||
4. [pydocstyle](#pydocstyle)
|
||||
5. [pyupgrade](#pyupgrade)
|
||||
6. [pep8-naming](#pep8-naming)
|
||||
7. [flake8-bandit](#flake8-bandit)
|
||||
8. [flake8-comprehensions](#flake8-comprehensions)
|
||||
9. [flake8-bugbear](#flake8-bugbear)
|
||||
10. [flake8-builtins](#flake8-builtins)
|
||||
11. [flake8-print](#flake8-print)
|
||||
12. [flake8-quotes](#flake8-quotes)
|
||||
13. [flake8-annotations](#flake8-annotations)
|
||||
14. [flake8-2020](#flake8-2020)
|
||||
15. [Ruff-specific rules](#ruff-specific-rules)
|
||||
16. [Meta rules](#meta-rules)
|
||||
5. [Editor Integrations](#editor-integrations)
|
||||
6. [FAQ](#faq)
|
||||
7. [Development](#development)
|
||||
8. [Releases](#releases)
|
||||
9. [Benchmarks](#benchmarks)
|
||||
10. [License](#license)
|
||||
11. [Contributing](#contributing)
|
||||
1. [Configuration](#configuration)
|
||||
1. [Supported Rules](#supported-rules)
|
||||
1. [Pyflakes (F)](#pyflakes)
|
||||
1. [pycodestyle (E)](#pycodestyle)
|
||||
1. [isort (I)](#isort)
|
||||
1. [pydocstyle (D)](#pydocstyle)
|
||||
1. [pyupgrade (U)](#pyupgrade)
|
||||
1. [pep8-naming (N)](#pep8-naming)
|
||||
1. [flake8-bandit (S)](#flake8-bandit)
|
||||
1. [flake8-comprehensions (C)](#flake8-comprehensions)
|
||||
1. [flake8-bugbear (B)](#flake8-bugbear)
|
||||
1. [flake8-builtins (A)](#flake8-builtins)
|
||||
1. [flake8-tidy-imports (I25)](#flake8-tidy-imports)
|
||||
1. [flake8-print (T)](#flake8-print)
|
||||
1. [flake8-quotes (Q)](#flake8-quotes)
|
||||
1. [flake8-annotations (ANN)](#flake8-annotations)
|
||||
1. [flake8-2020 (YTT)](#flake8-2020)
|
||||
1. [flake8-blind-except (BLE)](#flake8-blind-except)
|
||||
1. [flake8-boolean-trap (FBT)](#flake8-boolean-trap)
|
||||
1. [mccabe (C90)](#mccabe)
|
||||
1. [Ruff-specific rules (RUF)](#ruff-specific-rules)
|
||||
1. [Meta rules (M)](#meta-rules)
|
||||
1. [Editor Integrations](#editor-integrations)
|
||||
1. [FAQ](#faq)
|
||||
1. [Development](#development)
|
||||
1. [Releases](#releases)
|
||||
1. [Benchmarks](#benchmarks)
|
||||
1. [License](#license)
|
||||
1. [Contributing](#contributing)
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
@@ -118,7 +122,7 @@ default configuration is equivalent to:
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
|
||||
# Enable Flake's "E" and "F" codes by default.
|
||||
# Enable Pyflakes `E` and `F` codes by default.
|
||||
select = ["E", "F"]
|
||||
ignore = []
|
||||
|
||||
@@ -153,18 +157,24 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
target-version = "py310"
|
||||
```
|
||||
|
||||
As an example, the following would configure Ruff to (1) avoid checking for line-length
|
||||
violations (`E501`) and (2) ignore unused import rules in `__init__.py` files:
|
||||
As an example, the following would configure Ruff to: (1) avoid checking for line-length
|
||||
violations (`E501`); (2), always autofix, but never remove unused imports (`F401`); and (3) ignore
|
||||
import-at-top-of-file errors (`E402`) in `__init__.py` files:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Enable Pyflakes and pycodestyle rules.
|
||||
select = ["E", "F"]
|
||||
|
||||
# Never enforce `E501`.
|
||||
# Never enforce `E501` (line length violations).
|
||||
ignore = ["E501"]
|
||||
|
||||
# Ignore `F401` violations in any `__init__.py` file, and in `path/to/file.py`.
|
||||
per-file-ignores = {"__init__.py" = ["F401"], "path/to/file.py" = ["F401"]}
|
||||
# Always autofix, but never try to fix `F401` (unused imports).
|
||||
fix = true
|
||||
unfixable = ["F401"]
|
||||
|
||||
# Ignore `E402` (import violations in any `__init__.py` file, and in `path/to/file.py`.
|
||||
per-file-ignores = {"__init__.py" = ["E402"], "path/to/file.py" = ["E402"]}
|
||||
```
|
||||
|
||||
Plugin configurations should be expressed as subsections, e.g.:
|
||||
@@ -187,7 +197,7 @@ ruff path/to/code/ --select F401 --select F403
|
||||
See `ruff --help` for more:
|
||||
|
||||
```shell
|
||||
ruff: An extremely fast Python linter.
|
||||
Ruff: An extremely fast Python linter.
|
||||
|
||||
Usage: ruff [OPTIONS] <FILES>...
|
||||
|
||||
@@ -223,14 +233,20 @@ Options:
|
||||
List of paths, used to exclude files and/or directories from checks
|
||||
--extend-exclude <EXTEND_EXCLUDE>
|
||||
Like --exclude, but adds additional files and directories on top of the excluded ones
|
||||
--fixable <FIXABLE>
|
||||
List of error codes to treat as eligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
|
||||
--unfixable <UNFIXABLE>
|
||||
List of error codes to treat as ineligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
|
||||
--per-file-ignores <PER_FILE_IGNORES>
|
||||
List of mappings from file pattern to code to exclude
|
||||
--format <FORMAT>
|
||||
Output serialization format for error messages [default: text] [possible values: text, json]
|
||||
--show-source
|
||||
Show violations with source code
|
||||
--show-files
|
||||
See the files ruff will be run against with the current settings
|
||||
See the files Ruff will be run against with the current settings
|
||||
--show-settings
|
||||
See ruff's settings
|
||||
See Ruff's settings
|
||||
--add-noqa
|
||||
Enable automatic additions of noqa directives to failing lines
|
||||
--dummy-variable-rgx <DUMMY_VARIABLE_RGX>
|
||||
@@ -239,6 +255,8 @@ Options:
|
||||
The minimum Python version that should be supported
|
||||
--line-length <LINE_LENGTH>
|
||||
Set the line-length for length-associated checks and automatic formatting
|
||||
--max-complexity <MAX_COMPLEXITY>
|
||||
Max McCabe complexity allowed for a function
|
||||
--stdin-filename <STDIN_FILENAME>
|
||||
The name of the file when passing it through stdin
|
||||
-h, --help
|
||||
@@ -356,10 +374,10 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
|
||||
| ---- | ---- | ------- | --- |
|
||||
| E402 | ModuleImportNotAtTopOfFile | Module level import not at top of file | |
|
||||
| E501 | LineTooLong | Line too long (89 > 88 characters) | |
|
||||
| E711 | NoneComparison | Comparison to `None` should be `cond is None` | |
|
||||
| E712 | TrueFalseComparison | Comparison to `True` should be `cond is True` | |
|
||||
| E713 | NotInTest | Test for membership should be `not in` | |
|
||||
| E714 | NotIsTest | Test for object identity should be `is not` | |
|
||||
| E711 | NoneComparison | Comparison to `None` should be `cond is None` | 🛠 |
|
||||
| E712 | TrueFalseComparison | Comparison to `True` should be `cond is True` | 🛠 |
|
||||
| E713 | NotInTest | Test for membership should be `not in` | 🛠 |
|
||||
| E714 | NotIsTest | Test for object identity should be `is not` | 🛠 |
|
||||
| E721 | TypeComparison | Do not compare types, use `isinstance()` | |
|
||||
| E722 | DoNotUseBareExcept | Do not use bare `except` | |
|
||||
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def | |
|
||||
@@ -437,7 +455,6 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| U001 | UselessMetaclassType | `__metaclass__ = type` is implied | 🛠 |
|
||||
| U002 | UnnecessaryAbspath | `abspath(__file__)` is unnecessary in Python 3.9 and later | 🛠 |
|
||||
| U003 | TypeOfPrimitive | Use `str` instead of `type(...)` | 🛠 |
|
||||
| U004 | UselessObjectInheritance | Class `...` inherits from object | 🛠 |
|
||||
| U005 | DeprecatedUnittestAlias | `assertEquals` is deprecated, use `assertEqual` instead | 🛠 |
|
||||
@@ -448,6 +465,7 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
|
||||
| U010 | UnnecessaryFutureImport | Unnecessary `__future__` import `...` for target Python version | 🛠 |
|
||||
| U011 | UnnecessaryLRUCacheParams | Unnecessary parameters to `functools.lru_cache` | 🛠 |
|
||||
| U012 | UnnecessaryEncodeUTF8 | Unnecessary call to `encode` as UTF-8 | 🛠 |
|
||||
| U013 | ConvertTypedDictFunctionalToClass | Convert `TypedDict` functional syntax to class syntax | 🛠 |
|
||||
|
||||
### pep8-naming
|
||||
|
||||
@@ -507,6 +525,16 @@ For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehens
|
||||
| C416 | UnnecessaryComprehension | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`) | 🛠 |
|
||||
| C417 | UnnecessaryMap | Unnecessary `map` usage (rewrite using a `(list\|set\|dict)` comprehension) | |
|
||||
|
||||
### flake8-boolean-trap
|
||||
|
||||
For more, see [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap/0.1.0/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| FBT001 | BooleanPositionalArgInFunctionDefinition | Boolean positional arg in function definition | |
|
||||
| FBT002 | BooleanDefaultValueInFunctionDefinition | Boolean default value in function definition | |
|
||||
| FBT003 | BooleanPositionalValueInFunctionCall | Boolean positional value in function call | |
|
||||
|
||||
### flake8-bugbear
|
||||
|
||||
For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/) on PyPI.
|
||||
@@ -549,6 +577,14 @@ For more, see [flake8-builtins](https://pypi.org/project/flake8-builtins/2.0.1/)
|
||||
| A002 | BuiltinArgumentShadowing | Argument `...` is shadowing a python builtin | |
|
||||
| A003 | BuiltinAttributeShadowing | Class attribute `...` is shadowing a python builtin | |
|
||||
|
||||
### flake8-tidy-imports
|
||||
|
||||
For more, see [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/4.8.0/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| I252 | BannedRelativeImport | Relative imports are banned | |
|
||||
|
||||
### flake8-print
|
||||
|
||||
For more, see [flake8-print](https://pypi.org/project/flake8-print/5.0.0/) on PyPI.
|
||||
@@ -604,6 +640,22 @@ For more, see [flake8-2020](https://pypi.org/project/flake8-2020/1.7.0/) on PyPI
|
||||
| YTT302 | SysVersionCmpStr10 | `sys.version` compared to string (python10), use `sys.version_info` | |
|
||||
| YTT303 | SysVersionSlice1Referenced | `sys.version[:1]` referenced (python10), use `sys.version_info` | |
|
||||
|
||||
### flake8-blind-except
|
||||
|
||||
For more, see [flake8-blind-except](https://pypi.org/project/flake8-blind-except/0.2.1/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| BLE001 | BlindExcept | Blind except Exception: statement | |
|
||||
|
||||
### mccabe
|
||||
|
||||
For more, see [mccabe](https://pypi.org/project/mccabe/0.7.0/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| C901 | FunctionIsTooComplex | `...` is too complex (10) | |
|
||||
|
||||
### Ruff-specific rules
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
@@ -643,8 +695,59 @@ Ruff should then appear as a runnable action:
|
||||
Ruff is available as part of the [coc-pyright](https://github.com/fannheyward/coc-pyright) extension
|
||||
for coc.nvim.
|
||||
|
||||
Ruff can also be integrated via [efm](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#efm)
|
||||
in just a [few lines](https://github.com/JafarAbdi/myconfigs/blob/6f0b6b2450e92ec8fc50422928cd22005b919110/efm-langserver/config.yaml#L14-L20).
|
||||
<details>
|
||||
<summary>Ruff can also be integrated via <a href="https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#efm"><code>efm</code></a> in just a <a href="https://github.com/JafarAbdi/myconfigs/blob/6f0b6b2450e92ec8fc50422928cd22005b919110/efm-langserver/config.yaml#L14-L20">few lines</a>.</summary>
|
||||
<br>
|
||||
|
||||
```yaml
|
||||
tools:
|
||||
python-ruff: &python-ruff
|
||||
lint-command: 'ruff --config ~/myconfigs/linters/ruff.toml --quiet ${INPUT}'
|
||||
lint-stdin: true
|
||||
lint-formats:
|
||||
- '%f:%l:%c: %m'
|
||||
format-command: 'ruff --stdin-filename ${INPUT} --config ~/myconfigs/linters/ruff.toml --fix --exit-zero --quiet -'
|
||||
format-stdin: true
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>For neovim users using <a href="https://github.com/jose-elias-alvarez/null-ls.nvim"><code>null-ls</code></a>, Ruff is already <a href="https://github.com/jose-elias-alvarez/null-ls.nvim">integrated</a>.</summary>
|
||||
<br>
|
||||
|
||||
```lua
|
||||
local null_ls = require("null-ls")
|
||||
local methods = require("null-ls.methods")
|
||||
local helpers = require("null-ls.helpers")
|
||||
|
||||
local function ruff_fix()
|
||||
return helpers.make_builtin({
|
||||
name = "ruff",
|
||||
meta = {
|
||||
url = "https://github.com/charliermarsh/ruff/",
|
||||
description = "An extremely fast Python linter, written in Rust.",
|
||||
},
|
||||
method = methods.internal.FORMATTING,
|
||||
filetypes = { "python" },
|
||||
generator_opts = {
|
||||
command = "ruff",
|
||||
args = { "--fix", "-e", "-n", "--stdin-filename", "$FILENAME", "-" },
|
||||
to_stdin = true
|
||||
},
|
||||
factory = helpers.formatter_factory
|
||||
})
|
||||
end
|
||||
|
||||
null_ls.setup({
|
||||
sources = {
|
||||
ruff_fix(),
|
||||
null_ls.builtins.diagnostics.ruff,
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Language Server Protocol (Unofficial)
|
||||
|
||||
@@ -705,6 +808,7 @@ including:
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||
@@ -712,7 +816,11 @@ including:
|
||||
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (25/32)
|
||||
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (15/34)
|
||||
- [`flake8-blind-except`](https://pypi.org/project/flake8-blind-except/)
|
||||
- [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/)
|
||||
- [`mccabe`](https://pypi.org/project/mccabe/)
|
||||
- [`isort`](https://pypi.org/project/isort/)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/33)
|
||||
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
|
||||
|
||||
Beyond rule-set parity, Ruff suffers from the following limitations vis-à-vis Flake8:
|
||||
@@ -726,10 +834,12 @@ Beyond rule-set parity, Ruff suffers from the following limitations vis-à-vis F
|
||||
|
||||
Today, Ruff can be used to replace Flake8 when used with any of the following plugins:
|
||||
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||
@@ -737,9 +847,12 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (26/32)
|
||||
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
|
||||
- [`flake8-blind-except`](https://pypi.org/project/flake8-blind-except/)
|
||||
- [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/)
|
||||
- [`mccabe`](https://pypi.org/project/mccabe/)
|
||||
|
||||
Ruff can also replace [`isort`](https://pypi.org/project/isort/), [`yesqa`](https://github.com/asottile/yesqa),
|
||||
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (15/34).
|
||||
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/33).
|
||||
|
||||
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.
|
||||
|
||||
|
||||
4
flake8_to_ruff/Cargo.lock
generated
4
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.124"
|
||||
version = "0.0.128"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.124"
|
||||
version = "0.0.128"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.124-dev.0"
|
||||
version = "0.0.128-dev.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -3,9 +3,12 @@ use std::collections::{BTreeSet, HashMap};
|
||||
use anyhow::Result;
|
||||
use ruff::checks_gen::CheckCodePrefix;
|
||||
use ruff::flake8_quotes::settings::Quote;
|
||||
use ruff::flake8_tidy_imports::settings::Strictness;
|
||||
use ruff::settings::options::Options;
|
||||
use ruff::settings::pyproject::Pyproject;
|
||||
use ruff::{flake8_annotations, flake8_bugbear, flake8_quotes, pep8_naming};
|
||||
use ruff::{
|
||||
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, mccabe, pep8_naming,
|
||||
};
|
||||
|
||||
use crate::plugin::Plugin;
|
||||
use crate::{parser, plugin};
|
||||
@@ -71,6 +74,8 @@ pub fn convert(
|
||||
let mut flake8_annotations: flake8_annotations::settings::Options = Default::default();
|
||||
let mut flake8_bugbear: flake8_bugbear::settings::Options = Default::default();
|
||||
let mut flake8_quotes: flake8_quotes::settings::Options = Default::default();
|
||||
let mut flake8_tidy_imports: flake8_tidy_imports::settings::Options = Default::default();
|
||||
let mut mccabe: mccabe::settings::Options = Default::default();
|
||||
let mut pep8_naming: pep8_naming::settings::Options = Default::default();
|
||||
for (key, value) in flake8 {
|
||||
if let Some(value) = value {
|
||||
@@ -172,10 +177,23 @@ pub fn convert(
|
||||
pep8_naming.staticmethod_decorators =
|
||||
Some(parser::parse_strings(value.as_ref()));
|
||||
}
|
||||
// flake8-tidy-imports
|
||||
"ban-relative-imports" | "ban_relative_imports" => match value.trim() {
|
||||
"true" => flake8_tidy_imports.ban_relative_imports = Some(Strictness::All),
|
||||
"parents" => {
|
||||
flake8_tidy_imports.ban_relative_imports = Some(Strictness::Parents)
|
||||
}
|
||||
_ => eprintln!("Unexpected '{key}' value: {value}"),
|
||||
},
|
||||
// flake8-docstrings
|
||||
"docstring-convention" => {
|
||||
// No-op (handled above).
|
||||
}
|
||||
// mccabe
|
||||
"max-complexity" | "max_complexity" => match value.clone().parse::<usize>() {
|
||||
Ok(max_complexity) => mccabe.max_complexity = Some(max_complexity),
|
||||
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
|
||||
},
|
||||
// Unknown
|
||||
_ => eprintln!("Skipping unsupported property: {key}"),
|
||||
}
|
||||
@@ -194,6 +212,12 @@ pub fn convert(
|
||||
if flake8_quotes != Default::default() {
|
||||
options.flake8_quotes = Some(flake8_quotes);
|
||||
}
|
||||
if flake8_tidy_imports != Default::default() {
|
||||
options.flake8_tidy_imports = Some(flake8_tidy_imports);
|
||||
}
|
||||
if mccabe != Default::default() {
|
||||
options.mccabe = Some(mccabe);
|
||||
}
|
||||
if pep8_naming != Default::default() {
|
||||
options.pep8_naming = Some(pep8_naming);
|
||||
}
|
||||
@@ -219,26 +243,31 @@ mod tests {
|
||||
fn it_converts_empty() -> Result<()> {
|
||||
let actual = convert(&HashMap::from([]), None)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: None,
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -253,26 +282,31 @@ mod tests {
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: Some(100),
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: Some(100),
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -287,26 +321,31 @@ mod tests {
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: Some(100),
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: Some(100),
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -321,26 +360,31 @@ mod tests {
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: None,
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -355,22 +399,25 @@ mod tests {
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: None,
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||
@@ -379,7 +426,9 @@ mod tests {
|
||||
docstring_quotes: None,
|
||||
avoid_escape: None,
|
||||
}),
|
||||
flake8_tidy_imports: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -397,11 +446,16 @@ mod tests {
|
||||
Some(vec![Plugin::Flake8Docstrings]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: None,
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::D100,
|
||||
CheckCodePrefix::D101,
|
||||
@@ -442,16 +496,16 @@ mod tests {
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -466,23 +520,26 @@ mod tests {
|
||||
None,
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: None,
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::Q,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||
@@ -491,7 +548,9 @@ mod tests {
|
||||
docstring_quotes: None,
|
||||
avoid_escape: None,
|
||||
}),
|
||||
flake8_tidy_imports: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
@@ -11,9 +11,12 @@ pub enum Plugin {
|
||||
Flake8Builtins,
|
||||
Flake8Comprehensions,
|
||||
Flake8Docstrings,
|
||||
Flake8TidyImports,
|
||||
Flake8Print,
|
||||
Flake8Quotes,
|
||||
Flake8Annotations,
|
||||
McCabe,
|
||||
Flake8BlindExcept,
|
||||
PEP8Naming,
|
||||
Pyupgrade,
|
||||
}
|
||||
@@ -28,9 +31,12 @@ impl FromStr for Plugin {
|
||||
"flake8-builtins" => Ok(Plugin::Flake8Builtins),
|
||||
"flake8-comprehensions" => Ok(Plugin::Flake8Comprehensions),
|
||||
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
|
||||
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
|
||||
"flake8-print" => Ok(Plugin::Flake8Print),
|
||||
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
|
||||
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
|
||||
"flake8-blind-except" => Ok(Plugin::Flake8BlindExcept),
|
||||
"mccabe" => Ok(Plugin::McCabe),
|
||||
"pep8-naming" => Ok(Plugin::PEP8Naming),
|
||||
"pyupgrade" => Ok(Plugin::Pyupgrade),
|
||||
_ => Err(anyhow!("Unknown plugin: {}", string)),
|
||||
@@ -44,11 +50,14 @@ impl Plugin {
|
||||
Plugin::Flake8Bandit => CheckCodePrefix::S,
|
||||
Plugin::Flake8Bugbear => CheckCodePrefix::B,
|
||||
Plugin::Flake8Builtins => CheckCodePrefix::A,
|
||||
Plugin::Flake8Comprehensions => CheckCodePrefix::C,
|
||||
Plugin::Flake8Comprehensions => CheckCodePrefix::C4,
|
||||
Plugin::Flake8Docstrings => CheckCodePrefix::D,
|
||||
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
|
||||
Plugin::Flake8Print => CheckCodePrefix::T,
|
||||
Plugin::Flake8Quotes => CheckCodePrefix::Q,
|
||||
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
|
||||
Plugin::Flake8BlindExcept => CheckCodePrefix::BLE,
|
||||
Plugin::McCabe => CheckCodePrefix::C9,
|
||||
Plugin::PEP8Naming => CheckCodePrefix::N,
|
||||
Plugin::Pyupgrade => CheckCodePrefix::U,
|
||||
}
|
||||
@@ -59,7 +68,7 @@ impl Plugin {
|
||||
Plugin::Flake8Bandit => vec![CheckCodePrefix::S],
|
||||
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
|
||||
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
|
||||
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C],
|
||||
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C4],
|
||||
Plugin::Flake8Docstrings => {
|
||||
// Use the user-provided docstring.
|
||||
for key in ["docstring-convention", "docstring_convention"] {
|
||||
@@ -76,9 +85,12 @@ impl Plugin {
|
||||
// Default to PEP8.
|
||||
DocstringConvention::PEP8.select()
|
||||
}
|
||||
Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25],
|
||||
Plugin::Flake8Print => vec![CheckCodePrefix::T],
|
||||
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
|
||||
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
|
||||
Plugin::Flake8BlindExcept => vec![CheckCodePrefix::BLE],
|
||||
Plugin::McCabe => vec![CheckCodePrefix::C9],
|
||||
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
|
||||
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
|
||||
}
|
||||
@@ -315,6 +327,17 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
|
||||
"allow-star-arg-any" | "allow_star_arg_any" => {
|
||||
plugins.insert(Plugin::Flake8Annotations);
|
||||
}
|
||||
// flake8-tidy-imports
|
||||
"ban-relative-imports" | "ban_relative_imports" => {
|
||||
plugins.insert(Plugin::Flake8TidyImports);
|
||||
}
|
||||
"banned-modules" | "banned_modules" => {
|
||||
plugins.insert(Plugin::Flake8TidyImports);
|
||||
}
|
||||
// mccabe
|
||||
"max-complexity" | "max_complexity" => {
|
||||
plugins.insert(Plugin::McCabe);
|
||||
}
|
||||
// pep8-naming
|
||||
"ignore-names" | "ignore_names" => {
|
||||
plugins.insert(Plugin::PEP8Naming);
|
||||
@@ -342,9 +365,11 @@ pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin
|
||||
Plugin::Flake8Builtins,
|
||||
Plugin::Flake8Comprehensions,
|
||||
Plugin::Flake8Docstrings,
|
||||
Plugin::Flake8TidyImports,
|
||||
Plugin::Flake8Print,
|
||||
Plugin::Flake8Quotes,
|
||||
Plugin::Flake8Annotations,
|
||||
Plugin::Flake8BlindExcept,
|
||||
Plugin::PEP8Naming,
|
||||
Plugin::Pyupgrade,
|
||||
]
|
||||
|
||||
55
resources/test/fixtures/BLE.py
vendored
Normal file
55
resources/test/fixtures/BLE.py
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
try:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except BaseException as e:
|
||||
raise e
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise e
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
try:
|
||||
pass
|
||||
except BaseException as e:
|
||||
raise e
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception as e:
|
||||
try:
|
||||
raise e
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
try:
|
||||
pass
|
||||
except BaseException as e:
|
||||
raise e
|
||||
except Exception:
|
||||
pass
|
||||
108
resources/test/fixtures/C901.py
vendored
Normal file
108
resources/test/fixtures/C901.py
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
# Complexity = 1
|
||||
def trivial():
|
||||
pass
|
||||
|
||||
|
||||
# Complexity = 1
|
||||
def expr_as_statement():
|
||||
0xF00D
|
||||
|
||||
|
||||
# Complexity = 1
|
||||
def sequential(n):
|
||||
k = n + 4
|
||||
s = k + n
|
||||
return s
|
||||
|
||||
|
||||
# Complexity = 3
|
||||
def if_elif_else_dead_path(n):
|
||||
if n > 3:
|
||||
return "bigger than three"
|
||||
elif n > 4:
|
||||
return "is never executed"
|
||||
else:
|
||||
return "smaller than or equal to three"
|
||||
|
||||
|
||||
# Complexity = 3
|
||||
def nested_ifs():
|
||||
if n > 3:
|
||||
if n > 4:
|
||||
return "bigger than four"
|
||||
else:
|
||||
return "bigger than three"
|
||||
else:
|
||||
return "smaller than or equal to three"
|
||||
|
||||
|
||||
# Complexity = 2
|
||||
def for_loop():
|
||||
for i in range(10):
|
||||
print(i)
|
||||
|
||||
|
||||
# Complexity = 2
|
||||
def for_else(mylist):
|
||||
for i in mylist:
|
||||
print(i)
|
||||
else:
|
||||
print(None)
|
||||
|
||||
|
||||
# Complexity = 2
|
||||
def recursive(n):
|
||||
if n > 4:
|
||||
return f(n - 1)
|
||||
else:
|
||||
return n
|
||||
|
||||
|
||||
# Complexity = 3
|
||||
def nested_functions():
|
||||
def a():
|
||||
def b():
|
||||
pass
|
||||
|
||||
b()
|
||||
|
||||
a()
|
||||
|
||||
|
||||
# Complexity = 4
|
||||
def try_else():
|
||||
try:
|
||||
print(1)
|
||||
except TypeA:
|
||||
print(2)
|
||||
except TypeB:
|
||||
print(3)
|
||||
else:
|
||||
print(4)
|
||||
|
||||
|
||||
# Complexity = 3
|
||||
def nested_try_finally():
|
||||
try:
|
||||
try:
|
||||
print(1)
|
||||
finally:
|
||||
print(2)
|
||||
finally:
|
||||
print(3)
|
||||
|
||||
|
||||
# Complexity = 3
|
||||
async def foobar(a, b, c):
|
||||
await whatever(a, b, c)
|
||||
if await b:
|
||||
pass
|
||||
async with c:
|
||||
pass
|
||||
async for x in a:
|
||||
pass
|
||||
|
||||
|
||||
# Complexity = 1
|
||||
def annotated_assign():
|
||||
x: Any = None
|
||||
34
resources/test/fixtures/D.py
vendored
34
resources/test/fixtures/D.py
vendored
@@ -532,3 +532,37 @@ class Blah: # noqa: D203,D213
|
||||
|
||||
expect(os.path.normcase(__file__ if __file__[-1] != 'c' else __file__[:-1]),
|
||||
'D100: Missing docstring in public module')
|
||||
|
||||
|
||||
@expect('D201: No blank lines allowed before function docstring (found 1)')
|
||||
@expect('D213: Multi-line docstring summary should start at the second line')
|
||||
def multiline_leading_space():
|
||||
|
||||
"""Leading space.
|
||||
|
||||
More content.
|
||||
"""
|
||||
|
||||
|
||||
@expect('D202: No blank lines allowed after function docstring (found 1)')
|
||||
@expect('D213: Multi-line docstring summary should start at the second line')
|
||||
def multiline_trailing_space():
|
||||
"""Leading space.
|
||||
|
||||
More content.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@expect('D201: No blank lines allowed before function docstring (found 1)')
|
||||
@expect('D202: No blank lines allowed after function docstring (found 1)')
|
||||
@expect('D213: Multi-line docstring summary should start at the second line')
|
||||
def multiline_trailing_and_leading_space():
|
||||
|
||||
"""Trailing and leading space.
|
||||
|
||||
More content.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
3
resources/test/fixtures/E711.py
vendored
3
resources/test/fixtures/E711.py
vendored
@@ -23,6 +23,9 @@ if None != res[1]:
|
||||
if None == res[1]:
|
||||
pass
|
||||
|
||||
if x == None != None:
|
||||
pass
|
||||
|
||||
#: Okay
|
||||
if x not in y:
|
||||
pass
|
||||
|
||||
3
resources/test/fixtures/E712.py
vendored
3
resources/test/fixtures/E712.py
vendored
@@ -22,6 +22,9 @@ var = 1 if cond == True else -1 if cond == False else cond
|
||||
if (True) == TrueElement or x == TrueElement:
|
||||
pass
|
||||
|
||||
if res == True != False:
|
||||
pass
|
||||
|
||||
#: Okay
|
||||
if x not in y:
|
||||
pass
|
||||
|
||||
42
resources/test/fixtures/FBT.py
vendored
Normal file
42
resources/test/fixtures/FBT.py
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
def function(
|
||||
posonly_nohint,
|
||||
posonly_nonboolhint: int,
|
||||
posonly_boolhint: bool,
|
||||
posonly_boolstrhint: "bool",
|
||||
/,
|
||||
offset,
|
||||
posorkw_nonvalued_nohint,
|
||||
posorkw_nonvalued_nonboolhint: int,
|
||||
posorkw_nonvalued_boolhint: bool,
|
||||
posorkw_nonvalued_boolstrhint: "bool",
|
||||
posorkw_boolvalued_nohint=True,
|
||||
posorkw_boolvalued_nonboolhint: int = True,
|
||||
posorkw_boolvalued_boolhint: bool = True,
|
||||
posorkw_boolvalued_boolstrhint: "bool" = True,
|
||||
posorkw_nonboolvalued_nohint=1,
|
||||
posorkw_nonboolvalued_nonboolhint: int = 2,
|
||||
posorkw_nonboolvalued_boolhint: bool = 3,
|
||||
posorkw_nonboolvalued_boolstrhint: "bool" = 4,
|
||||
*,
|
||||
kwonly_nonvalued_nohint,
|
||||
kwonly_nonvalued_nonboolhint: int,
|
||||
kwonly_nonvalued_boolhint: bool,
|
||||
kwonly_nonvalued_boolstrhint: "bool",
|
||||
kwonly_boolvalued_nohint=True,
|
||||
kwonly_boolvalued_nonboolhint: int = False,
|
||||
kwonly_boolvalued_boolhint: bool = True,
|
||||
kwonly_boolvalued_boolstrhint: "bool" = True,
|
||||
kwonly_nonboolvalued_nohint=5,
|
||||
kwonly_nonboolvalued_nonboolhint: int = 1,
|
||||
kwonly_nonboolvalued_boolhint: bool = 1,
|
||||
kwonly_nonboolvalued_boolstrhint: "bool" = 1,
|
||||
**kw,
|
||||
):
|
||||
...
|
||||
|
||||
|
||||
def used(do):
|
||||
return do
|
||||
|
||||
used("a", True)
|
||||
used(do=True)
|
||||
12
resources/test/fixtures/I252.py
vendored
Normal file
12
resources/test/fixtures/I252.py
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
from . import sibling
|
||||
from .sibling import example
|
||||
|
||||
from .. import parent
|
||||
from ..parent import example
|
||||
|
||||
from ... import grandparent
|
||||
from ...grandparent import example
|
||||
|
||||
import other
|
||||
import other.example
|
||||
from other import example
|
||||
4
resources/test/fixtures/N806.py
vendored
4
resources/test/fixtures/N806.py
vendored
@@ -1,8 +1,12 @@
|
||||
import collections
|
||||
from collections import namedtuple
|
||||
|
||||
GLOBAL: str = "foo"
|
||||
|
||||
|
||||
def f():
|
||||
global GLOBAL
|
||||
GLOBAL = "bar"
|
||||
lower = 0
|
||||
Camel = 0
|
||||
CONSTANT = 0
|
||||
|
||||
8
resources/test/fixtures/N818.py
vendored
8
resources/test/fixtures/N818.py
vendored
@@ -8,3 +8,11 @@ class AnotherError(Exception):
|
||||
|
||||
class C(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class D(BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class E(AnotherError):
|
||||
pass
|
||||
|
||||
15
resources/test/fixtures/U002.py
vendored
15
resources/test/fixtures/U002.py
vendored
@@ -1,15 +0,0 @@
|
||||
from os.path import abspath
|
||||
|
||||
x = abspath(__file__)
|
||||
|
||||
|
||||
import os
|
||||
|
||||
|
||||
y = os.path.abspath(__file__)
|
||||
|
||||
|
||||
from os import path
|
||||
|
||||
|
||||
z = path.abspath(__file__)
|
||||
29
resources/test/fixtures/U013.py
vendored
Normal file
29
resources/test/fixtures/U013.py
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
from typing import TypedDict, NotRequired, Literal
|
||||
|
||||
# dict literal
|
||||
MyType1 = TypedDict("MyType1", {"a": int, "b": str})
|
||||
|
||||
# dict call
|
||||
MyType2 = TypedDict("MyType2", dict(a=int, b=str))
|
||||
|
||||
# kwargs
|
||||
MyType3 = TypedDict("MyType3", a=int, b=str)
|
||||
|
||||
# Empty TypedDict
|
||||
MyType4 = TypedDict("MyType4")
|
||||
|
||||
# Literal values
|
||||
MyType5 = TypedDict("MyType5", {"a": "hello"})
|
||||
MyType6 = TypedDict("MyType6", a="hello")
|
||||
|
||||
# NotRequired
|
||||
MyType7 = TypedDict("MyType7", {"a": NotRequired[dict]})
|
||||
|
||||
# total
|
||||
MyType8 = TypedDict("MyType8", {"x": int, "y": int}, total=False)
|
||||
|
||||
# invalid identifiers
|
||||
MyType9 = TypedDict("MyType9", {"in": int, "x-y": int})
|
||||
|
||||
# using Literal type
|
||||
MyType10 = TypedDict("MyType10", {"key": Literal["value"]})
|
||||
6
resources/test/fixtures/pyproject.toml
vendored
6
resources/test/fixtures/pyproject.toml
vendored
@@ -16,6 +16,9 @@ multiline-quotes = "double"
|
||||
docstring-quotes = "double"
|
||||
avoid-escape = true
|
||||
|
||||
[tool.ruff.mccabe]
|
||||
max-complexity = 10
|
||||
|
||||
[tool.ruff.pep8-naming]
|
||||
ignore-names = [
|
||||
"setUp",
|
||||
@@ -38,3 +41,6 @@ classmethod-decorators = [
|
||||
staticmethod-decorators = [
|
||||
"staticmethod",
|
||||
]
|
||||
|
||||
[tool.ruff.flake8-tidy-imports]
|
||||
ban-relative-imports = "parents"
|
||||
|
||||
@@ -4,4 +4,4 @@ import sysconfig
|
||||
|
||||
if __name__ == "__main__":
|
||||
ruff = os.path.join(sysconfig.get_path("scripts"), "ruff")
|
||||
os.spawnv(os.P_WAIT, ruff, [ruff, *sys.argv[1:]])
|
||||
sys.exit(os.spawnv(os.P_WAIT, ruff, [ruff, *sys.argv[1:]]))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.124"
|
||||
version = "0.0.128"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -79,8 +79,10 @@ pub enum BindingKind {
|
||||
Annotation,
|
||||
Argument,
|
||||
Assignment,
|
||||
// TODO(charlie): This seems to be a catch-all.
|
||||
Binding,
|
||||
LoopVar,
|
||||
Global,
|
||||
Builtin,
|
||||
ClassDefinition,
|
||||
Definition,
|
||||
|
||||
@@ -50,4 +50,15 @@ impl Fix {
|
||||
applied: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dummy(location: Location) -> Self {
|
||||
Self {
|
||||
patch: Patch {
|
||||
content: "".to_string(),
|
||||
location,
|
||||
end_location: location,
|
||||
},
|
||||
applied: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
174
src/check_ast.rs
174
src/check_ast.rs
@@ -19,8 +19,7 @@ use crate::ast::helpers::{
|
||||
use crate::ast::operations::extract_all_names;
|
||||
use crate::ast::relocate::relocate_expr;
|
||||
use crate::ast::types::{
|
||||
Binding, BindingContext, BindingKind, ClassScope, FunctionScope, ImportKind, Range, Scope,
|
||||
ScopeKind,
|
||||
Binding, BindingContext, BindingKind, ClassScope, ImportKind, Range, Scope, ScopeKind,
|
||||
};
|
||||
use crate::ast::visitor::{walk_excepthandler, Visitor};
|
||||
use crate::ast::{helpers, operations, visitor};
|
||||
@@ -36,8 +35,9 @@ use crate::settings::Settings;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
|
||||
use crate::{
|
||||
docstrings, flake8_2020, flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins,
|
||||
flake8_comprehensions, flake8_print, pep8_naming, pycodestyle, pydocstyle, pyflakes, pyupgrade,
|
||||
docstrings, flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except,
|
||||
flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_print,
|
||||
flake8_tidy_imports, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pyupgrade,
|
||||
};
|
||||
|
||||
const GLOBAL_SCOPE_INDEX: usize = 0;
|
||||
@@ -152,10 +152,10 @@ impl<'a> Checker<'a> {
|
||||
|
||||
/// Return `true` if a patch should be generated under the given autofix
|
||||
/// `Mode`.
|
||||
pub fn patch(&self) -> bool {
|
||||
pub fn patch(&self, code: &CheckCode) -> bool {
|
||||
// TODO(charlie): We can't fix errors in f-strings until RustPython adds
|
||||
// location data.
|
||||
self.autofix.patch() && self.in_f_string.is_none()
|
||||
self.autofix.patch() && self.in_f_string.is_none() && self.settings.fixable.contains(code)
|
||||
}
|
||||
|
||||
/// Return `true` if the `Expr` is a reference to `typing.${target}`.
|
||||
@@ -215,21 +215,18 @@ where
|
||||
match &stmt.node {
|
||||
StmtKind::Global { names } | StmtKind::Nonlocal { names } => {
|
||||
let global_scope_id = self.scopes[GLOBAL_SCOPE_INDEX].id;
|
||||
|
||||
let current_scope = self.current_scope();
|
||||
let current_scope_id = current_scope.id;
|
||||
if current_scope_id != global_scope_id {
|
||||
let scope =
|
||||
&mut self.scopes[*(self.scope_stack.last().expect("No current scope found."))];
|
||||
if scope.id != global_scope_id {
|
||||
for name in names {
|
||||
for scope in self.scopes.iter_mut().skip(GLOBAL_SCOPE_INDEX + 1) {
|
||||
scope.values.insert(
|
||||
name,
|
||||
Binding {
|
||||
kind: BindingKind::Assignment,
|
||||
used: Some((global_scope_id, Range::from_located(stmt))),
|
||||
range: Range::from_located(stmt),
|
||||
},
|
||||
);
|
||||
}
|
||||
scope.values.insert(
|
||||
name,
|
||||
Binding {
|
||||
kind: BindingKind::Global,
|
||||
used: Some((global_scope_id, Range::from_located(stmt))),
|
||||
range: Range::from_located(stmt),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,6 +349,16 @@ where
|
||||
if self.settings.enabled.contains(&CheckCode::B019) {
|
||||
flake8_bugbear::plugins::cached_instance_method(self, decorator_list);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::C901) {
|
||||
if let Some(check) = mccabe::checks::function_is_too_complex(
|
||||
stmt,
|
||||
name,
|
||||
body,
|
||||
self.settings.mccabe.max_complexity,
|
||||
) {
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::S107) {
|
||||
self.add_checks(
|
||||
@@ -731,10 +738,8 @@ where
|
||||
));
|
||||
}
|
||||
|
||||
let scope = &mut self.scopes[*(self
|
||||
.scope_stack
|
||||
.last_mut()
|
||||
.expect("No current scope found."))];
|
||||
let scope = &mut self.scopes
|
||||
[*(self.scope_stack.last().expect("No current scope found."))];
|
||||
scope.import_starred = true;
|
||||
} else {
|
||||
if let Some(asname) = &alias.node.asname {
|
||||
@@ -782,6 +787,16 @@ where
|
||||
)
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::I252) {
|
||||
if let Some(check) = flake8_tidy_imports::checks::banned_relative_import(
|
||||
stmt,
|
||||
level.as_ref(),
|
||||
&self.settings.flake8_tidy_imports.ban_relative_imports,
|
||||
) {
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(asname) = &alias.node.asname {
|
||||
if self.settings.enabled.contains(&CheckCode::N811) {
|
||||
if let Some(check) =
|
||||
@@ -906,6 +921,9 @@ where
|
||||
if self.settings.enabled.contains(&CheckCode::B013) {
|
||||
flake8_bugbear::plugins::redundant_tuple_in_exception_handler(self, handlers);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::BLE001) {
|
||||
flake8_blind_except::plugins::blind_except(self, handlers);
|
||||
}
|
||||
}
|
||||
StmtKind::Assign { targets, value, .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::E731) {
|
||||
@@ -930,6 +948,11 @@ where
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::U013) {
|
||||
pyupgrade::plugins::convert_typed_dict_functional_to_class(
|
||||
self, stmt, targets, value,
|
||||
);
|
||||
}
|
||||
}
|
||||
StmtKind::AnnAssign { target, value, .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::E731) {
|
||||
@@ -1233,7 +1256,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C400),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1247,7 +1270,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C401),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1261,7 +1284,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C402),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1276,7 +1299,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C403),
|
||||
Range::from_located(expr),
|
||||
)
|
||||
{
|
||||
@@ -1292,7 +1315,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C404),
|
||||
Range::from_located(expr),
|
||||
)
|
||||
{
|
||||
@@ -1307,7 +1330,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C405),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1321,7 +1344,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C406),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1335,7 +1358,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C408),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1349,7 +1372,7 @@ where
|
||||
func,
|
||||
args,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C409),
|
||||
Range::from_located(expr),
|
||||
)
|
||||
{
|
||||
@@ -1364,7 +1387,7 @@ where
|
||||
func,
|
||||
args,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C410),
|
||||
Range::from_located(expr),
|
||||
)
|
||||
{
|
||||
@@ -1378,7 +1401,7 @@ where
|
||||
func,
|
||||
args,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C411),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1392,7 +1415,7 @@ where
|
||||
func,
|
||||
args,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C413),
|
||||
Range::from_located(expr),
|
||||
)
|
||||
{
|
||||
@@ -1435,27 +1458,22 @@ where
|
||||
}
|
||||
|
||||
// pyupgrade
|
||||
if self.settings.enabled.contains(&CheckCode::U002)
|
||||
&& self.settings.target_version >= PythonVersion::Py310
|
||||
{
|
||||
pyupgrade::plugins::unnecessary_abspath(self, expr, func, args);
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::U003) {
|
||||
pyupgrade::plugins::type_of_primitive(self, expr, func, args);
|
||||
}
|
||||
|
||||
// flake8-boolean-trap
|
||||
if self.settings.enabled.contains(&CheckCode::FBT003) {
|
||||
flake8_boolean_trap::plugins::check_boolean_positional_value_in_function_call(
|
||||
self, args,
|
||||
);
|
||||
}
|
||||
if let ExprKind::Name { id, ctx } = &func.node {
|
||||
if id == "locals" && matches!(ctx, ExprContext::Load) {
|
||||
let scope = &mut self.scopes[*(self
|
||||
.scope_stack
|
||||
.last_mut()
|
||||
.expect("No current scope found."))];
|
||||
if matches!(
|
||||
scope.kind,
|
||||
ScopeKind::Function(FunctionScope { uses_locals: false })
|
||||
) {
|
||||
scope.kind = ScopeKind::Function(FunctionScope { uses_locals: true });
|
||||
let scope = &mut self.scopes
|
||||
[*(self.scope_stack.last().expect("No current scope found."))];
|
||||
if let ScopeKind::Function(inner) = &mut scope.kind {
|
||||
inner.uses_locals = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1513,9 +1531,13 @@ where
|
||||
let check_not_in = self.settings.enabled.contains(&CheckCode::E713);
|
||||
let check_not_is = self.settings.enabled.contains(&CheckCode::E714);
|
||||
if check_not_in || check_not_is {
|
||||
self.add_checks(
|
||||
pycodestyle::checks::not_tests(op, operand, check_not_in, check_not_is)
|
||||
.into_iter(),
|
||||
pycodestyle::plugins::not_tests(
|
||||
self,
|
||||
expr,
|
||||
op,
|
||||
operand,
|
||||
check_not_in,
|
||||
check_not_is,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1531,16 +1553,15 @@ where
|
||||
let check_none_comparisons = self.settings.enabled.contains(&CheckCode::E711);
|
||||
let check_true_false_comparisons = self.settings.enabled.contains(&CheckCode::E712);
|
||||
if check_none_comparisons || check_true_false_comparisons {
|
||||
self.add_checks(
|
||||
pycodestyle::checks::literal_comparisons(
|
||||
left,
|
||||
ops,
|
||||
comparators,
|
||||
check_none_comparisons,
|
||||
check_true_false_comparisons,
|
||||
)
|
||||
.into_iter(),
|
||||
);
|
||||
pycodestyle::plugins::literal_comparisons(
|
||||
self,
|
||||
expr,
|
||||
left,
|
||||
ops,
|
||||
comparators,
|
||||
check_none_comparisons,
|
||||
check_true_false_comparisons,
|
||||
)
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::F632) {
|
||||
@@ -1646,7 +1667,7 @@ where
|
||||
elt,
|
||||
generators,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C416),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1940,6 +1961,16 @@ where
|
||||
flake8_bugbear::plugins::function_call_argument_default(self, arguments)
|
||||
}
|
||||
|
||||
// flake8-boolean-trap
|
||||
if self.settings.enabled.contains(&CheckCode::FBT001) {
|
||||
flake8_boolean_trap::plugins::check_positional_boolean_in_def(self, arguments);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::FBT002) {
|
||||
flake8_boolean_trap::plugins::check_boolean_default_value_in_function_definition(
|
||||
self, arguments,
|
||||
);
|
||||
}
|
||||
|
||||
// Bind, but intentionally avoid walking default expressions, as we handle them
|
||||
// upstream.
|
||||
for arg in &arguments.posonlyargs {
|
||||
@@ -2239,7 +2270,16 @@ impl<'a> Checker<'a> {
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::N806) {
|
||||
if matches!(self.current_scope().kind, ScopeKind::Function(..)) {
|
||||
pep8_naming::plugins::non_lowercase_variable_in_function(self, expr, parent, id)
|
||||
// Ignore globals.
|
||||
if !self
|
||||
.current_scope()
|
||||
.values
|
||||
.get(id)
|
||||
.map(|binding| matches!(binding.kind, BindingKind::Global))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
pep8_naming::plugins::non_lowercase_variable_in_function(self, expr, parent, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2588,7 +2628,7 @@ impl<'a> Checker<'a> {
|
||||
let child = self.parents[defined_by];
|
||||
let parent = defined_in.map(|defined_in| self.parents[defined_in]);
|
||||
|
||||
let fix = if self.patch() {
|
||||
let fix = if self.patch(&CheckCode::F401) {
|
||||
let deleted: Vec<&Stmt> = self
|
||||
.deletions
|
||||
.iter()
|
||||
|
||||
@@ -73,7 +73,7 @@ pub fn check_lines(
|
||||
end_location: Location::new(lineno + 1, line_length + 1),
|
||||
},
|
||||
);
|
||||
if autofix.patch() {
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(lineno + 1, 0),
|
||||
Location::new(lineno + 1, line_length + 1),
|
||||
@@ -195,7 +195,7 @@ pub fn check_lines(
|
||||
end_location: Location::new(row + 1, end),
|
||||
},
|
||||
);
|
||||
if autofix.patch() {
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(row + 1, start),
|
||||
Location::new(row + 1, lines[row].chars().count()),
|
||||
@@ -223,7 +223,7 @@ pub fn check_lines(
|
||||
end_location: Location::new(row + 1, end),
|
||||
},
|
||||
);
|
||||
if autofix.patch() {
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
if valid_codes.is_empty() {
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(row + 1, start),
|
||||
|
||||
@@ -36,7 +36,7 @@ pub fn check_tokens(
|
||||
// RUF001, RUF002, RUF003
|
||||
if enforce_ambiguous_unicode_character {
|
||||
if matches!(tok, Tok::String { .. } | Tok::Comment) {
|
||||
for check in rules::checks::ambiguous_unicode_character(
|
||||
checks.extend(rules::checks::ambiguous_unicode_character(
|
||||
locator,
|
||||
start,
|
||||
end,
|
||||
@@ -49,12 +49,9 @@ pub fn check_tokens(
|
||||
} else {
|
||||
Context::Comment
|
||||
},
|
||||
autofix.patch(),
|
||||
) {
|
||||
if settings.enabled.contains(check.kind.code()) {
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
settings,
|
||||
autofix,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
105
src/checks.rs
105
src/checks.rs
@@ -8,6 +8,7 @@ use strum_macros::{AsRefStr, EnumIter, EnumString};
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::flake8_quotes::settings::Quote;
|
||||
use crate::flake8_tidy_imports::settings::Strictness;
|
||||
use crate::pyupgrade::types::Primitive;
|
||||
|
||||
#[derive(
|
||||
@@ -102,6 +103,8 @@ pub enum CheckCode {
|
||||
B025,
|
||||
B026,
|
||||
B027,
|
||||
// flake8-blind-except
|
||||
BLE001,
|
||||
// flake8-comprehensions
|
||||
C400,
|
||||
C401,
|
||||
@@ -119,6 +122,10 @@ pub enum CheckCode {
|
||||
C415,
|
||||
C416,
|
||||
C417,
|
||||
// mccabe
|
||||
C901,
|
||||
// flake8-tidy-imports
|
||||
I252,
|
||||
// flake8-print
|
||||
T201,
|
||||
T203,
|
||||
@@ -152,7 +159,6 @@ pub enum CheckCode {
|
||||
YTT303,
|
||||
// pyupgrade
|
||||
U001,
|
||||
U002,
|
||||
U003,
|
||||
U004,
|
||||
U005,
|
||||
@@ -163,6 +169,7 @@ pub enum CheckCode {
|
||||
U010,
|
||||
U011,
|
||||
U012,
|
||||
U013,
|
||||
// pydocstyle
|
||||
D100,
|
||||
D101,
|
||||
@@ -233,6 +240,10 @@ pub enum CheckCode {
|
||||
S105,
|
||||
S106,
|
||||
S107,
|
||||
// flake8-boolean-trap
|
||||
FBT001,
|
||||
FBT002,
|
||||
FBT003,
|
||||
// Ruff
|
||||
RUF001,
|
||||
RUF002,
|
||||
@@ -251,12 +262,16 @@ pub enum CheckCategory {
|
||||
PEP8Naming,
|
||||
Flake8Bandit,
|
||||
Flake8Comprehensions,
|
||||
Flake8BooleanTrap,
|
||||
Flake8Bugbear,
|
||||
Flake8Builtins,
|
||||
Flake8TidyImports,
|
||||
Flake8Print,
|
||||
Flake8Quotes,
|
||||
Flake8Annotations,
|
||||
Flake82020,
|
||||
Flake8BlindExcept,
|
||||
McCabe,
|
||||
Ruff,
|
||||
Meta,
|
||||
}
|
||||
@@ -268,16 +283,20 @@ impl CheckCategory {
|
||||
CheckCategory::Pyflakes => "Pyflakes",
|
||||
CheckCategory::Isort => "isort",
|
||||
CheckCategory::Flake8Bandit => "flake8-bandit",
|
||||
CheckCategory::Flake8BooleanTrap => "flake8-boolean-trap",
|
||||
CheckCategory::Flake8Builtins => "flake8-builtins",
|
||||
CheckCategory::Flake8Bugbear => "flake8-bugbear",
|
||||
CheckCategory::Flake8Comprehensions => "flake8-comprehensions",
|
||||
CheckCategory::Flake8TidyImports => "flake8-tidy-imports",
|
||||
CheckCategory::Flake8Print => "flake8-print",
|
||||
CheckCategory::Flake8Quotes => "flake8-quotes",
|
||||
CheckCategory::Flake8Annotations => "flake8-annotations",
|
||||
CheckCategory::Flake82020 => "flake8-2020",
|
||||
CheckCategory::Flake8BlindExcept => "flake8-blind-except",
|
||||
CheckCategory::Pyupgrade => "pyupgrade",
|
||||
CheckCategory::Pydocstyle => "pydocstyle",
|
||||
CheckCategory::PEP8Naming => "pep8-naming",
|
||||
CheckCategory::McCabe => "mccabe",
|
||||
CheckCategory::Ruff => "Ruff-specific rules",
|
||||
CheckCategory::Meta => "Meta rules",
|
||||
}
|
||||
@@ -297,6 +316,9 @@ impl CheckCategory {
|
||||
CheckCategory::Flake8Comprehensions => {
|
||||
Some("https://pypi.org/project/flake8-comprehensions/3.10.1/")
|
||||
}
|
||||
CheckCategory::Flake8TidyImports => {
|
||||
Some("https://pypi.org/project/flake8-tidy-imports/4.8.0/")
|
||||
}
|
||||
CheckCategory::Flake8Print => Some("https://pypi.org/project/flake8-print/5.0.0/"),
|
||||
CheckCategory::Flake8Quotes => Some("https://pypi.org/project/flake8-quotes/3.3.1/"),
|
||||
CheckCategory::Flake8Annotations => {
|
||||
@@ -307,6 +329,13 @@ impl CheckCategory {
|
||||
CheckCategory::Pydocstyle => Some("https://pypi.org/project/pydocstyle/6.1.1/"),
|
||||
CheckCategory::PEP8Naming => Some("https://pypi.org/project/pep8-naming/0.13.2/"),
|
||||
CheckCategory::Flake8Bandit => Some("https://pypi.org/project/flake8-bandit/4.1.1/"),
|
||||
CheckCategory::Flake8BlindExcept => {
|
||||
Some("https://pypi.org/project/flake8-blind-except/0.2.1/")
|
||||
}
|
||||
CheckCategory::McCabe => Some("https://pypi.org/project/mccabe/0.7.0/"),
|
||||
CheckCategory::Flake8BooleanTrap => {
|
||||
Some("https://pypi.org/project/flake8-boolean-trap/0.1.0/")
|
||||
}
|
||||
CheckCategory::Ruff => None,
|
||||
CheckCategory::Meta => None,
|
||||
}
|
||||
@@ -381,6 +410,8 @@ pub enum CheckKind {
|
||||
BuiltinVariableShadowing(String),
|
||||
BuiltinArgumentShadowing(String),
|
||||
BuiltinAttributeShadowing(String),
|
||||
// flake8-blind-except
|
||||
BlindExcept,
|
||||
// flake8-bugbear
|
||||
UnaryPrefixIncrement,
|
||||
AssignmentToOsEnviron,
|
||||
@@ -424,6 +455,8 @@ pub enum CheckKind {
|
||||
UnnecessarySubscriptReversal(String),
|
||||
UnnecessaryComprehension(String),
|
||||
UnnecessaryMap(String),
|
||||
// flake8-tidy-imports
|
||||
BannedRelativeImport(Strictness),
|
||||
// flake8-print
|
||||
PrintFound,
|
||||
PPrintFound,
|
||||
@@ -457,7 +490,6 @@ pub enum CheckKind {
|
||||
SysVersionSlice1Referenced,
|
||||
// pyupgrade
|
||||
TypeOfPrimitive(Primitive),
|
||||
UnnecessaryAbspath,
|
||||
UselessMetaclassType,
|
||||
DeprecatedUnittestAlias(String, String),
|
||||
UselessObjectInheritance(String),
|
||||
@@ -468,6 +500,7 @@ pub enum CheckKind {
|
||||
UnnecessaryFutureImport(Vec<String>),
|
||||
UnnecessaryLRUCacheParams,
|
||||
UnnecessaryEncodeUTF8,
|
||||
ConvertTypedDictFunctionalToClass,
|
||||
// pydocstyle
|
||||
BlankLineAfterLastSection(String),
|
||||
BlankLineAfterSection(String),
|
||||
@@ -538,6 +571,12 @@ pub enum CheckKind {
|
||||
HardcodedPasswordString(String),
|
||||
HardcodedPasswordFuncArg(String),
|
||||
HardcodedPasswordDefault(String),
|
||||
// mccabe
|
||||
FunctionIsTooComplex(String, usize),
|
||||
// flake8-boolean-trap
|
||||
BooleanPositionalArgInFunctionDefinition,
|
||||
BooleanDefaultValueInFunctionDefinition,
|
||||
BooleanPositionalValueInFunctionCall,
|
||||
// Ruff
|
||||
AmbiguousUnicodeCharacterString(char, char),
|
||||
AmbiguousUnicodeCharacterDocstring(char, char),
|
||||
@@ -684,6 +723,8 @@ impl CheckCode {
|
||||
}
|
||||
CheckCode::C416 => CheckKind::UnnecessaryComprehension("(list|set)".to_string()),
|
||||
CheckCode::C417 => CheckKind::UnnecessaryMap("(list|set|dict)".to_string()),
|
||||
// flake8-tidy-imports
|
||||
CheckCode::I252 => CheckKind::BannedRelativeImport(Strictness::All),
|
||||
// flake8-print
|
||||
CheckCode::T201 => CheckKind::PrintFound,
|
||||
CheckCode::T203 => CheckKind::PPrintFound,
|
||||
@@ -715,9 +756,10 @@ impl CheckCode {
|
||||
CheckCode::YTT301 => CheckKind::SysVersion0Referenced,
|
||||
CheckCode::YTT302 => CheckKind::SysVersionCmpStr10,
|
||||
CheckCode::YTT303 => CheckKind::SysVersionSlice1Referenced,
|
||||
// flake8-blind-except
|
||||
CheckCode::BLE001 => CheckKind::BlindExcept,
|
||||
// pyupgrade
|
||||
CheckCode::U001 => CheckKind::UselessMetaclassType,
|
||||
CheckCode::U002 => CheckKind::UnnecessaryAbspath,
|
||||
CheckCode::U003 => CheckKind::TypeOfPrimitive(Primitive::Str),
|
||||
CheckCode::U004 => CheckKind::UselessObjectInheritance("...".to_string()),
|
||||
CheckCode::U005 => CheckKind::DeprecatedUnittestAlias(
|
||||
@@ -731,6 +773,7 @@ impl CheckCode {
|
||||
CheckCode::U010 => CheckKind::UnnecessaryFutureImport(vec!["...".to_string()]),
|
||||
CheckCode::U011 => CheckKind::UnnecessaryLRUCacheParams,
|
||||
CheckCode::U012 => CheckKind::UnnecessaryEncodeUTF8,
|
||||
CheckCode::U013 => CheckKind::ConvertTypedDictFunctionalToClass,
|
||||
// pydocstyle
|
||||
CheckCode::D100 => CheckKind::PublicModule,
|
||||
CheckCode::D101 => CheckKind::PublicClass,
|
||||
@@ -817,6 +860,11 @@ impl CheckCode {
|
||||
CheckCode::S105 => CheckKind::HardcodedPasswordString("...".to_string()),
|
||||
CheckCode::S106 => CheckKind::HardcodedPasswordFuncArg("...".to_string()),
|
||||
CheckCode::S107 => CheckKind::HardcodedPasswordDefault("...".to_string()),
|
||||
CheckCode::C901 => CheckKind::FunctionIsTooComplex("...".to_string(), 10),
|
||||
// flake8-boolean-trap
|
||||
CheckCode::FBT001 => CheckKind::BooleanPositionalArgInFunctionDefinition,
|
||||
CheckCode::FBT002 => CheckKind::BooleanDefaultValueInFunctionDefinition,
|
||||
CheckCode::FBT003 => CheckKind::BooleanPositionalValueInFunctionCall,
|
||||
// Ruff
|
||||
CheckCode::RUF001 => CheckKind::AmbiguousUnicodeCharacterString('𝐁', 'B'),
|
||||
CheckCode::RUF002 => CheckKind::AmbiguousUnicodeCharacterDocstring('𝐁', 'B'),
|
||||
@@ -900,6 +948,7 @@ impl CheckCode {
|
||||
CheckCode::B025 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B026 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B027 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::BLE001 => CheckCategory::Flake8BlindExcept,
|
||||
CheckCode::C400 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C401 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C402 => CheckCategory::Flake8Comprehensions,
|
||||
@@ -916,6 +965,7 @@ impl CheckCode {
|
||||
CheckCode::C415 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C416 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C417 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::I252 => CheckCategory::Flake8TidyImports,
|
||||
CheckCode::T201 => CheckCategory::Flake8Print,
|
||||
CheckCode::T203 => CheckCategory::Flake8Print,
|
||||
CheckCode::Q000 => CheckCategory::Flake8Quotes,
|
||||
@@ -944,7 +994,6 @@ impl CheckCode {
|
||||
CheckCode::YTT302 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT303 => CheckCategory::Flake82020,
|
||||
CheckCode::U001 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U002 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U003 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U004 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U005 => CheckCategory::Pyupgrade,
|
||||
@@ -955,6 +1004,7 @@ impl CheckCode {
|
||||
CheckCode::U010 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U011 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U012 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U013 => CheckCategory::Pyupgrade,
|
||||
CheckCode::D100 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D101 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D102 => CheckCategory::Pydocstyle,
|
||||
@@ -1021,6 +1071,10 @@ impl CheckCode {
|
||||
CheckCode::S105 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::S106 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::S107 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::C901 => CheckCategory::McCabe,
|
||||
CheckCode::FBT001 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::FBT002 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::FBT003 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::RUF001 => CheckCategory::Ruff,
|
||||
CheckCode::RUF002 => CheckCategory::Ruff,
|
||||
CheckCode::RUF003 => CheckCategory::Ruff,
|
||||
@@ -1109,6 +1163,8 @@ impl CheckKind {
|
||||
CheckKind::DuplicateTryBlockException(_) => &CheckCode::B025,
|
||||
CheckKind::StarArgUnpackingAfterKeywordArg => &CheckCode::B026,
|
||||
CheckKind::EmptyMethodWithoutAbstractDecorator(_) => &CheckCode::B027,
|
||||
// flake8-blind-except
|
||||
CheckKind::BlindExcept => &CheckCode::BLE001,
|
||||
// flake8-comprehensions
|
||||
CheckKind::UnnecessaryGeneratorList => &CheckCode::C400,
|
||||
CheckKind::UnnecessaryGeneratorSet => &CheckCode::C401,
|
||||
@@ -1126,6 +1182,8 @@ impl CheckKind {
|
||||
CheckKind::UnnecessarySubscriptReversal(_) => &CheckCode::C415,
|
||||
CheckKind::UnnecessaryComprehension(..) => &CheckCode::C416,
|
||||
CheckKind::UnnecessaryMap(_) => &CheckCode::C417,
|
||||
// flake8-tidy-imports
|
||||
CheckKind::BannedRelativeImport(_) => &CheckCode::I252,
|
||||
// flake8-print
|
||||
CheckKind::PrintFound => &CheckCode::T201,
|
||||
CheckKind::PPrintFound => &CheckCode::T203,
|
||||
@@ -1159,7 +1217,6 @@ impl CheckKind {
|
||||
CheckKind::SysVersionSlice1Referenced => &CheckCode::YTT303,
|
||||
// pyupgrade
|
||||
CheckKind::TypeOfPrimitive(_) => &CheckCode::U003,
|
||||
CheckKind::UnnecessaryAbspath => &CheckCode::U002,
|
||||
CheckKind::UselessMetaclassType => &CheckCode::U001,
|
||||
CheckKind::DeprecatedUnittestAlias(..) => &CheckCode::U005,
|
||||
CheckKind::UsePEP585Annotation(_) => &CheckCode::U006,
|
||||
@@ -1170,6 +1227,7 @@ impl CheckKind {
|
||||
CheckKind::UnnecessaryFutureImport(_) => &CheckCode::U010,
|
||||
CheckKind::UnnecessaryLRUCacheParams => &CheckCode::U011,
|
||||
CheckKind::UnnecessaryEncodeUTF8 => &CheckCode::U012,
|
||||
CheckKind::ConvertTypedDictFunctionalToClass => &CheckCode::U013,
|
||||
// pydocstyle
|
||||
CheckKind::BlankLineAfterLastSection(_) => &CheckCode::D413,
|
||||
CheckKind::BlankLineAfterSection(_) => &CheckCode::D410,
|
||||
@@ -1240,6 +1298,12 @@ impl CheckKind {
|
||||
CheckKind::HardcodedPasswordString(..) => &CheckCode::S105,
|
||||
CheckKind::HardcodedPasswordFuncArg(..) => &CheckCode::S106,
|
||||
CheckKind::HardcodedPasswordDefault(..) => &CheckCode::S107,
|
||||
// McCabe
|
||||
CheckKind::FunctionIsTooComplex(..) => &CheckCode::C901,
|
||||
// flake8-boolean-trap
|
||||
CheckKind::BooleanPositionalArgInFunctionDefinition => &CheckCode::FBT001,
|
||||
CheckKind::BooleanDefaultValueInFunctionDefinition => &CheckCode::FBT002,
|
||||
CheckKind::BooleanPositionalValueInFunctionCall => &CheckCode::FBT003,
|
||||
// Ruff
|
||||
CheckKind::AmbiguousUnicodeCharacterString(..) => &CheckCode::RUF001,
|
||||
CheckKind::AmbiguousUnicodeCharacterDocstring(..) => &CheckCode::RUF002,
|
||||
@@ -1579,6 +1643,13 @@ impl CheckKind {
|
||||
format!("Unnecessary `map` usage (rewrite using a `{obj_type}` comprehension)")
|
||||
}
|
||||
}
|
||||
// flake8-tidy-imports
|
||||
CheckKind::BannedRelativeImport(strictness) => match strictness {
|
||||
Strictness::Parents => {
|
||||
"Relative imports from parent modules are banned".to_string()
|
||||
}
|
||||
Strictness::All => "Relative imports are banned".to_string(),
|
||||
},
|
||||
// flake8-print
|
||||
CheckKind::PrintFound => "`print` found".to_string(),
|
||||
CheckKind::PPrintFound => "`pprint` found".to_string(),
|
||||
@@ -1674,9 +1745,6 @@ impl CheckKind {
|
||||
CheckKind::TypeOfPrimitive(primitive) => {
|
||||
format!("Use `{}` instead of `type(...)`", primitive.builtin())
|
||||
}
|
||||
CheckKind::UnnecessaryAbspath => {
|
||||
"`abspath(__file__)` is unnecessary in Python 3.9 and later".to_string()
|
||||
}
|
||||
CheckKind::UselessMetaclassType => "`__metaclass__ = type` is implied".to_string(),
|
||||
CheckKind::DeprecatedUnittestAlias(alias, target) => {
|
||||
format!("`{alias}` is deprecated, use `{target}` instead")
|
||||
@@ -1708,6 +1776,9 @@ impl CheckKind {
|
||||
"Unnecessary parameters to `functools.lru_cache`".to_string()
|
||||
}
|
||||
CheckKind::UnnecessaryEncodeUTF8 => "Unnecessary call to `encode` as UTF-8".to_string(),
|
||||
CheckKind::ConvertTypedDictFunctionalToClass => {
|
||||
"Convert `TypedDict` functional syntax to class syntax".to_string()
|
||||
}
|
||||
// pydocstyle
|
||||
CheckKind::FitsOnOneLine => "One-line docstring should fit on one line".to_string(),
|
||||
CheckKind::BlankLineAfterSummary => {
|
||||
@@ -1888,6 +1959,22 @@ impl CheckKind {
|
||||
CheckKind::HardcodedPasswordDefault(string) => {
|
||||
format!("Possible hardcoded password: `\"{string}\"`")
|
||||
}
|
||||
// flake8-blind-except
|
||||
CheckKind::BlindExcept => "Blind except Exception: statement".to_string(),
|
||||
// McCabe
|
||||
CheckKind::FunctionIsTooComplex(name, complexity) => {
|
||||
format!("`{name}` is too complex ({complexity})")
|
||||
}
|
||||
// flake8-boolean-trap
|
||||
CheckKind::BooleanPositionalArgInFunctionDefinition => {
|
||||
"Boolean positional arg in function definition".to_string()
|
||||
}
|
||||
CheckKind::BooleanDefaultValueInFunctionDefinition => {
|
||||
"Boolean default value in function definition".to_string()
|
||||
}
|
||||
CheckKind::BooleanPositionalValueInFunctionCall => {
|
||||
"Boolean positional value in function call".to_string()
|
||||
}
|
||||
// Ruff
|
||||
CheckKind::AmbiguousUnicodeCharacterString(confusable, representant) => {
|
||||
format!(
|
||||
@@ -1986,10 +2073,10 @@ impl CheckKind {
|
||||
| CheckKind::SectionUnderlineNotOverIndented(_)
|
||||
| CheckKind::SuperCallWithParameters
|
||||
| CheckKind::TypeOfPrimitive(_)
|
||||
| CheckKind::UnnecessaryAbspath
|
||||
| CheckKind::UnnecessaryCollectionCall(_)
|
||||
| CheckKind::UnnecessaryComprehension(_)
|
||||
| CheckKind::UnnecessaryEncodeUTF8
|
||||
| CheckKind::ConvertTypedDictFunctionalToClass
|
||||
| CheckKind::UnnecessaryFutureImport(_)
|
||||
| CheckKind::UnnecessaryGeneratorDict
|
||||
| CheckKind::UnnecessaryGeneratorList
|
||||
|
||||
@@ -63,6 +63,10 @@ pub enum CheckCodePrefix {
|
||||
B025,
|
||||
B026,
|
||||
B027,
|
||||
BLE,
|
||||
BLE0,
|
||||
BLE00,
|
||||
BLE001,
|
||||
C,
|
||||
C4,
|
||||
C40,
|
||||
@@ -83,6 +87,9 @@ pub enum CheckCodePrefix {
|
||||
C415,
|
||||
C416,
|
||||
C417,
|
||||
C9,
|
||||
C90,
|
||||
C901,
|
||||
D,
|
||||
D1,
|
||||
D10,
|
||||
@@ -211,10 +218,19 @@ pub enum CheckCodePrefix {
|
||||
F9,
|
||||
F90,
|
||||
F901,
|
||||
FBT,
|
||||
FBT0,
|
||||
FBT00,
|
||||
FBT001,
|
||||
FBT002,
|
||||
FBT003,
|
||||
I,
|
||||
I0,
|
||||
I00,
|
||||
I001,
|
||||
I2,
|
||||
I25,
|
||||
I252,
|
||||
M,
|
||||
M0,
|
||||
M00,
|
||||
@@ -269,7 +285,6 @@ pub enum CheckCodePrefix {
|
||||
U0,
|
||||
U00,
|
||||
U001,
|
||||
U002,
|
||||
U003,
|
||||
U004,
|
||||
U005,
|
||||
@@ -281,6 +296,7 @@ pub enum CheckCodePrefix {
|
||||
U010,
|
||||
U011,
|
||||
U012,
|
||||
U013,
|
||||
W,
|
||||
W2,
|
||||
W29,
|
||||
@@ -478,6 +494,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::B025 => vec![CheckCode::B025],
|
||||
CheckCodePrefix::B026 => vec![CheckCode::B026],
|
||||
CheckCodePrefix::B027 => vec![CheckCode::B027],
|
||||
CheckCodePrefix::BLE => vec![CheckCode::BLE001],
|
||||
CheckCodePrefix::BLE0 => vec![CheckCode::BLE001],
|
||||
CheckCodePrefix::BLE00 => vec![CheckCode::BLE001],
|
||||
CheckCodePrefix::BLE001 => vec![CheckCode::BLE001],
|
||||
CheckCodePrefix::C => vec![
|
||||
CheckCode::C400,
|
||||
CheckCode::C401,
|
||||
@@ -495,6 +515,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::C415,
|
||||
CheckCode::C416,
|
||||
CheckCode::C417,
|
||||
CheckCode::C901,
|
||||
],
|
||||
CheckCodePrefix::C4 => vec![
|
||||
CheckCode::C400,
|
||||
@@ -550,6 +571,9 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::C415 => vec![CheckCode::C415],
|
||||
CheckCodePrefix::C416 => vec![CheckCode::C416],
|
||||
CheckCodePrefix::C417 => vec![CheckCode::C417],
|
||||
CheckCodePrefix::C9 => vec![CheckCode::C901],
|
||||
CheckCodePrefix::C90 => vec![CheckCode::C901],
|
||||
CheckCodePrefix::C901 => vec![CheckCode::C901],
|
||||
CheckCodePrefix::D => vec![
|
||||
CheckCode::D100,
|
||||
CheckCode::D101,
|
||||
@@ -926,10 +950,19 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::F9 => vec![CheckCode::F901],
|
||||
CheckCodePrefix::F90 => vec![CheckCode::F901],
|
||||
CheckCodePrefix::F901 => vec![CheckCode::F901],
|
||||
CheckCodePrefix::I => vec![CheckCode::I001],
|
||||
CheckCodePrefix::FBT => vec![CheckCode::FBT001, CheckCode::FBT002, CheckCode::FBT003],
|
||||
CheckCodePrefix::FBT0 => vec![CheckCode::FBT001, CheckCode::FBT002, CheckCode::FBT003],
|
||||
CheckCodePrefix::FBT00 => vec![CheckCode::FBT001, CheckCode::FBT002, CheckCode::FBT003],
|
||||
CheckCodePrefix::FBT001 => vec![CheckCode::FBT001],
|
||||
CheckCodePrefix::FBT002 => vec![CheckCode::FBT002],
|
||||
CheckCodePrefix::FBT003 => vec![CheckCode::FBT003],
|
||||
CheckCodePrefix::I => vec![CheckCode::I252, CheckCode::I001],
|
||||
CheckCodePrefix::I0 => vec![CheckCode::I001],
|
||||
CheckCodePrefix::I00 => vec![CheckCode::I001],
|
||||
CheckCodePrefix::I001 => vec![CheckCode::I001],
|
||||
CheckCodePrefix::I2 => vec![CheckCode::I252],
|
||||
CheckCodePrefix::I25 => vec![CheckCode::I252],
|
||||
CheckCodePrefix::I252 => vec![CheckCode::I252],
|
||||
CheckCodePrefix::M => vec![CheckCode::M001],
|
||||
CheckCodePrefix::M0 => vec![CheckCode::M001],
|
||||
CheckCodePrefix::M00 => vec![CheckCode::M001],
|
||||
@@ -1067,7 +1100,6 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::T203 => vec![CheckCode::T203],
|
||||
CheckCodePrefix::U => vec![
|
||||
CheckCode::U001,
|
||||
CheckCode::U002,
|
||||
CheckCode::U003,
|
||||
CheckCode::U004,
|
||||
CheckCode::U005,
|
||||
@@ -1078,10 +1110,10 @@ impl CheckCodePrefix {
|
||||
CheckCode::U010,
|
||||
CheckCode::U011,
|
||||
CheckCode::U012,
|
||||
CheckCode::U013,
|
||||
],
|
||||
CheckCodePrefix::U0 => vec![
|
||||
CheckCode::U001,
|
||||
CheckCode::U002,
|
||||
CheckCode::U003,
|
||||
CheckCode::U004,
|
||||
CheckCode::U005,
|
||||
@@ -1092,10 +1124,10 @@ impl CheckCodePrefix {
|
||||
CheckCode::U010,
|
||||
CheckCode::U011,
|
||||
CheckCode::U012,
|
||||
CheckCode::U013,
|
||||
],
|
||||
CheckCodePrefix::U00 => vec![
|
||||
CheckCode::U001,
|
||||
CheckCode::U002,
|
||||
CheckCode::U003,
|
||||
CheckCode::U004,
|
||||
CheckCode::U005,
|
||||
@@ -1105,7 +1137,6 @@ impl CheckCodePrefix {
|
||||
CheckCode::U009,
|
||||
],
|
||||
CheckCodePrefix::U001 => vec![CheckCode::U001],
|
||||
CheckCodePrefix::U002 => vec![CheckCode::U002],
|
||||
CheckCodePrefix::U003 => vec![CheckCode::U003],
|
||||
CheckCodePrefix::U004 => vec![CheckCode::U004],
|
||||
CheckCodePrefix::U005 => vec![CheckCode::U005],
|
||||
@@ -1113,10 +1144,16 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::U007 => vec![CheckCode::U007],
|
||||
CheckCodePrefix::U008 => vec![CheckCode::U008],
|
||||
CheckCodePrefix::U009 => vec![CheckCode::U009],
|
||||
CheckCodePrefix::U01 => vec![CheckCode::U010, CheckCode::U011, CheckCode::U012],
|
||||
CheckCodePrefix::U01 => vec![
|
||||
CheckCode::U010,
|
||||
CheckCode::U011,
|
||||
CheckCode::U012,
|
||||
CheckCode::U013,
|
||||
],
|
||||
CheckCodePrefix::U010 => vec![CheckCode::U010],
|
||||
CheckCodePrefix::U011 => vec![CheckCode::U011],
|
||||
CheckCodePrefix::U012 => vec![CheckCode::U012],
|
||||
CheckCodePrefix::U013 => vec![CheckCode::U013],
|
||||
CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605],
|
||||
CheckCodePrefix::W2 => vec![CheckCode::W292],
|
||||
CheckCodePrefix::W29 => vec![CheckCode::W292],
|
||||
@@ -1225,6 +1262,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::B025 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B026 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B027 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::BLE => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::BLE0 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::BLE00 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::BLE001 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::C => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::C4 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::C40 => PrefixSpecificity::Tens,
|
||||
@@ -1245,6 +1286,9 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::C415 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::C416 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::C417 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::C9 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::C90 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::C901 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::D => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::D1 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::D10 => PrefixSpecificity::Tens,
|
||||
@@ -1373,10 +1417,19 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::F9 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::F90 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::F901 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::FBT => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::FBT0 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::FBT00 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::FBT001 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::FBT002 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::FBT003 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::I => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::I0 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::I00 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::I001 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::I2 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::I25 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::I252 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::M => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::M0 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::M00 => PrefixSpecificity::Tens,
|
||||
@@ -1431,7 +1484,6 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::U0 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::U00 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::U001 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U002 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U003 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U004 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U005 => PrefixSpecificity::Explicit,
|
||||
@@ -1443,6 +1495,7 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::U010 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U011 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U012 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U013 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::W => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::W2 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::W29 => PrefixSpecificity::Tens,
|
||||
|
||||
81
src/cli.rs
81
src/cli.rs
@@ -1,19 +1,16 @@
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{command, Parser};
|
||||
use fnv::FnvHashMap;
|
||||
use log::warn;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::logging::LogLevel;
|
||||
use crate::printer::SerializationFormat;
|
||||
use crate::settings::configuration::Configuration;
|
||||
use crate::settings::types::{PatternPrefixPair, PerFileIgnore, PythonVersion};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(author, about = "ruff: An extremely fast Python linter.")]
|
||||
#[command(author, about = "Ruff: An extremely fast Python linter.")]
|
||||
#[command(version)]
|
||||
pub struct Cli {
|
||||
#[arg(required = true)]
|
||||
@@ -66,16 +63,27 @@ pub struct Cli {
|
||||
/// excluded ones.
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub extend_exclude: Vec<String>,
|
||||
/// List of error codes to treat as eligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub fixable: Vec<CheckCodePrefix>,
|
||||
/// List of error codes to treat as ineligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub unfixable: Vec<CheckCodePrefix>,
|
||||
/// List of mappings from file pattern to code to exclude
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub per_file_ignores: Vec<PatternPrefixPair>,
|
||||
/// Output serialization format for error messages.
|
||||
#[arg(long, value_enum, default_value_t=SerializationFormat::Text)]
|
||||
pub format: SerializationFormat,
|
||||
/// See the files ruff will be run against with the current settings.
|
||||
/// Show violations with source code.
|
||||
#[arg(long)]
|
||||
pub show_source: bool,
|
||||
/// See the files Ruff will be run against with the current settings.
|
||||
#[arg(long)]
|
||||
pub show_files: bool,
|
||||
/// See ruff's settings.
|
||||
/// See Ruff's settings.
|
||||
#[arg(long)]
|
||||
pub show_settings: bool,
|
||||
/// Enable automatic additions of noqa directives to failing lines.
|
||||
@@ -91,6 +99,9 @@ pub struct Cli {
|
||||
/// formatting.
|
||||
#[arg(long)]
|
||||
pub line_length: Option<usize>,
|
||||
/// Max McCabe complexity allowed for a function.
|
||||
#[arg(long)]
|
||||
pub max_complexity: Option<usize>,
|
||||
/// Round-trip auto-formatting.
|
||||
// TODO(charlie): This should be a sub-command.
|
||||
#[arg(long, hide = true)]
|
||||
@@ -131,64 +142,6 @@ pub fn extract_log_level(cli: &Cli) -> LogLevel {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Warnable {
|
||||
Select,
|
||||
ExtendSelect,
|
||||
}
|
||||
|
||||
impl fmt::Display for Warnable {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Warnable::Select => fmt.write_str("--select"),
|
||||
Warnable::ExtendSelect => fmt.write_str("--extend-select"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Warn the user if they attempt to enable a code that won't be respected.
|
||||
pub fn warn_on(
|
||||
flag: Warnable,
|
||||
codes: &[CheckCodePrefix],
|
||||
cli_ignore: &[CheckCodePrefix],
|
||||
cli_extend_ignore: &[CheckCodePrefix],
|
||||
pyproject_configuration: &Configuration,
|
||||
pyproject_path: Option<&PathBuf>,
|
||||
) {
|
||||
for code in codes {
|
||||
if !cli_ignore.is_empty() {
|
||||
if cli_ignore.contains(code) {
|
||||
warn!("{code:?} was passed to {flag}, but ignored via --ignore")
|
||||
}
|
||||
} else if pyproject_configuration.ignore.contains(code) {
|
||||
if let Some(path) = pyproject_path {
|
||||
warn!(
|
||||
"{code:?} was passed to {flag}, but ignored by the `ignore` field in {}",
|
||||
path.to_string_lossy()
|
||||
)
|
||||
} else {
|
||||
warn!("{code:?} was passed to {flag}, but ignored by the default `ignore` field",)
|
||||
}
|
||||
}
|
||||
if !cli_extend_ignore.is_empty() {
|
||||
if cli_extend_ignore.contains(code) {
|
||||
warn!("{code:?} was passed to {flag}, but ignored via --extend-ignore")
|
||||
}
|
||||
} else if pyproject_configuration.extend_ignore.contains(code) {
|
||||
if let Some(path) = pyproject_path {
|
||||
warn!(
|
||||
"{code:?} was passed to {flag}, but ignored by the `extend_ignore` field in {}",
|
||||
path.to_string_lossy()
|
||||
)
|
||||
} else {
|
||||
warn!(
|
||||
"{code:?} was passed to {flag}, but ignored by the default `extend_ignore` \
|
||||
field"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a list of `PatternPrefixPair` structs to `PerFileIgnore`.
|
||||
pub fn collect_per_file_ignores(
|
||||
pairs: Vec<PatternPrefixPair>,
|
||||
|
||||
182
src/code_gen.rs
182
src/code_gen.rs
@@ -55,18 +55,14 @@ impl SourceGenerator {
|
||||
}
|
||||
|
||||
fn newline(&mut self) -> fmt::Result {
|
||||
if self.initial {
|
||||
self.initial = false;
|
||||
} else {
|
||||
if !self.initial {
|
||||
self.new_lines = std::cmp::max(self.new_lines, 1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn newlines(&mut self, extra: usize) -> fmt::Result {
|
||||
if self.initial {
|
||||
self.initial = false;
|
||||
} else {
|
||||
if !self.initial {
|
||||
self.new_lines = std::cmp::max(self.new_lines, 1 + extra);
|
||||
}
|
||||
Ok(())
|
||||
@@ -121,6 +117,7 @@ impl SourceGenerator {
|
||||
self.newline()?;
|
||||
self.p(&" ".repeat(self.indentation))?;
|
||||
$body
|
||||
self.initial = false;
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -145,12 +142,11 @@ impl SourceGenerator {
|
||||
self.unparse_expr(returns, precedence::EXPR)?;
|
||||
}
|
||||
self.p(":")?;
|
||||
self.body(body)?;
|
||||
|
||||
if self.indentation == 0 {
|
||||
self.newlines(2)?;
|
||||
}
|
||||
})
|
||||
});
|
||||
self.body(body)?;
|
||||
if self.indentation == 0 {
|
||||
self.newlines(2)?;
|
||||
}
|
||||
}
|
||||
StmtKind::AsyncFunctionDef {
|
||||
name,
|
||||
@@ -172,11 +168,11 @@ impl SourceGenerator {
|
||||
self.unparse_expr(returns, precedence::EXPR)?;
|
||||
}
|
||||
self.p(":")?;
|
||||
self.body(body)?;
|
||||
if self.indentation == 0 {
|
||||
self.newlines(2)?;
|
||||
}
|
||||
})
|
||||
});
|
||||
self.body(body)?;
|
||||
if self.indentation == 0 {
|
||||
self.newlines(2)?;
|
||||
}
|
||||
}
|
||||
StmtKind::ClassDef {
|
||||
name,
|
||||
@@ -209,11 +205,11 @@ impl SourceGenerator {
|
||||
}
|
||||
self.p_if(!first, ")")?;
|
||||
self.p(":")?;
|
||||
self.body(body)?;
|
||||
if self.indentation == 0 {
|
||||
self.newlines(2)?;
|
||||
}
|
||||
})
|
||||
});
|
||||
self.body(body)?;
|
||||
if self.indentation == 0 {
|
||||
self.newlines(2)?;
|
||||
}
|
||||
}
|
||||
StmtKind::Return { value } => {
|
||||
statement!({
|
||||
@@ -299,14 +295,14 @@ impl SourceGenerator {
|
||||
self.p(" in ")?;
|
||||
self.unparse_expr(iter, precedence::TEST)?;
|
||||
self.p(":")?;
|
||||
self.body(body)?;
|
||||
if !orelse.is_empty() {
|
||||
statement!({
|
||||
self.p("else:")?;
|
||||
self.body(orelse)?;
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
self.body(body)?;
|
||||
if !orelse.is_empty() {
|
||||
statement!({
|
||||
self.p("else:")?;
|
||||
});
|
||||
self.body(orelse)?;
|
||||
}
|
||||
}
|
||||
StmtKind::AsyncFor {
|
||||
target,
|
||||
@@ -321,59 +317,59 @@ impl SourceGenerator {
|
||||
self.p(" in ")?;
|
||||
self.unparse_expr(iter, precedence::TEST)?;
|
||||
self.p(":")?;
|
||||
self.body(body)?;
|
||||
if !orelse.is_empty() {
|
||||
statement!({
|
||||
self.p("else:")?;
|
||||
self.body(orelse)?;
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
self.body(body)?;
|
||||
if !orelse.is_empty() {
|
||||
statement!({
|
||||
self.p("else:")?;
|
||||
});
|
||||
self.body(orelse)?;
|
||||
}
|
||||
}
|
||||
StmtKind::While { test, body, orelse } => {
|
||||
statement!({
|
||||
self.p("while ")?;
|
||||
self.unparse_expr(test, precedence::TEST)?;
|
||||
self.p(":")?;
|
||||
self.body(body)?;
|
||||
if !orelse.is_empty() {
|
||||
statement!({
|
||||
self.p("else:")?;
|
||||
self.body(orelse)?;
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
self.body(body)?;
|
||||
if !orelse.is_empty() {
|
||||
statement!({
|
||||
self.p("else:")?;
|
||||
});
|
||||
self.body(orelse)?;
|
||||
}
|
||||
}
|
||||
StmtKind::If { test, body, orelse } => {
|
||||
statement!({
|
||||
self.p("if ")?;
|
||||
self.unparse_expr(test, precedence::TEST)?;
|
||||
self.p(":")?;
|
||||
self.body(body)?;
|
||||
|
||||
let mut orelse_: &Vec<Stmt<U>> = orelse;
|
||||
loop {
|
||||
if orelse_.len() == 1 && matches!(orelse_[0].node, StmtKind::If { .. }) {
|
||||
if let StmtKind::If { body, test, orelse } = &orelse_[0].node {
|
||||
statement!({
|
||||
self.p("elif ")?;
|
||||
self.unparse_expr(test, precedence::TEST)?;
|
||||
self.p(":")?;
|
||||
self.body(body)?;
|
||||
});
|
||||
orelse_ = orelse;
|
||||
}
|
||||
} else {
|
||||
if !orelse_.is_empty() {
|
||||
statement!({
|
||||
self.p("else:")?;
|
||||
self.body(orelse_)?;
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
self.body(body)?;
|
||||
|
||||
let mut orelse_: &Vec<Stmt<U>> = orelse;
|
||||
loop {
|
||||
if orelse_.len() == 1 && matches!(orelse_[0].node, StmtKind::If { .. }) {
|
||||
if let StmtKind::If { body, test, orelse } = &orelse_[0].node {
|
||||
statement!({
|
||||
self.p("elif ")?;
|
||||
self.unparse_expr(test, precedence::TEST)?;
|
||||
self.p(":")?;
|
||||
});
|
||||
self.body(body)?;
|
||||
orelse_ = orelse;
|
||||
}
|
||||
} else {
|
||||
if !orelse_.is_empty() {
|
||||
statement!({
|
||||
self.p("else:")?;
|
||||
});
|
||||
self.body(orelse_)?;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
StmtKind::With { items, body, .. } => {
|
||||
statement!({
|
||||
@@ -384,8 +380,8 @@ impl SourceGenerator {
|
||||
self.unparse_withitem(item)?;
|
||||
}
|
||||
self.p(":")?;
|
||||
self.body(body)?;
|
||||
})
|
||||
});
|
||||
self.body(body)?;
|
||||
}
|
||||
StmtKind::AsyncWith { items, body, .. } => {
|
||||
statement!({
|
||||
@@ -396,8 +392,8 @@ impl SourceGenerator {
|
||||
self.unparse_withitem(item)?;
|
||||
}
|
||||
self.p(":")?;
|
||||
self.body(body)?;
|
||||
})
|
||||
});
|
||||
self.body(body)?;
|
||||
}
|
||||
StmtKind::Match { .. } => {}
|
||||
StmtKind::Raise { exc, cause } => {
|
||||
@@ -421,27 +417,27 @@ impl SourceGenerator {
|
||||
} => {
|
||||
statement!({
|
||||
self.p("try:")?;
|
||||
self.body(body)?;
|
||||
});
|
||||
self.body(body)?;
|
||||
|
||||
for handler in handlers {
|
||||
statement!({
|
||||
self.unparse_excepthandler(handler)?;
|
||||
});
|
||||
}
|
||||
for handler in handlers {
|
||||
statement!({
|
||||
self.unparse_excepthandler(handler)?;
|
||||
});
|
||||
}
|
||||
|
||||
if !orelse.is_empty() {
|
||||
statement!({
|
||||
self.p("else:")?;
|
||||
self.body(orelse)?;
|
||||
});
|
||||
}
|
||||
if !finalbody.is_empty() {
|
||||
statement!({
|
||||
self.p("finally:")?;
|
||||
self.body(finalbody)?;
|
||||
});
|
||||
}
|
||||
})
|
||||
if !orelse.is_empty() {
|
||||
statement!({
|
||||
self.p("else:")?;
|
||||
});
|
||||
self.body(orelse)?;
|
||||
}
|
||||
if !finalbody.is_empty() {
|
||||
statement!({
|
||||
self.p("finally:")?;
|
||||
});
|
||||
self.body(finalbody)?;
|
||||
}
|
||||
}
|
||||
StmtKind::Assert { test, msg } => {
|
||||
statement!({
|
||||
|
||||
1
src/flake8_blind_except/mod.rs
Normal file
1
src/flake8_blind_except/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod plugins;
|
||||
23
src/flake8_blind_except/plugins.rs
Normal file
23
src/flake8_blind_except/plugins.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
pub fn blind_except(checker: &mut Checker, handlers: &[Excepthandler]) {
|
||||
for handler in handlers {
|
||||
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler.node;
|
||||
if let Some(type_) = type_ {
|
||||
if let ExprKind::Name { id, .. } = &type_.node {
|
||||
for exception in ["BaseException", "Exception"] {
|
||||
if id == exception {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::BlindExcept,
|
||||
Range::from_located(type_),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
src/flake8_boolean_trap/mod.rs
Normal file
1
src/flake8_boolean_trap/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod plugins;
|
||||
71
src/flake8_boolean_trap/plugins.rs
Normal file
71
src/flake8_boolean_trap/plugins.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
use rustpython_ast::{Arguments, ExprKind};
|
||||
use rustpython_parser::ast::{Constant, Expr};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
fn is_boolean_arg(arg: &Expr) -> bool {
|
||||
matches!(
|
||||
&arg.node,
|
||||
ExprKind::Constant {
|
||||
value: Constant::Bool(_),
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn add_if_boolean(checker: &mut Checker, arg: &Expr, kind: CheckKind) {
|
||||
if is_boolean_arg(arg) {
|
||||
checker.add_check(Check::new(kind, Range::from_located(arg)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_positional_boolean_in_def(checker: &mut Checker, arguments: &Arguments) {
|
||||
for arg in arguments.posonlyargs.iter().chain(arguments.args.iter()) {
|
||||
if arg.node.annotation.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(expr) = &arg.node.annotation {
|
||||
// check for both bool (python class) and 'bool' (string annotation)
|
||||
let hint = match &expr.node {
|
||||
ExprKind::Name { id, .. } => id == "bool",
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
..
|
||||
} => value == "bool",
|
||||
_ => false,
|
||||
};
|
||||
if hint {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::BooleanPositionalArgInFunctionDefinition,
|
||||
Range::from_located(arg),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_boolean_default_value_in_function_definition(
|
||||
checker: &mut Checker,
|
||||
arguments: &Arguments,
|
||||
) {
|
||||
for arg in arguments.defaults.iter() {
|
||||
add_if_boolean(
|
||||
checker,
|
||||
arg,
|
||||
CheckKind::BooleanDefaultValueInFunctionDefinition,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_boolean_positional_value_in_function_call(checker: &mut Checker, args: &[Expr]) {
|
||||
for arg in args {
|
||||
add_if_boolean(
|
||||
checker,
|
||||
arg,
|
||||
CheckKind::BooleanPositionalValueInFunctionCall,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
mod constants;
|
||||
pub mod plugins;
|
||||
pub mod settings;
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ fn assertion_error(msg: Option<&Expr>) -> Stmt {
|
||||
)
|
||||
}
|
||||
|
||||
/// B011
|
||||
pub fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: Option<&Expr>) {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Bool(false),
|
||||
@@ -43,7 +44,7 @@ pub fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: Option
|
||||
} = &test.node
|
||||
{
|
||||
let mut check = Check::new(CheckKind::DoNotAssertFalse, Range::from_located(test));
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
let mut generator = SourceGenerator::new();
|
||||
if let Ok(()) = generator.unparse_stmt(&assertion_error(msg)) {
|
||||
if let Ok(content) = generator.generate() {
|
||||
|
||||
@@ -54,7 +54,7 @@ fn duplicate_handler_exceptions<'a>(
|
||||
),
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// TODO(charlie): If we have a single element, remove the tuple.
|
||||
let mut generator = SourceGenerator::new();
|
||||
if let Ok(()) = generator.unparse_expr(&type_pattern(unique_elts), 0) {
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::code_gen::SourceGenerator;
|
||||
use crate::flake8_bugbear::constants::IDENTIFIER_REGEX;
|
||||
use crate::python::identifiers::IDENTIFIER_REGEX;
|
||||
use crate::python::keyword::KWLIST;
|
||||
|
||||
fn attribute(value: &Expr, attr: &str) -> Expr {
|
||||
@@ -20,6 +20,7 @@ fn attribute(value: &Expr, attr: &str) -> Expr {
|
||||
)
|
||||
}
|
||||
|
||||
/// B009
|
||||
pub fn getattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
if id == "getattr" {
|
||||
@@ -32,7 +33,7 @@ pub fn getattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
|
||||
if IDENTIFIER_REGEX.is_match(value) && !KWLIST.contains(&value.as_str()) {
|
||||
let mut check =
|
||||
Check::new(CheckKind::GetAttrWithConstant, Range::from_located(expr));
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
let mut generator = SourceGenerator::new();
|
||||
if let Ok(()) = generator.unparse_expr(&attribute(obj, value), 0) {
|
||||
if let Ok(content) = generator.generate() {
|
||||
|
||||
@@ -3,7 +3,7 @@ use rustpython_ast::{Constant, Expr, ExprKind};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::flake8_bugbear::constants::IDENTIFIER_REGEX;
|
||||
use crate::python::identifiers::IDENTIFIER_REGEX;
|
||||
use crate::python::keyword::KWLIST;
|
||||
|
||||
/// B010
|
||||
|
||||
@@ -65,7 +65,7 @@ pub fn unused_loop_control_variable(checker: &mut Checker, target: &Expr, body:
|
||||
CheckKind::UnusedLoopControlVariable(name.to_string()),
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Prefix the variable name with an underscore.
|
||||
check.amend(Fix::replacement(
|
||||
format!("_{name}"),
|
||||
|
||||
@@ -16,7 +16,7 @@ pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
|
||||
checker.settings.enabled.contains(&CheckCode::T203),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
let context = checker.binding_context();
|
||||
if matches!(
|
||||
checker.parents[context.defined_by].node,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Settings for the `flake-quotes` plugin.
|
||||
//! Settings for the `flake8-quotes` plugin.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
26
src/flake8_tidy_imports/checks.rs
Normal file
26
src/flake8_tidy_imports/checks.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use rustpython_ast::Stmt;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::flake8_tidy_imports::settings::Strictness;
|
||||
|
||||
pub fn banned_relative_import(
|
||||
stmt: &Stmt,
|
||||
level: Option<&usize>,
|
||||
strictness: &Strictness,
|
||||
) -> Option<Check> {
|
||||
if let Some(level) = level {
|
||||
if level
|
||||
> &match strictness {
|
||||
Strictness::All => 0,
|
||||
Strictness::Parents => 1,
|
||||
}
|
||||
{
|
||||
return Some(Check::new(
|
||||
CheckKind::BannedRelativeImport(strictness.clone()),
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
49
src/flake8_tidy_imports/mod.rs
Normal file
49
src/flake8_tidy_imports/mod.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
pub mod checks;
|
||||
pub mod settings;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::autofix::fixer;
|
||||
use crate::checks::CheckCode;
|
||||
use crate::flake8_tidy_imports::settings::Strictness;
|
||||
use crate::linter::test_path;
|
||||
use crate::{flake8_tidy_imports, Settings};
|
||||
|
||||
#[test]
|
||||
fn ban_parent_imports() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/I252.py"),
|
||||
&Settings {
|
||||
flake8_tidy_imports: flake8_tidy_imports::settings::Settings {
|
||||
ban_relative_imports: Strictness::Parents,
|
||||
},
|
||||
..Settings::for_rules(vec![CheckCode::I252])
|
||||
},
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ban_all_imports() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/I252.py"),
|
||||
&Settings {
|
||||
flake8_tidy_imports: flake8_tidy_imports::settings::Settings {
|
||||
ban_relative_imports: Strictness::All,
|
||||
},
|
||||
..Settings::for_rules(vec![CheckCode::I252])
|
||||
},
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
37
src/flake8_tidy_imports/settings.rs
Normal file
37
src/flake8_tidy_imports/settings.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
//! Settings for the `flake8-tidy-imports` plugin.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
pub enum Strictness {
|
||||
Parents,
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
pub struct Options {
|
||||
pub ban_relative_imports: Option<Strictness>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct Settings {
|
||||
pub ban_relative_imports: Strictness,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn from_options(options: Options) -> Self {
|
||||
Self {
|
||||
ban_relative_imports: options.ban_relative_imports.unwrap_or(Strictness::Parents),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ban_relative_imports: Strictness::Parents,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
source: src/flake8_tidy_imports/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
BannedRelativeImport: all
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 1
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
BannedRelativeImport: all
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind:
|
||||
BannedRelativeImport: all
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
BannedRelativeImport: all
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind:
|
||||
BannedRelativeImport: all
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 27
|
||||
fix: ~
|
||||
- kind:
|
||||
BannedRelativeImport: all
|
||||
location:
|
||||
row: 8
|
||||
column: 0
|
||||
end_location:
|
||||
row: 8
|
||||
column: 34
|
||||
fix: ~
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
source: src/flake8_tidy_imports/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
BannedRelativeImport: parents
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
BannedRelativeImport: parents
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind:
|
||||
BannedRelativeImport: parents
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 27
|
||||
fix: ~
|
||||
- kind:
|
||||
BannedRelativeImport: parents
|
||||
location:
|
||||
row: 8
|
||||
column: 0
|
||||
end_location:
|
||||
row: 8
|
||||
column: 34
|
||||
fix: ~
|
||||
|
||||
@@ -91,7 +91,7 @@ pub fn check_imports(
|
||||
|
||||
if has_leading_content || has_trailing_content {
|
||||
let mut check = Check::new(CheckKind::UnsortedImports, range);
|
||||
if autofix.patch() {
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
let mut content = String::new();
|
||||
if has_leading_content {
|
||||
content.push('\n');
|
||||
@@ -119,7 +119,7 @@ pub fn check_imports(
|
||||
let actual = dedent(&locator.slice_source_code_range(&range));
|
||||
if actual != expected {
|
||||
let mut check = Check::new(CheckKind::UnsortedImports, range);
|
||||
if autofix.patch() {
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
indent(&expected, &indentation),
|
||||
range.location,
|
||||
|
||||
@@ -30,16 +30,20 @@ mod docstrings;
|
||||
mod flake8_2020;
|
||||
pub mod flake8_annotations;
|
||||
pub mod flake8_bandit;
|
||||
mod flake8_blind_except;
|
||||
pub mod flake8_boolean_trap;
|
||||
pub mod flake8_bugbear;
|
||||
mod flake8_builtins;
|
||||
mod flake8_comprehensions;
|
||||
mod flake8_print;
|
||||
pub mod flake8_quotes;
|
||||
pub mod flake8_tidy_imports;
|
||||
pub mod fs;
|
||||
mod isort;
|
||||
mod lex;
|
||||
pub mod linter;
|
||||
pub mod logging;
|
||||
pub mod mccabe;
|
||||
pub mod message;
|
||||
mod noqa;
|
||||
pub mod pep8_naming;
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::check_tokens::check_tokens;
|
||||
use crate::checks::{Check, CheckCode, CheckKind, LintSource};
|
||||
use crate::code_gen::SourceGenerator;
|
||||
use crate::directives::Directives;
|
||||
use crate::message::Message;
|
||||
use crate::message::{Message, Source};
|
||||
use crate::noqa::add_noqa;
|
||||
use crate::settings::Settings;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
@@ -176,7 +176,15 @@ pub fn lint_stdin(
|
||||
// Convert to messages.
|
||||
Ok(checks
|
||||
.into_iter()
|
||||
.map(|check| Message::from_check(path.to_string_lossy().to_string(), check))
|
||||
.map(|check| {
|
||||
let filename = path.to_string_lossy().to_string();
|
||||
let source = if settings.show_source {
|
||||
Some(Source::from_check(&check, &locator))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Message::from_check(check, filename, source)
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -233,7 +241,15 @@ pub fn lint_path(
|
||||
// Convert to messages.
|
||||
let messages: Vec<Message> = checks
|
||||
.into_iter()
|
||||
.map(|check| Message::from_check(path.to_string_lossy().to_string(), check))
|
||||
.map(|check| {
|
||||
let filename = path.to_string_lossy().to_string();
|
||||
let source = if settings.show_source {
|
||||
Some(Source::from_check(&check, &locator))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Message::from_check(check, filename, source)
|
||||
})
|
||||
.collect();
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
cache::set(path, &metadata, settings, autofix, &messages, mode);
|
||||
@@ -351,6 +367,7 @@ mod tests {
|
||||
#[test_case(CheckCode::B025, Path::new("B025.py"); "B025")]
|
||||
#[test_case(CheckCode::B026, Path::new("B026.py"); "B026")]
|
||||
#[test_case(CheckCode::B027, Path::new("B027.py"); "B027")]
|
||||
#[test_case(CheckCode::BLE001, Path::new("BLE.py"); "BLE001")]
|
||||
#[test_case(CheckCode::C400, Path::new("C400.py"); "C400")]
|
||||
#[test_case(CheckCode::C401, Path::new("C401.py"); "C401")]
|
||||
#[test_case(CheckCode::C402, Path::new("C402.py"); "C402")]
|
||||
@@ -487,7 +504,6 @@ mod tests {
|
||||
#[test_case(CheckCode::T201, Path::new("T201.py"); "T201")]
|
||||
#[test_case(CheckCode::T203, Path::new("T203.py"); "T203")]
|
||||
#[test_case(CheckCode::U001, Path::new("U001.py"); "U001")]
|
||||
#[test_case(CheckCode::U002, Path::new("U002.py"); "U002")]
|
||||
#[test_case(CheckCode::U003, Path::new("U003.py"); "U003")]
|
||||
#[test_case(CheckCode::U004, Path::new("U004.py"); "U004")]
|
||||
#[test_case(CheckCode::U005, Path::new("U005.py"); "U005")]
|
||||
@@ -502,6 +518,7 @@ mod tests {
|
||||
#[test_case(CheckCode::U011, Path::new("U011_0.py"); "U011_0")]
|
||||
#[test_case(CheckCode::U011, Path::new("U011_1.py"); "U011_1")]
|
||||
#[test_case(CheckCode::U012, Path::new("U012.py"); "U012")]
|
||||
#[test_case(CheckCode::U013, Path::new("U013.py"); "U013")]
|
||||
#[test_case(CheckCode::W292, Path::new("W292_0.py"); "W292_0")]
|
||||
#[test_case(CheckCode::W292, Path::new("W292_1.py"); "W292_1")]
|
||||
#[test_case(CheckCode::W292, Path::new("W292_2.py"); "W292_2")]
|
||||
@@ -520,6 +537,9 @@ mod tests {
|
||||
#[test_case(CheckCode::YTT301, Path::new("YTT301.py"); "YTT301")]
|
||||
#[test_case(CheckCode::YTT302, Path::new("YTT302.py"); "YTT302")]
|
||||
#[test_case(CheckCode::YTT303, Path::new("YTT303.py"); "YTT303")]
|
||||
#[test_case(CheckCode::FBT001, Path::new("FBT.py"); "FBT001")]
|
||||
#[test_case(CheckCode::FBT002, Path::new("FBT.py"); "FBT002")]
|
||||
#[test_case(CheckCode::FBT003, Path::new("FBT.py"); "FBT003")]
|
||||
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
|
||||
let mut checks = test_path(
|
||||
|
||||
31
src/main.rs
31
src/main.rs
@@ -7,7 +7,7 @@ use std::time::Instant;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use ::ruff::cache;
|
||||
use ::ruff::checks::{CheckCode, CheckKind};
|
||||
use ::ruff::cli::{collect_per_file_ignores, extract_log_level, warn_on, Cli, Warnable};
|
||||
use ::ruff::cli::{collect_per_file_ignores, extract_log_level, Cli};
|
||||
use ::ruff::fs::iter_python_files;
|
||||
use ::ruff::linter::{add_noqa_to_path, autoformat_path, lint_path, lint_stdin};
|
||||
use ::ruff::logging::{set_up_logging, LogLevel};
|
||||
@@ -117,6 +117,7 @@ fn run_once(
|
||||
location: Default::default(),
|
||||
end_location: Default::default(),
|
||||
filename: path.to_string_lossy().to_string(),
|
||||
source: None,
|
||||
}]
|
||||
} else {
|
||||
error!("Failed to check {}: {message}", path.to_string_lossy());
|
||||
@@ -239,25 +240,9 @@ fn inner_main() -> Result<ExitCode> {
|
||||
collect_per_file_ignores(cli.per_file_ignores, project_root.as_ref());
|
||||
}
|
||||
if !cli.select.is_empty() {
|
||||
warn_on(
|
||||
Warnable::Select,
|
||||
&cli.select,
|
||||
&cli.ignore,
|
||||
&cli.extend_ignore,
|
||||
&configuration,
|
||||
pyproject.as_ref(),
|
||||
);
|
||||
configuration.select = cli.select;
|
||||
}
|
||||
if !cli.extend_select.is_empty() {
|
||||
warn_on(
|
||||
Warnable::ExtendSelect,
|
||||
&cli.extend_select,
|
||||
&cli.ignore,
|
||||
&cli.extend_ignore,
|
||||
&configuration,
|
||||
pyproject.as_ref(),
|
||||
);
|
||||
configuration.extend_select = cli.extend_select;
|
||||
}
|
||||
if !cli.ignore.is_empty() {
|
||||
@@ -266,9 +251,18 @@ fn inner_main() -> Result<ExitCode> {
|
||||
if !cli.extend_ignore.is_empty() {
|
||||
configuration.extend_ignore = cli.extend_ignore;
|
||||
}
|
||||
if !cli.fixable.is_empty() {
|
||||
configuration.fixable = cli.fixable;
|
||||
}
|
||||
if !cli.unfixable.is_empty() {
|
||||
configuration.unfixable = cli.unfixable;
|
||||
}
|
||||
if let Some(line_length) = cli.line_length {
|
||||
configuration.line_length = line_length;
|
||||
}
|
||||
if let Some(max_complexity) = cli.max_complexity {
|
||||
configuration.mccabe.max_complexity = max_complexity;
|
||||
}
|
||||
if let Some(target_version) = cli.target_version {
|
||||
configuration.target_version = target_version;
|
||||
}
|
||||
@@ -278,6 +272,9 @@ fn inner_main() -> Result<ExitCode> {
|
||||
if let Some(fix) = fix {
|
||||
configuration.fix = fix;
|
||||
}
|
||||
if cli.show_source {
|
||||
configuration.show_source = true;
|
||||
}
|
||||
|
||||
if cli.show_settings && cli.show_files {
|
||||
eprintln!("Error: specify --show-settings or show-files (not both).");
|
||||
|
||||
73
src/mccabe/checks.rs
Normal file
73
src/mccabe/checks.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use rustpython_ast::{ExcepthandlerKind, ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
fn get_complexity_number(stmts: &[Stmt]) -> usize {
|
||||
let mut complexity = 0;
|
||||
for stmt in stmts {
|
||||
match &stmt.node {
|
||||
StmtKind::If { body, orelse, .. } => {
|
||||
complexity += 1;
|
||||
complexity += get_complexity_number(body);
|
||||
complexity += get_complexity_number(orelse);
|
||||
}
|
||||
StmtKind::For { body, orelse, .. } | StmtKind::AsyncFor { body, orelse, .. } => {
|
||||
complexity += 1;
|
||||
complexity += get_complexity_number(body);
|
||||
complexity += get_complexity_number(orelse);
|
||||
}
|
||||
StmtKind::While { test, body, orelse } => {
|
||||
complexity += 1;
|
||||
complexity += get_complexity_number(body);
|
||||
complexity += get_complexity_number(orelse);
|
||||
if let ExprKind::BoolOp { .. } = &test.node {
|
||||
complexity += 1;
|
||||
}
|
||||
}
|
||||
StmtKind::Try {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
} => {
|
||||
complexity += 1;
|
||||
complexity += get_complexity_number(body);
|
||||
complexity += get_complexity_number(orelse);
|
||||
complexity += get_complexity_number(finalbody);
|
||||
for handler in handlers {
|
||||
complexity += 1;
|
||||
let ExcepthandlerKind::ExceptHandler { body, .. } = &handler.node;
|
||||
complexity += get_complexity_number(body);
|
||||
}
|
||||
}
|
||||
StmtKind::FunctionDef { body, .. } | StmtKind::AsyncFunctionDef { body, .. } => {
|
||||
complexity += 1;
|
||||
complexity += get_complexity_number(body);
|
||||
}
|
||||
StmtKind::ClassDef { body, .. } => {
|
||||
complexity += 1;
|
||||
complexity += get_complexity_number(body);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
complexity
|
||||
}
|
||||
|
||||
pub fn function_is_too_complex(
|
||||
stmt: &Stmt,
|
||||
name: &str,
|
||||
body: &[Stmt],
|
||||
max_complexity: usize,
|
||||
) -> Option<Check> {
|
||||
let complexity = get_complexity_number(body) + 1;
|
||||
if complexity > max_complexity {
|
||||
Some(Check::new(
|
||||
CheckKind::FunctionIsTooComplex(name.to_string(), complexity),
|
||||
Range::from_located(stmt),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
33
src/mccabe/mod.rs
Normal file
33
src/mccabe/mod.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
pub mod checks;
|
||||
pub mod settings;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::autofix::fixer;
|
||||
use crate::checks::CheckCode;
|
||||
use crate::linter::test_path;
|
||||
use crate::{mccabe, Settings};
|
||||
|
||||
#[test_case(0)]
|
||||
#[test_case(3)]
|
||||
#[test_case(10)]
|
||||
fn max_complexity_zero(max_complexity: usize) -> Result<()> {
|
||||
let snapshot = format!("max_complexity_{}", max_complexity);
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/C901.py"),
|
||||
&Settings {
|
||||
mccabe: mccabe::settings::Settings { max_complexity },
|
||||
..Settings::for_rules(vec![CheckCode::C901])
|
||||
},
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
28
src/mccabe/settings.rs
Normal file
28
src/mccabe/settings.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
//! Settings for the `mccabe` plugin.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
pub struct Options {
|
||||
pub max_complexity: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct Settings {
|
||||
pub max_complexity: usize,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn from_options(options: Options) -> Self {
|
||||
Self {
|
||||
max_complexity: options.max_complexity.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self { max_complexity: 10 }
|
||||
}
|
||||
}
|
||||
170
src/mccabe/snapshots/ruff__mccabe__tests__max_complexity_0.snap
Normal file
170
src/mccabe/snapshots/ruff__mccabe__tests__max_complexity_0.snap
Normal file
@@ -0,0 +1,170 @@
|
||||
---
|
||||
source: src/mccabe/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- trivial
|
||||
- 1
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- expr_as_statement
|
||||
- 1
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 12
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- sequential
|
||||
- 1
|
||||
location:
|
||||
row: 12
|
||||
column: 0
|
||||
end_location:
|
||||
row: 19
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- if_elif_else_dead_path
|
||||
- 3
|
||||
location:
|
||||
row: 19
|
||||
column: 0
|
||||
end_location:
|
||||
row: 29
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- nested_ifs
|
||||
- 3
|
||||
location:
|
||||
row: 29
|
||||
column: 0
|
||||
end_location:
|
||||
row: 40
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- for_loop
|
||||
- 2
|
||||
location:
|
||||
row: 40
|
||||
column: 0
|
||||
end_location:
|
||||
row: 46
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- for_else
|
||||
- 2
|
||||
location:
|
||||
row: 46
|
||||
column: 0
|
||||
end_location:
|
||||
row: 54
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- recursive
|
||||
- 2
|
||||
location:
|
||||
row: 54
|
||||
column: 0
|
||||
end_location:
|
||||
row: 62
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- nested_functions
|
||||
- 3
|
||||
location:
|
||||
row: 62
|
||||
column: 0
|
||||
end_location:
|
||||
row: 73
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- a
|
||||
- 2
|
||||
location:
|
||||
row: 63
|
||||
column: 4
|
||||
end_location:
|
||||
row: 69
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- b
|
||||
- 1
|
||||
location:
|
||||
row: 64
|
||||
column: 8
|
||||
end_location:
|
||||
row: 67
|
||||
column: 8
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- try_else
|
||||
- 4
|
||||
location:
|
||||
row: 73
|
||||
column: 0
|
||||
end_location:
|
||||
row: 85
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- nested_try_finally
|
||||
- 3
|
||||
location:
|
||||
row: 85
|
||||
column: 0
|
||||
end_location:
|
||||
row: 96
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- foobar
|
||||
- 3
|
||||
location:
|
||||
row: 96
|
||||
column: 0
|
||||
end_location:
|
||||
row: 107
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- annotated_assign
|
||||
- 1
|
||||
location:
|
||||
row: 107
|
||||
column: 0
|
||||
end_location:
|
||||
row: 109
|
||||
column: 0
|
||||
fix: ~
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: src/mccabe/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
source: src/mccabe/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
FunctionIsTooComplex:
|
||||
- try_else
|
||||
- 4
|
||||
location:
|
||||
row: 73
|
||||
column: 0
|
||||
end_location:
|
||||
row: 85
|
||||
column: 0
|
||||
fix: ~
|
||||
|
||||
@@ -2,12 +2,16 @@ use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
|
||||
use annotate_snippets::display_list::{DisplayList, FormatOptions};
|
||||
use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation};
|
||||
use colored::Colorize;
|
||||
use rustpython_parser::ast::Location;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::fs::relativize_path;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Message {
|
||||
@@ -16,16 +20,18 @@ pub struct Message {
|
||||
pub location: Location,
|
||||
pub end_location: Location,
|
||||
pub filename: String,
|
||||
pub source: Option<Source>,
|
||||
}
|
||||
|
||||
impl Message {
|
||||
pub fn from_check(filename: String, check: Check) -> Self {
|
||||
pub fn from_check(check: Check, filename: String, source: Option<Source>) -> Self {
|
||||
Self {
|
||||
kind: check.kind,
|
||||
fixed: check.fix.map(|fix| fix.applied).unwrap_or_default(),
|
||||
location: Location::new(check.location.row(), check.location.column() + 1),
|
||||
end_location: Location::new(check.end_location.row(), check.end_location.column() + 1),
|
||||
filename,
|
||||
source,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,8 +54,7 @@ impl PartialOrd for Message {
|
||||
|
||||
impl fmt::Display for Message {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
let label = format!(
|
||||
"{}{}{}{}{}{} {} {}",
|
||||
relativize_path(Path::new(&self.filename)).white().bold(),
|
||||
":".cyan(),
|
||||
@@ -58,7 +63,71 @@ impl fmt::Display for Message {
|
||||
self.location.column(),
|
||||
":".cyan(),
|
||||
self.kind.code().as_ref().red().bold(),
|
||||
self.kind.body()
|
||||
)
|
||||
self.kind.body(),
|
||||
);
|
||||
match &self.source {
|
||||
None => write!(f, "{}", label),
|
||||
Some(source) => {
|
||||
let snippet = Snippet {
|
||||
title: Some(Annotation {
|
||||
label: Some(&label),
|
||||
annotation_type: AnnotationType::Error,
|
||||
// The ID (error number) is already encoded in the `label`.
|
||||
id: None,
|
||||
}),
|
||||
footer: vec![],
|
||||
slices: vec![Slice {
|
||||
source: &source.contents,
|
||||
line_start: self.location.row(),
|
||||
annotations: vec![SourceAnnotation {
|
||||
label: self.kind.code().as_ref(),
|
||||
annotation_type: AnnotationType::Error,
|
||||
range: source.range,
|
||||
}],
|
||||
// The origin (file name, line number, and column number) is already encoded
|
||||
// in the `label`.
|
||||
origin: None,
|
||||
fold: false,
|
||||
}],
|
||||
opt: FormatOptions {
|
||||
color: true,
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
// `split_once(' ')` strips "error: " from `message`.
|
||||
let message = DisplayList::from(snippet).to_string();
|
||||
let (_, message) = message.split_once(' ').unwrap();
|
||||
write!(f, "{}", message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Source {
|
||||
pub contents: String,
|
||||
pub range: (usize, usize),
|
||||
}
|
||||
|
||||
impl Source {
|
||||
pub fn from_check(check: &Check, locator: &SourceCodeLocator) -> Self {
|
||||
let source = locator.slice_source_code_range(&Range {
|
||||
location: Location::new(check.location.row(), 0),
|
||||
end_location: Location::new(check.end_location.row() + 1, 0),
|
||||
});
|
||||
let num_chars_in_range = locator
|
||||
.slice_source_code_range(&Range {
|
||||
location: check.location,
|
||||
end_location: check.end_location,
|
||||
})
|
||||
.chars()
|
||||
.count();
|
||||
Source {
|
||||
contents: source.to_string(),
|
||||
range: (
|
||||
check.location.column(),
|
||||
check.location.column() + num_chars_in_range,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ pub fn error_suffix_on_exception_name(
|
||||
) -> Option<Check> {
|
||||
if bases.iter().any(|base| {
|
||||
if let ExprKind::Name { id, .. } = &base.node {
|
||||
id == "Exception"
|
||||
id == "Exception" || id.ends_with("Error")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use itertools::izip;
|
||||
use rustpython_ast::Location;
|
||||
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Stmt, Unaryop};
|
||||
use rustpython_parser::ast::{Cmpop, Expr, ExprKind, Stmt};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind, RejectedCmpop};
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
fn is_ambiguous_name(name: &str) -> bool {
|
||||
@@ -59,152 +59,6 @@ pub fn do_not_assign_lambda(target: &Expr, value: &Expr, stmt: &Stmt) -> Option<
|
||||
None
|
||||
}
|
||||
|
||||
/// E713, E714
|
||||
pub fn not_tests(
|
||||
op: &Unaryop,
|
||||
operand: &Expr,
|
||||
check_not_in: bool,
|
||||
check_not_is: bool,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
if matches!(op, Unaryop::Not) {
|
||||
if let ExprKind::Compare { ops, .. } = &operand.node {
|
||||
for op in ops {
|
||||
match op {
|
||||
Cmpop::In => {
|
||||
if check_not_in {
|
||||
checks.push(Check::new(
|
||||
CheckKind::NotInTest,
|
||||
Range::from_located(operand),
|
||||
));
|
||||
}
|
||||
}
|
||||
Cmpop::Is => {
|
||||
if check_not_is {
|
||||
checks.push(Check::new(
|
||||
CheckKind::NotIsTest,
|
||||
Range::from_located(operand),
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checks
|
||||
}
|
||||
|
||||
/// E711, E712
|
||||
pub fn literal_comparisons(
|
||||
left: &Expr,
|
||||
ops: &[Cmpop],
|
||||
comparators: &[Expr],
|
||||
check_none_comparisons: bool,
|
||||
check_true_false_comparisons: bool,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
let op = ops.first().unwrap();
|
||||
let comparator = left;
|
||||
|
||||
// Check `left`.
|
||||
if check_none_comparisons
|
||||
&& matches!(
|
||||
comparator.node,
|
||||
ExprKind::Constant {
|
||||
value: Constant::None,
|
||||
kind: None
|
||||
}
|
||||
)
|
||||
{
|
||||
if matches!(op, Cmpop::Eq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::NoneComparison(RejectedCmpop::Eq),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
if matches!(op, Cmpop::NotEq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::NoneComparison(RejectedCmpop::NotEq),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if check_true_false_comparisons {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Bool(value),
|
||||
kind: None,
|
||||
} = comparator.node
|
||||
{
|
||||
if matches!(op, Cmpop::Eq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
if matches!(op, Cmpop::NotEq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check each comparator in order.
|
||||
for (op, comparator) in izip!(ops, comparators) {
|
||||
if check_none_comparisons
|
||||
&& matches!(
|
||||
comparator.node,
|
||||
ExprKind::Constant {
|
||||
value: Constant::None,
|
||||
kind: None
|
||||
}
|
||||
)
|
||||
{
|
||||
if matches!(op, Cmpop::Eq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::NoneComparison(RejectedCmpop::Eq),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
if matches!(op, Cmpop::NotEq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::NoneComparison(RejectedCmpop::NotEq),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if check_true_false_comparisons {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Bool(value),
|
||||
kind: None,
|
||||
} = comparator.node
|
||||
{
|
||||
if matches!(op, Cmpop::Eq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
if matches!(op, Cmpop::NotEq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checks
|
||||
}
|
||||
|
||||
/// E721
|
||||
pub fn type_comparison(ops: &[Cmpop], comparators: &[Expr], location: Range) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod checks;
|
||||
pub mod plugins;
|
||||
|
||||
262
src/pycodestyle/plugins.rs
Normal file
262
src/pycodestyle/plugins.rs
Normal file
@@ -0,0 +1,262 @@
|
||||
use fnv::FnvHashMap;
|
||||
use itertools::izip;
|
||||
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Unaryop};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind, RejectedCmpop};
|
||||
use crate::code_gen::SourceGenerator;
|
||||
|
||||
fn compare(left: &Expr, ops: &[Cmpop], comparators: &[Expr]) -> Option<String> {
|
||||
let cmp = Expr::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
ExprKind::Compare {
|
||||
left: Box::new(left.clone()),
|
||||
ops: ops.to_vec(),
|
||||
comparators: comparators.to_vec(),
|
||||
},
|
||||
);
|
||||
let mut generator = SourceGenerator::new();
|
||||
if let Ok(()) = generator.unparse_expr(&cmp, 0) {
|
||||
if let Ok(content) = generator.generate() {
|
||||
return Some(content);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// E711, E712
|
||||
pub fn literal_comparisons(
|
||||
checker: &mut Checker,
|
||||
expr: &Expr,
|
||||
left: &Expr,
|
||||
ops: &[Cmpop],
|
||||
comparators: &[Expr],
|
||||
check_none_comparisons: bool,
|
||||
check_true_false_comparisons: bool,
|
||||
) {
|
||||
// Mapping from (bad operator index) to (replacement operator). As we iterate
|
||||
// through the list of operators, we apply "dummy" fixes for each error,
|
||||
// then replace the entire expression at the end with one "real" fix, to
|
||||
// avoid conflicts.
|
||||
let mut bad_ops: FnvHashMap<usize, Cmpop> = FnvHashMap::default();
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
let op = ops.first().unwrap();
|
||||
let comparator = left;
|
||||
|
||||
// Check `left`.
|
||||
if check_none_comparisons
|
||||
&& matches!(
|
||||
comparator.node,
|
||||
ExprKind::Constant {
|
||||
value: Constant::None,
|
||||
kind: None
|
||||
}
|
||||
)
|
||||
{
|
||||
if matches!(op, Cmpop::Eq) {
|
||||
let mut check = Check::new(
|
||||
CheckKind::NoneComparison(RejectedCmpop::Eq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Dummy replacement
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(0, Cmpop::Is);
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
if matches!(op, Cmpop::NotEq) {
|
||||
let mut check = Check::new(
|
||||
CheckKind::NoneComparison(RejectedCmpop::NotEq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(0, Cmpop::IsNot);
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
|
||||
if check_true_false_comparisons {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Bool(value),
|
||||
kind: None,
|
||||
} = comparator.node
|
||||
{
|
||||
if matches!(op, Cmpop::Eq) {
|
||||
let mut check = Check::new(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(0, Cmpop::Is);
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
if matches!(op, Cmpop::NotEq) {
|
||||
let mut check = Check::new(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(0, Cmpop::IsNot);
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check each comparator in order.
|
||||
for (idx, (op, comparator)) in izip!(ops, comparators).enumerate() {
|
||||
if check_none_comparisons
|
||||
&& matches!(
|
||||
comparator.node,
|
||||
ExprKind::Constant {
|
||||
value: Constant::None,
|
||||
kind: None
|
||||
}
|
||||
)
|
||||
{
|
||||
if matches!(op, Cmpop::Eq) {
|
||||
let mut check = Check::new(
|
||||
CheckKind::NoneComparison(RejectedCmpop::Eq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(idx, Cmpop::Is);
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
if matches!(op, Cmpop::NotEq) {
|
||||
let mut check = Check::new(
|
||||
CheckKind::NoneComparison(RejectedCmpop::NotEq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(idx, Cmpop::IsNot);
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
|
||||
if check_true_false_comparisons {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Bool(value),
|
||||
kind: None,
|
||||
} = comparator.node
|
||||
{
|
||||
if matches!(op, Cmpop::Eq) {
|
||||
let mut check = Check::new(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(idx, Cmpop::Is);
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
if matches!(op, Cmpop::NotEq) {
|
||||
let mut check = Check::new(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(idx, Cmpop::IsNot);
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !bad_ops.is_empty() {
|
||||
// Replace the entire comparison expression.
|
||||
let ops = ops
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, op)| bad_ops.get(&idx).unwrap_or(op))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
if let Some(content) = compare(left, &ops, comparators) {
|
||||
if let Some(check) = checks.last_mut() {
|
||||
check.fix = Some(Fix::replacement(
|
||||
content,
|
||||
expr.location,
|
||||
expr.end_location.unwrap(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checker.add_checks(checks.into_iter());
|
||||
}
|
||||
|
||||
/// E713, E714
|
||||
pub fn not_tests(
|
||||
checker: &mut Checker,
|
||||
expr: &Expr,
|
||||
op: &Unaryop,
|
||||
operand: &Expr,
|
||||
check_not_in: bool,
|
||||
check_not_is: bool,
|
||||
) {
|
||||
if matches!(op, Unaryop::Not) {
|
||||
if let ExprKind::Compare {
|
||||
left,
|
||||
ops,
|
||||
comparators,
|
||||
..
|
||||
} = &operand.node
|
||||
{
|
||||
let should_fix = ops.len() == 1;
|
||||
for op in ops.iter() {
|
||||
match op {
|
||||
Cmpop::In => {
|
||||
if check_not_in {
|
||||
let mut check =
|
||||
Check::new(CheckKind::NotInTest, Range::from_located(operand));
|
||||
if checker.patch(check.kind.code()) && should_fix {
|
||||
if let Some(content) = compare(left, &[Cmpop::NotIn], comparators) {
|
||||
check.amend(Fix::replacement(
|
||||
content,
|
||||
expr.location,
|
||||
expr.end_location.unwrap(),
|
||||
));
|
||||
}
|
||||
}
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
Cmpop::Is => {
|
||||
if check_not_is {
|
||||
let mut check =
|
||||
Check::new(CheckKind::NotIsTest, Range::from_located(operand));
|
||||
if checker.patch(check.kind.code()) && should_fix {
|
||||
if let Some(content) = compare(left, &[Cmpop::IsNot], comparators) {
|
||||
check.amend(Fix::replacement(
|
||||
content,
|
||||
expr.location,
|
||||
expr.end_location.unwrap(),
|
||||
));
|
||||
}
|
||||
}
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,7 +179,7 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
|
||||
CheckKind::NoBlankLineBeforeFunction(blank_lines_before),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete the blank line before the docstring.
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(docstring.location.row() - blank_lines_before, 0),
|
||||
@@ -220,11 +220,14 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
|
||||
CheckKind::NoBlankLineAfterFunction(blank_lines_after),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete the blank line after the docstring.
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(docstring.location.row() + 1, 0),
|
||||
Location::new(docstring.location.row() + 1 + blank_lines_after, 0),
|
||||
Location::new(docstring.end_location.unwrap().row() + 1, 0),
|
||||
Location::new(
|
||||
docstring.end_location.unwrap().row() + 1 + blank_lines_after,
|
||||
0,
|
||||
),
|
||||
));
|
||||
}
|
||||
checker.add_check(check);
|
||||
@@ -266,7 +269,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
|
||||
CheckKind::NoBlankLineBeforeClass(blank_lines_before),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete the blank line before the class.
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(docstring.location.row() - blank_lines_before, 0),
|
||||
@@ -282,7 +285,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
|
||||
CheckKind::OneBlankLineBeforeClass(blank_lines_before),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Insert one blank line before the class.
|
||||
check.amend(Fix::replacement(
|
||||
"\n".to_string(),
|
||||
@@ -319,7 +322,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
|
||||
CheckKind::OneBlankLineAfterClass(blank_lines_after),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Insert a blank line before the class (replacing any existing lines).
|
||||
check.amend(Fix::replacement(
|
||||
"\n".to_string(),
|
||||
@@ -361,7 +364,7 @@ pub fn blank_after_summary(checker: &mut Checker, definition: &Definition) {
|
||||
CheckKind::BlankLineAfterSummary,
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Insert one blank line after the summary (replacing any existing lines).
|
||||
check.amend(Fix::replacement(
|
||||
"\n".to_string(),
|
||||
@@ -422,7 +425,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
end_location: Location::new(docstring.location.row() + i, 0),
|
||||
},
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
helpers::clean(&docstring_indent),
|
||||
Location::new(docstring.location.row() + i, 0),
|
||||
@@ -472,7 +475,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
end_location: Location::new(docstring.location.row() + i, 0),
|
||||
},
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
helpers::clean(&docstring_indent),
|
||||
Location::new(docstring.location.row() + i, 0),
|
||||
@@ -496,7 +499,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
end_location: Location::new(docstring.location.row() + i, 0),
|
||||
},
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
helpers::clean(&docstring_indent),
|
||||
Location::new(docstring.location.row() + i, 0),
|
||||
@@ -534,7 +537,7 @@ pub fn newline_after_last_paragraph(checker: &mut Checker, definition: &Definiti
|
||||
CheckKind::NewLineAfterLastParagraph,
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Insert a newline just before the end-quote(s).
|
||||
let content = format!(
|
||||
"\n{}",
|
||||
@@ -577,7 +580,7 @@ pub fn no_surrounding_whitespace(checker: &mut Checker, definition: &Definition)
|
||||
CheckKind::NoSurroundingWhitespace,
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
if let Some(first_line) = checker
|
||||
.locator
|
||||
.slice_source_code_range(&Range::from_located(docstring))
|
||||
@@ -913,7 +916,7 @@ fn blanks_and_section_underline(
|
||||
CheckKind::DashedUnderlineAfterSection(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Add a dashed line (of the appropriate length) under the section header.
|
||||
let content = format!(
|
||||
"{}{}\n",
|
||||
@@ -947,7 +950,7 @@ fn blanks_and_section_underline(
|
||||
CheckKind::DashedUnderlineAfterSection(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Add a dashed line (of the appropriate length) under the section header.
|
||||
let content = format!(
|
||||
"{}{}\n",
|
||||
@@ -969,7 +972,7 @@ fn blanks_and_section_underline(
|
||||
),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete any blank lines between the header and content.
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(docstring.location.row() + context.original_index + 1, 0),
|
||||
@@ -992,7 +995,7 @@ fn blanks_and_section_underline(
|
||||
CheckKind::SectionUnderlineAfterName(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete any blank lines between the header and the underline.
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(docstring.location.row() + context.original_index + 1, 0),
|
||||
@@ -1023,7 +1026,7 @@ fn blanks_and_section_underline(
|
||||
),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Replace the existing underline with a line of the appropriate length.
|
||||
let content = format!(
|
||||
"{}{}\n",
|
||||
@@ -1061,7 +1064,7 @@ fn blanks_and_section_underline(
|
||||
CheckKind::SectionUnderlineNotOverIndented(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Replace the existing indentation with whitespace of the appropriate length.
|
||||
check.amend(Fix::replacement(
|
||||
helpers::clean(&indentation),
|
||||
@@ -1110,7 +1113,7 @@ fn blanks_and_section_underline(
|
||||
),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete any blank lines between the header and content.
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(
|
||||
@@ -1169,7 +1172,7 @@ fn common_section(
|
||||
CheckKind::CapitalizeSectionName(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Replace the section title with the capitalized variant. This requires
|
||||
// locating the start and end of the section name.
|
||||
if let Some(index) = context.line.find(&context.section_name) {
|
||||
@@ -1202,7 +1205,7 @@ fn common_section(
|
||||
CheckKind::SectionNotOverIndented(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Replace the existing indentation with whitespace of the appropriate length.
|
||||
check.amend(Fix::replacement(
|
||||
helpers::clean(&indentation),
|
||||
@@ -1229,7 +1232,7 @@ fn common_section(
|
||||
CheckKind::BlankLineAfterLastSection(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Add a newline after the section.
|
||||
check.amend(Fix::insertion(
|
||||
"\n".to_string(),
|
||||
@@ -1250,7 +1253,7 @@ fn common_section(
|
||||
CheckKind::BlankLineAfterSection(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Add a newline after the section.
|
||||
check.amend(Fix::insertion(
|
||||
"\n".to_string(),
|
||||
@@ -1274,7 +1277,7 @@ fn common_section(
|
||||
CheckKind::BlankLineBeforeSection(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Add a blank line before the section.
|
||||
check.amend(Fix::insertion(
|
||||
"\n".to_string(),
|
||||
@@ -1359,7 +1362,10 @@ static GOOGLE_ARGS_REGEX: Lazy<Regex> =
|
||||
|
||||
fn args_section(checker: &mut Checker, definition: &Definition, context: &SectionContext) {
|
||||
let mut args_sections: Vec<String> = vec![];
|
||||
for line in textwrap::dedent(&context.following_lines.join("\n")).lines() {
|
||||
for line in textwrap::dedent(&context.following_lines.join("\n"))
|
||||
.trim()
|
||||
.lines()
|
||||
{
|
||||
if line
|
||||
.chars()
|
||||
.next()
|
||||
@@ -1438,7 +1444,7 @@ fn numpy_section(checker: &mut Checker, definition: &Definition, context: &Secti
|
||||
CheckKind::NewLineAfterSectionName(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete the suffix. This requires locating the end of the section name.
|
||||
if let Some(index) = context.line.find(&context.section_name) {
|
||||
// Map from bytes to characters.
|
||||
@@ -1487,7 +1493,7 @@ fn google_section(checker: &mut Checker, definition: &Definition, context: &Sect
|
||||
CheckKind::SectionNameEndsInColon(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Replace the suffix. This requires locating the end of the section name.
|
||||
if let Some(index) = context.line.find(&context.section_name) {
|
||||
// Map from bytes to characters.
|
||||
|
||||
@@ -55,7 +55,10 @@ pub fn unused_variables(scope: &Scope, dummy_variable_rgx: &Regex) -> Vec<Check>
|
||||
|
||||
if matches!(
|
||||
scope.kind,
|
||||
ScopeKind::Function(FunctionScope { uses_locals: true })
|
||||
ScopeKind::Function(FunctionScope {
|
||||
uses_locals: true,
|
||||
..
|
||||
})
|
||||
) {
|
||||
return checks;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ pub fn invalid_literal_comparison(
|
||||
&& (is_constant_non_singleton(left) || is_constant_non_singleton(right))
|
||||
{
|
||||
let mut check = Check::new(CheckKind::IsLiteral, location);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
match fix_invalid_literal_comparison(
|
||||
checker.locator,
|
||||
Range {
|
||||
|
||||
@@ -28,7 +28,7 @@ fn match_not_implemented(expr: &Expr) -> Option<&Expr> {
|
||||
pub fn raise_not_implemented(checker: &mut Checker, expr: &Expr) {
|
||||
if let Some(expr) = match_not_implemented(expr) {
|
||||
let mut check = Check::new(CheckKind::RaiseNotImplemented, Range::from_located(expr));
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
"NotImplementedError".to_string(),
|
||||
expr.location,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod builtins;
|
||||
pub mod future;
|
||||
pub mod identifiers;
|
||||
pub mod keyword;
|
||||
pub mod string;
|
||||
pub mod sys;
|
||||
|
||||
@@ -90,26 +90,6 @@ pub fn useless_metaclass_type(targets: &[Expr], value: &Expr, location: Range) -
|
||||
None
|
||||
}
|
||||
|
||||
/// U002
|
||||
pub fn unnecessary_abspath(func: &Expr, args: &[Expr], location: Range) -> Option<Check> {
|
||||
// Validate the arguments.
|
||||
if args.len() == 1 {
|
||||
if let ExprKind::Name { id, .. } = &args[0].node {
|
||||
if id == "__file__" {
|
||||
match &func.node {
|
||||
ExprKind::Attribute { attr: id, .. } | ExprKind::Name { id, .. } => {
|
||||
if id == "abspath" {
|
||||
return Some(Check::new(CheckKind::UnnecessaryAbspath, location));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// U004
|
||||
pub fn useless_object_inheritance(name: &str, bases: &[Expr], scope: &Scope) -> Option<Check> {
|
||||
for expr in bases {
|
||||
|
||||
233
src/pyupgrade/plugins/convert_typed_dict_functional_to_class.rs
Normal file
233
src/pyupgrade/plugins/convert_typed_dict_functional_to_class.rs
Normal file
@@ -0,0 +1,233 @@
|
||||
use anyhow::{bail, Result};
|
||||
use log::error;
|
||||
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Keyword, KeywordData, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::code_gen::SourceGenerator;
|
||||
use crate::python::identifiers::IDENTIFIER_REGEX;
|
||||
use crate::python::keyword::KWLIST;
|
||||
|
||||
/// Return the class name, arguments, and keywords for a `TypedDict` assignment.
|
||||
fn match_typed_dict_assign<'a>(
|
||||
targets: &'a [Expr],
|
||||
value: &'a Expr,
|
||||
) -> Option<(&'a str, &'a [Expr], &'a [Keyword])> {
|
||||
if let Some(target) = targets.get(0) {
|
||||
if let ExprKind::Name { id: class_name, .. } = &target.node {
|
||||
if let ExprKind::Call {
|
||||
func,
|
||||
args,
|
||||
keywords,
|
||||
} = &value.node
|
||||
{
|
||||
if let ExprKind::Name { id: func_name, .. } = &func.node {
|
||||
if func_name == "TypedDict" {
|
||||
return Some((class_name, args, keywords));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Generate a `StmtKind::AnnAssign` representing the provided property
|
||||
/// definition.
|
||||
fn create_property_assignment_stmt(property: &str, annotation: &ExprKind) -> Stmt {
|
||||
Stmt::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
StmtKind::AnnAssign {
|
||||
target: Box::new(Expr::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
ExprKind::Name {
|
||||
id: property.to_string(),
|
||||
ctx: ExprContext::Load,
|
||||
},
|
||||
)),
|
||||
annotation: Box::new(Expr::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
annotation.clone(),
|
||||
)),
|
||||
value: None,
|
||||
simple: 1,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate a `StmtKind::Pass` statement.
|
||||
fn create_pass_stmt() -> Stmt {
|
||||
Stmt::new(Default::default(), Default::default(), StmtKind::Pass)
|
||||
}
|
||||
|
||||
/// Generate a `StmtKind:ClassDef` statement bsaed on the provided body and
|
||||
/// keywords.
|
||||
fn create_class_def_stmt(
|
||||
class_name: &str,
|
||||
body: Vec<Stmt>,
|
||||
total_keyword: Option<KeywordData>,
|
||||
) -> Stmt {
|
||||
let keywords = match total_keyword {
|
||||
Some(keyword) => vec![Keyword::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
keyword,
|
||||
)],
|
||||
None => vec![],
|
||||
};
|
||||
Stmt::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
StmtKind::ClassDef {
|
||||
name: class_name.to_string(),
|
||||
bases: vec![Expr::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
ExprKind::Name {
|
||||
id: "TypedDict".to_string(),
|
||||
ctx: ExprContext::Load,
|
||||
},
|
||||
)],
|
||||
keywords,
|
||||
body,
|
||||
decorator_list: vec![],
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn get_properties_from_dict_literal(keys: &[Expr], values: &[Expr]) -> Result<Vec<Stmt>> {
|
||||
keys.iter()
|
||||
.zip(values.iter())
|
||||
.map(|(key, value)| match &key.node {
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(property),
|
||||
..
|
||||
} => {
|
||||
if IDENTIFIER_REGEX.is_match(property) && !KWLIST.contains(&property.as_str()) {
|
||||
Ok(create_property_assignment_stmt(property, &value.node))
|
||||
} else {
|
||||
bail!("Invalid property name: {}", property)
|
||||
}
|
||||
}
|
||||
_ => bail!("Expected `key` to be `Constant::Str`"),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_properties_from_dict_call(func: &Expr, keywords: &[Keyword]) -> Result<Vec<Stmt>> {
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
if id == "dict" {
|
||||
get_properties_from_keywords(keywords)
|
||||
} else {
|
||||
bail!("Expected `id` to be `\"dict\"`")
|
||||
}
|
||||
} else {
|
||||
bail!("Expected `func` to be `ExprKind::Name`")
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated in Python 3.11, removed in Python 3.13.
|
||||
fn get_properties_from_keywords(keywords: &[Keyword]) -> Result<Vec<Stmt>> {
|
||||
keywords
|
||||
.iter()
|
||||
.map(|keyword| {
|
||||
if let Some(property) = &keyword.node.arg {
|
||||
Ok(create_property_assignment_stmt(
|
||||
property,
|
||||
&keyword.node.value.node,
|
||||
))
|
||||
} else {
|
||||
bail!("Expected `arg` to be `Some`")
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// The only way to have the `total` keyword is to use the args version, like:
|
||||
// (`TypedDict('name', {'a': int}, total=True)`)
|
||||
fn get_total_from_only_keyword(keywords: &[Keyword]) -> Option<KeywordData> {
|
||||
match keywords.get(0) {
|
||||
Some(keyword) => match &keyword.node.arg {
|
||||
Some(arg) => match arg.as_str() {
|
||||
"total" => Some(keyword.node.clone()),
|
||||
_ => None,
|
||||
},
|
||||
None => None,
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_properties_and_total(
|
||||
args: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
) -> Result<(Vec<Stmt>, Option<KeywordData>)> {
|
||||
// We don't have to manage the hybrid case because it's not possible to have a
|
||||
// dict and keywords. For example, the following is illegal:
|
||||
// MyType = TypedDict('MyType', {'a': int, 'b': str}, a=int, b=str)
|
||||
if let Some(dict) = args.get(1) {
|
||||
let total = get_total_from_only_keyword(keywords);
|
||||
match &dict.node {
|
||||
ExprKind::Dict { keys, values } => {
|
||||
Ok((get_properties_from_dict_literal(keys, values)?, total))
|
||||
}
|
||||
ExprKind::Call { func, keywords, .. } => {
|
||||
Ok((get_properties_from_dict_call(func, keywords)?, total))
|
||||
}
|
||||
_ => Ok((vec![create_pass_stmt()], total)),
|
||||
}
|
||||
} else if !keywords.is_empty() {
|
||||
Ok((get_properties_from_keywords(keywords)?, None))
|
||||
} else {
|
||||
Ok((vec![create_pass_stmt()], None))
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a `Fix` to convert a `TypedDict` to a functional class.
|
||||
fn convert_to_functional_class(
|
||||
stmt: &Stmt,
|
||||
class_name: &str,
|
||||
body: Vec<Stmt>,
|
||||
total_keyword: Option<KeywordData>,
|
||||
) -> Result<Fix> {
|
||||
let mut generator = SourceGenerator::new();
|
||||
generator.unparse_stmt(&create_class_def_stmt(class_name, body, total_keyword))?;
|
||||
let content = generator.generate()?;
|
||||
Ok(Fix::replacement(
|
||||
content,
|
||||
stmt.location,
|
||||
stmt.end_location.unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
/// U013
|
||||
pub fn convert_typed_dict_functional_to_class(
|
||||
checker: &mut Checker,
|
||||
stmt: &Stmt,
|
||||
targets: &[Expr],
|
||||
value: &Expr,
|
||||
) {
|
||||
if let Some((class_name, args, keywords)) = match_typed_dict_assign(targets, value) {
|
||||
match get_properties_and_total(args, keywords) {
|
||||
Err(err) => error!("Failed to parse TypedDict: {}", err),
|
||||
Ok((body, total_keyword)) => {
|
||||
let mut check = Check::new(
|
||||
CheckKind::ConvertTypedDictFunctionalToClass,
|
||||
Range::from_located(stmt),
|
||||
);
|
||||
if checker.patch(check.kind.code()) {
|
||||
match convert_to_functional_class(stmt, class_name, body, total_keyword) {
|
||||
Ok(fix) => check.amend(fix),
|
||||
Err(err) => error!("Failed to convert TypedDict: {}", err),
|
||||
};
|
||||
}
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ pub fn deprecated_unittest_alias(checker: &mut Checker, expr: &Expr) {
|
||||
CheckKind::DeprecatedUnittestAlias(attr.to_string(), target.to_string()),
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
format!("self.{}", target),
|
||||
expr.location,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
pub use convert_typed_dict_functional_to_class::convert_typed_dict_functional_to_class;
|
||||
pub use deprecated_unittest_alias::deprecated_unittest_alias;
|
||||
pub use super_call_with_parameters::super_call_with_parameters;
|
||||
pub use type_of_primitive::type_of_primitive;
|
||||
pub use unnecessary_abspath::unnecessary_abspath;
|
||||
pub use unnecessary_encode_utf8::unnecessary_encode_utf8;
|
||||
pub use unnecessary_future_import::unnecessary_future_import;
|
||||
pub use unnecessary_lru_cache_params::unnecessary_lru_cache_params;
|
||||
@@ -10,10 +10,10 @@ pub use use_pep604_annotation::use_pep604_annotation;
|
||||
pub use useless_metaclass_type::useless_metaclass_type;
|
||||
pub use useless_object_inheritance::useless_object_inheritance;
|
||||
|
||||
mod convert_typed_dict_functional_to_class;
|
||||
mod deprecated_unittest_alias;
|
||||
mod super_call_with_parameters;
|
||||
mod type_of_primitive;
|
||||
mod unnecessary_abspath;
|
||||
mod unnecessary_encode_utf8;
|
||||
mod unnecessary_future_import;
|
||||
mod unnecessary_lru_cache_params;
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn super_call_with_parameters(checker: &mut Checker, expr: &Expr, func: &Exp
|
||||
.map(|index| checker.parents[*index])
|
||||
.collect();
|
||||
if let Some(mut check) = checks::super_args(scope, &parents, expr, func, args) {
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
if let Some(fix) = pyupgrade::fixes::remove_super_arguments(checker.locator, expr) {
|
||||
check.amend(fix);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::pyupgrade::checks;
|
||||
/// U003
|
||||
pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if let Some(mut check) = checks::type_of_primitive(func, args, Range::from_located(expr)) {
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
if let CheckKind::TypeOfPrimitive(primitive) = &check.kind {
|
||||
check.amend(Fix::replacement(
|
||||
primitive.builtin(),
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
use rustpython_ast::Expr;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::pyupgrade::checks;
|
||||
|
||||
/// U002
|
||||
pub fn unnecessary_abspath(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if let Some(mut check) = checks::unnecessary_abspath(func, args, Range::from_located(expr)) {
|
||||
if checker.patch() {
|
||||
check.amend(Fix::replacement(
|
||||
"__file__".to_string(),
|
||||
expr.location,
|
||||
expr.end_location.unwrap(),
|
||||
));
|
||||
}
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use rustpython_ast::{Constant, Expr, ExprKind, Keyword};
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
const UTF8_LITERALS: &[&str] = &["utf-8", "utf8", "utf_8", "u8", "utf", "cp65001"];
|
||||
@@ -124,13 +124,16 @@ pub fn unnecessary_encode_utf8(
|
||||
expr,
|
||||
variable,
|
||||
checker.locator,
|
||||
checker.patch(),
|
||||
checker.patch(&CheckCode::U012),
|
||||
));
|
||||
} else {
|
||||
// "unicode text©".encode("utf-8")
|
||||
if let Some(check) =
|
||||
delete_default_encode_arg_or_kwarg(expr, args, kwargs, checker.patch())
|
||||
{
|
||||
if let Some(check) = delete_default_encode_arg_or_kwarg(
|
||||
expr,
|
||||
args,
|
||||
kwargs,
|
||||
checker.patch(&CheckCode::U012),
|
||||
) {
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
@@ -139,9 +142,12 @@ pub fn unnecessary_encode_utf8(
|
||||
// f"foo{bar}".encode(*args, **kwargs)
|
||||
ExprKind::JoinedStr { .. } => {
|
||||
if is_default_encode(args, kwargs) {
|
||||
if let Some(check) =
|
||||
delete_default_encode_arg_or_kwarg(expr, args, kwargs, checker.patch())
|
||||
{
|
||||
if let Some(check) = delete_default_encode_arg_or_kwarg(
|
||||
expr,
|
||||
args,
|
||||
kwargs,
|
||||
checker.patch(&CheckCode::U012),
|
||||
) {
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Lo
|
||||
),
|
||||
Range::from_located(stmt),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
let context = checker.binding_context();
|
||||
let deleted: Vec<&Stmt> = checker
|
||||
.deletions
|
||||
|
||||
@@ -11,7 +11,7 @@ pub fn unnecessary_lru_cache_params(checker: &mut Checker, decorator_list: &[Exp
|
||||
&checker.from_imports,
|
||||
&checker.import_aliases,
|
||||
) {
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
if let Some(fix) =
|
||||
fixes::remove_unnecessary_lru_cache_params(checker.locator, &check.location)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ pub fn use_pep585_annotation(checker: &mut Checker, expr: &Expr, id: &str) {
|
||||
CheckKind::UsePEP585Annotation(replacement.to_string()),
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
replacement.to_lowercase(),
|
||||
expr.location,
|
||||
|
||||
@@ -47,7 +47,7 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s
|
||||
let call_path = dealias_call_path(collect_call_paths(value), &checker.import_aliases);
|
||||
if checker.match_typing_call_path(&call_path, "Optional") {
|
||||
let mut check = Check::new(CheckKind::UsePEP604Annotation, Range::from_located(expr));
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
let mut generator = SourceGenerator::new();
|
||||
if let Ok(()) = generator.unparse_expr(&optional(slice), 0) {
|
||||
if let Ok(content) = generator.generate() {
|
||||
@@ -62,7 +62,7 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s
|
||||
checker.add_check(check);
|
||||
} else if checker.match_typing_call_path(&call_path, "Union") {
|
||||
let mut check = Check::new(CheckKind::UsePEP604Annotation, Range::from_located(expr));
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
match &slice.node {
|
||||
ExprKind::Slice { .. } => {
|
||||
// Invalid type annotation.
|
||||
|
||||
@@ -11,7 +11,7 @@ pub fn useless_metaclass_type(checker: &mut Checker, stmt: &Stmt, value: &Expr,
|
||||
if let Some(mut check) =
|
||||
checks::useless_metaclass_type(targets, value, Range::from_located(stmt))
|
||||
{
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
let context = checker.binding_context();
|
||||
let deleted: Vec<&Stmt> = checker
|
||||
.deletions
|
||||
|
||||
@@ -14,7 +14,7 @@ pub fn useless_object_inheritance(
|
||||
) {
|
||||
let scope = checker.current_scope();
|
||||
if let Some(mut check) = checks::useless_object_inheritance(name, bases, scope) {
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
if let Some(fix) = pyupgrade::fixes::remove_class_def_base(
|
||||
checker.locator,
|
||||
&stmt.location,
|
||||
|
||||
@@ -3,10 +3,10 @@ use once_cell::sync::Lazy;
|
||||
use rustpython_ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::autofix::{fixer, Fix};
|
||||
use crate::checks::CheckKind;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
use crate::Check;
|
||||
use crate::{Check, Settings};
|
||||
|
||||
/// See: https://github.com/microsoft/vscode/blob/095ddabc52b82498ee7f718a34f9dd11d59099a8/src/vs/base/common/strings.ts#L1094
|
||||
static CONFUSABLES: Lazy<FnvHashMap<u32, u32>> = Lazy::new(|| {
|
||||
@@ -1606,7 +1606,8 @@ pub fn ambiguous_unicode_character(
|
||||
start: &Location,
|
||||
end: &Location,
|
||||
context: Context,
|
||||
fix: bool,
|
||||
settings: &Settings,
|
||||
autofix: &fixer::Mode,
|
||||
) -> Vec<Check> {
|
||||
let mut checks = vec![];
|
||||
|
||||
@@ -1645,14 +1646,16 @@ pub fn ambiguous_unicode_character(
|
||||
end_location,
|
||||
},
|
||||
);
|
||||
if fix {
|
||||
check.amend(Fix::replacement(
|
||||
representant.to_string(),
|
||||
location,
|
||||
end_location,
|
||||
));
|
||||
if settings.enabled.contains(check.kind.code()) {
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
representant.to_string(),
|
||||
location,
|
||||
end_location,
|
||||
));
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,10 @@ use regex::Regex;
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::settings::pyproject::load_options;
|
||||
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
||||
use crate::{flake8_annotations, flake8_bugbear, flake8_quotes, fs, isort, pep8_naming};
|
||||
use crate::{
|
||||
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, fs, isort, mccabe,
|
||||
pep8_naming,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Configuration {
|
||||
@@ -22,17 +25,22 @@ pub struct Configuration {
|
||||
pub extend_ignore: Vec<CheckCodePrefix>,
|
||||
pub extend_select: Vec<CheckCodePrefix>,
|
||||
pub fix: bool,
|
||||
pub fixable: Vec<CheckCodePrefix>,
|
||||
pub ignore: Vec<CheckCodePrefix>,
|
||||
pub line_length: usize,
|
||||
pub per_file_ignores: Vec<PerFileIgnore>,
|
||||
pub select: Vec<CheckCodePrefix>,
|
||||
pub show_source: bool,
|
||||
pub src: Vec<PathBuf>,
|
||||
pub target_version: PythonVersion,
|
||||
pub unfixable: Vec<CheckCodePrefix>,
|
||||
// Plugins
|
||||
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||
pub flake8_bugbear: flake8_bugbear::settings::Settings,
|
||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||
pub flake8_tidy_imports: flake8_tidy_imports::settings::Settings,
|
||||
pub isort: isort::settings::Settings,
|
||||
pub mccabe: mccabe::settings::Settings,
|
||||
pub pep8_naming: pep8_naming::settings::Settings,
|
||||
}
|
||||
|
||||
@@ -116,6 +124,28 @@ impl Configuration {
|
||||
.unwrap_or_else(|| vec![CheckCodePrefix::E, CheckCodePrefix::F]),
|
||||
extend_select: options.extend_select.unwrap_or_default(),
|
||||
fix: options.fix.unwrap_or_default(),
|
||||
fixable: options.fixable.unwrap_or_else(|| {
|
||||
// TODO(charlie): Autogenerate this list.
|
||||
vec![
|
||||
CheckCodePrefix::A,
|
||||
CheckCodePrefix::B,
|
||||
CheckCodePrefix::BLE,
|
||||
CheckCodePrefix::C,
|
||||
CheckCodePrefix::D,
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::I,
|
||||
CheckCodePrefix::M,
|
||||
CheckCodePrefix::N,
|
||||
CheckCodePrefix::Q,
|
||||
CheckCodePrefix::S,
|
||||
CheckCodePrefix::T,
|
||||
CheckCodePrefix::U,
|
||||
CheckCodePrefix::W,
|
||||
CheckCodePrefix::YTT,
|
||||
]
|
||||
}),
|
||||
unfixable: options.unfixable.unwrap_or_default(),
|
||||
ignore: options.ignore.unwrap_or_default(),
|
||||
line_length: options.line_length.unwrap_or(88),
|
||||
per_file_ignores: options
|
||||
@@ -129,6 +159,7 @@ impl Configuration {
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
show_source: options.show_source.unwrap_or_default(),
|
||||
// Plugins
|
||||
flake8_annotations: options
|
||||
.flake8_annotations
|
||||
@@ -142,10 +173,18 @@ impl Configuration {
|
||||
.flake8_quotes
|
||||
.map(flake8_quotes::settings::Settings::from_options)
|
||||
.unwrap_or_default(),
|
||||
flake8_tidy_imports: options
|
||||
.flake8_tidy_imports
|
||||
.map(flake8_tidy_imports::settings::Settings::from_options)
|
||||
.unwrap_or_default(),
|
||||
isort: options
|
||||
.isort
|
||||
.map(isort::settings::Settings::from_options)
|
||||
.unwrap_or_default(),
|
||||
mccabe: options
|
||||
.mccabe
|
||||
.map(mccabe::settings::Settings::from_options)
|
||||
.unwrap_or_default(),
|
||||
pep8_naming: options
|
||||
.pep8_naming
|
||||
.map(pep8_naming::settings::Settings::from_options)
|
||||
|
||||
@@ -13,7 +13,10 @@ use crate::checks::CheckCode;
|
||||
use crate::checks_gen::{CheckCodePrefix, PrefixSpecificity};
|
||||
use crate::settings::configuration::Configuration;
|
||||
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
||||
use crate::{flake8_annotations, flake8_bugbear, flake8_quotes, isort, pep8_naming};
|
||||
use crate::{
|
||||
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, isort, mccabe,
|
||||
pep8_naming,
|
||||
};
|
||||
|
||||
pub mod configuration;
|
||||
pub mod options;
|
||||
@@ -27,15 +30,19 @@ pub struct Settings {
|
||||
pub enabled: FnvHashSet<CheckCode>,
|
||||
pub exclude: Vec<FilePattern>,
|
||||
pub extend_exclude: Vec<FilePattern>,
|
||||
pub fixable: FnvHashSet<CheckCode>,
|
||||
pub line_length: usize,
|
||||
pub per_file_ignores: Vec<PerFileIgnore>,
|
||||
pub show_source: bool,
|
||||
pub src: Vec<PathBuf>,
|
||||
pub target_version: PythonVersion,
|
||||
// Plugins
|
||||
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||
pub flake8_bugbear: flake8_bugbear::settings::Settings,
|
||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||
pub flake8_tidy_imports: flake8_tidy_imports::settings::Settings,
|
||||
pub isort: isort::settings::Settings,
|
||||
pub mccabe: mccabe::settings::Settings,
|
||||
pub pep8_naming: pep8_naming::settings::Settings,
|
||||
}
|
||||
|
||||
@@ -44,29 +51,40 @@ impl Settings {
|
||||
Self {
|
||||
dummy_variable_rgx: config.dummy_variable_rgx,
|
||||
enabled: resolve_codes(
|
||||
&config.select,
|
||||
&config.extend_select,
|
||||
&config.ignore,
|
||||
&config.extend_ignore,
|
||||
&config
|
||||
.select
|
||||
.into_iter()
|
||||
.chain(config.extend_select.into_iter())
|
||||
.collect::<Vec<_>>(),
|
||||
&config
|
||||
.ignore
|
||||
.into_iter()
|
||||
.chain(config.extend_ignore.into_iter())
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
exclude: config.exclude,
|
||||
extend_exclude: config.extend_exclude,
|
||||
fixable: resolve_codes(&config.fixable, &config.unfixable),
|
||||
flake8_annotations: config.flake8_annotations,
|
||||
flake8_bugbear: config.flake8_bugbear,
|
||||
flake8_quotes: config.flake8_quotes,
|
||||
flake8_tidy_imports: config.flake8_tidy_imports,
|
||||
isort: config.isort,
|
||||
mccabe: config.mccabe,
|
||||
line_length: config.line_length,
|
||||
pep8_naming: config.pep8_naming,
|
||||
per_file_ignores: config.per_file_ignores,
|
||||
src: config.src,
|
||||
target_version: config.target_version,
|
||||
show_source: config.show_source,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_rule(check_code: CheckCode) -> Self {
|
||||
Self {
|
||||
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
|
||||
enabled: FnvHashSet::from_iter([check_code]),
|
||||
enabled: FnvHashSet::from_iter([check_code.clone()]),
|
||||
fixable: FnvHashSet::from_iter([check_code]),
|
||||
exclude: Default::default(),
|
||||
extend_exclude: Default::default(),
|
||||
line_length: 88,
|
||||
@@ -76,15 +94,19 @@ impl Settings {
|
||||
flake8_annotations: Default::default(),
|
||||
flake8_bugbear: Default::default(),
|
||||
flake8_quotes: Default::default(),
|
||||
flake8_tidy_imports: Default::default(),
|
||||
isort: Default::default(),
|
||||
mccabe: Default::default(),
|
||||
pep8_naming: Default::default(),
|
||||
show_source: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_rules(check_codes: Vec<CheckCode>) -> Self {
|
||||
Self {
|
||||
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
|
||||
enabled: FnvHashSet::from_iter(check_codes),
|
||||
enabled: FnvHashSet::from_iter(check_codes.clone()),
|
||||
fixable: FnvHashSet::from_iter(check_codes),
|
||||
exclude: Default::default(),
|
||||
extend_exclude: Default::default(),
|
||||
line_length: 88,
|
||||
@@ -94,8 +116,11 @@ impl Settings {
|
||||
flake8_annotations: Default::default(),
|
||||
flake8_bugbear: Default::default(),
|
||||
flake8_quotes: Default::default(),
|
||||
flake8_tidy_imports: Default::default(),
|
||||
isort: Default::default(),
|
||||
mccabe: Default::default(),
|
||||
pep8_naming: Default::default(),
|
||||
show_source: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,28 +132,29 @@ impl Hash for Settings {
|
||||
for value in self.enabled.iter() {
|
||||
value.hash(state);
|
||||
}
|
||||
for value in self.fixable.iter() {
|
||||
value.hash(state);
|
||||
}
|
||||
self.line_length.hash(state);
|
||||
for value in self.per_file_ignores.iter() {
|
||||
value.hash(state);
|
||||
}
|
||||
self.show_source.hash(state);
|
||||
self.target_version.hash(state);
|
||||
// Add plugin properties in alphabetical order.
|
||||
self.flake8_annotations.hash(state);
|
||||
self.flake8_bugbear.hash(state);
|
||||
self.flake8_quotes.hash(state);
|
||||
self.flake8_tidy_imports.hash(state);
|
||||
self.isort.hash(state);
|
||||
self.mccabe.hash(state);
|
||||
self.pep8_naming.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a set of selected and ignored prefixes, resolve the set of enabled
|
||||
/// error codes.
|
||||
fn resolve_codes(
|
||||
select: &[CheckCodePrefix],
|
||||
extend_select: &[CheckCodePrefix],
|
||||
ignore: &[CheckCodePrefix],
|
||||
extend_ignore: &[CheckCodePrefix],
|
||||
) -> FnvHashSet<CheckCode> {
|
||||
fn resolve_codes(select: &[CheckCodePrefix], ignore: &[CheckCodePrefix]) -> FnvHashSet<CheckCode> {
|
||||
let mut codes: FnvHashSet<CheckCode> = FnvHashSet::default();
|
||||
for specificity in [
|
||||
PrefixSpecificity::Category,
|
||||
@@ -141,11 +167,6 @@ fn resolve_codes(
|
||||
codes.extend(prefix.codes());
|
||||
}
|
||||
}
|
||||
for prefix in extend_select {
|
||||
if prefix.specificity() == specificity {
|
||||
codes.extend(prefix.codes());
|
||||
}
|
||||
}
|
||||
for prefix in ignore {
|
||||
if prefix.specificity() == specificity {
|
||||
for code in prefix.codes() {
|
||||
@@ -153,13 +174,6 @@ fn resolve_codes(
|
||||
}
|
||||
}
|
||||
}
|
||||
for prefix in extend_ignore {
|
||||
if prefix.specificity() == specificity {
|
||||
for code in prefix.codes() {
|
||||
codes.remove(&code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
codes
|
||||
}
|
||||
@@ -174,19 +188,19 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn resolver() {
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W], &[], &[], &[]);
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W], &[]);
|
||||
let expected = FnvHashSet::from_iter([CheckCode::W292, CheckCode::W605]);
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W6], &[], &[], &[]);
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W6], &[]);
|
||||
let expected = FnvHashSet::from_iter([CheckCode::W605]);
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W], &[], &[CheckCodePrefix::W292], &[]);
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W], &[CheckCodePrefix::W292]);
|
||||
let expected = FnvHashSet::from_iter([CheckCode::W605]);
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W605], &[], &[CheckCodePrefix::W605], &[]);
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W605], &[CheckCodePrefix::W605]);
|
||||
let expected = FnvHashSet::from_iter([]);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::settings::types::PythonVersion;
|
||||
use crate::{flake8_annotations, flake8_bugbear, flake8_quotes, isort, pep8_naming};
|
||||
use crate::{
|
||||
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, isort, mccabe,
|
||||
pep8_naming,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
@@ -16,16 +19,21 @@ pub struct Options {
|
||||
pub extend_ignore: Option<Vec<CheckCodePrefix>>,
|
||||
pub extend_select: Option<Vec<CheckCodePrefix>>,
|
||||
pub fix: Option<bool>,
|
||||
pub fixable: Option<Vec<CheckCodePrefix>>,
|
||||
pub ignore: Option<Vec<CheckCodePrefix>>,
|
||||
pub line_length: Option<usize>,
|
||||
pub select: Option<Vec<CheckCodePrefix>>,
|
||||
pub show_source: Option<bool>,
|
||||
pub src: Option<Vec<String>>,
|
||||
pub target_version: Option<PythonVersion>,
|
||||
pub unfixable: Option<Vec<CheckCodePrefix>>,
|
||||
// Plugins
|
||||
pub flake8_annotations: Option<flake8_annotations::settings::Options>,
|
||||
pub flake8_bugbear: Option<flake8_bugbear::settings::Options>,
|
||||
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
|
||||
pub flake8_tidy_imports: Option<flake8_tidy_imports::settings::Options>,
|
||||
pub isort: Option<isort::settings::Options>,
|
||||
pub mccabe: Option<mccabe::settings::Options>,
|
||||
pub pep8_naming: Option<pep8_naming::settings::Options>,
|
||||
// Tables are required to go last.
|
||||
pub per_file_ignores: Option<FnvHashMap<String, Vec<CheckCodePrefix>>>,
|
||||
|
||||
@@ -105,11 +105,12 @@ mod tests {
|
||||
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::flake8_quotes::settings::Quote;
|
||||
use crate::flake8_tidy_imports::settings::Strictness;
|
||||
use crate::settings::pyproject::{
|
||||
find_project_root, find_pyproject_toml, parse_pyproject_toml, Options, Pyproject, Tools,
|
||||
};
|
||||
use crate::settings::types::PatternPrefixPair;
|
||||
use crate::{flake8_bugbear, flake8_quotes, pep8_naming};
|
||||
use crate::{flake8_bugbear, flake8_quotes, flake8_tidy_imports, mccabe, pep8_naming};
|
||||
|
||||
#[test]
|
||||
fn deserialize() -> Result<()> {
|
||||
@@ -133,22 +134,27 @@ mod tests {
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
extend_select: None,
|
||||
ignore: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: None,
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
select: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
})
|
||||
@@ -165,22 +171,27 @@ line-length = 79
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: Some(79),
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
extend_select: None,
|
||||
ignore: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: None,
|
||||
line_length: Some(79),
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
select: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
})
|
||||
@@ -205,14 +216,19 @@ exclude = ["foo.py"]
|
||||
extend_select: None,
|
||||
ignore: None,
|
||||
extend_ignore: None,
|
||||
fixable: None,
|
||||
unfixable: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
show_source: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
})
|
||||
@@ -229,22 +245,27 @@ select = ["E501"]
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: Some(vec![CheckCodePrefix::E501]),
|
||||
extend_select: None,
|
||||
ignore: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: None,
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
select: Some(vec![CheckCodePrefix::E501]),
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
})
|
||||
@@ -262,22 +283,27 @@ ignore = ["E501"]
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
extend_select: Some(vec![CheckCodePrefix::M001]),
|
||||
ignore: Some(vec![CheckCodePrefix::E501]),
|
||||
extend_ignore: None,
|
||||
extend_select: Some(vec![CheckCodePrefix::M001]),
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![CheckCodePrefix::E501]),
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
select: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
})
|
||||
@@ -346,6 +372,8 @@ other-attribute = 1
|
||||
extend_select: None,
|
||||
ignore: None,
|
||||
extend_ignore: None,
|
||||
fixable: None,
|
||||
unfixable: None,
|
||||
per_file_ignores: Some(FnvHashMap::from_iter([(
|
||||
"__init__.py".to_string(),
|
||||
vec![CheckCodePrefix::F401]
|
||||
@@ -353,6 +381,7 @@ other-attribute = 1
|
||||
dummy_variable_rgx: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
show_source: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: Some(flake8_bugbear::settings::Options {
|
||||
extend_immutable_calls: Some(vec![
|
||||
@@ -366,7 +395,13 @@ other-attribute = 1
|
||||
docstring_quotes: Some(Quote::Double),
|
||||
avoid_escape: Some(true),
|
||||
}),
|
||||
flake8_tidy_imports: Some(flake8_tidy_imports::settings::Options {
|
||||
ban_relative_imports: Some(Strictness::Parents)
|
||||
}),
|
||||
isort: None,
|
||||
mccabe: Some(mccabe::settings::Options {
|
||||
max_complexity: Some(10),
|
||||
}),
|
||||
pep8_naming: Some(pep8_naming::settings::Options {
|
||||
ignore_names: Some(vec![
|
||||
"setUp".to_string(),
|
||||
@@ -382,7 +417,10 @@ other-attribute = 1
|
||||
"longMessage".to_string(),
|
||||
"maxDiff".to_string(),
|
||||
]),
|
||||
classmethod_decorators: Some(vec!["classmethod".to_string()]),
|
||||
classmethod_decorators: Some(vec![
|
||||
"classmethod".to_string(),
|
||||
"pydantic.validator".to_string()
|
||||
]),
|
||||
staticmethod_decorators: Some(vec!["staticmethod".to_string()]),
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use regex::Regex;
|
||||
use crate::checks::CheckCode;
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::settings::types::{FilePattern, PythonVersion};
|
||||
use crate::{flake8_annotations, flake8_quotes, isort, pep8_naming, Configuration};
|
||||
use crate::{
|
||||
flake8_annotations, flake8_quotes, flake8_tidy_imports, isort, pep8_naming, Configuration,
|
||||
};
|
||||
|
||||
/// Struct to render user-facing exclusion patterns.
|
||||
#[derive(Debug)]
|
||||
@@ -41,15 +43,19 @@ pub struct UserConfiguration {
|
||||
pub extend_ignore: Vec<CheckCodePrefix>,
|
||||
pub extend_select: Vec<CheckCodePrefix>,
|
||||
pub fix: bool,
|
||||
pub fixable: Vec<CheckCodePrefix>,
|
||||
pub ignore: Vec<CheckCodePrefix>,
|
||||
pub line_length: usize,
|
||||
pub per_file_ignores: Vec<(Exclusion, Vec<CheckCode>)>,
|
||||
pub select: Vec<CheckCodePrefix>,
|
||||
pub show_source: bool,
|
||||
pub src: Vec<PathBuf>,
|
||||
pub target_version: PythonVersion,
|
||||
pub unfixable: Vec<CheckCodePrefix>,
|
||||
// Plugins
|
||||
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||
pub flake8_tidy_imports: flake8_tidy_imports::settings::Settings,
|
||||
pub isort: isort::settings::Settings,
|
||||
pub pep8_naming: pep8_naming::settings::Settings,
|
||||
// Non-settings exposed to the user
|
||||
@@ -78,6 +84,8 @@ impl UserConfiguration {
|
||||
extend_ignore: configuration.extend_ignore,
|
||||
extend_select: configuration.extend_select,
|
||||
fix: configuration.fix,
|
||||
fixable: configuration.fixable,
|
||||
unfixable: configuration.unfixable,
|
||||
ignore: configuration.ignore,
|
||||
line_length: configuration.line_length,
|
||||
per_file_ignores: configuration
|
||||
@@ -93,8 +101,10 @@ impl UserConfiguration {
|
||||
select: configuration.select,
|
||||
src: configuration.src,
|
||||
target_version: configuration.target_version,
|
||||
show_source: configuration.show_source,
|
||||
flake8_annotations: configuration.flake8_annotations,
|
||||
flake8_quotes: configuration.flake8_quotes,
|
||||
flake8_tidy_imports: configuration.flake8_tidy_imports,
|
||||
isort: configuration.isort,
|
||||
pep8_naming: configuration.pep8_naming,
|
||||
project_root,
|
||||
|
||||
85
src/snapshots/ruff__linter__tests__BLE001_BLE.py.snap
Normal file
85
src/snapshots/ruff__linter__tests__BLE001_BLE.py.snap
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 5
|
||||
column: 7
|
||||
end_location:
|
||||
row: 5
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 13
|
||||
column: 7
|
||||
end_location:
|
||||
row: 13
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 23
|
||||
column: 7
|
||||
end_location:
|
||||
row: 23
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 25
|
||||
column: 7
|
||||
end_location:
|
||||
row: 25
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 31
|
||||
column: 7
|
||||
end_location:
|
||||
row: 31
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 36
|
||||
column: 11
|
||||
end_location:
|
||||
row: 36
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 42
|
||||
column: 7
|
||||
end_location:
|
||||
row: 42
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 45
|
||||
column: 11
|
||||
end_location:
|
||||
row: 45
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 52
|
||||
column: 11
|
||||
end_location:
|
||||
row: 52
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 54
|
||||
column: 7
|
||||
end_location:
|
||||
row: 54
|
||||
column: 16
|
||||
fix: ~
|
||||
|
||||
@@ -38,4 +38,40 @@ expression: checks
|
||||
row: 146
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
NoBlankLineBeforeFunction: 1
|
||||
location:
|
||||
row: 541
|
||||
column: 4
|
||||
end_location:
|
||||
row: 544
|
||||
column: 7
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 540
|
||||
column: 0
|
||||
end_location:
|
||||
row: 541
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
NoBlankLineBeforeFunction: 1
|
||||
location:
|
||||
row: 563
|
||||
column: 4
|
||||
end_location:
|
||||
row: 566
|
||||
column: 7
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 562
|
||||
column: 0
|
||||
end_location:
|
||||
row: 563
|
||||
column: 0
|
||||
applied: false
|
||||
|
||||
|
||||
@@ -38,4 +38,40 @@ expression: checks
|
||||
row: 148
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
NoBlankLineAfterFunction: 1
|
||||
location:
|
||||
row: 550
|
||||
column: 4
|
||||
end_location:
|
||||
row: 553
|
||||
column: 7
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 554
|
||||
column: 0
|
||||
end_location:
|
||||
row: 555
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
NoBlankLineAfterFunction: 1
|
||||
location:
|
||||
row: 563
|
||||
column: 4
|
||||
end_location:
|
||||
row: 566
|
||||
column: 7
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 567
|
||||
column: 0
|
||||
end_location:
|
||||
row: 568
|
||||
column: 0
|
||||
applied: false
|
||||
|
||||
|
||||
@@ -130,4 +130,28 @@ expression: checks
|
||||
row: 527
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 541
|
||||
column: 4
|
||||
end_location:
|
||||
row: 544
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 550
|
||||
column: 4
|
||||
end_location:
|
||||
row: 553
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 563
|
||||
column: 4
|
||||
end_location:
|
||||
row: 566
|
||||
column: 7
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -10,7 +10,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 2
|
||||
column: 14
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: res is None
|
||||
location:
|
||||
row: 2
|
||||
column: 3
|
||||
end_location:
|
||||
row: 2
|
||||
column: 14
|
||||
applied: false
|
||||
- kind:
|
||||
NoneComparison: NotEq
|
||||
location:
|
||||
@@ -19,7 +28,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 5
|
||||
column: 14
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: res is not None
|
||||
location:
|
||||
row: 5
|
||||
column: 3
|
||||
end_location:
|
||||
row: 5
|
||||
column: 14
|
||||
applied: false
|
||||
- kind:
|
||||
NoneComparison: Eq
|
||||
location:
|
||||
@@ -28,7 +46,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 8
|
||||
column: 7
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: None is res
|
||||
location:
|
||||
row: 8
|
||||
column: 3
|
||||
end_location:
|
||||
row: 8
|
||||
column: 14
|
||||
applied: false
|
||||
- kind:
|
||||
NoneComparison: NotEq
|
||||
location:
|
||||
@@ -37,7 +64,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 11
|
||||
column: 7
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: None is not res
|
||||
location:
|
||||
row: 11
|
||||
column: 3
|
||||
end_location:
|
||||
row: 11
|
||||
column: 14
|
||||
applied: false
|
||||
- kind:
|
||||
NoneComparison: Eq
|
||||
location:
|
||||
@@ -46,7 +82,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 14
|
||||
column: 17
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: "res[1] is None"
|
||||
location:
|
||||
row: 14
|
||||
column: 3
|
||||
end_location:
|
||||
row: 14
|
||||
column: 17
|
||||
applied: false
|
||||
- kind:
|
||||
NoneComparison: NotEq
|
||||
location:
|
||||
@@ -55,7 +100,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 17
|
||||
column: 17
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: "res[1] is not None"
|
||||
location:
|
||||
row: 17
|
||||
column: 3
|
||||
end_location:
|
||||
row: 17
|
||||
column: 17
|
||||
applied: false
|
||||
- kind:
|
||||
NoneComparison: NotEq
|
||||
location:
|
||||
@@ -64,7 +118,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 20
|
||||
column: 7
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: "None is not res[1]"
|
||||
location:
|
||||
row: 20
|
||||
column: 3
|
||||
end_location:
|
||||
row: 20
|
||||
column: 17
|
||||
applied: false
|
||||
- kind:
|
||||
NoneComparison: Eq
|
||||
location:
|
||||
@@ -73,5 +136,50 @@ expression: checks
|
||||
end_location:
|
||||
row: 23
|
||||
column: 7
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: "None is res[1]"
|
||||
location:
|
||||
row: 23
|
||||
column: 3
|
||||
end_location:
|
||||
row: 23
|
||||
column: 17
|
||||
applied: false
|
||||
- kind:
|
||||
NoneComparison: Eq
|
||||
location:
|
||||
row: 26
|
||||
column: 8
|
||||
end_location:
|
||||
row: 26
|
||||
column: 12
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 26
|
||||
column: 3
|
||||
end_location:
|
||||
row: 26
|
||||
column: 3
|
||||
applied: false
|
||||
- kind:
|
||||
NoneComparison: NotEq
|
||||
location:
|
||||
row: 26
|
||||
column: 16
|
||||
end_location:
|
||||
row: 26
|
||||
column: 20
|
||||
fix:
|
||||
patch:
|
||||
content: x is None is not None
|
||||
location:
|
||||
row: 26
|
||||
column: 3
|
||||
end_location:
|
||||
row: 26
|
||||
column: 20
|
||||
applied: false
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
assertion_line: 531
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
@@ -12,7 +13,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 2
|
||||
column: 14
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: res is True
|
||||
location:
|
||||
row: 2
|
||||
column: 3
|
||||
end_location:
|
||||
row: 2
|
||||
column: 14
|
||||
applied: false
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- false
|
||||
@@ -23,7 +33,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 5
|
||||
column: 15
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: res is not False
|
||||
location:
|
||||
row: 5
|
||||
column: 3
|
||||
end_location:
|
||||
row: 5
|
||||
column: 15
|
||||
applied: false
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- true
|
||||
@@ -34,7 +53,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 8
|
||||
column: 7
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: True is not res
|
||||
location:
|
||||
row: 8
|
||||
column: 3
|
||||
end_location:
|
||||
row: 8
|
||||
column: 14
|
||||
applied: false
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- false
|
||||
@@ -45,7 +73,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 11
|
||||
column: 8
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: False is res
|
||||
location:
|
||||
row: 11
|
||||
column: 3
|
||||
end_location:
|
||||
row: 11
|
||||
column: 15
|
||||
applied: false
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- true
|
||||
@@ -56,7 +93,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 14
|
||||
column: 17
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: "res[1] is True"
|
||||
location:
|
||||
row: 14
|
||||
column: 3
|
||||
end_location:
|
||||
row: 14
|
||||
column: 17
|
||||
applied: false
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- false
|
||||
@@ -67,7 +113,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 17
|
||||
column: 18
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: "res[1] is not False"
|
||||
location:
|
||||
row: 17
|
||||
column: 3
|
||||
end_location:
|
||||
row: 17
|
||||
column: 18
|
||||
applied: false
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- true
|
||||
@@ -78,7 +133,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 20
|
||||
column: 23
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: cond is True
|
||||
location:
|
||||
row: 20
|
||||
column: 11
|
||||
end_location:
|
||||
row: 20
|
||||
column: 23
|
||||
applied: false
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- false
|
||||
@@ -89,7 +153,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 20
|
||||
column: 48
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: cond is False
|
||||
location:
|
||||
row: 20
|
||||
column: 35
|
||||
end_location:
|
||||
row: 20
|
||||
column: 48
|
||||
applied: false
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- true
|
||||
@@ -100,5 +173,54 @@ expression: checks
|
||||
end_location:
|
||||
row: 22
|
||||
column: 8
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: True is TrueElement
|
||||
location:
|
||||
row: 22
|
||||
column: 3
|
||||
end_location:
|
||||
row: 22
|
||||
column: 24
|
||||
applied: false
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- true
|
||||
- Eq
|
||||
location:
|
||||
row: 25
|
||||
column: 10
|
||||
end_location:
|
||||
row: 25
|
||||
column: 14
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 25
|
||||
column: 3
|
||||
end_location:
|
||||
row: 25
|
||||
column: 3
|
||||
applied: false
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- false
|
||||
- NotEq
|
||||
location:
|
||||
row: 25
|
||||
column: 18
|
||||
end_location:
|
||||
row: 25
|
||||
column: 23
|
||||
fix:
|
||||
patch:
|
||||
content: res is True is not False
|
||||
location:
|
||||
row: 25
|
||||
column: 3
|
||||
end_location:
|
||||
row: 25
|
||||
column: 23
|
||||
applied: false
|
||||
|
||||
|
||||
@@ -9,7 +9,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 2
|
||||
column: 13
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: X not in Y
|
||||
location:
|
||||
row: 2
|
||||
column: 3
|
||||
end_location:
|
||||
row: 2
|
||||
column: 13
|
||||
applied: false
|
||||
- kind: NotInTest
|
||||
location:
|
||||
row: 5
|
||||
@@ -17,7 +26,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 5
|
||||
column: 15
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: X.B not in Y
|
||||
location:
|
||||
row: 5
|
||||
column: 3
|
||||
end_location:
|
||||
row: 5
|
||||
column: 15
|
||||
applied: false
|
||||
- kind: NotInTest
|
||||
location:
|
||||
row: 8
|
||||
@@ -25,7 +43,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 8
|
||||
column: 13
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: X not in Y
|
||||
location:
|
||||
row: 8
|
||||
column: 3
|
||||
end_location:
|
||||
row: 8
|
||||
column: 13
|
||||
applied: false
|
||||
- kind: NotInTest
|
||||
location:
|
||||
row: 11
|
||||
@@ -33,7 +60,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 11
|
||||
column: 28
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: Y not in Z
|
||||
location:
|
||||
row: 11
|
||||
column: 18
|
||||
end_location:
|
||||
row: 11
|
||||
column: 28
|
||||
applied: false
|
||||
- kind: NotInTest
|
||||
location:
|
||||
row: 14
|
||||
@@ -41,5 +77,14 @@ expression: checks
|
||||
end_location:
|
||||
row: 14
|
||||
column: 14
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: X not in Y
|
||||
location:
|
||||
row: 14
|
||||
column: 3
|
||||
end_location:
|
||||
row: 14
|
||||
column: 15
|
||||
applied: false
|
||||
|
||||
|
||||
@@ -9,7 +9,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 2
|
||||
column: 13
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: X is not Y
|
||||
location:
|
||||
row: 2
|
||||
column: 3
|
||||
end_location:
|
||||
row: 2
|
||||
column: 13
|
||||
applied: false
|
||||
- kind: NotIsTest
|
||||
location:
|
||||
row: 5
|
||||
@@ -17,7 +26,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 5
|
||||
column: 15
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: X.B is not Y
|
||||
location:
|
||||
row: 5
|
||||
column: 3
|
||||
end_location:
|
||||
row: 5
|
||||
column: 15
|
||||
applied: false
|
||||
- kind: NotIsTest
|
||||
location:
|
||||
row: 8
|
||||
|
||||
69
src/snapshots/ruff__linter__tests__FBT001_FBT.py.snap
Normal file
69
src/snapshots/ruff__linter__tests__FBT001_FBT.py.snap
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: BooleanPositionalArgInFunctionDefinition
|
||||
location:
|
||||
row: 4
|
||||
column: 4
|
||||
end_location:
|
||||
row: 4
|
||||
column: 26
|
||||
fix: ~
|
||||
- kind: BooleanPositionalArgInFunctionDefinition
|
||||
location:
|
||||
row: 5
|
||||
column: 4
|
||||
end_location:
|
||||
row: 5
|
||||
column: 31
|
||||
fix: ~
|
||||
- kind: BooleanPositionalArgInFunctionDefinition
|
||||
location:
|
||||
row: 10
|
||||
column: 4
|
||||
end_location:
|
||||
row: 10
|
||||
column: 36
|
||||
fix: ~
|
||||
- kind: BooleanPositionalArgInFunctionDefinition
|
||||
location:
|
||||
row: 11
|
||||
column: 4
|
||||
end_location:
|
||||
row: 11
|
||||
column: 41
|
||||
fix: ~
|
||||
- kind: BooleanPositionalArgInFunctionDefinition
|
||||
location:
|
||||
row: 14
|
||||
column: 4
|
||||
end_location:
|
||||
row: 14
|
||||
column: 37
|
||||
fix: ~
|
||||
- kind: BooleanPositionalArgInFunctionDefinition
|
||||
location:
|
||||
row: 15
|
||||
column: 4
|
||||
end_location:
|
||||
row: 15
|
||||
column: 42
|
||||
fix: ~
|
||||
- kind: BooleanPositionalArgInFunctionDefinition
|
||||
location:
|
||||
row: 18
|
||||
column: 4
|
||||
end_location:
|
||||
row: 18
|
||||
column: 40
|
||||
fix: ~
|
||||
- kind: BooleanPositionalArgInFunctionDefinition
|
||||
location:
|
||||
row: 19
|
||||
column: 4
|
||||
end_location:
|
||||
row: 19
|
||||
column: 45
|
||||
fix: ~
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user