mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 22:01:47 +00:00
Move around and rename some of the Settings structs (#496)
This commit is contained in:
parent
8fc5e91ec7
commit
7df903dc4d
16 changed files with 574 additions and 536 deletions
|
@ -26,7 +26,8 @@ use crate::checks::{Check, CheckCode, CheckKind};
|
||||||
use crate::docstrings::definition::{Definition, DefinitionKind, Documentable};
|
use crate::docstrings::definition::{Definition, DefinitionKind, Documentable};
|
||||||
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
|
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
|
||||||
use crate::python::future::ALL_FEATURE_NAMES;
|
use crate::python::future::ALL_FEATURE_NAMES;
|
||||||
use crate::settings::{PythonVersion, Settings};
|
use crate::settings::types::PythonVersion;
|
||||||
|
use crate::settings::Settings;
|
||||||
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
|
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
|
||||||
use crate::{
|
use crate::{
|
||||||
docstrings, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_print, pep8_naming,
|
docstrings, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_print, pep8_naming,
|
||||||
|
|
|
@ -229,7 +229,7 @@ pub fn check_lines(
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::autofix::fixer;
|
use crate::autofix::fixer;
|
||||||
use crate::checks::{Check, CheckCode};
|
use crate::checks::{Check, CheckCode};
|
||||||
use crate::settings;
|
use crate::settings::Settings;
|
||||||
|
|
||||||
use super::check_lines;
|
use super::check_lines;
|
||||||
|
|
||||||
|
@ -243,9 +243,9 @@ mod tests {
|
||||||
&mut checks,
|
&mut checks,
|
||||||
line,
|
line,
|
||||||
&noqa_line_for,
|
&noqa_line_for,
|
||||||
&settings::Settings {
|
&Settings {
|
||||||
line_length,
|
line_length,
|
||||||
..settings::Settings::for_rule(CheckCode::E501)
|
..Settings::for_rule(CheckCode::E501)
|
||||||
},
|
},
|
||||||
&fixer::Mode::Generate,
|
&fixer::Mode::Generate,
|
||||||
);
|
);
|
||||||
|
|
12
src/cli.rs
12
src/cli.rs
|
@ -7,9 +7,9 @@ use regex::Regex;
|
||||||
|
|
||||||
use crate::checks_gen::CheckCodePrefix;
|
use crate::checks_gen::CheckCodePrefix;
|
||||||
use crate::printer::SerializationFormat;
|
use crate::printer::SerializationFormat;
|
||||||
use crate::pyproject::StrCheckCodePair;
|
use crate::settings::configuration::Configuration;
|
||||||
use crate::settings::PythonVersion;
|
use crate::settings::types::PythonVersion;
|
||||||
use crate::RawSettings;
|
use crate::settings::types::StrCheckCodePair;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(author, about = "ruff: An extremely fast Python linter.")]
|
#[command(author, about = "ruff: An extremely fast Python linter.")]
|
||||||
|
@ -109,7 +109,7 @@ pub fn warn_on(
|
||||||
codes: &[CheckCodePrefix],
|
codes: &[CheckCodePrefix],
|
||||||
cli_ignore: &[CheckCodePrefix],
|
cli_ignore: &[CheckCodePrefix],
|
||||||
cli_extend_ignore: &[CheckCodePrefix],
|
cli_extend_ignore: &[CheckCodePrefix],
|
||||||
pyproject_settings: &RawSettings,
|
pyproject_configuration: &Configuration,
|
||||||
pyproject_path: &Option<PathBuf>,
|
pyproject_path: &Option<PathBuf>,
|
||||||
) {
|
) {
|
||||||
for code in codes {
|
for code in codes {
|
||||||
|
@ -117,7 +117,7 @@ pub fn warn_on(
|
||||||
if cli_ignore.contains(code) {
|
if cli_ignore.contains(code) {
|
||||||
warn!("{code:?} was passed to {flag}, but ignored via --ignore")
|
warn!("{code:?} was passed to {flag}, but ignored via --ignore")
|
||||||
}
|
}
|
||||||
} else if pyproject_settings.ignore.contains(code) {
|
} else if pyproject_configuration.ignore.contains(code) {
|
||||||
if let Some(path) = pyproject_path {
|
if let Some(path) = pyproject_path {
|
||||||
warn!(
|
warn!(
|
||||||
"{code:?} was passed to {flag}, but ignored by the `ignore` field in {}",
|
"{code:?} was passed to {flag}, but ignored by the `ignore` field in {}",
|
||||||
|
@ -131,7 +131,7 @@ pub fn warn_on(
|
||||||
if cli_extend_ignore.contains(code) {
|
if cli_extend_ignore.contains(code) {
|
||||||
warn!("{code:?} was passed to {flag}, but ignored via --extend-ignore")
|
warn!("{code:?} was passed to {flag}, but ignored via --extend-ignore")
|
||||||
}
|
}
|
||||||
} else if pyproject_settings.extend_ignore.contains(code) {
|
} else if pyproject_configuration.extend_ignore.contains(code) {
|
||||||
if let Some(path) = pyproject_path {
|
if let Some(path) = pyproject_path {
|
||||||
warn!(
|
warn!(
|
||||||
"{code:?} was passed to {flag}, but ignored by the `extend_ignore` field in {}",
|
"{code:?} was passed to {flag}, but ignored by the `extend_ignore` field in {}",
|
||||||
|
|
|
@ -148,15 +148,10 @@ mod tests {
|
||||||
use crate::checks::{Check, CheckCode};
|
use crate::checks::{Check, CheckCode};
|
||||||
use crate::flake8_quotes::settings::Quote;
|
use crate::flake8_quotes::settings::Quote;
|
||||||
use crate::linter::tokenize;
|
use crate::linter::tokenize;
|
||||||
use crate::settings;
|
use crate::{flake8_quotes, linter, Settings};
|
||||||
use crate::{flake8_quotes, linter};
|
|
||||||
use crate::{fs, noqa};
|
use crate::{fs, noqa};
|
||||||
|
|
||||||
fn check_path(
|
fn check_path(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> Result<Vec<Check>> {
|
||||||
path: &Path,
|
|
||||||
settings: &settings::Settings,
|
|
||||||
autofix: &fixer::Mode,
|
|
||||||
) -> Result<Vec<Check>> {
|
|
||||||
let contents = fs::read_file(path)?;
|
let contents = fs::read_file(path)?;
|
||||||
let tokens: Vec<LexResult> = tokenize(&contents);
|
let tokens: Vec<LexResult> = tokenize(&contents);
|
||||||
let noqa_line_for = noqa::extract_noqa_line_for(&tokens);
|
let noqa_line_for = noqa::extract_noqa_line_for(&tokens);
|
||||||
|
@ -174,14 +169,14 @@ mod tests {
|
||||||
Path::new("./resources/test/fixtures/flake8_quotes")
|
Path::new("./resources/test/fixtures/flake8_quotes")
|
||||||
.join(path)
|
.join(path)
|
||||||
.as_path(),
|
.as_path(),
|
||||||
&settings::Settings {
|
&Settings {
|
||||||
flake8_quotes: flake8_quotes::settings::Settings {
|
flake8_quotes: flake8_quotes::settings::Settings {
|
||||||
inline_quotes: Quote::Single,
|
inline_quotes: Quote::Single,
|
||||||
multiline_quotes: Quote::Single,
|
multiline_quotes: Quote::Single,
|
||||||
docstring_quotes: Quote::Single,
|
docstring_quotes: Quote::Single,
|
||||||
avoid_escape: true,
|
avoid_escape: true,
|
||||||
},
|
},
|
||||||
..settings::Settings::for_rules(vec![
|
..Settings::for_rules(vec![
|
||||||
CheckCode::Q000,
|
CheckCode::Q000,
|
||||||
CheckCode::Q001,
|
CheckCode::Q001,
|
||||||
CheckCode::Q002,
|
CheckCode::Q002,
|
||||||
|
@ -206,14 +201,14 @@ mod tests {
|
||||||
Path::new("./resources/test/fixtures/flake8_quotes")
|
Path::new("./resources/test/fixtures/flake8_quotes")
|
||||||
.join(path)
|
.join(path)
|
||||||
.as_path(),
|
.as_path(),
|
||||||
&settings::Settings {
|
&Settings {
|
||||||
flake8_quotes: flake8_quotes::settings::Settings {
|
flake8_quotes: flake8_quotes::settings::Settings {
|
||||||
inline_quotes: Quote::Double,
|
inline_quotes: Quote::Double,
|
||||||
multiline_quotes: Quote::Double,
|
multiline_quotes: Quote::Double,
|
||||||
docstring_quotes: Quote::Double,
|
docstring_quotes: Quote::Double,
|
||||||
avoid_escape: true,
|
avoid_escape: true,
|
||||||
},
|
},
|
||||||
..settings::Settings::for_rules(vec![
|
..Settings::for_rules(vec![
|
||||||
CheckCode::Q000,
|
CheckCode::Q000,
|
||||||
CheckCode::Q001,
|
CheckCode::Q001,
|
||||||
CheckCode::Q002,
|
CheckCode::Q002,
|
||||||
|
@ -243,14 +238,14 @@ mod tests {
|
||||||
Path::new("./resources/test/fixtures/flake8_quotes")
|
Path::new("./resources/test/fixtures/flake8_quotes")
|
||||||
.join(path)
|
.join(path)
|
||||||
.as_path(),
|
.as_path(),
|
||||||
&settings::Settings {
|
&Settings {
|
||||||
flake8_quotes: flake8_quotes::settings::Settings {
|
flake8_quotes: flake8_quotes::settings::Settings {
|
||||||
inline_quotes: Quote::Single,
|
inline_quotes: Quote::Single,
|
||||||
multiline_quotes: Quote::Single,
|
multiline_quotes: Quote::Single,
|
||||||
docstring_quotes: Quote::Double,
|
docstring_quotes: Quote::Double,
|
||||||
avoid_escape: true,
|
avoid_escape: true,
|
||||||
},
|
},
|
||||||
..settings::Settings::for_rules(vec![
|
..Settings::for_rules(vec![
|
||||||
CheckCode::Q000,
|
CheckCode::Q000,
|
||||||
CheckCode::Q001,
|
CheckCode::Q001,
|
||||||
CheckCode::Q002,
|
CheckCode::Q002,
|
||||||
|
@ -280,14 +275,14 @@ mod tests {
|
||||||
Path::new("./resources/test/fixtures/flake8_quotes")
|
Path::new("./resources/test/fixtures/flake8_quotes")
|
||||||
.join(path)
|
.join(path)
|
||||||
.as_path(),
|
.as_path(),
|
||||||
&settings::Settings {
|
&Settings {
|
||||||
flake8_quotes: flake8_quotes::settings::Settings {
|
flake8_quotes: flake8_quotes::settings::Settings {
|
||||||
inline_quotes: Quote::Single,
|
inline_quotes: Quote::Single,
|
||||||
multiline_quotes: Quote::Double,
|
multiline_quotes: Quote::Double,
|
||||||
docstring_quotes: Quote::Single,
|
docstring_quotes: Quote::Single,
|
||||||
avoid_escape: true,
|
avoid_escape: true,
|
||||||
},
|
},
|
||||||
..settings::Settings::for_rules(vec![
|
..Settings::for_rules(vec![
|
||||||
CheckCode::Q000,
|
CheckCode::Q000,
|
||||||
CheckCode::Q001,
|
CheckCode::Q001,
|
||||||
CheckCode::Q002,
|
CheckCode::Q002,
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub enum Quote {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub struct Config {
|
pub struct Options {
|
||||||
pub inline_quotes: Option<Quote>,
|
pub inline_quotes: Option<Quote>,
|
||||||
pub multiline_quotes: Option<Quote>,
|
pub multiline_quotes: Option<Quote>,
|
||||||
pub docstring_quotes: Option<Quote>,
|
pub docstring_quotes: Option<Quote>,
|
||||||
|
@ -27,7 +27,7 @@ pub struct Settings {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
pub fn from_config(config: Config) -> Self {
|
pub fn from_config(config: Options) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inline_quotes: config.inline_quotes.unwrap_or(Quote::Single),
|
inline_quotes: config.inline_quotes.unwrap_or(Quote::Single),
|
||||||
multiline_quotes: config.multiline_quotes.unwrap_or(Quote::Double),
|
multiline_quotes: config.multiline_quotes.unwrap_or(Quote::Double),
|
||||||
|
|
|
@ -12,7 +12,7 @@ use path_absolutize::Absolutize;
|
||||||
use walkdir::{DirEntry, WalkDir};
|
use walkdir::{DirEntry, WalkDir};
|
||||||
|
|
||||||
use crate::checks::CheckCode;
|
use crate::checks::CheckCode;
|
||||||
use crate::settings::{FilePattern, PerFileIgnore};
|
use crate::settings::types::{FilePattern, PerFileIgnore};
|
||||||
|
|
||||||
/// Extract the absolute path and basename (as strings) from a Path.
|
/// Extract the absolute path and basename (as strings) from a Path.
|
||||||
fn extract_path_names(path: &Path) -> Result<(&str, &str)> {
|
fn extract_path_names(path: &Path) -> Result<(&str, &str)> {
|
||||||
|
@ -178,7 +178,7 @@ mod tests {
|
||||||
use path_absolutize::Absolutize;
|
use path_absolutize::Absolutize;
|
||||||
|
|
||||||
use crate::fs::{extract_path_names, is_excluded, is_included};
|
use crate::fs::{extract_path_names, is_excluded, is_included};
|
||||||
use crate::settings::FilePattern;
|
use crate::settings::types::FilePattern;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn inclusions() {
|
fn inclusions() {
|
||||||
|
|
|
@ -5,11 +5,13 @@ use std::path::Path;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use rustpython_parser::lexer::LexResult;
|
use rustpython_parser::lexer::LexResult;
|
||||||
|
use settings::pyproject;
|
||||||
|
|
||||||
use crate::autofix::fixer::Mode;
|
use crate::autofix::fixer::Mode;
|
||||||
use crate::linter::{check_path, tokenize};
|
use crate::linter::{check_path, tokenize};
|
||||||
use crate::message::Message;
|
use crate::message::Message;
|
||||||
use crate::settings::{RawSettings, Settings};
|
use crate::settings::configuration::Configuration;
|
||||||
|
use settings::Settings;
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
mod autofix;
|
mod autofix;
|
||||||
|
@ -38,7 +40,6 @@ pub mod printer;
|
||||||
mod pycodestyle;
|
mod pycodestyle;
|
||||||
mod pydocstyle;
|
mod pydocstyle;
|
||||||
mod pyflakes;
|
mod pyflakes;
|
||||||
pub mod pyproject;
|
|
||||||
mod python;
|
mod python;
|
||||||
mod pyupgrade;
|
mod pyupgrade;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
@ -58,7 +59,7 @@ pub fn check(path: &Path, contents: &str, quiet: bool) -> Result<Vec<Message>> {
|
||||||
None => debug!("Unable to find pyproject.toml; using default settings..."),
|
None => debug!("Unable to find pyproject.toml; using default settings..."),
|
||||||
};
|
};
|
||||||
|
|
||||||
let settings = Settings::from_raw(RawSettings::from_pyproject(
|
let settings = Settings::from_configuration(Configuration::from_pyproject(
|
||||||
&pyproject,
|
&pyproject,
|
||||||
&project_root,
|
&project_root,
|
||||||
quiet,
|
quiet,
|
||||||
|
|
|
@ -236,16 +236,12 @@ mod tests {
|
||||||
|
|
||||||
use crate::autofix::fixer;
|
use crate::autofix::fixer;
|
||||||
use crate::checks::{Check, CheckCode};
|
use crate::checks::{Check, CheckCode};
|
||||||
use crate::linter;
|
|
||||||
use crate::linter::tokenize;
|
use crate::linter::tokenize;
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
use crate::{fs, noqa};
|
use crate::{fs, noqa};
|
||||||
|
use crate::{linter, Settings};
|
||||||
|
|
||||||
fn check_path(
|
fn check_path(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> Result<Vec<Check>> {
|
||||||
path: &Path,
|
|
||||||
settings: &settings::Settings,
|
|
||||||
autofix: &fixer::Mode,
|
|
||||||
) -> Result<Vec<Check>> {
|
|
||||||
let contents = fs::read_file(path)?;
|
let contents = fs::read_file(path)?;
|
||||||
let tokens: Vec<LexResult> = tokenize(&contents);
|
let tokens: Vec<LexResult> = tokenize(&contents);
|
||||||
let noqa_line_for = noqa::extract_noqa_line_for(&tokens);
|
let noqa_line_for = noqa::extract_noqa_line_for(&tokens);
|
||||||
|
|
45
src/main.rs
45
src/main.rs
|
@ -25,10 +25,11 @@ use ruff::linter::{lint_path, lint_stdin};
|
||||||
use ruff::logging::set_up_logging;
|
use ruff::logging::set_up_logging;
|
||||||
use ruff::message::Message;
|
use ruff::message::Message;
|
||||||
use ruff::printer::{Printer, SerializationFormat};
|
use ruff::printer::{Printer, SerializationFormat};
|
||||||
use ruff::pyproject::{self};
|
use ruff::settings::configuration::Configuration;
|
||||||
use ruff::settings::CurrentSettings;
|
use ruff::settings::pyproject;
|
||||||
use ruff::settings::RawSettings;
|
use ruff::settings::types::{FilePattern, PerFileIgnore};
|
||||||
use ruff::settings::{FilePattern, PerFileIgnore, Settings};
|
use ruff::settings::user::UserConfiguration;
|
||||||
|
use ruff::settings::Settings;
|
||||||
use ruff::tell_user;
|
use ruff::tell_user;
|
||||||
|
|
||||||
#[cfg(feature = "update-informer")]
|
#[cfg(feature = "update-informer")]
|
||||||
|
@ -73,10 +74,14 @@ fn check_for_updates() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_settings(settings: RawSettings, project_root: Option<PathBuf>, pyproject: Option<PathBuf>) {
|
fn show_settings(
|
||||||
|
configuration: Configuration,
|
||||||
|
project_root: Option<PathBuf>,
|
||||||
|
pyproject: Option<PathBuf>,
|
||||||
|
) {
|
||||||
println!(
|
println!(
|
||||||
"{:#?}",
|
"{:#?}",
|
||||||
CurrentSettings::from_settings(settings, project_root, pyproject)
|
UserConfiguration::from_configuration(configuration, project_root, pyproject)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,15 +261,15 @@ fn inner_main() -> Result<ExitCode> {
|
||||||
.map(|pair| PerFileIgnore::new(pair, &project_root))
|
.map(|pair| PerFileIgnore::new(pair, &project_root))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut settings = RawSettings::from_pyproject(&pyproject, &project_root, cli.quiet)?;
|
let mut configuration = Configuration::from_pyproject(&pyproject, &project_root, cli.quiet)?;
|
||||||
if !exclude.is_empty() {
|
if !exclude.is_empty() {
|
||||||
settings.exclude = exclude;
|
configuration.exclude = exclude;
|
||||||
}
|
}
|
||||||
if !extend_exclude.is_empty() {
|
if !extend_exclude.is_empty() {
|
||||||
settings.extend_exclude = extend_exclude;
|
configuration.extend_exclude = extend_exclude;
|
||||||
}
|
}
|
||||||
if !per_file_ignores.is_empty() {
|
if !per_file_ignores.is_empty() {
|
||||||
settings.per_file_ignores = per_file_ignores;
|
configuration.per_file_ignores = per_file_ignores;
|
||||||
}
|
}
|
||||||
if !cli.select.is_empty() {
|
if !cli.select.is_empty() {
|
||||||
warn_on(
|
warn_on(
|
||||||
|
@ -272,10 +277,10 @@ fn inner_main() -> Result<ExitCode> {
|
||||||
&cli.select,
|
&cli.select,
|
||||||
&cli.ignore,
|
&cli.ignore,
|
||||||
&cli.extend_ignore,
|
&cli.extend_ignore,
|
||||||
&settings,
|
&configuration,
|
||||||
&pyproject,
|
&pyproject,
|
||||||
);
|
);
|
||||||
settings.select = cli.select;
|
configuration.select = cli.select;
|
||||||
}
|
}
|
||||||
if !cli.extend_select.is_empty() {
|
if !cli.extend_select.is_empty() {
|
||||||
warn_on(
|
warn_on(
|
||||||
|
@ -283,22 +288,22 @@ fn inner_main() -> Result<ExitCode> {
|
||||||
&cli.extend_select,
|
&cli.extend_select,
|
||||||
&cli.ignore,
|
&cli.ignore,
|
||||||
&cli.extend_ignore,
|
&cli.extend_ignore,
|
||||||
&settings,
|
&configuration,
|
||||||
&pyproject,
|
&pyproject,
|
||||||
);
|
);
|
||||||
settings.extend_select = cli.extend_select;
|
configuration.extend_select = cli.extend_select;
|
||||||
}
|
}
|
||||||
if !cli.ignore.is_empty() {
|
if !cli.ignore.is_empty() {
|
||||||
settings.ignore = cli.ignore;
|
configuration.ignore = cli.ignore;
|
||||||
}
|
}
|
||||||
if !cli.extend_ignore.is_empty() {
|
if !cli.extend_ignore.is_empty() {
|
||||||
settings.extend_ignore = cli.extend_ignore;
|
configuration.extend_ignore = cli.extend_ignore;
|
||||||
}
|
}
|
||||||
if let Some(target_version) = cli.target_version {
|
if let Some(target_version) = cli.target_version {
|
||||||
settings.target_version = target_version;
|
configuration.target_version = target_version;
|
||||||
}
|
}
|
||||||
if let Some(dummy_variable_rgx) = cli.dummy_variable_rgx {
|
if let Some(dummy_variable_rgx) = cli.dummy_variable_rgx {
|
||||||
settings.dummy_variable_rgx = dummy_variable_rgx;
|
configuration.dummy_variable_rgx = dummy_variable_rgx;
|
||||||
}
|
}
|
||||||
|
|
||||||
if cli.show_settings && cli.show_files {
|
if cli.show_settings && cli.show_files {
|
||||||
|
@ -306,11 +311,11 @@ fn inner_main() -> Result<ExitCode> {
|
||||||
return Ok(ExitCode::FAILURE);
|
return Ok(ExitCode::FAILURE);
|
||||||
}
|
}
|
||||||
if cli.show_settings {
|
if cli.show_settings {
|
||||||
show_settings(settings, project_root, pyproject);
|
show_settings(configuration, project_root, pyproject);
|
||||||
return Ok(ExitCode::SUCCESS);
|
return Ok(ExitCode::SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
let settings = Settings::from_raw(settings);
|
let settings = Settings::from_configuration(configuration);
|
||||||
|
|
||||||
if cli.show_files {
|
if cli.show_files {
|
||||||
show_files(&cli.files, &settings);
|
show_files(&cli.files, &settings);
|
||||||
|
|
367
src/settings.rs
367
src/settings.rs
|
@ -1,367 +0,0 @@
|
||||||
use std::collections::BTreeSet;
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use glob::Pattern;
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use regex::Regex;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::checks::CheckCode;
|
|
||||||
use crate::checks_gen::{CheckCodePrefix, PrefixSpecificity};
|
|
||||||
use crate::pyproject::{load_config, StrCheckCodePair};
|
|
||||||
use crate::{flake8_quotes, fs};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub enum PythonVersion {
|
|
||||||
Py33,
|
|
||||||
Py34,
|
|
||||||
Py35,
|
|
||||||
Py36,
|
|
||||||
Py37,
|
|
||||||
Py38,
|
|
||||||
Py39,
|
|
||||||
Py310,
|
|
||||||
Py311,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for PythonVersion {
|
|
||||||
type Err = anyhow::Error;
|
|
||||||
|
|
||||||
fn from_str(string: &str) -> Result<Self, Self::Err> {
|
|
||||||
match string {
|
|
||||||
"py33" => Ok(PythonVersion::Py33),
|
|
||||||
"py34" => Ok(PythonVersion::Py34),
|
|
||||||
"py35" => Ok(PythonVersion::Py35),
|
|
||||||
"py36" => Ok(PythonVersion::Py36),
|
|
||||||
"py37" => Ok(PythonVersion::Py37),
|
|
||||||
"py38" => Ok(PythonVersion::Py38),
|
|
||||||
"py39" => Ok(PythonVersion::Py39),
|
|
||||||
"py310" => Ok(PythonVersion::Py310),
|
|
||||||
"py311" => Ok(PythonVersion::Py311),
|
|
||||||
_ => Err(anyhow!("Unknown version: {}", string)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash)]
|
|
||||||
pub enum FilePattern {
|
|
||||||
Simple(&'static str),
|
|
||||||
Complex(Pattern, Option<Pattern>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilePattern {
|
|
||||||
pub fn from_user(pattern: &str, project_root: &Option<PathBuf>) -> Self {
|
|
||||||
let path = Path::new(pattern);
|
|
||||||
let absolute_path = match project_root {
|
|
||||||
Some(project_root) => fs::normalize_path_to(path, project_root),
|
|
||||||
None => fs::normalize_path(path),
|
|
||||||
};
|
|
||||||
|
|
||||||
let absolute = Pattern::new(&absolute_path.to_string_lossy()).expect("Invalid pattern.");
|
|
||||||
let basename = if !pattern.contains(std::path::MAIN_SEPARATOR) {
|
|
||||||
Some(Pattern::new(pattern).expect("Invalid pattern."))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
FilePattern::Complex(absolute, basename)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash)]
|
|
||||||
pub struct PerFileIgnore {
|
|
||||||
pub pattern: FilePattern,
|
|
||||||
pub codes: BTreeSet<CheckCode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PerFileIgnore {
|
|
||||||
pub fn new(user_in: StrCheckCodePair, project_root: &Option<PathBuf>) -> Self {
|
|
||||||
let pattern = FilePattern::from_user(user_in.pattern.as_str(), project_root);
|
|
||||||
let codes = BTreeSet::from_iter(user_in.code.codes());
|
|
||||||
Self { pattern, codes }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RawSettings {
|
|
||||||
pub dummy_variable_rgx: Regex,
|
|
||||||
pub exclude: Vec<FilePattern>,
|
|
||||||
pub extend_exclude: Vec<FilePattern>,
|
|
||||||
pub extend_ignore: Vec<CheckCodePrefix>,
|
|
||||||
pub extend_select: Vec<CheckCodePrefix>,
|
|
||||||
pub ignore: Vec<CheckCodePrefix>,
|
|
||||||
pub line_length: usize,
|
|
||||||
pub per_file_ignores: Vec<PerFileIgnore>,
|
|
||||||
pub select: Vec<CheckCodePrefix>,
|
|
||||||
pub target_version: PythonVersion,
|
|
||||||
// Plugins
|
|
||||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
|
||||||
}
|
|
||||||
|
|
||||||
static DEFAULT_EXCLUDE: Lazy<Vec<FilePattern>> = Lazy::new(|| {
|
|
||||||
vec![
|
|
||||||
FilePattern::Simple(".bzr"),
|
|
||||||
FilePattern::Simple(".direnv"),
|
|
||||||
FilePattern::Simple(".eggs"),
|
|
||||||
FilePattern::Simple(".git"),
|
|
||||||
FilePattern::Simple(".hg"),
|
|
||||||
FilePattern::Simple(".mypy_cache"),
|
|
||||||
FilePattern::Simple(".nox"),
|
|
||||||
FilePattern::Simple(".pants.d"),
|
|
||||||
FilePattern::Simple(".ruff_cache"),
|
|
||||||
FilePattern::Simple(".svn"),
|
|
||||||
FilePattern::Simple(".tox"),
|
|
||||||
FilePattern::Simple(".venv"),
|
|
||||||
FilePattern::Simple("__pypackages__"),
|
|
||||||
FilePattern::Simple("_build"),
|
|
||||||
FilePattern::Simple("buck-out"),
|
|
||||||
FilePattern::Simple("build"),
|
|
||||||
FilePattern::Simple("dist"),
|
|
||||||
FilePattern::Simple("node_modules"),
|
|
||||||
FilePattern::Simple("venv"),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
static DEFAULT_DUMMY_VARIABLE_RGX: Lazy<Regex> =
|
|
||||||
Lazy::new(|| Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap());
|
|
||||||
|
|
||||||
impl RawSettings {
|
|
||||||
pub fn from_pyproject(
|
|
||||||
pyproject: &Option<PathBuf>,
|
|
||||||
project_root: &Option<PathBuf>,
|
|
||||||
quiet: bool,
|
|
||||||
) -> Result<Self> {
|
|
||||||
let config = load_config(pyproject, quiet)?;
|
|
||||||
Ok(RawSettings {
|
|
||||||
dummy_variable_rgx: match config.dummy_variable_rgx {
|
|
||||||
Some(pattern) => Regex::new(&pattern)
|
|
||||||
.map_err(|e| anyhow!("Invalid dummy-variable-rgx value: {e}"))?,
|
|
||||||
None => DEFAULT_DUMMY_VARIABLE_RGX.clone(),
|
|
||||||
},
|
|
||||||
target_version: config.target_version.unwrap_or(PythonVersion::Py310),
|
|
||||||
exclude: config
|
|
||||||
.exclude
|
|
||||||
.map(|paths| {
|
|
||||||
paths
|
|
||||||
.iter()
|
|
||||||
.map(|path| FilePattern::from_user(path, project_root))
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| DEFAULT_EXCLUDE.clone()),
|
|
||||||
extend_exclude: config
|
|
||||||
.extend_exclude
|
|
||||||
.iter()
|
|
||||||
.map(|path| FilePattern::from_user(path, project_root))
|
|
||||||
.collect(),
|
|
||||||
extend_ignore: config.extend_ignore,
|
|
||||||
select: config
|
|
||||||
.select
|
|
||||||
.unwrap_or_else(|| vec![CheckCodePrefix::E, CheckCodePrefix::F]),
|
|
||||||
extend_select: config.extend_select,
|
|
||||||
ignore: config.ignore,
|
|
||||||
line_length: config.line_length.unwrap_or(88),
|
|
||||||
per_file_ignores: config
|
|
||||||
.per_file_ignores
|
|
||||||
.into_iter()
|
|
||||||
.map(|pair| PerFileIgnore::new(pair, project_root))
|
|
||||||
.collect(),
|
|
||||||
// Plugins
|
|
||||||
flake8_quotes: config
|
|
||||||
.flake8_quotes
|
|
||||||
.map(flake8_quotes::settings::Settings::from_config)
|
|
||||||
.unwrap_or_default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Settings {
|
|
||||||
pub dummy_variable_rgx: Regex,
|
|
||||||
pub enabled: BTreeSet<CheckCode>,
|
|
||||||
pub exclude: Vec<FilePattern>,
|
|
||||||
pub extend_exclude: Vec<FilePattern>,
|
|
||||||
pub line_length: usize,
|
|
||||||
pub per_file_ignores: Vec<PerFileIgnore>,
|
|
||||||
pub target_version: PythonVersion,
|
|
||||||
// Plugins
|
|
||||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given a set of selected and ignored prefixes, resolve the set of enabled error codes.
|
|
||||||
fn resolve_codes(
|
|
||||||
select: &[CheckCodePrefix],
|
|
||||||
extend_select: &[CheckCodePrefix],
|
|
||||||
ignore: &[CheckCodePrefix],
|
|
||||||
extend_ignore: &[CheckCodePrefix],
|
|
||||||
) -> BTreeSet<CheckCode> {
|
|
||||||
let mut codes: BTreeSet<CheckCode> = BTreeSet::new();
|
|
||||||
for specificity in [
|
|
||||||
PrefixSpecificity::Category,
|
|
||||||
PrefixSpecificity::Hundreds,
|
|
||||||
PrefixSpecificity::Tens,
|
|
||||||
PrefixSpecificity::Explicit,
|
|
||||||
] {
|
|
||||||
for prefix in select {
|
|
||||||
if prefix.specificity() == specificity {
|
|
||||||
codes.extend(prefix.codes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for prefix in extend_select {
|
|
||||||
if prefix.specificity() == specificity {
|
|
||||||
codes.extend(prefix.codes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for prefix in ignore {
|
|
||||||
if prefix.specificity() == specificity {
|
|
||||||
for code in prefix.codes() {
|
|
||||||
codes.remove(&code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for prefix in extend_ignore {
|
|
||||||
if prefix.specificity() == specificity {
|
|
||||||
for code in prefix.codes() {
|
|
||||||
codes.remove(&code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
codes
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Settings {
|
|
||||||
pub fn from_raw(settings: RawSettings) -> Self {
|
|
||||||
Self {
|
|
||||||
dummy_variable_rgx: settings.dummy_variable_rgx,
|
|
||||||
enabled: resolve_codes(
|
|
||||||
&settings.select,
|
|
||||||
&settings.extend_select,
|
|
||||||
&settings.ignore,
|
|
||||||
&settings.extend_ignore,
|
|
||||||
),
|
|
||||||
exclude: settings.exclude,
|
|
||||||
extend_exclude: settings.extend_exclude,
|
|
||||||
flake8_quotes: settings.flake8_quotes,
|
|
||||||
line_length: settings.line_length,
|
|
||||||
per_file_ignores: settings.per_file_ignores,
|
|
||||||
target_version: settings.target_version,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn for_rule(check_code: CheckCode) -> Self {
|
|
||||||
Self {
|
|
||||||
dummy_variable_rgx: DEFAULT_DUMMY_VARIABLE_RGX.clone(),
|
|
||||||
enabled: BTreeSet::from([check_code]),
|
|
||||||
exclude: vec![],
|
|
||||||
extend_exclude: vec![],
|
|
||||||
line_length: 88,
|
|
||||||
per_file_ignores: vec![],
|
|
||||||
target_version: PythonVersion::Py310,
|
|
||||||
flake8_quotes: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn for_rules(check_codes: Vec<CheckCode>) -> Self {
|
|
||||||
Self {
|
|
||||||
dummy_variable_rgx: DEFAULT_DUMMY_VARIABLE_RGX.clone(),
|
|
||||||
enabled: BTreeSet::from_iter(check_codes),
|
|
||||||
exclude: vec![],
|
|
||||||
extend_exclude: vec![],
|
|
||||||
line_length: 88,
|
|
||||||
per_file_ignores: vec![],
|
|
||||||
target_version: PythonVersion::Py310,
|
|
||||||
flake8_quotes: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for Settings {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.line_length.hash(state);
|
|
||||||
self.dummy_variable_rgx.as_str().hash(state);
|
|
||||||
for value in self.enabled.iter() {
|
|
||||||
value.hash(state);
|
|
||||||
}
|
|
||||||
for value in self.per_file_ignores.iter() {
|
|
||||||
value.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Struct to render user-facing exclusion patterns.
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct Exclusion {
|
|
||||||
basename: Option<String>,
|
|
||||||
absolute: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Exclusion {
|
|
||||||
pub fn from_file_pattern(file_pattern: FilePattern) -> Self {
|
|
||||||
match file_pattern {
|
|
||||||
FilePattern::Simple(basename) => Exclusion {
|
|
||||||
basename: Some(basename.to_string()),
|
|
||||||
absolute: None,
|
|
||||||
},
|
|
||||||
FilePattern::Complex(absolute, basename) => Exclusion {
|
|
||||||
basename: basename.map(|pattern| pattern.to_string()),
|
|
||||||
absolute: Some(absolute.to_string()),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Struct to render user-facing Settings.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct CurrentSettings {
|
|
||||||
pub dummy_variable_rgx: Regex,
|
|
||||||
pub exclude: Vec<Exclusion>,
|
|
||||||
pub extend_exclude: Vec<Exclusion>,
|
|
||||||
pub extend_ignore: Vec<CheckCodePrefix>,
|
|
||||||
pub extend_select: Vec<CheckCodePrefix>,
|
|
||||||
pub ignore: Vec<CheckCodePrefix>,
|
|
||||||
pub line_length: usize,
|
|
||||||
pub per_file_ignores: Vec<PerFileIgnore>,
|
|
||||||
pub select: Vec<CheckCodePrefix>,
|
|
||||||
pub target_version: PythonVersion,
|
|
||||||
// Plugins
|
|
||||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
|
||||||
// Non-settings exposed to the user
|
|
||||||
pub project_root: Option<PathBuf>,
|
|
||||||
pub pyproject: Option<PathBuf>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CurrentSettings {
|
|
||||||
pub fn from_settings(
|
|
||||||
settings: RawSettings,
|
|
||||||
project_root: Option<PathBuf>,
|
|
||||||
pyproject: Option<PathBuf>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
dummy_variable_rgx: settings.dummy_variable_rgx,
|
|
||||||
exclude: settings
|
|
||||||
.exclude
|
|
||||||
.into_iter()
|
|
||||||
.map(Exclusion::from_file_pattern)
|
|
||||||
.collect(),
|
|
||||||
extend_exclude: settings
|
|
||||||
.extend_exclude
|
|
||||||
.into_iter()
|
|
||||||
.map(Exclusion::from_file_pattern)
|
|
||||||
.collect(),
|
|
||||||
extend_ignore: settings.extend_ignore,
|
|
||||||
extend_select: settings.extend_select,
|
|
||||||
ignore: settings.ignore,
|
|
||||||
line_length: settings.line_length,
|
|
||||||
per_file_ignores: settings.per_file_ignores,
|
|
||||||
select: settings.select,
|
|
||||||
target_version: settings.target_version,
|
|
||||||
flake8_quotes: settings.flake8_quotes,
|
|
||||||
project_root,
|
|
||||||
pyproject,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
102
src/settings/configuration.rs
Normal file
102
src/settings/configuration.rs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
use crate::checks_gen::CheckCodePrefix;
|
||||||
|
use crate::flake8_quotes;
|
||||||
|
use crate::settings::pyproject::load_options;
|
||||||
|
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Configuration {
|
||||||
|
pub dummy_variable_rgx: Regex,
|
||||||
|
pub exclude: Vec<FilePattern>,
|
||||||
|
pub extend_exclude: Vec<FilePattern>,
|
||||||
|
pub extend_ignore: Vec<CheckCodePrefix>,
|
||||||
|
pub extend_select: Vec<CheckCodePrefix>,
|
||||||
|
pub ignore: Vec<CheckCodePrefix>,
|
||||||
|
pub line_length: usize,
|
||||||
|
pub per_file_ignores: Vec<PerFileIgnore>,
|
||||||
|
pub select: Vec<CheckCodePrefix>,
|
||||||
|
pub target_version: PythonVersion,
|
||||||
|
// Plugins
|
||||||
|
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_EXCLUDE: Lazy<Vec<FilePattern>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
FilePattern::Simple(".bzr"),
|
||||||
|
FilePattern::Simple(".direnv"),
|
||||||
|
FilePattern::Simple(".eggs"),
|
||||||
|
FilePattern::Simple(".git"),
|
||||||
|
FilePattern::Simple(".hg"),
|
||||||
|
FilePattern::Simple(".mypy_cache"),
|
||||||
|
FilePattern::Simple(".nox"),
|
||||||
|
FilePattern::Simple(".pants.d"),
|
||||||
|
FilePattern::Simple(".ruff_cache"),
|
||||||
|
FilePattern::Simple(".svn"),
|
||||||
|
FilePattern::Simple(".tox"),
|
||||||
|
FilePattern::Simple(".venv"),
|
||||||
|
FilePattern::Simple("__pypackages__"),
|
||||||
|
FilePattern::Simple("_build"),
|
||||||
|
FilePattern::Simple("buck-out"),
|
||||||
|
FilePattern::Simple("build"),
|
||||||
|
FilePattern::Simple("dist"),
|
||||||
|
FilePattern::Simple("node_modules"),
|
||||||
|
FilePattern::Simple("venv"),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
static DEFAULT_DUMMY_VARIABLE_RGX: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap());
|
||||||
|
|
||||||
|
impl Configuration {
|
||||||
|
pub fn from_pyproject(
|
||||||
|
pyproject: &Option<PathBuf>,
|
||||||
|
project_root: &Option<PathBuf>,
|
||||||
|
quiet: bool,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let options = load_options(pyproject, quiet)?;
|
||||||
|
Ok(Configuration {
|
||||||
|
dummy_variable_rgx: match options.dummy_variable_rgx {
|
||||||
|
Some(pattern) => Regex::new(&pattern)
|
||||||
|
.map_err(|e| anyhow!("Invalid dummy-variable-rgx value: {e}"))?,
|
||||||
|
None => DEFAULT_DUMMY_VARIABLE_RGX.clone(),
|
||||||
|
},
|
||||||
|
target_version: options.target_version.unwrap_or(PythonVersion::Py310),
|
||||||
|
exclude: options
|
||||||
|
.exclude
|
||||||
|
.map(|paths| {
|
||||||
|
paths
|
||||||
|
.iter()
|
||||||
|
.map(|path| FilePattern::from_user(path, project_root))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| DEFAULT_EXCLUDE.clone()),
|
||||||
|
extend_exclude: options
|
||||||
|
.extend_exclude
|
||||||
|
.iter()
|
||||||
|
.map(|path| FilePattern::from_user(path, project_root))
|
||||||
|
.collect(),
|
||||||
|
extend_ignore: options.extend_ignore,
|
||||||
|
select: options
|
||||||
|
.select
|
||||||
|
.unwrap_or_else(|| vec![CheckCodePrefix::E, CheckCodePrefix::F]),
|
||||||
|
extend_select: options.extend_select,
|
||||||
|
ignore: options.ignore,
|
||||||
|
line_length: options.line_length.unwrap_or(88),
|
||||||
|
per_file_ignores: options
|
||||||
|
.per_file_ignores
|
||||||
|
.into_iter()
|
||||||
|
.map(|pair| PerFileIgnore::new(pair, project_root))
|
||||||
|
.collect(),
|
||||||
|
// Plugins
|
||||||
|
flake8_quotes: options
|
||||||
|
.flake8_quotes
|
||||||
|
.map(flake8_quotes::settings::Settings::from_config)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
133
src/settings/mod.rs
Normal file
133
src/settings/mod.rs
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
//! Effective program settings, taking into account pyproject.toml and command-line options.
|
||||||
|
//! Structure is optimized for internal usage, as opposed to external visibility or parsing.
|
||||||
|
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
use crate::checks::CheckCode;
|
||||||
|
use crate::checks_gen::{CheckCodePrefix, PrefixSpecificity};
|
||||||
|
use crate::flake8_quotes;
|
||||||
|
use crate::settings::configuration::Configuration;
|
||||||
|
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
||||||
|
|
||||||
|
pub mod configuration;
|
||||||
|
pub mod options;
|
||||||
|
pub mod pyproject;
|
||||||
|
pub mod types;
|
||||||
|
pub mod user;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub dummy_variable_rgx: Regex,
|
||||||
|
pub enabled: BTreeSet<CheckCode>,
|
||||||
|
pub exclude: Vec<FilePattern>,
|
||||||
|
pub extend_exclude: Vec<FilePattern>,
|
||||||
|
pub line_length: usize,
|
||||||
|
pub per_file_ignores: Vec<PerFileIgnore>,
|
||||||
|
pub target_version: PythonVersion,
|
||||||
|
// Plugins
|
||||||
|
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
pub fn from_configuration(config: Configuration) -> Self {
|
||||||
|
Self {
|
||||||
|
dummy_variable_rgx: config.dummy_variable_rgx,
|
||||||
|
enabled: resolve_codes(
|
||||||
|
&config.select,
|
||||||
|
&config.extend_select,
|
||||||
|
&config.ignore,
|
||||||
|
&config.extend_ignore,
|
||||||
|
),
|
||||||
|
exclude: config.exclude,
|
||||||
|
extend_exclude: config.extend_exclude,
|
||||||
|
flake8_quotes: config.flake8_quotes,
|
||||||
|
line_length: config.line_length,
|
||||||
|
per_file_ignores: config.per_file_ignores,
|
||||||
|
target_version: config.target_version,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn for_rule(check_code: CheckCode) -> Self {
|
||||||
|
Self {
|
||||||
|
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
|
||||||
|
enabled: BTreeSet::from([check_code]),
|
||||||
|
exclude: vec![],
|
||||||
|
extend_exclude: vec![],
|
||||||
|
line_length: 88,
|
||||||
|
per_file_ignores: vec![],
|
||||||
|
target_version: PythonVersion::Py310,
|
||||||
|
flake8_quotes: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn for_rules(check_codes: Vec<CheckCode>) -> Self {
|
||||||
|
Self {
|
||||||
|
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
|
||||||
|
enabled: BTreeSet::from_iter(check_codes),
|
||||||
|
exclude: vec![],
|
||||||
|
extend_exclude: vec![],
|
||||||
|
line_length: 88,
|
||||||
|
per_file_ignores: vec![],
|
||||||
|
target_version: PythonVersion::Py310,
|
||||||
|
flake8_quotes: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Settings {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.line_length.hash(state);
|
||||||
|
self.dummy_variable_rgx.as_str().hash(state);
|
||||||
|
for value in self.enabled.iter() {
|
||||||
|
value.hash(state);
|
||||||
|
}
|
||||||
|
for value in self.per_file_ignores.iter() {
|
||||||
|
value.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a set of selected and ignored prefixes, resolve the set of enabled error codes.
|
||||||
|
fn resolve_codes(
|
||||||
|
select: &[CheckCodePrefix],
|
||||||
|
extend_select: &[CheckCodePrefix],
|
||||||
|
ignore: &[CheckCodePrefix],
|
||||||
|
extend_ignore: &[CheckCodePrefix],
|
||||||
|
) -> BTreeSet<CheckCode> {
|
||||||
|
let mut codes: BTreeSet<CheckCode> = BTreeSet::new();
|
||||||
|
for specificity in [
|
||||||
|
PrefixSpecificity::Category,
|
||||||
|
PrefixSpecificity::Hundreds,
|
||||||
|
PrefixSpecificity::Tens,
|
||||||
|
PrefixSpecificity::Explicit,
|
||||||
|
] {
|
||||||
|
for prefix in select {
|
||||||
|
if prefix.specificity() == specificity {
|
||||||
|
codes.extend(prefix.codes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for prefix in extend_select {
|
||||||
|
if prefix.specificity() == specificity {
|
||||||
|
codes.extend(prefix.codes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for prefix in ignore {
|
||||||
|
if prefix.specificity() == specificity {
|
||||||
|
for code in prefix.codes() {
|
||||||
|
codes.remove(&code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for prefix in extend_ignore {
|
||||||
|
if prefix.specificity() == specificity {
|
||||||
|
for code in prefix.codes() {
|
||||||
|
codes.remove(&code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
codes
|
||||||
|
}
|
28
src/settings/options.rs
Normal file
28
src/settings/options.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
//! Options that the user can provide via pyproject.toml.
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::checks_gen::CheckCodePrefix;
|
||||||
|
use crate::flake8_quotes;
|
||||||
|
use crate::settings::types::{PythonVersion, StrCheckCodePair};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Deserialize, Default)]
|
||||||
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
|
pub struct Options {
|
||||||
|
pub line_length: Option<usize>,
|
||||||
|
pub exclude: Option<Vec<String>>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub extend_exclude: Vec<String>,
|
||||||
|
pub select: Option<Vec<CheckCodePrefix>>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub extend_select: Vec<CheckCodePrefix>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub ignore: Vec<CheckCodePrefix>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub extend_ignore: Vec<CheckCodePrefix>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub per_file_ignores: Vec<StrCheckCodePair>,
|
||||||
|
pub dummy_variable_rgx: Option<String>,
|
||||||
|
pub target_version: Option<PythonVersion>,
|
||||||
|
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
|
||||||
|
}
|
|
@ -1,106 +1,26 @@
|
||||||
use std::path::{Path, PathBuf};
|
//! Utilities for locating (and extracting configuration from) a pyproject.toml.
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use common_path::common_path_all;
|
use common_path::common_path_all;
|
||||||
use path_absolutize::Absolutize;
|
use path_absolutize::Absolutize;
|
||||||
use serde::de;
|
use serde::Deserialize;
|
||||||
use serde::{Deserialize, Deserializer};
|
|
||||||
|
|
||||||
use crate::checks_gen::CheckCodePrefix;
|
use crate::fs;
|
||||||
use crate::settings::PythonVersion;
|
use crate::settings::options::Options;
|
||||||
use crate::{flake8_quotes, fs};
|
|
||||||
|
|
||||||
pub fn load_config(pyproject: &Option<PathBuf>, quiet: bool) -> Result<Config> {
|
|
||||||
match pyproject {
|
|
||||||
Some(pyproject) => Ok(parse_pyproject_toml(pyproject)?
|
|
||||||
.tool
|
|
||||||
.and_then(|tool| tool.ruff)
|
|
||||||
.unwrap_or_default()),
|
|
||||||
None => {
|
|
||||||
if !quiet {
|
|
||||||
eprintln!("No pyproject.toml found.");
|
|
||||||
eprintln!("Falling back to default configuration...");
|
|
||||||
}
|
|
||||||
Ok(Default::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Deserialize, Default)]
|
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
|
||||||
pub struct Config {
|
|
||||||
pub line_length: Option<usize>,
|
|
||||||
pub exclude: Option<Vec<String>>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub extend_exclude: Vec<String>,
|
|
||||||
pub select: Option<Vec<CheckCodePrefix>>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub extend_select: Vec<CheckCodePrefix>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub ignore: Vec<CheckCodePrefix>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub extend_ignore: Vec<CheckCodePrefix>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub per_file_ignores: Vec<StrCheckCodePair>,
|
|
||||||
pub dummy_variable_rgx: Option<String>,
|
|
||||||
pub target_version: Option<PythonVersion>,
|
|
||||||
pub flake8_quotes: Option<flake8_quotes::settings::Config>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct StrCheckCodePair {
|
|
||||||
pub pattern: String,
|
|
||||||
pub code: CheckCodePrefix,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StrCheckCodePair {
|
|
||||||
const EXPECTED_PATTERN: &'static str = "<FilePattern>:<CheckCode> pattern";
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for StrCheckCodePair {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let str_result = String::deserialize(deserializer)?;
|
|
||||||
Self::from_str(str_result.as_str()).map_err(|_| {
|
|
||||||
de::Error::invalid_value(
|
|
||||||
de::Unexpected::Str(str_result.as_str()),
|
|
||||||
&Self::EXPECTED_PATTERN,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for StrCheckCodePair {
|
|
||||||
type Err = anyhow::Error;
|
|
||||||
|
|
||||||
fn from_str(string: &str) -> Result<Self, Self::Err> {
|
|
||||||
let (pattern_str, code_string) = {
|
|
||||||
let tokens = string.split(':').collect::<Vec<_>>();
|
|
||||||
if tokens.len() != 2 {
|
|
||||||
return Err(anyhow!("Expected {}", Self::EXPECTED_PATTERN));
|
|
||||||
}
|
|
||||||
(tokens[0].trim(), tokens[1].trim())
|
|
||||||
};
|
|
||||||
let code = CheckCodePrefix::from_str(code_string)?;
|
|
||||||
let pattern = pattern_str.into();
|
|
||||||
Ok(Self { pattern, code })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||||
struct Tools {
|
struct Tools {
|
||||||
ruff: Option<Config>,
|
ruff: Option<Options>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||||
struct PyProject {
|
struct Pyproject {
|
||||||
tool: Option<Tools>,
|
tool: Option<Tools>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_pyproject_toml(path: &Path) -> Result<PyProject> {
|
fn parse_pyproject_toml(path: &Path) -> Result<Pyproject> {
|
||||||
let contents = fs::read_file(path)?;
|
let contents = fs::read_file(path)?;
|
||||||
toml::from_str(&contents).map_err(|e| e.into())
|
toml::from_str(&contents).map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
@ -149,6 +69,22 @@ pub fn find_project_root(sources: &[PathBuf]) -> Option<PathBuf> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_options(pyproject: &Option<PathBuf>, quiet: bool) -> Result<Options> {
|
||||||
|
match pyproject {
|
||||||
|
Some(pyproject) => Ok(parse_pyproject_toml(pyproject)?
|
||||||
|
.tool
|
||||||
|
.and_then(|tool| tool.ruff)
|
||||||
|
.unwrap_or_default()),
|
||||||
|
None => {
|
||||||
|
if !quiet {
|
||||||
|
eprintln!("No pyproject.toml found.");
|
||||||
|
eprintln!("Falling back to default configuration...");
|
||||||
|
}
|
||||||
|
Ok(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::env::current_dir;
|
use std::env::current_dir;
|
||||||
|
@ -160,25 +96,24 @@ mod tests {
|
||||||
use crate::checks_gen::CheckCodePrefix;
|
use crate::checks_gen::CheckCodePrefix;
|
||||||
use crate::flake8_quotes;
|
use crate::flake8_quotes;
|
||||||
use crate::flake8_quotes::settings::Quote;
|
use crate::flake8_quotes::settings::Quote;
|
||||||
use crate::pyproject::{
|
use crate::settings::pyproject::{
|
||||||
find_project_root, find_pyproject_toml, parse_pyproject_toml, Config, PyProject, Tools,
|
find_project_root, find_pyproject_toml, parse_pyproject_toml, Options, Pyproject, Tools,
|
||||||
};
|
};
|
||||||
|
use crate::settings::types::StrCheckCodePair;
|
||||||
use super::StrCheckCodePair;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize() -> Result<()> {
|
fn deserialize() -> Result<()> {
|
||||||
let pyproject: PyProject = toml::from_str(r#""#)?;
|
let pyproject: Pyproject = toml::from_str(r#""#)?;
|
||||||
assert_eq!(pyproject.tool, None);
|
assert_eq!(pyproject.tool, None);
|
||||||
|
|
||||||
let pyproject: PyProject = toml::from_str(
|
let pyproject: Pyproject = toml::from_str(
|
||||||
r#"
|
r#"
|
||||||
[tool.black]
|
[tool.black]
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
assert_eq!(pyproject.tool, Some(Tools { ruff: None }));
|
assert_eq!(pyproject.tool, Some(Tools { ruff: None }));
|
||||||
|
|
||||||
let pyproject: PyProject = toml::from_str(
|
let pyproject: Pyproject = toml::from_str(
|
||||||
r#"
|
r#"
|
||||||
[tool.black]
|
[tool.black]
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
@ -187,7 +122,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pyproject.tool,
|
pyproject.tool,
|
||||||
Some(Tools {
|
Some(Tools {
|
||||||
ruff: Some(Config {
|
ruff: Some(Options {
|
||||||
line_length: None,
|
line_length: None,
|
||||||
exclude: None,
|
exclude: None,
|
||||||
extend_exclude: vec![],
|
extend_exclude: vec![],
|
||||||
|
@ -203,7 +138,7 @@ mod tests {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let pyproject: PyProject = toml::from_str(
|
let pyproject: Pyproject = toml::from_str(
|
||||||
r#"
|
r#"
|
||||||
[tool.black]
|
[tool.black]
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
@ -213,7 +148,7 @@ line-length = 79
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pyproject.tool,
|
pyproject.tool,
|
||||||
Some(Tools {
|
Some(Tools {
|
||||||
ruff: Some(Config {
|
ruff: Some(Options {
|
||||||
line_length: Some(79),
|
line_length: Some(79),
|
||||||
exclude: None,
|
exclude: None,
|
||||||
extend_exclude: vec![],
|
extend_exclude: vec![],
|
||||||
|
@ -229,7 +164,7 @@ line-length = 79
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let pyproject: PyProject = toml::from_str(
|
let pyproject: Pyproject = toml::from_str(
|
||||||
r#"
|
r#"
|
||||||
[tool.black]
|
[tool.black]
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
@ -239,7 +174,7 @@ exclude = ["foo.py"]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pyproject.tool,
|
pyproject.tool,
|
||||||
Some(Tools {
|
Some(Tools {
|
||||||
ruff: Some(Config {
|
ruff: Some(Options {
|
||||||
line_length: None,
|
line_length: None,
|
||||||
exclude: Some(vec!["foo.py".to_string()]),
|
exclude: Some(vec!["foo.py".to_string()]),
|
||||||
extend_exclude: vec![],
|
extend_exclude: vec![],
|
||||||
|
@ -255,7 +190,7 @@ exclude = ["foo.py"]
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let pyproject: PyProject = toml::from_str(
|
let pyproject: Pyproject = toml::from_str(
|
||||||
r#"
|
r#"
|
||||||
[tool.black]
|
[tool.black]
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
@ -265,7 +200,7 @@ select = ["E501"]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pyproject.tool,
|
pyproject.tool,
|
||||||
Some(Tools {
|
Some(Tools {
|
||||||
ruff: Some(Config {
|
ruff: Some(Options {
|
||||||
line_length: None,
|
line_length: None,
|
||||||
exclude: None,
|
exclude: None,
|
||||||
extend_exclude: vec![],
|
extend_exclude: vec![],
|
||||||
|
@ -281,7 +216,7 @@ select = ["E501"]
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let pyproject: PyProject = toml::from_str(
|
let pyproject: Pyproject = toml::from_str(
|
||||||
r#"
|
r#"
|
||||||
[tool.black]
|
[tool.black]
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
@ -292,7 +227,7 @@ ignore = ["E501"]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pyproject.tool,
|
pyproject.tool,
|
||||||
Some(Tools {
|
Some(Tools {
|
||||||
ruff: Some(Config {
|
ruff: Some(Options {
|
||||||
line_length: None,
|
line_length: None,
|
||||||
exclude: None,
|
exclude: None,
|
||||||
extend_exclude: vec![],
|
extend_exclude: vec![],
|
||||||
|
@ -308,7 +243,7 @@ ignore = ["E501"]
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(toml::from_str::<PyProject>(
|
assert!(toml::from_str::<Pyproject>(
|
||||||
r#"
|
r#"
|
||||||
[tool.black]
|
[tool.black]
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
@ -317,7 +252,7 @@ line_length = 79
|
||||||
)
|
)
|
||||||
.is_err());
|
.is_err());
|
||||||
|
|
||||||
assert!(toml::from_str::<PyProject>(
|
assert!(toml::from_str::<Pyproject>(
|
||||||
r#"
|
r#"
|
||||||
[tool.black]
|
[tool.black]
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
@ -326,7 +261,7 @@ select = ["E123"]
|
||||||
)
|
)
|
||||||
.is_err());
|
.is_err());
|
||||||
|
|
||||||
assert!(toml::from_str::<PyProject>(
|
assert!(toml::from_str::<Pyproject>(
|
||||||
r#"
|
r#"
|
||||||
[tool.black]
|
[tool.black]
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
@ -358,7 +293,7 @@ other-attribute = 1
|
||||||
.expect("Unable to find tool.ruff.");
|
.expect("Unable to find tool.ruff.");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config,
|
config,
|
||||||
Config {
|
Options {
|
||||||
line_length: Some(88),
|
line_length: Some(88),
|
||||||
exclude: None,
|
exclude: None,
|
||||||
extend_exclude: vec![
|
extend_exclude: vec![
|
||||||
|
@ -376,7 +311,7 @@ other-attribute = 1
|
||||||
}],
|
}],
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
flake8_quotes: Some(flake8_quotes::settings::Config {
|
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||||
inline_quotes: Some(Quote::Single),
|
inline_quotes: Some(Quote::Single),
|
||||||
multiline_quotes: Some(Quote::Double),
|
multiline_quotes: Some(Quote::Double),
|
||||||
docstring_quotes: Some(Quote::Double),
|
docstring_quotes: Some(Quote::Double),
|
125
src/settings/types.rs
Normal file
125
src/settings/types.rs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use glob::Pattern;
|
||||||
|
use serde::{de, Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
|
use crate::checks::CheckCode;
|
||||||
|
use crate::checks_gen::CheckCodePrefix;
|
||||||
|
use crate::fs;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum PythonVersion {
|
||||||
|
Py33,
|
||||||
|
Py34,
|
||||||
|
Py35,
|
||||||
|
Py36,
|
||||||
|
Py37,
|
||||||
|
Py38,
|
||||||
|
Py39,
|
||||||
|
Py310,
|
||||||
|
Py311,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for PythonVersion {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(string: &str) -> Result<Self, Self::Err> {
|
||||||
|
match string {
|
||||||
|
"py33" => Ok(PythonVersion::Py33),
|
||||||
|
"py34" => Ok(PythonVersion::Py34),
|
||||||
|
"py35" => Ok(PythonVersion::Py35),
|
||||||
|
"py36" => Ok(PythonVersion::Py36),
|
||||||
|
"py37" => Ok(PythonVersion::Py37),
|
||||||
|
"py38" => Ok(PythonVersion::Py38),
|
||||||
|
"py39" => Ok(PythonVersion::Py39),
|
||||||
|
"py310" => Ok(PythonVersion::Py310),
|
||||||
|
"py311" => Ok(PythonVersion::Py311),
|
||||||
|
_ => Err(anyhow!("Unknown version: {}", string)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
pub enum FilePattern {
|
||||||
|
Simple(&'static str),
|
||||||
|
Complex(Pattern, Option<Pattern>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilePattern {
|
||||||
|
pub fn from_user(pattern: &str, project_root: &Option<PathBuf>) -> Self {
|
||||||
|
let path = Path::new(pattern);
|
||||||
|
let absolute_path = match project_root {
|
||||||
|
Some(project_root) => fs::normalize_path_to(path, project_root),
|
||||||
|
None => fs::normalize_path(path),
|
||||||
|
};
|
||||||
|
|
||||||
|
let absolute = Pattern::new(&absolute_path.to_string_lossy()).expect("Invalid pattern.");
|
||||||
|
let basename = if !pattern.contains(std::path::MAIN_SEPARATOR) {
|
||||||
|
Some(Pattern::new(pattern).expect("Invalid pattern."))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
FilePattern::Complex(absolute, basename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
pub struct PerFileIgnore {
|
||||||
|
pub pattern: FilePattern,
|
||||||
|
pub codes: BTreeSet<CheckCode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PerFileIgnore {
|
||||||
|
pub fn new(user_in: StrCheckCodePair, project_root: &Option<PathBuf>) -> Self {
|
||||||
|
let pattern = FilePattern::from_user(user_in.pattern.as_str(), project_root);
|
||||||
|
let codes = BTreeSet::from_iter(user_in.code.codes());
|
||||||
|
Self { pattern, codes }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct StrCheckCodePair {
|
||||||
|
pub pattern: String,
|
||||||
|
pub code: CheckCodePrefix,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StrCheckCodePair {
|
||||||
|
const EXPECTED_PATTERN: &'static str = "<FilePattern>:<CheckCode> pattern";
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for StrCheckCodePair {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let str_result = String::deserialize(deserializer)?;
|
||||||
|
Self::from_str(str_result.as_str()).map_err(|_| {
|
||||||
|
de::Error::invalid_value(
|
||||||
|
de::Unexpected::Str(str_result.as_str()),
|
||||||
|
&Self::EXPECTED_PATTERN,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for StrCheckCodePair {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(string: &str) -> Result<Self, Self::Err> {
|
||||||
|
let (pattern_str, code_string) = {
|
||||||
|
let tokens = string.split(':').collect::<Vec<_>>();
|
||||||
|
if tokens.len() != 2 {
|
||||||
|
return Err(anyhow!("Expected {}", Self::EXPECTED_PATTERN));
|
||||||
|
}
|
||||||
|
(tokens[0].trim(), tokens[1].trim())
|
||||||
|
};
|
||||||
|
let code = CheckCodePrefix::from_str(code_string)?;
|
||||||
|
let pattern = pattern_str.into();
|
||||||
|
Ok(Self { pattern, code })
|
||||||
|
}
|
||||||
|
}
|
84
src/settings/user.rs
Normal file
84
src/settings/user.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
//! Structs to render user-facing settings.
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
use crate::checks_gen::CheckCodePrefix;
|
||||||
|
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
||||||
|
use crate::{flake8_quotes, Configuration};
|
||||||
|
|
||||||
|
/// Struct to render user-facing exclusion patterns.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct Exclusion {
|
||||||
|
basename: Option<String>,
|
||||||
|
absolute: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Exclusion {
|
||||||
|
pub fn from_file_pattern(file_pattern: FilePattern) -> Self {
|
||||||
|
match file_pattern {
|
||||||
|
FilePattern::Simple(basename) => Exclusion {
|
||||||
|
basename: Some(basename.to_string()),
|
||||||
|
absolute: None,
|
||||||
|
},
|
||||||
|
FilePattern::Complex(absolute, basename) => Exclusion {
|
||||||
|
basename: basename.map(|pattern| pattern.to_string()),
|
||||||
|
absolute: Some(absolute.to_string()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Struct to render user-facing configuration.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UserConfiguration {
|
||||||
|
pub dummy_variable_rgx: Regex,
|
||||||
|
pub exclude: Vec<Exclusion>,
|
||||||
|
pub extend_exclude: Vec<Exclusion>,
|
||||||
|
pub extend_ignore: Vec<CheckCodePrefix>,
|
||||||
|
pub extend_select: Vec<CheckCodePrefix>,
|
||||||
|
pub ignore: Vec<CheckCodePrefix>,
|
||||||
|
pub line_length: usize,
|
||||||
|
pub per_file_ignores: Vec<PerFileIgnore>,
|
||||||
|
pub select: Vec<CheckCodePrefix>,
|
||||||
|
pub target_version: PythonVersion,
|
||||||
|
// Plugins
|
||||||
|
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||||
|
// Non-settings exposed to the user
|
||||||
|
pub project_root: Option<PathBuf>,
|
||||||
|
pub pyproject: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserConfiguration {
|
||||||
|
pub fn from_configuration(
|
||||||
|
settings: Configuration,
|
||||||
|
project_root: Option<PathBuf>,
|
||||||
|
pyproject: Option<PathBuf>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
dummy_variable_rgx: settings.dummy_variable_rgx,
|
||||||
|
exclude: settings
|
||||||
|
.exclude
|
||||||
|
.into_iter()
|
||||||
|
.map(Exclusion::from_file_pattern)
|
||||||
|
.collect(),
|
||||||
|
extend_exclude: settings
|
||||||
|
.extend_exclude
|
||||||
|
.into_iter()
|
||||||
|
.map(Exclusion::from_file_pattern)
|
||||||
|
.collect(),
|
||||||
|
extend_ignore: settings.extend_ignore,
|
||||||
|
extend_select: settings.extend_select,
|
||||||
|
ignore: settings.ignore,
|
||||||
|
line_length: settings.line_length,
|
||||||
|
per_file_ignores: settings.per_file_ignores,
|
||||||
|
select: settings.select,
|
||||||
|
target_version: settings.target_version,
|
||||||
|
flake8_quotes: settings.flake8_quotes,
|
||||||
|
project_root,
|
||||||
|
pyproject,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue