queue-func 0.1__py3-none-any.whl → 0.2.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.
Files changed (39) hide show
  1. queue_func/__init__.py +6 -0
  2. queue_func/broker/base.py +9 -31
  3. queue_func/broker/queue.py +16 -26
  4. queue_func/broker/rabbit_aio_pika.py +47 -0
  5. queue_func/config/general.py +7 -11
  6. queue_func/config/queue.py +4 -1
  7. queue_func/config/rabbit_aio_pika.py +55 -0
  8. queue_func/consumer/base.py +38 -22
  9. queue_func/consumer/queue.py +12 -35
  10. queue_func/consumer/rabbit_aio_pika.py +99 -0
  11. queue_func/errors.py +7 -0
  12. queue_func/factory/backend.py +1 -0
  13. queue_func/factory/builders/__init__.py +2 -0
  14. queue_func/factory/builders/rabbit_aio_pika.py +26 -0
  15. queue_func/factory/deco.py +34 -6
  16. queue_func/producer/base.py +44 -11
  17. queue_func/producer/queue.py +12 -21
  18. queue_func/producer/rabbit_aio_pika.py +83 -0
  19. queue_func/serializer/base.py +10 -21
  20. queue_func/serializer/orjson_ser.py +14 -17
  21. queue_func/serializer/raw.py +24 -0
  22. queue_func/shared_resource/base.py +25 -0
  23. queue_func/shared_resource/queue.py +23 -0
  24. queue_func/shared_resource/rabbit_aio_pika.py +111 -0
  25. queue_func/utils/__init__.py +1 -0
  26. queue_func/utils/deco.py +36 -0
  27. queue_func/utils/hook.py +50 -0
  28. queue_func/utils/waiter.py +35 -0
  29. queue_func-0.2.1.dist-info/METADATA +255 -0
  30. queue_func-0.2.1.dist-info/RECORD +50 -0
  31. queue_func/message/base.py +0 -39
  32. queue_func/message/general.py +0 -30
  33. queue_func/serializer/json_ser.py +0 -25
  34. queue_func/serializer/utils.py +0 -16
  35. queue_func-0.1.dist-info/METADATA +0 -121
  36. queue_func-0.1.dist-info/RECORD +0 -40
  37. /queue_func/{message → shared_resource}/__init__.py +0 -0
  38. {queue_func-0.1.dist-info → queue_func-0.2.1.dist-info}/WHEEL +0 -0
  39. {queue_func-0.1.dist-info → queue_func-0.2.1.dist-info}/licenses/LICENSE +0 -0
queue_func/__init__.py CHANGED
@@ -1 +1,7 @@
1
1
  # coding: utf-8
2
+
3
+ from queue_func.errors import PublishTimeoutError
4
+
5
+ __all__ = (
6
+ "PublishTimeoutError",
7
+ )
queue_func/broker/base.py CHANGED
@@ -3,7 +3,8 @@
3
3
  import abc
4
4
  import typing as t
5
5
 
6
- from queue_func.message.base import BaseMessage, Packed
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
@@ -14,52 +15,29 @@ if t.TYPE_CHECKING:
14
15
  ConfigType = t.TypeVar("ConfigType", bound="Config")
15
16
 
16
17
 
17
- class BaseBroker(t.Generic[ConfigType, Packed], metaclass=abc.ABCMeta):
18
+ class BaseBroker(t.Generic[ConfigType, Message], metaclass=abc.ABCMeta):
18
19
  """一条通道的装配者。
19
20
 
20
- 创建时准备好双方共用的 Message,并持有配置与 Producer/Consumer。
21
+ 创建时准备好双方共用的 Serializer,并持有配置与 Producer/Consumer。
21
22
  对外通过 publish、start_consumer 等入口操作;运行期业务逻辑仍在 Producer/Consumer 内完成。
22
23
  """
23
24
 
24
25
  __slots__ = (
25
- "_config",
26
- "_message",
27
26
  "_producer",
28
27
  "_consumer",
29
28
  )
30
29
 
31
- def __init__(self, config: ConfigType):
32
-
33
- self._config = config
34
- self._message: BaseMessage[Packed] = self._config.message_cls(
35
- message_serializer_cls=self._config.message_serializer_cls,
36
- enable_serializer=self._config.enable_message_serializer,
37
- )
38
- self._producer = self._create_producer()
39
- self._consumer = self._create_consumer()
40
-
41
- @abc.abstractmethod
42
- def _create_producer(self) -> "BaseProducer[Packed]":
43
-
44
- pass
45
-
46
- @abc.abstractmethod
47
- def _create_consumer(self) -> "BaseConsumer[Packed]":
48
-
49
- pass
50
-
51
- @property
52
- def message(self) -> BaseMessage[Packed]:
53
-
54
- return self._message
30
+ _producer: "BaseProducer"
31
+ _consumer: "BaseConsumer"
55
32
 
56
33
  async def publish(
57
34
  self,
58
35
  coro_args: tuple[t.Any, ...],
59
36
  coro_kwargs: dict[str, t.Any] | None = None,
60
- **kwargs: t.Any) -> None:
37
+ *,
38
+ timeout: float | None = None) -> None:
61
39
 
62
- await self._producer.publish(coro_args, coro_kwargs, **kwargs)
40
+ await self._producer.publish(coro_args, coro_kwargs, timeout=timeout)
63
41
 
64
42
  async def start_consumer(self) -> None:
65
43
 
@@ -3,7 +3,8 @@
3
3
  import asyncio
4
4
  import typing as t
5
5
 
6
- from queue_func.message.base import Packed
6
+ from queue_func.serializer.base import Message
7
+ from queue_func.shared_resource.queue import Resource
7
8
  from queue_func.broker.base import BaseBroker as _BaseBroker
8
9
  from queue_func.producer.queue import Producer
9
10
  from queue_func.consumer.queue import Consumer
@@ -12,33 +13,22 @@ if t.TYPE_CHECKING:
12
13
  from queue_func.config.queue import Config
13
14
 
14
15
 
15
- class Broker(_BaseBroker["Config", Packed]):
16
+ class Broker(_BaseBroker["Config", Message]):
16
17
  """内存队列通道。队列由这里创建,再交给 Producer 和 Consumer。"""
17
18
 
18
- __slots__ = (
19
- "_queue",
20
- )
21
-
22
19
  def __init__(self, config: "Config"):
23
- self._queue: asyncio.Queue[Packed] = asyncio.Queue(maxsize=config.maxsize)
24
- super().__init__(config)
25
-
26
- def _create_producer(self) -> Producer[Packed]:
27
-
28
- return Producer(
29
- queue=self._queue,
30
- publish_timeout=self._config.publish_timeout,
31
- message=self._message,
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,
32
26
  )
33
-
34
- def _create_consumer(self) -> Consumer[Packed]:
35
-
36
- return Consumer(
37
- queue=self._queue,
38
- consume_timeout=self._config.consume_timeout,
39
- message=self._message,
40
- coro=self._config.coro,
41
- enable_consumer=self._config.enable_consumer,
42
- pool_executor_cls=self._config.consumer_pool_executor_cls,
43
- pool_maxsize=self._config.consumer_pool_maxsize,
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,
44
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
+ )
@@ -5,28 +5,24 @@ import typing as t
5
5
  from pydantic.dataclasses import dataclass as pyd_dataclass
6
6
 
7
7
  from queue_func.config.utils import ExtraIgnoreConfig
8
- from queue_func.message.base import BaseMessage
9
- from queue_func.message.general import Message
10
8
  from queue_func.serializer.base import BaseSerializer
11
- from queue_func.serializer.orjson_ser import OrJSONSerializer
9
+ from queue_func.serializer.orjson_ser import Serializer
12
10
  from queue_func.pool_executor.base import BasePoolExecutor
13
11
  from queue_func.pool_executor.general import PoolExecutor
14
12
 
15
13
 
16
- @pyd_dataclass(frozen=True, config=ExtraIgnoreConfig)
14
+ @pyd_dataclass(frozen=True, kw_only=True, config=ExtraIgnoreConfig)
17
15
  class Config:
18
16
 
19
- # consumer
17
+ ## shared resource
18
+ serializer_cls: type[BaseSerializer] = Serializer
19
+
20
+ ## consumer
20
21
  coro: t.Callable[[...], t.Coroutine]
21
22
  enable_consumer: bool = True
22
23
  consume_timeout: float = 2.0
23
24
  consumer_pool_executor_cls: type[BasePoolExecutor] = PoolExecutor
24
25
  consumer_pool_maxsize: int = 200
25
26
 
26
- # broker
27
- message_cls: type[BaseMessage] = Message
28
- message_serializer_cls: type[BaseSerializer] = OrJSONSerializer
29
- enable_message_serializer: bool = True
30
-
31
- # producer
27
+ ## producer
32
28
  publish_timeout: float | None = None
@@ -5,10 +5,13 @@ from pydantic.dataclasses import dataclass as pyd_dataclass
5
5
 
6
6
  from queue_func.config.utils import ExtraIgnoreConfig
7
7
  from queue_func.config.general import Config as _Config
8
+ from queue_func.serializer.base import BaseSerializer
9
+ from queue_func.serializer.raw import Serializer
8
10
 
9
11
 
10
12
  @pyd_dataclass(frozen=True, config=ExtraIgnoreConfig)
11
13
  class Config(_Config):
12
14
 
13
- # broker
15
+ ## shared resource
14
16
  maxsize: int = 200
17
+ serializer_cls: type[BaseSerializer] = Serializer
@@ -0,0 +1,55 @@
1
+ # coding: utf-8
2
+
3
+ from enum import unique, StrEnum, IntEnum
4
+
5
+ from pydantic.dataclasses import dataclass as pyd_dataclass
6
+
7
+ from queue_func.config.utils import ExtraIgnoreConfig
8
+ from queue_func.config.general import Config as _Config
9
+
10
+
11
+ @unique
12
+ class ExchangeName(StrEnum):
13
+
14
+ DEFAULT = ""
15
+ DIRECT = "amq.direct"
16
+ TOPIC = "amq.topic"
17
+ FANOUT = "amq.fanout"
18
+ HEADERS = "amq.headers"
19
+
20
+
21
+ @unique
22
+ class DeliveryMode(IntEnum):
23
+
24
+ NOT_PERSISTENT = 1
25
+ PERSISTENT = 2
26
+
27
+
28
+ @pyd_dataclass(frozen=True, config=ExtraIgnoreConfig, kw_only=True)
29
+ class Config(_Config):
30
+
31
+ ## shared resource
32
+ # amqp://user:password@host/vhost?heartbeat=60&x=y
33
+ url: str
34
+ queue_name: str
35
+ enable_declare_queue: bool = False
36
+ # broker 重启后队列仍在;消息是否保留看 delivery_mode
37
+ queue_durable: bool = False
38
+ # 仅当前连接可使用,连接断开后队列删除
39
+ queue_exclusive: bool = False
40
+ # 不创建队列,只确认同名队列已存在,不存在则失败
41
+ queue_passive: bool = False
42
+ # 最后一个消费者取消或断开后删除队列
43
+ queue_auto_delete: bool = False
44
+ # 声明时的附加参数,例如 x-message-ttl、x-dead-letter-exchange
45
+ queue_arguments: dict | None = None
46
+
47
+ ## producer
48
+ exchange_name: ExchangeName | str = ExchangeName.DEFAULT
49
+ routing_key: str = ""
50
+ delivery_mode: DeliveryMode = DeliveryMode.PERSISTENT
51
+
52
+ ## consumer
53
+ prefetch_count: int = 0
54
+ prefetch_size: int = 0
55
+ global_qos: bool = False
@@ -7,36 +7,43 @@ import contextlib
7
7
  import typing as t
8
8
  from functools import wraps
9
9
 
10
- from queue_func.message.base import BaseMessage, Packed
10
+ from queue_func.serializer.base import Message
11
+ from queue_func.shared_resource.base import Resource
11
12
  from queue_func.pool_executor.base import BasePoolExecutor
13
+ from queue_func.utils.hook import OnceHooks
12
14
 
13
15
 
14
- class BaseConsumer(t.Generic[Packed], metaclass=abc.ABCMeta):
16
+ class BaseConsumer(t.Generic[Message, Resource], metaclass=abc.ABCMeta):
15
17
 
16
18
  __slots__ = (
17
- "_message",
19
+ "_resource",
18
20
  "_coro",
19
21
  "_enable_consumer",
20
22
  "_pool_executor",
21
- "_task",
22
- "_stop_flag",
23
+ "_consume_timeout",
24
+ "_startup",
25
+ "_shutdown",
26
+ "_stopped",
23
27
  "_logger",
24
28
  )
25
29
 
26
30
  def __init__(
27
31
  self,
28
- message: BaseMessage[Packed],
32
+ resource: Resource,
29
33
  coro: t.Callable[..., t.Coroutine],
30
34
  enable_consumer: bool,
35
+ consume_timeout: float,
31
36
  pool_executor_cls: type[BasePoolExecutor],
32
37
  pool_maxsize: int):
33
38
 
34
- self._message = message
39
+ self._resource = resource
35
40
  self._coro = coro
36
41
  self._enable_consumer = enable_consumer
37
42
  self._pool_executor = self._init_pool_executor(pool_executor_cls, pool_maxsize)
38
- self._task: asyncio.Task | None = None
39
- self._stop_flag = False
43
+ self._consume_timeout = consume_timeout
44
+ self._stopped = False
45
+ self._startup = OnceHooks()
46
+ self._shutdown = OnceHooks()
40
47
  self._logger = logging.getLogger(__name__)
41
48
 
42
49
  def _init_pool_executor(
@@ -50,15 +57,15 @@ class BaseConsumer(t.Generic[Packed], metaclass=abc.ABCMeta):
50
57
 
51
58
  def _run_until_stopped(
52
59
  self,
53
- coro: t.Callable[..., t.Awaitable],
54
- delay: float = 0.02) -> t.Callable[..., t.Coroutine]:
60
+ fn: t.Callable[..., t.Awaitable],
61
+ delay: float = 0.05) -> t.Callable[..., t.Coroutine]:
55
62
 
56
- @wraps(coro)
63
+ @wraps(fn)
57
64
  async def wrapped(*args, **kwargs) -> None:
58
65
 
59
- while not self._stop_flag:
66
+ while not self._stopped:
60
67
  try:
61
- await coro(*args, **kwargs)
68
+ await fn(*args, **kwargs)
62
69
  except Exception as e:
63
70
  self._logger.exception(e)
64
71
  await asyncio.sleep(delay)
@@ -73,10 +80,10 @@ class BaseConsumer(t.Generic[Packed], metaclass=abc.ABCMeta):
73
80
  async def consume(self) -> None:
74
81
 
75
82
  if self._enable_consumer:
76
- self._stop_flag = False
77
- consume_loop = self._run_until_stopped(self._consume)
78
- self._task = asyncio.create_task(consume_loop())
79
- return None
83
+ self._stopped = False
84
+ self._startup.register(self._run_until_stopped(self._consume))
85
+ self._register_shutdown()
86
+ await self._startup.start()
80
87
 
81
88
  @abc.abstractmethod
82
89
  async def _consume(self) -> None: # pragma: no cover
@@ -85,13 +92,22 @@ class BaseConsumer(t.Generic[Packed], metaclass=abc.ABCMeta):
85
92
 
86
93
  async def stop(self) -> None:
87
94
 
88
- self._stop_flag = True
95
+ await self._shutdown.start(prepare=self._mark_stopped)
96
+
97
+ def _mark_stopped(self) -> None:
98
+
99
+ self._stopped = True
89
100
 
90
101
  async def join(self) -> None:
91
102
 
92
- if self._task is not None:
93
- with contextlib.suppress(asyncio.CancelledError):
94
- await self._task
103
+ await self._shutdown.wait()
104
+
105
+ async def _join_pool_executor(self):
95
106
 
96
107
  if self._pool_executor is not None:
97
108
  await self._pool_executor.join()
109
+
110
+ def _register_shutdown(self) -> None:
111
+
112
+ self._shutdown.register(self._startup.wait)
113
+ self._shutdown.register(self._join_pool_executor)
@@ -3,53 +3,30 @@
3
3
  import asyncio
4
4
  import typing as t
5
5
 
6
- from queue_func.message.base import BaseMessage, Packed
6
+ from queue_func.serializer.base import Message
7
+ from queue_func.shared_resource.queue import Resource
7
8
  from queue_func.pool_executor.base import BasePoolExecutor
8
9
  from queue_func.consumer.base import BaseConsumer as _BaseConsumer
9
10
 
10
11
 
11
- class Consumer(_BaseConsumer[Packed]):
12
-
13
- __slots__ = (
14
- "_queue",
15
- "_consume_timeout",
16
- )
17
-
18
- def __init__(
19
- self,
20
- queue: asyncio.Queue[Packed],
21
- consume_timeout: float,
22
- message: BaseMessage[Packed],
23
- coro: t.Callable[..., t.Coroutine],
24
- enable_consumer: bool,
25
- pool_executor_cls: type[BasePoolExecutor],
26
- pool_maxsize: int):
27
-
28
- super().__init__(
29
- message,
30
- coro,
31
- enable_consumer,
32
- pool_executor_cls,
33
- pool_maxsize,
34
- )
35
- self._queue = queue
36
- self._consume_timeout = consume_timeout
12
+ class Consumer(_BaseConsumer[Message, Resource]):
37
13
 
38
14
  async def _consume(self) -> None:
39
15
 
40
- while (not self._stop_flag) or (not self._queue.empty()):
16
+ while (not self._stopped) or (not self._resource.queue.empty()):
41
17
  try:
42
- message: Packed = await asyncio.wait_for(
43
- self._queue.get(),
18
+ message: Message = await asyncio.wait_for(
19
+ self._resource.queue.get(),
44
20
  timeout=self._consume_timeout,
45
21
  )
46
22
  except asyncio.TimeoutError:
47
23
  continue
48
- coro_args, coro_kwargs = self._message.unpack(message)
24
+ coro_args, coro_kwargs = self._resource.serializer.unpack(message)
49
25
  await self._dispatch(coro_args, coro_kwargs)
50
- self._queue.task_done()
26
+ self._resource.queue.task_done()
51
27
 
52
- async def join(self) -> None:
28
+ def _register_shutdown(self) -> None:
53
29
 
54
- await super().join()
55
- await self._queue.join()
30
+ self._shutdown.register(self._startup.wait)
31
+ self._shutdown.register(self._resource.queue.join)
32
+ self._shutdown.register(self._join_pool_executor)
@@ -0,0 +1,99 @@
1
+ # coding: utf-8
2
+
3
+ import asyncio
4
+ import typing as t
5
+
6
+ from queue_func.consumer.base import BaseConsumer as _BaseConsumer
7
+ from queue_func.pool_executor.base import BasePoolExecutor
8
+ from queue_func.shared_resource.rabbit_aio_pika import Resource
9
+
10
+ if t.TYPE_CHECKING:
11
+ from aio_pika.abc import AbstractIncomingMessage, AbstractQueue
12
+
13
+
14
+ class Consumer(_BaseConsumer[bytes, Resource]):
15
+
16
+ __slots__ = (
17
+ "_prefetch_count",
18
+ "_prefetch_size",
19
+ "_global_qos",
20
+ "_channel",
21
+ "_queue",
22
+ "_iterator",
23
+ "_setup_lock",
24
+ )
25
+
26
+ def __init__(
27
+ self,
28
+ resource: Resource,
29
+ prefetch_count: int,
30
+ prefetch_size: int,
31
+ global_qos: bool,
32
+ coro: t.Callable[..., t.Coroutine],
33
+ enable_consumer: bool,
34
+ consume_timeout: float,
35
+ pool_executor_cls: type[BasePoolExecutor],
36
+ pool_maxsize: int):
37
+
38
+ super().__init__(
39
+ resource=resource,
40
+ coro=coro,
41
+ enable_consumer=enable_consumer,
42
+ consume_timeout=consume_timeout,
43
+ pool_executor_cls=pool_executor_cls,
44
+ pool_maxsize=pool_maxsize,
45
+ )
46
+ self._prefetch_count = prefetch_count
47
+ self._prefetch_size = prefetch_size
48
+ self._global_qos = global_qos
49
+ self._channel: "AbstractChannel | None" = None
50
+ self._queue: "AbstractQueue | None" = None
51
+ self._setup_lock = asyncio.Lock()
52
+
53
+ async def _consume(self) -> None:
54
+
55
+ await self._ensure_queue()
56
+ while not self._stopped:
57
+ async with self._queue.iterator(timeout=self._consume_timeout) as queue_iter:
58
+ try:
59
+ message: "AbstractIncomingMessage"
60
+ async for message in queue_iter:
61
+ body: bytes = message.body
62
+ coro_args, coro_kwargs = self._resource.serializer.unpack(body)
63
+ await self._dispatch(coro_args, coro_kwargs)
64
+ await message.ack()
65
+ except asyncio.TimeoutError:
66
+ continue
67
+
68
+ async def _ensure_queue(self) -> None:
69
+
70
+ if self._queue is not None:
71
+ return None
72
+
73
+ async with self._setup_lock:
74
+ if self._queue is not None:
75
+ return None
76
+ channel = await self._resource.get_channel()
77
+ await channel.set_qos(
78
+ prefetch_count=self._prefetch_count,
79
+ prefetch_size=self._prefetch_size,
80
+ global_=self._global_qos,
81
+ )
82
+ queue = await self._resource.declare_queue(channel)
83
+ self._channel, self._queue = channel, queue
84
+
85
+ async def _release(self) -> None:
86
+
87
+ channel = self._channel
88
+ if channel is None:
89
+ return None
90
+ self._channel = None
91
+ self._queue = None
92
+ await self._resource.close_channel(channel)
93
+ await self._resource.try_close_connection()
94
+
95
+ def _register_shutdown(self) -> None:
96
+
97
+ self._shutdown.register(self._startup.wait)
98
+ self._shutdown.register(self._release)
99
+ self._shutdown.register(self._join_pool_executor)
queue_func/errors.py ADDED
@@ -0,0 +1,7 @@
1
+ # coding: utf-8
2
+
3
+
4
+ class PublishTimeoutError(TimeoutError):
5
+ """入队在给定时间内未完成"""
6
+
7
+ pass
@@ -11,3 +11,4 @@ __all__ = (
11
11
  class Backend(StrEnum):
12
12
 
13
13
  QUEUE = "queue"
14
+ RABBIT_AIO_PIKA = "rabbit_aio_pika"
@@ -3,7 +3,9 @@
3
3
  """Import builder modules so they register with :func:`register_backend`."""
4
4
 
5
5
  from queue_func.factory.builders import queue as queue
6
+ from queue_func.factory.builders import rabbit_aio_pika as rabbit_aio_pika
6
7
 
7
8
  __all__ = (
8
9
  "queue",
10
+ "rabbit_aio_pika",
9
11
  )
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+
3
+ import typing as t
4
+
5
+ from queue_func.factory.backend import Backend
6
+ from queue_func.factory.registry import register_backend
7
+
8
+ if t.TYPE_CHECKING:
9
+ from queue_func.broker.base import BaseBroker
10
+
11
+
12
+ @register_backend(Backend.RABBIT_AIO_PIKA)
13
+ def build_rabbit_aio_pika_broker(
14
+ coro: t.Callable[..., t.Coroutine],
15
+ **kwargs: t.Any) -> "BaseBroker[t.Any, t.Any]":
16
+
17
+ try:
18
+ from queue_func.broker.rabbit_aio_pika import Broker
19
+ from queue_func.config.rabbit_aio_pika import Config
20
+ except ImportError as exc:
21
+ raise ImportError(
22
+ "Backend.RABBIT_AIO_PIKA requires aio-pika. "
23
+ "Install it with the rabbit extra.",
24
+ ) from exc
25
+
26
+ return Broker(Config(coro=coro, **kwargs))