Avoid converting f-strings within Django gettext calls (#7898)

## Summary

Django's `gettext` doesn't support f-strings, so we should avoid
translating `.format` calls in those cases.

Closes https://github.com/astral-sh/ruff/issues/7891.
This commit is contained in:
Charlie Marsh 2023-10-10 12:31:09 -04:00 committed by GitHub
parent 2b95d3832b
commit 090c1a4a19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 0 deletions

View file

@ -0,0 +1,9 @@
from django.utils.translation import gettext
long = 'long'
split_to = 'split_to'
gettext(
'some super {} and complicated string so that the error code '
'E501 Triggers when this is not {} multi-line'.format(
long, split_to)
)

View file

@ -28,6 +28,7 @@ mod tests {
#[test_case(Rule::FString, Path::new("UP032_0.py"))] #[test_case(Rule::FString, Path::new("UP032_0.py"))]
#[test_case(Rule::FString, Path::new("UP032_1.py"))] #[test_case(Rule::FString, Path::new("UP032_1.py"))]
#[test_case(Rule::FString, Path::new("UP032_2.py"))] #[test_case(Rule::FString, Path::new("UP032_2.py"))]
#[test_case(Rule::FString, Path::new("UP032_3.py"))]
#[test_case(Rule::FormatLiterals, Path::new("UP030_0.py"))] #[test_case(Rule::FormatLiterals, Path::new("UP030_0.py"))]
#[test_case(Rule::FormatLiterals, Path::new("UP030_1.py"))] #[test_case(Rule::FormatLiterals, Path::new("UP030_1.py"))]
#[test_case(Rule::LRUCacheWithMaxsizeNone, Path::new("UP033_0.py"))] #[test_case(Rule::LRUCacheWithMaxsizeNone, Path::new("UP033_0.py"))]

View file

@ -328,6 +328,7 @@ pub(crate) fn f_strings(
let Some(mut summary) = FormatSummaryValues::try_from_call(call, checker.locator()) else { let Some(mut summary) = FormatSummaryValues::try_from_call(call, checker.locator()) else {
return; return;
}; };
let mut patches: Vec<(TextRange, String)> = vec![]; let mut patches: Vec<(TextRange, String)> = vec![];
let mut lex = lexer::lex_starts_at( let mut lex = lexer::lex_starts_at(
checker.locator().slice(call.func.range()), checker.locator().slice(call.func.range()),
@ -405,6 +406,25 @@ pub(crate) fn f_strings(
return; return;
} }
// Finally, avoid refactors that would introduce a runtime error.
// For example, Django's `gettext` supports `format`-style arguments, but not f-strings.
// See: https://docs.djangoproject.com/en/4.2/topics/i18n/translation
if checker.semantic().current_expressions().any(|expr| {
expr.as_call_expr().is_some_and(|call| {
checker
.semantic()
.resolve_call_path(call.func.as_ref())
.map_or(false, |call_path| {
matches!(
call_path.as_slice(),
["django", "utils", "translation", "gettext" | "gettext_lazy"]
)
})
})
}) {
return;
}
let mut diagnostic = Diagnostic::new(FString, call.range()); let mut diagnostic = Diagnostic::new(FString, call.range());
// Avoid fix if there are comments within the call: // Avoid fix if there are comments within the call:

View file

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