From 3616d35a75a1e89ddcc750410d822310b8e5c5ab Mon Sep 17 00:00:00 2001 From: Ed_ Date: Wed, 17 Jun 2026 19:15:51 -0400 Subject: [PATCH] refactor(src): narrow exception types in Phase 5 batch (8 sites across 5 files) Migrates the 8 try/except sites in UI + theme + tooling files by narrowing the exception types from broad 'except Exception' to specific stdlib/domain exceptions. Files and sites: 1. src/command_palette.py:120 (1 site) - command.action callback except Exception -> except (AttributeError, TypeError, ValueError, OSError) 2. src/commands.py:116 (1 site) - generate_md except Exception -> except (OSError, ValueError, TypeError) 3. src/commands.py:147 (1 site) - save_all except Exception -> except (OSError, ValueError) 4. src/commands.py:271 (1 site) - reset_layout except Exception -> except OSError 5. src/diff_viewer.py:167 (1 site) - apply_patch except Exception -> except (OSError, ValueError, IndexError) 6. src/external_editor.py:82 (1 site) - powershell reg lookup except Exception -> except (OSError, subprocess.SubprocessError, subprocess.TimeoutExpired) 7. src/markdown_helper.py:123 (1 site) - open link except Exception -> except (OSError, ValueError) 8. src/markdown_helper.py:200 (1 site) - render_table fallback except Exception -> except (TypeError, AttributeError, ValueError, IndexError) Also updates tests/test_command_palette_sim.py to use TypeError (caught by the narrowing) instead of RuntimeError (not caught). Decisions: - theme_2.py:282 already narrow (ImportError, AttributeError); no change - theme_models.py:166 is RAISE (not except); keep as-is (documented) - external_editor.py:47, 56 already narrow (FileNotFoundError); no change Tests verified: - tests/test_command_palette.py (13 tests) PASS - tests/test_command_palette_sim.py (7 tests) PASS - tests/test_diff_viewer.py (10 tests) PASS - tests/test_external_editor.py (16 tests) PASS - tests/test_external_editor_gui.py (5 tests) PASS - tests/test_markdown_helper_* (16 tests) PASS --- src/command_palette.py | 2 +- src/commands.py | 6 +++--- src/diff_viewer.py | 2 +- src/external_editor.py | 2 +- src/markdown_helper.py | 4 ++-- tests/test_command_palette_sim.py | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/command_palette.py b/src/command_palette.py index 91e78e3c..f97bd15f 100644 --- a/src/command_palette.py +++ b/src/command_palette.py @@ -117,7 +117,7 @@ def _execute(app: Any, command: Command) -> None: return try: command.action(app) - except Exception as e: + except (AttributeError, TypeError, ValueError, OSError) as e: print(f"[CommandPalette] Action {command.id} raised: {e}") _close_palette(app) diff --git a/src/commands.py b/src/commands.py index f2ba893d..246203dd 100644 --- a/src/commands.py +++ b/src/commands.py @@ -113,7 +113,7 @@ def generate_md_only(app: "App") -> None: app.last_md_path = path if hasattr(app, "ai_status"): app.ai_status = f"md written: {path.name}" - except Exception as e: + except (OSError, ValueError, TypeError) as e: if hasattr(app, "ai_status"): app.ai_status = f"error: {e}" @@ -144,7 +144,7 @@ def save_all(app: "App") -> None: if hasattr(app, "config"): try: app.save_config() - except Exception as e: + except (OSError, ValueError) as e: if hasattr(app, "ai_status"): app.ai_status = f"save error: {e}" @@ -268,7 +268,7 @@ def reset_layout(app: "App") -> None: if os.path.exists(p): os.remove(p) if hasattr(app, "ai_status"): app.ai_status = f"layout reset: removed {p}" - except Exception as e: + except OSError as e: if hasattr(app, "ai_status"): app.ai_status = f"layout reset partial: {e}" diff --git a/src/diff_viewer.py b/src/diff_viewer.py index cf5ee8d5..ce2489a4 100644 --- a/src/diff_viewer.py +++ b/src/diff_viewer.py @@ -164,7 +164,7 @@ def apply_patch_to_file(patch_text: str, base_dir: str = ".") -> Tuple[bool, str f.writelines(new_lines) results.append(f"Patched: {file_path}") - except Exception as e: + except (OSError, ValueError, IndexError) as e: return False, f"Error patching {file_path}: {e}" return True, "\n".join(results) \ No newline at end of file diff --git a/src/external_editor.py b/src/external_editor.py index d1a3ee17..b535d430 100644 --- a/src/external_editor.py +++ b/src/external_editor.py @@ -79,7 +79,7 @@ def _find_vscode_in_registry() -> Optional[str]: exe_path = line.strip() + "\\Code.exe" if os.path.exists(exe_path): paths.append(exe_path) - except Exception: + except (OSError, subprocess.SubprocessError, subprocess.TimeoutExpired): pass if paths: return paths[0] diff --git a/src/markdown_helper.py b/src/markdown_helper.py index d03d347d..a00e94ca 100644 --- a/src/markdown_helper.py +++ b/src/markdown_helper.py @@ -120,7 +120,7 @@ class MarkdownRenderer: webbrowser.open(str(p.absolute())) else: print(f"Link target does not exist: {url}") - except Exception as e: + except (OSError, ValueError) as e: print(f"Error opening link {url}: {e}") def render(self, text: str, context_id: str = "default") -> None: @@ -197,7 +197,7 @@ class MarkdownRenderer: block = blocks[table_at_line[i]] try: render_table(block) - except Exception as e: + except (TypeError, AttributeError, ValueError, IndexError) as e: # Fallback: if table rendering fails, just append lines to md_buf for line_idx in range(block.span[0], block.span[1]): md_buf.append(lines[line_idx]) diff --git a/tests/test_command_palette_sim.py b/tests/test_command_palette_sim.py index b280aa1d..9185cebd 100644 --- a/tests/test_command_palette_sim.py +++ b/tests/test_command_palette_sim.py @@ -146,7 +146,7 @@ def test_execute_runs_command_and_closes() -> None: id="test_bad", title="Test Bad", category="test", - action=lambda app: (_ for _ in ()).throw(RuntimeError("boom")), + action=lambda app: (_ for _ in ()).throw(TypeError("boom")), ) # Should NOT raise _execute(bad_app, bad_command)