mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 14:52:01 +00:00
Avoid missing namespace violations in scripts with shebangs (#8710)
## Summary I think it's reasonable to avoid raising `INP001` for scripts, and shebangs are one sufficient way to detect scripts. Closes https://github.com/astral-sh/ruff/issues/8690.
This commit is contained in:
parent
d1e88dc984
commit
6d5d079a18
9 changed files with 48 additions and 40 deletions
|
@ -1,6 +1,8 @@
|
|||
use std::path::Path;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
|
||||
|
@ -10,15 +12,22 @@ use crate::settings::LinterSettings;
|
|||
pub(crate) fn check_file_path(
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
settings: &LinterSettings,
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
// flake8-no-pep420
|
||||
if settings.rules.enabled(Rule::ImplicitNamespacePackage) {
|
||||
if let Some(diagnostic) =
|
||||
implicit_namespace_package(path, package, &settings.project_root, &settings.src)
|
||||
{
|
||||
if let Some(diagnostic) = implicit_namespace_package(
|
||||
path,
|
||||
package,
|
||||
locator,
|
||||
indexer,
|
||||
&settings.project_root,
|
||||
&settings.src,
|
||||
) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,7 +167,7 @@ pub(crate) fn check_tokens(
|
|||
Rule::ShebangNotFirstLine,
|
||||
Rule::ShebangMissingPython,
|
||||
]) {
|
||||
flake8_executable::rules::from_tokens(tokens, path, locator, &mut diagnostics);
|
||||
flake8_executable::rules::from_tokens(&mut diagnostics, path, locator, indexer);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
|
|
|
@ -117,7 +117,7 @@ pub fn check_path(
|
|||
.iter_enabled()
|
||||
.any(|rule_code| rule_code.lint_source().is_filesystem())
|
||||
{
|
||||
diagnostics.extend(check_file_path(path, package, settings));
|
||||
diagnostics.extend(check_file_path(path, package, locator, indexer, settings));
|
||||
}
|
||||
|
||||
// Run the logical line-based rules.
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use std::path::Path;
|
||||
|
||||
use ruff_python_parser::lexer::LexResult;
|
||||
use ruff_python_parser::Tok;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_source_file::Locator;
|
||||
pub(crate) use shebang_leading_whitespace::*;
|
||||
pub(crate) use shebang_missing_executable_file::*;
|
||||
|
@ -20,14 +18,14 @@ mod shebang_not_executable;
|
|||
mod shebang_not_first_line;
|
||||
|
||||
pub(crate) fn from_tokens(
|
||||
tokens: &[LexResult],
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
path: &Path,
|
||||
locator: &Locator,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
indexer: &Indexer,
|
||||
) {
|
||||
let mut has_any_shebang = false;
|
||||
for (tok, range) in tokens.iter().flatten() {
|
||||
if let Tok::Comment(comment) = tok {
|
||||
for range in indexer.comment_ranges() {
|
||||
let comment = locator.slice(*range);
|
||||
if let Some(shebang) = ShebangDirective::try_extract(comment) {
|
||||
has_any_shebang = true;
|
||||
|
||||
|
@ -48,7 +46,6 @@ pub(crate) fn from_tokens(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !has_any_shebang {
|
||||
if let Some(diagnostic) = shebang_missing_executable_file(path) {
|
||||
|
|
|
@ -16,7 +16,7 @@ mod tests {
|
|||
#[test_case(Path::new("test_pass_init"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_fail_empty"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_fail_nonempty"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_fail_shebang"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_pass_shebang"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_ignored"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_pass_namespace_package"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_pass_pyi"), Path::new("example.pyi"))]
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use crate::comments::shebang::ShebangDirective;
|
||||
use crate::fs;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -42,6 +44,8 @@ impl Violation for ImplicitNamespacePackage {
|
|||
pub(crate) fn implicit_namespace_package(
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
project_root: &Path,
|
||||
src: &[PathBuf],
|
||||
) -> Option<Diagnostic> {
|
||||
|
@ -56,6 +60,11 @@ pub(crate) fn implicit_namespace_package(
|
|||
&& !path
|
||||
.parent()
|
||||
.is_some_and( |parent| src.iter().any(|src| src == parent))
|
||||
// Ignore files that contain a shebang.
|
||||
&& !indexer
|
||||
.comment_ranges()
|
||||
.first().filter(|range| range.start() == TextSize::from(0))
|
||||
.is_some_and(|range| ShebangDirective::try_extract(locator.slice(*range)).is_some())
|
||||
{
|
||||
#[cfg(all(test, windows))]
|
||||
let path = path
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_no_pep420/mod.rs
|
||||
---
|
||||
example.py:1:1: INP001 File `./resources/test/fixtures/flake8_no_pep420/test_fail_shebang/example.py` is part of an implicit namespace package. Add an `__init__.py`.
|
||||
|
|
||||
1 | #!/bin/env/python
|
||||
| INP001
|
||||
2 | print('hi')
|
||||
|
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_no_pep420/mod.rs
|
||||
---
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue