docs(gui_2): __getattr__ hasattr-guard + startup architecture section
Critical fix:
- Update __getattr__ code example to show the current bcdc26d0 version
(with hasattr guard); old example showed the silent-None bug version
New section 'Startup Architecture (Lazy Imports, Profiler, Refresh Rate)':
- _LazyModule proxies (np, filedialog, Tk, win32gui, win32con)
- _FiledialogStub for headless/tkinter-less envs
- startup_profiler + render_warmup_status_indicator (defer_warmup=True)
- Native _detect_refresh_rate_win32 (ctypes.EnumDisplaySettingsW)
- immapp.run try/except error handling (native 0xc0000005 graceful degrade)
This commit is contained in:
+16
-2
@@ -429,7 +429,9 @@ The `App` class (around line 478-487) defines two descriptor hooks that delegate
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
if name == 'controller':
|
||||
raise AttributeError(name)
|
||||
return getattr(self.controller, name)
|
||||
if hasattr(self, 'controller') and hasattr(self.controller, name):
|
||||
return getattr(self.controller, name)
|
||||
raise AttributeError(name)
|
||||
|
||||
def __setattr__(self, name: str, value: Any) -> None:
|
||||
if name != 'controller' and hasattr(self, 'controller') and hasattr(self.controller, name):
|
||||
@@ -438,6 +440,8 @@ def __setattr__(self, name: str, value: Any) -> None:
|
||||
object.__setattr__(self, name, value)
|
||||
```
|
||||
|
||||
> **Critical (bcdc26d0):** The current code includes the `hasattr(self.controller, name)` guard in `__getattr__`. The previous version (without this guard) was a silent-None bug: any uninitialized `ui_` attribute on the App would have called `getattr(self.controller, name)` → raised `AttributeError` from Python → the ImGui code would catch that and return `None` → the GUI would render blanks silently instead of crashing. The `hasattr` guard makes the `AttributeError` propagate correctly so the bug surfaces during development. The fix is in `src/gui_2.py:688-693`.
|
||||
|
||||
**Why this matters:**
|
||||
- The `Controller` is the single source of truth for settable state (e.g. `ui_ai_input`, `ui_separate_tier1`, `show_windows`, `temperature`).
|
||||
- The `App` is a thin view layer that delegates reads (`__getattr__`) and writes (`__setattr__`) to the Controller.
|
||||
@@ -461,9 +465,19 @@ uv run python -c "import ast; tree = ast.parse(open('src/gui_2.py').read()); [pr
|
||||
|
||||
**How to fix:** Re-indent the affected method to 2-space class level. This bit the project in 2026-06-05 during a cleanup commit: `_capture_workspace_profile` was being parsed as nested inside `_apply_snapshot` due to a 1-space indentation drift, breaking 3 live_gui tests (test_auto_switch_sim, test_workspace_profiles_restoration, test_undo_redo_lifecycle).
|
||||
|
||||
---
|
||||
### Startup Architecture (Lazy Imports, Profiler, Refresh Rate)
|
||||
|
||||
The 2026-06-06 `startup_speedup_20260606` track restructured `gui_2.py` for ~2400ms faster startup. The key components:
|
||||
|
||||
**`_LazyModule` proxies** (`np`, `filedialog`, `Tk`, `win32gui`, `win32con`): Defer `import numpy`, `import tkinter.filedialog`, `import tkinter`, `import win32gui`, `import win32con` until first attribute access. The first `gui_2` import drops from ~1770ms to ~341ms.
|
||||
|
||||
**`_FiledialogStub`**: No-op fallback for tkinter-less environments (e.g., headless CI). Sets `available = False` so the GUI can detect and skip file dialogs gracefully.
|
||||
|
||||
**`startup_profiler` + `render_warmup_status_indicator(app)`**: `AppController(defer_warmup=True)` defers heavy SDK warmup (google.genai, anthropic, openai, fastapi) to a background thread. `startup_profiler.phase(name)` wraps each init phase and reports per-phase duration. `render_warmup_status_indicator` is called per-frame during the warmup window to show a progress indicator in the UI. The warmup completes asynchronously; `_on_warmup_complete_callback` is invoked when done. See [guide_architecture.md](guide_architecture.md#warmup-architecture) for the full mechanism.
|
||||
|
||||
**Native refresh rate detection** (`_detect_refresh_rate_win32`): The old implementation used a PowerShell/WMI subprocess (~350ms blocking). The new implementation uses `ctypes.windll.user32.EnumDisplaySettingsW` directly (~0.3ms, 1000x faster). Used by the ImGui IO setup to set the display refresh rate.
|
||||
|
||||
**`immapp.run` error handling**: `immapp.run` is wrapped in try/except catching `RuntimeError` from native ImGui bundle assertions. On native crash, `_gui_degraded_reason` and `_last_imgui_assert` are set on the controller, and the GUI enters a degraded mode (rendering a static error panel instead of the live UI). This prevents the Python process from being killed by an uncatchable Windows access violation (`0xc0000005`).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user