Skip to content

Implement PEP 798 comprehension unpacking - #9342

Merged
youknowone merged 4 commits into
RustPython:mainfrom
youknowdot:split/pep798-comprehension-unpacking
Oct 27, 2026
Merged

youknowone merged 4 commits into
RustPython:mainfrom
youknowdot:split/pep798-comprehension-unpacking

Conversation

@youknowdot

@youknowdot youknowdot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Split the PEP 798 implementation from #8954.

Current draft status

This PR implements RustPython's AST/compiler execution support, not a new Ruff grammar implementation. Ruff upstream completed comprehension unpacking in astral-sh/ruff#25104, released in 0.15.14. The Ruff revision already on RustPython main (9290a8849542134d83f77e9360e7f0b93897fd93) parses the valid list, set, dictionary, and generator-expression forms. Waiting for upstream PEP 798 support or upgrading to Ruff 0.17.2 is not necessary for those forms.

Hosted correctness CI for 247eb615 failed in the CPython test jobs on Linux, macOS, Windows, and WASI; their build and snippet steps passed. The Linux log identifies two failing modules: test_exceptions retains the obsolete expectation that [*x for x in xs] raises SyntaxError, and test_unpack_ex has fourteen malformed-input diagnostic mismatches. These are canonical-test migration and CPython-compatible error-message/location gaps, not missing valid-syntax parsing.

The successor candidate now migrates test_exceptions.py as a whole canonical file through the existing scripts/update_lib workflow and adds a narrow RustPython-side normalization of existing parser diagnostics. Ruff source, its Cargo revision, and Cargo.lock are unchanged. The full warning/type-comment producer extension in RustPython/ruff#7 is not required. The latest follow-up fixes all eight remaining malformed-input canonical diagnostics in finite local comparisons; all 30 canonical exec/doctest-mode records now match CPython. Correctness CI for the latest commit ddc36651932bf9f3a3836e23e1085ca364e25870 passed all 24 jobs, and Lib Dependencies Check passed. Surrounding malformed inputs retain the known finite-comparison differences documented below. Whole-file migration also brings other exception/location and resource-sensitive cases; the complete test_exceptions module has not been runtime-validated locally. No selective method copies or new skips/xfails were introduced.

Changes

  • Compile list/set comprehension unpacking using the existing extend/update operations, and dictionary comprehension unpacking using DictUpdate.
  • Flatten unpacked generator-expression items through an explicit iterator loop, including async generator wrapping. Sent values are not forwarded to the inner iterator.
  • Handle unpacking dictionary comprehensions consistently in symbol-table traversal, AST conversion, validation, and generated AST documentation.
  • Migrate test_unpack_ex.py, test_unparse.py, and test_exceptions.py as whole canonical files using scripts/update_lib, pinned to CPython d220409a13b77de7a6c30abe16ea90db01528edc.

The Ruff revision already on main parses these forms: starred comprehension elements use Expr::Starred, while dictionary unpacking uses ExprDictComp with no key. This PR implements the RustPython AST/compiler side. It does not change Ruff, VM instruction definitions, unrelated compiler location handling, or interpreter lifecycle code. No custom tests or snippets are added.

Historical validation

Validated original commit: 247eb615c61e3f167245d55618fbfad611b5fd37.

  • Normal commit hooks, including Rust/Ruff formatting and lint checks, passed.
  • cargo check --offline --locked -p rustpython-codegen -p rustpython-vm --lib passed.
  • cargo clippy --offline --locked -p rustpython-codegen -p rustpython-vm --lib -- -D warnings passed.
  • cargo build --offline --locked --bin rustpython succeeded. The exact Cargo artifact was independently copied and sealed with its committed standard library.
  • 37 unchanged canonical examples from two bounded test_unpack_ex doctest segments passed under ordinary checkers. Their raw output matched pinned CPython exactly.
  • Six finite synchronous/asynchronous observations matched pinned CPython byte-for-byte, including generator send() behavior.
  • Seven unchanged canonical AST/unparse methods passed individually, with no skips.

The full test_unpack_ex module and broader parser, lifecycle, GC and fuzz suites were not run locally. Local validation used the unchanged commit above; a source-only merge check against main 5bccf31156996d42882485ba70f17edbc77a713e was conflict-free and retained the same nine-file patch.

Historical successor local validation (partial)

Validated candidate tree: 21011e3035c2f5cce41935d2bd719e274212fb5e (successor of 247eb615; separate from the historical results above). Final local commit: 6ff465bd7c678c94ce455e7f35ca51e5b166b52d, tree be9bba0359348c12c2ac366aeca31b2d7cc5148f. Normal commit hooks passed. The final tree differs from the validated tree only by two source-reviewed, token-equivalent rustfmt line wraps; the six build gates and paired runtime checks below were run before that formatting-only change, not rerun on the final commit.

  • Compiler and VM checks/strict Clippy, and the minimal RustPython binary build/strict Clippy, passed: six bounded gates.
  • Compared the same 34 finite compile-only inputs against freshly built official CPython d220409a13b77de7a6c30abe16ea90db01528edc. No compiled case bytecode was executed; canonical expected messages were unchanged.
  • All six corrected canonical conditional-unpacking diagnostics (examples 87–92) matched CPython's message, source text and location fields. Including the existing passing example98, this is 7/15 canonical diagnostic messages, not a passing full module.
  • 17/34 inputs matched every recorded field. The 19 boundary controls contain 10 exact matches and 9 remaining mismatches. Narrowing the double-star normalization removed two unintended new hints (subsequent dictionary spread and dictionary value), but did not make those two diagnostics CPython-compatible. All other results within these 34 inputs were unchanged, and the CPython oracle was stable.
  • Eight canonical cases remain unresolved: dictionary unpacking in list comprehension (93), parenthesized/call generator expressions (94/95), starred/double-starred dictionary keys (96/97), double-starred dictionary value (99), parenthesized double-star expression (100), and unpacked dictionary comprehension followed by a colon (101). These need further ownership/range/diagnostic work; no parser changes or expectation relaxations were used to hide them.

The whole-file test_exceptions update is included but its full runtime suite was not run. No full-suite success, global non-regression, or green successor CI is claimed. This PR remains a draft.

Historical mapping-rest CI follow-up (RustPython only; Ruff unchanged)

  • Correct the mapping-rest diagnostic to point at the rest capture rather than the following mapping item. The adapter uses the existing parser error and adjacent token information, and abstains when there are additional parser errors.
  • Retire the stale SyntaxErrorTests.test_encodings expected-failure marker through the existing whole-file update_lib --patches workflow. Linux, macOS, and Windows CI all reported this test as an unexpected success. Preserve the existing Windows-only test_windows_message marker; add no skip or expected failure.
  • No Ruff source, revision, version, Cargo manifest, or lockfile changes.

Validation:

  • Compiler and threading-VM checks/Clippy, plus the minimal binary build/Clippy, passed (six gates).
  • Compiled the same 56 finite source inputs, without executing their code, under the preserved previous RustPython binary, the new binary, and canonical CPython 3.15 (d220409a13b77de7a6c30abe16ea90db01528edc). The two CI mapping cases now match CPython exactly; all 34 previous unpacking-diagnostic results are unchanged.
  • Strict full-field comparison did not pass: nine mapping outputs changed; eight match CPython exactly. The multiline/comment case now has the correct message and complete location range, but RustPython's unchanged SyntaxError.text serialization adds a trailing newline that CPython omits. The strict comparison and this residual are retained, not relaxed or hidden by dropping the case.
  • The eight previously identified unpacking-diagnostic failures remain outside this follow-up. These focused results do not establish full CI success.

Unpacking diagnostic follow-up

Validated commit: ddc36651932bf9f3a3836e23e1085ca364e25870.

  • Fix the eight remaining canonical unpacking diagnostics on the RustPython side. Ruff source, version, dependency pins, and test expectations are unchanged.
  • Use retained AST/operator/error ownership for the list/generator/dictionary cases. For the call-generator case only, validate one exactly owned argument slice with the same fixed Ruff parser and options; do not infer clause validity from recovery tokens or implement another grammar.
  • All 30 canonical cases 87–101 (exec and doctest-style single/LF modes) now match the CPython 3.15 oracle in every captured SyntaxError field. This describes local finite validation. Separately, hosted correctness CI for this exact commit passed all 24 configured jobs, including Linux, macOS, Windows, and WASI test jobs; it does not establish parity for every malformed input.
  • Source-bound builds and targeted compiler Clippy (-D warnings) passed. Validation progressed through successive 71-, 85-, and 98-input compile-only comparisons, retaining earlier inputs and prior binaries/results. No previously matching control regressed. The final candidate fully matches 63/98 inputs versus 59/98 for the validated call-generator checkpoint. In that comparison, five rows changed: four fully match CPython; the multiline/comment control now has the correct message and positions but retains the existing serializer's extra LF in SyntaxError.text. The earlier comment-key LF residual also remains. Comparisons use raw fields without normalization.
  • Deliberately leave uncertain malformed/nested recovery shapes unchanged rather than broadening diagnostic precedence or weakening tests.
  • AI assistance: dot authored and reviewed the changes and performed the stated validation. The runtime does not expose its exact model version.

Commit-hook limitation: cspell alone was omitted for this commit with the maintainer’s explicit one-time approval because its configured npm bootstrap downloads returned HTTP 403. Cspell has not passed. The normal pre-commit hook ran: merge-conflict and rustfmt checks passed; other configured hooks had no applicable files. Hosted correctness CI for this exact commit completed successfully on 2026-10-27 at 02:50 UTC (24/24 jobs); Lib Dependencies Check also passed.

Extract the remaining Python 3.15 comprehension unpacking implementation
from the integration branch. Use existing list/set/dict update operations
and an explicit inner iterator loop for generator unpacking. Align the
public DictComp AST representation and schema with CPython.

Migrate test_unpack_ex and test_unparse with scripts/update_lib from
CPython d220409a13b77de7a6c30abe16ea90db01528edc. Preserve existing Rust tests;
add no duplicate regression tests or snippets.

AI-assisted source extraction, canonical migration, and independent source
review with OpenAI Codex. Runtime validation is not yet complete.

Assisted-by: OpenAI Codex:model version unavailable
@coderabbitai

coderabbitai Bot commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

📦 Library Dependencies

The following Lib/ modules were modified. Here are their dependencies:

[x] test: cpython/Lib/test/test_exceptions.py (TODO: 1)
[ ] test: cpython/Lib/test/test_baseexception.py
[x] test: cpython/Lib/test/test_except_star.py
[x] test: cpython/Lib/test/test_exception_group.py
[x] test: cpython/Lib/test/test_exception_hierarchy.py
[x] test: cpython/Lib/test/test_exception_variations.py

dependencies:

dependent tests: (no tests depend on exception)

[x] lib: cpython/Lib/ast.py
[x] lib: cpython/Lib/_ast_unparse.py
[x] test: cpython/Lib/test/test_unparse.py
[x] test: cpython/Lib/test/test_type_comments.py

dependencies:

  • ast

dependent tests: (164 tests)

  • ast: test_ast test_builtin test_codeop test_compile test_compiler_codegen test_coroutines test_dis test_fstring test_future_stmt test_peepholer test_peg_generator test_site test_ssl test_type_comments test_ucn test_unparse
    • annotationlib: test_annotationlib test_functools test_grammar test_inspect test_reprlib test_type_annotations test_type_params test_typing
      • dataclasses: test__colorize test_copy test_ctypes test_enum test_genericalias test_patma test_pprint test_pydoc test_regrtest test_zoneinfo
      • inspect: test_abc test_argparse test_asyncgen test_buffer test_clinic test_code test_collections test_curses test_decimal test_generators test_monitoring test_ntpath test_operator test_posixpath test_profiling test_pyrepl test_signal test_sqlite3 test_traceback test_turtle test_types test_unittest test_yield_from test_zipimport test_zipimport_support
    • dbm.dumb: test_dbm_dumb
    • inspect:
      • bdb: test_bdb test_pdb
      • cmd: test_cmd
      • importlib.metadata: test_importlib
      • pdb: test_remote_pdb
      • pkgutil: test_pkgutil test_pyrepl test_runpy
      • rlcompleter: test_pyrepl test_rlcompleter
      • trace: test_trace
      • traceback: test_asyncio test_code_module test_concurrent_futures test_contextlib test_contextlib_async test_dictcomps test_exceptions test_http_cookiejar test_importlib test_iter test_listcomps test_pyexpat test_setcomps test_socket test_subprocess test_sys test_threadedtempfile test_threading test_unittest test_with
      • xmlrpc.server: test_docxmlrpc test_xmlrpc
    • pyclbr: test_pyclbr
    • traceback:
      • concurrent.futures.interpreter: test_concurrent_futures
      • concurrent.futures.process: test_compileall test_concurrent_futures
      • http.cookiejar: test_urllib2
      • logging: test_asyncio test_hashlib test_logging test_support test_urllib2net
      • multiprocessing: test_asyncio test_concurrent_futures test_fcntl test_memoryview test_multiprocessing_forkserver test_multiprocessing_main_handling test_profiling test_re
      • profiling.sampling.dump: test_profiling
      • py_compile: test_cmd_line_script test_importlib test_modulefinder test_py_compile
      • socketserver: test_imaplib test_socketserver test_wsgiref
      • threading: test_android test_asyncio test_bytes test_bz2 test_concurrent_futures test_context test_ctypes test_email test_enumerate test_external_inspection test_fork1 test_frame test_ftplib test_gc test_httplib test_httpservers test_importlib test_interpreters test_io test_ioctl test_itertools test_linecache test_opcache test_pathlib test_poll test_poplib test_pyrepl test_queue test_robotparser test_sched test_smtplib test_super test_syslog test_termios test_threading_local test_time test_urllib2_localnet test_weakref test_winreg test_zstd
      • timeit: test_timeit
      • tkinter: test_tcl test_tkinter test_ttk test_ttk_textonly

[x] test: cpython/Lib/test/test_unpack.py
[x] test: cpython/Lib/test/test_unpack_ex.py

dependencies:

dependent tests: (no tests depend on unpack)

Legend:

  • [+] path exists in CPython
  • [x] up-to-date, [ ] outdated

youknowdot added a commit to youknowdot/RustPython that referenced this pull request Oct 26, 2026
Remove the 40 newly added tests that compile Python source and assert
Python-visible compiler behavior, following the Python-behavior test
placement policy. The deletion is a net 1160 lines.

Preserve the entire existing main test module byte-for-byte and leave
all production code unchanged, including the production changes owned
by RustPython#9342. Canonical Python suites have topic-level overlap, not proven
one-to-one coverage of every removed oracle case; no equivalent coverage
claim or test translation is made.

Bounded locked/offline codegen --tests cargo check and strict Clippy
passed. These compile test targets only; no tests or runtime were executed.

Assisted-by: OpenAI Codex:model version unavailable
codex added 3 commits October 27, 2026 02:41
…n tests

Normalize six supported comprehension-unpacking diagnostics using existing parser context. Restrict the dictionary-spread hint to its first entry to avoid changing unrelated diagnostics.

Migrate the whole test_exceptions module from CPython d220409a13b77de7a6c30abe16ea90db01528edc with the existing updater, preserving only its two existing RustPython markers. Ruff sources and dependency pins are unchanged.

Six focused check/build/strict Clippy gates passed. A paired 34-input compile-only comparison retains six canonical diagnostic improvements, with eight of fifteen canonical cases still differing; this is not full test_exceptions or CI validation.
Use exact retained AST and error ownership to normalize the remaining CPython 3.15 unpacking diagnostics. Reparse only the owned call argument slice with the unchanged Ruff parser when its original recovery loses generator clauses.

Validated all 30 canonical compile-only cases in exec and doctest-style single modes against CPython 3.15.0, plus finite surrounding controls. Targeted compiler Clippy and source-bound builds passed. Full CI is pending. Cspell alone was omitted with the maintainer’s explicit one-commit approval because its npm bootstrap downloads returned HTTP 403; all other applicable configured commit hooks ran.

AI assistance: dot authored, reviewed, and validated the changes. The runtime does not expose its exact model version.

Assisted-by: dot: model version not exposed
@youknowone
youknowone marked this pull request as ready for review October 27, 2026 04:29
@youknowone
youknowone merged commit 75df7a8 into RustPython:main Oct 27, 2026
29 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-27T04:31:05.588103Z
We couldn’t complete this review. Comment @codex review to try again.
ddc3665 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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.

3 participants