Skip to content

Commit

Permalink
[pydoclint] Consider DOC201 satisfied if docstring begins with "R…
Browse files Browse the repository at this point in the history
…eturns" (astral-sh#12675)

<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

Resolves astral-sh#12636

Consider docstrings which begin with the word "Returns" as having
satisfactorily documented they're returns. For example
```python
def f():
    """Returns 1."""
    return 1
```
is valid.

## Test Plan

Added example to test fixture.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
  • Loading branch information
2 people authored and dylwil3 committed Aug 7, 2024
1 parent 1c089d8 commit 31c8077
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,25 @@ def baz(self) -> str:
num (int): A number
"""
return 'test'


# OK
def f():
"""Returns 1."""
return 1


# OK
def f():
"""Return 1."""
return 1


# OK
def f(num: int):
"""Returns 1.
Args:
num (int): A number
"""
return 1
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,24 @@ def test():
"""Do something."""
yield from range(10)


# OK
def f():
"""Yields 1."""
yield 1


# OK
def f():
"""Yield 1."""
yield 1


# OK
def f(num: int):
"""Yields 1.
Args:
num (int): A number
"""
yield 1
1 change: 1 addition & 0 deletions crates/ruff_linter/src/checkers/ast/analyze/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
pydoclint::rules::check_docstring(
checker,
definition,
&docstring,
&section_contexts,
checker.settings.pydocstyle.convention(),
);
Expand Down
40 changes: 37 additions & 3 deletions crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
use crate::docstrings::sections::{SectionContext, SectionContexts, SectionKind};
use crate::docstrings::styles::SectionStyle;
use crate::docstrings::Docstring;
use crate::registry::Rule;
use crate::rules::pydocstyle::settings::Convention;

Expand Down Expand Up @@ -649,10 +650,43 @@ fn is_exception_or_base_exception(qualified_name: &QualifiedName) -> bool {
)
}

fn starts_with_returns(docstring: &Docstring) -> bool {
if let Some(first_word) = docstring.body().as_str().split(' ').next() {
return matches!(first_word, "Return" | "Returns");
}
false
}

fn returns_documented(
docstring: &Docstring,
docstring_sections: &DocstringSections,
convention: Option<Convention>,
) -> bool {
docstring_sections.returns.is_some()
|| (matches!(convention, Some(Convention::Google)) && starts_with_returns(docstring))
}

fn starts_with_yields(docstring: &Docstring) -> bool {
if let Some(first_word) = docstring.body().as_str().split(' ').next() {
return matches!(first_word, "Yield" | "Yields");
}
false
}

fn yields_documented(
docstring: &Docstring,
docstring_sections: &DocstringSections,
convention: Option<Convention>,
) -> bool {
docstring_sections.yields.is_some()
|| (matches!(convention, Some(Convention::Google)) && starts_with_yields(docstring))
}

/// DOC201, DOC202, DOC402, DOC403, DOC501, DOC502
pub(crate) fn check_docstring(
checker: &mut Checker,
definition: &Definition,
docstring: &Docstring,
section_contexts: &SectionContexts,
convention: Option<Convention>,
) {
Expand Down Expand Up @@ -687,7 +721,7 @@ pub(crate) fn check_docstring(

// DOC201
if checker.enabled(Rule::DocstringMissingReturns) {
if docstring_sections.returns.is_none() {
if !returns_documented(docstring, &docstring_sections, convention) {
let extra_property_decorators = checker.settings.pydocstyle.property_decorators();
if !definition.is_property(extra_property_decorators, checker.semantic()) {
if let Some(body_return) = body_entries.returns.first() {
Expand All @@ -700,7 +734,7 @@ pub(crate) fn check_docstring(

// DOC202
if checker.enabled(Rule::DocstringExtraneousReturns) {
if let Some(docstring_returns) = docstring_sections.returns {
if let Some(ref docstring_returns) = docstring_sections.returns {
if body_entries.returns.is_empty() {
let diagnostic =
Diagnostic::new(DocstringExtraneousReturns, docstring_returns.range());
Expand All @@ -711,7 +745,7 @@ pub(crate) fn check_docstring(

// DOC402
if checker.enabled(Rule::DocstringMissingYields) {
if docstring_sections.yields.is_none() {
if !yields_documented(docstring, &docstring_sections, convention) {
if let Some(body_yield) = body_entries.yields.first() {
let diagnostic = Diagnostic::new(DocstringMissingYields, body_yield.range());
diagnostics.push(diagnostic);
Expand Down

0 comments on commit 31c8077

Please sign in to comment.