mirror of
				https://github.com/astral-sh/ruff.git
				synced 2025-10-31 03:55:09 +00:00 
			
		
		
		
	 4bc73dd87e
			
		
	
	
		4bc73dd87e
		
			
		
	
	
	
	
		
			
			## Summary These are the first rules implemented as part of #458, but I plan to implement more. Specifically, this implements `docstring-missing-exception` which checks for raised exceptions not documented in the docstring, and `docstring-extraneous-exception` which checks for exceptions in the docstring not present in the body. ## Test Plan Test fixtures added for both google and numpy style.
		
			
				
	
	
		
			137 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			137 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #!/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/ \
 | |
|         --prefix PIE
 | |
| """
 | |
| 
 | |
| from __future__ import annotations
 | |
| 
 | |
| import argparse
 | |
| 
 | |
| from _utils import ROOT_DIR, dir_name, get_indent, pascal_case
 | |
| 
 | |
| 
 | |
| def main(*, plugin: str, url: str, prefix_code: str) -> None:
 | |
|     """Generate boilerplate for a new plugin."""
 | |
|     # Create the test fixture folder.
 | |
|     (ROOT_DIR / "crates/ruff_linter/resources/test/fixtures" / dir_name(plugin)).mkdir(
 | |
|         exist_ok=True,
 | |
|     )
 | |
| 
 | |
|     # Create the Plugin rules module.
 | |
|     plugin_dir = ROOT_DIR / "crates/ruff_linter/src/rules" / dir_name(plugin)
 | |
|     plugin_dir.mkdir(exist_ok=True)
 | |
| 
 | |
|     with (plugin_dir / "mod.rs").open("w+") as fp:
 | |
|         fp.write(f"//! Rules from [{plugin}]({url}).\n")
 | |
|         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::test::test_path;
 | |
|     use crate::{assert_messages, 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("%s").join(path).as_path(),
 | |
|             &settings::LinterSettings::for_rule(rule_code),
 | |
|         )?;
 | |
|         assert_messages!(snapshot, diagnostics);
 | |
|         Ok(())
 | |
|     }
 | |
| }
 | |
| """  # noqa: UP031  # Using an f-string here is ugly as all the curly parens need to be escaped
 | |
|             % dir_name(plugin),
 | |
|         )
 | |
| 
 | |
|     # Create a subdirectory for rules and create a `mod.rs` placeholder
 | |
|     rules_dir = plugin_dir / "rules"
 | |
|     rules_dir.mkdir(exist_ok=True)
 | |
| 
 | |
|     (rules_dir / "mod.rs").touch()
 | |
| 
 | |
|     # Create the snapshots subdirectory
 | |
|     (plugin_dir / "snapshots").mkdir(exist_ok=True)
 | |
| 
 | |
|     # Add the plugin to `rules/mod.rs`.
 | |
|     rules_mod_path = ROOT_DIR / "crates/ruff_linter/src/rules/mod.rs"
 | |
|     lines = rules_mod_path.read_text().strip().splitlines()
 | |
|     lines.append(f"pub mod {dir_name(plugin)};")
 | |
|     lines.sort()
 | |
|     rules_mod_path.write_text("\n".join(lines) + "\n")
 | |
| 
 | |
|     # Add the relevant sections to `src/registry.rs`.
 | |
|     content = (ROOT_DIR / "crates/ruff_linter/src/registry.rs").read_text()
 | |
| 
 | |
|     with (ROOT_DIR / "crates/ruff_linter/src/registry.rs").open("w") as fp:
 | |
|         for line in content.splitlines():
 | |
|             indent = get_indent(line)
 | |
| 
 | |
|             if line.strip() == "// ruff":
 | |
|                 fp.write(f"{indent}// {plugin}")
 | |
|                 fp.write("\n")
 | |
| 
 | |
|             elif line.strip() == "/// Ruff-specific rules":
 | |
|                 fp.write(f"{indent}/// [{plugin}]({url})\n")
 | |
|                 fp.write(f'{indent}#[prefix = "{prefix_code}"]\n')
 | |
|                 fp.write(f"{indent}{pascal_case(plugin)},")
 | |
|                 fp.write("\n")
 | |
| 
 | |
|             fp.write(line)
 | |
|             fp.write("\n")
 | |
| 
 | |
|     text = ""
 | |
|     with (ROOT_DIR / "crates/ruff_linter/src/codes.rs").open("r") as fp:
 | |
|         while (line := next(fp)).strip() != "// ruff":
 | |
|             text += line
 | |
|         text += " " * 8 + f"// {plugin}\n\n"
 | |
|         text += line
 | |
|         text += fp.read()
 | |
| 
 | |
|     with (ROOT_DIR / "crates/ruff_linter/src/codes.rs").open("w") as fp:
 | |
|         fp.write(text)
 | |
| 
 | |
| 
 | |
| 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/"
 | |
|         ),
 | |
|     )
 | |
|     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.",
 | |
|     )
 | |
|     parser.add_argument(
 | |
|         "--prefix",
 | |
|         required=False,
 | |
|         default="TODO",
 | |
|         type=str,
 | |
|         help="Prefix code for the plugin. Leave empty to manually fill.",
 | |
|     )
 | |
|     args = parser.parse_args()
 | |
| 
 | |
|     main(plugin=args.plugin, url=args.url, prefix_code=args.prefix)
 |