rabbitkit 0.9.0__tar.gz → 0.9.1__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 (100) hide show
  1. {rabbitkit-0.9.0/src/rabbitkit.egg-info → rabbitkit-0.9.1}/PKG-INFO +28 -25
  2. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/README.md +26 -24
  3. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/pyproject.toml +11 -1
  4. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/_version.py +1 -1
  5. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/async_/broker.py +11 -0
  6. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/pipeline.py +72 -0
  7. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/sync/broker.py +11 -0
  8. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/sync/transport.py +16 -1
  9. {rabbitkit-0.9.0 → rabbitkit-0.9.1/src/rabbitkit.egg-info}/PKG-INFO +28 -25
  10. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit.egg-info/requires.txt +1 -0
  11. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/LICENSE +0 -0
  12. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/setup.cfg +0 -0
  13. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/__init__.py +0 -0
  14. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/aio/__init__.py +0 -0
  15. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/async_/__init__.py +0 -0
  16. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/async_/batch.py +0 -0
  17. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/async_/connection.py +0 -0
  18. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/async_/pool.py +0 -0
  19. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/async_/transport.py +0 -0
  20. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/asyncapi/__init__.py +0 -0
  21. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/asyncapi/generator.py +0 -0
  22. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/asyncapi/schema.py +0 -0
  23. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/cli/__init__.py +0 -0
  24. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/cli/_utils.py +0 -0
  25. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/cli/commands/__init__.py +0 -0
  26. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/cli/commands/dlq.py +0 -0
  27. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/cli/commands/health.py +0 -0
  28. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/cli/commands/migrate.py +0 -0
  29. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/cli/commands/routes.py +0 -0
  30. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/cli/commands/run.py +0 -0
  31. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/cli/commands/shell.py +0 -0
  32. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/cli/commands/topology.py +0 -0
  33. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/concurrency.py +0 -0
  34. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/__init__.py +0 -0
  35. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/app.py +0 -0
  36. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/config.py +0 -0
  37. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/env_config.py +0 -0
  38. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/errors.py +0 -0
  39. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/logging.py +0 -0
  40. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/message.py +0 -0
  41. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/path.py +0 -0
  42. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/protocols.py +0 -0
  43. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/registry.py +0 -0
  44. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/route.py +0 -0
  45. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/router.py +0 -0
  46. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/topology.py +0 -0
  47. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/topology_dispatch.py +0 -0
  48. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/core/types.py +0 -0
  49. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/dashboard/__init__.py +0 -0
  50. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/dashboard/app.py +0 -0
  51. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/di/__init__.py +0 -0
  52. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/di/context.py +0 -0
  53. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/di/depends.py +0 -0
  54. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/di/resolver.py +0 -0
  55. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/dlq.py +0 -0
  56. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/experimental/__init__.py +0 -0
  57. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/fastapi.py +0 -0
  58. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/health.py +0 -0
  59. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/highload/__init__.py +0 -0
  60. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/highload/backpressure.py +0 -0
  61. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/highload/batch.py +0 -0
  62. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/locking.py +0 -0
  63. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/management.py +0 -0
  64. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/__init__.py +0 -0
  65. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/base.py +0 -0
  66. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/circuit_breaker.py +0 -0
  67. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/compression.py +0 -0
  68. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/deduplication.py +0 -0
  69. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/error_classifier.py +0 -0
  70. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/exception.py +0 -0
  71. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/metrics.py +0 -0
  72. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/otel.py +0 -0
  73. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/rate_limit.py +0 -0
  74. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/retry.py +0 -0
  75. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/signing.py +0 -0
  76. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/middleware/timeout.py +0 -0
  77. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/py.typed +0 -0
  78. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/queue_metrics.py +0 -0
  79. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/results/__init__.py +0 -0
  80. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/results/backend.py +0 -0
  81. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/results/middleware.py +0 -0
  82. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/rpc.py +0 -0
  83. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/serialization/__init__.py +0 -0
  84. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/serialization/base.py +0 -0
  85. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/serialization/json.py +0 -0
  86. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/serialization/msgspec.py +0 -0
  87. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/serialization/pipeline.py +0 -0
  88. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/streams.py +0 -0
  89. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/sync/__init__.py +0 -0
  90. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/sync/batch.py +0 -0
  91. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/sync/connection.py +0 -0
  92. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/sync/pool.py +0 -0
  93. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/testing/__init__.py +0 -0
  94. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/testing/app.py +0 -0
  95. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/testing/broker.py +0 -0
  96. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit/testing/fixtures.py +0 -0
  97. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit.egg-info/SOURCES.txt +0 -0
  98. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit.egg-info/dependency_links.txt +0 -0
  99. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit.egg-info/entry_points.txt +0 -0
  100. {rabbitkit-0.9.0 → rabbitkit-0.9.1}/src/rabbitkit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rabbitkit
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: Production-grade RabbitMQ toolkit — sync/async, decorator routing, retry, compression, full configurability
5
5
  Author-email: Talaat Magdy <talaatmagdy75@gmail.com>
6
6
  License-Expression: MIT
@@ -60,6 +60,7 @@ Requires-Dist: rabbitkit[all]; extra == "dev"
60
60
  Requires-Dist: pytest>=8.0; extra == "dev"
61
61
  Requires-Dist: httpx<1.0,>=0.27; extra == "dev"
62
62
  Requires-Dist: prometheus-client<2.0,>=0.20; extra == "dev"
63
+ Requires-Dist: psutil>=5.9; extra == "dev"
63
64
  Requires-Dist: mkdocs>=1.6.0; extra == "dev"
64
65
  Requires-Dist: mkdocs-material>=9.5.0; extra == "dev"
65
66
  Requires-Dist: mkdocstrings[python]>=0.26.0; extra == "dev"
@@ -76,7 +77,7 @@ Requires-Dist: testcontainers[rabbitmq]>=4.0.0; extra == "integration"
76
77
  Requires-Dist: docker>=7.0.0; extra == "integration"
77
78
  Dynamic: license-file
78
79
 
79
- <p align="center"><img src="assets/logo.svg" alt="rabbitkit" width="420"></p>
80
+ <p align="center"><img src="https://raw.githubusercontent.com/talaatmagdyx/rabbitkit/main/assets/logo.svg" alt="rabbitkit" width="420"></p>
80
81
 
81
82
  # rabbitkit
82
83
 
@@ -84,10 +85,10 @@ Dynamic: license-file
84
85
 
85
86
  [![PyPI](https://img.shields.io/pypi/v/rabbitkit)](https://pypi.org/project/rabbitkit/)
86
87
  [![CI](https://github.com/talaatmagdyx/rabbitkit/actions/workflows/ci.yml/badge.svg)](https://github.com/talaatmagdyx/rabbitkit/actions/workflows/ci.yml)
87
- [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](pyproject.toml)
88
- [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
89
- [![Typed](https://img.shields.io/badge/types-mypy%20--strict-blue)](pyproject.toml)
90
- [![Style](https://img.shields.io/badge/style-ruff-261230)](pyproject.toml)
88
+ [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](https://github.com/talaatmagdyx/rabbitkit/blob/main/pyproject.toml)
89
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/talaatmagdyx/rabbitkit/blob/main/LICENSE)
90
+ [![Typed](https://img.shields.io/badge/types-mypy%20--strict-blue)](https://github.com/talaatmagdyx/rabbitkit/blob/main/pyproject.toml)
91
+ [![Style](https://img.shields.io/badge/style-ruff-261230)](https://github.com/talaatmagdyx/rabbitkit/blob/main/pyproject.toml)
91
92
 
92
93
  rabbitkit is a **RabbitMQ-first toolkit for Python services**. It gives you
93
94
  clean decorators, safe retries, dead-letter queues, publisher confirms,
@@ -192,6 +193,8 @@ tests, production-ready lifecycle.
192
193
 
193
194
  ## Installation
194
195
 
196
+ Available on PyPI: **[pypi.org/project/rabbitkit](https://pypi.org/project/rabbitkit/)**
197
+
195
198
  ```bash
196
199
  pip install rabbitkit[async] # AsyncBroker (aio-pika)
197
200
  pip install rabbitkit[sync] # SyncBroker (pika)
@@ -401,13 +404,13 @@ only when loss is acceptable.
401
404
  ## Production profile
402
405
 
403
406
  The recommended baseline (see the
404
- [production checklist](docs/production/checklist.md)): quorum queues (+
407
+ [production checklist](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/production/checklist.md)): quorum queues (+
405
408
  `delivery_limit`), per-queue retry/DLQ topology, publisher confirms on,
406
409
  mandatory publishing where routing matters, checked `PublishOutcome`s,
407
410
  explicit ack policies, structured logs, split readiness/liveness probes,
408
411
  management-API metrics for queue depth and consumer lag, idempotent
409
412
  handlers. Migrating existing classic queues to quorum? There's a tool:
410
- `rabbitkit topology migrate` ([guide](docs/quorum-migration.md)).
413
+ `rabbitkit topology migrate` ([guide](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/quorum-migration.md)).
411
414
 
412
415
  ## Observability
413
416
 
@@ -430,7 +433,7 @@ middleware (bring any `CircuitBreakerProtocol` implementation, e.g.
430
433
  pybreaker).
431
434
 
432
435
  **Experimental** (may change without a deprecation cycle — read the
433
- [stability policy](docs/stability-policy.md)): RPC over direct reply-to,
436
+ [stability policy](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/stability-policy.md)): RPC over direct reply-to,
434
437
  distributed locking, message signing, result backends, stream queues, the
435
438
  monitoring dashboard. Notable caveats: the default signing nonce cache is
436
439
  per-process (use a shared cache for real replay protection), and never
@@ -494,7 +497,7 @@ rabbitkit health myapp.main:broker
494
497
  The DLQ replay acks a message only after its republish is broker-confirmed —
495
498
  the recovery tool cannot itself lose messages.
496
499
 
497
- <p align="center"><img src="assets/demo.svg" alt="rabbitkit dlq inspect and replay demo" width="720"></p>
500
+ <p align="center"><img src="https://raw.githubusercontent.com/talaatmagdyx/rabbitkit/main/assets/demo.svg" alt="rabbitkit dlq inspect and replay demo" width="720"></p>
498
501
 
499
502
  ## Where rabbitkit fits
500
503
 
@@ -520,7 +523,7 @@ test infrastructure in every service.
520
523
  - at-most-once behavior is acceptable and a raw client is enough
521
524
 
522
525
  A detailed framework-by-framework comparison lives in
523
- [docs/comparison.md](docs/comparison.md).
526
+ [docs/comparison.md](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/comparison.md).
524
527
 
525
528
  ## Architecture
526
529
 
@@ -551,25 +554,25 @@ Python ≥ 3.11 (tested: 3.11 / 3.12 / 3.13 / 3.14; 3.15 pre-release experimenta
551
554
 
552
555
  **📚 Full rendered docs: [talaatmagdyx.github.io/rabbitkit](https://talaatmagdyx.github.io/rabbitkit/)**
553
556
 
554
- - [Getting Started](docs/guide/getting-started.md)
555
- - [Full Guide](docs/guide/full-guide.md)
556
- - [Message Safety](docs/message-safety.md)
557
- - [Retry & DLQ](docs/retry-and-dlq.md)
558
- - [Production Checklist](docs/production/checklist.md)
559
- - [Idempotency Contract](docs/production/idempotency.md)
560
- - [Kubernetes](docs/kubernetes.md)
561
- - [Quorum Migration](docs/quorum-migration.md)
562
- - [Security](docs/security.md)
563
- - [Stability Policy](docs/stability-policy.md)
564
- - [Troubleshooting](docs/troubleshooting.md)
557
+ - [Getting Started](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/guide/getting-started.md)
558
+ - [Full Guide](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/guide/full-guide.md)
559
+ - [Message Safety](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/message-safety.md)
560
+ - [Retry & DLQ](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/retry-and-dlq.md)
561
+ - [Production Checklist](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/production/checklist.md)
562
+ - [Idempotency Contract](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/production/idempotency.md)
563
+ - [Kubernetes](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/kubernetes.md)
564
+ - [Quorum Migration](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/quorum-migration.md)
565
+ - [Security](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/security.md)
566
+ - [Stability Policy](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/stability-policy.md)
567
+ - [Troubleshooting](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/troubleshooting.md)
565
568
 
566
569
  ## Contributing & security
567
570
 
568
- See [CONTRIBUTING.md](CONTRIBUTING.md) for local development and quality
571
+ See [CONTRIBUTING.md](https://github.com/talaatmagdyx/rabbitkit/blob/main/CONTRIBUTING.md) for local development and quality
569
572
  gates (ruff, `mypy --strict`, near-total test coverage — the bar is real).
570
- Found a vulnerability? Follow [SECURITY.md](SECURITY.md) and report it
573
+ Found a vulnerability? Follow [SECURITY.md](https://github.com/talaatmagdyx/rabbitkit/blob/main/SECURITY.md) and report it
571
574
  privately.
572
575
 
573
576
  ## License
574
577
 
575
- [MIT](LICENSE)
578
+ [MIT](https://github.com/talaatmagdyx/rabbitkit/blob/main/LICENSE)
@@ -1,4 +1,4 @@
1
- <p align="center"><img src="assets/logo.svg" alt="rabbitkit" width="420"></p>
1
+ <p align="center"><img src="https://raw.githubusercontent.com/talaatmagdyx/rabbitkit/main/assets/logo.svg" alt="rabbitkit" width="420"></p>
2
2
 
3
3
  # rabbitkit
4
4
 
@@ -6,10 +6,10 @@
6
6
 
7
7
  [![PyPI](https://img.shields.io/pypi/v/rabbitkit)](https://pypi.org/project/rabbitkit/)
8
8
  [![CI](https://github.com/talaatmagdyx/rabbitkit/actions/workflows/ci.yml/badge.svg)](https://github.com/talaatmagdyx/rabbitkit/actions/workflows/ci.yml)
9
- [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](pyproject.toml)
10
- [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
11
- [![Typed](https://img.shields.io/badge/types-mypy%20--strict-blue)](pyproject.toml)
12
- [![Style](https://img.shields.io/badge/style-ruff-261230)](pyproject.toml)
9
+ [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](https://github.com/talaatmagdyx/rabbitkit/blob/main/pyproject.toml)
10
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/talaatmagdyx/rabbitkit/blob/main/LICENSE)
11
+ [![Typed](https://img.shields.io/badge/types-mypy%20--strict-blue)](https://github.com/talaatmagdyx/rabbitkit/blob/main/pyproject.toml)
12
+ [![Style](https://img.shields.io/badge/style-ruff-261230)](https://github.com/talaatmagdyx/rabbitkit/blob/main/pyproject.toml)
13
13
 
14
14
  rabbitkit is a **RabbitMQ-first toolkit for Python services**. It gives you
15
15
  clean decorators, safe retries, dead-letter queues, publisher confirms,
@@ -114,6 +114,8 @@ tests, production-ready lifecycle.
114
114
 
115
115
  ## Installation
116
116
 
117
+ Available on PyPI: **[pypi.org/project/rabbitkit](https://pypi.org/project/rabbitkit/)**
118
+
117
119
  ```bash
118
120
  pip install rabbitkit[async] # AsyncBroker (aio-pika)
119
121
  pip install rabbitkit[sync] # SyncBroker (pika)
@@ -323,13 +325,13 @@ only when loss is acceptable.
323
325
  ## Production profile
324
326
 
325
327
  The recommended baseline (see the
326
- [production checklist](docs/production/checklist.md)): quorum queues (+
328
+ [production checklist](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/production/checklist.md)): quorum queues (+
327
329
  `delivery_limit`), per-queue retry/DLQ topology, publisher confirms on,
328
330
  mandatory publishing where routing matters, checked `PublishOutcome`s,
329
331
  explicit ack policies, structured logs, split readiness/liveness probes,
330
332
  management-API metrics for queue depth and consumer lag, idempotent
331
333
  handlers. Migrating existing classic queues to quorum? There's a tool:
332
- `rabbitkit topology migrate` ([guide](docs/quorum-migration.md)).
334
+ `rabbitkit topology migrate` ([guide](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/quorum-migration.md)).
333
335
 
334
336
  ## Observability
335
337
 
@@ -352,7 +354,7 @@ middleware (bring any `CircuitBreakerProtocol` implementation, e.g.
352
354
  pybreaker).
353
355
 
354
356
  **Experimental** (may change without a deprecation cycle — read the
355
- [stability policy](docs/stability-policy.md)): RPC over direct reply-to,
357
+ [stability policy](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/stability-policy.md)): RPC over direct reply-to,
356
358
  distributed locking, message signing, result backends, stream queues, the
357
359
  monitoring dashboard. Notable caveats: the default signing nonce cache is
358
360
  per-process (use a shared cache for real replay protection), and never
@@ -416,7 +418,7 @@ rabbitkit health myapp.main:broker
416
418
  The DLQ replay acks a message only after its republish is broker-confirmed —
417
419
  the recovery tool cannot itself lose messages.
418
420
 
419
- <p align="center"><img src="assets/demo.svg" alt="rabbitkit dlq inspect and replay demo" width="720"></p>
421
+ <p align="center"><img src="https://raw.githubusercontent.com/talaatmagdyx/rabbitkit/main/assets/demo.svg" alt="rabbitkit dlq inspect and replay demo" width="720"></p>
420
422
 
421
423
  ## Where rabbitkit fits
422
424
 
@@ -442,7 +444,7 @@ test infrastructure in every service.
442
444
  - at-most-once behavior is acceptable and a raw client is enough
443
445
 
444
446
  A detailed framework-by-framework comparison lives in
445
- [docs/comparison.md](docs/comparison.md).
447
+ [docs/comparison.md](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/comparison.md).
446
448
 
447
449
  ## Architecture
448
450
 
@@ -473,25 +475,25 @@ Python ≥ 3.11 (tested: 3.11 / 3.12 / 3.13 / 3.14; 3.15 pre-release experimenta
473
475
 
474
476
  **📚 Full rendered docs: [talaatmagdyx.github.io/rabbitkit](https://talaatmagdyx.github.io/rabbitkit/)**
475
477
 
476
- - [Getting Started](docs/guide/getting-started.md)
477
- - [Full Guide](docs/guide/full-guide.md)
478
- - [Message Safety](docs/message-safety.md)
479
- - [Retry & DLQ](docs/retry-and-dlq.md)
480
- - [Production Checklist](docs/production/checklist.md)
481
- - [Idempotency Contract](docs/production/idempotency.md)
482
- - [Kubernetes](docs/kubernetes.md)
483
- - [Quorum Migration](docs/quorum-migration.md)
484
- - [Security](docs/security.md)
485
- - [Stability Policy](docs/stability-policy.md)
486
- - [Troubleshooting](docs/troubleshooting.md)
478
+ - [Getting Started](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/guide/getting-started.md)
479
+ - [Full Guide](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/guide/full-guide.md)
480
+ - [Message Safety](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/message-safety.md)
481
+ - [Retry & DLQ](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/retry-and-dlq.md)
482
+ - [Production Checklist](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/production/checklist.md)
483
+ - [Idempotency Contract](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/production/idempotency.md)
484
+ - [Kubernetes](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/kubernetes.md)
485
+ - [Quorum Migration](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/quorum-migration.md)
486
+ - [Security](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/security.md)
487
+ - [Stability Policy](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/stability-policy.md)
488
+ - [Troubleshooting](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/troubleshooting.md)
487
489
 
488
490
  ## Contributing & security
489
491
 
490
- See [CONTRIBUTING.md](CONTRIBUTING.md) for local development and quality
492
+ See [CONTRIBUTING.md](https://github.com/talaatmagdyx/rabbitkit/blob/main/CONTRIBUTING.md) for local development and quality
491
493
  gates (ruff, `mypy --strict`, near-total test coverage — the bar is real).
492
- Found a vulnerability? Follow [SECURITY.md](SECURITY.md) and report it
494
+ Found a vulnerability? Follow [SECURITY.md](https://github.com/talaatmagdyx/rabbitkit/blob/main/SECURITY.md) and report it
493
495
  privately.
494
496
 
495
497
  ## License
496
498
 
497
- [MIT](LICENSE)
499
+ [MIT](https://github.com/talaatmagdyx/rabbitkit/blob/main/LICENSE)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "rabbitkit"
3
- version = "0.9.0"
3
+ version = "0.9.1"
4
4
  description = "Production-grade RabbitMQ toolkit — sync/async, decorator routing, retry, compression, full configurability"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -50,6 +50,7 @@ dev = [
50
50
  "pytest>=8.0",
51
51
  "httpx>=0.27,<1.0", # starlette TestClient (dashboard tests)
52
52
  "prometheus-client>=0.20,<2.0", # PrometheusCollector tests
53
+ "psutil>=5.9", # bench_resources (memory/FD tracking)
53
54
  "mkdocs>=1.6.0",
54
55
  "mkdocs-material>=9.5.0",
55
56
  "mkdocstrings[python]>=0.26.0",
@@ -138,3 +139,12 @@ warn_unused_configs = true
138
139
  asyncio_mode = "auto"
139
140
  testpaths = ["tests"]
140
141
  markers = ["integration: integration tests (may need RabbitMQ)"]
142
+ filterwarnings = [
143
+ # rabbitkit's own operator-guidance warnings, exercised deliberately by
144
+ # tests (dedicated pytest.warns tests still assert they fire — pytest.warns
145
+ # bypasses these filters). Real users still see them.
146
+ "ignore:Starting a single-worker sync consumer:RuntimeWarning",
147
+ "ignore:SigningMiddleware is using the default in-memory:RuntimeWarning",
148
+ # third-party: testcontainers' own deprecated decorator
149
+ "ignore:The @wait_container_is_ready decorator is deprecated:DeprecationWarning",
150
+ ]
@@ -1,3 +1,3 @@
1
1
  """rabbitkit version — single source of truth."""
2
2
 
3
- __version__ = "0.9.0"
3
+ __version__ = "0.9.1"
@@ -219,6 +219,17 @@ class AsyncBroker:
219
219
  if self._in_flight == 0:
220
220
  cond.notify_all()
221
221
 
222
+ @property
223
+ def started(self) -> bool:
224
+ """True between a successful ``start()`` and ``stop()``.
225
+
226
+ Public counterpart of ``_started`` — health checks
227
+ (:func:`rabbitkit.health.broker_health_check`) read this instead of
228
+ falling back to the private attribute (which emits a
229
+ DeprecationWarning).
230
+ """
231
+ return self._started
232
+
222
233
  @property
223
234
  def config(self) -> RabbitConfig:
224
235
  return self._config
@@ -37,6 +37,37 @@ from rabbitkit.serialization.base import Serializer
37
37
  logger = structlog.stdlib.get_logger(__name__)
38
38
  _stdlib_logger = logging.getLogger(__name__)
39
39
 
40
+ # Transport "channel/connection died" exception class names. Matched by NAME
41
+ # (not isinstance) because core/ never imports pika or aio-pika. Covers pika
42
+ # (ChannelWrongStateError, ChannelClosed*, ConnectionClosed*, StreamLostError)
43
+ # and aio-pika/aiormq (ChannelInvalidStateError, AMQPConnectionError).
44
+ _CHANNEL_GONE_NAMES = frozenset(
45
+ {
46
+ "ChannelWrongStateError",
47
+ "ChannelClosed",
48
+ "ChannelClosedByBroker",
49
+ "ChannelClosedByClient",
50
+ "ChannelInvalidStateError",
51
+ "ConnectionClosed",
52
+ "ConnectionClosedByBroker",
53
+ "ConnectionWrongStateError",
54
+ "AMQPConnectionError",
55
+ "StreamLostError",
56
+ }
57
+ )
58
+
59
+
60
+ def _is_channel_gone(exc: BaseException) -> bool:
61
+ """True if *exc* (or its cause chain) is a transport channel/connection-death error."""
62
+ seen: set[int] = set()
63
+ current: BaseException | None = exc
64
+ while current is not None and id(current) not in seen:
65
+ if type(current).__name__ in _CHANNEL_GONE_NAMES:
66
+ return True
67
+ seen.add(id(current))
68
+ current = current.__cause__ or current.__context__
69
+ return False
70
+
40
71
  # M10: on the async path, bodies at/above this size are decoded in a worker
41
72
  # thread (asyncio.to_thread) so a large JSON/msgspec/pydantic parse doesn't
42
73
  # block the event loop. Below it, inline decode is faster than the thread hop.
@@ -1220,6 +1251,28 @@ class HandlerPipeline:
1220
1251
  exc: Exception,
1221
1252
  ) -> None:
1222
1253
  """Handle exception in sync pipeline per AckPolicy (Contract 1)."""
1254
+ try:
1255
+ self._handle_sync_exception_inner(route, message, exc)
1256
+ except Exception as settle_exc:
1257
+ # The settlement attempt itself failed because the channel or
1258
+ # connection died (SIGTERM drain, broker restart, network cut).
1259
+ # Nothing further can be settled on a dead channel and the broker
1260
+ # will redeliver the unacked message — warn instead of letting a
1261
+ # secondary exception escape as a full ERROR traceback.
1262
+ if not _is_channel_gone(settle_exc):
1263
+ raise
1264
+ logger.warning(
1265
+ "Channel closed before settlement (%s); leaving message "
1266
+ "unsettled — the broker will redeliver it (at-least-once)",
1267
+ type(settle_exc).__name__,
1268
+ )
1269
+
1270
+ def _handle_sync_exception_inner(
1271
+ self,
1272
+ route: RouteDefinition,
1273
+ message: RabbitMessage,
1274
+ exc: Exception,
1275
+ ) -> None:
1223
1276
  if message.is_settled:
1224
1277
  # Already settled (e.g., MANUAL mode handler settled then raised)
1225
1278
  logger.warning("Exception after settlement: %s", exc)
@@ -1261,6 +1314,25 @@ class HandlerPipeline:
1261
1314
  exc: Exception,
1262
1315
  ) -> None:
1263
1316
  """Handle exception in async pipeline per AckPolicy (Contract 1)."""
1317
+ try:
1318
+ await self._handle_async_exception_inner(route, message, exc)
1319
+ except Exception as settle_exc:
1320
+ # See _handle_sync_exception: a dead channel means nothing can be
1321
+ # settled and the broker redelivers — warn, don't raise.
1322
+ if not _is_channel_gone(settle_exc):
1323
+ raise
1324
+ logger.warning(
1325
+ "Channel closed before settlement (%s); leaving message "
1326
+ "unsettled — the broker will redeliver it (at-least-once)",
1327
+ type(settle_exc).__name__,
1328
+ )
1329
+
1330
+ async def _handle_async_exception_inner(
1331
+ self,
1332
+ route: RouteDefinition,
1333
+ message: RabbitMessage,
1334
+ exc: Exception,
1335
+ ) -> None:
1264
1336
  if message.is_settled:
1265
1337
  logger.warning("Exception after settlement: %s", exc)
1266
1338
  return
@@ -146,6 +146,17 @@ class SyncBroker:
146
146
  self._transport.on_blocked(value.on_blocked)
147
147
  self._transport.on_unblocked(value.on_unblocked)
148
148
 
149
+ @property
150
+ def started(self) -> bool:
151
+ """True between a successful ``start()`` and ``stop()``.
152
+
153
+ Public counterpart of ``_started`` — health checks
154
+ (:func:`rabbitkit.health.broker_health_check`) read this instead of
155
+ falling back to the private attribute (which emits a
156
+ DeprecationWarning).
157
+ """
158
+ return self._started
159
+
149
160
  @property
150
161
  def config(self) -> RabbitConfig:
151
162
  return self._config
@@ -930,7 +930,22 @@ class SyncTransport:
930
930
  while self._consuming:
931
931
  # process_data_events drains ALL channels' consumers + queued
932
932
  # add_callback_threadsafe callbacks (acks from worker threads).
933
- self._connection.process_data_events(time_limit=1.0)
933
+ try:
934
+ self._connection.process_data_events(time_limit=1.0)
935
+ except ValueError as exc:
936
+ # pika's SelectConnection ioloop raises a bare
937
+ # ValueError("Timeout closed before call") when the
938
+ # connection died between poll ticks (e.g. broker restart).
939
+ # Re-raise it as the connection error it really is so
940
+ # SyncBroker.run()'s recovery loop reconnects instead of
941
+ # the consumer thread dying on an unrecognized exception.
942
+ if self._connection.is_closed or "Timeout closed before call" in str(exc):
943
+ import pika.exceptions
944
+
945
+ raise pika.exceptions.AMQPConnectionError(
946
+ f"connection lost mid-poll: {exc}"
947
+ ) from exc
948
+ raise
934
949
  # L14: process_data_events returning (rather than raising a
935
950
  # connection error) is itself evidence the I/O loop is alive
936
951
  # and pumping -- fire once per tick regardless of whether any
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rabbitkit
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: Production-grade RabbitMQ toolkit — sync/async, decorator routing, retry, compression, full configurability
5
5
  Author-email: Talaat Magdy <talaatmagdy75@gmail.com>
6
6
  License-Expression: MIT
@@ -60,6 +60,7 @@ Requires-Dist: rabbitkit[all]; extra == "dev"
60
60
  Requires-Dist: pytest>=8.0; extra == "dev"
61
61
  Requires-Dist: httpx<1.0,>=0.27; extra == "dev"
62
62
  Requires-Dist: prometheus-client<2.0,>=0.20; extra == "dev"
63
+ Requires-Dist: psutil>=5.9; extra == "dev"
63
64
  Requires-Dist: mkdocs>=1.6.0; extra == "dev"
64
65
  Requires-Dist: mkdocs-material>=9.5.0; extra == "dev"
65
66
  Requires-Dist: mkdocstrings[python]>=0.26.0; extra == "dev"
@@ -76,7 +77,7 @@ Requires-Dist: testcontainers[rabbitmq]>=4.0.0; extra == "integration"
76
77
  Requires-Dist: docker>=7.0.0; extra == "integration"
77
78
  Dynamic: license-file
78
79
 
79
- <p align="center"><img src="assets/logo.svg" alt="rabbitkit" width="420"></p>
80
+ <p align="center"><img src="https://raw.githubusercontent.com/talaatmagdyx/rabbitkit/main/assets/logo.svg" alt="rabbitkit" width="420"></p>
80
81
 
81
82
  # rabbitkit
82
83
 
@@ -84,10 +85,10 @@ Dynamic: license-file
84
85
 
85
86
  [![PyPI](https://img.shields.io/pypi/v/rabbitkit)](https://pypi.org/project/rabbitkit/)
86
87
  [![CI](https://github.com/talaatmagdyx/rabbitkit/actions/workflows/ci.yml/badge.svg)](https://github.com/talaatmagdyx/rabbitkit/actions/workflows/ci.yml)
87
- [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](pyproject.toml)
88
- [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
89
- [![Typed](https://img.shields.io/badge/types-mypy%20--strict-blue)](pyproject.toml)
90
- [![Style](https://img.shields.io/badge/style-ruff-261230)](pyproject.toml)
88
+ [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](https://github.com/talaatmagdyx/rabbitkit/blob/main/pyproject.toml)
89
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/talaatmagdyx/rabbitkit/blob/main/LICENSE)
90
+ [![Typed](https://img.shields.io/badge/types-mypy%20--strict-blue)](https://github.com/talaatmagdyx/rabbitkit/blob/main/pyproject.toml)
91
+ [![Style](https://img.shields.io/badge/style-ruff-261230)](https://github.com/talaatmagdyx/rabbitkit/blob/main/pyproject.toml)
91
92
 
92
93
  rabbitkit is a **RabbitMQ-first toolkit for Python services**. It gives you
93
94
  clean decorators, safe retries, dead-letter queues, publisher confirms,
@@ -192,6 +193,8 @@ tests, production-ready lifecycle.
192
193
 
193
194
  ## Installation
194
195
 
196
+ Available on PyPI: **[pypi.org/project/rabbitkit](https://pypi.org/project/rabbitkit/)**
197
+
195
198
  ```bash
196
199
  pip install rabbitkit[async] # AsyncBroker (aio-pika)
197
200
  pip install rabbitkit[sync] # SyncBroker (pika)
@@ -401,13 +404,13 @@ only when loss is acceptable.
401
404
  ## Production profile
402
405
 
403
406
  The recommended baseline (see the
404
- [production checklist](docs/production/checklist.md)): quorum queues (+
407
+ [production checklist](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/production/checklist.md)): quorum queues (+
405
408
  `delivery_limit`), per-queue retry/DLQ topology, publisher confirms on,
406
409
  mandatory publishing where routing matters, checked `PublishOutcome`s,
407
410
  explicit ack policies, structured logs, split readiness/liveness probes,
408
411
  management-API metrics for queue depth and consumer lag, idempotent
409
412
  handlers. Migrating existing classic queues to quorum? There's a tool:
410
- `rabbitkit topology migrate` ([guide](docs/quorum-migration.md)).
413
+ `rabbitkit topology migrate` ([guide](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/quorum-migration.md)).
411
414
 
412
415
  ## Observability
413
416
 
@@ -430,7 +433,7 @@ middleware (bring any `CircuitBreakerProtocol` implementation, e.g.
430
433
  pybreaker).
431
434
 
432
435
  **Experimental** (may change without a deprecation cycle — read the
433
- [stability policy](docs/stability-policy.md)): RPC over direct reply-to,
436
+ [stability policy](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/stability-policy.md)): RPC over direct reply-to,
434
437
  distributed locking, message signing, result backends, stream queues, the
435
438
  monitoring dashboard. Notable caveats: the default signing nonce cache is
436
439
  per-process (use a shared cache for real replay protection), and never
@@ -494,7 +497,7 @@ rabbitkit health myapp.main:broker
494
497
  The DLQ replay acks a message only after its republish is broker-confirmed —
495
498
  the recovery tool cannot itself lose messages.
496
499
 
497
- <p align="center"><img src="assets/demo.svg" alt="rabbitkit dlq inspect and replay demo" width="720"></p>
500
+ <p align="center"><img src="https://raw.githubusercontent.com/talaatmagdyx/rabbitkit/main/assets/demo.svg" alt="rabbitkit dlq inspect and replay demo" width="720"></p>
498
501
 
499
502
  ## Where rabbitkit fits
500
503
 
@@ -520,7 +523,7 @@ test infrastructure in every service.
520
523
  - at-most-once behavior is acceptable and a raw client is enough
521
524
 
522
525
  A detailed framework-by-framework comparison lives in
523
- [docs/comparison.md](docs/comparison.md).
526
+ [docs/comparison.md](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/comparison.md).
524
527
 
525
528
  ## Architecture
526
529
 
@@ -551,25 +554,25 @@ Python ≥ 3.11 (tested: 3.11 / 3.12 / 3.13 / 3.14; 3.15 pre-release experimenta
551
554
 
552
555
  **📚 Full rendered docs: [talaatmagdyx.github.io/rabbitkit](https://talaatmagdyx.github.io/rabbitkit/)**
553
556
 
554
- - [Getting Started](docs/guide/getting-started.md)
555
- - [Full Guide](docs/guide/full-guide.md)
556
- - [Message Safety](docs/message-safety.md)
557
- - [Retry & DLQ](docs/retry-and-dlq.md)
558
- - [Production Checklist](docs/production/checklist.md)
559
- - [Idempotency Contract](docs/production/idempotency.md)
560
- - [Kubernetes](docs/kubernetes.md)
561
- - [Quorum Migration](docs/quorum-migration.md)
562
- - [Security](docs/security.md)
563
- - [Stability Policy](docs/stability-policy.md)
564
- - [Troubleshooting](docs/troubleshooting.md)
557
+ - [Getting Started](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/guide/getting-started.md)
558
+ - [Full Guide](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/guide/full-guide.md)
559
+ - [Message Safety](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/message-safety.md)
560
+ - [Retry & DLQ](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/retry-and-dlq.md)
561
+ - [Production Checklist](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/production/checklist.md)
562
+ - [Idempotency Contract](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/production/idempotency.md)
563
+ - [Kubernetes](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/kubernetes.md)
564
+ - [Quorum Migration](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/quorum-migration.md)
565
+ - [Security](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/security.md)
566
+ - [Stability Policy](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/stability-policy.md)
567
+ - [Troubleshooting](https://github.com/talaatmagdyx/rabbitkit/blob/main/docs/troubleshooting.md)
565
568
 
566
569
  ## Contributing & security
567
570
 
568
- See [CONTRIBUTING.md](CONTRIBUTING.md) for local development and quality
571
+ See [CONTRIBUTING.md](https://github.com/talaatmagdyx/rabbitkit/blob/main/CONTRIBUTING.md) for local development and quality
569
572
  gates (ruff, `mypy --strict`, near-total test coverage — the bar is real).
570
- Found a vulnerability? Follow [SECURITY.md](SECURITY.md) and report it
573
+ Found a vulnerability? Follow [SECURITY.md](https://github.com/talaatmagdyx/rabbitkit/blob/main/SECURITY.md) and report it
571
574
  privately.
572
575
 
573
576
  ## License
574
577
 
575
- [MIT](LICENSE)
578
+ [MIT](https://github.com/talaatmagdyx/rabbitkit/blob/main/LICENSE)
@@ -24,6 +24,7 @@ rabbitkit[all]
24
24
  pytest>=8.0
25
25
  httpx<1.0,>=0.27
26
26
  prometheus-client<2.0,>=0.20
27
+ psutil>=5.9
27
28
  mkdocs>=1.6.0
28
29
  mkdocs-material>=9.5.0
29
30
  mkdocstrings[python]>=0.26.0
File without changes
File without changes