Skip to content

Defer the box-tree imports in cli/impl so the modules import from the wheel - #253

Merged
danielrmerskine merged 1 commit into
mainfrom
de/impl-lazy-box-imports
Aug 13, 2026
Merged

Defer the box-tree imports in cli/impl so the modules import from the wheel#253
danielrmerskine merged 1 commit into
mainfrom
de/impl-lazy-box-imports

Conversation

@danielrmerskine

Copy link
Copy Markdown
Collaborator

Closes #241.

What was wrong

cli/impl/box_config.py:13 and cli/impl/power/enable_disable.py:6 imported lager.* at
module scope. That package lives under box/ and is not shipped in the lager-cli wheel, so
both modules raised ModuleNotFoundError on any clean pip install. They worked only where
box/ happened to be on sys.path — dev checkouts via the test PYTHONPATH convention, and
boxes.

Why this is a small fix and not a code move

Worth stating up front, because the issue offered "move the needed code under cli/" as an
option and that would have been a mistake:

  • These files are uploaded to the box and executed there (cli/core/net_helpers.py:683-698,
    cli/commands/box/config.py:84-102). The CLI never imports them — the coupling is a filename
    string passed to get_impl_path(), not an import.
  • They must keep shipping in the wheel, because get_impl_path() resolves them from the
    installed package on disk. Excluding them would break lager logic and lager box-config on
    a pip install.
  • So the only thing that was actually wrong is when the import happens. Moving Net under
    cli/ would have dragged 100 modules, ~28.6k lines and 18 hardware SDKs into a CLI wheel
    whose install_requires is 16 pure-Python packages.

The fix is the pattern already in the tree two directories over: cli/impl/measurement/scope.py
imports Net/NetType inside get_rigol_net() and get_source_net() rather than at module
scope. Both defective files now do the same. box_config.py's sys.path.insert('/app/lager')
moved inside __main__ alongside its import, since it is meaningless off-box.

The import path is deliberately unchanged (from lager.nets.net import Net, NetType, not
from lager import ...), so on-box resolution is byte-for-byte what it was. Only the timing
moved.

The regression test matters more than the fix

The fix itself is six lines moved. What makes it stick is test_impl_host_importable.py, which
imports every cli/impl module — discovered from disk, not hardcoded — in a subprocess with
box/ stripped from sys.path and a meta_path finder that raises on any lager import.

That subprocess isolation is the point. The unit suites run with box/ on PYTHONPATH
(unit-tests.yml), which is exactly what hid this defect from every existing test. A same-process
test would have passed regardless of whether the bug was present.

Fault-injected to confirm it is not tautological: restoring the module-level import makes it fail
with cli.impl.power.enable_disable does not import without the box tree, and the failure message
points at the scope.py precedent. Restoring the fix makes it pass.

Also in here

Two comments in box/lager/http_handlers/usb_scanner.py instructed the reader to mirror changes
into cli/impl/query_instruments.py, which was deleted in the :9000 migration. Following them was
impossible. One is dropped, the other now records that this is the only remaining copy. The
neighbouring "Extracted from…" / "previously the … script executed over :5000" comments are
untouched — those are accurate history rather than stale instructions.

Verified

  • Packaging gate green end to end: build → twine check (both artifacts PASSED) → wheel into a
    fresh venv → import walk. 177 modules walked, 31 failures, 0 unexpected. All 31 are the
    vendored-elftools entries that Remove the vendored elftools tree and the unreachable GDB source-archive path #249 removes; the two cli.impl failures are gone.
  • The baseline is two-sided, so deleting those two lines is itself the proof: had either module
    still failed, or had the entries been left behind after the fix, the gate would have failed.
  • unit (cli): 1232 passed, 2 xfailed (was 1224 + 2; +8 from the new test).
  • tools/check_coverage_counts.py passes, including the per-file inventory row. unit (box)
    reports as platform-gated here (7 tests need /proc and flock(1); this is macOS) — untouched
    by this change and checked on Linux CI.
  • ruff --select E9,F63,F7,F82 clean.

Note for whoever merges second

This and #249 both edit tools/packaging_import_baseline.txt, and this, #249 and #251 all add a
## [Unreleased] CHANGELOG section. The baseline hunks are disjoint (elftools block vs cli.impl
block) and should auto-merge; the CHANGELOG sections will want a trivial manual merge into one.
Once both this and #249 land, the baseline holds no functional entries at all — only the
cli.setup note explaining why that one is deliberately unlisted.

@danielrmerskine
danielrmerskine force-pushed the de/impl-lazy-box-imports branch from ecc8b13 to 21354ba Compare August 13, 2026 19:44
@danielrmerskine
danielrmerskine merged commit 0fae454 into main Aug 13, 2026
16 checks passed
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.

cli.impl.box_config and cli.impl.power.enable_disable import lager.* from the box tree

1 participant