mirror of
https://github.com/mvt-project/mvt.git
synced 2026-09-03 00:21:07 +02:00
register_cli_plugins() ran while mvt.ios and mvt.android were being imported, so importing any part of MVT executed the entry points of every installed command package. That made plugin loading depend on import order: a plugin importing from MVT while MVT was still initializing got an ImportError and was quietly demoted to a broken command, and the same plugin worked when MVT happened to be imported first. Move the call into a main() function in each CLI module and point the console scripts at it, so registration happens once when the program starts and importing MVT no longer runs third-party code. For packagers: mvt.ios:cli and mvt.android:cli stay importable, but a wrapper invoking cli() directly no longer registers the installed plugin commands and should call main() instead.
86 lines
2.9 KiB
Python
86 lines
2.9 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/
|
|
|
|
"""Helpers building throwaway plugin distributions for the tests.
|
|
|
|
Some plugin behaviour only shows up in a fresh interpreter: what an import
|
|
executes, and what a plugin sees when MVT is imported before or after it.
|
|
These helpers write an importable distribution with a real entry point and
|
|
run a script against it in a subprocess, with a temporary home so that the
|
|
subprocess cannot touch the configuration of whoever runs the tests.
|
|
"""
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
FIXTURE_COMMAND_NAME = "fixture-plugin"
|
|
FIXTURE_MODULE_NAME = "fixture_cli_plugin"
|
|
FIXTURE_DISTRIBUTION_NAME = "fixture-cli-plugin"
|
|
|
|
|
|
def write_cli_plugin_distribution(
|
|
site_path: Path,
|
|
entry_point_group: str,
|
|
module_source: str,
|
|
) -> Path:
|
|
"""Write a distribution registering a CLI plugin entry point.
|
|
|
|
:param site_path: Folder to write the distribution into, to be added to
|
|
the import path of the interpreter loading it.
|
|
:param entry_point_group: Entry-point group to register the command in.
|
|
:param module_source: Source of the plugin module, which must define a
|
|
Click command named `cli`.
|
|
:returns: The folder the distribution was written to.
|
|
"""
|
|
site_path.mkdir(parents=True, exist_ok=True)
|
|
(site_path / f"{FIXTURE_MODULE_NAME}.py").write_text(
|
|
module_source, encoding="utf-8"
|
|
)
|
|
|
|
dist_info = (
|
|
site_path / f"{FIXTURE_DISTRIBUTION_NAME.replace('-', '_')}-1.0.dist-info"
|
|
)
|
|
dist_info.mkdir(exist_ok=True)
|
|
(dist_info / "METADATA").write_text(
|
|
f"Metadata-Version: 2.1\nName: {FIXTURE_DISTRIBUTION_NAME}\nVersion: 1.0\n",
|
|
encoding="utf-8",
|
|
)
|
|
(dist_info / "entry_points.txt").write_text(
|
|
f"[{entry_point_group}]\n{FIXTURE_COMMAND_NAME} = {FIXTURE_MODULE_NAME}:cli\n",
|
|
encoding="utf-8",
|
|
)
|
|
return site_path
|
|
|
|
|
|
def run_isolated_python(
|
|
script: str,
|
|
home: Path,
|
|
site_path: Optional[Path] = None,
|
|
**environment: str,
|
|
) -> subprocess.CompletedProcess:
|
|
"""Run a script in a fresh interpreter with its own configuration folder.
|
|
|
|
Importing MVT writes its configuration file, so the subprocess gets a
|
|
temporary home and no MVT environment variables from the test session.
|
|
"""
|
|
isolated_environment = {
|
|
key: value for key, value in os.environ.items() if not key.startswith("MVT_")
|
|
}
|
|
isolated_environment["HOME"] = str(home)
|
|
isolated_environment["XDG_CONFIG_HOME"] = str(home / "config")
|
|
if site_path is not None:
|
|
isolated_environment["PYTHONPATH"] = str(site_path)
|
|
isolated_environment.update(environment)
|
|
|
|
return subprocess.run(
|
|
[sys.executable, "-c", script],
|
|
capture_output=True,
|
|
text=True,
|
|
env=isolated_environment,
|
|
)
|