mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 05:20:49 -05:00
# This commit was automatically generated by running the following
# script (followed by `cargo +nightly fmt`):
import glob
import re
from typing import NamedTuple
class Rule(NamedTuple):
code: str
name: str
path: str
def rules() -> list[Rule]:
"""Returns all the rules defined in `src/registry.rs`."""
file = open('src/registry.rs')
rules = []
while next(file) != 'ruff_macros::define_rule_mapping!(\n':
continue
while (line := next(file)) != ');\n':
line = line.strip().rstrip(',')
if line.startswith('//'):
continue
code, path = line.split(' => ')
name = path.rsplit('::')[-1]
rules.append(Rule(code, name, path))
return rules
code2name = {r.code: r.name for r in rules()}
for pattern in ('src/**/*.rs', 'ruff_cli/**/*.rs', 'ruff_dev/**/*.rs', 'scripts/add_*.py'):
for name in glob.glob(pattern, recursive=True):
with open(name) as f:
text = f.read()
text = re.sub('Rule(?:Code)?::([A-Z]\w+)', lambda m: 'Rule::' + code2name[m.group(1)], text)
text = re.sub(r'(?<!"<FilePattern>:<)RuleCode\b', 'Rule', text)
text = re.sub('(use crate::registry::{.*, Rule), Rule(.*)', r'\1\2', text) # fix duplicate import
with open(name, 'w') as f:
f.write(text)
147 lines
4.6 KiB
Python
147 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate boilerplate for a new Flake8 plugin.
|
|
|
|
Example usage:
|
|
|
|
python scripts/add_plugin.py \
|
|
flake8-pie \
|
|
--url https://pypi.org/project/flake8-pie/0.16.0/
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
from pathlib import Path
|
|
|
|
ROOT_DIR = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
|
|
def dir_name(plugin: str) -> str:
|
|
return plugin.replace("-", "_")
|
|
|
|
|
|
def pascal_case(plugin: str) -> str:
|
|
return "".join(word.title() for word in plugin.split("-"))
|
|
|
|
|
|
def main(*, plugin: str, url: str) -> None:
|
|
# Create the test fixture folder.
|
|
os.makedirs(
|
|
ROOT_DIR / "resources/test/fixtures" / dir_name(plugin),
|
|
exist_ok=True,
|
|
)
|
|
|
|
# Create the Rust module.
|
|
rust_module = ROOT_DIR / "src/rules" / dir_name(plugin)
|
|
os.makedirs(rust_module, exist_ok=True)
|
|
with open(rust_module / "rules.rs", "w+") as fp:
|
|
fp.write("use crate::checkers::ast::Checker;\n")
|
|
with open(rust_module / "mod.rs", "w+") as fp:
|
|
fp.write("pub(crate) mod rules;\n")
|
|
fp.write("\n")
|
|
fp.write(
|
|
"""#[cfg(test)]
|
|
mod tests {
|
|
use std::convert::AsRef;
|
|
use std::path::Path;
|
|
|
|
use anyhow::Result;
|
|
use test_case::test_case;
|
|
|
|
use crate::registry::Rule;
|
|
use crate::linter::test_path;
|
|
use crate::settings;
|
|
|
|
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
|
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
|
let diagnostics =test_path(
|
|
Path::new("./resources/test/fixtures/%s")
|
|
.join(path)
|
|
.as_path(),
|
|
&settings::Settings::for_rule(rule_code),
|
|
)?;
|
|
insta::assert_yaml_snapshot!(snapshot, diagnostics);
|
|
Ok(())
|
|
}
|
|
}
|
|
"""
|
|
% dir_name(plugin)
|
|
)
|
|
|
|
# Add the plugin to `rules/mod.rs`.
|
|
with open(ROOT_DIR / "src/rules/mod.rs", "a") as fp:
|
|
fp.write(f"pub mod {dir_name(plugin)};")
|
|
|
|
# Add the relevant sections to `src/registry.rs`.
|
|
content = (ROOT_DIR / "src/registry.rs").read_text()
|
|
|
|
with open(ROOT_DIR / "src/registry.rs", "w") as fp:
|
|
for line in content.splitlines():
|
|
if line.strip() == "// Ruff":
|
|
indent = line.split("// Ruff")[0]
|
|
fp.write(f"{indent}// {plugin}")
|
|
fp.write("\n")
|
|
|
|
elif line.strip() == "Ruff,":
|
|
indent = line.split("Ruff,")[0]
|
|
fp.write(f"{indent}{pascal_case(plugin)},")
|
|
fp.write("\n")
|
|
|
|
elif line.strip() == 'RuleOrigin::Ruff => "Ruff-specific rules",':
|
|
indent = line.split('RuleOrigin::Ruff => "Ruff-specific rules",')[0]
|
|
fp.write(f'{indent}RuleOrigin::{pascal_case(plugin)} => "{plugin}",')
|
|
fp.write("\n")
|
|
|
|
elif line.strip() == "RuleOrigin::Ruff => vec![RuleCodePrefix::RUF],":
|
|
indent = line.split("RuleOrigin::Ruff => vec![RuleCodePrefix::RUF],")[0]
|
|
fp.write(
|
|
f"{indent}RuleOrigin::{pascal_case(plugin)} => vec![\n"
|
|
f'{indent} todo!("Fill-in prefix after generating codes")\n'
|
|
f"{indent}],"
|
|
)
|
|
fp.write("\n")
|
|
|
|
elif line.strip() == "RuleOrigin::Ruff => None,":
|
|
indent = line.split("RuleOrigin::Ruff => None,")[0]
|
|
fp.write(f"{indent}RuleOrigin::{pascal_case(plugin)} => " f'Some(("{url}", &Platform::PyPI)),')
|
|
fp.write("\n")
|
|
|
|
fp.write(line)
|
|
fp.write("\n")
|
|
|
|
# Add the relevant section to `src/violations.rs`.
|
|
content = (ROOT_DIR / "src/violations.rs").read_text()
|
|
|
|
with open(ROOT_DIR / "src/violations.rs", "w") as fp:
|
|
for line in content.splitlines():
|
|
if line.strip() == "// Ruff":
|
|
indent = line.split("// Ruff")[0]
|
|
fp.write(f"{indent}// {plugin}")
|
|
fp.write("\n")
|
|
|
|
fp.write(line)
|
|
fp.write("\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(
|
|
description="Generate boilerplate for a new Flake8 plugin.",
|
|
epilog=(
|
|
"Example usage: python scripts/add_plugin.py flake8-pie "
|
|
"--url https://pypi.org/project/flake8-pie/0.16.0/"
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"plugin",
|
|
type=str,
|
|
help="The name of the plugin to generate.",
|
|
)
|
|
parser.add_argument(
|
|
"--url",
|
|
required=True,
|
|
type=str,
|
|
help="The URL of the latest release in PyPI.",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
main(plugin=args.plugin, url=args.url)
|