mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 22:01:18 +00:00
[DOC201
] Permit explicit None
in functions that only return None
(#13064)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
This commit is contained in:
parent
e6d0c4a65d
commit
96b42b0c8f
5 changed files with 297 additions and 14 deletions
|
@ -119,3 +119,91 @@ class A(metaclass=abc.abcmeta):
|
|||
def f(self):
|
||||
"""Lorem ipsum."""
|
||||
return True
|
||||
|
||||
|
||||
# OK - implicit None early return
|
||||
def foo(obj: object) -> None:
|
||||
"""A very helpful docstring.
|
||||
|
||||
Args:
|
||||
obj (object): An object.
|
||||
"""
|
||||
if obj is None:
|
||||
return
|
||||
print(obj)
|
||||
|
||||
|
||||
# OK - explicit None early return
|
||||
def foo(obj: object) -> None:
|
||||
"""A very helpful docstring.
|
||||
|
||||
Args:
|
||||
obj (object): An object.
|
||||
"""
|
||||
if obj is None:
|
||||
return None
|
||||
print(obj)
|
||||
|
||||
|
||||
# OK - explicit None early return w/o useful type annotations
|
||||
def foo(obj):
|
||||
"""A very helpful docstring.
|
||||
|
||||
Args:
|
||||
obj (object): An object.
|
||||
"""
|
||||
if obj is None:
|
||||
return None
|
||||
print(obj)
|
||||
|
||||
|
||||
# OK - multiple explicit None early returns
|
||||
def foo(obj: object) -> None:
|
||||
"""A very helpful docstring.
|
||||
|
||||
Args:
|
||||
obj (object): An object.
|
||||
"""
|
||||
if obj is None:
|
||||
return None
|
||||
if obj == "None":
|
||||
return
|
||||
if obj == 0:
|
||||
return None
|
||||
print(obj)
|
||||
|
||||
|
||||
# DOC201 - non-early return explicit None
|
||||
def foo(x: int) -> int | None:
|
||||
"""A very helpful docstring.
|
||||
|
||||
Args:
|
||||
x (int): An interger.
|
||||
"""
|
||||
if x < 0:
|
||||
return None
|
||||
else:
|
||||
return x
|
||||
|
||||
|
||||
# DOC201 - non-early return explicit None w/o useful type annotations
|
||||
def foo(x):
|
||||
"""A very helpful docstring.
|
||||
|
||||
Args:
|
||||
x (int): An interger.
|
||||
"""
|
||||
if x < 0:
|
||||
return None
|
||||
else:
|
||||
return x
|
||||
|
||||
|
||||
# DOC201 - only returns None, but return annotation is not None
|
||||
def foo(s: str) -> str | None:
|
||||
"""A very helpful docstring.
|
||||
|
||||
Args:
|
||||
s (str): A string.
|
||||
"""
|
||||
return None
|
||||
|
|
|
@ -85,3 +85,105 @@ class A(metaclass=abc.abcmeta):
|
|||
def f(self):
|
||||
"""Lorem ipsum."""
|
||||
return True
|
||||
|
||||
|
||||
# OK - implicit None early return
|
||||
def foo(obj: object) -> None:
|
||||
"""A very helpful docstring.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
obj : object
|
||||
An object.
|
||||
"""
|
||||
if obj is None:
|
||||
return
|
||||
print(obj)
|
||||
|
||||
|
||||
# OK - explicit None early return
|
||||
def foo(obj: object) -> None:
|
||||
"""A very helpful docstring.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
obj : object
|
||||
An object.
|
||||
"""
|
||||
if obj is None:
|
||||
return None
|
||||
print(obj)
|
||||
|
||||
|
||||
# OK - explicit None early return w/o useful type annotations
|
||||
def foo(obj):
|
||||
"""A very helpful docstring.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
obj : object
|
||||
An object.
|
||||
"""
|
||||
if obj is None:
|
||||
return None
|
||||
print(obj)
|
||||
|
||||
|
||||
# OK - multiple explicit None early returns
|
||||
def foo(obj: object) -> None:
|
||||
"""A very helpful docstring.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
obj : object
|
||||
An object.
|
||||
"""
|
||||
if obj is None:
|
||||
return None
|
||||
if obj == "None":
|
||||
return
|
||||
if obj == 0:
|
||||
return None
|
||||
print(obj)
|
||||
|
||||
|
||||
# DOC201 - non-early return explicit None
|
||||
def foo(x: int) -> int | None:
|
||||
"""A very helpful docstring.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x : int
|
||||
An interger.
|
||||
"""
|
||||
if x < 0:
|
||||
return None
|
||||
else:
|
||||
return x
|
||||
|
||||
|
||||
# DOC201 - non-early return explicit None w/o useful type annotations
|
||||
def foo(x):
|
||||
"""A very helpful docstring.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x : int
|
||||
An interger.
|
||||
"""
|
||||
if x < 0:
|
||||
return None
|
||||
else:
|
||||
return x
|
||||
|
||||
|
||||
# DOC201 - only returns None, but return annotation is not None
|
||||
def foo(s: str) -> str | None:
|
||||
"""A very helpful docstring.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x : str
|
||||
A string.
|
||||
"""
|
||||
return None
|
||||
|
|
|
@ -25,7 +25,8 @@ use crate::rules::pydocstyle::settings::Convention;
|
|||
/// Docstrings missing return sections are a sign of incomplete documentation
|
||||
/// or refactors.
|
||||
///
|
||||
/// This rule is not enforced for abstract methods and stubs functions.
|
||||
/// This rule is not enforced for abstract methods, stubs functions, or
|
||||
/// functions that only return `None`.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
|
@ -494,13 +495,26 @@ fn parse_entries_numpy(content: &str) -> Vec<QualifiedName> {
|
|||
entries
|
||||
}
|
||||
|
||||
/// An individual documentable statement in a function body.
|
||||
/// An individual `yield` expression in a function body.
|
||||
#[derive(Debug)]
|
||||
struct Entry {
|
||||
struct YieldEntry {
|
||||
range: TextRange,
|
||||
}
|
||||
|
||||
impl Ranged for Entry {
|
||||
impl Ranged for YieldEntry {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range
|
||||
}
|
||||
}
|
||||
|
||||
/// An individual `return` statement in a function body.
|
||||
#[derive(Debug)]
|
||||
struct ReturnEntry {
|
||||
range: TextRange,
|
||||
is_none_return: bool,
|
||||
}
|
||||
|
||||
impl Ranged for ReturnEntry {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range
|
||||
}
|
||||
|
@ -522,15 +536,15 @@ impl Ranged for ExceptionEntry<'_> {
|
|||
/// A summary of documentable statements from the function body
|
||||
#[derive(Debug)]
|
||||
struct BodyEntries<'a> {
|
||||
returns: Vec<Entry>,
|
||||
yields: Vec<Entry>,
|
||||
returns: Vec<ReturnEntry>,
|
||||
yields: Vec<YieldEntry>,
|
||||
raised_exceptions: Vec<ExceptionEntry<'a>>,
|
||||
}
|
||||
|
||||
/// An AST visitor to extract a summary of documentable statements from a function body.
|
||||
struct BodyVisitor<'a> {
|
||||
returns: Vec<Entry>,
|
||||
yields: Vec<Entry>,
|
||||
returns: Vec<ReturnEntry>,
|
||||
yields: Vec<YieldEntry>,
|
||||
currently_suspended_exceptions: Option<&'a ast::Expr>,
|
||||
raised_exceptions: Vec<ExceptionEntry<'a>>,
|
||||
semantic: &'a SemanticModel<'a>,
|
||||
|
@ -623,9 +637,12 @@ impl<'a> Visitor<'a> for BodyVisitor<'a> {
|
|||
}
|
||||
Stmt::Return(ast::StmtReturn {
|
||||
range,
|
||||
value: Some(_),
|
||||
value: Some(value),
|
||||
}) => {
|
||||
self.returns.push(Entry { range: *range });
|
||||
self.returns.push(ReturnEntry {
|
||||
range: *range,
|
||||
is_none_return: value.is_none_literal_expr(),
|
||||
});
|
||||
}
|
||||
Stmt::FunctionDef(_) | Stmt::ClassDef(_) => return,
|
||||
_ => {}
|
||||
|
@ -640,10 +657,10 @@ impl<'a> Visitor<'a> for BodyVisitor<'a> {
|
|||
range,
|
||||
value: Some(_),
|
||||
}) => {
|
||||
self.yields.push(Entry { range: *range });
|
||||
self.yields.push(YieldEntry { range: *range });
|
||||
}
|
||||
Expr::YieldFrom(ast::ExprYieldFrom { range, .. }) => {
|
||||
self.yields.push(Entry { range: *range });
|
||||
self.yields.push(YieldEntry { range: *range });
|
||||
}
|
||||
Expr::Lambda(_) => return,
|
||||
_ => {}
|
||||
|
@ -737,8 +754,22 @@ pub(crate) fn check_docstring(
|
|||
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() {
|
||||
let diagnostic = Diagnostic::new(DocstringMissingReturns, body_return.range());
|
||||
diagnostics.push(diagnostic);
|
||||
match function_def.returns.as_deref() {
|
||||
Some(returns) if !Expr::is_none_literal_expr(returns) => diagnostics.push(
|
||||
Diagnostic::new(DocstringMissingReturns, body_return.range()),
|
||||
),
|
||||
None if body_entries
|
||||
.returns
|
||||
.iter()
|
||||
.any(|entry| !entry.is_none_return) =>
|
||||
{
|
||||
diagnostics.push(Diagnostic::new(
|
||||
DocstringMissingReturns,
|
||||
body_return.range(),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,3 +38,34 @@ DOC201_google.py:121:9: DOC201 `return` is not documented in docstring
|
|||
| ^^^^^^^^^^^ DOC201
|
||||
|
|
||||
= help: Add a "Returns" section to the docstring
|
||||
|
||||
DOC201_google.py:184:9: DOC201 `return` is not documented in docstring
|
||||
|
|
||||
182 | """
|
||||
183 | if x < 0:
|
||||
184 | return None
|
||||
| ^^^^^^^^^^^ DOC201
|
||||
185 | else:
|
||||
186 | return x
|
||||
|
|
||||
= help: Add a "Returns" section to the docstring
|
||||
|
||||
DOC201_google.py:197:9: DOC201 `return` is not documented in docstring
|
||||
|
|
||||
195 | """
|
||||
196 | if x < 0:
|
||||
197 | return None
|
||||
| ^^^^^^^^^^^ DOC201
|
||||
198 | else:
|
||||
199 | return x
|
||||
|
|
||||
= help: Add a "Returns" section to the docstring
|
||||
|
||||
DOC201_google.py:209:5: DOC201 `return` is not documented in docstring
|
||||
|
|
||||
207 | s (str): A string.
|
||||
208 | """
|
||||
209 | return None
|
||||
| ^^^^^^^^^^^ DOC201
|
||||
|
|
||||
= help: Add a "Returns" section to the docstring
|
||||
|
|
|
@ -27,3 +27,34 @@ DOC201_numpy.py:87:9: DOC201 `return` is not documented in docstring
|
|||
| ^^^^^^^^^^^ DOC201
|
||||
|
|
||||
= help: Add a "Returns" section to the docstring
|
||||
|
||||
DOC201_numpy.py:160:9: DOC201 `return` is not documented in docstring
|
||||
|
|
||||
158 | """
|
||||
159 | if x < 0:
|
||||
160 | return None
|
||||
| ^^^^^^^^^^^ DOC201
|
||||
161 | else:
|
||||
162 | return x
|
||||
|
|
||||
= help: Add a "Returns" section to the docstring
|
||||
|
||||
DOC201_numpy.py:175:9: DOC201 `return` is not documented in docstring
|
||||
|
|
||||
173 | """
|
||||
174 | if x < 0:
|
||||
175 | return None
|
||||
| ^^^^^^^^^^^ DOC201
|
||||
176 | else:
|
||||
177 | return x
|
||||
|
|
||||
= help: Add a "Returns" section to the docstring
|
||||
|
||||
DOC201_numpy.py:189:5: DOC201 `return` is not documented in docstring
|
||||
|
|
||||
187 | A string.
|
||||
188 | """
|
||||
189 | return None
|
||||
| ^^^^^^^^^^^ DOC201
|
||||
|
|
||||
= help: Add a "Returns" section to the docstring
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue