Skip to content

Commit f2c9f94

Browse files
Avoid some false positives for ends-in-period checks (#1521)
1 parent 605c606 commit f2c9f94

File tree

4 files changed

+103
-32
lines changed

4 files changed

+103
-32
lines changed

resources/test/fixtures/pydocstyle/D400.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ def f():
3535
...
3636

3737

38-
3938
def f():
4039
r"Here's a line without a period"
4140
...
@@ -71,3 +70,24 @@ def f():
7170
Here's a line without a period,
7271
but here's the next line with trailing space """
7372
...
73+
74+
75+
def f(rounds: list[int], number: int) -> bool:
76+
"""
77+
:param rounds: list - rounds played.
78+
:param number: int - round number.
79+
:return: bool - was the round played?
80+
"""
81+
return number in rounds
82+
83+
84+
def f(rounds: list[int], number: int) -> bool:
85+
"""
86+
Args:
87+
rounds (list): rounds played.
88+
number (int): round number.
89+
90+
Returns:
91+
bool: was the round played?
92+
"""
93+
return number in rounds

src/docstrings/styles.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@ use crate::docstrings::google::{GOOGLE_SECTION_NAMES, LOWERCASE_GOOGLE_SECTION_N
55
use crate::docstrings::numpy::{LOWERCASE_NUMPY_SECTION_NAMES, NUMPY_SECTION_NAMES};
66

77
pub(crate) enum SectionStyle {
8-
NumPy,
8+
Numpy,
99
Google,
1010
}
1111

1212
impl SectionStyle {
1313
pub(crate) fn section_names(&self) -> &Lazy<FxHashSet<&'static str>> {
1414
match self {
15-
SectionStyle::NumPy => &NUMPY_SECTION_NAMES,
15+
SectionStyle::Numpy => &NUMPY_SECTION_NAMES,
1616
SectionStyle::Google => &GOOGLE_SECTION_NAMES,
1717
}
1818
}
1919

2020
pub(crate) fn lowercase_section_names(&self) -> &Lazy<FxHashSet<&'static str>> {
2121
match self {
22-
SectionStyle::NumPy => &LOWERCASE_NUMPY_SECTION_NAMES,
22+
SectionStyle::Numpy => &LOWERCASE_NUMPY_SECTION_NAMES,
2323
SectionStyle::Google => &LOWERCASE_GOOGLE_SECTION_NAMES,
2424
}
2525
}

src/pydocstyle/plugins.rs

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -665,9 +665,35 @@ pub fn ends_with_period(checker: &mut Checker, docstring: &Docstring) {
665665
let contents = docstring.contents;
666666
let body = docstring.body;
667667

668+
if let Some(first_line) = body.trim().lines().next() {
669+
let trimmed = first_line.trim();
670+
671+
// Avoid false-positives: `:param`, etc.
672+
for prefix in [":param", ":type", ":raises", ":return", ":rtype"] {
673+
if trimmed.starts_with(prefix) {
674+
return;
675+
}
676+
}
677+
678+
// Avoid false-positives: `Args:`, etc.
679+
for style in [SectionStyle::Google, SectionStyle::Numpy] {
680+
for section_name in style.section_names().iter() {
681+
if let Some(suffix) = trimmed.strip_suffix(section_name) {
682+
if suffix.is_empty() {
683+
return;
684+
}
685+
if suffix == ":" {
686+
return;
687+
}
688+
}
689+
}
690+
}
691+
}
692+
668693
if let Some(index) = logical_line(body) {
669694
let line = body.lines().nth(index).unwrap();
670695
let trimmed = line.trim_end();
696+
671697
if !trimmed.ends_with('.') {
672698
let mut check =
673699
Check::new(CheckKind::EndsInPeriod, Range::from_located(docstring.expr));
@@ -712,7 +738,7 @@ pub fn no_signature(checker: &mut Checker, docstring: &Docstring) {
712738

713739
let body = docstring.body;
714740

715-
let Some(first_line) = body.lines().next() else {
741+
let Some(first_line) = body.trim().lines().next() else {
716742
return;
717743
};
718744
if !first_line.contains(&format!("{name}(")) {
@@ -785,6 +811,31 @@ pub fn ends_with_punctuation(checker: &mut Checker, docstring: &Docstring) {
785811
let contents = docstring.contents;
786812
let body = docstring.body;
787813

814+
if let Some(first_line) = body.trim().lines().next() {
815+
let trimmed = first_line.trim();
816+
817+
// Avoid false-positives: `:param`, etc.
818+
for prefix in [":param", ":type", ":raises", ":return", ":rtype"] {
819+
if trimmed.starts_with(prefix) {
820+
return;
821+
}
822+
}
823+
824+
// Avoid false-positives: `Args:`, etc.
825+
for style in [SectionStyle::Google, SectionStyle::Numpy] {
826+
for section_name in style.section_names().iter() {
827+
if let Some(suffix) = trimmed.strip_suffix(section_name) {
828+
if suffix.is_empty() {
829+
return;
830+
}
831+
if suffix == ":" {
832+
return;
833+
}
834+
}
835+
}
836+
}
837+
}
838+
788839
if let Some(index) = logical_line(body) {
789840
let line = body.lines().nth(index).unwrap();
790841
let trimmed = line.trim_end();
@@ -869,14 +920,14 @@ pub fn sections(checker: &mut Checker, docstring: &Docstring, convention: Option
869920
}
870921
}
871922
Some(Convention::Numpy) => {
872-
for context in &section_contexts(&lines, &SectionStyle::NumPy) {
923+
for context in &section_contexts(&lines, &SectionStyle::Numpy) {
873924
numpy_section(checker, docstring, context);
874925
}
875926
}
876927
None => {
877928
// First, interpret as NumPy-style sections.
878929
let mut found_numpy_section = false;
879-
for context in &section_contexts(&lines, &SectionStyle::NumPy) {
930+
for context in &section_contexts(&lines, &SectionStyle::Numpy) {
880931
found_numpy_section = true;
881932
numpy_section(checker, docstring, context);
882933
}
@@ -1418,7 +1469,7 @@ fn parameters_section(checker: &mut Checker, docstring: &Docstring, context: &Se
14181469
}
14191470

14201471
fn numpy_section(checker: &mut Checker, docstring: &Docstring, context: &SectionContext) {
1421-
common_section(checker, docstring, context, &SectionStyle::NumPy);
1472+
common_section(checker, docstring, context, &SectionStyle::Numpy);
14221473

14231474
if checker.settings.enabled.contains(&CheckCode::D406) {
14241475
let suffix = context

src/pydocstyle/snapshots/ruff__pydocstyle__tests__D400_D400.py.snap

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -100,98 +100,98 @@ expression: checks
100100
parent: ~
101101
- kind: EndsInPeriod
102102
location:
103-
row: 40
103+
row: 39
104104
column: 4
105105
end_location:
106-
row: 40
106+
row: 39
107107
column: 37
108108
fix:
109109
content: "."
110110
location:
111-
row: 40
111+
row: 39
112112
column: 36
113113
end_location:
114-
row: 40
114+
row: 39
115115
column: 36
116116
parent: ~
117117
- kind: EndsInPeriod
118118
location:
119-
row: 45
119+
row: 44
120120
column: 4
121121
end_location:
122-
row: 45
122+
row: 44
123123
column: 41
124124
fix:
125125
content: "."
126126
location:
127-
row: 45
127+
row: 44
128128
column: 38
129129
end_location:
130-
row: 45
130+
row: 44
131131
column: 38
132132
parent: ~
133133
- kind: EndsInPeriod
134134
location:
135-
row: 50
135+
row: 49
136136
column: 4
137137
end_location:
138-
row: 53
138+
row: 52
139139
column: 7
140140
fix:
141141
content: "."
142142
location:
143-
row: 52
143+
row: 51
144144
column: 28
145145
end_location:
146-
row: 52
146+
row: 51
147147
column: 28
148148
parent: ~
149149
- kind: EndsInPeriod
150150
location:
151-
row: 58
151+
row: 57
152152
column: 4
153153
end_location:
154-
row: 58
154+
row: 57
155155
column: 41
156156
fix:
157157
content: "."
158158
location:
159-
row: 58
159+
row: 57
160160
column: 38
161161
end_location:
162-
row: 58
162+
row: 57
163163
column: 38
164164
parent: ~
165165
- kind: EndsInPeriod
166166
location:
167-
row: 63
167+
row: 62
168168
column: 4
169169
end_location:
170-
row: 65
170+
row: 64
171171
column: 31
172172
fix:
173173
content: "."
174174
location:
175-
row: 65
175+
row: 64
176176
column: 28
177177
end_location:
178-
row: 65
178+
row: 64
179179
column: 28
180180
parent: ~
181181
- kind: EndsInPeriod
182182
location:
183-
row: 70
183+
row: 69
184184
column: 4
185185
end_location:
186-
row: 72
186+
row: 71
187187
column: 52
188188
fix:
189189
content: "."
190190
location:
191-
row: 72
191+
row: 71
192192
column: 48
193193
end_location:
194-
row: 72
194+
row: 71
195195
column: 48
196196
parent: ~
197197

0 commit comments

Comments
 (0)