Compare commits

...

23 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
77 changed files with 2130 additions and 475 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.185
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.185-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.185"
version = "0.0.188"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1901,7 +1901,7 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.185"
version = "0.0.188"
dependencies = [
"anyhow",
"clap 4.0.29",
@@ -1919,7 +1919,7 @@ dependencies = [
[[package]]
name = "ruff_macros"
version = "0.0.185"
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.185"
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.185", 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"] }

172
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.185
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.
@@ -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,6 +1203,7 @@ 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/)
@@ -1111,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)
@@ -1151,6 +1258,7 @@ 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/)
@@ -1167,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.
@@ -1488,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 = "^_$"
```
---

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.185"
version = "0.0.188"
dependencies = [
"anyhow",
"clap",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.185"
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.185-dev.0"
version = "0.0.188-dev.0"
edition = "2021"
[lib]

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

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

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

@@ -1,6 +1,6 @@
[package]
name = "ruff_dev"
version = "0.0.185"
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.185"
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

@@ -37,10 +37,10 @@ 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;
@@ -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,7 +96,7 @@ 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> {
@@ -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![],
}
}
@@ -543,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()),
},
);
}
@@ -648,7 +650,7 @@ where
),
used: None,
range: Range::from_located(alias),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
} else {
@@ -688,7 +690,7 @@ where
None
},
range: Range::from_located(alias),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
}
@@ -847,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()),
},
);
@@ -878,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()),
},
);
@@ -947,7 +949,7 @@ where
None
},
range,
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
}
@@ -1179,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);
}
@@ -1382,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()),
},
);
}
@@ -1403,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
@@ -1432,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, .. } => {
@@ -1543,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 {
@@ -1639,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
@@ -1928,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 {
@@ -1949,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
@@ -2507,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) {
@@ -2652,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()),
},
);
@@ -2687,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>) {
@@ -2718,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"))]
}
@@ -2729,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,
@@ -2966,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
@@ -3011,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;
@@ -3028,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;
@@ -3041,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;
@@ -3091,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;
@@ -3104,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()),
},
);
}

View File

@@ -2,40 +2,58 @@
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, settings: &Settings, autofix: flags::Autofix) -> Vec<Check> {
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);
for (lineno, line) in contents.lines().enumerate() {
// Enforce unnecessary coding comments (UP009).
if enforce_unnecessary_coding_comment {
if lineno < 2 {
if let Some(check) = unnecessary_coding_comment(
lineno,
line,
matches!(autofix, flags::Autofix::Enabled)
&& settings.fixable.contains(&CheckCode::UP009),
) {
checks.push(check);
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);
}
}
}
}
// Enforce line length violations (E501).
if enforce_line_too_long {
if let Some(check) = line_too_long(lineno, line, settings.line_length) {
if let Some(check) = line_too_long(index, line, settings.line_length) {
checks.push(check);
}
}
}
// Enforce newlines at end of files (W292).
if enforce_no_newline_at_end_of_file {
if let Some(check) = no_newline_at_end_of_file(contents) {
checks.push(check);
@@ -58,6 +76,7 @@ mod tests {
let check_with_max_line_length = |line_length: usize| {
check_lines(
line,
&[],
&Settings {
line_length,
..Settings::for_rule(CheckCode::E501)

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))
}
@@ -887,6 +906,8 @@ pub enum CheckKind {
BooleanPositionalValueInFunctionCall,
// pygrep-hooks
NoEval,
DeprecatedLogWarn,
BlanketTypeIgnore,
// flake8-unused-arguments
UnusedFunctionArgument(String),
UnusedMethodArgument(String),
@@ -917,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 {
@@ -925,7 +956,9 @@ impl CheckCode {
pub fn lint_source(&self) -> &'static LintSource {
match self {
CheckCode::RUF100 => &LintSource::NoQA,
CheckCode::E501 | CheckCode::W292 | CheckCode::UP009 => &LintSource::Lines,
CheckCode::E501 | CheckCode::W292 | CheckCode::UP009 | CheckCode::PGH003 => {
&LintSource::Lines
}
CheckCode::ERA001
| CheckCode::Q000
| CheckCode::Q001
@@ -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,
@@ -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,
@@ -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(),
}
}
@@ -2939,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),
])
});
@@ -2955,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

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

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

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

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

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

@@ -0,0 +1,36 @@
pub mod plugins;
#[cfg(test)]
mod tests {
use std::convert::AsRef;
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::checks::CheckCode;
use crate::linter::test_path;
use crate::settings;
#[test_case(CheckCode::DTZ001, Path::new("DTZ001.py"); "DTZ001")]
#[test_case(CheckCode::DTZ002, Path::new("DTZ002.py"); "DTZ002")]
#[test_case(CheckCode::DTZ003, Path::new("DTZ003.py"); "DTZ003")]
#[test_case(CheckCode::DTZ004, Path::new("DTZ004.py"); "DTZ004")]
#[test_case(CheckCode::DTZ005, Path::new("DTZ005.py"); "DTZ005")]
#[test_case(CheckCode::DTZ006, Path::new("DTZ006.py"); "DTZ006")]
#[test_case(CheckCode::DTZ007, Path::new("DTZ007.py"); "DTZ007")]
#[test_case(CheckCode::DTZ011, Path::new("DTZ011.py"); "DTZ011")]
#[test_case(CheckCode::DTZ012, Path::new("DTZ012.py"); "DTZ012")]
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
let mut checks = test_path(
Path::new("./resources/test/fixtures/flake8_datetimez")
.join(path)
.as_path(),
&settings::Settings::for_rule(check_code),
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);
Ok(())
}
}

View File

@@ -0,0 +1,245 @@
use rustpython_ast::{Constant, Expr, ExprKind, Keyword};
use crate::ast::helpers::{
collect_call_paths, dealias_call_path, has_non_none_keyword, is_const_none, match_call_path,
};
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
pub fn call_datetime_without_tzinfo(
checker: &mut Checker,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
location: Range,
) {
let call_path = dealias_call_path(collect_call_paths(func), &checker.import_aliases);
if !match_call_path(&call_path, "datetime", "datetime", &checker.from_imports) {
return;
}
// no args / no args unqualified
if args.len() < 8 && keywords.is_empty() {
checker.add_check(Check::new(CheckKind::CallDatetimeWithoutTzinfo, location));
return;
}
// none args
if args.len() == 8 && is_const_none(&args[7]) {
checker.add_check(Check::new(CheckKind::CallDatetimeWithoutTzinfo, location));
return;
}
// no kwargs / none kwargs
if !has_non_none_keyword(keywords, "tzinfo") {
checker.add_check(Check::new(CheckKind::CallDatetimeWithoutTzinfo, location));
}
}
/// DTZ002
pub fn call_datetime_today(checker: &mut Checker, func: &Expr, location: Range) {
let call_path = dealias_call_path(collect_call_paths(func), &checker.import_aliases);
if match_call_path(
&call_path,
"datetime.datetime",
"today",
&checker.from_imports,
) {
checker.add_check(Check::new(CheckKind::CallDatetimeToday, location));
}
}
/// DTZ003
pub fn call_datetime_utcnow(checker: &mut Checker, func: &Expr, location: Range) {
let call_path = dealias_call_path(collect_call_paths(func), &checker.import_aliases);
if match_call_path(
&call_path,
"datetime.datetime",
"utcnow",
&checker.from_imports,
) {
checker.add_check(Check::new(CheckKind::CallDatetimeUtcnow, location));
}
}
/// DTZ004
pub fn call_datetime_utcfromtimestamp(checker: &mut Checker, func: &Expr, location: Range) {
let call_path = dealias_call_path(collect_call_paths(func), &checker.import_aliases);
if match_call_path(
&call_path,
"datetime.datetime",
"utcfromtimestamp",
&checker.from_imports,
) {
checker.add_check(Check::new(
CheckKind::CallDatetimeUtcfromtimestamp,
location,
));
}
}
/// DTZ005
pub fn call_datetime_now_without_tzinfo(
checker: &mut Checker,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
location: Range,
) {
let call_path = dealias_call_path(collect_call_paths(func), &checker.import_aliases);
if !match_call_path(
&call_path,
"datetime.datetime",
"now",
&checker.from_imports,
) {
return;
}
// no args / no args unqualified
if args.is_empty() && keywords.is_empty() {
checker.add_check(Check::new(
CheckKind::CallDatetimeNowWithoutTzinfo,
location,
));
return;
}
// none args
if !args.is_empty() && is_const_none(&args[0]) {
checker.add_check(Check::new(
CheckKind::CallDatetimeNowWithoutTzinfo,
location,
));
return;
}
// wrong keywords / none keyword
if !keywords.is_empty() && !has_non_none_keyword(keywords, "tz") {
checker.add_check(Check::new(
CheckKind::CallDatetimeNowWithoutTzinfo,
location,
));
}
}
/// DTZ006
pub fn call_datetime_fromtimestamp(
checker: &mut Checker,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
location: Range,
) {
let call_path = dealias_call_path(collect_call_paths(func), &checker.import_aliases);
if !match_call_path(
&call_path,
"datetime.datetime",
"fromtimestamp",
&checker.from_imports,
) {
return;
}
// no args / no args unqualified
if args.len() < 2 && keywords.is_empty() {
checker.add_check(Check::new(CheckKind::CallDatetimeFromtimestamp, location));
return;
}
// none args
if args.len() > 1 && is_const_none(&args[1]) {
checker.add_check(Check::new(CheckKind::CallDatetimeFromtimestamp, location));
return;
}
// wrong keywords / none keyword
if !keywords.is_empty() && !has_non_none_keyword(keywords, "tz") {
checker.add_check(Check::new(CheckKind::CallDatetimeFromtimestamp, location));
}
}
/// DTZ007
pub fn call_datetime_strptime_without_zone(
checker: &mut Checker,
func: &Expr,
args: &[Expr],
location: Range,
) {
let call_path = dealias_call_path(collect_call_paths(func), &checker.import_aliases);
if !match_call_path(
&call_path,
"datetime.datetime",
"strptime",
&checker.from_imports,
) {
return;
}
let Some(ExprKind::Constant {
value: Constant::Str(format),
kind: None,
}) = args.get(1).as_ref().map(|arg| &arg.node) else {
checker.add_check(Check::new(
CheckKind::CallDatetimeStrptimeWithoutZone,
location,
));
return;
};
// Does the `strptime` call contain a format string with a timezone specifier?
if format.contains("%z") {
return;
}
let (Some(grandparent), Some(parent)) = (checker.current_expr_grandparent(), checker.current_expr_parent()) else {
checker.add_check(Check::new(
CheckKind::CallDatetimeStrptimeWithoutZone,
location,
));
return;
};
if let ExprKind::Call { keywords, .. } = &grandparent.0.node {
if let ExprKind::Attribute { attr, .. } = &parent.0.node {
// Ex) `datetime.strptime(...).astimezone()`
if attr == "astimezone" {
return;
}
// Ex) `datetime.strptime(...).replace(tzinfo=UTC)`
if attr == "replace" {
if has_non_none_keyword(keywords, "tzinfo") {
return;
}
}
}
}
checker.add_check(Check::new(
CheckKind::CallDatetimeStrptimeWithoutZone,
location,
));
}
/// DTZ011
pub fn call_date_today(checker: &mut Checker, func: &Expr, location: Range) {
let call_path = dealias_call_path(collect_call_paths(func), &checker.import_aliases);
if match_call_path(&call_path, "datetime.date", "today", &checker.from_imports) {
checker.add_check(Check::new(CheckKind::CallDateToday, location));
}
}
/// DTZ012
pub fn call_date_fromtimestamp(checker: &mut Checker, func: &Expr, location: Range) {
let call_path = dealias_call_path(collect_call_paths(func), &checker.import_aliases);
if match_call_path(
&call_path,
"datetime.date",
"fromtimestamp",
&checker.from_imports,
) {
checker.add_check(Check::new(CheckKind::CallDateFromtimestamp, location));
}
}

View File

@@ -0,0 +1,45 @@
---
source: src/flake8_datetimez/mod.rs
expression: checks
---
- kind: CallDatetimeWithoutTzinfo
location:
row: 4
column: 0
end_location:
row: 4
column: 38
fix: ~
- kind: CallDatetimeWithoutTzinfo
location:
row: 7
column: 0
end_location:
row: 7
column: 47
fix: ~
- kind: CallDatetimeWithoutTzinfo
location:
row: 10
column: 0
end_location:
row: 10
column: 37
fix: ~
- kind: CallDatetimeWithoutTzinfo
location:
row: 13
column: 0
end_location:
row: 13
column: 42
fix: ~
- kind: CallDatetimeWithoutTzinfo
location:
row: 18
column: 0
end_location:
row: 18
column: 29
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/flake8_datetimez/mod.rs
expression: checks
---
- kind: CallDatetimeToday
location:
row: 4
column: 0
end_location:
row: 4
column: 25
fix: ~
- kind: CallDatetimeToday
location:
row: 9
column: 0
end_location:
row: 9
column: 16
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/flake8_datetimez/mod.rs
expression: checks
---
- kind: CallDatetimeUtcnow
location:
row: 4
column: 0
end_location:
row: 4
column: 26
fix: ~
- kind: CallDatetimeUtcnow
location:
row: 9
column: 0
end_location:
row: 9
column: 17
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/flake8_datetimez/mod.rs
expression: checks
---
- kind: CallDatetimeUtcfromtimestamp
location:
row: 4
column: 0
end_location:
row: 4
column: 40
fix: ~
- kind: CallDatetimeUtcfromtimestamp
location:
row: 9
column: 0
end_location:
row: 9
column: 31
fix: ~

View File

@@ -0,0 +1,45 @@
---
source: src/flake8_datetimez/mod.rs
expression: checks
---
- kind: CallDatetimeNowWithoutTzinfo
location:
row: 4
column: 0
end_location:
row: 4
column: 23
fix: ~
- kind: CallDatetimeNowWithoutTzinfo
location:
row: 7
column: 0
end_location:
row: 7
column: 48
fix: ~
- kind: CallDatetimeNowWithoutTzinfo
location:
row: 10
column: 0
end_location:
row: 10
column: 27
fix: ~
- kind: CallDatetimeNowWithoutTzinfo
location:
row: 13
column: 0
end_location:
row: 13
column: 30
fix: ~
- kind: CallDatetimeNowWithoutTzinfo
location:
row: 18
column: 0
end_location:
row: 18
column: 14
fix: ~

View File

@@ -0,0 +1,45 @@
---
source: src/flake8_datetimez/mod.rs
expression: checks
---
- kind: CallDatetimeFromtimestamp
location:
row: 4
column: 0
end_location:
row: 4
column: 37
fix: ~
- kind: CallDatetimeFromtimestamp
location:
row: 7
column: 0
end_location:
row: 7
column: 64
fix: ~
- kind: CallDatetimeFromtimestamp
location:
row: 10
column: 0
end_location:
row: 10
column: 43
fix: ~
- kind: CallDatetimeFromtimestamp
location:
row: 13
column: 0
end_location:
row: 13
column: 46
fix: ~
- kind: CallDatetimeFromtimestamp
location:
row: 18
column: 0
end_location:
row: 18
column: 28
fix: ~

View File

@@ -0,0 +1,45 @@
---
source: src/flake8_datetimez/mod.rs
expression: checks
---
- kind: CallDatetimeStrptimeWithoutZone
location:
row: 4
column: 0
end_location:
row: 4
column: 53
fix: ~
- kind: CallDatetimeStrptimeWithoutZone
location:
row: 7
column: 0
end_location:
row: 7
column: 52
fix: ~
- kind: CallDatetimeStrptimeWithoutZone
location:
row: 10
column: 0
end_location:
row: 10
column: 52
fix: ~
- kind: CallDatetimeStrptimeWithoutZone
location:
row: 13
column: 0
end_location:
row: 13
column: 52
fix: ~
- kind: CallDatetimeStrptimeWithoutZone
location:
row: 29
column: 0
end_location:
row: 29
column: 43
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/flake8_datetimez/mod.rs
expression: checks
---
- kind: CallDateToday
location:
row: 4
column: 0
end_location:
row: 4
column: 21
fix: ~
- kind: CallDateToday
location:
row: 9
column: 0
end_location:
row: 9
column: 12
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/flake8_datetimez/mod.rs
expression: checks
---
- kind: CallDateFromtimestamp
location:
row: 4
column: 0
end_location:
row: 4
column: 33
fix: ~
- kind: CallDateFromtimestamp
location:
row: 9
column: 0
end_location:
row: 9
column: 24
fix: ~

View File

@@ -1,2 +1,29 @@
pub mod checks;
pub mod types;
#[cfg(test)]
mod tests {
use std::convert::AsRef;
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::checks::CheckCode;
use crate::linter::test_path;
use crate::settings;
#[test_case(CheckCode::T100, Path::new("T100.py"); "T100")]
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
let mut checks = test_path(
Path::new("./resources/test/fixtures/flake8_debugger")
.join(path)
.as_path(),
&settings::Settings::for_rule(check_code),
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);
Ok(())
}
}

View File

@@ -1,5 +1,5 @@
---
source: src/flake8_print/mod.rs
source: src/flake8_debugger/mod.rs
expression: checks
---
- kind:
@@ -16,80 +16,80 @@ expression: checks
Debugger:
Import: pdb
location:
row: 4
row: 3
column: 0
end_location:
row: 4
row: 3
column: 10
fix: ~
- kind:
Debugger:
Import: builtins.breakpoint
location:
row: 6
row: 5
column: 0
end_location:
row: 6
row: 5
column: 31
fix: ~
- kind:
Debugger:
Import: pdb.set_trace
location:
row: 7
row: 6
column: 0
end_location:
row: 7
row: 6
column: 31
fix: ~
- kind:
Debugger:
Import: celery.contrib.rdb.set_trace
location:
row: 8
row: 7
column: 0
end_location:
row: 8
row: 7
column: 40
fix: ~
- kind:
Debugger:
Import: celery.contrib.rdb
location:
row: 10
row: 9
column: 0
end_location:
row: 10
row: 9
column: 25
fix: ~
- kind:
Debugger:
Call: breakpoint
location:
row: 13
row: 11
column: 0
end_location:
row: 13
row: 11
column: 12
fix: ~
- kind:
Debugger:
Call: set_trace
location:
row: 14
row: 12
column: 0
end_location:
row: 14
row: 12
column: 4
fix: ~
- kind:
Debugger:
Call: set_trace
location:
row: 15
row: 13
column: 0
end_location:
row: 15
row: 13
column: 11
fix: ~

View File

@@ -1,30 +0,0 @@
use rustpython_ast::{Expr, ExprKind};
use crate::ast::types::Range;
use crate::checks::{Check, CheckKind};
/// Check whether a function call is a `print` or `pprint` invocation
pub fn print_call(
func: &Expr,
check_print: bool,
check_pprint: bool,
location: Range,
) -> Option<Check> {
if let ExprKind::Name { id, .. } = &func.node {
if check_print && id == "print" {
return Some(Check::new(CheckKind::PrintFound, location));
} else if check_pprint && id == "pprint" {
return Some(Check::new(CheckKind::PPrintFound, location));
}
}
if let ExprKind::Attribute { value, attr, .. } = &func.node {
if let ExprKind::Name { id, .. } = &value.node {
if check_pprint && id == "pprint" && attr == "pprint" {
return Some(Check::new(CheckKind::PPrintFound, location));
}
}
}
None
}

View File

@@ -1,4 +1,3 @@
mod checks;
pub mod plugins;
#[cfg(test)]
@@ -13,7 +12,6 @@ mod tests {
use crate::linter::test_path;
use crate::settings;
#[test_case(CheckCode::T100, Path::new("T100.py"); "T100")]
#[test_case(CheckCode::T201, Path::new("T201.py"); "T201")]
#[test_case(CheckCode::T203, Path::new("T203.py"); "T203")]
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {

View File

@@ -1,26 +1,47 @@
use log::error;
use rustpython_ast::{Expr, Stmt, StmtKind};
use rustpython_ast::{Expr, Keyword, Stmt, StmtKind};
use crate::ast::helpers::{collect_call_paths, dealias_call_path, is_const_none, match_call_path};
use crate::ast::types::Range;
use crate::autofix::helpers;
use crate::checkers::ast::Checker;
use crate::checks::CheckCode;
use crate::flake8_print::checks;
use crate::checks::{Check, CheckKind};
/// T201, T203
pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
let Some(mut check) = checks::print_call(
func,
checker.settings.enabled.contains(&CheckCode::T201),
checker.settings.enabled.contains(&CheckCode::T203),
Range::from_located(expr),
) else {
return;
pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr, keywords: &[Keyword]) {
let mut check = {
let call_path = dealias_call_path(collect_call_paths(func), &checker.import_aliases);
if match_call_path(&call_path, "", "print", &checker.from_imports) {
// If the print call has a `file=` argument (that isn't `None`, `"sys.stdout"`,
// or `"sys.stderr"`), don't trigger T201.
if let Some(keyword) = keywords
.iter()
.find(|keyword| keyword.node.arg.as_ref().map_or(false, |arg| arg == "file"))
{
if !is_const_none(&keyword.node.value) {
let call_path = collect_call_paths(&keyword.node.value);
if !(match_call_path(&call_path, "sys", "stdout", &checker.from_imports)
|| match_call_path(&call_path, "sys", "stderr", &checker.from_imports))
{
return;
}
}
}
Check::new(CheckKind::PrintFound, Range::from_located(expr))
} else if match_call_path(&call_path, "pprint", "pprint", &checker.from_imports) {
Check::new(CheckKind::PPrintFound, Range::from_located(expr))
} else {
return;
}
};
if !checker.settings.enabled.contains(check.kind.code()) {
return;
}
if checker.patch(check.kind.code()) {
let defined_by = checker.current_parent();
let defined_in = checker.current_grandparent();
let defined_by = checker.current_stmt();
let defined_in = checker.current_stmt_parent();
if matches!(defined_by.0.node, StmtKind::Expr { .. }) {
let deleted: Vec<&Stmt> = checker.deletions.iter().map(|node| node.0).collect();
match helpers::delete_stmt(

View File

@@ -4,17 +4,62 @@ expression: checks
---
- kind: PrintFound
location:
row: 1
row: 4
column: 0
end_location:
row: 1
row: 4
column: 22
fix:
content: ""
location:
row: 1
row: 4
column: 0
end_location:
row: 2
row: 5
column: 0
- kind: PrintFound
location:
row: 5
column: 0
end_location:
row: 5
column: 33
fix:
content: ""
location:
row: 5
column: 0
end_location:
row: 6
column: 0
- kind: PrintFound
location:
row: 6
column: 0
end_location:
row: 6
column: 39
fix:
content: ""
location:
row: 6
column: 0
end_location:
row: 7
column: 0
- kind: PrintFound
location:
row: 7
column: 0
end_location:
row: 7
column: 39
fix:
content: ""
location:
row: 7
column: 0
end_location:
row: 8
column: 0

View File

@@ -19,17 +19,17 @@ expression: checks
column: 0
- kind: PPrintFound
location:
row: 8
row: 7
column: 0
end_location:
row: 8
row: 7
column: 30
fix:
content: ""
location:
row: 8
row: 7
column: 0
end_location:
row: 9
row: 8
column: 0

View File

@@ -47,6 +47,7 @@ pub mod flake8_boolean_trap;
pub mod flake8_bugbear;
mod flake8_builtins;
mod flake8_comprehensions;
mod flake8_datetimez;
mod flake8_debugger;
pub mod flake8_errmsg;
mod flake8_import_conventions;

View File

@@ -126,7 +126,12 @@ pub(crate) fn check_path(
.iter()
.any(|check_code| matches!(check_code.lint_source(), LintSource::Lines))
{
checks.extend(check_lines(contents, settings, autofix));
checks.extend(check_lines(
contents,
&directives.commented_lines,
settings,
autofix,
));
}
// Enforce `noqa` directives.

View File

@@ -1,6 +1,16 @@
use anyhow::Result;
use fern;
#[macro_export]
macro_rules! one_time_warning {
($($arg:tt)*) => {
static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
if !WARNED.swap(true, std::sync::atomic::Ordering::SeqCst) {
eprintln!($($arg)*);
}
};
}
#[macro_export]
macro_rules! tell_user {
($($arg:tt)*) => {

View File

@@ -35,18 +35,27 @@ use path_absolutize::path_dedot;
/// Resolve the relevant settings strategy and defaults for the current
/// invocation.
fn resolve(config: Option<PathBuf>, overrides: &Overrides) -> Result<PyprojectDiscovery> {
fn resolve(
config: Option<&Path>,
overrides: &Overrides,
stdin_filename: Option<&Path>,
) -> Result<PyprojectDiscovery> {
if let Some(pyproject) = config {
// First priority: the user specified a `pyproject.toml` file. Use that
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
// current working directory. (This matches ESLint's behavior.)
let settings = resolve_settings(&pyproject, &Relativity::Cwd, Some(overrides))?;
let settings = resolve_settings(pyproject, &Relativity::Cwd, Some(overrides))?;
Ok(PyprojectDiscovery::Fixed(settings))
} else if let Some(pyproject) = pyproject::find_pyproject_toml(path_dedot::CWD.as_path())? {
// Second priority: find a `pyproject.toml` file in the current working path,
// and resolve all paths relative to that directory. (With
// `Strategy::Hierarchical`, we'll end up finding the "closest" `pyproject.toml`
// file for every Python file later on, so these act as the "default" settings.)
} else if let Some(pyproject) = pyproject::find_pyproject_toml(
stdin_filename
.as_ref()
.unwrap_or(&path_dedot::CWD.as_path()),
)? {
// Second priority: find a `pyproject.toml` file in either an ancestor of
// `stdin_filename` (if set) or the current working path all paths relative to
// that directory. (With `Strategy::Hierarchical`, we'll end up finding
// the "closest" `pyproject.toml` file for every Python file later on,
// so these act as the "default" settings.)
let settings = resolve_settings(&pyproject, &Relativity::Parent, Some(overrides))?;
Ok(PyprojectDiscovery::Hierarchical(settings))
} else if let Some(pyproject) = pyproject::find_user_pyproject_toml() {
@@ -85,7 +94,11 @@ fn inner_main() -> Result<ExitCode> {
// Construct the "default" settings. These are used when no `pyproject.toml`
// files are present, or files are injected from outside of the hierarchy.
let pyproject_strategy = resolve(cli.config, &overrides)?;
let pyproject_strategy = resolve(
cli.config.as_deref(),
&overrides,
cli.stdin_filename.as_deref(),
)?;
// Extract options that are included in `Settings`, but only apply at the top
// level.
@@ -207,9 +220,8 @@ fn inner_main() -> Result<ExitCode> {
// Generate lint violations.
let diagnostics = if is_stdin {
let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string());
let path = Path::new(&filename);
commands::run_stdin(&pyproject_strategy, path, autofix)?
let path = cli.stdin_filename.unwrap_or_else(|| PathBuf::from("-"));
commands::run_stdin(&pyproject_strategy, &path, autofix)?
} else {
commands::run(
&cli.files,

View File

@@ -3,7 +3,7 @@ use rustpython_ast::{Constant, Expr, ExprKind, Keyword};
use crate::ast::types::Range;
use crate::checks::{Check, CheckKind};
/// PDV002
/// PD002
pub fn inplace_argument(keywords: &[Keyword]) -> Option<Check> {
for keyword in keywords {
let arg = keyword.node.arg.as_ref()?;
@@ -27,7 +27,7 @@ pub fn inplace_argument(keywords: &[Keyword]) -> Option<Check> {
None
}
/// PDV015
/// PD015
pub fn use_of_pd_merge(func: &Expr) -> Option<Check> {
if let ExprKind::Attribute { attr, value, .. } = &func.node {
if let ExprKind::Name { id, .. } = &value.node {
@@ -42,7 +42,7 @@ pub fn use_of_pd_merge(func: &Expr) -> Option<Check> {
None
}
/// PDV901
/// PD901
pub fn assignment_to_df(targets: &[Expr]) -> Option<Check> {
if targets.len() != 1 {
return None;

View File

@@ -18,7 +18,7 @@ mod tests {
fn check_code(contents: &str, expected: &[CheckCode]) -> Result<()> {
let contents = dedent(contents);
let settings = settings::Settings::for_rules(CheckCodePrefix::PDV.codes());
let settings = settings::Settings::for_rules(CheckCodePrefix::PD.codes());
let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents);
let locator = SourceCodeLocator::new(&contents);
let directives = directives::extract_directives(
@@ -46,20 +46,20 @@ mod tests {
Ok(())
}
#[test_case("df.drop(['a'], axis=1, inplace=False)", &[]; "PDV002_pass")]
#[test_case("df.drop(['a'], axis=1, inplace=True)", &[CheckCode::PDV002]; "PDV002_fail")]
#[test_case("nas = pd.isna(val)", &[]; "PDV003_pass")]
#[test_case("nulls = pd.isnull(val)", &[CheckCode::PDV003]; "PDV003_fail")]
#[test_case("print('bah humbug')", &[]; "PDV003_allows_other_calls")]
#[test_case("not_nas = pd.notna(val)", &[]; "PDV004_pass")]
#[test_case("not_nulls = pd.notnull(val)", &[CheckCode::PDV004]; "PDV004_fail")]
#[test_case("new_df = df.loc['d':, 'A':'C']", &[]; "PDV007_pass_loc")]
#[test_case("new_df = df.iloc[[1, 3, 5], [1, 3]]", &[]; "PDV007_pass_iloc")]
#[test_case("s = df.ix[[0, 2], 'A']", &[CheckCode::PDV007]; "PDV007_fail")]
#[test_case("index = df.loc[:, ['B', 'A']]", &[]; "PDV008_pass")]
#[test_case("index = df.at[:, ['B', 'A']]", &[CheckCode::PDV008]; "PDV008_fail")]
#[test_case("index = df.iloc[:, 1:3]", &[]; "PDV009_pass")]
#[test_case("index = df.iat[:, 1:3]", &[CheckCode::PDV009]; "PDV009_fail")]
#[test_case("df.drop(['a'], axis=1, inplace=False)", &[]; "PD002_pass")]
#[test_case("df.drop(['a'], axis=1, inplace=True)", &[CheckCode::PD002]; "PD002_fail")]
#[test_case("nas = pd.isna(val)", &[]; "PD003_pass")]
#[test_case("nulls = pd.isnull(val)", &[CheckCode::PD003]; "PD003_fail")]
#[test_case("print('bah humbug')", &[]; "PD003_allows_other_calls")]
#[test_case("not_nas = pd.notna(val)", &[]; "PD004_pass")]
#[test_case("not_nulls = pd.notnull(val)", &[CheckCode::PD004]; "PD004_fail")]
#[test_case("new_df = df.loc['d':, 'A':'C']", &[]; "PD007_pass_loc")]
#[test_case("new_df = df.iloc[[1, 3, 5], [1, 3]]", &[]; "PD007_pass_iloc")]
#[test_case("s = df.ix[[0, 2], 'A']", &[CheckCode::PD007]; "PD007_fail")]
#[test_case("index = df.loc[:, ['B', 'A']]", &[]; "PD008_pass")]
#[test_case("index = df.at[:, ['B', 'A']]", &[CheckCode::PD008]; "PD008_fail")]
#[test_case("index = df.iloc[:, 1:3]", &[]; "PD009_pass")]
#[test_case("index = df.iat[:, 1:3]", &[CheckCode::PD009]; "PD009_fail")]
#[test_case(r#"table = df.pivot_table(
df,
values='D',
@@ -68,42 +68,42 @@ mod tests {
aggfunc=np.sum,
fill_value=0
)
"#, &[]; "PDV010_pass")]
"#, &[]; "PD010_pass")]
#[test_case(r#"table = pd.pivot(
df,
index='foo',
columns='bar',
values='baz'
)
"#, &[CheckCode::PDV010]; "PDV010_fail_pivot")]
#[test_case("result = df.to_array()", &[]; "PDV011_pass_to_array")]
#[test_case("result = df.array", &[]; "PDV011_pass_array")]
#[test_case("result = df.values", &[CheckCode::PDV011]; "PDV011_fail_values")]
// TODO: Check that the attribute access is NOT a method call
// #[test_case("result = {}.values()", &[]; "PDV011_pass_values_call")]
#[test_case("result = values", &[]; "PDV011_pass_node_name")]
#[test_case("employees = pd.read_csv(input_file)", &[]; "PDV012_pass_read_csv")]
#[test_case("employees = pd.read_table(input_file)", &[CheckCode::PDV012]; "PDV012_fail_read_table")]
#[test_case("employees = read_table", &[]; "PDV012_node_Name_pass")]
"#, &[CheckCode::PD010]; "PD010_fail_pivot")]
#[test_case("result = df.to_array()", &[]; "PD011_pass_to_array")]
#[test_case("result = df.array", &[]; "PD011_pass_array")]
#[test_case("result = df.values", &[CheckCode::PD011]; "PD011_fail_values")]
// TODO(edgarrmondragon): Check that the attribute access is NOT a method call.
// #[test_case("result = {}.values()", &[]; "PD011_pass_values_call")]
#[test_case("result = values", &[]; "PD011_pass_node_name")]
#[test_case("employees = pd.read_csv(input_file)", &[]; "PD012_pass_read_csv")]
#[test_case("employees = pd.read_table(input_file)", &[CheckCode::PD012]; "PD012_fail_read_table")]
#[test_case("employees = read_table", &[]; "PD012_node_Name_pass")]
#[test_case(r#"table = df.melt(
id_vars='airline',
value_vars=['ATL', 'DEN', 'DFW'],
value_name='airline delay'
)
"#, &[]; "PDV013_pass")]
#[test_case("table = df.stack(level=-1, dropna=True)", &[CheckCode::PDV013]; "PDV013_fail_stack")]
"#, &[]; "PD013_pass")]
#[test_case("table = df.stack(level=-1, dropna=True)", &[CheckCode::PD013]; "PD013_fail_stack")]
#[test_case("df1.merge(df2)", &[]; "PD015_pass_merge_on_dataframe")]
#[test_case("df1.merge(df2, 'inner')", &[]; "PD015_pass_merge_on_dataframe_with_multiple_args")]
#[test_case("pd.merge(df1, df2)", &[CheckCode::PDV015]; "PD015_fail_merge_on_pandas_object")]
#[test_case("pd.merge(df1, df2)", &[CheckCode::PD015]; "PD015_fail_merge_on_pandas_object")]
#[test_case(
"pd.to_datetime(timestamp * 10 ** 9).strftime('%Y-%m-%d %H:%M:%S.%f')",
&[];
"PD015_pass_other_pd_function"
)]
#[test_case("employees = pd.DataFrame(employee_dict)", &[]; "PDV901_pass_non_df")]
#[test_case("employees_df = pd.DataFrame(employee_dict)", &[]; "PDV901_pass_part_df")]
#[test_case("my_function(df=data)", &[]; "PDV901_pass_df_param")]
#[test_case("df = pd.DataFrame()", &[CheckCode::PDV901]; "PDV901_fail_df_var")]
#[test_case("employees = pd.DataFrame(employee_dict)", &[]; "PD901_pass_non_df")]
#[test_case("employees_df = pd.DataFrame(employee_dict)", &[]; "PD901_pass_part_df")]
#[test_case("my_function(df=data)", &[]; "PD901_pass_df_param")]
#[test_case("df = pd.DataFrame()", &[CheckCode::PD901]; "PD901_fail_df_var")]
fn test_pandas_vet(code: &str, expected: &[CheckCode]) -> Result<()> {
check_code(code, expected)?;
Ok(())

View File

@@ -26,7 +26,7 @@ pub fn line_too_long(lineno: usize, line: &str, max_line_length: usize) -> Optio
// Do not enforce the line length for commented lines that end with a URL
// or contain only a single word.
if first == "#" || chunks.last().map_or(true, |c| URL_REGEX.is_match(c)) {
if first == "#" && chunks.last().map_or(true, |c| URL_REGEX.is_match(c)) {
return None;
}

View File

@@ -46,4 +46,15 @@ expression: checks
row: 43
column: 105
fix: ~
- kind:
LineTooLong:
- 129
- 88
location:
row: 61
column: 88
end_location:
row: 61
column: 129
fix: ~

View File

@@ -1,4 +1,4 @@
pub mod checks;
pub mod plugins;
#[cfg(test)]
mod tests {
@@ -14,6 +14,9 @@ mod tests {
#[test_case(CheckCode::PGH001, Path::new("PGH001_0.py"); "PGH001_0")]
#[test_case(CheckCode::PGH001, Path::new("PGH001_1.py"); "PGH001_1")]
#[test_case(CheckCode::PGH002, Path::new("PGH002_0.py"); "PGH002_0")]
#[test_case(CheckCode::PGH002, Path::new("PGH002_1.py"); "PGH002_1")]
#[test_case(CheckCode::PGH003, Path::new("PGH003_0.py"); "PGH003_0")]
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
let mut checks = test_path(

View File

@@ -0,0 +1,22 @@
use once_cell::sync::Lazy;
use regex::Regex;
use rustpython_ast::Location;
use crate::ast::types::Range;
use crate::checks::{Check, CheckKind};
static BLANKET_TYPE_IGNORE_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"# type:? *ignore($|\s)").unwrap());
/// PGH003 - use of blanket type ignore comments
pub fn blanket_type_ignore(lineno: usize, line: &str) -> Option<Check> {
BLANKET_TYPE_IGNORE_REGEX.find(line).map(|m| {
Check::new(
CheckKind::BlanketTypeIgnore,
Range {
location: Location::new(lineno + 1, m.start()),
end_location: Location::new(lineno + 1, m.end()),
},
)
})
}

View File

@@ -0,0 +1,19 @@
use rustpython_ast::Expr;
use crate::ast::helpers::{collect_call_paths, dealias_call_path, match_call_path};
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// PGH002 - deprecated use of logging.warn
pub fn deprecated_log_warn(checker: &mut Checker, func: &Expr) {
let call_path = dealias_call_path(collect_call_paths(func), &checker.import_aliases);
if call_path == ["log", "warn"]
|| match_call_path(&call_path, "logging", "warn", &checker.from_imports)
{
checker.add_check(Check::new(
CheckKind::DeprecatedLogWarn,
Range::from_located(func),
));
}
}

View File

@@ -0,0 +1,7 @@
pub use blanket_type_ignore::blanket_type_ignore;
pub use deprecated_log_warn::deprecated_log_warn;
pub use no_eval::no_eval;
mod blanket_type_ignore;
mod deprecated_log_warn;
mod no_eval;

View File

@@ -4,6 +4,7 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
/// PGH001 - no eval
pub fn no_eval(checker: &mut Checker, func: &Expr) {
let ExprKind::Name { id, .. } = &func.node else {
return;

View File

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

View File

@@ -0,0 +1,37 @@
---
source: src/pygrep_hooks/mod.rs
expression: checks
---
- kind: DeprecatedLogWarn
location:
row: 4
column: 0
end_location:
row: 4
column: 12
fix: ~
- kind: DeprecatedLogWarn
location:
row: 5
column: 0
end_location:
row: 5
column: 8
fix: ~
- kind: DeprecatedLogWarn
location:
row: 6
column: 0
end_location:
row: 6
column: 4
fix: ~
- kind: DeprecatedLogWarn
location:
row: 15
column: 4
end_location:
row: 15
column: 8
fix: ~

View File

@@ -0,0 +1,29 @@
---
source: src/pygrep_hooks/mod.rs
expression: checks
---
- kind: BlanketTypeIgnore
location:
row: 1
column: 7
end_location:
row: 1
column: 21
fix: ~
- kind: BlanketTypeIgnore
location:
row: 2
column: 7
end_location:
row: 2
column: 20
fix: ~
- kind: BlanketTypeIgnore
location:
row: 3
column: 7
end_location:
row: 3
column: 20
fix: ~

View File

@@ -65,8 +65,8 @@ pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Lo
if checker.patch(check.kind.code()) {
let deleted: Vec<&Stmt> = checker.deletions.iter().map(|node| node.0).collect();
let defined_by = checker.current_parent();
let defined_in = checker.current_grandparent();
let defined_by = checker.current_stmt();
let defined_in = checker.current_stmt_parent();
match fixes::remove_unnecessary_future_import(
checker.locator,
&removable_index,

View File

@@ -14,8 +14,8 @@ pub fn useless_metaclass_type(checker: &mut Checker, stmt: &Stmt, value: &Expr,
};
if checker.patch(check.kind.code()) {
let deleted: Vec<&Stmt> = checker.deletions.iter().map(|node| node.0).collect();
let defined_by = checker.current_parent();
let defined_in = checker.current_grandparent();
let defined_by = checker.current_stmt();
let defined_in = checker.current_stmt_parent();
match helpers::delete_stmt(
defined_by.0,
defined_in.map(|node| node.0),

View File

@@ -38,7 +38,7 @@ pub struct Options {
value_type = "Regex",
example = r#"
# Only ignore variables named "_".
dummy_variable_rgx = "^_$"
dummy-variable-rgx = "^_$"
"#
)]
pub dummy_variable_rgx: Option<String>,