queue-func 0.1__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.
Potentially problematic release.
This version of queue-func might be problematic. Click here for more details.
- queue_func/__init__.py +1 -0
- queue_func/broker/__init__.py +1 -0
- queue_func/broker/base.py +82 -0
- queue_func/broker/queue.py +44 -0
- queue_func/config/__init__.py +1 -0
- queue_func/config/general.py +32 -0
- queue_func/config/queue.py +14 -0
- queue_func/config/utils.py +15 -0
- queue_func/consumer/__init__.py +1 -0
- queue_func/consumer/base.py +97 -0
- queue_func/consumer/queue.py +55 -0
- queue_func/factory/__init__.py +12 -0
- queue_func/factory/backend.py +13 -0
- queue_func/factory/builders/__init__.py +9 -0
- queue_func/factory/builders/queue.py +20 -0
- queue_func/factory/deco.py +93 -0
- queue_func/factory/registry.py +41 -0
- queue_func/factory/runtime.py +40 -0
- queue_func/message/__init__.py +1 -0
- queue_func/message/base.py +39 -0
- queue_func/message/general.py +30 -0
- queue_func/pool_executor/__init__.py +1 -0
- queue_func/pool_executor/base.py +31 -0
- queue_func/pool_executor/general.py +42 -0
- queue_func/pool_executor/utils.py +49 -0
- queue_func/producer/__init__.py +1 -0
- queue_func/producer/base.py +36 -0
- queue_func/producer/queue.py +30 -0
- queue_func/serializer/__init__.py +1 -0
- queue_func/serializer/base.py +38 -0
- queue_func/serializer/json_ser.py +25 -0
- queue_func/serializer/orjson_ser.py +30 -0
- queue_func/serializer/utils.py +16 -0
- queue_func/task/__init__.py +1 -0
- queue_func/task/base.py +28 -0
- queue_func/task/thread.py +26 -0
- queue_func-0.1.dist-info/METADATA +121 -0
- queue_func-0.1.dist-info/RECORD +40 -0
- queue_func-0.1.dist-info/WHEEL +4 -0
- queue_func-0.1.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
import asyncio
|
|
5
|
+
import typing as t
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BasePoolExecutor(metaclass=abc.ABCMeta):
|
|
9
|
+
|
|
10
|
+
__slots__ = (
|
|
11
|
+
"_maxsize",
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
def __init__(self, maxsize: int):
|
|
15
|
+
|
|
16
|
+
self._maxsize = maxsize
|
|
17
|
+
|
|
18
|
+
@abc.abstractmethod
|
|
19
|
+
async def submit(
|
|
20
|
+
self,
|
|
21
|
+
coro: t.Callable[..., t.Coroutine],
|
|
22
|
+
coro_args: tuple[t.Any, ...],
|
|
23
|
+
coro_kwargs: dict[str, t.Any],
|
|
24
|
+
**kwargs) -> asyncio.Future:
|
|
25
|
+
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
@abc.abstractmethod
|
|
29
|
+
async def join(self) -> None:
|
|
30
|
+
|
|
31
|
+
pass
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import typing as t
|
|
5
|
+
|
|
6
|
+
from queue_func.pool_executor.base import BasePoolExecutor
|
|
7
|
+
from queue_func.pool_executor.utils import JoinableSemaphore
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PoolExecutor(BasePoolExecutor):
|
|
11
|
+
|
|
12
|
+
__slots__ = (
|
|
13
|
+
"_sem",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
def __init__(self, maxsize: int):
|
|
17
|
+
super().__init__(maxsize)
|
|
18
|
+
self._sem = JoinableSemaphore(self._maxsize)
|
|
19
|
+
|
|
20
|
+
async def submit(
|
|
21
|
+
self,
|
|
22
|
+
coro: t.Callable[..., t.Coroutine],
|
|
23
|
+
coro_args: tuple[t.Any, ...],
|
|
24
|
+
coro_kwargs: dict[str, t.Any],
|
|
25
|
+
**kwargs) -> asyncio.Future:
|
|
26
|
+
|
|
27
|
+
await self._sem.acquire()
|
|
28
|
+
return asyncio.gather(self._consume(coro, coro_args, coro_kwargs))
|
|
29
|
+
|
|
30
|
+
async def _consume(
|
|
31
|
+
self,
|
|
32
|
+
coro: t.Callable[..., t.Coroutine],
|
|
33
|
+
coro_args: tuple[t.Any, ...],
|
|
34
|
+
coro_kwargs: dict[str, t.Any]) -> None:
|
|
35
|
+
try:
|
|
36
|
+
await coro(*coro_args, **coro_kwargs)
|
|
37
|
+
finally:
|
|
38
|
+
self._sem.release()
|
|
39
|
+
|
|
40
|
+
async def join(self) -> None:
|
|
41
|
+
|
|
42
|
+
await self._sem.join()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class JoinableSemaphore:
|
|
7
|
+
|
|
8
|
+
__slots__ = (
|
|
9
|
+
"_maxsize",
|
|
10
|
+
"_count",
|
|
11
|
+
"_sem",
|
|
12
|
+
"_event",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def __init__(self, maxsize: int):
|
|
16
|
+
|
|
17
|
+
self._maxsize = maxsize
|
|
18
|
+
self._count = 0
|
|
19
|
+
self._sem = asyncio.Semaphore(self._maxsize)
|
|
20
|
+
self._event = self._init_event()
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def _init_event() -> "asyncio.Event":
|
|
24
|
+
|
|
25
|
+
event = asyncio.Event()
|
|
26
|
+
event.set()
|
|
27
|
+
return event
|
|
28
|
+
|
|
29
|
+
async def acquire(self) -> bool:
|
|
30
|
+
|
|
31
|
+
is_ok = await self._sem.acquire()
|
|
32
|
+
if is_ok:
|
|
33
|
+
self._count += 1
|
|
34
|
+
# block until set
|
|
35
|
+
self._event.clear()
|
|
36
|
+
return is_ok
|
|
37
|
+
|
|
38
|
+
def release(self) -> None:
|
|
39
|
+
|
|
40
|
+
self._sem.release()
|
|
41
|
+
self._count = max(0, self._count - 1)
|
|
42
|
+
if self._count == 0:
|
|
43
|
+
# not block at all
|
|
44
|
+
self._event.set()
|
|
45
|
+
|
|
46
|
+
async def join(self) -> None:
|
|
47
|
+
|
|
48
|
+
while self._count > 0:
|
|
49
|
+
await self._event.wait()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# coding: utf-8
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
import typing as t
|
|
5
|
+
|
|
6
|
+
from queue_func.message.base import BaseMessage, Packed
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseProducer(t.Generic[Packed], metaclass=abc.ABCMeta):
|
|
10
|
+
|
|
11
|
+
__slots__ = (
|
|
12
|
+
"_message",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def __init__(self, message: BaseMessage[Packed]):
|
|
16
|
+
|
|
17
|
+
self._message = message
|
|
18
|
+
|
|
19
|
+
async def publish(
|
|
20
|
+
self,
|
|
21
|
+
coro_args: tuple[t.Any],
|
|
22
|
+
coro_kwargs: dict[str, t.Any] | None = None,
|
|
23
|
+
**kwargs) -> None:
|
|
24
|
+
|
|
25
|
+
message = self._message.pack(coro_args, coro_kwargs)
|
|
26
|
+
await self._publish(message, **kwargs)
|
|
27
|
+
|
|
28
|
+
@abc.abstractmethod
|
|
29
|
+
async def _publish(self, data: Packed, **kwargs) -> None:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
async def stop(self) -> None:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
async def join(self) -> None:
|
|
36
|
+
pass
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
import asyncio
|
|
3
|
+
|
|
4
|
+
from queue_func.message.base import BaseMessage, Packed
|
|
5
|
+
from queue_func.producer.base import BaseProducer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Producer(BaseProducer[Packed]):
|
|
9
|
+
|
|
10
|
+
__slots__ = (
|
|
11
|
+
"_queue",
|
|
12
|
+
"_publish_timeout",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
queue: asyncio.Queue[Packed],
|
|
18
|
+
publish_timeout: float | None,
|
|
19
|
+
message: BaseMessage[Packed]):
|
|
20
|
+
|
|
21
|
+
super().__init__(message)
|
|
22
|
+
self._queue = queue
|
|
23
|
+
self._publish_timeout = publish_timeout
|
|
24
|
+
|
|
25
|
+
async def _publish(self, data: Packed, **kwargs) -> None:
|
|
26
|
+
|
|
27
|
+
if timeout := self._publish_timeout:
|
|
28
|
+
await asyncio.wait_for(self._queue.put(data), timeout)
|
|
29
|
+
return None
|
|
30
|
+
await self._queue.put(data)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# coding: utf-8
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
import typing as t
|
|
5
|
+
|
|
6
|
+
__all__ = (
|
|
7
|
+
"BaseSerializer",
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BaseSerializer(metaclass=abc.ABCMeta):
|
|
12
|
+
|
|
13
|
+
@t.overload
|
|
14
|
+
def dumps(self, data: t.Any, return_bytes: t.Literal[True]) -> bytes: ...
|
|
15
|
+
@t.overload
|
|
16
|
+
def dumps(self, data: t.Any, return_bytes: t.Literal[False]) -> str: ...
|
|
17
|
+
@t.overload
|
|
18
|
+
def dumps(self, data: t.Any, return_bytes: bool) -> t.Union[str, bytes]: ...
|
|
19
|
+
|
|
20
|
+
def dumps(self, data: t.Any, return_bytes: bool = False) -> t.Union[str, bytes]:
|
|
21
|
+
|
|
22
|
+
if return_bytes:
|
|
23
|
+
return self._dumps_to_bytes(data)
|
|
24
|
+
return self._dumps_to_str(data)
|
|
25
|
+
|
|
26
|
+
@abc.abstractmethod
|
|
27
|
+
def _dumps_to_str(self, data: t.Any) -> str:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
@abc.abstractmethod
|
|
31
|
+
def _dumps_to_bytes(self, data: t.Any) -> bytes:
|
|
32
|
+
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
@abc.abstractmethod
|
|
36
|
+
def loads(self, data: t.Any) -> t.Any:
|
|
37
|
+
|
|
38
|
+
pass
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import typing as t
|
|
5
|
+
|
|
6
|
+
from queue_func.serializer.base import BaseSerializer
|
|
7
|
+
from queue_func.serializer.utils import default as _default
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class JSONSerializer(BaseSerializer):
|
|
11
|
+
|
|
12
|
+
def __init__(self, default: t.Optional[t.Callable[[t.Any], t.Any]] = None):
|
|
13
|
+
self._default = default or _default
|
|
14
|
+
|
|
15
|
+
def _dumps_to_str(self, data: t.Any) -> str:
|
|
16
|
+
|
|
17
|
+
return json.dumps(data, default=self._default)
|
|
18
|
+
|
|
19
|
+
def _dumps_to_bytes(self, data: t.Any) -> bytes:
|
|
20
|
+
|
|
21
|
+
return self._dumps_to_str(data).encode("utf-8")
|
|
22
|
+
|
|
23
|
+
def loads(self, data: t.Union[str, bytes]) -> t.Any:
|
|
24
|
+
|
|
25
|
+
return json.loads(data)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
import typing as t
|
|
4
|
+
|
|
5
|
+
import orjson
|
|
6
|
+
|
|
7
|
+
from queue_func.serializer.base import BaseSerializer
|
|
8
|
+
from queue_func.serializer.utils import default as _default
|
|
9
|
+
|
|
10
|
+
__all__ = (
|
|
11
|
+
"OrJSONSerializer",
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class OrJSONSerializer(BaseSerializer):
|
|
16
|
+
|
|
17
|
+
def __init__(self, default: t.Optional[t.Callable[[t.Any], t.Any]] = None):
|
|
18
|
+
self._default = default or _default
|
|
19
|
+
|
|
20
|
+
def _dumps_to_str(self, data: t.Any) -> str:
|
|
21
|
+
|
|
22
|
+
return self._dumps_to_bytes(data).decode("utf-8")
|
|
23
|
+
|
|
24
|
+
def _dumps_to_bytes(self, data: t.Any) -> bytes:
|
|
25
|
+
|
|
26
|
+
return orjson.dumps(data, default=self._default)
|
|
27
|
+
|
|
28
|
+
def loads(self, data: t.Union[str, bytes]) -> t.Any:
|
|
29
|
+
|
|
30
|
+
return orjson.loads(data)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
import typing as t
|
|
4
|
+
from decimal import Decimal
|
|
5
|
+
from datetime import datetime, date
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def default(o: t.Any) -> t.Any:
|
|
9
|
+
|
|
10
|
+
if isinstance(o, Decimal):
|
|
11
|
+
return str(o)
|
|
12
|
+
if isinstance(o, datetime):
|
|
13
|
+
return o.strftime("%Y-%m-%d %H:%M:%S")
|
|
14
|
+
if isinstance(o, date):
|
|
15
|
+
return o.strftime("%Y-%m-%d")
|
|
16
|
+
raise TypeError(f"Object of type {o.__class__.__name__} is not JSON serializable")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# coding: utf-8
|
queue_func/task/base.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
from abc import ABCMeta, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseTask(metaclass=ABCMeta):
|
|
7
|
+
|
|
8
|
+
@abstractmethod
|
|
9
|
+
def running(self) -> bool:
|
|
10
|
+
"""Return True if the task is currently executing."""
|
|
11
|
+
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def done(self) -> bool:
|
|
16
|
+
"""Return True if the task was cancelled or finished executing."""
|
|
17
|
+
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def cancel(self) -> bool:
|
|
22
|
+
"""Cancel the task if possible.
|
|
23
|
+
|
|
24
|
+
Returns True if the task was cancelled, False otherwise. A task
|
|
25
|
+
cannot be cancelled if it is running or has already completed.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
pass
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
import typing as t
|
|
4
|
+
|
|
5
|
+
from .base import BaseTask
|
|
6
|
+
|
|
7
|
+
if t.TYPE_CHECKING:
|
|
8
|
+
from concurrent.futures import Future
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ThreadTask(BaseTask):
|
|
12
|
+
|
|
13
|
+
def __init__(self, fut: "Future"):
|
|
14
|
+
self._fut = fut
|
|
15
|
+
|
|
16
|
+
def running(self) -> bool:
|
|
17
|
+
|
|
18
|
+
return self._fut.running()
|
|
19
|
+
|
|
20
|
+
def done(self) -> bool:
|
|
21
|
+
|
|
22
|
+
return self._fut.done()
|
|
23
|
+
|
|
24
|
+
def cancel(self) -> bool:
|
|
25
|
+
|
|
26
|
+
return self._fut.cancel()
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: queue-func
|
|
3
|
+
Version: 0.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
|
+
Requires-Dist: orjson
|
|
17
|
+
Requires-Dist: pydantic (>=2.14.1,<3.0.0)
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# queue-func
|
|
21
|
+
|
|
22
|
+
**把 async 协程当成队列里的任务来投递与消费**——进程内用内存队列跑通链路,并为 RabbitMQ、Kafka 等后端预留同一套 Broker / Producer / Consumer 抽象。
|
|
23
|
+
|
|
24
|
+
> 仅支持 **`async def`**,不支持普通同步函数。需要 **Python 3.12+**。
|
|
25
|
+
|
|
26
|
+
## 关键概念
|
|
27
|
+
|
|
28
|
+
1. **`@queue`**:把协程包成 `QueueFunc`,提供 `push(args=..., kwargs=...)` 与 `broker`。
|
|
29
|
+
2. **消息**:`(args, kwargs)` 经 `Message` 打包;可选 JSON / orjson 序列化。
|
|
30
|
+
3. **消费**:Consumer 从通道取消息,经池化 `await` 你的协程(`consumer_pool_maxsize` 限并发)。
|
|
31
|
+
|
|
32
|
+
运行期的 `publish` / `consume` 在 Producer / Consumer 内完成;Broker 主要负责装配与生命周期,不做「万事中转」。
|
|
33
|
+
|
|
34
|
+
## 快速开始
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
poetry install
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
单个任务:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import asyncio
|
|
44
|
+
|
|
45
|
+
from queue_func.factory import Backend, queue
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@queue(Backend.QUEUE, enable_message_serializer=False, consume_timeout=0.5, maxsize=1000)
|
|
49
|
+
async def process_order(order_id: int):
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def main():
|
|
54
|
+
await process_order.broker.start_consumer()
|
|
55
|
+
await process_order.push(args=(42,))
|
|
56
|
+
await process_order.broker.stop_consumer()
|
|
57
|
+
await process_order.broker.join_consumer()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
asyncio.run(main())
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- `push` → `broker.publish(coro_args=..., coro_kwargs=...)`
|
|
64
|
+
- 仍可直接 `await process_order(1)` 做本地调用(不经队列)
|
|
65
|
+
|
|
66
|
+
多个 `QueueFunc`(各自独立队列)用 **`QueueFuncRuntime`** 统一启停;`start()` 按注册列表**反序**启动 consumer(下游先就绪,便于链式 `push`):
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from queue_func.factory import Backend, QueueFuncRuntime, queue
|
|
70
|
+
|
|
71
|
+
# task_a 内 await task_b.push(...),task_b 内 await task_c.push(...) 等
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def main():
|
|
75
|
+
runtime = QueueFuncRuntime([task_a, task_b, task_c])
|
|
76
|
+
await runtime.start()
|
|
77
|
+
await task_a.push(args=("job-1",))
|
|
78
|
+
await runtime.stop()
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
可运行示例(a → b → c 链式投递 + 控制台日志):
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
poetry run python examples/demo_queue.py
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
需要自行组装通道时,使用 `Config` + `queue_func.broker.queue.Broker`(见 `queue_func/config/`)。
|
|
88
|
+
|
|
89
|
+
## 使用场景
|
|
90
|
+
|
|
91
|
+
| 场景 | 说明 |
|
|
92
|
+
|------|------|
|
|
93
|
+
| 同进程解耦 + 控并发 | Handler 里 `push`,后台 consumer 跑重逻辑;`maxsize` / `publish_timeout` 做背压 |
|
|
94
|
+
| 多段流水线 | 多个 `QueueFunc`,在协程里继续 `push` 到下一环;用 `QueueFuncRuntime` 管生命周期 |
|
|
95
|
+
| 先单机、后换 MQ | 打开序列化后消息已是 bytes,后续可换 Broker 实现,协程签名尽量不变 |
|
|
96
|
+
| 可测 | Producer / Consumer / `push` 副作用分开测;`tests/` 有集成示例 |
|
|
97
|
+
|
|
98
|
+
**规划中**:`Backend` 除内存队列外的 RabbitMQ / Kafka(**当前仅 `Backend.QUEUE` 可用**)。
|
|
99
|
+
|
|
100
|
+
**不太适合**:同步任务、分布式调度/结果后端/Celery 级平台、无界堆积不限流、纯 CPU 密集(应另配进程池或 worker)。
|
|
101
|
+
|
|
102
|
+
## 项目结构
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
queue_func/
|
|
106
|
+
broker/ producer/ consumer/ message/ config/
|
|
107
|
+
factory/ # Backend、queue、QueueFunc、QueueFuncRuntime
|
|
108
|
+
pool_executor/ serializer/
|
|
109
|
+
examples/demo_queue.py
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 开发与测试
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pytest tests/ -q
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## 许可证
|
|
119
|
+
|
|
120
|
+
Apache-2.0
|
|
121
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
queue_func/__init__.py,sha256=7DxiEpT1g6hMwW1hBF17OOFRJ3DW03giEu6rgvRCTHY,17
|
|
2
|
+
queue_func/broker/__init__.py,sha256=7DxiEpT1g6hMwW1hBF17OOFRJ3DW03giEu6rgvRCTHY,17
|
|
3
|
+
queue_func/broker/base.py,sha256=Y0MMZGSHfm54LOWkso6OaP5UmRD0Gs94QYa6eyY8aHk,2192
|
|
4
|
+
queue_func/broker/queue.py,sha256=yG2C_DQ5FhxTTPDw3lNI-jKLRAGEkQeSG750lzg2uoc,1353
|
|
5
|
+
queue_func/config/__init__.py,sha256=7DxiEpT1g6hMwW1hBF17OOFRJ3DW03giEu6rgvRCTHY,17
|
|
6
|
+
queue_func/config/general.py,sha256=_G-gfatX8Sfhb9vbEo-RPgOjcn_ctTgn5yDA0xdbZ2Y,1039
|
|
7
|
+
queue_func/config/queue.py,sha256=A8pTbKUaee-653eeby4jqpxdwEwg6EowzPRrHh-1fRo,337
|
|
8
|
+
queue_func/config/utils.py,sha256=DOZnwPkjMsfBVfZVkHEEK1sa-ZVbPjn9gwHFAjTn2UA,263
|
|
9
|
+
queue_func/consumer/__init__.py,sha256=7DxiEpT1g6hMwW1hBF17OOFRJ3DW03giEu6rgvRCTHY,17
|
|
10
|
+
queue_func/consumer/base.py,sha256=wuCPoB0zUgxhF3GBoKF33Zq43LJFIugzoiTRQeccNrE,2889
|
|
11
|
+
queue_func/consumer/queue.py,sha256=bDf3qYCCTnT3CFazWdz3ivvWUsvqm0UpKa9qibrqi54,1579
|
|
12
|
+
queue_func/factory/__init__.py,sha256=7sSCKz_-rx29nJuNFAZlvKro8PuruvZ2ArGcDFFKNb8,269
|
|
13
|
+
queue_func/factory/backend.py,sha256=dWfxozQsm_uTaVyCgVRL8llUzTFQLmYeJRx3fjq1rng,148
|
|
14
|
+
queue_func/factory/builders/__init__.py,sha256=149GwvM0jyWl-CmdjBb8XRH1LFibofGA_6MbxuaE1Mc,187
|
|
15
|
+
queue_func/factory/builders/queue.py,sha256=Yxa8ySiqZurFrCSl5m9OBS6yKv6MF-joyTepkYbDdio,534
|
|
16
|
+
queue_func/factory/deco.py,sha256=cD_aQssAGgvOgC9ioJNTLBT_JaHTnYYnKgNAXdJc100,2834
|
|
17
|
+
queue_func/factory/registry.py,sha256=RN8PgcfkpM1pDFCYqOab-AYq6379IVEyAurtkgtDHwM,1027
|
|
18
|
+
queue_func/factory/runtime.py,sha256=A5CUSVRi4YtlRnroOc-FVeWEbPgEGQ8A-gR49ZgRz8c,958
|
|
19
|
+
queue_func/message/__init__.py,sha256=7DxiEpT1g6hMwW1hBF17OOFRJ3DW03giEu6rgvRCTHY,17
|
|
20
|
+
queue_func/message/base.py,sha256=cVQwFDKWpX5v7MVYzPYko5LPHg9txsJ2vBqkHtjwyNo,877
|
|
21
|
+
queue_func/message/general.py,sha256=GU9Enr1UMyM1cFV4yIdM0qVTFPlG3260AWIMxkfnC1U,842
|
|
22
|
+
queue_func/pool_executor/__init__.py,sha256=7DxiEpT1g6hMwW1hBF17OOFRJ3DW03giEu6rgvRCTHY,17
|
|
23
|
+
queue_func/pool_executor/base.py,sha256=reYgENOa9bRMBKfzvlPP_aTMPSXFCZaNStn6vPwnWak,585
|
|
24
|
+
queue_func/pool_executor/general.py,sha256=MzYZUKNom-xcoop_BBJpma2LPuV3pnnpyWKno5KWa-s,1110
|
|
25
|
+
queue_func/pool_executor/utils.py,sha256=FQR6nE2IVPk_rgfEzQEh6ZeelTc3iqeJegKZxN7EM0c,1042
|
|
26
|
+
queue_func/producer/__init__.py,sha256=7DxiEpT1g6hMwW1hBF17OOFRJ3DW03giEu6rgvRCTHY,17
|
|
27
|
+
queue_func/producer/base.py,sha256=oVJvFTz1nCGGSEl76Xj-_Z03_pxIZSveU2Zstk1pH-E,809
|
|
28
|
+
queue_func/producer/queue.py,sha256=dlrTh88j0344PG5vhhobyKLrTG825H5dSZI3DW5HOfc,791
|
|
29
|
+
queue_func/serializer/__init__.py,sha256=7DxiEpT1g6hMwW1hBF17OOFRJ3DW03giEu6rgvRCTHY,17
|
|
30
|
+
queue_func/serializer/base.py,sha256=yBIVEZ3XImKOuFAqnlu5trrbV9pDvTC_2nF7tkBHawI,913
|
|
31
|
+
queue_func/serializer/json_ser.py,sha256=S3HTxWDLSuyKi5g9SBLigNxUCpbmGyJwBqmOYqIcVMM,661
|
|
32
|
+
queue_func/serializer/orjson_ser.py,sha256=NAYd74TCjg8lPhbcCrNNO_qVHEPt89SJqgR3j0Lk7xI,716
|
|
33
|
+
queue_func/serializer/utils.py,sha256=ozUCUw6-8eMqYqljFcykoEb-NaLAEyBj3dj0YcAnuyY,436
|
|
34
|
+
queue_func/task/__init__.py,sha256=7DxiEpT1g6hMwW1hBF17OOFRJ3DW03giEu6rgvRCTHY,17
|
|
35
|
+
queue_func/task/base.py,sha256=JblRVlT5go9ztFXsv0-e25U7Lz27yUIOHe4oszCNBHA,649
|
|
36
|
+
queue_func/task/thread.py,sha256=OIGG0H_z_pDWYHvjSCX-PyvGovuob8wJaaUaUg6PD1M,443
|
|
37
|
+
queue_func-0.1.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
38
|
+
queue_func-0.1.dist-info/METADATA,sha256=xnUtrdkPN84tLNrQjRKxELL6O1C2KhIvscLIcwvWuKk,3876
|
|
39
|
+
queue_func-0.1.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
|
|
40
|
+
queue_func-0.1.dist-info/RECORD,,
|