huace-aigc-auth-client 1.1.31__py3-none-any.whl → 1.1.32__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.
- huace_aigc_auth_client/__init__.py +3 -1
- huace_aigc_auth_client/auth_request.py +366 -1
- {huace_aigc_auth_client-1.1.31.dist-info → huace_aigc_auth_client-1.1.32.dist-info}/METADATA +1 -1
- {huace_aigc_auth_client-1.1.31.dist-info → huace_aigc_auth_client-1.1.32.dist-info}/RECORD +7 -7
- {huace_aigc_auth_client-1.1.31.dist-info → huace_aigc_auth_client-1.1.32.dist-info}/WHEEL +0 -0
- {huace_aigc_auth_client-1.1.31.dist-info → huace_aigc_auth_client-1.1.32.dist-info}/licenses/LICENSE +0 -0
- {huace_aigc_auth_client-1.1.31.dist-info → huace_aigc_auth_client-1.1.32.dist-info}/top_level.txt +0 -0
|
@@ -67,6 +67,7 @@ from .api_stats_collector import (
|
|
|
67
67
|
|
|
68
68
|
from .auth_request import (
|
|
69
69
|
auth_request,
|
|
70
|
+
async_auth_httpx_request
|
|
70
71
|
)
|
|
71
72
|
|
|
72
73
|
from .legacy_adapter import (
|
|
@@ -214,5 +215,6 @@ __all__ = [
|
|
|
214
215
|
"stop_api_stats_collector",
|
|
215
216
|
# 认证请求封装
|
|
216
217
|
"auth_request",
|
|
218
|
+
"async_auth_httpx_request",
|
|
217
219
|
]
|
|
218
|
-
__version__ = "1.1.
|
|
220
|
+
__version__ = "1.1.32"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
认证请求封装模块
|
|
3
|
-
提供对 requests.request 的封装,自动添加认证信息和统计上报
|
|
3
|
+
提供对 requests.request 和 httpx 的封装,自动添加认证信息和统计上报
|
|
4
4
|
"""
|
|
5
5
|
import time
|
|
6
6
|
import logging
|
|
@@ -8,6 +8,13 @@ from typing import Optional, Dict, Any, Union
|
|
|
8
8
|
import requests
|
|
9
9
|
from requests import Response
|
|
10
10
|
|
|
11
|
+
try:
|
|
12
|
+
import httpx
|
|
13
|
+
HTTPX_AVAILABLE = True
|
|
14
|
+
except ImportError:
|
|
15
|
+
HTTPX_AVAILABLE = False
|
|
16
|
+
httpx = None
|
|
17
|
+
|
|
11
18
|
from .user_context import get_request_context
|
|
12
19
|
from .api_stats_collector import get_api_stats_collector
|
|
13
20
|
|
|
@@ -240,6 +247,324 @@ def _report_stats(
|
|
|
240
247
|
logger.debug(f"统计上报失败: {e}")
|
|
241
248
|
|
|
242
249
|
|
|
250
|
+
# ============ HTTPX 异步请求封装 ============
|
|
251
|
+
|
|
252
|
+
if HTTPX_AVAILABLE:
|
|
253
|
+
|
|
254
|
+
async def async_auth_httpx_request(
|
|
255
|
+
method: str,
|
|
256
|
+
url: str,
|
|
257
|
+
params: Optional[Dict[str, Any]] = None,
|
|
258
|
+
data: Optional[Union[Dict[str, Any], str, bytes]] = None,
|
|
259
|
+
json: Optional[Dict[str, Any]] = None,
|
|
260
|
+
headers: Optional[Dict[str, str]] = None,
|
|
261
|
+
cookies: Optional[Dict[str, str]] = None,
|
|
262
|
+
files: Optional[Dict[str, Any]] = None,
|
|
263
|
+
auth: Optional[tuple] = None,
|
|
264
|
+
timeout: Optional[Union[float, httpx.Timeout]] = None,
|
|
265
|
+
follow_redirects: bool = True,
|
|
266
|
+
**kwargs
|
|
267
|
+
) -> httpx.Response:
|
|
268
|
+
"""
|
|
269
|
+
异步认证请求封装函数(httpx)
|
|
270
|
+
|
|
271
|
+
基本参数与 httpx.request 一致,会自动从 request_context 中获取认证信息并添加到请求头
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
method: HTTP 方法 (GET, POST, PUT, DELETE 等)
|
|
275
|
+
url: 请求 URL
|
|
276
|
+
params: URL 查询参数
|
|
277
|
+
data: 请求体数据 (form-data 或 raw)
|
|
278
|
+
json: JSON 请求体数据
|
|
279
|
+
headers: 请求头(会自动添加认证相关的头)
|
|
280
|
+
cookies: Cookies
|
|
281
|
+
files: 文件上传
|
|
282
|
+
auth: HTTP 认证元组
|
|
283
|
+
timeout: 超时时间(秒)
|
|
284
|
+
follow_redirects: 是否允许重定向
|
|
285
|
+
**kwargs: 其他 httpx.request 支持的参数
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
httpx.Response: 响应对象
|
|
289
|
+
|
|
290
|
+
自动添加的请求头(如果在 request_context 中存在):
|
|
291
|
+
- X-App-ID: 应用ID
|
|
292
|
+
- X-App-Secret: 应用密钥
|
|
293
|
+
- Authorization: Bearer token
|
|
294
|
+
- x-real-ip: 客户端真实IP
|
|
295
|
+
- user-agent: User Agent
|
|
296
|
+
- X-Trace-ID: 追踪ID
|
|
297
|
+
"""
|
|
298
|
+
# 初始化 headers
|
|
299
|
+
if headers is None:
|
|
300
|
+
headers = {}
|
|
301
|
+
else:
|
|
302
|
+
headers = headers.copy() # 避免修改原始 headers
|
|
303
|
+
|
|
304
|
+
# 从 request_context 获取认证信息
|
|
305
|
+
request_context = get_request_context()
|
|
306
|
+
|
|
307
|
+
if request_context:
|
|
308
|
+
# 添加应用ID
|
|
309
|
+
app_id = request_context.get('app_id')
|
|
310
|
+
if app_id is not None:
|
|
311
|
+
headers['X-App-ID'] = str(app_id)
|
|
312
|
+
|
|
313
|
+
# 添加应用密钥
|
|
314
|
+
app_secret = request_context.get('app_secret')
|
|
315
|
+
if app_secret:
|
|
316
|
+
headers['X-App-Secret'] = app_secret
|
|
317
|
+
|
|
318
|
+
# 添加认证令牌
|
|
319
|
+
token = request_context.get('token')
|
|
320
|
+
if token:
|
|
321
|
+
headers['Authorization'] = f'Bearer {token}'
|
|
322
|
+
|
|
323
|
+
# 添加客户端IP
|
|
324
|
+
ip_address = request_context.get('ip_address')
|
|
325
|
+
if ip_address:
|
|
326
|
+
headers['x-real-ip'] = ip_address
|
|
327
|
+
|
|
328
|
+
# 添加 User Agent
|
|
329
|
+
user_agent = request_context.get('user_agent')
|
|
330
|
+
if user_agent:
|
|
331
|
+
headers['user-agent'] = user_agent
|
|
332
|
+
|
|
333
|
+
# 添加追踪ID
|
|
334
|
+
trace_id = request_context.get('trace_id')
|
|
335
|
+
if trace_id:
|
|
336
|
+
headers['X-Trace-ID'] = trace_id
|
|
337
|
+
|
|
338
|
+
# 记录开始时间
|
|
339
|
+
start_time = time.time()
|
|
340
|
+
response = None
|
|
341
|
+
error_message = None
|
|
342
|
+
|
|
343
|
+
try:
|
|
344
|
+
# 执行异步请求
|
|
345
|
+
logger.info(f"发起异步认证请求: {method} {url} with headers={headers} params={params} json={json} data={data}")
|
|
346
|
+
|
|
347
|
+
async with httpx.AsyncClient(timeout=timeout) as client:
|
|
348
|
+
response = await client.request(
|
|
349
|
+
method=method,
|
|
350
|
+
url=url,
|
|
351
|
+
params=params,
|
|
352
|
+
data=data,
|
|
353
|
+
json=json,
|
|
354
|
+
headers=headers,
|
|
355
|
+
cookies=cookies,
|
|
356
|
+
files=files,
|
|
357
|
+
auth=auth,
|
|
358
|
+
follow_redirects=follow_redirects,
|
|
359
|
+
**kwargs
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
logger.info(f"异步认证请求响应: {method} {url} 状态码: {response.status_code}")
|
|
363
|
+
return response
|
|
364
|
+
|
|
365
|
+
except Exception as e:
|
|
366
|
+
error_message = str(e)
|
|
367
|
+
logger.error(f"异步认证请求失败: {method} {url} 错误: {error_message}")
|
|
368
|
+
raise
|
|
369
|
+
|
|
370
|
+
finally:
|
|
371
|
+
# 计算响应时间
|
|
372
|
+
response_time = time.time() - start_time
|
|
373
|
+
|
|
374
|
+
# 上报统计信息
|
|
375
|
+
_report_stats(
|
|
376
|
+
url=url,
|
|
377
|
+
method=method,
|
|
378
|
+
response=response,
|
|
379
|
+
response_time=response_time,
|
|
380
|
+
error_message=error_message,
|
|
381
|
+
request_context=request_context,
|
|
382
|
+
headers=headers,
|
|
383
|
+
params=params,
|
|
384
|
+
json_data=json,
|
|
385
|
+
form_data=data
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class AsyncAuthClient:
|
|
390
|
+
"""
|
|
391
|
+
异步认证客户端(httpx.AsyncClient 包装)
|
|
392
|
+
|
|
393
|
+
提供可复用的异步 HTTP 客户端,自动处理认证信息和统计上报
|
|
394
|
+
"""
|
|
395
|
+
|
|
396
|
+
def __init__(
|
|
397
|
+
self,
|
|
398
|
+
base_url: Optional[str] = None,
|
|
399
|
+
timeout: Optional[Union[float, httpx.Timeout]] = None,
|
|
400
|
+
follow_redirects: bool = True,
|
|
401
|
+
**kwargs
|
|
402
|
+
):
|
|
403
|
+
"""
|
|
404
|
+
初始化异步认证客户端
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
base_url: 基础 URL
|
|
408
|
+
timeout: 超时配置
|
|
409
|
+
follow_redirects: 是否允许重定向
|
|
410
|
+
**kwargs: 其他 httpx.AsyncClient 支持的参数
|
|
411
|
+
"""
|
|
412
|
+
self.base_url = base_url
|
|
413
|
+
self.timeout = timeout
|
|
414
|
+
self.follow_redirects = follow_redirects
|
|
415
|
+
self.client_kwargs = kwargs
|
|
416
|
+
self._client: Optional[httpx.AsyncClient] = None
|
|
417
|
+
|
|
418
|
+
async def __aenter__(self):
|
|
419
|
+
"""进入异步上下文"""
|
|
420
|
+
self._client = httpx.AsyncClient(
|
|
421
|
+
base_url=self.base_url,
|
|
422
|
+
timeout=self.timeout,
|
|
423
|
+
follow_redirects=self.follow_redirects,
|
|
424
|
+
**self.client_kwargs
|
|
425
|
+
)
|
|
426
|
+
return self
|
|
427
|
+
|
|
428
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
429
|
+
"""退出异步上下文"""
|
|
430
|
+
if self._client:
|
|
431
|
+
await self._client.aclose()
|
|
432
|
+
self._client = None
|
|
433
|
+
|
|
434
|
+
def _prepare_headers(self, headers: Optional[Dict[str, str]] = None) -> Dict[str, str]:
|
|
435
|
+
"""准备请求头(添加认证信息)"""
|
|
436
|
+
# 初始化 headers
|
|
437
|
+
if headers is None:
|
|
438
|
+
headers = {}
|
|
439
|
+
else:
|
|
440
|
+
headers = headers.copy()
|
|
441
|
+
|
|
442
|
+
# 从 request_context 获取认证信息
|
|
443
|
+
request_context = get_request_context()
|
|
444
|
+
|
|
445
|
+
if request_context:
|
|
446
|
+
# 添加应用ID
|
|
447
|
+
app_id = request_context.get('app_id')
|
|
448
|
+
if app_id is not None:
|
|
449
|
+
headers['X-App-ID'] = str(app_id)
|
|
450
|
+
|
|
451
|
+
# 添加应用密钥
|
|
452
|
+
app_secret = request_context.get('app_secret')
|
|
453
|
+
if app_secret:
|
|
454
|
+
headers['X-App-Secret'] = app_secret
|
|
455
|
+
|
|
456
|
+
# 添加认证令牌
|
|
457
|
+
token = request_context.get('token')
|
|
458
|
+
if token:
|
|
459
|
+
headers['Authorization'] = f'Bearer {token}'
|
|
460
|
+
|
|
461
|
+
# 添加客户端IP
|
|
462
|
+
ip_address = request_context.get('ip_address')
|
|
463
|
+
if ip_address:
|
|
464
|
+
headers['x-real-ip'] = ip_address
|
|
465
|
+
|
|
466
|
+
# 添加 User Agent
|
|
467
|
+
user_agent = request_context.get('user_agent')
|
|
468
|
+
if user_agent:
|
|
469
|
+
headers['user-agent'] = user_agent
|
|
470
|
+
|
|
471
|
+
# 添加追踪ID
|
|
472
|
+
trace_id = request_context.get('trace_id')
|
|
473
|
+
if trace_id:
|
|
474
|
+
headers['X-Trace-ID'] = trace_id
|
|
475
|
+
|
|
476
|
+
return headers
|
|
477
|
+
|
|
478
|
+
async def _request(
|
|
479
|
+
self,
|
|
480
|
+
method: str,
|
|
481
|
+
url: str,
|
|
482
|
+
**kwargs
|
|
483
|
+
) -> httpx.Response:
|
|
484
|
+
"""执行请求(内部方法)"""
|
|
485
|
+
if self._client is None:
|
|
486
|
+
raise RuntimeError("Client not initialized. Use 'async with AsyncAuthClient()' context manager.")
|
|
487
|
+
|
|
488
|
+
# 准备 headers
|
|
489
|
+
headers = kwargs.pop('headers', None)
|
|
490
|
+
headers = self._prepare_headers(headers)
|
|
491
|
+
|
|
492
|
+
# 提取参数用于统计
|
|
493
|
+
params = kwargs.get('params')
|
|
494
|
+
json_data = kwargs.get('json')
|
|
495
|
+
form_data = kwargs.get('data')
|
|
496
|
+
|
|
497
|
+
# 记录开始时间
|
|
498
|
+
start_time = time.time()
|
|
499
|
+
response = None
|
|
500
|
+
error_message = None
|
|
501
|
+
request_context = get_request_context()
|
|
502
|
+
|
|
503
|
+
try:
|
|
504
|
+
# 执行请求
|
|
505
|
+
logger.info(f"AsyncAuthClient 发起请求: {method} {url} with headers={headers}")
|
|
506
|
+
response = await self._client.request(
|
|
507
|
+
method=method,
|
|
508
|
+
url=url,
|
|
509
|
+
headers=headers,
|
|
510
|
+
**kwargs
|
|
511
|
+
)
|
|
512
|
+
logger.info(f"AsyncAuthClient 请求响应: {method} {url} 状态码: {response.status_code}")
|
|
513
|
+
return response
|
|
514
|
+
|
|
515
|
+
except Exception as e:
|
|
516
|
+
error_message = str(e)
|
|
517
|
+
logger.error(f"AsyncAuthClient 请求失败: {method} {url} 错误: {error_message}")
|
|
518
|
+
raise
|
|
519
|
+
|
|
520
|
+
finally:
|
|
521
|
+
# 计算响应时间
|
|
522
|
+
response_time = time.time() - start_time
|
|
523
|
+
|
|
524
|
+
# 上报统计信息
|
|
525
|
+
full_url = str(self._client.build_request(method, url).url) if self._client else url
|
|
526
|
+
_report_stats(
|
|
527
|
+
url=full_url,
|
|
528
|
+
method=method,
|
|
529
|
+
response=response,
|
|
530
|
+
response_time=response_time,
|
|
531
|
+
error_message=error_message,
|
|
532
|
+
request_context=request_context,
|
|
533
|
+
headers=headers,
|
|
534
|
+
params=params,
|
|
535
|
+
json_data=json_data,
|
|
536
|
+
form_data=form_data
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
async def get(self, url: str, **kwargs) -> httpx.Response:
|
|
540
|
+
"""GET 请求"""
|
|
541
|
+
return await self._request('GET', url, **kwargs)
|
|
542
|
+
|
|
543
|
+
async def post(self, url: str, **kwargs) -> httpx.Response:
|
|
544
|
+
"""POST 请求"""
|
|
545
|
+
return await self._request('POST', url, **kwargs)
|
|
546
|
+
|
|
547
|
+
async def put(self, url: str, **kwargs) -> httpx.Response:
|
|
548
|
+
"""PUT 请求"""
|
|
549
|
+
return await self._request('PUT', url, **kwargs)
|
|
550
|
+
|
|
551
|
+
async def delete(self, url: str, **kwargs) -> httpx.Response:
|
|
552
|
+
"""DELETE 请求"""
|
|
553
|
+
return await self._request('DELETE', url, **kwargs)
|
|
554
|
+
|
|
555
|
+
async def patch(self, url: str, **kwargs) -> httpx.Response:
|
|
556
|
+
"""PATCH 请求"""
|
|
557
|
+
return await self._request('PATCH', url, **kwargs)
|
|
558
|
+
|
|
559
|
+
async def head(self, url: str, **kwargs) -> httpx.Response:
|
|
560
|
+
"""HEAD 请求"""
|
|
561
|
+
return await self._request('HEAD', url, **kwargs)
|
|
562
|
+
|
|
563
|
+
async def options(self, url: str, **kwargs) -> httpx.Response:
|
|
564
|
+
"""OPTIONS 请求"""
|
|
565
|
+
return await self._request('OPTIONS', url, **kwargs)
|
|
566
|
+
|
|
567
|
+
|
|
243
568
|
# ============ 使用示例 ============
|
|
244
569
|
"""
|
|
245
570
|
使用示例:
|
|
@@ -321,4 +646,44 @@ def _report_stats(
|
|
|
321
646
|
|
|
322
647
|
# 发起请求(会自动上报统计)
|
|
323
648
|
response = auth_request('GET', 'https://api.example.com/data')
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
5. 使用 httpx 异步请求(需要安装 httpx):
|
|
652
|
+
|
|
653
|
+
from huace_aigc_auth_client.auth_request import async_auth_request, AsyncAuthClient
|
|
654
|
+
from huace_aigc_auth_client.user_context import set_request_context
|
|
655
|
+
import asyncio
|
|
656
|
+
|
|
657
|
+
# 设置请求上下文
|
|
658
|
+
set_request_context(
|
|
659
|
+
app_id='your-app-id',
|
|
660
|
+
app_secret='your-app-secret',
|
|
661
|
+
token='user-access-token'
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
# 方式1:使用 async_auth_request 函数
|
|
665
|
+
async def example1():
|
|
666
|
+
response = await async_auth_request('GET', 'https://api.example.com/data')
|
|
667
|
+
print(response.json())
|
|
668
|
+
|
|
669
|
+
# 方式2:使用 AsyncAuthClient(推荐用于多个请求)
|
|
670
|
+
async def example2():
|
|
671
|
+
async with AsyncAuthClient(timeout=10.0) as client:
|
|
672
|
+
# POST 请求带 JSON
|
|
673
|
+
response = await client.post(
|
|
674
|
+
'https://api.example.com/users',
|
|
675
|
+
json={'name': 'John', 'email': 'john@example.com'}
|
|
676
|
+
)
|
|
677
|
+
print(response.json())
|
|
678
|
+
|
|
679
|
+
# GET 请求带参数
|
|
680
|
+
response = await client.get(
|
|
681
|
+
'https://api.example.com/users',
|
|
682
|
+
params={'page': 1, 'size': 10}
|
|
683
|
+
)
|
|
684
|
+
print(response.json())
|
|
685
|
+
|
|
686
|
+
# 运行
|
|
687
|
+
asyncio.run(example1())
|
|
688
|
+
asyncio.run(example2())
|
|
324
689
|
"""
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
huace_aigc_auth_client/__init__.py,sha256=
|
|
1
|
+
huace_aigc_auth_client/__init__.py,sha256=t5GBEVaTo-vcjRWWbI-SG51-fR1AFzZK9Cuz3wQC5a8,6020
|
|
2
2
|
huace_aigc_auth_client/api_stats_collector.py,sha256=ADpjpHXMqn80YI4UltWHbzAO_szykU9ZCvwXgBRWFIM,11046
|
|
3
|
-
huace_aigc_auth_client/auth_request.py,sha256=
|
|
3
|
+
huace_aigc_auth_client/auth_request.py,sha256=lNIVytSpmbr2oYHEMuXX032cLo323IPhr7eK_KaCqVs,22564
|
|
4
4
|
huace_aigc_auth_client/legacy_adapter.py,sha256=TVCBAKejE2z2HQFsEwDW8LMiaIkXNfz3Mxv6_E-UJFY,24102
|
|
5
5
|
huace_aigc_auth_client/sdk.py,sha256=rproo913OAi37wz_rMYgxzP3F1YyY3nc5e35JS5WvoY,37751
|
|
6
6
|
huace_aigc_auth_client/user_context.py,sha256=IqdX6Xd2jJwvij6Hc2qWAFWj5pn3wHqk0RBsaXKLP8g,6795
|
|
7
7
|
huace_aigc_auth_client/webhook.py,sha256=XQZYEbMoqIdqZWCGSTcedeDKJpDbUVSq5g08g-6Qucg,4124
|
|
8
8
|
huace_aigc_auth_client/webhook_flask.py,sha256=Iosu4dBtRhQZM_ytn-bn82MpVsyOiV28FBnt7Tfh31U,7225
|
|
9
|
-
huace_aigc_auth_client-1.1.
|
|
10
|
-
huace_aigc_auth_client-1.1.
|
|
11
|
-
huace_aigc_auth_client-1.1.
|
|
12
|
-
huace_aigc_auth_client-1.1.
|
|
13
|
-
huace_aigc_auth_client-1.1.
|
|
9
|
+
huace_aigc_auth_client-1.1.32.dist-info/licenses/LICENSE,sha256=z7dgC7KljhBLNvKjN15391nMj3aLt0gbud8-Yf1F8EQ,1063
|
|
10
|
+
huace_aigc_auth_client-1.1.32.dist-info/METADATA,sha256=YdHV3D86QX4XloiBoP3ydX5Wmph2Wuscwx6mPUTUUBQ,23629
|
|
11
|
+
huace_aigc_auth_client-1.1.32.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
+
huace_aigc_auth_client-1.1.32.dist-info/top_level.txt,sha256=kbv0nQ6PQ0JVneWPH7O2AbtlJnP7AjvFJ6JjM6ZEBxo,23
|
|
13
|
+
huace_aigc_auth_client-1.1.32.dist-info/RECORD,,
|
|
File without changes
|
{huace_aigc_auth_client-1.1.31.dist-info → huace_aigc_auth_client-1.1.32.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{huace_aigc_auth_client-1.1.31.dist-info → huace_aigc_auth_client-1.1.32.dist-info}/top_level.txt
RENAMED
|
File without changes
|