Skip to content

Reject the string format flags CPython refuses - #8571

Merged
youknowone merged 1 commit into
RustPython:mainfrom
luantaraschi:reject-string-format-flags
Aug 22, 2026
Merged

Reject the string format flags CPython refuses#8571
youknowone merged 1 commit into
RustPython:mainfrom
luantaraschi:reject-string-format-flags

Conversation

@luantaraschi

@luantaraschi luantaraschi commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

A sign, a space or an alternate form in a string format spec is accepted and then dropped:

>>> format('ab', '+')
'ab'           # CPython: ValueError: Sign not allowed in string format specifier
>>> f"{'ab':+5}"
'ab   '        # CPython: ValueError: Sign not allowed in string format specifier
>>> format('ab', '#')
'ab'           # CPython: ValueError: Alternate form (#) not allowed in string format specifier

format_string in crates/common/src/format.rs already turns down z and an explicit = alignment for strings, with the wording CPython uses. These three were never checked, so the spec parses, the flag means nothing to a string, and the caller gets plain padding where CPython refuses the spec outright.

The three checks sit next to the two that were already there. Their order is the one CPython reports in, which I read off these, under CPython 3.14:

spec first error what it settles
+z#5 Sign sign comes before z and #
x=z#5 Negative zero coercion (z) z comes before #
x=#5 Alternate form (#) # comes before the = alignment

FormatSpecError gains one variant that carries the flag name, so the three share a message built the same way as StringAlignmentFlag.

Tests

I ran the string spec shapes side by side under CPython 3.14 and this build: the four flags, fill and align pairs, width, precision, grouping, the format codes, and specs where two errors compete. Two differences are left afterwards, neither on this path. Invalid format specifier is still missing the spec and the type, which #8477 covers. And format('ab', '+,5') reports the unknown format code here while CPython reports the grouping.

  • extra_tests/snippets/builtin_format.py: eight specs checked by message, next to the =8s case that was already there. It passes under CPython 3.14 too.
  • two unit tests in crates/common/src/format.rs, one per flag and one for the order in the table
  • cargo run --release -- -m test test_str test_format test_fstring: SUCCESS, 138, 18 and 90 tests
  • cargo test --workspace --exclude rustpython_wasm --exclude rustpython-venvlauncher --exclude rustpython-capi: no failures
  • cargo fmt --check, ruff format --check, ruff check --select I, and clippy with the flags CI uses: clean

test_str.test_format asserts all three, but its marker records an unrelated reason to stay an expected failure ('{0.}'.format() raises ValueError instead of IndexError), so the snippet carries the coverage instead.

AI assistance

Claude Code (claude-opus-5) helped with the comparison against CPython, the change and this description. I read the final diff and ran the checks above on Linux.

Summary by CodeRabbit

  • Bug Fixes

    • Improved string formatting validation to match CPython behavior.
    • String format specifications now correctly reject unsupported sign, space, and alternate-form (#) flags.
    • Errors now report the specific unsupported formatting option and follow the expected validation precedence.
  • Tests

    • Added regression coverage for rejected string format specifications and corresponding ValueError messages.

A sign, a space or an alternate form parsed fine on a string spec and
then had no effect:

    >>> format('ab', '+')
    'ab'
    >>> f"{'ab':RustPython#5}"
    'ab   '

CPython raises ValueError for all three. format_string already refused z
and an explicit '=' alignment with the same family of messages, so the
three checks go next to those, in the order CPython reports them: sign,
then z, then the alternate form, then the alignment.

Assisted-by: Claude Code:claude-opus-5
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f30f721-6552-47b3-bc66-beed14c32630

📥 Commits

Reviewing files that changed from the base of the PR and between 3d2ee64 and c9da10d.

📒 Files selected for processing (3)
  • crates/common/src/format.rs
  • crates/vm/src/format.rs
  • extra_tests/snippets/builtin_format.py

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

String formatting now rejects unsupported sign and alternate-form flags for strings. The formatter reports errors in CPython precedence order, the VM maps them to ValueError, and unit and regression tests verify the error variants and messages.

Changes

String format validation

Layer / File(s) Summary
Validate unsupported string flags
crates/common/src/format.rs
Adds StringSpecNotAllowed and rejects sign and alternate-form flags before later string-format checks. Tests verify the reported flags and CPython precedence.
Map errors and verify messages
crates/vm/src/format.rs, extra_tests/snippets/builtin_format.py
Maps StringSpecNotAllowed to ValueError and adds regression cases for sign, space, and alternate-form flags.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to c9da1

This localized change makes string formatting reject flags that CPython rejects, with tests and checks reported passing; no actionable merge-blocking risk remains.

Suggested reviewers: shaharnaveh, shan-kor, youknowone

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: rejecting string format flags that CPython rejects.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@youknowone
youknowone merged commit 30be390 into RustPython:main Aug 22, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants