From 948094e691afd282cfac482e8eb3d476b8c18c4b Mon Sep 17 00:00:00 2001 From: Adrian Date: Wed, 22 Nov 2023 00:44:23 +0100 Subject: [PATCH] [`pylint`] Add `allow-dunder-method-names` setting for `bad-dunder-method-name` (`PLW3201`) (#8812) closes #8732 I noticed that the reference to the setting in the rule docs doesn't work, but there seem to be something wrong with pylint settings in general in the docs - the "For related settings, see ...." is also missing there. --- .../fixtures/pylint/bad_dunder_method_name.py | 6 +++++- crates/ruff_linter/src/rules/pylint/mod.rs | 11 ++++++++++- .../rules/pylint/rules/bad_dunder_method_name.rs | 15 ++++++++++++++- crates/ruff_linter/src/rules/pylint/settings.rs | 3 +++ crates/ruff_workspace/src/options.rs | 12 ++++++++++++ ruff.schema.json | 11 +++++++++++ 6 files changed, 55 insertions(+), 3 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/bad_dunder_method_name.py b/crates/ruff_linter/resources/test/fixtures/pylint/bad_dunder_method_name.py index 760951260e..5032f42ff1 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/bad_dunder_method_name.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/bad_dunder_method_name.py @@ -50,7 +50,7 @@ class Apples: return "Docstring" # Added in Python 3.12 - def __buffer__(self): + def __buffer__(self): return memoryview(b'') def __release_buffer__(self, buf): @@ -83,6 +83,10 @@ class Apples: def _(self): pass + # Allow custom dunder names (via setting). + def __special_custom_magic__(self): + pass + def __foo_bar__(): # this is not checked by the [bad-dunder-name] rule ... diff --git a/crates/ruff_linter/src/rules/pylint/mod.rs b/crates/ruff_linter/src/rules/pylint/mod.rs index 2c4e28ca36..e2f272619b 100644 --- a/crates/ruff_linter/src/rules/pylint/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/mod.rs @@ -9,6 +9,7 @@ mod tests { use anyhow::Result; use regex::Regex; + use rustc_hash::FxHashSet; use test_case::test_case; use crate::assert_messages; @@ -157,7 +158,15 @@ mod tests { let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let diagnostics = test_path( Path::new("pylint").join(path).as_path(), - &LinterSettings::for_rule(rule_code), + &LinterSettings { + pylint: pylint::settings::Settings { + allow_dunder_method_names: FxHashSet::from_iter([ + "__special_custom_magic__".to_string() + ]), + ..pylint::settings::Settings::default() + }, + ..LinterSettings::for_rule(rule_code) + }, )?; assert_messages!(snapshot, diagnostics); Ok(()) diff --git a/crates/ruff_linter/src/rules/pylint/rules/bad_dunder_method_name.rs b/crates/ruff_linter/src/rules/pylint/rules/bad_dunder_method_name.rs index 752c3a6735..87c14839cf 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/bad_dunder_method_name.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/bad_dunder_method_name.rs @@ -22,6 +22,9 @@ use crate::checkers::ast::Checker; /// one underscore (e.g., `_str_`), but ignores known dunder methods (like /// `__init__`), as well as methods that are marked with `@override`. /// +/// Additional dunder methods names can be allowed via the +/// [`pylint.allow-dunder-method-names`] setting. +/// /// ## Example /// ```python /// class Foo: @@ -35,6 +38,9 @@ use crate::checkers::ast::Checker; /// def __init__(self): /// ... /// ``` +/// +/// ## Options +/// - `pylint.allow-dunder-method-names` #[violation] pub struct BadDunderMethodName { name: String, @@ -54,7 +60,14 @@ pub(crate) fn bad_dunder_method_name(checker: &mut Checker, class_body: &[Stmt]) .iter() .filter_map(ruff_python_ast::Stmt::as_function_def_stmt) .filter(|method| { - if is_known_dunder_method(&method.name) || matches!(method.name.as_str(), "_") { + if is_known_dunder_method(&method.name) + || checker + .settings + .pylint + .allow_dunder_method_names + .contains(method.name.as_str()) + || matches!(method.name.as_str(), "_") + { return false; } method.name.starts_with('_') && method.name.ends_with('_') diff --git a/crates/ruff_linter/src/rules/pylint/settings.rs b/crates/ruff_linter/src/rules/pylint/settings.rs index 4925211952..cb9846b11e 100644 --- a/crates/ruff_linter/src/rules/pylint/settings.rs +++ b/crates/ruff_linter/src/rules/pylint/settings.rs @@ -1,5 +1,6 @@ //! Settings for the `pylint` plugin. +use rustc_hash::FxHashSet; use serde::{Deserialize, Serialize}; use ruff_macros::CacheKey; @@ -36,6 +37,7 @@ impl ConstantType { #[derive(Debug, CacheKey)] pub struct Settings { pub allow_magic_value_types: Vec, + pub allow_dunder_method_names: FxHashSet, pub max_args: usize, pub max_returns: usize, pub max_bool_expr: usize, @@ -48,6 +50,7 @@ impl Default for Settings { fn default() -> Self { Self { allow_magic_value_types: vec![ConstantType::Str, ConstantType::Bytes], + allow_dunder_method_names: FxHashSet::default(), max_args: 5, max_returns: 6, max_bool_expr: 5, diff --git a/crates/ruff_workspace/src/options.rs b/crates/ruff_workspace/src/options.rs index fff62ddd6f..6d8569dca4 100644 --- a/crates/ruff_workspace/src/options.rs +++ b/crates/ruff_workspace/src/options.rs @@ -2573,6 +2573,17 @@ pub struct PylintOptions { )] pub allow_magic_value_types: Option>, + /// Dunder methods name to allow, in addition to the default set from the + /// Python standard library (see: `PLW3201`). + #[option( + default = r#"[]"#, + value_type = r#"list[str]"#, + example = r#" + allow-dunder-method-names = ["__tablename__", "__table_args__"] + "# + )] + pub allow_dunder_method_names: Option>, + /// Maximum number of branches allowed for a function or method body (see: /// `PLR0912`). #[option(default = r"12", value_type = "int", example = r"max-branches = 12")] @@ -2614,6 +2625,7 @@ impl PylintOptions { allow_magic_value_types: self .allow_magic_value_types .unwrap_or(defaults.allow_magic_value_types), + allow_dunder_method_names: self.allow_dunder_method_names.unwrap_or_default(), max_args: self.max_args.unwrap_or(defaults.max_args), max_bool_expr: self.max_bool_expr.unwrap_or(defaults.max_bool_expr), max_returns: self.max_returns.unwrap_or(defaults.max_returns), diff --git a/ruff.schema.json b/ruff.schema.json index 57613064df..c4acb767cd 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2307,6 +2307,17 @@ "PylintOptions": { "type": "object", "properties": { + "allow-dunder-method-names": { + "description": "Dunder methods name to allow, in addition to the default set from the Python standard library (see: `PLW3201`).", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + }, + "uniqueItems": true + }, "allow-magic-value-types": { "description": "Constant types to ignore when used as \"magic values\" (see: `PLR2004`).", "type": [