mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:35 +00:00
No newline after function docstrings (#8375)
Fixup for #8216 to not apply to function docstrings. Main before #8216: | project | similarity index | total files | changed files | |----------------|------------------:|------------------:|------------------:| | cpython | 0.75804 | 1799 | 1648 | | django | 0.99984 | 2772 | 33 | | home-assistant | 0.99963 | 10596 | 148 | | poetry | 0.99925 | 317 | 12 | | transformers | 0.99967 | 2657 | 328 | | twine | 1.00000 | 33 | 0 | | typeshed | 0.99980 | 3669 | 18 | | warehouse | 0.99977 | 654 | 13 | | zulip | 0.99970 | 1459 | 22 | main now: | project | similarity index | total files | changed files | |----------------|------------------:|------------------:|------------------:| | cpython | 0.75804 | 1799 | 1648 | | django | 0.99984 | 2772 | 48 | | home-assistant | 0.99963 | 10596 | 181 | | poetry | 0.99925 | 317 | 12 | | transformers | 0.99967 | 2657 | 339 | | twine | 1.00000 | 33 | 0 | | typeshed | 0.99980 | 3669 | 18 | | warehouse | 0.99977 | 654 | 13 | | zulip | 0.99970 | 1459 | 23 | PR: | project | similarity index | total files | changed files | |----------------|------------------:|------------------:|------------------:| | cpython | 0.75804 | 1799 | 1648 | | django | 0.99984 | 2772 | 33 | | home-assistant | 0.99963 | 10596 | 148 | | poetry | 0.99925 | 317 | 12 | | transformers | 0.99967 | 2657 | 328 | | twine | 1.00000 | 33 | 0 | | typeshed | 0.99980 | 3669 | 18 | | warehouse | 0.99977 | 654 | 13 | | zulip | 0.99970 | 1459 | 22 |
This commit is contained in:
parent
23ed4e9616
commit
3076d76b0a
4 changed files with 82 additions and 34 deletions
|
@ -136,6 +136,13 @@ class CommentAfterDocstring5:
|
||||||
# This class is also the base class for pathbrowser.PathBrowser.
|
# This class is also the base class for pathbrowser.PathBrowser.
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
"""Browse module classes and functions in IDLE."""
|
||||||
|
# ^ Do not insert a newline above here
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TabbedIndent:
|
class TabbedIndent:
|
||||||
def tabbed_indent(self):
|
def tabbed_indent(self):
|
||||||
"""check for correct tabbed formatting
|
"""check for correct tabbed formatting
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
||||||
}
|
}
|
||||||
|
|
||||||
SuiteKind::Function => {
|
SuiteKind::Function => {
|
||||||
if let Some(docstring) = DocstringStmt::try_from_statement(first) {
|
if let Some(docstring) = DocstringStmt::try_from_statement(first, self.kind) {
|
||||||
SuiteChildStatement::Docstring(docstring)
|
SuiteChildStatement::Docstring(docstring)
|
||||||
} else {
|
} else {
|
||||||
SuiteChildStatement::Other(first)
|
SuiteChildStatement::Other(first)
|
||||||
|
@ -97,7 +97,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
||||||
}
|
}
|
||||||
|
|
||||||
SuiteKind::Class => {
|
SuiteKind::Class => {
|
||||||
if let Some(docstring) = DocstringStmt::try_from_statement(first) {
|
if let Some(docstring) = DocstringStmt::try_from_statement(first, self.kind) {
|
||||||
if !comments.has_leading(first)
|
if !comments.has_leading(first)
|
||||||
&& lines_before(first.start(), source) > 1
|
&& lines_before(first.start(), source) > 1
|
||||||
&& !source_type.is_stub()
|
&& !source_type.is_stub()
|
||||||
|
@ -150,7 +150,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
||||||
true
|
true
|
||||||
} else if f.options().preview().is_enabled()
|
} else if f.options().preview().is_enabled()
|
||||||
&& self.kind == SuiteKind::TopLevel
|
&& self.kind == SuiteKind::TopLevel
|
||||||
&& DocstringStmt::try_from_statement(first.statement()).is_some()
|
&& DocstringStmt::try_from_statement(first.statement(), self.kind).is_some()
|
||||||
{
|
{
|
||||||
// Only in preview mode, insert a newline after a module level docstring, but treat
|
// Only in preview mode, insert a newline after a module level docstring, but treat
|
||||||
// it as a docstring otherwise. See: https://github.com/psf/black/pull/3932.
|
// it as a docstring otherwise. See: https://github.com/psf/black/pull/3932.
|
||||||
|
@ -543,17 +543,25 @@ impl<'ast> IntoFormat<PyFormatContext<'ast>> for Suite {
|
||||||
|
|
||||||
/// A statement representing a docstring.
|
/// A statement representing a docstring.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub(crate) struct DocstringStmt<'a>(&'a Stmt);
|
pub(crate) struct DocstringStmt<'a> {
|
||||||
|
/// The [`Stmt::Expr`]
|
||||||
|
docstring: &'a Stmt,
|
||||||
|
/// The parent suite kind
|
||||||
|
suite_kind: SuiteKind,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> DocstringStmt<'a> {
|
impl<'a> DocstringStmt<'a> {
|
||||||
/// Checks if the statement is a simple string that can be formatted as a docstring
|
/// Checks if the statement is a simple string that can be formatted as a docstring
|
||||||
fn try_from_statement(stmt: &'a Stmt) -> Option<DocstringStmt<'a>> {
|
fn try_from_statement(stmt: &'a Stmt, suite_kind: SuiteKind) -> Option<DocstringStmt<'a>> {
|
||||||
let Stmt::Expr(ast::StmtExpr { value, .. }) = stmt else {
|
let Stmt::Expr(ast::StmtExpr { value, .. }) = stmt else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
match value.as_ref() {
|
match value.as_ref() {
|
||||||
Expr::StringLiteral(value) if !value.implicit_concatenated => Some(DocstringStmt(stmt)),
|
Expr::StringLiteral(value) if !value.implicit_concatenated => Some(DocstringStmt {
|
||||||
|
docstring: stmt,
|
||||||
|
suite_kind,
|
||||||
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -562,14 +570,14 @@ impl<'a> DocstringStmt<'a> {
|
||||||
impl Format<PyFormatContext<'_>> for DocstringStmt<'_> {
|
impl Format<PyFormatContext<'_>> for DocstringStmt<'_> {
|
||||||
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
||||||
let comments = f.context().comments().clone();
|
let comments = f.context().comments().clone();
|
||||||
let node_comments = comments.leading_dangling_trailing(self.0);
|
let node_comments = comments.leading_dangling_trailing(self.docstring);
|
||||||
|
|
||||||
if FormatStmtExpr.is_suppressed(node_comments.trailing, f.context()) {
|
if FormatStmtExpr.is_suppressed(node_comments.trailing, f.context()) {
|
||||||
suppressed_node(self.0).fmt(f)
|
suppressed_node(self.docstring).fmt(f)
|
||||||
} else {
|
} else {
|
||||||
// SAFETY: Safe because `DocStringStmt` guarantees that it only ever wraps a `ExprStmt` containing a `ExprStringLiteral`.
|
// SAFETY: Safe because `DocStringStmt` guarantees that it only ever wraps a `ExprStmt` containing a `ExprStringLiteral`.
|
||||||
let string_literal = self
|
let string_literal = self
|
||||||
.0
|
.docstring
|
||||||
.as_expr_stmt()
|
.as_expr_stmt()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.value
|
.value
|
||||||
|
@ -587,8 +595,9 @@ impl Format<PyFormatContext<'_>> for DocstringStmt<'_> {
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Comments after docstrings need a newline between the docstring and the comment.
|
if self.suite_kind == SuiteKind::Class {
|
||||||
// (https://github.com/astral-sh/ruff/issues/7948)
|
// Comments after class docstrings need a newline between the docstring and the
|
||||||
|
// comment (https://github.com/astral-sh/ruff/issues/7948).
|
||||||
// ```python
|
// ```python
|
||||||
// class ModuleBrowser:
|
// class ModuleBrowser:
|
||||||
// """Browse module classes and functions in IDLE."""
|
// """Browse module classes and functions in IDLE."""
|
||||||
|
@ -606,6 +615,7 @@ impl Format<PyFormatContext<'_>> for DocstringStmt<'_> {
|
||||||
empty_line().fmt(f)?;
|
empty_line().fmt(f)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
trailing_comments(node_comments.trailing).fmt(f)
|
trailing_comments(node_comments.trailing).fmt(f)
|
||||||
}
|
}
|
||||||
|
@ -625,7 +635,7 @@ pub(crate) enum SuiteChildStatement<'a> {
|
||||||
impl<'a> SuiteChildStatement<'a> {
|
impl<'a> SuiteChildStatement<'a> {
|
||||||
pub(crate) const fn statement(self) -> &'a Stmt {
|
pub(crate) const fn statement(self) -> &'a Stmt {
|
||||||
match self {
|
match self {
|
||||||
SuiteChildStatement::Docstring(docstring) => docstring.0,
|
SuiteChildStatement::Docstring(docstring) => docstring.docstring,
|
||||||
SuiteChildStatement::Other(statement) => statement,
|
SuiteChildStatement::Other(statement) => statement,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,11 +225,8 @@ d={'a':1,
|
||||||
# fmt: on
|
# fmt: on
|
||||||
goes + here,
|
goes + here,
|
||||||
andhere,
|
andhere,
|
||||||
@@ -120,10 +121,13 @@
|
@@ -122,8 +123,10 @@
|
||||||
|
|
||||||
The comments between will be formatted. This is a known limitation.
|
|
||||||
"""
|
"""
|
||||||
+
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
|
|
||||||
- # hey, that won't work
|
- # hey, that won't work
|
||||||
|
@ -240,7 +237,7 @@ d={'a':1,
|
||||||
# fmt: on
|
# fmt: on
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -138,7 +142,7 @@
|
@@ -138,7 +141,7 @@
|
||||||
now . considers . multiple . fmt . directives . within . one . prefix
|
now . considers . multiple . fmt . directives . within . one . prefix
|
||||||
# fmt: on
|
# fmt: on
|
||||||
# fmt: off
|
# fmt: off
|
||||||
|
@ -249,7 +246,7 @@ d={'a':1,
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
|
|
||||||
@@ -178,14 +182,18 @@
|
@@ -178,14 +181,18 @@
|
||||||
$
|
$
|
||||||
""",
|
""",
|
||||||
# fmt: off
|
# fmt: off
|
||||||
|
@ -398,7 +395,6 @@ def off_and_on_without_data():
|
||||||
|
|
||||||
The comments between will be formatted. This is a known limitation.
|
The comments between will be formatted. This is a known limitation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -142,6 +142,13 @@ class CommentAfterDocstring5:
|
||||||
# This class is also the base class for pathbrowser.PathBrowser.
|
# This class is also the base class for pathbrowser.PathBrowser.
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
"""Browse module classes and functions in IDLE."""
|
||||||
|
# ^ Do not insert a newline above here
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TabbedIndent:
|
class TabbedIndent:
|
||||||
def tabbed_indent(self):
|
def tabbed_indent(self):
|
||||||
"""check for correct tabbed formatting
|
"""check for correct tabbed formatting
|
||||||
|
@ -301,6 +308,13 @@ class CommentAfterDocstring5:
|
||||||
# This class is also the base class for pathbrowser.PathBrowser.
|
# This class is also the base class for pathbrowser.PathBrowser.
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
"""Browse module classes and functions in IDLE."""
|
||||||
|
# ^ Do not insert a newline above here
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TabbedIndent:
|
class TabbedIndent:
|
||||||
def tabbed_indent(self):
|
def tabbed_indent(self):
|
||||||
"""check for correct tabbed formatting
|
"""check for correct tabbed formatting
|
||||||
|
@ -460,6 +474,13 @@ class CommentAfterDocstring5:
|
||||||
# This class is also the base class for pathbrowser.PathBrowser.
|
# This class is also the base class for pathbrowser.PathBrowser.
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
"""Browse module classes and functions in IDLE."""
|
||||||
|
# ^ Do not insert a newline above here
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TabbedIndent:
|
class TabbedIndent:
|
||||||
def tabbed_indent(self):
|
def tabbed_indent(self):
|
||||||
"""check for correct tabbed formatting
|
"""check for correct tabbed formatting
|
||||||
|
@ -619,6 +640,13 @@ class CommentAfterDocstring5:
|
||||||
# This class is also the base class for pathbrowser.PathBrowser.
|
# This class is also the base class for pathbrowser.PathBrowser.
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
"""Browse module classes and functions in IDLE."""
|
||||||
|
# ^ Do not insert a newline above here
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TabbedIndent:
|
class TabbedIndent:
|
||||||
def tabbed_indent(self):
|
def tabbed_indent(self):
|
||||||
"""check for correct tabbed formatting
|
"""check for correct tabbed formatting
|
||||||
|
@ -778,6 +806,13 @@ class CommentAfterDocstring5:
|
||||||
# This class is also the base class for pathbrowser.PathBrowser.
|
# This class is also the base class for pathbrowser.PathBrowser.
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
"""Browse module classes and functions in IDLE."""
|
||||||
|
# ^ Do not insert a newline above here
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TabbedIndent:
|
class TabbedIndent:
|
||||||
def tabbed_indent(self):
|
def tabbed_indent(self):
|
||||||
"""check for correct tabbed formatting
|
"""check for correct tabbed formatting
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue