aury-boot 0.0.39__py3-none-any.whl → 0.0.41__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.
- aury/boot/_version.py +2 -2
- aury/boot/application/adapter/http.py +17 -6
- aury/boot/application/app/base.py +1 -0
- aury/boot/application/app/components.py +93 -3
- aury/boot/application/config/settings.py +80 -2
- aury/boot/commands/init.py +20 -0
- aury/boot/commands/pkg.py +31 -1
- aury/boot/commands/templates/project/aury_docs/00-overview.md.tpl +1 -0
- aury/boot/commands/templates/project/aury_docs/18-monitoring-profiling.md.tpl +239 -0
- aury/boot/commands/templates/project/env_templates/monitoring.tpl +15 -0
- aury/boot/common/logging/setup.py +8 -3
- aury/boot/infrastructure/cache/redis.py +82 -16
- aury/boot/infrastructure/channel/__init__.py +2 -1
- aury/boot/infrastructure/channel/backends/__init__.py +2 -1
- aury/boot/infrastructure/channel/backends/redis_cluster.py +124 -0
- aury/boot/infrastructure/channel/backends/redis_cluster_channel.py +139 -0
- aury/boot/infrastructure/channel/base.py +2 -0
- aury/boot/infrastructure/channel/manager.py +9 -1
- aury/boot/infrastructure/clients/redis/manager.py +90 -19
- aury/boot/infrastructure/database/manager.py +6 -4
- aury/boot/infrastructure/monitoring/__init__.py +10 -2
- aury/boot/infrastructure/monitoring/alerting/notifiers/feishu.py +33 -16
- aury/boot/infrastructure/monitoring/alerting/notifiers/webhook.py +14 -13
- aury/boot/infrastructure/monitoring/profiling/__init__.py +664 -0
- aury/boot/infrastructure/scheduler/__init__.py +2 -0
- aury/boot/infrastructure/scheduler/jobstores/__init__.py +10 -0
- aury/boot/infrastructure/scheduler/jobstores/redis_cluster.py +255 -0
- aury/boot/infrastructure/scheduler/manager.py +15 -3
- aury/boot/toolkit/http/__init__.py +180 -85
- {aury_boot-0.0.39.dist-info → aury_boot-0.0.41.dist-info}/METADATA +14 -4
- {aury_boot-0.0.39.dist-info → aury_boot-0.0.41.dist-info}/RECORD +33 -27
- {aury_boot-0.0.39.dist-info → aury_boot-0.0.41.dist-info}/WHEEL +0 -0
- {aury_boot-0.0.39.dist-info → aury_boot-0.0.41.dist-info}/entry_points.txt +0 -0
|
@@ -7,14 +7,18 @@
|
|
|
7
7
|
- 超时控制
|
|
8
8
|
- 错误处理
|
|
9
9
|
- 请求日志
|
|
10
|
+
|
|
11
|
+
基于 aiohttp 实现,全异步无阻塞。
|
|
10
12
|
"""
|
|
11
13
|
|
|
12
14
|
from __future__ import annotations
|
|
13
15
|
|
|
16
|
+
import time
|
|
14
17
|
from abc import ABC, abstractmethod
|
|
15
|
-
from
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from typing import Any, TypeVar
|
|
16
20
|
|
|
17
|
-
import
|
|
21
|
+
import aiohttp
|
|
18
22
|
from pydantic import BaseModel
|
|
19
23
|
from tenacity import (
|
|
20
24
|
retry,
|
|
@@ -35,7 +39,6 @@ class RetryConfig(BaseModel):
|
|
|
35
39
|
retry_delay: float = 1.0
|
|
36
40
|
backoff_factor: float = 2.0
|
|
37
41
|
retry_on_status: list[int] = [500, 502, 503, 504]
|
|
38
|
-
retry_on_exceptions: tuple = (httpx.TimeoutException, httpx.NetworkError)
|
|
39
42
|
|
|
40
43
|
|
|
41
44
|
class HttpClientConfig(BaseModel):
|
|
@@ -50,16 +53,83 @@ class HttpClientConfig(BaseModel):
|
|
|
50
53
|
retry: RetryConfig | None = None
|
|
51
54
|
|
|
52
55
|
|
|
56
|
+
@dataclass
|
|
57
|
+
class HttpRequest:
|
|
58
|
+
"""请求对象(用于拦截器)。"""
|
|
59
|
+
|
|
60
|
+
method: str
|
|
61
|
+
url: str
|
|
62
|
+
headers: dict[str, str] = field(default_factory=dict)
|
|
63
|
+
params: dict[str, Any] | None = None
|
|
64
|
+
json_data: Any = None
|
|
65
|
+
data: Any = None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class HttpResponse:
|
|
70
|
+
"""响应对象(用于拦截器和返回)。"""
|
|
71
|
+
|
|
72
|
+
status_code: int
|
|
73
|
+
url: str
|
|
74
|
+
headers: dict[str, str]
|
|
75
|
+
text: str
|
|
76
|
+
content: bytes
|
|
77
|
+
elapsed_seconds: float
|
|
78
|
+
|
|
79
|
+
def json(self) -> Any:
|
|
80
|
+
"""解析 JSON 响应。"""
|
|
81
|
+
import json
|
|
82
|
+
return json.loads(self.text)
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def is_success(self) -> bool:
|
|
86
|
+
"""是否成功响应。"""
|
|
87
|
+
return 200 <= self.status_code < 400
|
|
88
|
+
|
|
89
|
+
def raise_for_status(self) -> None:
|
|
90
|
+
"""如果状态码表示错误,抛出异常。"""
|
|
91
|
+
if self.status_code >= 400:
|
|
92
|
+
raise HttpStatusError(
|
|
93
|
+
f"HTTP {self.status_code}",
|
|
94
|
+
status_code=self.status_code,
|
|
95
|
+
response=self,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class HttpError(Exception):
|
|
100
|
+
"""HTTP 错误基类。"""
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class HttpStatusError(HttpError):
|
|
105
|
+
"""HTTP 状态码错误。"""
|
|
106
|
+
|
|
107
|
+
def __init__(self, message: str, status_code: int, response: HttpResponse) -> None:
|
|
108
|
+
super().__init__(message)
|
|
109
|
+
self.status_code = status_code
|
|
110
|
+
self.response = response
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class HttpTimeoutError(HttpError):
|
|
114
|
+
"""HTTP 超时错误。"""
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class HttpNetworkError(HttpError):
|
|
119
|
+
"""HTTP 网络错误。"""
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
|
|
53
123
|
class RequestInterceptor(ABC):
|
|
54
124
|
"""请求拦截器接口。"""
|
|
55
125
|
|
|
56
126
|
@abstractmethod
|
|
57
|
-
async def before_request(self, request:
|
|
127
|
+
async def before_request(self, request: HttpRequest) -> HttpRequest:
|
|
58
128
|
"""请求前处理。"""
|
|
59
129
|
pass
|
|
60
130
|
|
|
61
131
|
@abstractmethod
|
|
62
|
-
async def after_response(self, response:
|
|
132
|
+
async def after_response(self, response: HttpResponse) -> HttpResponse:
|
|
63
133
|
"""响应后处理。"""
|
|
64
134
|
pass
|
|
65
135
|
|
|
@@ -67,25 +137,25 @@ class RequestInterceptor(ABC):
|
|
|
67
137
|
class LoggingInterceptor(RequestInterceptor):
|
|
68
138
|
"""日志拦截器。"""
|
|
69
139
|
|
|
70
|
-
async def before_request(self, request:
|
|
140
|
+
async def before_request(self, request: HttpRequest) -> HttpRequest:
|
|
71
141
|
"""记录请求日志。"""
|
|
72
142
|
logger.debug(
|
|
73
143
|
f"HTTP请求: {request.method} {request.url} | "
|
|
74
|
-
f"Headers: {
|
|
144
|
+
f"Headers: {request.headers}"
|
|
75
145
|
)
|
|
76
146
|
return request
|
|
77
147
|
|
|
78
|
-
async def after_response(self, response:
|
|
148
|
+
async def after_response(self, response: HttpResponse) -> HttpResponse:
|
|
79
149
|
"""记录响应日志。"""
|
|
80
150
|
logger.debug(
|
|
81
151
|
f"HTTP响应: {response.status_code} {response.url} | "
|
|
82
|
-
f"耗时: {response.
|
|
152
|
+
f"耗时: {response.elapsed_seconds:.3f}s"
|
|
83
153
|
)
|
|
84
154
|
return response
|
|
85
155
|
|
|
86
156
|
|
|
87
157
|
class HttpClient:
|
|
88
|
-
"""企业级HTTP
|
|
158
|
+
"""企业级HTTP客户端(基于 aiohttp)。
|
|
89
159
|
|
|
90
160
|
特性:
|
|
91
161
|
- 连接池管理
|
|
@@ -93,6 +163,7 @@ class HttpClient:
|
|
|
93
163
|
- 拦截器支持
|
|
94
164
|
- 超时控制
|
|
95
165
|
- 错误处理
|
|
166
|
+
- 全异步无阻塞
|
|
96
167
|
|
|
97
168
|
使用示例:
|
|
98
169
|
# 基础使用
|
|
@@ -125,33 +196,39 @@ class HttpClient:
|
|
|
125
196
|
Args:
|
|
126
197
|
base_url: 基础URL
|
|
127
198
|
timeout: 超时时间(秒)
|
|
128
|
-
follow_redirects:
|
|
199
|
+
follow_redirects: 是否跟随重定向(aiohttp 默认不跟随)
|
|
129
200
|
max_connections: 最大连接数
|
|
130
201
|
retry_config: 重试配置
|
|
131
202
|
"""
|
|
132
|
-
self._base_url = base_url
|
|
133
|
-
self._timeout = timeout
|
|
203
|
+
self._base_url = base_url.rstrip("/") if base_url else ""
|
|
204
|
+
self._timeout = aiohttp.ClientTimeout(total=timeout)
|
|
134
205
|
self._follow_redirects = follow_redirects
|
|
135
206
|
self._max_connections = max_connections
|
|
136
207
|
self._retry_config = retry_config or RetryConfig()
|
|
137
208
|
self._interceptors: list[RequestInterceptor] = []
|
|
138
209
|
|
|
139
|
-
#
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
210
|
+
# 创建连接器(连接池)
|
|
211
|
+
self._connector = aiohttp.TCPConnector(
|
|
212
|
+
limit=max_connections,
|
|
213
|
+
limit_per_host=max_connections // 4,
|
|
214
|
+
keepalive_timeout=30,
|
|
215
|
+
enable_cleanup_closed=True,
|
|
144
216
|
)
|
|
145
217
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
timeout=timeout,
|
|
149
|
-
follow_redirects=follow_redirects,
|
|
150
|
-
limits=limits,
|
|
151
|
-
)
|
|
218
|
+
# aiohttp 会话(延迟创建)
|
|
219
|
+
self._session: aiohttp.ClientSession | None = None
|
|
152
220
|
|
|
153
221
|
logger.debug(f"HTTP客户端初始化: base_url={base_url}, timeout={timeout}")
|
|
154
222
|
|
|
223
|
+
async def _ensure_session(self) -> aiohttp.ClientSession:
|
|
224
|
+
"""确保会话已创建。"""
|
|
225
|
+
if self._session is None or self._session.closed:
|
|
226
|
+
self._session = aiohttp.ClientSession(
|
|
227
|
+
connector=self._connector,
|
|
228
|
+
timeout=self._timeout,
|
|
229
|
+
)
|
|
230
|
+
return self._session
|
|
231
|
+
|
|
155
232
|
@classmethod
|
|
156
233
|
def from_config(cls, config: HttpClientConfig) -> HttpClient:
|
|
157
234
|
"""从配置创建客户端。
|
|
@@ -181,9 +258,9 @@ class HttpClient:
|
|
|
181
258
|
|
|
182
259
|
async def _apply_interceptors(
|
|
183
260
|
self,
|
|
184
|
-
request:
|
|
185
|
-
response:
|
|
186
|
-
) -> tuple[
|
|
261
|
+
request: HttpRequest,
|
|
262
|
+
response: HttpResponse | None = None,
|
|
263
|
+
) -> tuple[HttpRequest, HttpResponse | None]:
|
|
187
264
|
"""应用拦截器。"""
|
|
188
265
|
# 请求前拦截
|
|
189
266
|
for interceptor in self._interceptors:
|
|
@@ -196,28 +273,6 @@ class HttpClient:
|
|
|
196
273
|
|
|
197
274
|
return request, response
|
|
198
275
|
|
|
199
|
-
async def _make_request(
|
|
200
|
-
self,
|
|
201
|
-
method: str,
|
|
202
|
-
url: str,
|
|
203
|
-
**kwargs: Any,
|
|
204
|
-
) -> httpx.Response:
|
|
205
|
-
"""执行HTTP请求(内部方法,使用tenacity重试)。"""
|
|
206
|
-
response = await self._client.request(method, url, **kwargs)
|
|
207
|
-
|
|
208
|
-
# 检查状态码是否需要重试
|
|
209
|
-
if response.status_code in self._retry_config.retry_on_status:
|
|
210
|
-
logger.warning(
|
|
211
|
-
f"请求失败,状态码: {response.status_code}, 将重试"
|
|
212
|
-
)
|
|
213
|
-
raise httpx.HTTPStatusError(
|
|
214
|
-
f"HTTP {response.status_code}",
|
|
215
|
-
request=response.request,
|
|
216
|
-
response=response,
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
return response
|
|
220
|
-
|
|
221
276
|
def _get_retry_decorator(self):
|
|
222
277
|
"""动态构建重试装饰器。"""
|
|
223
278
|
return retry(
|
|
@@ -227,7 +282,7 @@ class HttpClient:
|
|
|
227
282
|
min=self._retry_config.retry_delay,
|
|
228
283
|
max=self._retry_config.retry_delay * (self._retry_config.backoff_factor ** self._retry_config.max_retries),
|
|
229
284
|
),
|
|
230
|
-
retry=retry_if_exception_type(
|
|
285
|
+
retry=retry_if_exception_type((HttpStatusError, aiohttp.ClientError)),
|
|
231
286
|
reraise=True,
|
|
232
287
|
)
|
|
233
288
|
|
|
@@ -240,9 +295,8 @@ class HttpClient:
|
|
|
240
295
|
params: dict[str, Any] | None = None,
|
|
241
296
|
json: Any = None,
|
|
242
297
|
data: Any = None,
|
|
243
|
-
files: Any = None,
|
|
244
298
|
**kwargs: Any,
|
|
245
|
-
) ->
|
|
299
|
+
) -> HttpResponse:
|
|
246
300
|
"""发送HTTP请求。
|
|
247
301
|
|
|
248
302
|
Args:
|
|
@@ -252,48 +306,79 @@ class HttpClient:
|
|
|
252
306
|
params: 查询参数
|
|
253
307
|
json: JSON数据
|
|
254
308
|
data: 表单数据
|
|
255
|
-
|
|
256
|
-
**kwargs: 其他参数
|
|
309
|
+
**kwargs: 其他 aiohttp 参数
|
|
257
310
|
|
|
258
311
|
Returns:
|
|
259
|
-
|
|
312
|
+
HttpResponse: 响应对象
|
|
260
313
|
|
|
261
314
|
Raises:
|
|
262
|
-
|
|
315
|
+
HttpError: 请求失败
|
|
263
316
|
"""
|
|
264
317
|
# 构建完整URL
|
|
265
318
|
full_url = f"{self._base_url}{url}" if self._base_url else url
|
|
266
319
|
|
|
267
320
|
# 创建请求对象
|
|
268
|
-
request =
|
|
321
|
+
request = HttpRequest(
|
|
269
322
|
method=method,
|
|
270
323
|
url=full_url,
|
|
271
|
-
headers=headers,
|
|
324
|
+
headers=headers or {},
|
|
272
325
|
params=params,
|
|
273
|
-
|
|
326
|
+
json_data=json,
|
|
274
327
|
data=data,
|
|
275
|
-
files=files,
|
|
276
|
-
**kwargs,
|
|
277
328
|
)
|
|
278
329
|
|
|
279
330
|
# 应用拦截器(请求前)
|
|
280
331
|
request, _ = await self._apply_interceptors(request)
|
|
281
332
|
|
|
333
|
+
session = await self._ensure_session()
|
|
334
|
+
|
|
335
|
+
async def _execute_request() -> HttpResponse:
|
|
336
|
+
start_time = time.perf_counter()
|
|
337
|
+
try:
|
|
338
|
+
async with session.request(
|
|
339
|
+
method=request.method,
|
|
340
|
+
url=request.url,
|
|
341
|
+
headers=request.headers or None,
|
|
342
|
+
params=request.params,
|
|
343
|
+
json=request.json_data,
|
|
344
|
+
data=request.data,
|
|
345
|
+
allow_redirects=self._follow_redirects,
|
|
346
|
+
**kwargs,
|
|
347
|
+
) as resp:
|
|
348
|
+
elapsed = time.perf_counter() - start_time
|
|
349
|
+
content = await resp.read()
|
|
350
|
+
text = content.decode("utf-8", errors="replace")
|
|
351
|
+
|
|
352
|
+
response = HttpResponse(
|
|
353
|
+
status_code=resp.status,
|
|
354
|
+
url=str(resp.url),
|
|
355
|
+
headers=dict(resp.headers),
|
|
356
|
+
text=text,
|
|
357
|
+
content=content,
|
|
358
|
+
elapsed_seconds=elapsed,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
# 检查是否需要重试
|
|
362
|
+
if resp.status in self._retry_config.retry_on_status:
|
|
363
|
+
logger.warning(f"请求失败,状态码: {resp.status}, 将重试")
|
|
364
|
+
raise HttpStatusError(
|
|
365
|
+
f"HTTP {resp.status}",
|
|
366
|
+
status_code=resp.status,
|
|
367
|
+
response=response,
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
return response
|
|
371
|
+
|
|
372
|
+
except aiohttp.ClientError as exc:
|
|
373
|
+
elapsed = time.perf_counter() - start_time
|
|
374
|
+
if isinstance(exc, aiohttp.ServerTimeoutError):
|
|
375
|
+
raise HttpTimeoutError(f"请求超时: {request.url}") from exc
|
|
376
|
+
raise HttpNetworkError(f"网络错误: {exc}") from exc
|
|
377
|
+
|
|
282
378
|
try:
|
|
283
379
|
# 使用tenacity重试装饰器
|
|
284
380
|
retry_decorator = self._get_retry_decorator()
|
|
285
|
-
|
|
286
|
-
@retry_decorator
|
|
287
|
-
async def _execute_request():
|
|
288
|
-
return await self._make_request(
|
|
289
|
-
method=request.method,
|
|
290
|
-
url=str(request.url),
|
|
291
|
-
headers=request.headers,
|
|
292
|
-
content=request.content,
|
|
293
|
-
**kwargs,
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
response = await _execute_request()
|
|
381
|
+
response = await retry_decorator(_execute_request)()
|
|
297
382
|
|
|
298
383
|
# 应用拦截器(响应后)
|
|
299
384
|
_, response = await self._apply_interceptors(request, response)
|
|
@@ -303,44 +388,49 @@ class HttpClient:
|
|
|
303
388
|
|
|
304
389
|
return response
|
|
305
390
|
|
|
306
|
-
except
|
|
391
|
+
except HttpError:
|
|
392
|
+
raise
|
|
393
|
+
except Exception as exc:
|
|
307
394
|
logger.error(
|
|
308
395
|
f"HTTP请求失败: {method} {full_url} | "
|
|
309
396
|
f"错误: {type(exc).__name__}: {exc}"
|
|
310
397
|
)
|
|
311
|
-
raise
|
|
398
|
+
raise HttpNetworkError(f"请求失败: {exc}") from exc
|
|
312
399
|
|
|
313
|
-
async def get(self, url: str, **kwargs: Any) ->
|
|
400
|
+
async def get(self, url: str, **kwargs: Any) -> HttpResponse:
|
|
314
401
|
"""GET请求。"""
|
|
315
402
|
return await self.request("GET", url, **kwargs)
|
|
316
403
|
|
|
317
|
-
async def post(self, url: str, **kwargs: Any) ->
|
|
404
|
+
async def post(self, url: str, **kwargs: Any) -> HttpResponse:
|
|
318
405
|
"""POST请求。"""
|
|
319
406
|
return await self.request("POST", url, **kwargs)
|
|
320
407
|
|
|
321
|
-
async def put(self, url: str, **kwargs: Any) ->
|
|
408
|
+
async def put(self, url: str, **kwargs: Any) -> HttpResponse:
|
|
322
409
|
"""PUT请求。"""
|
|
323
410
|
return await self.request("PUT", url, **kwargs)
|
|
324
411
|
|
|
325
|
-
async def patch(self, url: str, **kwargs: Any) ->
|
|
412
|
+
async def patch(self, url: str, **kwargs: Any) -> HttpResponse:
|
|
326
413
|
"""PATCH请求。"""
|
|
327
414
|
return await self.request("PATCH", url, **kwargs)
|
|
328
415
|
|
|
329
|
-
async def delete(self, url: str, **kwargs: Any) ->
|
|
416
|
+
async def delete(self, url: str, **kwargs: Any) -> HttpResponse:
|
|
330
417
|
"""DELETE请求。"""
|
|
331
418
|
return await self.request("DELETE", url, **kwargs)
|
|
332
419
|
|
|
333
|
-
async def head(self, url: str, **kwargs: Any) ->
|
|
420
|
+
async def head(self, url: str, **kwargs: Any) -> HttpResponse:
|
|
334
421
|
"""HEAD请求。"""
|
|
335
422
|
return await self.request("HEAD", url, **kwargs)
|
|
336
423
|
|
|
337
|
-
async def options(self, url: str, **kwargs: Any) ->
|
|
424
|
+
async def options(self, url: str, **kwargs: Any) -> HttpResponse:
|
|
338
425
|
"""OPTIONS请求。"""
|
|
339
426
|
return await self.request("OPTIONS", url, **kwargs)
|
|
340
427
|
|
|
341
428
|
async def close(self) -> None:
|
|
342
429
|
"""关闭客户端。"""
|
|
343
|
-
|
|
430
|
+
if self._session and not self._session.closed:
|
|
431
|
+
await self._session.close()
|
|
432
|
+
if self._connector and not self._connector.closed:
|
|
433
|
+
await self._connector.close()
|
|
344
434
|
logger.debug("HTTP客户端已关闭")
|
|
345
435
|
|
|
346
436
|
async def __aenter__(self) -> HttpClient:
|
|
@@ -353,15 +443,20 @@ class HttpClient:
|
|
|
353
443
|
|
|
354
444
|
def __repr__(self) -> str:
|
|
355
445
|
"""字符串表示。"""
|
|
356
|
-
return f"<HttpClient base_url={self._base_url} timeout={self._timeout}>"
|
|
446
|
+
return f"<HttpClient base_url={self._base_url} timeout={self._timeout.total}>"
|
|
357
447
|
|
|
358
448
|
|
|
359
449
|
__all__ = [
|
|
360
450
|
"HttpClient",
|
|
361
451
|
"HttpClientConfig",
|
|
452
|
+
"HttpError",
|
|
453
|
+
"HttpNetworkError",
|
|
454
|
+
"HttpRequest",
|
|
455
|
+
"HttpResponse",
|
|
456
|
+
"HttpStatusError",
|
|
457
|
+
"HttpTimeoutError",
|
|
362
458
|
"LoggingInterceptor",
|
|
363
459
|
"RequestInterceptor",
|
|
364
460
|
"RetryConfig",
|
|
365
461
|
]
|
|
366
462
|
|
|
367
|
-
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aury-boot
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.41
|
|
4
4
|
Summary: Aury Boot - 基于 FastAPI 生态的企业级 API 开发框架
|
|
5
5
|
Requires-Python: >=3.13
|
|
6
|
+
Requires-Dist: aiohttp>=3.11.0
|
|
6
7
|
Requires-Dist: alembic>=1.17.2
|
|
7
8
|
Requires-Dist: aury-sdk-storage[aws]>=0.0.6
|
|
8
9
|
Requires-Dist: babel>=2.17.0
|
|
@@ -10,7 +11,6 @@ Requires-Dist: broadcaster[redis]>=0.3.1
|
|
|
10
11
|
Requires-Dist: faker>=38.2.0
|
|
11
12
|
Requires-Dist: fastapi>=0.122.0
|
|
12
13
|
Requires-Dist: greenlet>=3.2.4
|
|
13
|
-
Requires-Dist: httpx>=0.28.1
|
|
14
14
|
Requires-Dist: loguru>=0.7.3
|
|
15
15
|
Requires-Dist: pydantic-settings>=2.12.0
|
|
16
16
|
Requires-Dist: pydantic>=2.12.5
|
|
@@ -28,12 +28,18 @@ Requires-Dist: aiosqlite>=0.21.0; extra == 'all'
|
|
|
28
28
|
Requires-Dist: apscheduler>=3.11.1; extra == 'all'
|
|
29
29
|
Requires-Dist: asyncpg>=0.31.0; extra == 'all'
|
|
30
30
|
Requires-Dist: aury-sdk-storage[aws]>=0.0.1; extra == 'all'
|
|
31
|
+
Requires-Dist: coredis>=5.6.0; extra == 'all'
|
|
31
32
|
Requires-Dist: dramatiq>=1.18.0; extra == 'all'
|
|
32
33
|
Requires-Dist: pika>=1.3.2; extra == 'all'
|
|
34
|
+
Requires-Dist: psutil>=7.0.0; extra == 'all'
|
|
35
|
+
Requires-Dist: pyroscope-io>=0.8.7; extra == 'all'
|
|
33
36
|
Requires-Dist: redis>=7.1.0; extra == 'all'
|
|
34
37
|
Provides-Extra: broadcaster
|
|
35
38
|
Requires-Dist: broadcaster[redis]>=0.3.1; extra == 'broadcaster'
|
|
39
|
+
Provides-Extra: channel-cluster
|
|
40
|
+
Requires-Dist: coredis>=5.6.0; extra == 'channel-cluster'
|
|
36
41
|
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: httpx>=0.28.1; extra == 'dev'
|
|
37
43
|
Requires-Dist: mypy>=1.19.0; extra == 'dev'
|
|
38
44
|
Requires-Dist: pytest-asyncio>=1.3.0; extra == 'dev'
|
|
39
45
|
Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
|
|
@@ -47,14 +53,17 @@ Provides-Extra: mysql
|
|
|
47
53
|
Requires-Dist: aiomysql>=0.3.2; extra == 'mysql'
|
|
48
54
|
Provides-Extra: otel
|
|
49
55
|
Requires-Dist: opentelemetry-api>=1.25.0; extra == 'otel'
|
|
56
|
+
Requires-Dist: opentelemetry-instrumentation-aiohttp-client>=0.46b0; extra == 'otel'
|
|
50
57
|
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.46b0; extra == 'otel'
|
|
51
|
-
Requires-Dist: opentelemetry-instrumentation-httpx>=0.46b0; extra == 'otel'
|
|
52
58
|
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.46b0; extra == 'otel'
|
|
53
59
|
Requires-Dist: opentelemetry-sdk>=1.25.0; extra == 'otel'
|
|
54
60
|
Provides-Extra: otel-exporter
|
|
55
61
|
Requires-Dist: opentelemetry-exporter-otlp>=1.25.0; extra == 'otel-exporter'
|
|
56
62
|
Provides-Extra: postgres
|
|
57
63
|
Requires-Dist: asyncpg>=0.31.0; extra == 'postgres'
|
|
64
|
+
Provides-Extra: profiling
|
|
65
|
+
Requires-Dist: psutil>=7.0.0; extra == 'profiling'
|
|
66
|
+
Requires-Dist: pyroscope-io>=0.8.7; extra == 'profiling'
|
|
58
67
|
Provides-Extra: rabbitmq
|
|
59
68
|
Requires-Dist: amqp>=5.3.1; extra == 'rabbitmq'
|
|
60
69
|
Provides-Extra: recommended
|
|
@@ -62,10 +71,11 @@ Requires-Dist: aiosqlite>=0.21.0; extra == 'recommended'
|
|
|
62
71
|
Requires-Dist: apscheduler>=3.11.1; extra == 'recommended'
|
|
63
72
|
Requires-Dist: asyncpg>=0.31.0; extra == 'recommended'
|
|
64
73
|
Requires-Dist: aury-sdk-storage[aws]>=0.0.1; extra == 'recommended'
|
|
74
|
+
Requires-Dist: coredis>=5.6.0; extra == 'recommended'
|
|
65
75
|
Requires-Dist: dramatiq>=1.18.0; extra == 'recommended'
|
|
66
76
|
Requires-Dist: opentelemetry-api>=1.25.0; extra == 'recommended'
|
|
77
|
+
Requires-Dist: opentelemetry-instrumentation-aiohttp-client>=0.46b0; extra == 'recommended'
|
|
67
78
|
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.46b0; extra == 'recommended'
|
|
68
|
-
Requires-Dist: opentelemetry-instrumentation-httpx>=0.46b0; extra == 'recommended'
|
|
69
79
|
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.46b0; extra == 'recommended'
|
|
70
80
|
Requires-Dist: opentelemetry-sdk>=1.25.0; extra == 'recommended'
|
|
71
81
|
Requires-Dist: redis>=7.1.0; extra == 'recommended'
|