Skip to content

Commit

Permalink
[pydocstyle] Add setting to ignore missing documentation for*args
Browse files Browse the repository at this point in the history
… and `**kwargs` parameters (`D417`) (#15210)

Co-authored-by: Micha Reiser <[email protected]>
  • Loading branch information
InSyncWithFoo and MichaReiser authored Dec 31, 2024
1 parent 3c9021f commit cfd6093
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 50 deletions.
9 changes: 9 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pydocstyle/D417.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,12 @@ def select_data(
database:
Which database to connect to ("origin" or "destination").
"""

def f(x, *args, **kwargs):
"""Do something.
Args:
x: the value
*args: var-arguments
"""
return x
10 changes: 8 additions & 2 deletions crates/ruff_linter/src/rules/pydoclint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ mod tests {
let diagnostics = test_path(
Path::new("pydoclint").join(path).as_path(),
&settings::LinterSettings {
pydocstyle: pydocstyle::settings::Settings::new(Some(Convention::Google), [], []),
pydocstyle: pydocstyle::settings::Settings {
convention: Some(Convention::Google),
..pydocstyle::settings::Settings::default()
},
..settings::LinterSettings::for_rule(rule_code)
},
)?;
Expand All @@ -56,7 +59,10 @@ mod tests {
let diagnostics = test_path(
Path::new("pydoclint").join(path).as_path(),
&settings::LinterSettings {
pydocstyle: pydocstyle::settings::Settings::new(Some(Convention::Numpy), [], []),
pydocstyle: pydocstyle::settings::Settings {
convention: Some(Convention::Numpy),
..pydocstyle::settings::Settings::default()
},
..settings::LinterSettings::for_rule(rule_code)
},
)?;
Expand Down
55 changes: 46 additions & 9 deletions crates/ruff_linter/src/rules/pydocstyle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ mod tests {

use crate::registry::Rule;

use super::settings::{Convention, Settings};
use crate::test::test_path;
use crate::{assert_messages, settings};

use super::settings::{Convention, Settings};

#[test_case(Rule::MissingBlankLineAfterLastSection, Path::new("sections.py"))]
#[test_case(Rule::NoBlankLineAfterSection, Path::new("sections.py"))]
#[test_case(Rule::MissingBlankLineAfterLastSection, Path::new("D413.py"))]
Expand Down Expand Up @@ -100,11 +99,13 @@ mod tests {
let diagnostics = test_path(
Path::new("pydocstyle").join(path).as_path(),
&settings::LinterSettings {
pydocstyle: Settings::new(
None,
["functools.wraps".to_string()],
["gi.repository.GObject.Property".to_string()],
),
pydocstyle: Settings {
ignore_decorators: ["functools.wraps".to_string()].into_iter().collect(),
property_decorators: ["gi.repository.GObject.Property".to_string()]
.into_iter()
.collect(),
..Settings::default()
},
..settings::LinterSettings::for_rule(rule_code)
},
)?;
Expand Down Expand Up @@ -137,13 +138,46 @@ mod tests {
Ok(())
}

#[test]
fn d417_unspecified_ignore_var_parameters() -> Result<()> {
let diagnostics = test_path(
Path::new("pydocstyle/D417.py"),
&settings::LinterSettings {
pydocstyle: Settings::default(),
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
},
)?;
assert_messages!(diagnostics);
Ok(())
}

#[test]
fn d417_google() -> Result<()> {
let diagnostics = test_path(
Path::new("pydocstyle/D417.py"),
&settings::LinterSettings {
// With explicit Google convention, we should flag every function.
pydocstyle: Settings::new(Some(Convention::Google), [], []),
pydocstyle: Settings {
convention: Some(Convention::Google),
..Settings::default()
},
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
},
)?;
assert_messages!(diagnostics);
Ok(())
}

#[test]
fn d417_google_ignore_var_parameters() -> Result<()> {
let diagnostics = test_path(
Path::new("pydocstyle/D417.py"),
&settings::LinterSettings {
pydocstyle: Settings {
convention: Some(Convention::Google),
ignore_var_parameters: true,
..Settings::default()
},
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
},
)?;
Expand All @@ -157,7 +191,10 @@ mod tests {
Path::new("pydocstyle/D417.py"),
&settings::LinterSettings {
// With explicit numpy convention, we shouldn't flag anything.
pydocstyle: Settings::new(Some(Convention::Numpy), [], []),
pydocstyle: Settings {
convention: Some(Convention::Numpy),
..Settings::default()
},
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
},
)?;
Expand Down
37 changes: 20 additions & 17 deletions crates/ruff_linter/src/rules/pydocstyle/rules/sections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,7 @@ impl AlwaysFixableViolation for MissingSectionNameColon {
///
/// ## Options
/// - `lint.pydocstyle.convention`
/// - `lint.pydocstyle.ignore-var-parameters`
///
/// ## References
/// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/)
Expand Down Expand Up @@ -1810,24 +1811,26 @@ fn missing_args(checker: &mut Checker, docstring: &Docstring, docstrings_args: &

// Check specifically for `vararg` and `kwarg`, which can be prefixed with a
// single or double star, respectively.
if let Some(arg) = function.parameters.vararg.as_ref() {
let arg_name = arg.name.as_str();
let starred_arg_name = format!("*{arg_name}");
if !arg_name.starts_with('_')
&& !docstrings_args.contains(arg_name)
&& !docstrings_args.contains(&starred_arg_name)
{
missing_arg_names.insert(starred_arg_name);
if !checker.settings.pydocstyle.ignore_var_parameters() {
if let Some(arg) = function.parameters.vararg.as_ref() {
let arg_name = arg.name.as_str();
let starred_arg_name = format!("*{arg_name}");
if !arg_name.starts_with('_')
&& !docstrings_args.contains(arg_name)
&& !docstrings_args.contains(&starred_arg_name)
{
missing_arg_names.insert(starred_arg_name);
}
}
}
if let Some(arg) = function.parameters.kwarg.as_ref() {
let arg_name = arg.name.as_str();
let starred_arg_name = format!("**{arg_name}");
if !arg_name.starts_with('_')
&& !docstrings_args.contains(arg_name)
&& !docstrings_args.contains(&starred_arg_name)
{
missing_arg_names.insert(starred_arg_name);
if let Some(arg) = function.parameters.kwarg.as_ref() {
let arg_name = arg.name.as_str();
let starred_arg_name = format!("**{arg_name}");
if !arg_name.starts_with('_')
&& !docstrings_args.contains(arg_name)
&& !docstrings_args.contains(&starred_arg_name)
{
missing_arg_names.insert(starred_arg_name);
}
}
}

Expand Down
27 changes: 10 additions & 17 deletions crates/ruff_linter/src/rules/pydocstyle/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,25 +87,13 @@ impl fmt::Display for Convention {

#[derive(Debug, Clone, Default, CacheKey)]
pub struct Settings {
convention: Option<Convention>,
ignore_decorators: BTreeSet<String>,
property_decorators: BTreeSet<String>,
pub convention: Option<Convention>,
pub ignore_decorators: BTreeSet<String>,
pub property_decorators: BTreeSet<String>,
pub ignore_var_parameters: bool,
}

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
}
Expand All @@ -117,6 +105,10 @@ impl Settings {
pub fn property_decorators(&self) -> DecoratorIterator {
DecoratorIterator::new(&self.property_decorators)
}

pub fn ignore_var_parameters(&self) -> bool {
self.ignore_var_parameters
}
}

impl fmt::Display for Settings {
Expand All @@ -127,7 +119,8 @@ impl fmt::Display for Settings {
fields = [
self.convention | optional,
self.ignore_decorators | set,
self.property_decorators | set
self.property_decorators | set,
self.ignore_var_parameters
]
}
Ok(())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,12 @@ D417.py:155:5: D417 Missing argument description in the docstring for `select_da
156 | query: str,
157 | args: tuple,
|

D417.py:172:5: D417 Missing argument description in the docstring for `f`: `**kwargs`
|
170 | """
171 |
172 | def f(x, *args, **kwargs):
| ^ D417
173 | """Do something.
|
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
source: crates/ruff_linter/src/rules/pydocstyle/mod.rs
snapshot_kind: text
---
D417.py:1:5: D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
1 | def f(x, y, z):
| ^ D417
2 | """Do something.
|

D417.py:14:5: D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
14 | def f(x, y, z):
| ^ D417
15 | """Do something.
|

D417.py:27:5: D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
27 | def f(x, y, z):
| ^ D417
28 | """Do something.
|

D417.py:39:5: D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
39 | def f(x, y, z):
| ^ D417
40 | """Do something.
|

D417.py:52:5: D417 Missing argument description in the docstring for `f`: `y`
|
52 | def f(x, y, z):
| ^ D417
53 | """Do something.
|

D417.py:65:5: D417 Missing argument description in the docstring for `f`: `y`
|
65 | def f(x, y, z):
| ^ D417
66 | """Do something.
|

D417.py:77:5: D417 Missing argument description in the docstring for `f`: `y`
|
77 | def f(x, y, z):
| ^ D417
78 | """Do something.
|

D417.py:98:5: D417 Missing argument description in the docstring for `f`: `x`
|
98 | def f(x, *args, **kwargs):
| ^ D417
99 | """Do something.
|

D417.py:155:5: D417 Missing argument description in the docstring for `select_data`: `auto_save`
|
155 | def select_data(
| ^^^^^^^^^^^ D417
156 | query: str,
157 | args: tuple,
|
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,12 @@ D417.py:155:5: D417 Missing argument description in the docstring for `select_da
156 | query: str,
157 | args: tuple,
|

D417.py:172:5: D417 Missing argument description in the docstring for `f`: `**kwargs`
|
170 | """
171 |
172 | def f(x, *args, **kwargs):
| ^ D417
173 | """Do something.
|
Loading

0 comments on commit cfd6093

Please sign in to comment.