Repository navigation
Break the exir capture <-> program/verifier import cycle - #24799
Conversation
verifier.py imported EdgeCompileConfig from executorch.exir.capture._config at module scope. That runs exir/capture/__init__.py, which imports _capture.py and exir/program/_program.py, and both import back from the still half-initialised verifier. Normally exir/__init__.py loads capture first, so the cycle stays hidden. Under lazy imports (Cinder, Python 3.14) the first import can enter through verifier, e.g. via backends/xnnpack/xnnpack_preprocess.py, and fails with "cannot import name 'EXIRATenDialectVerifierBase' from partially initialized module". EdgeCompileConfig is now imported only for type checking, plus a local import at its single runtime use in _EXIREdgeDialectVerifier.__init__. Test plan: simulated lazy imports by deferring exir/__init__.py until first attribute access, then imported exir.verification.verifier and backends.xnnpack.partition.xnnpack_partitioner first; both failed before this change and succeed after. exir/verification/test/test_verifier.py and backends/xnnpack/test/ops/test_add.py pass. Authored with AI assistance (Claude Code).
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/24799
Note: Links to docs will display an error until the docs builds have been completed. ❌ 1 New FailureAs of commit 5f26860 with merge base 3f08f3f ( This comment was automatically generated by Dr. CI and updates every 15 minutes. |
|
@claude review effort=max |
|
Claude finished @JakeStevens's task in 22m 47s —— View job Request Changes: the new local import still runs while Detailed reviewPR Review: #24799SummaryThis PR moves Code Quality
Testing
Backward Compatibility
Specific Comments
RecommendationRequest Changes. The line 378 fix is what blocks approval: without it the PR moves the cycle instead of breaking it, and CI can't catch it coming back. The other items are not blocking. Review steps
|
Address review: the verifier.py-only fix still ran the cycle during verifier's own import (through the module-level EXIREdgeDialectVerifier() instance), left the exir.program and exir._serialize entry points broken, and stopped EdgeCompileConfig being importable from verifier. capture/__init__.py loads _capture.py, which imported exir.program and exir.verification.verifier at module scope, so any import of the leaf module capture._config pulled both back in. Those imports now live in the two functions that use them, and verifier.py is unchanged from main. The new test imports each entry point in a fresh interpreter with a bare executorch.exir package, so exir/__init__.py doesn't load capture first. It fails on main for all three modules and passes with this change. Authored with AI assistance (Claude Code).
This PR needs a
|
|
@claude review effort=max |
|
@claude review effort=max |
|
Claude finished @JakeStevens's task in 19m 10s —— View job Request Changes: the Detailed reviewPR Review: #24799SummaryThis PR moves Testing
Specific Comments
RecommendationRequest Changes. Only the test location blocks approval. As written, the regression this PR guards against could come back without any OSS check noticing. Separately, the PR says the fix hasn't been tried in a Cinder lazy-import runtime yet. That check is still worth doing before landing, because the new test only models an eager import order. Review steps
|
pytest.ini ignores exir/verification/test/test_verifier.py, and that file's relative import needs a parent package, so OSS pytest never ran the test. It now lives in exir/tests/test_import_order.py, which OSS pytest collects, with a matching Buck target. Authored with AI assistance (Claude Code).
|
@claude review effort:max |
|
Claude finished @JakeStevens's task in 3m 32s —— View job Approve: the Detailed reviewPR Review: #24799SummaryThis PR moves All three reviewers traced every module-scope import chain and found no loop left for those entry points. Every use of the moved names is inside a function that now imports it, including the early Testing
Specific Comments
RecommendationApprove. Nothing blocks. The PR notes the fix hasn't been tried in a real Cinder lazy-import runtime. That check is still worth doing before landing, because the test only models an eager import order where Review steps
|
### Summary **Stacked on #24799.** Until that lands this branch also contains its commits; review only the last commit, 121f6d6. `exir/backend/backend_api.py` imports `executorch.exir.program._fake_program`, which runs `exir/program/__init__.py` and so `_program.py`. `_program.py` imported `MethodProgramsPartitionerSpec` and `to_backend` from the still half-initialised `backend_api` at module scope: ``` ImportError: cannot import name 'MethodProgramsPartitionerSpec' from partially initialized module 'executorch.exir.backend.backend_api' (most likely due to a circular import) ``` Normally `exir/__init__.py` loads `program` first, so this only fails when `backend_api` is the first `executorch.exir` import, for example under lazy imports (Cinder lazy imports on Python 3.14) when a backend imports `to_backend` directly. On main, that import order fails earlier on the `capture` cycle fixed in #24799, which is why this is stacked. `_program.py` uses those two names only in `EdgeProgramManager.to_backend`, so the import moves into that method. Nothing imports them through `_program`. This PR was authored with AI assistance (Claude Code). ### Test plan Added `executorch.exir.backend.backend_api` to `exir/tests/test_import_order.py` (added in #24799). It fails with the error above without the `_program.py` change and passes with it. The other entry points in that test still pass. `python -m unittest executorch.exir.program.test.test_program executorch.exir.backend.test.test_backends_lifted executorch.exir.backend.test.test_partitioner executorch.backends.xnnpack.test.ops.test_add` gives the same results with and without this commit, apart from the new import-order case. The shared failures come from my local runtime build, which lacks `BackendWithCompilerDemo` and some kernels such as `aten::_embedding_bag.out`.
Summary
exir/capture/__init__.pyloads_capture.py, which importedexecutorch.exir.programandexecutorch.exir.verification.verifierat module scope.verifier.py,program/_program.pyand_serialize/_serialize.pyall importexecutorch.exir.capture._config, so each of them rancapture/__init__.pyand looped back into itself while still half-initialised.Normally
exir/__init__.pyloadscapturefirst, so the cycle stays hidden. Under lazy imports (e.g. Cinder lazy imports on Python 3.14) the first import can enter through one of those modules instead, for example viabackends/xnnpack/xnnpack_preprocess.py->verifier, and fails:_capture.pyusesExirExportedProgram,_transform,HackedUpExportedProgramDONOTUSEandEXIRATenDialectVerifierBaseonly insidecapture()and_capture_legacy_do_not_use(), so those imports move into the two functions, with aTYPE_CHECKINGimport for the return annotations. No public import paths change.The separate
exir/backend/backend_api.py<->exir/program/_program.pycycle is not addressed here.This PR was authored with AI assistance (Claude Code).
Test plan
New
exir/tests/test_import_order.py(collected by OSS pytest, plus a Buck target) importsexecutorch.exir.verification.verifier,executorch.exir.programandexecutorch.exir._serialize._serializein a fresh interpreter whoseexecutorch.exiris a bare package, soexir/__init__.pydoesn't run first. On main all three fail with a circular-import error; with this change all three pass. That stub imports eagerly, so on main it reports a different name than the lazy-import traceback above (e.g.EXIRATenDialectVerifierfrom_program.py); same cycle.Smoke-tested
capture()(with and withoutenable_aot)._capture_legacy_do_not_use()already fails on main withExportedProgram.__init__() got an unexpected keyword argument 'verifier', and fails the same way with this change.python -m unittest executorch.exir.verification.test.test_verifier executorch.exir.tests.test_passes executorch.backends.xnnpack.test.ops.test_add executorch.backends.xnnpack.test.ops.test_conv2d: same results as main (3 errors,test_to_out_variant_none_outputand twotest_qs8_conv*, which also error on main in my environment).Not yet verified in a real Cinder lazy-imports Python 3.14 runtime.
cc @JacobSzwejbka @angelayi