"""Custom exceptions and error handling for YouTrack CLI."""
from typing import List, Optional
__all__ = [
"YouTrackError",
"AuthenticationError",
"ConnectionError",
"ValidationError",
"NotFoundError",
"PermissionError",
"RateLimitError",
"YouTrackNetworkError",
"YouTrackServerError",
"CommandValidationError",
"ParameterError",
"UsageError",
"TokenRefreshError",
"TokenExpiredError",
]
[docs]
class YouTrackError(Exception):
"""Base exception for YouTrack CLI errors."""
[docs]
def __init__(self, message: str, suggestion: Optional[str] = None):
super().__init__(message)
self.message = message
self.suggestion = suggestion
[docs]
class AuthenticationError(YouTrackError):
"""Authentication related errors."""
[docs]
def __init__(self, message: str = "Authentication failed"):
super().__init__(message, suggestion="Run 'yt auth login' to authenticate with YouTrack")
[docs]
class ConnectionError(YouTrackError):
"""Connection related errors."""
[docs]
def __init__(self, message: str = "Failed to connect to YouTrack"):
super().__init__(message, suggestion="Check your internet connection and YouTrack URL")
[docs]
class ValidationError(YouTrackError):
"""Input validation errors."""
[docs]
def __init__(self, message: str, field: Optional[str] = None):
if field:
message = f"Invalid {field}: {message}"
super().__init__(message)
self.field = field
[docs]
class NotFoundError(YouTrackError):
"""Resource not found errors."""
[docs]
def __init__(self, resource_type: str, identifier: str):
super().__init__(
f"{resource_type} '{identifier}' not found",
suggestion=(f"Check if the {resource_type.lower()} exists and you have access to it"),
)
[docs]
class PermissionError(YouTrackError):
"""Permission denied errors."""
[docs]
def __init__(self, action: str, resource: Optional[str] = None):
if resource:
message = f"Permission denied to {action} {resource}"
else:
message = f"Permission denied to {action}"
super().__init__(message, suggestion="Check your user permissions in YouTrack")
[docs]
class RateLimitError(YouTrackError):
"""Rate limit exceeded errors."""
[docs]
def __init__(self, retry_after: Optional[int] = None):
message = "Rate limit exceeded"
if retry_after:
message += f". Retry after {retry_after} seconds"
super().__init__(
message,
suggestion=("Wait a moment and try again, or reduce the frequency of requests"),
)
[docs]
class YouTrackNetworkError(YouTrackError):
"""Network related errors that may be retryable."""
[docs]
def __init__(self, message: str = "Network error occurred"):
super().__init__(message, suggestion="Check your internet connection and try again")
[docs]
class YouTrackServerError(YouTrackError):
"""Server-side errors that may be retryable."""
[docs]
def __init__(self, message: str = "Server error occurred", status_code: Optional[int] = None):
if status_code:
message = f"Server error (HTTP {status_code}): {message}"
super().__init__(message, suggestion="The server may be temporarily unavailable. Try again later")
self.status_code = status_code
[docs]
class CommandValidationError(YouTrackError):
"""Errors related to command structure and usage."""
[docs]
def __init__(
self,
message: str,
command_path: Optional[str] = None,
usage_example: Optional[str] = None,
similar_commands: Optional[List[str]] = None,
):
suggestion_parts = []
if similar_commands:
suggestion_parts.append(f"Did you mean: {', '.join(similar_commands)}")
if usage_example:
suggestion_parts.append(f"Usage: {usage_example}")
suggestion = " | ".join(suggestion_parts) if suggestion_parts else None
super().__init__(message, suggestion)
self.command_path = command_path
self.usage_example = usage_example
self.similar_commands = similar_commands
[docs]
class ParameterError(YouTrackError):
"""Errors related to command parameters and arguments."""
[docs]
def __init__(
self,
message: str,
parameter_name: Optional[str] = None,
expected_type: Optional[str] = None,
usage_example: Optional[str] = None,
valid_choices: Optional[List[str]] = None,
):
suggestion_parts = []
if expected_type:
suggestion_parts.append(f"Expected {expected_type}")
if valid_choices:
suggestion_parts.append(f"Valid choices: {', '.join(valid_choices)}")
if usage_example:
suggestion_parts.append(f"Example: {usage_example}")
suggestion = " | ".join(suggestion_parts) if suggestion_parts else None
super().__init__(message, suggestion)
self.parameter_name = parameter_name
self.expected_type = expected_type
self.usage_example = usage_example
self.valid_choices = valid_choices
[docs]
class UsageError(YouTrackError):
"""Errors that provide comprehensive usage guidance."""
[docs]
def __init__(
self,
message: str,
command_path: str,
usage_syntax: str,
examples: Optional[List[str]] = None,
common_mistakes: Optional[List[str]] = None,
):
suggestion_parts = [f"Usage: {usage_syntax}"]
if examples:
suggestion_parts.append("Examples:")
for i, example in enumerate(examples, 1):
suggestion_parts.append(f" {i}. {example}")
if common_mistakes:
suggestion_parts.append("Common mistakes to avoid:")
for mistake in common_mistakes:
suggestion_parts.append(f" - {mistake}")
suggestion = "\n".join(suggestion_parts)
super().__init__(message, suggestion)
self.command_path = command_path
self.usage_syntax = usage_syntax
self.examples = examples or []
self.common_mistakes = common_mistakes or []
[docs]
class TokenRefreshError(AuthenticationError):
"""Token refresh related errors."""
[docs]
def __init__(self, message: str = "Token refresh failed"):
super().__init__(message)
self.suggestion = "Try 'yt auth login' to re-authenticate or check if your token supports refresh"
[docs]
class TokenExpiredError(AuthenticationError):
"""Token expiration related errors."""
[docs]
def __init__(self, message: str = "Token has expired"):
super().__init__(message)
self.suggestion = "Run 'yt auth refresh' to renew your token or 'yt auth login' to re-authenticate"