Skip to content

Comments

[ruff] Suppress diagnostic for strings with backslashes in interpolations before Python 3.12 (RUF027)#21069

Merged
ntBre merged 6 commits intoastral-sh:mainfrom
danparizher:fix-21033
Feb 16, 2026
Merged

[ruff] Suppress diagnostic for strings with backslashes in interpolations before Python 3.12 (RUF027)#21069
ntBre merged 6 commits intoastral-sh:mainfrom
danparizher:fix-21033

Conversation

@danparizher
Copy link
Contributor

@danparizher danparizher commented Oct 24, 2025

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_fstring function 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_fstring function to suppress RUF027 for strings containing backslashes within interpolation expressions when the target Python version is below 3.12. This ensures:

  • For Python < 3.12: RUF027 is suppressed for strings with backslashes in interpolations (e.g., "{'\\n'}{x}")
  • For Python ≥ 3.12: RUF027 works normally for strings with backslashes in interpolations

@github-actions
Copy link
Contributor

github-actions bot commented Oct 24, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@dscorbett
Copy link

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.
@danparizher danparizher changed the title [ruff] Suppress strings with backslashes before Python 3.12 (RUF027) [ruff] Suppress strings with backslashes in interpolations before Python 3.12 (RUF027) Oct 25, 2025
@ntBre
Copy link
Contributor

ntBre commented Oct 27, 2025

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 ValueError, not an f-string syntax error. It looks like the code here would flag this because it's within the interpolation but not within the interpolation expression.

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 interpolations iterator doesn't recurse into nested interpolations, so the fix is also not as easy as only checking the expression part (that's what I got wrong in #20867).

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.

@ntBre ntBre added bug Something isn't working fixes Related to suggested fixes for violations labels Oct 27, 2025
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.
@danparizher danparizher requested a review from ntBre October 27, 2025 22:15
Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 d

But 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.

@ntBre ntBre removed the fixes Related to suggested fixes for violations label Feb 16, 2026
@ntBre ntBre changed the title [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) Feb 16, 2026
@ntBre ntBre merged commit 2534a01 into astral-sh:main Feb 16, 2026
42 checks passed
@danparizher danparizher deleted the fix-21033 branch February 16, 2026 21:33
carljm added a commit that referenced this pull request Feb 16, 2026
* 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)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RUF027 creates invalid f-string interpolations with backslashes before Python 3.12

3 participants