Make setting and retrieving pydocstyle settings less tedious (#12582)

This commit is contained in:
Alex Waygood 2024-07-31 10:39:33 +01:00 committed by GitHub
parent 138e70bd5c
commit 83b1c48a93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 146 additions and 134 deletions

View file

@ -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,
&section_contexts, &section_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,
&section_contexts, &section_contexts,
checker.settings.pydocstyle.convention.as_ref(), checker.settings.pydocstyle.convention(),
); );
} }
} }

View file

@ -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;

View file

@ -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)
}, },
)?; )?;

View file

@ -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);

View file

@ -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) => {

View file

@ -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)
}, },
)?; )?;

View file

@ -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;
} }

View file

@ -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),

View file

@ -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()
}
}

View file

@ -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(

View file

@ -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()));

View file

@ -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)
}) })
}) })
} }

View file

@ -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)

View file

@ -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(),
)
} }
} }