diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.pyi b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.pyi new file mode 100644 index 0000000000000..96323e1e8a092 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.pyi @@ -0,0 +1,50 @@ +import json + +from typing import Any, Sequence + +class MissingCommand(TypeError): ... +class AnoherClass: ... + +def a(): ... + +@overload +def a(arg: int): ... + +@overload +def a(arg: int, name: str): ... + + +def grouped1(): ... +def grouped2(): ... +def grouped3( ): ... + + +class BackendProxy: + backend_module: str + backend_object: str | None + backend: Any + + def grouped1(): ... + def grouped2(): ... + def grouped3( ): ... + @decorated + + def with_blank_line(): ... + + + def ungrouped(): ... +a = "test" + +def function_def(): + pass +b = "test" + + +def outer(): + def inner(): + pass + def inner2(): + pass + +class Foo: ... +class Bar: ... diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30_isort.pyi b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30_isort.pyi new file mode 100644 index 0000000000000..007097427b016 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30_isort.pyi @@ -0,0 +1,62 @@ +import json + + + +from typing import Any, Sequence + + +class MissingCommand(TypeError): ... # noqa: N818 + + +class BackendProxy: + backend_module: str + backend_object: str | None + backend: Any + + +if __name__ == "__main__": + import abcd + + + abcd.foo() + +def __init__(self, backend_module: str, backend_obj: str | None) -> None: ... + +if TYPE_CHECKING: + import os + + + + from typing_extensions import TypeAlias + + + abcd.foo() + +def __call__(self, name: str, *args: Any, **kwargs: Any) -> Any: + ... + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + +def __call__2(self, name: str, *args: Any, **kwargs: Any) -> Any: + ... + + +def _exit(self) -> None: ... + + +def _optional_commands(self) -> dict[str, bool]: ... + + +def run(argv: Sequence[str]) -> int: ... + + +def read_line(fd: int = 0) -> bytearray: ... + + +def flush() -> None: ... + + +from typing import Any, Sequence + +class MissingCommand(TypeError): ... # noqa: N818 diff --git a/crates/ruff_linter/src/checkers/tokens.rs b/crates/ruff_linter/src/checkers/tokens.rs index dea5c8d8a9e4d..762f4cc463cc1 100644 --- a/crates/ruff_linter/src/checkers/tokens.rs +++ b/crates/ruff_linter/src/checkers/tokens.rs @@ -41,7 +41,8 @@ pub(crate) fn check_tokens( Rule::BlankLinesAfterFunctionOrClass, Rule::BlankLinesBeforeNestedDefinition, ]) { - BlankLinesChecker::new(locator, stylist, settings).check_lines(tokens, &mut diagnostics); + BlankLinesChecker::new(locator, stylist, settings, source_type) + .check_lines(tokens, &mut diagnostics); } if settings.rules.enabled(Rule::BlanketNOQA) { diff --git a/crates/ruff_linter/src/rules/pycodestyle/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/mod.rs index cf2e002f492d8..a6d850f862aef 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/mod.rs @@ -222,6 +222,38 @@ mod tests { Ok(()) } + #[test_case(Rule::BlankLineBetweenMethods)] + #[test_case(Rule::BlankLinesTopLevel)] + #[test_case(Rule::TooManyBlankLines)] + #[test_case(Rule::BlankLineAfterDecorator)] + #[test_case(Rule::BlankLinesAfterFunctionOrClass)] + #[test_case(Rule::BlankLinesBeforeNestedDefinition)] + fn blank_lines_typing_stub(rule_code: Rule) -> Result<()> { + let snapshot = format!("blank_lines_{}_typing_stub", rule_code.noqa_code()); + let diagnostics = test_path( + Path::new("pycodestyle").join("E30.pyi"), + &settings::LinterSettings::for_rule(rule_code), + )?; + assert_messages!(snapshot, diagnostics); + Ok(()) + } + + #[test] + fn blank_lines_typing_stub_isort() -> Result<()> { + let diagnostics = test_path( + Path::new("pycodestyle").join("E30_isort.pyi"), + &settings::LinterSettings { + ..settings::LinterSettings::for_rules([ + Rule::TooManyBlankLines, + Rule::BlankLinesTopLevel, + Rule::UnsortedImports, + ]) + }, + )?; + assert_messages!(diagnostics); + Ok(()) + } + #[test] fn constant_literals() -> Result<()> { let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 9120c4d3ffff1..1ef34f16c8775 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -8,6 +8,7 @@ use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Edit; use ruff_diagnostics::Fix; use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::PySourceType; use ruff_python_codegen::Stylist; use ruff_python_parser::lexer::LexResult; use ruff_python_parser::lexer::LexicalError; @@ -51,9 +52,14 @@ const BLANK_LINES_NESTED_LEVEL: u32 = 1; /// pass /// ``` /// +/// ## Typing stub files (`.pyi`) +/// The typing style guide recommends to not use blank lines between methods except to group +/// them. That's why this rule is not enabled in typing stub files. +/// /// ## References /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E301.html) +/// - [Typing Style Guide](https://typing.readthedocs.io/en/latest/source/stubs.html#blank-lines) #[violation] pub struct BlankLineBetweenMethods; @@ -96,9 +102,14 @@ impl AlwaysFixableViolation for BlankLineBetweenMethods { /// pass /// ``` /// +/// ## Typing stub files (`.pyi`) +/// The typing style guide recommends to not use blank lines between classes and functions except to group +/// them. That's why this rule is not enabled in typing stub files. +/// /// ## References /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E302.html) +/// - [Typing Style Guide](https://typing.readthedocs.io/en/latest/source/stubs.html#blank-lines) #[violation] pub struct BlankLinesTopLevel { actual_blank_lines: u32, @@ -150,6 +161,9 @@ impl AlwaysFixableViolation for BlankLinesTopLevel { /// pass /// ``` /// +/// ## Typing stub files (`.pyi`) +/// The rule allows at most one blank line in typing stub files in accordance to the typing style guide recommendation. +/// /// Note: The rule respects the following `isort` settings when determining the maximum number of blank lines allowed between two statements: /// * [`lint.isort.lines-after-imports`]: For top-level statements directly following an import statement. /// * [`lint.isort.lines-between-types`]: For `import` statements directly following a `from ... import ...` statement or vice versa. @@ -157,6 +171,7 @@ impl AlwaysFixableViolation for BlankLinesTopLevel { /// ## References /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E303.html) +/// - [Typing Style Guide](https://typing.readthedocs.io/en/latest/source/stubs.html#blank-lines) #[violation] pub struct TooManyBlankLines { actual_blank_lines: u32, @@ -246,9 +261,14 @@ impl AlwaysFixableViolation for BlankLineAfterDecorator { /// user = User() /// ``` /// +/// ## Typing stub files (`.pyi`) +/// The typing style guide recommends to not use blank lines between statements except to group +/// them. That's why this rule is not enabled in typing stub files. +/// /// ## References /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E305.html) +/// - [Typing Style Guide](https://typing.readthedocs.io/en/latest/source/stubs.html#blank-lines) #[violation] pub struct BlankLinesAfterFunctionOrClass { actual_blank_lines: u32, @@ -295,9 +315,14 @@ impl AlwaysFixableViolation for BlankLinesAfterFunctionOrClass { /// pass /// ``` /// +/// ## Typing stub files (`.pyi`) +/// The typing style guide recommends to not use blank lines between classes and functions except to group +/// them. That's why this rule is not enabled in typing stub files. +/// /// ## References /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E306.html) +/// - [Typing Style Guide](https://typing.readthedocs.io/en/latest/source/stubs.html#blank-lines) #[violation] pub struct BlankLinesBeforeNestedDefinition; @@ -628,6 +653,7 @@ pub(crate) struct BlankLinesChecker<'a> { indent_width: IndentWidth, lines_after_imports: isize, lines_between_types: usize, + source_type: PySourceType, } impl<'a> BlankLinesChecker<'a> { @@ -635,6 +661,7 @@ impl<'a> BlankLinesChecker<'a> { locator: &'a Locator<'a>, stylist: &'a Stylist<'a>, settings: &crate::settings::LinterSettings, + source_type: PySourceType, ) -> BlankLinesChecker<'a> { BlankLinesChecker { stylist, @@ -642,6 +669,7 @@ impl<'a> BlankLinesChecker<'a> { indent_width: settings.tab_size, lines_after_imports: settings.isort.lines_after_imports, lines_between_types: settings.isort.lines_between_types, + source_type, } } @@ -739,6 +767,8 @@ impl<'a> BlankLinesChecker<'a> { && !matches!(state.follows, Follows::Docstring | Follows::Decorator) // Do not trigger when the def follows an if/while/etc... && prev_indent_length.is_some_and(|prev_indent_length| prev_indent_length >= line.indent_length) + // Blank lines in stub files are only used for grouping. Don't enforce blank lines. + && !self.source_type.is_stub() { // E301 let mut diagnostic = Diagnostic::new(BlankLineBetweenMethods, line.first_token_range); @@ -750,20 +780,31 @@ impl<'a> BlankLinesChecker<'a> { diagnostics.push(diagnostic); } + // Blank lines in stub files are used to group definitions. Don't enforce blank lines. + let max_lines_level = if self.source_type.is_stub() { + 1 + } else { + if line.indent_length == 0 { + BLANK_LINES_TOP_LEVEL + } else { + BLANK_LINES_NESTED_LEVEL + } + }; + let expected_blank_lines_before_definition = if line.indent_length == 0 { // Mimic the isort rules for the number of blank lines before classes and functions if state.follows.is_any_import() { // Fallback to the default if the value is too large for an u32 or if it is negative. // A negative value means that isort should determine the blank lines automatically. - // `isort` defaults to 2 if before a class or function definition and 1 otherwise. - // Defaulting to 2 here is correct because the variable is only used when testing the + // `isort` defaults to 2 if before a class or function definition (except in stubs where it is one) and 1 otherwise. + // Defaulting to 2 (or 1 in stubs) here is correct because the variable is only used when testing the // blank lines before a class or function definition. - u32::try_from(self.lines_after_imports).unwrap_or(BLANK_LINES_TOP_LEVEL) + u32::try_from(self.lines_after_imports).unwrap_or(max_lines_level) } else { - BLANK_LINES_TOP_LEVEL + max_lines_level } } else { - BLANK_LINES_NESTED_LEVEL + max_lines_level }; if line.preceding_blank_lines < expected_blank_lines_before_definition @@ -775,6 +816,8 @@ impl<'a> BlankLinesChecker<'a> { && line.indent_length == 0 // Only apply to functions or classes. && line.kind.is_class_function_or_decorator() + // Blank lines in stub files are used to group definitions. Don't enforce blank lines. + && !self.source_type.is_stub() { // E302 let mut diagnostic = Diagnostic::new( @@ -804,12 +847,6 @@ impl<'a> BlankLinesChecker<'a> { diagnostics.push(diagnostic); } - let max_lines_level = if line.indent_length == 0 { - BLANK_LINES_TOP_LEVEL - } else { - BLANK_LINES_NESTED_LEVEL - }; - // If between `import` and `from .. import ..` or the other way round, // allow up to `lines_between_types` newlines for isort compatibility. // We let `isort` remove extra blank lines when the imports belong @@ -893,6 +930,8 @@ impl<'a> BlankLinesChecker<'a> { && line.indent_length == 0 && !line.is_comment_only && !line.kind.is_class_function_or_decorator() + // Blank lines in stub files are used for grouping, don't enforce blank lines. + && !self.source_type.is_stub() { // E305 let mut diagnostic = Diagnostic::new( @@ -933,6 +972,8 @@ impl<'a> BlankLinesChecker<'a> { && prev_indent_length.is_some_and(|prev_indent_length| prev_indent_length >= line.indent_length) // Allow groups of one-liners. && !(matches!(state.follows, Follows::Def) && line.last_token != TokenKind::Colon) + // Blank lines in stub files are only used for grouping. Don't enforce blank lines. + && !self.source_type.is_stub() { // E306 let mut diagnostic = diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E301_typing_stub.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E301_typing_stub.snap new file mode 100644 index 0000000000000..6dcc4546f11f9 --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E301_typing_stub.snap @@ -0,0 +1,4 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E302_typing_stub.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E302_typing_stub.snap new file mode 100644 index 0000000000000..6dcc4546f11f9 --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E302_typing_stub.snap @@ -0,0 +1,4 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E303_typing_stub.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E303_typing_stub.snap new file mode 100644 index 0000000000000..520a83ea86ff2 --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E303_typing_stub.snap @@ -0,0 +1,73 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- +E30.pyi:17:1: E303 [*] Too many blank lines (2) + | +17 | def grouped1(): ... + | ^^^ E303 +18 | def grouped2(): ... +19 | def grouped3( ): ... + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +13 13 | @overload +14 14 | def a(arg: int, name: str): ... +15 15 | +16 |- +17 16 | def grouped1(): ... +18 17 | def grouped2(): ... +19 18 | def grouped3( ): ... + +E30.pyi:22:1: E303 [*] Too many blank lines (2) + | +22 | class BackendProxy: + | ^^^^^ E303 +23 | backend_module: str +24 | backend_object: str | None + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +18 18 | def grouped2(): ... +19 19 | def grouped3( ): ... +20 20 | +21 |- +22 21 | class BackendProxy: +23 22 | backend_module: str +24 23 | backend_object: str | None + +E30.pyi:35:5: E303 [*] Too many blank lines (2) + | +35 | def ungrouped(): ... + | ^^^ E303 +36 | a = "test" + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +31 31 | +32 32 | def with_blank_line(): ... +33 33 | +34 |- +35 34 | def ungrouped(): ... +36 35 | a = "test" +37 36 | + +E30.pyi:43:1: E303 [*] Too many blank lines (2) + | +43 | def outer(): + | ^^^ E303 +44 | def inner(): +45 | pass + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +39 39 | pass +40 40 | b = "test" +41 41 | +42 |- +43 42 | def outer(): +44 43 | def inner(): +45 44 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E304_typing_stub.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E304_typing_stub.snap new file mode 100644 index 0000000000000..565eeed36e6a6 --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E304_typing_stub.snap @@ -0,0 +1,20 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- +E30.pyi:32:5: E304 [*] Blank lines found after function decorator (1) + | +30 | @decorated +31 | +32 | def with_blank_line(): ... + | ^^^ E304 + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +28 28 | def grouped2(): ... +29 29 | def grouped3( ): ... +30 30 | @decorated +31 |- +32 31 | def with_blank_line(): ... +33 32 | +34 33 | diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E305_typing_stub.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E305_typing_stub.snap new file mode 100644 index 0000000000000..6dcc4546f11f9 --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E305_typing_stub.snap @@ -0,0 +1,4 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E306_typing_stub.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E306_typing_stub.snap new file mode 100644 index 0000000000000..6dcc4546f11f9 --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_E306_typing_stub.snap @@ -0,0 +1,4 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_typing_stub_isort.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_typing_stub_isort.snap new file mode 100644 index 0000000000000..ea026fcf0733a --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__blank_lines_typing_stub_isort.snap @@ -0,0 +1,270 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- +E30_isort.pyi:1:1: I001 [*] Import block is un-sorted or un-formatted + | +1 | / import json +2 | | +3 | | +4 | | +5 | | from typing import Any, Sequence +6 | | +7 | | +8 | | class MissingCommand(TypeError): ... # noqa: N818 + | |_^ I001 + | + = help: Organize imports + +ℹ Safe fix +1 1 | import json +2 |- +3 |- +4 |- +5 2 | from typing import Any, Sequence +6 |- +7 3 | +8 4 | class MissingCommand(TypeError): ... # noqa: N818 +9 5 | + +E30_isort.pyi:5:1: E303 [*] Too many blank lines (3) + | +5 | from typing import Any, Sequence + | ^^^^ E303 + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +1 1 | import json +2 2 | +3 |- +4 |- +5 3 | from typing import Any, Sequence +6 4 | +7 5 | + +E30_isort.pyi:8:1: E303 [*] Too many blank lines (2) + | +8 | class MissingCommand(TypeError): ... # noqa: N818 + | ^^^^^ E303 + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +4 4 | +5 5 | from typing import Any, Sequence +6 6 | +7 |- +8 7 | class MissingCommand(TypeError): ... # noqa: N818 +9 8 | +10 9 | + +E30_isort.pyi:11:1: E303 [*] Too many blank lines (2) + | +11 | class BackendProxy: + | ^^^^^ E303 +12 | backend_module: str +13 | backend_object: str | None + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +7 7 | +8 8 | class MissingCommand(TypeError): ... # noqa: N818 +9 9 | +10 |- +11 10 | class BackendProxy: +12 11 | backend_module: str +13 12 | backend_object: str | None + +E30_isort.pyi:17:1: E303 [*] Too many blank lines (2) + | +17 | if __name__ == "__main__": + | ^^ E303 +18 | import abcd + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +13 13 | backend_object: str | None +14 14 | backend: Any +15 15 | +16 |- +17 16 | if __name__ == "__main__": +18 17 | import abcd +19 18 | + +E30_isort.pyi:21:5: E303 [*] Too many blank lines (2) + | +21 | abcd.foo() + | ^^^^ E303 +22 | +23 | def __init__(self, backend_module: str, backend_obj: str | None) -> None: ... + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +17 17 | if __name__ == "__main__": +18 18 | import abcd +19 19 | +20 |- +21 20 | abcd.foo() +22 21 | +23 22 | def __init__(self, backend_module: str, backend_obj: str | None) -> None: ... + +E30_isort.pyi:26:1: I001 [*] Import block is un-sorted or un-formatted + | +25 | if TYPE_CHECKING: +26 | / import os +27 | | +28 | | +29 | | +30 | | from typing_extensions import TypeAlias +31 | | + | |_^ I001 +32 | +33 | abcd.foo() + | + = help: Organize imports + +ℹ Safe fix +25 25 | if TYPE_CHECKING: +26 26 | import os +27 27 | +28 |- +29 |- +30 28 | from typing_extensions import TypeAlias +31 29 | +32 30 | + +E30_isort.pyi:30:5: E303 [*] Too many blank lines (3) + | +30 | from typing_extensions import TypeAlias + | ^^^^ E303 + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +25 25 | if TYPE_CHECKING: +26 26 | import os +27 27 | +28 |- +29 |- +30 28 | from typing_extensions import TypeAlias +31 29 | +32 30 | + +E30_isort.pyi:33:5: E303 [*] Too many blank lines (2) + | +33 | abcd.foo() + | ^^^^ E303 +34 | +35 | def __call__(self, name: str, *args: Any, **kwargs: Any) -> Any: + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +29 29 | +30 30 | from typing_extensions import TypeAlias +31 31 | +32 |- +33 32 | abcd.foo() +34 33 | +35 34 | def __call__(self, name: str, *args: Any, **kwargs: Any) -> Any: + +E30_isort.pyi:45:1: E303 [*] Too many blank lines (2) + | +45 | def _exit(self) -> None: ... + | ^^^ E303 + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +41 41 | def __call__2(self, name: str, *args: Any, **kwargs: Any) -> Any: +42 42 | ... +43 43 | +44 |- +45 44 | def _exit(self) -> None: ... +46 45 | +47 46 | + +E30_isort.pyi:48:1: E303 [*] Too many blank lines (2) + | +48 | def _optional_commands(self) -> dict[str, bool]: ... + | ^^^ E303 + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +44 44 | +45 45 | def _exit(self) -> None: ... +46 46 | +47 |- +48 47 | def _optional_commands(self) -> dict[str, bool]: ... +49 48 | +50 49 | + +E30_isort.pyi:51:1: E303 [*] Too many blank lines (2) + | +51 | def run(argv: Sequence[str]) -> int: ... + | ^^^ E303 + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +47 47 | +48 48 | def _optional_commands(self) -> dict[str, bool]: ... +49 49 | +50 |- +51 50 | def run(argv: Sequence[str]) -> int: ... +52 51 | +53 52 | + +E30_isort.pyi:54:1: E303 [*] Too many blank lines (2) + | +54 | def read_line(fd: int = 0) -> bytearray: ... + | ^^^ E303 + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +50 50 | +51 51 | def run(argv: Sequence[str]) -> int: ... +52 52 | +53 |- +54 53 | def read_line(fd: int = 0) -> bytearray: ... +55 54 | +56 55 | + +E30_isort.pyi:57:1: E303 [*] Too many blank lines (2) + | +57 | def flush() -> None: ... + | ^^^ E303 + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +53 53 | +54 54 | def read_line(fd: int = 0) -> bytearray: ... +55 55 | +56 |- +57 56 | def flush() -> None: ... +58 57 | +59 58 | + +E30_isort.pyi:60:1: E303 [*] Too many blank lines (2) + | +60 | from typing import Any, Sequence + | ^^^^ E303 +61 | +62 | class MissingCommand(TypeError): ... # noqa: N818 + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +56 56 | +57 57 | def flush() -> None: ... +58 58 | +59 |- +60 59 | from typing import Any, Sequence +61 60 | +62 61 | class MissingCommand(TypeError): ... # noqa: N818