Skip to content

[mypyc] Fix Extension.depends empty for fully-cached groups in incremental builds#6

Merged
VaggelisD merged 2 commits into
release-1.20from
fix-cached-group-extension-depends
May 18, 2026
Merged

[mypyc] Fix Extension.depends empty for fully-cached groups in incremental builds#6
VaggelisD merged 2 commits into
release-1.20from
fix-cached-group-extension-depends

Conversation

@georgesittas

Copy link
Copy Markdown
Collaborator

Summary

In separate=True mode, when generate_c returns empty cfiles for a group (the fully-cached path — typical of pip's second setup.py invocation for the wheel-build phase), per_cfile_deps was never populated for that group. Extension.depends therefore stayed empty, so cross-group export-table header changes went undetected by setuptools.

The concrete failure mode: inserting a new class earlier in a compiled module shifts subsequent members' export_table_<group> struct offsets. If the consumer group's .o is not recompiled, it bakes in the old offsets, silently resolving them to wrong classes at runtime (e.g. TypeError: Unexpected keyword 'is_string' for UniformSample when constructing Uuid).

Fix

When the fully-cached path fires and the on-disk .c file exists, read it before calling get_header_deps so resolve_cfile_deps can walk the transitive header chain and include cross-group headers in Extension.depends. Setuptools then sees the changed header and recompiles the consumer's .o.

Also fixes an inconsistent errors="replace" in resolve_cfile_deps (now plain encoding="utf-8" throughout, matching the rest of the file).

Test

test_cached_group_deps_populated_from_disk_cfile directly demonstrates the before/after:

  • Without fix: get_header_deps([(cfile, "")]) returns no includes → Extension.depends is empty → cross-group header changes undetected
  • With fix: on-disk .c is read → transitive resolver finds cross-group header → it appears in Extension.depends

…ental builds

In separate=True mode, when generate_c returns empty cfiles for a group
(the fully-cached path — typical of pip's second setup.py invocation),
per_cfile_deps was never populated for that group.  Extension.depends
therefore stayed empty, so cross-group export-table header changes caused
by inserting a new class that shifts struct offsets never triggered a
recompile of the cached consumer's .o.  The stale .o then baked in the
old struct offsets, silently resolving them to wrong classes at runtime.

Fix: when the on-disk .c file exists for a cached group, read it before
calling get_header_deps so the dep resolver can walk the transitive header
chain and include cross-group headers in Extension.depends.  Also fixes
an inconsistent errors="replace" in resolve_cfile_deps (now plain
encoding="utf-8" throughout) and adds a test that directly demonstrates
the before/after behavior.
@georgesittas
georgesittas requested review from VaggelisD and Copilot May 18, 2026 16:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes mypyc incremental build dependency tracking for fully cached compilation groups by reading the existing generated .c file so header dependencies can still populate Extension.depends.

Changes:

  • Reads cached on-disk .c files in mypyc_build when no generated C files are returned for a group.
  • Uses strict UTF-8 decoding consistently in header dependency resolution.
  • Adds a regression-oriented test for cached group dependency discovery.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
mypyc/build.py Populates dependency metadata from existing cached C files and standardizes file decoding.
mypyc/test/test_misc.py Adds a test scenario for resolving cross-group header dependencies from cached C output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread mypyc/test/test_misc.py
Comment on lines +197 to +199
per_cfile_deps = [
(consumer_c, get_header_deps([(os.path.basename(consumer_c), existing_text)]))
]
@VaggelisD
VaggelisD merged commit bb54f5a into release-1.20 May 18, 2026
15 of 17 checks passed
VaggelisD added a commit that referenced this pull request May 18, 2026
Fixes mypy var-annotated errors introduced in #6 where iterating over a
bare empty list left cfile_full/dep_names untyped.
@georgesittas
georgesittas deleted the fix-cached-group-extension-depends branch May 20, 2026 18:15
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