Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a3f06bab1 | ||
|
|
db59d5b558 | ||
|
|
2fcbf3ab62 | ||
|
|
fa9b10be72 | ||
|
|
c495cef529 | ||
|
|
c0c8dff6ce | ||
|
|
80b00cc89f |
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.89
|
||||
rev: v0.0.90
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -2054,7 +2054,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.89"
|
||||
version = "0.0.90"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.89"
|
||||
version = "0.0.90"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
30
README.md
30
README.md
@@ -38,13 +38,25 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
|
||||
1. [Installation and Usage](#installation-and-usage)
|
||||
2. [Configuration](#configuration)
|
||||
3. [Supported Rules](#supported-rules)
|
||||
4. [Editor Integrations](#editor-integrations)
|
||||
5. [FAQ](#faq)
|
||||
6. [Development](#development)
|
||||
7. [Releases](#releases)
|
||||
8. [Benchmarks](#benchmarks)
|
||||
9. [License](#license)
|
||||
10. [Contributing](#contributing)
|
||||
1. [Pyflakes](#pyflakes)
|
||||
2. [pycodestyle (error)](#pycodestyle-error)
|
||||
3. [pycodestyle (warning)](#pycodestyle-warning)
|
||||
4. [pydocstyle](#pydocstyle)
|
||||
5. [pyupgrade](#pyupgrade)
|
||||
6. [pep8-naming](#pep8-naming)
|
||||
7. [flake8-comprehensions](#flake8-comprehensions)
|
||||
8. [flake8-bugbear](#flake8-bugbear)
|
||||
9. [flake8-builtins](#flake8-builtins)
|
||||
10. [flake8-print](#flake8-print)
|
||||
11. [flake8-quotes](#flake8-quotes)
|
||||
12. [Meta rules](#meta-rules)
|
||||
5. [Editor Integrations](#editor-integrations)
|
||||
6. [FAQ](#faq)
|
||||
7. [Development](#development)
|
||||
8. [Releases](#releases)
|
||||
9. [Benchmarks](#benchmarks)
|
||||
10. [License](#license)
|
||||
11. [Contributing](#contributing)
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
@@ -77,7 +89,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.89
|
||||
rev: v0.0.90
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -397,7 +409,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
|
||||
| C413 | UnnecessaryCallAroundSorted | Unnecessary `(list\|reversed)` call around `sorted()` | |
|
||||
| C414 | UnnecessaryDoubleCastOrProcess | Unnecessary `(list\|reversed\|set\|sorted\|tuple)` call within `(list\|set\|sorted\|tuple)()` | |
|
||||
| C415 | UnnecessarySubscriptReversal | Unnecessary subscript reversal of iterable within `(reversed\|set\|sorted)()` | |
|
||||
| C416 | UnnecessaryComprehension | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`) | |
|
||||
| C416 | UnnecessaryComprehension | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`) | |
|
||||
| C417 | UnnecessaryMap | Unnecessary `map` usage (rewrite using a `(list\|set\|dict)` comprehension) | |
|
||||
|
||||
### flake8-bugbear
|
||||
|
||||
22
resources/test/fixtures/pyproject.toml
vendored
22
resources/test/fixtures/pyproject.toml
vendored
@@ -14,3 +14,25 @@ inline-quotes = "single"
|
||||
multiline-quotes = "double"
|
||||
docstring-quotes = "double"
|
||||
avoid-escape = true
|
||||
|
||||
[tool.ruff.pep8-naming]
|
||||
ignore-names = [
|
||||
"setUp",
|
||||
"tearDown",
|
||||
"setUpClass",
|
||||
"tearDownClass",
|
||||
"setUpModule",
|
||||
"tearDownModule",
|
||||
"asyncSetUp",
|
||||
"asyncTearDown",
|
||||
"setUpTestData",
|
||||
"failureException",
|
||||
"longMessage",
|
||||
"maxDiff",
|
||||
]
|
||||
classmethod-decorators = [
|
||||
"classmethod",
|
||||
]
|
||||
staticmethod-decorators = [
|
||||
"staticmethod",
|
||||
]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use once_cell::unsync::OnceCell;
|
||||
use rustpython_parser::ast::{Constant, Expr, ExprKind, Location, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::{BindingKind, Range, Scope};
|
||||
@@ -120,45 +121,47 @@ pub fn is_unpacking_assignment(stmt: &Stmt) -> bool {
|
||||
|
||||
/// Struct used to efficiently slice source code at (row, column) Locations.
|
||||
pub struct SourceCodeLocator<'a> {
|
||||
content: &'a str,
|
||||
offsets: Vec<Vec<usize>>,
|
||||
contents: &'a str,
|
||||
offsets: OnceCell<Vec<Vec<usize>>>,
|
||||
}
|
||||
|
||||
impl<'a> SourceCodeLocator<'a> {
|
||||
pub fn new(content: &'a str) -> Self {
|
||||
pub fn new(contents: &'a str) -> Self {
|
||||
SourceCodeLocator {
|
||||
content,
|
||||
offsets: Self::compute_offsets(content),
|
||||
contents,
|
||||
offsets: OnceCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_offsets(content: &str) -> Vec<Vec<usize>> {
|
||||
let mut offsets = vec![];
|
||||
let mut offset = 0;
|
||||
for line in content.lines() {
|
||||
let mut newline = 0;
|
||||
let mut line_offsets: Vec<usize> = vec![];
|
||||
for (i, char) in line.char_indices() {
|
||||
line_offsets.push(offset + i);
|
||||
newline = i + char.len_utf8();
|
||||
let mut offsets = vec![vec![]];
|
||||
let mut line_index = 0;
|
||||
for (i, char) in content.char_indices() {
|
||||
offsets[line_index].push(i);
|
||||
if char == '\n' {
|
||||
line_index += 1;
|
||||
offsets.push(vec![]);
|
||||
}
|
||||
line_offsets.push(offset + newline);
|
||||
offsets.push(line_offsets);
|
||||
offset += newline + 1;
|
||||
}
|
||||
offsets.push(vec![offset]);
|
||||
offsets
|
||||
}
|
||||
|
||||
fn get_or_init_offsets(&self) -> &Vec<Vec<usize>> {
|
||||
self.offsets
|
||||
.get_or_init(|| Self::compute_offsets(self.contents))
|
||||
}
|
||||
|
||||
pub fn slice_source_code_at(&self, location: &Location) -> &'a str {
|
||||
let offset = self.offsets[location.row() - 1][location.column() - 1];
|
||||
&self.content[offset..]
|
||||
let offsets = self.get_or_init_offsets();
|
||||
let offset = offsets[location.row() - 1][location.column() - 1];
|
||||
&self.contents[offset..]
|
||||
}
|
||||
|
||||
pub fn slice_source_code_range(&self, range: &Range) -> &'a str {
|
||||
let start = self.offsets[range.location.row() - 1][range.location.column() - 1];
|
||||
let end = self.offsets[range.end_location.row() - 1][range.end_location.column() - 1];
|
||||
&self.content[start..end]
|
||||
let offsets = self.get_or_init_offsets();
|
||||
let start = offsets[range.location.row() - 1][range.location.column() - 1];
|
||||
let end = offsets[range.end_location.row() - 1][range.end_location.column() - 1];
|
||||
&self.contents[start..end]
|
||||
}
|
||||
|
||||
pub fn partition_source_code_at(
|
||||
@@ -166,14 +169,15 @@ impl<'a> SourceCodeLocator<'a> {
|
||||
outer: &Range,
|
||||
inner: &Range,
|
||||
) -> (&'a str, &'a str, &'a str) {
|
||||
let outer_start = self.offsets[outer.location.row() - 1][outer.location.column() - 1];
|
||||
let outer_end = self.offsets[outer.end_location.row() - 1][outer.end_location.column() - 1];
|
||||
let inner_start = self.offsets[inner.location.row() - 1][inner.location.column() - 1];
|
||||
let inner_end = self.offsets[inner.end_location.row() - 1][inner.end_location.column() - 1];
|
||||
let offsets = self.get_or_init_offsets();
|
||||
let outer_start = offsets[outer.location.row() - 1][outer.location.column() - 1];
|
||||
let outer_end = offsets[outer.end_location.row() - 1][outer.end_location.column() - 1];
|
||||
let inner_start = offsets[inner.location.row() - 1][inner.location.column() - 1];
|
||||
let inner_end = offsets[inner.end_location.row() - 1][inner.end_location.column() - 1];
|
||||
(
|
||||
&self.content[outer_start..inner_start],
|
||||
&self.content[inner_start..inner_end],
|
||||
&self.content[inner_end..outer_end],
|
||||
&self.contents[outer_start..inner_start],
|
||||
&self.contents[inner_start..inner_end],
|
||||
&self.contents[inner_end..outer_end],
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -184,15 +188,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn source_code_locator_init() {
|
||||
let content = "x = 1\ny = 2\nz = x + y\n";
|
||||
let locator = SourceCodeLocator::new(content);
|
||||
let offsets = locator.get_or_init_offsets();
|
||||
assert_eq!(offsets.len(), 4);
|
||||
assert_eq!(offsets[0], [0, 1, 2, 3, 4, 5]);
|
||||
assert_eq!(offsets[1], [6, 7, 8, 9, 10, 11]);
|
||||
assert_eq!(offsets[2], [12, 13, 14, 15, 16, 17, 18, 19, 20, 21]);
|
||||
assert!(offsets[3].is_empty());
|
||||
|
||||
let content = "# \u{4e9c}\nclass Foo:\n \"\"\".\"\"\"";
|
||||
let locator = SourceCodeLocator::new(content);
|
||||
assert_eq!(locator.offsets.len(), 4);
|
||||
assert_eq!(locator.offsets[0], [0, 1, 2, 5]);
|
||||
assert_eq!(locator.offsets[1], [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
|
||||
assert_eq!(
|
||||
locator.offsets[2],
|
||||
[17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]
|
||||
);
|
||||
assert_eq!(locator.offsets[3], [29]);
|
||||
let offsets = locator.get_or_init_offsets();
|
||||
assert_eq!(offsets.len(), 3);
|
||||
assert_eq!(offsets[0], [0, 1, 2, 5]);
|
||||
assert_eq!(offsets[1], [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
|
||||
assert_eq!(offsets[2], [17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
|
||||
use log::error;
|
||||
use once_cell::unsync::OnceCell;
|
||||
use rustpython_parser::ast::{
|
||||
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind,
|
||||
KeywordData, Operator, Stmt, StmtKind, Suite,
|
||||
@@ -39,13 +38,11 @@ pub const GLOBAL_SCOPE_INDEX: usize = 0;
|
||||
pub struct Checker<'a> {
|
||||
// Input data.
|
||||
path: &'a Path,
|
||||
content: &'a str,
|
||||
autofix: &'a fixer::Mode,
|
||||
pub(crate) settings: &'a Settings,
|
||||
pub(crate) locator: &'a SourceCodeLocator<'a>,
|
||||
// Computed checks.
|
||||
checks: Vec<Check>,
|
||||
// Efficient source-code slicing.
|
||||
locator: OnceCell<SourceCodeLocator<'a>>,
|
||||
// Docstring tracking.
|
||||
docstrings: Vec<(Definition<'a>, Visibility)>,
|
||||
// Edit tracking.
|
||||
@@ -79,14 +76,13 @@ impl<'a> Checker<'a> {
|
||||
settings: &'a Settings,
|
||||
autofix: &'a fixer::Mode,
|
||||
path: &'a Path,
|
||||
content: &'a str,
|
||||
locator: &'a SourceCodeLocator,
|
||||
) -> Checker<'a> {
|
||||
Checker {
|
||||
settings,
|
||||
autofix,
|
||||
path,
|
||||
content,
|
||||
locator: OnceCell::new(),
|
||||
locator,
|
||||
checks: Default::default(),
|
||||
docstrings: Default::default(),
|
||||
deletions: Default::default(),
|
||||
@@ -114,12 +110,6 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get access to a lazily-initialized `SourceCodeLocator` for the file contents.
|
||||
pub fn get_locator(&self) -> &SourceCodeLocator {
|
||||
self.locator
|
||||
.get_or_init(|| SourceCodeLocator::new(self.content))
|
||||
}
|
||||
|
||||
/// Return `true` if a patch should be generated under the given autofix `Mode`.
|
||||
pub fn patch(&self) -> bool {
|
||||
self.autofix.patch()
|
||||
@@ -236,7 +226,11 @@ where
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::N802) {
|
||||
if let Some(check) = pep8_naming::checks::invalid_function_name(stmt, name) {
|
||||
if let Some(check) = pep8_naming::checks::invalid_function_name(
|
||||
stmt,
|
||||
name,
|
||||
&self.settings.pep8_naming,
|
||||
) {
|
||||
self.checks.push(check);
|
||||
}
|
||||
}
|
||||
@@ -247,6 +241,7 @@ where
|
||||
self.current_scope(),
|
||||
decorator_list,
|
||||
args,
|
||||
&self.settings.pep8_naming,
|
||||
)
|
||||
{
|
||||
self.checks.push(check);
|
||||
@@ -258,6 +253,7 @@ where
|
||||
self.current_scope(),
|
||||
decorator_list,
|
||||
args,
|
||||
&self.settings.pep8_naming,
|
||||
) {
|
||||
self.checks.push(check);
|
||||
}
|
||||
@@ -2140,7 +2136,7 @@ impl<'a> Checker<'a> {
|
||||
ImportKind::ImportFrom => pyflakes::fixes::remove_unused_import_froms,
|
||||
};
|
||||
|
||||
match removal_fn(self.get_locator(), &full_names, child, parent, &deleted) {
|
||||
match removal_fn(self.locator, &full_names, child, parent, &deleted) {
|
||||
Ok(fix) => Some(fix),
|
||||
Err(e) => {
|
||||
error!("Failed to fix unused imports: {}", e);
|
||||
@@ -2300,12 +2296,12 @@ impl<'a> Checker<'a> {
|
||||
|
||||
pub fn check_ast(
|
||||
python_ast: &Suite,
|
||||
contents: &str,
|
||||
locator: &SourceCodeLocator,
|
||||
settings: &Settings,
|
||||
autofix: &fixer::Mode,
|
||||
path: &Path,
|
||||
) -> Vec<Check> {
|
||||
let mut checker = Checker::new(settings, autofix, path, contents);
|
||||
let mut checker = Checker::new(settings, autofix, path, locator);
|
||||
checker.push_scope(Scope::new(ScopeKind::Module));
|
||||
checker.bind_builtins();
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{flake8_quotes, pycodestyle, Settings};
|
||||
|
||||
pub fn check_tokens(
|
||||
checks: &mut Vec<Check>,
|
||||
contents: &str,
|
||||
locator: &SourceCodeLocator,
|
||||
tokens: &[LexResult],
|
||||
settings: &Settings,
|
||||
) {
|
||||
@@ -19,16 +19,13 @@ pub fn check_tokens(
|
||||
| settings.enabled.contains(&CheckCode::Q002)
|
||||
| settings.enabled.contains(&CheckCode::Q003);
|
||||
|
||||
// TODO(charlie): Use a shared SourceCodeLocator between this site and the AST traversal.
|
||||
let locator = SourceCodeLocator::new(contents);
|
||||
|
||||
let mut state_machine = StateMachine::new();
|
||||
for (start, tok, end) in tokens.iter().flatten() {
|
||||
// W605
|
||||
if enforce_invalid_escape_sequence {
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
checks.extend(pycodestyle::checks::invalid_escape_sequence(
|
||||
&locator, start, end,
|
||||
locator, start, end,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -38,7 +35,7 @@ pub fn check_tokens(
|
||||
let is_docstring = state_machine.consume(tok);
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
if let Some(check) = flake8_quotes::checks::quotes(
|
||||
&locator,
|
||||
locator,
|
||||
start,
|
||||
end,
|
||||
is_docstring,
|
||||
|
||||
@@ -1150,7 +1150,7 @@ impl CheckKind {
|
||||
format!("Unnecessary subscript reversal of iterable within `{func}()`")
|
||||
}
|
||||
CheckKind::UnnecessaryComprehension(obj_type) => {
|
||||
format!(" Unnecessary `{obj_type}` comprehension (rewrite using `{obj_type}()`)")
|
||||
format!("Unnecessary `{obj_type}` comprehension (rewrite using `{obj_type}()`)")
|
||||
}
|
||||
CheckKind::UnnecessaryMap(obj_type) => {
|
||||
if obj_type == "generator" {
|
||||
|
||||
@@ -27,7 +27,7 @@ pub fn leading_space(line: &str) -> String {
|
||||
/// Extract the leading indentation from a docstring.
|
||||
pub fn indentation<'a>(checker: &'a Checker, docstring: &Expr) -> &'a str {
|
||||
let range = Range::from_located(docstring);
|
||||
checker.get_locator().slice_source_code_range(&Range {
|
||||
checker.locator.slice_source_code_range(&Range {
|
||||
location: Location::new(range.location.row(), 1),
|
||||
end_location: Location::new(range.location.row(), range.location.column()),
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Settings for the `flake_quotes` plugin.
|
||||
//! Settings for the `flake-quotes` plugin.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -27,12 +27,12 @@ pub struct Settings {
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn from_config(config: Options) -> Self {
|
||||
pub fn from_options(options: Options) -> Self {
|
||||
Self {
|
||||
inline_quotes: config.inline_quotes.unwrap_or(Quote::Single),
|
||||
multiline_quotes: config.multiline_quotes.unwrap_or(Quote::Double),
|
||||
docstring_quotes: config.docstring_quotes.unwrap_or(Quote::Double),
|
||||
avoid_escape: config.avoid_escape.unwrap_or(true),
|
||||
inline_quotes: options.inline_quotes.unwrap_or(Quote::Single),
|
||||
multiline_quotes: options.multiline_quotes.unwrap_or(Quote::Double),
|
||||
docstring_quotes: options.docstring_quotes.unwrap_or(Quote::Double),
|
||||
avoid_escape: options.avoid_escape.unwrap_or(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ pub mod settings;
|
||||
pub mod visibility;
|
||||
|
||||
/// Run ruff over Python source code directly.
|
||||
pub fn check(path: &Path, contents: &str, quiet: bool) -> Result<Vec<Message>> {
|
||||
pub fn check(path: &Path, contents: &str) -> Result<Vec<Message>> {
|
||||
// Find the project root and pyproject.toml.
|
||||
let project_root = pyproject::find_project_root(&[path.to_path_buf()]);
|
||||
match &project_root {
|
||||
@@ -60,11 +60,8 @@ pub fn check(path: &Path, contents: &str, quiet: bool) -> Result<Vec<Message>> {
|
||||
None => debug!("Unable to find pyproject.toml; using default settings..."),
|
||||
};
|
||||
|
||||
let settings = Settings::from_configuration(Configuration::from_pyproject(
|
||||
&pyproject,
|
||||
&project_root,
|
||||
quiet,
|
||||
)?);
|
||||
let settings =
|
||||
Settings::from_configuration(Configuration::from_pyproject(&pyproject, &project_root)?);
|
||||
|
||||
// Tokenize once.
|
||||
let tokens: Vec<LexResult> = tokenize(contents);
|
||||
|
||||
@@ -9,6 +9,7 @@ use log::debug;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
use rustpython_parser::{lexer, parser};
|
||||
|
||||
use crate::ast::operations::SourceCodeLocator;
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::fixer;
|
||||
use crate::autofix::fixer::fix_file;
|
||||
@@ -46,13 +47,16 @@ pub(crate) fn check_path(
|
||||
// Aggregate all checks.
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
// Initialize the SourceCodeLocator (which computes offsets lazily).
|
||||
let locator = SourceCodeLocator::new(contents);
|
||||
|
||||
// Run the token-based checks.
|
||||
if settings
|
||||
.enabled
|
||||
.iter()
|
||||
.any(|check_code| matches!(check_code.lint_source(), LintSource::Tokens))
|
||||
{
|
||||
check_tokens(&mut checks, contents, &tokens, settings);
|
||||
check_tokens(&mut checks, &locator, &tokens, settings);
|
||||
}
|
||||
|
||||
// Run the AST-based checks.
|
||||
@@ -63,7 +67,7 @@ pub(crate) fn check_path(
|
||||
{
|
||||
match parser::parse_program_tokens(tokens, "<filename>") {
|
||||
Ok(python_ast) => {
|
||||
checks.extend(check_ast(&python_ast, contents, settings, autofix, path))
|
||||
checks.extend(check_ast(&python_ast, &locator, settings, autofix, path))
|
||||
}
|
||||
Err(parse_error) => {
|
||||
if settings.enabled.contains(&CheckCode::E999) {
|
||||
|
||||
@@ -261,7 +261,7 @@ fn inner_main() -> Result<ExitCode> {
|
||||
.map(|pair| PerFileIgnore::new(pair, &project_root))
|
||||
.collect();
|
||||
|
||||
let mut configuration = Configuration::from_pyproject(&pyproject, &project_root, cli.quiet)?;
|
||||
let mut configuration = Configuration::from_pyproject(&pyproject, &project_root)?;
|
||||
if !exclude.is_empty() {
|
||||
configuration.exclude = exclude;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ use rustpython_ast::{Arguments, Expr, ExprKind, Stmt};
|
||||
|
||||
use crate::ast::types::{FunctionScope, Range, Scope, ScopeKind};
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::pep8_naming::settings::Settings;
|
||||
|
||||
/// N801
|
||||
pub fn invalid_class_name(class_def: &Stmt, name: &str) -> Option<Check> {
|
||||
let stripped = name.strip_prefix('_').unwrap_or(name);
|
||||
if !stripped
|
||||
@@ -21,23 +23,14 @@ pub fn invalid_class_name(class_def: &Stmt, name: &str) -> Option<Check> {
|
||||
None
|
||||
}
|
||||
|
||||
const IGNORE_NAMES: &[&str] = &[
|
||||
"setUp",
|
||||
"tearDown",
|
||||
"setUpClass",
|
||||
"tearDownClass",
|
||||
"setUpModule",
|
||||
"tearDownModule",
|
||||
"asyncSetUp",
|
||||
"asyncTearDown",
|
||||
"setUpTestData",
|
||||
"failureException",
|
||||
"longMessage",
|
||||
"maxDiff",
|
||||
];
|
||||
|
||||
pub fn invalid_function_name(func_def: &Stmt, name: &str) -> Option<Check> {
|
||||
if !is_lower(name) && !IGNORE_NAMES.contains(&name) {
|
||||
/// N802
|
||||
pub fn invalid_function_name(func_def: &Stmt, name: &str, settings: &Settings) -> Option<Check> {
|
||||
if !is_lower(name)
|
||||
&& !settings
|
||||
.ignore_names
|
||||
.iter()
|
||||
.any(|ignore_name| ignore_name == name)
|
||||
{
|
||||
return Some(Check::new(
|
||||
CheckKind::InvalidFunctionName(name.to_string()),
|
||||
Range::from_located(func_def),
|
||||
@@ -46,6 +39,7 @@ pub fn invalid_function_name(func_def: &Stmt, name: &str) -> Option<Check> {
|
||||
None
|
||||
}
|
||||
|
||||
/// N803
|
||||
pub fn invalid_argument_name(location: Range, name: &str) -> Option<Check> {
|
||||
if !is_lower(name) {
|
||||
return Some(Check::new(
|
||||
@@ -56,10 +50,12 @@ pub fn invalid_argument_name(location: Range, name: &str) -> Option<Check> {
|
||||
None
|
||||
}
|
||||
|
||||
/// N804
|
||||
pub fn invalid_first_argument_name_for_class_method(
|
||||
scope: &Scope,
|
||||
decorator_list: &[Expr],
|
||||
args: &Arguments,
|
||||
settings: &Settings,
|
||||
) -> Option<Check> {
|
||||
if !matches!(scope.kind, ScopeKind::Class) {
|
||||
return None;
|
||||
@@ -67,7 +63,7 @@ pub fn invalid_first_argument_name_for_class_method(
|
||||
|
||||
if decorator_list.iter().any(|decorator| {
|
||||
if let ExprKind::Name { id, .. } = &decorator.node {
|
||||
id == "classmethod"
|
||||
settings.classmethod_decorators.contains(id)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -84,10 +80,12 @@ pub fn invalid_first_argument_name_for_class_method(
|
||||
None
|
||||
}
|
||||
|
||||
/// N805
|
||||
pub fn invalid_first_argument_name_for_method(
|
||||
scope: &Scope,
|
||||
decorator_list: &[Expr],
|
||||
args: &Arguments,
|
||||
settings: &Settings,
|
||||
) -> Option<Check> {
|
||||
if !matches!(scope.kind, ScopeKind::Class) {
|
||||
return None;
|
||||
@@ -95,7 +93,8 @@ pub fn invalid_first_argument_name_for_method(
|
||||
|
||||
if decorator_list.iter().any(|decorator| {
|
||||
if let ExprKind::Name { id, .. } = &decorator.node {
|
||||
id == "classmethod" || id == "staticmethod"
|
||||
settings.classmethod_decorators.contains(id)
|
||||
|| settings.staticmethod_decorators.contains(id)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -114,6 +113,7 @@ pub fn invalid_first_argument_name_for_method(
|
||||
None
|
||||
}
|
||||
|
||||
/// N806
|
||||
pub fn non_lowercase_variable_in_function(scope: &Scope, expr: &Expr, name: &str) -> Option<Check> {
|
||||
if !matches!(scope.kind, ScopeKind::Function(FunctionScope { .. })) {
|
||||
return None;
|
||||
@@ -127,6 +127,83 @@ pub fn non_lowercase_variable_in_function(scope: &Scope, expr: &Expr, name: &str
|
||||
None
|
||||
}
|
||||
|
||||
/// N807
|
||||
pub fn dunder_function_name(func_def: &Stmt, scope: &Scope, name: &str) -> Option<Check> {
|
||||
if matches!(scope.kind, ScopeKind::Class) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if name.starts_with("__") && name.ends_with("__") {
|
||||
return Some(Check::new(
|
||||
CheckKind::DunderFunctionName,
|
||||
Range::from_located(func_def),
|
||||
));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// N811
|
||||
pub fn constant_imported_as_non_constant(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: &str,
|
||||
) -> Option<Check> {
|
||||
if is_upper(name) && !is_upper(asname) {
|
||||
return Some(Check::new(
|
||||
CheckKind::ConstantImportedAsNonConstant(name.to_string(), asname.to_string()),
|
||||
Range::from_located(import_from),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// N812
|
||||
pub fn lowercase_imported_as_non_lowercase(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: &str,
|
||||
) -> Option<Check> {
|
||||
if is_lower(name) && asname.to_lowercase() != asname {
|
||||
return Some(Check::new(
|
||||
CheckKind::LowercaseImportedAsNonLowercase(name.to_string(), asname.to_string()),
|
||||
Range::from_located(import_from),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// N813
|
||||
pub fn camelcase_imported_as_lowercase(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: &str,
|
||||
) -> Option<Check> {
|
||||
if is_camelcase(name) && is_lower(asname) {
|
||||
return Some(Check::new(
|
||||
CheckKind::CamelcaseImportedAsLowercase(name.to_string(), asname.to_string()),
|
||||
Range::from_located(import_from),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// N814
|
||||
pub fn camelcase_imported_as_constant(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: &str,
|
||||
) -> Option<Check> {
|
||||
if is_camelcase(name) && is_upper(asname) && !is_acronym(name, asname) {
|
||||
return Some(Check::new(
|
||||
CheckKind::CamelcaseImportedAsConstant(name.to_string(), asname.to_string()),
|
||||
Range::from_located(import_from),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// N815
|
||||
pub fn mixed_case_variable_in_class_scope(scope: &Scope, expr: &Expr, name: &str) -> Option<Check> {
|
||||
if !matches!(scope.kind, ScopeKind::Class) {
|
||||
return None;
|
||||
@@ -140,6 +217,7 @@ pub fn mixed_case_variable_in_class_scope(scope: &Scope, expr: &Expr, name: &str
|
||||
None
|
||||
}
|
||||
|
||||
/// N816
|
||||
pub fn mixed_case_variable_in_global_scope(
|
||||
scope: &Scope,
|
||||
expr: &Expr,
|
||||
@@ -157,18 +235,41 @@ pub fn mixed_case_variable_in_global_scope(
|
||||
None
|
||||
}
|
||||
|
||||
pub fn dunder_function_name(func_def: &Stmt, scope: &Scope, name: &str) -> Option<Check> {
|
||||
if matches!(scope.kind, ScopeKind::Class) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if name.starts_with("__") && name.ends_with("__") {
|
||||
/// N817
|
||||
pub fn camelcase_imported_as_acronym(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: &str,
|
||||
) -> Option<Check> {
|
||||
if is_camelcase(name) && is_upper(asname) && is_acronym(name, asname) {
|
||||
return Some(Check::new(
|
||||
CheckKind::DunderFunctionName,
|
||||
Range::from_located(func_def),
|
||||
CheckKind::CamelcaseImportedAsAcronym(name.to_string(), asname.to_string()),
|
||||
Range::from_located(import_from),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// N818
|
||||
pub fn error_suffix_on_exception_name(
|
||||
class_def: &Stmt,
|
||||
bases: &[Expr],
|
||||
name: &str,
|
||||
) -> Option<Check> {
|
||||
if bases.iter().any(|base| {
|
||||
if let ExprKind::Name { id, .. } = &base.node {
|
||||
id == "Exception"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
if !name.ends_with("Error") {
|
||||
return Some(Check::new(
|
||||
CheckKind::ErrorSuffixOnExceptionName(name.to_string()),
|
||||
Range::from_located(class_def),
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -196,34 +297,6 @@ fn is_upper(s: &str) -> bool {
|
||||
cased
|
||||
}
|
||||
|
||||
pub fn constant_imported_as_non_constant(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: &str,
|
||||
) -> Option<Check> {
|
||||
if is_upper(name) && !is_upper(asname) {
|
||||
return Some(Check::new(
|
||||
CheckKind::ConstantImportedAsNonConstant(name.to_string(), asname.to_string()),
|
||||
Range::from_located(import_from),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn lowercase_imported_as_non_lowercase(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: &str,
|
||||
) -> Option<Check> {
|
||||
if is_lower(name) && asname.to_lowercase() != asname {
|
||||
return Some(Check::new(
|
||||
CheckKind::LowercaseImportedAsNonLowercase(name.to_string(), asname.to_string()),
|
||||
Range::from_located(import_from),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn is_camelcase(name: &str) -> bool {
|
||||
!is_lower(name) && !is_upper(name) && !name.contains('_')
|
||||
}
|
||||
@@ -242,70 +315,6 @@ fn is_acronym(name: &str, asname: &str) -> bool {
|
||||
name.chars().filter(|c| c.is_uppercase()).join("") == asname
|
||||
}
|
||||
|
||||
pub fn camelcase_imported_as_lowercase(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: &str,
|
||||
) -> Option<Check> {
|
||||
if is_camelcase(name) && is_lower(asname) {
|
||||
return Some(Check::new(
|
||||
CheckKind::CamelcaseImportedAsLowercase(name.to_string(), asname.to_string()),
|
||||
Range::from_located(import_from),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn camelcase_imported_as_constant(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: &str,
|
||||
) -> Option<Check> {
|
||||
if is_camelcase(name) && is_upper(asname) && !is_acronym(name, asname) {
|
||||
return Some(Check::new(
|
||||
CheckKind::CamelcaseImportedAsConstant(name.to_string(), asname.to_string()),
|
||||
Range::from_located(import_from),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn camelcase_imported_as_acronym(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: &str,
|
||||
) -> Option<Check> {
|
||||
if is_camelcase(name) && is_upper(asname) && is_acronym(name, asname) {
|
||||
return Some(Check::new(
|
||||
CheckKind::CamelcaseImportedAsAcronym(name.to_string(), asname.to_string()),
|
||||
Range::from_located(import_from),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn error_suffix_on_exception_name(
|
||||
class_def: &Stmt,
|
||||
bases: &[Expr],
|
||||
name: &str,
|
||||
) -> Option<Check> {
|
||||
if bases.iter().any(|base| {
|
||||
if let ExprKind::Name { id, .. } = &base.node {
|
||||
id == "Exception"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
if !name.ends_with("Error") {
|
||||
return Some(Check::new(
|
||||
CheckKind::ErrorSuffixOnExceptionName(name.to_string()),
|
||||
Range::from_located(class_def),
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{is_acronym, is_camelcase, is_lower, is_mixed_case, is_upper};
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod checks;
|
||||
pub mod settings;
|
||||
|
||||
63
src/pep8_naming/settings.rs
Normal file
63
src/pep8_naming/settings.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
//! Settings for the `pep8-naming` plugin.
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
const IGNORE_NAMES: [&str; 12] = [
|
||||
"setUp",
|
||||
"tearDown",
|
||||
"setUpClass",
|
||||
"tearDownClass",
|
||||
"setUpModule",
|
||||
"tearDownModule",
|
||||
"asyncSetUp",
|
||||
"asyncTearDown",
|
||||
"setUpTestData",
|
||||
"failureException",
|
||||
"longMessage",
|
||||
"maxDiff",
|
||||
];
|
||||
|
||||
const CLASSMETHOD_DECORATORS: [&str; 1] = ["classmethod"];
|
||||
|
||||
const STATICMETHOD_DECORATORS: [&str; 1] = ["staticmethod"];
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
pub struct Options {
|
||||
pub ignore_names: Option<Vec<String>>,
|
||||
pub classmethod_decorators: Option<Vec<String>>,
|
||||
pub staticmethod_decorators: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Settings {
|
||||
pub ignore_names: Vec<String>,
|
||||
pub classmethod_decorators: Vec<String>,
|
||||
pub staticmethod_decorators: Vec<String>,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn from_options(options: Options) -> Self {
|
||||
Self {
|
||||
ignore_names: options
|
||||
.ignore_names
|
||||
.unwrap_or_else(|| IGNORE_NAMES.map(String::from).to_vec()),
|
||||
classmethod_decorators: options
|
||||
.classmethod_decorators
|
||||
.unwrap_or_else(|| CLASSMETHOD_DECORATORS.map(String::from).to_vec()),
|
||||
staticmethod_decorators: options
|
||||
.staticmethod_decorators
|
||||
.unwrap_or_else(|| STATICMETHOD_DECORATORS.map(String::from).to_vec()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ignore_names: IGNORE_NAMES.map(String::from).to_vec(),
|
||||
classmethod_decorators: CLASSMETHOD_DECORATORS.map(String::from).to_vec(),
|
||||
staticmethod_decorators: STATICMETHOD_DECORATORS.map(String::from).to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,7 +162,7 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
|
||||
} = &docstring.node
|
||||
{
|
||||
if checker.settings.enabled.contains(&CheckCode::D201) {
|
||||
let (before, _, _) = checker.get_locator().partition_source_code_at(
|
||||
let (before, _, _) = checker.locator.partition_source_code_at(
|
||||
&Range::from_located(parent),
|
||||
&Range::from_located(docstring),
|
||||
);
|
||||
@@ -190,7 +190,7 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
|
||||
}
|
||||
|
||||
if checker.settings.enabled.contains(&CheckCode::D202) {
|
||||
let (_, _, after) = checker.get_locator().partition_source_code_at(
|
||||
let (_, _, after) = checker.locator.partition_source_code_at(
|
||||
&Range::from_located(parent),
|
||||
&Range::from_located(docstring),
|
||||
);
|
||||
@@ -253,7 +253,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
|
||||
if checker.settings.enabled.contains(&CheckCode::D203)
|
||||
|| checker.settings.enabled.contains(&CheckCode::D211)
|
||||
{
|
||||
let (before, _, _) = checker.get_locator().partition_source_code_at(
|
||||
let (before, _, _) = checker.locator.partition_source_code_at(
|
||||
&Range::from_located(parent),
|
||||
&Range::from_located(docstring),
|
||||
);
|
||||
@@ -300,7 +300,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
|
||||
}
|
||||
|
||||
if checker.settings.enabled.contains(&CheckCode::D204) {
|
||||
let (_, _, after) = checker.get_locator().partition_source_code_at(
|
||||
let (_, _, after) = checker.locator.partition_source_code_at(
|
||||
&Range::from_located(parent),
|
||||
&Range::from_located(docstring),
|
||||
);
|
||||
@@ -530,7 +530,7 @@ pub fn newline_after_last_paragraph(checker: &mut Checker, definition: &Definiti
|
||||
}
|
||||
if line_count > 1 {
|
||||
let content = checker
|
||||
.get_locator()
|
||||
.locator
|
||||
.slice_source_code_range(&Range::from_located(docstring));
|
||||
if let Some(last_line) = content.lines().last().map(|line| line.trim()) {
|
||||
if last_line != "\"\"\"" && last_line != "'''" {
|
||||
@@ -583,7 +583,7 @@ pub fn no_surrounding_whitespace(checker: &mut Checker, definition: &Definition)
|
||||
);
|
||||
if checker.patch() {
|
||||
if let Some(first_line) = checker
|
||||
.get_locator()
|
||||
.locator
|
||||
.slice_source_code_range(&Range::from_located(docstring))
|
||||
.lines()
|
||||
.next()
|
||||
@@ -629,7 +629,7 @@ pub fn multi_line_summary_start(checker: &mut Checker, definition: &Definition)
|
||||
{
|
||||
if string.lines().nth(1).is_some() {
|
||||
if let Some(first_line) = checker
|
||||
.get_locator()
|
||||
.locator
|
||||
.slice_source_code_range(&Range::from_located(docstring))
|
||||
.lines()
|
||||
.next()
|
||||
@@ -665,7 +665,7 @@ pub fn triple_quotes(checker: &mut Checker, definition: &Definition) {
|
||||
} = &docstring.node
|
||||
{
|
||||
if let Some(first_line) = checker
|
||||
.get_locator()
|
||||
.locator
|
||||
.slice_source_code_range(&Range::from_located(docstring))
|
||||
.lines()
|
||||
.next()
|
||||
|
||||
@@ -17,9 +17,7 @@ pub fn super_call_with_parameters(checker: &mut Checker, expr: &Expr, func: &Exp
|
||||
.collect();
|
||||
if let Some(mut check) = checks::super_args(scope, &parents, expr, func, args) {
|
||||
if checker.patch() {
|
||||
if let Some(fix) =
|
||||
pyupgrade::fixes::remove_super_arguments(checker.get_locator(), expr)
|
||||
{
|
||||
if let Some(fix) = pyupgrade::fixes::remove_super_arguments(checker.locator, expr) {
|
||||
check.amend(fix);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ pub fn useless_object_inheritance(
|
||||
if let Some(mut check) = checks::useless_object_inheritance(name, bases, scope) {
|
||||
if checker.patch() {
|
||||
if let Some(fix) = pyupgrade::fixes::remove_class_def_base(
|
||||
checker.get_locator(),
|
||||
checker.locator,
|
||||
&stmt.location,
|
||||
check.location,
|
||||
bases,
|
||||
|
||||
@@ -8,9 +8,9 @@ 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};
|
||||
use crate::{flake8_quotes, pep8_naming};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Configuration {
|
||||
@@ -26,6 +26,7 @@ pub struct Configuration {
|
||||
pub target_version: PythonVersion,
|
||||
// Plugins
|
||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||
pub pep8_naming: pep8_naming::settings::Settings,
|
||||
}
|
||||
|
||||
static DEFAULT_EXCLUDE: Lazy<Vec<FilePattern>> = Lazy::new(|| {
|
||||
@@ -59,9 +60,8 @@ impl Configuration {
|
||||
pub fn from_pyproject(
|
||||
pyproject: &Option<PathBuf>,
|
||||
project_root: &Option<PathBuf>,
|
||||
quiet: bool,
|
||||
) -> Result<Self> {
|
||||
let options = load_options(pyproject, quiet)?;
|
||||
let options = load_options(pyproject)?;
|
||||
Ok(Configuration {
|
||||
dummy_variable_rgx: match options.dummy_variable_rgx {
|
||||
Some(pattern) => Regex::new(&pattern)
|
||||
@@ -98,7 +98,11 @@ impl Configuration {
|
||||
// Plugins
|
||||
flake8_quotes: options
|
||||
.flake8_quotes
|
||||
.map(flake8_quotes::settings::Settings::from_config)
|
||||
.map(flake8_quotes::settings::Settings::from_options)
|
||||
.unwrap_or_default(),
|
||||
pep8_naming: options
|
||||
.pep8_naming
|
||||
.map(pep8_naming::settings::Settings::from_options)
|
||||
.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ 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};
|
||||
use crate::{flake8_quotes, pep8_naming};
|
||||
|
||||
pub mod configuration;
|
||||
pub mod options;
|
||||
@@ -29,6 +29,7 @@ pub struct Settings {
|
||||
pub target_version: PythonVersion,
|
||||
// Plugins
|
||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||
pub pep8_naming: pep8_naming::settings::Settings,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
@@ -45,6 +46,7 @@ impl Settings {
|
||||
extend_exclude: config.extend_exclude,
|
||||
flake8_quotes: config.flake8_quotes,
|
||||
line_length: config.line_length,
|
||||
pep8_naming: config.pep8_naming,
|
||||
per_file_ignores: config.per_file_ignores,
|
||||
target_version: config.target_version,
|
||||
}
|
||||
@@ -60,6 +62,7 @@ impl Settings {
|
||||
per_file_ignores: vec![],
|
||||
target_version: PythonVersion::Py310,
|
||||
flake8_quotes: Default::default(),
|
||||
pep8_naming: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +76,7 @@ impl Settings {
|
||||
per_file_ignores: vec![],
|
||||
target_version: PythonVersion::Py310,
|
||||
flake8_quotes: Default::default(),
|
||||
pep8_naming: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::flake8_quotes;
|
||||
use crate::settings::types::{PythonVersion, StrCheckCodePair};
|
||||
use crate::{flake8_quotes, pep8_naming};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
@@ -24,5 +24,7 @@ pub struct Options {
|
||||
pub per_file_ignores: Vec<StrCheckCodePair>,
|
||||
pub dummy_variable_rgx: Option<String>,
|
||||
pub target_version: Option<PythonVersion>,
|
||||
// Plugins
|
||||
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
|
||||
pub pep8_naming: Option<pep8_naming::settings::Options>,
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use common_path::common_path_all;
|
||||
use log::debug;
|
||||
use path_absolutize::Absolutize;
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -69,17 +70,15 @@ pub fn find_project_root(sources: &[PathBuf]) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn load_options(pyproject: &Option<PathBuf>, quiet: bool) -> Result<Options> {
|
||||
pub fn load_options(pyproject: &Option<PathBuf>) -> 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...");
|
||||
}
|
||||
debug!("No pyproject.toml found.");
|
||||
debug!("Falling back to default configuration...");
|
||||
Ok(Default::default())
|
||||
}
|
||||
}
|
||||
@@ -94,12 +93,12 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::flake8_quotes;
|
||||
use crate::flake8_quotes::settings::Quote;
|
||||
use crate::settings::pyproject::{
|
||||
find_project_root, find_pyproject_toml, parse_pyproject_toml, Options, Pyproject, Tools,
|
||||
};
|
||||
use crate::settings::types::StrCheckCodePair;
|
||||
use crate::{flake8_quotes, pep8_naming};
|
||||
|
||||
#[test]
|
||||
fn deserialize() -> Result<()> {
|
||||
@@ -133,7 +132,8 @@ mod tests {
|
||||
per_file_ignores: vec![],
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_quotes: None
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
})
|
||||
);
|
||||
@@ -159,7 +159,8 @@ line-length = 79
|
||||
per_file_ignores: vec![],
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_quotes: None
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
})
|
||||
);
|
||||
@@ -185,7 +186,8 @@ exclude = ["foo.py"]
|
||||
per_file_ignores: vec![],
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_quotes: None
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
})
|
||||
);
|
||||
@@ -211,7 +213,8 @@ select = ["E501"]
|
||||
per_file_ignores: vec![],
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_quotes: None
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
})
|
||||
);
|
||||
@@ -238,7 +241,8 @@ ignore = ["E501"]
|
||||
per_file_ignores: vec![],
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_quotes: None
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
})
|
||||
);
|
||||
@@ -316,7 +320,25 @@ other-attribute = 1
|
||||
multiline_quotes: Some(Quote::Double),
|
||||
docstring_quotes: Some(Quote::Double),
|
||||
avoid_escape: Some(true),
|
||||
})
|
||||
}),
|
||||
pep8_naming: Some(pep8_naming::settings::Options {
|
||||
ignore_names: Some(vec![
|
||||
"setUp".to_string(),
|
||||
"tearDown".to_string(),
|
||||
"setUpClass".to_string(),
|
||||
"tearDownClass".to_string(),
|
||||
"setUpModule".to_string(),
|
||||
"tearDownModule".to_string(),
|
||||
"asyncSetUp".to_string(),
|
||||
"asyncTearDown".to_string(),
|
||||
"setUpTestData".to_string(),
|
||||
"failureException".to_string(),
|
||||
"longMessage".to_string(),
|
||||
"maxDiff".to_string(),
|
||||
]),
|
||||
classmethod_decorators: Some(vec!["classmethod".to_string()]),
|
||||
staticmethod_decorators: Some(vec!["staticmethod".to_string()]),
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use regex::Regex;
|
||||
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
||||
use crate::{flake8_quotes, Configuration};
|
||||
use crate::{flake8_quotes, pep8_naming, Configuration};
|
||||
|
||||
/// Struct to render user-facing exclusion patterns.
|
||||
#[derive(Debug)]
|
||||
@@ -46,6 +46,7 @@ pub struct UserConfiguration {
|
||||
pub target_version: PythonVersion,
|
||||
// Plugins
|
||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||
pub pep8_naming: pep8_naming::settings::Settings,
|
||||
// Non-settings exposed to the user
|
||||
pub project_root: Option<PathBuf>,
|
||||
pub pyproject: Option<PathBuf>,
|
||||
@@ -53,30 +54,31 @@ pub struct UserConfiguration {
|
||||
|
||||
impl UserConfiguration {
|
||||
pub fn from_configuration(
|
||||
settings: Configuration,
|
||||
configuration: Configuration,
|
||||
project_root: Option<PathBuf>,
|
||||
pyproject: Option<PathBuf>,
|
||||
) -> Self {
|
||||
Self {
|
||||
dummy_variable_rgx: settings.dummy_variable_rgx,
|
||||
exclude: settings
|
||||
dummy_variable_rgx: configuration.dummy_variable_rgx,
|
||||
exclude: configuration
|
||||
.exclude
|
||||
.into_iter()
|
||||
.map(Exclusion::from_file_pattern)
|
||||
.collect(),
|
||||
extend_exclude: settings
|
||||
extend_exclude: configuration
|
||||
.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,
|
||||
extend_ignore: configuration.extend_ignore,
|
||||
extend_select: configuration.extend_select,
|
||||
ignore: configuration.ignore,
|
||||
line_length: configuration.line_length,
|
||||
per_file_ignores: configuration.per_file_ignores,
|
||||
select: configuration.select,
|
||||
target_version: configuration.target_version,
|
||||
flake8_quotes: configuration.flake8_quotes,
|
||||
pep8_naming: configuration.pep8_naming,
|
||||
project_root,
|
||||
pyproject,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user