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.
Files changed (46) hide show
  1. {taraqueue-0.1.0 → taraqueue-0.3.0}/.github/workflows/publish.yml +1 -1
  2. {taraqueue-0.1.0 → taraqueue-0.3.0}/.github/workflows/test.yml +1 -1
  3. taraqueue-0.3.0/CHANGES.rst +20 -0
  4. {taraqueue-0.1.0 → taraqueue-0.3.0}/PKG-INFO +1 -2
  5. taraqueue-0.3.0/compose.yml +4 -0
  6. {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/taraqueue.rst +18 -2
  7. taraqueue-0.3.0/docs/taraqueue.testing.rst +37 -0
  8. {taraqueue-0.1.0 → taraqueue-0.3.0}/pyproject.toml +2 -3
  9. taraqueue-0.3.0/taraqueue/__init__.py +51 -0
  10. taraqueue-0.3.0/taraqueue/memory.py +50 -0
  11. taraqueue-0.3.0/taraqueue/redis.py +67 -0
  12. {taraqueue-0.1.0 → taraqueue-0.3.0}/taraqueue/testing/compose.py +2 -2
  13. {taraqueue-0.1.0 → taraqueue-0.3.0}/taraqueue/testing/queue.py +2 -2
  14. {taraqueue-0.1.0 → taraqueue-0.3.0}/taraqueue/testing/services.py +2 -2
  15. {taraqueue-0.1.0 → taraqueue-0.3.0}/tests/test_queue.py +1 -1
  16. {taraqueue-0.1.0 → taraqueue-0.3.0}/uv.lock +190 -174
  17. taraqueue-0.1.0/CHANGES.rst +0 -6
  18. taraqueue-0.1.0/compose.yml +0 -4
  19. taraqueue-0.1.0/taraqueue/queue.py +0 -149
  20. taraqueue-0.1.0/taraqueue/testing/__init__.py +0 -0
  21. {taraqueue-0.1.0 → taraqueue-0.3.0}/.editorconfig +0 -0
  22. {taraqueue-0.1.0 → taraqueue-0.3.0}/.gitattributes +0 -0
  23. {taraqueue-0.1.0 → taraqueue-0.3.0}/.github/CODEOWNERS +0 -0
  24. {taraqueue-0.1.0 → taraqueue-0.3.0}/.github/actions/setup-uv-env/action.yml +0 -0
  25. {taraqueue-0.1.0 → taraqueue-0.3.0}/.github/renovate.json +0 -0
  26. {taraqueue-0.1.0 → taraqueue-0.3.0}/.github/workflows/renovate.yaml +0 -0
  27. {taraqueue-0.1.0 → taraqueue-0.3.0}/.gitignore +0 -0
  28. {taraqueue-0.1.0 → taraqueue-0.3.0}/.vscode/extensions.json +0 -0
  29. {taraqueue-0.1.0 → taraqueue-0.3.0}/.vscode/launch.json +0 -0
  30. {taraqueue-0.1.0 → taraqueue-0.3.0}/.vscode/settings.json +0 -0
  31. {taraqueue-0.1.0 → taraqueue-0.3.0}/CONTRIBUTING.rst +0 -0
  32. {taraqueue-0.1.0 → taraqueue-0.3.0}/LICENSE.rst +0 -0
  33. {taraqueue-0.1.0 → taraqueue-0.3.0}/Makefile +0 -0
  34. {taraqueue-0.1.0 → taraqueue-0.3.0}/README.rst +0 -0
  35. {taraqueue-0.1.0 → taraqueue-0.3.0}/STYLE_GUIDE.rst +0 -0
  36. {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/changes.rst +0 -0
  37. {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/conf.py +0 -0
  38. {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/contributing.rst +0 -0
  39. {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/index.rst +0 -0
  40. {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/license.rst +0 -0
  41. {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/modules.rst +0 -0
  42. {taraqueue-0.1.0 → taraqueue-0.3.0}/docs/style_guide.rst +0 -0
  43. {taraqueue-0.1.0 → taraqueue-0.3.0}/taraqueue/registry.py +0 -0
  44. {taraqueue-0.1.0/taraqueue → taraqueue-0.3.0/taraqueue/testing}/__init__.py +0 -0
  45. {taraqueue-0.1.0 → taraqueue-0.3.0}/tests/test_compose.py +0 -0
  46. {taraqueue-0.1.0 → taraqueue-0.3.0}/tests/test_registry.py +0 -0
@@ -41,7 +41,7 @@ jobs:
41
41
  - name: Set up the environment
42
42
  uses: ./.github/actions/setup-uv-env
43
43
  with:
44
- extras: docs
44
+ extras: docs,test
45
45
 
46
46
  - name: Export tag
47
47
  id: vars
@@ -88,7 +88,7 @@ jobs:
88
88
  - name: Set up the environment
89
89
  uses: ./.github/actions/setup-uv-env
90
90
  with:
91
- extras: docs
91
+ extras: docs,test
92
92
 
93
93
  - name: Run docs target
94
94
  run: make docs
@@ -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.1.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'
@@ -0,0 +1,4 @@
1
+ services:
2
+ redis:
3
+ image: redis:8.6.2-alpine
4
+ command: redis-server --requirepass ${REDIS_PASSWORD:-test}
@@ -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.queue module
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.queue
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.queue:MemoryQueue"
50
- redis = "taraqueue.queue:RedisQueue"
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
- @define(frozen=True)
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.queue import Queue
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["REDISPASS"],
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
- "REDISPASS": "test",
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["REDISPASS"],
86
+ password=env_vars["REDIS_PASSWORD"],
87
87
  )
@@ -2,7 +2,7 @@
2
2
 
3
3
  import pytest
4
4
 
5
- from taraqueue.queue import QueueEmpty
5
+ from taraqueue import QueueEmpty
6
6
 
7
7
 
8
8
  async def test_queue_send_receive(queue, unique):