Skip to content

fix(parser): make C# outer bindings survive the process-pool parser - #1215

Open
mccalpink wants to merge 1 commit into
tirth8205:stagingfrom
mccalpink:fix/1214-outer-binding-pickle
Open

mccalpink wants to merge 1 commit into
tirth8205:stagingfrom
mccalpink:fix/1214-outer-binding-pickle

Conversation

@mccalpink

Copy link
Copy Markdown

Pull Request

Linked issue

Closes #1214

Implements the fix suggested in the issue (#1214).

What & why

parser._OuterBinding is a str subclass whose __new__ takes a second argument, levels. C# nested classes that use a field of their outer class store it in extra["receiver_type"] of the CALLS edge. The process-pool parser pickles every worker result; pickle rebuilds a str subclass through __new__(cls, str(self)), which drops levels. The parent process then fails with TypeError: _OuterBinding.__new__() missing 1 required positional argument: 'levels', and build (and update with 8 or more files) ends with BrokenProcessPool.

The fix adds _OuterBinding.__reduce__, which returns (_OuterBinding, (str(self), self.levels)). The value and levels now survive the round trip, and nothing else changes. The _OuterBinding already sits in receiver_type, so it has to survive the process-pool queue; keeping the type means the edge is the same on the serial, thread and process paths. (The resolver itself reads the int extra["csharp_receiver_levels"], which the worker sets before pickling, not .levels of the unpickled value.) I checked for other str/int/tuple subclasses in code_review_graph/: this is the only one.

Changes:

  • code_review_graph/parser.py: _OuterBinding.__reduce__.
  • tests/test_csharp_outer_binding_pickle.py: pickle round trip of _OuterBinding; pickle round trip of the (nodes, edges) parsed from the snippet in the issue; full_build with CRG_PARSE_EXECUTOR=process on 9 C# files with a nested outer-field call (it also asserts that the parallel executor was built, so a serial fallback cannot make it pass).
  • CHANGELOG.md: a bullet under ## [Unreleased] / ### Fixed.

Without the fix all three new tests fail (the last one with the BrokenProcessPool from the issue); with it they pass.

How it was tested

uv run pytest tests/test_csharp_outer_binding_pickle.py --tb=short -q
# before the fix: 3 failed (BrokenProcessPool, TypeError ... missing 'levels')
# after the fix:  3 passed

uv run pytest tests/ --tb=short -q
# 11 failed, 8255 passed, 981 skipped, 3 xfailed, 2 xpassed

uv run ruff check code_review_graph/
# All checks passed!

uv run mypy code_review_graph/ --ignore-missing-imports --no-strict-optional
# Success: no issues found in 95 source files

The same 11 tests fail on an unmodified staging in my environment (8252 passed there, 3 fewer than here: my 3 new tests), also with a clean temporary directory (TMPDIR pointed at a fresh directory). They exercise git and non-repository directory handling, not the parser code changed here. Python 3.13.

Checklist

  • Tests added for new functionality
  • All tests pass: uv run pytest tests/ --tb=short -q (11 tests fail locally, identically on staging without this change; see above; CI is the reference)
  • Linting passes: uv run ruff check code_review_graph/
  • Type checking passes: uv run mypy code_review_graph/ --ignore-missing-imports --no-strict-optional
  • Lines are at most 100 characters
  • Docs updated where behaviour changed (README, docs/, docstrings)

This PR was prepared with AI assistance (Claude Code). I reproduced the bug, reviewed the change, and ran the tests myself.

_OuterBinding is a str subclass whose __new__ needs levels, so pickle could
not rebuild it and build failed with BrokenProcessPool on a nested class that
calls a field of its outer class. Define __reduce__ to keep levels.

Closes tirth8205#1214

This branch has not been deployed

No deployments
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.

[Bug]: C# build crashes with BrokenProcessPool: _OuterBinding cannot be unpickled (missing levels)

1 participant