Add comprehensive developer tooling for Gitea integration: CLI Tool (cmd/gitea-cli/): - gitea-cli auth login/logout/status - Authentication management - gitea-cli upload release-asset - Chunked upload with progress - gitea-cli upload resume - Resume interrupted uploads - gitea-cli upload list - List pending upload sessions - Parallel chunk uploads with configurable workers - SHA256 checksum verification - Progress bar with speed and ETA display Go SDK (sdk/go/): - GiteaClient with token authentication - User, Repository, Release, Attachment types - ChunkedUpload with parallel workers - Progress callbacks for upload tracking - Functional options pattern (WithChunkSize, WithParallel, etc.) Python SDK (sdk/python/): - GiteaClient with requests-based HTTP - Full type hints and dataclasses - ThreadPoolExecutor for parallel uploads - Resume capability for interrupted uploads - Exception hierarchy (APIError, UploadError, etc.) TypeScript SDK (sdk/typescript/): - Full TypeScript types and interfaces - Async/await API design - Browser and Node.js compatible - Web Crypto API for checksums - ESM and CJS build outputs All SDKs support: - Chunked uploads for large files - Parallel upload workers - Progress tracking with callbacks - Checksum verification - Resume interrupted uploads 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
91 lines
2.6 KiB
Python
91 lines
2.6 KiB
Python
# Copyright 2026 The Gitea Authors. All rights reserved.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""Exception classes for the Gitea SDK."""
|
|
|
|
from typing import Optional, Dict, Any
|
|
|
|
|
|
class GiteaError(Exception):
|
|
"""Base exception for all Gitea SDK errors."""
|
|
|
|
def __init__(self, message: str):
|
|
self.message = message
|
|
super().__init__(message)
|
|
|
|
|
|
class APIError(GiteaError):
|
|
"""Raised when the API returns an error response."""
|
|
|
|
def __init__(
|
|
self,
|
|
message: str,
|
|
code: Optional[str] = None,
|
|
status: int = 0,
|
|
details: Optional[Dict[str, Any]] = None,
|
|
):
|
|
super().__init__(message)
|
|
self.code = code
|
|
self.status = status
|
|
self.details = details or {}
|
|
|
|
def __str__(self) -> str:
|
|
if self.code:
|
|
return f"[{self.code}] {self.message}"
|
|
return self.message
|
|
|
|
|
|
class AuthenticationError(APIError):
|
|
"""Raised when authentication fails."""
|
|
|
|
def __init__(self, message: str = "Authentication failed", code: str = "AUTH_FAILED"):
|
|
super().__init__(message, code=code, status=401)
|
|
|
|
|
|
class NotFoundError(APIError):
|
|
"""Raised when a resource is not found."""
|
|
|
|
def __init__(self, resource: str, identifier: str = ""):
|
|
message = f"{resource} not found"
|
|
if identifier:
|
|
message = f"{resource} '{identifier}' not found"
|
|
super().__init__(message, code="NOT_FOUND", status=404)
|
|
self.resource = resource
|
|
self.identifier = identifier
|
|
|
|
|
|
class ValidationError(APIError):
|
|
"""Raised when request validation fails."""
|
|
|
|
def __init__(self, message: str, field: Optional[str] = None):
|
|
super().__init__(message, code="VALIDATION_FAILED", status=400)
|
|
self.field = field
|
|
|
|
|
|
class UploadError(GiteaError):
|
|
"""Raised when an upload fails."""
|
|
|
|
def __init__(self, message: str, session_id: Optional[str] = None, chunk: Optional[int] = None):
|
|
super().__init__(message)
|
|
self.session_id = session_id
|
|
self.chunk = chunk
|
|
|
|
def __str__(self) -> str:
|
|
parts = [self.message]
|
|
if self.session_id:
|
|
parts.append(f"session={self.session_id}")
|
|
if self.chunk is not None:
|
|
parts.append(f"chunk={self.chunk}")
|
|
return " ".join(parts)
|
|
|
|
|
|
class RateLimitError(APIError):
|
|
"""Raised when rate limit is exceeded."""
|
|
|
|
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, code="RATE_LIMIT_EXCEEDED", status=429)
|
|
self.retry_after = retry_after
|