mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-22 11:25:26 +00:00
Make setting and retrieving pydocstyle settings less tedious (#12582)
This commit is contained in:
parent
138e70bd5c
commit
83b1c48a93
14 changed files with 146 additions and 134 deletions
|
@ -174,7 +174,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||||
if enforce_docstrings || enforce_pydoclint {
|
if enforce_docstrings || enforce_pydoclint {
|
||||||
if pydocstyle::helpers::should_ignore_definition(
|
if pydocstyle::helpers::should_ignore_definition(
|
||||||
definition,
|
definition,
|
||||||
&checker.settings.pydocstyle.ignore_decorators,
|
&checker.settings.pydocstyle,
|
||||||
&checker.semantic,
|
&checker.semantic,
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -271,7 +271,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||||
pydocstyle::rules::non_imperative_mood(
|
pydocstyle::rules::non_imperative_mood(
|
||||||
checker,
|
checker,
|
||||||
&docstring,
|
&docstring,
|
||||||
&checker.settings.pydocstyle.property_decorators,
|
&checker.settings.pydocstyle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if checker.enabled(Rule::NoSignature) {
|
if checker.enabled(Rule::NoSignature) {
|
||||||
|
@ -310,7 +310,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||||
if enforce_sections || enforce_pydoclint {
|
if enforce_sections || enforce_pydoclint {
|
||||||
let section_contexts = pydocstyle::helpers::get_section_contexts(
|
let section_contexts = pydocstyle::helpers::get_section_contexts(
|
||||||
&docstring,
|
&docstring,
|
||||||
checker.settings.pydocstyle.convention.as_ref(),
|
checker.settings.pydocstyle.convention(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if enforce_sections {
|
if enforce_sections {
|
||||||
|
@ -318,7 +318,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||||
checker,
|
checker,
|
||||||
&docstring,
|
&docstring,
|
||||||
§ion_contexts,
|
§ion_contexts,
|
||||||
checker.settings.pydocstyle.convention.as_ref(),
|
checker.settings.pydocstyle.convention(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||||
checker,
|
checker,
|
||||||
definition,
|
definition,
|
||||||
§ion_contexts,
|
§ion_contexts,
|
||||||
checker.settings.pydocstyle.convention.as_ref(),
|
checker.settings.pydocstyle.convention(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ use ruff_diagnostics::{AlwaysFixableViolation, FixAvailability, Violation};
|
||||||
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::{is_const_false, is_const_true};
|
use ruff_python_ast::helpers::{is_const_false, is_const_true};
|
||||||
use ruff_python_ast::name::QualifiedName;
|
|
||||||
use ruff_python_ast::stmt_if::elif_else_range;
|
use ruff_python_ast::stmt_if::elif_else_range;
|
||||||
use ruff_python_ast::visitor::Visitor;
|
use ruff_python_ast::visitor::Visitor;
|
||||||
use ruff_python_ast::whitespace::indentation;
|
use ruff_python_ast::whitespace::indentation;
|
||||||
|
@ -375,18 +374,10 @@ fn unnecessary_return_none(checker: &mut Checker, decorator_list: &[Decorator],
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let extra_property_decorators = checker
|
|
||||||
.settings
|
|
||||||
.pydocstyle
|
|
||||||
.property_decorators
|
|
||||||
.iter()
|
|
||||||
.map(|decorator| QualifiedName::from_dotted_name(decorator))
|
|
||||||
.collect::<Vec<QualifiedName>>();
|
|
||||||
|
|
||||||
// Skip property functions
|
// Skip property functions
|
||||||
if is_property(
|
if is_property(
|
||||||
decorator_list,
|
decorator_list,
|
||||||
&extra_property_decorators,
|
checker.settings.pydocstyle.property_decorators(),
|
||||||
checker.semantic(),
|
checker.semantic(),
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -3,7 +3,6 @@ pub(crate) mod rules;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::BTreeSet;
|
|
||||||
use std::convert::AsRef;
|
use std::convert::AsRef;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
@ -11,7 +10,8 @@ mod tests {
|
||||||
use test_case::test_case;
|
use test_case::test_case;
|
||||||
|
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
use crate::rules::pydocstyle::settings::{Convention, Settings};
|
use crate::rules::pydocstyle;
|
||||||
|
use crate::rules::pydocstyle::settings::Convention;
|
||||||
use crate::test::test_path;
|
use crate::test::test_path;
|
||||||
use crate::{assert_messages, settings};
|
use crate::{assert_messages, settings};
|
||||||
|
|
||||||
|
@ -35,11 +35,7 @@ mod tests {
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
Path::new("pydoclint").join(path).as_path(),
|
Path::new("pydoclint").join(path).as_path(),
|
||||||
&settings::LinterSettings {
|
&settings::LinterSettings {
|
||||||
pydocstyle: Settings {
|
pydocstyle: pydocstyle::settings::Settings::new(Some(Convention::Google), [], []),
|
||||||
convention: Some(Convention::Google),
|
|
||||||
ignore_decorators: BTreeSet::new(),
|
|
||||||
property_decorators: BTreeSet::new(),
|
|
||||||
},
|
|
||||||
..settings::LinterSettings::for_rule(rule_code)
|
..settings::LinterSettings::for_rule(rule_code)
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
@ -56,11 +52,7 @@ mod tests {
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
Path::new("pydoclint").join(path).as_path(),
|
Path::new("pydoclint").join(path).as_path(),
|
||||||
&settings::LinterSettings {
|
&settings::LinterSettings {
|
||||||
pydocstyle: Settings {
|
pydocstyle: pydocstyle::settings::Settings::new(Some(Convention::Numpy), [], []),
|
||||||
convention: Some(Convention::Numpy),
|
|
||||||
ignore_decorators: BTreeSet::new(),
|
|
||||||
property_decorators: BTreeSet::new(),
|
|
||||||
},
|
|
||||||
..settings::LinterSettings::for_rule(rule_code)
|
..settings::LinterSettings::for_rule(rule_code)
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -444,7 +444,7 @@ pub(crate) fn check_docstring(
|
||||||
checker: &mut Checker,
|
checker: &mut Checker,
|
||||||
definition: &Definition,
|
definition: &Definition,
|
||||||
section_contexts: &SectionContexts,
|
section_contexts: &SectionContexts,
|
||||||
convention: Option<&Convention>,
|
convention: Option<Convention>,
|
||||||
) {
|
) {
|
||||||
let mut diagnostics = Vec::new();
|
let mut diagnostics = Vec::new();
|
||||||
let Definition::Member(member) = definition else {
|
let Definition::Member(member) = definition else {
|
||||||
|
@ -478,14 +478,8 @@ pub(crate) fn check_docstring(
|
||||||
|
|
||||||
// DOC201
|
// DOC201
|
||||||
if checker.enabled(Rule::DocstringMissingReturns) && docstring_sections.returns.is_none() {
|
if checker.enabled(Rule::DocstringMissingReturns) && docstring_sections.returns.is_none() {
|
||||||
let extra_property_decorators = checker
|
let extra_property_decorators = checker.settings.pydocstyle.property_decorators();
|
||||||
.settings
|
if !definition.is_property(extra_property_decorators, checker.semantic()) {
|
||||||
.pydocstyle
|
|
||||||
.property_decorators
|
|
||||||
.iter()
|
|
||||||
.map(|decorator| QualifiedName::from_dotted_name(decorator))
|
|
||||||
.collect::<Vec<QualifiedName>>();
|
|
||||||
if !definition.is_property(&extra_property_decorators, checker.semantic()) {
|
|
||||||
if let Some(body_return) = body_entries.returns.first() {
|
if let Some(body_return) = body_entries.returns.first() {
|
||||||
let diagnostic = Diagnostic::new(DocstringMissingReturns, body_return.range());
|
let diagnostic = Diagnostic::new(DocstringMissingReturns, body_return.range());
|
||||||
diagnostics.push(diagnostic);
|
diagnostics.push(diagnostic);
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
use std::collections::BTreeSet;
|
|
||||||
|
|
||||||
use ruff_python_ast::helpers::map_callable;
|
use ruff_python_ast::helpers::map_callable;
|
||||||
use ruff_python_ast::name::QualifiedName;
|
|
||||||
use ruff_python_semantic::{Definition, SemanticModel};
|
use ruff_python_semantic::{Definition, SemanticModel};
|
||||||
use ruff_source_file::UniversalNewlines;
|
use ruff_source_file::UniversalNewlines;
|
||||||
|
|
||||||
use crate::docstrings::sections::{SectionContexts, SectionKind};
|
use crate::docstrings::sections::{SectionContexts, SectionKind};
|
||||||
use crate::docstrings::styles::SectionStyle;
|
use crate::docstrings::styles::SectionStyle;
|
||||||
use crate::docstrings::Docstring;
|
use crate::docstrings::Docstring;
|
||||||
use crate::rules::pydocstyle::settings::Convention;
|
use crate::rules::pydocstyle::settings::{Convention, Settings};
|
||||||
|
|
||||||
/// Return the index of the first logical line in a string.
|
/// Return the index of the first logical line in a string.
|
||||||
pub(super) fn logical_line(content: &str) -> Option<usize> {
|
pub(super) fn logical_line(content: &str) -> Option<usize> {
|
||||||
|
@ -45,10 +42,12 @@ pub(super) fn ends_with_backslash(line: &str) -> bool {
|
||||||
/// Check decorator list to see if function should be ignored.
|
/// Check decorator list to see if function should be ignored.
|
||||||
pub(crate) fn should_ignore_definition(
|
pub(crate) fn should_ignore_definition(
|
||||||
definition: &Definition,
|
definition: &Definition,
|
||||||
ignore_decorators: &BTreeSet<String>,
|
settings: &Settings,
|
||||||
semantic: &SemanticModel,
|
semantic: &SemanticModel,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if ignore_decorators.is_empty() {
|
let ignore_decorators = settings.ignore_decorators();
|
||||||
|
|
||||||
|
if ignore_decorators.len() == 0 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,15 +60,15 @@ pub(crate) fn should_ignore_definition(
|
||||||
.resolve_qualified_name(map_callable(&decorator.expression))
|
.resolve_qualified_name(map_callable(&decorator.expression))
|
||||||
.is_some_and(|qualified_name| {
|
.is_some_and(|qualified_name| {
|
||||||
ignore_decorators
|
ignore_decorators
|
||||||
.iter()
|
.clone()
|
||||||
.any(|decorator| QualifiedName::from_dotted_name(decorator) == qualified_name)
|
.any(|decorator| decorator == qualified_name)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_section_contexts<'a>(
|
pub(crate) fn get_section_contexts<'a>(
|
||||||
docstring: &'a Docstring<'a>,
|
docstring: &'a Docstring<'a>,
|
||||||
convention: Option<&'a Convention>,
|
convention: Option<Convention>,
|
||||||
) -> SectionContexts<'a> {
|
) -> SectionContexts<'a> {
|
||||||
match convention {
|
match convention {
|
||||||
Some(Convention::Google) => {
|
Some(Convention::Google) => {
|
||||||
|
|
|
@ -5,7 +5,6 @@ pub mod settings;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::BTreeSet;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
@ -98,13 +97,11 @@ mod tests {
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
Path::new("pydocstyle").join(path).as_path(),
|
Path::new("pydocstyle").join(path).as_path(),
|
||||||
&settings::LinterSettings {
|
&settings::LinterSettings {
|
||||||
pydocstyle: Settings {
|
pydocstyle: Settings::new(
|
||||||
convention: None,
|
None,
|
||||||
ignore_decorators: BTreeSet::from_iter(["functools.wraps".to_string()]),
|
["functools.wraps".to_string()],
|
||||||
property_decorators: BTreeSet::from_iter([
|
["gi.repository.GObject.Property".to_string()],
|
||||||
"gi.repository.GObject.Property".to_string()
|
),
|
||||||
]),
|
|
||||||
},
|
|
||||||
..settings::LinterSettings::for_rule(rule_code)
|
..settings::LinterSettings::for_rule(rule_code)
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
@ -129,11 +126,7 @@ mod tests {
|
||||||
&settings::LinterSettings {
|
&settings::LinterSettings {
|
||||||
// When inferring the convention, we'll see a few false negatives.
|
// When inferring the convention, we'll see a few false negatives.
|
||||||
// See: https://github.com/PyCQA/pydocstyle/issues/459.
|
// See: https://github.com/PyCQA/pydocstyle/issues/459.
|
||||||
pydocstyle: Settings {
|
pydocstyle: Settings::default(),
|
||||||
convention: None,
|
|
||||||
ignore_decorators: BTreeSet::new(),
|
|
||||||
property_decorators: BTreeSet::new(),
|
|
||||||
},
|
|
||||||
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
|
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
@ -147,11 +140,7 @@ mod tests {
|
||||||
Path::new("pydocstyle/D417.py"),
|
Path::new("pydocstyle/D417.py"),
|
||||||
&settings::LinterSettings {
|
&settings::LinterSettings {
|
||||||
// With explicit Google convention, we should flag every function.
|
// With explicit Google convention, we should flag every function.
|
||||||
pydocstyle: Settings {
|
pydocstyle: Settings::new(Some(Convention::Google), [], []),
|
||||||
convention: Some(Convention::Google),
|
|
||||||
ignore_decorators: BTreeSet::new(),
|
|
||||||
property_decorators: BTreeSet::new(),
|
|
||||||
},
|
|
||||||
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
|
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
@ -164,12 +153,8 @@ mod tests {
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
Path::new("pydocstyle/D417.py"),
|
Path::new("pydocstyle/D417.py"),
|
||||||
&settings::LinterSettings {
|
&settings::LinterSettings {
|
||||||
// With explicit Google convention, we shouldn't flag anything.
|
// With explicit numpy convention, we shouldn't flag anything.
|
||||||
pydocstyle: Settings {
|
pydocstyle: Settings::new(Some(Convention::Numpy), [], []),
|
||||||
convention: Some(Convention::Numpy),
|
|
||||||
ignore_decorators: BTreeSet::new(),
|
|
||||||
property_decorators: BTreeSet::new(),
|
|
||||||
},
|
|
||||||
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
|
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
use std::collections::BTreeSet;
|
|
||||||
|
|
||||||
use imperative::Mood;
|
use imperative::Mood;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::name::QualifiedName;
|
|
||||||
use ruff_python_semantic::analyze::visibility::{is_property, is_test};
|
use ruff_python_semantic::analyze::visibility::{is_property, is_test};
|
||||||
use ruff_source_file::UniversalNewlines;
|
use ruff_source_file::UniversalNewlines;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
@ -13,6 +10,7 @@ use ruff_text_size::Ranged;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::docstrings::Docstring;
|
use crate::docstrings::Docstring;
|
||||||
use crate::rules::pydocstyle::helpers::normalize_word;
|
use crate::rules::pydocstyle::helpers::normalize_word;
|
||||||
|
use crate::rules::pydocstyle::settings::Settings;
|
||||||
|
|
||||||
static MOOD: Lazy<Mood> = Lazy::new(Mood::new);
|
static MOOD: Lazy<Mood> = Lazy::new(Mood::new);
|
||||||
|
|
||||||
|
@ -66,24 +64,21 @@ impl Violation for NonImperativeMood {
|
||||||
pub(crate) fn non_imperative_mood(
|
pub(crate) fn non_imperative_mood(
|
||||||
checker: &mut Checker,
|
checker: &mut Checker,
|
||||||
docstring: &Docstring,
|
docstring: &Docstring,
|
||||||
property_decorators: &BTreeSet<String>,
|
settings: &Settings,
|
||||||
) {
|
) {
|
||||||
let Some(function) = docstring.definition.as_function_def() else {
|
let Some(function) = docstring.definition.as_function_def() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let property_decorators = property_decorators
|
if is_test(&function.name) {
|
||||||
.iter()
|
return;
|
||||||
.map(|decorator| QualifiedName::from_dotted_name(decorator))
|
}
|
||||||
.collect::<Vec<QualifiedName>>();
|
|
||||||
|
|
||||||
if is_test(&function.name)
|
if is_property(
|
||||||
|| is_property(
|
|
||||||
&function.decorator_list,
|
&function.decorator_list,
|
||||||
&property_decorators,
|
settings.property_decorators(),
|
||||||
checker.semantic(),
|
checker.semantic(),
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1325,7 +1325,7 @@ pub(crate) fn sections(
|
||||||
checker: &mut Checker,
|
checker: &mut Checker,
|
||||||
docstring: &Docstring,
|
docstring: &Docstring,
|
||||||
section_contexts: &SectionContexts,
|
section_contexts: &SectionContexts,
|
||||||
convention: Option<&Convention>,
|
convention: Option<Convention>,
|
||||||
) {
|
) {
|
||||||
match convention {
|
match convention {
|
||||||
Some(Convention::Google) => parse_google_sections(checker, docstring, section_contexts),
|
Some(Convention::Google) => parse_google_sections(checker, docstring, section_contexts),
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::iter::FusedIterator;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::display_settings;
|
|
||||||
use ruff_macros::CacheKey;
|
use ruff_macros::CacheKey;
|
||||||
|
use ruff_python_ast::name::QualifiedName;
|
||||||
|
|
||||||
|
use crate::display_settings;
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, CacheKey)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, CacheKey)]
|
||||||
|
@ -85,9 +87,36 @@ impl fmt::Display for Convention {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, CacheKey)]
|
#[derive(Debug, Clone, Default, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub convention: Option<Convention>,
|
convention: Option<Convention>,
|
||||||
pub ignore_decorators: BTreeSet<String>,
|
ignore_decorators: BTreeSet<String>,
|
||||||
pub property_decorators: BTreeSet<String>,
|
property_decorators: BTreeSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(
|
||||||
|
convention: Option<Convention>,
|
||||||
|
ignore_decorators: impl IntoIterator<Item = String>,
|
||||||
|
property_decorators: impl IntoIterator<Item = String>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
convention,
|
||||||
|
ignore_decorators: ignore_decorators.into_iter().collect(),
|
||||||
|
property_decorators: property_decorators.into_iter().collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn convention(&self) -> Option<Convention> {
|
||||||
|
self.convention
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ignore_decorators(&self) -> DecoratorIterator {
|
||||||
|
DecoratorIterator::new(&self.ignore_decorators)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn property_decorators(&self) -> DecoratorIterator {
|
||||||
|
DecoratorIterator::new(&self.property_decorators)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Settings {
|
impl fmt::Display for Settings {
|
||||||
|
@ -104,3 +133,34 @@ impl fmt::Display for Settings {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DecoratorIterator<'a> {
|
||||||
|
decorators: std::collections::btree_set::Iter<'a, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DecoratorIterator<'a> {
|
||||||
|
fn new(decorators: &'a BTreeSet<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
decorators: decorators.iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for DecoratorIterator<'a> {
|
||||||
|
type Item = QualifiedName<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<QualifiedName<'a>> {
|
||||||
|
self.decorators
|
||||||
|
.next()
|
||||||
|
.map(|deco| QualifiedName::from_dotted_name(deco))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FusedIterator for DecoratorIterator<'_> {}
|
||||||
|
|
||||||
|
impl ExactSizeIterator for DecoratorIterator<'_> {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.decorators.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::identifier::Identifier;
|
use ruff_python_ast::identifier::Identifier;
|
||||||
use ruff_python_ast::name::QualifiedName;
|
|
||||||
use ruff_python_semantic::{
|
use ruff_python_semantic::{
|
||||||
analyze::{function_type, visibility},
|
analyze::{function_type, visibility},
|
||||||
Scope, ScopeId, ScopeKind,
|
Scope, ScopeId, ScopeKind,
|
||||||
|
@ -49,7 +48,9 @@ pub(crate) fn no_self_use(
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
diagnostics: &mut Vec<Diagnostic>,
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
let Some(parent) = checker.semantic().first_non_type_parent_scope(scope) else {
|
let semantic = checker.semantic();
|
||||||
|
|
||||||
|
let Some(parent) = semantic.first_non_type_parent_scope(scope) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ pub(crate) fn no_self_use(
|
||||||
name,
|
name,
|
||||||
decorator_list,
|
decorator_list,
|
||||||
parent,
|
parent,
|
||||||
checker.semantic(),
|
semantic,
|
||||||
&checker.settings.pep8_naming.classmethod_decorators,
|
&checker.settings.pep8_naming.classmethod_decorators,
|
||||||
&checker.settings.pep8_naming.staticmethod_decorators,
|
&checker.settings.pep8_naming.staticmethod_decorators,
|
||||||
),
|
),
|
||||||
|
@ -78,20 +79,14 @@ pub(crate) fn no_self_use(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let property_decorators = checker
|
let extra_property_decorators = checker.settings.pydocstyle.property_decorators();
|
||||||
.settings
|
|
||||||
.pydocstyle
|
|
||||||
.property_decorators
|
|
||||||
.iter()
|
|
||||||
.map(|decorator| QualifiedName::from_dotted_name(decorator))
|
|
||||||
.collect::<Vec<QualifiedName>>();
|
|
||||||
|
|
||||||
if function_type::is_stub(func, checker.semantic())
|
if function_type::is_stub(func, semantic)
|
||||||
|| visibility::is_magic(name)
|
|| visibility::is_magic(name)
|
||||||
|| visibility::is_abstract(decorator_list, checker.semantic())
|
|| visibility::is_abstract(decorator_list, semantic)
|
||||||
|| visibility::is_override(decorator_list, checker.semantic())
|
|| visibility::is_override(decorator_list, semantic)
|
||||||
|| visibility::is_overload(decorator_list, checker.semantic())
|
|| visibility::is_overload(decorator_list, semantic)
|
||||||
|| visibility::is_property(decorator_list, &property_decorators, checker.semantic())
|
|| visibility::is_property(decorator_list, extra_property_decorators, semantic)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -113,12 +108,12 @@ pub(crate) fn no_self_use(
|
||||||
|
|
||||||
// If the method contains a `super` reference, then it should be considered to use self
|
// If the method contains a `super` reference, then it should be considered to use self
|
||||||
// implicitly.
|
// implicitly.
|
||||||
if let Some(binding_id) = checker.semantic().global_scope().get("super") {
|
if let Some(binding_id) = semantic.global_scope().get("super") {
|
||||||
let binding = checker.semantic().binding(binding_id);
|
let binding = semantic.binding(binding_id);
|
||||||
if binding.kind.is_builtin() {
|
if binding.kind.is_builtin() {
|
||||||
if binding
|
if binding
|
||||||
.references()
|
.references()
|
||||||
.any(|id| checker.semantic().reference(id).scope_id() == scope_id)
|
.any(|id| semantic.reference(id).scope_id() == scope_id)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -127,7 +122,7 @@ pub(crate) fn no_self_use(
|
||||||
|
|
||||||
if scope
|
if scope
|
||||||
.get("self")
|
.get("self")
|
||||||
.map(|binding_id| checker.semantic().binding(binding_id))
|
.map(|binding_id| semantic.binding(binding_id))
|
||||||
.is_some_and(|binding| binding.kind.is_argument() && !binding.is_used())
|
.is_some_and(|binding| binding.kind.is_argument() && !binding.is_used())
|
||||||
{
|
{
|
||||||
diagnostics.push(Diagnostic::new(
|
diagnostics.push(Diagnostic::new(
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::name::QualifiedName;
|
|
||||||
use ruff_python_ast::{identifier::Identifier, Decorator, Parameters, Stmt};
|
use ruff_python_ast::{identifier::Identifier, Decorator, Parameters, Stmt};
|
||||||
use ruff_python_semantic::analyze::visibility::is_property;
|
use ruff_python_semantic::analyze::visibility::is_property;
|
||||||
|
|
||||||
|
@ -57,14 +56,8 @@ pub(crate) fn property_with_parameters(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let semantic = checker.semantic();
|
let semantic = checker.semantic();
|
||||||
let extra_property_decorators = checker
|
let extra_property_decorators = checker.settings.pydocstyle.property_decorators();
|
||||||
.settings
|
if is_property(decorator_list, extra_property_decorators, semantic) {
|
||||||
.pydocstyle
|
|
||||||
.property_decorators
|
|
||||||
.iter()
|
|
||||||
.map(|decorator| QualifiedName::from_dotted_name(decorator))
|
|
||||||
.collect::<Vec<QualifiedName>>();
|
|
||||||
if is_property(decorator_list, &extra_property_decorators, semantic) {
|
|
||||||
checker
|
checker
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.push(Diagnostic::new(PropertyWithParameters, stmt.identifier()));
|
.push(Diagnostic::new(PropertyWithParameters, stmt.identifier()));
|
||||||
|
|
|
@ -63,11 +63,16 @@ pub fn is_abstract(decorator_list: &[Decorator], semantic: &SemanticModel) -> bo
|
||||||
/// Returns `true` if a function definition is a `@property`.
|
/// Returns `true` if a function definition is a `@property`.
|
||||||
/// `extra_properties` can be used to check additional non-standard
|
/// `extra_properties` can be used to check additional non-standard
|
||||||
/// `@property`-like decorators.
|
/// `@property`-like decorators.
|
||||||
pub fn is_property(
|
pub fn is_property<'a, P, I>(
|
||||||
decorator_list: &[Decorator],
|
decorator_list: &[Decorator],
|
||||||
extra_properties: &[QualifiedName],
|
extra_properties: P,
|
||||||
semantic: &SemanticModel,
|
semantic: &SemanticModel,
|
||||||
) -> bool {
|
) -> bool
|
||||||
|
where
|
||||||
|
P: IntoIterator<IntoIter = I>,
|
||||||
|
I: Iterator<Item = QualifiedName<'a>> + Clone,
|
||||||
|
{
|
||||||
|
let extra_properties = extra_properties.into_iter();
|
||||||
decorator_list.iter().any(|decorator| {
|
decorator_list.iter().any(|decorator| {
|
||||||
semantic
|
semantic
|
||||||
.resolve_qualified_name(map_callable(&decorator.expression))
|
.resolve_qualified_name(map_callable(&decorator.expression))
|
||||||
|
@ -79,8 +84,8 @@ pub fn is_property(
|
||||||
| ["abc", "abstractproperty"]
|
| ["abc", "abstractproperty"]
|
||||||
| ["types", "DynamicClassAttribute"]
|
| ["types", "DynamicClassAttribute"]
|
||||||
) || extra_properties
|
) || extra_properties
|
||||||
.iter()
|
.clone()
|
||||||
.any(|extra_property| extra_property.segments() == qualified_name.segments())
|
.any(|extra_property| extra_property == qualified_name)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,11 +151,11 @@ impl<'a> Definition<'a> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_property(
|
pub fn is_property<P, I>(&self, extra_properties: P, semantic: &SemanticModel) -> bool
|
||||||
&self,
|
where
|
||||||
extra_properties: &[QualifiedName],
|
P: IntoIterator<IntoIter = I>,
|
||||||
semantic: &SemanticModel,
|
I: Iterator<Item = QualifiedName<'a>> + Clone,
|
||||||
) -> bool {
|
{
|
||||||
self.as_function_def()
|
self.as_function_def()
|
||||||
.is_some_and(|StmtFunctionDef { decorator_list, .. }| {
|
.is_some_and(|StmtFunctionDef { decorator_list, .. }| {
|
||||||
is_property(decorator_list, extra_properties, semantic)
|
is_property(decorator_list, extra_properties, semantic)
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::collections::BTreeSet;
|
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
|
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -2762,11 +2760,16 @@ pub struct PydocstyleOptions {
|
||||||
|
|
||||||
impl PydocstyleOptions {
|
impl PydocstyleOptions {
|
||||||
pub fn into_settings(self) -> pydocstyle::settings::Settings {
|
pub fn into_settings(self) -> pydocstyle::settings::Settings {
|
||||||
pydocstyle::settings::Settings {
|
let PydocstyleOptions {
|
||||||
convention: self.convention,
|
convention,
|
||||||
ignore_decorators: BTreeSet::from_iter(self.ignore_decorators.unwrap_or_default()),
|
ignore_decorators,
|
||||||
property_decorators: BTreeSet::from_iter(self.property_decorators.unwrap_or_default()),
|
property_decorators,
|
||||||
}
|
} = self;
|
||||||
|
pydocstyle::settings::Settings::new(
|
||||||
|
convention,
|
||||||
|
ignore_decorators.unwrap_or_default(),
|
||||||
|
property_decorators.unwrap_or_default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue