Skip to content

[ruff] New rule RUF073: warn when using % operator on an f-string#24162

Merged
MichaReiser merged 3 commits intoastral-sh:mainfrom
RenzoMXD:feat/ruf072-fstring-percent-format
Mar 25, 2026
Merged

[ruff] New rule RUF073: warn when using % operator on an f-string#24162
MichaReiser merged 3 commits intoastral-sh:mainfrom
RenzoMXD:feat/ruf072-fstring-percent-format

Conversation

@RenzoMXD
Copy link
Copy Markdown
Contributor

Summary

Fixes #24159

Adds a new rule RUF072 (fstring-percent-format) that flags any use of the % operator on an f-string.

Mixing f-string interpolation with %-formatting is almost certainly a mistake since both serve the same purpose. There's no valid use case for using % on an f-string.

# Flagged
f"{name}" % name
f"hello %s %s" % (1, 2)
f"value: {x}" % {"key": "value"}

# OK — plain string literals are handled by existing F50x rules
"hello %s" % name
"%s %s" % (1, 2)

Test Plan

  • Added test cases for f-strings with % on various RHS types (variables, tuples, dicts, literals)
  • Added test cases for plain string literals (not flagged — handled by F50x)
  • Verified existing ruff and pyflakes tests still pass
cargo test -p ruff_linter -- fstring_percent_format

Test file: crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF072_RUF072.py.snap

Test cases cover:

  • F-string with % and variable RHS: f"{banana}" % banana
  • F-string with % and tuple RHS: f"hello %s %s" % (1, 2)
  • F-string with % and dict RHS: f"value: {x}" % {"key": "value"}
  • F-string with % and literal RHS: f"{x}" % 42
  • Plain string literals not flagged (handled by existing F50x rules)

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Mar 24, 2026

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+1 -0 violations, +0 -0 fixes in 1 projects; 55 projects unchanged)

pytest-dev/pytest (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ src/_pytest/main.py:622:16: RUF073 `%` operator used on an f-string

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
RUF073 1 1 0 0 0

@MichaReiser
Copy link
Copy Markdown
Member

I'll close this for now until we have agreement on #24159. We can always reopen it

@MichaReiser MichaReiser reopened this Mar 25, 2026
@MichaReiser MichaReiser added rule Implementing or modifying a lint rule preview Related to preview mode features labels Mar 25, 2026
Copy link
Copy Markdown
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

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

Thanks. This overall looks good to me.

/// "hello %s %s" % (1, 2)
/// ```
#[derive(ViolationMetadata)]
#[violation_metadata(preview_since = "0.15.7")]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
#[violation_metadata(preview_since = "0.15.7")]
#[violation_metadata(preview_since = "RUFF_NEXT_VERSION")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm sorry, it's NEXT_RUFF_VERSION

/// ## Example
/// ```python
/// f"{name}" % name
/// f"hello %s %s" % (1, 2)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's use variables instead of 1 and 2 and I think we should recommend using f-string interpolation over %.

@MichaReiser
Copy link
Copy Markdown
Member

This now requires a rebase.

@RenzoMXD RenzoMXD force-pushed the feat/ruf072-fstring-percent-format branch from bc9651f to ff21afc Compare March 25, 2026 12:40
@RenzoMXD RenzoMXD force-pushed the feat/ruf072-fstring-percent-format branch from 0ad7bb2 to 8692aad Compare March 25, 2026 12:57
@MichaReiser
Copy link
Copy Markdown
Member

Thank you

@MichaReiser MichaReiser merged commit 32f644c into astral-sh:main Mar 25, 2026
42 checks passed
carljm added a commit that referenced this pull request Mar 25, 2026
* main:
  [ty] make `test-case` a dev-dependency (#24187)
  [ty] implement cycle normalization for more types to prevent too-many-cycle panics (#24061)
  [ty] Silence all diagnostics in unreachable code (#24179)
  [ty] Intern `InferableTypeVars` (#24161)
  Implement unnecessary-if (RUF050) (#24114)
  Recognize `Self` annotation and `self` assignment in SLF001 (#24144)
  Bump the npm version before publish (#24178)
  [ty] Disallow Self in metaclass and static methods (#23231)
  Use trusted publishing for NPM packages (#24171)
  [ty] Respect non-explicitly defined dataclass params (#24170)
  Add RUF072: warn when using  operator on an f-string (#24162)
  [ty] Check return type of generator functions (#24026)
  Implement useless-finally (RUF-072) (#24165)
  [ty] Add test for a dataclass with a default field converter (#24169)
  [ty] Dataclass field converters (#23088)
  [flake8-bandit] Treat sys.executable as trusted input in S603 (#24106)
  [ty] Add support for `typing.Concatenate` (#23689)
  `ASYNC115`: autofix to use full qualified `anyio.lowlevel` import (#24166)
  [ty] Disallow read-only fields in TypedDict updates (#24128)
  Speed up diagnostic rendering (#24146)
@amyreese amyreese changed the title Add RUF072: warn when using operator on an f-string Add RUF072: warn when using % operator on an f-string Mar 26, 2026
@amyreese amyreese changed the title Add RUF072: warn when using % operator on an f-string Add RUF073: warn when using % operator on an f-string Mar 26, 2026
@amyreese amyreese changed the title Add RUF073: warn when using % operator on an f-string [ruff] New rule RUF073: warn when using % operator on an f-string Mar 26, 2026
nicopauss pushed a commit to Intersec/lib-common that referenced this pull request Apr 1, 2026
##### [\`v0.15.8\`](https://github.com/astral-sh/ruff/blob/HEAD/CHANGELOG.md#0158)

Released on 2026-03-26.

##### Preview features

- \[`ruff`] New rule `unnecessary-if` (`RUF050`) ([#24114](astral-sh/ruff#24114))
- \[`ruff`] New rule `useless-finally` (`RUF072`) ([#24165](astral-sh/ruff#24165))
- \[`ruff`] New rule `f-string-percent-format` (`RUF073`): warn when using `%` operator on an f-string ([#24162](astral-sh/ruff#24162))
- \[`pyflakes`] Recognize `frozendict` as a builtin for Python 3.15+ ([#24100](astral-sh/ruff#24100))

##### Bug fixes

- \[`flake8-async`] Use fully-qualified `anyio.lowlevel` import in autofix (`ASYNC115`) ([#24166](astral-sh/ruff#24166))
- \[`flake8-bandit`] Check tuple arguments for partial paths in `S607` ([#24080](astral-sh/ruff#24080))
- \[`pyflakes`] Skip `undefined-name` (`F821`) for conditionally deleted variables ([#24088](astral-sh/ruff#24088))
- `E501`/`W505`/formatter: Exclude nested pragma comments from line width calculation ([#24071](astral-sh/ruff#24071))
- Fix `%foo?` parsing in IPython assignment expressions ([#24152](astral-sh/ruff#24152))
- `analyze graph`: resolve string imports that reference attributes, not just modules ([#24058](astral-sh/ruff#24058))

##### Rule changes

- \[`eradicate`] ignore `ty: ignore` comments in `ERA001` ([#24192](astral-sh/ruff#24192))
- \[`flake8-bandit`] Treat `sys.executable` as trusted input in `S603` ([#24106](astral-sh/ruff#24106))
- \[`flake8-self`] Recognize `Self` annotation and `self` assignment in `SLF001` ([#24144](astral-sh/ruff#24144))
- \[`pyflakes`] `F507`: Fix false negative for non-tuple RHS in `%`-formatting ([#24142](astral-sh/ruff#24142))
- \[`refurb`] Parenthesize generator arguments in `FURB142` fixer ([#24200](astral-sh/ruff#24200))

##### Performance

- Speed up diagnostic rendering ([#24146](astral-sh/ruff#24146))

##### Server

- Warn when Markdown files are skipped due to preview being disabled ([#24150](astral-sh/ruff#24150))

##### Documentation

- Clarify `extend-ignore` and `extend-select` settings documentation ([#24064](astral-sh/ruff#24064))
- Mention AI policy in PR template ([#24198](astral-sh/ruff#24198))

##### Other changes

- Use trusted publishing for NPM packages ([#24171](astral-sh/ruff#24171))

##### Contributors

- [@bitloi](https://github.com/bitloi)
- [@Sim-hu](https://github.com/Sim-hu)
- [@mvanhorn](https://github.com/mvanhorn)
- [@chinar-amrutkar](https://github.com/chinar-amrutkar)
- [@markjm](https://github.com/markjm)
- [@RenzoMXD](https://github.com/RenzoMXD)
- [@vivekkhimani](https://github.com/vivekkhimani)
- [@seroperson](https://github.com/seroperson)
- [@moktamd](https://github.com/moktamd)
- [@charliermarsh](https://github.com/charliermarsh)
- [@ntBre](https://github.com/ntBre)
- [@zanieb](https://github.com/zanieb)
- [@dylwil3](https://github.com/dylwil3)
- [@MichaReiser](https://github.com/MichaReiser)

Renovate-Branch: renovate/2024.6-ruff-0.15.x
Change-Id: Ifd4216a963962ffb24a4df69802bc60fcc29628d
Priv-Id: 46d2f61be3a5e65a9fdd2fef998ba41ea3388f12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Related to preview mode features rule Implementing or modifying a lint rule

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Warn when using % operator on an f-string

3 participants