[flake8-builtins] Implement import, lambda, and module shadowing (#12546)

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
Extend `flake8-builtins` to imports, lambda-arguments, and modules to be
consistent with original checker
[flake8_builtins](https://github.com/gforcada/flake8-builtins/blob/main/flake8_builtins.py).

closes #12540 

## Details

- Implement builtin-import-shadowing (A004)
- Stop tracking imports shadowing in builtin-variable-shadowing (A001)
in preview mode.
- Implement builtin-lambda-argument-shadowing (A005)
- Implement builtin-module-shadowing (A006)
  - Add new option `linter.flake8_builtins.builtins_allowed_modules`

## Test Plan

cargo test
This commit is contained in:
Aleksei Latyshev 2024-07-29 03:42:42 +02:00 committed by GitHub
parent 665c75f7ab
commit 9cdc578dd9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 589 additions and 24 deletions

View file

@ -232,6 +232,7 @@ linter.flake8_bandit.hardcoded_tmp_directory = [
] ]
linter.flake8_bandit.check_typed_exception = false linter.flake8_bandit.check_typed_exception = false
linter.flake8_bugbear.extend_immutable_calls = [] linter.flake8_bugbear.extend_immutable_calls = []
linter.flake8_builtins.builtins_allowed_modules = []
linter.flake8_builtins.builtins_ignorelist = [] linter.flake8_builtins.builtins_ignorelist = []
linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}((-|,\s)\d{4})* linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}((-|,\s)\d{4})*

View file

@ -0,0 +1,5 @@
import some as sum
import float
from some import other as int
from some import input, exec
from directory import new as dir

View file

@ -0,0 +1,5 @@
lambda print, copyright: print
lambda x, float, y: x + y
lambda min, max: min
lambda id: id
lambda dir: dir

View file

@ -2,7 +2,7 @@ use ruff_python_ast::Expr;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::codes::Rule; use crate::codes::Rule;
use crate::rules::{flake8_pie, pylint, refurb}; use crate::rules::{flake8_builtins, flake8_pie, pylint, refurb};
/// Run lint rules over all deferred lambdas in the [`SemanticModel`]. /// Run lint rules over all deferred lambdas in the [`SemanticModel`].
pub(crate) fn deferred_lambdas(checker: &mut Checker) { pub(crate) fn deferred_lambdas(checker: &mut Checker) {
@ -24,6 +24,9 @@ pub(crate) fn deferred_lambdas(checker: &mut Checker) {
if checker.enabled(Rule::ReimplementedOperator) { if checker.enabled(Rule::ReimplementedOperator) {
refurb::rules::reimplemented_operator(checker, &lambda.into()); refurb::rules::reimplemented_operator(checker, &lambda.into());
} }
if checker.enabled(Rule::BuiltinLambdaArgumentShadowing) {
flake8_builtins::rules::builtin_lambda_argument_shadowing(checker, lambda);
}
} }
} }
} }

View file

@ -597,8 +597,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::NonAsciiImportName) { if checker.enabled(Rule::NonAsciiImportName) {
pylint::rules::non_ascii_module_import(checker, alias); pylint::rules::non_ascii_module_import(checker, alias);
} }
// TODO(charlie): Remove when stabilizing A004.
if let Some(asname) = &alias.asname { if let Some(asname) = &alias.asname {
if checker.enabled(Rule::BuiltinVariableShadowing) { if checker.settings.preview.is_disabled()
&& checker.enabled(Rule::BuiltinVariableShadowing)
{
flake8_builtins::rules::builtin_variable_shadowing( flake8_builtins::rules::builtin_variable_shadowing(
checker, checker,
asname, asname,
@ -739,6 +742,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
checker.diagnostics.push(diagnostic); checker.diagnostics.push(diagnostic);
} }
} }
if checker.enabled(Rule::BuiltinImportShadowing) {
flake8_builtins::rules::builtin_import_shadowing(checker, alias);
}
} }
} }
Stmt::ImportFrom( Stmt::ImportFrom(
@ -917,8 +923,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
)); ));
} }
} else { } else {
// TODO(charlie): Remove when stabilizing A004.
if let Some(asname) = &alias.asname { if let Some(asname) = &alias.asname {
if checker.enabled(Rule::BuiltinVariableShadowing) { if checker.settings.preview.is_disabled()
&& checker.enabled(Rule::BuiltinVariableShadowing)
{
flake8_builtins::rules::builtin_variable_shadowing( flake8_builtins::rules::builtin_variable_shadowing(
checker, checker,
asname, asname,
@ -1030,6 +1039,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
} }
} }
} }
if checker.enabled(Rule::BuiltinImportShadowing) {
flake8_builtins::rules::builtin_import_shadowing(checker, alias);
}
} }
if checker.enabled(Rule::ImportSelf) { if checker.enabled(Rule::ImportSelf) {
if let Some(diagnostic) = pylint::rules::import_from_self( if let Some(diagnostic) = pylint::rules::import_from_self(

View file

@ -5,6 +5,7 @@ use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator; use ruff_source_file::Locator;
use crate::registry::Rule; use crate::registry::Rule;
use crate::rules::flake8_builtins::rules::builtin_module_shadowing;
use crate::rules::flake8_no_pep420::rules::implicit_namespace_package; use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
use crate::rules::pep8_naming::rules::invalid_module_name; use crate::rules::pep8_naming::rules::invalid_module_name;
use crate::settings::LinterSettings; use crate::settings::LinterSettings;
@ -41,5 +42,17 @@ pub(crate) fn check_file_path(
} }
} }
// flake8-builtins
if settings.rules.enabled(Rule::BuiltinModuleShadowing) {
if let Some(diagnostic) = builtin_module_shadowing(
path,
package,
&settings.flake8_builtins.builtins_allowed_modules,
settings.target_version,
) {
diagnostics.push(diagnostic);
}
}
diagnostics diagnostics
} }

View file

@ -310,6 +310,10 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Flake8Builtins, "001") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinVariableShadowing), (Flake8Builtins, "001") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinVariableShadowing),
(Flake8Builtins, "002") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinArgumentShadowing), (Flake8Builtins, "002") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinArgumentShadowing),
(Flake8Builtins, "003") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinAttributeShadowing), (Flake8Builtins, "003") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinAttributeShadowing),
// TODO(charlie): When stabilizing, remove preview gating for A001's treatment of imports.
(Flake8Builtins, "004") => (RuleGroup::Preview, rules::flake8_builtins::rules::BuiltinImportShadowing),
(Flake8Builtins, "005") => (RuleGroup::Preview, rules::flake8_builtins::rules::BuiltinModuleShadowing),
(Flake8Builtins, "006") => (RuleGroup::Preview, rules::flake8_builtins::rules::BuiltinLambdaArgumentShadowing),
// flake8-bugbear // flake8-bugbear
(Flake8Bugbear, "002") => (RuleGroup::Stable, rules::flake8_bugbear::rules::UnaryPrefixIncrementDecrement), (Flake8Bugbear, "002") => (RuleGroup::Stable, rules::flake8_bugbear::rules::UnaryPrefixIncrementDecrement),

View file

@ -304,7 +304,9 @@ impl Rule {
| Rule::UTF8EncodingDeclaration => LintSource::Tokens, | Rule::UTF8EncodingDeclaration => LintSource::Tokens,
Rule::IOError => LintSource::Io, Rule::IOError => LintSource::Io,
Rule::UnsortedImports | Rule::MissingRequiredImport => LintSource::Imports, Rule::UnsortedImports | Rule::MissingRequiredImport => LintSource::Imports,
Rule::ImplicitNamespacePackage | Rule::InvalidModuleName => LintSource::Filesystem, Rule::ImplicitNamespacePackage
| Rule::InvalidModuleName
| Rule::BuiltinModuleShadowing => LintSource::Filesystem,
Rule::IndentationWithInvalidMultiple Rule::IndentationWithInvalidMultiple
| Rule::IndentationWithInvalidMultipleComment | Rule::IndentationWithInvalidMultipleComment
| Rule::MissingWhitespace | Rule::MissingWhitespace

View file

@ -18,6 +18,25 @@ mod tests {
#[test_case(Rule::BuiltinVariableShadowing, Path::new("A001.py"))] #[test_case(Rule::BuiltinVariableShadowing, Path::new("A001.py"))]
#[test_case(Rule::BuiltinArgumentShadowing, Path::new("A002.py"))] #[test_case(Rule::BuiltinArgumentShadowing, Path::new("A002.py"))]
#[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))] #[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))]
#[test_case(Rule::BuiltinImportShadowing, Path::new("A004.py"))]
#[test_case(
Rule::BuiltinModuleShadowing,
Path::new("A005/modules/non_builtin/__init__.py")
)]
#[test_case(
Rule::BuiltinModuleShadowing,
Path::new("A005/modules/logging/__init__.py")
)]
#[test_case(
Rule::BuiltinModuleShadowing,
Path::new("A005/modules/string/__init__.py")
)]
#[test_case(
Rule::BuiltinModuleShadowing,
Path::new("A005/modules/package/bisect.py")
)]
#[test_case(Rule::BuiltinModuleShadowing, Path::new("A005/modules/package/xml.py"))]
#[test_case(Rule::BuiltinLambdaArgumentShadowing, Path::new("A006.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> { fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path( let diagnostics = test_path(
@ -31,6 +50,8 @@ mod tests {
#[test_case(Rule::BuiltinVariableShadowing, Path::new("A001.py"))] #[test_case(Rule::BuiltinVariableShadowing, Path::new("A001.py"))]
#[test_case(Rule::BuiltinArgumentShadowing, Path::new("A002.py"))] #[test_case(Rule::BuiltinArgumentShadowing, Path::new("A002.py"))]
#[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))] #[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))]
#[test_case(Rule::BuiltinImportShadowing, Path::new("A004.py"))]
#[test_case(Rule::BuiltinLambdaArgumentShadowing, Path::new("A006.py"))]
fn builtins_ignorelist(rule_code: Rule, path: &Path) -> Result<()> { fn builtins_ignorelist(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!( let snapshot = format!(
"{}_{}_builtins_ignorelist", "{}_{}_builtins_ignorelist",
@ -43,6 +64,46 @@ mod tests {
&LinterSettings { &LinterSettings {
flake8_builtins: super::settings::Settings { flake8_builtins: super::settings::Settings {
builtins_ignorelist: vec!["id".to_string(), "dir".to_string()], builtins_ignorelist: vec!["id".to_string(), "dir".to_string()],
..Default::default()
},
..LinterSettings::for_rules(vec![rule_code])
},
)?;
assert_messages!(snapshot, diagnostics);
Ok(())
}
#[test_case(
Rule::BuiltinModuleShadowing,
Path::new("A005/modules/non_builtin/__init__.py")
)]
#[test_case(
Rule::BuiltinModuleShadowing,
Path::new("A005/modules/logging/__init__.py")
)]
#[test_case(
Rule::BuiltinModuleShadowing,
Path::new("A005/modules/string/__init__.py")
)]
#[test_case(
Rule::BuiltinModuleShadowing,
Path::new("A005/modules/package/bisect.py")
)]
#[test_case(Rule::BuiltinModuleShadowing, Path::new("A005/modules/package/xml.py"))]
fn builtins_allowed_modules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!(
"{}_{}_builtins_allowed_modules",
rule_code.noqa_code(),
path.to_string_lossy()
);
let diagnostics = test_path(
Path::new("flake8_builtins").join(path).as_path(),
&LinterSettings {
flake8_builtins: super::settings::Settings {
builtins_allowed_modules: vec!["xml".to_string(), "logging".to_string()],
..Default::default()
}, },
..LinterSettings::for_rules(vec![rule_code]) ..LinterSettings::for_rules(vec![rule_code])
}, },

View file

@ -1,8 +1,7 @@
use ruff_python_ast::Parameter;
use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::Violation; use ruff_diagnostics::Violation;
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::Parameter;
use ruff_python_semantic::analyze::visibility::{is_overload, is_override}; use ruff_python_semantic::analyze::visibility::{is_overload, is_override};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -11,7 +10,7 @@ use crate::checkers::ast::Checker;
use super::super::helpers::shadows_builtin; use super::super::helpers::shadows_builtin;
/// ## What it does /// ## What it does
/// Checks for any function arguments that use the same name as a builtin. /// Checks for function arguments that use the same names as builtins.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// Reusing a builtin name for the name of an argument increases the /// Reusing a builtin name for the name of an argument increases the

View file

@ -10,8 +10,8 @@ use crate::checkers::ast::Checker;
use crate::rules::flake8_builtins::helpers::shadows_builtin; use crate::rules::flake8_builtins::helpers::shadows_builtin;
/// ## What it does /// ## What it does
/// Checks for any class attributes or methods that use the same name as a /// Checks for class attributes and methods that use the same names as
/// builtin. /// Python builtins.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// Reusing a builtin name for the name of an attribute increases the /// Reusing a builtin name for the name of an attribute increases the

View file

@ -0,0 +1,49 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::Alias;
use crate::checkers::ast::Checker;
use crate::rules::flake8_builtins::helpers::shadows_builtin;
/// ## What it does
/// Checks for imports that use the same names as builtins.
///
/// ## Why is this bad?
/// Reusing a builtin for the name of an import increases the difficulty
/// of reading and maintaining the code, and can cause non-obvious errors,
/// as readers may mistake the variable for the builtin and vice versa.
///
/// Builtins can be marked as exceptions to this rule via the
/// [`lint.flake8-builtins.builtins-ignorelist`] configuration option.
///
/// ## Options
/// - `lint.flake8-builtins.builtins-ignorelist`
#[violation]
pub struct BuiltinImportShadowing {
name: String,
}
impl Violation for BuiltinImportShadowing {
#[derive_message_formats]
fn message(&self) -> String {
let BuiltinImportShadowing { name } = self;
format!("Import `{name}` is shadowing a Python builtin")
}
}
/// A004
pub(crate) fn builtin_import_shadowing(checker: &mut Checker, alias: &Alias) {
let name = alias.asname.as_ref().unwrap_or(&alias.name);
if shadows_builtin(
name.as_str(),
&checker.settings.flake8_builtins.builtins_ignorelist,
checker.source_type,
) {
checker.diagnostics.push(Diagnostic::new(
BuiltinImportShadowing {
name: name.to_string(),
},
name.range,
));
}
}

View file

@ -0,0 +1,56 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::ExprLambda;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::rules::flake8_builtins::helpers::shadows_builtin;
/// ## What it does
/// Checks for lambda arguments that use the same names as Python builtins.
///
/// ## Why is this bad?
/// Reusing a builtin name for the name of a lambda argument increases the
/// difficulty of reading and maintaining the code, and can cause
/// non-obvious errors, as readers may mistake the variable for the
/// builtin and vice versa.
///
/// Builtins can be marked as exceptions to this rule via the
/// [`lint.flake8-builtins.builtins-ignorelist`] configuration option.
///
/// ## Options
/// - `lint.flake8-builtins.builtins-ignorelist`
#[violation]
pub struct BuiltinLambdaArgumentShadowing {
name: String,
}
impl Violation for BuiltinLambdaArgumentShadowing {
#[derive_message_formats]
fn message(&self) -> String {
let BuiltinLambdaArgumentShadowing { name } = self;
format!("Lambda argument `{name}` is shadowing a Python builtin")
}
}
/// A006
pub(crate) fn builtin_lambda_argument_shadowing(checker: &mut Checker, lambda: &ExprLambda) {
let Some(parameters) = lambda.parameters.as_ref() else {
return;
};
for param in parameters.iter_non_variadic_params() {
let name = &param.parameter.name;
if shadows_builtin(
name.as_ref(),
&checker.settings.flake8_builtins.builtins_ignorelist,
checker.source_type,
) {
checker.diagnostics.push(Diagnostic::new(
BuiltinLambdaArgumentShadowing {
name: name.to_string(),
},
name.range(),
));
}
}
}

View file

@ -0,0 +1,73 @@
use std::path::Path;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_stdlib::path::is_module_file;
use ruff_python_stdlib::sys::is_known_standard_library;
use ruff_text_size::TextRange;
use crate::settings::types::PythonVersion;
/// ## What it does
/// Checks for modules that use the same names as Python builtin modules.
///
/// ## Why is this bad?
/// Reusing a builtin module name for the name of a module increases the
/// difficulty of reading and maintaining the code, and can cause
/// non-obvious errors, as readers may mistake the variable for the
/// builtin and vice versa.
///
/// Builtin modules can be marked as exceptions to this rule via the
/// [`lint.flake8-builtins.builtins-allowed-modules`] configuration option.
///
/// ## Options
/// - `lint.flake8-builtins.builtins-allowed-modules`
#[violation]
pub struct BuiltinModuleShadowing {
name: String,
}
impl Violation for BuiltinModuleShadowing {
#[derive_message_formats]
fn message(&self) -> String {
let BuiltinModuleShadowing { name } = self;
format!("Module `{name}` is shadowing a Python builtin module")
}
}
/// A005
pub(crate) fn builtin_module_shadowing(
path: &Path,
package: Option<&Path>,
allowed_modules: &[String],
target_version: PythonVersion,
) -> Option<Diagnostic> {
if !path
.extension()
.is_some_and(|ext| ext == "py" || ext == "pyi")
{
return None;
}
if let Some(package) = package {
let module_name = if is_module_file(path) {
package.file_name().unwrap().to_string_lossy()
} else {
path.file_stem().unwrap().to_string_lossy()
};
if is_known_standard_library(target_version.minor(), &module_name)
&& allowed_modules
.iter()
.all(|allowed_module| allowed_module != &module_name)
{
return Some(Diagnostic::new(
BuiltinModuleShadowing {
name: module_name.to_string(),
},
TextRange::default(),
));
}
}
None
}

View file

@ -1,15 +1,14 @@
use ruff_text_size::TextRange;
use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::Violation; use ruff_diagnostics::Violation;
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_text_size::TextRange;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::flake8_builtins::helpers::shadows_builtin; use crate::rules::flake8_builtins::helpers::shadows_builtin;
/// ## What it does /// ## What it does
/// Checks for variable (and function) assignments that use the same name /// Checks for variable (and function) assignments that use the same names
/// as a builtin. /// as builtins.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// Reusing a builtin name for the name of a variable increases the /// Reusing a builtin name for the name of a variable increases the

View file

@ -1,7 +1,13 @@
pub(crate) use builtin_argument_shadowing::*; pub(crate) use builtin_argument_shadowing::*;
pub(crate) use builtin_attribute_shadowing::*; pub(crate) use builtin_attribute_shadowing::*;
pub(crate) use builtin_import_shadowing::*;
pub(crate) use builtin_lambda_argument_shadowing::*;
pub(crate) use builtin_module_shadowing::*;
pub(crate) use builtin_variable_shadowing::*; pub(crate) use builtin_variable_shadowing::*;
mod builtin_argument_shadowing; mod builtin_argument_shadowing;
mod builtin_attribute_shadowing; mod builtin_attribute_shadowing;
mod builtin_import_shadowing;
mod builtin_lambda_argument_shadowing;
mod builtin_module_shadowing;
mod builtin_variable_shadowing; mod builtin_variable_shadowing;

View file

@ -7,6 +7,7 @@ use std::fmt::{Display, Formatter};
#[derive(Debug, Clone, Default, CacheKey)] #[derive(Debug, Clone, Default, CacheKey)]
pub struct Settings { pub struct Settings {
pub builtins_ignorelist: Vec<String>, pub builtins_ignorelist: Vec<String>,
pub builtins_allowed_modules: Vec<String>,
} }
impl Display for Settings { impl Display for Settings {
@ -15,7 +16,8 @@ impl Display for Settings {
formatter = f, formatter = f,
namespace = "linter.flake8_builtins", namespace = "linter.flake8_builtins",
fields = [ fields = [
self.builtins_ignorelist | array self.builtins_allowed_modules | array,
self.builtins_ignorelist | array,
] ]
} }
Ok(()) Ok(())

View file

@ -0,0 +1,55 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
A004.py:1:16: A004 Import `sum` is shadowing a Python builtin
|
1 | import some as sum
| ^^^ A004
2 | import float
3 | from some import other as int
|
A004.py:2:8: A004 Import `float` is shadowing a Python builtin
|
1 | import some as sum
2 | import float
| ^^^^^ A004
3 | from some import other as int
4 | from some import input, exec
|
A004.py:3:27: A004 Import `int` is shadowing a Python builtin
|
1 | import some as sum
2 | import float
3 | from some import other as int
| ^^^ A004
4 | from some import input, exec
5 | from directory import new as dir
|
A004.py:4:18: A004 Import `input` is shadowing a Python builtin
|
2 | import float
3 | from some import other as int
4 | from some import input, exec
| ^^^^^ A004
5 | from directory import new as dir
|
A004.py:4:25: A004 Import `exec` is shadowing a Python builtin
|
2 | import float
3 | from some import other as int
4 | from some import input, exec
| ^^^^ A004
5 | from directory import new as dir
|
A004.py:5:30: A004 Import `dir` is shadowing a Python builtin
|
3 | from some import other as int
4 | from some import input, exec
5 | from directory import new as dir
| ^^^ A004
|

View file

@ -0,0 +1,47 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
A004.py:1:16: A004 Import `sum` is shadowing a Python builtin
|
1 | import some as sum
| ^^^ A004
2 | import float
3 | from some import other as int
|
A004.py:2:8: A004 Import `float` is shadowing a Python builtin
|
1 | import some as sum
2 | import float
| ^^^^^ A004
3 | from some import other as int
4 | from some import input, exec
|
A004.py:3:27: A004 Import `int` is shadowing a Python builtin
|
1 | import some as sum
2 | import float
3 | from some import other as int
| ^^^ A004
4 | from some import input, exec
5 | from directory import new as dir
|
A004.py:4:18: A004 Import `input` is shadowing a Python builtin
|
2 | import float
3 | from some import other as int
4 | from some import input, exec
| ^^^^^ A004
5 | from directory import new as dir
|
A004.py:4:25: A004 Import `exec` is shadowing a Python builtin
|
2 | import float
3 | from some import other as int
4 | from some import input, exec
| ^^^^ A004
5 | from directory import new as dir
|

View file

@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
__init__.py:1:1: A005 Module `logging` is shadowing a Python builtin module

View file

@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---

View file

@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
bisect.py:1:1: A005 Module `bisect` is shadowing a Python builtin module

View file

@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
bisect.py:1:1: A005 Module `bisect` is shadowing a Python builtin module

View file

@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
xml.py:1:1: A005 Module `xml` is shadowing a Python builtin module

View file

@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---

View file

@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
__init__.py:1:1: A005 Module `string` is shadowing a Python builtin module

View file

@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
__init__.py:1:1: A005 Module `string` is shadowing a Python builtin module

View file

@ -0,0 +1,64 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
A006.py:1:8: A006 Lambda argument `print` is shadowing a Python builtin
|
1 | lambda print, copyright: print
| ^^^^^ A006
2 | lambda x, float, y: x + y
3 | lambda min, max: min
|
A006.py:1:15: A006 Lambda argument `copyright` is shadowing a Python builtin
|
1 | lambda print, copyright: print
| ^^^^^^^^^ A006
2 | lambda x, float, y: x + y
3 | lambda min, max: min
|
A006.py:2:11: A006 Lambda argument `float` is shadowing a Python builtin
|
1 | lambda print, copyright: print
2 | lambda x, float, y: x + y
| ^^^^^ A006
3 | lambda min, max: min
4 | lambda id: id
|
A006.py:3:8: A006 Lambda argument `min` is shadowing a Python builtin
|
1 | lambda print, copyright: print
2 | lambda x, float, y: x + y
3 | lambda min, max: min
| ^^^ A006
4 | lambda id: id
5 | lambda dir: dir
|
A006.py:3:13: A006 Lambda argument `max` is shadowing a Python builtin
|
1 | lambda print, copyright: print
2 | lambda x, float, y: x + y
3 | lambda min, max: min
| ^^^ A006
4 | lambda id: id
5 | lambda dir: dir
|
A006.py:4:8: A006 Lambda argument `id` is shadowing a Python builtin
|
2 | lambda x, float, y: x + y
3 | lambda min, max: min
4 | lambda id: id
| ^^ A006
5 | lambda dir: dir
|
A006.py:5:8: A006 Lambda argument `dir` is shadowing a Python builtin
|
3 | lambda min, max: min
4 | lambda id: id
5 | lambda dir: dir
| ^^^ A006
|

View file

@ -0,0 +1,47 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
A006.py:1:8: A006 Lambda argument `print` is shadowing a Python builtin
|
1 | lambda print, copyright: print
| ^^^^^ A006
2 | lambda x, float, y: x + y
3 | lambda min, max: min
|
A006.py:1:15: A006 Lambda argument `copyright` is shadowing a Python builtin
|
1 | lambda print, copyright: print
| ^^^^^^^^^ A006
2 | lambda x, float, y: x + y
3 | lambda min, max: min
|
A006.py:2:11: A006 Lambda argument `float` is shadowing a Python builtin
|
1 | lambda print, copyright: print
2 | lambda x, float, y: x + y
| ^^^^^ A006
3 | lambda min, max: min
4 | lambda id: id
|
A006.py:3:8: A006 Lambda argument `min` is shadowing a Python builtin
|
1 | lambda print, copyright: print
2 | lambda x, float, y: x + y
3 | lambda min, max: min
| ^^^ A006
4 | lambda id: id
5 | lambda dir: dir
|
A006.py:3:13: A006 Lambda argument `max` is shadowing a Python builtin
|
1 | lambda print, copyright: print
2 | lambda x, float, y: x + y
3 | lambda min, max: min
| ^^^ A006
4 | lambda id: id
5 | lambda dir: dir
|

View file

@ -4,6 +4,7 @@ use std::path::Path;
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_stdlib::identifiers::{is_migration_name, is_module_name}; use ruff_python_stdlib::identifiers::{is_migration_name, is_module_name};
use ruff_python_stdlib::path::is_module_file;
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
use crate::rules::pep8_naming::settings::IgnoreNames; use crate::rules::pep8_naming::settings::IgnoreNames;
@ -92,16 +93,6 @@ pub(crate) fn invalid_module_name(
None None
} }
/// Return `true` if a [`Path`] should use the name of its parent directory as its module name.
fn is_module_file(path: &Path) -> bool {
path.file_name().is_some_and(|file_name| {
file_name == "__init__.py"
|| file_name == "__init__.pyi"
|| file_name == "__main__.py"
|| file_name == "__main__.pyi"
})
}
/// Return `true` if a [`Path`] refers to a migration file. /// Return `true` if a [`Path`] refers to a migration file.
fn is_migration_file(path: &Path) -> bool { fn is_migration_file(path: &Path) -> bool {
path.parent() path.parent()

View file

@ -16,6 +16,16 @@ pub fn is_jupyter_notebook(path: &Path) -> bool {
path.extension().is_some_and(|ext| ext == "ipynb") path.extension().is_some_and(|ext| ext == "ipynb")
} }
/// Return `true` if a [`Path`] should use the name of its parent directory as its module name.
pub fn is_module_file(path: &Path) -> bool {
path.file_name().is_some_and(|file_name| {
file_name == "__init__.py"
|| file_name == "__init__.pyi"
|| file_name == "__main__.py"
|| file_name == "__main__.pyi"
})
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::path::Path; use std::path::Path;

View file

@ -1104,12 +1104,20 @@ pub struct Flake8BuiltinsOptions {
)] )]
/// Ignore list of builtins. /// Ignore list of builtins.
pub builtins_ignorelist: Option<Vec<String>>, pub builtins_ignorelist: Option<Vec<String>>,
#[option(
default = r#"[]"#,
value_type = "list[str]",
example = "builtins-allowed-modules = [\"id\"]"
)]
/// List of builtin module names to allow.
pub builtins_allowed_modules: Option<Vec<String>>,
} }
impl Flake8BuiltinsOptions { impl Flake8BuiltinsOptions {
pub fn into_settings(self) -> ruff_linter::rules::flake8_builtins::settings::Settings { pub fn into_settings(self) -> ruff_linter::rules::flake8_builtins::settings::Settings {
ruff_linter::rules::flake8_builtins::settings::Settings { ruff_linter::rules::flake8_builtins::settings::Settings {
builtins_ignorelist: self.builtins_ignorelist.unwrap_or_default(), builtins_ignorelist: self.builtins_ignorelist.unwrap_or_default(),
builtins_allowed_modules: self.builtins_allowed_modules.unwrap_or_default(),
} }
} }
} }

13
ruff.schema.json generated
View file

@ -933,6 +933,16 @@
"Flake8BuiltinsOptions": { "Flake8BuiltinsOptions": {
"type": "object", "type": "object",
"properties": { "properties": {
"builtins-allowed-modules": {
"description": "List of builtin module names to allow.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"builtins-ignorelist": { "builtins-ignorelist": {
"description": "Ignore list of builtins.", "description": "Ignore list of builtins.",
"type": [ "type": [
@ -2669,6 +2679,9 @@
"A001", "A001",
"A002", "A002",
"A003", "A003",
"A004",
"A005",
"A006",
"AIR", "AIR",
"AIR0", "AIR0",
"AIR00", "AIR00",