Compare commits

...

11 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
71 changed files with 1943 additions and 344 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.186
rev: v0.0.188
hooks:
- id: ruff

8
Cargo.lock generated
View File

@@ -724,7 +724,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.186-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.186"
version = "0.0.188"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1901,7 +1901,7 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.186"
version = "0.0.188"
dependencies = [
"anyhow",
"clap 4.0.29",
@@ -1919,7 +1919,7 @@ dependencies = [
[[package]]
name = "ruff_macros"
version = "0.0.186"
version = "0.0.188"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -6,7 +6,7 @@ members = [
[package]
name = "ruff"
version = "0.0.186"
version = "0.0.188"
edition = "2021"
rust-version = "1.65.0"
@@ -43,7 +43,7 @@ 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.186", 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 = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }

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. -->
@@ -159,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.186
rev: v0.0.188
hooks:
- id: ruff
```
@@ -866,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.
@@ -874,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` is deprecated; use more explicit `.loc` or `.iloc` | |
| PDV008 | UseOfDotAt | Use `.loc` instead of `.at`. If speed is important, use numpy. | |
| PDV009 | UseOfDotIat | Use `.iloc` instead 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)
@@ -900,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)
@@ -1184,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/)
@@ -1198,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)
@@ -1238,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/)
@@ -1254,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.

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.186"
version = "0.0.188"
dependencies = [
"anyhow",
"clap",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.186"
version = "0.0.188"
dependencies = [
"anyhow",
"bincode",

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.186-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.186"
version = "0.0.188"
edition = "2021"
[dependencies]

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 {
@@ -204,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.186"
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}`")
@@ -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

@@ -163,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,
@@ -304,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,
@@ -326,6 +356,8 @@ pub enum CheckCodePrefix {
PGH0,
PGH00,
PGH001,
PGH002,
PGH003,
PLC,
PLC0,
PLC04,
@@ -972,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,
@@ -1369,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]
}
@@ -2096,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,
@@ -2237,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,
@@ -2259,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,
@@ -2430,6 +2721,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
CheckCodePrefix::BLE,
CheckCodePrefix::C,
CheckCodePrefix::D,
CheckCodePrefix::DTZ,
CheckCodePrefix::E,
CheckCodePrefix::EM,
CheckCodePrefix::ERA,
@@ -2438,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

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

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

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

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