mirror of https://github.com/astral-sh/ruff
Implement `flake8-no-pep420` (#1942)
Closes https://github.com/charliermarsh/ruff/issues/1844.
This commit is contained in:
parent
84d1df08be
commit
c880d744fd
|
|
@ -59,7 +59,7 @@ There are four phases to adding a new lint rule:
|
|||
1. Define the violation struct in `src/violations.rs` (e.g., `ModuleImportNotAtTopOfFile`).
|
||||
2. Map the violation struct to a rule code in `src/registry.rs` (e.g., `E402`).
|
||||
3. Define the logic for triggering the violation in `src/checkers/ast.rs` (for AST-based checks),
|
||||
`src/checkers/tokens.rs` (for token-based checks), or `src/checkers/lines.rs` (for text-based checks).
|
||||
`src/checkers/tokens.rs` (for token-based checks), `src/checkers/lines.rs` (for text-based checks) or `src/checkers/filesystem.rs` (for filesystem-based checks).
|
||||
4. Add a test fixture.
|
||||
5. Update the generated files (documentation and generated code).
|
||||
|
||||
|
|
|
|||
11
README.md
11
README.md
|
|
@ -137,6 +137,7 @@ developer of [Zulip](https://github.com/zulip/zulip):
|
|||
1. [Pylint (PLC, PLE, PLR, PLW)](#pylint-plc-ple-plr-plw)
|
||||
1. [flake8-pie (PIE)](#flake8-pie-pie)
|
||||
1. [flake8-commas (COM)](#flake8-commas-com)
|
||||
1. [flake8-no-pep420 (INP)](#flake8-no-pep420-inp)
|
||||
1. [Ruff-specific rules (RUF)](#ruff-specific-rules-ruf)<!-- End auto-generated table of contents. -->
|
||||
1. [Editor Integrations](#editor-integrations)
|
||||
1. [FAQ](#faq)
|
||||
|
|
@ -1158,6 +1159,14 @@ For more, see [flake8-commas](https://pypi.org/project/flake8-commas/2.1.0/) on
|
|||
| COM818 | TrailingCommaOnBareTupleProhibited | Trailing comma on bare tuple prohibited | |
|
||||
| COM819 | TrailingCommaProhibited | Trailing comma prohibited | 🛠 |
|
||||
|
||||
### flake8-no-pep420 (INP)
|
||||
|
||||
For more, see [flake8-no-pep420](https://pypi.org/project/flake8-boolean-trap/2.3.0/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| INP001 | ImplicitNamespacePackage | File `...` is part of an implicit namespace package. Add an `__init__.py`. | |
|
||||
|
||||
### Ruff-specific rules (RUF)
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
|
|
@ -1451,6 +1460,7 @@ natively, including:
|
|||
- [`flake8-errmsg`](https://pypi.org/project/flake8-errmsg/)
|
||||
- [`flake8-implicit-str-concat`](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
- [`flake8-no-pep420`](https://pypi.org/project/flake8-no-pep420)
|
||||
- [`flake8-pie`](https://pypi.org/project/flake8-pie/) ([#1543](https://github.com/charliermarsh/ruff/issues/1543))
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
|
|
@ -1518,6 +1528,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
|||
- [`flake8-errmsg`](https://pypi.org/project/flake8-errmsg/)
|
||||
- [`flake8-implicit-str-concat`](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
- [`flake8-no-pep420`](https://pypi.org/project/flake8-no-pep420)
|
||||
- [`flake8-pie`](https://pypi.org/project/flake8-pie/) ([#1543](https://github.com/charliermarsh/ruff/issues/1543))
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Adam Johnson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1 @@
|
|||
print('hi')
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/env/python
|
||||
print('hi')
|
||||
|
|
@ -0,0 +1 @@
|
|||
import os # noqa: INP001
|
||||
|
|
@ -1356,6 +1356,10 @@
|
|||
"ICN0",
|
||||
"ICN00",
|
||||
"ICN001",
|
||||
"INP",
|
||||
"INP0",
|
||||
"INP00",
|
||||
"INP001",
|
||||
"ISC",
|
||||
"ISC0",
|
||||
"ISC00",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
use std::path::Path;
|
||||
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
|
||||
use crate::settings::Settings;
|
||||
|
||||
pub fn check_file_path(path: &Path, settings: &Settings) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
// flake8-no-pep420
|
||||
if settings.rules.enabled(&RuleCode::INP001) {
|
||||
if let Some(diagnostic) = implicit_namespace_package(path) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
pub mod ast;
|
||||
pub mod filesystem;
|
||||
pub mod imports;
|
||||
pub mod lines;
|
||||
pub mod noqa;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use rustpython_parser::lexer::LexResult;
|
|||
use crate::ast::types::Range;
|
||||
use crate::autofix::fix_file;
|
||||
use crate::checkers::ast::check_ast;
|
||||
use crate::checkers::filesystem::check_file_path;
|
||||
use crate::checkers::imports::check_imports;
|
||||
use crate::checkers::lines::check_lines;
|
||||
use crate::checkers::noqa::check_noqa;
|
||||
|
|
@ -62,6 +63,15 @@ pub fn check_path(
|
|||
diagnostics.extend(check_tokens(locator, &tokens, settings, autofix));
|
||||
}
|
||||
|
||||
// Run the filesystem-based rules.
|
||||
if settings
|
||||
.rules
|
||||
.iter_enabled()
|
||||
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::Filesystem))
|
||||
{
|
||||
diagnostics.extend(check_file_path(path, settings));
|
||||
}
|
||||
|
||||
// Run the AST-based rules.
|
||||
let use_ast = settings
|
||||
.rules
|
||||
|
|
|
|||
|
|
@ -416,6 +416,8 @@ ruff_macros::define_rule_mapping!(
|
|||
COM812 => violations::TrailingCommaMissing,
|
||||
COM818 => violations::TrailingCommaOnBareTupleProhibited,
|
||||
COM819 => violations::TrailingCommaProhibited,
|
||||
// flake8-no-pep420
|
||||
INP001 => violations::ImplicitNamespacePackage,
|
||||
// Ruff
|
||||
RUF001 => violations::AmbiguousUnicodeCharacterString,
|
||||
RUF002 => violations::AmbiguousUnicodeCharacterDocstring,
|
||||
|
|
@ -459,6 +461,7 @@ pub enum RuleOrigin {
|
|||
Pylint,
|
||||
Flake8Pie,
|
||||
Flake8Commas,
|
||||
Flake8NoPep420,
|
||||
Ruff,
|
||||
}
|
||||
|
||||
|
|
@ -525,6 +528,7 @@ impl RuleOrigin {
|
|||
RuleOrigin::Pyupgrade => Prefixes::Single(RuleCodePrefix::UP),
|
||||
RuleOrigin::Flake8Pie => Prefixes::Single(RuleCodePrefix::PIE),
|
||||
RuleOrigin::Flake8Commas => Prefixes::Single(RuleCodePrefix::COM),
|
||||
RuleOrigin::Flake8NoPep420 => Prefixes::Single(RuleCodePrefix::INP),
|
||||
RuleOrigin::Ruff => Prefixes::Single(RuleCodePrefix::RUF),
|
||||
}
|
||||
}
|
||||
|
|
@ -537,6 +541,7 @@ pub enum LintSource {
|
|||
Tokens,
|
||||
Imports,
|
||||
NoQa,
|
||||
Filesystem,
|
||||
}
|
||||
|
||||
impl RuleCode {
|
||||
|
|
@ -567,6 +572,7 @@ impl RuleCode {
|
|||
| RuleCode::RUF003 => &LintSource::Tokens,
|
||||
RuleCode::E902 => &LintSource::Io,
|
||||
RuleCode::I001 | RuleCode::I002 => &LintSource::Imports,
|
||||
RuleCode::INP001 => &LintSource::Filesystem,
|
||||
_ => &LintSource::Ast,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
//! Rules from [flake8-no-pep420](https://pypi.org/project/flake8-boolean-trap/2.3.0/).
|
||||
pub(crate) mod rules;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::settings::Settings;
|
||||
|
||||
#[test_case(Path::new("test_pass"); "INP001_0")]
|
||||
#[test_case(Path::new("test_fail_empty"); "INP001_1")]
|
||||
#[test_case(Path::new("test_fail_nonempty"); "INP001_2")]
|
||||
#[test_case(Path::new("test_fail_shebang"); "INP001_3")]
|
||||
#[test_case(Path::new("test_ignored"); "INP001_4")]
|
||||
fn test_flake8_no_pep420(path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}", path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_no_pep420")
|
||||
.join(path)
|
||||
.join("example.py")
|
||||
.as_path(),
|
||||
&Settings::for_rule(RuleCode::INP001),
|
||||
)?;
|
||||
insta::assert_yaml_snapshot!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
use std::path::Path;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
|
||||
/// INP001
|
||||
pub fn implicit_namespace_package(path: &Path) -> Option<Diagnostic> {
|
||||
if let Some(parent) = path.parent() {
|
||||
if !parent.join("__init__.py").as_path().exists() {
|
||||
return Some(Diagnostic::new(
|
||||
violations::ImplicitNamespacePackage(path.to_string_lossy().to_string()),
|
||||
Range::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
source: src/rules/flake8_no_pep420/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
ImplicitNamespacePackage: "./resources/test/fixtures/flake8_no_pep420/test_fail_empty/example.py"
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 1
|
||||
column: 0
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
source: src/rules/flake8_no_pep420/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
ImplicitNamespacePackage: "./resources/test/fixtures/flake8_no_pep420/test_fail_nonempty/example.py"
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 1
|
||||
column: 0
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
source: src/rules/flake8_no_pep420/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
ImplicitNamespacePackage: "./resources/test/fixtures/flake8_no_pep420/test_fail_shebang/example.py"
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 1
|
||||
column: 0
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/rules/flake8_no_pep420/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/rules/flake8_no_pep420/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
||||
|
|
@ -13,6 +13,7 @@ pub mod flake8_debugger;
|
|||
pub mod flake8_errmsg;
|
||||
pub mod flake8_implicit_str_concat;
|
||||
pub mod flake8_import_conventions;
|
||||
pub mod flake8_no_pep420;
|
||||
pub mod flake8_pie;
|
||||
pub mod flake8_print;
|
||||
pub mod flake8_pytest_style;
|
||||
|
|
|
|||
|
|
@ -6088,6 +6088,22 @@ impl AlwaysAutofixableViolation for TrailingCommaProhibited {
|
|||
}
|
||||
}
|
||||
|
||||
// flake8-no-pep420
|
||||
|
||||
define_violation!(
|
||||
pub struct ImplicitNamespacePackage(pub String);
|
||||
);
|
||||
impl Violation for ImplicitNamespacePackage {
|
||||
fn message(&self) -> String {
|
||||
let ImplicitNamespacePackage(filename) = self;
|
||||
format!("File `{filename}` is part of an implicit namespace package. Add an `__init__.py`.")
|
||||
}
|
||||
|
||||
fn placeholder() -> Self {
|
||||
ImplicitNamespacePackage("...".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
// Ruff
|
||||
|
||||
define_violation!(
|
||||
|
|
|
|||
Loading…
Reference in New Issue