Files
mvt/tests/test_cli_startup.py
T
Donncha Ó Cearbhaill 7d41d0646e Keep the start of mvt-ios and mvt-android cheap for shell completion
Each console script imports its CLI module before Click can answer a
shell completion request, and the completion scripts run the program on
every keystroke. Importing mvt.ios.cli or mvt.android.cli took ~230 ms,
of which building the command tree needed almost nothing: cli_plugins
imported one constant from module_loader, which pulled in MVTModule, the
indicators, the pydantic settings, requests and rich; the command
implementations pulled in the same, and the iOS CLI imported iOSbackup
(pycryptodome) for decrypt-backup.

The two platform CLI modules now only build the command tree: each
command imports what it runs when it is invoked. cli_plugins owns the
custom command prefix instead of importing it from module_loader, and
exec_or_profile() loads the settings when it runs.

Completion of mvt-ios and mvt-android drops from ~260 ms to ~85 ms per
keystroke on a clean install, and every command starts that much
sooner. A test fails as soon as a CLI module imports the module
machinery again.
2026-08-28 00:42:08 +02:00

29 lines
1.2 KiB
Python

# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2026 The MVT Authors.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import pytest
from .plugin_fixtures import run_isolated_python
# Importing a platform CLI must only build its command tree: the console
# scripts import it before Click can answer a shell completion request, which
# the completion scripts make on every keystroke. Every command imports what
# it runs when it is invoked. Each of these costs tens of milliseconds to
# import and is the sign that a command implementation is imported too early.
HEAVY_MODULES = ("pydantic", "requests", "Crypto", "mvt.common.module")
@pytest.mark.parametrize("cli_module", ("mvt.ios.cli", "mvt.android.cli"))
def test_importing_a_cli_does_not_import_the_module_machinery(cli_module, tmp_path):
result = run_isolated_python(
"import sys\n"
f"import {cli_module}\n"
f"print(','.join(name for name in {HEAVY_MODULES!r} if name in sys.modules))\n",
home=tmp_path / "home",
)
assert result.returncode == 0, result.stderr
assert result.stdout.strip() == "", f"{cli_module} imported {result.stdout.strip()}"