Repository navigation
Conversation
Fixes samuelcolvin#157 Assisted-by: OpenAI GPT-6 (Codex)
There was a problem hiding this comment.
1 issue found across 3 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="devtools/prettier.py">
<violation number="1" location="devtools/prettier.py:119">
P2: Short self-referential containers still bypass the new recursion marker through the `simple_cutoff` fast path. Ensure recursive containers are dispatched through the path-aware formatter instead of returning `repr(value)` unchanged.</violation>
</file>
Reply to a comment to ask cubic a question or push back. It learns from your replies.
View guided diff | Re-trigger cubic
|
|
||
| self._active_ids.add(value_id) | ||
| try: | ||
| self._format_value(value, indent_current) |
There was a problem hiding this comment.
P2: Short self-referential containers still bypass the new recursion marker through the simple_cutoff fast path. Ensure recursive containers are dispatched through the path-aware formatter instead of returning repr(value) unchanged.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At devtools/prettier.py, line 119:
<comment>Short self-referential containers still bypass the new recursion marker through the `simple_cutoff` fast path. Ensure recursive containers are dispatched through the path-aware formatter instead of returning `repr(value)` unchanged.</comment>
<file context>
@@ -108,6 +109,18 @@ def _format(self, value: Any, indent_current: int, indent_first: bool) -> None:
+
+ self._active_ids.add(value_id)
+ try:
+ self._format_value(value, indent_current)
+ finally:
+ self._active_ids.remove(value_id)
</file context>
There was a problem hiding this comment.
Checked this with self-referential lists and dictionaries. The compact path already terminates safely using Python's recursion-safe repr (for example, [[...]]), so forcing it through the expanded formatter would change the existing simple_cutoff behavior without fixing an additional recursion failure. In adfa031 I added four cases covering both compact and forced-expanded output and clarified this distinction in the documentation. The expanded path uses <Recursion on TYPE>; compact output remains unchanged. Full suite: 164 passed, 4 skipped, 1 xfailed, 1 xpassed on Windows/Python 3.13 (same clean NO_COLOR subprocess setup documented in the PR); Ruff and strict basedpyright passed. Prepared and validated with Codex assistance.
There was a problem hiding this comment.
🔵 Needs a closer look
It modifies the central _format recursion path used by every debug()/pformat call and adds shared mutable state on the module-level singleton, so a human should confirm the broad formatting impact despite no defects being found.
No open issues 🎉
What changed in this PR
This PR fixes issue #157, where debug() / PrettyFormat could exhaust the recursion limit when formatting an object that references itself (directly or mutually). The fix tracks the object identities currently on the active formatting path and renders a repeated ancestor as <Recursion on TYPE> instead of re-expanding it, then continues formatting the remaining fields.
The implementation splits the existing _format into a thin guard (_format) plus the original body (_format_value). The guard records id(value) in a per-instance _active_ids set before recursing and removes it in a finally, so shared (non-ancestor) children still format normally in each branch and formatting errors don't leave stale state. Because the id set is shared across the existing reentrant-stream mechanism, recursion triggered from within a __repr__ is also handled.
Changes:
- Add active-path identity tracking in
PrettyFormat._format, emitting<Recursion on TYPE>for repeated ancestors and cleaning up viafinally. - Add
docs/usage.mdnote describing the new recursion handling. - Add
tests/test_recursion.pywith 12 tests covering self/mutual cycles, dataclasses (with/without slots),__pretty__, generators, reentrant__repr__, shared children, error recovery, anddebug.format().
| File | Description |
|---|---|
devtools/prettier.py |
Introduces _active_ids guard in _format and extracts the formatting body into _format_value to break circular-reference recursion. |
tests/test_recursion.py |
New test module validating recursion rendering across containers, dataclasses, __pretty__, generators, reentrancy, shared children, and error recovery. |
docs/usage.md |
Documents that circular references render as <Recursion on TYPE> while other fields and shared objects still format normally. |
Review effort: Balanced
Assisted-by: OpenAI GPT-6 (Codex)
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
Assisted-by: OpenAI GPT-6 (Codex)
Fixes #157. Formatting a dictionary or other expanded object that points back to itself can exhaust the recursion limit and prevent
debug()from showing its contents.Track object identities only along the active formatting path. Render a repeated ancestor as
<Recursion on TYPE>, then continue with the remaining fields. Remove identities infinally, so shared children are formatted normally in each branch and formatting errors do not leave stale state. The same tracking handles reentrant calls from__repr__without changing the existing stream restoration.Adds documentation and 12 tests covering self/mutual container cycles, dataclasses with and without slots,
__pretty__, a self-yielding generator, reentrant__repr__, repeated shared children, recovery after an exception anddebug.format().Validation on Windows:
git diff --checkpassed.The inherited
NO_COLORenvironment variable causes the existingtest_print_is_ttyto fail on both base and patched code. The full-suite results above omit only that variable from the test subprocess environment; repository settings and tests are unchanged. The hosted OS/Python CI matrix remains to be verified.No new dependencies, credentials, network/API calls in the new tests, or changes to workflows/security checks.
AI disclosure: Codex investigated the issue, implemented the change and tests, reviewed the diff, and ran the validation above.
Summary by cubic
Fixes recursive objects causing an infinite recursion error in
debug()andPrettyFormat. Repeated ancestors are now rendered as<Recursion on TYPE>and formatting continues with the remaining fields.finallyso shared children format normally in each branch and errors don't leave stale state.__repr__, containers, dataclasses,__pretty__, and generators; values withinsimple_cutoffkeep Python's compact...recursion notation.Written for commit 9b2da78. Summary will update on new commits.