diff --git a/hermes_cli/update_cmd.py b/hermes_cli/update_cmd.py index 4d56344dca..5fd8434d84 100644 --- a/hermes_cli/update_cmd.py +++ b/hermes_cli/update_cmd.py @@ -98,7 +98,7 @@ from hermes_cli.update_cmd_git import ( # noqa: F401 _sync_with_upstream_if_needed) from hermes_cli.update_cmd_maint import ( # noqa: F401 _PRE_UPDATE_SNAPSHOT_KEEP, _PRE_UPDATE_SNAPSHOT_MAX_FILE_SIZE, - _STALE_PURGE_EXCLUDED_TOP_LEVEL, _STALE_PURGE_PROTECTED, + _STALE_PURGE_PROTECTED, _UPDATE_RUNTIME_RELOAD_MODULES, _clear_stale_sqlite_sidecars, _ensure_acp_launcher, _ensure_fhs_path_guard, _finish_dashboard_update_cleanup, _format_time_ago, _post_update_sqlite_runtime_status, _print_bundled_skills_sync_report, diff --git a/hermes_cli/update_cmd_config.py b/hermes_cli/update_cmd_config.py index 4b002571fd..a57c16e7f5 100644 --- a/hermes_cli/update_cmd_config.py +++ b/hermes_cli/update_cmd_config.py @@ -186,8 +186,8 @@ def _check_and_apply_config_migration( _m()._purge_stale_hermes_modules() # Reload BEFORE any config reads so all checks use the updated code. _reload_config_modules() - # Post-purge this re-executes the NEW config.py against whatever root modules - # (hermes_constants, utils, ...) are still cached, so it can fail too. + # Post-purge this re-executes the NEW config.py; it still fails if the pulled tree + # is internally inconsistent, hence the try. from hermes_cli.config import get_missing_env_vars, get_missing_config_fields # Log, point at the manual command, and return. See #91360. missing_env = get_missing_env_vars(required_only=True) diff --git a/hermes_cli/update_cmd_maint.py b/hermes_cli/update_cmd_maint.py index 7fb13cfd9c..d5b3ea68dd 100644 --- a/hermes_cli/update_cmd_maint.py +++ b/hermes_cli/update_cmd_maint.py @@ -25,10 +25,6 @@ logger = logging.getLogger("hermes_cli.update_cmd") _UPDATE_RUNTIME_RELOAD_MODULES = "hermes_constants", "tools.environments.local", "tools.lazy_deps" -#: Owned by the checkout but never purged: pytest resolves fixtures through the identity of -#: its own already-imported test modules, and evicting them mid-session breaks that. -_STALE_PURGE_EXCLUDED_TOP_LEVEL = frozenset({"tests"}) - #: Modules EXECUTING the update survive the purge: evicting them buys nothing (running frames #: keep them alive) and reloading them mid-flight is the one genuinely unsafe move. #: ``hermes_logging`` is protected for a different reason: its queue listener, handler list and @@ -105,7 +101,9 @@ def _stale_purge_prefixes() -> frozenset: names.add(entry.stem) elif (entry / "__init__.py").is_file(): names.add(entry.name) - return frozenset(names) - _STALE_PURGE_EXCLUDED_TOP_LEVEL + # ``tests`` is owned by the checkout but never purged: the in-process purge tests would + # otherwise re-import a fresh copy of the very test module their monkeypatches point at. + return frozenset(names) - {"tests"} def _purge_stale_hermes_modules() -> None: diff --git a/tests/hermes_cli/test_update_stale_module_purge.py b/tests/hermes_cli/test_update_stale_module_purge.py index c14f435e2a..876ed88076 100644 --- a/tests/hermes_cli/test_update_stale_module_purge.py +++ b/tests/hermes_cli/test_update_stale_module_purge.py @@ -9,9 +9,10 @@ import ...` in the restart phase raised ImportError, the whole phase aborted, and the running gateway kept serving pre-update code. The old mitigation (_UPDATE_RUNTIME_RELOAD_MODULES) reloaded 3 hardcoded -modules — re-fixed per symptom. The purge evicts EVERY cached module under -the Hermes package prefixes so later imports rebuild a self-consistent -module graph from the updated checkout. +modules — re-fixed per symptom. The purge evicts EVERY cached module whose +top-level name is a ``.py`` file or package in the checkout root (minus +``tests``) so later imports rebuild a self-consistent module graph from the +updated checkout. """ from __future__ import annotations