sdkrouter 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sdkrouter/__init__.py +110 -0
- sdkrouter/_api/__init__.py +28 -0
- sdkrouter/_api/client.py +204 -0
- sdkrouter/_api/generated/__init__.py +21 -0
- sdkrouter/_api/generated/cdn/__init__.py +209 -0
- sdkrouter/_api/generated/cdn/cdn__api__cdn/__init__.py +7 -0
- sdkrouter/_api/generated/cdn/cdn__api__cdn/client.py +133 -0
- sdkrouter/_api/generated/cdn/cdn__api__cdn/models.py +163 -0
- sdkrouter/_api/generated/cdn/cdn__api__cdn/sync_client.py +132 -0
- sdkrouter/_api/generated/cdn/client.py +75 -0
- sdkrouter/_api/generated/cdn/logger.py +256 -0
- sdkrouter/_api/generated/cdn/pyproject.toml +55 -0
- sdkrouter/_api/generated/cdn/retry.py +272 -0
- sdkrouter/_api/generated/cdn/sync_client.py +58 -0
- sdkrouter/_api/generated/cleaner/__init__.py +212 -0
- sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/__init__.py +7 -0
- sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/client.py +83 -0
- sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/models.py +117 -0
- sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/sync_client.py +82 -0
- sdkrouter/_api/generated/cleaner/client.py +75 -0
- sdkrouter/_api/generated/cleaner/enums.py +55 -0
- sdkrouter/_api/generated/cleaner/logger.py +256 -0
- sdkrouter/_api/generated/cleaner/pyproject.toml +55 -0
- sdkrouter/_api/generated/cleaner/retry.py +272 -0
- sdkrouter/_api/generated/cleaner/sync_client.py +58 -0
- sdkrouter/_api/generated/keys/__init__.py +212 -0
- sdkrouter/_api/generated/keys/client.py +75 -0
- sdkrouter/_api/generated/keys/enums.py +64 -0
- sdkrouter/_api/generated/keys/keys__api__keys/__init__.py +7 -0
- sdkrouter/_api/generated/keys/keys__api__keys/client.py +150 -0
- sdkrouter/_api/generated/keys/keys__api__keys/models.py +152 -0
- sdkrouter/_api/generated/keys/keys__api__keys/sync_client.py +149 -0
- sdkrouter/_api/generated/keys/logger.py +256 -0
- sdkrouter/_api/generated/keys/pyproject.toml +55 -0
- sdkrouter/_api/generated/keys/retry.py +272 -0
- sdkrouter/_api/generated/keys/sync_client.py +58 -0
- sdkrouter/_api/generated/models/__init__.py +209 -0
- sdkrouter/_api/generated/models/client.py +75 -0
- sdkrouter/_api/generated/models/logger.py +256 -0
- sdkrouter/_api/generated/models/models__api__llm_models/__init__.py +7 -0
- sdkrouter/_api/generated/models/models__api__llm_models/client.py +99 -0
- sdkrouter/_api/generated/models/models__api__llm_models/models.py +206 -0
- sdkrouter/_api/generated/models/models__api__llm_models/sync_client.py +99 -0
- sdkrouter/_api/generated/models/pyproject.toml +55 -0
- sdkrouter/_api/generated/models/retry.py +272 -0
- sdkrouter/_api/generated/models/sync_client.py +58 -0
- sdkrouter/_api/generated/shortlinks/__init__.py +209 -0
- sdkrouter/_api/generated/shortlinks/client.py +75 -0
- sdkrouter/_api/generated/shortlinks/logger.py +256 -0
- sdkrouter/_api/generated/shortlinks/pyproject.toml +55 -0
- sdkrouter/_api/generated/shortlinks/retry.py +272 -0
- sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/__init__.py +7 -0
- sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/client.py +137 -0
- sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/models.py +153 -0
- sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/sync_client.py +136 -0
- sdkrouter/_api/generated/shortlinks/sync_client.py +58 -0
- sdkrouter/_api/generated/vision/__init__.py +212 -0
- sdkrouter/_api/generated/vision/client.py +75 -0
- sdkrouter/_api/generated/vision/enums.py +40 -0
- sdkrouter/_api/generated/vision/logger.py +256 -0
- sdkrouter/_api/generated/vision/pyproject.toml +55 -0
- sdkrouter/_api/generated/vision/retry.py +272 -0
- sdkrouter/_api/generated/vision/sync_client.py +58 -0
- sdkrouter/_api/generated/vision/vision__api__vision/__init__.py +7 -0
- sdkrouter/_api/generated/vision/vision__api__vision/client.py +65 -0
- sdkrouter/_api/generated/vision/vision__api__vision/models.py +138 -0
- sdkrouter/_api/generated/vision/vision__api__vision/sync_client.py +65 -0
- sdkrouter/_client.py +432 -0
- sdkrouter/_config.py +74 -0
- sdkrouter/_constants.py +21 -0
- sdkrouter/_internal/__init__.py +1 -0
- sdkrouter/_types/__init__.py +30 -0
- sdkrouter/_types/cdn.py +27 -0
- sdkrouter/_types/models.py +26 -0
- sdkrouter/_types/ocr.py +24 -0
- sdkrouter/_types/parsed.py +101 -0
- sdkrouter/_types/shortlinks.py +27 -0
- sdkrouter/_types/vision.py +29 -0
- sdkrouter/_version.py +3 -0
- sdkrouter/helpers/__init__.py +13 -0
- sdkrouter/helpers/formatting.py +15 -0
- sdkrouter/helpers/html.py +100 -0
- sdkrouter/helpers/json_cleaner.py +53 -0
- sdkrouter/tools/__init__.py +129 -0
- sdkrouter/tools/cdn.py +285 -0
- sdkrouter/tools/cleaner.py +186 -0
- sdkrouter/tools/keys.py +215 -0
- sdkrouter/tools/models.py +196 -0
- sdkrouter/tools/shortlinks.py +165 -0
- sdkrouter/tools/vision.py +173 -0
- sdkrouter/utils/__init__.py +27 -0
- sdkrouter/utils/parsing.py +109 -0
- sdkrouter/utils/tokens.py +375 -0
- sdkrouter-0.1.1.dist-info/METADATA +411 -0
- sdkrouter-0.1.1.dist-info/RECORD +96 -0
- sdkrouter-0.1.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from .models import *
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SyncKeysKeysAPI:
|
|
9
|
+
"""Synchronous API endpoints for Keys."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, client: httpx.Client):
|
|
12
|
+
"""Initialize sync sub-client with shared httpx client."""
|
|
13
|
+
self._client = client
|
|
14
|
+
|
|
15
|
+
def list(self, page: int | None = None, page_size: int | None = None) -> list[PaginatedAPIKeyListList]:
|
|
16
|
+
"""
|
|
17
|
+
ViewSet for API key management. Endpoints: - GET /api/keys/ - List
|
|
18
|
+
user's API keys - POST /api/keys/ - Create a new API key - GET
|
|
19
|
+
/api/keys/{id}/ - Get API key details - DELETE /api/keys/{id}/ -
|
|
20
|
+
Deactivate API key - POST /api/keys/{id}/rotate/ - Rotate API key - POST
|
|
21
|
+
/api/keys/{id}/reactivate/ - Reactivate API key
|
|
22
|
+
"""
|
|
23
|
+
url = "/api/keys/"
|
|
24
|
+
response = self._client.get(url, params={"page": page if page is not None else None, "page_size": page_size if page_size is not None else None})
|
|
25
|
+
if not response.is_success:
|
|
26
|
+
try:
|
|
27
|
+
error_body = response.json()
|
|
28
|
+
except Exception:
|
|
29
|
+
error_body = response.text
|
|
30
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
31
|
+
return PaginatedAPIKeyListList.model_validate(response.json())
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create(self, data: APIKeyCreateRequest) -> APIKeyCreate:
|
|
35
|
+
"""
|
|
36
|
+
Create a new API key.
|
|
37
|
+
"""
|
|
38
|
+
url = "/api/keys/"
|
|
39
|
+
response = self._client.post(url, json=data.model_dump(exclude_unset=True))
|
|
40
|
+
if not response.is_success:
|
|
41
|
+
try:
|
|
42
|
+
error_body = response.json()
|
|
43
|
+
except Exception:
|
|
44
|
+
error_body = response.text
|
|
45
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
46
|
+
return APIKeyCreate.model_validate(response.json())
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def retrieve(self, id: str) -> APIKeyDetail:
|
|
50
|
+
"""
|
|
51
|
+
ViewSet for API key management. Endpoints: - GET /api/keys/ - List
|
|
52
|
+
user's API keys - POST /api/keys/ - Create a new API key - GET
|
|
53
|
+
/api/keys/{id}/ - Get API key details - DELETE /api/keys/{id}/ -
|
|
54
|
+
Deactivate API key - POST /api/keys/{id}/rotate/ - Rotate API key - POST
|
|
55
|
+
/api/keys/{id}/reactivate/ - Reactivate API key
|
|
56
|
+
"""
|
|
57
|
+
url = f"/api/keys/{id}/"
|
|
58
|
+
response = self._client.get(url)
|
|
59
|
+
if not response.is_success:
|
|
60
|
+
try:
|
|
61
|
+
error_body = response.json()
|
|
62
|
+
except Exception:
|
|
63
|
+
error_body = response.text
|
|
64
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
65
|
+
return APIKeyDetail.model_validate(response.json())
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def update(self, id: str) -> APIKeyList:
|
|
69
|
+
"""
|
|
70
|
+
ViewSet for API key management. Endpoints: - GET /api/keys/ - List
|
|
71
|
+
user's API keys - POST /api/keys/ - Create a new API key - GET
|
|
72
|
+
/api/keys/{id}/ - Get API key details - DELETE /api/keys/{id}/ -
|
|
73
|
+
Deactivate API key - POST /api/keys/{id}/rotate/ - Rotate API key - POST
|
|
74
|
+
/api/keys/{id}/reactivate/ - Reactivate API key
|
|
75
|
+
"""
|
|
76
|
+
url = f"/api/keys/{id}/"
|
|
77
|
+
response = self._client.put(url)
|
|
78
|
+
if not response.is_success:
|
|
79
|
+
try:
|
|
80
|
+
error_body = response.json()
|
|
81
|
+
except Exception:
|
|
82
|
+
error_body = response.text
|
|
83
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
84
|
+
return APIKeyList.model_validate(response.json())
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def partial_update(self, id: str) -> APIKeyList:
|
|
88
|
+
"""
|
|
89
|
+
ViewSet for API key management. Endpoints: - GET /api/keys/ - List
|
|
90
|
+
user's API keys - POST /api/keys/ - Create a new API key - GET
|
|
91
|
+
/api/keys/{id}/ - Get API key details - DELETE /api/keys/{id}/ -
|
|
92
|
+
Deactivate API key - POST /api/keys/{id}/rotate/ - Rotate API key - POST
|
|
93
|
+
/api/keys/{id}/reactivate/ - Reactivate API key
|
|
94
|
+
"""
|
|
95
|
+
url = f"/api/keys/{id}/"
|
|
96
|
+
response = self._client.patch(url)
|
|
97
|
+
if not response.is_success:
|
|
98
|
+
try:
|
|
99
|
+
error_body = response.json()
|
|
100
|
+
except Exception:
|
|
101
|
+
error_body = response.text
|
|
102
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
103
|
+
return APIKeyList.model_validate(response.json())
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def destroy(self, id: str) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Deactivate an API key (soft delete).
|
|
109
|
+
"""
|
|
110
|
+
url = f"/api/keys/{id}/"
|
|
111
|
+
response = self._client.delete(url)
|
|
112
|
+
if not response.is_success:
|
|
113
|
+
try:
|
|
114
|
+
error_body = response.json()
|
|
115
|
+
except Exception:
|
|
116
|
+
error_body = response.text
|
|
117
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def reactivate_create(self, id: str) -> APIKeyList:
|
|
121
|
+
"""
|
|
122
|
+
Reactivate a deactivated API key.
|
|
123
|
+
"""
|
|
124
|
+
url = f"/api/keys/{id}/reactivate/"
|
|
125
|
+
response = self._client.post(url)
|
|
126
|
+
if not response.is_success:
|
|
127
|
+
try:
|
|
128
|
+
error_body = response.json()
|
|
129
|
+
except Exception:
|
|
130
|
+
error_body = response.text
|
|
131
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
132
|
+
return APIKeyList.model_validate(response.json())
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def rotate_create(self, id: str) -> APIKeyList:
|
|
136
|
+
"""
|
|
137
|
+
Rotate an API key (generate new key).
|
|
138
|
+
"""
|
|
139
|
+
url = f"/api/keys/{id}/rotate/"
|
|
140
|
+
response = self._client.post(url)
|
|
141
|
+
if not response.is_success:
|
|
142
|
+
try:
|
|
143
|
+
error_body = response.json()
|
|
144
|
+
except Exception:
|
|
145
|
+
error_body = response.text
|
|
146
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
147
|
+
return APIKeyList.model_validate(response.json())
|
|
148
|
+
|
|
149
|
+
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# Auto-generated by DjangoCFG - see CLAUDE.md
|
|
2
|
+
"""
|
|
3
|
+
API Logger with Rich
|
|
4
|
+
Beautiful console logging for API requests and responses
|
|
5
|
+
|
|
6
|
+
Installation:
|
|
7
|
+
pip install rich
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import time
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Any, Dict, Optional
|
|
15
|
+
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
from rich.panel import Panel
|
|
18
|
+
from rich.table import Table
|
|
19
|
+
from rich.text import Text
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class RequestLog:
|
|
24
|
+
"""Request log data."""
|
|
25
|
+
|
|
26
|
+
method: str
|
|
27
|
+
url: str
|
|
28
|
+
headers: Optional[Dict[str, str]] = None
|
|
29
|
+
body: Optional[Any] = None
|
|
30
|
+
timestamp: float = field(default_factory=time.time)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class ResponseLog:
|
|
35
|
+
"""Response log data."""
|
|
36
|
+
|
|
37
|
+
status: int
|
|
38
|
+
status_text: str
|
|
39
|
+
data: Optional[Any] = None
|
|
40
|
+
duration: float = 0.0
|
|
41
|
+
timestamp: float = field(default_factory=time.time)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class ErrorLog:
|
|
46
|
+
"""Error log data."""
|
|
47
|
+
|
|
48
|
+
message: str
|
|
49
|
+
status_code: Optional[int] = None
|
|
50
|
+
field_errors: Optional[Dict[str, list[str]]] = None
|
|
51
|
+
duration: float = 0.0
|
|
52
|
+
timestamp: float = field(default_factory=time.time)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class LoggerConfig:
|
|
57
|
+
"""Logger configuration."""
|
|
58
|
+
|
|
59
|
+
enabled: bool = True
|
|
60
|
+
log_requests: bool = True
|
|
61
|
+
log_responses: bool = True
|
|
62
|
+
log_errors: bool = True
|
|
63
|
+
log_bodies: bool = True
|
|
64
|
+
log_headers: bool = False
|
|
65
|
+
console: Optional[Console] = None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Sensitive header names to filter out
|
|
69
|
+
SENSITIVE_HEADERS = [
|
|
70
|
+
"authorization",
|
|
71
|
+
"cookie",
|
|
72
|
+
"set-cookie",
|
|
73
|
+
"x-api-key",
|
|
74
|
+
"x-csrf-token",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class APILogger:
|
|
79
|
+
"""API Logger class."""
|
|
80
|
+
|
|
81
|
+
def __init__(self, config: Optional[LoggerConfig] = None):
|
|
82
|
+
"""Initialize logger."""
|
|
83
|
+
self.config = config or LoggerConfig()
|
|
84
|
+
self.console = self.config.console or Console()
|
|
85
|
+
|
|
86
|
+
def enable(self) -> None:
|
|
87
|
+
"""Enable logging."""
|
|
88
|
+
self.config.enabled = True
|
|
89
|
+
|
|
90
|
+
def disable(self) -> None:
|
|
91
|
+
"""Disable logging."""
|
|
92
|
+
self.config.enabled = False
|
|
93
|
+
|
|
94
|
+
def set_config(self, **kwargs: Any) -> None:
|
|
95
|
+
"""Update configuration."""
|
|
96
|
+
for key, value in kwargs.items():
|
|
97
|
+
if hasattr(self.config, key):
|
|
98
|
+
setattr(self.config, key, value)
|
|
99
|
+
|
|
100
|
+
def _filter_headers(self, headers: Optional[Dict[str, str]]) -> Dict[str, str]:
|
|
101
|
+
"""Filter sensitive headers."""
|
|
102
|
+
if not headers:
|
|
103
|
+
return {}
|
|
104
|
+
|
|
105
|
+
filtered = {}
|
|
106
|
+
for key, value in headers.items():
|
|
107
|
+
if key.lower() in SENSITIVE_HEADERS:
|
|
108
|
+
filtered[key] = "***"
|
|
109
|
+
else:
|
|
110
|
+
filtered[key] = value
|
|
111
|
+
|
|
112
|
+
return filtered
|
|
113
|
+
|
|
114
|
+
def log_request(self, request: RequestLog) -> None:
|
|
115
|
+
"""Log request."""
|
|
116
|
+
if not self.config.enabled or not self.config.log_requests:
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
# Create request info
|
|
120
|
+
text = Text()
|
|
121
|
+
text.append("→ ", style="bold blue")
|
|
122
|
+
text.append(request.method, style="bold yellow")
|
|
123
|
+
text.append(" ", style="")
|
|
124
|
+
text.append(request.url, style="cyan")
|
|
125
|
+
|
|
126
|
+
self.console.print(text)
|
|
127
|
+
|
|
128
|
+
if self.config.log_headers and request.headers:
|
|
129
|
+
headers = self._filter_headers(request.headers)
|
|
130
|
+
self.console.print(" Headers:", style="dim")
|
|
131
|
+
for key, value in headers.items():
|
|
132
|
+
self.console.print(f" {key}: {value}", style="dim")
|
|
133
|
+
|
|
134
|
+
if self.config.log_bodies and request.body:
|
|
135
|
+
self.console.print(" Body:", style="dim")
|
|
136
|
+
self.console.print(request.body, style="dim")
|
|
137
|
+
|
|
138
|
+
def log_response(self, request: RequestLog, response: ResponseLog) -> None:
|
|
139
|
+
"""Log response."""
|
|
140
|
+
if not self.config.enabled or not self.config.log_responses:
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
# Determine color based on status
|
|
144
|
+
if response.status >= 500:
|
|
145
|
+
status_style = "bold red"
|
|
146
|
+
elif response.status >= 400:
|
|
147
|
+
status_style = "bold yellow"
|
|
148
|
+
elif response.status >= 300:
|
|
149
|
+
status_style = "bold cyan"
|
|
150
|
+
else:
|
|
151
|
+
status_style = "bold green"
|
|
152
|
+
|
|
153
|
+
# Create response info
|
|
154
|
+
text = Text()
|
|
155
|
+
text.append("← ", style="bold green")
|
|
156
|
+
text.append(request.method, style="bold yellow")
|
|
157
|
+
text.append(" ", style="")
|
|
158
|
+
text.append(request.url, style="cyan")
|
|
159
|
+
text.append(" ", style="")
|
|
160
|
+
text.append(str(response.status), style=status_style)
|
|
161
|
+
text.append(" ", style="")
|
|
162
|
+
text.append(response.status_text, style=status_style)
|
|
163
|
+
text.append(f" ({response.duration:.0f}ms)", style="dim")
|
|
164
|
+
|
|
165
|
+
self.console.print(text)
|
|
166
|
+
|
|
167
|
+
if self.config.log_bodies and response.data:
|
|
168
|
+
self.console.print(" Response:", style="dim")
|
|
169
|
+
self.console.print(response.data, style="dim")
|
|
170
|
+
|
|
171
|
+
def log_error(self, request: RequestLog, error: ErrorLog) -> None:
|
|
172
|
+
"""Log error."""
|
|
173
|
+
if not self.config.enabled or not self.config.log_errors:
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
# Create error header
|
|
177
|
+
text = Text()
|
|
178
|
+
text.append("✗ ", style="bold red")
|
|
179
|
+
text.append(request.method, style="bold yellow")
|
|
180
|
+
text.append(" ", style="")
|
|
181
|
+
text.append(request.url, style="cyan")
|
|
182
|
+
text.append(" ", style="")
|
|
183
|
+
text.append(
|
|
184
|
+
str(error.status_code) if error.status_code else "Network",
|
|
185
|
+
style="bold red",
|
|
186
|
+
)
|
|
187
|
+
text.append(" Error", style="bold red")
|
|
188
|
+
text.append(f" ({error.duration:.0f}ms)", style="dim")
|
|
189
|
+
|
|
190
|
+
self.console.print(text)
|
|
191
|
+
self.console.print(f" Message: {error.message}", style="red")
|
|
192
|
+
|
|
193
|
+
if error.field_errors:
|
|
194
|
+
self.console.print(" Field Errors:", style="red")
|
|
195
|
+
for field, errors in error.field_errors.items():
|
|
196
|
+
for err in errors:
|
|
197
|
+
self.console.print(f" • {field}: {err}", style="red dim")
|
|
198
|
+
|
|
199
|
+
def info(self, message: str, **kwargs: Any) -> None:
|
|
200
|
+
"""Log info message."""
|
|
201
|
+
if not self.config.enabled:
|
|
202
|
+
return
|
|
203
|
+
self.console.print(f"ℹ {message}", style="blue", **kwargs)
|
|
204
|
+
|
|
205
|
+
def warn(self, message: str, **kwargs: Any) -> None:
|
|
206
|
+
"""Log warning message."""
|
|
207
|
+
if not self.config.enabled:
|
|
208
|
+
return
|
|
209
|
+
self.console.print(f"⚠ {message}", style="yellow", **kwargs)
|
|
210
|
+
|
|
211
|
+
def error(self, message: str, **kwargs: Any) -> None:
|
|
212
|
+
"""Log error message."""
|
|
213
|
+
if not self.config.enabled:
|
|
214
|
+
return
|
|
215
|
+
self.console.print(f"✗ {message}", style="red", **kwargs)
|
|
216
|
+
|
|
217
|
+
def success(self, message: str, **kwargs: Any) -> None:
|
|
218
|
+
"""Log success message."""
|
|
219
|
+
if not self.config.enabled:
|
|
220
|
+
return
|
|
221
|
+
self.console.print(f"✓ {message}", style="green", **kwargs)
|
|
222
|
+
|
|
223
|
+
def debug(self, message: str, **kwargs: Any) -> None:
|
|
224
|
+
"""Log debug message."""
|
|
225
|
+
if not self.config.enabled:
|
|
226
|
+
return
|
|
227
|
+
self.console.print(f"🔍 {message}", style="dim", **kwargs)
|
|
228
|
+
|
|
229
|
+
def panel(self, content: Any, title: str, style: str = "blue") -> None:
|
|
230
|
+
"""Log content in a panel."""
|
|
231
|
+
if not self.config.enabled:
|
|
232
|
+
return
|
|
233
|
+
self.console.print(Panel(content, title=title, border_style=style))
|
|
234
|
+
|
|
235
|
+
def table(
|
|
236
|
+
self,
|
|
237
|
+
headers: list[str],
|
|
238
|
+
rows: list[list[Any]],
|
|
239
|
+
title: Optional[str] = None,
|
|
240
|
+
) -> None:
|
|
241
|
+
"""Log data in a table."""
|
|
242
|
+
if not self.config.enabled:
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
table = Table(title=title)
|
|
246
|
+
for header in headers:
|
|
247
|
+
table.add_column(header, style="cyan")
|
|
248
|
+
|
|
249
|
+
for row in rows:
|
|
250
|
+
table.add_row(*[str(cell) for cell in row])
|
|
251
|
+
|
|
252
|
+
self.console.print(table)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
# Default logger instance
|
|
256
|
+
default_logger = APILogger()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Auto-generated by DjangoCFG - see CLAUDE.md
|
|
2
|
+
[tool.poetry]
|
|
3
|
+
name = "api-client"
|
|
4
|
+
version = "1.0.0"
|
|
5
|
+
description = "Auto-generated Python client for SDKRouter API"
|
|
6
|
+
authors = ["Author \u003cauthor@example.com\u003e"]
|
|
7
|
+
license = "MIT"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
keywords = ["api", "client", "python", "openapi"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 5 - Production/Stable",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Typing :: Typed",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[tool.poetry.dependencies]
|
|
20
|
+
python = "^3.12"
|
|
21
|
+
pydantic = "^2.12"
|
|
22
|
+
httpx = "^0.28"
|
|
23
|
+
tenacity = "^9.1"
|
|
24
|
+
rich = "^14.1.0"
|
|
25
|
+
|
|
26
|
+
[tool.poetry.group.dev.dependencies]
|
|
27
|
+
pytest = "^8.0"
|
|
28
|
+
pytest-asyncio = "^0.24"
|
|
29
|
+
pytest-cov = "^6.0"
|
|
30
|
+
mypy = "^1.18"
|
|
31
|
+
ruff = "^0.13"
|
|
32
|
+
|
|
33
|
+
[build-system]
|
|
34
|
+
requires = ["poetry-core"]
|
|
35
|
+
build-backend = "poetry.core.masonry.api"
|
|
36
|
+
|
|
37
|
+
[tool.mypy]
|
|
38
|
+
python_version = "3.12"
|
|
39
|
+
strict = true
|
|
40
|
+
warn_return_any = true
|
|
41
|
+
warn_unused_configs = true
|
|
42
|
+
disallow_untyped_defs = true
|
|
43
|
+
|
|
44
|
+
[tool.ruff]
|
|
45
|
+
line-length = 100
|
|
46
|
+
target-version = "py312"
|
|
47
|
+
|
|
48
|
+
[tool.ruff.lint]
|
|
49
|
+
select = ["E", "F", "I", "N", "UP", "B"]
|
|
50
|
+
ignore = []
|
|
51
|
+
|
|
52
|
+
[tool.pytest.ini_options]
|
|
53
|
+
asyncio_mode = "auto"
|
|
54
|
+
testpaths = ["tests"]
|
|
55
|
+
addopts = "--cov=api_client --cov-report=html --cov-report=term"
|