feat(gui): add _install_default_layout_if_empty helpers for install-on-empty-INI
Module-level _install_default_layout_if_empty(src, dst) reads the bundled layout from src, decides if dst is missing/empty/small (< 1000 bytes or no [Window][ header), copies src -> dst on true, and returns Result[bool]. On OSError reading/writing, returns Result[data=False, errors=[ErrorInfo]] so App._post_init can drain to _startup_timeline_errors per the data-oriented convention. _install_default_layout_if_empty_result(app, src, dst) is the drain-plane passthrough that mirrors _post_init_callback_result. Wiring into App._post_init lands in the next commit.
This commit is contained in:
@@ -1466,7 +1466,58 @@ def _post_init_callback_result(app: "App") -> Result[None]:
|
|||||||
message=f"on_warmup_complete callback registration failed: {e}",
|
message=f"on_warmup_complete callback registration failed: {e}",
|
||||||
source="gui_2._post_init_callback_result",
|
source="gui_2._post_init_callback_result",
|
||||||
original=e,
|
original=e,
|
||||||
|
)])
|
||||||
|
def _install_default_layout_if_empty(src_ini: Path, dst_ini: Path) -> Result[bool]:
|
||||||
|
"""Install bundled layout to cwd when the user's INI is missing/empty/small.
|
||||||
|
|
||||||
|
Decision rule: dst_ini is "empty" when its content is fewer than 1000
|
||||||
|
bytes OR has no [Window][ header. On empty, copies src_ini -> dst_ini
|
||||||
|
and returns Result(data=True). On non-empty (user customized), returns
|
||||||
|
Result(data=False) without overwriting. On OSError reading src or
|
||||||
|
writing dst, returns Result(data=False, errors=[ErrorInfo]) so the
|
||||||
|
legacy wrapper in App._post_init can drain to _startup_timeline_errors.
|
||||||
|
|
||||||
|
[C: src/gui_2.py:_install_default_layout_if_empty_result,
|
||||||
|
src/gui_2.py:App._post_init]"""
|
||||||
|
try:
|
||||||
|
dst_text: str = dst_ini.read_text(encoding="utf-8", errors="replace") if dst_ini.exists() else ""
|
||||||
|
except OSError:
|
||||||
|
dst_text = ""
|
||||||
|
is_dst_empty: bool = len(dst_text) < 1000 or "[Window][" not in dst_text
|
||||||
|
if not is_dst_empty:
|
||||||
|
return Result(data=False)
|
||||||
|
try:
|
||||||
|
src_text: str = src_ini.read_text(encoding="utf-8", errors="replace")
|
||||||
|
except OSError as e:
|
||||||
|
return Result(data=False, errors=[ErrorInfo(
|
||||||
|
kind=ErrorKind.INTERNAL,
|
||||||
|
message=f"Could not read bundled layout {src_ini}: {e}",
|
||||||
|
source="gui_2._install_default_layout_if_empty",
|
||||||
|
original=e,
|
||||||
)])
|
)])
|
||||||
|
try:
|
||||||
|
dst_ini.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
dst_ini.write_text(src_text, encoding="utf-8")
|
||||||
|
except OSError as e:
|
||||||
|
return Result(data=False, errors=[ErrorInfo(
|
||||||
|
kind=ErrorKind.INTERNAL,
|
||||||
|
message=f"Could not write layout {dst_ini}: {e}",
|
||||||
|
source="gui_2._install_default_layout_if_empty",
|
||||||
|
original=e,
|
||||||
|
)])
|
||||||
|
sys.stderr.write(f"[GUI] installed default layout: {src_ini} -> {dst_ini}\n")
|
||||||
|
return Result(data=True)
|
||||||
|
def _install_default_layout_if_empty_result(app: "App", src: Path, dst: Path) -> Result[bool]:
|
||||||
|
"""Drain-aware variant of _install_default_layout_if_empty.
|
||||||
|
|
||||||
|
Passthrough wrapper so App._post_init can call the install via the
|
||||||
|
drain-plane naming convention (mirrors _post_init_callback_result).
|
||||||
|
The wrapped function already returns Result[bool] and catches OSError
|
||||||
|
internally; this wrapper exists for naming consistency and to give
|
||||||
|
any future exception-logging policy a single hook point.
|
||||||
|
|
||||||
|
[C: src/gui_2.py:_install_default_layout_if_empty, src/gui_2.py:App._post_init]"""
|
||||||
|
return _install_default_layout_if_empty(src, dst)
|
||||||
def _run_immapp_result(app: "App") -> Result[None]:
|
def _run_immapp_result(app: "App") -> Result[None]:
|
||||||
"""Drain-aware variant of App.run immapp.run() call (L728 INTERNAL_SILENT_SWALLOW).
|
"""Drain-aware variant of App.run immapp.run() call (L728 INTERNAL_SILENT_SWALLOW).
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user