diff --git a/resources/test/fixtures/pydocstyle/D400.py b/resources/test/fixtures/pydocstyle/D400.py index aff12aac01b7f..38547d4addd9a 100644 --- a/resources/test/fixtures/pydocstyle/D400.py +++ b/resources/test/fixtures/pydocstyle/D400.py @@ -35,7 +35,6 @@ def f(): ... - def f(): r"Here's a line without a period" ... @@ -71,3 +70,24 @@ def f(): Here's a line without a period, but here's the next line with trailing space """ ... + + +def f(rounds: list[int], number: int) -> bool: + """ + :param rounds: list - rounds played. + :param number: int - round number. + :return: bool - was the round played? + """ + return number in rounds + + +def f(rounds: list[int], number: int) -> bool: + """ + Args: + rounds (list): rounds played. + number (int): round number. + + Returns: + bool: was the round played? + """ + return number in rounds diff --git a/src/docstrings/styles.rs b/src/docstrings/styles.rs index 5cdc5b9442123..7215d0b4ec4b1 100644 --- a/src/docstrings/styles.rs +++ b/src/docstrings/styles.rs @@ -5,21 +5,21 @@ use crate::docstrings::google::{GOOGLE_SECTION_NAMES, LOWERCASE_GOOGLE_SECTION_N use crate::docstrings::numpy::{LOWERCASE_NUMPY_SECTION_NAMES, NUMPY_SECTION_NAMES}; pub(crate) enum SectionStyle { - NumPy, + Numpy, Google, } impl SectionStyle { pub(crate) fn section_names(&self) -> &Lazy> { match self { - SectionStyle::NumPy => &NUMPY_SECTION_NAMES, + SectionStyle::Numpy => &NUMPY_SECTION_NAMES, SectionStyle::Google => &GOOGLE_SECTION_NAMES, } } pub(crate) fn lowercase_section_names(&self) -> &Lazy> { match self { - SectionStyle::NumPy => &LOWERCASE_NUMPY_SECTION_NAMES, + SectionStyle::Numpy => &LOWERCASE_NUMPY_SECTION_NAMES, SectionStyle::Google => &LOWERCASE_GOOGLE_SECTION_NAMES, } } diff --git a/src/pydocstyle/plugins.rs b/src/pydocstyle/plugins.rs index dc5ab5103865d..d5103cf893ccd 100644 --- a/src/pydocstyle/plugins.rs +++ b/src/pydocstyle/plugins.rs @@ -665,9 +665,35 @@ pub fn ends_with_period(checker: &mut Checker, docstring: &Docstring) { let contents = docstring.contents; let body = docstring.body; + if let Some(first_line) = body.trim().lines().next() { + let trimmed = first_line.trim(); + + // Avoid false-positives: `:param`, etc. + for prefix in [":param", ":type", ":raises", ":return", ":rtype"] { + if trimmed.starts_with(prefix) { + return; + } + } + + // Avoid false-positives: `Args:`, etc. + for style in [SectionStyle::Google, SectionStyle::Numpy] { + for section_name in style.section_names().iter() { + if let Some(suffix) = trimmed.strip_suffix(section_name) { + if suffix.is_empty() { + return; + } + if suffix == ":" { + return; + } + } + } + } + } + if let Some(index) = logical_line(body) { let line = body.lines().nth(index).unwrap(); let trimmed = line.trim_end(); + if !trimmed.ends_with('.') { let mut check = Check::new(CheckKind::EndsInPeriod, Range::from_located(docstring.expr)); @@ -712,7 +738,7 @@ pub fn no_signature(checker: &mut Checker, docstring: &Docstring) { let body = docstring.body; - let Some(first_line) = body.lines().next() else { + let Some(first_line) = body.trim().lines().next() else { return; }; if !first_line.contains(&format!("{name}(")) { @@ -785,6 +811,31 @@ pub fn ends_with_punctuation(checker: &mut Checker, docstring: &Docstring) { let contents = docstring.contents; let body = docstring.body; + if let Some(first_line) = body.trim().lines().next() { + let trimmed = first_line.trim(); + + // Avoid false-positives: `:param`, etc. + for prefix in [":param", ":type", ":raises", ":return", ":rtype"] { + if trimmed.starts_with(prefix) { + return; + } + } + + // Avoid false-positives: `Args:`, etc. + for style in [SectionStyle::Google, SectionStyle::Numpy] { + for section_name in style.section_names().iter() { + if let Some(suffix) = trimmed.strip_suffix(section_name) { + if suffix.is_empty() { + return; + } + if suffix == ":" { + return; + } + } + } + } + } + if let Some(index) = logical_line(body) { let line = body.lines().nth(index).unwrap(); let trimmed = line.trim_end(); @@ -869,14 +920,14 @@ pub fn sections(checker: &mut Checker, docstring: &Docstring, convention: Option } } Some(Convention::Numpy) => { - for context in §ion_contexts(&lines, &SectionStyle::NumPy) { + for context in §ion_contexts(&lines, &SectionStyle::Numpy) { numpy_section(checker, docstring, context); } } None => { // First, interpret as NumPy-style sections. let mut found_numpy_section = false; - for context in §ion_contexts(&lines, &SectionStyle::NumPy) { + for context in §ion_contexts(&lines, &SectionStyle::Numpy) { found_numpy_section = true; numpy_section(checker, docstring, context); } @@ -1418,7 +1469,7 @@ fn parameters_section(checker: &mut Checker, docstring: &Docstring, context: &Se } fn numpy_section(checker: &mut Checker, docstring: &Docstring, context: &SectionContext) { - common_section(checker, docstring, context, &SectionStyle::NumPy); + common_section(checker, docstring, context, &SectionStyle::Numpy); if checker.settings.enabled.contains(&CheckCode::D406) { let suffix = context diff --git a/src/pydocstyle/snapshots/ruff__pydocstyle__tests__D400_D400.py.snap b/src/pydocstyle/snapshots/ruff__pydocstyle__tests__D400_D400.py.snap index a44309892734f..36549d3996bce 100644 --- a/src/pydocstyle/snapshots/ruff__pydocstyle__tests__D400_D400.py.snap +++ b/src/pydocstyle/snapshots/ruff__pydocstyle__tests__D400_D400.py.snap @@ -100,98 +100,98 @@ expression: checks parent: ~ - kind: EndsInPeriod location: - row: 40 + row: 39 column: 4 end_location: - row: 40 + row: 39 column: 37 fix: content: "." location: - row: 40 + row: 39 column: 36 end_location: - row: 40 + row: 39 column: 36 parent: ~ - kind: EndsInPeriod location: - row: 45 + row: 44 column: 4 end_location: - row: 45 + row: 44 column: 41 fix: content: "." location: - row: 45 + row: 44 column: 38 end_location: - row: 45 + row: 44 column: 38 parent: ~ - kind: EndsInPeriod location: - row: 50 + row: 49 column: 4 end_location: - row: 53 + row: 52 column: 7 fix: content: "." location: - row: 52 + row: 51 column: 28 end_location: - row: 52 + row: 51 column: 28 parent: ~ - kind: EndsInPeriod location: - row: 58 + row: 57 column: 4 end_location: - row: 58 + row: 57 column: 41 fix: content: "." location: - row: 58 + row: 57 column: 38 end_location: - row: 58 + row: 57 column: 38 parent: ~ - kind: EndsInPeriod location: - row: 63 + row: 62 column: 4 end_location: - row: 65 + row: 64 column: 31 fix: content: "." location: - row: 65 + row: 64 column: 28 end_location: - row: 65 + row: 64 column: 28 parent: ~ - kind: EndsInPeriod location: - row: 70 + row: 69 column: 4 end_location: - row: 72 + row: 71 column: 52 fix: content: "." location: - row: 72 + row: 71 column: 48 end_location: - row: 72 + row: 71 column: 48 parent: ~