Compare commits

...

45 Commits

Author SHA1 Message Date
Charlie Marsh
51bda28a7d Bump version to 0.0.192 2022-12-22 17:31:31 -05:00
Reiner Gerecke
cc26051b7a Implement "datetime.UTC alias" check from pyupgrade (#1341) 2022-12-22 17:21:36 -05:00
Charlie Marsh
3ac5a9aa31 Respect --force-exclude for files passed via stdin (#1342) 2022-12-22 16:40:15 -05:00
Charlie Marsh
451047c30d Exclude directly-passed files nested in excluded subdirectories 2022-12-22 15:08:11 -05:00
Charlie Marsh
6907df489b Extend false-positive list for flake8-boolean-trap (#1338) 2022-12-22 10:56:04 -05:00
Charlie Marsh
970f882b03 Set force-exclude for pre-commit in README (#1337) 2022-12-22 10:51:20 -05:00
Charlie Marsh
3eff9a2860 Allow unittest methods in flake8-boolean-trap (#1333) 2022-12-22 08:40:22 -05:00
Charlie Marsh
a4a24a0ef3 Add some more repositories to the user list (#1328) 2022-12-21 22:16:47 -05:00
Charlie Marsh
48e3c046b0 Fix integration tests 2022-12-21 21:25:37 -05:00
Charlie Marsh
03e4f5be8a Bump version to 0.0.191 2022-12-21 21:16:21 -05:00
Charlie Marsh
99657b7d92 Implement E401 ("multiple imports on one line") (#1326) 2022-12-21 21:15:57 -05:00
Charlie Marsh
40377aa1fc Move number of errors to the bottom of the output summary (#1325) 2022-12-21 21:04:26 -05:00
Charlie Marsh
2a37017e8c Add src to Settings hash 2022-12-21 21:01:20 -05:00
Charlie Marsh
ff66d08cef Run generate-options 2022-12-21 20:58:14 -05:00
Charlie Marsh
dad8035eef Support shell expansion in src field (#1324) 2022-12-21 20:57:20 -05:00
Charlie Marsh
bf5fec342c Support shell expansion in extend paths (#1323) 2022-12-21 20:46:38 -05:00
Charlie Marsh
66a6c81ebf Infer package roots when running via stdin (#1321) 2022-12-21 20:30:10 -05:00
Charlie Marsh
5c70f5044b Improve debug logging in flake8-to-ruff (#1320) 2022-12-21 20:05:48 -05:00
Charlie Marsh
953d141ab2 Support code redirects in flake8-to-ruff (#1318) 2022-12-21 19:31:20 -05:00
Charlie Marsh
07dba46039 Extract line length from pyproject.toml Black section (#1317) 2022-12-21 19:05:18 -05:00
Ran Benita
3b02da9d7b Fix false positive DTZ001 on datetime(2000, 1, 1, 0, 0, 0, 0, utc) (#1308) 2022-12-21 19:03:36 -05:00
Charlie Marsh
20234c6156 Bump version to 0.0.190 2022-12-21 16:01:48 -05:00
Charlie Marsh
de767cc026 Avoid used-prior-global-declaration false-positives in f-strings (#1314) 2022-12-21 14:34:09 -05:00
Charlie Marsh
ce1663d302 Allow overriding cache location via RUFF_CACHE_DIR (#1312) 2022-12-21 14:24:10 -05:00
Charlie Marsh
f40e4bcd14 Avoid flagging RUF100 as a RUF100 violation (#1305) 2022-12-20 17:40:10 -05:00
Charlie Marsh
e7d40d435f Avoid F821 false positives for Mypy extensions (#1304) 2022-12-20 16:29:33 -05:00
Charlie Marsh
ef8fe31c0c Bump version to 0.0.189 2022-12-20 13:26:17 -05:00
Charlie Marsh
226f682c99 Avoid DTZ007 false-positives for non-string arguments (#1300) 2022-12-20 13:20:53 -05:00
Hannes Käufler
468ffd29fb [Stylistic/non-functional] Use an r# format string to make json easier to read (#1299) 2022-12-20 12:55:21 -05:00
Charlie Marsh
a61126ab23 Run generate-options 2022-12-19 23:19:05 -05:00
Charlie Marsh
54c7c25861 Revert test changes to setup.py 2022-12-19 20:09:47 -05:00
Charlie Marsh
eff7700d92 Add --force-exclude setting to force exclusions with pre-commit (#1295) 2022-12-19 20:08:59 -05:00
Charlie Marsh
8934f6938d Avoid RET504 errors for intermediary function calls (#1294) 2022-12-19 19:48:09 -05:00
Charlie Marsh
8f0fc3033a Update Arg section checking to match latest pydocstyle (#1293) 2022-12-19 16:39:42 -05:00
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
115 changed files with 3400 additions and 631 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.192
hooks:
- id: ruff

20
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.192-dev.0"
dependencies = [
"anyhow",
"clap 4.0.29",
@@ -735,6 +735,8 @@ dependencies = [
"rustc-hash",
"serde",
"serde_json",
"strum",
"strum_macros",
"toml",
]
@@ -1845,7 +1847,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.186"
version = "0.0.192"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1888,6 +1890,7 @@ dependencies = [
"rustpython-parser",
"serde",
"serde_json",
"shellexpand",
"strum",
"strum_macros",
"test-case",
@@ -1901,7 +1904,7 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.186"
version = "0.0.192"
dependencies = [
"anyhow",
"clap 4.0.29",
@@ -1919,7 +1922,7 @@ dependencies = [
[[package]]
name = "ruff_macros"
version = "0.0.186"
version = "0.0.192"
dependencies = [
"proc-macro2",
"quote",
@@ -2114,6 +2117,15 @@ dependencies = [
"serde",
]
[[package]]
name = "shellexpand"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd1c7ddea665294d484c39fd0c0d2b7e35bbfe10035c5fe1854741a57f6880e1"
dependencies = [
"dirs 4.0.0",
]
[[package]]
name = "similar"
version = "2.2.1"

View File

@@ -6,7 +6,7 @@ members = [
[package]
name = "ruff"
version = "0.0.186"
version = "0.0.192"
edition = "2021"
rust-version = "1.65.0"
@@ -43,13 +43,14 @@ 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.192", path = "ruff_macros" }
rustc-hash = { version = "1.1.0" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "c01f014b1269eedcf4bdb45d5fbc62ae2beecf31" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
shellexpand = { version = "3.0.0" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }
textwrap = { version = "0.16.0" }

107
README.md
View File

@@ -39,9 +39,11 @@ Ruff is extremely actively developed and used in major open-source projects like
- [Bokeh](https://github.com/bokeh/bokeh)
- [Zulip](https://github.com/zulip/zulip)
- [Pydantic](https://github.com/pydantic/pydantic)
- [Saleor](https://github.com/saleor/saleor)
- [Hatch](https://github.com/pypa/hatch)
- [Jupyter Server](https://github.com/jupyter-server/jupyter_server)
- [Jupyter](https://github.com/jupyter-server/jupyter_server)
- [Synapse](https://github.com/matrix-org/synapse)
- [Ibis](https://github.com/ibis-project/ibis)
- [Saleor](https://github.com/saleor/saleor)
Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster).
@@ -94,8 +96,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. -->
@@ -157,11 +160,13 @@ ruff path/to/code/ --watch
Ruff also works with [pre-commit](https://pre-commit.com):
```yaml
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.186
hooks:
- id: ruff
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.0.192'
hooks:
- id: ruff
# Respect `exclude` and `extend-exclude` settings.
args: ["--force-exclude"]
```
## Configuration
@@ -516,6 +521,7 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| E401 | MultipleImportsOnOneLine | Multiple imports on one line | |
| E402 | ModuleImportNotAtTopOfFile | Module level import not at top of file | |
| E501 | LineTooLong | Line too long (89 > 88 characters) | |
| E711 | NoneComparison | Comparison to `None` should be `cond is None` | 🛠 |
@@ -622,6 +628,7 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
| UP014 | ConvertNamedTupleFunctionalToClass | Convert `...` from `NamedTuple` functional to class syntax | 🛠 |
| UP015 | RedundantOpenModes | Unnecessary open mode parameters | 🛠 |
| UP016 | RemoveSixCompat | Unnecessary `six` compatibility usage | 🛠 |
| UP017 | DatetimeTimezoneUTC | Use `datetime.UTC` alias | 🛠 |
### pep8-naming (N)
@@ -866,6 +873,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 +897,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 +923,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 +1209,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,8 +1224,8 @@ 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)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (16/33)
- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (17/33)
- [`yesqa`](https://github.com/asottile/yesqa)
Note that, in some cases, Ruff uses different error code prefixes than would be found in the
@@ -1238,6 +1264,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,8 +1281,8 @@ 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
implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (16/33).
[`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/) (17/33).
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.
@@ -1611,7 +1638,8 @@ exclude = [".venv"]
#### [`extend`](#extend)
A path to a local `pyproject.toml` file to merge into this configuration.
A path to a local `pyproject.toml` file to merge into this configuration. User home
directory and environment variables will be expanded.
To resolve the current `pyproject.toml` file, Ruff will first resolve this base
configuration file, then merge in any properties defined in the current configuration
@@ -1744,6 +1772,30 @@ fixable = ["E", "F"]
---
#### [`force-exclude`](#force-exclude)
Whether to enforce `exclude` and `extend-exclude` patterns, even for paths that are
passed to Ruff explicitly. Typically, Ruff will lint any paths passed in directly, even
if they would typically be excluded. Setting `force-exclude = true` will cause Ruff to
respect these exclusions unequivocally.
This is useful for [`pre-commit`](https://pre-commit.com/), which explicitly passes all
changed files to the [`ruff-pre-commit`](https://github.com/charliermarsh/ruff-pre-commit)
plugin, regardless of whether they're marked as excluded by Ruff's own settings.
**Default value**: `false`
**Type**: `bool`
**Example usage**:
```toml
[tool.ruff]
force-exclude = true
```
---
#### [`format`](#format)
The style in which violation messages should be formatted: `"text"` (default),
@@ -1925,7 +1977,8 @@ when resolving imports, `my_package.foo` is considered a first-party import.
This field supports globs. For example, if you have a series of Python packages in
a `python_modules` directory, `src = ["python_modules/*"]` would expand to incorporate
all of the packages in that directory.
all of the packages in that directory. User home directory and environment variables
will also be expanded.
**Default value**: `["."]`

View File

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

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.186-dev.0"
version = "0.0.192-dev.0"
edition = "2021"
[lib]
@@ -16,6 +16,8 @@ ruff = { path = "..", default-features = false }
rustc-hash = { version = "1.1.0" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }
toml = { version = "0.5.9" }
[dev-dependencies]

View File

@@ -0,0 +1,65 @@
[build-system]
requires = [
# The minimum setuptools version is specific to the PEP 517 backend,
# and may be stricter than the version required in `setup.cfg`
"setuptools>=40.6.0,!=60.9.0",
"wheel",
# Must be kept in sync with the `install_requirements` in `setup.cfg`
"cffi>=1.12; platform_python_implementation != 'PyPy'",
"setuptools-rust>=0.11.4",
]
build-backend = "setuptools.build_meta"
[tool.black]
line-length = 79
target-version = ["py36"]
[tool.pytest.ini_options]
addopts = "-r s --capture=no --strict-markers --benchmark-disable"
markers = [
"skip_fips: this test is not executed in FIPS mode",
"supported: parametrized test requiring only_if and skip_message",
]
[tool.mypy]
show_error_codes = true
check_untyped_defs = true
no_implicit_reexport = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_unused_configs = true
strict_equality = true
[[tool.mypy.overrides]]
module = [
"pretend"
]
ignore_missing_imports = true
[tool.coverage.run]
branch = true
relative_files = true
source = [
"cryptography",
"tests/",
]
[tool.coverage.paths]
source = [
"src/cryptography",
"*.tox/*/lib*/python*/site-packages/cryptography",
"*.tox\\*\\Lib\\site-packages\\cryptography",
"*.tox/pypy/site-packages/cryptography",
]
tests =[
"tests/",
"*tests\\",
]
[tool.coverage.report]
exclude_lines = [
"@abc.abstractmethod",
"@abc.abstractproperty",
"@typing.overload",
"if typing.TYPE_CHECKING",
]

View File

@@ -0,0 +1,91 @@
[metadata]
name = cryptography
version = attr: cryptography.__version__
description = cryptography is a package which provides cryptographic recipes and primitives to Python developers.
long_description = file: README.rst
long_description_content_type = text/x-rst
license = BSD-3-Clause OR Apache-2.0
url = https://github.com/pyca/cryptography
author = The Python Cryptographic Authority and individual contributors
author_email = cryptography-dev@python.org
project_urls =
Documentation=https://cryptography.io/
Source=https://github.com/pyca/cryptography/
Issues=https://github.com/pyca/cryptography/issues
Changelog=https://cryptography.io/en/latest/changelog/
classifiers =
Development Status :: 5 - Production/Stable
Intended Audience :: Developers
License :: OSI Approved :: Apache Software License
License :: OSI Approved :: BSD License
Natural Language :: English
Operating System :: MacOS :: MacOS X
Operating System :: POSIX
Operating System :: POSIX :: BSD
Operating System :: POSIX :: Linux
Operating System :: Microsoft :: Windows
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy
Topic :: Security :: Cryptography
[options]
python_requires = >=3.6
include_package_data = True
zip_safe = False
package_dir =
=src
packages = find:
# `install_requires` must be kept in sync with `pyproject.toml`
install_requires =
cffi >=1.12
[options.packages.find]
where = src
exclude =
_cffi_src
_cffi_src.*
[options.extras_require]
test =
pytest>=6.2.0
pytest-benchmark
pytest-cov
pytest-subtests
pytest-xdist
pretend
iso8601
pytz
hypothesis>=1.11.4,!=3.79.2
docs =
sphinx >= 1.6.5,!=1.8.0,!=3.1.0,!=3.1.1,!=5.2.0,!=5.2.0.post0
sphinx_rtd_theme
docstest =
pyenchant >= 1.6.11
twine >= 1.12.0
sphinxcontrib-spelling >= 4.0.1
sdist =
setuptools_rust >= 0.11.4
pep8test =
black
flake8
flake8-import-order
pep8-naming
# This extra is for OpenSSH private keys that use bcrypt KDF
# Versions: v3.1.3 - ignore_few_rounds, v3.1.5 - abi3
ssh =
bcrypt >= 3.1.5
[flake8]
ignore = E203,E211,W503,W504,N818
exclude = .tox,*.egg,.git,_build,.hypothesis
select = E,W,F,N,I
application-import-names = cryptography,cryptography_vectors,tests

View File

@@ -0,0 +1,32 @@
//! Extract Black configuration settings from a pyproject.toml.
use std::path::Path;
use anyhow::Result;
use ruff::settings::types::PythonVersion;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct Black {
#[serde(alias = "line-length", alias = "line_length")]
pub line_length: Option<usize>,
#[serde(alias = "target-version", alias = "target_version")]
pub target_version: Option<Vec<PythonVersion>>,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Tools {
black: Option<Black>,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Pyproject {
tool: Option<Tools>,
}
pub fn parse_black_options<P: AsRef<Path>>(path: P) -> Result<Option<Black>> {
let contents = std::fs::read_to_string(path)?;
Ok(toml::from_str::<Pyproject>(&contents)?
.tool
.and_then(|tool| tool.black))
}

View File

@@ -11,13 +11,20 @@ use ruff::{
pep8_naming,
};
use crate::black::Black;
use crate::plugin::Plugin;
use crate::{parser, plugin};
pub fn convert(
flake8: &HashMap<String, Option<String>>,
config: &HashMap<String, HashMap<String, Option<String>>>,
black: Option<&Black>,
plugins: Option<Vec<Plugin>>,
) -> Result<Pyproject> {
// Extract the Flake8 section.
let flake8 = config
.get("flake8")
.expect("Unable to find flake8 section in INI file");
// Extract all referenced check code prefixes, to power plugin inference.
let mut referenced_codes: BTreeSet<CheckCodePrefix> = BTreeSet::default();
for (key, value) in flake8 {
@@ -54,10 +61,18 @@ pub fn convert(
plugin::resolve_select(
flake8,
&plugins.unwrap_or_else(|| {
plugin::infer_plugins_from_options(flake8)
.into_iter()
.chain(plugin::infer_plugins_from_codes(&referenced_codes))
.collect()
let from_options = plugin::infer_plugins_from_options(flake8);
if !from_options.is_empty() {
eprintln!("Inferred plugins from settings: {from_options:#?}");
}
let from_codes = plugin::infer_plugins_from_codes(&referenced_codes);
if !from_codes.is_empty() {
eprintln!(
"Inferred plugins from referenced check codes: {:#?}",
from_codes
);
}
from_options.into_iter().chain(from_codes).collect()
}),
)
});
@@ -236,6 +251,19 @@ pub fn convert(
options.pep8_naming = Some(pep8_naming);
}
// Extract any settings from the existing `pyproject.toml`.
if let Some(black) = black {
if let Some(line_length) = &black.line_length {
options.line_length = Some(*line_length);
}
if let Some(target_version) = &black.target_version {
if let Some(target_version) = target_version.iter().min() {
options.target_version = Some(*target_version);
}
}
}
// Create the pyproject.toml.
Ok(Pyproject::new(options))
}
@@ -255,7 +283,11 @@ mod tests {
#[test]
fn it_converts_empty() -> Result<()> {
let actual = convert(&HashMap::from([]), None)?;
let actual = convert(
&HashMap::from([("flake8".to_string(), HashMap::default())]),
None,
None,
)?;
let expected = Pyproject::new(Options {
allowed_confusables: None,
dummy_variable_rgx: None,
@@ -268,6 +300,7 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
@@ -302,7 +335,11 @@ mod tests {
#[test]
fn it_converts_dashes() -> Result<()> {
let actual = convert(
&HashMap::from([("max-line-length".to_string(), Some("100".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("max-line-length".to_string(), Some("100".to_string()))]),
)]),
None,
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
@@ -317,6 +354,7 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: Some(100),
@@ -351,7 +389,11 @@ mod tests {
#[test]
fn it_converts_underscores() -> Result<()> {
let actual = convert(
&HashMap::from([("max_line_length".to_string(), Some("100".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("max_line_length".to_string(), Some("100".to_string()))]),
)]),
None,
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
@@ -366,6 +408,7 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: Some(100),
@@ -400,7 +443,11 @@ mod tests {
#[test]
fn it_ignores_parse_errors() -> Result<()> {
let actual = convert(
&HashMap::from([("max_line_length".to_string(), Some("abc".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("max_line_length".to_string(), Some("abc".to_string()))]),
)]),
None,
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
@@ -415,6 +462,7 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
@@ -449,7 +497,11 @@ mod tests {
#[test]
fn it_converts_plugin_options() -> Result<()> {
let actual = convert(
&HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
)]),
None,
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
@@ -464,6 +516,7 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
@@ -504,9 +557,13 @@ mod tests {
fn it_converts_docstring_conventions() -> Result<()> {
let actual = convert(
&HashMap::from([(
"docstring-convention".to_string(),
Some("numpy".to_string()),
"flake8".to_string(),
HashMap::from([(
"docstring-convention".to_string(),
Some("numpy".to_string()),
)]),
)]),
None,
Some(vec![Plugin::Flake8Docstrings]),
)?;
let expected = Pyproject::new(Options {
@@ -521,6 +578,7 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
@@ -591,7 +649,11 @@ mod tests {
#[test]
fn it_infers_plugins_if_omitted() -> Result<()> {
let actual = convert(
&HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
&HashMap::from([(
"flake8".to_string(),
HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
)]),
None,
None,
)?;
let expected = Pyproject::new(Options {
@@ -606,6 +668,7 @@ mod tests {
fix: None,
fixable: None,
format: None,
force_exclude: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,

View File

@@ -11,6 +11,7 @@
clippy::too_many_lines
)]
pub mod black;
pub mod converter;
mod parser;
pub mod plugin;

View File

@@ -17,6 +17,7 @@ use std::path::PathBuf;
use anyhow::Result;
use clap::Parser;
use configparser::ini::Ini;
use flake8_to_ruff::black::parse_black_options;
use flake8_to_ruff::converter;
use flake8_to_ruff::plugin::Plugin;
@@ -26,10 +27,14 @@ use flake8_to_ruff::plugin::Plugin;
long_about = None
)]
struct Cli {
/// Path to the Flake8 configuration file (e.g., 'setup.cfg', 'tox.ini', or
/// '.flake8').
/// Path to the Flake8 configuration file (e.g., `setup.cfg`, `tox.ini`, or
/// `.flake8`).
#[arg(required = true)]
file: PathBuf,
/// Optional path to a `pyproject.toml` file, used to ensure compatibility
/// with Black.
#[arg(long)]
pyproject: Option<PathBuf>,
/// List of plugins to enable.
#[arg(long, value_delimiter = ',')]
plugin: Option<Vec<Plugin>>,
@@ -43,13 +48,15 @@ fn main() -> Result<()> {
ini.set_multiline(true);
let config = ini.load(cli.file).map_err(|msg| anyhow::anyhow!(msg))?;
// Extract the Flake8 section.
let flake8 = config
.get("flake8")
.expect("Unable to find flake8 section in INI file");
// Read the pyproject.toml file.
let black = cli
.pyproject
.map(parse_black_options)
.transpose()?
.flatten();
// Create the pyproject.toml.
let pyproject = converter::convert(flake8, cli.plugin)?;
// Create Ruff's pyproject.toml section.
let pyproject = converter::convert(&config, black.as_ref(), cli.plugin)?;
println!("{}", toml::to_string_pretty(&pyproject)?);
Ok(())

View File

@@ -3,6 +3,7 @@ use std::str::FromStr;
use anyhow::{bail, Result};
use once_cell::sync::Lazy;
use regex::Regex;
use ruff::checks::PREFIX_REDIRECTS;
use ruff::checks_gen::CheckCodePrefix;
use ruff::settings::types::PatternPrefixPair;
use rustc_hash::FxHashMap;
@@ -18,7 +19,9 @@ pub fn parse_prefix_codes(value: &str) -> Vec<CheckCodePrefix> {
if code.is_empty() {
continue;
}
if let Ok(code) = CheckCodePrefix::from_str(code) {
if let Some(code) = PREFIX_REDIRECTS.get(code) {
codes.push(code.clone());
} else if let Ok(code) = CheckCodePrefix::from_str(code) {
codes.push(code);
} else {
eprintln!("Unsupported prefix code: {code}");
@@ -83,16 +86,22 @@ impl State {
fn parse(&self) -> Vec<PatternPrefixPair> {
let mut codes: Vec<PatternPrefixPair> = vec![];
for code in &self.codes {
match CheckCodePrefix::from_str(code) {
Ok(code) => {
for filename in &self.filenames {
codes.push(PatternPrefixPair {
pattern: filename.clone(),
prefix: code.clone(),
});
}
if let Some(code) = PREFIX_REDIRECTS.get(code.as_str()) {
for filename in &self.filenames {
codes.push(PatternPrefixPair {
pattern: filename.clone(),
prefix: code.clone(),
});
}
Err(_) => eprintln!("Skipping unrecognized prefix: {code}"),
} else if let Ok(code) = CheckCodePrefix::from_str(code) {
for filename in &self.filenames {
codes.push(PatternPrefixPair {
pattern: filename.clone(),
prefix: code.clone(),
});
}
} else {
eprintln!("Unsupported prefix code: {code}");
}
}
codes

View File

@@ -1,10 +1,11 @@
use std::collections::{BTreeSet, HashMap};
use std::fmt;
use std::str::FromStr;
use anyhow::anyhow;
use ruff::checks_gen::CheckCodePrefix;
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Plugin {
Flake8Annotations,
Flake8Bandit,
@@ -12,6 +13,7 @@ pub enum Plugin {
Flake8Bugbear,
Flake8Builtins,
Flake8Comprehensions,
Flake8Datetimez,
Flake8Debugger,
Flake8Docstrings,
Flake8ErrMsg,
@@ -38,9 +40,10 @@ 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),
"flake8-eradicate" => Ok(Plugin::Flake8Eradicate),
"flake8-errmsg" => Ok(Plugin::Flake8ErrMsg),
"flake8-print" => Ok(Plugin::Flake8Print),
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
@@ -56,6 +59,37 @@ impl FromStr for Plugin {
}
}
impl fmt::Debug for Plugin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Plugin::Flake8Annotations => "flake8-annotations",
Plugin::Flake8Bandit => "flake8-bandit",
Plugin::Flake8BlindExcept => "flake8-blind-except",
Plugin::Flake8Bugbear => "flake8-bugbear",
Plugin::Flake8Builtins => "flake8-builtins",
Plugin::Flake8Comprehensions => "flake8-comprehensions",
Plugin::Flake8Datetimez => "flake8-datetimez",
Plugin::Flake8Debugger => "flake8-debugger",
Plugin::Flake8Docstrings => "flake8-docstrings",
Plugin::Flake8Eradicate => "flake8-eradicate",
Plugin::Flake8ErrMsg => "flake8-errmsg",
Plugin::Flake8Print => "flake8-print",
Plugin::Flake8Quotes => "flake8-quotes",
Plugin::Flake8Return => "flake8-return",
Plugin::Flake8Simplify => "flake8-simplify",
Plugin::Flake8TidyImports => "flake8-tidy-imports",
Plugin::McCabe => "mccabe",
Plugin::PandasVet => "pandas-vet",
Plugin::PEP8Naming => "pep8-naming",
Plugin::Pyupgrade => "pyupgrade",
}
)
}
}
impl Plugin {
pub fn default(&self) -> CheckCodePrefix {
match self {
@@ -66,6 +100,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`.
@@ -75,12 +110,11 @@ impl Plugin {
Plugin::Flake8Quotes => CheckCodePrefix::Q,
Plugin::Flake8Return => CheckCodePrefix::RET,
Plugin::Flake8Simplify => CheckCodePrefix::SIM,
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
Plugin::Flake8TidyImports => CheckCodePrefix::TID25,
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,
Plugin::Pyupgrade => CheckCodePrefix::UP,
}
}
@@ -92,6 +126,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 +152,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 +444,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,
@@ -420,7 +456,6 @@ pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin
Plugin::Flake8TidyImports,
Plugin::PandasVet,
Plugin::PEP8Naming,
Plugin::Pyupgrade,
]
.into_iter()
.filter(|plugin| {

View File

@@ -32,6 +32,8 @@ build-backend = "maturin"
bindings = "bin"
strip = true
[tool.ruff]
[tool.ruff.isort]
force-wrap-aliases = true
combine-as-imports = true

View File

@@ -55,3 +55,5 @@ a.get("hello", False)
{}.pop(True, False)
dict.fromkeys(("world",), True)
{}.deploy(True, False)
getattr(someobj, attrname, False)
mylist.index(True)

View File

@@ -0,0 +1,21 @@
import datetime
# no args
datetime.datetime(2000, 1, 1, 0, 0, 0)
# none args
datetime.datetime(2000, 1, 1, 0, 0, 0, 0, None)
# not none arg
datetime.datetime(2000, 1, 1, 0, 0, 0, 0, datetime.timezone.utc)
# 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,35 @@
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")
# OK
datetime.datetime.strptime("something", something).astimezone()
# OK
datetime.datetime.strptime("something", something).replace(tzinfo=datetime.timezone.utc)
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

@@ -6,18 +6,6 @@ def x():
return a # error
def x():
b, a = 1, 2
print(b)
return a # error
def x():
a = 1
print()
return a # error
def x():
a = 1
print(a)
@@ -53,7 +41,6 @@ def x():
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def user_agent_username(username=None):
if not username:
return ""
@@ -136,6 +123,20 @@ def x():
return a
# Considered OK, since functions can have side effects.
def x():
b, a = 1, 2
print(b)
return a
# Considered OK, since functions can have side effects.
def x():
a = 1
print()
return a
# Test cases for using value for assignment then returning it
# See:https://github.com/afonasev/flake8-return/issues/47
def resolve_from_url(self, url: str) -> dict:

View File

@@ -0,0 +1,61 @@
#: E401
import os, sys
#: Okay
import os
import sys
from subprocess import Popen, PIPE
from myclass import MyClass
from foo.bar.yourclass import YourClass
import myclass
import foo.bar.yourclass
#: Okay
__all__ = ['abc']
import foo
#: Okay
__version__ = "42"
import foo
#: Okay
__author__ = "Simon Gomizelj"
import foo
#: Okay
try:
import foo
except ImportError:
pass
else:
print('imported foo')
finally:
print('made attempt to import foo')
import bar
#: Okay
with warnings.catch_warnings():
warnings.filterwarnings("ignore", DeprecationWarning)
import foo
import bar
#: Okay
if False:
import foo
elif not True:
import bar
else:
import mwahaha
import bar
#: E402
VERSION = '1.2.3'
import foo
#: E402
import foo
a = 1
import bar

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,13 @@
"""Test: Mypy extensions."""
from mypy_extensions import DefaultNamedArg
# OK
_ = DefaultNamedArg(bool | None, name="some_prop_name")
_ = DefaultNamedArg(type=bool | None, name="some_prop_name")
_ = DefaultNamedArg(bool | None, "some_prop_name")
# Not OK
_ = DefaultNamedArg("Undefined", name="some_prop_name")
_ = DefaultNamedArg(type="Undefined", name="some_prop_name")
_ = DefaultNamedArg("Undefined", "some_prop_name")

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

@@ -109,6 +109,11 @@ def f():
del x
def f():
print(f"{x=}")
global x
###
# Non-errors.
###
@@ -146,3 +151,8 @@ def f():
global x, y
del x
def f():
global x
print(f"{x=}")

View File

@@ -0,0 +1,11 @@
import datetime
import datetime as dt
from datetime import timezone
from datetime import timezone as tz
print(datetime.timezone(-1))
print(timezone.utc)
print(tz.utc)
print(datetime.timezone.utc)
print(dt.timezone.utc)

View File

@@ -69,3 +69,20 @@ _ = """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
import collections # noqa
import os # noqa: F401, RUF100
import shelve # noqa: RUF100
import sys # noqa: F401, RUF100
print(sys.path)

View File

@@ -9,14 +9,14 @@ Running from the repo root should pick up and enforce the appropriate settings f
```
∴ cargo run resources/test/project/
Found 7 error(s).
resources/test/project/examples/.dotfiles/script.py:1:1: I001 Import block is un-sorted or un-formatted
resources/test/project/examples/.dotfiles/script.py:1:8: F401 `numpy` imported but unused
resources/test/project/examples/.dotfiles/script.py:2:17: F401 `app.app_file` imported but unused
resources/test/project/examples/docs/docs/file.py:1:1: I001 Import block is un-sorted or un-formatted
resources/test/project/examples/docs/docs/file.py:8:5: F841 Local variable `x` is assigned to but never used
resources/test/project/src/file.py:1:8: F401 `os` imported but unused
resources/test/project/src/import_file.py:1:1: I001 Import block is un-sorted or un-formatted
resources/test/project/project/file.py:1:8: F401 `os` imported but unused
resources/test/project/project/import_file.py:1:1: I001 Import block is un-sorted or un-formatted
Found 7 error(s).
6 potentially fixable with the --fix option.
```
@@ -24,14 +24,14 @@ Running from the project directory itself should exhibit the same behavior:
```
∴ (cd resources/test/project/ && cargo run .)
Found 7 error(s).
examples/.dotfiles/script.py:1:1: I001 Import block is un-sorted or un-formatted
examples/.dotfiles/script.py:1:8: F401 `numpy` imported but unused
examples/.dotfiles/script.py:2:17: F401 `app.app_file` imported but unused
examples/docs/docs/file.py:1:1: I001 Import block is un-sorted or un-formatted
examples/docs/docs/file.py:8:5: F841 Local variable `x` is assigned to but never used
src/file.py:1:8: F401 `os` imported but unused
src/import_file.py:1:1: I001 Import block is un-sorted or un-formatted
project/file.py:1:8: F401 `os` imported but unused
project/import_file.py:1:1: I001 Import block is un-sorted or un-formatted
Found 7 error(s).
6 potentially fixable with the --fix option.
```
@@ -40,9 +40,9 @@ files:
```
∴ (cd resources/test/project/examples/docs && cargo run .)
Found 2 error(s).
docs/file.py:1:1: I001 Import block is un-sorted or un-formatted
docs/file.py:8:5: F841 Local variable `x` is assigned to but never used
Found 2 error(s).
1 potentially fixable with the --fix option.
```
@@ -51,8 +51,6 @@ file paths from the current working directory:
```
∴ (cargo run -- --config=resources/test/project/pyproject.toml resources/test/project/)
Found 11 error(s).
resources/test/project/examples/.dotfiles/script.py:1:1: I001 Import block is un-sorted or un-formatted
resources/test/project/examples/.dotfiles/script.py:1:8: F401 `numpy` imported but unused
resources/test/project/examples/.dotfiles/script.py:2:17: F401 `app.app_file` imported but unused
resources/test/project/examples/docs/docs/concepts/file.py:1:8: F401 `os` imported but unused
@@ -61,9 +59,9 @@ resources/test/project/examples/docs/docs/file.py:1:8: F401 `os` imported but un
resources/test/project/examples/docs/docs/file.py:3:8: F401 `numpy` imported but unused
resources/test/project/examples/docs/docs/file.py:4:27: F401 `docs.concepts.file` imported but unused
resources/test/project/examples/excluded/script.py:1:8: F401 `os` imported but unused
resources/test/project/src/file.py:1:8: F401 `os` imported but unused
resources/test/project/src/import_file.py:1:1: I001 Import block is un-sorted or un-formatted
11 potentially fixable with the --fix option.
resources/test/project/project/file.py:1:8: F401 `os` imported but unused
Found 9 error(s).
9 potentially fixable with the --fix option.
```
Running from a parent directory should this "ignore" the `exclude` (hence, `concepts/file.py` gets
@@ -71,11 +69,11 @@ included in the output):
```
∴ (cd resources/test/project/examples && cargo run -- --config=docs/pyproject.toml .)
Found 4 error(s).
docs/docs/concepts/file.py:5:5: F841 Local variable `x` is assigned to but never used
docs/docs/file.py:1:1: I001 Import block is un-sorted or un-formatted
docs/docs/file.py:8:5: F841 Local variable `x` is assigned to but never used
excluded/script.py:5:5: F841 Local variable `x` is assigned to but never used
Found 4 error(s).
1 potentially fixable with the --fix option.
```
@@ -83,7 +81,14 @@ Passing an excluded directory directly should report errors in the contained fil
```
∴ cargo run resources/test/project/examples/excluded/
Found 1 error(s).
resources/test/project/examples/excluded/script.py:1:8: F401 `os` imported but unused
Found 1 error(s).
1 potentially fixable with the --fix option.
```
Unless we `--force-exclude`:
```
∴ cargo run resources/test/project/examples/excluded/ --force-exclude
∴ cargo run resources/test/project/examples/excluded/script.py --force-exclude
```

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_dev"
version = "0.0.186"
version = "0.0.192"
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 ruff::checks::{CheckCode, PREFIX_REDIRECTS};
use strum::IntoEnumIterator;
const FILE: &str = "src/checks_gen.rs";
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
@@ -40,18 +40,7 @@ pub fn main(cli: &Cli) -> Result<()> {
}
// Add any prefix aliases (e.g., "U" to "UP").
for (alias, source) in PREFIX_REDIRECTS.iter() {
prefix_to_codes.insert(
(*alias).to_string(),
prefix_to_codes
.get(&(*source).to_string())
.unwrap_or_else(|| panic!("Unknown CheckCode: {source:?}"))
.clone(),
);
}
// Add any check code aliases (e.g., "U001" to "UP001").
for (alias, check_code) in CODE_REDIRECTS.iter() {
for (alias, check_code) in PREFIX_REDIRECTS.iter() {
prefix_to_codes.insert(
(*alias).to_string(),
prefix_to_codes
@@ -105,7 +94,7 @@ pub fn main(cli: &Cli) -> Result<()> {
.line("#[allow(clippy::match_same_arms)]")
.line("match self {");
for (prefix, codes) in &prefix_to_codes {
if let Some(target) = CODE_REDIRECTS.get(&prefix.as_str()) {
if let Some(target) = PREFIX_REDIRECTS.get(&prefix.as_str()) {
gen = gen.line(format!(
"CheckCodePrefix::{prefix} => {{ one_time_warning!(\"{{}}{{}} {{}}\", \
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been remapped to \
@@ -117,18 +106,6 @@ pub fn main(cli: &Cli) -> Result<()> {
.map(|code| format!("CheckCode::{}", code.as_ref()))
.join(", ")
));
} else if let Some(target) = PREFIX_REDIRECTS.get(&prefix.as_str()) {
gen = gen.line(format!(
"CheckCodePrefix::{prefix} => {{ one_time_warning!(\"{{}}{{}} {{}}\", \
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been remapped to \
`{}`\".bold()); \n vec![{}] }}",
prefix,
target,
codes
.iter()
.map(|code| format!("CheckCode::{}", code.as_ref()))
.join(", ")
));
} else {
gen = gen.line(format!(
"CheckCodePrefix::{prefix} => vec![{}],",
@@ -204,12 +181,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.192"
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

@@ -8,6 +8,7 @@ use std::path::Path;
use anyhow::Result;
use filetime::FileTime;
use log::error;
use once_cell::sync::Lazy;
use path_absolutize::Absolutize;
use serde::{Deserialize, Serialize};
@@ -15,6 +16,7 @@ use crate::autofix::fixer;
use crate::message::Message;
use crate::settings::{flags, Settings};
static CACHE_DIR: Lazy<Option<String>> = Lazy::new(|| std::env::var("RUFF_CACHE_DIR").ok());
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Serialize, Deserialize)]
@@ -34,12 +36,12 @@ struct CheckResult {
messages: Vec<Message>,
}
fn cache_dir() -> &'static str {
"./.ruff_cache"
fn cache_dir() -> &'static Path {
Path::new(CACHE_DIR.as_ref().map_or(".ruff_cache", String::as_str))
}
fn content_dir() -> &'static str {
"content"
fn content_dir() -> &'static Path {
Path::new("content")
}
fn cache_key<P: AsRef<Path>>(path: P, settings: &Settings, autofix: fixer::Mode) -> u64 {
@@ -53,7 +55,7 @@ fn cache_key<P: AsRef<Path>>(path: P, settings: &Settings, autofix: fixer::Mode)
/// Initialize the cache directory.
pub fn init() -> Result<()> {
let path = Path::new(cache_dir());
let path = cache_dir();
// Create the cache directories.
create_dir_all(path.join(content_dir()))?;
@@ -75,19 +77,13 @@ pub fn init() -> Result<()> {
fn write_sync(key: u64, value: &[u8]) -> Result<(), std::io::Error> {
fs::write(
Path::new(cache_dir())
.join(content_dir())
.join(format!("{key:x}")),
cache_dir().join(content_dir()).join(format!("{key:x}")),
value,
)
}
fn read_sync(key: u64) -> Result<Vec<u8>, std::io::Error> {
fs::read(
Path::new(cache_dir())
.join(content_dir())
.join(format!("{key:x}")),
)
fs::read(cache_dir().join(content_dir()).join(format!("{key:x}")))
}
/// Get a value from the cache.

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,24 +151,19 @@ impl<'a> Checker<'a> {
annotations_future_enabled: false,
except_handlers: vec![],
// Check-specific state.
seen_b023: vec![],
flake8_bugbear_seen: vec![],
}
}
/// Add a `Check` to the `Checker`.
pub(crate) fn add_check(&mut self, check: Check) {
pub(crate) fn add_check(&mut self, mut check: Check) {
// If we're in an f-string, override the location. RustPython doesn't produce
// reliable locations for expressions within f-strings, so we use the
// span of the f-string itself as a best-effort default.
let check = if let Some(range) = self.in_f_string {
Check {
location: range.location,
end_location: range.end_location,
..check
}
} else {
check
};
if let Some(range) = self.in_f_string {
check.location = range.location;
check.end_location = range.end_location;
}
self.checks.push(check);
}
@@ -187,6 +184,13 @@ impl<'a> Checker<'a> {
&& self.settings.fixable.contains(code)
}
/// Return the amended `Range` from a `Located`.
pub fn range_for<T>(&self, located: &Located<T>) -> Range {
// If we're in an f-string, override the location.
self.in_f_string
.unwrap_or_else(|| Range::from_located(located))
}
/// Return `true` if the `Expr` is a reference to `typing.${target}`.
pub fn match_typing_expr(&self, expr: &Expr, target: &str) -> bool {
let call_path = dealias_call_path(collect_call_paths(expr), &self.import_aliases);
@@ -543,7 +547,7 @@ where
kind: BindingKind::FunctionDefinition,
used: None,
range: Range::from_located(stmt),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
}
@@ -624,6 +628,15 @@ where
}
}
StmtKind::Import { names } => {
if self.settings.enabled.contains(&CheckCode::E401) {
if names.len() > 1 {
self.add_check(Check::new(
CheckKind::MultipleImportsOnOneLine,
Range::from_located(stmt),
));
}
}
if self.settings.enabled.contains(&CheckCode::E402) {
if self.seen_import_boundary && stmt.location.column() == 0 {
self.add_check(Check::new(
@@ -648,7 +661,7 @@ where
),
used: None,
range: Range::from_located(alias),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
} else {
@@ -688,7 +701,7 @@ where
None
},
range: Range::from_located(alias),
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
}
@@ -847,7 +860,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 +891,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 +960,7 @@ where
None
},
range,
source: Some(self.current_parent().clone()),
source: Some(self.current_stmt().clone()),
},
);
}
@@ -1179,7 +1192,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 +1395,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 +1416,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 +1441,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, .. } => {
@@ -1538,15 +1553,21 @@ where
pyupgrade::plugins::remove_six_compat(self, expr);
}
if self.settings.enabled.contains(&CheckCode::UP017)
&& self.settings.target_version >= PythonVersion::Py311
{
pyupgrade::plugins::datetime_utc_alias(self, expr);
}
if self.settings.enabled.contains(&CheckCode::YTT202) {
flake8_2020::plugins::name_or_attribute(self, expr);
}
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 +1660,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 +1949,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 +1970,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
@@ -2428,6 +2524,29 @@ where
self.visit_expr(value);
self.in_type_definition = prev_in_type_definition;
}
} else if ["Arg", "DefaultArg", "NamedArg", "DefaultNamedArg"]
.iter()
.any(|target| {
match_call_path(&call_path, "mypy_extensions", target, &self.from_imports)
})
{
self.visit_expr(func);
// Ex) DefaultNamedArg(bool | None, name="some_prop_name")
let mut arguments = args.iter().chain(keywords.iter().map(|keyword| {
let KeywordData { value, .. } = &keyword.node;
value
}));
if let Some(expr) = arguments.next() {
self.in_type_definition = true;
self.visit_expr(expr);
self.in_type_definition = prev_in_type_definition;
}
for expr in arguments {
self.in_type_definition = false;
self.visit_expr(expr);
self.in_type_definition = prev_in_type_definition;
}
} else {
visitor::walk_expr(self, expr);
}
@@ -2507,6 +2626,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 +2773,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 +2808,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 +2849,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 +2890,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 +3119,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 +3164,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 +3181,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 +3194,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 +3244,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 +3257,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

@@ -100,18 +100,32 @@ pub fn check_noqa(
Directive::Codes(spaces, start, end, codes) => {
let mut invalid_codes = vec![];
let mut valid_codes = vec![];
let mut self_ignore = false;
for code in codes {
let code = CODE_REDIRECTS.get(code).map_or(code, AsRef::as_ref);
if matches.contains(&code) || settings.external.contains(code) {
valid_codes.push(code.to_string());
if code == CheckCode::RUF100.as_ref() {
self_ignore = true;
} else {
invalid_codes.push(code.to_string());
if matches.contains(&code) || settings.external.contains(code) {
valid_codes.push(code);
} else {
invalid_codes.push(code);
}
}
}
if self_ignore {
continue;
}
if !invalid_codes.is_empty() {
let mut check = Check::new(
CheckKind::UnusedNOQA(Some(invalid_codes)),
CheckKind::UnusedNOQA(Some(
invalid_codes
.iter()
.map(|code| (*code).to_string())
.collect(),
)),
Range {
location: Location::new(row + 1, start),
end_location: Location::new(row + 1, end),

View File

@@ -33,6 +33,7 @@ use crate::pyupgrade::types::Primitive;
)]
pub enum CheckCode {
// pycodestyle errors
E401,
E402,
E501,
E711,
@@ -224,6 +225,7 @@ pub enum CheckCode {
UP014,
UP015,
UP016,
UP017,
// pydocstyle
D100,
D101,
@@ -309,6 +311,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 +328,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 +375,7 @@ pub enum CheckCategory {
Flake8Simplify,
Flake8TidyImports,
Flake8UnusedArguments,
Flake8Datetimez,
Eradicate,
PandasVet,
PygrepHooks,
@@ -403,6 +418,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 +452,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 +543,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))
}
@@ -625,6 +646,7 @@ pub enum CheckKind {
IOError(String),
LineTooLong(usize, usize),
ModuleImportNotAtTopOfFile,
MultipleImportsOnOneLine,
NoneComparison(RejectedCmpop),
NotInTest,
NotIsTest,
@@ -806,6 +828,7 @@ pub enum CheckKind {
ConvertNamedTupleFunctionalToClass(String),
RedundantOpenModes,
RemoveSixCompat,
DatetimeTimezoneUTC,
// pydocstyle
BlankLineAfterLastSection(String),
BlankLineAfterSection(String),
@@ -887,6 +910,8 @@ pub enum CheckKind {
BooleanPositionalValueInFunctionCall,
// pygrep-hooks
NoEval,
DeprecatedLogWarn,
BlanketTypeIgnore,
// flake8-unused-arguments
UnusedFunctionArgument(String),
UnusedMethodArgument(String),
@@ -917,6 +942,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 +960,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
@@ -945,6 +982,7 @@ impl CheckCode {
pub fn kind(&self) -> CheckKind {
match self {
// pycodestyle errors
CheckCode::E401 => CheckKind::MultipleImportsOnOneLine,
CheckCode::E402 => CheckKind::ModuleImportNotAtTopOfFile,
CheckCode::E501 => CheckKind::LineTooLong(89, 88),
CheckCode::E711 => CheckKind::NoneComparison(RejectedCmpop::Eq),
@@ -1164,6 +1202,7 @@ impl CheckCode {
CheckCode::UP014 => CheckKind::ConvertNamedTupleFunctionalToClass("...".to_string()),
CheckCode::UP015 => CheckKind::RedundantOpenModes,
CheckCode::UP016 => CheckKind::RemoveSixCompat,
CheckCode::UP017 => CheckKind::DatetimeTimezoneUTC,
// pydocstyle
CheckCode::D100 => CheckKind::PublicModule,
CheckCode::D101 => CheckKind::PublicClass,
@@ -1260,6 +1299,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 +1312,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 +1459,16 @@ 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::E401 => CheckCategory::Pycodestyle,
CheckCode::E402 => CheckCategory::Pycodestyle,
CheckCode::E501 => CheckCategory::Pycodestyle,
CheckCode::E711 => CheckCategory::Pycodestyle,
@@ -1491,19 +1552,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,
@@ -1557,6 +1620,7 @@ impl CheckCode {
CheckCode::UP014 => CheckCategory::Pyupgrade,
CheckCode::UP015 => CheckCategory::Pyupgrade,
CheckCode::UP016 => CheckCategory::Pyupgrade,
CheckCode::UP017 => CheckCategory::Pyupgrade,
CheckCode::W292 => CheckCategory::Pycodestyle,
CheckCode::W605 => CheckCategory::Pycodestyle,
CheckCode::YTT101 => CheckCategory::Flake82020,
@@ -1601,6 +1665,7 @@ impl CheckKind {
CheckKind::IsLiteral => &CheckCode::F632,
CheckKind::LateFutureImport => &CheckCode::F404,
CheckKind::LineTooLong(..) => &CheckCode::E501,
CheckKind::MultipleImportsOnOneLine => &CheckCode::E401,
CheckKind::ModuleImportNotAtTopOfFile => &CheckCode::E402,
CheckKind::MultiValueRepeatedKeyLiteral => &CheckCode::F601,
CheckKind::MultiValueRepeatedKeyVariable(_) => &CheckCode::F602,
@@ -1766,6 +1831,7 @@ impl CheckKind {
CheckKind::ConvertNamedTupleFunctionalToClass(_) => &CheckCode::UP014,
CheckKind::RedundantOpenModes => &CheckCode::UP015,
CheckKind::RemoveSixCompat => &CheckCode::UP016,
CheckKind::DatetimeTimezoneUTC => &CheckCode::UP017,
// pydocstyle
CheckKind::BlankLineAfterLastSection(_) => &CheckCode::D413,
CheckKind::BlankLineAfterSection(_) => &CheckCode::D410,
@@ -1847,6 +1913,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 +1924,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,
@@ -1948,6 +2026,7 @@ impl CheckKind {
CheckKind::ModuleImportNotAtTopOfFile => {
"Module level import not at top of file".to_string()
}
CheckKind::MultipleImportsOnOneLine => "Multiple imports on one line".to_string(),
CheckKind::MultiValueRepeatedKeyLiteral => {
"Dictionary key literal repeated".to_string()
}
@@ -2475,6 +2554,7 @@ impl CheckKind {
CheckKind::UnnecessaryEncodeUTF8 => "Unnecessary call to `encode` as UTF-8".to_string(),
CheckKind::RedundantOpenModes => "Unnecessary open mode parameters".to_string(),
CheckKind::RemoveSixCompat => "Unnecessary `six` compatibility usage".to_string(),
CheckKind::DatetimeTimezoneUTC => "Use `datetime.UTC` alias".to_string(),
CheckKind::ConvertTypedDictFunctionalToClass(name) => {
format!("Convert `{name}` from `TypedDict` functional to class syntax")
}
@@ -2684,6 +2764,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 +2866,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 +2920,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(),
}
}
@@ -2853,6 +2991,7 @@ impl CheckKind {
| CheckKind::RedundantOpenModes
| CheckKind::RedundantTupleInExceptionHandler(..)
| CheckKind::RemoveSixCompat
| CheckKind::DatetimeTimezoneUTC
| CheckKind::SectionNameEndsInColon(..)
| CheckKind::SectionNotOverIndented(..)
| CheckKind::SectionUnderlineAfterName(..)
@@ -2917,6 +3056,81 @@ impl Check {
}
}
/// A hash map from deprecated `CheckCodePrefix` to latest `CheckCodePrefix`.
pub static PREFIX_REDIRECTS: Lazy<FxHashMap<&'static str, CheckCodePrefix>> = Lazy::new(|| {
FxHashMap::from_iter([
// TODO(charlie): Remove by 2023-01-01.
("U001", CheckCodePrefix::UP001),
("U003", CheckCodePrefix::UP003),
("U004", CheckCodePrefix::UP004),
("U005", CheckCodePrefix::UP005),
("U006", CheckCodePrefix::UP006),
("U007", CheckCodePrefix::UP007),
("U008", CheckCodePrefix::UP008),
("U009", CheckCodePrefix::UP009),
("U010", CheckCodePrefix::UP010),
("U011", CheckCodePrefix::UP011),
("U012", CheckCodePrefix::UP012),
("U013", CheckCodePrefix::UP013),
("U014", CheckCodePrefix::UP014),
("U015", CheckCodePrefix::UP015),
("U016", CheckCodePrefix::UP016),
("U017", CheckCodePrefix::UP017),
// TODO(charlie): Remove by 2023-02-01.
("I252", CheckCodePrefix::TID252),
("M001", CheckCodePrefix::RUF100),
// TODO(charlie): Remove by 2023-02-01.
("PDV002", CheckCodePrefix::PD002),
("PDV003", CheckCodePrefix::PD003),
("PDV004", CheckCodePrefix::PD004),
("PDV007", CheckCodePrefix::PD007),
("PDV008", CheckCodePrefix::PD008),
("PDV009", CheckCodePrefix::PD009),
("PDV010", CheckCodePrefix::PD010),
("PDV011", CheckCodePrefix::PD011),
("PDV012", CheckCodePrefix::PD012),
("PDV013", CheckCodePrefix::PD013),
("PDV015", CheckCodePrefix::PD015),
("PDV901", CheckCodePrefix::PD901),
// TODO(charlie): Remove by 2023-02-01.
("R501", CheckCodePrefix::RET501),
("R502", CheckCodePrefix::RET502),
("R503", CheckCodePrefix::RET503),
("R504", CheckCodePrefix::RET504),
("R505", CheckCodePrefix::RET505),
("R506", CheckCodePrefix::RET506),
("R507", CheckCodePrefix::RET507),
("R508", CheckCodePrefix::RET508),
("IC001", CheckCodePrefix::ICN001),
("IC002", CheckCodePrefix::ICN001),
("IC003", CheckCodePrefix::ICN001),
("IC004", CheckCodePrefix::ICN001),
// TODO(charlie): Remove by 2023-01-01.
("U", CheckCodePrefix::UP),
("U0", CheckCodePrefix::UP0),
("U00", CheckCodePrefix::UP00),
("U01", CheckCodePrefix::UP01),
// TODO(charlie): Remove by 2023-02-01.
("I2", CheckCodePrefix::TID2),
("I25", CheckCodePrefix::TID25),
("M", CheckCodePrefix::RUF100),
("M0", CheckCodePrefix::RUF100),
// TODO(charlie): Remove by 2023-02-01.
("PDV", CheckCodePrefix::PD),
("PDV0", CheckCodePrefix::PD0),
("PDV01", CheckCodePrefix::PD01),
("PDV9", CheckCodePrefix::PD9),
("PDV90", CheckCodePrefix::PD90),
// TODO(charlie): Remove by 2023-02-01.
("R", CheckCodePrefix::RET),
("R5", CheckCodePrefix::RET5),
("R50", CheckCodePrefix::RET50),
// TODO(charlie): Remove by 2023-02-01.
("IC", CheckCodePrefix::ICN),
("IC0", CheckCodePrefix::ICN0),
])
});
/// A hash map from deprecated to latest `CheckCode`.
pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(|| {
FxHashMap::from_iter([
@@ -2936,25 +3150,37 @@ pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(
("U014", CheckCode::UP014),
("U015", CheckCode::UP015),
("U016", CheckCode::UP016),
("U017", CheckCode::UP017),
// TODO(charlie): Remove by 2023-02-01.
("I252", CheckCode::TID252),
("M001", CheckCode::RUF100),
])
});
/// A hash map from deprecated `CheckCodePrefix` to latest `CheckCodePrefix`.
pub static PREFIX_REDIRECTS: Lazy<FxHashMap<&'static str, &'static str>> = Lazy::new(|| {
FxHashMap::from_iter([
// TODO(charlie): Remove by 2023-01-01.
("U", "UP"),
("U0", "UP0"),
("U00", "UP00"),
("U01", "UP01"),
// TODO(charlie): Remove by 2023-02-01.
("I2", "TID2"),
("I25", "TID25"),
("M", "RUF100"),
("M0", "RUF100"),
("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),
// TODO(charlie): Remove by 2023-02-01.
("R501", CheckCode::RET501),
("R502", CheckCode::RET502),
("R503", CheckCode::RET503),
("R504", CheckCode::RET504),
("R505", CheckCode::RET505),
("R506", CheckCode::RET506),
("R507", CheckCode::RET507),
("R508", CheckCode::RET508),
// TODO(charlie): Remove by 2023-02-01.
("IC001", CheckCode::ICN001),
("IC002", CheckCode::ICN001),
("IC003", CheckCode::ICN001),
("IC004", CheckCode::ICN001),
])
});

View File

@@ -163,9 +163,23 @@ pub enum CheckCodePrefix {
D417,
D418,
D419,
DTZ,
DTZ0,
DTZ00,
DTZ001,
DTZ002,
DTZ003,
DTZ004,
DTZ005,
DTZ006,
DTZ007,
DTZ01,
DTZ011,
DTZ012,
E,
E4,
E40,
E401,
E402,
E5,
E50,
@@ -278,6 +292,12 @@ pub enum CheckCodePrefix {
I2,
I25,
I252,
IC,
IC0,
IC001,
IC002,
IC003,
IC004,
ICN,
ICN0,
ICN00,
@@ -304,9 +324,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 +363,8 @@ pub enum CheckCodePrefix {
PGH0,
PGH00,
PGH001,
PGH002,
PGH003,
PLC,
PLC0,
PLC04,
@@ -378,6 +417,17 @@ pub enum CheckCodePrefix {
Q001,
Q002,
Q003,
R,
R5,
R50,
R501,
R502,
R503,
R504,
R505,
R506,
R507,
R508,
RET,
RET5,
RET50,
@@ -442,6 +492,7 @@ pub enum CheckCodePrefix {
U014,
U015,
U016,
U017,
UP,
UP0,
UP00,
@@ -461,6 +512,7 @@ pub enum CheckCodePrefix {
UP014,
UP015,
UP016,
UP017,
W,
W2,
W29,
@@ -972,7 +1024,49 @@ 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::E401,
CheckCode::E402,
CheckCode::E501,
CheckCode::E711,
@@ -988,8 +1082,9 @@ impl CheckCodePrefix {
CheckCode::E902,
CheckCode::E999,
],
CheckCodePrefix::E4 => vec![CheckCode::E402],
CheckCodePrefix::E40 => vec![CheckCode::E402],
CheckCodePrefix::E4 => vec![CheckCode::E401, CheckCode::E402],
CheckCodePrefix::E40 => vec![CheckCode::E401, CheckCode::E402],
CheckCodePrefix::E401 => vec![CheckCode::E401],
CheckCodePrefix::E402 => vec![CheckCode::E402],
CheckCodePrefix::E5 => vec![CheckCode::E501],
CheckCodePrefix::E50 => vec![CheckCode::E501],
@@ -1270,6 +1365,60 @@ impl CheckCodePrefix {
);
vec![CheckCode::TID252]
}
CheckCodePrefix::IC => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`IC` has been remapped to `ICN`".bold()
);
vec![CheckCode::ICN001]
}
CheckCodePrefix::IC0 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`IC0` has been remapped to `ICN0`".bold()
);
vec![CheckCode::ICN001]
}
CheckCodePrefix::IC001 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`IC001` has been remapped to `ICN001`".bold()
);
vec![CheckCode::ICN001]
}
CheckCodePrefix::IC002 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`IC002` has been remapped to `ICN001`".bold()
);
vec![CheckCode::ICN001]
}
CheckCodePrefix::IC003 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`IC003` has been remapped to `ICN001`".bold()
);
vec![CheckCode::ICN001]
}
CheckCodePrefix::IC004 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`IC004` has been remapped to `ICN001`".bold()
);
vec![CheckCode::ICN001]
}
CheckCodePrefix::ICN => vec![CheckCode::ICN001],
CheckCodePrefix::ICN0 => vec![CheckCode::ICN001],
CheckCodePrefix::ICN00 => vec![CheckCode::ICN001],
@@ -1369,66 +1518,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]
}
@@ -1505,6 +1840,132 @@ impl CheckCodePrefix {
CheckCodePrefix::Q001 => vec![CheckCode::Q001],
CheckCodePrefix::Q002 => vec![CheckCode::Q002],
CheckCodePrefix::Q003 => vec![CheckCode::Q003],
CheckCodePrefix::R => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`R` has been remapped to `RET`".bold()
);
vec![
CheckCode::RET501,
CheckCode::RET502,
CheckCode::RET503,
CheckCode::RET504,
CheckCode::RET505,
CheckCode::RET506,
CheckCode::RET507,
CheckCode::RET508,
]
}
CheckCodePrefix::R5 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`R5` has been remapped to `RET5`".bold()
);
vec![
CheckCode::RET501,
CheckCode::RET502,
CheckCode::RET503,
CheckCode::RET504,
CheckCode::RET505,
CheckCode::RET506,
CheckCode::RET507,
CheckCode::RET508,
]
}
CheckCodePrefix::R50 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`R50` has been remapped to `RET50`".bold()
);
vec![
CheckCode::RET501,
CheckCode::RET502,
CheckCode::RET503,
CheckCode::RET504,
CheckCode::RET505,
CheckCode::RET506,
CheckCode::RET507,
CheckCode::RET508,
]
}
CheckCodePrefix::R501 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`R501` has been remapped to `RET501`".bold()
);
vec![CheckCode::RET501]
}
CheckCodePrefix::R502 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`R502` has been remapped to `RET502`".bold()
);
vec![CheckCode::RET502]
}
CheckCodePrefix::R503 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`R503` has been remapped to `RET503`".bold()
);
vec![CheckCode::RET503]
}
CheckCodePrefix::R504 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`R504` has been remapped to `RET504`".bold()
);
vec![CheckCode::RET504]
}
CheckCodePrefix::R505 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`R505` has been remapped to `RET505`".bold()
);
vec![CheckCode::RET505]
}
CheckCodePrefix::R506 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`R506` has been remapped to `RET506`".bold()
);
vec![CheckCode::RET506]
}
CheckCodePrefix::R507 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`R507` has been remapped to `RET507`".bold()
);
vec![CheckCode::RET507]
}
CheckCodePrefix::R508 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`R508` has been remapped to `RET508`".bold()
);
vec![CheckCode::RET508]
}
CheckCodePrefix::RET => vec![
CheckCode::RET501,
CheckCode::RET502,
@@ -1626,6 +2087,7 @@ impl CheckCodePrefix {
CheckCode::UP014,
CheckCode::UP015,
CheckCode::UP016,
CheckCode::UP017,
]
}
CheckCodePrefix::U0 => {
@@ -1651,6 +2113,7 @@ impl CheckCodePrefix {
CheckCode::UP014,
CheckCode::UP015,
CheckCode::UP016,
CheckCode::UP017,
]
}
CheckCodePrefix::U00 => {
@@ -1758,6 +2221,7 @@ impl CheckCodePrefix {
CheckCode::UP014,
CheckCode::UP015,
CheckCode::UP016,
CheckCode::UP017,
]
}
CheckCodePrefix::U010 => {
@@ -1823,6 +2287,15 @@ impl CheckCodePrefix {
);
vec![CheckCode::UP016]
}
CheckCodePrefix::U017 => {
one_time_warning!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U017` has been remapped to `UP017`".bold()
);
vec![CheckCode::UP017]
}
CheckCodePrefix::UP => vec![
CheckCode::UP001,
CheckCode::UP003,
@@ -1839,6 +2312,7 @@ impl CheckCodePrefix {
CheckCode::UP014,
CheckCode::UP015,
CheckCode::UP016,
CheckCode::UP017,
],
CheckCodePrefix::UP0 => vec![
CheckCode::UP001,
@@ -1856,6 +2330,7 @@ impl CheckCodePrefix {
CheckCode::UP014,
CheckCode::UP015,
CheckCode::UP016,
CheckCode::UP017,
],
CheckCodePrefix::UP00 => vec![
CheckCode::UP001,
@@ -1883,6 +2358,7 @@ impl CheckCodePrefix {
CheckCode::UP014,
CheckCode::UP015,
CheckCode::UP016,
CheckCode::UP017,
],
CheckCodePrefix::UP010 => vec![CheckCode::UP010],
CheckCodePrefix::UP011 => vec![CheckCode::UP011],
@@ -1891,6 +2367,7 @@ impl CheckCodePrefix {
CheckCodePrefix::UP014 => vec![CheckCode::UP014],
CheckCodePrefix::UP015 => vec![CheckCode::UP015],
CheckCodePrefix::UP016 => vec![CheckCode::UP016],
CheckCodePrefix::UP017 => vec![CheckCode::UP017],
CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605],
CheckCodePrefix::W2 => vec![CheckCode::W292],
CheckCodePrefix::W29 => vec![CheckCode::W292],
@@ -2096,9 +2573,23 @@ 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,
CheckCodePrefix::E401 => SuffixLength::Three,
CheckCodePrefix::E402 => SuffixLength::Three,
CheckCodePrefix::E5 => SuffixLength::One,
CheckCodePrefix::E50 => SuffixLength::Two,
@@ -2211,6 +2702,12 @@ impl CheckCodePrefix {
CheckCodePrefix::I2 => SuffixLength::One,
CheckCodePrefix::I25 => SuffixLength::Two,
CheckCodePrefix::I252 => SuffixLength::Three,
CheckCodePrefix::IC => SuffixLength::Zero,
CheckCodePrefix::IC0 => SuffixLength::One,
CheckCodePrefix::IC001 => SuffixLength::Three,
CheckCodePrefix::IC002 => SuffixLength::Three,
CheckCodePrefix::IC003 => SuffixLength::Three,
CheckCodePrefix::IC004 => SuffixLength::Three,
CheckCodePrefix::ICN => SuffixLength::Zero,
CheckCodePrefix::ICN0 => SuffixLength::One,
CheckCodePrefix::ICN00 => SuffixLength::Two,
@@ -2237,9 +2734,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 +2773,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,
@@ -2311,6 +2827,17 @@ impl CheckCodePrefix {
CheckCodePrefix::Q001 => SuffixLength::Three,
CheckCodePrefix::Q002 => SuffixLength::Three,
CheckCodePrefix::Q003 => SuffixLength::Three,
CheckCodePrefix::R => SuffixLength::Zero,
CheckCodePrefix::R5 => SuffixLength::One,
CheckCodePrefix::R50 => SuffixLength::Two,
CheckCodePrefix::R501 => SuffixLength::Three,
CheckCodePrefix::R502 => SuffixLength::Three,
CheckCodePrefix::R503 => SuffixLength::Three,
CheckCodePrefix::R504 => SuffixLength::Three,
CheckCodePrefix::R505 => SuffixLength::Three,
CheckCodePrefix::R506 => SuffixLength::Three,
CheckCodePrefix::R507 => SuffixLength::Three,
CheckCodePrefix::R508 => SuffixLength::Three,
CheckCodePrefix::RET => SuffixLength::Zero,
CheckCodePrefix::RET5 => SuffixLength::One,
CheckCodePrefix::RET50 => SuffixLength::Two,
@@ -2375,6 +2902,7 @@ impl CheckCodePrefix {
CheckCodePrefix::U014 => SuffixLength::Three,
CheckCodePrefix::U015 => SuffixLength::Three,
CheckCodePrefix::U016 => SuffixLength::Three,
CheckCodePrefix::U017 => SuffixLength::Three,
CheckCodePrefix::UP => SuffixLength::Zero,
CheckCodePrefix::UP0 => SuffixLength::One,
CheckCodePrefix::UP00 => SuffixLength::Two,
@@ -2394,6 +2922,7 @@ impl CheckCodePrefix {
CheckCodePrefix::UP014 => SuffixLength::Three,
CheckCodePrefix::UP015 => SuffixLength::Three,
CheckCodePrefix::UP016 => SuffixLength::Three,
CheckCodePrefix::UP017 => SuffixLength::Three,
CheckCodePrefix::W => SuffixLength::Zero,
CheckCodePrefix::W2 => SuffixLength::One,
CheckCodePrefix::W29 => SuffixLength::Two,
@@ -2430,6 +2959,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
CheckCodePrefix::BLE,
CheckCodePrefix::C,
CheckCodePrefix::D,
CheckCodePrefix::DTZ,
CheckCodePrefix::E,
CheckCodePrefix::EM,
CheckCodePrefix::ERA,
@@ -2438,7 +2968,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
CheckCodePrefix::I,
CheckCodePrefix::ICN,
CheckCodePrefix::N,
CheckCodePrefix::PDV,
CheckCodePrefix::PD,
CheckCodePrefix::PGH,
CheckCodePrefix::PLC,
CheckCodePrefix::PLE,

View File

@@ -92,6 +92,12 @@ pub struct Cli {
respect_gitignore: bool,
#[clap(long, overrides_with("respect_gitignore"), hide = true)]
no_respect_gitignore: bool,
/// Enforce exclusions, even for paths passed to Ruff directly on the
/// command-line.
#[arg(long, overrides_with("no_show_source"))]
force_exclude: bool,
#[clap(long, overrides_with("force_exclude"), hide = true)]
no_force_exclude: bool,
/// See the files Ruff will be run against with the current settings.
#[arg(long)]
pub show_files: bool,
@@ -120,7 +126,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>,
@@ -173,6 +179,7 @@ impl Cli {
// TODO(charlie): Included in `pyproject.toml`, but not inherited.
fix: resolve_bool_arg(self.fix, self.no_fix),
format: self.format,
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
},
)
}
@@ -203,7 +210,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,
}
@@ -230,6 +237,7 @@ pub struct Overrides {
// TODO(charlie): Captured in pyproject.toml as a default, but not part of `Settings`.
pub fix: Option<bool>,
pub format: Option<SerializationFormat>,
pub force_exclude: Option<bool>,
}
/// Map the CLI settings to a `LogLevel`.

View File

@@ -114,16 +114,26 @@ fn read_from_stdin() -> Result<String> {
/// Run the linter over a single file, read from `stdin`.
pub fn run_stdin(
strategy: &PyprojectDiscovery,
filename: &Path,
filename: Option<&Path>,
pyproject_strategy: &PyprojectDiscovery,
file_strategy: &FileDiscovery,
overrides: &Overrides,
autofix: fixer::Mode,
) -> Result<Diagnostics> {
let stdin = read_from_stdin()?;
let settings = match strategy {
if let Some(filename) = filename {
if !resolver::python_file_at_path(filename, pyproject_strategy, file_strategy, overrides)? {
return Ok(Diagnostics::default());
}
}
let settings = match pyproject_strategy {
PyprojectDiscovery::Fixed(settings) => settings,
PyprojectDiscovery::Hierarchical(settings) => settings,
};
let mut diagnostics = lint_stdin(filename, &stdin, settings, autofix)?;
let package_root = filename
.and_then(Path::parent)
.and_then(packages::detect_package_root);
let stdin = read_from_stdin()?;
let mut diagnostics = lint_stdin(filename, package_root, &stdin, settings, autofix)?;
diagnostics.messages.sort_unstable();
Ok(diagnostics)
}

View File

@@ -5,16 +5,35 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind};
const FUNC_NAME_ALLOWLIST: &[&str] = &["get", "setdefault", "pop", "fromkeys"];
const FUNC_NAME_ALLOWLIST: &[&str] = &[
"assertEqual",
"assertEquals",
"assertNotEqual",
"assertNotEquals",
"failIfEqual",
"failUnlessEqual",
"fromkeys",
"get",
"getattr",
"index",
"pop",
"setattr",
"setdefault",
];
/// Returns `true` if an argument is allowed to use a boolean trap. To return
/// `true`, the function name must be explicitly allowed, and the argument must
/// be either the first or second argument in the call.
fn allow_boolean_trap(func: &Expr) -> bool {
let ExprKind::Attribute { attr, .. } = &func.node else {
return false;
};
FUNC_NAME_ALLOWLIST.contains(&attr.as_ref())
if let ExprKind::Attribute { attr, .. } = &func.node {
return FUNC_NAME_ALLOWLIST.contains(&attr.as_ref());
}
if let ExprKind::Name { id, .. } = &func.node {
return FUNC_NAME_ALLOWLIST.contains(&id.as_ref());
}
false
}
fn is_boolean_arg(arg: &Expr) -> bool {
@@ -79,8 +98,8 @@ pub fn check_boolean_positional_value_in_function_call(
args: &[Expr],
func: &Expr,
) {
for (index, arg) in args.iter().enumerate() {
if index < 2 && allow_boolean_trap(func) {
for arg in args {
if allow_boolean_trap(func) {
continue;
}
add_if_boolean(

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,234 @@
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 positional arg: keyword is missing or constant None.
if args.len() < 8 && !has_non_none_keyword(keywords, "tzinfo") {
checker.add_check(Check::new(CheckKind::CallDatetimeWithoutTzinfo, location));
return;
}
// Positional arg: is constant None.
if args.len() >= 8 && is_const_none(&args[7]) {
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;
}
// Does the `strptime` call contain a format string with a timezone specifier?
if let Some(ExprKind::Constant {
value: Constant::Str(format),
kind: None,
}) = args.get(1).as_ref().map(|arg| &arg.node)
{
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: 13
column: 0
end_location:
row: 13
column: 37
fix: ~
- kind: CallDatetimeWithoutTzinfo
location:
row: 16
column: 0
end_location:
row: 16
column: 42
fix: ~
- kind: CallDatetimeWithoutTzinfo
location:
row: 21
column: 0
end_location:
row: 21
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: 35
column: 0
end_location:
row: 35
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

@@ -12,50 +12,34 @@ expression: checks
fix: ~
- kind: UnnecessaryAssign
location:
row: 12
row: 13
column: 11
end_location:
row: 12
row: 13
column: 12
fix: ~
- kind: UnnecessaryAssign
location:
row: 18
column: 11
end_location:
row: 18
column: 12
fix: ~
- kind: UnnecessaryAssign
location:
row: 25
column: 11
end_location:
row: 25
column: 12
fix: ~
- kind: UnnecessaryAssign
location:
row: 31
row: 19
column: 15
end_location:
row: 31
row: 19
column: 16
fix: ~
- kind: UnnecessaryAssign
location:
row: 43
row: 31
column: 11
end_location:
row: 43
row: 31
column: 17
fix: ~
- kind: UnnecessaryAssign
location:
row: 51
row: 39
column: 11
end_location:
row: 51
row: 39
column: 20
fix: ~

View File

@@ -100,6 +100,7 @@ impl<'a> Visitor<'a> for ReturnVisitor<'a> {
.push((stmt.location, stmt.end_location.unwrap()));
visitor::walk_stmt(self, stmt);
}
_ => {
visitor::walk_stmt(self, stmt);
}
@@ -108,6 +109,17 @@ impl<'a> Visitor<'a> for ReturnVisitor<'a> {
fn visit_expr(&mut self, expr: &'a Expr) {
match &expr.node {
ExprKind::Call { .. } => {
// Arbitrary function calls can have side effects, so we conservatively treat
// every function call as a reference to every known variable.
for name in self.stack.assigns.keys() {
self.stack
.refs
.entry(name)
.or_insert_with(Vec::new)
.push(self.in_f_string.unwrap_or(expr.location));
}
}
ExprKind::Name { id, .. } => {
self.stack
.refs

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.
@@ -255,7 +260,8 @@ pub fn autoformat_path(path: &Path, _settings: &Settings) -> Result<()> {
/// Generate a list of `Check` violations from source code content derived from
/// stdin.
pub fn lint_stdin(
path: &Path,
path: Option<&Path>,
package: Option<&Path>,
stdin: &str,
settings: &Settings,
autofix: fixer::Mode,
@@ -264,7 +270,13 @@ pub fn lint_stdin(
let contents = stdin.to_string();
// Lint the file.
let (contents, fixed, messages) = lint(contents, path, None, settings, autofix)?;
let (contents, fixed, messages) = lint(
contents,
path.unwrap_or_else(|| Path::new("-")),
package,
settings,
autofix,
)?;
// Write the fixed contents to stdout.
if matches!(autofix, fixer::Mode::Apply) {

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,11 +94,19 @@ 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.
let file_strategy = FileDiscovery {
force_exclude: match &pyproject_strategy {
PyprojectDiscovery::Fixed(settings) => settings.force_exclude,
PyprojectDiscovery::Hierarchical(settings) => settings.force_exclude,
},
respect_gitignore: match &pyproject_strategy {
PyprojectDiscovery::Fixed(settings) => settings.respect_gitignore,
PyprojectDiscovery::Hierarchical(settings) => settings.respect_gitignore,
@@ -207,9 +224,13 @@ 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)?
commands::run_stdin(
cli.stdin_filename.as_deref(),
&pyproject_strategy,
&file_strategy,
&overrides,
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

@@ -44,7 +44,7 @@ impl<'a> Printer<'a> {
}
}
fn pre_text(&self, diagnostics: &Diagnostics) {
fn post_text(&self, diagnostics: &Diagnostics, autofix: fixer::Mode) {
if self.log_level >= &LogLevel::Default {
let fixed = diagnostics.fixed;
let remaining = diagnostics.messages.len();
@@ -54,13 +54,16 @@ impl<'a> Printer<'a> {
} else if remaining > 0 {
println!("Found {remaining} error(s).");
}
}
}
fn post_text(&self, num_fixable: usize, autofix: fixer::Mode) {
if self.log_level >= &LogLevel::Default {
if num_fixable > 0 && !matches!(autofix, fixer::Mode::Apply) {
println!("{num_fixable} potentially fixable with the --fix option.");
if !matches!(autofix, fixer::Mode::Apply) {
let num_fixable = diagnostics
.messages
.iter()
.filter(|message| message.kind.fixable())
.count();
if num_fixable > 0 {
println!("{num_fixable} potentially fixable with the --fix option.");
}
}
}
}
@@ -70,12 +73,6 @@ impl<'a> Printer<'a> {
return Ok(());
}
let num_fixable = diagnostics
.messages
.iter()
.filter(|message| message.kind.fixable())
.count();
match self.format {
SerializationFormat::Json => {
println!(
@@ -141,18 +138,13 @@ impl<'a> Printer<'a> {
println!("{}", report.to_string().unwrap());
}
SerializationFormat::Text => {
self.pre_text(diagnostics);
for message in &diagnostics.messages {
print_message(message);
}
self.post_text(num_fixable, autofix);
self.post_text(diagnostics, autofix);
}
SerializationFormat::Grouped => {
self.pre_text(diagnostics);
println!();
// Group by filename.
let mut grouped_messages = BTreeMap::default();
for message in &diagnostics.messages {
@@ -190,11 +182,9 @@ impl<'a> Printer<'a> {
println!();
}
self.post_text(num_fixable, autofix);
self.post_text(diagnostics, autofix);
}
SerializationFormat::Github => {
self.pre_text(diagnostics);
// Generate error workflow command in GitHub Actions format
// https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message
diagnostics.messages.iter().for_each(|message| {

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

@@ -13,24 +13,26 @@ mod tests {
use crate::linter::test_path;
use crate::settings;
#[test_case(CheckCode::E402, Path::new("E402.py"); "E402")]
#[test_case(CheckCode::E501, Path::new("E501.py"); "E501")]
#[test_case(CheckCode::E711, Path::new("E711.py"); "E711")]
#[test_case(CheckCode::E712, Path::new("E712.py"); "E712")]
#[test_case(CheckCode::E713, Path::new("E713.py"); "E713")]
#[test_case(CheckCode::E714, Path::new("E714.py"); "E714")]
#[test_case(CheckCode::E721, Path::new("E721.py"); "E721")]
#[test_case(CheckCode::E722, Path::new("E722.py"); "E722")]
#[test_case(CheckCode::E731, Path::new("E731.py"); "E731")]
#[test_case(CheckCode::E741, Path::new("E741.py"); "E741")]
#[test_case(CheckCode::E742, Path::new("E742.py"); "E742")]
#[test_case(CheckCode::E743, Path::new("E743.py"); "E743")]
#[test_case(CheckCode::E999, Path::new("E999.py"); "E999")]
#[test_case(CheckCode::W292, Path::new("W292_0.py"); "W292_0")]
#[test_case(CheckCode::W292, Path::new("W292_1.py"); "W292_1")]
#[test_case(CheckCode::W292, Path::new("W292_2.py"); "W292_2")]
#[test_case(CheckCode::W605, Path::new("W605_0.py"); "W605_0")]
#[test_case(CheckCode::W605, Path::new("W605_1.py"); "W605_1")]
#[test_case(CheckCode::E401, Path::new("E40.py"))]
#[test_case(CheckCode::E402, Path::new("E40.py"))]
#[test_case(CheckCode::E402, Path::new("E402.py"))]
#[test_case(CheckCode::E501, Path::new("E501.py"))]
#[test_case(CheckCode::E711, Path::new("E711.py"))]
#[test_case(CheckCode::E712, Path::new("E712.py"))]
#[test_case(CheckCode::E713, Path::new("E713.py"))]
#[test_case(CheckCode::E714, Path::new("E714.py"))]
#[test_case(CheckCode::E721, Path::new("E721.py"))]
#[test_case(CheckCode::E722, Path::new("E722.py"))]
#[test_case(CheckCode::E731, Path::new("E731.py"))]
#[test_case(CheckCode::E741, Path::new("E741.py"))]
#[test_case(CheckCode::E742, Path::new("E742.py"))]
#[test_case(CheckCode::E743, Path::new("E743.py"))]
#[test_case(CheckCode::E999, Path::new("E999.py"))]
#[test_case(CheckCode::W292, Path::new("W292_0.py"))]
#[test_case(CheckCode::W292, Path::new("W292_1.py"))]
#[test_case(CheckCode::W292, Path::new("W292_2.py"))]
#[test_case(CheckCode::W605, Path::new("W605_0.py"))]
#[test_case(CheckCode::W605, Path::new("W605_1.py"))]
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,13 @@
---
source: src/pycodestyle/mod.rs
expression: checks
---
- kind: MultipleImportsOnOneLine
location:
row: 2
column: 0
end_location:
row: 2
column: 14
fix: ~

View File

@@ -0,0 +1,29 @@
---
source: src/pycodestyle/mod.rs
expression: checks
---
- kind: ModuleImportNotAtTopOfFile
location:
row: 55
column: 0
end_location:
row: 55
column: 10
fix: ~
- kind: ModuleImportNotAtTopOfFile
location:
row: 57
column: 0
end_location:
row: 57
column: 10
fix: ~
- kind: ModuleImportNotAtTopOfFile
location:
row: 61
column: 0
end_location:
row: 61
column: 10
fix: ~

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

@@ -1349,39 +1349,22 @@ fn missing_args(checker: &mut Checker, docstring: &Docstring, docstrings_args: &
// See: `GOOGLE_ARGS_REGEX` in `pydocstyle/checker.py`.
static GOOGLE_ARGS_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^\s*(\w+)\s*(\(.*?\))?\s*:\n?\s*.+").unwrap());
Lazy::new(|| Regex::new(r"^\s*(\w+)\s*(\(.*?\))?\s*:.+").unwrap());
fn args_section(checker: &mut Checker, docstring: &Docstring, context: &SectionContext) {
let mut args_sections: Vec<String> = vec![];
for line in textwrap::dedent(&context.following_lines.join("\n"))
.trim()
.lines()
{
if line.chars().next().map_or(true, char::is_whitespace) {
// This is a continuation of documentation for the last
// parameter because it does start with whitespace.
if let Some(current) = args_sections.last_mut() {
current.push_str(line);
}
} else {
// This line is the start of documentation for the next
// parameter because it doesn't start with any whitespace.
args_sections.push(line.to_string());
let mut matches = Vec::new();
for line in context.following_lines {
if let Some(captures) = GOOGLE_ARGS_REGEX.captures(line) {
matches.push(captures);
}
}
missing_args(
checker,
docstring,
// Collect the list of arguments documented in the docstring.
&args_sections
&matches
.iter()
.filter_map(
|section| match GOOGLE_ARGS_REGEX.captures(section.as_str()) {
Some(caps) => caps.get(1).map(|arg_name| arg_name.as_str()),
None => None,
},
)
.filter_map(|captures| captures.get(1).map(|arg_name| arg_name.as_str()))
.collect(),
);
}

View File

@@ -69,6 +69,17 @@ expression: checks
row: 367
column: 11
fix: ~
- kind:
DocumentAllArguments:
- skip
- verbose
location:
row: 370
column: 4
end_location:
row: 382
column: 11
fix: ~
- kind:
DocumentAllArguments:
- y

View File

@@ -96,6 +96,7 @@ mod tests {
#[test_case(CheckCode::F821, Path::new("F821_4.py"); "F821_4")]
#[test_case(CheckCode::F821, Path::new("F821_5.py"); "F821_5")]
#[test_case(CheckCode::F821, Path::new("F821_6.py"); "F821_6")]
#[test_case(CheckCode::F821, Path::new("F821_7.py"); "F821_7")]
#[test_case(CheckCode::F822, Path::new("F822.py"); "F822")]
#[test_case(CheckCode::F823, Path::new("F823.py"); "F823")]
#[test_case(CheckCode::F831, Path::new("F831.py"); "F831")]

View File

@@ -0,0 +1,32 @@
---
source: src/pyflakes/mod.rs
expression: checks
---
- kind:
UndefinedName: Undefined
location:
row: 11
column: 20
end_location:
row: 11
column: 31
fix: ~
- kind:
UndefinedName: Undefined
location:
row: 12
column: 25
end_location:
row: 12
column: 36
fix: ~
- kind:
UndefinedName: Undefined
location:
row: 13
column: 20
end_location:
row: 13
column: 31
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

@@ -13,7 +13,7 @@ pub fn used_prior_global_declaration(checker: &mut Checker, name: &str, expr: &E
_ => return,
};
if let Some(stmt) = globals.get(name) {
if expr.location < stmt.location {
if checker.range_for(expr).location < stmt.location {
checker.add_check(Check::new(
CheckKind::UsedPriorGlobalDeclaration(name.to_string(), stmt.location.row()),
Range::from_located(expr),

View File

@@ -134,4 +134,15 @@ expression: checks
row: 105
column: 9
fix: ~
- kind:
UsedPriorGlobalDeclaration:
- x
- 114
location:
row: 113
column: 10
end_location:
row: 113
column: 17
fix: ~

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