diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index e134c3e001..df30f0c969 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -974,3 +974,39 @@ BANANA = 100 APPLE = 200 # end + + +# https://github.com/astral-sh/ruff/issues/19752 +class foo: + async def recv(self, *, length=65536): + loop = asyncio.get_event_loop() + def callback(): + loop.remove_reader(self._fd) + loop.add_reader(self._fd, callback) +# end + + +# E301 +class Foo: + if True: + print("conditional") + def test(): + pass +# end + + +# Test case for nested class scenario +class Bar: + def f(): + x = 1 + def g(): + return 1 + return 2 + + def f(): + class Baz: + x = 1 + def g(): + return 1 + return 2 +# end 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 07f5627f36..5e44676ca4 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -836,7 +836,10 @@ impl<'a, 'b> BlankLinesChecker<'a, 'b> { // Allow groups of one-liners. && !(state.follows.is_any_def() && line.last_token != TokenKind::Colon) && !state.follows.follows_def_with_dummy_body() + // Only for class scope: we must be inside a class block && matches!(state.class_status, Status::Inside(_)) + // But NOT inside a function body; nested defs inside methods are handled by E306 + && matches!(state.fn_status, Status::Outside | Status::CommentAfter(_)) // The class/parent method's docstring can directly precede the def. // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !matches!(state.follows, Follows::Docstring | Follows::Decorator) diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index e4c13172b2..13360489e4 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -94,3 +94,22 @@ help: Add missing blank line 527 | def bar(self, x: int | str) -> int | str: 528 | return x 529 | # end + +E301 [*] Expected 1 blank line, found 0 + --> E30.py:993:9 + | +991 | if True: +992 | print("conditional") +993 | def test(): + | ^^^ +994 | pass +995 | # end + | +help: Add missing blank line +990 | class Foo: +991 | if True: +992 | print("conditional") +993 + +994 | def test(): +995 | pass +996 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 204834fc20..b3cba08a97 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -208,3 +208,60 @@ help: Add missing blank line 926 | async def b(): 927 | pass 928 | # end + +E306 [*] Expected 1 blank line before a nested definition, found 0 + --> E30.py:983:9 + | +981 | async def recv(self, *, length=65536): +982 | loop = asyncio.get_event_loop() +983 | def callback(): + | ^^^ +984 | loop.remove_reader(self._fd) +985 | loop.add_reader(self._fd, callback) + | +help: Add missing blank line +980 | class foo: +981 | async def recv(self, *, length=65536): +982 | loop = asyncio.get_event_loop() +983 + +984 | def callback(): +985 | loop.remove_reader(self._fd) +986 | loop.add_reader(self._fd, callback) + +E306 [*] Expected 1 blank line before a nested definition, found 0 + --> E30.py:1002:9 + | +1000 | def f(): +1001 | x = 1 +1002 | def g(): + | ^^^ +1003 | return 1 +1004 | return 2 + | +help: Add missing blank line +999 | class Bar: +1000 | def f(): +1001 | x = 1 +1002 + +1003 | def g(): +1004 | return 1 +1005 | return 2 + +E306 [*] Expected 1 blank line before a nested definition, found 0 + --> E30.py:1009:13 + | +1007 | class Baz: +1008 | x = 1 +1009 | def g(): + | ^^^ +1010 | return 1 +1011 | return 2 + | +help: Add missing blank line +1006 | def f(): +1007 | class Baz: +1008 | x = 1 +1009 + +1010 | def g(): +1011 | return 1 +1012 | return 2