Skip to content

Commit cfd6093

Browse files
[pydocstyle] Add setting to ignore missing documentation for*args and **kwargs parameters (D417) (#15210)
Co-authored-by: Micha Reiser <[email protected]>
1 parent 3c9021f commit cfd6093

11 files changed

+285
-50
lines changed

crates/ruff_linter/resources/test/fixtures/pydocstyle/D417.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,12 @@ def select_data(
168168
database:
169169
Which database to connect to ("origin" or "destination").
170170
"""
171+
172+
def f(x, *args, **kwargs):
173+
"""Do something.
174+
175+
Args:
176+
x: the value
177+
*args: var-arguments
178+
"""
179+
return x

crates/ruff_linter/src/rules/pydoclint/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ mod tests {
3737
let diagnostics = test_path(
3838
Path::new("pydoclint").join(path).as_path(),
3939
&settings::LinterSettings {
40-
pydocstyle: pydocstyle::settings::Settings::new(Some(Convention::Google), [], []),
40+
pydocstyle: pydocstyle::settings::Settings {
41+
convention: Some(Convention::Google),
42+
..pydocstyle::settings::Settings::default()
43+
},
4144
..settings::LinterSettings::for_rule(rule_code)
4245
},
4346
)?;
@@ -56,7 +59,10 @@ mod tests {
5659
let diagnostics = test_path(
5760
Path::new("pydoclint").join(path).as_path(),
5861
&settings::LinterSettings {
59-
pydocstyle: pydocstyle::settings::Settings::new(Some(Convention::Numpy), [], []),
62+
pydocstyle: pydocstyle::settings::Settings {
63+
convention: Some(Convention::Numpy),
64+
..pydocstyle::settings::Settings::default()
65+
},
6066
..settings::LinterSettings::for_rule(rule_code)
6167
},
6268
)?;

crates/ruff_linter/src/rules/pydocstyle/mod.rs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@ mod tests {
1212

1313
use crate::registry::Rule;
1414

15+
use super::settings::{Convention, Settings};
1516
use crate::test::test_path;
1617
use crate::{assert_messages, settings};
1718

18-
use super::settings::{Convention, Settings};
19-
2019
#[test_case(Rule::MissingBlankLineAfterLastSection, Path::new("sections.py"))]
2120
#[test_case(Rule::NoBlankLineAfterSection, Path::new("sections.py"))]
2221
#[test_case(Rule::MissingBlankLineAfterLastSection, Path::new("D413.py"))]
@@ -100,11 +99,13 @@ mod tests {
10099
let diagnostics = test_path(
101100
Path::new("pydocstyle").join(path).as_path(),
102101
&settings::LinterSettings {
103-
pydocstyle: Settings::new(
104-
None,
105-
["functools.wraps".to_string()],
106-
["gi.repository.GObject.Property".to_string()],
107-
),
102+
pydocstyle: Settings {
103+
ignore_decorators: ["functools.wraps".to_string()].into_iter().collect(),
104+
property_decorators: ["gi.repository.GObject.Property".to_string()]
105+
.into_iter()
106+
.collect(),
107+
..Settings::default()
108+
},
108109
..settings::LinterSettings::for_rule(rule_code)
109110
},
110111
)?;
@@ -137,13 +138,46 @@ mod tests {
137138
Ok(())
138139
}
139140

141+
#[test]
142+
fn d417_unspecified_ignore_var_parameters() -> Result<()> {
143+
let diagnostics = test_path(
144+
Path::new("pydocstyle/D417.py"),
145+
&settings::LinterSettings {
146+
pydocstyle: Settings::default(),
147+
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
148+
},
149+
)?;
150+
assert_messages!(diagnostics);
151+
Ok(())
152+
}
153+
140154
#[test]
141155
fn d417_google() -> Result<()> {
142156
let diagnostics = test_path(
143157
Path::new("pydocstyle/D417.py"),
144158
&settings::LinterSettings {
145159
// With explicit Google convention, we should flag every function.
146-
pydocstyle: Settings::new(Some(Convention::Google), [], []),
160+
pydocstyle: Settings {
161+
convention: Some(Convention::Google),
162+
..Settings::default()
163+
},
164+
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
165+
},
166+
)?;
167+
assert_messages!(diagnostics);
168+
Ok(())
169+
}
170+
171+
#[test]
172+
fn d417_google_ignore_var_parameters() -> Result<()> {
173+
let diagnostics = test_path(
174+
Path::new("pydocstyle/D417.py"),
175+
&settings::LinterSettings {
176+
pydocstyle: Settings {
177+
convention: Some(Convention::Google),
178+
ignore_var_parameters: true,
179+
..Settings::default()
180+
},
147181
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
148182
},
149183
)?;
@@ -157,7 +191,10 @@ mod tests {
157191
Path::new("pydocstyle/D417.py"),
158192
&settings::LinterSettings {
159193
// With explicit numpy convention, we shouldn't flag anything.
160-
pydocstyle: Settings::new(Some(Convention::Numpy), [], []),
194+
pydocstyle: Settings {
195+
convention: Some(Convention::Numpy),
196+
..Settings::default()
197+
},
161198
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
162199
},
163200
)?;

crates/ruff_linter/src/rules/pydocstyle/rules/sections.rs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,7 @@ impl AlwaysFixableViolation for MissingSectionNameColon {
12151215
///
12161216
/// ## Options
12171217
/// - `lint.pydocstyle.convention`
1218+
/// - `lint.pydocstyle.ignore-var-parameters`
12181219
///
12191220
/// ## References
12201221
/// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/)
@@ -1810,24 +1811,26 @@ fn missing_args(checker: &mut Checker, docstring: &Docstring, docstrings_args: &
18101811

18111812
// Check specifically for `vararg` and `kwarg`, which can be prefixed with a
18121813
// single or double star, respectively.
1813-
if let Some(arg) = function.parameters.vararg.as_ref() {
1814-
let arg_name = arg.name.as_str();
1815-
let starred_arg_name = format!("*{arg_name}");
1816-
if !arg_name.starts_with('_')
1817-
&& !docstrings_args.contains(arg_name)
1818-
&& !docstrings_args.contains(&starred_arg_name)
1819-
{
1820-
missing_arg_names.insert(starred_arg_name);
1814+
if !checker.settings.pydocstyle.ignore_var_parameters() {
1815+
if let Some(arg) = function.parameters.vararg.as_ref() {
1816+
let arg_name = arg.name.as_str();
1817+
let starred_arg_name = format!("*{arg_name}");
1818+
if !arg_name.starts_with('_')
1819+
&& !docstrings_args.contains(arg_name)
1820+
&& !docstrings_args.contains(&starred_arg_name)
1821+
{
1822+
missing_arg_names.insert(starred_arg_name);
1823+
}
18211824
}
1822-
}
1823-
if let Some(arg) = function.parameters.kwarg.as_ref() {
1824-
let arg_name = arg.name.as_str();
1825-
let starred_arg_name = format!("**{arg_name}");
1826-
if !arg_name.starts_with('_')
1827-
&& !docstrings_args.contains(arg_name)
1828-
&& !docstrings_args.contains(&starred_arg_name)
1829-
{
1830-
missing_arg_names.insert(starred_arg_name);
1825+
if let Some(arg) = function.parameters.kwarg.as_ref() {
1826+
let arg_name = arg.name.as_str();
1827+
let starred_arg_name = format!("**{arg_name}");
1828+
if !arg_name.starts_with('_')
1829+
&& !docstrings_args.contains(arg_name)
1830+
&& !docstrings_args.contains(&starred_arg_name)
1831+
{
1832+
missing_arg_names.insert(starred_arg_name);
1833+
}
18311834
}
18321835
}
18331836

crates/ruff_linter/src/rules/pydocstyle/settings.rs

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -87,25 +87,13 @@ impl fmt::Display for Convention {
8787

8888
#[derive(Debug, Clone, Default, CacheKey)]
8989
pub struct Settings {
90-
convention: Option<Convention>,
91-
ignore_decorators: BTreeSet<String>,
92-
property_decorators: BTreeSet<String>,
90+
pub convention: Option<Convention>,
91+
pub ignore_decorators: BTreeSet<String>,
92+
pub property_decorators: BTreeSet<String>,
93+
pub ignore_var_parameters: bool,
9394
}
9495

9596
impl Settings {
96-
#[must_use]
97-
pub fn new(
98-
convention: Option<Convention>,
99-
ignore_decorators: impl IntoIterator<Item = String>,
100-
property_decorators: impl IntoIterator<Item = String>,
101-
) -> Self {
102-
Self {
103-
convention,
104-
ignore_decorators: ignore_decorators.into_iter().collect(),
105-
property_decorators: property_decorators.into_iter().collect(),
106-
}
107-
}
108-
10997
pub fn convention(&self) -> Option<Convention> {
11098
self.convention
11199
}
@@ -117,6 +105,10 @@ impl Settings {
117105
pub fn property_decorators(&self) -> DecoratorIterator {
118106
DecoratorIterator::new(&self.property_decorators)
119107
}
108+
109+
pub fn ignore_var_parameters(&self) -> bool {
110+
self.ignore_var_parameters
111+
}
120112
}
121113

122114
impl fmt::Display for Settings {
@@ -127,7 +119,8 @@ impl fmt::Display for Settings {
127119
fields = [
128120
self.convention | optional,
129121
self.ignore_decorators | set,
130-
self.property_decorators | set
122+
self.property_decorators | set,
123+
self.ignore_var_parameters
131124
]
132125
}
133126
Ok(())

crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_google.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,12 @@ D417.py:155:5: D417 Missing argument description in the docstring for `select_da
7272
156 | query: str,
7373
157 | args: tuple,
7474
|
75+
76+
D417.py:172:5: D417 Missing argument description in the docstring for `f`: `**kwargs`
77+
|
78+
170 | """
79+
171 |
80+
172 | def f(x, *args, **kwargs):
81+
| ^ D417
82+
173 | """Do something.
83+
|
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
source: crates/ruff_linter/src/rules/pydocstyle/mod.rs
3+
snapshot_kind: text
4+
---
5+
D417.py:1:5: D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
6+
|
7+
1 | def f(x, y, z):
8+
| ^ D417
9+
2 | """Do something.
10+
|
11+
12+
D417.py:14:5: D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
13+
|
14+
14 | def f(x, y, z):
15+
| ^ D417
16+
15 | """Do something.
17+
|
18+
19+
D417.py:27:5: D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
20+
|
21+
27 | def f(x, y, z):
22+
| ^ D417
23+
28 | """Do something.
24+
|
25+
26+
D417.py:39:5: D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
27+
|
28+
39 | def f(x, y, z):
29+
| ^ D417
30+
40 | """Do something.
31+
|
32+
33+
D417.py:52:5: D417 Missing argument description in the docstring for `f`: `y`
34+
|
35+
52 | def f(x, y, z):
36+
| ^ D417
37+
53 | """Do something.
38+
|
39+
40+
D417.py:65:5: D417 Missing argument description in the docstring for `f`: `y`
41+
|
42+
65 | def f(x, y, z):
43+
| ^ D417
44+
66 | """Do something.
45+
|
46+
47+
D417.py:77:5: D417 Missing argument description in the docstring for `f`: `y`
48+
|
49+
77 | def f(x, y, z):
50+
| ^ D417
51+
78 | """Do something.
52+
|
53+
54+
D417.py:98:5: D417 Missing argument description in the docstring for `f`: `x`
55+
|
56+
98 | def f(x, *args, **kwargs):
57+
| ^ D417
58+
99 | """Do something.
59+
|
60+
61+
D417.py:155:5: D417 Missing argument description in the docstring for `select_data`: `auto_save`
62+
|
63+
155 | def select_data(
64+
| ^^^^^^^^^^^ D417
65+
156 | query: str,
66+
157 | args: tuple,
67+
|

crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_unspecified.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,12 @@ D417.py:155:5: D417 Missing argument description in the docstring for `select_da
7272
156 | query: str,
7373
157 | args: tuple,
7474
|
75+
76+
D417.py:172:5: D417 Missing argument description in the docstring for `f`: `**kwargs`
77+
|
78+
170 | """
79+
171 |
80+
172 | def f(x, *args, **kwargs):
81+
| ^ D417
82+
173 | """Do something.
83+
|

0 commit comments

Comments
 (0)