Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51bda28a7d | ||
|
|
cc26051b7a | ||
|
|
3ac5a9aa31 | ||
|
|
451047c30d | ||
|
|
6907df489b | ||
|
|
970f882b03 | ||
|
|
3eff9a2860 | ||
|
|
a4a24a0ef3 | ||
|
|
48e3c046b0 | ||
|
|
03e4f5be8a | ||
|
|
99657b7d92 | ||
|
|
40377aa1fc | ||
|
|
2a37017e8c | ||
|
|
ff66d08cef | ||
|
|
dad8035eef | ||
|
|
bf5fec342c | ||
|
|
66a6c81ebf | ||
|
|
5c70f5044b | ||
|
|
953d141ab2 | ||
|
|
07dba46039 | ||
|
|
3b02da9d7b | ||
|
|
20234c6156 | ||
|
|
de767cc026 | ||
|
|
ce1663d302 | ||
|
|
f40e4bcd14 | ||
|
|
e7d40d435f | ||
|
|
ef8fe31c0c | ||
|
|
226f682c99 | ||
|
|
468ffd29fb | ||
|
|
a61126ab23 | ||
|
|
54c7c25861 | ||
|
|
eff7700d92 | ||
|
|
8934f6938d | ||
|
|
8f0fc3033a |
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.188
|
||||
rev: v0.0.192
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
20
Cargo.lock
generated
20
Cargo.lock
generated
@@ -724,7 +724,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.188-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.188"
|
||||
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.188"
|
||||
version = "0.0.192"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
@@ -1919,7 +1922,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.188"
|
||||
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"
|
||||
|
||||
@@ -6,7 +6,7 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.188"
|
||||
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.188", 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" }
|
||||
|
||||
54
README.md
54
README.md
@@ -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).
|
||||
|
||||
@@ -158,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.188
|
||||
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
|
||||
@@ -517,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` | 🛠 |
|
||||
@@ -623,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)
|
||||
|
||||
@@ -1219,7 +1225,7 @@ natively, including:
|
||||
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (16/33)
|
||||
- [`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
|
||||
@@ -1276,7 +1282,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
Ruff can also replace [`isort`](https://pypi.org/project/isort/),
|
||||
[`yesqa`](https://github.com/asottile/yesqa), [`eradicate`](https://pypi.org/project/eradicate/),
|
||||
[`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10), and a subset of the rules
|
||||
implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (16/33).
|
||||
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.
|
||||
|
||||
@@ -1632,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
|
||||
@@ -1765,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),
|
||||
@@ -1946,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**: `["."]`
|
||||
|
||||
|
||||
4
flake8_to_ruff/Cargo.lock
generated
4
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.188"
|
||||
version = "0.0.192"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.188"
|
||||
version = "0.0.192"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.188-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]
|
||||
|
||||
65
flake8_to_ruff/examples/cryptography/pyproject.toml
Normal file
65
flake8_to_ruff/examples/cryptography/pyproject.toml
Normal 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",
|
||||
]
|
||||
91
flake8_to_ruff/examples/cryptography/setup.cfg
Normal file
91
flake8_to_ruff/examples/cryptography/setup.cfg
Normal 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
|
||||
32
flake8_to_ruff/src/black.rs
Normal file
32
flake8_to_ruff/src/black.rs
Normal 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))
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
pub mod black;
|
||||
pub mod converter;
|
||||
mod parser;
|
||||
pub mod plugin;
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
@@ -42,7 +43,7 @@ impl FromStr for Plugin {
|
||||
"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),
|
||||
@@ -58,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 {
|
||||
@@ -78,11 +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,
|
||||
Plugin::PandasVet => CheckCodePrefix::PD,
|
||||
Plugin::PEP8Naming => CheckCodePrefix::N,
|
||||
Plugin::Pyupgrade => CheckCodePrefix::U,
|
||||
Plugin::Pyupgrade => CheckCodePrefix::UP,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,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| {
|
||||
|
||||
@@ -32,6 +32,8 @@ build-backend = "maturin"
|
||||
bindings = "bin"
|
||||
strip = true
|
||||
|
||||
[tool.ruff]
|
||||
|
||||
[tool.ruff.isort]
|
||||
force-wrap-aliases = true
|
||||
combine-as-imports = true
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -6,6 +6,9 @@ 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)
|
||||
|
||||
|
||||
@@ -23,6 +23,12 @@ 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
|
||||
|
||||
27
resources/test/fixtures/flake8_return/RET504.py
vendored
27
resources/test/fixtures/flake8_return/RET504.py
vendored
@@ -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:
|
||||
|
||||
61
resources/test/fixtures/pycodestyle/E40.py
vendored
Normal file
61
resources/test/fixtures/pycodestyle/E40.py
vendored
Normal 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
|
||||
13
resources/test/fixtures/pyflakes/F821_7.py
vendored
Normal file
13
resources/test/fixtures/pyflakes/F821_7.py
vendored
Normal 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")
|
||||
@@ -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=}")
|
||||
|
||||
11
resources/test/fixtures/pyupgrade/UP017.py
vendored
Normal file
11
resources/test/fixtures/pyupgrade/UP017.py
vendored
Normal 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)
|
||||
7
resources/test/fixtures/ruff/RUF100.py
vendored
7
resources/test/fixtures/ruff/RUF100.py
vendored
@@ -79,3 +79,10 @@ _ = """Here's a source: https://github.com/ethereum/web3.py/blob/ffe59daf10edc19
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.188"
|
||||
version = "0.0.192"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -10,7 +10,7 @@ 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;
|
||||
|
||||
#[derive(Parser)]
|
||||
@@ -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![{}],",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.188"
|
||||
version = "0.0.192"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
22
src/cache.rs
22
src/cache.rs
@@ -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.
|
||||
|
||||
@@ -156,19 +156,14 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
@@ -189,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);
|
||||
@@ -626,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(
|
||||
@@ -1542,6 +1553,12 @@ 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);
|
||||
}
|
||||
@@ -2507,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);
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
121
src/checks.rs
121
src/checks.rs
@@ -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,
|
||||
@@ -644,6 +646,7 @@ pub enum CheckKind {
|
||||
IOError(String),
|
||||
LineTooLong(usize, usize),
|
||||
ModuleImportNotAtTopOfFile,
|
||||
MultipleImportsOnOneLine,
|
||||
NoneComparison(RejectedCmpop),
|
||||
NotInTest,
|
||||
NotIsTest,
|
||||
@@ -825,6 +828,7 @@ pub enum CheckKind {
|
||||
ConvertNamedTupleFunctionalToClass(String),
|
||||
RedundantOpenModes,
|
||||
RemoveSixCompat,
|
||||
DatetimeTimezoneUTC,
|
||||
// pydocstyle
|
||||
BlankLineAfterLastSection(String),
|
||||
BlankLineAfterSection(String),
|
||||
@@ -978,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),
|
||||
@@ -1197,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,
|
||||
@@ -1462,6 +1468,7 @@ impl CheckCode {
|
||||
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,
|
||||
@@ -1613,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,
|
||||
@@ -1657,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,
|
||||
@@ -1822,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,
|
||||
@@ -2016,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()
|
||||
}
|
||||
@@ -2543,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")
|
||||
}
|
||||
@@ -2979,6 +2991,7 @@ impl CheckKind {
|
||||
| CheckKind::RedundantOpenModes
|
||||
| CheckKind::RedundantTupleInExceptionHandler(..)
|
||||
| CheckKind::RemoveSixCompat
|
||||
| CheckKind::DatetimeTimezoneUTC
|
||||
| CheckKind::SectionNameEndsInColon(..)
|
||||
| CheckKind::SectionNotOverIndented(..)
|
||||
| CheckKind::SectionUnderlineAfterName(..)
|
||||
@@ -3043,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([
|
||||
@@ -3062,6 +3150,7 @@ 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),
|
||||
@@ -3078,28 +3167,20 @@ pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(
|
||||
("PDV013", CheckCode::PD013),
|
||||
("PDV015", CheckCode::PD015),
|
||||
("PDV901", CheckCode::PD901),
|
||||
])
|
||||
});
|
||||
|
||||
/// 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"),
|
||||
("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.
|
||||
("PDV", "PD"),
|
||||
("PDV0", "PD0"),
|
||||
("PDV01", "PD01"),
|
||||
("PDV9", "PD9"),
|
||||
("PDV90", "PD90"),
|
||||
("IC001", CheckCode::ICN001),
|
||||
("IC002", CheckCode::ICN001),
|
||||
("IC003", CheckCode::ICN001),
|
||||
("IC004", CheckCode::ICN001),
|
||||
])
|
||||
});
|
||||
|
||||
|
||||
@@ -179,6 +179,7 @@ pub enum CheckCodePrefix {
|
||||
E,
|
||||
E4,
|
||||
E40,
|
||||
E401,
|
||||
E402,
|
||||
E5,
|
||||
E50,
|
||||
@@ -291,6 +292,12 @@ pub enum CheckCodePrefix {
|
||||
I2,
|
||||
I25,
|
||||
I252,
|
||||
IC,
|
||||
IC0,
|
||||
IC001,
|
||||
IC002,
|
||||
IC003,
|
||||
IC004,
|
||||
ICN,
|
||||
ICN0,
|
||||
ICN00,
|
||||
@@ -410,6 +417,17 @@ pub enum CheckCodePrefix {
|
||||
Q001,
|
||||
Q002,
|
||||
Q003,
|
||||
R,
|
||||
R5,
|
||||
R50,
|
||||
R501,
|
||||
R502,
|
||||
R503,
|
||||
R504,
|
||||
R505,
|
||||
R506,
|
||||
R507,
|
||||
R508,
|
||||
RET,
|
||||
RET5,
|
||||
RET50,
|
||||
@@ -474,6 +492,7 @@ pub enum CheckCodePrefix {
|
||||
U014,
|
||||
U015,
|
||||
U016,
|
||||
U017,
|
||||
UP,
|
||||
UP0,
|
||||
UP00,
|
||||
@@ -493,6 +512,7 @@ pub enum CheckCodePrefix {
|
||||
UP014,
|
||||
UP015,
|
||||
UP016,
|
||||
UP017,
|
||||
W,
|
||||
W2,
|
||||
W29,
|
||||
@@ -1046,6 +1066,7 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::DTZ011 => vec![CheckCode::DTZ011],
|
||||
CheckCodePrefix::DTZ012 => vec![CheckCode::DTZ012],
|
||||
CheckCodePrefix::E => vec![
|
||||
CheckCode::E401,
|
||||
CheckCode::E402,
|
||||
CheckCode::E501,
|
||||
CheckCode::E711,
|
||||
@@ -1061,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],
|
||||
@@ -1343,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],
|
||||
@@ -1764,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,
|
||||
@@ -1885,6 +2087,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
CheckCode::UP016,
|
||||
CheckCode::UP017,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U0 => {
|
||||
@@ -1910,6 +2113,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
CheckCode::UP016,
|
||||
CheckCode::UP017,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U00 => {
|
||||
@@ -2017,6 +2221,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
CheckCode::UP016,
|
||||
CheckCode::UP017,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U010 => {
|
||||
@@ -2082,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,
|
||||
@@ -2098,6 +2312,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
CheckCode::UP016,
|
||||
CheckCode::UP017,
|
||||
],
|
||||
CheckCodePrefix::UP0 => vec![
|
||||
CheckCode::UP001,
|
||||
@@ -2115,6 +2330,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
CheckCode::UP016,
|
||||
CheckCode::UP017,
|
||||
],
|
||||
CheckCodePrefix::UP00 => vec![
|
||||
CheckCode::UP001,
|
||||
@@ -2142,6 +2358,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
CheckCode::UP016,
|
||||
CheckCode::UP017,
|
||||
],
|
||||
CheckCodePrefix::UP010 => vec![CheckCode::UP010],
|
||||
CheckCodePrefix::UP011 => vec![CheckCode::UP011],
|
||||
@@ -2150,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],
|
||||
@@ -2371,6 +2589,7 @@ impl CheckCodePrefix {
|
||||
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,
|
||||
@@ -2483,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,
|
||||
@@ -2602,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,
|
||||
@@ -2666,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,
|
||||
@@ -2685,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,
|
||||
|
||||
@@ -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,
|
||||
@@ -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),
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -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`.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -19,20 +19,14 @@ pub fn call_datetime_without_tzinfo(
|
||||
return;
|
||||
}
|
||||
|
||||
// no args / no args unqualified
|
||||
if args.len() < 8 && keywords.is_empty() {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// none args
|
||||
if args.len() == 8 && is_const_none(&args[7]) {
|
||||
checker.add_check(Check::new(CheckKind::CallDatetimeWithoutTzinfo, location));
|
||||
return;
|
||||
}
|
||||
|
||||
// no kwargs / none kwargs
|
||||
if !has_non_none_keyword(keywords, "tzinfo") {
|
||||
// Positional arg: is constant None.
|
||||
if args.len() >= 8 && is_const_none(&args[7]) {
|
||||
checker.add_check(Check::new(CheckKind::CallDatetimeWithoutTzinfo, location));
|
||||
}
|
||||
}
|
||||
@@ -177,22 +171,17 @@ pub fn call_datetime_strptime_without_zone(
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(ExprKind::Constant {
|
||||
// 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) else {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::CallDatetimeStrptimeWithoutZone,
|
||||
location,
|
||||
));
|
||||
return;
|
||||
}) = args.get(1).as_ref().map(|arg| &arg.node)
|
||||
{
|
||||
if format.contains("%z") {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Does the `strptime` call contain a format string with a timezone specifier?
|
||||
if format.contains("%z") {
|
||||
return;
|
||||
}
|
||||
|
||||
let (Some(grandparent), Some(parent)) = (checker.current_expr_grandparent(), checker.current_expr_parent()) else {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::CallDatetimeStrptimeWithoutZone,
|
||||
|
||||
@@ -20,26 +20,26 @@ expression: checks
|
||||
fix: ~
|
||||
- kind: CallDatetimeWithoutTzinfo
|
||||
location:
|
||||
row: 10
|
||||
row: 13
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
row: 13
|
||||
column: 37
|
||||
fix: ~
|
||||
- kind: CallDatetimeWithoutTzinfo
|
||||
location:
|
||||
row: 13
|
||||
row: 16
|
||||
column: 0
|
||||
end_location:
|
||||
row: 13
|
||||
row: 16
|
||||
column: 42
|
||||
fix: ~
|
||||
- kind: CallDatetimeWithoutTzinfo
|
||||
location:
|
||||
row: 18
|
||||
row: 21
|
||||
column: 0
|
||||
end_location:
|
||||
row: 18
|
||||
row: 21
|
||||
column: 29
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -36,10 +36,10 @@ expression: checks
|
||||
fix: ~
|
||||
- kind: CallDatetimeStrptimeWithoutZone
|
||||
location:
|
||||
row: 29
|
||||
row: 35
|
||||
column: 0
|
||||
end_location:
|
||||
row: 29
|
||||
row: 35
|
||||
column: 43
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -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: ~
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -260,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,
|
||||
@@ -269,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) {
|
||||
|
||||
13
src/main.rs
13
src/main.rs
@@ -103,6 +103,10 @@ fn inner_main() -> Result<ExitCode> {
|
||||
// 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,
|
||||
@@ -220,8 +224,13 @@ fn inner_main() -> Result<ExitCode> {
|
||||
|
||||
// Generate lint violations.
|
||||
let diagnostics = if is_stdin {
|
||||
let path = cli.stdin_filename.unwrap_or_else(|| PathBuf::from("-"));
|
||||
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,
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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: ~
|
||||
|
||||
@@ -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: ~
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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: ~
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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: ~
|
||||
|
||||
|
||||
@@ -105,4 +105,18 @@ mod tests {
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn datetime_utc_alias_py311() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/pyupgrade/UP017.py"),
|
||||
&settings::Settings {
|
||||
target_version: PythonVersion::Py311,
|
||||
..settings::Settings::for_rule(CheckCode::UP017)
|
||||
},
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
25
src/pyupgrade/plugins/datetime_utc_alias.rs
Normal file
25
src/pyupgrade/plugins/datetime_utc_alias.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use rustpython_ast::Expr;
|
||||
|
||||
use crate::ast::helpers::{collect_call_paths, compose_call_path, dealias_call_path};
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
|
||||
/// UP017
|
||||
pub fn datetime_utc_alias(checker: &mut Checker, expr: &Expr) {
|
||||
let dealiased_call_path = dealias_call_path(collect_call_paths(expr), &checker.import_aliases);
|
||||
if dealiased_call_path == ["datetime", "timezone", "utc"] {
|
||||
let mut check = Check::new(CheckKind::DatetimeTimezoneUTC, Range::from_located(expr));
|
||||
if checker.patch(&CheckCode::UP017) {
|
||||
check.amend(Fix::replacement(
|
||||
compose_call_path(expr)
|
||||
.unwrap()
|
||||
.replace("timezone.utc", "UTC"),
|
||||
expr.location,
|
||||
expr.end_location.unwrap(),
|
||||
));
|
||||
}
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub use convert_named_tuple_functional_to_class::convert_named_tuple_functional_to_class;
|
||||
pub use convert_typed_dict_functional_to_class::convert_typed_dict_functional_to_class;
|
||||
pub use datetime_utc_alias::datetime_utc_alias;
|
||||
pub use deprecated_unittest_alias::deprecated_unittest_alias;
|
||||
pub use redundant_open_modes::redundant_open_modes;
|
||||
pub use remove_six_compat::remove_six_compat;
|
||||
@@ -15,6 +16,7 @@ pub use useless_object_inheritance::useless_object_inheritance;
|
||||
|
||||
mod convert_named_tuple_functional_to_class;
|
||||
mod convert_typed_dict_functional_to_class;
|
||||
mod datetime_utc_alias;
|
||||
mod deprecated_unittest_alias;
|
||||
mod redundant_open_modes;
|
||||
mod remove_six_compat;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
source: src/pyupgrade/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: DatetimeTimezoneUTC
|
||||
location:
|
||||
row: 10
|
||||
column: 6
|
||||
end_location:
|
||||
row: 10
|
||||
column: 27
|
||||
fix:
|
||||
content: datetime.UTC
|
||||
location:
|
||||
row: 10
|
||||
column: 6
|
||||
end_location:
|
||||
row: 10
|
||||
column: 27
|
||||
- kind: DatetimeTimezoneUTC
|
||||
location:
|
||||
row: 11
|
||||
column: 6
|
||||
end_location:
|
||||
row: 11
|
||||
column: 21
|
||||
fix:
|
||||
content: dt.UTC
|
||||
location:
|
||||
row: 11
|
||||
column: 6
|
||||
end_location:
|
||||
row: 11
|
||||
column: 21
|
||||
|
||||
105
src/resolver.rs
105
src/resolver.rs
@@ -20,6 +20,7 @@ use crate::settings::{pyproject, Settings};
|
||||
/// The strategy used to discover Python files in the filesystem..
|
||||
#[derive(Debug)]
|
||||
pub struct FileDiscovery {
|
||||
pub force_exclude: bool,
|
||||
pub respect_gitignore: bool,
|
||||
}
|
||||
|
||||
@@ -163,8 +164,8 @@ pub fn resolve_settings(
|
||||
|
||||
/// Return `true` if the given file should be ignored based on the exclusion
|
||||
/// criteria.
|
||||
fn is_excluded(file_path: &str, file_basename: &str, exclude: &globset::GlobSet) -> bool {
|
||||
exclude.is_match(file_path) || exclude.is_match(file_basename)
|
||||
fn match_exclusion(file_path: &str, file_basename: &str, exclusion: &globset::GlobSet) -> bool {
|
||||
exclusion.is_match(file_path) || exclusion.is_match(file_basename)
|
||||
}
|
||||
|
||||
/// Return `true` if the `Path` appears to be that of a Python file.
|
||||
@@ -189,7 +190,7 @@ pub fn python_files_in_path(
|
||||
overrides: &Overrides,
|
||||
) -> Result<(Vec<Result<DirEntry, ignore::Error>>, Resolver)> {
|
||||
// Normalize every path (e.g., convert from relative to absolute).
|
||||
let paths: Vec<PathBuf> = paths.iter().map(fs::normalize_path).collect();
|
||||
let mut paths: Vec<PathBuf> = paths.iter().map(fs::normalize_path).collect();
|
||||
|
||||
// Search for `pyproject.toml` files in all parent directories.
|
||||
let mut resolver = Resolver::default();
|
||||
@@ -206,6 +207,14 @@ pub fn python_files_in_path(
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the paths themselves are excluded.
|
||||
if file_strategy.force_exclude {
|
||||
paths.retain(|path| !is_file_excluded(path, &resolver, pyproject_strategy));
|
||||
if paths.is_empty() {
|
||||
return Ok((vec![], resolver));
|
||||
}
|
||||
}
|
||||
|
||||
// Create the `WalkBuilder`.
|
||||
let mut builder = WalkBuilder::new(
|
||||
paths
|
||||
@@ -270,12 +279,16 @@ pub fn python_files_in_path(
|
||||
match fs::extract_path_names(path) {
|
||||
Ok((file_path, file_basename)) => {
|
||||
if !settings.exclude.is_empty()
|
||||
&& is_excluded(file_path, file_basename, &settings.exclude)
|
||||
&& match_exclusion(file_path, file_basename, &settings.exclude)
|
||||
{
|
||||
debug!("Ignored path via `exclude`: {:?}", path);
|
||||
return WalkState::Skip;
|
||||
} else if !settings.extend_exclude.is_empty()
|
||||
&& is_excluded(file_path, file_basename, &settings.extend_exclude)
|
||||
&& match_exclusion(
|
||||
file_path,
|
||||
file_basename,
|
||||
&settings.extend_exclude,
|
||||
)
|
||||
{
|
||||
debug!("Ignored path via `extend-exclude`: {:?}", path);
|
||||
return WalkState::Skip;
|
||||
@@ -302,6 +315,72 @@ pub fn python_files_in_path(
|
||||
Ok((files.into_inner().unwrap(), resolver.into_inner().unwrap()))
|
||||
}
|
||||
|
||||
/// Return `true` if the Python file at `Path` is _not_ excluded.
|
||||
pub fn python_file_at_path(
|
||||
path: &Path,
|
||||
pyproject_strategy: &PyprojectDiscovery,
|
||||
file_strategy: &FileDiscovery,
|
||||
overrides: &Overrides,
|
||||
) -> Result<bool> {
|
||||
if !file_strategy.force_exclude {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// Normalize the path (e.g., convert from relative to absolute).
|
||||
let path = fs::normalize_path(path);
|
||||
|
||||
// Search for `pyproject.toml` files in all parent directories.
|
||||
let mut resolver = Resolver::default();
|
||||
for ancestor in path.ancestors() {
|
||||
let pyproject = ancestor.join("pyproject.toml");
|
||||
if pyproject.is_file() {
|
||||
if has_ruff_section(&pyproject)? {
|
||||
let (root, settings) =
|
||||
resolve_scoped_settings(&pyproject, &Relativity::Parent, Some(overrides))?;
|
||||
resolver.add(root, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check exclusions.
|
||||
Ok(!is_file_excluded(&path, &resolver, pyproject_strategy))
|
||||
}
|
||||
|
||||
/// Return `true` if the given top-level `Path` should be excluded.
|
||||
fn is_file_excluded(
|
||||
path: &Path,
|
||||
resolver: &Resolver,
|
||||
pyproject_strategy: &PyprojectDiscovery,
|
||||
) -> bool {
|
||||
// TODO(charlie): Respect gitignore.
|
||||
for path in path.ancestors() {
|
||||
if path.file_name().is_none() {
|
||||
break;
|
||||
}
|
||||
let settings = resolver.resolve(path, pyproject_strategy);
|
||||
match fs::extract_path_names(path) {
|
||||
Ok((file_path, file_basename)) => {
|
||||
if !settings.exclude.is_empty()
|
||||
&& match_exclusion(file_path, file_basename, &settings.exclude)
|
||||
{
|
||||
debug!("Ignored path via `exclude`: {:?}", path);
|
||||
return true;
|
||||
} else if !settings.extend_exclude.is_empty()
|
||||
&& match_exclusion(file_path, file_basename, &settings.extend_exclude)
|
||||
{
|
||||
debug!("Ignored path via `extend-exclude`: {:?}", path);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
debug!("Ignored path due to error in parsing: {:?}: {}", path, err);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
@@ -311,7 +390,7 @@ mod tests {
|
||||
use path_absolutize::Absolutize;
|
||||
|
||||
use crate::fs;
|
||||
use crate::resolver::{is_excluded, is_python_path};
|
||||
use crate::resolver::{is_python_path, match_exclusion};
|
||||
use crate::settings::types::FilePattern;
|
||||
|
||||
#[test]
|
||||
@@ -348,7 +427,7 @@ mod tests {
|
||||
.to_path_buf(),
|
||||
);
|
||||
let (file_path, file_basename) = fs::extract_path_names(&path)?;
|
||||
assert!(is_excluded(
|
||||
assert!(match_exclusion(
|
||||
file_path,
|
||||
file_basename,
|
||||
&make_exclusion(exclude,)
|
||||
@@ -363,7 +442,7 @@ mod tests {
|
||||
.to_path_buf(),
|
||||
);
|
||||
let (file_path, file_basename) = fs::extract_path_names(&path)?;
|
||||
assert!(is_excluded(
|
||||
assert!(match_exclusion(
|
||||
file_path,
|
||||
file_basename,
|
||||
&make_exclusion(exclude,)
|
||||
@@ -380,7 +459,7 @@ mod tests {
|
||||
.to_path_buf(),
|
||||
);
|
||||
let (file_path, file_basename) = fs::extract_path_names(&path)?;
|
||||
assert!(is_excluded(
|
||||
assert!(match_exclusion(
|
||||
file_path,
|
||||
file_basename,
|
||||
&make_exclusion(exclude,)
|
||||
@@ -395,7 +474,7 @@ mod tests {
|
||||
.to_path_buf(),
|
||||
);
|
||||
let (file_path, file_basename) = fs::extract_path_names(&path)?;
|
||||
assert!(is_excluded(
|
||||
assert!(match_exclusion(
|
||||
file_path,
|
||||
file_basename,
|
||||
&make_exclusion(exclude,)
|
||||
@@ -412,7 +491,7 @@ mod tests {
|
||||
.to_path_buf(),
|
||||
);
|
||||
let (file_path, file_basename) = fs::extract_path_names(&path)?;
|
||||
assert!(is_excluded(
|
||||
assert!(match_exclusion(
|
||||
file_path,
|
||||
file_basename,
|
||||
&make_exclusion(exclude,)
|
||||
@@ -429,7 +508,7 @@ mod tests {
|
||||
.to_path_buf(),
|
||||
);
|
||||
let (file_path, file_basename) = fs::extract_path_names(&path)?;
|
||||
assert!(is_excluded(
|
||||
assert!(match_exclusion(
|
||||
file_path,
|
||||
file_basename,
|
||||
&make_exclusion(exclude,)
|
||||
@@ -446,7 +525,7 @@ mod tests {
|
||||
.to_path_buf(),
|
||||
);
|
||||
let (file_path, file_basename) = fs::extract_path_names(&path)?;
|
||||
assert!(!is_excluded(
|
||||
assert!(!match_exclusion(
|
||||
file_path,
|
||||
file_basename,
|
||||
&make_exclusion(exclude,)
|
||||
|
||||
@@ -38,6 +38,7 @@ mod tests {
|
||||
&settings::Settings::for_rules(vec![
|
||||
CheckCode::RUF100,
|
||||
CheckCode::E501,
|
||||
CheckCode::F401,
|
||||
CheckCode::F841,
|
||||
]),
|
||||
)?;
|
||||
|
||||
@@ -182,4 +182,22 @@ expression: checks
|
||||
end_location:
|
||||
row: 71
|
||||
column: 11
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- shelve
|
||||
- false
|
||||
location:
|
||||
row: 85
|
||||
column: 7
|
||||
end_location:
|
||||
row: 85
|
||||
column: 13
|
||||
fix:
|
||||
content: ""
|
||||
location:
|
||||
row: 85
|
||||
column: 0
|
||||
end_location:
|
||||
row: 86
|
||||
column: 0
|
||||
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
//! command-line options. Structure mirrors the user-facing representation of
|
||||
//! the various parameters.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::env::VarError;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use glob::{glob, GlobError, Paths, PatternError};
|
||||
use regex::Regex;
|
||||
use shellexpand;
|
||||
use shellexpand::LookupError;
|
||||
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::cli::{collect_per_file_ignores, Overrides};
|
||||
@@ -31,6 +35,7 @@ pub struct Configuration {
|
||||
pub fix: Option<bool>,
|
||||
pub fixable: Option<Vec<CheckCodePrefix>>,
|
||||
pub format: Option<SerializationFormat>,
|
||||
pub force_exclude: Option<bool>,
|
||||
pub ignore: Option<Vec<CheckCodePrefix>>,
|
||||
pub ignore_init_module_imports: Option<bool>,
|
||||
pub line_length: Option<usize>,
|
||||
@@ -77,7 +82,14 @@ impl Configuration {
|
||||
})
|
||||
.collect()
|
||||
}),
|
||||
extend: options.extend.map(PathBuf::from),
|
||||
extend: options
|
||||
.extend
|
||||
.map(|extend| {
|
||||
let extend = shellexpand::full(&extend);
|
||||
extend.map(|extend| PathBuf::from(extend.as_ref()))
|
||||
})
|
||||
.transpose()
|
||||
.map_err(|e| anyhow!("Invalid `extend` value: {e}"))?,
|
||||
extend_exclude: options
|
||||
.extend_exclude
|
||||
.map(|paths| {
|
||||
@@ -96,6 +108,7 @@ impl Configuration {
|
||||
fix: options.fix,
|
||||
fixable: options.fixable,
|
||||
format: options.format,
|
||||
force_exclude: options.force_exclude,
|
||||
ignore: options.ignore,
|
||||
ignore_init_module_imports: options.ignore_init_module_imports,
|
||||
line_length: options.line_length,
|
||||
@@ -159,6 +172,7 @@ impl Configuration {
|
||||
fix: self.fix.or(config.fix),
|
||||
fixable: self.fixable.or(config.fixable),
|
||||
format: self.format.or(config.format),
|
||||
force_exclude: self.force_exclude.or(config.force_exclude),
|
||||
ignore: self.ignore.or(config.ignore),
|
||||
ignore_init_module_imports: self
|
||||
.ignore_init_module_imports
|
||||
@@ -208,6 +222,9 @@ impl Configuration {
|
||||
if let Some(format) = overrides.format {
|
||||
self.format = Some(format);
|
||||
}
|
||||
if let Some(force_exclude) = overrides.force_exclude {
|
||||
self.force_exclude = Some(force_exclude);
|
||||
}
|
||||
if let Some(ignore) = overrides.ignore {
|
||||
self.ignore = Some(ignore);
|
||||
}
|
||||
@@ -260,9 +277,13 @@ impl Configuration {
|
||||
/// Given a list of source paths, which could include glob patterns, resolve the
|
||||
/// matching paths.
|
||||
pub fn resolve_src(src: &[String], project_root: &Path) -> Result<Vec<PathBuf>> {
|
||||
let globs = src
|
||||
let expansions = src
|
||||
.iter()
|
||||
.map(Path::new)
|
||||
.map(shellexpand::full)
|
||||
.collect::<Result<Vec<Cow<'_, str>>, LookupError<VarError>>>()?;
|
||||
let globs = expansions
|
||||
.iter()
|
||||
.map(|path| Path::new(path.as_ref()))
|
||||
.map(|path| fs::normalize_path_to(path, project_root))
|
||||
.map(|path| glob(&path.to_string_lossy()))
|
||||
.collect::<Result<Vec<Paths>, PatternError>>()?;
|
||||
|
||||
@@ -41,6 +41,7 @@ pub struct Settings {
|
||||
pub fix: bool,
|
||||
pub fixable: FxHashSet<CheckCode>,
|
||||
pub format: SerializationFormat,
|
||||
pub force_exclude: bool,
|
||||
pub ignore_init_module_imports: bool,
|
||||
pub line_length: usize,
|
||||
pub per_file_ignores: Vec<(GlobMatcher, GlobMatcher, FxHashSet<CheckCode>)>,
|
||||
@@ -127,6 +128,7 @@ impl Settings {
|
||||
.into_iter(),
|
||||
),
|
||||
format: config.format.unwrap_or(SerializationFormat::Text),
|
||||
force_exclude: config.force_exclude.unwrap_or(false),
|
||||
ignore_init_module_imports: config.ignore_init_module_imports.unwrap_or_default(),
|
||||
line_length: config.line_length.unwrap_or(88),
|
||||
per_file_ignores: resolve_per_file_ignores(
|
||||
@@ -199,6 +201,7 @@ impl Settings {
|
||||
fix: false,
|
||||
fixable: FxHashSet::from_iter([check_code]),
|
||||
format: SerializationFormat::Text,
|
||||
force_exclude: false,
|
||||
ignore_init_module_imports: false,
|
||||
line_length: 88,
|
||||
per_file_ignores: vec![],
|
||||
@@ -231,6 +234,7 @@ impl Settings {
|
||||
fix: false,
|
||||
fixable: FxHashSet::from_iter(check_codes),
|
||||
format: SerializationFormat::Text,
|
||||
force_exclude: false,
|
||||
ignore_init_module_imports: false,
|
||||
line_length: 88,
|
||||
per_file_ignores: vec![],
|
||||
@@ -279,6 +283,7 @@ impl Hash for Settings {
|
||||
}
|
||||
}
|
||||
self.show_source.hash(state);
|
||||
self.src.hash(state);
|
||||
self.target_version.hash(state);
|
||||
// Add plugin properties in alphabetical order.
|
||||
self.flake8_annotations.hash(state);
|
||||
|
||||
@@ -67,7 +67,8 @@ pub struct Options {
|
||||
pub exclude: Option<Vec<String>>,
|
||||
#[option(
|
||||
doc = r#"
|
||||
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
|
||||
@@ -165,6 +166,24 @@ pub struct Options {
|
||||
"#
|
||||
)]
|
||||
pub format: Option<SerializationFormat>,
|
||||
#[option(
|
||||
doc = r#"
|
||||
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 = r#"false"#,
|
||||
value_type = "bool",
|
||||
example = r#"
|
||||
force-exclude = true
|
||||
"#
|
||||
)]
|
||||
pub force_exclude: Option<bool>,
|
||||
#[option(
|
||||
doc = r"
|
||||
A list of check code prefixes to ignore. Prefixes can specify exact checks (like
|
||||
@@ -267,7 +286,8 @@ pub struct Options {
|
||||
|
||||
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 = r#"["."]"#,
|
||||
value_type = "Vec<PathBuf>",
|
||||
|
||||
@@ -129,6 +129,8 @@ mod tests {
|
||||
external: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
format: None,
|
||||
force_exclude: None,
|
||||
ignore: None,
|
||||
ignore_init_module_imports: None,
|
||||
line_length: None,
|
||||
@@ -138,7 +140,6 @@ mod tests {
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
format: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
@@ -176,6 +177,8 @@ line-length = 79
|
||||
external: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
force_exclude: None,
|
||||
format: None,
|
||||
ignore: None,
|
||||
ignore_init_module_imports: None,
|
||||
line_length: Some(79),
|
||||
@@ -185,7 +188,6 @@ line-length = 79
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
format: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
@@ -214,26 +216,27 @@ exclude = ["foo.py"]
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
allowed_confusables: None,
|
||||
line_length: None,
|
||||
fix: None,
|
||||
extend: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: Some(vec!["foo.py".to_string()]),
|
||||
extend: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
external: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
force_exclude: None,
|
||||
format: None,
|
||||
ignore: None,
|
||||
ignore_init_module_imports: None,
|
||||
extend_ignore: None,
|
||||
fixable: None,
|
||||
format: None,
|
||||
unfixable: None,
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
respect_gitignore: None,
|
||||
dummy_variable_rgx: None,
|
||||
select: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
show_source: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_errmsg: None,
|
||||
flake8_bugbear: None,
|
||||
@@ -270,6 +273,8 @@ select = ["E501"]
|
||||
external: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
force_exclude: None,
|
||||
format: None,
|
||||
ignore: None,
|
||||
ignore_init_module_imports: None,
|
||||
line_length: None,
|
||||
@@ -279,7 +284,6 @@ select = ["E501"]
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
format: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
@@ -318,6 +322,8 @@ ignore = ["E501"]
|
||||
external: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
force_exclude: None,
|
||||
format: None,
|
||||
ignore: Some(vec![CheckCodePrefix::E501]),
|
||||
ignore_init_module_imports: None,
|
||||
line_length: None,
|
||||
@@ -327,7 +333,6 @@ ignore = ["E501"]
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
format: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
@@ -408,6 +413,7 @@ other-attribute = 1
|
||||
extend_ignore: None,
|
||||
fixable: None,
|
||||
format: None,
|
||||
force_exclude: None,
|
||||
unfixable: None,
|
||||
per_file_ignores: Some(FxHashMap::from_iter([(
|
||||
"__init__.py".to_string(),
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::checks::CheckCode;
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::fs;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialOrd, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum PythonVersion {
|
||||
Py33,
|
||||
|
||||
@@ -23,7 +23,7 @@ fn test_stdin_error() -> Result<()> {
|
||||
.failure();
|
||||
assert_eq!(
|
||||
str::from_utf8(&output.get_output().stdout)?,
|
||||
"Found 1 error(s).\n-:1:8: F401 `os` imported but unused\n1 potentially fixable with the \
|
||||
"-:1:8: F401 `os` imported but unused\nFound 1 error(s).\n1 potentially fixable with the \
|
||||
--fix option.\n"
|
||||
);
|
||||
Ok(())
|
||||
@@ -39,7 +39,7 @@ fn test_stdin_filename() -> Result<()> {
|
||||
.failure();
|
||||
assert_eq!(
|
||||
str::from_utf8(&output.get_output().stdout)?,
|
||||
"Found 1 error(s).\nF401.py:1:8: F401 `os` imported but unused\n1 potentially fixable \
|
||||
"F401.py:1:8: F401 `os` imported but unused\nFound 1 error(s).\n1 potentially fixable \
|
||||
with the --fix option.\n"
|
||||
);
|
||||
Ok(())
|
||||
@@ -55,12 +55,33 @@ fn test_stdin_json() -> Result<()> {
|
||||
.failure();
|
||||
assert_eq!(
|
||||
str::from_utf8(&output.get_output().stdout)?,
|
||||
"[\n {\n \"code\": \"F401\",\n \"message\": \"`os` imported but unused\",\n \
|
||||
\"fix\": {\n \"content\": \"\",\n \"location\": {\n \"row\": 1,\n \
|
||||
\"column\": 0\n },\n \"end_location\": {\n \"row\": 2,\n \
|
||||
\"column\": 0\n }\n },\n \"location\": {\n \"row\": 1,\n \
|
||||
\"column\": 8\n },\n \"end_location\": {\n \"row\": 1,\n \"column\": \
|
||||
10\n },\n \"filename\": \"F401.py\"\n }\n]\n"
|
||||
r#"[
|
||||
{
|
||||
"code": "F401",
|
||||
"message": "`os` imported but unused",
|
||||
"fix": {
|
||||
"content": "",
|
||||
"location": {
|
||||
"row": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end_location": {
|
||||
"row": 2,
|
||||
"column": 0
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"row": 1,
|
||||
"column": 8
|
||||
},
|
||||
"end_location": {
|
||||
"row": 1,
|
||||
"column": 10
|
||||
},
|
||||
"filename": "F401.py"
|
||||
}
|
||||
]
|
||||
"#
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user