taraqueue 0.0.2.dev0__tar.gz → 0.2.0__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.
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/.github/actions/setup-uv-env/action.yml +5 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/.github/workflows/publish.yml +5 -4
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/.github/workflows/test.yml +1 -1
- taraqueue-0.2.0/CHANGES.rst +13 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/PKG-INFO +1 -2
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/docs/taraqueue.rst +18 -2
- taraqueue-0.2.0/docs/taraqueue.testing.rst +37 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/pyproject.toml +3 -3
- taraqueue-0.2.0/taraqueue/__init__.py +51 -0
- taraqueue-0.2.0/taraqueue/memory.py +50 -0
- taraqueue-0.2.0/taraqueue/redis.py +67 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/taraqueue/testing/compose.py +2 -2
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/taraqueue/testing/queue.py +1 -1
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/tests/test_queue.py +1 -1
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/uv.lock +0 -2
- taraqueue-0.0.2.dev0/CHANGES.rst +0 -6
- taraqueue-0.0.2.dev0/taraqueue/queue.py +0 -149
- taraqueue-0.0.2.dev0/taraqueue/testing/__init__.py +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/.editorconfig +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/.gitattributes +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/.github/CODEOWNERS +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/.github/renovate.json +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/.github/workflows/renovate.yaml +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/.gitignore +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/.vscode/extensions.json +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/.vscode/launch.json +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/.vscode/settings.json +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/CONTRIBUTING.rst +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/LICENSE.rst +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/Makefile +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/README.rst +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/STYLE_GUIDE.rst +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/compose.yml +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/docs/changes.rst +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/docs/conf.py +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/docs/contributing.rst +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/docs/index.rst +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/docs/license.rst +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/docs/modules.rst +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/docs/style_guide.rst +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/taraqueue/registry.py +0 -0
- {taraqueue-0.0.2.dev0/taraqueue → taraqueue-0.2.0/taraqueue/testing}/__init__.py +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/taraqueue/testing/services.py +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/tests/test_compose.py +0 -0
- {taraqueue-0.0.2.dev0 → taraqueue-0.2.0}/tests/test_registry.py +0 -0
|
@@ -14,6 +14,10 @@ inputs:
|
|
|
14
14
|
required: false
|
|
15
15
|
description: "Include optional dependencies"
|
|
16
16
|
default: ""
|
|
17
|
+
sync:
|
|
18
|
+
required: false
|
|
19
|
+
description: "Run uv sync"
|
|
20
|
+
default: "true"
|
|
17
21
|
working-directory:
|
|
18
22
|
required: false
|
|
19
23
|
description: "Working directory"
|
|
@@ -36,6 +40,7 @@ runs:
|
|
|
36
40
|
enable-cache: true
|
|
37
41
|
|
|
38
42
|
- name: Install dependencies
|
|
43
|
+
if: ${{ inputs.sync == 'true' }}
|
|
39
44
|
shell: bash
|
|
40
45
|
working-directory: ${{ inputs.working-directory }}
|
|
41
46
|
run: |
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
name: Publish
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
workflow_dispatch:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
8
7
|
|
|
9
8
|
permissions:
|
|
10
9
|
contents: write
|
|
@@ -21,6 +20,8 @@ jobs:
|
|
|
21
20
|
|
|
22
21
|
- name: Set up the environment
|
|
23
22
|
uses: ./.github/actions/setup-uv-env
|
|
23
|
+
with:
|
|
24
|
+
sync: "false"
|
|
24
25
|
|
|
25
26
|
- name: Build
|
|
26
27
|
run: uv build
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: taraqueue
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python queue abstraction layer
|
|
5
5
|
Project-URL: Repository, https://github.com/taradix/taraqueue
|
|
6
6
|
Author-email: Marc Tardif <marc@taram.ca>
|
|
7
7
|
License-File: LICENSE.rst
|
|
8
8
|
Requires-Python: <4.0,>=3.12
|
|
9
|
-
Requires-Dist: attrs>=26.1.0
|
|
10
9
|
Requires-Dist: yarl>=1.23.0
|
|
11
10
|
Provides-Extra: check
|
|
12
11
|
Requires-Dist: ruff<1.0.0,>=0.15.0; extra == 'check'
|
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
taraqueue package
|
|
2
2
|
=================
|
|
3
3
|
|
|
4
|
+
Subpackages
|
|
5
|
+
-----------
|
|
6
|
+
|
|
7
|
+
.. toctree::
|
|
8
|
+
:maxdepth: 4
|
|
9
|
+
|
|
10
|
+
taraqueue.testing
|
|
11
|
+
|
|
4
12
|
Submodules
|
|
5
13
|
----------
|
|
6
14
|
|
|
7
|
-
taraqueue.
|
|
15
|
+
taraqueue.memory module
|
|
16
|
+
-----------------------
|
|
17
|
+
|
|
18
|
+
.. automodule:: taraqueue.memory
|
|
19
|
+
:members:
|
|
20
|
+
:show-inheritance:
|
|
21
|
+
:undoc-members:
|
|
22
|
+
|
|
23
|
+
taraqueue.redis module
|
|
8
24
|
----------------------
|
|
9
25
|
|
|
10
|
-
.. automodule:: taraqueue.
|
|
26
|
+
.. automodule:: taraqueue.redis
|
|
11
27
|
:members:
|
|
12
28
|
:show-inheritance:
|
|
13
29
|
:undoc-members:
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
taraqueue.testing package
|
|
2
|
+
=========================
|
|
3
|
+
|
|
4
|
+
Submodules
|
|
5
|
+
----------
|
|
6
|
+
|
|
7
|
+
taraqueue.testing.compose module
|
|
8
|
+
--------------------------------
|
|
9
|
+
|
|
10
|
+
.. automodule:: taraqueue.testing.compose
|
|
11
|
+
:members:
|
|
12
|
+
:show-inheritance:
|
|
13
|
+
:undoc-members:
|
|
14
|
+
|
|
15
|
+
taraqueue.testing.queue module
|
|
16
|
+
------------------------------
|
|
17
|
+
|
|
18
|
+
.. automodule:: taraqueue.testing.queue
|
|
19
|
+
:members:
|
|
20
|
+
:show-inheritance:
|
|
21
|
+
:undoc-members:
|
|
22
|
+
|
|
23
|
+
taraqueue.testing.services module
|
|
24
|
+
---------------------------------
|
|
25
|
+
|
|
26
|
+
.. automodule:: taraqueue.testing.services
|
|
27
|
+
:members:
|
|
28
|
+
:show-inheritance:
|
|
29
|
+
:undoc-members:
|
|
30
|
+
|
|
31
|
+
Module contents
|
|
32
|
+
---------------
|
|
33
|
+
|
|
34
|
+
.. automodule:: taraqueue.testing
|
|
35
|
+
:members:
|
|
36
|
+
:show-inheritance:
|
|
37
|
+
:undoc-members:
|
|
@@ -8,7 +8,6 @@ authors = [
|
|
|
8
8
|
readme = "README.rst"
|
|
9
9
|
requires-python = ">=3.12,<4.0"
|
|
10
10
|
dependencies = [
|
|
11
|
-
"attrs>=26.1.0",
|
|
12
11
|
"yarl>=1.23.0",
|
|
13
12
|
]
|
|
14
13
|
|
|
@@ -46,8 +45,8 @@ taraqueue-services = "taraqueue.testing.services"
|
|
|
46
45
|
taraqueue-queue = "taraqueue.testing.queue"
|
|
47
46
|
|
|
48
47
|
[project.entry-points."taraqueue"]
|
|
49
|
-
memory = "taraqueue.
|
|
50
|
-
redis = "taraqueue.
|
|
48
|
+
memory = "taraqueue.memory:MemoryQueue"
|
|
49
|
+
redis = "taraqueue.redis:RedisQueue"
|
|
51
50
|
|
|
52
51
|
[build-system]
|
|
53
52
|
requires = ["hatchling", "hatch-vcs"]
|
|
@@ -58,6 +57,7 @@ source = "vcs"
|
|
|
58
57
|
|
|
59
58
|
[tool.hatch.version.raw-options]
|
|
60
59
|
local_scheme = "no-local-version"
|
|
60
|
+
tag_regex = "^v(?P<version>\\d+\\.\\d+\\.\\d+)$"
|
|
61
61
|
|
|
62
62
|
[tool.ruff]
|
|
63
63
|
target-version = "py312"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Queue abstraction."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from yarl import URL
|
|
8
|
+
|
|
9
|
+
from taraqueue.registry import registry_load
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class QueueEmpty(Exception):
|
|
13
|
+
"""Raised when the queue is empty."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class Queue(ABC):
|
|
18
|
+
"""Base queue class."""
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def from_url(cls, url: URL | str, registry=None) -> "Queue":
|
|
22
|
+
if registry is None:
|
|
23
|
+
registry = registry_load("taraqueue")
|
|
24
|
+
scheme = URL(url).scheme
|
|
25
|
+
queue_cls = registry["taraqueue"][scheme]
|
|
26
|
+
return queue_cls.from_url(url)
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
async def subscribe(self, topic: str) -> None:
|
|
30
|
+
"""Subscribe to a topic before receiving messages."""
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
async def unsubscribe(self, topic: str) -> None:
|
|
34
|
+
"""Unsubscribe from a topic after receiving messages."""
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
async def receive(self, timeout=None) -> str:
|
|
38
|
+
"""Listen for messages on the subscribed topics."""
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
async def publish(self, topic: str, message: str) -> None:
|
|
42
|
+
"""Publish a message to a topic."""
|
|
43
|
+
|
|
44
|
+
@asynccontextmanager
|
|
45
|
+
async def connect(self, topic: str):
|
|
46
|
+
"""Context manager that subscribes on entry and unsubscribes on exit."""
|
|
47
|
+
await self.subscribe(topic)
|
|
48
|
+
try:
|
|
49
|
+
yield self
|
|
50
|
+
finally:
|
|
51
|
+
await self.unsubscribe(topic)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Memory queue implementation."""
|
|
2
|
+
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
from contextlib import suppress
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
from yarl import URL
|
|
8
|
+
|
|
9
|
+
from taraqueue import Queue, QueueEmpty
|
|
10
|
+
|
|
11
|
+
_global_memory_queues = defaultdict(list)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class MemoryQueue(Queue):
|
|
16
|
+
|
|
17
|
+
topics: list = field(default_factory=list)
|
|
18
|
+
queues: dict = field(default_factory=lambda: _global_memory_queues)
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def from_url(cls, url: URL) -> "MemoryQueue":
|
|
22
|
+
return cls()
|
|
23
|
+
|
|
24
|
+
async def subscribe(self, topic: str) -> None:
|
|
25
|
+
"""See `Queue.subscribe`."""
|
|
26
|
+
self.topics.append(topic)
|
|
27
|
+
|
|
28
|
+
async def unsubscribe(self, topic: str) -> None:
|
|
29
|
+
"""See `Queue.unsubscribe`."""
|
|
30
|
+
with suppress(ValueError):
|
|
31
|
+
self.topics.remove(topic)
|
|
32
|
+
|
|
33
|
+
async def receive(self, timeout=None) -> str:
|
|
34
|
+
"""See `Queue.receive`."""
|
|
35
|
+
for topic in self.topics[:]:
|
|
36
|
+
# Cycle through topics.
|
|
37
|
+
self.topics.append(self.topics.pop(0))
|
|
38
|
+
queue = self.queues[topic]
|
|
39
|
+
with suppress(IndexError):
|
|
40
|
+
return queue.pop(0)
|
|
41
|
+
|
|
42
|
+
raise QueueEmpty("Queue is empty")
|
|
43
|
+
|
|
44
|
+
async def publish(self, topic: str, message: str) -> None:
|
|
45
|
+
"""See `Queue.publish`."""
|
|
46
|
+
queue = self.queues[topic]
|
|
47
|
+
queue.append(message)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Redis queue implementation."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from time import time
|
|
6
|
+
|
|
7
|
+
from redis.asyncio import Redis
|
|
8
|
+
from redis.asyncio.client import PubSub
|
|
9
|
+
from yarl import URL
|
|
10
|
+
|
|
11
|
+
from taraqueue import Queue, QueueEmpty
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class RedisQueue(Queue):
|
|
16
|
+
|
|
17
|
+
client: Redis = field()
|
|
18
|
+
pubsub: PubSub = field()
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def from_env(cls, env=os.environ) -> "RedisQueue":
|
|
22
|
+
host = env.get("REDIS_SLAVEOF_IP", "") or env.get("IPV4_NETWORK", "172.22.1") + ".249"
|
|
23
|
+
port = int(env.get("REDIS_SLAVEOF_PORT", "") or "6379")
|
|
24
|
+
password = env.get("REDISPASS")
|
|
25
|
+
return cls.from_host(host, port, password=password)
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def from_host(cls, host: str, port: int = 6379, password: str | None = None) -> "RedisQueue":
|
|
29
|
+
from redis.asyncio import StrictRedis
|
|
30
|
+
|
|
31
|
+
client = StrictRedis(
|
|
32
|
+
host=host,
|
|
33
|
+
port=port,
|
|
34
|
+
decode_responses=True,
|
|
35
|
+
db=0,
|
|
36
|
+
password=password,
|
|
37
|
+
)
|
|
38
|
+
pubsub = client.pubsub(ignore_subscribe_messages=True)
|
|
39
|
+
return cls(client, pubsub)
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def from_url(cls, url: URL | str) -> "RedisQueue":
|
|
43
|
+
url = URL(url)
|
|
44
|
+
return cls.from_host(url.host, url.port, password=url.password)
|
|
45
|
+
|
|
46
|
+
async def subscribe(self, topic: str) -> None:
|
|
47
|
+
"""See `Queue.subscribe`."""
|
|
48
|
+
await self.pubsub.subscribe(topic)
|
|
49
|
+
|
|
50
|
+
async def unsubscribe(self, topic: str) -> None:
|
|
51
|
+
"""See `Queue.unsubscribe`."""
|
|
52
|
+
await self.pubsub.unsubscribe(topic)
|
|
53
|
+
|
|
54
|
+
async def receive(self, timeout=0) -> str:
|
|
55
|
+
"""See `Queue.receive`."""
|
|
56
|
+
stop_time = time() + timeout
|
|
57
|
+
while True:
|
|
58
|
+
remaining_timeout = max(0.0, stop_time - time())
|
|
59
|
+
message = await self.pubsub.get_message(ignore_subscribe_messages=True, timeout=remaining_timeout)
|
|
60
|
+
if message:
|
|
61
|
+
return message["data"]
|
|
62
|
+
if time() >= stop_time:
|
|
63
|
+
raise QueueEmpty("Queue is empty")
|
|
64
|
+
|
|
65
|
+
async def publish(self, topic: str, message: str) -> None:
|
|
66
|
+
"""See `Queue.publish`."""
|
|
67
|
+
await self.client.publish(topic, message)
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"""Compose server module."""
|
|
2
2
|
|
|
3
3
|
from contextlib import contextmanager
|
|
4
|
+
from dataclasses import dataclass
|
|
4
5
|
from datetime import datetime
|
|
5
6
|
|
|
6
|
-
from attrs import define
|
|
7
7
|
from more_itertools import only
|
|
8
8
|
from pytest_xdocker.docker import DockerContainer
|
|
9
9
|
from pytest_xdocker.process import ProcessData, ProcessServer
|
|
10
10
|
from pytest_xdocker.xdocker import xdocker
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
@
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
14
|
class ComposeService:
|
|
15
15
|
"""Compose service.
|
|
16
16
|
|
|
@@ -859,7 +859,6 @@ wheels = [
|
|
|
859
859
|
name = "taraqueue"
|
|
860
860
|
source = { editable = "." }
|
|
861
861
|
dependencies = [
|
|
862
|
-
{ name = "attrs" },
|
|
863
862
|
{ name = "yarl" },
|
|
864
863
|
]
|
|
865
864
|
|
|
@@ -887,7 +886,6 @@ test = [
|
|
|
887
886
|
|
|
888
887
|
[package.metadata]
|
|
889
888
|
requires-dist = [
|
|
890
|
-
{ name = "attrs", specifier = ">=26.1.0" },
|
|
891
889
|
{ name = "coverage", marker = "extra == 'test'", specifier = ">=7.2.3,<8.0.0" },
|
|
892
890
|
{ name = "more-itertools", marker = "extra == 'test'", specifier = ">=11.0.1,<11.1.0" },
|
|
893
891
|
{ name = "pytest", marker = "extra == 'test'", specifier = ">=9.0.0,<10.0.0" },
|
taraqueue-0.0.2.dev0/CHANGES.rst
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
"""Queue abstraction and implementation."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
from abc import ABC, abstractmethod
|
|
5
|
-
from collections import defaultdict
|
|
6
|
-
from contextlib import asynccontextmanager, suppress
|
|
7
|
-
from time import time
|
|
8
|
-
|
|
9
|
-
from attrs import define, field
|
|
10
|
-
from yarl import URL
|
|
11
|
-
|
|
12
|
-
from taraqueue.registry import registry_load
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class QueueEmpty(Exception):
|
|
16
|
-
"""Raised when the queue is empty."""
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@define
|
|
20
|
-
class Queue(ABC):
|
|
21
|
-
"""Base queue class."""
|
|
22
|
-
|
|
23
|
-
@classmethod
|
|
24
|
-
def from_url(cls, url: URL | str, registry=None) -> "Queue":
|
|
25
|
-
if registry is None:
|
|
26
|
-
registry = registry_load("taraqueue")
|
|
27
|
-
scheme = URL(url).scheme
|
|
28
|
-
queue_cls = registry["taraqueue"][scheme]
|
|
29
|
-
return queue_cls.from_url(url)
|
|
30
|
-
|
|
31
|
-
@abstractmethod
|
|
32
|
-
async def subscribe(self, topic: str) -> None:
|
|
33
|
-
"""Subscribe to a topic before receiving messages."""
|
|
34
|
-
|
|
35
|
-
@abstractmethod
|
|
36
|
-
async def unsubscribe(self, topic: str) -> None:
|
|
37
|
-
"""Unsubscribe from a topic after receiving messages."""
|
|
38
|
-
|
|
39
|
-
@abstractmethod
|
|
40
|
-
async def receive(self, timeout=None) -> str:
|
|
41
|
-
"""Listen for messages on the subscribed topics."""
|
|
42
|
-
|
|
43
|
-
@abstractmethod
|
|
44
|
-
async def publish(self, topic: str, message: str) -> None:
|
|
45
|
-
"""Publish a message to a topic."""
|
|
46
|
-
|
|
47
|
-
@asynccontextmanager
|
|
48
|
-
async def connect(self, topic: str):
|
|
49
|
-
"""Context manager that subscribes on entry and unsubscribes on exit."""
|
|
50
|
-
await self.subscribe(topic)
|
|
51
|
-
try:
|
|
52
|
-
yield self
|
|
53
|
-
finally:
|
|
54
|
-
await self.unsubscribe(topic)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
_global_memory_queues = defaultdict(list)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@define
|
|
61
|
-
class MemoryQueue(Queue):
|
|
62
|
-
|
|
63
|
-
topics = field(factory=list)
|
|
64
|
-
queues = field(default=_global_memory_queues)
|
|
65
|
-
|
|
66
|
-
@classmethod
|
|
67
|
-
def from_url(cls, url: URL) -> "MemoryQueue":
|
|
68
|
-
return cls()
|
|
69
|
-
|
|
70
|
-
async def subscribe(self, topic: str) -> None:
|
|
71
|
-
"""See `Queue.subscribe`."""
|
|
72
|
-
self.topics.append(topic)
|
|
73
|
-
|
|
74
|
-
async def unsubscribe(self, topic: str) -> None:
|
|
75
|
-
"""See `Queue.unsubscribe`."""
|
|
76
|
-
with suppress(ValueError):
|
|
77
|
-
self.topics.remove(topic)
|
|
78
|
-
|
|
79
|
-
async def receive(self, timeout=None) -> str:
|
|
80
|
-
"""See `Queue.receive`."""
|
|
81
|
-
for topic in self.topics[:]:
|
|
82
|
-
# Cycle through topics.
|
|
83
|
-
self.topics.append(self.topics.pop(0))
|
|
84
|
-
queue = self.queues[topic]
|
|
85
|
-
with suppress(IndexError):
|
|
86
|
-
return queue.pop(0)
|
|
87
|
-
|
|
88
|
-
raise QueueEmpty("Queue is empty")
|
|
89
|
-
|
|
90
|
-
async def publish(self, topic: str, message: str) -> None:
|
|
91
|
-
"""See `Queue.publish`."""
|
|
92
|
-
queue = self.queues[topic]
|
|
93
|
-
queue.append(message)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@define
|
|
97
|
-
class RedisQueue(Queue):
|
|
98
|
-
|
|
99
|
-
client = field()
|
|
100
|
-
pubsub = field()
|
|
101
|
-
|
|
102
|
-
@classmethod
|
|
103
|
-
def from_env(cls, env=os.environ) -> "RedisQueue":
|
|
104
|
-
host = env.get("REDIS_SLAVEOF_IP", "") or env.get("IPV4_NETWORK", "172.22.1") + ".249"
|
|
105
|
-
port = int(env.get("REDIS_SLAVEOF_PORT", "") or "6379")
|
|
106
|
-
password = env.get("REDISPASS")
|
|
107
|
-
return cls.from_host(host, port, password=password)
|
|
108
|
-
|
|
109
|
-
@classmethod
|
|
110
|
-
def from_host(cls, host: str, port: int = 6379, password: str | None = None) -> "RedisQueue":
|
|
111
|
-
from redis.asyncio import StrictRedis
|
|
112
|
-
|
|
113
|
-
client = StrictRedis(
|
|
114
|
-
host=host,
|
|
115
|
-
port=port,
|
|
116
|
-
decode_responses=True,
|
|
117
|
-
db=0,
|
|
118
|
-
password=password,
|
|
119
|
-
)
|
|
120
|
-
pubsub = client.pubsub(ignore_subscribe_messages=True)
|
|
121
|
-
return cls(client, pubsub)
|
|
122
|
-
|
|
123
|
-
@classmethod
|
|
124
|
-
def from_url(cls, url: URL | str) -> "RedisQueue":
|
|
125
|
-
url = URL(url)
|
|
126
|
-
return cls.from_host(url.host, url.port, password=url.password)
|
|
127
|
-
|
|
128
|
-
async def subscribe(self, topic: str) -> None:
|
|
129
|
-
"""See `Queue.subscribe`."""
|
|
130
|
-
await self.pubsub.subscribe(topic)
|
|
131
|
-
|
|
132
|
-
async def unsubscribe(self, topic: str) -> None:
|
|
133
|
-
"""See `Queue.unsubscribe`."""
|
|
134
|
-
await self.pubsub.unsubscribe(topic)
|
|
135
|
-
|
|
136
|
-
async def receive(self, timeout=0) -> str:
|
|
137
|
-
"""See `Queue.receive`."""
|
|
138
|
-
stop_time = time() + timeout
|
|
139
|
-
while True:
|
|
140
|
-
remaining_timeout = max(0.0, stop_time - time())
|
|
141
|
-
message = await self.pubsub.get_message(ignore_subscribe_messages=True, timeout=remaining_timeout)
|
|
142
|
-
if message:
|
|
143
|
-
return message["data"]
|
|
144
|
-
if time() >= stop_time:
|
|
145
|
-
raise QueueEmpty("Queue is empty")
|
|
146
|
-
|
|
147
|
-
async def publish(self, topic: str, message: str) -> None:
|
|
148
|
-
"""See `Queue.publish`."""
|
|
149
|
-
await self.client.publish(topic, message)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|