uapi-sdk-python 0.1.13__tar.gz → 0.1.14__tar.gz
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.
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/PKG-INFO +65 -24
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/README.md +64 -23
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/pyproject.toml +1 -1
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/uapi/client.py +8 -2
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/uapi/errors.py +39 -0
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/uapi_sdk_python.egg-info/PKG-INFO +65 -24
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/setup.cfg +0 -0
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/tests/test_client.py +0 -0
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/uapi/__init__.py +0 -0
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/uapi_sdk_python.egg-info/SOURCES.txt +0 -0
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/uapi_sdk_python.egg-info/dependency_links.txt +0 -0
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/uapi_sdk_python.egg-info/requires.txt +0 -0
- {uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/uapi_sdk_python.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: uapi-sdk-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.14
|
|
4
4
|
Summary: Idiomatic UAPI SDK for Python
|
|
5
5
|
Author-email: UAPI <dev@uapis.cn>
|
|
6
6
|
Requires-Python: >=3.9
|
|
@@ -32,11 +32,13 @@ pip install uapi-sdk-python
|
|
|
32
32
|
```python
|
|
33
33
|
from uapi import UapiClient
|
|
34
34
|
|
|
35
|
-
client = UapiClient("https://uapis.cn
|
|
36
|
-
result = client.
|
|
35
|
+
client = UapiClient("https://uapis.cn", "YOUR_API_KEY")
|
|
36
|
+
result = client.misc.get_misc_hotboard(type="weibo")
|
|
37
37
|
print(result)
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
这个接口默认只要传 `type` 就可以拿当前热榜。`time`、`keyword`、`time_start`、`time_end`、`limit`、`sources` 都是按场景再传的可选参数。
|
|
41
|
+
|
|
40
42
|
> [!TIP]
|
|
41
43
|
> 请使用与运行脚本相同的 Python 解释器安装依赖,例如执行 `python -m pip install uapi-sdk-python` 后再运行 `python main.py`。在 VS Code / Pyright 中若提示 “Import uapi could not be resolved”,将解释器切换到当前虚拟环境即可恢复补全。
|
|
42
44
|
|
|
@@ -54,6 +56,60 @@ print(result)
|
|
|
54
56
|
|
|
55
57
|
如果你需要查看字段细节或内部逻辑,仓库中的 `./internal` 目录同步保留了由 `openapi-generator` 生成的完整结构体,随时可供参考。
|
|
56
58
|
|
|
59
|
+
## 响应元信息
|
|
60
|
+
|
|
61
|
+
每次请求完成后,SDK 会自动把响应 Header 解析成结构化的 `ResponseMeta`,你不用自己拆原始字符串。
|
|
62
|
+
|
|
63
|
+
成功时可以通过 `client.last_response_meta` 读取,失败时可以通过 `err.meta` 读取,两条路径拿到的是同一套字段。
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from uapi import UapiClient, UapiError
|
|
67
|
+
|
|
68
|
+
client = UapiClient("https://uapis.cn", "YOUR_API_KEY")
|
|
69
|
+
|
|
70
|
+
# 成功路径
|
|
71
|
+
client.social.get_social_qq_userinfo(qq="10001")
|
|
72
|
+
meta = client.last_response_meta
|
|
73
|
+
if meta:
|
|
74
|
+
print("这次请求原价:", meta.credits_requested or 0, "积分")
|
|
75
|
+
print("这次实际扣费:", meta.credits_charged or 0, "积分")
|
|
76
|
+
print("特殊计价:", meta.credits_pricing or "原价")
|
|
77
|
+
print("余额剩余:", meta.balance_remaining_cents or 0, "分")
|
|
78
|
+
print("资源包剩余:", meta.quota_remaining_credits or 0, "积分")
|
|
79
|
+
print("当前有效额度桶:", meta.active_quota_buckets or 0)
|
|
80
|
+
print("额度用空即停:", meta.stop_on_empty)
|
|
81
|
+
print("Key QPS:", meta.billing_key_rate_remaining or 0, "/", meta.billing_key_rate_limit or 0, meta.billing_key_rate_unit or "req")
|
|
82
|
+
print("Request ID:", meta.request_id)
|
|
83
|
+
|
|
84
|
+
# 失败路径
|
|
85
|
+
try:
|
|
86
|
+
client.social.get_social_qq_userinfo(qq="10001")
|
|
87
|
+
except UapiError as err:
|
|
88
|
+
if err.meta:
|
|
89
|
+
print("Retry-After 秒数:", err.meta.retry_after_seconds)
|
|
90
|
+
print("Retry-After 原始值:", err.meta.retry_after_raw)
|
|
91
|
+
print("访客 QPS:", err.meta.visitor_rate_remaining or 0, "/", err.meta.visitor_rate_limit or 0)
|
|
92
|
+
print("Request ID:", err.meta.request_id)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
常用字段一览:
|
|
96
|
+
|
|
97
|
+
| 字段 | 说明 |
|
|
98
|
+
|------|------|
|
|
99
|
+
| `credits_requested` | 这次请求原本要扣多少积分,也就是请求价 |
|
|
100
|
+
| `credits_charged` | 这次请求实际扣了多少积分 |
|
|
101
|
+
| `credits_pricing` | 特殊计价原因,例如缓存半价 `cache-hit-half-price` |
|
|
102
|
+
| `balance_remaining_cents` | 账户余额剩余(分) |
|
|
103
|
+
| `quota_remaining_credits` | 资源包剩余积分 |
|
|
104
|
+
| `active_quota_buckets` | 当前还有多少个有效额度桶参与计费 |
|
|
105
|
+
| `stop_on_empty` | 额度耗尽后是否直接停止服务 |
|
|
106
|
+
| `retry_after_seconds` / `retry_after_raw` | 限流后的等待时长;当服务端返回 HTTP 时间字符串时看 `retry_after_raw` |
|
|
107
|
+
| `request_id` | 请求唯一 ID,排障时使用 |
|
|
108
|
+
| `billing_key_rate_limit` / `billing_key_rate_remaining` | Billing Key 当前 QPS 规则的上限与剩余 |
|
|
109
|
+
| `billing_ip_rate_limit` / `billing_ip_rate_remaining` | Billing Key 单 IP 当前 QPS 规则的上限与剩余 |
|
|
110
|
+
| `visitor_rate_limit` / `visitor_rate_remaining` | 访客当前 QPS 规则的上限与剩余 |
|
|
111
|
+
| `rate_limit_policies` / `rate_limits` | 完整结构化限流策略数据 |
|
|
112
|
+
|
|
57
113
|
## 进阶实践
|
|
58
114
|
|
|
59
115
|
### 缓存与幂等
|
|
@@ -62,7 +118,7 @@ print(result)
|
|
|
62
118
|
from functools import lru_cache
|
|
63
119
|
from uapi import UapiClient
|
|
64
120
|
|
|
65
|
-
client = UapiClient("https://uapis.cn
|
|
121
|
+
client = UapiClient("https://uapis.cn", token="YOUR_API_KEY")
|
|
66
122
|
|
|
67
123
|
@lru_cache(maxsize=128)
|
|
68
124
|
def cached_lookup(qq: str):
|
|
@@ -73,34 +129,19 @@ user = cached_lookup("10001")
|
|
|
73
129
|
|
|
74
130
|
也可以在 FastAPI / Django 项目里配合 Redis,将 SDK 的响应序列化后写入缓存,命中即直接返回。
|
|
75
131
|
|
|
76
|
-
###
|
|
132
|
+
### 调整超时与环境
|
|
77
133
|
|
|
78
134
|
```python
|
|
79
|
-
import httpx
|
|
80
|
-
from httpx import Auth
|
|
81
135
|
from uapi import UapiClient
|
|
82
136
|
|
|
83
|
-
class StaticToken(Auth):
|
|
84
|
-
def __init__(self, token: str):
|
|
85
|
-
self.token = token
|
|
86
|
-
def auth_flow(self, request):
|
|
87
|
-
request.headers["Authorization"] = f"Bearer {self.token}"
|
|
88
|
-
yield request
|
|
89
|
-
|
|
90
|
-
http_client = httpx.Client(
|
|
91
|
-
timeout=5,
|
|
92
|
-
transport=httpx.HTTPTransport(retries=3),
|
|
93
|
-
event_hooks={"request": [lambda request: print("->", request.url)]},
|
|
94
|
-
)
|
|
95
|
-
|
|
96
137
|
client = UapiClient(
|
|
97
|
-
"https://uapis.cn
|
|
98
|
-
|
|
99
|
-
|
|
138
|
+
"https://uapis.cn",
|
|
139
|
+
token="YOUR_API_KEY",
|
|
140
|
+
timeout=5.0,
|
|
100
141
|
)
|
|
101
142
|
```
|
|
102
143
|
|
|
103
|
-
|
|
144
|
+
如果你需要切换到别的环境,直接改 `base_url` 就可以;如果你只想缩短等待时间,传 `timeout` 就够了。
|
|
104
145
|
|
|
105
146
|
## 错误模型概览
|
|
106
147
|
|
|
@@ -18,11 +18,13 @@ pip install uapi-sdk-python
|
|
|
18
18
|
```python
|
|
19
19
|
from uapi import UapiClient
|
|
20
20
|
|
|
21
|
-
client = UapiClient("https://uapis.cn
|
|
22
|
-
result = client.
|
|
21
|
+
client = UapiClient("https://uapis.cn", "YOUR_API_KEY")
|
|
22
|
+
result = client.misc.get_misc_hotboard(type="weibo")
|
|
23
23
|
print(result)
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
这个接口默认只要传 `type` 就可以拿当前热榜。`time`、`keyword`、`time_start`、`time_end`、`limit`、`sources` 都是按场景再传的可选参数。
|
|
27
|
+
|
|
26
28
|
> [!TIP]
|
|
27
29
|
> 请使用与运行脚本相同的 Python 解释器安装依赖,例如执行 `python -m pip install uapi-sdk-python` 后再运行 `python main.py`。在 VS Code / Pyright 中若提示 “Import uapi could not be resolved”,将解释器切换到当前虚拟环境即可恢复补全。
|
|
28
30
|
|
|
@@ -40,6 +42,60 @@ print(result)
|
|
|
40
42
|
|
|
41
43
|
如果你需要查看字段细节或内部逻辑,仓库中的 `./internal` 目录同步保留了由 `openapi-generator` 生成的完整结构体,随时可供参考。
|
|
42
44
|
|
|
45
|
+
## 响应元信息
|
|
46
|
+
|
|
47
|
+
每次请求完成后,SDK 会自动把响应 Header 解析成结构化的 `ResponseMeta`,你不用自己拆原始字符串。
|
|
48
|
+
|
|
49
|
+
成功时可以通过 `client.last_response_meta` 读取,失败时可以通过 `err.meta` 读取,两条路径拿到的是同一套字段。
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from uapi import UapiClient, UapiError
|
|
53
|
+
|
|
54
|
+
client = UapiClient("https://uapis.cn", "YOUR_API_KEY")
|
|
55
|
+
|
|
56
|
+
# 成功路径
|
|
57
|
+
client.social.get_social_qq_userinfo(qq="10001")
|
|
58
|
+
meta = client.last_response_meta
|
|
59
|
+
if meta:
|
|
60
|
+
print("这次请求原价:", meta.credits_requested or 0, "积分")
|
|
61
|
+
print("这次实际扣费:", meta.credits_charged or 0, "积分")
|
|
62
|
+
print("特殊计价:", meta.credits_pricing or "原价")
|
|
63
|
+
print("余额剩余:", meta.balance_remaining_cents or 0, "分")
|
|
64
|
+
print("资源包剩余:", meta.quota_remaining_credits or 0, "积分")
|
|
65
|
+
print("当前有效额度桶:", meta.active_quota_buckets or 0)
|
|
66
|
+
print("额度用空即停:", meta.stop_on_empty)
|
|
67
|
+
print("Key QPS:", meta.billing_key_rate_remaining or 0, "/", meta.billing_key_rate_limit or 0, meta.billing_key_rate_unit or "req")
|
|
68
|
+
print("Request ID:", meta.request_id)
|
|
69
|
+
|
|
70
|
+
# 失败路径
|
|
71
|
+
try:
|
|
72
|
+
client.social.get_social_qq_userinfo(qq="10001")
|
|
73
|
+
except UapiError as err:
|
|
74
|
+
if err.meta:
|
|
75
|
+
print("Retry-After 秒数:", err.meta.retry_after_seconds)
|
|
76
|
+
print("Retry-After 原始值:", err.meta.retry_after_raw)
|
|
77
|
+
print("访客 QPS:", err.meta.visitor_rate_remaining or 0, "/", err.meta.visitor_rate_limit or 0)
|
|
78
|
+
print("Request ID:", err.meta.request_id)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
常用字段一览:
|
|
82
|
+
|
|
83
|
+
| 字段 | 说明 |
|
|
84
|
+
|------|------|
|
|
85
|
+
| `credits_requested` | 这次请求原本要扣多少积分,也就是请求价 |
|
|
86
|
+
| `credits_charged` | 这次请求实际扣了多少积分 |
|
|
87
|
+
| `credits_pricing` | 特殊计价原因,例如缓存半价 `cache-hit-half-price` |
|
|
88
|
+
| `balance_remaining_cents` | 账户余额剩余(分) |
|
|
89
|
+
| `quota_remaining_credits` | 资源包剩余积分 |
|
|
90
|
+
| `active_quota_buckets` | 当前还有多少个有效额度桶参与计费 |
|
|
91
|
+
| `stop_on_empty` | 额度耗尽后是否直接停止服务 |
|
|
92
|
+
| `retry_after_seconds` / `retry_after_raw` | 限流后的等待时长;当服务端返回 HTTP 时间字符串时看 `retry_after_raw` |
|
|
93
|
+
| `request_id` | 请求唯一 ID,排障时使用 |
|
|
94
|
+
| `billing_key_rate_limit` / `billing_key_rate_remaining` | Billing Key 当前 QPS 规则的上限与剩余 |
|
|
95
|
+
| `billing_ip_rate_limit` / `billing_ip_rate_remaining` | Billing Key 单 IP 当前 QPS 规则的上限与剩余 |
|
|
96
|
+
| `visitor_rate_limit` / `visitor_rate_remaining` | 访客当前 QPS 规则的上限与剩余 |
|
|
97
|
+
| `rate_limit_policies` / `rate_limits` | 完整结构化限流策略数据 |
|
|
98
|
+
|
|
43
99
|
## 进阶实践
|
|
44
100
|
|
|
45
101
|
### 缓存与幂等
|
|
@@ -48,7 +104,7 @@ print(result)
|
|
|
48
104
|
from functools import lru_cache
|
|
49
105
|
from uapi import UapiClient
|
|
50
106
|
|
|
51
|
-
client = UapiClient("https://uapis.cn
|
|
107
|
+
client = UapiClient("https://uapis.cn", token="YOUR_API_KEY")
|
|
52
108
|
|
|
53
109
|
@lru_cache(maxsize=128)
|
|
54
110
|
def cached_lookup(qq: str):
|
|
@@ -59,34 +115,19 @@ user = cached_lookup("10001")
|
|
|
59
115
|
|
|
60
116
|
也可以在 FastAPI / Django 项目里配合 Redis,将 SDK 的响应序列化后写入缓存,命中即直接返回。
|
|
61
117
|
|
|
62
|
-
###
|
|
118
|
+
### 调整超时与环境
|
|
63
119
|
|
|
64
120
|
```python
|
|
65
|
-
import httpx
|
|
66
|
-
from httpx import Auth
|
|
67
121
|
from uapi import UapiClient
|
|
68
122
|
|
|
69
|
-
class StaticToken(Auth):
|
|
70
|
-
def __init__(self, token: str):
|
|
71
|
-
self.token = token
|
|
72
|
-
def auth_flow(self, request):
|
|
73
|
-
request.headers["Authorization"] = f"Bearer {self.token}"
|
|
74
|
-
yield request
|
|
75
|
-
|
|
76
|
-
http_client = httpx.Client(
|
|
77
|
-
timeout=5,
|
|
78
|
-
transport=httpx.HTTPTransport(retries=3),
|
|
79
|
-
event_hooks={"request": [lambda request: print("->", request.url)]},
|
|
80
|
-
)
|
|
81
|
-
|
|
82
123
|
client = UapiClient(
|
|
83
|
-
"https://uapis.cn
|
|
84
|
-
|
|
85
|
-
|
|
124
|
+
"https://uapis.cn",
|
|
125
|
+
token="YOUR_API_KEY",
|
|
126
|
+
timeout=5.0,
|
|
86
127
|
)
|
|
87
128
|
```
|
|
88
129
|
|
|
89
|
-
|
|
130
|
+
如果你需要切换到别的环境,直接改 `base_url` 就可以;如果你只想缩短等待时间,传 `timeout` 就够了。
|
|
90
131
|
|
|
91
132
|
## 错误模型概览
|
|
92
133
|
|
|
@@ -12,6 +12,13 @@ class _Config:
|
|
|
12
12
|
token: Optional[str] = None
|
|
13
13
|
timeout: float = 15.0
|
|
14
14
|
|
|
15
|
+
|
|
16
|
+
def _normalize_base_url(base_url: str) -> str:
|
|
17
|
+
normalized = base_url.rstrip("/")
|
|
18
|
+
if normalized.endswith("/api/v1"):
|
|
19
|
+
normalized = normalized[: -len("/api/v1")]
|
|
20
|
+
return normalized
|
|
21
|
+
|
|
15
22
|
class _HTTP:
|
|
16
23
|
def __init__(self, cfg: _Config):
|
|
17
24
|
self._cfg = cfg
|
|
@@ -43,7 +50,7 @@ class UapiClient:
|
|
|
43
50
|
"""
|
|
44
51
|
|
|
45
52
|
def __init__(self, base_url: str, token: str | None = None, timeout: float = 15.0):
|
|
46
|
-
self._http = _HTTP(_Config(base_url, token, timeout))
|
|
53
|
+
self._http = _HTTP(_Config(_normalize_base_url(base_url), token, timeout))
|
|
47
54
|
# 动态挂载每个 Tag 的 API 门面
|
|
48
55
|
_clipzy_zai_xian_jian_tie_ban = _ClipzyZaiXianJianTieBanApi(self._http)
|
|
49
56
|
self.clipzy_zai_xian_jian_tie_ban = _clipzy_zai_xian_jian_tie_ban
|
|
@@ -2608,4 +2615,3 @@ UAPI Pro Search 是一个智能搜索引擎,采用机器学习算法对搜索
|
|
|
2608
2615
|
|
|
2609
2616
|
return self._http.request("POST", path, params=params, json=body if body else None)
|
|
2610
2617
|
|
|
2611
|
-
|
|
@@ -20,6 +20,7 @@ class RateLimitStateEntry:
|
|
|
20
20
|
@dataclass
|
|
21
21
|
class ResponseMeta:
|
|
22
22
|
request_id: Optional[str] = None
|
|
23
|
+
retry_after_raw: Optional[str] = None
|
|
23
24
|
retry_after_seconds: Optional[int] = None
|
|
24
25
|
debit_status: Optional[str] = None
|
|
25
26
|
credits_requested: Optional[int] = None
|
|
@@ -37,6 +38,21 @@ class ResponseMeta:
|
|
|
37
38
|
quota_remaining_credits: Optional[int] = None
|
|
38
39
|
visitor_quota_limit_credits: Optional[int] = None
|
|
39
40
|
visitor_quota_remaining_credits: Optional[int] = None
|
|
41
|
+
billing_key_rate_limit: Optional[int] = None
|
|
42
|
+
billing_key_rate_remaining: Optional[int] = None
|
|
43
|
+
billing_key_rate_unit: Optional[str] = None
|
|
44
|
+
billing_key_rate_window_seconds: Optional[int] = None
|
|
45
|
+
billing_key_rate_reset_after_seconds: Optional[int] = None
|
|
46
|
+
billing_ip_rate_limit: Optional[int] = None
|
|
47
|
+
billing_ip_rate_remaining: Optional[int] = None
|
|
48
|
+
billing_ip_rate_unit: Optional[str] = None
|
|
49
|
+
billing_ip_rate_window_seconds: Optional[int] = None
|
|
50
|
+
billing_ip_rate_reset_after_seconds: Optional[int] = None
|
|
51
|
+
visitor_rate_limit: Optional[int] = None
|
|
52
|
+
visitor_rate_remaining: Optional[int] = None
|
|
53
|
+
visitor_rate_unit: Optional[str] = None
|
|
54
|
+
visitor_rate_window_seconds: Optional[int] = None
|
|
55
|
+
visitor_rate_reset_after_seconds: Optional[int] = None
|
|
40
56
|
raw_headers: Dict[str, str] = field(default_factory=dict)
|
|
41
57
|
|
|
42
58
|
class UapiError(Exception):
|
|
@@ -230,8 +246,16 @@ def extract_meta(headers: Mapping[str, str]) -> ResponseMeta:
|
|
|
230
246
|
reset_after_seconds=_parse_int(params.get("t")),
|
|
231
247
|
)
|
|
232
248
|
|
|
249
|
+
billing_key_rate_policy = rate_limit_policies.get("billing-key-rate")
|
|
250
|
+
billing_key_rate_state = rate_limits.get("billing-key-rate")
|
|
251
|
+
billing_ip_rate_policy = rate_limit_policies.get("billing-ip-rate")
|
|
252
|
+
billing_ip_rate_state = rate_limits.get("billing-ip-rate")
|
|
253
|
+
visitor_rate_policy = rate_limit_policies.get("visitor-rate")
|
|
254
|
+
visitor_rate_state = rate_limits.get("visitor-rate")
|
|
255
|
+
|
|
233
256
|
return ResponseMeta(
|
|
234
257
|
request_id=raw_headers.get("x-request-id"),
|
|
258
|
+
retry_after_raw=raw_headers.get("retry-after"),
|
|
235
259
|
retry_after_seconds=_parse_int(raw_headers.get("retry-after")),
|
|
236
260
|
debit_status=raw_headers.get("uapi-debit-status"),
|
|
237
261
|
credits_requested=_parse_int(raw_headers.get("uapi-credits-requested")),
|
|
@@ -249,6 +273,21 @@ def extract_meta(headers: Mapping[str, str]) -> ResponseMeta:
|
|
|
249
273
|
quota_remaining_credits=rate_limits.get("billing-quota").remaining if "billing-quota" in rate_limits else None,
|
|
250
274
|
visitor_quota_limit_credits=rate_limit_policies.get("visitor-quota").quota if "visitor-quota" in rate_limit_policies else None,
|
|
251
275
|
visitor_quota_remaining_credits=rate_limits.get("visitor-quota").remaining if "visitor-quota" in rate_limits else None,
|
|
276
|
+
billing_key_rate_limit=billing_key_rate_policy.quota if billing_key_rate_policy else None,
|
|
277
|
+
billing_key_rate_remaining=billing_key_rate_state.remaining if billing_key_rate_state else None,
|
|
278
|
+
billing_key_rate_unit=billing_key_rate_policy.unit if billing_key_rate_policy and billing_key_rate_policy.unit is not None else (billing_key_rate_state.unit if billing_key_rate_state else None),
|
|
279
|
+
billing_key_rate_window_seconds=billing_key_rate_policy.window_seconds if billing_key_rate_policy else None,
|
|
280
|
+
billing_key_rate_reset_after_seconds=billing_key_rate_state.reset_after_seconds if billing_key_rate_state else None,
|
|
281
|
+
billing_ip_rate_limit=billing_ip_rate_policy.quota if billing_ip_rate_policy else None,
|
|
282
|
+
billing_ip_rate_remaining=billing_ip_rate_state.remaining if billing_ip_rate_state else None,
|
|
283
|
+
billing_ip_rate_unit=billing_ip_rate_policy.unit if billing_ip_rate_policy and billing_ip_rate_policy.unit is not None else (billing_ip_rate_state.unit if billing_ip_rate_state else None),
|
|
284
|
+
billing_ip_rate_window_seconds=billing_ip_rate_policy.window_seconds if billing_ip_rate_policy else None,
|
|
285
|
+
billing_ip_rate_reset_after_seconds=billing_ip_rate_state.reset_after_seconds if billing_ip_rate_state else None,
|
|
286
|
+
visitor_rate_limit=visitor_rate_policy.quota if visitor_rate_policy else None,
|
|
287
|
+
visitor_rate_remaining=visitor_rate_state.remaining if visitor_rate_state else None,
|
|
288
|
+
visitor_rate_unit=visitor_rate_policy.unit if visitor_rate_policy and visitor_rate_policy.unit is not None else (visitor_rate_state.unit if visitor_rate_state else None),
|
|
289
|
+
visitor_rate_window_seconds=visitor_rate_policy.window_seconds if visitor_rate_policy else None,
|
|
290
|
+
visitor_rate_reset_after_seconds=visitor_rate_state.reset_after_seconds if visitor_rate_state else None,
|
|
252
291
|
raw_headers=raw_headers,
|
|
253
292
|
)
|
|
254
293
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: uapi-sdk-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.14
|
|
4
4
|
Summary: Idiomatic UAPI SDK for Python
|
|
5
5
|
Author-email: UAPI <dev@uapis.cn>
|
|
6
6
|
Requires-Python: >=3.9
|
|
@@ -32,11 +32,13 @@ pip install uapi-sdk-python
|
|
|
32
32
|
```python
|
|
33
33
|
from uapi import UapiClient
|
|
34
34
|
|
|
35
|
-
client = UapiClient("https://uapis.cn
|
|
36
|
-
result = client.
|
|
35
|
+
client = UapiClient("https://uapis.cn", "YOUR_API_KEY")
|
|
36
|
+
result = client.misc.get_misc_hotboard(type="weibo")
|
|
37
37
|
print(result)
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
这个接口默认只要传 `type` 就可以拿当前热榜。`time`、`keyword`、`time_start`、`time_end`、`limit`、`sources` 都是按场景再传的可选参数。
|
|
41
|
+
|
|
40
42
|
> [!TIP]
|
|
41
43
|
> 请使用与运行脚本相同的 Python 解释器安装依赖,例如执行 `python -m pip install uapi-sdk-python` 后再运行 `python main.py`。在 VS Code / Pyright 中若提示 “Import uapi could not be resolved”,将解释器切换到当前虚拟环境即可恢复补全。
|
|
42
44
|
|
|
@@ -54,6 +56,60 @@ print(result)
|
|
|
54
56
|
|
|
55
57
|
如果你需要查看字段细节或内部逻辑,仓库中的 `./internal` 目录同步保留了由 `openapi-generator` 生成的完整结构体,随时可供参考。
|
|
56
58
|
|
|
59
|
+
## 响应元信息
|
|
60
|
+
|
|
61
|
+
每次请求完成后,SDK 会自动把响应 Header 解析成结构化的 `ResponseMeta`,你不用自己拆原始字符串。
|
|
62
|
+
|
|
63
|
+
成功时可以通过 `client.last_response_meta` 读取,失败时可以通过 `err.meta` 读取,两条路径拿到的是同一套字段。
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from uapi import UapiClient, UapiError
|
|
67
|
+
|
|
68
|
+
client = UapiClient("https://uapis.cn", "YOUR_API_KEY")
|
|
69
|
+
|
|
70
|
+
# 成功路径
|
|
71
|
+
client.social.get_social_qq_userinfo(qq="10001")
|
|
72
|
+
meta = client.last_response_meta
|
|
73
|
+
if meta:
|
|
74
|
+
print("这次请求原价:", meta.credits_requested or 0, "积分")
|
|
75
|
+
print("这次实际扣费:", meta.credits_charged or 0, "积分")
|
|
76
|
+
print("特殊计价:", meta.credits_pricing or "原价")
|
|
77
|
+
print("余额剩余:", meta.balance_remaining_cents or 0, "分")
|
|
78
|
+
print("资源包剩余:", meta.quota_remaining_credits or 0, "积分")
|
|
79
|
+
print("当前有效额度桶:", meta.active_quota_buckets or 0)
|
|
80
|
+
print("额度用空即停:", meta.stop_on_empty)
|
|
81
|
+
print("Key QPS:", meta.billing_key_rate_remaining or 0, "/", meta.billing_key_rate_limit or 0, meta.billing_key_rate_unit or "req")
|
|
82
|
+
print("Request ID:", meta.request_id)
|
|
83
|
+
|
|
84
|
+
# 失败路径
|
|
85
|
+
try:
|
|
86
|
+
client.social.get_social_qq_userinfo(qq="10001")
|
|
87
|
+
except UapiError as err:
|
|
88
|
+
if err.meta:
|
|
89
|
+
print("Retry-After 秒数:", err.meta.retry_after_seconds)
|
|
90
|
+
print("Retry-After 原始值:", err.meta.retry_after_raw)
|
|
91
|
+
print("访客 QPS:", err.meta.visitor_rate_remaining or 0, "/", err.meta.visitor_rate_limit or 0)
|
|
92
|
+
print("Request ID:", err.meta.request_id)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
常用字段一览:
|
|
96
|
+
|
|
97
|
+
| 字段 | 说明 |
|
|
98
|
+
|------|------|
|
|
99
|
+
| `credits_requested` | 这次请求原本要扣多少积分,也就是请求价 |
|
|
100
|
+
| `credits_charged` | 这次请求实际扣了多少积分 |
|
|
101
|
+
| `credits_pricing` | 特殊计价原因,例如缓存半价 `cache-hit-half-price` |
|
|
102
|
+
| `balance_remaining_cents` | 账户余额剩余(分) |
|
|
103
|
+
| `quota_remaining_credits` | 资源包剩余积分 |
|
|
104
|
+
| `active_quota_buckets` | 当前还有多少个有效额度桶参与计费 |
|
|
105
|
+
| `stop_on_empty` | 额度耗尽后是否直接停止服务 |
|
|
106
|
+
| `retry_after_seconds` / `retry_after_raw` | 限流后的等待时长;当服务端返回 HTTP 时间字符串时看 `retry_after_raw` |
|
|
107
|
+
| `request_id` | 请求唯一 ID,排障时使用 |
|
|
108
|
+
| `billing_key_rate_limit` / `billing_key_rate_remaining` | Billing Key 当前 QPS 规则的上限与剩余 |
|
|
109
|
+
| `billing_ip_rate_limit` / `billing_ip_rate_remaining` | Billing Key 单 IP 当前 QPS 规则的上限与剩余 |
|
|
110
|
+
| `visitor_rate_limit` / `visitor_rate_remaining` | 访客当前 QPS 规则的上限与剩余 |
|
|
111
|
+
| `rate_limit_policies` / `rate_limits` | 完整结构化限流策略数据 |
|
|
112
|
+
|
|
57
113
|
## 进阶实践
|
|
58
114
|
|
|
59
115
|
### 缓存与幂等
|
|
@@ -62,7 +118,7 @@ print(result)
|
|
|
62
118
|
from functools import lru_cache
|
|
63
119
|
from uapi import UapiClient
|
|
64
120
|
|
|
65
|
-
client = UapiClient("https://uapis.cn
|
|
121
|
+
client = UapiClient("https://uapis.cn", token="YOUR_API_KEY")
|
|
66
122
|
|
|
67
123
|
@lru_cache(maxsize=128)
|
|
68
124
|
def cached_lookup(qq: str):
|
|
@@ -73,34 +129,19 @@ user = cached_lookup("10001")
|
|
|
73
129
|
|
|
74
130
|
也可以在 FastAPI / Django 项目里配合 Redis,将 SDK 的响应序列化后写入缓存,命中即直接返回。
|
|
75
131
|
|
|
76
|
-
###
|
|
132
|
+
### 调整超时与环境
|
|
77
133
|
|
|
78
134
|
```python
|
|
79
|
-
import httpx
|
|
80
|
-
from httpx import Auth
|
|
81
135
|
from uapi import UapiClient
|
|
82
136
|
|
|
83
|
-
class StaticToken(Auth):
|
|
84
|
-
def __init__(self, token: str):
|
|
85
|
-
self.token = token
|
|
86
|
-
def auth_flow(self, request):
|
|
87
|
-
request.headers["Authorization"] = f"Bearer {self.token}"
|
|
88
|
-
yield request
|
|
89
|
-
|
|
90
|
-
http_client = httpx.Client(
|
|
91
|
-
timeout=5,
|
|
92
|
-
transport=httpx.HTTPTransport(retries=3),
|
|
93
|
-
event_hooks={"request": [lambda request: print("->", request.url)]},
|
|
94
|
-
)
|
|
95
|
-
|
|
96
137
|
client = UapiClient(
|
|
97
|
-
"https://uapis.cn
|
|
98
|
-
|
|
99
|
-
|
|
138
|
+
"https://uapis.cn",
|
|
139
|
+
token="YOUR_API_KEY",
|
|
140
|
+
timeout=5.0,
|
|
100
141
|
)
|
|
101
142
|
```
|
|
102
143
|
|
|
103
|
-
|
|
144
|
+
如果你需要切换到别的环境,直接改 `base_url` 就可以;如果你只想缩短等待时间,传 `timeout` 就够了。
|
|
104
145
|
|
|
105
146
|
## 错误模型概览
|
|
106
147
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{uapi_sdk_python-0.1.13 → uapi_sdk_python-0.1.14}/uapi_sdk_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|