diff --git a/README.md b/README.md index 65508d6a2c..7bbb3902e4 100644 --- a/README.md +++ b/README.md @@ -632,7 +632,7 @@ For more, see [pydocstyle](https://pypi.org/project/pydocstyle/6.1.1/) on PyPI. | D202 | NoBlankLineAfterFunction | No blank lines allowed after function docstring (found 1) | 🛠 | | D203 | OneBlankLineBeforeClass | 1 blank line required before class docstring | 🛠 | | D204 | OneBlankLineAfterClass | 1 blank line required after class docstring | 🛠 | -| D205 | BlankLineAfterSummary | 1 blank line required between summary line and description | 🛠 | +| D205 | BlankLineAfterSummary | 1 blank line required between summary line and description (found 2) | 🛠 | | D206 | IndentWithSpaces | Docstring should be indented with spaces, not tabs | | | D207 | NoUnderIndentation | Docstring is under-indented | 🛠 | | D208 | NoOverIndentation | Docstring is over-indented | 🛠 | diff --git a/src/pydocstyle/plugins.rs b/src/pydocstyle/plugins.rs index 285209d5e7..c328304a53 100644 --- a/src/pydocstyle/plugins.rs +++ b/src/pydocstyle/plugins.rs @@ -336,21 +336,21 @@ pub fn blank_after_summary(checker: &mut Checker, docstring: &Docstring) { } if lines_count > 1 && blanks_count != 1 { let mut check = Check::new( - CheckKind::BlankLineAfterSummary, + CheckKind::BlankLineAfterSummary(blanks_count), Range::from_located(docstring.expr), ); if checker.patch(check.kind.code()) { - // Find the "summary" line (defined as the first non-blank line). - let mut summary_line = 0; - for line in body.lines() { - if line.trim().is_empty() { - summary_line += 1; - } else { - break; - } - } - if blanks_count > 1 { + // Find the "summary" line (defined as the first non-blank line). + let mut summary_line = 0; + for line in body.lines() { + if line.trim().is_empty() { + summary_line += 1; + } else { + break; + } + } + // Insert one blank line after the summary (replacing any existing lines). check.amend(Fix::replacement( "\n".to_string(), diff --git a/src/pydocstyle/snapshots/ruff__pydocstyle__tests__D205_D.py.snap b/src/pydocstyle/snapshots/ruff__pydocstyle__tests__D205_D.py.snap index 579aefe7d6..47e90ed3ea 100644 --- a/src/pydocstyle/snapshots/ruff__pydocstyle__tests__D205_D.py.snap +++ b/src/pydocstyle/snapshots/ruff__pydocstyle__tests__D205_D.py.snap @@ -2,7 +2,8 @@ source: src/pydocstyle/mod.rs expression: checks --- -- kind: BlankLineAfterSummary +- kind: + BlankLineAfterSummary: 0 location: row: 200 column: 4 @@ -11,7 +12,8 @@ expression: checks column: 7 fix: ~ parent: ~ -- kind: BlankLineAfterSummary +- kind: + BlankLineAfterSummary: 2 location: row: 210 column: 4 diff --git a/src/registry.rs b/src/registry.rs index 1c43d18c8c..c5acf4ec61 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -1005,7 +1005,7 @@ pub enum CheckKind { // pydocstyle BlankLineAfterLastSection(String), BlankLineAfterSection(String), - BlankLineAfterSummary, + BlankLineAfterSummary(usize), BlankLineBeforeSection(String), CapitalizeSectionName(String), DashedUnderlineAfterSection(String), @@ -1465,7 +1465,7 @@ impl CheckCode { CheckCode::D202 => CheckKind::NoBlankLineAfterFunction(1), CheckCode::D203 => CheckKind::OneBlankLineBeforeClass(0), CheckCode::D204 => CheckKind::OneBlankLineAfterClass(0), - CheckCode::D205 => CheckKind::BlankLineAfterSummary, + CheckCode::D205 => CheckKind::BlankLineAfterSummary(2), CheckCode::D206 => CheckKind::IndentWithSpaces, CheckCode::D207 => CheckKind::NoUnderIndentation, CheckCode::D208 => CheckKind::NoOverIndentation, @@ -2268,7 +2268,7 @@ impl CheckKind { CheckKind::NewLineAfterLastParagraph => &CheckCode::D209, CheckKind::NewLineAfterSectionName(..) => &CheckCode::D406, CheckKind::NoBlankLineAfterFunction(..) => &CheckCode::D202, - CheckKind::BlankLineAfterSummary => &CheckCode::D205, + CheckKind::BlankLineAfterSummary(..) => &CheckCode::D205, CheckKind::NoBlankLineBeforeClass(..) => &CheckCode::D211, CheckKind::NoBlankLineBeforeFunction(..) => &CheckCode::D201, CheckKind::NoBlankLinesBetweenHeaderAndContent(..) => &CheckCode::D412, @@ -3093,8 +3093,15 @@ impl CheckKind { } // pydocstyle CheckKind::FitsOnOneLine => "One-line docstring should fit on one line".to_string(), - CheckKind::BlankLineAfterSummary => { - "1 blank line required between summary line and description".to_string() + CheckKind::BlankLineAfterSummary(num_lines) => { + if *num_lines == 0 { + "1 blank line required between summary line and description".to_string() + } else { + format!( + "1 blank line required between summary line and description (found \ + {num_lines})" + ) + } } CheckKind::NewLineAfterLastParagraph => { "Multi-line docstring closing quotes should be on a separate line".to_string() @@ -3624,125 +3631,128 @@ impl CheckKind { /// Whether the check kind is (potentially) fixable. pub fn fixable(&self) -> bool { - matches!( - self, + match self { + // Always-fixable checks. CheckKind::AAndNotA(..) - | CheckKind::AOrNotA(..) - | CheckKind::AmbiguousUnicodeCharacterComment(..) - | CheckKind::AmbiguousUnicodeCharacterDocstring(..) - | CheckKind::AmbiguousUnicodeCharacterString(..) - | CheckKind::AndFalse - | CheckKind::BlankLineAfterLastSection(..) - | CheckKind::BlankLineAfterSection(..) - | CheckKind::BlankLineAfterSummary - | CheckKind::BlankLineBeforeSection(..) - | CheckKind::CapitalizeSectionName(..) - | CheckKind::CommentedOutCode - | CheckKind::ConvertLoopToAll(..) - | CheckKind::ConvertLoopToAny(..) - | CheckKind::ConvertNamedTupleFunctionalToClass(..) - | CheckKind::ConvertTypedDictFunctionalToClass(..) - | CheckKind::DashedUnderlineAfterSection(..) - | CheckKind::DatetimeTimezoneUTC - | CheckKind::DeprecatedUnittestAlias(..) - | CheckKind::DoNotAssertFalse - | CheckKind::DoNotAssignLambda(..) - | CheckKind::DupeClassFieldDefinitions(..) - | CheckKind::DuplicateHandlerException(..) - | CheckKind::DuplicateIsinstanceCall(..) - | CheckKind::EndsInPeriod - | CheckKind::EndsInPunctuation - | CheckKind::FStringMissingPlaceholders - | CheckKind::GetAttrWithConstant - | CheckKind::ImplicitReturn - | CheckKind::ImplicitReturnValue - | CheckKind::IncorrectFixtureParenthesesStyle(..) - | CheckKind::IncorrectMarkParenthesesStyle(..) - | CheckKind::InvalidEscapeSequence(..) - | CheckKind::IsLiteral(..) - | CheckKind::KeyInDict(..) - | CheckKind::MisplacedComparisonConstant(..) - | CheckKind::MissingReturnTypeSpecialMethod(..) - | CheckKind::NativeLiterals(..) - | CheckKind::NewLineAfterLastParagraph - | CheckKind::NewLineAfterSectionName(..) - | CheckKind::NoBlankLineAfterFunction(..) - | CheckKind::NoBlankLineBeforeClass(..) - | CheckKind::NoBlankLineBeforeFunction(..) - | CheckKind::NoBlankLinesBetweenHeaderAndContent(..) - | CheckKind::NoNewLineAtEndOfFile - | CheckKind::NoOverIndentation - | CheckKind::NoSurroundingWhitespace - | CheckKind::NoUnderIndentation - | CheckKind::NoUnnecessaryPass - | CheckKind::NoneComparison(..) - | CheckKind::NotInTest - | CheckKind::NotIsTest - | CheckKind::OSErrorAlias(..) - | CheckKind::OneBlankLineAfterClass(..) - | CheckKind::OneBlankLineBeforeClass(..) - | CheckKind::OpenAlias - | CheckKind::OrTrue - | CheckKind::PEP3120UnnecessaryCodingComment - | CheckKind::PPrintFound - | CheckKind::ParametrizeNamesWrongType(..) - | CheckKind::PercentFormatExtraNamedArguments(..) - | CheckKind::PreferListBuiltin - | CheckKind::PrintFound - | CheckKind::RaiseNotImplemented - | CheckKind::RedundantOpenModes(..) - | CheckKind::RedundantTupleInExceptionHandler(..) - | CheckKind::RemoveSixCompat - | CheckKind::ReplaceStdoutStderr - | CheckKind::ReplaceUniversalNewlines - | CheckKind::RewriteCElementTree - | CheckKind::RewriteListComprehension - | CheckKind::RewriteMockImport(..) - | CheckKind::RewriteUnicodeLiteral - | CheckKind::RewriteYieldFrom - | CheckKind::SectionNameEndsInColon(..) - | CheckKind::SectionNotOverIndented(..) - | CheckKind::SectionUnderlineAfterName(..) - | CheckKind::SectionUnderlineMatchesSectionLength(..) - | CheckKind::SectionUnderlineNotOverIndented(..) - | CheckKind::SetAttrWithConstant - | CheckKind::StringDotFormatExtraNamedArguments(..) - | CheckKind::SuperCallWithParameters - | CheckKind::TrueFalseComparison(..) - | CheckKind::TypeOfPrimitive(..) - | CheckKind::TypingTextStrAlias - | CheckKind::UnnecessaryBuiltinImport(..) - | CheckKind::UnnecessaryCallAroundSorted(..) - | CheckKind::UnnecessaryCollectionCall(..) - | CheckKind::UnnecessaryComprehension(..) - | CheckKind::UnnecessaryEncodeUTF8 - | CheckKind::UnnecessaryFutureImport(..) - | CheckKind::UnnecessaryGeneratorDict - | CheckKind::UnnecessaryGeneratorList - | CheckKind::UnnecessaryGeneratorSet - | CheckKind::UnnecessaryLRUCacheParams - | CheckKind::UnnecessaryListCall - | CheckKind::UnnecessaryListComprehensionDict - | CheckKind::UnnecessaryListComprehensionSet - | CheckKind::UnnecessaryLiteralDict(..) - | CheckKind::UnnecessaryLiteralSet(..) - | CheckKind::UnnecessaryLiteralWithinListCall(..) - | CheckKind::UnnecessaryLiteralWithinTupleCall(..) - | CheckKind::UnnecessaryReturnNone - | CheckKind::UnsortedImports - | CheckKind::UnusedImport(_, false, _) - | CheckKind::UnusedLoopControlVariable(..) - | CheckKind::UnusedNOQA(..) - | CheckKind::UseFixturesWithoutParameters - | CheckKind::UsePEP585Annotation(..) - | CheckKind::UsePEP604Annotation - | CheckKind::UseSysExit(..) - | CheckKind::UselessImportAlias - | CheckKind::UselessMetaclassType - | CheckKind::UselessObjectInheritance(..) - | CheckKind::UselessYieldFixture(..) - | CheckKind::YodaConditions(..) - ) + | CheckKind::AOrNotA(..) + | CheckKind::AmbiguousUnicodeCharacterComment(..) + | CheckKind::AmbiguousUnicodeCharacterDocstring(..) + | CheckKind::AmbiguousUnicodeCharacterString(..) + | CheckKind::AndFalse + | CheckKind::BlankLineAfterLastSection(..) + | CheckKind::BlankLineAfterSection(..) + | CheckKind::BlankLineBeforeSection(..) + | CheckKind::CapitalizeSectionName(..) + | CheckKind::CommentedOutCode + | CheckKind::ConvertLoopToAll(..) + | CheckKind::ConvertLoopToAny(..) + | CheckKind::ConvertNamedTupleFunctionalToClass(..) + | CheckKind::ConvertTypedDictFunctionalToClass(..) + | CheckKind::DashedUnderlineAfterSection(..) + | CheckKind::DatetimeTimezoneUTC + | CheckKind::DeprecatedUnittestAlias(..) + | CheckKind::DoNotAssertFalse + | CheckKind::DoNotAssignLambda(..) + | CheckKind::DupeClassFieldDefinitions(..) + | CheckKind::DuplicateHandlerException(..) + | CheckKind::DuplicateIsinstanceCall(..) + | CheckKind::EndsInPeriod + | CheckKind::EndsInPunctuation + | CheckKind::FStringMissingPlaceholders + | CheckKind::GetAttrWithConstant + | CheckKind::ImplicitReturn + | CheckKind::ImplicitReturnValue + | CheckKind::IncorrectFixtureParenthesesStyle(..) + | CheckKind::IncorrectMarkParenthesesStyle(..) + | CheckKind::InvalidEscapeSequence(..) + | CheckKind::IsLiteral(..) + | CheckKind::KeyInDict(..) + | CheckKind::MisplacedComparisonConstant(..) + | CheckKind::MissingReturnTypeSpecialMethod(..) + | CheckKind::NativeLiterals(..) + | CheckKind::NewLineAfterLastParagraph + | CheckKind::NewLineAfterSectionName(..) + | CheckKind::NoBlankLineAfterFunction(..) + | CheckKind::NoBlankLineBeforeClass(..) + | CheckKind::NoBlankLineBeforeFunction(..) + | CheckKind::NoBlankLinesBetweenHeaderAndContent(..) + | CheckKind::NoNewLineAtEndOfFile + | CheckKind::NoOverIndentation + | CheckKind::NoSurroundingWhitespace + | CheckKind::NoUnderIndentation + | CheckKind::NoUnnecessaryPass + | CheckKind::NoneComparison(..) + | CheckKind::NotInTest + | CheckKind::NotIsTest + | CheckKind::OSErrorAlias(..) + | CheckKind::OneBlankLineAfterClass(..) + | CheckKind::OneBlankLineBeforeClass(..) + | CheckKind::OpenAlias + | CheckKind::OrTrue + | CheckKind::PEP3120UnnecessaryCodingComment + | CheckKind::PPrintFound + | CheckKind::ParametrizeNamesWrongType(..) + | CheckKind::PercentFormatExtraNamedArguments(..) + | CheckKind::PreferListBuiltin + | CheckKind::PrintFound + | CheckKind::RaiseNotImplemented + | CheckKind::RedundantOpenModes(..) + | CheckKind::RedundantTupleInExceptionHandler(..) + | CheckKind::RemoveSixCompat + | CheckKind::ReplaceStdoutStderr + | CheckKind::ReplaceUniversalNewlines + | CheckKind::RewriteCElementTree + | CheckKind::RewriteListComprehension + | CheckKind::RewriteMockImport(..) + | CheckKind::RewriteUnicodeLiteral + | CheckKind::RewriteYieldFrom + | CheckKind::SectionNameEndsInColon(..) + | CheckKind::SectionNotOverIndented(..) + | CheckKind::SectionUnderlineAfterName(..) + | CheckKind::SectionUnderlineMatchesSectionLength(..) + | CheckKind::SectionUnderlineNotOverIndented(..) + | CheckKind::SetAttrWithConstant + | CheckKind::StringDotFormatExtraNamedArguments(..) + | CheckKind::SuperCallWithParameters + | CheckKind::TrueFalseComparison(..) + | CheckKind::TypeOfPrimitive(..) + | CheckKind::TypingTextStrAlias + | CheckKind::UnnecessaryBuiltinImport(..) + | CheckKind::UnnecessaryCallAroundSorted(..) + | CheckKind::UnnecessaryCollectionCall(..) + | CheckKind::UnnecessaryComprehension(..) + | CheckKind::UnnecessaryEncodeUTF8 + | CheckKind::UnnecessaryFutureImport(..) + | CheckKind::UnnecessaryGeneratorDict + | CheckKind::UnnecessaryGeneratorList + | CheckKind::UnnecessaryGeneratorSet + | CheckKind::UnnecessaryLRUCacheParams + | CheckKind::UnnecessaryListCall + | CheckKind::UnnecessaryListComprehensionDict + | CheckKind::UnnecessaryListComprehensionSet + | CheckKind::UnnecessaryLiteralDict(..) + | CheckKind::UnnecessaryLiteralSet(..) + | CheckKind::UnnecessaryLiteralWithinListCall(..) + | CheckKind::UnnecessaryLiteralWithinTupleCall(..) + | CheckKind::UnnecessaryReturnNone + | CheckKind::UnsortedImports + | CheckKind::UnusedLoopControlVariable(..) + | CheckKind::UnusedNOQA(..) + | CheckKind::UseFixturesWithoutParameters + | CheckKind::UsePEP585Annotation(..) + | CheckKind::UsePEP604Annotation + | CheckKind::UseSysExit(..) + | CheckKind::UselessImportAlias + | CheckKind::UselessMetaclassType + | CheckKind::UselessObjectInheritance(..) + | CheckKind::UselessYieldFixture(..) + | CheckKind::YodaConditions(..) => true, + // Conditionally-fixable checks. + CheckKind::UnusedImport(_, false, _) => true, + CheckKind::BlankLineAfterSummary(num_lines) if *num_lines > 0 => true, + // Non-fixable checks. + _ => false, + } } /// The message used to describe the fix action for a given `CheckKind`. @@ -3762,7 +3772,7 @@ impl CheckKind { CheckKind::BlankLineAfterSection(name) => { Some(format!("Add blank line after \"{name}\"")) } - CheckKind::BlankLineAfterSummary => Some("Insert single blank line".to_string()), + CheckKind::BlankLineAfterSummary(..) => Some("Insert single blank line".to_string()), CheckKind::BlankLineBeforeSection(name) => { Some(format!("Add blank line before \"{name}\"")) }