taraqueue 0.1.0__tar.gz → 0.3.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.1.0 → taraqueue-0.3.0}/.github/workflows/publish.yml +1 -1
- {taraqueue-0.1.0 → taraqueue-0.3.0}/.github/workflows/test.yml +1 -1
- taraqueue-0.3.0/CHANGES.rst +20 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/PKG-INFO +1 -2
- taraqueue-0.3.0/compose.yml +4 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/taraqueue.rst +18 -2
- taraqueue-0.3.0/docs/taraqueue.testing.rst +37 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/pyproject.toml +2 -3
- taraqueue-0.3.0/taraqueue/__init__.py +51 -0
- taraqueue-0.3.0/taraqueue/memory.py +50 -0
- taraqueue-0.3.0/taraqueue/redis.py +67 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/taraqueue/testing/compose.py +2 -2
- {taraqueue-0.1.0 → taraqueue-0.3.0}/taraqueue/testing/queue.py +2 -2
- {taraqueue-0.1.0 → taraqueue-0.3.0}/taraqueue/testing/services.py +2 -2
- {taraqueue-0.1.0 → taraqueue-0.3.0}/tests/test_queue.py +1 -1
- {taraqueue-0.1.0 → taraqueue-0.3.0}/uv.lock +190 -174
- taraqueue-0.1.0/CHANGES.rst +0 -6
- taraqueue-0.1.0/compose.yml +0 -4
- taraqueue-0.1.0/taraqueue/queue.py +0 -149
- taraqueue-0.1.0/taraqueue/testing/__init__.py +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/.editorconfig +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/.gitattributes +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/.github/CODEOWNERS +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/.github/actions/setup-uv-env/action.yml +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/.github/renovate.json +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/.github/workflows/renovate.yaml +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/.gitignore +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/.vscode/extensions.json +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/.vscode/launch.json +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/.vscode/settings.json +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/CONTRIBUTING.rst +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/LICENSE.rst +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/Makefile +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/README.rst +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/STYLE_GUIDE.rst +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/changes.rst +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/conf.py +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/contributing.rst +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/index.rst +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/license.rst +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/modules.rst +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/style_guide.rst +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/taraqueue/registry.py +0 -0
- {taraqueue-0.1.0/taraqueue → taraqueue-0.3.0/taraqueue/testing}/__init__.py +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/tests/test_compose.py +0 -0
- {taraqueue-0.1.0 → taraqueue-0.3.0}/tests/test_registry.py +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Version 0.3.0
|
|
2
|
+
-------------
|
|
3
|
+
|
|
4
|
+
Released 2026-05-04
|
|
5
|
+
|
|
6
|
+
- Rename REDISPASS to REDIS_PASSWORD.
|
|
7
|
+
|
|
8
|
+
Version 0.2.0
|
|
9
|
+
-------------
|
|
10
|
+
|
|
11
|
+
Released 2026-05-04
|
|
12
|
+
|
|
13
|
+
- Split implementations.
|
|
14
|
+
|
|
15
|
+
Version 0.1.0
|
|
16
|
+
-------------
|
|
17
|
+
|
|
18
|
+
Released 2026-05-01
|
|
19
|
+
|
|
20
|
+
- Initial release.
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: taraqueue
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.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"]
|
|
@@ -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("REDIS_PASSWORD")
|
|
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
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import pytest
|
|
4
4
|
from yarl import URL
|
|
5
5
|
|
|
6
|
-
from taraqueue
|
|
6
|
+
from taraqueue import Queue
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
@pytest.fixture
|
|
@@ -20,7 +20,7 @@ def redis_queue(redis_service, env_vars):
|
|
|
20
20
|
scheme="redis",
|
|
21
21
|
host=redis_service.ip,
|
|
22
22
|
port=6379,
|
|
23
|
-
password=env_vars["
|
|
23
|
+
password=env_vars["REDIS_PASSWORD"],
|
|
24
24
|
)
|
|
25
25
|
return Queue.from_url(url)
|
|
26
26
|
|
|
@@ -18,7 +18,7 @@ def env_vars(project):
|
|
|
18
18
|
"""Environment variables for the services."""
|
|
19
19
|
return {
|
|
20
20
|
"COMPOSE_PROJECT_NAME": project,
|
|
21
|
-
"
|
|
21
|
+
"REDIS_PASSWORD": "test",
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
|
|
@@ -83,5 +83,5 @@ def redis_client(redis_service, env_vars):
|
|
|
83
83
|
port=6379,
|
|
84
84
|
decode_responses=True,
|
|
85
85
|
db=0,
|
|
86
|
-
password=env_vars["
|
|
86
|
+
password=env_vars["REDIS_PASSWORD"],
|
|
87
87
|
)
|