From 5a9970ad4b4e4a69978bb34776cb1effd1819bd1 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 2 Sep 2026 16:04:45 -0700 Subject: [PATCH] refactor(cli/sessions_cmd): lift recover progress printer + verdict out of _cmd_recover --- hermes_cli/sessions_cmd.py | 63 ++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/hermes_cli/sessions_cmd.py b/hermes_cli/sessions_cmd.py index 2e4c591d61..e130cc303d 100644 --- a/hermes_cli/sessions_cmd.py +++ b/hermes_cli/sessions_cmd.py @@ -171,31 +171,13 @@ def _cmd_recover(args): if inspect_only: report = inspect_session_database(source, work_dir=work_dir) else: - last_progress = {"table": None} - - def _recovery_progress(info): - table = info.get("table") - copied = int(info.get("copied_rows") or 0) - total = info.get("source_rows") - if table != last_progress["table"]: - if last_progress["table"] is not None: - print() - print(f" {table}: ", end="", flush=True) - last_progress["table"] = table - suffix = f"/{int(total):,}" if total is not None else "" - print(f"\r {table}: {copied:,}{suffix}", end="", flush=True) - print("Recovering canonical session data into a new database…") + progress = _RecoveryProgress() report = recover_session_database( - source, - output, - work_dir=work_dir, - chunk_size=getattr(args, "chunk_size", 1000), - progress_cb=_recovery_progress, - allow_partial=allow_partial, + source, output, work_dir=work_dir, chunk_size=getattr(args, "chunk_size", 1000), + progress_cb=progress, allow_partial=allow_partial, ) - if last_progress["table"] is not None: - print() + progress.finish() except (SessionRecoveryError, OSError, sqlite3.DatabaseError) as exc: print(f"Error: session recovery failed: {exc}") print("The supplied source database was not replaced or deleted.") @@ -213,6 +195,31 @@ def _cmd_recover(args): if inspect_only: return 0 if report.get("recoverable") else 1 + return _print_recovery_verdict(report, output, allow_partial) + + +class _RecoveryProgress: + """`recover` progress printer: one live-updating `` : n/total`` line per table.""" + + def __init__(self): + self.table = None + + def __call__(self, info): + table = info.get("table") + if table != self.table: + self.finish() + print(f" {table}: ", end="", flush=True) + self.table = table + total = info.get("source_rows") + suffix = f"/{int(total):,}" if total is not None else "" + print(f"\r {table}: {int(info.get('copied_rows') or 0):,}{suffix}", end="", flush=True) + + def finish(self): + if self.table is not None: + print() + + +def _print_recovery_verdict(report, output, allow_partial) -> int: if report.get("complete"): print(f"✓ Recovered database verified at: {output}") print(" The active session database was not changed.") @@ -223,21 +230,19 @@ def _cmd_recover(args): if report.get("best_effort"): print(f"✓ BEST-EFFORT page-level salvage verified at: {output}") print( - " The source table schemas were unreadable; rows were " - "rebuilt from raw pages via sqlite3 .recover and mapped " - "heuristically." + " The source table schemas were unreadable; rows were rebuilt from raw pages " + "via sqlite3 .recover and mapped heuristically." ) else: print(f"✓ Partial recovery output verified at: {output}") print( - " Recovered " - f"{int(counts.get('sessions') or 0):,} sessions and " + f" Recovered {int(counts.get('sessions') or 0):,} sessions and " f"{int(counts.get('messages') or 0):,} messages." ) print(" The active session database was not changed.") print( - " This output is incomplete. Review every skipped range " - "and orphan count in the JSON report before installing it." + " This output is incomplete. Review every skipped range and orphan count in the " + "JSON report before installing it." ) return 0 print("✗ Recovery output did not pass every verification check.")