refactor: Standardize error handling patterns across codebase

Comprehensive error handling improvements addressing inconsistent patterns:

• Created markitect/exceptions.py with complete domain-specific exception hierarchy
  - MarkitectError base class with context and cause chaining support
  - Specific exceptions for Document, AST, Cache, Database, Schema operations
  - Built-in logging and context preservation

• Fixed overly broad exception handling in tddai modules:
  - issue_fetcher.py: Replace generic Exception with specific Gitea errors
  - project_manager.py: Proper error translation with context preservation
  - coverage_analyzer.py: Replace silent suppression with logging

• Enhanced cache_service.py error handling:
  - Specific OSError/PermissionError handling for file operations
  - Logging integration for unexpected errors
  - Preserved error collection and reporting

• Implemented proper exception chaining patterns:
  - All error translations use `raise ... from e` for debugging
  - Preserved original exception context and stack traces
  - Added docstring declarations of raised exceptions

• Benefits:
  - Eliminates silent error suppression and debugging black holes
  - Provides specific, actionable error messages
  - Preserves full error context for troubleshooting
  - Establishes consistent patterns for future development

Resolves issue #21: Error handling standardization

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-26 16:35:13 +02:00
parent 7f5309c4b0
commit bbc6192fe1
7 changed files with 420 additions and 18 deletions

View File

@@ -8,6 +8,7 @@ maintaining backwards compatibility while using the cleaner API.
from typing import List, Dict, Any
from gitea import GiteaClient, Issue as GiteaIssue, GiteaConfig
from gitea.exceptions import GiteaError, GiteaNotFoundError, GiteaAuthError, GiteaApiError
from .config import get_config
from .exceptions import IssueError
@@ -26,27 +27,54 @@ class IssueFetcher:
self.gitea_client = GiteaClient(gitea_config)
def fetch_issue(self, issue_number: int) -> Issue:
"""Fetch a specific issue by number."""
"""Fetch a specific issue by number.
Raises:
IssueError: When issue cannot be fetched (with specific context)
"""
try:
return self.gitea_client.issues.get(issue_number)
except Exception as e:
# Convert gitea exceptions to IssueError for backwards compatibility
raise IssueError(f"Failed to fetch issue #{issue_number}: {e}")
except GiteaNotFoundError as e:
raise IssueError(f"Issue #{issue_number} not found") from e
except GiteaAuthError as e:
raise IssueError(f"Authentication failed when fetching issue #{issue_number}") from e
except GiteaApiError as e:
raise IssueError(f"API error fetching issue #{issue_number}: {e}") from e
except GiteaError as e:
raise IssueError(f"Gitea error fetching issue #{issue_number}: {e}") from e
def fetch_issues(self, state: str = "all") -> List[Issue]:
"""Fetch all issues with optional state filter."""
"""Fetch all issues with optional state filter.
Args:
state: Issue state filter ("all", "open", "closed")
Raises:
IssueError: When issues cannot be fetched (with specific context)
"""
try:
return self.gitea_client.issues.list(state=state)
except Exception as e:
# Convert gitea exceptions to IssueError for backwards compatibility
raise IssueError(f"Failed to fetch issues: {e}")
except GiteaAuthError as e:
raise IssueError("Authentication failed when fetching issues") from e
except GiteaApiError as e:
raise IssueError(f"API error fetching issues with state '{state}': {e}") from e
except GiteaError as e:
raise IssueError(f"Gitea error fetching issues: {e}") from e
def fetch_open_issues(self) -> List[Issue]:
"""Fetch only open issues."""
"""Fetch only open issues.
Raises:
IssueError: When open issues cannot be fetched (with specific context)
"""
try:
return self.gitea_client.issues.list_open()
except Exception as e:
raise IssueError(f"Failed to fetch open issues: {e}")
except GiteaAuthError as e:
raise IssueError("Authentication failed when fetching open issues") from e
except GiteaApiError as e:
raise IssueError(f"API error fetching open issues: {e}") from e
except GiteaError as e:
raise IssueError(f"Gitea error fetching open issues: {e}") from e
def get_issue_data_dict(self, issue_number: int) -> Dict[str, Any]:
"""Get issue data as dictionary for workspace creation."""