perf(1.1): lazy topology.generate re-export (C2)

topology/__init__ eagerly imported generator -> allocator -> repository ->
the full SQLModel ORM. Defer via PEP 562 __getattr__ so importing the
package doesn't drag the DB layer into DB-less workers. Public API
(from decnet.topology import generate) unchanged. Guard test locks it in.
This commit is contained in:
2026-06-17 16:35:30 -04:00
parent 825d7d72c9
commit 4e2b1cdaf3
2 changed files with 48 additions and 1 deletions

View File

@@ -7,7 +7,6 @@ is marked as the DMZ (Internet-facing). Persisted via the repo pattern;
deployed via :mod:`decnet.engine.deployer`. deployed via :mod:`decnet.engine.deployer`.
""" """
from decnet.topology.config import TopologyConfig, GeneratedTopology from decnet.topology.config import TopologyConfig, GeneratedTopology
from decnet.topology.generator import generate
from decnet.topology.status import ( from decnet.topology.status import (
TopologyStatus, TopologyStatus,
assert_transition, assert_transition,
@@ -22,3 +21,14 @@ __all__ = [
"assert_transition", "assert_transition",
"TopologyStatusError", "TopologyStatusError",
] ]
def __getattr__(name: str):
# ponytail: lazy re-export — `generate` pulls generator→allocator→repository→the
# full SQLModel ORM (~38MB). Defer it so importing this package (which every worker
# does transitively via the CLI) doesn't drag the DB layer into DB-less workers.
if name == "generate":
from decnet.topology.generator import generate
return generate
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -0,0 +1,37 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Guard: importing the topology package must not eagerly pull the generator
(which drags the full SQLModel ORM, ~38MB, into every worker via the CLI).
See development/RELEASE-1.1.md (C2).
"""
import subprocess
import sys
def _import_and_report(stmt: str) -> set[str]:
"""Run `stmt` in a fresh interpreter, return the set of decnet.* modules loaded."""
code = (
f"import sys\n{stmt}\n"
"print('\\n'.join(m for m in sys.modules if m.startswith('decnet')))"
)
out = subprocess.run(
[sys.executable, "-c", code], capture_output=True, text=True, check=True
)
return set(out.stdout.split())
def test_topology_import_does_not_pull_generator():
loaded = _import_and_report("import decnet.topology")
assert "decnet.topology.generator" not in loaded, (
"topology/__init__ regressed to eager generator import — this pulls the "
"repository → SQLModel ORM into every DB-less worker"
)
def test_generate_still_resolvable_lazily():
loaded = _import_and_report("from decnet.topology import generate")
assert "decnet.topology.generator" in loaded # accessing it loads it
# and it's actually callable
from decnet.topology import generate
assert callable(generate)