Infer Flake8 plugins from .flake8 config (#573)

This commit is contained in:
Charlie Marsh 2022-11-03 22:05:46 -04:00 committed by GitHub
parent cc915371ca
commit 83dfb5fe8b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 565 additions and 47 deletions

View file

@ -31,6 +31,8 @@ fn main() {
.derive("Debug") .derive("Debug")
.derive("PartialEq") .derive("PartialEq")
.derive("Eq") .derive("Eq")
.derive("PartialOrd")
.derive("Ord")
.derive("Clone") .derive("Clone")
.derive("Serialize") .derive("Serialize")
.derive("Deserialize"); .derive("Deserialize");

View file

@ -17,7 +17,7 @@ pip install flake8-to-ruff
### Usage ### Usage
To run Ruff, try any of the following: To run `flake8-to-ruff`:
```shell ```shell
flake8-to-ruff path/to/setup.cfg flake8-to-ruff path/to/setup.cfg
@ -25,6 +25,60 @@ flake8-to-ruff path/to/tox.ini
flake8-to-ruff path/to/.flake8 flake8-to-ruff path/to/.flake8
``` ```
`flake8-to-ruff` will print the relevant `pyproject.toml` sections to standard output, like so:
```toml
[tool.ruff]
exclude = [
'.svn',
'CVS',
'.bzr',
'.hg',
'.git',
'__pycache__',
'.tox',
'.idea',
'.mypy_cache',
'.venv',
'node_modules',
'_state_machine.py',
'test_fstring.py',
'bad_coding2.py',
'badsyntax_*.py',
]
select = [
'A',
'E',
'F',
'Q',
]
ignore = []
[tool.ruff.flake8-quotes]
inline-quotes = 'single'
[tool.ruff.pep8-naming]
ignore-names = [
'foo',
'bar',
]
```
### Plugins
`flake8-to-ruff` will attempt to infer any activated plugins based on the settings provided in your
configuration file.
For example, if your `.flake8` file includes a `docstring-convention` property, `flake8-to-ruff`
will enable the appropriate [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
checks.
Alternatively, you can manually specify plugins on the command-line:
```shell
flake8-to-ruff path/to/.flake8 --plugin flake8-builtins --plugin flake8-quotes
```
## Limitations ## Limitations
1. Ruff only supports a subset of the Flake configuration options. `flake8-to-ruff` will warn on and 1. Ruff only supports a subset of the Flake configuration options. `flake8-to-ruff` will warn on and
@ -33,9 +87,6 @@ flake8-to-ruff path/to/.flake8
2. Ruff will omit any error codes that are unimplemented or unsupported by Ruff, including error 2. Ruff will omit any error codes that are unimplemented or unsupported by Ruff, including error
codes from unsupported plugins. (See the [Ruff README](https://github.com/charliermarsh/ruff#user-content-how-does-ruff-compare-to-flake8) codes from unsupported plugins. (See the [Ruff README](https://github.com/charliermarsh/ruff#user-content-how-does-ruff-compare-to-flake8)
for the complete list of supported plugins.) for the complete list of supported plugins.)
3. `flake8-to-ruff` does not auto-detect your Flake8 plugins, so any reliance on Flake8 plugins that
implicitly enable third-party checks will be ignored. Instead, add those error codes to your
`select` or `extend-select` fields, so that `flake8-to-ruff` can pick them up.
## License ## License

View file

@ -1,4 +1,4 @@
use std::collections::HashMap; use std::collections::{BTreeSet, HashMap};
use anyhow::Result; use anyhow::Result;
use ruff::flake8_quotes::settings::Quote; use ruff::flake8_quotes::settings::Quote;
@ -6,13 +6,38 @@ use ruff::settings::options::Options;
use ruff::settings::pyproject::Pyproject; use ruff::settings::pyproject::Pyproject;
use ruff::{flake8_quotes, pep8_naming}; use ruff::{flake8_quotes, pep8_naming};
use crate::parser; use crate::plugin::Plugin;
use crate::{parser, plugin};
pub fn convert(config: HashMap<String, HashMap<String, Option<String>>>) -> Result<Pyproject> { pub fn convert(
// Extract the Flake8 section. flake8: &HashMap<String, Option<String>>,
let flake8 = config plugins: Option<Vec<Plugin>>,
.get("flake8") ) -> Result<Pyproject> {
.expect("Unable to find flake8 section in INI file."); // 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
.get("select")
.map(|value| {
value
.as_ref()
.map(|value| BTreeSet::from_iter(parser::parse_prefix_codes(value)))
})
.flatten()
.unwrap_or_else(|| {
plugin::resolve_select(
flake8,
&plugins.unwrap_or_else(|| plugin::infer_plugins(&flake8)),
)
});
let mut ignore = flake8
.get("ignore")
.map(|value| {
value
.as_ref()
.map(|value| BTreeSet::from_iter(parser::parse_prefix_codes(value)))
})
.flatten()
.unwrap_or_default();
// Parse each supported option. // Parse each supported option.
let mut options: Options = Default::default(); let mut options: Options = Default::default();
@ -27,16 +52,18 @@ pub fn convert(config: HashMap<String, HashMap<String, Option<String>>>) -> Resu
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"), Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
}, },
"select" => { "select" => {
options.select = Some(parser::parse_prefix_codes(value.as_ref())); // No-op (handled above).
}
"extend-select" | "extend_select" => {
options.extend_select = Some(parser::parse_prefix_codes(value.as_ref()));
} }
"ignore" => { "ignore" => {
options.ignore = Some(parser::parse_prefix_codes(value.as_ref())); // No-op (handled above).
}
"extend-select" | "extend_select" => {
// Unlike Flake8, use a single explicit `select`.
select.extend(parser::parse_prefix_codes(value.as_ref()));
} }
"extend-ignore" | "extend_ignore" => { "extend-ignore" | "extend_ignore" => {
options.extend_ignore = Some(parser::parse_prefix_codes(value.as_ref())); // Unlike Flake8, use a single explicit `ignore`.
ignore.extend(parser::parse_prefix_codes(value.as_ref()));
} }
"exclude" => { "exclude" => {
options.exclude = Some(parser::parse_strings(value.as_ref())); options.exclude = Some(parser::parse_strings(value.as_ref()));
@ -92,6 +119,9 @@ pub fn convert(config: HashMap<String, HashMap<String, Option<String>>>) -> Resu
} }
} }
// Deduplicate and sort.
options.select = Some(Vec::from_iter(select));
options.ignore = Some(Vec::from_iter(ignore));
if flake8_quotes != Default::default() { if flake8_quotes != Default::default() {
options.flake8_quotes = Some(flake8_quotes); options.flake8_quotes = Some(flake8_quotes);
} }
@ -108,22 +138,24 @@ mod tests {
use std::collections::HashMap; use std::collections::HashMap;
use anyhow::Result; use anyhow::Result;
use ruff::checks_gen::CheckCodePrefix;
use ruff::flake8_quotes; use ruff::flake8_quotes;
use ruff::settings::options::Options; use ruff::settings::options::Options;
use ruff::settings::pyproject::Pyproject; use ruff::settings::pyproject::Pyproject;
use crate::converter::convert; use crate::converter::convert;
use crate::plugin::Plugin;
#[test] #[test]
fn it_converts_empty() -> Result<()> { fn it_converts_empty() -> Result<()> {
let actual = convert(HashMap::from([("flake8".to_string(), HashMap::from([]))]))?; let actual = convert(&HashMap::from([]), None)?;
let expected = Pyproject::new(Options { let expected = Pyproject::new(Options {
line_length: None, line_length: None,
exclude: None, exclude: None,
extend_exclude: None, extend_exclude: None,
select: None, select: Some(vec![CheckCodePrefix::E, CheckCodePrefix::F]),
extend_select: None, extend_select: None,
ignore: None, ignore: Some(vec![]),
extend_ignore: None, extend_ignore: None,
per_file_ignores: None, per_file_ignores: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
@ -138,17 +170,17 @@ mod tests {
#[test] #[test]
fn it_converts_dashes() -> Result<()> { fn it_converts_dashes() -> Result<()> {
let actual = convert(HashMap::from([( let actual = convert(
"flake8".to_string(), &HashMap::from([("max-line-length".to_string(), Some("100".to_string()))]),
HashMap::from([("max-line-length".to_string(), Some("100".to_string()))]), Some(vec![]),
)]))?; )?;
let expected = Pyproject::new(Options { let expected = Pyproject::new(Options {
line_length: Some(100), line_length: Some(100),
exclude: None, exclude: None,
extend_exclude: None, extend_exclude: None,
select: None, select: Some(vec![CheckCodePrefix::E, CheckCodePrefix::F]),
extend_select: None, extend_select: None,
ignore: None, ignore: Some(vec![]),
extend_ignore: None, extend_ignore: None,
per_file_ignores: None, per_file_ignores: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
@ -163,17 +195,17 @@ mod tests {
#[test] #[test]
fn it_converts_underscores() -> Result<()> { fn it_converts_underscores() -> Result<()> {
let actual = convert(HashMap::from([( let actual = convert(
"flake8".to_string(), &HashMap::from([("max_line_length".to_string(), Some("100".to_string()))]),
HashMap::from([("max_line_length".to_string(), Some("100".to_string()))]), Some(vec![]),
)]))?; )?;
let expected = Pyproject::new(Options { let expected = Pyproject::new(Options {
line_length: Some(100), line_length: Some(100),
exclude: None, exclude: None,
extend_exclude: None, extend_exclude: None,
select: None, select: Some(vec![CheckCodePrefix::E, CheckCodePrefix::F]),
extend_select: None, extend_select: None,
ignore: None, ignore: Some(vec![]),
extend_ignore: None, extend_ignore: None,
per_file_ignores: None, per_file_ignores: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
@ -188,17 +220,17 @@ mod tests {
#[test] #[test]
fn it_ignores_parse_errors() -> Result<()> { fn it_ignores_parse_errors() -> Result<()> {
let actual = convert(HashMap::from([( let actual = convert(
"flake8".to_string(), &HashMap::from([("max_line_length".to_string(), Some("abc".to_string()))]),
HashMap::from([("max_line_length".to_string(), Some("abc".to_string()))]), Some(vec![]),
)]))?; )?;
let expected = Pyproject::new(Options { let expected = Pyproject::new(Options {
line_length: None, line_length: None,
exclude: None, exclude: None,
extend_exclude: None, extend_exclude: None,
select: None, select: Some(vec![CheckCodePrefix::E, CheckCodePrefix::F]),
extend_select: None, extend_select: None,
ignore: None, ignore: Some(vec![]),
extend_ignore: None, extend_ignore: None,
per_file_ignores: None, per_file_ignores: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
@ -212,18 +244,118 @@ mod tests {
} }
#[test] #[test]
fn it_converts_extensions() -> Result<()> { fn it_converts_plugin_options() -> Result<()> {
let actual = convert(HashMap::from([( let actual = convert(
"flake8".to_string(), &HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]), Some(vec![]),
)]))?; )?;
let expected = Pyproject::new(Options { let expected = Pyproject::new(Options {
line_length: None, line_length: None,
exclude: None, exclude: None,
extend_exclude: None, extend_exclude: None,
select: None, select: Some(vec![CheckCodePrefix::E, CheckCodePrefix::F]),
extend_select: None, extend_select: None,
ignore: None, ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
target_version: None,
flake8_quotes: Some(flake8_quotes::settings::Options {
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
multiline_quotes: None,
docstring_quotes: None,
avoid_escape: None,
}),
pep8_naming: None,
});
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn it_converts_docstring_conventions() -> Result<()> {
let actual = convert(
&HashMap::from([(
"docstring-convention".to_string(),
Some("numpy".to_string()),
)]),
Some(vec![Plugin::Flake8Docstrings]),
)?;
let expected = Pyproject::new(Options {
line_length: None,
exclude: None,
extend_exclude: None,
select: Some(vec![
CheckCodePrefix::D100,
CheckCodePrefix::D101,
CheckCodePrefix::D102,
CheckCodePrefix::D103,
CheckCodePrefix::D104,
CheckCodePrefix::D105,
CheckCodePrefix::D106,
CheckCodePrefix::D200,
CheckCodePrefix::D201,
CheckCodePrefix::D202,
CheckCodePrefix::D204,
CheckCodePrefix::D205,
CheckCodePrefix::D206,
CheckCodePrefix::D207,
CheckCodePrefix::D208,
CheckCodePrefix::D209,
CheckCodePrefix::D210,
CheckCodePrefix::D211,
CheckCodePrefix::D214,
CheckCodePrefix::D215,
CheckCodePrefix::D300,
CheckCodePrefix::D400,
CheckCodePrefix::D403,
CheckCodePrefix::D404,
CheckCodePrefix::D405,
CheckCodePrefix::D406,
CheckCodePrefix::D407,
CheckCodePrefix::D408,
CheckCodePrefix::D409,
CheckCodePrefix::D410,
CheckCodePrefix::D411,
CheckCodePrefix::D412,
CheckCodePrefix::D414,
CheckCodePrefix::D418,
CheckCodePrefix::D419,
CheckCodePrefix::E,
CheckCodePrefix::F,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
target_version: None,
flake8_quotes: None,
pep8_naming: None,
});
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn it_infers_plugins_if_omitted() -> Result<()> {
let actual = convert(
&HashMap::from([("inline-quotes".to_string(), Some("single".to_string()))]),
None,
)?;
let expected = Pyproject::new(Options {
line_length: None,
exclude: None,
extend_exclude: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::Q,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None, extend_ignore: None,
per_file_ignores: None, per_file_ignores: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,

View file

@ -2,3 +2,4 @@
pub mod converter; pub mod converter;
mod parser; mod parser;
pub mod plugin;

View file

@ -6,6 +6,7 @@ use anyhow::Result;
use clap::Parser; use clap::Parser;
use configparser::ini::Ini; use configparser::ini::Ini;
use flake8_to_ruff::converter; use flake8_to_ruff::converter;
use flake8_to_ruff::plugin::Plugin;
#[derive(Parser)] #[derive(Parser)]
#[command( #[command(
@ -17,6 +18,9 @@ struct Cli {
/// '.flake8'). /// '.flake8').
#[arg(required = true)] #[arg(required = true)]
file: PathBuf, file: PathBuf,
/// List of plugins to enable.
#[arg(long, value_delimiter = ',')]
plugin: Option<Vec<Plugin>>,
} }
fn main() -> Result<()> { fn main() -> Result<()> {
@ -27,8 +31,13 @@ fn main() -> Result<()> {
ini.set_multiline(true); ini.set_multiline(true);
let config = ini.load(cli.file).map_err(|msg| anyhow::anyhow!(msg))?; 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.");
// Create the pyproject.toml. // Create the pyproject.toml.
let pyproject = converter::convert(config)?; let pyproject = converter::convert(flake8, cli.plugin)?;
println!("{}", toml::to_string_pretty(&pyproject)?); println!("{}", toml::to_string_pretty(&pyproject)?);
Ok(()) Ok(())

View file

@ -0,0 +1,321 @@
use std::collections::{BTreeSet, HashMap};
use std::str::FromStr;
use anyhow::anyhow;
use ruff::checks_gen::CheckCodePrefix;
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Plugin {
Flake8Bugbear,
Flake8Builtins,
Flake8Comprehensions,
Flake8Docstrings,
Flake8Print,
Flake8Quotes,
PEP8Naming,
Pyupgrade,
}
impl FromStr for Plugin {
type Err = anyhow::Error;
fn from_str(string: &str) -> Result<Self, Self::Err> {
match string {
"flake8-bugbear" => Ok(Plugin::Flake8Bugbear),
"flake8-builtins" => Ok(Plugin::Flake8Builtins),
"flake8-comprehensions" => Ok(Plugin::Flake8Comprehensions),
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
"flake8-print" => Ok(Plugin::Flake8Print),
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
"pep8-naming" => Ok(Plugin::PEP8Naming),
"pyupgrade" => Ok(Plugin::Pyupgrade),
_ => Err(anyhow!("Unknown plugin: {}", string)),
}
}
}
impl Plugin {
pub fn select(&self, flake8: &HashMap<String, Option<String>>) -> Vec<CheckCodePrefix> {
match self {
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C],
Plugin::Flake8Docstrings => {
// Use the user-provided docstring.
for key in ["docstring-convention", "docstring_convention"] {
if let Some(Some(value)) = flake8.get(key) {
match DocstringConvention::from_str(value) {
Ok(convention) => return convention.select(),
Err(e) => {
eprintln!("Unexpected '{key}' value: {value} ({e}");
return vec![];
}
}
}
}
// Default to PEP8.
DocstringConvention::PEP8.select()
}
Plugin::Flake8Print => vec![CheckCodePrefix::T],
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
}
}
}
pub enum DocstringConvention {
All,
PEP8,
NumPy,
Google,
}
impl FromStr for DocstringConvention {
type Err = anyhow::Error;
fn from_str(string: &str) -> Result<Self, Self::Err> {
match string {
"all" => Ok(DocstringConvention::All),
"pep8" => Ok(DocstringConvention::PEP8),
"numpy" => Ok(DocstringConvention::NumPy),
"google" => Ok(DocstringConvention::Google),
_ => Err(anyhow!("Unknown docstring convention: {}", string)),
}
}
}
impl DocstringConvention {
fn select(&self) -> Vec<CheckCodePrefix> {
match self {
DocstringConvention::All => vec![CheckCodePrefix::D],
DocstringConvention::PEP8 => vec![
// All errors except D203, D212, D213, D214, D215, D404, D405, D406, D407, D408,
// D409, D410, D411, D413, D415, D416 and D417.
CheckCodePrefix::D100,
CheckCodePrefix::D101,
CheckCodePrefix::D102,
CheckCodePrefix::D103,
CheckCodePrefix::D104,
CheckCodePrefix::D105,
CheckCodePrefix::D106,
CheckCodePrefix::D107,
CheckCodePrefix::D200,
CheckCodePrefix::D201,
CheckCodePrefix::D202,
// CheckCodePrefix::D203,
CheckCodePrefix::D204,
CheckCodePrefix::D205,
CheckCodePrefix::D206,
CheckCodePrefix::D207,
CheckCodePrefix::D208,
CheckCodePrefix::D209,
CheckCodePrefix::D210,
CheckCodePrefix::D211,
// CheckCodePrefix::D212,
// CheckCodePrefix::D213,
// CheckCodePrefix::D214,
// CheckCodePrefix::D215,
CheckCodePrefix::D300,
CheckCodePrefix::D400,
CheckCodePrefix::D402,
CheckCodePrefix::D403,
// CheckCodePrefix::D404,
// CheckCodePrefix::D405,
// CheckCodePrefix::D406,
// CheckCodePrefix::D407,
// CheckCodePrefix::D408,
// CheckCodePrefix::D409,
// CheckCodePrefix::D410,
// CheckCodePrefix::D411,
CheckCodePrefix::D412,
// CheckCodePrefix::D413,
CheckCodePrefix::D414,
// CheckCodePrefix::D415,
// CheckCodePrefix::D416,
// CheckCodePrefix::D417,
CheckCodePrefix::D418,
CheckCodePrefix::D419,
],
DocstringConvention::NumPy => vec![
// All errors except D107, D203, D212, D213, D402, D413, D415, D416, and D417.
CheckCodePrefix::D100,
CheckCodePrefix::D101,
CheckCodePrefix::D102,
CheckCodePrefix::D103,
CheckCodePrefix::D104,
CheckCodePrefix::D105,
CheckCodePrefix::D106,
// CheckCodePrefix::D107,
CheckCodePrefix::D200,
CheckCodePrefix::D201,
CheckCodePrefix::D202,
// CheckCodePrefix::D203,
CheckCodePrefix::D204,
CheckCodePrefix::D205,
CheckCodePrefix::D206,
CheckCodePrefix::D207,
CheckCodePrefix::D208,
CheckCodePrefix::D209,
CheckCodePrefix::D210,
CheckCodePrefix::D211,
// CheckCodePrefix::D212,
// CheckCodePrefix::D213,
CheckCodePrefix::D214,
CheckCodePrefix::D215,
CheckCodePrefix::D300,
CheckCodePrefix::D400,
// CheckCodePrefix::D402,
CheckCodePrefix::D403,
CheckCodePrefix::D404,
CheckCodePrefix::D405,
CheckCodePrefix::D406,
CheckCodePrefix::D407,
CheckCodePrefix::D408,
CheckCodePrefix::D409,
CheckCodePrefix::D410,
CheckCodePrefix::D411,
CheckCodePrefix::D412,
// CheckCodePrefix::D413,
CheckCodePrefix::D414,
// CheckCodePrefix::D415,
// CheckCodePrefix::D416,
// CheckCodePrefix::D417,
CheckCodePrefix::D418,
CheckCodePrefix::D419,
],
DocstringConvention::Google => vec![
// All errors except D203, D204, D213, D215, D400, D401, D404, D406, D407, D408,
// D409 and D413.
CheckCodePrefix::D100,
CheckCodePrefix::D101,
CheckCodePrefix::D102,
CheckCodePrefix::D103,
CheckCodePrefix::D104,
CheckCodePrefix::D105,
CheckCodePrefix::D106,
CheckCodePrefix::D107,
CheckCodePrefix::D200,
CheckCodePrefix::D201,
CheckCodePrefix::D202,
// CheckCodePrefix::D203,
// CheckCodePrefix::D204,
CheckCodePrefix::D205,
CheckCodePrefix::D206,
CheckCodePrefix::D207,
CheckCodePrefix::D208,
CheckCodePrefix::D209,
CheckCodePrefix::D210,
CheckCodePrefix::D211,
CheckCodePrefix::D212,
// CheckCodePrefix::D213,
CheckCodePrefix::D214,
// CheckCodePrefix::D215,
CheckCodePrefix::D300,
// CheckCodePrefix::D400,
CheckCodePrefix::D402,
CheckCodePrefix::D403,
// CheckCodePrefix::D404,
CheckCodePrefix::D405,
// CheckCodePrefix::D406,
// CheckCodePrefix::D407,
// CheckCodePrefix::D408,
// CheckCodePrefix::D409,
CheckCodePrefix::D410,
CheckCodePrefix::D411,
CheckCodePrefix::D412,
// CheckCodePrefix::D413,
CheckCodePrefix::D414,
CheckCodePrefix::D415,
CheckCodePrefix::D416,
CheckCodePrefix::D417,
CheckCodePrefix::D418,
CheckCodePrefix::D419,
],
}
}
}
/// Infer the enabled plugins based on user-provided settings.
pub fn infer_plugins(flake8: &HashMap<String, Option<String>>) -> Vec<Plugin> {
let mut plugins = BTreeSet::new();
for (key, _) in flake8 {
match key.as_str() {
// flake8-docstrings
"docstring-convention" | "docstring_convention" => {
plugins.insert(Plugin::Flake8Docstrings);
}
// flake8-builtins
"builtins-ignorelist" | "builtins_ignorelist" => {
plugins.insert(Plugin::Flake8Builtins);
}
// flake8-quotes
"quotes" | "inline-quotes" | "inline_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"multiline-quotes" | "multiline_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"docstring-quotes" | "docstring_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"avoid-escape" | "avoid_escape" => {
plugins.insert(Plugin::Flake8Quotes);
}
// pep8-naming
"ignore-names" | "ignore_names" => {
plugins.insert(Plugin::PEP8Naming);
}
"classmethod-decorators" | "classmethod_decorators" => {
plugins.insert(Plugin::PEP8Naming);
}
"staticmethod-decorators" | "staticmethod_decorators" => {
plugins.insert(Plugin::PEP8Naming);
}
_ => {}
}
}
Vec::from_iter(plugins)
}
/// Resolve the set of enabled `CheckCodePrefix` values for the given plugins.
pub fn resolve_select(
flake8: &HashMap<String, Option<String>>,
plugins: &[Plugin],
) -> BTreeSet<CheckCodePrefix> {
// Include default Pyflakes and pycodestyle checks.
let mut select = BTreeSet::from([CheckCodePrefix::E, CheckCodePrefix::F]);
// Add prefix codes for every plugin.
for plugin in plugins {
for prefix in plugin.select(flake8) {
select.insert(prefix);
}
}
select
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use crate::plugin::{infer_plugins, Plugin};
#[test]
fn it_infers_plugins() {
let actual = infer_plugins(&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([(
"staticmethod-decorators".to_string(),
Some("[]".to_string()),
)]));
let expected = vec![Plugin::PEP8Naming];
assert_eq!(actual, expected);
}
}

View file

@ -5,7 +5,9 @@ use strum_macros::{AsRefStr, EnumString};
use crate::checks::CheckCode; use crate::checks::CheckCode;
#[derive(AsRefStr, EnumString, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] #[derive(
AsRefStr, EnumString, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize,
)]
pub enum CheckCodePrefix { pub enum CheckCodePrefix {
A, A,
A0, A0,