ringo-task-queue 0.1.0.dev0__py3-none-win_amd64.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.
- ringo_task_queue/__init__.py +68 -0
- ringo_task_queue/_pb.py +154 -0
- ringo_task_queue/_proto/ringo/v1/queue_pb2.py +133 -0
- ringo_task_queue/_proto/ringo/v1/queue_pb2_grpc.py +574 -0
- ringo_task_queue/_version.py +13 -0
- ringo_task_queue/bin/manifest.json +20 -0
- ringo_task_queue/bin/ringo-task-queue-windows-amd64.exe +0 -0
- ringo_task_queue/binary.py +253 -0
- ringo_task_queue/client.py +863 -0
- ringo_task_queue/daemon.py +507 -0
- ringo_task_queue/errors.py +152 -0
- ringo_task_queue/models.py +364 -0
- ringo_task_queue/py.typed +0 -0
- ringo_task_queue/worker.py +785 -0
- ringo_task_queue-0.1.0.dev0.dist-info/METADATA +445 -0
- ringo_task_queue-0.1.0.dev0.dist-info/RECORD +18 -0
- ringo_task_queue-0.1.0.dev0.dist-info/WHEEL +4 -0
- ringo_task_queue-0.1.0.dev0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ringo-task-queue
|
|
3
|
+
Version: 0.1.0.dev0
|
|
4
|
+
Summary: Python asyncio SDK for Ringo Task Queue with a bundled offline Go daemon
|
|
5
|
+
Author: Ringo Task Queue contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: asyncio,grpc,postgresql,sqlite,task-queue
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Framework :: AsyncIO
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
13
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: grpcio-health-checking<2,>=1.71.0
|
|
23
|
+
Requires-Dist: grpcio-status<2,>=1.71.0
|
|
24
|
+
Requires-Dist: grpcio<2,>=1.71.0
|
|
25
|
+
Requires-Dist: protobuf<7,>=5.29.5
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: build<2,>=1; extra == 'dev'
|
|
28
|
+
Requires-Dist: grpcio-tools<2,>=1.71.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: hatchling<2,>=1.27; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy<2,>=1.15; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff<1,>=0.11; extra == 'dev'
|
|
34
|
+
Provides-Extra: generate
|
|
35
|
+
Requires-Dist: grpcio-tools<2,>=1.71.0; extra == 'generate'
|
|
36
|
+
Provides-Extra: test
|
|
37
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'test'
|
|
38
|
+
Requires-Dist: pytest>=8; extra == 'test'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# Ringo Task Queue — Python SDK
|
|
42
|
+
|
|
43
|
+
面向 Python 3.11+ 的 asyncio 任务队列 SDK。它支持 SDK 管理的本地 SQLite/PostgreSQL daemon 和远程 gRPC 服务,提供永久去重、至少一次执行、lease fencing、自动续租、重试和 DEAD/DLQ 语义。
|
|
44
|
+
|
|
45
|
+
## 安装后即可离线运行
|
|
46
|
+
|
|
47
|
+
```shell
|
|
48
|
+
pip install ringo-task-queue
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
正式发布包含五个同版本平台 wheel:
|
|
52
|
+
|
|
53
|
+
| 系统 | wheel tag | 内置 daemon |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| Windows x64 | `win_amd64` | Windows x64 |
|
|
56
|
+
| Linux x64 | `manylinux_2_17_x86_64` | Linux x64 |
|
|
57
|
+
| Linux ARM64 | `manylinux_2_17_aarch64` | Linux ARM64 |
|
|
58
|
+
| macOS Intel | `macosx_11_0_x86_64` | macOS Intel |
|
|
59
|
+
| macOS Apple Silicon | `macosx_11_0_arm64` | macOS ARM64 |
|
|
60
|
+
|
|
61
|
+
`pip` 会自动选择当前平台的 wheel。每个 wheel 已包含匹配的 Go daemon;本地模式不会在安装或运行时下载二进制,不要求安装 Go,也不需要设置 `RINGO_DAEMON_PATH`。PostgreSQL 服务本身不包含在包内。
|
|
62
|
+
|
|
63
|
+
开发版本需要允许预发布:
|
|
64
|
+
|
|
65
|
+
```shell
|
|
66
|
+
pip install --pre ringo-task-queue
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 快速开始
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
import asyncio
|
|
73
|
+
from ringo_task_queue import Ringo
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def main() -> None:
|
|
77
|
+
async with Ringo.local("./data/ringo") as client:
|
|
78
|
+
queue = client.queue("email")
|
|
79
|
+
handled = asyncio.Event()
|
|
80
|
+
|
|
81
|
+
async def handle(task, ctx) -> None:
|
|
82
|
+
print(task.id, task.payload)
|
|
83
|
+
await ctx.report_progress(1, 1, "sent")
|
|
84
|
+
handled.set()
|
|
85
|
+
|
|
86
|
+
worker = queue.worker(
|
|
87
|
+
handle,
|
|
88
|
+
task_types=["send-email"],
|
|
89
|
+
concurrency=4,
|
|
90
|
+
)
|
|
91
|
+
worker_task = asyncio.create_task(worker.run())
|
|
92
|
+
|
|
93
|
+
result = await queue.enqueue(
|
|
94
|
+
unique_key="welcome:user-42",
|
|
95
|
+
task_type="send-email",
|
|
96
|
+
payload={"user_id": 42},
|
|
97
|
+
)
|
|
98
|
+
print(result.task_id, result.created)
|
|
99
|
+
|
|
100
|
+
await asyncio.wait_for(handled.wait(), timeout=10)
|
|
101
|
+
await worker.close()
|
|
102
|
+
await worker_task
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
asyncio.run(main())
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`(queue, unique_key)` 在任务完整生命周期内永久唯一。重复 enqueue 返回原任务 ID 和 `created=False`,不会创建副本,也不会抛出重复异常。
|
|
109
|
+
|
|
110
|
+
## 本地模式
|
|
111
|
+
|
|
112
|
+
### SQLite
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
async with Ringo.local("./data/ringo") as client:
|
|
116
|
+
queue = client.queue("crawl")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
SDK 自动完成 daemon 校验、随机 loopback 端口启动、健康检查、协议协商和退出回收。SQLite 数据保存在 `data_dir`;daemon 日志追加到 `data_dir/ringo-daemon.stderr.log`。同一时间不能有两个本地 daemon 占用同一个 data dir。
|
|
120
|
+
|
|
121
|
+
### PostgreSQL
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
import os
|
|
125
|
+
from ringo_task_queue import PostgresStorage, Ringo
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
async with Ringo.local(
|
|
129
|
+
"./data/ringo-process",
|
|
130
|
+
storage=PostgresStorage(os.environ["RINGO_POSTGRES_DSN"]),
|
|
131
|
+
) as client:
|
|
132
|
+
queue = client.queue("crawl")
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
PostgreSQL 必须由应用提供。SDK 不下载、启动或停止 PostgreSQL。DSN 通过子进程环境传递,不放进进程参数,`PostgresStorage` 的 `repr` 也会隐藏它。
|
|
136
|
+
|
|
137
|
+
## 远程模式
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
async with Ringo.connect(
|
|
141
|
+
"https://queue.internal.example:7233",
|
|
142
|
+
token="service-token",
|
|
143
|
+
) as client:
|
|
144
|
+
queue = client.queue("crawl")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`grpc://`/`http://` 使用明文,`grpcs://`/`https://` 使用 TLS;省略端口时默认 `7233`。
|
|
148
|
+
|
|
149
|
+
受限链路可以启用整个 gRPC channel 的 gzip:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
async with Ringo.connect(
|
|
153
|
+
"https://queue.internal.example:7233",
|
|
154
|
+
token="service-token",
|
|
155
|
+
compression="gzip",
|
|
156
|
+
) as client:
|
|
157
|
+
...
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`compression` 只接受 `"none"` 或 `"gzip"`。它覆盖 unary RPC 和 Work stream 并在重连后保持;本地模式始终不压缩。
|
|
161
|
+
|
|
162
|
+
## 提交任务
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from datetime import datetime, timedelta, timezone
|
|
166
|
+
from ringo_task_queue import RetryPolicy, RetryStrategy, TaskSpec
|
|
167
|
+
|
|
168
|
+
spec = TaskSpec(
|
|
169
|
+
unique_key="page:10001",
|
|
170
|
+
task_type="fetch-page",
|
|
171
|
+
payload={"url": "https://example.com/10001"},
|
|
172
|
+
priority=10,
|
|
173
|
+
available_at=datetime.now(timezone.utc) + timedelta(seconds=5),
|
|
174
|
+
retry_policy=RetryPolicy(
|
|
175
|
+
max_attempts=5,
|
|
176
|
+
strategy=RetryStrategy.EXPONENTIAL,
|
|
177
|
+
initial_delay=timedelta(seconds=1),
|
|
178
|
+
max_delay=timedelta(seconds=60),
|
|
179
|
+
multiplier=2.0,
|
|
180
|
+
jitter=0.2,
|
|
181
|
+
),
|
|
182
|
+
)
|
|
183
|
+
result = await queue.enqueue(spec)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
也可以使用关键字参数:
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
result = await queue.enqueue(
|
|
190
|
+
unique_key="page:10001",
|
|
191
|
+
task_type="fetch-page",
|
|
192
|
+
payload={"url": "https://example.com/10001"},
|
|
193
|
+
)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
payload 必须显式提供;显式 `None` 表示 JSON null。允许 JSON 对象、数组和标量,但:
|
|
197
|
+
|
|
198
|
+
- UTF-8 JSON 编码不超过 1 MiB;
|
|
199
|
+
- 不允许非有限浮点数;
|
|
200
|
+
- 整数必须位于 `[-2**53, 2**53]`;
|
|
201
|
+
- JSON 对象的键必须是字符串;
|
|
202
|
+
- 实际使用建议保持在 16–64 KiB 内,大对象只传外部引用。
|
|
203
|
+
|
|
204
|
+
### 批量提交
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
batch = await queue.enqueue_many(
|
|
208
|
+
[
|
|
209
|
+
TaskSpec(
|
|
210
|
+
unique_key=f"page:{i}",
|
|
211
|
+
task_type="fetch-page",
|
|
212
|
+
payload={"page": i},
|
|
213
|
+
)
|
|
214
|
+
for i in range(100)
|
|
215
|
+
]
|
|
216
|
+
)
|
|
217
|
+
print(batch.created_count, batch.duplicate_count)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
每批 1–1,000 条,`batch.results` 保留逐项结果。
|
|
221
|
+
|
|
222
|
+
## 查询和手工重试
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
task = await queue.get_by_id(result.task_id)
|
|
226
|
+
task = await queue.get_by_unique_key("page:10001")
|
|
227
|
+
|
|
228
|
+
# 始终按业务 unique_key 解释
|
|
229
|
+
reopened = await queue.retry("page:10001")
|
|
230
|
+
|
|
231
|
+
# 显式按服务端 task ID
|
|
232
|
+
reopened = await queue.retry_by_id(result.task_id)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
状态包括 `SCHEDULED`、`READY`、`LEASED`、`SUCCEEDED`、`DEAD` 和 `CANCELLED`。`Task` 还提供 attempt 历史、最后错误、进度和当前 lease 摘要。手工 retry 保留历史和永久去重关系。
|
|
236
|
+
|
|
237
|
+
## Worker
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
from datetime import timedelta
|
|
241
|
+
from ringo_task_queue import PermanentTaskError, RetryTaskError
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
async def handle(task, ctx) -> None:
|
|
245
|
+
await ctx.report_progress(1, 2, "started")
|
|
246
|
+
if should_retry(task):
|
|
247
|
+
raise RetryTaskError("rate limited", delay=30) # 秒
|
|
248
|
+
if invalid(task):
|
|
249
|
+
raise PermanentTaskError("invalid input")
|
|
250
|
+
await process(task)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
worker = queue.worker(
|
|
254
|
+
handle,
|
|
255
|
+
task_types=["fetch-page"],
|
|
256
|
+
concurrency=32,
|
|
257
|
+
lease_duration=timedelta(seconds=60),
|
|
258
|
+
grace_period=timedelta(seconds=30),
|
|
259
|
+
)
|
|
260
|
+
await worker.run()
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
`run()` 一直运行到其他协程调用 `await worker.close()`,并且只能调用一次。通常用 `asyncio.create_task(worker.run())` 启动。
|
|
264
|
+
|
|
265
|
+
Handler 映射:
|
|
266
|
+
|
|
267
|
+
| 结果 | 动作 |
|
|
268
|
+
|---|---|
|
|
269
|
+
| 正常返回 | ACK / `SUCCEEDED` |
|
|
270
|
+
| 普通异常 | NACK / 按策略重试 |
|
|
271
|
+
| `RetryTaskError` | NACK,可覆盖本次延迟 |
|
|
272
|
+
| `PermanentTaskError` | Reject / `DEAD` |
|
|
273
|
+
| 取消 | 不发送终态,等待 lease 到期重投 |
|
|
274
|
+
|
|
275
|
+
Worker 持有 lease token,自动续租并严格控制并发。`TaskContext` 提供 `attempt`、`max_attempts`、`worker_id`、`cancelled`、`wait_cancelled()` 和 `report_progress()`。lease 丢失、stream 断开或强制关闭时,context 会被取消。
|
|
276
|
+
|
|
277
|
+
这是至少一次系统:handler 的外部副作用必须使用 `unique_key` 或任务 ID 实现幂等。
|
|
278
|
+
|
|
279
|
+
## 手工 Claim
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
from datetime import timedelta
|
|
283
|
+
|
|
284
|
+
leased = await queue.claim(
|
|
285
|
+
limit=10,
|
|
286
|
+
task_types=["fetch-page"],
|
|
287
|
+
wait_timeout=timedelta(seconds=5),
|
|
288
|
+
lease_duration=timedelta(seconds=60),
|
|
289
|
+
worker_id="manual-worker-1",
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
for item in leased:
|
|
293
|
+
try:
|
|
294
|
+
await process(item.task)
|
|
295
|
+
await item.ack()
|
|
296
|
+
except TimeoutError as exc:
|
|
297
|
+
await item.nack(str(exc), delay=timedelta(seconds=10))
|
|
298
|
+
except ValueError as exc:
|
|
299
|
+
await item.reject(str(exc))
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
续租:
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
await item.extend_lease(timedelta(seconds=60))
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
lease token 不公开。旧 attempt 或迟到 ACK 会被 fencing 拒绝并映射成 `LeaseLostError`。
|
|
309
|
+
|
|
310
|
+
## 生命周期和关闭
|
|
311
|
+
|
|
312
|
+
推荐:
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
async with Ringo.local("./data/ringo") as client:
|
|
316
|
+
...
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
或手工:
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
client = Ringo.local("./data/ringo")
|
|
323
|
+
await client.start()
|
|
324
|
+
try:
|
|
325
|
+
...
|
|
326
|
+
finally:
|
|
327
|
+
await client.close()
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
`close()` 可重复调用,且调用者取消不会中断底层清理。它会先 drain worker,在 grace period 内继续续租并允许 handler 完成,再关闭 channel 和 daemon。被强制取消的 handler 不发送 ACK/NACK。
|
|
331
|
+
|
|
332
|
+
本地 daemon 意外退出时最多自动重启三次。结果不确定的写操作不会被盲目重放;只读查询可以在重新协商后重试。
|
|
333
|
+
|
|
334
|
+
## 错误处理
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
from ringo_task_queue import NotFoundError, RingoError, UnavailableError
|
|
338
|
+
|
|
339
|
+
try:
|
|
340
|
+
task = await queue.get_by_unique_key("missing")
|
|
341
|
+
except NotFoundError:
|
|
342
|
+
...
|
|
343
|
+
except UnavailableError as exc:
|
|
344
|
+
logger.warning("temporarily unavailable: %s", exc)
|
|
345
|
+
except RingoError as exc:
|
|
346
|
+
logger.exception(
|
|
347
|
+
"queue error code=%s request_id=%s metadata=%r",
|
|
348
|
+
exc.code,
|
|
349
|
+
exc.request_id,
|
|
350
|
+
exc.metadata,
|
|
351
|
+
)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
公共异常:
|
|
355
|
+
|
|
356
|
+
- `InvalidArgumentError`;
|
|
357
|
+
- `DuplicateError`(普通重复 enqueue 返回 `created=False`);
|
|
358
|
+
- `NotFoundError`;
|
|
359
|
+
- `ConflictError`;
|
|
360
|
+
- `LeaseLostError`;
|
|
361
|
+
- `UnavailableError`;
|
|
362
|
+
- `DeadlineExceededError`;
|
|
363
|
+
- `IncompatibleVersionError`;
|
|
364
|
+
- `InternalError`。
|
|
365
|
+
|
|
366
|
+
## 默认值与限制
|
|
367
|
+
|
|
368
|
+
| 项目 | 默认值或限制 |
|
|
369
|
+
|---|---|
|
|
370
|
+
| 协议 | 1.0 |
|
|
371
|
+
| schema | 3 |
|
|
372
|
+
| lease | 60 秒 |
|
|
373
|
+
| worker concurrency | 1 |
|
|
374
|
+
| graceful shutdown | 30 秒 |
|
|
375
|
+
| retry attempts | 默认 3,最大 32 |
|
|
376
|
+
| retry | 指数退避,1 秒到 60 秒,multiplier 2,jitter 0.2 |
|
|
377
|
+
| batch | 最大 1,000 |
|
|
378
|
+
| payload | 最大 1 MiB UTF-8 JSON |
|
|
379
|
+
| error text | 最大 256 KiB UTF-8 |
|
|
380
|
+
| daemon 自动重启 | 最多 3 次 |
|
|
381
|
+
|
|
382
|
+
## wheel 完整性和二进制解析
|
|
383
|
+
|
|
384
|
+
本地 daemon 解析优先级:
|
|
385
|
+
|
|
386
|
+
1. `Ringo.local(..., daemon_path=...)`;
|
|
387
|
+
2. `RINGO_DAEMON_PATH`;
|
|
388
|
+
3. wheel 内置平台 daemon;
|
|
389
|
+
4. 仅用于开发 checkout 的搜索。
|
|
390
|
+
|
|
391
|
+
普通 PyPI 安装直接使用第 3 项。SDK 在启动前验证 daemon 是普通文件、长度和 SHA-256 与 manifest 一致,然后运行 `version --json` 校验产品名、版本、协议和 schema。校验失败时拒绝启动,不会静默使用错误二进制。
|
|
392
|
+
|
|
393
|
+
## 常见问题
|
|
394
|
+
|
|
395
|
+
### `No matching distribution found`
|
|
396
|
+
|
|
397
|
+
确认 Python 为 3.11+,对应平台 wheel 已上传;安装 `.dev0` 时使用 `--pre`。
|
|
398
|
+
|
|
399
|
+
### 找不到 daemon
|
|
400
|
+
|
|
401
|
+
正式 wheel 不应出现。使用 `python -m pip show -f ringo-task-queue` 检查是否误装了 editable checkout 或非正式产物。
|
|
402
|
+
|
|
403
|
+
### `ConflictError`
|
|
404
|
+
|
|
405
|
+
另一个进程通常已占用同一个 SQLite data dir。复用已有客户端或换一个目录。
|
|
406
|
+
|
|
407
|
+
### 任务重复执行
|
|
408
|
+
|
|
409
|
+
这是至少一次语义的正常边界。使用任务 ID/`unique_key` 为外部副作用建立幂等约束。
|
|
410
|
+
|
|
411
|
+
### Worker 关闭较慢
|
|
412
|
+
|
|
413
|
+
默认允许 handler 在 30 秒内完成。可以调整 `grace_period`,并让 handler 响应 cancellation 或 `ctx.cancelled`。
|
|
414
|
+
|
|
415
|
+
## 维护者构建平台 wheel
|
|
416
|
+
|
|
417
|
+
真实 daemon 不提交到源码树。必须先从同一源码版本生成并验证五平台 release manifest,再逐个平台构建 wheel:
|
|
418
|
+
|
|
419
|
+
```shell
|
|
420
|
+
go run ./tools/release build --out dist --version 0.1.0-dev --commit <revision> --date <RFC3339-UTC>
|
|
421
|
+
go run ./tools/release verify --manifest dist/manifest.json
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
然后从 `sdk/python` 运行:
|
|
425
|
+
|
|
426
|
+
```shell
|
|
427
|
+
python tools/build_platform_wheel.py \
|
|
428
|
+
--binary ../../dist/ringo-task-queue-linux-amd64 \
|
|
429
|
+
--release-manifest ../../dist/manifest.json \
|
|
430
|
+
--platform linux-amd64 \
|
|
431
|
+
--output-dir ../../package-dist/python
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
分别构建 `windows-amd64`、`linux-amd64`、`linux-arm64`、`macos-amd64`、`macos-arm64`。构建会记录根 manifest 的 SHA-256 来源,完成后清理暂存二进制。上传前运行:
|
|
435
|
+
|
|
436
|
+
```shell
|
|
437
|
+
python tools/verify_wheel_set.py ../../package-dist/python
|
|
438
|
+
python -m twine check ../../package-dist/python/*
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
没有经过完整五目标 manifest 暂存的 wheel 和 sdist 会被主动拒绝,避免发布不含 daemon 或平台集合不完整的包。
|
|
442
|
+
|
|
443
|
+
## 许可证
|
|
444
|
+
|
|
445
|
+
MIT License。
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
ringo_task_queue/__init__.py,sha256=MUeo1atWuaoNITczk6hDs4zYLI18IEpkVBHOE3wGhFc,1381
|
|
2
|
+
ringo_task_queue/_pb.py,sha256=gA8HMCx9ZXEMB-ca6me8CYXdRLLPzUXVIz63Q4bz75I,5683
|
|
3
|
+
ringo_task_queue/_version.py,sha256=5fU57YlC-4FhzBd2ppLripDFU1qd0QMs-5SWFnFz_9U,388
|
|
4
|
+
ringo_task_queue/binary.py,sha256=lJtFz_t5BuxDV4dMAqQDpP3mhTvJlnPBGnaiB0Z9D04,9526
|
|
5
|
+
ringo_task_queue/client.py,sha256=GT-JiCMshtf_SFUFvjU_ayi7C0JyuQYOeLrmwISoFLc,33570
|
|
6
|
+
ringo_task_queue/daemon.py,sha256=YS8otZpD9hqKeASb2S8ZWLCWzQj6-FYUb-grmYSnrIs,19849
|
|
7
|
+
ringo_task_queue/errors.py,sha256=UCPl1GJSlHzvGPO5WOmkYwLBqZj8Aozlz7sChAXR8pw,4719
|
|
8
|
+
ringo_task_queue/models.py,sha256=MEXh2JKgCbf3vo0EmnjJ_TscwabaW5Yd9i98LSPIrnk,11691
|
|
9
|
+
ringo_task_queue/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
ringo_task_queue/worker.py,sha256=LD-J9XnQBxJWC2xvVi4tCAzms-O7wfkflSnoEFZXwnM,30366
|
|
11
|
+
ringo_task_queue/_proto/ringo/v1/queue_pb2.py,sha256=Z4S0mNwPerMshRr7DUooj9oeecbaYG4JUpMJJlwVb0o,18074
|
|
12
|
+
ringo_task_queue/_proto/ringo/v1/queue_pb2_grpc.py,sha256=eKtdXhZk3BP1I9IaYQlEgUF_K2WZLzBuIq7koWLhQ00,26066
|
|
13
|
+
ringo_task_queue/bin/manifest.json,sha256=6mfoRB8VFN4NAX1NVfYHx4IJmmImAV1eBgJZhK9UrFY,572
|
|
14
|
+
ringo_task_queue/bin/ringo-task-queue-windows-amd64.exe,sha256=t5i_fAW3fq0GIjOSpiDY5M246bMYfviVHnZPhDHL5N8,22699520
|
|
15
|
+
ringo_task_queue-0.1.0.dev0.dist-info/METADATA,sha256=s_SkJVKmqC2OqhdIDGGKlu0hZvPWqFypqbhtdT5iF7A,13515
|
|
16
|
+
ringo_task_queue-0.1.0.dev0.dist-info/WHEEL,sha256=0nQjyt02uAQbhbYKWZzlDb_ZXUmJUwgypQJ-m16yWKo,94
|
|
17
|
+
ringo_task_queue-0.1.0.dev0.dist-info/licenses/LICENSE,sha256=yK3nnJz_bbOAPlcJqAooYcvecNJnBH9miT_X8Qrerv4,1086
|
|
18
|
+
ringo_task_queue-0.1.0.dev0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ringo Task Queue contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|