vs-queue 0.1.0__tar.gz → 0.1.2__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 (28) hide show
  1. {vs_queue-0.1.0 → vs_queue-0.1.2}/PKG-INFO +43 -13
  2. {vs_queue-0.1.0 → vs_queue-0.1.2}/README.md +42 -12
  3. {vs_queue-0.1.0 → vs_queue-0.1.2}/pyproject.toml +1 -1
  4. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/base/vs_base_consumer.py +2 -2
  5. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/provider/vs_rabbitmq_queue.py +4 -0
  6. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/provider/vs_redis_queue.py +4 -0
  7. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue.egg-info/PKG-INFO +43 -13
  8. {vs_queue-0.1.0 → vs_queue-0.1.2}/setup.cfg +0 -0
  9. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/__init__.py +0 -0
  10. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/base/__init__.py +0 -0
  11. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/base/vs_base_queue.py +0 -0
  12. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/decorator/__init__.py +0 -0
  13. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/decorator/vs_queue_listener.py +0 -0
  14. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/factory/__init__.py +0 -0
  15. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/factory/vs_queue_factory.py +0 -0
  16. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/manager/__init__.py +0 -0
  17. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/manager/vs_queue_manager.py +0 -0
  18. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/provider/__init__.py +0 -0
  19. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/registry/__init__.py +0 -0
  20. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/registry/vs_queue_registry.py +0 -0
  21. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/retry/__init__.py +0 -0
  22. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/retry/vs_retry_policy.py +0 -0
  23. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/schema/__init__.py +0 -0
  24. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/schema/vs_message.py +0 -0
  25. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue.egg-info/SOURCES.txt +0 -0
  26. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue.egg-info/dependency_links.txt +0 -0
  27. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue.egg-info/requires.txt +0 -0
  28. {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vs-queue
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Pluggable async message queue library for Viveka Sutra — Redis Streams, RabbitMQ, retry, dead letter, and annotation-based consumers
5
5
  Project-URL: Homepage, https://vivekasutra.com/
6
6
  Project-URL: Source, https://github.com/vivekasutra/viveka-mula
@@ -315,13 +315,13 @@ Abstract base class for all consumers. Extend this and implement `handle` to pro
315
315
  | Method | Description |
316
316
  |---|---|
317
317
  | `handle(message)` | Called for every message received. Raise any exception to trigger retry. |
318
- | `start()` | Called once when the consumer begins. Use for setup. |
319
- | `stop()` | Called once on graceful shutdown. Use for cleanup. |
320
318
 
321
- **Optional override:**
319
+ **Optional overrides** (concrete no-ops by default — override only if you need them):
322
320
 
323
321
  | Method | Description |
324
322
  |---|---|
323
+ | `start()` | Called once when the consumer begins. Use for setup. |
324
+ | `stop()` | Called once on graceful shutdown. Use for cleanup. |
325
325
  | `on_error(message, error)` | Called when `handle` raises an exception, before retry logic runs. Use for logging or alerting. |
326
326
 
327
327
  **Example with error hook:**
@@ -374,9 +374,6 @@ Decorator that registers a `VsBaseConsumer` subclass as a listener. `VsQueueMana
374
374
  class SimpleConsumer(VsBaseConsumer):
375
375
  async def handle(self, message: VsMessage) -> None:
376
376
  print(message.payload)
377
-
378
- async def start(self) -> None: pass
379
- async def stop(self) -> None: pass
380
377
  ```
381
378
 
382
379
  **Example — full configuration:**
@@ -543,9 +540,6 @@ class DlqConsumer(VsBaseConsumer):
543
540
  f"Message permanently failed | id={message.id} retries={message.retry_count}",
544
541
  payload=message.payload,
545
542
  )
546
-
547
- async def start(self) -> None: pass
548
- async def stop(self) -> None: pass
549
543
  ```
550
544
 
551
545
  **Example — replaying a DLQ message:**
@@ -560,6 +554,42 @@ await queue.publish("orchestrator:tasks", dlq_message)
560
554
 
561
555
  ---
562
556
 
557
+ ## Logging
558
+
559
+ Both backends log consumer lifecycle and per-message activity on the provider logger
560
+ (`vs_queue.provider.vs_redis_queue` / `vs_queue.provider.vs_rabbitmq_queue`). Every line uses a
561
+ `Label | key=value` format.
562
+
563
+ | Level | Line | When |
564
+ |---|---|---|
565
+ | `INFO` | `Subscribed \| queue=<q> consumer=<ClassName>` | `consumer.start()` has run and the queue / consumer group is ready — the loop is now listening |
566
+ | `INFO` | `Message received \| queue=<q> id=<msg_id> retry_count=<n>` | Each message pulled off the queue, before `handle()` is called |
567
+ | `DEBUG` | `Message processed \| queue=<q> id=<msg_id>` | `handle()` returned and the message was acked |
568
+ | `INFO` | `Unsubscribed \| queue=<q> consumer=<ClassName>` | The subscribe loop exited (graceful shutdown or permanent failure) |
569
+ | `ERROR` | `Message failed, no retry policy \| id=<msg_id> error=<e>` | `handle()` raised and no `VsRetryPolicy` was supplied |
570
+
571
+ `VsQueueManager` logs lifecycle events on its own logger (`vs_queue.manager.vs_queue_manager`):
572
+
573
+ | Level | Line |
574
+ |---|---|
575
+ | `INFO` | `Listener registered \| queue=<q> uuid=<uid>` |
576
+ | `INFO` | `Listeners registered \| queue=<q> concurrency=<n>` |
577
+ | `INFO` | `Listener stopped \| queue=<q> uuid=<uid>` |
578
+ | `ERROR` | `Consumer crashed, restarting in <n>s \| queue=<q> attempt=<n> error=<e>` |
579
+ | `CRITICAL` | `Consumer permanently failed after <n> restarts \| queue=<q> error=<e>` |
580
+
581
+ `Subscribed` / `Message received` land only once the loop is actually consuming, so the absence of
582
+ `Subscribed` after `Listener registered` means `subscribe()` is failing before it starts.
583
+
584
+ Turn on the debug lines:
585
+
586
+ ```python
587
+ import logging
588
+ logging.getLogger("vs_queue").setLevel(logging.DEBUG)
589
+ ```
590
+
591
+ ---
592
+
563
593
  ## Redis Streams Backend
564
594
 
565
595
  `VsRedisQueue` implements the queue using Redis Streams (`XADD` / `XREADGROUP`). Redis Streams provide persistent, ordered, consumer-group-aware message delivery.
@@ -902,9 +932,9 @@ Abstract base. Extend and implement `handle` to process messages.
902
932
  | Method | Required | Description |
903
933
  |---|---|---|
904
934
  | `handle(message)` | Yes | Process a message. Raise to trigger retry. |
905
- | `start()` | Yes | Setup before consuming begins |
906
- | `stop()` | Yes | Cleanup on shutdown — called on graceful cancel |
907
- | `on_error(message, error)` | No | Called on `handle` failure before retry |
935
+ | `start()` | No — no-op by default | Setup before consuming begins |
936
+ | `stop()` | No — no-op by default | Cleanup on shutdown — called on graceful cancel |
937
+ | `on_error(message, error)` | No — no-op by default | Called on `handle` failure before retry |
908
938
 
909
939
  ---
910
940
 
@@ -284,13 +284,13 @@ Abstract base class for all consumers. Extend this and implement `handle` to pro
284
284
  | Method | Description |
285
285
  |---|---|
286
286
  | `handle(message)` | Called for every message received. Raise any exception to trigger retry. |
287
- | `start()` | Called once when the consumer begins. Use for setup. |
288
- | `stop()` | Called once on graceful shutdown. Use for cleanup. |
289
287
 
290
- **Optional override:**
288
+ **Optional overrides** (concrete no-ops by default — override only if you need them):
291
289
 
292
290
  | Method | Description |
293
291
  |---|---|
292
+ | `start()` | Called once when the consumer begins. Use for setup. |
293
+ | `stop()` | Called once on graceful shutdown. Use for cleanup. |
294
294
  | `on_error(message, error)` | Called when `handle` raises an exception, before retry logic runs. Use for logging or alerting. |
295
295
 
296
296
  **Example with error hook:**
@@ -343,9 +343,6 @@ Decorator that registers a `VsBaseConsumer` subclass as a listener. `VsQueueMana
343
343
  class SimpleConsumer(VsBaseConsumer):
344
344
  async def handle(self, message: VsMessage) -> None:
345
345
  print(message.payload)
346
-
347
- async def start(self) -> None: pass
348
- async def stop(self) -> None: pass
349
346
  ```
350
347
 
351
348
  **Example — full configuration:**
@@ -512,9 +509,6 @@ class DlqConsumer(VsBaseConsumer):
512
509
  f"Message permanently failed | id={message.id} retries={message.retry_count}",
513
510
  payload=message.payload,
514
511
  )
515
-
516
- async def start(self) -> None: pass
517
- async def stop(self) -> None: pass
518
512
  ```
519
513
 
520
514
  **Example — replaying a DLQ message:**
@@ -529,6 +523,42 @@ await queue.publish("orchestrator:tasks", dlq_message)
529
523
 
530
524
  ---
531
525
 
526
+ ## Logging
527
+
528
+ Both backends log consumer lifecycle and per-message activity on the provider logger
529
+ (`vs_queue.provider.vs_redis_queue` / `vs_queue.provider.vs_rabbitmq_queue`). Every line uses a
530
+ `Label | key=value` format.
531
+
532
+ | Level | Line | When |
533
+ |---|---|---|
534
+ | `INFO` | `Subscribed \| queue=<q> consumer=<ClassName>` | `consumer.start()` has run and the queue / consumer group is ready — the loop is now listening |
535
+ | `INFO` | `Message received \| queue=<q> id=<msg_id> retry_count=<n>` | Each message pulled off the queue, before `handle()` is called |
536
+ | `DEBUG` | `Message processed \| queue=<q> id=<msg_id>` | `handle()` returned and the message was acked |
537
+ | `INFO` | `Unsubscribed \| queue=<q> consumer=<ClassName>` | The subscribe loop exited (graceful shutdown or permanent failure) |
538
+ | `ERROR` | `Message failed, no retry policy \| id=<msg_id> error=<e>` | `handle()` raised and no `VsRetryPolicy` was supplied |
539
+
540
+ `VsQueueManager` logs lifecycle events on its own logger (`vs_queue.manager.vs_queue_manager`):
541
+
542
+ | Level | Line |
543
+ |---|---|
544
+ | `INFO` | `Listener registered \| queue=<q> uuid=<uid>` |
545
+ | `INFO` | `Listeners registered \| queue=<q> concurrency=<n>` |
546
+ | `INFO` | `Listener stopped \| queue=<q> uuid=<uid>` |
547
+ | `ERROR` | `Consumer crashed, restarting in <n>s \| queue=<q> attempt=<n> error=<e>` |
548
+ | `CRITICAL` | `Consumer permanently failed after <n> restarts \| queue=<q> error=<e>` |
549
+
550
+ `Subscribed` / `Message received` land only once the loop is actually consuming, so the absence of
551
+ `Subscribed` after `Listener registered` means `subscribe()` is failing before it starts.
552
+
553
+ Turn on the debug lines:
554
+
555
+ ```python
556
+ import logging
557
+ logging.getLogger("vs_queue").setLevel(logging.DEBUG)
558
+ ```
559
+
560
+ ---
561
+
532
562
  ## Redis Streams Backend
533
563
 
534
564
  `VsRedisQueue` implements the queue using Redis Streams (`XADD` / `XREADGROUP`). Redis Streams provide persistent, ordered, consumer-group-aware message delivery.
@@ -871,9 +901,9 @@ Abstract base. Extend and implement `handle` to process messages.
871
901
  | Method | Required | Description |
872
902
  |---|---|---|
873
903
  | `handle(message)` | Yes | Process a message. Raise to trigger retry. |
874
- | `start()` | Yes | Setup before consuming begins |
875
- | `stop()` | Yes | Cleanup on shutdown — called on graceful cancel |
876
- | `on_error(message, error)` | No | Called on `handle` failure before retry |
904
+ | `start()` | No — no-op by default | Setup before consuming begins |
905
+ | `stop()` | No — no-op by default | Cleanup on shutdown — called on graceful cancel |
906
+ | `on_error(message, error)` | No — no-op by default | Called on `handle` failure before retry |
877
907
 
878
908
  ---
879
909
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vs-queue"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "Pluggable async message queue library for Viveka Sutra — Redis Streams, RabbitMQ, retry, dead letter, and annotation-based consumers"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -13,12 +13,12 @@ class VsBaseConsumer(ABC):
13
13
  async def handle(self, message: VsMessage) -> None:
14
14
  pass
15
15
 
16
- @abstractmethod
17
16
  async def start(self) -> None:
17
+ """Called once before consuming begins. Override for setup."""
18
18
  pass
19
19
 
20
- @abstractmethod
21
20
  async def stop(self) -> None:
21
+ """Called once on graceful shutdown. Override for cleanup."""
22
22
  pass
23
23
 
24
24
  async def on_error(self, message: VsMessage, error: Exception) -> None:
@@ -62,6 +62,7 @@ class VsRabbitMQQueue(VsBaseQueue):
62
62
  async def subscribe(self, queue: str, consumer: VsBaseConsumer, retry_policy: Optional[VsRetryPolicy] = None) -> None:
63
63
  q = await self._channel.declare_queue(queue, durable=True)
64
64
  await consumer.start()
65
+ _logger.info(f"Subscribed | queue={queue} consumer={type(consumer).__name__}")
65
66
  try:
66
67
  async with q.iterator() as iterator:
67
68
  async for amqp_message in iterator:
@@ -73,9 +74,11 @@ class VsRabbitMQQueue(VsBaseQueue):
73
74
  headers=data["headers"],
74
75
  payload=data["payload"],
75
76
  )
77
+ _logger.info(f"Message received | queue={queue} id={message.id} retry_count={message.retry_count}")
76
78
  try:
77
79
  await consumer.handle(message)
78
80
  await amqp_message.ack()
81
+ _logger.debug(f"Message processed | queue={queue} id={message.id}")
79
82
  except Exception as e:
80
83
  await consumer.on_error(message, e)
81
84
  if retry_policy:
@@ -89,3 +92,4 @@ class VsRabbitMQQueue(VsBaseQueue):
89
92
  _logger.error(f"Message failed, no retry policy | id={message.id} error={e}")
90
93
  finally:
91
94
  await consumer.stop()
95
+ _logger.info(f"Unsubscribed | queue={queue} consumer={type(consumer).__name__}")
@@ -68,6 +68,7 @@ class VsRedisQueue(VsBaseQueue):
68
68
  pass
69
69
 
70
70
  await consumer.start()
71
+ _logger.info(f"Subscribed | queue={queue} consumer={type(consumer).__name__}")
71
72
  try:
72
73
  while True:
73
74
  results = await self._client.xreadgroup(
@@ -89,9 +90,11 @@ class VsRedisQueue(VsBaseQueue):
89
90
  headers=json.loads(data["headers"]),
90
91
  payload=json.loads(data["payload"]),
91
92
  )
93
+ _logger.info(f"Message received | queue={queue} id={message.id} retry_count={message.retry_count}")
92
94
  try:
93
95
  await consumer.handle(message)
94
96
  await self._client.xack(queue, group, msg_id)
97
+ _logger.debug(f"Message processed | queue={queue} id={message.id}")
95
98
  except Exception as e:
96
99
  await consumer.on_error(message, e)
97
100
  if retry_policy:
@@ -102,3 +105,4 @@ class VsRedisQueue(VsBaseQueue):
102
105
  _logger.error(f"Message failed, no retry policy | id={message.id} error={e}")
103
106
  finally:
104
107
  await consumer.stop()
108
+ _logger.info(f"Unsubscribed | queue={queue} consumer={type(consumer).__name__}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vs-queue
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Pluggable async message queue library for Viveka Sutra — Redis Streams, RabbitMQ, retry, dead letter, and annotation-based consumers
5
5
  Project-URL: Homepage, https://vivekasutra.com/
6
6
  Project-URL: Source, https://github.com/vivekasutra/viveka-mula
@@ -315,13 +315,13 @@ Abstract base class for all consumers. Extend this and implement `handle` to pro
315
315
  | Method | Description |
316
316
  |---|---|
317
317
  | `handle(message)` | Called for every message received. Raise any exception to trigger retry. |
318
- | `start()` | Called once when the consumer begins. Use for setup. |
319
- | `stop()` | Called once on graceful shutdown. Use for cleanup. |
320
318
 
321
- **Optional override:**
319
+ **Optional overrides** (concrete no-ops by default — override only if you need them):
322
320
 
323
321
  | Method | Description |
324
322
  |---|---|
323
+ | `start()` | Called once when the consumer begins. Use for setup. |
324
+ | `stop()` | Called once on graceful shutdown. Use for cleanup. |
325
325
  | `on_error(message, error)` | Called when `handle` raises an exception, before retry logic runs. Use for logging or alerting. |
326
326
 
327
327
  **Example with error hook:**
@@ -374,9 +374,6 @@ Decorator that registers a `VsBaseConsumer` subclass as a listener. `VsQueueMana
374
374
  class SimpleConsumer(VsBaseConsumer):
375
375
  async def handle(self, message: VsMessage) -> None:
376
376
  print(message.payload)
377
-
378
- async def start(self) -> None: pass
379
- async def stop(self) -> None: pass
380
377
  ```
381
378
 
382
379
  **Example — full configuration:**
@@ -543,9 +540,6 @@ class DlqConsumer(VsBaseConsumer):
543
540
  f"Message permanently failed | id={message.id} retries={message.retry_count}",
544
541
  payload=message.payload,
545
542
  )
546
-
547
- async def start(self) -> None: pass
548
- async def stop(self) -> None: pass
549
543
  ```
550
544
 
551
545
  **Example — replaying a DLQ message:**
@@ -560,6 +554,42 @@ await queue.publish("orchestrator:tasks", dlq_message)
560
554
 
561
555
  ---
562
556
 
557
+ ## Logging
558
+
559
+ Both backends log consumer lifecycle and per-message activity on the provider logger
560
+ (`vs_queue.provider.vs_redis_queue` / `vs_queue.provider.vs_rabbitmq_queue`). Every line uses a
561
+ `Label | key=value` format.
562
+
563
+ | Level | Line | When |
564
+ |---|---|---|
565
+ | `INFO` | `Subscribed \| queue=<q> consumer=<ClassName>` | `consumer.start()` has run and the queue / consumer group is ready — the loop is now listening |
566
+ | `INFO` | `Message received \| queue=<q> id=<msg_id> retry_count=<n>` | Each message pulled off the queue, before `handle()` is called |
567
+ | `DEBUG` | `Message processed \| queue=<q> id=<msg_id>` | `handle()` returned and the message was acked |
568
+ | `INFO` | `Unsubscribed \| queue=<q> consumer=<ClassName>` | The subscribe loop exited (graceful shutdown or permanent failure) |
569
+ | `ERROR` | `Message failed, no retry policy \| id=<msg_id> error=<e>` | `handle()` raised and no `VsRetryPolicy` was supplied |
570
+
571
+ `VsQueueManager` logs lifecycle events on its own logger (`vs_queue.manager.vs_queue_manager`):
572
+
573
+ | Level | Line |
574
+ |---|---|
575
+ | `INFO` | `Listener registered \| queue=<q> uuid=<uid>` |
576
+ | `INFO` | `Listeners registered \| queue=<q> concurrency=<n>` |
577
+ | `INFO` | `Listener stopped \| queue=<q> uuid=<uid>` |
578
+ | `ERROR` | `Consumer crashed, restarting in <n>s \| queue=<q> attempt=<n> error=<e>` |
579
+ | `CRITICAL` | `Consumer permanently failed after <n> restarts \| queue=<q> error=<e>` |
580
+
581
+ `Subscribed` / `Message received` land only once the loop is actually consuming, so the absence of
582
+ `Subscribed` after `Listener registered` means `subscribe()` is failing before it starts.
583
+
584
+ Turn on the debug lines:
585
+
586
+ ```python
587
+ import logging
588
+ logging.getLogger("vs_queue").setLevel(logging.DEBUG)
589
+ ```
590
+
591
+ ---
592
+
563
593
  ## Redis Streams Backend
564
594
 
565
595
  `VsRedisQueue` implements the queue using Redis Streams (`XADD` / `XREADGROUP`). Redis Streams provide persistent, ordered, consumer-group-aware message delivery.
@@ -902,9 +932,9 @@ Abstract base. Extend and implement `handle` to process messages.
902
932
  | Method | Required | Description |
903
933
  |---|---|---|
904
934
  | `handle(message)` | Yes | Process a message. Raise to trigger retry. |
905
- | `start()` | Yes | Setup before consuming begins |
906
- | `stop()` | Yes | Cleanup on shutdown — called on graceful cancel |
907
- | `on_error(message, error)` | No | Called on `handle` failure before retry |
935
+ | `start()` | No — no-op by default | Setup before consuming begins |
936
+ | `stop()` | No — no-op by default | Cleanup on shutdown — called on graceful cancel |
937
+ | `on_error(message, error)` | No — no-op by default | Called on `handle` failure before retry |
908
938
 
909
939
  ---
910
940
 
File without changes
File without changes