queue-func 0.1.2__tar.gz → 0.2.1__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.
Files changed (56) hide show
  1. queue_func-0.2.1/PKG-INFO +255 -0
  2. queue_func-0.2.1/README.md +228 -0
  3. {queue_func-0.1.2 → queue_func-0.2.1}/pyproject.toml +6 -1
  4. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/broker/base.py +4 -24
  5. queue_func-0.2.1/queue_func/broker/queue.py +34 -0
  6. queue_func-0.2.1/queue_func/broker/rabbit_aio_pika.py +47 -0
  7. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/config/general.py +6 -6
  8. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/config/queue.py +1 -1
  9. queue_func-0.2.1/queue_func/config/rabbit_aio_pika.py +55 -0
  10. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/consumer/base.py +38 -22
  11. queue_func-0.2.1/queue_func/consumer/queue.py +32 -0
  12. queue_func-0.2.1/queue_func/consumer/rabbit_aio_pika.py +99 -0
  13. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/factory/backend.py +1 -0
  14. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/factory/builders/__init__.py +2 -0
  15. queue_func-0.2.1/queue_func/factory/builders/rabbit_aio_pika.py +26 -0
  16. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/factory/deco.py +30 -0
  17. queue_func-0.2.1/queue_func/producer/base.py +69 -0
  18. queue_func-0.2.1/queue_func/producer/queue.py +21 -0
  19. queue_func-0.2.1/queue_func/producer/rabbit_aio_pika.py +83 -0
  20. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/serializer/base.py +1 -1
  21. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/serializer/orjson_ser.py +2 -2
  22. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/serializer/raw.py +2 -2
  23. queue_func-0.2.1/queue_func/shared_resource/base.py +25 -0
  24. queue_func-0.2.1/queue_func/shared_resource/queue.py +23 -0
  25. queue_func-0.2.1/queue_func/shared_resource/rabbit_aio_pika.py +111 -0
  26. queue_func-0.2.1/queue_func/task/__init__.py +1 -0
  27. queue_func-0.2.1/queue_func/utils/__init__.py +1 -0
  28. queue_func-0.2.1/queue_func/utils/deco.py +36 -0
  29. queue_func-0.2.1/queue_func/utils/hook.py +50 -0
  30. queue_func-0.2.1/queue_func/utils/waiter.py +35 -0
  31. queue_func-0.1.2/PKG-INFO +0 -183
  32. queue_func-0.1.2/README.md +0 -158
  33. queue_func-0.1.2/queue_func/broker/queue.py +0 -44
  34. queue_func-0.1.2/queue_func/consumer/queue.py +0 -55
  35. queue_func-0.1.2/queue_func/producer/base.py +0 -44
  36. queue_func-0.1.2/queue_func/producer/queue.py +0 -32
  37. {queue_func-0.1.2 → queue_func-0.2.1}/LICENSE +0 -0
  38. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/__init__.py +0 -0
  39. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/broker/__init__.py +0 -0
  40. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/config/__init__.py +0 -0
  41. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/config/utils.py +0 -0
  42. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/consumer/__init__.py +0 -0
  43. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/errors.py +0 -0
  44. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/factory/__init__.py +0 -0
  45. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/factory/builders/queue.py +0 -0
  46. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/factory/registry.py +0 -0
  47. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/factory/runtime.py +0 -0
  48. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/pool_executor/__init__.py +0 -0
  49. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/pool_executor/base.py +0 -0
  50. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/pool_executor/general.py +0 -0
  51. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/pool_executor/utils.py +0 -0
  52. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/producer/__init__.py +0 -0
  53. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/serializer/__init__.py +0 -0
  54. {queue_func-0.1.2/queue_func/task → queue_func-0.2.1/queue_func/shared_resource}/__init__.py +0 -0
  55. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/task/base.py +0 -0
  56. {queue_func-0.1.2 → queue_func-0.2.1}/queue_func/task/thread.py +0 -0
@@ -0,0 +1,255 @@
1
+ Metadata-Version: 2.4
2
+ Name: queue-func
3
+ Version: 0.2.1
4
+ Summary: Async coroutine functions as queue-backed tasks (QueueFunc).
5
+ License: Apache-2.0
6
+ License-File: LICENSE
7
+ Author: liuseki
8
+ Author-email: liuseki@163.com
9
+ Requires-Python: >=3.12,<4.0
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Programming Language :: Python :: 3.15
16
+ Provides-Extra: rabbit
17
+ Requires-Dist: aio-pika (>=9.6,<10.0) ; extra == "rabbit"
18
+ Requires-Dist: orjson
19
+ Requires-Dist: pydantic (>=2.14.1,<3.0.0)
20
+ Project-URL: Changelog, https://github.com/sekil9529/queue-func/blob/master/Changelog.rst
21
+ Project-URL: Documentation, https://github.com/sekil9529/queue-func#readme
22
+ Project-URL: Homepage, https://github.com/sekil9529/queue-func
23
+ Project-URL: Issues, https://github.com/sekil9529/queue-func/issues
24
+ Project-URL: Repository, https://github.com/sekil9529/queue-func
25
+ Description-Content-Type: text/markdown
26
+
27
+ # queue-func
28
+
29
+ 把 **`async def` 协程** 变成可 **`push`** 的队列任务:在 asyncio 里投递、消费,并按通道控制并发与背压。
30
+
31
+ 仓库:[github.com/sekil9529/queue-func](https://github.com/sekil9529/queue-func)。版本记录见 [Changelog.rst](Changelog.rst)。
32
+
33
+ > **需要 Python 3.12 及以上。** 任务入口必须是协程函数,普通函数不能作为任务。
34
+ >
35
+ > RabbitMQ 后端需要另装可选依赖,底层使用 [aio-pika](https://github.com/mosquito/aio-pika) `^9.6`。
36
+
37
+ ## 特点
38
+
39
+ - 完全在 asyncio 里运行。消费者在同一事件循环里按上限并发执行任务,可以和 FastAPI / Starlette 放在一个进程里。
40
+ - 一条链路是序列化、投递、反序列化、执行。启停由通道或 `QueueFuncRuntime` 显式管理。
41
+ - 每个 `QueueFunc` 有自己的队列和消费并发上限。`QueueFuncRuntime` 可以同时跑多段流水线,各段互不影响。
42
+ - 序列化器的 `pack` / `unpack` 决定队列里的数据结构。
43
+ - 通过 `register_backend` 接入新的队列后端。
44
+
45
+ ## 当前后端
46
+
47
+ | 后端 | 说明 |
48
+ |------|------|
49
+ | `Backend.QUEUE` | 进程内 `asyncio.Queue` |
50
+ | `Backend.RABBIT_AIO_PIKA` | RabbitMQ,安装可选依赖 `rabbit` |
51
+
52
+ 内存队列的消息留在进程里,序列化默认是 `raw`(队列中仍是 `(args, kwargs)`)。RabbitMQ 默认用 orjson,队列中是 `bytes`。
53
+
54
+ ## 安装
55
+
56
+ ```shell
57
+ pip install queue-func
58
+ ```
59
+
60
+ 使用 RabbitMQ:
61
+
62
+ ```shell
63
+ pip install "queue-func[rabbit]"
64
+ ```
65
+
66
+ 从仓库安装(开发):
67
+
68
+ ```shell
69
+ git clone https://github.com/sekil9529/queue-func.git
70
+ cd queue-func
71
+ poetry install -E rabbit
72
+ ```
73
+
74
+ ## 用法
75
+
76
+ ### 进程内队列
77
+
78
+ ```python
79
+ import asyncio
80
+
81
+ from queue_func.factory import Backend, queue
82
+
83
+
84
+ @queue(Backend.QUEUE, consume_timeout=0.5, maxsize=1000)
85
+ async def process_order(order_id: int) -> None:
86
+ print(order_id)
87
+
88
+
89
+ async def main() -> None:
90
+ await process_order.broker.start_consumer()
91
+ await process_order.push(args=(42,))
92
+ await process_order.broker.stop_consumer()
93
+ await process_order.broker.join_consumer()
94
+
95
+
96
+ asyncio.run(main())
97
+ ```
98
+
99
+ `push` 就是 `broker.publish`。不传 `timeout` 时用配置里的 `publish_timeout`;传入数字只作用于这一次。`None` 表示不限时。超时抛出 `queue_func.PublishTimeoutError`。
100
+
101
+ 装饰之后仍可直接调用,不经过队列:
102
+
103
+ ```python
104
+ await process_order(1)
105
+ ```
106
+
107
+ ### 多段流水线
108
+
109
+ `QueueFuncRuntime.start()` 按列表反序启动消费者,下游先就绪。`stop()` 先逐个停止消费者,再停止全部生产者。
110
+
111
+ ```python
112
+ import asyncio
113
+
114
+ from queue_func.factory import Backend, QueueFuncRuntime, queue
115
+
116
+
117
+ @queue(Backend.QUEUE, consumer_pool_maxsize=4)
118
+ async def task_b(job: str) -> None:
119
+ print(job)
120
+
121
+
122
+ @queue(Backend.QUEUE, consumer_pool_maxsize=2)
123
+ async def task_a(job: str) -> None:
124
+ await task_b.push(args=(job,))
125
+
126
+
127
+ async def main() -> None:
128
+ runtime = QueueFuncRuntime([task_a, task_b])
129
+ await runtime.start()
130
+ await task_a.push(args=("job-1",))
131
+ await runtime.stop()
132
+
133
+
134
+ asyncio.run(main())
135
+ ```
136
+
137
+ 三段链式示例(含日志):
138
+
139
+ ```shell
140
+ poetry run python examples/demo_queue.py
141
+ ```
142
+
143
+ ### RabbitMQ
144
+
145
+ `url` 和 `queue_name` 必填。`enable_declare_queue=True` 时,队列不存在会创建。默认交换机名是 `""`,路由键默认用队列名。`push` 会等待发布确认;`publish_timeout` 是这次确认的超时。
146
+
147
+ 消息交给消费池之后就确认。处理函数出错不会把消息退回队列。`stop` 会设上停止标志,当前这次拉取结束(空闲时最多再等一个 `consume_timeout`)后退出;还没取走的消息留在 RabbitMQ 上。
148
+
149
+ 同一个 `QueueFunc` 的生产者和消费者可以分进程运行。只投递的进程把 `enable_consumer` 设为 `False`。
150
+
151
+ ```python
152
+ import asyncio
153
+
154
+ from queue_func.factory import Backend, queue
155
+
156
+
157
+ @queue(
158
+ Backend.RABBIT_AIO_PIKA,
159
+ url="amqp://guest:guest@127.0.0.1/",
160
+ queue_name="process_order",
161
+ enable_declare_queue=True,
162
+ )
163
+ async def process_order(order_id: int) -> None:
164
+ print(order_id)
165
+
166
+
167
+ async def main() -> None:
168
+ await process_order.broker.start_consumer()
169
+ await process_order.push(args=(42,))
170
+ await process_order.broker.stop_consumer()
171
+ await process_order.broker.join_consumer()
172
+ await process_order.broker.stop_producer()
173
+ await process_order.broker.join_producer()
174
+
175
+
176
+ asyncio.run(main())
177
+ ```
178
+
179
+ 本地 RabbitMQ(`127.0.0.1:5672`,`guest` / `guest`)上的链式示例:
180
+
181
+ ```shell
182
+ poetry run python examples/demo_rabbit_aio_pika.py
183
+ ```
184
+
185
+ 常用参数:
186
+
187
+ | 参数 | 作用 |
188
+ |------|------|
189
+ | `consumer_pool_maxsize` | 同时执行的协程数,默认 200 |
190
+ | `consume_timeout` | 一次拉取的等待时间,默认 2 秒 |
191
+ | `publish_timeout` | 投递等待上限,默认不限 |
192
+ | `maxsize` | 仅内存队列,`asyncio.Queue` 容量,默认 200 |
193
+ | `serializer_cls` | 队列中的数据结构 |
194
+ | `prefetch_count` | 仅 RabbitMQ,未确认消息窗口 |
195
+ | `delivery_mode` | 仅 RabbitMQ,默认持久化 |
196
+ | `exchange_name` / `routing_key` | 仅 RabbitMQ |
197
+
198
+ 其余字段见 `queue_func.config.queue.Config` 和 `queue_func.config.rabbit_aio_pika.Config`。
199
+
200
+ ## 架构
201
+
202
+ 一个 `@queue` 装饰的协程就是一条队列。`push` 投到它自己这条队列上。
203
+
204
+ - **Broker** 组装生产者、消费者和双方共用的资源,并提供启停与投递。
205
+ - **Producer** 序列化后把数据写入队列。
206
+ - **Consumer** 取出数据,反序列化后交给消费池。
207
+ - **PoolExecutor** 按并发上限执行业务协程。
208
+
209
+ ```mermaid
210
+ flowchart TB
211
+ PUSH["投递"] --> B["通道"]
212
+ B --> PRO["生产者"]
213
+ B -.->|启停| CON["消费者"]
214
+
215
+ PRO -->|序列化| Q[("队列")]
216
+ Q -->|反序列化| CON
217
+ CON --> POOL["消费池"]
218
+ POOL --> FN["业务协程"]
219
+ ```
220
+
221
+ ## 项目结构
222
+
223
+ ```
224
+ queue_func/
225
+ ├── broker/
226
+ ├── producer/
227
+ ├── consumer/
228
+ ├── serializer/ # raw、orjson
229
+ ├── shared_resource/
230
+ ├── config/
231
+ ├── factory/ # @queue、QueueFunc、QueueFuncRuntime
232
+ ├── pool_executor/
233
+ └── utils/
234
+ examples/
235
+ ├── demo_queue.py
236
+ └── demo_rabbit_aio_pika.py
237
+ ```
238
+
239
+ ## 开发与测试
240
+
241
+ ```shell
242
+ poetry install -E rabbit
243
+ pytest
244
+ ```
245
+
246
+ `pytest.ini` 会生成覆盖率页面(`htmlcov/`)和 `report.html`。
247
+
248
+ ## 版本
249
+
250
+ 遵循[语义化版本](https://semver.org/lang/zh-CN/)。变更记录在 [Changelog.rst](Changelog.rst)。
251
+
252
+ ## 许可证
253
+
254
+ Apache-2.0
255
+
@@ -0,0 +1,228 @@
1
+ # queue-func
2
+
3
+ 把 **`async def` 协程** 变成可 **`push`** 的队列任务:在 asyncio 里投递、消费,并按通道控制并发与背压。
4
+
5
+ 仓库:[github.com/sekil9529/queue-func](https://github.com/sekil9529/queue-func)。版本记录见 [Changelog.rst](Changelog.rst)。
6
+
7
+ > **需要 Python 3.12 及以上。** 任务入口必须是协程函数,普通函数不能作为任务。
8
+ >
9
+ > RabbitMQ 后端需要另装可选依赖,底层使用 [aio-pika](https://github.com/mosquito/aio-pika) `^9.6`。
10
+
11
+ ## 特点
12
+
13
+ - 完全在 asyncio 里运行。消费者在同一事件循环里按上限并发执行任务,可以和 FastAPI / Starlette 放在一个进程里。
14
+ - 一条链路是序列化、投递、反序列化、执行。启停由通道或 `QueueFuncRuntime` 显式管理。
15
+ - 每个 `QueueFunc` 有自己的队列和消费并发上限。`QueueFuncRuntime` 可以同时跑多段流水线,各段互不影响。
16
+ - 序列化器的 `pack` / `unpack` 决定队列里的数据结构。
17
+ - 通过 `register_backend` 接入新的队列后端。
18
+
19
+ ## 当前后端
20
+
21
+ | 后端 | 说明 |
22
+ |------|------|
23
+ | `Backend.QUEUE` | 进程内 `asyncio.Queue` |
24
+ | `Backend.RABBIT_AIO_PIKA` | RabbitMQ,安装可选依赖 `rabbit` |
25
+
26
+ 内存队列的消息留在进程里,序列化默认是 `raw`(队列中仍是 `(args, kwargs)`)。RabbitMQ 默认用 orjson,队列中是 `bytes`。
27
+
28
+ ## 安装
29
+
30
+ ```shell
31
+ pip install queue-func
32
+ ```
33
+
34
+ 使用 RabbitMQ:
35
+
36
+ ```shell
37
+ pip install "queue-func[rabbit]"
38
+ ```
39
+
40
+ 从仓库安装(开发):
41
+
42
+ ```shell
43
+ git clone https://github.com/sekil9529/queue-func.git
44
+ cd queue-func
45
+ poetry install -E rabbit
46
+ ```
47
+
48
+ ## 用法
49
+
50
+ ### 进程内队列
51
+
52
+ ```python
53
+ import asyncio
54
+
55
+ from queue_func.factory import Backend, queue
56
+
57
+
58
+ @queue(Backend.QUEUE, consume_timeout=0.5, maxsize=1000)
59
+ async def process_order(order_id: int) -> None:
60
+ print(order_id)
61
+
62
+
63
+ async def main() -> None:
64
+ await process_order.broker.start_consumer()
65
+ await process_order.push(args=(42,))
66
+ await process_order.broker.stop_consumer()
67
+ await process_order.broker.join_consumer()
68
+
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ `push` 就是 `broker.publish`。不传 `timeout` 时用配置里的 `publish_timeout`;传入数字只作用于这一次。`None` 表示不限时。超时抛出 `queue_func.PublishTimeoutError`。
74
+
75
+ 装饰之后仍可直接调用,不经过队列:
76
+
77
+ ```python
78
+ await process_order(1)
79
+ ```
80
+
81
+ ### 多段流水线
82
+
83
+ `QueueFuncRuntime.start()` 按列表反序启动消费者,下游先就绪。`stop()` 先逐个停止消费者,再停止全部生产者。
84
+
85
+ ```python
86
+ import asyncio
87
+
88
+ from queue_func.factory import Backend, QueueFuncRuntime, queue
89
+
90
+
91
+ @queue(Backend.QUEUE, consumer_pool_maxsize=4)
92
+ async def task_b(job: str) -> None:
93
+ print(job)
94
+
95
+
96
+ @queue(Backend.QUEUE, consumer_pool_maxsize=2)
97
+ async def task_a(job: str) -> None:
98
+ await task_b.push(args=(job,))
99
+
100
+
101
+ async def main() -> None:
102
+ runtime = QueueFuncRuntime([task_a, task_b])
103
+ await runtime.start()
104
+ await task_a.push(args=("job-1",))
105
+ await runtime.stop()
106
+
107
+
108
+ asyncio.run(main())
109
+ ```
110
+
111
+ 三段链式示例(含日志):
112
+
113
+ ```shell
114
+ poetry run python examples/demo_queue.py
115
+ ```
116
+
117
+ ### RabbitMQ
118
+
119
+ `url` 和 `queue_name` 必填。`enable_declare_queue=True` 时,队列不存在会创建。默认交换机名是 `""`,路由键默认用队列名。`push` 会等待发布确认;`publish_timeout` 是这次确认的超时。
120
+
121
+ 消息交给消费池之后就确认。处理函数出错不会把消息退回队列。`stop` 会设上停止标志,当前这次拉取结束(空闲时最多再等一个 `consume_timeout`)后退出;还没取走的消息留在 RabbitMQ 上。
122
+
123
+ 同一个 `QueueFunc` 的生产者和消费者可以分进程运行。只投递的进程把 `enable_consumer` 设为 `False`。
124
+
125
+ ```python
126
+ import asyncio
127
+
128
+ from queue_func.factory import Backend, queue
129
+
130
+
131
+ @queue(
132
+ Backend.RABBIT_AIO_PIKA,
133
+ url="amqp://guest:guest@127.0.0.1/",
134
+ queue_name="process_order",
135
+ enable_declare_queue=True,
136
+ )
137
+ async def process_order(order_id: int) -> None:
138
+ print(order_id)
139
+
140
+
141
+ async def main() -> None:
142
+ await process_order.broker.start_consumer()
143
+ await process_order.push(args=(42,))
144
+ await process_order.broker.stop_consumer()
145
+ await process_order.broker.join_consumer()
146
+ await process_order.broker.stop_producer()
147
+ await process_order.broker.join_producer()
148
+
149
+
150
+ asyncio.run(main())
151
+ ```
152
+
153
+ 本地 RabbitMQ(`127.0.0.1:5672`,`guest` / `guest`)上的链式示例:
154
+
155
+ ```shell
156
+ poetry run python examples/demo_rabbit_aio_pika.py
157
+ ```
158
+
159
+ 常用参数:
160
+
161
+ | 参数 | 作用 |
162
+ |------|------|
163
+ | `consumer_pool_maxsize` | 同时执行的协程数,默认 200 |
164
+ | `consume_timeout` | 一次拉取的等待时间,默认 2 秒 |
165
+ | `publish_timeout` | 投递等待上限,默认不限 |
166
+ | `maxsize` | 仅内存队列,`asyncio.Queue` 容量,默认 200 |
167
+ | `serializer_cls` | 队列中的数据结构 |
168
+ | `prefetch_count` | 仅 RabbitMQ,未确认消息窗口 |
169
+ | `delivery_mode` | 仅 RabbitMQ,默认持久化 |
170
+ | `exchange_name` / `routing_key` | 仅 RabbitMQ |
171
+
172
+ 其余字段见 `queue_func.config.queue.Config` 和 `queue_func.config.rabbit_aio_pika.Config`。
173
+
174
+ ## 架构
175
+
176
+ 一个 `@queue` 装饰的协程就是一条队列。`push` 投到它自己这条队列上。
177
+
178
+ - **Broker** 组装生产者、消费者和双方共用的资源,并提供启停与投递。
179
+ - **Producer** 序列化后把数据写入队列。
180
+ - **Consumer** 取出数据,反序列化后交给消费池。
181
+ - **PoolExecutor** 按并发上限执行业务协程。
182
+
183
+ ```mermaid
184
+ flowchart TB
185
+ PUSH["投递"] --> B["通道"]
186
+ B --> PRO["生产者"]
187
+ B -.->|启停| CON["消费者"]
188
+
189
+ PRO -->|序列化| Q[("队列")]
190
+ Q -->|反序列化| CON
191
+ CON --> POOL["消费池"]
192
+ POOL --> FN["业务协程"]
193
+ ```
194
+
195
+ ## 项目结构
196
+
197
+ ```
198
+ queue_func/
199
+ ├── broker/
200
+ ├── producer/
201
+ ├── consumer/
202
+ ├── serializer/ # raw、orjson
203
+ ├── shared_resource/
204
+ ├── config/
205
+ ├── factory/ # @queue、QueueFunc、QueueFuncRuntime
206
+ ├── pool_executor/
207
+ └── utils/
208
+ examples/
209
+ ├── demo_queue.py
210
+ └── demo_rabbit_aio_pika.py
211
+ ```
212
+
213
+ ## 开发与测试
214
+
215
+ ```shell
216
+ poetry install -E rabbit
217
+ pytest
218
+ ```
219
+
220
+ `pytest.ini` 会生成覆盖率页面(`htmlcov/`)和 `report.html`。
221
+
222
+ ## 版本
223
+
224
+ 遵循[语义化版本](https://semver.org/lang/zh-CN/)。变更记录在 [Changelog.rst](Changelog.rst)。
225
+
226
+ ## 许可证
227
+
228
+ Apache-2.0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "queue-func"
3
- version = "0.1.2"
3
+ version = "0.2.1"
4
4
  description = "Async coroutine functions as queue-backed tasks (QueueFunc)."
5
5
  authors = ["liuseki <liuseki@163.com>"]
6
6
  license = "Apache-2.0"
@@ -18,6 +18,10 @@ Issues = "https://github.com/sekil9529/queue-func/issues"
18
18
  python = "^3.12"
19
19
  orjson = "*"
20
20
  pydantic = "^2.14.1"
21
+ aio-pika = { version = "^9.6", optional = true }
22
+
23
+ [tool.poetry.extras]
24
+ rabbit = ["aio-pika"]
21
25
 
22
26
  [[tool.poetry.source]]
23
27
  name = "aliyun"
@@ -28,6 +32,7 @@ priority = "primary"
28
32
  pytest = "*"
29
33
  pytest-cov = "*"
30
34
  pytest-html = "*"
35
+ aio-pika = "^9.6"
31
36
 
32
37
  [tool.pytest.ini_options]
33
38
  testpaths = ["tests"]
@@ -3,7 +3,8 @@
3
3
  import abc
4
4
  import typing as t
5
5
 
6
- from queue_func.serializer.base import BaseSerializer, Message
6
+ from queue_func.serializer.base import Message
7
+ from queue_func.shared_resource.base import Resource
7
8
 
8
9
  if t.TYPE_CHECKING:
9
10
  from queue_func.config.general import Config
@@ -22,33 +23,12 @@ class BaseBroker(t.Generic[ConfigType, Message], metaclass=abc.ABCMeta):
22
23
  """
23
24
 
24
25
  __slots__ = (
25
- "_config",
26
- "_serializer",
27
26
  "_producer",
28
27
  "_consumer",
29
28
  )
30
29
 
31
- def __init__(self, config: ConfigType):
32
-
33
- self._config = config
34
- self._serializer: type[BaseSerializer[Message]] = self._config.serializer_cls
35
- self._producer = self._create_producer()
36
- self._consumer = self._create_consumer()
37
-
38
- @abc.abstractmethod
39
- def _create_producer(self) -> "BaseProducer[Message]":
40
-
41
- pass
42
-
43
- @abc.abstractmethod
44
- def _create_consumer(self) -> "BaseConsumer[Message]":
45
-
46
- pass
47
-
48
- @property
49
- def serializer(self) -> type[BaseSerializer[Message]]:
50
-
51
- return self._serializer
30
+ _producer: "BaseProducer"
31
+ _consumer: "BaseConsumer"
52
32
 
53
33
  async def publish(
54
34
  self,
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+
3
+ import asyncio
4
+ import typing as t
5
+
6
+ from queue_func.serializer.base import Message
7
+ from queue_func.shared_resource.queue import Resource
8
+ from queue_func.broker.base import BaseBroker as _BaseBroker
9
+ from queue_func.producer.queue import Producer
10
+ from queue_func.consumer.queue import Consumer
11
+
12
+ if t.TYPE_CHECKING:
13
+ from queue_func.config.queue import Config
14
+
15
+
16
+ class Broker(_BaseBroker["Config", Message]):
17
+ """内存队列通道。队列由这里创建,再交给 Producer 和 Consumer。"""
18
+
19
+ def __init__(self, config: "Config"):
20
+ resource = Resource(
21
+ serializer_cls=config.serializer_cls,
22
+ maxsize=config.maxsize)
23
+ self._producer = Producer(
24
+ resource=resource,
25
+ publish_timeout=config.publish_timeout,
26
+ )
27
+ self._consumer = Consumer(
28
+ resource=resource,
29
+ coro=config.coro,
30
+ enable_consumer=config.enable_consumer,
31
+ consume_timeout=config.consume_timeout,
32
+ pool_executor_cls=config.consumer_pool_executor_cls,
33
+ pool_maxsize=config.consumer_pool_maxsize,
34
+ )
@@ -0,0 +1,47 @@
1
+ # coding: utf-8
2
+
3
+ import typing as t
4
+
5
+ from queue_func.broker.base import BaseBroker as _BaseBroker
6
+ from queue_func.consumer.rabbit_aio_pika import Consumer
7
+ from queue_func.producer.rabbit_aio_pika import Producer
8
+ from queue_func.shared_resource.rabbit_aio_pika import Resource
9
+
10
+ if t.TYPE_CHECKING:
11
+ from queue_func.config.rabbit_aio_pika import Config
12
+
13
+
14
+ class Broker(_BaseBroker["Config", bytes]):
15
+ """RabbitMQ 通道。一条连接上分开发布和消费,再交给 Producer 和 Consumer。"""
16
+
17
+ def __init__(self, config: "Config"):
18
+
19
+ resource = Resource(
20
+ serializer_cls=config.serializer_cls,
21
+ url=config.url,
22
+ queue_name=config.queue_name,
23
+ enable_declare_queue=config.enable_declare_queue,
24
+ queue_durable=config.queue_durable,
25
+ queue_exclusive=config.queue_exclusive,
26
+ queue_passive=config.queue_passive,
27
+ queue_auto_delete=config.queue_auto_delete,
28
+ queue_arguments=config.queue_arguments,
29
+ )
30
+ self._producer = Producer(
31
+ resource=resource,
32
+ publish_timeout=config.publish_timeout,
33
+ exchange_name=config.exchange_name,
34
+ routing_key=config.routing_key,
35
+ delivery_mode=config.delivery_mode,
36
+ )
37
+ self._consumer = Consumer(
38
+ resource=resource,
39
+ prefetch_count=config.prefetch_count,
40
+ prefetch_size=config.prefetch_size,
41
+ global_qos=config.global_qos,
42
+ coro=config.coro,
43
+ enable_consumer=config.enable_consumer,
44
+ consume_timeout=config.consume_timeout,
45
+ pool_executor_cls=config.consumer_pool_executor_cls,
46
+ pool_maxsize=config.consumer_pool_maxsize,
47
+ )
@@ -11,18 +11,18 @@ from queue_func.pool_executor.base import BasePoolExecutor
11
11
  from queue_func.pool_executor.general import PoolExecutor
12
12
 
13
13
 
14
- @pyd_dataclass(frozen=True, config=ExtraIgnoreConfig)
14
+ @pyd_dataclass(frozen=True, kw_only=True, config=ExtraIgnoreConfig)
15
15
  class Config:
16
16
 
17
- # consumer
17
+ ## shared resource
18
+ serializer_cls: type[BaseSerializer] = Serializer
19
+
20
+ ## consumer
18
21
  coro: t.Callable[[...], t.Coroutine]
19
22
  enable_consumer: bool = True
20
23
  consume_timeout: float = 2.0
21
24
  consumer_pool_executor_cls: type[BasePoolExecutor] = PoolExecutor
22
25
  consumer_pool_maxsize: int = 200
23
26
 
24
- # broker
25
- serializer_cls: type[BaseSerializer] = Serializer
26
-
27
- # producer
27
+ ## producer
28
28
  publish_timeout: float | None = None
@@ -12,6 +12,6 @@ from queue_func.serializer.raw import Serializer
12
12
  @pyd_dataclass(frozen=True, config=ExtraIgnoreConfig)
13
13
  class Config(_Config):
14
14
 
15
- # broker
15
+ ## shared resource
16
16
  maxsize: int = 200
17
17
  serializer_cls: type[BaseSerializer] = Serializer