mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-19 01:51:30 +00:00
Implement copyright notice detection (#4701)
## Summary Add copyright notice detection to enforce the presence of copyright headers in Python files. Configurable settings include: the relevant regular expression, the author name, and the minimum file size, similar to [flake8-copyright](https://github.com/savoirfairelinux/flake8-copyright). Closes https://github.com/charliermarsh/ruff/issues/3579 --------- Signed-off-by: ryan <ryang@waabi.ai> Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
This commit is contained in:
parent
9f7cc86a22
commit
ab3c02342b
26 changed files with 465 additions and 24 deletions
|
@ -261,6 +261,7 @@ quality tools, including:
|
|||
- [flake8-builtins](https://pypi.org/project/flake8-builtins/)
|
||||
- [flake8-commas](https://pypi.org/project/flake8-commas/)
|
||||
- [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [flake8-copyright](https://pypi.org/project/flake8-copyright/)
|
||||
- [flake8-datetimez](https://pypi.org/project/flake8-datetimez/)
|
||||
- [flake8-debugger](https://pypi.org/project/flake8-debugger/)
|
||||
- [flake8-django](https://pypi.org/project/flake8-django/)
|
||||
|
|
|
@ -8,6 +8,7 @@ use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
|
|||
use ruff_python_whitespace::UniversalNewlines;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::copyright::rules::missing_copyright_notice;
|
||||
use crate::rules::flake8_executable::helpers::{extract_shebang, ShebangDirective};
|
||||
use crate::rules::flake8_executable::rules::{
|
||||
shebang_missing, shebang_newline, shebang_not_executable, shebang_python, shebang_whitespace,
|
||||
|
@ -49,6 +50,7 @@ pub(crate) fn check_physical_lines(
|
|||
let enforce_blank_line_contains_whitespace =
|
||||
settings.rules.enabled(Rule::BlankLineWithWhitespace);
|
||||
let enforce_tab_indentation = settings.rules.enabled(Rule::TabIndentation);
|
||||
let enforce_copyright_notice = settings.rules.enabled(Rule::MissingCopyrightNotice);
|
||||
|
||||
let fix_unnecessary_coding_comment = settings.rules.should_fix(Rule::UTF8EncodingDeclaration);
|
||||
let fix_shebang_whitespace = settings.rules.should_fix(Rule::ShebangLeadingWhitespace);
|
||||
|
@ -172,6 +174,12 @@ pub(crate) fn check_physical_lines(
|
|||
}
|
||||
}
|
||||
|
||||
if enforce_copyright_notice {
|
||||
if let Some(diagnostic) = missing_copyright_notice(locator, settings) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics
|
||||
}
|
||||
|
||||
|
|
|
@ -374,6 +374,9 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
|||
(Flake8Simplify, "401") => (RuleGroup::Unspecified, rules::flake8_simplify::rules::IfElseBlockInsteadOfDictGet),
|
||||
(Flake8Simplify, "910") => (RuleGroup::Unspecified, rules::flake8_simplify::rules::DictGetWithNoneDefault),
|
||||
|
||||
// copyright
|
||||
(Copyright, "001") => (RuleGroup::Unspecified, rules::copyright::rules::MissingCopyrightNotice),
|
||||
|
||||
// pyupgrade
|
||||
(Pyupgrade, "001") => (RuleGroup::Unspecified, rules::pyupgrade::rules::UselessMetaclassType),
|
||||
(Pyupgrade, "003") => (RuleGroup::Unspecified, rules::pyupgrade::rules::TypeOfPrimitive),
|
||||
|
|
|
@ -80,6 +80,9 @@ pub enum Linter {
|
|||
/// [flake8-commas](https://pypi.org/project/flake8-commas/)
|
||||
#[prefix = "COM"]
|
||||
Flake8Commas,
|
||||
/// Copyright-related rules
|
||||
#[prefix = "CPY"]
|
||||
Copyright,
|
||||
/// [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/)
|
||||
#[prefix = "C4"]
|
||||
Flake8Comprehensions,
|
||||
|
@ -267,6 +270,7 @@ impl Rule {
|
|||
| Rule::ShebangLeadingWhitespace
|
||||
| Rule::TrailingWhitespace
|
||||
| Rule::TabIndentation
|
||||
| Rule::MissingCopyrightNotice
|
||||
| Rule::BlankLineWithWhitespace => LintSource::PhysicalLines,
|
||||
Rule::AmbiguousUnicodeCharacterComment
|
||||
| Rule::AmbiguousUnicodeCharacterDocstring
|
||||
|
|
158
crates/ruff/src/rules/copyright/mod.rs
Normal file
158
crates/ruff/src/rules/copyright/mod.rs
Normal file
|
@ -0,0 +1,158 @@
|
|||
//! Rules related to copyright notices.
|
||||
pub(crate) mod rules;
|
||||
|
||||
pub mod settings;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::registry::Rule;
|
||||
use crate::test::test_snippet;
|
||||
use crate::{assert_messages, settings};
|
||||
|
||||
#[test]
|
||||
fn notice() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
# Copyright 2023
|
||||
|
||||
import os
|
||||
"#
|
||||
.trim(),
|
||||
&settings::Settings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
assert_messages!(diagnostics);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notice_with_c() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
# Copyright (C) 2023
|
||||
|
||||
import os
|
||||
"#
|
||||
.trim(),
|
||||
&settings::Settings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
assert_messages!(diagnostics);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notice_with_caps() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
# COPYRIGHT (C) 2023
|
||||
|
||||
import os
|
||||
"#
|
||||
.trim(),
|
||||
&settings::Settings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
assert_messages!(diagnostics);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notice_with_range() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
# Copyright (C) 2021-2023
|
||||
|
||||
import os
|
||||
"#
|
||||
.trim(),
|
||||
&settings::Settings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
assert_messages!(diagnostics);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_author() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
# Copyright (C) 2023 Ruff
|
||||
|
||||
import os
|
||||
"#
|
||||
.trim(),
|
||||
&settings::Settings {
|
||||
copyright: super::settings::Settings {
|
||||
author: Some("Ruff".to_string()),
|
||||
..super::settings::Settings::default()
|
||||
},
|
||||
..settings::Settings::for_rules(vec![Rule::MissingCopyrightNotice])
|
||||
},
|
||||
);
|
||||
assert_messages!(diagnostics);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_author() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
# Copyright (C) 2023 Some Author
|
||||
|
||||
import os
|
||||
"#
|
||||
.trim(),
|
||||
&settings::Settings {
|
||||
copyright: super::settings::Settings {
|
||||
author: Some("Ruff".to_string()),
|
||||
..super::settings::Settings::default()
|
||||
},
|
||||
..settings::Settings::for_rules(vec![Rule::MissingCopyrightNotice])
|
||||
},
|
||||
);
|
||||
assert_messages!(diagnostics);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn small_file() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
import os
|
||||
"#
|
||||
.trim(),
|
||||
&settings::Settings {
|
||||
copyright: super::settings::Settings {
|
||||
min_file_size: 256,
|
||||
..super::settings::Settings::default()
|
||||
},
|
||||
..settings::Settings::for_rules(vec![Rule::MissingCopyrightNotice])
|
||||
},
|
||||
);
|
||||
assert_messages!(diagnostics);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn late_notice() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
|
||||
# Copyright 2023
|
||||
"#
|
||||
.trim(),
|
||||
&settings::Settings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
assert_messages!(diagnostics);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
|
||||
use crate::settings::Settings;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the absence of copyright notices within Python files.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// In some codebases, it's common to have a license header at the top of every
|
||||
/// file. This rule ensures that the license header is present.
|
||||
#[violation]
|
||||
pub struct MissingCopyrightNotice;
|
||||
|
||||
impl Violation for MissingCopyrightNotice {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Missing copyright notice at top of file")
|
||||
}
|
||||
}
|
||||
|
||||
/// CPY001
|
||||
pub(crate) fn missing_copyright_notice(
|
||||
locator: &Locator,
|
||||
settings: &Settings,
|
||||
) -> Option<Diagnostic> {
|
||||
// Ignore files that are too small to contain a copyright notice.
|
||||
if locator.len() < settings.copyright.min_file_size {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Only search the first 1024 bytes in the file.
|
||||
let contents = if locator.len() < 1024 {
|
||||
locator.contents()
|
||||
} else {
|
||||
locator.up_to(TextSize::from(1024))
|
||||
};
|
||||
|
||||
// Locate the copyright notice.
|
||||
if let Some(match_) = settings.copyright.notice_rgx.find(contents) {
|
||||
match settings.copyright.author {
|
||||
Some(ref author) => {
|
||||
// Ensure that it's immediately followed by the author.
|
||||
if contents[match_.end()..].trim_start().starts_with(author) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
|
||||
Some(Diagnostic::new(
|
||||
MissingCopyrightNotice,
|
||||
TextRange::default(),
|
||||
))
|
||||
}
|
3
crates/ruff/src/rules/copyright/rules/mod.rs
Normal file
3
crates/ruff/src/rules/copyright/rules/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub(crate) use missing_copyright_notice::{missing_copyright_notice, MissingCopyrightNotice};
|
||||
|
||||
mod missing_copyright_notice;
|
94
crates/ruff/src/rules/copyright/settings.rs
Normal file
94
crates/ruff/src/rules/copyright/settings.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
//! Settings for the `copyright` plugin.
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions};
|
||||
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions,
|
||||
)]
|
||||
#[serde(
|
||||
deny_unknown_fields,
|
||||
rename_all = "kebab-case",
|
||||
rename = "CopyrightOptions"
|
||||
)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct Options {
|
||||
#[option(
|
||||
default = r#"(?i)Copyright\s+(\(C\)\s+)?\d{4}([-,]\d{4})*"#,
|
||||
value_type = "str",
|
||||
example = r#"notice-rgx = "(?i)Copyright \\(C\\) \\d{4}""#
|
||||
)]
|
||||
/// The regular expression used to match the copyright notice, compiled
|
||||
/// with the [`regex`](https://docs.rs/regex/latest/regex/) crate.
|
||||
///
|
||||
/// Defaults to `(?i)Copyright\s+(\(C\)\s+)?\d{4}(-\d{4})*`, which matches
|
||||
/// the following:
|
||||
/// - `Copyright 2023`
|
||||
/// - `Copyright (C) 2023`
|
||||
/// - `Copyright 2021-2023`
|
||||
/// - `Copyright (C) 2021-2023`
|
||||
pub notice_rgx: Option<String>,
|
||||
#[option(default = "None", value_type = "str", example = r#"author = "Ruff""#)]
|
||||
/// Author to enforce within the copyright notice. If provided, the
|
||||
/// author must be present immediately following the copyright notice.
|
||||
pub author: Option<String>,
|
||||
#[option(
|
||||
default = r#"0"#,
|
||||
value_type = "int",
|
||||
example = r#"
|
||||
# Avoid enforcing a header on files smaller than 1024 bytes.
|
||||
min-file-size = 1024
|
||||
"#
|
||||
)]
|
||||
/// A minimum file size (in bytes) required for a copyright notice to
|
||||
/// be enforced. By default, all files are validated.
|
||||
pub min_file_size: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub notice_rgx: Regex,
|
||||
pub author: Option<String>,
|
||||
pub min_file_size: usize,
|
||||
}
|
||||
|
||||
static COPYRIGHT: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"(?i)Copyright\s+(\(C\)\s+)?\d{4}(-\d{4})*").unwrap());
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
notice_rgx: COPYRIGHT.clone(),
|
||||
author: None,
|
||||
min_file_size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Options> for Settings {
|
||||
fn from(options: Options) -> Self {
|
||||
Self {
|
||||
notice_rgx: options
|
||||
.notice_rgx
|
||||
.map(|pattern| Regex::new(&pattern))
|
||||
.transpose()
|
||||
.expect("Invalid `notice-rgx`")
|
||||
.unwrap_or_else(|| COPYRIGHT.clone()),
|
||||
author: options.author,
|
||||
min_file_size: options.min_file_size.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Settings> for Options {
|
||||
fn from(settings: Settings) -> Self {
|
||||
Self {
|
||||
notice_rgx: Some(settings.notice_rgx.to_string()),
|
||||
author: settings.author,
|
||||
min_file_size: Some(settings.min_file_size),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/copyright/mod.rs
|
||||
---
|
||||
<filename>:1:1: CPY001 Missing copyright notice at top of file
|
||||
|
|
||||
1 | # Copyright (C) 2023 Some Author
|
||||
| CPY001
|
||||
2 |
|
||||
3 | import os
|
||||
|
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/copyright/mod.rs
|
||||
---
|
||||
<filename>:1:1: CPY001 Missing copyright notice at top of file
|
||||
|
|
||||
1 | # Content Content Content Content Content Content Content Content Content Content
|
||||
| CPY001
|
||||
2 | # Content Content Content Content Content Content Content Content Content Content
|
||||
3 | # Content Content Content Content Content Content Content Content Content Content
|
||||
|
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/copyright/mod.rs
|
||||
---
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/copyright/mod.rs
|
||||
---
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/copyright/mod.rs
|
||||
---
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/copyright/mod.rs
|
||||
---
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/copyright/mod.rs
|
||||
---
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/copyright/mod.rs
|
||||
---
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#![allow(clippy::useless_format)]
|
||||
pub mod airflow;
|
||||
pub mod copyright;
|
||||
pub mod eradicate;
|
||||
pub mod flake8_2020;
|
||||
pub mod flake8_annotations;
|
||||
|
|
|
@ -16,10 +16,11 @@ use crate::fs;
|
|||
use crate::line_width::{LineLength, TabSize};
|
||||
use crate::rule_selector::RuleSelector;
|
||||
use crate::rules::{
|
||||
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions,
|
||||
flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, flake8_import_conventions,
|
||||
flake8_pytest_style, flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking,
|
||||
flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pylint,
|
||||
copyright, flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins,
|
||||
flake8_comprehensions, flake8_errmsg, flake8_gettext, flake8_implicit_str_concat,
|
||||
flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_self,
|
||||
flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming,
|
||||
pycodestyle, pydocstyle, pyflakes, pylint,
|
||||
};
|
||||
use crate::settings::options::Options;
|
||||
use crate::settings::types::{
|
||||
|
@ -74,6 +75,7 @@ pub struct Configuration {
|
|||
pub flake8_bugbear: Option<flake8_bugbear::settings::Options>,
|
||||
pub flake8_builtins: Option<flake8_builtins::settings::Options>,
|
||||
pub flake8_comprehensions: Option<flake8_comprehensions::settings::Options>,
|
||||
pub copyright: Option<copyright::settings::Options>,
|
||||
pub flake8_errmsg: Option<flake8_errmsg::settings::Options>,
|
||||
pub flake8_implicit_str_concat: Option<flake8_implicit_str_concat::settings::Options>,
|
||||
pub flake8_import_conventions: Option<flake8_import_conventions::settings::Options>,
|
||||
|
@ -227,6 +229,7 @@ impl Configuration {
|
|||
flake8_bugbear: options.flake8_bugbear,
|
||||
flake8_builtins: options.flake8_builtins,
|
||||
flake8_comprehensions: options.flake8_comprehensions,
|
||||
copyright: options.copyright,
|
||||
flake8_errmsg: options.flake8_errmsg,
|
||||
flake8_gettext: options.flake8_gettext,
|
||||
flake8_implicit_str_concat: options.flake8_implicit_str_concat,
|
||||
|
@ -305,6 +308,7 @@ impl Configuration {
|
|||
flake8_comprehensions: self
|
||||
.flake8_comprehensions
|
||||
.combine(config.flake8_comprehensions),
|
||||
copyright: self.copyright.combine(config.copyright),
|
||||
flake8_errmsg: self.flake8_errmsg.combine(config.flake8_errmsg),
|
||||
flake8_gettext: self.flake8_gettext.combine(config.flake8_gettext),
|
||||
flake8_implicit_str_concat: self
|
||||
|
|
|
@ -10,10 +10,11 @@ use crate::line_width::{LineLength, TabSize};
|
|||
use crate::registry::Linter;
|
||||
use crate::rule_selector::{prefix_to_selector, RuleSelector};
|
||||
use crate::rules::{
|
||||
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions,
|
||||
flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, flake8_import_conventions,
|
||||
flake8_pytest_style, flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking,
|
||||
flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pylint,
|
||||
copyright, flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins,
|
||||
flake8_comprehensions, flake8_errmsg, flake8_gettext, flake8_implicit_str_concat,
|
||||
flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_self,
|
||||
flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming,
|
||||
pycodestyle, pydocstyle, pyflakes, pylint,
|
||||
};
|
||||
use crate::settings::types::FilePatternSet;
|
||||
|
||||
|
@ -95,6 +96,7 @@ impl Default for Settings {
|
|||
flake8_bugbear: flake8_bugbear::settings::Settings::default(),
|
||||
flake8_builtins: flake8_builtins::settings::Settings::default(),
|
||||
flake8_comprehensions: flake8_comprehensions::settings::Settings::default(),
|
||||
copyright: copyright::settings::Settings::default(),
|
||||
flake8_errmsg: flake8_errmsg::settings::Settings::default(),
|
||||
flake8_implicit_str_concat: flake8_implicit_str_concat::settings::Settings::default(),
|
||||
flake8_import_conventions: flake8_import_conventions::settings::Settings::default(),
|
||||
|
|
|
@ -16,10 +16,11 @@ use ruff_macros::CacheKey;
|
|||
use crate::registry::{Rule, RuleNamespace, RuleSet, INCOMPATIBLE_CODES};
|
||||
use crate::rule_selector::{RuleSelector, Specificity};
|
||||
use crate::rules::{
|
||||
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions,
|
||||
flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, flake8_import_conventions,
|
||||
flake8_pytest_style, flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking,
|
||||
flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pylint,
|
||||
copyright, flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins,
|
||||
flake8_comprehensions, flake8_errmsg, flake8_gettext, flake8_implicit_str_concat,
|
||||
flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_self,
|
||||
flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming,
|
||||
pycodestyle, pydocstyle, pyflakes, pylint,
|
||||
};
|
||||
use crate::settings::configuration::Configuration;
|
||||
use crate::settings::types::{FilePatternSet, PerFileIgnore, PythonVersion, SerializationFormat};
|
||||
|
@ -111,6 +112,7 @@ pub struct Settings {
|
|||
pub flake8_bugbear: flake8_bugbear::settings::Settings,
|
||||
pub flake8_builtins: flake8_builtins::settings::Settings,
|
||||
pub flake8_comprehensions: flake8_comprehensions::settings::Settings,
|
||||
pub copyright: copyright::settings::Settings,
|
||||
pub flake8_errmsg: flake8_errmsg::settings::Settings,
|
||||
pub flake8_implicit_str_concat: flake8_implicit_str_concat::settings::Settings,
|
||||
pub flake8_import_conventions: flake8_import_conventions::settings::Settings,
|
||||
|
@ -199,6 +201,7 @@ impl Settings {
|
|||
.flake8_comprehensions
|
||||
.map(Into::into)
|
||||
.unwrap_or_default(),
|
||||
copyright: config.copyright.map(Into::into).unwrap_or_default(),
|
||||
flake8_errmsg: config.flake8_errmsg.map(Into::into).unwrap_or_default(),
|
||||
flake8_implicit_str_concat: config
|
||||
.flake8_implicit_str_concat
|
||||
|
|
|
@ -8,10 +8,11 @@ use ruff_macros::ConfigurationOptions;
|
|||
use crate::line_width::{LineLength, TabSize};
|
||||
use crate::rule_selector::RuleSelector;
|
||||
use crate::rules::{
|
||||
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions,
|
||||
flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, flake8_import_conventions,
|
||||
flake8_pytest_style, flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking,
|
||||
flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pylint,
|
||||
copyright, flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins,
|
||||
flake8_comprehensions, flake8_errmsg, flake8_gettext, flake8_implicit_str_concat,
|
||||
flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_self,
|
||||
flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming,
|
||||
pycodestyle, pydocstyle, pyflakes, pylint,
|
||||
};
|
||||
use crate::settings::types::{PythonVersion, SerializationFormat, Version};
|
||||
|
||||
|
@ -495,6 +496,9 @@ pub struct Options {
|
|||
/// Options for the `flake8-comprehensions` plugin.
|
||||
pub flake8_comprehensions: Option<flake8_comprehensions::settings::Options>,
|
||||
#[option_group]
|
||||
/// Options for the `copyright` plugin.
|
||||
pub copyright: Option<copyright::settings::Options>,
|
||||
#[option_group]
|
||||
/// Options for the `flake8-errmsg` plugin.
|
||||
pub flake8_errmsg: Option<flake8_errmsg::settings::Options>,
|
||||
#[option_group]
|
||||
|
|
|
@ -15,9 +15,9 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenS
|
|||
let mut parsed = Vec::new();
|
||||
|
||||
let mut common_prefix_match_arms = quote!();
|
||||
let mut name_match_arms =
|
||||
quote!(Self::Ruff => "Ruff-specific rules", Self::Numpy => "NumPy-specific rules", );
|
||||
let mut url_match_arms = quote!(Self::Ruff => None, Self::Numpy => None, );
|
||||
let mut name_match_arms = quote!(Self::Ruff => "Ruff-specific rules", Self::Numpy => "NumPy-specific rules", Self::Copyright => "Copyright-related rules", );
|
||||
let mut url_match_arms =
|
||||
quote!(Self::Ruff => None, Self::Numpy => None, Self::Copyright => None, );
|
||||
|
||||
let mut all_prefixes = HashSet::new();
|
||||
|
||||
|
@ -59,7 +59,7 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenS
|
|||
|
||||
let variant_ident = variant.ident;
|
||||
|
||||
if variant_ident != "Ruff" && variant_ident != "Numpy" {
|
||||
if variant_ident != "Ruff" && variant_ident != "Numpy" && variant_ident != "Copyright" {
|
||||
let (name, url) = parse_doc_attr(doc_attr)?;
|
||||
name_match_arms.extend(quote! {Self::#variant_ident => #name,});
|
||||
url_match_arms.extend(quote! {Self::#variant_ident => Some(#url),});
|
||||
|
|
|
@ -9,10 +9,11 @@ use ruff::line_width::{LineLength, TabSize};
|
|||
use ruff::linter::{check_path, LinterResult};
|
||||
use ruff::registry::AsRule;
|
||||
use ruff::rules::{
|
||||
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions,
|
||||
flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, flake8_import_conventions,
|
||||
flake8_pytest_style, flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking,
|
||||
flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pylint,
|
||||
copyright, flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins,
|
||||
flake8_comprehensions, flake8_errmsg, flake8_gettext, flake8_implicit_str_concat,
|
||||
flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_self,
|
||||
flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming,
|
||||
pycodestyle, pydocstyle, pyflakes, pylint,
|
||||
};
|
||||
use ruff::settings::configuration::Configuration;
|
||||
use ruff::settings::options::Options;
|
||||
|
@ -137,6 +138,7 @@ pub fn defaultSettings() -> Result<JsValue, JsValue> {
|
|||
flake8_bugbear: Some(flake8_bugbear::settings::Settings::default().into()),
|
||||
flake8_builtins: Some(flake8_builtins::settings::Settings::default().into()),
|
||||
flake8_comprehensions: Some(flake8_comprehensions::settings::Settings::default().into()),
|
||||
copyright: Some(copyright::settings::Settings::default().into()),
|
||||
flake8_errmsg: Some(flake8_errmsg::settings::Settings::default().into()),
|
||||
flake8_pytest_style: Some(flake8_pytest_style::settings::Settings::default().into()),
|
||||
flake8_quotes: Some(flake8_quotes::settings::Settings::default().into()),
|
||||
|
|
|
@ -41,6 +41,7 @@ natively, including:
|
|||
- [flake8-builtins](https://pypi.org/project/flake8-builtins/)
|
||||
- [flake8-commas](https://pypi.org/project/flake8-commas/)
|
||||
- [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [flake8-copyright](https://pypi.org/project/flake8-copyright/)
|
||||
- [flake8-datetimez](https://pypi.org/project/flake8-datetimez/)
|
||||
- [flake8-debugger](https://pypi.org/project/flake8-debugger/)
|
||||
- [flake8-django](https://pypi.org/project/flake8-django/)
|
||||
|
@ -143,6 +144,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
|||
- [flake8-builtins](https://pypi.org/project/flake8-builtins/)
|
||||
- [flake8-commas](https://pypi.org/project/flake8-commas/)
|
||||
- [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [flake8-copyright](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [flake8-datetimez](https://pypi.org/project/flake8-datetimez/)
|
||||
- [flake8-debugger](https://pypi.org/project/flake8-debugger/)
|
||||
- [flake8-django](https://pypi.org/project/flake8-django/)
|
||||
|
|
44
ruff.schema.json
generated
44
ruff.schema.json
generated
|
@ -32,6 +32,17 @@
|
|||
"null"
|
||||
]
|
||||
},
|
||||
"copyright": {
|
||||
"description": "Options for the `copyright` plugin.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/CopyrightOptions"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dummy-variable-rgx": {
|
||||
"description": "A regular expression used to identify \"dummy\" variables, or those which should be ignored when enforcing (e.g.) unused-variable rules. The default expression matches `_`, `__`, and `_var`, but not `_var_`.",
|
||||
"type": [
|
||||
|
@ -620,6 +631,35 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"CopyrightOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"author": {
|
||||
"description": "Author to enforce within the copyright notice. If provided, the author must be present immediately following the copyright notice.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"min-file-size": {
|
||||
"description": "A minimum file size (in bytes) required for a copyright notice to be enforced. By default, all files are validated.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"notice-rgx": {
|
||||
"description": "The regular expression used to match the copyright notice, compiled with the [`regex`](https://docs.rs/regex/latest/regex/) crate.\n\nDefaults to `(?i)Copyright\\s+(\\(C\\)\\s+)?\\d{4}(-\\d{4})*`, which matches the following: - `Copyright 2023` - `Copyright (C) 2023` - `Copyright 2021-2023` - `Copyright (C) 2021-2023`",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Flake8AnnotationsOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1679,6 +1719,10 @@
|
|||
"COM812",
|
||||
"COM818",
|
||||
"COM819",
|
||||
"CPY",
|
||||
"CPY0",
|
||||
"CPY00",
|
||||
"CPY001",
|
||||
"D",
|
||||
"D1",
|
||||
"D10",
|
||||
|
|
|
@ -15,6 +15,7 @@ ignore = [
|
|||
"D", # pydocstyle
|
||||
"PL", # pylint
|
||||
"S", # bandit
|
||||
"CPY", # copyright
|
||||
"G", # flake8-logging
|
||||
"T", # flake8-print
|
||||
"FBT", # flake8-boolean-trap
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue