Skip to content

Commit e944c16

Browse files
authored
[pycodestyle] Do not ignore lines before the first logical line in blank lines rules (#10382)
## Summary Ignoring all lines until the first logical line does not match the behavior from pycodestyle. This PR therefore removes the `if state.is_not_first_logical_line` skipping the line check before the first logical line, and applies it only to `E302`. For example, in the snippet below a rule violation should be detected on the second comment and on the import. ```python # first comment # second comment import foo ``` Fixes #10374 ## Test Plan Add test cases, update the snapshots and verify the ecosystem check output
1 parent 5f40371 commit e944c16

18 files changed

+211
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Test where the error is after the module's docstring."""
2+
3+
def fn():
4+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"Test where the first line is a comment, " + "and the rule violation follows it."
2+
3+
def fn():
4+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def fn1():
2+
pass
3+
4+
def fn2():
5+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
print("Test where the first line is a statement, and the rule violation follows it.")
2+
3+
def fn():
4+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Test where the first line is a comment, and the rule violation follows it.
2+
3+
4+
5+
def fn():
6+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""Test where the error is after the module's docstring."""
2+
3+
4+
5+
def fn():
6+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"Test where the first line is a comment, " + "and the rule violation follows it."
2+
3+
4+
5+
def fn():
6+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
print("Test where the first line is a statement, and the rule violation follows it.")
2+
3+
4+
5+
def fn():
6+
pass

crates/ruff_linter/src/rules/pycodestyle/mod.rs

+18
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,24 @@ mod tests {
171171
Ok(())
172172
}
173173

174+
#[test_case(Rule::BlankLinesTopLevel, Path::new("E302_first_line_docstring.py"))]
175+
#[test_case(Rule::BlankLinesTopLevel, Path::new("E302_first_line_expression.py"))]
176+
#[test_case(Rule::BlankLinesTopLevel, Path::new("E302_first_line_function.py"))]
177+
#[test_case(Rule::BlankLinesTopLevel, Path::new("E302_first_line_statement.py"))]
178+
#[test_case(Rule::TooManyBlankLines, Path::new("E303_first_line_comment.py"))]
179+
#[test_case(Rule::TooManyBlankLines, Path::new("E303_first_line_docstring.py"))]
180+
#[test_case(Rule::TooManyBlankLines, Path::new("E303_first_line_expression.py"))]
181+
#[test_case(Rule::TooManyBlankLines, Path::new("E303_first_line_statement.py"))]
182+
fn blank_lines_first_line(rule_code: Rule, path: &Path) -> Result<()> {
183+
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
184+
let diagnostics = test_path(
185+
Path::new("pycodestyle").join(path).as_path(),
186+
&settings::LinterSettings::for_rule(rule_code),
187+
)?;
188+
assert_messages!(snapshot, diagnostics);
189+
Ok(())
190+
}
191+
174192
#[test_case(Rule::BlankLineBetweenMethods, Path::new("E30.py"))]
175193
#[test_case(Rule::BlankLinesTopLevel, Path::new("E30.py"))]
176194
#[test_case(Rule::TooManyBlankLines, Path::new("E30.py"))]

crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -696,9 +696,7 @@ impl<'a> BlankLinesChecker<'a> {
696696
state.class_status.update(&logical_line);
697697
state.fn_status.update(&logical_line);
698698

699-
if state.is_not_first_logical_line {
700-
self.check_line(&logical_line, &state, prev_indent_length, diagnostics);
701-
}
699+
self.check_line(&logical_line, &state, prev_indent_length, diagnostics);
702700

703701
match logical_line.kind {
704702
LogicalLineKind::Class => {
@@ -818,6 +816,8 @@ impl<'a> BlankLinesChecker<'a> {
818816
&& line.kind.is_class_function_or_decorator()
819817
// Blank lines in stub files are used to group definitions. Don't enforce blank lines.
820818
&& !self.source_type.is_stub()
819+
// Do not expect blank lines before the first logical line.
820+
&& state.is_not_first_logical_line
821821
{
822822
// E302
823823
let mut diagnostic = Diagnostic::new(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
3+
---
4+
E302_first_line_docstring.py:3:1: E302 [*] Expected 2 blank lines, found 1
5+
|
6+
1 | """Test where the error is after the module's docstring."""
7+
2 |
8+
3 | def fn():
9+
| ^^^ E302
10+
4 | pass
11+
|
12+
= help: Add missing blank line(s)
13+
14+
Safe fix
15+
1 1 | """Test where the error is after the module's docstring."""
16+
2 2 |
17+
3 |+
18+
3 4 | def fn():
19+
4 5 | pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
3+
---
4+
E302_first_line_expression.py:3:1: E302 [*] Expected 2 blank lines, found 1
5+
|
6+
1 | "Test where the first line is a comment, " + "and the rule violation follows it."
7+
2 |
8+
3 | def fn():
9+
| ^^^ E302
10+
4 | pass
11+
|
12+
= help: Add missing blank line(s)
13+
14+
Safe fix
15+
1 1 | "Test where the first line is a comment, " + "and the rule violation follows it."
16+
2 2 |
17+
3 |+
18+
3 4 | def fn():
19+
4 5 | pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
3+
---
4+
E302_first_line_function.py:4:1: E302 [*] Expected 2 blank lines, found 1
5+
|
6+
2 | pass
7+
3 |
8+
4 | def fn2():
9+
| ^^^ E302
10+
5 | pass
11+
|
12+
= help: Add missing blank line(s)
13+
14+
Safe fix
15+
1 1 | def fn1():
16+
2 2 | pass
17+
3 3 |
18+
4 |+
19+
4 5 | def fn2():
20+
5 6 | pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
3+
---
4+
E302_first_line_statement.py:3:1: E302 [*] Expected 2 blank lines, found 1
5+
|
6+
1 | print("Test where the first line is a statement, and the rule violation follows it.")
7+
2 |
8+
3 | def fn():
9+
| ^^^ E302
10+
4 | pass
11+
|
12+
= help: Add missing blank line(s)
13+
14+
Safe fix
15+
1 1 | print("Test where the first line is a statement, and the rule violation follows it.")
16+
2 2 |
17+
3 |+
18+
3 4 | def fn():
19+
4 5 | pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
3+
---
4+
E303_first_line_comment.py:5:1: E303 [*] Too many blank lines (3)
5+
|
6+
5 | def fn():
7+
| ^^^ E303
8+
6 | pass
9+
|
10+
= help: Remove extraneous blank line(s)
11+
12+
Safe fix
13+
1 1 | # Test where the first line is a comment, and the rule violation follows it.
14+
2 2 |
15+
3 3 |
16+
4 |-
17+
5 4 | def fn():
18+
6 5 | pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
3+
---
4+
E303_first_line_docstring.py:5:1: E303 [*] Too many blank lines (3)
5+
|
6+
5 | def fn():
7+
| ^^^ E303
8+
6 | pass
9+
|
10+
= help: Remove extraneous blank line(s)
11+
12+
Safe fix
13+
1 1 | """Test where the error is after the module's docstring."""
14+
2 2 |
15+
3 3 |
16+
4 |-
17+
5 4 | def fn():
18+
6 5 | pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
3+
---
4+
E303_first_line_expression.py:5:1: E303 [*] Too many blank lines (3)
5+
|
6+
5 | def fn():
7+
| ^^^ E303
8+
6 | pass
9+
|
10+
= help: Remove extraneous blank line(s)
11+
12+
Safe fix
13+
1 1 | "Test where the first line is a comment, " + "and the rule violation follows it."
14+
2 2 |
15+
3 3 |
16+
4 |-
17+
5 4 | def fn():
18+
6 5 | pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
3+
---
4+
E303_first_line_statement.py:5:1: E303 [*] Too many blank lines (3)
5+
|
6+
5 | def fn():
7+
| ^^^ E303
8+
6 | pass
9+
|
10+
= help: Remove extraneous blank line(s)
11+
12+
Safe fix
13+
1 1 | print("Test where the first line is a statement, and the rule violation follows it.")
14+
2 2 |
15+
3 3 |
16+
4 |-
17+
5 4 | def fn():
18+
6 5 | pass

0 commit comments

Comments
 (0)