Move flake8-executable rules out of physical lines checker (#6039)

## Summary

These only need the token stream, and we always prefer token-based to
physical line-based rules.

There are a few other changes snuck in here:

- Renaming the rule files to match the diagnostic names (likely an
error).
- The "leading whitespace before shebang" rule now works regardless of
where the comment occurs (i.e., if the shebang is on the second line,
and the first line is blank, we flag and remove that leading
whitespace).
This commit is contained in:
Charlie Marsh 2023-07-24 14:38:05 -04:00 committed by GitHub
parent 7f3797185c
commit 776d598738
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 235 additions and 224 deletions

View file

@ -0,0 +1,2 @@
#!/usr/bin/env python

View file

@ -1,18 +1,12 @@
//! Lint rules based on checking physical lines.
use std::path::Path;
use ruff_text_size::TextSize;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use ruff_python_trivia::UniversalNewlines;
use crate::comments::shebang::ShebangDirective;
use crate::registry::Rule;
use crate::rules::flake8_copyright::rules::missing_copyright_notice;
use crate::rules::flake8_executable::rules::{
shebang_missing, shebang_newline, shebang_not_executable, shebang_python, shebang_whitespace,
};
use crate::rules::pycodestyle::rules::{
doc_line_too_long, line_too_long, mixed_spaces_and_tabs, no_newline_at_end_of_file,
tab_indentation, trailing_whitespace,
@ -22,7 +16,6 @@ use crate::rules::pyupgrade::rules::unnecessary_coding_comment;
use crate::settings::Settings;
pub(crate) fn check_physical_lines(
path: &Path,
locator: &Locator,
stylist: &Stylist,
indexer: &Indexer,
@ -30,13 +23,7 @@ pub(crate) fn check_physical_lines(
settings: &Settings,
) -> Vec<Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = vec![];
let mut has_any_shebang = false;
let enforce_shebang_not_executable = settings.rules.enabled(Rule::ShebangNotExecutable);
let enforce_shebang_missing = settings.rules.enabled(Rule::ShebangMissingExecutableFile);
let enforce_shebang_whitespace = settings.rules.enabled(Rule::ShebangLeadingWhitespace);
let enforce_shebang_newline = settings.rules.enabled(Rule::ShebangNotFirstLine);
let enforce_shebang_python = settings.rules.enabled(Rule::ShebangMissingPython);
let enforce_doc_line_too_long = settings.rules.enabled(Rule::DocLineTooLong);
let enforce_line_too_long = settings.rules.enabled(Rule::LineTooLong);
let enforce_no_newline_at_end_of_file = settings.rules.enabled(Rule::MissingNewlineAtEndOfFile);
@ -50,7 +37,6 @@ pub(crate) fn check_physical_lines(
let enforce_copyright_notice = settings.rules.enabled(Rule::MissingCopyrightNotice);
let fix_unnecessary_coding_comment = settings.rules.should_fix(Rule::UTF8EncodingDeclaration);
let fix_shebang_whitespace = settings.rules.should_fix(Rule::ShebangLeadingWhitespace);
let mut commented_lines_iter = indexer.comment_ranges().iter().peekable();
let mut doc_lines_iter = doc_lines.iter().peekable();
@ -69,43 +55,6 @@ pub(crate) fn check_physical_lines(
}
}
}
if enforce_shebang_missing
|| enforce_shebang_not_executable
|| enforce_shebang_whitespace
|| enforce_shebang_newline
|| enforce_shebang_python
{
if let Some(shebang) = ShebangDirective::try_extract(&line) {
has_any_shebang = true;
if enforce_shebang_not_executable {
if let Some(diagnostic) =
shebang_not_executable(path, line.range(), &shebang)
{
diagnostics.push(diagnostic);
}
}
if enforce_shebang_whitespace {
if let Some(diagnostic) =
shebang_whitespace(line.range(), &shebang, fix_shebang_whitespace)
{
diagnostics.push(diagnostic);
}
}
if enforce_shebang_newline {
if let Some(diagnostic) =
shebang_newline(line.range(), &shebang, index == 0)
{
diagnostics.push(diagnostic);
}
}
if enforce_shebang_python {
if let Some(diagnostic) = shebang_python(line.range(), &shebang) {
diagnostics.push(diagnostic);
}
}
}
}
}
while doc_lines_iter
@ -158,12 +107,6 @@ pub(crate) fn check_physical_lines(
}
}
if enforce_shebang_missing && !has_any_shebang {
if let Some(diagnostic) = shebang_missing(path) {
diagnostics.push(diagnostic);
}
}
if enforce_copyright_notice {
if let Some(diagnostic) = missing_copyright_notice(locator, settings) {
diagnostics.push(diagnostic);
@ -175,8 +118,6 @@ pub(crate) fn check_physical_lines(
#[cfg(test)]
mod tests {
use std::path::Path;
use rustpython_parser::lexer::lex;
use rustpython_parser::Mode;
@ -198,7 +139,6 @@ mod tests {
let check_with_max_line_length = |line_length: LineLength| {
check_physical_lines(
Path::new("foo.py"),
&locator,
&stylist,
&indexer,

View file

@ -1,5 +1,7 @@
//! Lint rules based on token traversal.
use std::path::Path;
use rustpython_parser::lexer::LexResult;
use rustpython_parser::Tok;
@ -11,15 +13,16 @@ use crate::lex::docstring_detection::StateMachine;
use crate::registry::{AsRule, Rule};
use crate::rules::ruff::rules::Context;
use crate::rules::{
eradicate, flake8_commas, flake8_fixme, flake8_implicit_str_concat, flake8_pyi, flake8_quotes,
flake8_todos, pycodestyle, pygrep_hooks, pylint, pyupgrade, ruff,
eradicate, flake8_commas, flake8_executable, flake8_fixme, flake8_implicit_str_concat,
flake8_pyi, flake8_quotes, flake8_todos, pycodestyle, pygrep_hooks, pylint, pyupgrade, ruff,
};
use crate::settings::Settings;
pub(crate) fn check_tokens(
tokens: &[LexResult],
path: &Path,
locator: &Locator,
indexer: &Indexer,
tokens: &[LexResult],
settings: &Settings,
is_stub: bool,
) -> Vec<Diagnostic> {
@ -143,6 +146,16 @@ pub(crate) fn check_tokens(
flake8_pyi::rules::type_comment_in_stub(&mut diagnostics, locator, indexer);
}
if settings.rules.any_enabled(&[
Rule::ShebangNotExecutable,
Rule::ShebangMissingExecutableFile,
Rule::ShebangLeadingWhitespace,
Rule::ShebangNotFirstLine,
Rule::ShebangMissingPython,
]) {
flake8_executable::rules::from_tokens(tokens, path, locator, settings, &mut diagnostics);
}
if settings.rules.any_enabled(&[
Rule::InvalidTodoTag,
Rule::MissingTodoAuthor,

View file

@ -1,15 +1,10 @@
use ruff_python_trivia::{is_python_whitespace, Cursor};
use ruff_text_size::{TextLen, TextSize};
use std::ops::Deref;
use ruff_python_trivia::Cursor;
/// A shebang directive (e.g., `#!/usr/bin/env python3`).
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ShebangDirective<'a> {
/// The offset of the directive contents (e.g., `/usr/bin/env python3`) from the start of the
/// line.
pub(crate) offset: TextSize,
/// The contents of the directive (e.g., `"/usr/bin/env python3"`).
pub(crate) contents: &'a str,
}
pub(crate) struct ShebangDirective<'a>(&'a str);
impl<'a> ShebangDirective<'a> {
/// Parse a shebang directive from a line, or return `None` if the line does not contain a
@ -17,9 +12,6 @@ impl<'a> ShebangDirective<'a> {
pub(crate) fn try_extract(line: &'a str) -> Option<Self> {
let mut cursor = Cursor::new(line);
// Trim whitespace.
cursor.eat_while(is_python_whitespace);
// Trim the `#!` prefix.
if !cursor.eat_char('#') {
return None;
@ -28,10 +20,15 @@ impl<'a> ShebangDirective<'a> {
return None;
}
Some(Self {
offset: line.text_len() - cursor.text_len(),
contents: cursor.chars().as_str(),
})
Some(Self(cursor.chars().as_str()))
}
}
impl Deref for ShebangDirective<'_> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.0
}
}
@ -59,6 +56,12 @@ mod tests {
assert_debug_snapshot!(ShebangDirective::try_extract(source));
}
#[test]
fn shebang_match_trailing_comment() {
let source = "#!/usr/bin/env python # trailing comment";
assert_debug_snapshot!(ShebangDirective::try_extract(source));
}
#[test]
fn shebang_leading_space() {
let source = " #!/usr/bin/env python";

View file

@ -2,9 +2,4 @@
source: crates/ruff/src/comments/shebang.rs
expression: "ShebangDirective::try_extract(source)"
---
Some(
ShebangDirective {
offset: 4,
contents: "/usr/bin/env python",
},
)
None

View file

@ -3,8 +3,7 @@ source: crates/ruff/src/comments/shebang.rs
expression: "ShebangDirective::try_extract(source)"
---
Some(
ShebangDirective {
offset: 2,
contents: "/usr/bin/env python",
},
ShebangDirective(
"/usr/bin/env python",
),
)

View file

@ -0,0 +1,9 @@
---
source: crates/ruff/src/comments/shebang.rs
expression: "ShebangDirective::try_extract(source)"
---
Some(
ShebangDirective(
"/usr/bin/env python # trailing comment",
),
)

View file

@ -100,7 +100,9 @@ pub fn check_path(
.any(|rule_code| rule_code.lint_source().is_tokens())
{
let is_stub = is_python_stub_file(path);
diagnostics.extend(check_tokens(locator, indexer, &tokens, settings, is_stub));
diagnostics.extend(check_tokens(
&tokens, path, locator, indexer, settings, is_stub,
));
}
// Run the filesystem-based rules.
@ -193,7 +195,7 @@ pub fn check_path(
.any(|rule_code| rule_code.lint_source().is_physical_lines())
{
diagnostics.extend(check_physical_lines(
path, locator, stylist, indexer, &doc_lines, settings,
locator, stylist, indexer, &doc_lines, settings,
));
}

View file

@ -238,22 +238,16 @@ impl Rule {
match self {
Rule::InvalidPyprojectToml => LintSource::PyprojectToml,
Rule::UnusedNOQA => LintSource::Noqa,
Rule::DocLineTooLong
Rule::BidirectionalUnicode
| Rule::BlankLineWithWhitespace
| Rule::DocLineTooLong
| Rule::LineTooLong
| Rule::MixedSpacesAndTabs
| Rule::MissingNewlineAtEndOfFile
| Rule::UTF8EncodingDeclaration
| Rule::ShebangMissingExecutableFile
| Rule::ShebangNotExecutable
| Rule::ShebangNotFirstLine
| Rule::BidirectionalUnicode
| Rule::ShebangMissingPython
| Rule::ShebangLeadingWhitespace
| Rule::TrailingWhitespace
| Rule::TabIndentation
| Rule::MissingCopyrightNotice
| Rule::BlankLineWithWhitespace => LintSource::PhysicalLines,
| Rule::MissingNewlineAtEndOfFile
| Rule::MixedSpacesAndTabs
| Rule::TabIndentation
| Rule::TrailingWhitespace
| Rule::UTF8EncodingDeclaration => LintSource::PhysicalLines,
Rule::AmbiguousUnicodeCharacterComment
| Rule::AmbiguousUnicodeCharacterDocstring
| Rule::AmbiguousUnicodeCharacterString
@ -264,33 +258,38 @@ impl Rule {
| Rule::BlanketNOQA
| Rule::BlanketTypeIgnore
| Rule::CommentedOutCode
| Rule::MultiLineImplicitStringConcatenation
| Rule::ExtraneousParentheses
| Rule::InvalidCharacterBackspace
| Rule::InvalidCharacterSub
| Rule::InvalidCharacterEsc
| Rule::InvalidCharacterNul
| Rule::InvalidCharacterSub
| Rule::InvalidCharacterZeroWidthSpace
| Rule::ExtraneousParentheses
| Rule::InvalidEscapeSequence
| Rule::SingleLineImplicitStringConcatenation
| Rule::MissingTrailingComma
| Rule::TrailingCommaOnBareTuple
| Rule::MultipleStatementsOnOneLineColon
| Rule::UselessSemicolon
| Rule::MultipleStatementsOnOneLineSemicolon
| Rule::ProhibitedTrailingComma
| Rule::TypeCommentInStub
| Rule::InvalidTodoTag
| Rule::MissingTodoAuthor
| Rule::MissingTodoLink
| Rule::MissingTodoColon
| Rule::MissingTodoDescription
| Rule::InvalidTodoCapitalization
| Rule::MissingSpaceAfterTodoColon
| Rule::InvalidTodoTag
| Rule::LineContainsFixme
| Rule::LineContainsHack
| Rule::LineContainsTodo
| Rule::LineContainsXxx => LintSource::Tokens,
| Rule::LineContainsXxx
| Rule::MissingSpaceAfterTodoColon
| Rule::MissingTodoAuthor
| Rule::MissingTodoColon
| Rule::MissingTodoDescription
| Rule::MissingTodoLink
| Rule::MissingTrailingComma
| Rule::MultiLineImplicitStringConcatenation
| Rule::MultipleStatementsOnOneLineColon
| Rule::MultipleStatementsOnOneLineSemicolon
| Rule::ProhibitedTrailingComma
| Rule::ShebangLeadingWhitespace
| Rule::ShebangMissingExecutableFile
| Rule::ShebangMissingPython
| Rule::ShebangNotExecutable
| Rule::ShebangNotFirstLine
| Rule::SingleLineImplicitStringConcatenation
| Rule::TrailingCommaOnBareTuple
| Rule::TypeCommentInStub
| Rule::UselessSemicolon => LintSource::Tokens,
Rule::IOError => LintSource::Io,
Rule::UnsortedImports | Rule::MissingRequiredImport => LintSource::Imports,
Rule::ImplicitNamespacePackage | Rule::InvalidModuleName => LintSource::Filesystem,

View file

@ -24,6 +24,7 @@ mod tests {
#[test_case(Path::new("EXE004_1.py"))]
#[test_case(Path::new("EXE004_2.py"))]
#[test_case(Path::new("EXE004_3.py"))]
#[test_case(Path::new("EXE004_4.py"))]
#[test_case(Path::new("EXE005_1.py"))]
#[test_case(Path::new("EXE005_2.py"))]
#[test_case(Path::new("EXE005_3.py"))]

View file

@ -1,11 +1,60 @@
pub(crate) use shebang_missing::*;
pub(crate) use shebang_newline::*;
pub(crate) use shebang_not_executable::*;
pub(crate) use shebang_python::*;
pub(crate) use shebang_whitespace::*;
use std::path::Path;
mod shebang_missing;
mod shebang_newline;
use rustpython_parser::lexer::LexResult;
use rustpython_parser::Tok;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::Locator;
pub(crate) use shebang_leading_whitespace::*;
pub(crate) use shebang_missing_executable_file::*;
pub(crate) use shebang_missing_python::*;
pub(crate) use shebang_not_executable::*;
pub(crate) use shebang_not_first_line::*;
use crate::comments::shebang::ShebangDirective;
use crate::settings::Settings;
mod shebang_leading_whitespace;
mod shebang_missing_executable_file;
mod shebang_missing_python;
mod shebang_not_executable;
mod shebang_python;
mod shebang_whitespace;
mod shebang_not_first_line;
pub(crate) fn from_tokens(
tokens: &[LexResult],
path: &Path,
locator: &Locator,
settings: &Settings,
diagnostics: &mut Vec<Diagnostic>,
) {
let mut has_any_shebang = false;
for (tok, range) in tokens.iter().flatten() {
if let Tok::Comment(comment) = tok {
if let Some(shebang) = ShebangDirective::try_extract(comment) {
has_any_shebang = true;
if let Some(diagnostic) = shebang_missing_python(*range, &shebang) {
diagnostics.push(diagnostic);
}
if let Some(diagnostic) = shebang_not_executable(path, *range) {
diagnostics.push(diagnostic);
}
if let Some(diagnostic) = shebang_leading_whitespace(*range, locator, settings) {
diagnostics.push(diagnostic);
}
if let Some(diagnostic) = shebang_not_first_line(*range, locator) {
diagnostics.push(diagnostic);
}
}
}
}
if !has_any_shebang {
if let Some(diagnostic) = shebang_missing_executable_file(path) {
diagnostics.push(diagnostic);
}
}
}

View file

@ -1,11 +1,12 @@
use std::ops::Sub;
use ruff_text_size::{TextRange, TextSize};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::source_code::Locator;
use ruff_python_trivia::is_python_whitespace;
use crate::comments::shebang::ShebangDirective;
use crate::registry::AsRule;
use crate::settings::Settings;
/// ## What it does
/// Checks for whitespace before a shebang directive.
@ -46,31 +47,29 @@ impl AlwaysAutofixableViolation for ShebangLeadingWhitespace {
}
/// EXE004
pub(crate) fn shebang_whitespace(
pub(crate) fn shebang_leading_whitespace(
range: TextRange,
shebang: &ShebangDirective,
autofix: bool,
locator: &Locator,
settings: &Settings,
) -> Option<Diagnostic> {
let ShebangDirective {
offset,
contents: _,
} = shebang;
if *offset > TextSize::from(2) {
let leading_space_start = range.start();
let leading_space_len = offset.sub(TextSize::new(2));
let mut diagnostic = Diagnostic::new(
ShebangLeadingWhitespace,
TextRange::at(leading_space_start, leading_space_len),
);
if autofix {
diagnostic.set_fix(Fix::automatic(Edit::range_deletion(TextRange::at(
leading_space_start,
leading_space_len,
))));
}
Some(diagnostic)
} else {
None
// If the shebang is at the beginning of the file, abort.
if range.start() == TextSize::from(0) {
return None;
}
// If the entire prefix _isn't_ whitespace, abort (this is handled by EXE005).
if !locator
.up_to(range.start())
.chars()
.all(|c| is_python_whitespace(c) || matches!(c, '\r' | '\n'))
{
return None;
}
let prefix = TextRange::up_to(range.start());
let mut diagnostic = Diagnostic::new(ShebangLeadingWhitespace, prefix);
if settings.rules.should_fix(diagnostic.kind.rule()) {
diagnostic.set_fix(Fix::automatic(Edit::range_deletion(prefix)));
}
Some(diagnostic)
}

View file

@ -2,9 +2,8 @@
use std::path::Path;
use wsl;
use ruff_text_size::TextRange;
use wsl;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
@ -43,20 +42,22 @@ impl Violation for ShebangMissingExecutableFile {
/// EXE002
#[cfg(target_family = "unix")]
pub(crate) fn shebang_missing(filepath: &Path) -> Option<Diagnostic> {
pub(crate) fn shebang_missing_executable_file(filepath: &Path) -> Option<Diagnostic> {
// WSL supports Windows file systems, which do not have executable bits.
// Instead, everything is executable. Therefore, we skip this rule on WSL.
if wsl::is_wsl() {
return None;
}
if let Ok(true) = is_executable(filepath) {
let diagnostic = Diagnostic::new(ShebangMissingExecutableFile, TextRange::default());
return Some(diagnostic);
return Some(Diagnostic::new(
ShebangMissingExecutableFile,
TextRange::default(),
));
}
None
}
#[cfg(not(target_family = "unix"))]
pub(crate) fn shebang_missing(_filepath: &Path) -> Option<Diagnostic> {
pub(crate) fn shebang_missing_executable_file(_filepath: &Path) -> Option<Diagnostic> {
None
}

View file

@ -1,4 +1,4 @@
use ruff_text_size::{TextLen, TextRange, TextSize};
use ruff_text_size::TextRange;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
@ -41,15 +41,13 @@ impl Violation for ShebangMissingPython {
}
/// EXE003
pub(crate) fn shebang_python(range: TextRange, shebang: &ShebangDirective) -> Option<Diagnostic> {
let ShebangDirective { offset, contents } = shebang;
if contents.contains("python") || contents.contains("pytest") {
None
} else {
Some(Diagnostic::new(
ShebangMissingPython,
TextRange::at(range.start() + offset, contents.text_len()).sub_start(TextSize::from(2)),
))
pub(crate) fn shebang_missing_python(
range: TextRange,
shebang: &ShebangDirective,
) -> Option<Diagnostic> {
if shebang.contains("python") || shebang.contains("pytest") {
return None;
}
Some(Diagnostic::new(ShebangMissingPython, range))
}

View file

@ -2,15 +2,12 @@
use std::path::Path;
use ruff_text_size::TextRange;
use wsl;
use ruff_text_size::{TextLen, TextRange, TextSize};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use crate::comments::shebang::ShebangDirective;
use crate::registry::AsRule;
#[cfg(target_family = "unix")]
use crate::rules::flake8_executable::helpers::is_executable;
@ -45,34 +42,21 @@ impl Violation for ShebangNotExecutable {
/// EXE001
#[cfg(target_family = "unix")]
pub(crate) fn shebang_not_executable(
filepath: &Path,
range: TextRange,
shebang: &ShebangDirective,
) -> Option<Diagnostic> {
pub(crate) fn shebang_not_executable(filepath: &Path, range: TextRange) -> Option<Diagnostic> {
// WSL supports Windows file systems, which do not have executable bits.
// Instead, everything is executable. Therefore, we skip this rule on WSL.
if wsl::is_wsl() {
return None;
}
let ShebangDirective { offset, contents } = shebang;
if let Ok(false) = is_executable(filepath) {
let diagnostic = Diagnostic::new(
ShebangNotExecutable,
TextRange::at(range.start() + offset, contents.text_len()),
);
return Some(diagnostic);
return Some(Diagnostic::new(ShebangNotExecutable, range));
}
None
}
#[cfg(not(target_family = "unix"))]
pub(crate) fn shebang_not_executable(
_filepath: &Path,
_range: TextRange,
_shebang: &ShebangDirective,
) -> Option<Diagnostic> {
pub(crate) fn shebang_not_executable(_filepath: &Path, _range: TextRange) -> Option<Diagnostic> {
None
}

View file

@ -1,9 +1,9 @@
use ruff_text_size::{TextLen, TextRange};
use ruff_text_size::{TextRange, TextSize};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use crate::comments::shebang::ShebangDirective;
use ruff_python_ast::source_code::Locator;
use ruff_python_trivia::is_python_whitespace;
/// ## What it does
/// Checks for a shebang directive that is not at the beginning of the file.
@ -42,19 +42,20 @@ impl Violation for ShebangNotFirstLine {
}
/// EXE005
pub(crate) fn shebang_newline(
range: TextRange,
shebang: &ShebangDirective,
first_line: bool,
) -> Option<Diagnostic> {
let ShebangDirective { offset, contents } = shebang;
if first_line {
None
} else {
Some(Diagnostic::new(
ShebangNotFirstLine,
TextRange::at(range.start() + offset, contents.text_len()),
))
pub(crate) fn shebang_not_first_line(range: TextRange, locator: &Locator) -> Option<Diagnostic> {
// If the shebang is at the beginning of the file, abort.
if range.start() == TextSize::from(0) {
return None;
}
// If the entire prefix is whitespace, abort (this is handled by EXE004).
if locator
.up_to(range.start())
.chars()
.all(|c| is_python_whitespace(c) || matches!(c, '\r' | '\n'))
{
return None;
}
Some(Diagnostic::new(ShebangNotFirstLine, range))
}

View file

@ -1,10 +1,10 @@
---
source: crates/ruff/src/rules/flake8_executable/mod.rs
---
EXE001_1.py:1:3: EXE001 Shebang is present but file is not executable
EXE001_1.py:1:1: EXE001 Shebang is present but file is not executable
|
1 | #!/usr/bin/python
| ^^^^^^^^^^^^^^^ EXE001
| ^^^^^^^^^^^^^^^^^ EXE001
2 |
3 | if __name__ == '__main__':
|

View file

@ -1,11 +1,10 @@
---
source: crates/ruff/src/rules/flake8_executable/mod.rs
---
EXE004_3.py:1:1: EXE002 The file is executable but no shebang is present
EXE004_3.py:2:7: EXE005 Shebang should be at the beginning of the file
|
1 |
| EXE002
2 | pass #!/usr/bin/env python
| ^^^^^^^^^^^^^^^^^^^^^ EXE005
|

View file

@ -0,0 +1,17 @@
---
source: crates/ruff/src/rules/flake8_executable/mod.rs
---
EXE004_4.py:1:1: EXE004 [*] Avoid whitespace before shebang
|
1 | /
2 | | #!/usr/bin/env python
| |____^ EXE004
|
= help: Remove whitespace before shebang
Fix
1 |-
2 |- #!/usr/bin/env python
1 |+#!/usr/bin/env python

View file

@ -1,11 +1,11 @@
---
source: crates/ruff/src/rules/flake8_executable/mod.rs
---
EXE005_1.py:3:3: EXE005 Shebang should be at the beginning of the file
EXE005_1.py:3:1: EXE005 Shebang should be at the beginning of the file
|
2 | # A python comment
3 | #!/usr/bin/python
| ^^^^^^^^^^^^^^^ EXE005
| ^^^^^^^^^^^^^^^^^ EXE005
|

View file

@ -1,11 +1,11 @@
---
source: crates/ruff/src/rules/flake8_executable/mod.rs
---
EXE005_2.py:4:3: EXE005 Shebang should be at the beginning of the file
EXE005_2.py:4:1: EXE005 Shebang should be at the beginning of the file
|
3 | # A python comment
4 | #!/usr/bin/python
| ^^^^^^^^^^^^^^^ EXE005
| ^^^^^^^^^^^^^^^^^ EXE005
|

View file

@ -1,12 +1,12 @@
---
source: crates/ruff/src/rules/flake8_executable/mod.rs
---
EXE005_3.py:6:3: EXE005 Shebang should be at the beginning of the file
EXE005_3.py:6:1: EXE005 Shebang should be at the beginning of the file
|
4 | """
5 | # A python comment
6 | #!/usr/bin/python
| ^^^^^^^^^^^^^^^ EXE005
| ^^^^^^^^^^^^^^^^^ EXE005
|