Skip to content

[AAASM-1696] 🐛 (agent_assembly): Lazy-load top-level exports so runtime probe works without httpx#51

Merged
Chisanan232 merged 3 commits into
masterfrom
v0.0.1/AAASM-1696/fix/lazy_init_imports
May 21, 2026
Merged

[AAASM-1696] 🐛 (agent_assembly): Lazy-load top-level exports so runtime probe works without httpx#51
Chisanan232 merged 3 commits into
masterfrom
v0.0.1/AAASM-1696/fix/lazy_init_imports

Conversation

@Chisanan232
Copy link
Copy Markdown
Contributor

Summary

  • Convert agent_assembly/__init__.py to PEP 562 __getattr__ lazy attribute resolution so from agent_assembly.runtime import … no longer drags httpx / pydantic into sys.modules.
  • Add a subprocess-based regression test that masks httpx and pydantic from import and verifies both agent_assembly.runtime and the top-level agent_assembly package import cleanly.

Closes AAASM-1696.

Why

agent_assembly/runtime.py was designed to be stdlib-only (its docstring is explicit: "intentionally NOT re-exported from agent_assembly at the top level"), so it could be reached via PYTHONPATH=…/python-sdk without installing the SDK's third-party deps.

That intent was undone by the eager imports in __init__.py. Resolving from agent_assembly.runtime import find_aasm_binary first executes agent_assembly/__init__.py, which chained through:

agent_assembly/__init__.py
  → agent_assembly.core           (assembly.py:14)
    → agent_assembly.client.gateway (gateway.py:5)
      → import httpx

In agent-assembly's aa-integration-tests CI, python3 is set up by actions/setup-python@v6 but the SDK's runtime deps (httpx, pydantic, typing-extensions) are never installed — only PYTHON_SDK_PATH is set. After the posix_spawnp fix in agent-assembly commit 39f53be0 unblocked the probe, the next layer of the bug surfaced as ModuleNotFoundError: No module named 'httpx' in run 26211782822 (both ubuntu-latest and macos-latest).

How the fix works

  • _LAZY_EXPORTS maps each public name to the submodule that owns it.
  • __getattr__(name) (PEP 562) imports the owning submodule on first access and caches the attribute back into globals() so subsequent accesses are O(1).
  • __all__ is built statically from _ALWAYS_EXPORTED, plus the optional native _core exports (RuntimeClient, GovernanceEvent, PolicyResult, PolicyTimeoutError) when the extension is available. Availability is probed via sys.modules first then importlib.util.find_spec, preserving the semantics of the existing test_init_exports.py fixture which pre-populates sys.modules with a fake _core.
  • A TYPE_CHECKING block re-imports the same names statically so mypy / IDE completion are unchanged.

Test plan

  • pytest test/unit/test_runtime_import_isolation.py — 4 new regression tests pass.
  • pytest test/ — 377 passed, 11 pre-existing skips (native _core, optional adapter deps).
  • ruff check agent_assembly/__init__.py test/unit/test_runtime_import_isolation.py — clean.
  • ruff format --check — clean.
  • mypy agent_assembly — no new errors introduced (verified against pristine worktree: 58 → 58).
  • After merge: agent-assembly's aa-integration-tests::e2e_sdk_runtime_lifecycle::python_binary_in_path_returns_resolved_path should pass on Linux + macOS.

Out of scope

The pre-existing mypy noise on master (proto .pyi/.py duplicates, gateway.py generic dict warnings, …) is untouched.

🤖 Generated with Claude Code

`agent_assembly/__init__.py` previously imported the full SDK surface
eagerly: `from agent_assembly.core import …` → `agent_assembly.client.gateway`
→ `import httpx`. That defeated `agent_assembly/runtime`'s design intent
of being stdlib-only and broke
aa-integration-tests::e2e_sdk_runtime_lifecycle::python_binary_in_path_returns_resolved_path
with `ModuleNotFoundError: No module named 'httpx'` whenever the SDK
was used via PYTHONPATH without installing third-party deps
(agent-assembly CI run 26211782822).

Convert the package to lazy attribute resolution using PEP 562's
`__getattr__` so accessing `agent_assembly.init_assembly` still works,
but `from agent_assembly.runtime import …` no longer drags httpx/pydantic
into sys.modules. `__all__` and `__version__` remain static; the optional
native `_core` exports are still gated on availability (now via
sys.modules + importlib.util.find_spec to preserve the existing
test_init_exports.py behaviour). TYPE_CHECKING re-exports keep mypy and
IDE completion identical to before.
Spawns a child interpreter with a sys.meta_path finder that raises
ModuleNotFoundError for `httpx` and `pydantic`, then asserts:

* `from agent_assembly.runtime import find_aasm_binary, init_assembly,
  is_running` succeeds in that environment.
* `import agent_assembly` resolves to the package without dragging in
  the gateway client's third-party deps.
* Eager attribute access (`agent_assembly.init_assembly`, etc.) still
  resolves through the PEP 562 `__getattr__` loader.
* Unknown attributes raise AttributeError with the offending name.

Guards against the eager-import regression that broke agent-assembly
CI run 26211782822.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

Codecov Report

❌ Patch coverage is 79.41176% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
agent_assembly/__init__.py 79.41% 7 Missing ⚠️

📢 Thoughts on this report? Let us know!

`ruff format` flagged the single-line failure-message assert as
non-canonical when run across the whole tree (vs. per-file). Switch to
the trailing-tuple form to match the rest of the file. No behavioural
change.
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
79.4% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@Chisanan232 Chisanan232 merged commit c7239b2 into master May 21, 2026
22 of 24 checks passed
@Chisanan232 Chisanan232 deleted the v0.0.1/AAASM-1696/fix/lazy_init_imports branch May 21, 2026 08:25
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.

1 participant