[ruff] Suppress diagnostic for strings with backslashes in interpolations before Python 3.12 (RUF027)#21069
Conversation
|
|
Backslashes in f-strings aren’t the problem. >>> f"\n"
'\n'The problem is backslashes in f-string interpolations. >>> f"{'\n'}"
File "<stdin>", line 1
f"{'\n'}"
^
SyntaxError: f-string expression part cannot include a backslash |
Updated the RUF027 rule to only skip f-string suggestions for Python < 3.12 when backslashes appear in interpolations, not in the entire string. Adjusted test cases and snapshots to reflect this refined behavior.
ruff] Suppress strings with backslashes before Python 3.12 (RUF027)ruff] Suppress strings with backslashes in interpolations before Python 3.12 (RUF027)
|
I just worked on two issues for our syntax error related to this: #20867 and #20949. I think the fix here is similar to the incorrect code I had originally in the parser, which turned out to be too naive in the presence of nested interpolations. To summarize the two issues, code like this is valid on 3.11: >>> f"{1:\"d\"}"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Invalid format specifier '"d"' for object of type 'int'Note the In contrast, code like this is still invalid on 3.11 because it's in the expression part of a nested interpolation: >>> f'{1: abcd "{'aa'}" }'
File "<stdin>", line 1
f'{1: abcd "{'aa'}" }'
^^
SyntaxError: f-string: expecting '}'But the It might be fair to be more conservative here since it's almost definitely easier to implement, but I wanted to mention this since it was relevant. |
crates/ruff_linter/src/rules/ruff/rules/missing_fstring_syntax.rs
Outdated
Show resolved
Hide resolved
...uff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF027_RUF027_0.py.snap
Show resolved
Hide resolved
Updates the MissingFStringSyntax rule to correctly skip diagnostics for f-string interpolations containing backslashes when targeting Python 3.11, matching Python's behavior. Adds a dedicated test and snapshot to verify the change.
ntBre
left a comment
There was a problem hiding this comment.
Thank you!
I pushed a couple of commits, mainly just using assert_diagnostics_diff! to highlight the difference between the two Python versions. I also thought about adding a test like:
def false_negative(c):
print("{c:\x64}") # \x64 is dBut apparently this is an existing false negative because FormatSpec::parse fails to parse this format spec, even though it works fine even on Python 3.11:
Python 3.11.13 (main, Sep 2 2025, 14:20:25) [Clang 20.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f"{1:\x64}"
'1'(You can see that GitHub's syntax highlighting doesn't like it either)
So our check for \ in the whole interpolation is actually the best we can do for now. I'll open a follow-up issue for FormatSpec.
ruff] Suppress strings with backslashes in interpolations before Python 3.12 (RUF027)ruff] Suppress diagnostic for strings with backslashes in interpolations before Python 3.12 (RUF027)
* main: (43 commits) [`ruff`] Suppress diagnostic for strings with backslashes in interpolations before Python 3.12 (`RUF027`) (#21069) [flake8-bugbear] Fix B023 false positive for immediately-invoked lambdas (#23294) [ty] Add `Final` mdtests for loops and redeclaration (#23331) [`flake8-pyi`] Also check string annotations (`PYI041`) (#19023) Remove AlexWaygood as a flake8-pyi codeowner (#23347) [ty] Add comments to clarify the purpose of `NominalInstanceType::class_name` and `NominalInstanceType::class_module_name` (#23339) Add attestations for release artifacts and Docker images (#23111) [ty] Fix `assert_type` diagnostic messages (#23342) [ty] Force-update all insta snapshots (#23343) Add Q004 to the list of conflicting rules (#23340) [ty] Fix `invalid-match-pattern` false positives (#23338) [ty] new diagnostic called-match-pattern-must-be-a-type (#22939) [ty] Update flaky projects (#23337) [ty] Increase timeout for ecosystem report to 40 min (#23336) Bump ecosystem-analyzer pin (#23335) [ty] Replace `strsim` with CPython-based Levenshtein implementation (#23291) [ty] Add mdtest for staticmethod assigned in class body (#23330) [ty] fix inferring type variable from string literal argument (#23326) [ty] bytes literal is a sequence of integers (#23329) Update rand and getrandom (#23333) ...
Summary
Fixes #21033 (RUF027) to suppress f-string suggestions for strings containing backslashes in interpolation expressions when targeting Python versions below 3.12, preventing syntax errors.
Problem Analysis
RUF027 was suggesting f-string conversions for strings with backslashes in interpolation expressions (e.g.,
"{'\\n'}"), but f-strings with backslashes in interpolations are only valid syntax in Python 3.12+. This caused syntax errors when the fix was applied to code targeting older Python versions.The issue occurred because the
should_be_fstringfunction was checking for backslashes anywhere in the string literal, rather than specifically within the interpolation expressions where the syntax restriction applies.Approach
Added a version check in the
should_be_fstringfunction to suppress RUF027 for strings containing backslashes within interpolation expressions when the target Python version is below 3.12. This ensures:"{'\\n'}{x}")