Decouple non-UI CLI startup from config_ui

Stop aman.py from importing the GTK settings module at module load so version, init, bench, diagnostics, and top-level help can start without pulling in the UI stack.\n\nPromote PyGObject and python-xlib into main project dependencies, switch the documented source install surface to plain uv/pip commands, and teach the portable, deb, and Arch packaging flows to install filtered runtime requirements before the Aman wheel so they still rely on distro-provided GTK/X11 packages.\n\nAdd regression coverage for importing aman with config_ui blocked and for the portable bundle's new requirements payload, then rerun the focused CLI/diagnostics/portable tests plus py_compile.
This commit is contained in:
Thales Maciel 2026-03-14 13:38:15 -03:00
parent b4a3d446fa
commit 721248ca26
No known key found for this signature in database
GPG key ID: 33112E6833C34679
15 changed files with 173 additions and 35 deletions

View file

@ -21,7 +21,6 @@ from typing import Any
from aiprocess import LlamaProcessor
from config import Config, ConfigValidationError, load, redacted_dict, save, validate
from constants import DEFAULT_CONFIG_PATH, MODEL_PATH, RECORD_TIMEOUT_SEC
from config_ui import ConfigUiResult, run_config_ui, show_about_dialog, show_help_dialog
from desktop import get_desktop_adapter
from diagnostics import (
doctor_command,
@ -791,6 +790,30 @@ def _app_version() -> str:
return "0.0.0-dev"
def _load_config_ui_attr(attr_name: str) -> Any:
try:
from config_ui import __dict__ as config_ui_exports
except ModuleNotFoundError as exc:
missing_name = exc.name or "unknown"
raise RuntimeError(
"settings UI is unavailable because a required X11 Python dependency "
f"is missing ({missing_name})"
) from exc
return config_ui_exports[attr_name]
def _run_config_ui(*args, **kwargs):
return _load_config_ui_attr("run_config_ui")(*args, **kwargs)
def _show_help_dialog() -> None:
_load_config_ui_attr("show_help_dialog")()
def _show_about_dialog() -> None:
_load_config_ui_attr("show_about_dialog")()
def _read_json_file(path: Path) -> Any:
if not path.exists():
raise RuntimeError(f"file does not exist: {path}")
@ -1446,8 +1469,8 @@ def _run_settings_required_tray(desktop, config_path: Path) -> bool:
lambda: "settings_required",
lambda: None,
on_open_settings=open_settings_callback,
on_show_help=show_help_dialog,
on_show_about=show_about_dialog,
on_show_help=_show_help_dialog,
on_show_about=_show_about_dialog,
on_open_config=lambda: logging.info("config path: %s", config_path),
)
return reopen_settings["value"]
@ -1456,7 +1479,7 @@ def _run_settings_required_tray(desktop, config_path: Path) -> bool:
def _run_settings_until_config_ready(desktop, config_path: Path, initial_cfg: Config) -> Config | None:
draft_cfg = initial_cfg
while True:
result: ConfigUiResult = run_config_ui(
result = _run_config_ui(
draft_cfg,
desktop,
required=True,
@ -1665,7 +1688,7 @@ def _run_command(args: argparse.Namespace) -> int:
if daemon.get_state() != State.IDLE:
logging.info("settings UI is available only while idle")
return
result = run_config_ui(
result = _run_config_ui(
cfg,
desktop,
required=False,
@ -1740,8 +1763,8 @@ def _run_command(args: argparse.Namespace) -> int:
daemon.get_state,
lambda: shutdown("quit requested"),
on_open_settings=open_settings_callback,
on_show_help=show_help_dialog,
on_show_about=show_about_dialog,
on_show_help=_show_help_dialog,
on_show_about=_show_about_dialog,
is_paused_getter=daemon.is_paused,
on_toggle_pause=daemon.toggle_paused,
on_reload_config=reload_config_callback,

View file

@ -102,7 +102,7 @@ def _sounddevice():
import sounddevice as sd # type: ignore[import-not-found]
except ModuleNotFoundError as exc:
raise RuntimeError(
"sounddevice is not installed; install dependencies with `uv sync --extra x11`"
"sounddevice is not installed; install dependencies with `uv sync`"
) from exc
return sd