Files
markitect-main/tddai/issue_fetcher.py
tegwick bbc6192fe1 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>
2025-09-26 16:35:13 +02:00

92 lines
3.5 KiB
Python

"""
Issue fetching using the Gitea facade.
This module now acts as an adapter to the new gitea package,
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
# Re-export Issue for backwards compatibility
Issue = GiteaIssue
class IssueFetcher:
"""Fetches issues using the Gitea facade."""
def __init__(self, config=None):
self.config = config or get_config()
# Create Gitea client from tddai config
gitea_config = GiteaConfig.from_tddai_config(self.config)
self.gitea_client = GiteaClient(gitea_config)
def fetch_issue(self, issue_number: int) -> Issue:
"""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 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.
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 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.
Raises:
IssueError: When open issues cannot be fetched (with specific context)
"""
try:
return self.gitea_client.issues.list_open()
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."""
issue = self.fetch_issue(issue_number)
return {
'number': issue.number,
'title': issue.title,
'body': issue.body,
'state': issue.state,
'created_at': issue.created_at.isoformat(),
'updated_at': issue.updated_at.isoformat(),
'html_url': issue.html_url,
'assignee': {'login': issue.assignee.login} if issue.assignee else None,
'labels': [{'name': label.name} for label in issue.labels]
}