diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 78fb6b8c3a..54bc6ee478 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -2900,7 +2900,7 @@ where Rule::FStringInGetTextFuncCall, Rule::FormatInGetTextFuncCall, Rule::PrintfInGetTextFuncCall, - ]) && flake8_gettext::rules::is_gettext_func_call( + ]) && flake8_gettext::is_gettext_func_call( func, &self.settings.flake8_gettext.functions_names, ) { diff --git a/crates/ruff/src/rules/flake8_gettext/mod.rs b/crates/ruff/src/rules/flake8_gettext/mod.rs index d9da7fad86..d9d681a85b 100644 --- a/crates/ruff/src/rules/flake8_gettext/mod.rs +++ b/crates/ruff/src/rules/flake8_gettext/mod.rs @@ -1,7 +1,18 @@ //! Rules from [flake8-gettext](https://pypi.org/project/flake8-gettext/). +use rustpython_parser::ast::{self, Expr}; + pub(crate) mod rules; pub mod settings; +/// Returns true if the [`Expr`] is an internationalization function call. +pub(crate) fn is_gettext_func_call(func: &Expr, functions_names: &[String]) -> bool { + if let Expr::Name(ast::ExprName { id, .. }) = func { + functions_names.contains(id) + } else { + false + } +} + #[cfg(test)] mod tests { use std::path::Path; diff --git a/crates/ruff/src/rules/flake8_gettext/rules/f_string_in_gettext_func_call.rs b/crates/ruff/src/rules/flake8_gettext/rules/f_string_in_gettext_func_call.rs index 89957a02ac..0818ac0d7a 100644 --- a/crates/ruff/src/rules/flake8_gettext/rules/f_string_in_gettext_func_call.rs +++ b/crates/ruff/src/rules/flake8_gettext/rules/f_string_in_gettext_func_call.rs @@ -5,6 +5,40 @@ use ruff_macros::{derive_message_formats, violation}; use crate::checkers::ast::Checker; +/// ## What it does +/// Checks for f-strings in `gettext` function calls. +/// +/// ## Why is this bad? +/// In the `gettext` API, the `gettext` function (often aliased to `_`) returns +/// a translation of its input argument by looking it up in a translation +/// catalog. +/// +/// Calling `gettext` with an f-string as its argument can cause unexpected +/// behavior. Since the f-string is resolved before the function call, the +/// translation catalog will look up the formatted string, rather than the +/// f-string template. +/// +/// Instead, format the value returned by the function call, rather than +/// its argument. +/// +/// ## Example +/// ```python +/// from gettext import gettext as _ +/// +/// name = "Maria" +/// _(f"Hello, {name}!") # Looks for "Hello, Maria!". +/// ``` +/// +/// Use instead: +/// ```python +/// from gettext import gettext as _ +/// +/// name = "Maria" +/// _("Hello, %s!") % name # Looks for "Hello, %s!". +/// ``` +/// +/// ## References +/// - [Python documentation: gettext](https://docs.python.org/3/library/gettext.html) #[violation] pub struct FStringInGetTextFuncCall; diff --git a/crates/ruff/src/rules/flake8_gettext/rules/format_in_gettext_func_call.rs b/crates/ruff/src/rules/flake8_gettext/rules/format_in_gettext_func_call.rs index 2f99369f25..813d392821 100644 --- a/crates/ruff/src/rules/flake8_gettext/rules/format_in_gettext_func_call.rs +++ b/crates/ruff/src/rules/flake8_gettext/rules/format_in_gettext_func_call.rs @@ -5,6 +5,40 @@ use ruff_macros::{derive_message_formats, violation}; use crate::checkers::ast::Checker; +/// ## What it does +/// Checks for `str.format` calls in `gettext` function calls. +/// +/// ## Why is this bad? +/// In the `gettext` API, the `gettext` function (often aliased to `_`) returns +/// a translation of its input argument by looking it up in a translation +/// catalog. +/// +/// Calling `gettext` with a formatted string as its argument can cause +/// unexpected behavior. Since the formatted string is resolved before the +/// function call, the translation catalog will look up the formatted string, +/// rather than the `str.format`-style template. +/// +/// Instead, format the value returned by the function call, rather than +/// its argument. +/// +/// ## Example +/// ```python +/// from gettext import gettext as _ +/// +/// name = "Maria" +/// _("Hello, %s!" % name) # Looks for "Hello, Maria!". +/// ``` +/// +/// Use instead: +/// ```python +/// from gettext import gettext as _ +/// +/// name = "Maria" +/// _("Hello, %s!") % name # Looks for "Hello, %s!". +/// ``` +/// +/// ## References +/// - [Python documentation: gettext](https://docs.python.org/3/library/gettext.html) #[violation] pub struct FormatInGetTextFuncCall; diff --git a/crates/ruff/src/rules/flake8_gettext/rules/is_gettext_func_call.rs b/crates/ruff/src/rules/flake8_gettext/rules/is_gettext_func_call.rs deleted file mode 100644 index e2f78c4476..0000000000 --- a/crates/ruff/src/rules/flake8_gettext/rules/is_gettext_func_call.rs +++ /dev/null @@ -1,10 +0,0 @@ -use rustpython_parser::ast::{self, Expr}; - -/// Returns true if the [`Expr`] is an internationalization function call. -pub(crate) fn is_gettext_func_call(func: &Expr, functions_names: &[String]) -> bool { - if let Expr::Name(ast::ExprName { id, .. }) = func { - functions_names.contains(id) - } else { - false - } -} diff --git a/crates/ruff/src/rules/flake8_gettext/rules/mod.rs b/crates/ruff/src/rules/flake8_gettext/rules/mod.rs index 490011e5fb..da86d9ce3c 100644 --- a/crates/ruff/src/rules/flake8_gettext/rules/mod.rs +++ b/crates/ruff/src/rules/flake8_gettext/rules/mod.rs @@ -1,9 +1,7 @@ pub(crate) use f_string_in_gettext_func_call::*; pub(crate) use format_in_gettext_func_call::*; -pub(crate) use is_gettext_func_call::*; pub(crate) use printf_in_gettext_func_call::*; mod f_string_in_gettext_func_call; mod format_in_gettext_func_call; -mod is_gettext_func_call; mod printf_in_gettext_func_call; diff --git a/crates/ruff/src/rules/flake8_gettext/rules/printf_in_gettext_func_call.rs b/crates/ruff/src/rules/flake8_gettext/rules/printf_in_gettext_func_call.rs index eab5d74c93..082ff1ae6c 100644 --- a/crates/ruff/src/rules/flake8_gettext/rules/printf_in_gettext_func_call.rs +++ b/crates/ruff/src/rules/flake8_gettext/rules/printf_in_gettext_func_call.rs @@ -4,6 +4,40 @@ use crate::checkers::ast::Checker; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; +/// ## What it does +/// Checks for printf-style formatted strings in `gettext` function calls. +/// +/// ## Why is this bad? +/// In the `gettext` API, the `gettext` function (often aliased to `_`) returns +/// a translation of its input argument by looking it up in a translation +/// catalog. +/// +/// Calling `gettext` with a formatted string as its argument can cause +/// unexpected behavior. Since the formatted string is resolved before the +/// function call, the translation catalog will look up the formatted string, +/// rather than the printf-style template. +/// +/// Instead, format the value returned by the function call, rather than +/// its argument. +/// +/// ## Example +/// ```python +/// from gettext import gettext as _ +/// +/// name = "Maria" +/// _("Hello, {}!".format(name)) # Looks for "Hello, Maria!". +/// ``` +/// +/// Use instead: +/// ```python +/// from gettext import gettext as _ +/// +/// name = "Maria" +/// _("Hello, %s!") % name # Looks for "Hello, %s!". +/// ``` +/// +/// ## References +/// - [Python documentation: gettext](https://docs.python.org/3/library/gettext.html) #[violation] pub struct PrintfInGetTextFuncCall;