Skip to content

generated code: keep source.py parseable/runnable on a pre-3.13 target (PyPy 3.11) - #256

Merged
devdanzin merged 1 commit into
mainfrom
pypy311-fstring-compat
Aug 18, 2026
Merged

generated code: keep source.py parseable/runnable on a pre-3.13 target (PyPy 3.11)#256
devdanzin merged 1 commit into
mainfrom
pypy311-fstring-compat

Conversation

@devdanzin

Copy link
Copy Markdown
Owner

Why

fusil's requires-python floor of 3.13 constrains the runner. The generated
source.py is executed by --python, which is routinely older or alternative — PyPy 3.11
is a fuzzing target in its own right.

Two 3.12+/3.13+ constructs leaked into the emitted script and aborted every session
before any fuzzing happened
:

  1. f"{expr!r }" — a space between the conversion and the closing brace is PEP 701
    syntax (3.12+). On a 3.11 target the entire script is a SyntaxError, so nothing
    parses. Three emitter sites in write_python_code.py; tightening them to {expr!r}
    leaves the printed text byte-identical.
  2. types.CapsuleType — 3.13+, read unguarded in the tricky-objects prelude. On PyPy
    3.11 every session died at that line with AttributeError. Now guarded exactly the way
    the neighbouring types.GenericAlias already is; the if tricky_capsule: use site
    immediately below already expects a falsy value.

Why this is worth a test

Both failure modes are invisible from the outside: the run looks clean — no crashes,
no kept dirs — because nothing ever ran. A 12-session PyPy run reported zero findings while
in fact zero calls had been fuzzed.

Measured on PyPy 3.11.15 / 7.3.23 (--discover-in-target, CPython 3.14 runner): sessions
went from dying in the prelude to thousands of lines of real activity each.

Tests

tests/python/test_target_py311_compat.py — verified each test fails when its fix is
reverted:

  • execs the tricky-objects prelude under a types shim with CapsuleType removed, asserting
    it survives and yields None;
  • scans every emitter under fusil/ for a conversion field followed by whitespace, and
    checks the golden snapshot for the same.

Full suite green (1247 tests); ruff check + ruff format --check clean. Golden snapshot
regenerated for the intentional output change.

🤖 Generated with Claude Code

fusil's own floor is 3.13, but that constrains the *runner*. The generated script is
executed by --python, which is routinely older -- PyPy 3.11 is a fuzzing target in its
own right. Two constructs leaked into the emitted script and killed every session
before any fuzzing happened:

- `f"{expr!r }"` (a space between the conversion and the closing brace) is PEP 701
  syntax, so the whole script is a SyntaxError on a 3.11 target. Three emitter sites in
  write_python_code.py; tightening them to `{expr!r}` leaves the printed text identical.
- `types.CapsuleType` is 3.13+, read unguarded in the tricky-objects prelude. On PyPy
  3.11 every session died at that line with AttributeError. Guarded the same way the
  neighbouring `types.GenericAlias` already is; the `if tricky_capsule:` use site below
  it already expects a falsy value.

Both failure modes are invisible from the outside: the run looks *clean* (no crashes
kept) because nothing ever ran. Measured on PyPy 3.11.15 / 7.3.23, a 12-session run went
from 0 fuzzed calls to thousands of lines of real activity per session.

tests/python/test_target_py311_compat.py locks both in (each fails if its fix is
reverted). Golden snapshot regenerated for the intentional output change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WhcpLoyjUWLbETGZnA9boj
@devdanzin
devdanzin merged commit 6a54d58 into main Aug 18, 2026
1 of 2 checks passed
@devdanzin
devdanzin deleted the pypy311-fstring-compat branch August 18, 2026 20:55
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.

1 participant