Compare commits

...

39 Commits

Author SHA1 Message Date
Charlie Marsh
4107bc828d Bump version to 0.0.188 2022-12-19 12:18:06 -05:00
Charlie Marsh
706d28cabc Rename PDV checks to PD (#1288) 2022-12-19 00:20:28 -05:00
Charlie Marsh
4da2264722 Avoid T201 errors for print(..., file=fp)-like calls (#1287) 2022-12-19 00:10:07 -05:00
Charlie Marsh
bf88c815aa Move flake8-debugger tests into flake8-debugger subdirectory (#1286) 2022-12-18 22:06:42 -05:00
Yasu_umi
8a4831dd5b Implement flake8-datetimez (#1270) 2022-12-18 22:06:06 -05:00
Charlie Marsh
b5ab492a70 Bump version to 0.0.187 2022-12-18 20:09:02 -05:00
Charlie Marsh
1fc09ebd5c Fix inverted E501 condition (#1285) 2022-12-18 20:08:30 -05:00
Charlie Marsh
6cf047976c Bump pygrep-hooks tally in README 2022-12-18 18:05:32 -05:00
Reiner Gerecke
87465daacc pygrep-hooks - deprecated use of logging.warn & no blanket type ignore (#1275) 2022-12-18 18:04:21 -05:00
Chris Brendel
a52bed7101 Use --stdin-filename when resolving configuration files (#1281) 2022-12-18 17:51:55 -05:00
Anders Kaseorg
20ac823778 generate-check-code-prefix: Run rustfmt automatically; only write if changed (#1282) 2022-12-18 17:46:23 -05:00
Charlie Marsh
1028ed3565 Bump version to 0.0.186 2022-12-18 14:30:30 -05:00
Anders Kaseorg
98897db6ac Add packaging status badge from repology (#1276) 2022-12-18 14:27:29 -05:00
Honkertonken
5ce4262112 Readme : Fix incorrect exmaple. (#1277) 2022-12-18 12:04:48 -05:00
Charlie Marsh
6b2359384d Print redirect warnings exactly once per code (#1280) 2022-12-18 12:03:49 -05:00
Harutaka Kawamura
d3443d7c19 Update RustPython to use correct Tuple location (#1278) 2022-12-18 08:53:57 -05:00
Anders Kaseorg
04b1e1de6f README: Add missing backtick (#1274) 2022-12-18 00:29:33 -05:00
Anders Kaseorg
c93c85300f Repair corrupted PDV007, PDV009 messages (#1273) 2022-12-18 00:29:12 -05:00
Charlie Marsh
73ed6f8654 Touch-up README 2022-12-17 21:38:45 -05:00
Charlie Marsh
eb183645f3 Add ruff-lsp to README (#1272)
Add ruff-lsp to README
2022-12-17 21:37:11 -05:00
Charlie Marsh
ef17aa93da Add ruff-lsp to README (#1272) 2022-12-17 21:37:00 -05:00
Charlie Marsh
f366b0147f Add ruff-lsp to README (#1272) 2022-12-17 21:35:44 -05:00
Charlie Marsh
fc88fa35ff Add instructions for Sublime Text installation (#1271) 2022-12-17 16:22:50 -05:00
Charlie Marsh
a2806eb8ef Bump version to 0.0.185 2022-12-16 23:47:56 -05:00
Charlie Marsh
89d919eac5 Re-remove W605_1.py from Black compatibility test 2022-12-16 23:17:27 -05:00
Charlie Marsh
5ad77fbc8d Move checkers into their own module (#1268) 2022-12-16 22:55:47 -05:00
Charlie Marsh
9cb18a481b Separate line-based checker from noqa enforcement (#1267) 2022-12-16 22:49:27 -05:00
Charlie Marsh
2393e270ed Change a few more methods to take AsRef<Path> 2022-12-16 21:38:52 -05:00
Charlie Marsh
f36e6035c8 Change a few methods to take AsRef<Path> 2022-12-16 21:28:19 -05:00
Charlie Marsh
ecf0dd05d6 Auto-detect same-package imports (#1266) 2022-12-16 21:19:11 -05:00
Charlie Marsh
5f67ee93f7 Replace cache bool with an enum 2022-12-16 15:45:30 -05:00
Charlie Marsh
1e19142d0e Bump version to 0.0.184 2022-12-16 14:36:25 -05:00
Charlie Marsh
6a95dade6d Actually check-in snapshots for #1265 2022-12-16 14:36:00 -05:00
Charlie Marsh
d6e765877e Enable autofix for __init__ method with missing None-return (#1265) 2022-12-16 14:28:56 -05:00
Charlie Marsh
e4d36bae57 Replace ignore_noqa and autofix booleans with enums (#1264) 2022-12-16 14:01:25 -05:00
Harutaka Kawamura
e3531276a7 Fix F501 (line-too-long) start location (#1262) 2022-12-16 11:29:47 -05:00
Charlie Marsh
634553f188 Add ignore-variadic-names options to flake8-unused-arguments (#1261) 2022-12-16 00:22:38 -05:00
Edgar R. M
4ff0b75045 test: Fix flake8-errmsg snapshots (#1260) 2022-12-15 23:53:15 -05:00
Charlie Marsh
481d668511 Add flake8-errmsg to README 2022-12-15 23:16:38 -05:00
206 changed files with 3548 additions and 1188 deletions

View File

@@ -39,7 +39,7 @@ jobs:
- run: ./target/release/ruff_dev generate-rules-table
- run: ./target/release/ruff_dev generate-options
- run: git diff --quiet README.md || echo "::error file=README.md::This file is outdated. You may have to rerun 'cargo dev generate-options' and/or 'cargo dev generate-rules-table'."
- run: ./target/release/ruff_dev generate-check-code-prefix && cargo fmt -- src/checks_gen.rs
- run: ./target/release/ruff_dev generate-check-code-prefix
- run: git diff --quiet src/checks_gen.rs || echo "::error file=src/checks_gen.rs::This file is outdated. You may have to rerun 'cargo dev generate-check-code-prefix'."
- run: git diff --exit-code -- README.md src/checks_gen.rs

View File

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

16
Cargo.lock generated
View File

@@ -724,7 +724,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.183-dev.0"
version = "0.0.188-dev.0"
dependencies = [
"anyhow",
"clap 4.0.29",
@@ -1845,7 +1845,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.183"
version = "0.0.188"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1901,7 +1901,7 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.183"
version = "0.0.188"
dependencies = [
"anyhow",
"clap 4.0.29",
@@ -1919,7 +1919,7 @@ dependencies = [
[[package]]
name = "ruff_macros"
version = "0.0.183"
version = "0.0.188"
dependencies = [
"proc-macro2",
"quote",
@@ -1962,7 +1962,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=8d879a53197f9c73062f6160410bdba796a71cbf#8d879a53197f9c73062f6160410bdba796a71cbf"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
dependencies = [
"num-bigint",
"rustpython-common",
@@ -1972,7 +1972,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.0.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=8d879a53197f9c73062f6160410bdba796a71cbf#8d879a53197f9c73062f6160410bdba796a71cbf"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
dependencies = [
"ascii",
"cfg-if 1.0.0",
@@ -1995,7 +1995,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=8d879a53197f9c73062f6160410bdba796a71cbf#8d879a53197f9c73062f6160410bdba796a71cbf"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
dependencies = [
"bincode",
"bitflags",
@@ -2012,7 +2012,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=8d879a53197f9c73062f6160410bdba796a71cbf#8d879a53197f9c73062f6160410bdba796a71cbf"
source = "git+https://github.com/RustPython/RustPython.git?rev=c01f014b1269eedcf4bdb45d5fbc62ae2beecf31#c01f014b1269eedcf4bdb45d5fbc62ae2beecf31"
dependencies = [
"ahash",
"anyhow",

View File

@@ -6,7 +6,7 @@ members = [
[package]
name = "ruff"
version = "0.0.183"
version = "0.0.188"
edition = "2021"
rust-version = "1.65.0"
@@ -43,11 +43,11 @@ quick-junit = { version = "0.3.2" }
rayon = { version = "1.5.3" }
regex = { version = "1.6.0" }
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
ruff_macros = { version = "0.0.183", path = "ruff_macros" }
ruff_macros = { version = "0.0.188", path = "ruff_macros" }
rustc-hash = { version = "1.1.0" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "8d879a53197f9c73062f6160410bdba796a71cbf" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "8d879a53197f9c73062f6160410bdba796a71cbf" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "8d879a53197f9c73062f6160410bdba796a71cbf" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
strum = { version = "0.24.1", features = ["strum_macros"] }

195
README.md
View File

@@ -94,8 +94,9 @@ of [Conda](https://docs.conda.io/en/latest/):
1. [flake8-simplify (SIM)](#flake8-simplify-sim)
1. [flake8-tidy-imports (TID)](#flake8-tidy-imports-tid)
1. [flake8-unused-arguments (ARG)](#flake8-unused-arguments-arg)
1. [flake8-datetimez (DTZ)](#flake8-datetimez-dtz)
1. [eradicate (ERA)](#eradicate-era)
1. [pandas-vet (PDV)](#pandas-vet-pdv)
1. [pandas-vet (PD)](#pandas-vet-pd)
1. [pygrep-hooks (PGH)](#pygrep-hooks-pgh)
1. [Pylint (PLC, PLE, PLR, PLW)](#pylint-plc-ple-plr-plw)
1. [Ruff-specific rules (RUF)](#ruff-specific-rules-ruf)<!-- End auto-generated table of contents. -->
@@ -118,6 +119,8 @@ Ruff is available as [`ruff`](https://pypi.org/project/ruff/) on PyPI:
pip install ruff
```
[![Packaging status](https://repology.org/badge/vertical-allrepos/ruff-python-linter.svg?exclude_unsupported=1)](https://repology.org/project/ruff-python-linter/versions)
For **macOS Homebrew** and **Linuxbrew** users, Ruff is also available as [`ruff`](https://formulae.brew.sh/formula/ruff) on Homebrew:
```shell
@@ -157,7 +160,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
```yaml
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.183
rev: v0.0.188
hooks:
- id: ruff
```
@@ -354,7 +357,7 @@ There are a few exceptions to these rules:
a default configuration. If a user-specific configuration file exists
at `${config_dir}/ruff/pyproject.toml`,
that file will be used instead of the default configuration, with `${config_dir}` being
determined via the [`dirs](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) crate, and all
determined via the [`dirs`](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) crate, and all
relative paths being again resolved relative to the _current working directory_.
4. Any `pyproject.toml`-supported settings that are provided on the command-line (e.g., via
`--select`) will override the settings in _every_ resolved configuration file.
@@ -673,7 +676,7 @@ For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/2
| ANN102 | MissingTypeCls | Missing type annotation for `...` in classmethod | |
| ANN201 | MissingReturnTypePublicFunction | Missing return type annotation for public function `...` | |
| ANN202 | MissingReturnTypePrivateFunction | Missing return type annotation for private function `...` | |
| ANN204 | MissingReturnTypeMagicMethod | Missing return type annotation for magic method `...` | |
| ANN204 | MissingReturnTypeSpecialMethod | Missing return type annotation for special method `...` | 🛠 |
| ANN205 | MissingReturnTypeStaticMethod | Missing return type annotation for staticmethod `...` | |
| ANN206 | MissingReturnTypeClassMethod | Missing return type annotation for classmethod `...` | |
| ANN401 | DynamicallyTypedExpression | Dynamically typed expressions (typing.Any) are disallowed in `...` | |
@@ -864,6 +867,22 @@ For more, see [flake8-unused-arguments](https://pypi.org/project/flake8-unused-a
| ARG004 | UnusedStaticMethodArgument | Unused static method argument: `...` | |
| ARG005 | UnusedLambdaArgument | Unused lambda argument: `...` | |
### flake8-datetimez (DTZ)
For more, see [flake8-datetimez](https://pypi.org/project/flake8-datetimez/20.10.0/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| DTZ001 | CallDatetimeWithoutTzinfo | The use of `datetime.datetime()` without `tzinfo` argument is not allowed | |
| DTZ002 | CallDatetimeToday | The use of `datetime.datetime.today()` is not allowed | |
| DTZ003 | CallDatetimeUtcnow | The use of `datetime.datetime.utcnow()` is not allowed | |
| DTZ004 | CallDatetimeUtcfromtimestamp | The use of `datetime.datetime.utcfromtimestamp()` is not allowed | |
| DTZ005 | CallDatetimeNowWithoutTzinfo | The use of `datetime.datetime.now()` without `tz` argument is not allowed | |
| DTZ006 | CallDatetimeFromtimestamp | The use of `datetime.datetime.fromtimestamp()` without `tz` argument is not allowed | |
| DTZ007 | CallDatetimeStrptimeWithoutZone | The use of `datetime.datetime.strptime()` without %z must be followed by `.replace(tzinfo=)` | |
| DTZ011 | CallDateToday | The use of `datetime.date.today()` is not allowed. | |
| DTZ012 | CallDateFromtimestamp | The use of `datetime.date.fromtimestamp()` is not allowed | |
### eradicate (ERA)
For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
@@ -872,24 +891,24 @@ For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
| ---- | ---- | ------- | --- |
| ERA001 | CommentedOutCode | Found commented-out code | 🛠 |
### pandas-vet (PDV)
### pandas-vet (PD)
For more, see [pandas-vet](https://pypi.org/project/pandas-vet/0.2.3/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| PDV002 | UseOfInplaceArgument | `inplace=True` should be avoided; it has inconsistent behavior | |
| PDV003 | UseOfDotIsNull | `.isna` is preferred to `.isnull`; functionality is equivalent | |
| PDV004 | UseOfDotNotNull | `.notna` is preferred to `.notnull`; functionality is equivalent | |
| PDV007 | UseOfDotIx | ``ix` i` deprecated; use more explicit `.loc` o` `.iloc` | |
| PDV008 | UseOfDotAt | Use `.loc` instead of `.at`. If speed is important, use numpy. | |
| PDV009 | UseOfDotIat | Use `.iloc` instea` of `.iat`. If speed is important, use numpy. | |
| PDV010 | UseOfDotPivotOrUnstack | `.pivot_table` is preferred to `.pivot` or `.unstack`; provides same functionality | |
| PDV011 | UseOfDotValues | Use `.to_numpy()` instead of `.values` | |
| PDV012 | UseOfDotReadTable | `.read_csv` is preferred to `.read_table`; provides same functionality | |
| PDV013 | UseOfDotStack | `.melt` is preferred to `.stack`; provides same functionality | |
| PDV015 | UseOfPdMerge | Use `.merge` method instead of `pd.merge` function. They have equivalent functionality. | |
| PDV901 | DfIsABadVariableName | `df` is a bad variable name. Be kinder to your future self. | |
| PD002 | UseOfInplaceArgument | `inplace=True` should be avoided; it has inconsistent behavior | |
| PD003 | UseOfDotIsNull | `.isna` is preferred to `.isnull`; functionality is equivalent | |
| PD004 | UseOfDotNotNull | `.notna` is preferred to `.notnull`; functionality is equivalent | |
| PD007 | UseOfDotIx | `.ix` is deprecated; use more explicit `.loc` or `.iloc` | |
| PD008 | UseOfDotAt | Use `.loc` instead of `.at`. If speed is important, use numpy. | |
| PD009 | UseOfDotIat | Use `.iloc` instead of `.iat`. If speed is important, use numpy. | |
| PD010 | UseOfDotPivotOrUnstack | `.pivot_table` is preferred to `.pivot` or `.unstack`; provides same functionality | |
| PD011 | UseOfDotValues | Use `.to_numpy()` instead of `.values` | |
| PD012 | UseOfDotReadTable | `.read_csv` is preferred to `.read_table`; provides same functionality | |
| PD013 | UseOfDotStack | `.melt` is preferred to `.stack`; provides same functionality | |
| PD015 | UseOfPdMerge | Use `.merge` method instead of `pd.merge` function. They have equivalent functionality. | |
| PD901 | DfIsABadVariableName | `df` is a bad variable name. Be kinder to your future self. | |
### pygrep-hooks (PGH)
@@ -898,6 +917,8 @@ For more, see [pygrep-hooks](https://github.com/pre-commit/pygrep-hooks) on GitH
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| PGH001 | NoEval | No builtin `eval()` allowed | |
| PGH002 | DeprecatedLogWarn | `warn` is deprecated in favor of `warning` | |
| PGH003 | BlanketTypeIgnore | Use specific error codes when ignoring type issues | |
### Pylint (PLC, PLE, PLR, PLW)
@@ -933,21 +954,100 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI.
### VS Code (Official)
Download the [Ruff VS Code extension](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff).
Download the [Ruff VS Code extension](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff),
which supports autofix actions, import sorting, and more.
### Language Server Protocol
![](https://user-images.githubusercontent.com/1309177/205175763-cf34871d-5c05-4abf-9916-440afc82dbf8.gif)
Ruff is available as a [Language Server Protocol (LSP)](https://microsoft.github.io/language-server-protocol/)
server, distributed as the [`python-lsp-ruff`](https://github.com/python-lsp/python-lsp-ruff) plugin
for [`python-lsp-server`](https://github.com/python-lsp/python-lsp-server), both of which are
installable via [PyPI](https://pypi.org/project/python-lsp-ruff/):
### Language Server Protocol (Official)
Ruff supports the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/)
via the [`ruff-lsp`](https://github.com/charliermarsh/ruff-lsp) Python package, available on
[PyPI](https://pypi.org/project/ruff-lsp/).
[`ruff-lsp`](https://github.com/charliermarsh/ruff-lsp) enables Ruff to be used with any editor that
supports the Language Server Protocol, including [Neovim](https://github.com/charliermarsh/ruff-lsp#example-neovim),
[Sublime Text](https://github.com/charliermarsh/ruff-lsp#example-sublime-text), Emacs, and more.
For example, to use `ruff-lsp` with Neovim, install `ruff-lsp` from PyPI along with
[`nvim-lspconfig`](https://github.com/neovim/nvim-lspconfig). Then, add something like the following
to your `init.lua`:
```lua
-- See: https://github.com/neovim/nvim-lspconfig/tree/54eb2a070a4f389b1be0f98070f81d23e2b1a715#suggested-configuration
local opts = { noremap=true, silent=true }
vim.keymap.set('n', '<space>e', vim.diagnostic.open_float, opts)
vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts)
vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts)
vim.keymap.set('n', '<space>q', vim.diagnostic.setloclist, opts)
-- Use an on_attach function to only map the following keys
-- after the language server attaches to the current buffer
local on_attach = function(client, bufnr)
-- Enable completion triggered by <c-x><c-o>
vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
-- Mappings.
-- See `:help vim.lsp.*` for documentation on any of the below functions
local bufopts = { noremap=true, silent=true, buffer=bufnr }
vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, bufopts)
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, bufopts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, bufopts)
vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, bufopts)
vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, bufopts)
vim.keymap.set('n', '<space>wa', vim.lsp.buf.add_workspace_folder, bufopts)
vim.keymap.set('n', '<space>wr', vim.lsp.buf.remove_workspace_folder, bufopts)
vim.keymap.set('n', '<space>wl', function()
print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
end, bufopts)
vim.keymap.set('n', '<space>D', vim.lsp.buf.type_definition, bufopts)
vim.keymap.set('n', '<space>rn', vim.lsp.buf.rename, bufopts)
vim.keymap.set('n', '<space>ca', vim.lsp.buf.code_action, bufopts)
vim.keymap.set('n', 'gr', vim.lsp.buf.references, bufopts)
vim.keymap.set('n', '<space>f', function() vim.lsp.buf.format { async = true } end, bufopts)
end
-- Configure `ruff-lsp`.
local configs = require 'lspconfig.configs'
if not configs.ruff_lsp then
configs.ruff_lsp = {
default_config = {
cmd = { "ruff-lsp" },
filetypes = {'python'},
root_dir = require('lspconfig').util.find_git_ancestor,
settings = {
ruff_lsp = {
-- Any extra CLI arguments for `ruff` go here.
args = {}
}
}
}
}
end
require('lspconfig').ruff_lsp.setup {
on_attach = on_attach,
}
```
Upon successful installation, you should see Ruff's diagnostics surfaced directly in your editor:
![Code Actions available in Neovim](https://user-images.githubusercontent.com/1309177/208278707-25fa37e4-079d-4597-ad35-b95dba066960.png)
### Language Server Protocol (Unofficial)
Ruff is also available as the [`python-lsp-ruff`](https://github.com/python-lsp/python-lsp-ruff)
plugin for [`python-lsp-server`](https://github.com/python-lsp/python-lsp-ruff), both of which are
installable from PyPI:
```shell
pip install python-lsp-server python-lsp-ruff
```
The LSP server can be used with any editor that supports the Language Server Protocol. For example,
to use it with Neovim, you would add something like the following to your `init.lua`:
The LSP server can then be used with any editor that supports the Language Server Protocol.
For example, to use `python-lsp-ruff` with Neovim, add something like the following to your
`init.lua`:
```lua
require'lspconfig'.pylsp.setup {
@@ -956,6 +1056,15 @@ require'lspconfig'.pylsp.setup {
plugins = {
ruff = {
enabled = true
},
pycodestyle = {
enabled = false
},
pyflakes = {
enabled = false
},
mccabe = {
enabled = false
}
}
}
@@ -963,9 +1072,6 @@ require'lspconfig'.pylsp.setup {
}
```
[`ruffd`](https://github.com/Seamooo/ruffd) is another implementation of the Language Server
Protocol (LSP) for Ruff, written in Rust.
### PyCharm
Ruff can be installed as an [External Tool](https://www.jetbrains.com/help/pycharm/configuring-third-party-tools.html)
@@ -980,8 +1086,8 @@ Ruff should then appear as a runnable action:
### Vim & Neovim
Ruff can be integrated into any editor that supports the Language Server Protocol (LSP) (see:
[Language Server Protocol](#language-server-protocol)).
Ruff can be integrated into any editor that supports the Language Server Protocol via [`ruff-lsp`](https://github.com/charliermarsh/ruff-lsp)
(see: [Language Server Protocol](#language-server-protocol-official)).
Ruff is also available as part of the [coc-pyright](https://github.com/fannheyward/coc-pyright)
extension for `coc.nvim`.
@@ -1097,9 +1203,11 @@ natively, including:
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
- [`flake8-datetimez`](https://pypi.org/project/flake8-datetimez/)
- [`flake8-debugger`](https://pypi.org/project/flake8-debugger/)
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
- [`flake8-errmsg`](https://pypi.org/project/flake8-errmsg/)
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
- [`flake8-print`](https://pypi.org/project/flake8-print/)
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
@@ -1110,7 +1218,7 @@ natively, including:
- [`mccabe`](https://pypi.org/project/mccabe/)
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (1/10)
- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (16/33)
- [`yesqa`](https://github.com/asottile/yesqa)
@@ -1150,9 +1258,11 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
- [`flake8-datetimez`](https://pypi.org/project/flake8-datetimez/)
- [`flake8-debugger`](https://pypi.org/project/flake8-debugger/)
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
- [`flake8-errmsg`](https://pypi.org/project/flake8-errmsg/)
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
- [`flake8-print`](https://pypi.org/project/flake8-print/)
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
@@ -1165,7 +1275,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
Ruff can also replace [`isort`](https://pypi.org/project/isort/),
[`yesqa`](https://github.com/asottile/yesqa), [`eradicate`](https://pypi.org/project/eradicate/),
[`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (1/10), and a subset of the rules
[`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10), and a subset of the rules
implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (16/33).
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.
@@ -1486,7 +1596,7 @@ ignored when evaluating (e.g.) unused-variable checks. The default expression ma
```toml
[tool.ruff]
# Only ignore variables named "_".
dummy_variable_rgx = "^_$"
dummy-variable-rgx = "^_$"
```
---
@@ -2142,6 +2252,25 @@ ban-relative-imports = "all"
---
### `flake8-unused-arguments`
#### [`ignore-variadic-names`](#ignore-variadic-names)
Whether to allow unused variadic arguments, like `*args` and `**kwargs`.
**Default value**: `false`
**Type**: `bool`
**Example usage**:
```toml
[tool.ruff.flake8-unused-arguments]
ignore-variadic-names = true
```
---
### `isort`
#### [`combine-as-imports`](#combine-as-imports)

View File

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

View File

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

View File

@@ -288,6 +288,7 @@ mod tests {
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
@@ -336,6 +337,7 @@ mod tests {
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
@@ -384,6 +386,7 @@ mod tests {
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
@@ -432,6 +435,7 @@ mod tests {
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
@@ -485,6 +489,7 @@ mod tests {
}),
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
@@ -572,6 +577,7 @@ mod tests {
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,
@@ -626,6 +632,7 @@ mod tests {
}),
flake8_tidy_imports: None,
flake8_import_conventions: None,
flake8_unused_arguments: None,
isort: None,
mccabe: None,
pep8_naming: None,

View File

@@ -12,6 +12,7 @@ pub enum Plugin {
Flake8Bugbear,
Flake8Builtins,
Flake8Comprehensions,
Flake8Datetimez,
Flake8Debugger,
Flake8Docstrings,
Flake8ErrMsg,
@@ -38,6 +39,7 @@ impl FromStr for Plugin {
"flake8-bugbear" => Ok(Plugin::Flake8Bugbear),
"flake8-builtins" => Ok(Plugin::Flake8Builtins),
"flake8-comprehensions" => Ok(Plugin::Flake8Comprehensions),
"flake8-datetimez" => Ok(Plugin::Flake8Datetimez),
"flake8-debugger" => Ok(Plugin::Flake8Debugger),
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
"flake8-eradicate" => Ok(Plugin::Flake8BlindExcept),
@@ -66,6 +68,7 @@ impl Plugin {
Plugin::Flake8Bugbear => CheckCodePrefix::B,
Plugin::Flake8Builtins => CheckCodePrefix::A,
Plugin::Flake8Comprehensions => CheckCodePrefix::C4,
Plugin::Flake8Datetimez => CheckCodePrefix::DTZ,
Plugin::Flake8Debugger => CheckCodePrefix::T1,
Plugin::Flake8Docstrings => CheckCodePrefix::D,
// TODO(charlie): Handle rename of `E` to `ERA`.
@@ -77,8 +80,7 @@ impl Plugin {
Plugin::Flake8Simplify => CheckCodePrefix::SIM,
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
Plugin::McCabe => CheckCodePrefix::C9,
// TODO(charlie): Handle rename of `PD` to `PDV`.
Plugin::PandasVet => CheckCodePrefix::PDV,
Plugin::PandasVet => CheckCodePrefix::PD,
Plugin::PEP8Naming => CheckCodePrefix::N,
Plugin::Pyupgrade => CheckCodePrefix::U,
}
@@ -92,6 +94,7 @@ impl Plugin {
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C4],
Plugin::Flake8Datetimez => vec![CheckCodePrefix::DTZ],
Plugin::Flake8Debugger => vec![CheckCodePrefix::T1],
Plugin::Flake8Docstrings => {
// Use the user-provided docstring.
@@ -117,7 +120,7 @@ impl Plugin {
Plugin::Flake8Simplify => vec![CheckCodePrefix::SIM],
Plugin::Flake8TidyImports => vec![CheckCodePrefix::TID],
Plugin::McCabe => vec![CheckCodePrefix::C9],
Plugin::PandasVet => vec![CheckCodePrefix::PDV],
Plugin::PandasVet => vec![CheckCodePrefix::PD],
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
Plugin::Pyupgrade => vec![CheckCodePrefix::UP],
}
@@ -409,6 +412,7 @@ pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin
Plugin::Flake8Bugbear,
Plugin::Flake8Builtins,
Plugin::Flake8Comprehensions,
Plugin::Flake8Datetimez,
Plugin::Flake8Debugger,
Plugin::Flake8Docstrings,
Plugin::Flake8Eradicate,

View File

@@ -0,0 +1,18 @@
import datetime
# no args
datetime.datetime(2000, 1, 1, 0, 0, 0)
# none args
datetime.datetime(2000, 1, 1, 0, 0, 0, 0, None)
# no kwargs
datetime.datetime(2000, 1, 1, fold=1)
# none kwargs
datetime.datetime(2000, 1, 1, tzinfo=None)
from datetime import datetime
# no args unqualified
datetime(2000, 1, 1, 0, 0, 0)

View File

@@ -0,0 +1,9 @@
import datetime
# qualified
datetime.datetime.today()
from datetime import datetime
# unqualified
datetime.today()

View File

@@ -0,0 +1,9 @@
import datetime
# qualified
datetime.datetime.utcnow()
from datetime import datetime
# unqualified
datetime.utcnow()

View File

@@ -0,0 +1,9 @@
import datetime
# qualified
datetime.datetime.utcfromtimestamp(1234)
from datetime import datetime
# unqualified
datetime.utcfromtimestamp(1234)

View File

@@ -0,0 +1,18 @@
import datetime
# no args
datetime.datetime.now()
# wrong keywords
datetime.datetime.now(bad=datetime.timezone.utc)
# none args
datetime.datetime.now(None)
# none keywords
datetime.datetime.now(tz=None)
from datetime import datetime
# no args unqualified
datetime.now()

View File

@@ -0,0 +1,18 @@
import datetime
# no args
datetime.datetime.fromtimestamp(1234)
# wrong keywords
datetime.datetime.fromtimestamp(1234, bad=datetime.timezone.utc)
# none args
datetime.datetime.fromtimestamp(1234, None)
# none keywords
datetime.datetime.fromtimestamp(1234, tz=None)
from datetime import datetime
# no args unqualified
datetime.fromtimestamp(1234)

View File

@@ -0,0 +1,29 @@
import datetime
# bad format
datetime.datetime.strptime("something", "%H:%M:%S%Z")
# no replace or astimezone
datetime.datetime.strptime("something", "something")
# wrong replace
datetime.datetime.strptime("something", "something").replace(hour=1)
# none replace
datetime.datetime.strptime("something", "something").replace(tzinfo=None)
# OK
datetime.datetime.strptime("something", "something").replace(
tzinfo=datetime.timezone.utc
)
# OK
datetime.datetime.strptime("something", "something").astimezone()
# OK
datetime.datetime.strptime("something", "%H:%M:%S%z")
from datetime import datetime
# no replace orastimezone unqualified
datetime.strptime("something", "something")

View File

@@ -0,0 +1,9 @@
import datetime
# qualified
datetime.date.today()
from datetime import date
# unqualified
date.today()

View File

@@ -0,0 +1,9 @@
import datetime
# qualified
datetime.date.fromtimestamp(1234)
from datetime import date
# unqualified
date.fromtimestamp(1234)

View File

@@ -1,6 +1,5 @@
breakpoint()
import pdb
import builtins
from builtins import breakpoint
@@ -9,7 +8,6 @@ from celery.contrib.rdb import set_trace
from celery.contrib import rdb
import celery.contrib.rdb
breakpoint()
st()
set_trace()

View File

@@ -5,6 +5,10 @@ def f_a():
raise RuntimeError("This is an example exception")
def f_a_short():
raise RuntimeError("Error")
def f_b():
example = "example"
raise RuntimeError(f"This is an {example} exception")

View File

@@ -1 +1,10 @@
import sys
import tempfile
print("Hello, world!") # T201
print("Hello, world!", file=None) # T201
print("Hello, world!", file=sys.stdout) # T201
print("Hello, world!", file=sys.stderr) # T201
with tempfile.NamedTemporaryFile() as fp:
print("Hello, world!", file=fp) # OK

View File

@@ -2,7 +2,6 @@ from pprint import pprint
pprint("Hello, world!") # T203
import pprint
pprint.pprint("Hello, world!") # T203

View File

@@ -27,7 +27,7 @@ def f(cls, x):
lambda x: print("Hello, world!")
class X:
class C:
###
# Unused arguments.
###

View File

@@ -0,0 +1,14 @@
def f(a, b):
print("Hello, world!")
def f(a, b, *args, **kwargs):
print("Hello, world!")
class C:
def f(self, a, b):
print("Hello, world!")
def f(self, a, b, *args, **kwargs):
print("Hello, world!")

View File

@@ -55,3 +55,8 @@ sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labor
# OK
# https://loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong.url.com
# Not OK
_ = """
Source: https://github.com/PyCQA/pycodestyle/pull/258/files#diff-841c622497a8033d10152bfdfb15b20b92437ecdea21a260944ea86b77b51533
"""

View File

@@ -0,0 +1,8 @@
import logging
import warnings
from warnings import warn
warnings.warn("this is ok")
warn("by itself is also ok")
logging.warning("this is fine")
log.warning("this is ok")

View File

@@ -0,0 +1,15 @@
import logging
from logging import warn
logging.warn("this is not ok")
log.warn("this is also not ok")
warn("not ok")
def foo():
from logging import warn
def warn():
pass
warn("has been redefined, but we will still report it")

View File

@@ -0,0 +1,11 @@
x = 1 # type: ignore
x = 1 # type ignore
x = 1 # type:ignore
x = 1
x = 1 # type ignore # noqa
x = 1 # type: ignore[attr-defined]
x = 1 # type: ignore[attr-defined, name-defined]
x = 1 # type: ignore[type-mismatch] # noqa
x = 1 # type: Union[int, str]
x = 1 # type: ignoreme

View File

@@ -69,3 +69,13 @@ _ = """Lorem ipsum dolor sit amet.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.
""" # noqa
# Valid
# this is a veryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy long comment # noqa: E501
# Valid
_ = """Here's a source: https://github.com/ethereum/web3.py/blob/ffe59daf10edc19ee5f05227b25bac8d090e8aa4/web3/_utils/events.py#L201
May raise:
- DeserializationError if the abi string is invalid or abi or log topics/data do not match
""" # noqa: E501

View File

@@ -0,0 +1,2 @@
[tool.ruff]
src = ["."]

View File

@@ -0,0 +1,4 @@
from package.core import method
if __name__ == "__main__":
method()

View File

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

View File

@@ -1,18 +1,18 @@
//! Generate the `CheckCodePrefix` enum.
use std::collections::{BTreeMap, BTreeSet};
use std::fs::OpenOptions;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use std::process::{Command, Output, Stdio};
use anyhow::Result;
use anyhow::{ensure, Result};
use clap::Parser;
use codegen::{Scope, Type, Variant};
use itertools::Itertools;
use ruff::checks::{CheckCode, CODE_REDIRECTS, PREFIX_REDIRECTS};
use strum::IntoEnumIterator;
const FILE: &str = "src/checks_gen.rs";
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
@@ -107,7 +107,7 @@ pub fn main(cli: &Cli) -> Result<()> {
for (prefix, codes) in &prefix_to_codes {
if let Some(target) = CODE_REDIRECTS.get(&prefix.as_str()) {
gen = gen.line(format!(
"CheckCodePrefix::{prefix} => {{ eprintln!(\"{{}}{{}} {{}}\", \
"CheckCodePrefix::{prefix} => {{ one_time_warning!(\"{{}}{{}} {{}}\", \
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been remapped to \
`{}`\".bold()); \n vec![{}] }}",
prefix,
@@ -119,7 +119,7 @@ pub fn main(cli: &Cli) -> Result<()> {
));
} else if let Some(target) = PREFIX_REDIRECTS.get(&prefix.as_str()) {
gen = gen.line(format!(
"CheckCodePrefix::{prefix} => {{ eprintln!(\"{{}}{{}} {{}}\", \
"CheckCodePrefix::{prefix} => {{ one_time_warning!(\"{{}}{{}} {{}}\", \
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been remapped to \
`{}`\".bold()); \n vec![{}] }}",
prefix,
@@ -182,6 +182,8 @@ pub fn main(cli: &Cli) -> Result<()> {
output.push('\n');
output.push_str("use crate::checks::CheckCode;");
output.push('\n');
output.push_str("use crate::one_time_warning;");
output.push('\n');
output.push('\n');
output.push_str(&scope.to_string());
output.push('\n');
@@ -202,12 +204,25 @@ pub fn main(cli: &Cli) -> Result<()> {
output.push('\n');
output.push('\n');
let rustfmt = Command::new("rustfmt")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
write!(rustfmt.stdin.as_ref().unwrap(), "{output}")?;
let Output { status, stdout, .. } = rustfmt.wait_with_output()?;
ensure!(status.success(), "rustfmt failed with {status}");
// Write the output to `src/checks_gen.rs` (or stdout).
if cli.dry_run {
println!("{output}");
println!("{}", String::from_utf8(stdout)?);
} else {
let mut f = OpenOptions::new().write(true).truncate(true).open(FILE)?;
write!(f, "{output}")?;
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("Failed to find root directory")
.join("src/checks_gen.rs");
if fs::read(&file).map_or(true, |old| old != stdout) {
fs::write(&file, stdout)?;
}
}
Ok(())

View File

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

View File

@@ -3,7 +3,8 @@ use once_cell::sync::Lazy;
use regex::Regex;
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{
Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind,
Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Keyword, KeywordData,
Location, Stmt, StmtKind,
};
use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok;
@@ -210,6 +211,34 @@ pub fn is_constant_non_singleton(expr: &Expr) -> bool {
is_constant(expr) && !is_singleton(expr)
}
/// Return the `Keyword` with the given name, if it's present in the list of
/// `Keyword` arguments.
pub fn find_keyword<'a>(keywords: &'a [Keyword], keyword_name: &str) -> Option<&'a Keyword> {
keywords.iter().find(|keyword| {
let KeywordData { arg, .. } = &keyword.node;
arg.as_ref().map_or(false, |arg| arg == keyword_name)
})
}
/// Return `true` if an `Expr` is `None`.
pub fn is_const_none(expr: &Expr) -> bool {
matches!(
&expr.node,
ExprKind::Constant {
value: Constant::None,
kind: None
},
)
}
/// Return `true` if a keyword argument is present with a non-`None` value.
pub fn has_non_none_keyword(keywords: &[Keyword], keyword: &str) -> bool {
find_keyword(keywords, keyword).map_or(false, |keyword| {
let KeywordData { value, .. } = &keyword.node;
!is_const_none(value)
})
}
/// Extract the names of all handled exceptions.
pub fn extract_handler_names(handlers: &[Excepthandler]) -> Vec<Vec<&str>> {
let mut handler_names = vec![];

View File

@@ -4,7 +4,7 @@ use std::str::Lines;
use rustpython_ast::{Located, Location};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
/// Extract the leading indentation from a line.
pub fn indentation<'a, T>(checker: &'a Checker, located: &'a Located<T>) -> Cow<'a, str> {

View File

@@ -10,7 +10,7 @@ use crate::autofix::Fix;
use crate::checks::Check;
use crate::source_code_locator::SourceCodeLocator;
#[derive(Debug, Hash)]
#[derive(Debug, Copy, Clone, Hash)]
pub enum Mode {
Generate,
Apply,
@@ -27,15 +27,6 @@ impl From<bool> for Mode {
}
}
impl From<&Mode> for bool {
fn from(value: &Mode) -> Self {
match value {
Mode::Generate | Mode::Apply => true,
Mode::None => false,
}
}
}
/// Auto-fix errors in a file, and write the fixed source code to disk.
pub fn fix_file<'a>(
checks: &'a [Check],

View File

@@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
use crate::autofix::fixer;
use crate::message::Message;
use crate::settings::Settings;
use crate::settings::{flags, Settings};
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -34,43 +34,6 @@ struct CheckResult {
messages: Vec<Message>,
}
pub enum Mode {
ReadWrite,
ReadOnly,
WriteOnly,
None,
}
impl Mode {
fn allow_read(&self) -> bool {
match self {
Mode::ReadWrite => true,
Mode::ReadOnly => true,
Mode::WriteOnly => false,
Mode::None => false,
}
}
fn allow_write(&self) -> bool {
match self {
Mode::ReadWrite => true,
Mode::ReadOnly => false,
Mode::WriteOnly => true,
Mode::None => false,
}
}
}
impl From<bool> for Mode {
fn from(value: bool) -> Self {
if value {
Mode::ReadWrite
} else {
Mode::None
}
}
}
fn cache_dir() -> &'static str {
"./.ruff_cache"
}
@@ -79,10 +42,10 @@ fn content_dir() -> &'static str {
"content"
}
fn cache_key(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> u64 {
fn cache_key<P: AsRef<Path>>(path: P, settings: &Settings, autofix: fixer::Mode) -> u64 {
let mut hasher = DefaultHasher::new();
CARGO_PKG_VERSION.hash(&mut hasher);
path.absolutize().unwrap().hash(&mut hasher);
path.as_ref().absolutize().unwrap().hash(&mut hasher);
settings.hash(&mut hasher);
autofix.hash(&mut hasher);
hasher.finish()
@@ -128,14 +91,14 @@ fn read_sync(key: u64) -> Result<Vec<u8>, std::io::Error> {
}
/// Get a value from the cache.
pub fn get(
path: &Path,
pub fn get<P: AsRef<Path>>(
path: P,
metadata: &Metadata,
settings: &Settings,
autofix: &fixer::Mode,
mode: &Mode,
autofix: fixer::Mode,
cache: flags::Cache,
) -> Option<Vec<Message>> {
if !mode.allow_read() {
if matches!(cache, flags::Cache::Disabled) {
return None;
};
@@ -157,15 +120,15 @@ pub fn get(
}
/// Set a value in the cache.
pub fn set(
path: &Path,
pub fn set<P: AsRef<Path>>(
path: P,
metadata: &Metadata,
settings: &Settings,
autofix: &fixer::Mode,
autofix: fixer::Mode,
messages: &[Message],
mode: &Mode,
cache: flags::Cache,
) {
if !mode.allow_write() {
if matches!(cache, flags::Cache::Disabled) {
return;
};

View File

@@ -1,288 +0,0 @@
//! Lint rules based on checking raw physical lines.
use nohash_hasher::IntMap;
use once_cell::sync::Lazy;
use regex::Regex;
use rustpython_parser::ast::Location;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checks::{Check, CheckCode, CheckKind, CODE_REDIRECTS};
use crate::noqa;
use crate::noqa::{is_file_exempt, Directive};
use crate::settings::Settings;
// Regex from PEP263.
static CODING_COMMENT_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*utf-?8").unwrap());
static URL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^https?://\S+$").unwrap());
/// Whether the given line is too long and should be reported.
fn should_enforce_line_length(line: &str, length: usize, limit: usize) -> bool {
if length <= limit {
return false;
}
let mut chunks = line.split_whitespace();
let (Some(first), Some(_)) = (chunks.next(), chunks.next()) else {
// Single word / no printable chars - no way to make the line shorter
return false;
};
// Do not enforce the line length for commented lines that end with a URL
// or contain only a single word.
!(first == "#" && chunks.last().map_or(true, |c| URL_REGEX.is_match(c)))
}
pub fn check_lines(
checks: &mut Vec<Check>,
contents: &str,
noqa_line_for: &IntMap<usize, usize>,
settings: &Settings,
autofix: bool,
ignore_noqa: bool,
) {
let enforce_unnecessary_coding_comment = settings.enabled.contains(&CheckCode::UP009);
let enforce_line_too_long = settings.enabled.contains(&CheckCode::E501);
let enforce_noqa = settings.enabled.contains(&CheckCode::RUF100);
let mut noqa_directives: IntMap<usize, (Directive, Vec<&str>)> = IntMap::default();
let mut line_checks = vec![];
let mut ignored = vec![];
checks.sort_by_key(|check| check.location);
let mut checks_iter = checks.iter().enumerate().peekable();
if let Some((_index, check)) = checks_iter.peek() {
assert!(check.location.row() >= 1);
}
macro_rules! add_if {
($check:expr, $noqa_lineno:expr, $line:expr) => {{
match noqa_directives
.entry($noqa_lineno)
.or_insert_with(|| (noqa::extract_noqa_directive($line), vec![]))
{
(Directive::All(..), matches) => {
matches.push($check.kind.code().as_ref());
if ignore_noqa {
line_checks.push($check);
}
}
(Directive::Codes(.., codes), matches) => {
if noqa::includes($check.kind.code(), codes) {
matches.push($check.kind.code().as_ref());
if ignore_noqa {
line_checks.push($check);
}
} else {
line_checks.push($check);
}
}
(Directive::None, ..) => line_checks.push($check),
}
}};
}
let lines: Vec<&str> = contents.lines().collect();
for (lineno, line) in lines.iter().enumerate() {
// If we hit an exemption for the entire file, bail.
if is_file_exempt(line) {
checks.drain(..);
return;
}
// Grab the noqa (logical) line number for the current (physical) line.
// If there are newlines at the end of the file, they won't be represented in
// `noqa_line_for`, so fallback to the current line.
let noqa_lineno = noqa_line_for.get(&(lineno + 1)).unwrap_or(&(lineno + 1)) - 1;
// Enforce unnecessary coding comments (UP009).
if enforce_unnecessary_coding_comment {
if lineno < 2 {
// PEP3120 makes utf-8 the default encoding.
if CODING_COMMENT_REGEX.is_match(line) {
let mut check = Check::new(
CheckKind::PEP3120UnnecessaryCodingComment,
Range {
location: Location::new(lineno + 1, 0),
end_location: Location::new(lineno + 2, 0),
},
);
if autofix && settings.fixable.contains(check.kind.code()) {
check.amend(Fix::deletion(
Location::new(lineno + 1, 0),
Location::new(lineno + 2, 0),
));
}
add_if!(check, noqa_lineno, lines[noqa_lineno]);
}
}
}
if enforce_noqa {
noqa_directives
.entry(noqa_lineno)
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
}
// Remove any ignored checks.
while let Some((index, check)) =
checks_iter.next_if(|(_index, check)| check.location.row() == lineno + 1)
{
let noqa = noqa_directives
.entry(noqa_lineno)
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
match noqa {
(Directive::All(..), matches) => {
matches.push(check.kind.code().as_ref());
ignored.push(index);
}
(Directive::Codes(.., codes), matches) => {
if noqa::includes(check.kind.code(), codes) {
matches.push(check.kind.code().as_ref());
ignored.push(index);
}
}
(Directive::None, ..) => {}
}
}
// Enforce line length violations (E501).
if enforce_line_too_long {
let line_length = line.chars().count();
if should_enforce_line_length(line, line_length, settings.line_length) {
let check = Check::new(
CheckKind::LineTooLong(line_length, settings.line_length),
Range {
location: Location::new(lineno + 1, 0),
end_location: Location::new(lineno + 1, line_length),
},
);
add_if!(check, noqa_lineno, lines[noqa_lineno]);
}
}
}
// Enforce newlines at end of files (W292).
if settings.enabled.contains(&CheckCode::W292) && !contents.ends_with('\n') {
// Note: if `lines.last()` is `None`, then `contents` is empty (and so we don't
// want to raise W292 anyway).
if let Some(line) = lines.last() {
let check = Check::new(
CheckKind::NoNewLineAtEndOfFile,
Range {
location: Location::new(lines.len(), line.len() + 1),
end_location: Location::new(lines.len(), line.len() + 1),
},
);
let lineno = lines.len() - 1;
let noqa_lineno = noqa_line_for.get(&(lineno + 1)).unwrap_or(&(lineno + 1)) - 1;
add_if!(check, noqa_lineno, lines[noqa_lineno]);
}
}
// Enforce that the noqa directive was actually used (RUF100).
if enforce_noqa {
for (row, (directive, matches)) in noqa_directives {
match directive {
Directive::All(spaces, start, end) => {
if matches.is_empty() {
let mut check = Check::new(
CheckKind::UnusedNOQA(None),
Range {
location: Location::new(row + 1, start),
end_location: Location::new(row + 1, end),
},
);
if autofix && settings.fixable.contains(check.kind.code()) {
check.amend(Fix::deletion(
Location::new(row + 1, start - spaces),
Location::new(row + 1, lines[row].chars().count()),
));
}
line_checks.push(check);
}
}
Directive::Codes(spaces, start, end, codes) => {
let mut invalid_codes = vec![];
let mut valid_codes = vec![];
for code in codes {
let code = CODE_REDIRECTS.get(code).map_or(code, AsRef::as_ref);
if matches.contains(&code) || settings.external.contains(code) {
valid_codes.push(code.to_string());
} else {
invalid_codes.push(code.to_string());
}
}
if !invalid_codes.is_empty() {
let mut check = Check::new(
CheckKind::UnusedNOQA(Some(invalid_codes)),
Range {
location: Location::new(row + 1, start),
end_location: Location::new(row + 1, end),
},
);
if autofix && settings.fixable.contains(check.kind.code()) {
if valid_codes.is_empty() {
check.amend(Fix::deletion(
Location::new(row + 1, start - spaces),
Location::new(row + 1, lines[row].chars().count()),
));
} else {
check.amend(Fix::replacement(
format!("# noqa: {}", valid_codes.join(", ")),
Location::new(row + 1, start),
Location::new(row + 1, lines[row].chars().count()),
));
}
}
line_checks.push(check);
}
}
Directive::None => {}
}
}
}
if !ignore_noqa {
ignored.sort_unstable();
for index in ignored.iter().rev() {
checks.swap_remove(*index);
}
}
checks.extend(line_checks);
}
#[cfg(test)]
mod tests {
use nohash_hasher::IntMap;
use super::check_lines;
use crate::checks::{Check, CheckCode};
use crate::settings::Settings;
#[test]
fn e501_non_ascii_char() {
let line = "'\u{4e9c}' * 2"; // 7 in UTF-32, 9 in UTF-8.
let check_with_max_line_length = |line_length: usize| {
let mut checks: Vec<Check> = vec![];
check_lines(
&mut checks,
line,
&IntMap::default(),
&Settings {
line_length,
..Settings::for_rule(CheckCode::E501)
},
true,
false,
);
checks
};
assert!(!check_with_max_line_length(6).is_empty());
assert!(check_with_max_line_length(7).is_empty());
}
}

View File

@@ -31,16 +31,16 @@ use crate::python::future::ALL_FEATURE_NAMES;
use crate::python::typing;
use crate::python::typing::SubscriptKind;
use crate::settings::types::PythonVersion;
use crate::settings::Settings;
use crate::settings::{flags, Settings};
use crate::source_code_locator::SourceCodeLocator;
use crate::vendored::cformat::{CFormatError, CFormatErrorType};
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
use crate::{
docstrings, flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except,
flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_debugger,
flake8_errmsg, flake8_import_conventions, flake8_print, flake8_return, flake8_simplify,
flake8_tidy_imports, flake8_unused_arguments, mccabe, noqa, pandas_vet, pep8_naming,
pycodestyle, pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade, visibility,
flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_datetimez,
flake8_debugger, flake8_errmsg, flake8_import_conventions, flake8_print, flake8_return,
flake8_simplify, flake8_tidy_imports, flake8_unused_arguments, mccabe, noqa, pandas_vet,
pep8_naming, pycodestyle, pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade, visibility,
};
const GLOBAL_SCOPE_INDEX: usize = 0;
@@ -51,8 +51,8 @@ type DeferralContext<'a> = (Vec<usize>, Vec<RefEquality<'a, Stmt>>);
pub struct Checker<'a> {
// Input data.
path: &'a Path,
autofix: bool,
ignore_noqa: bool,
autofix: flags::Autofix,
noqa: flags::Noqa,
pub(crate) settings: &'a Settings,
pub(crate) noqa_line_for: &'a IntMap<usize, usize>,
pub(crate) locator: &'a SourceCodeLocator<'a>,
@@ -73,6 +73,7 @@ pub struct Checker<'a> {
pub(crate) child_to_parent: FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
pub(crate) bindings: Vec<Binding<'a>>,
pub(crate) redefinitions: IntMap<usize, Vec<usize>>,
exprs: Vec<RefEquality<'a, Expr>>,
scopes: Vec<Scope<'a>>,
scope_stack: Vec<usize>,
dead_scopes: Vec<usize>,
@@ -95,15 +96,15 @@ pub struct Checker<'a> {
annotations_future_enabled: bool,
except_handlers: Vec<Vec<Vec<&'a str>>>,
// Check-specific state.
pub(crate) seen_b023: Vec<&'a Expr>,
pub(crate) flake8_bugbear_seen: Vec<&'a Expr>,
}
impl<'a> Checker<'a> {
pub fn new(
settings: &'a Settings,
noqa_line_for: &'a IntMap<usize, usize>,
autofix: bool,
ignore_noqa: bool,
autofix: flags::Autofix,
noqa: flags::Noqa,
path: &'a Path,
locator: &'a SourceCodeLocator,
) -> Checker<'a> {
@@ -111,7 +112,7 @@ impl<'a> Checker<'a> {
settings,
noqa_line_for,
autofix,
ignore_noqa,
noqa,
path,
locator,
checks: vec![],
@@ -124,6 +125,7 @@ impl<'a> Checker<'a> {
child_to_parent: FxHashMap::default(),
bindings: vec![],
redefinitions: IntMap::default(),
exprs: vec![],
scopes: vec![],
scope_stack: vec![],
dead_scopes: vec![],
@@ -149,7 +151,7 @@ impl<'a> Checker<'a> {
annotations_future_enabled: false,
except_handlers: vec![],
// Check-specific state.
seen_b023: vec![],
flake8_bugbear_seen: vec![],
}
}
@@ -182,7 +184,9 @@ impl<'a> Checker<'a> {
pub fn patch(&self, code: &CheckCode) -> bool {
// TODO(charlie): We can't fix errors in f-strings until RustPython adds
// location data.
self.autofix && self.in_f_string.is_none() && self.settings.fixable.contains(code)
matches!(self.autofix, flags::Autofix::Enabled)
&& self.in_f_string.is_none()
&& self.settings.fixable.contains(code)
}
/// Return `true` if the `Expr` is a reference to `typing.${target}`.
@@ -216,7 +220,7 @@ impl<'a> Checker<'a> {
// members from the fix that will eventually be excluded by a `noqa`.
// Unfortunately, we _do_ want to register a `Check` for each eventually-ignored
// import, so that our `noqa` counts are accurate.
if self.ignore_noqa {
if matches!(self.noqa, flags::Noqa::Disabled) {
return false;
}
let noqa_lineno = self.noqa_line_for.get(&lineno).unwrap_or(&lineno);
@@ -541,7 +545,7 @@ where
kind: BindingKind::FunctionDefinition,
used: None,
range: Range::from_located(stmt),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
}
@@ -646,7 +650,7 @@ where
),
used: None,
range: Range::from_located(alias),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
} else {
@@ -686,7 +690,7 @@ where
None
},
range: Range::from_located(alias),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
}
@@ -845,7 +849,7 @@ where
Range::from_located(alias),
)),
range: Range::from_located(alias),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
@@ -876,7 +880,7 @@ where
kind: BindingKind::StarImportation(*level, module.clone()),
used: None,
range: Range::from_located(stmt),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
@@ -945,7 +949,7 @@ where
None
},
range,
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
}
@@ -1177,7 +1181,7 @@ where
self, stmt, targets, value,
);
}
if self.settings.enabled.contains(&CheckCode::PDV901) {
if self.settings.enabled.contains(&CheckCode::PD901) {
if let Some(check) = pandas_vet::checks::assignment_to_df(targets) {
self.add_check(check);
}
@@ -1380,7 +1384,7 @@ where
kind: BindingKind::ClassDefinition,
used: None,
range: Range::from_located(stmt),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
}
@@ -1401,10 +1405,6 @@ where
}
fn visit_expr(&mut self, expr: &'b Expr) {
let prev_in_f_string = self.in_f_string;
let prev_in_literal = self.in_literal;
let prev_in_type_definition = self.in_type_definition;
if !(self.in_deferred_type_definition || self.in_deferred_string_type_definition)
&& self.in_type_definition
&& self.annotations_future_enabled
@@ -1430,6 +1430,12 @@ where
return;
}
self.push_expr(expr);
let prev_in_f_string = self.in_f_string;
let prev_in_literal = self.in_literal;
let prev_in_type_definition = self.in_type_definition;
// Pre-visit.
match &expr.node {
ExprKind::Subscript { value, slice, .. } => {
@@ -1541,10 +1547,10 @@ where
}
for (code, name) in vec![
(CheckCode::PDV007, "ix"),
(CheckCode::PDV008, "at"),
(CheckCode::PDV009, "iat"),
(CheckCode::PDV011, "values"),
(CheckCode::PD007, "ix"),
(CheckCode::PD008, "at"),
(CheckCode::PD009, "iat"),
(CheckCode::PD011, "values"),
] {
if self.settings.enabled.contains(&code) {
if attr == name {
@@ -1637,7 +1643,7 @@ where
if self.settings.enabled.contains(&CheckCode::T201)
|| self.settings.enabled.contains(&CheckCode::T203)
{
flake8_print::plugins::print_call(self, expr, func);
flake8_print::plugins::print_call(self, expr, func, keywords);
}
// flake8-bugbear
@@ -1926,17 +1932,17 @@ where
}
// pandas-vet
if self.settings.enabled.contains(&CheckCode::PDV002) {
if self.settings.enabled.contains(&CheckCode::PD002) {
self.add_checks(pandas_vet::checks::inplace_argument(keywords).into_iter());
}
for (code, name) in vec![
(CheckCode::PDV003, "isnull"),
(CheckCode::PDV004, "notnull"),
(CheckCode::PDV010, "pivot"),
(CheckCode::PDV010, "unstack"),
(CheckCode::PDV012, "read_table"),
(CheckCode::PDV013, "stack"),
(CheckCode::PD003, "isnull"),
(CheckCode::PD004, "notnull"),
(CheckCode::PD010, "pivot"),
(CheckCode::PD010, "unstack"),
(CheckCode::PD012, "read_table"),
(CheckCode::PD013, "stack"),
] {
if self.settings.enabled.contains(&code) {
if let ExprKind::Attribute { attr, .. } = &func.node {
@@ -1947,15 +1953,90 @@ where
}
}
if self.settings.enabled.contains(&CheckCode::PDV015) {
if self.settings.enabled.contains(&CheckCode::PD015) {
if let Some(check) = pandas_vet::checks::use_of_pd_merge(func) {
self.add_check(check);
};
}
// flake8-datetimez
if self.settings.enabled.contains(&CheckCode::DTZ001) {
flake8_datetimez::plugins::call_datetime_without_tzinfo(
self,
func,
args,
keywords,
Range::from_located(expr),
);
}
if self.settings.enabled.contains(&CheckCode::DTZ002) {
flake8_datetimez::plugins::call_datetime_today(
self,
func,
Range::from_located(expr),
);
}
if self.settings.enabled.contains(&CheckCode::DTZ003) {
flake8_datetimez::plugins::call_datetime_utcnow(
self,
func,
Range::from_located(expr),
);
}
if self.settings.enabled.contains(&CheckCode::DTZ004) {
flake8_datetimez::plugins::call_datetime_utcfromtimestamp(
self,
func,
Range::from_located(expr),
);
}
if self.settings.enabled.contains(&CheckCode::DTZ005) {
flake8_datetimez::plugins::call_datetime_now_without_tzinfo(
self,
func,
args,
keywords,
Range::from_located(expr),
);
}
if self.settings.enabled.contains(&CheckCode::DTZ006) {
flake8_datetimez::plugins::call_datetime_fromtimestamp(
self,
func,
args,
keywords,
Range::from_located(expr),
);
}
if self.settings.enabled.contains(&CheckCode::DTZ007) {
flake8_datetimez::plugins::call_datetime_strptime_without_zone(
self,
func,
args,
Range::from_located(expr),
);
}
if self.settings.enabled.contains(&CheckCode::DTZ011) {
flake8_datetimez::plugins::call_date_today(
self,
func,
Range::from_located(expr),
);
}
if self.settings.enabled.contains(&CheckCode::DTZ012) {
flake8_datetimez::plugins::call_date_fromtimestamp(
self,
func,
Range::from_located(expr),
);
}
// pygrep-hooks
if self.settings.enabled.contains(&CheckCode::PGH001) {
pygrep_hooks::checks::no_eval(self, func);
pygrep_hooks::plugins::no_eval(self, func);
}
if self.settings.enabled.contains(&CheckCode::PGH002) {
pygrep_hooks::plugins::deprecated_log_warn(self, func);
}
// pylint
@@ -2505,6 +2586,8 @@ where
self.in_type_definition = prev_in_type_definition;
self.in_literal = prev_in_literal;
self.in_f_string = prev_in_f_string;
self.pop_expr();
}
fn visit_excepthandler(&mut self, excepthandler: &'b Excepthandler) {
@@ -2650,7 +2733,7 @@ where
kind: BindingKind::Argument,
used: None,
range: Range::from_located(arg),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
@@ -2685,7 +2768,17 @@ impl<'a> Checker<'a> {
}
fn pop_parent(&mut self) {
self.parents.pop().expect("Attempted to pop without scope");
self.parents.pop().expect("Attempted to pop without parent");
}
fn push_expr(&mut self, expr: &'a Expr) {
self.exprs.push(RefEquality(expr));
}
fn pop_expr(&mut self) {
self.exprs
.pop()
.expect("Attempted to pop without expression");
}
fn push_scope(&mut self, scope: Scope<'a>) {
@@ -2716,6 +2809,36 @@ impl<'a> Checker<'a> {
}
}
/// Return the current `Stmt`.
pub fn current_stmt(&self) -> &RefEquality<'a, Stmt> {
self.parents.iter().rev().next().expect("No parent found")
}
/// Return the parent `Stmt` of the current `Stmt`, if any.
pub fn current_stmt_parent(&self) -> Option<&RefEquality<'a, Stmt>> {
self.parents.iter().rev().nth(1)
}
/// Return the grandparent `Stmt` of the current `Stmt`, if any.
pub fn current_stmt_grandparent(&self) -> Option<&RefEquality<'a, Stmt>> {
self.parents.iter().rev().nth(2)
}
/// Return the current `Expr`.
pub fn current_expr(&self) -> Option<&RefEquality<'a, Expr>> {
self.exprs.iter().rev().next()
}
/// Return the parent `Expr` of the current `Expr`.
pub fn current_expr_parent(&self) -> Option<&RefEquality<'a, Expr>> {
self.exprs.iter().rev().nth(1)
}
/// Return the grandparent `Expr` of the current `Expr`.
pub fn current_expr_grandparent(&self) -> Option<&RefEquality<'a, Expr>> {
self.exprs.iter().rev().nth(2)
}
pub fn current_scope(&self) -> &Scope {
&self.scopes[*(self.scope_stack.last().expect("No current scope found"))]
}
@@ -2727,14 +2850,6 @@ impl<'a> Checker<'a> {
.map(|index| &self.scopes[*index])
}
pub fn current_parent(&self) -> &RefEquality<'a, Stmt> {
self.parents.iter().rev().next().expect("No parent found")
}
pub fn current_grandparent(&self) -> Option<&RefEquality<'a, Stmt>> {
self.parents.iter().rev().nth(1)
}
fn add_binding<'b>(&mut self, name: &'b str, binding: Binding<'a>)
where
'b: 'a,
@@ -2964,7 +3079,7 @@ impl<'a> Checker<'a> {
where
'b: 'a,
{
let parent = self.current_parent().0;
let parent = self.current_stmt().0;
if self.settings.enabled.contains(&CheckCode::F823) {
let scopes: Vec<&Scope> = self
@@ -3009,7 +3124,7 @@ impl<'a> Checker<'a> {
kind: BindingKind::Annotation,
used: None,
range: Range::from_located(expr),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
return;
@@ -3026,7 +3141,7 @@ impl<'a> Checker<'a> {
kind: BindingKind::LoopVar,
used: None,
range: Range::from_located(expr),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
return;
@@ -3039,7 +3154,7 @@ impl<'a> Checker<'a> {
kind: BindingKind::Binding,
used: None,
range: Range::from_located(expr),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
return;
@@ -3089,7 +3204,7 @@ impl<'a> Checker<'a> {
)),
used: None,
range: Range::from_located(expr),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
return;
@@ -3102,7 +3217,7 @@ impl<'a> Checker<'a> {
kind: BindingKind::Assignment,
used: None,
range: Range::from_located(expr),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
}
@@ -3758,11 +3873,11 @@ pub fn check_ast(
locator: &SourceCodeLocator,
noqa_line_for: &IntMap<usize, usize>,
settings: &Settings,
autofix: bool,
ignore_noqa: bool,
autofix: flags::Autofix,
noqa: flags::Noqa,
path: &Path,
) -> Vec<Check> {
let mut checker = Checker::new(settings, noqa_line_for, autofix, ignore_noqa, path, locator);
let mut checker = Checker::new(settings, noqa_line_for, autofix, noqa, path, locator);
checker.push_scope(Scope::new(ScopeKind::Module));
checker.bind_builtins();

View File

@@ -9,19 +9,22 @@ use crate::checks::Check;
use crate::directives::IsortDirectives;
use crate::isort;
use crate::isort::track::ImportTracker;
use crate::settings::Settings;
use crate::settings::{flags, Settings};
use crate::source_code_locator::SourceCodeLocator;
fn check_import_blocks(
tracker: ImportTracker,
locator: &SourceCodeLocator,
settings: &Settings,
autofix: bool,
autofix: flags::Autofix,
package: Option<&Path>,
) -> Vec<Check> {
let mut checks = vec![];
for block in tracker.into_iter() {
if !block.imports.is_empty() {
if let Some(check) = isort::plugins::check_imports(&block, locator, settings, autofix) {
if let Some(check) =
isort::plugins::check_imports(&block, locator, settings, autofix, package)
{
checks.push(check);
}
}
@@ -34,12 +37,13 @@ pub fn check_imports(
locator: &SourceCodeLocator,
directives: &IsortDirectives,
settings: &Settings,
autofix: bool,
autofix: flags::Autofix,
path: &Path,
package: Option<&Path>,
) -> Vec<Check> {
let mut tracker = ImportTracker::new(locator, directives, path);
for stmt in python_ast {
tracker.visit_stmt(stmt);
}
check_import_blocks(tracker, locator, settings, autofix)
check_import_blocks(tracker, locator, settings, autofix, package)
}

90
src/checkers/lines.rs Normal file
View File

@@ -0,0 +1,90 @@
//! Lint rules based on checking raw physical lines.
use crate::checks::{Check, CheckCode};
use crate::pycodestyle::checks::{line_too_long, no_newline_at_end_of_file};
use crate::pygrep_hooks::plugins::blanket_type_ignore;
use crate::pyupgrade::checks::unnecessary_coding_comment;
use crate::settings::{flags, Settings};
pub fn check_lines(
contents: &str,
commented_lines: &[usize],
settings: &Settings,
autofix: flags::Autofix,
) -> Vec<Check> {
let mut checks: Vec<Check> = vec![];
let enforce_unnecessary_coding_comment = settings.enabled.contains(&CheckCode::UP009);
let enforce_line_too_long = settings.enabled.contains(&CheckCode::E501);
let enforce_no_newline_at_end_of_file = settings.enabled.contains(&CheckCode::W292);
let enforce_blanket_type_ignore = settings.enabled.contains(&CheckCode::PGH003);
let mut commented_lines_iter = commented_lines.iter().peekable();
for (index, line) in contents.lines().enumerate() {
while commented_lines_iter
.next_if(|lineno| &(index + 1) == *lineno)
.is_some()
{
if enforce_unnecessary_coding_comment {
if index < 2 {
if let Some(check) = unnecessary_coding_comment(
index,
line,
matches!(autofix, flags::Autofix::Enabled)
&& settings.fixable.contains(&CheckCode::UP009),
) {
checks.push(check);
}
}
}
if enforce_blanket_type_ignore {
if commented_lines.contains(&(index + 1)) {
if let Some(check) = blanket_type_ignore(index, line) {
checks.push(check);
}
}
}
}
if enforce_line_too_long {
if let Some(check) = line_too_long(index, line, settings.line_length) {
checks.push(check);
}
}
}
if enforce_no_newline_at_end_of_file {
if let Some(check) = no_newline_at_end_of_file(contents) {
checks.push(check);
}
}
checks
}
#[cfg(test)]
mod tests {
use super::check_lines;
use crate::checks::CheckCode;
use crate::settings::{flags, Settings};
#[test]
fn e501_non_ascii_char() {
let line = "'\u{4e9c}' * 2"; // 7 in UTF-32, 9 in UTF-8.
let check_with_max_line_length = |line_length: usize| {
check_lines(
line,
&[],
&Settings {
line_length,
..Settings::for_rule(CheckCode::E501)
},
flags::Autofix::Enabled,
)
};
assert!(!check_with_max_line_length(6).is_empty());
assert!(check_with_max_line_length(7).is_empty());
}
}

5
src/checkers/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
pub mod ast;
pub mod imports;
pub mod lines;
pub mod noqa;
pub mod tokens;

148
src/checkers/noqa.rs Normal file
View File

@@ -0,0 +1,148 @@
//! `NoQA` enforcement and validation.
use nohash_hasher::IntMap;
use rustpython_parser::ast::Location;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checks::{Check, CheckCode, CheckKind, CODE_REDIRECTS};
use crate::noqa;
use crate::noqa::{is_file_exempt, Directive};
use crate::settings::{flags, Settings};
pub fn check_noqa(
checks: &mut Vec<Check>,
contents: &str,
commented_lines: &[usize],
noqa_line_for: &IntMap<usize, usize>,
settings: &Settings,
autofix: flags::Autofix,
) {
let mut noqa_directives: IntMap<usize, (Directive, Vec<&str>)> = IntMap::default();
let mut ignored = vec![];
let enforce_noqa = settings.enabled.contains(&CheckCode::RUF100);
checks.sort_by_key(|check| check.location);
let mut checks_iter = checks.iter().enumerate().peekable();
if let Some((_index, check)) = checks_iter.peek() {
assert!(check.location.row() >= 1);
}
let lines: Vec<&str> = contents.lines().collect();
for lineno in commented_lines {
// If we hit an exemption for the entire file, bail.
if is_file_exempt(lines[lineno - 1]) {
checks.drain(..);
return;
}
if enforce_noqa {
noqa_directives
.entry(lineno - 1)
.or_insert_with(|| (noqa::extract_noqa_directive(lines[lineno - 1]), vec![]));
}
// Remove any ignored checks.
while let Some((index, check)) =
checks_iter.next_if(|(_index, check)| check.location.row() <= *lineno)
{
// Grab the noqa (logical) line number for the current (physical) line.
// If there are newlines at the end of the file, they won't be represented in
// `noqa_line_for`, so fallback to the current line.
let check_lineno = check.location.row();
let noqa_lineno = noqa_line_for.get(&check_lineno).unwrap_or(&check_lineno);
if noqa_lineno == lineno {
let noqa = noqa_directives.entry(noqa_lineno - 1).or_insert_with(|| {
(noqa::extract_noqa_directive(lines[noqa_lineno - 1]), vec![])
});
match noqa {
(Directive::All(..), matches) => {
matches.push(check.kind.code().as_ref());
ignored.push(index);
}
(Directive::Codes(.., codes), matches) => {
if noqa::includes(check.kind.code(), codes) {
matches.push(check.kind.code().as_ref());
ignored.push(index);
}
}
(Directive::None, ..) => {}
}
}
}
}
// Enforce that the noqa directive was actually used (RUF100).
if enforce_noqa {
for (row, (directive, matches)) in noqa_directives {
match directive {
Directive::All(spaces, start, end) => {
if matches.is_empty() {
let mut check = Check::new(
CheckKind::UnusedNOQA(None),
Range {
location: Location::new(row + 1, start),
end_location: Location::new(row + 1, end),
},
);
if matches!(autofix, flags::Autofix::Enabled)
&& settings.fixable.contains(check.kind.code())
{
check.amend(Fix::deletion(
Location::new(row + 1, start - spaces),
Location::new(row + 1, lines[row].chars().count()),
));
}
checks.push(check);
}
}
Directive::Codes(spaces, start, end, codes) => {
let mut invalid_codes = vec![];
let mut valid_codes = vec![];
for code in codes {
let code = CODE_REDIRECTS.get(code).map_or(code, AsRef::as_ref);
if matches.contains(&code) || settings.external.contains(code) {
valid_codes.push(code.to_string());
} else {
invalid_codes.push(code.to_string());
}
}
if !invalid_codes.is_empty() {
let mut check = Check::new(
CheckKind::UnusedNOQA(Some(invalid_codes)),
Range {
location: Location::new(row + 1, start),
end_location: Location::new(row + 1, end),
},
);
if matches!(autofix, flags::Autofix::Enabled)
&& settings.fixable.contains(check.kind.code())
{
if valid_codes.is_empty() {
check.amend(Fix::deletion(
Location::new(row + 1, start - spaces),
Location::new(row + 1, lines[row].chars().count()),
));
} else {
check.amend(Fix::replacement(
format!("# noqa: {}", valid_codes.join(", ")),
Location::new(row + 1, start),
Location::new(row + 1, lines[row].chars().count()),
));
}
}
checks.push(check);
}
}
Directive::None => {}
}
}
}
ignored.sort_unstable();
for index in ignored.iter().rev() {
checks.swap_remove(*index);
}
}

View File

@@ -5,6 +5,7 @@ use rustpython_parser::lexer::{LexResult, Tok};
use crate::checks::{Check, CheckCode};
use crate::lex::docstring_detection::StateMachine;
use crate::ruff::checks::Context;
use crate::settings::flags;
use crate::source_code_locator::SourceCodeLocator;
use crate::{eradicate, flake8_quotes, pycodestyle, ruff, Settings};
@@ -12,7 +13,7 @@ pub fn check_tokens(
locator: &SourceCodeLocator,
tokens: &[LexResult],
settings: &Settings,
autofix: bool,
autofix: flags::Autofix,
) -> Vec<Check> {
let mut checks: Vec<Check> = vec![];

View File

@@ -309,6 +309,16 @@ pub enum CheckCode {
ARG005,
// flake8-import-conventions
ICN001,
// flake8-datetimez
DTZ001,
DTZ002,
DTZ003,
DTZ004,
DTZ005,
DTZ006,
DTZ007,
DTZ011,
DTZ012,
// Ruff
RUF001,
RUF002,
@@ -316,19 +326,21 @@ pub enum CheckCode {
RUF100,
// pygrep-hooks
PGH001,
PGH002,
PGH003,
// pandas-vet
PDV002,
PDV003,
PDV004,
PDV007,
PDV008,
PDV009,
PDV010,
PDV011,
PDV012,
PDV013,
PDV015,
PDV901,
PD002,
PD003,
PD004,
PD007,
PD008,
PD009,
PD010,
PD011,
PD012,
PD013,
PD015,
PD901,
// flake8-errmsg
EM101,
EM102,
@@ -361,6 +373,7 @@ pub enum CheckCategory {
Flake8Simplify,
Flake8TidyImports,
Flake8UnusedArguments,
Flake8Datetimez,
Eradicate,
PandasVet,
PygrepHooks,
@@ -403,6 +416,7 @@ impl CheckCategory {
CheckCategory::Flake8TidyImports => "flake8-tidy-imports",
CheckCategory::Flake8Simplify => "flake8-simplify",
CheckCategory::Flake8UnusedArguments => "flake8-unused-arguments",
CheckCategory::Flake8Datetimez => "flake8-datetimez",
CheckCategory::Isort => "isort",
CheckCategory::McCabe => "mccabe",
CheckCategory::PandasVet => "pandas-vet",
@@ -436,9 +450,10 @@ impl CheckCategory {
CheckCategory::Flake8Simplify => vec![CheckCodePrefix::SIM],
CheckCategory::Flake8TidyImports => vec![CheckCodePrefix::TID],
CheckCategory::Flake8UnusedArguments => vec![CheckCodePrefix::ARG],
CheckCategory::Flake8Datetimez => vec![CheckCodePrefix::DTZ],
CheckCategory::Isort => vec![CheckCodePrefix::I],
CheckCategory::McCabe => vec![CheckCodePrefix::C90],
CheckCategory::PandasVet => vec![CheckCodePrefix::PDV],
CheckCategory::PandasVet => vec![CheckCodePrefix::PD],
CheckCategory::PEP8Naming => vec![CheckCodePrefix::N],
CheckCategory::Pycodestyle => vec![CheckCodePrefix::E, CheckCodePrefix::W],
CheckCategory::Pydocstyle => vec![CheckCodePrefix::D],
@@ -526,6 +541,10 @@ impl CheckCategory {
"https://pypi.org/project/flake8-unused-arguments/0.0.12/",
&Platform::PyPI,
)),
CheckCategory::Flake8Datetimez => Some((
"https://pypi.org/project/flake8-datetimez/20.10.0/",
&Platform::PyPI,
)),
CheckCategory::Isort => {
Some(("https://pypi.org/project/isort/5.10.1/", &Platform::PyPI))
}
@@ -573,6 +592,7 @@ pub enum LintSource {
Lines,
Tokens,
Imports,
NoQA,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
@@ -772,7 +792,7 @@ pub enum CheckKind {
MissingTypeCls(String),
MissingReturnTypePublicFunction(String),
MissingReturnTypePrivateFunction(String),
MissingReturnTypeMagicMethod(String),
MissingReturnTypeSpecialMethod(String),
MissingReturnTypeStaticMethod(String),
MissingReturnTypeClassMethod(String),
DynamicallyTypedExpression(String),
@@ -886,6 +906,8 @@ pub enum CheckKind {
BooleanPositionalValueInFunctionCall,
// pygrep-hooks
NoEval,
DeprecatedLogWarn,
BlanketTypeIgnore,
// flake8-unused-arguments
UnusedFunctionArgument(String),
UnusedMethodArgument(String),
@@ -916,6 +938,16 @@ pub enum CheckKind {
AmbiguousUnicodeCharacterDocstring(char, char),
AmbiguousUnicodeCharacterComment(char, char),
UnusedNOQA(Option<Vec<String>>),
// flake8-datetimez
CallDatetimeWithoutTzinfo,
CallDatetimeToday,
CallDatetimeUtcnow,
CallDatetimeUtcfromtimestamp,
CallDatetimeNowWithoutTzinfo,
CallDatetimeFromtimestamp,
CallDatetimeStrptimeWithoutZone,
CallDateToday,
CallDateFromtimestamp,
}
impl CheckCode {
@@ -923,7 +955,8 @@ impl CheckCode {
/// physical lines).
pub fn lint_source(&self) -> &'static LintSource {
match self {
CheckCode::E501 | CheckCode::W292 | CheckCode::RUF100 | CheckCode::UP009 => {
CheckCode::RUF100 => &LintSource::NoQA,
CheckCode::E501 | CheckCode::W292 | CheckCode::UP009 | CheckCode::PGH003 => {
&LintSource::Lines
}
CheckCode::ERA001
@@ -1126,7 +1159,7 @@ impl CheckCode {
CheckCode::ANN102 => CheckKind::MissingTypeCls("...".to_string()),
CheckCode::ANN201 => CheckKind::MissingReturnTypePublicFunction("...".to_string()),
CheckCode::ANN202 => CheckKind::MissingReturnTypePrivateFunction("...".to_string()),
CheckCode::ANN204 => CheckKind::MissingReturnTypeMagicMethod("...".to_string()),
CheckCode::ANN204 => CheckKind::MissingReturnTypeSpecialMethod("...".to_string()),
CheckCode::ANN205 => CheckKind::MissingReturnTypeStaticMethod("...".to_string()),
CheckCode::ANN206 => CheckKind::MissingReturnTypeClassMethod("...".to_string()),
CheckCode::ANN401 => CheckKind::DynamicallyTypedExpression("...".to_string()),
@@ -1260,6 +1293,8 @@ impl CheckCode {
CheckCode::FBT003 => CheckKind::BooleanPositionalValueInFunctionCall,
// pygrep-hooks
CheckCode::PGH001 => CheckKind::NoEval,
CheckCode::PGH002 => CheckKind::DeprecatedLogWarn,
CheckCode::PGH003 => CheckKind::BlanketTypeIgnore,
// flake8-unused-arguments
CheckCode::ARG001 => CheckKind::UnusedFunctionArgument("...".to_string()),
CheckCode::ARG002 => CheckKind::UnusedMethodArgument("...".to_string()),
@@ -1271,22 +1306,32 @@ impl CheckCode {
CheckKind::ImportAliasIsNotConventional("...".to_string(), "...".to_string())
}
// pandas-vet
CheckCode::PDV002 => CheckKind::UseOfInplaceArgument,
CheckCode::PDV003 => CheckKind::UseOfDotIsNull,
CheckCode::PDV004 => CheckKind::UseOfDotNotNull,
CheckCode::PDV007 => CheckKind::UseOfDotIx,
CheckCode::PDV008 => CheckKind::UseOfDotAt,
CheckCode::PDV009 => CheckKind::UseOfDotIat,
CheckCode::PDV010 => CheckKind::UseOfDotPivotOrUnstack,
CheckCode::PDV011 => CheckKind::UseOfDotValues,
CheckCode::PDV012 => CheckKind::UseOfDotReadTable,
CheckCode::PDV013 => CheckKind::UseOfDotStack,
CheckCode::PDV015 => CheckKind::UseOfPdMerge,
CheckCode::PDV901 => CheckKind::DfIsABadVariableName,
CheckCode::PD002 => CheckKind::UseOfInplaceArgument,
CheckCode::PD003 => CheckKind::UseOfDotIsNull,
CheckCode::PD004 => CheckKind::UseOfDotNotNull,
CheckCode::PD007 => CheckKind::UseOfDotIx,
CheckCode::PD008 => CheckKind::UseOfDotAt,
CheckCode::PD009 => CheckKind::UseOfDotIat,
CheckCode::PD010 => CheckKind::UseOfDotPivotOrUnstack,
CheckCode::PD011 => CheckKind::UseOfDotValues,
CheckCode::PD012 => CheckKind::UseOfDotReadTable,
CheckCode::PD013 => CheckKind::UseOfDotStack,
CheckCode::PD015 => CheckKind::UseOfPdMerge,
CheckCode::PD901 => CheckKind::DfIsABadVariableName,
// flake8-errmsg
CheckCode::EM101 => CheckKind::RawStringInException,
CheckCode::EM102 => CheckKind::FStringInException,
CheckCode::EM103 => CheckKind::DotFormatInException,
// flake8-datetimez
CheckCode::DTZ001 => CheckKind::CallDatetimeWithoutTzinfo,
CheckCode::DTZ002 => CheckKind::CallDatetimeToday,
CheckCode::DTZ003 => CheckKind::CallDatetimeUtcnow,
CheckCode::DTZ004 => CheckKind::CallDatetimeUtcfromtimestamp,
CheckCode::DTZ005 => CheckKind::CallDatetimeNowWithoutTzinfo,
CheckCode::DTZ006 => CheckKind::CallDatetimeFromtimestamp,
CheckCode::DTZ007 => CheckKind::CallDatetimeStrptimeWithoutZone,
CheckCode::DTZ011 => CheckKind::CallDateToday,
CheckCode::DTZ012 => CheckKind::CallDateFromtimestamp,
// Ruff
CheckCode::RUF001 => CheckKind::AmbiguousUnicodeCharacterString('𝐁', 'B'),
CheckCode::RUF002 => CheckKind::AmbiguousUnicodeCharacterDocstring('𝐁', 'B'),
@@ -1408,6 +1453,15 @@ impl CheckCode {
CheckCode::D417 => CheckCategory::Pydocstyle,
CheckCode::D418 => CheckCategory::Pydocstyle,
CheckCode::D419 => CheckCategory::Pydocstyle,
CheckCode::DTZ001 => CheckCategory::Flake8Datetimez,
CheckCode::DTZ002 => CheckCategory::Flake8Datetimez,
CheckCode::DTZ003 => CheckCategory::Flake8Datetimez,
CheckCode::DTZ004 => CheckCategory::Flake8Datetimez,
CheckCode::DTZ005 => CheckCategory::Flake8Datetimez,
CheckCode::DTZ006 => CheckCategory::Flake8Datetimez,
CheckCode::DTZ007 => CheckCategory::Flake8Datetimez,
CheckCode::DTZ011 => CheckCategory::Flake8Datetimez,
CheckCode::DTZ012 => CheckCategory::Flake8Datetimez,
CheckCode::E402 => CheckCategory::Pycodestyle,
CheckCode::E501 => CheckCategory::Pycodestyle,
CheckCode::E711 => CheckCategory::Pycodestyle,
@@ -1491,19 +1545,21 @@ impl CheckCode {
CheckCode::N816 => CheckCategory::PEP8Naming,
CheckCode::N817 => CheckCategory::PEP8Naming,
CheckCode::N818 => CheckCategory::PEP8Naming,
CheckCode::PDV002 => CheckCategory::PandasVet,
CheckCode::PDV003 => CheckCategory::PandasVet,
CheckCode::PDV004 => CheckCategory::PandasVet,
CheckCode::PDV007 => CheckCategory::PandasVet,
CheckCode::PDV008 => CheckCategory::PandasVet,
CheckCode::PDV009 => CheckCategory::PandasVet,
CheckCode::PDV010 => CheckCategory::PandasVet,
CheckCode::PDV011 => CheckCategory::PandasVet,
CheckCode::PDV012 => CheckCategory::PandasVet,
CheckCode::PDV013 => CheckCategory::PandasVet,
CheckCode::PDV015 => CheckCategory::PandasVet,
CheckCode::PDV901 => CheckCategory::PandasVet,
CheckCode::PD002 => CheckCategory::PandasVet,
CheckCode::PD003 => CheckCategory::PandasVet,
CheckCode::PD004 => CheckCategory::PandasVet,
CheckCode::PD007 => CheckCategory::PandasVet,
CheckCode::PD008 => CheckCategory::PandasVet,
CheckCode::PD009 => CheckCategory::PandasVet,
CheckCode::PD010 => CheckCategory::PandasVet,
CheckCode::PD011 => CheckCategory::PandasVet,
CheckCode::PD012 => CheckCategory::PandasVet,
CheckCode::PD013 => CheckCategory::PandasVet,
CheckCode::PD015 => CheckCategory::PandasVet,
CheckCode::PD901 => CheckCategory::PandasVet,
CheckCode::PGH001 => CheckCategory::PygrepHooks,
CheckCode::PGH002 => CheckCategory::PygrepHooks,
CheckCode::PGH003 => CheckCategory::PygrepHooks,
CheckCode::PLC0414 => CheckCategory::Pylint,
CheckCode::PLC2201 => CheckCategory::Pylint,
CheckCode::PLC3002 => CheckCategory::Pylint,
@@ -1733,7 +1789,7 @@ impl CheckKind {
CheckKind::MissingTypeCls(_) => &CheckCode::ANN102,
CheckKind::MissingReturnTypePublicFunction(_) => &CheckCode::ANN201,
CheckKind::MissingReturnTypePrivateFunction(_) => &CheckCode::ANN202,
CheckKind::MissingReturnTypeMagicMethod(_) => &CheckCode::ANN204,
CheckKind::MissingReturnTypeSpecialMethod(_) => &CheckCode::ANN204,
CheckKind::MissingReturnTypeStaticMethod(_) => &CheckCode::ANN205,
CheckKind::MissingReturnTypeClassMethod(_) => &CheckCode::ANN206,
CheckKind::DynamicallyTypedExpression(_) => &CheckCode::ANN401,
@@ -1847,6 +1903,8 @@ impl CheckKind {
CheckKind::BooleanPositionalValueInFunctionCall => &CheckCode::FBT003,
// pygrep-hooks
CheckKind::NoEval => &CheckCode::PGH001,
CheckKind::DeprecatedLogWarn => &CheckCode::PGH002,
CheckKind::BlanketTypeIgnore => &CheckCode::PGH003,
// flake8-unused-arguments
CheckKind::UnusedFunctionArgument(..) => &CheckCode::ARG001,
CheckKind::UnusedMethodArgument(..) => &CheckCode::ARG002,
@@ -1856,22 +1914,32 @@ impl CheckKind {
// flake8-import-conventions
CheckKind::ImportAliasIsNotConventional(..) => &CheckCode::ICN001,
// pandas-vet
CheckKind::UseOfInplaceArgument => &CheckCode::PDV002,
CheckKind::UseOfDotIsNull => &CheckCode::PDV003,
CheckKind::UseOfDotNotNull => &CheckCode::PDV004,
CheckKind::UseOfDotIx => &CheckCode::PDV007,
CheckKind::UseOfDotAt => &CheckCode::PDV008,
CheckKind::UseOfDotIat => &CheckCode::PDV009,
CheckKind::UseOfDotPivotOrUnstack => &CheckCode::PDV010,
CheckKind::UseOfDotValues => &CheckCode::PDV011,
CheckKind::UseOfDotReadTable => &CheckCode::PDV012,
CheckKind::UseOfDotStack => &CheckCode::PDV013,
CheckKind::UseOfPdMerge => &CheckCode::PDV015,
CheckKind::DfIsABadVariableName => &CheckCode::PDV901,
CheckKind::UseOfInplaceArgument => &CheckCode::PD002,
CheckKind::UseOfDotIsNull => &CheckCode::PD003,
CheckKind::UseOfDotNotNull => &CheckCode::PD004,
CheckKind::UseOfDotIx => &CheckCode::PD007,
CheckKind::UseOfDotAt => &CheckCode::PD008,
CheckKind::UseOfDotIat => &CheckCode::PD009,
CheckKind::UseOfDotPivotOrUnstack => &CheckCode::PD010,
CheckKind::UseOfDotValues => &CheckCode::PD011,
CheckKind::UseOfDotReadTable => &CheckCode::PD012,
CheckKind::UseOfDotStack => &CheckCode::PD013,
CheckKind::UseOfPdMerge => &CheckCode::PD015,
CheckKind::DfIsABadVariableName => &CheckCode::PD901,
// flake8-errmsg
CheckKind::RawStringInException => &CheckCode::EM101,
CheckKind::FStringInException => &CheckCode::EM102,
CheckKind::DotFormatInException => &CheckCode::EM103,
// flake8-datetimez
CheckKind::CallDatetimeWithoutTzinfo => &CheckCode::DTZ001,
CheckKind::CallDatetimeToday => &CheckCode::DTZ002,
CheckKind::CallDatetimeUtcnow => &CheckCode::DTZ003,
CheckKind::CallDatetimeUtcfromtimestamp => &CheckCode::DTZ004,
CheckKind::CallDatetimeNowWithoutTzinfo => &CheckCode::DTZ005,
CheckKind::CallDatetimeFromtimestamp => &CheckCode::DTZ006,
CheckKind::CallDatetimeStrptimeWithoutZone => &CheckCode::DTZ007,
CheckKind::CallDateToday => &CheckCode::DTZ011,
CheckKind::CallDateFromtimestamp => &CheckCode::DTZ012,
// Ruff
CheckKind::AmbiguousUnicodeCharacterString(..) => &CheckCode::RUF001,
CheckKind::AmbiguousUnicodeCharacterDocstring(..) => &CheckCode::RUF002,
@@ -2390,8 +2458,8 @@ impl CheckKind {
CheckKind::MissingReturnTypePrivateFunction(name) => {
format!("Missing return type annotation for private function `{name}`")
}
CheckKind::MissingReturnTypeMagicMethod(name) => {
format!("Missing return type annotation for magic method `{name}`")
CheckKind::MissingReturnTypeSpecialMethod(name) => {
format!("Missing return type annotation for special method `{name}`")
}
CheckKind::MissingReturnTypeStaticMethod(name) => {
format!("Missing return type annotation for staticmethod `{name}`")
@@ -2684,6 +2752,12 @@ impl CheckKind {
}
// pygrep-hooks
CheckKind::NoEval => "No builtin `eval()` allowed".to_string(),
CheckKind::DeprecatedLogWarn => {
"`warn` is deprecated in favor of `warning`".to_string()
}
CheckKind::BlanketTypeIgnore => {
"Use specific error codes when ignoring type issues".to_string()
}
// flake8-unused-arguments
CheckKind::UnusedFunctionArgument(name) => {
format!("Unused function argument: `{name}`")
@@ -2711,13 +2785,13 @@ impl CheckKind {
"`.notna` is preferred to `.notnull`; functionality is equivalent".to_string()
}
CheckKind::UseOfDotIx => {
"``ix` i` deprecated; use more explicit `.loc` o` `.iloc`".to_string()
"`.ix` is deprecated; use more explicit `.loc` or `.iloc`".to_string()
}
CheckKind::UseOfDotAt => {
"Use `.loc` instead of `.at`. If speed is important, use numpy.".to_string()
}
CheckKind::UseOfDotIat => {
"Use `.iloc` instea` of `.iat`. If speed is important, use numpy.".to_string()
"Use `.iloc` instead of `.iat`. If speed is important, use numpy.".to_string()
}
CheckKind::UseOfDotPivotOrUnstack => "`.pivot_table` is preferred to `.pivot` or \
`.unstack`; provides same functionality"
@@ -2780,6 +2854,41 @@ impl CheckKind {
format!("Unused `noqa` directive for: {codes}")
}
},
// flake8-datetimez
CheckKind::CallDatetimeWithoutTzinfo => "The use of `datetime.datetime()` without \
`tzinfo` argument is not allowed"
.to_string(),
CheckKind::CallDatetimeToday => "The use of `datetime.datetime.today()` is not \
allowed. Use `datetime.datetime.now(tz=)` instead."
.to_string(),
CheckKind::CallDatetimeUtcnow => "The use of `datetime.datetime.utcnow()` is not \
allowed. Use `datetime.datetime.now(tz=)` instead."
.to_string(),
CheckKind::CallDatetimeUtcfromtimestamp => {
"The use of `datetime.datetime.utcfromtimestamp()` is not allowed. Use \
`datetime.datetime.fromtimestamp(, tz=)` instead."
.to_string()
}
CheckKind::CallDatetimeNowWithoutTzinfo => "The use of `datetime.datetime.now()` \
without `tz` argument is not allowed"
.to_string(),
CheckKind::CallDatetimeFromtimestamp => "The use of \
`datetime.datetime.fromtimestamp()` without \
`tz` argument is not allowed"
.to_string(),
CheckKind::CallDatetimeStrptimeWithoutZone => {
"The use of `datetime.datetime.strptime()` without %z must be followed by \
`.replace(tzinfo=)`"
.to_string()
}
CheckKind::CallDateToday => "The use of `datetime.date.today()` is not allowed. Use \
`datetime.datetime.now(tz=).date()` instead."
.to_string(),
CheckKind::CallDateFromtimestamp => {
"The use of `datetime.date.fromtimestamp()` is not allowed. Use \
`datetime.datetime.fromtimestamp(, tz=).date()` instead."
.to_string()
}
}
}
@@ -2799,6 +2908,23 @@ impl CheckKind {
CheckKind::StarArgUnpackingAfterKeywordArg => {
"Star-arg unpacking after a keyword argument is strongly discouraged".to_string()
}
// flake8-datetimez
CheckKind::CallDatetimeToday => {
"The use of `datetime.datetime.today()` is not allowed".to_string()
}
CheckKind::CallDatetimeUtcnow => {
"The use of `datetime.datetime.utcnow()` is not allowed".to_string()
}
CheckKind::CallDatetimeUtcfromtimestamp => {
"The use of `datetime.datetime.utcfromtimestamp()` is not allowed".to_string()
}
CheckKind::CallDateToday => {
"The use of `datetime.date.today()` is not allowed.".to_string()
}
CheckKind::CallDateFromtimestamp => {
"The use of `datetime.date.fromtimestamp()` is not allowed".to_string()
}
_ => self.body(),
}
}
@@ -2817,7 +2943,6 @@ impl CheckKind {
| CheckKind::CommentedOutCode
| CheckKind::ConvertNamedTupleFunctionalToClass(..)
| CheckKind::ConvertTypedDictFunctionalToClass(..)
| CheckKind::RemoveSixCompat
| CheckKind::DashedUnderlineAfterSection(..)
| CheckKind::DeprecatedUnittestAlias(..)
| CheckKind::DoNotAssertFalse
@@ -2831,6 +2956,7 @@ impl CheckKind {
| CheckKind::IsLiteral
| CheckKind::KeyInDict(..)
| CheckKind::MisplacedComparisonConstant(..)
| CheckKind::MissingReturnTypeSpecialMethod(..)
| CheckKind::NewLineAfterLastParagraph
| CheckKind::NewLineAfterSectionName(..)
| CheckKind::NoBlankLineAfterFunction(..)
@@ -2845,13 +2971,14 @@ impl CheckKind {
| CheckKind::NotIsTest
| CheckKind::OneBlankLineAfterClass(..)
| CheckKind::OneBlankLineBeforeClass(..)
| CheckKind::PercentFormatExtraNamedArguments(..)
| CheckKind::PEP3120UnnecessaryCodingComment
| CheckKind::PPrintFound
| CheckKind::PercentFormatExtraNamedArguments(..)
| CheckKind::PrintFound
| CheckKind::RaiseNotImplemented
| CheckKind::RedundantOpenModes
| CheckKind::RedundantTupleInExceptionHandler(..)
| CheckKind::RemoveSixCompat
| CheckKind::SectionNameEndsInColon(..)
| CheckKind::SectionNotOverIndented(..)
| CheckKind::SectionUnderlineAfterName(..)
@@ -2938,6 +3065,19 @@ pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(
// TODO(charlie): Remove by 2023-02-01.
("I252", CheckCode::TID252),
("M001", CheckCode::RUF100),
// TODO(charlie): Remove by 2023-02-01.
("PDV002", CheckCode::PD002),
("PDV003", CheckCode::PD003),
("PDV004", CheckCode::PD004),
("PDV007", CheckCode::PD007),
("PDV008", CheckCode::PD008),
("PDV009", CheckCode::PD009),
("PDV010", CheckCode::PD010),
("PDV011", CheckCode::PD011),
("PDV012", CheckCode::PD012),
("PDV013", CheckCode::PD013),
("PDV015", CheckCode::PD015),
("PDV901", CheckCode::PD901),
])
});
@@ -2954,6 +3094,12 @@ pub static PREFIX_REDIRECTS: Lazy<FxHashMap<&'static str, &'static str>> = Lazy:
("I25", "TID25"),
("M", "RUF100"),
("M0", "RUF100"),
// TODO(charlie): Remove by 2023-02-01.
("PDV", "PD"),
("PDV0", "PD0"),
("PDV01", "PD01"),
("PDV9", "PD9"),
("PDV90", "PD90"),
])
});

View File

@@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
use strum_macros::{AsRefStr, EnumString};
use crate::checks::CheckCode;
use crate::one_time_warning;
#[derive(
EnumString, AsRefStr, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize,
@@ -162,6 +163,19 @@ pub enum CheckCodePrefix {
D417,
D418,
D419,
DTZ,
DTZ0,
DTZ00,
DTZ001,
DTZ002,
DTZ003,
DTZ004,
DTZ005,
DTZ006,
DTZ007,
DTZ01,
DTZ011,
DTZ012,
E,
E4,
E40,
@@ -303,9 +317,26 @@ pub enum CheckCodePrefix {
N816,
N817,
N818,
PD,
PD0,
PD00,
PD002,
PD003,
PD004,
PD007,
PD008,
PD009,
PD01,
PD010,
PD011,
PD012,
PD013,
PD015,
PD9,
PD90,
PD901,
PDV,
PDV0,
PDV00,
PDV002,
PDV003,
PDV004,
@@ -325,6 +356,8 @@ pub enum CheckCodePrefix {
PGH0,
PGH00,
PGH001,
PGH002,
PGH003,
PLC,
PLC0,
PLC04,
@@ -971,6 +1004,47 @@ impl CheckCodePrefix {
CheckCodePrefix::D417 => vec![CheckCode::D417],
CheckCodePrefix::D418 => vec![CheckCode::D418],
CheckCodePrefix::D419 => vec![CheckCode::D419],
CheckCodePrefix::DTZ => vec![
CheckCode::DTZ001,
CheckCode::DTZ002,
CheckCode::DTZ003,
CheckCode::DTZ004,
CheckCode::DTZ005,
CheckCode::DTZ006,
CheckCode::DTZ007,
CheckCode::DTZ011,
CheckCode::DTZ012,
],
CheckCodePrefix::DTZ0 => vec![
CheckCode::DTZ001,
CheckCode::DTZ002,
CheckCode::DTZ003,
CheckCode::DTZ004,
CheckCode::DTZ005,
CheckCode::DTZ006,
CheckCode::DTZ007,
CheckCode::DTZ011,
CheckCode::DTZ012,
],
CheckCodePrefix::DTZ00 => vec![
CheckCode::DTZ001,
CheckCode::DTZ002,
CheckCode::DTZ003,
CheckCode::DTZ004,
CheckCode::DTZ005,
CheckCode::DTZ006,
CheckCode::DTZ007,
],
CheckCodePrefix::DTZ001 => vec![CheckCode::DTZ001],
CheckCodePrefix::DTZ002 => vec![CheckCode::DTZ002],
CheckCodePrefix::DTZ003 => vec![CheckCode::DTZ003],
CheckCodePrefix::DTZ004 => vec![CheckCode::DTZ004],
CheckCodePrefix::DTZ005 => vec![CheckCode::DTZ005],
CheckCodePrefix::DTZ006 => vec![CheckCode::DTZ006],
CheckCodePrefix::DTZ007 => vec![CheckCode::DTZ007],
CheckCodePrefix::DTZ01 => vec![CheckCode::DTZ011, CheckCode::DTZ012],
CheckCodePrefix::DTZ011 => vec![CheckCode::DTZ011],
CheckCodePrefix::DTZ012 => vec![CheckCode::DTZ012],
CheckCodePrefix::E => vec![
CheckCode::E402,
CheckCode::E501,
@@ -1243,7 +1317,7 @@ impl CheckCodePrefix {
CheckCodePrefix::I00 => vec![CheckCode::I001],
CheckCodePrefix::I001 => vec![CheckCode::I001],
CheckCodePrefix::I2 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1252,7 +1326,7 @@ impl CheckCodePrefix {
vec![CheckCode::TID252]
}
CheckCodePrefix::I25 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1261,7 +1335,7 @@ impl CheckCodePrefix {
vec![CheckCode::TID252]
}
CheckCodePrefix::I252 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1274,7 +1348,7 @@ impl CheckCodePrefix {
CheckCodePrefix::ICN00 => vec![CheckCode::ICN001],
CheckCodePrefix::ICN001 => vec![CheckCode::ICN001],
CheckCodePrefix::M => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1283,7 +1357,7 @@ impl CheckCodePrefix {
vec![CheckCode::RUF100]
}
CheckCodePrefix::M0 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1292,7 +1366,7 @@ impl CheckCodePrefix {
vec![CheckCode::RUF100]
}
CheckCodePrefix::M001 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1368,66 +1442,252 @@ impl CheckCodePrefix {
CheckCodePrefix::N816 => vec![CheckCode::N816],
CheckCodePrefix::N817 => vec![CheckCode::N817],
CheckCodePrefix::N818 => vec![CheckCode::N818],
CheckCodePrefix::PDV => vec![
CheckCode::PDV002,
CheckCode::PDV003,
CheckCode::PDV004,
CheckCode::PDV007,
CheckCode::PDV008,
CheckCode::PDV009,
CheckCode::PDV010,
CheckCode::PDV011,
CheckCode::PDV012,
CheckCode::PDV013,
CheckCode::PDV015,
CheckCode::PDV901,
CheckCodePrefix::PD => vec![
CheckCode::PD002,
CheckCode::PD003,
CheckCode::PD004,
CheckCode::PD007,
CheckCode::PD008,
CheckCode::PD009,
CheckCode::PD010,
CheckCode::PD011,
CheckCode::PD012,
CheckCode::PD013,
CheckCode::PD015,
CheckCode::PD901,
],
CheckCodePrefix::PDV0 => vec![
CheckCode::PDV002,
CheckCode::PDV003,
CheckCode::PDV004,
CheckCode::PDV007,
CheckCode::PDV008,
CheckCode::PDV009,
CheckCode::PDV010,
CheckCode::PDV011,
CheckCode::PDV012,
CheckCode::PDV013,
CheckCode::PDV015,
CheckCodePrefix::PD0 => vec![
CheckCode::PD002,
CheckCode::PD003,
CheckCode::PD004,
CheckCode::PD007,
CheckCode::PD008,
CheckCode::PD009,
CheckCode::PD010,
CheckCode::PD011,
CheckCode::PD012,
CheckCode::PD013,
CheckCode::PD015,
],
CheckCodePrefix::PDV00 => vec![
CheckCode::PDV002,
CheckCode::PDV003,
CheckCode::PDV004,
CheckCode::PDV007,
CheckCode::PDV008,
CheckCode::PDV009,
CheckCodePrefix::PD00 => vec![
CheckCode::PD002,
CheckCode::PD003,
CheckCode::PD004,
CheckCode::PD007,
CheckCode::PD008,
CheckCode::PD009,
],
CheckCodePrefix::PDV002 => vec![CheckCode::PDV002],
CheckCodePrefix::PDV003 => vec![CheckCode::PDV003],
CheckCodePrefix::PDV004 => vec![CheckCode::PDV004],
CheckCodePrefix::PDV007 => vec![CheckCode::PDV007],
CheckCodePrefix::PDV008 => vec![CheckCode::PDV008],
CheckCodePrefix::PDV009 => vec![CheckCode::PDV009],
CheckCodePrefix::PDV01 => vec![
CheckCode::PDV010,
CheckCode::PDV011,
CheckCode::PDV012,
CheckCode::PDV013,
CheckCode::PDV015,
CheckCodePrefix::PD002 => vec![CheckCode::PD002],
CheckCodePrefix::PD003 => vec![CheckCode::PD003],
CheckCodePrefix::PD004 => vec![CheckCode::PD004],
CheckCodePrefix::PD007 => vec![CheckCode::PD007],
CheckCodePrefix::PD008 => vec![CheckCode::PD008],
CheckCodePrefix::PD009 => vec![CheckCode::PD009],
CheckCodePrefix::PD01 => vec![
CheckCode::PD010,
CheckCode::PD011,
CheckCode::PD012,
CheckCode::PD013,
CheckCode::PD015,
],
CheckCodePrefix::PDV010 => vec![CheckCode::PDV010],
CheckCodePrefix::PDV011 => vec![CheckCode::PDV011],
CheckCodePrefix::PDV012 => vec![CheckCode::PDV012],
CheckCodePrefix::PDV013 => vec![CheckCode::PDV013],
CheckCodePrefix::PDV015 => vec![CheckCode::PDV015],
CheckCodePrefix::PDV9 => vec![CheckCode::PDV901],
CheckCodePrefix::PDV90 => vec![CheckCode::PDV901],
CheckCodePrefix::PDV901 => vec![CheckCode::PDV901],
CheckCodePrefix::PGH => vec![CheckCode::PGH001],
CheckCodePrefix::PGH0 => vec![CheckCode::PGH001],
CheckCodePrefix::PGH00 => vec![CheckCode::PGH001],
CheckCodePrefix::PD010 => vec![CheckCode::PD010],
CheckCodePrefix::PD011 => vec![CheckCode::PD011],
CheckCodePrefix::PD012 => vec![CheckCode::PD012],
CheckCodePrefix::PD013 => vec![CheckCode::PD013],
CheckCodePrefix::PD015 => vec![CheckCode::PD015],
CheckCodePrefix::PD9 => vec![CheckCode::PD901],
CheckCodePrefix::PD90 => vec![CheckCode::PD901],
CheckCodePrefix::PD901 => vec![CheckCode::PD901],
CheckCodePrefix::PDV => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV` has been remapped to `PD`".bold()
);
vec![
CheckCode::PD002,
CheckCode::PD003,
CheckCode::PD004,
CheckCode::PD007,
CheckCode::PD008,
CheckCode::PD009,
CheckCode::PD010,
CheckCode::PD011,
CheckCode::PD012,
CheckCode::PD013,
CheckCode::PD015,
CheckCode::PD901,
]
}
CheckCodePrefix::PDV0 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV0` has been remapped to `PD0`".bold()
);
vec![
CheckCode::PD002,
CheckCode::PD003,
CheckCode::PD004,
CheckCode::PD007,
CheckCode::PD008,
CheckCode::PD009,
CheckCode::PD010,
CheckCode::PD011,
CheckCode::PD012,
CheckCode::PD013,
CheckCode::PD015,
]
}
CheckCodePrefix::PDV002 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV002` has been remapped to `PD002`".bold()
);
vec![CheckCode::PD002]
}
CheckCodePrefix::PDV003 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV003` has been remapped to `PD003`".bold()
);
vec![CheckCode::PD003]
}
CheckCodePrefix::PDV004 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV004` has been remapped to `PD004`".bold()
);
vec![CheckCode::PD004]
}
CheckCodePrefix::PDV007 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV007` has been remapped to `PD007`".bold()
);
vec![CheckCode::PD007]
}
CheckCodePrefix::PDV008 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV008` has been remapped to `PD008`".bold()
);
vec![CheckCode::PD008]
}
CheckCodePrefix::PDV009 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV009` has been remapped to `PD009`".bold()
);
vec![CheckCode::PD009]
}
CheckCodePrefix::PDV01 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV01` has been remapped to `PD01`".bold()
);
vec![
CheckCode::PD010,
CheckCode::PD011,
CheckCode::PD012,
CheckCode::PD013,
CheckCode::PD015,
]
}
CheckCodePrefix::PDV010 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV010` has been remapped to `PD010`".bold()
);
vec![CheckCode::PD010]
}
CheckCodePrefix::PDV011 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV011` has been remapped to `PD011`".bold()
);
vec![CheckCode::PD011]
}
CheckCodePrefix::PDV012 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV012` has been remapped to `PD012`".bold()
);
vec![CheckCode::PD012]
}
CheckCodePrefix::PDV013 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV013` has been remapped to `PD013`".bold()
);
vec![CheckCode::PD013]
}
CheckCodePrefix::PDV015 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV015` has been remapped to `PD015`".bold()
);
vec![CheckCode::PD015]
}
CheckCodePrefix::PDV9 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV9` has been remapped to `PD9`".bold()
);
vec![CheckCode::PD901]
}
CheckCodePrefix::PDV90 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV90` has been remapped to `PD90`".bold()
);
vec![CheckCode::PD901]
}
CheckCodePrefix::PDV901 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`PDV901` has been remapped to `PD901`".bold()
);
vec![CheckCode::PD901]
}
CheckCodePrefix::PGH => vec![CheckCode::PGH001, CheckCode::PGH002, CheckCode::PGH003],
CheckCodePrefix::PGH0 => vec![CheckCode::PGH001, CheckCode::PGH002, CheckCode::PGH003],
CheckCodePrefix::PGH00 => vec![CheckCode::PGH001, CheckCode::PGH002, CheckCode::PGH003],
CheckCodePrefix::PGH001 => vec![CheckCode::PGH001],
CheckCodePrefix::PGH002 => vec![CheckCode::PGH002],
CheckCodePrefix::PGH003 => vec![CheckCode::PGH003],
CheckCodePrefix::PLC => {
vec![CheckCode::PLC0414, CheckCode::PLC2201, CheckCode::PLC3002]
}
@@ -1603,7 +1863,7 @@ impl CheckCodePrefix {
CheckCodePrefix::TID25 => vec![CheckCode::TID252],
CheckCodePrefix::TID252 => vec![CheckCode::TID252],
CheckCodePrefix::U => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1628,7 +1888,7 @@ impl CheckCodePrefix {
]
}
CheckCodePrefix::U0 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1653,7 +1913,7 @@ impl CheckCodePrefix {
]
}
CheckCodePrefix::U00 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1671,7 +1931,7 @@ impl CheckCodePrefix {
]
}
CheckCodePrefix::U001 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1680,7 +1940,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP001]
}
CheckCodePrefix::U003 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1689,7 +1949,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP003]
}
CheckCodePrefix::U004 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1698,7 +1958,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP004]
}
CheckCodePrefix::U005 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1707,7 +1967,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP005]
}
CheckCodePrefix::U006 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1716,7 +1976,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP006]
}
CheckCodePrefix::U007 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1725,7 +1985,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP007]
}
CheckCodePrefix::U008 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1734,7 +1994,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP008]
}
CheckCodePrefix::U009 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1743,7 +2003,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP009]
}
CheckCodePrefix::U01 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1760,7 +2020,7 @@ impl CheckCodePrefix {
]
}
CheckCodePrefix::U010 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1769,7 +2029,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP010]
}
CheckCodePrefix::U011 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1778,7 +2038,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP011]
}
CheckCodePrefix::U012 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1787,7 +2047,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP012]
}
CheckCodePrefix::U013 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1796,7 +2056,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP013]
}
CheckCodePrefix::U014 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1805,7 +2065,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP014]
}
CheckCodePrefix::U015 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -1814,7 +2074,7 @@ impl CheckCodePrefix {
vec![CheckCode::UP015]
}
CheckCodePrefix::U016 => {
eprintln!(
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -2095,6 +2355,19 @@ impl CheckCodePrefix {
CheckCodePrefix::D417 => SuffixLength::Three,
CheckCodePrefix::D418 => SuffixLength::Three,
CheckCodePrefix::D419 => SuffixLength::Three,
CheckCodePrefix::DTZ => SuffixLength::Zero,
CheckCodePrefix::DTZ0 => SuffixLength::One,
CheckCodePrefix::DTZ00 => SuffixLength::Two,
CheckCodePrefix::DTZ001 => SuffixLength::Three,
CheckCodePrefix::DTZ002 => SuffixLength::Three,
CheckCodePrefix::DTZ003 => SuffixLength::Three,
CheckCodePrefix::DTZ004 => SuffixLength::Three,
CheckCodePrefix::DTZ005 => SuffixLength::Three,
CheckCodePrefix::DTZ006 => SuffixLength::Three,
CheckCodePrefix::DTZ007 => SuffixLength::Three,
CheckCodePrefix::DTZ01 => SuffixLength::Two,
CheckCodePrefix::DTZ011 => SuffixLength::Three,
CheckCodePrefix::DTZ012 => SuffixLength::Three,
CheckCodePrefix::E => SuffixLength::Zero,
CheckCodePrefix::E4 => SuffixLength::One,
CheckCodePrefix::E40 => SuffixLength::Two,
@@ -2236,9 +2509,26 @@ impl CheckCodePrefix {
CheckCodePrefix::N816 => SuffixLength::Three,
CheckCodePrefix::N817 => SuffixLength::Three,
CheckCodePrefix::N818 => SuffixLength::Three,
CheckCodePrefix::PD => SuffixLength::Zero,
CheckCodePrefix::PD0 => SuffixLength::One,
CheckCodePrefix::PD00 => SuffixLength::Two,
CheckCodePrefix::PD002 => SuffixLength::Three,
CheckCodePrefix::PD003 => SuffixLength::Three,
CheckCodePrefix::PD004 => SuffixLength::Three,
CheckCodePrefix::PD007 => SuffixLength::Three,
CheckCodePrefix::PD008 => SuffixLength::Three,
CheckCodePrefix::PD009 => SuffixLength::Three,
CheckCodePrefix::PD01 => SuffixLength::Two,
CheckCodePrefix::PD010 => SuffixLength::Three,
CheckCodePrefix::PD011 => SuffixLength::Three,
CheckCodePrefix::PD012 => SuffixLength::Three,
CheckCodePrefix::PD013 => SuffixLength::Three,
CheckCodePrefix::PD015 => SuffixLength::Three,
CheckCodePrefix::PD9 => SuffixLength::One,
CheckCodePrefix::PD90 => SuffixLength::Two,
CheckCodePrefix::PD901 => SuffixLength::Three,
CheckCodePrefix::PDV => SuffixLength::Zero,
CheckCodePrefix::PDV0 => SuffixLength::One,
CheckCodePrefix::PDV00 => SuffixLength::Two,
CheckCodePrefix::PDV002 => SuffixLength::Three,
CheckCodePrefix::PDV003 => SuffixLength::Three,
CheckCodePrefix::PDV004 => SuffixLength::Three,
@@ -2258,6 +2548,8 @@ impl CheckCodePrefix {
CheckCodePrefix::PGH0 => SuffixLength::One,
CheckCodePrefix::PGH00 => SuffixLength::Two,
CheckCodePrefix::PGH001 => SuffixLength::Three,
CheckCodePrefix::PGH002 => SuffixLength::Three,
CheckCodePrefix::PGH003 => SuffixLength::Three,
CheckCodePrefix::PLC => SuffixLength::Zero,
CheckCodePrefix::PLC0 => SuffixLength::One,
CheckCodePrefix::PLC04 => SuffixLength::Two,
@@ -2429,6 +2721,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
CheckCodePrefix::BLE,
CheckCodePrefix::C,
CheckCodePrefix::D,
CheckCodePrefix::DTZ,
CheckCodePrefix::E,
CheckCodePrefix::EM,
CheckCodePrefix::ERA,
@@ -2437,7 +2730,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
CheckCodePrefix::I,
CheckCodePrefix::ICN,
CheckCodePrefix::N,
CheckCodePrefix::PDV,
CheckCodePrefix::PD,
CheckCodePrefix::PGH,
CheckCodePrefix::PLC,
CheckCodePrefix::PLE,

View File

@@ -120,7 +120,7 @@ pub struct Cli {
pub autoformat: bool,
/// The name of the file when passing it through stdin.
#[arg(long)]
pub stdin_filename: Option<String>,
pub stdin_filename: Option<PathBuf>,
/// Explain a rule.
#[arg(long)]
pub explain: Option<CheckCode>,
@@ -203,7 +203,7 @@ pub struct Arguments {
pub show_files: bool,
pub show_settings: bool,
pub silent: bool,
pub stdin_filename: Option<String>,
pub stdin_filename: Option<PathBuf>,
pub verbose: bool,
pub watch: bool,
}

View File

@@ -17,9 +17,10 @@ use crate::cli::Overrides;
use crate::iterators::par_iter;
use crate::linter::{add_noqa_to_path, autoformat_path, lint_path, lint_stdin, Diagnostics};
use crate::message::Message;
use crate::resolver;
use crate::resolver::{FileDiscovery, PyprojectDiscovery};
use crate::settings::flags;
use crate::settings::types::SerializationFormat;
use crate::{packages, resolver};
/// Run the linter over a collection of files.
pub fn run(
@@ -27,24 +28,37 @@ pub fn run(
pyproject_strategy: &PyprojectDiscovery,
file_strategy: &FileDiscovery,
overrides: &Overrides,
cache: bool,
autofix: &fixer::Mode,
cache: flags::Cache,
autofix: fixer::Mode,
) -> Result<Diagnostics> {
// Collect all the files to check.
// Collect all the Python files to check.
let start = Instant::now();
let (paths, resolver) =
resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?;
let duration = start.elapsed();
debug!("Identified files to lint in: {:?}", duration);
// Discover the package root for each Python file.
let package_roots = packages::detect_package_roots(
&paths
.iter()
.flatten()
.map(ignore::DirEntry::path)
.collect::<Vec<_>>(),
);
let start = Instant::now();
let mut diagnostics: Diagnostics = par_iter(&paths)
.map(|entry| {
match entry {
Ok(entry) => {
let path = entry.path();
let package = path
.parent()
.and_then(|parent| package_roots.get(parent))
.and_then(|package| *package);
let settings = resolver.resolve(path, pyproject_strategy);
lint_path(path, settings, &cache.into(), autofix)
lint_path(path, package, settings, cache, autofix)
.map_err(|e| (Some(path.to_owned()), e.to_string()))
}
Err(e) => Err((
@@ -102,7 +116,7 @@ fn read_from_stdin() -> Result<String> {
pub fn run_stdin(
strategy: &PyprojectDiscovery,
filename: &Path,
autofix: &fixer::Mode,
autofix: fixer::Mode,
) -> Result<Diagnostics> {
let stdin = read_from_stdin()?;
let settings = match strategy {

View File

@@ -37,6 +37,7 @@ pub struct IsortDirectives {
}
pub struct Directives {
pub commented_lines: Vec<usize>,
pub noqa_line_for: IntMap<usize, usize>,
pub isort: IsortDirectives,
}
@@ -47,6 +48,7 @@ pub fn extract_directives(
flags: Flags,
) -> Directives {
Directives {
commented_lines: extract_commented_lines(lxr),
noqa_line_for: if flags.contains(Flags::NOQA) {
extract_noqa_line_for(lxr)
} else {
@@ -60,6 +62,16 @@ pub fn extract_directives(
}
}
pub fn extract_commented_lines(lxr: &[LexResult]) -> Vec<usize> {
let mut commented_lines = Vec::new();
for (start, tok, ..) in lxr.iter().flatten() {
if matches!(tok, Tok::Comment) {
commented_lines.push(start.row());
}
}
commented_lines
}
/// Extract a mapping from logical line to noqa line.
pub fn extract_noqa_line_for(lxr: &[LexResult]) -> IntMap<usize, usize> {
let mut noqa_line_for: IntMap<usize, usize> = IntMap::default();

View File

@@ -4,6 +4,7 @@ use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checks::{CheckCode, CheckKind};
use crate::eradicate::detection::comment_contains_code;
use crate::settings::flags;
use crate::{Check, Settings, SourceCodeLocator};
fn is_standalone_comment(line: &str) -> bool {
@@ -23,7 +24,7 @@ pub fn commented_out_code(
start: Location,
end: Location,
settings: &Settings,
autofix: bool,
autofix: flags::Autofix,
) -> Option<Check> {
let location = Location::new(start.row(), 0);
let end_location = Location::new(end.row() + 1, 0);
@@ -41,7 +42,9 @@ pub fn commented_out_code(
end_location: end,
},
);
if autofix && settings.fixable.contains(&CheckCode::ERA001) {
if matches!(autofix, flags::Autofix::Enabled)
&& settings.fixable.contains(&CheckCode::ERA001)
{
check.amend(Fix::deletion(location, end_location));
}
Some(check)

View File

@@ -21,7 +21,6 @@ mod tests {
.join(path)
.as_path(),
&settings::Settings::for_rule(check_code),
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);

View File

@@ -29,7 +29,6 @@ mod tests {
.join(path)
.as_path(),
&settings::Settings::for_rule(check_code),
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);

View File

@@ -3,7 +3,7 @@ use rustpython_ast::{Cmpop, Constant, Expr, ExprKind, Located};
use crate::ast::helpers::match_module_member;
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckCode, CheckKind};
fn is_sys(checker: &Checker, expr: &Expr, target: &str) -> bool {

View File

@@ -0,0 +1,45 @@
use anyhow::{bail, Result};
use rustpython_ast::Stmt;
use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok;
use crate::ast::helpers;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::source_code_locator::SourceCodeLocator;
/// ANN204
pub fn add_return_none_annotation(locator: &SourceCodeLocator, stmt: &Stmt) -> Result<Fix> {
let range = Range::from_located(stmt);
let contents = locator.slice_source_code_range(&range);
// Find the colon (following the `def` keyword).
let mut seen_lpar = false;
let mut seen_rpar = false;
let mut count: usize = 0;
for (start, tok, ..) in lexer::make_tokenizer(&contents).flatten() {
if seen_lpar && seen_rpar {
if matches!(tok, Tok::Colon) {
return Ok(Fix::insertion(
" -> None".to_string(),
helpers::to_absolute(start, range.location),
));
}
}
if matches!(tok, Tok::Lpar) {
if count == 0 {
seen_lpar = true;
}
count += 1;
}
if matches!(tok, Tok::Rpar) {
count -= 1;
if count == 0 {
seen_rpar = true;
}
}
}
bail!("Unable to locate colon in function definition");
}

View File

@@ -1,7 +1,7 @@
use rustpython_ast::{Arguments, Expr, Stmt, StmtKind};
use crate::ast::cast;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::docstrings::definition::{Definition, DefinitionKind};
use crate::visibility;

View File

@@ -1,3 +1,4 @@
mod fixes;
pub mod helpers;
pub mod plugins;
pub mod settings;
@@ -31,7 +32,6 @@ mod tests {
CheckCode::ANN401,
])
},
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
@@ -57,7 +57,6 @@ mod tests {
CheckCode::ANN102,
])
},
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
@@ -83,7 +82,6 @@ mod tests {
CheckCode::ANN206,
])
},
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
@@ -109,7 +107,6 @@ mod tests {
CheckCode::ANN206,
])
},
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
@@ -129,7 +126,6 @@ mod tests {
},
..Settings::for_rules(vec![CheckCode::ANN401])
},
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
@@ -149,7 +145,6 @@ mod tests {
CheckCode::ANN206,
])
},
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);

View File

@@ -1,11 +1,13 @@
use log::error;
use rustpython_ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
use crate::ast::types::Range;
use crate::ast::visitor::Visitor;
use crate::ast::{cast, visitor};
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{CheckCode, CheckKind};
use crate::docstrings::definition::{Definition, DefinitionKind};
use crate::flake8_annotations::fixes;
use crate::flake8_annotations::helpers::match_function_def;
use crate::visibility::Visibility;
use crate::{visibility, Check};
@@ -295,8 +297,8 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
check_dynamically_typed(checker, expr, || name.to_string());
}
} else {
// Allow omission of return annotation in `__init__` functions, if the function
// only returns `None` (explicitly or implicitly).
// Allow omission of return annotation if the function only returns `None`
// (explicitly or implicitly).
if checker.settings.flake8_annotations.suppress_none_returning
&& is_none_returning(body)
{
@@ -317,13 +319,6 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
Range::from_located(stmt),
));
}
} else if visibility::is_magic(stmt) {
if checker.settings.enabled.contains(&CheckCode::ANN204) {
checker.add_check(Check::new(
CheckKind::MissingReturnTypeMagicMethod(name.to_string()),
Range::from_located(stmt),
));
}
} else if visibility::is_init(stmt) {
// Allow omission of return annotation in `__init__` functions, as long as at
// least one argument is typed.
@@ -331,12 +326,26 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
if !(checker.settings.flake8_annotations.mypy_init_return
&& has_any_typed_arg)
{
checker.add_check(Check::new(
CheckKind::MissingReturnTypeMagicMethod(name.to_string()),
let mut check = Check::new(
CheckKind::MissingReturnTypeSpecialMethod(name.to_string()),
Range::from_located(stmt),
));
);
if checker.patch(check.kind.code()) {
match fixes::add_return_none_annotation(checker.locator, stmt) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.add_check(check);
}
}
} else if visibility::is_magic(stmt) {
if checker.settings.enabled.contains(&CheckCode::ANN204) {
checker.add_check(Check::new(
CheckKind::MissingReturnTypeSpecialMethod(name.to_string()),
Range::from_located(stmt),
));
}
} else {
match visibility {
Visibility::Public => {

View File

@@ -3,23 +3,37 @@ source: src/flake8_annotations/mod.rs
expression: checks
---
- kind:
MissingReturnTypeMagicMethod: __init__
MissingReturnTypeSpecialMethod: __init__
location:
row: 5
column: 4
end_location:
row: 6
column: 11
fix: ~
fix:
content: " -> None"
location:
row: 5
column: 22
end_location:
row: 5
column: 22
- kind:
MissingReturnTypeMagicMethod: __init__
MissingReturnTypeSpecialMethod: __init__
location:
row: 11
column: 4
end_location:
row: 12
column: 11
fix: ~
fix:
content: " -> None"
location:
row: 11
column: 27
end_location:
row: 11
column: 27
- kind:
MissingReturnTypePrivateFunction: __init__
location:

View File

@@ -26,7 +26,6 @@ mod tests {
.join(path)
.as_path(),
&settings::Settings::for_rule(check_code),
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);

View File

@@ -20,7 +20,6 @@ mod tests {
.join(path)
.as_path(),
&settings::Settings::for_rule(check_code),
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);

View File

@@ -1,7 +1,7 @@
use rustpython_ast::{Expr, ExprKind, Stmt, StmtKind};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// BLE001

View File

@@ -22,7 +22,6 @@ mod tests {
.join(path)
.as_path(),
&settings::Settings::for_rule(check_code),
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);

View File

@@ -2,7 +2,7 @@ use rustpython_ast::{Arguments, ExprKind};
use rustpython_parser::ast::{Constant, Expr};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
const FUNC_NAME_ALLOWLIST: &[&str] = &["get", "setdefault", "pop", "fromkeys"];

View File

@@ -47,7 +47,6 @@ mod tests {
.join(path)
.as_path(),
&Settings::for_rule(check_code),
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);
@@ -68,7 +67,6 @@ mod tests {
},
..Settings::for_rules(vec![CheckCode::B008])
},
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);

View File

@@ -3,7 +3,7 @@ use rustpython_ast::{Constant, Expr, ExprKind, Keyword, Stmt, StmtKind};
use crate::ast::helpers::match_module_member;
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckCode, CheckKind};
fn is_abc_class(

View File

@@ -2,7 +2,7 @@ use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location, Stmt, Stmt
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
use crate::code_gen::SourceGenerator;

View File

@@ -2,7 +2,7 @@ use rustpython_ast::{ExprKind, Stmt, Withitem};
use crate::ast::helpers::match_module_member;
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// B017

View File

@@ -1,7 +1,7 @@
use rustpython_ast::{Expr, ExprKind};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// B003

View File

@@ -2,7 +2,7 @@ use rustpython_ast::{Expr, ExprKind};
use crate::ast::helpers::{collect_call_paths, dealias_call_path, match_call_path};
use crate::ast::types::{Range, ScopeKind};
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
fn is_cache_func(checker: &Checker, expr: &Expr) -> bool {

View File

@@ -1,7 +1,7 @@
use rustpython_ast::{Expr, ExprKind};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// B016

View File

@@ -7,7 +7,7 @@ use rustpython_ast::{
use crate::ast::helpers;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckCode, CheckKind};
use crate::code_gen::SourceGenerator;
@@ -56,9 +56,12 @@ fn duplicate_handler_exceptions<'a>(
Range::from_located(expr),
);
if checker.patch(check.kind.code()) {
// TODO(charlie): If we have a single element, remove the tuple.
let mut generator = SourceGenerator::new();
generator.unparse_expr(&type_pattern(unique_elts), 0);
if unique_elts.len() == 1 {
generator.unparse_expr(unique_elts[0], 0);
} else {
generator.unparse_expr(&type_pattern(unique_elts), 0);
}
if let Ok(content) = generator.generate() {
check.amend(Fix::replacement(
content,

View File

@@ -1,7 +1,7 @@
use rustpython_ast::{ExprKind, Stmt, StmtKind};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// B021

View File

@@ -7,7 +7,7 @@ use crate::ast::helpers::{
use crate::ast::types::Range;
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
use crate::flake8_bugbear::plugins::mutable_argument_default::is_mutable_func;

View File

@@ -5,7 +5,7 @@ use crate::ast::helpers::collect_arg_names;
use crate::ast::types::{Node, Range};
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
#[derive(Default)]
@@ -219,8 +219,8 @@ where
// loop, flag it.
for (name, expr, range) in suspicious_variables {
if reassigned_in_loop.contains(name) {
if !checker.seen_b023.contains(&expr) {
checker.seen_b023.push(expr);
if !checker.flake8_bugbear_seen.contains(&expr) {
checker.flake8_bugbear_seen.push(expr);
checker.add_check(Check::new(
CheckKind::FunctionUsesLoopVariable(name.to_string()),
range,

View File

@@ -2,7 +2,7 @@ use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location};
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
use crate::code_gen::SourceGenerator;
use crate::python::identifiers::IDENTIFIER_REGEX;

View File

@@ -1,7 +1,7 @@
use rustpython_ast::{Stmt, StmtKind};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
fn walk_stmt(checker: &mut Checker, body: &[Stmt], f: fn(&Stmt) -> bool) {

View File

@@ -4,7 +4,7 @@ use rustpython_ast::{Expr, ExprKind};
use crate::ast::types::Range;
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
#[derive(Default)]

View File

@@ -3,7 +3,7 @@ use rustpython_ast::{Arguments, Constant, Expr, ExprKind, Operator};
use crate::ast::helpers::{collect_call_paths, dealias_call_path, match_call_path};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
const MUTABLE_FUNCS: &[(&str, &str)] = &[

View File

@@ -2,7 +2,7 @@ use rustpython_ast::{ExprKind, Stmt, StmtKind};
use crate::ast::types::Range;
use crate::ast::visitor::Visitor;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
use crate::python::string::is_lower;

View File

@@ -1,52 +1,10 @@
use anyhow::{bail, Result};
use log::error;
use rustpython_ast::{Excepthandler, ExcepthandlerKind, ExprKind, Located};
use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok;
use rustpython_ast::{Excepthandler, ExcepthandlerKind, ExprKind};
use crate::ast::helpers;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
use crate::code_gen::SourceGenerator;
use crate::SourceCodeLocator;
/// Given a statement like `except (ValueError,)`, find the range of the
/// parenthesized expression.
fn match_tuple_range<T>(located: &Located<T>, locator: &SourceCodeLocator) -> Result<Range> {
// Extract contents from the source code.
let range = Range::from_located(located);
let contents = locator.slice_source_code_range(&range);
// Find the left (opening) and right (closing) parentheses.
let mut location = None;
let mut end_location = None;
let mut count: usize = 0;
for (start, tok, end) in lexer::make_tokenizer(&contents).flatten() {
if matches!(tok, Tok::Lpar) {
if count == 0 {
location = Some(helpers::to_absolute(start, range.location));
}
count += 1;
}
if matches!(tok, Tok::Rpar) {
count -= 1;
if count == 0 {
end_location = Some(helpers::to_absolute(end, range.location));
break;
}
}
}
let (Some(location), Some(end_location)) = (location, end_location) else {
bail!("Unable to find left and right parentheses");
};
Ok(Range {
location,
end_location,
})
}
/// B013
pub fn redundant_tuple_in_exception_handler(checker: &mut Checker, handlers: &[Excepthandler]) {
@@ -68,16 +26,11 @@ pub fn redundant_tuple_in_exception_handler(checker: &mut Checker, handlers: &[E
let mut generator = SourceGenerator::new();
generator.unparse_expr(elt, 0);
if let Ok(content) = generator.generate() {
match match_tuple_range(handler, checker.locator) {
Ok(range) => {
check.amend(Fix::replacement(
content,
range.location,
range.end_location,
));
}
Err(e) => error!("Failed to locate parentheses: {e}"),
}
check.amend(Fix::replacement(
content,
type_.location,
type_.end_location.unwrap(),
));
}
}
checker.add_check(check);

View File

@@ -4,7 +4,7 @@ use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location, Stmt, Stmt
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
use crate::code_gen::SourceGenerator;
use crate::python::identifiers::IDENTIFIER_REGEX;
@@ -59,7 +59,7 @@ pub fn setattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
// We can only replace a `setattr` call (which is an `Expr`) with an assignment
// (which is a `Stmt`) if the `Expr` is already being used as a `Stmt`
// (i.e., it's directly within an `StmtKind::Expr`).
if let StmtKind::Expr { value: child } = &checker.current_parent().0.node {
if let StmtKind::Expr { value: child } = &checker.current_stmt().0.node {
if expr == child.as_ref() {
let mut check = Check::new(CheckKind::SetAttrWithConstant, Range::from_located(expr));
if checker.patch(check.kind.code()) {

View File

@@ -1,7 +1,7 @@
use rustpython_ast::{Expr, ExprKind, Keyword};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// B026

View File

@@ -2,7 +2,7 @@ use itertools::Itertools;
use rustpython_ast::{Constant, Expr, ExprKind};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// B005

View File

@@ -1,7 +1,7 @@
use rustpython_ast::{Expr, ExprKind, Unaryop};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// B002

View File

@@ -1,7 +1,7 @@
use rustpython_ast::{Constant, Expr, ExprKind};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// B004

View File

@@ -5,7 +5,7 @@ use crate::ast::types::Range;
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// Identify all `ExprKind::Name` nodes in an AST.

View File

@@ -1,7 +1,7 @@
use rustpython_ast::{Expr, ExprKind};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
pub fn useless_comparison(checker: &mut Checker, expr: &Expr) {

View File

@@ -2,7 +2,7 @@ use rustpython_ast::Expr;
use crate::ast::helpers::{collect_call_paths, match_call_path};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// B005

View File

@@ -1,7 +1,7 @@
use rustpython_ast::{Constant, ExprKind, Stmt, StmtKind};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
// B018

View File

@@ -1,7 +1,7 @@
use rustpython_ast::{Expr, ExprKind, Keyword};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// B905

View File

@@ -6,10 +6,10 @@ expression: checks
RedundantTupleInExceptionHandler: ValueError
location:
row: 3
column: 8
column: 7
end_location:
row: 3
column: 19
column: 20
fix:
content: ValueError
location:

View File

@@ -7,50 +7,50 @@ expression: checks
- OSError
location:
row: 17
column: 8
column: 7
end_location:
row: 17
column: 24
column: 25
fix:
content: "OSError,"
content: OSError
location:
row: 17
column: 8
column: 7
end_location:
row: 17
column: 24
column: 25
- kind:
DuplicateHandlerException:
- MyError
location:
row: 28
column: 8
column: 7
end_location:
row: 28
column: 24
column: 25
fix:
content: "MyError,"
content: MyError
location:
row: 28
column: 8
column: 7
end_location:
row: 28
column: 24
column: 25
- kind:
DuplicateHandlerException:
- re.error
location:
row: 49
column: 8
column: 7
end_location:
row: 49
column: 26
column: 27
fix:
content: "re.error,"
content: re.error
location:
row: 49
column: 8
column: 7
end_location:
row: 49
column: 26
column: 27

View File

@@ -23,7 +23,6 @@ mod tests {
.join(path)
.as_path(),
&settings::Settings::for_rule(check_code),
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);

View File

@@ -37,7 +37,6 @@ mod tests {
.join(path)
.as_path(),
&settings::Settings::for_rule(check_code),
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);

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