Infer plugins based on per-file-ignores, ignores, etc. (#632)

This commit is contained in:
Charlie Marsh 2022-11-06 22:39:06 -05:00 committed by GitHub
parent da8ee1df3e
commit dd20b23576
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 89 additions and 11 deletions

View File

@ -1,6 +1,7 @@
use std::collections::{BTreeSet, HashMap};
use anyhow::Result;
use ruff::checks_gen::CheckCodePrefix;
use ruff::flake8_quotes::settings::Quote;
use ruff::settings::options::Options;
use ruff::settings::pyproject::Pyproject;
@ -13,6 +14,29 @@ pub fn convert(
flake8: &HashMap<String, Option<String>>,
plugins: Option<Vec<Plugin>>,
) -> Result<Pyproject> {
// Extract all referenced check code prefixes, to power plugin inference.
let mut referenced_codes: BTreeSet<CheckCodePrefix> = Default::default();
for (key, value) in flake8 {
if let Some(value) = value {
match key.as_str() {
"select" | "ignore" | "extend-select" | "extend_select" | "extend-ignore"
| "extend_ignore" => {
referenced_codes.extend(parser::parse_prefix_codes(value.as_ref()));
}
"per-file-ignores" | "per_file_ignores" => {
if let Ok(per_file_ignores) =
parser::parse_files_to_codes_mapping(value.as_ref())
{
for (_, codes) in parser::collect_per_file_ignores(per_file_ignores) {
referenced_codes.extend(codes);
}
}
}
_ => {}
}
}
}
// Check if the user has specified a `select`. If not, we'll add our own
// default `select`, and populate it based on user plugins.
let mut select = flake8
@ -25,7 +49,12 @@ pub fn convert(
.unwrap_or_else(|| {
plugin::resolve_select(
flake8,
&plugins.unwrap_or_else(|| plugin::infer_plugins(flake8)),
&plugins.unwrap_or_else(|| {
plugin::infer_plugins_from_options(flake8)
.into_iter()
.chain(plugin::infer_plugins_from_codes(&referenced_codes))
.collect()
}),
)
});
let mut ignore = flake8
@ -52,6 +81,7 @@ pub fn convert(
},
"select" => {
// No-op (handled above).
select.extend(parser::parse_prefix_codes(value.as_ref()));
}
"ignore" => {
// No-op (handled above).

View File

@ -37,6 +37,20 @@ impl FromStr for Plugin {
}
impl Plugin {
pub fn default(&self) -> CheckCodePrefix {
match self {
Plugin::Flake8Bugbear => CheckCodePrefix::B,
Plugin::Flake8Builtins => CheckCodePrefix::A,
Plugin::Flake8Comprehensions => CheckCodePrefix::C,
Plugin::Flake8Docstrings => CheckCodePrefix::D,
Plugin::Flake8Print => CheckCodePrefix::T,
Plugin::Flake8Quotes => CheckCodePrefix::Q,
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
Plugin::PEP8Naming => CheckCodePrefix::N,
Plugin::Pyupgrade => CheckCodePrefix::U,
}
}
pub fn select(&self, flake8: &HashMap<String, Option<String>>) -> Vec<CheckCodePrefix> {
match self {
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
@ -239,8 +253,11 @@ impl DocstringConvention {
}
}
/// Infer the enabled plugins based on user-provided settings.
pub fn infer_plugins(flake8: &HashMap<String, Option<String>>) -> Vec<Plugin> {
/// Infer the enabled plugins based on user-provided options.
///
/// For example, if the user specified a `mypy-init-return` setting, we should
/// infer that `flake8-annotations` is active.
pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> Vec<Plugin> {
let mut plugins = BTreeSet::new();
for key in flake8.keys() {
match key.as_str() {
@ -306,6 +323,38 @@ pub fn infer_plugins(flake8: &HashMap<String, Option<String>>) -> Vec<Plugin> {
Vec::from_iter(plugins)
}
/// Infer the enabled plugins based on the referenced prefixes.
///
/// For example, if the user ignores `ANN101`, we should infer that
/// `flake8-annotations` is active.
pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin> {
[
Plugin::Flake8Bugbear,
Plugin::Flake8Builtins,
Plugin::Flake8Comprehensions,
Plugin::Flake8Docstrings,
Plugin::Flake8Print,
Plugin::Flake8Quotes,
Plugin::Flake8Annotations,
Plugin::PEP8Naming,
Plugin::Pyupgrade,
]
.into_iter()
.filter(|plugin| {
for prefix in codes {
if prefix
.codes()
.iter()
.any(|code| plugin.default().codes().contains(code))
{
return true;
}
}
false
})
.collect()
}
/// Resolve the set of enabled `CheckCodePrefix` values for the given plugins.
pub fn resolve_select(
flake8: &HashMap<String, Option<String>>,
@ -316,9 +365,7 @@ pub fn resolve_select(
// Add prefix codes for every plugin.
for plugin in plugins {
for prefix in plugin.select(flake8) {
select.insert(prefix);
}
select.extend(plugin.select(flake8));
}
select
@ -328,18 +375,18 @@ pub fn resolve_select(
mod tests {
use std::collections::HashMap;
use crate::plugin::{infer_plugins, Plugin};
use crate::plugin::{infer_plugins_from_options, Plugin};
#[test]
fn it_infers_plugins() {
let actual = infer_plugins(&HashMap::from([(
let actual = infer_plugins_from_options(&HashMap::from([(
"inline-quotes".to_string(),
Some("single".to_string()),
)]));
let expected = vec![Plugin::Flake8Quotes];
assert_eq!(actual, expected);
let actual = infer_plugins(&HashMap::from([(
let actual = infer_plugins_from_options(&HashMap::from([(
"staticmethod-decorators".to_string(),
Some("[]".to_string()),
)]));

View File

@ -73,8 +73,9 @@ fn match_returns(stmt: &Stmt) -> &Option<Box<Expr>> {
/// Generate flake8-annotation checks for a given `Definition`.
pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &Visibility) {
// TODO(charlie): Consider using the AST directly here rather than `Definition`. We could adhere
// more closely to `flake8-annotations` by defining public vs. secret vs. protected.
// TODO(charlie): Consider using the AST directly here rather than `Definition`.
// We could adhere more closely to `flake8-annotations` by defining public
// vs. secret vs. protected.
match &definition.kind {
DefinitionKind::Module => {}
DefinitionKind::Package => {}