redqueue 0.11.2__tar.gz → 0.12.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 (57) hide show
  1. {redqueue-0.11.2 → redqueue-0.12.0}/CHANGELOG.md +41 -11
  2. {redqueue-0.11.2 → redqueue-0.12.0}/PKG-INFO +41 -1
  3. {redqueue-0.11.2 → redqueue-0.12.0}/README-zh-CN.md +52 -12
  4. {redqueue-0.11.2 → redqueue-0.12.0}/README.md +54 -14
  5. {redqueue-0.11.2 → redqueue-0.12.0}/docs/API.md +44 -13
  6. {redqueue-0.11.2 → redqueue-0.12.0}/pyproject.toml +13 -10
  7. redqueue-0.12.0/src/redqueue/__main__.py +9 -0
  8. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/_version.py +1 -1
  9. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/async_client.py +93 -93
  10. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/backends/async_list.py +22 -1
  11. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/backends/list.py +22 -1
  12. redqueue-0.12.0/src/redqueue/cli.py +533 -0
  13. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/client.py +48 -48
  14. {redqueue-0.11.2 → redqueue-0.12.0}/tests/fakes.py +19 -16
  15. redqueue-0.12.0/tests/test_cli.py +332 -0
  16. {redqueue-0.11.2 → redqueue-0.12.0}/tests/test_project_skeleton.py +118 -89
  17. {redqueue-0.11.2 → redqueue-0.12.0}/.github/workflows/ci.yml +0 -0
  18. {redqueue-0.11.2 → redqueue-0.12.0}/.gitignore +0 -0
  19. {redqueue-0.11.2 → redqueue-0.12.0}/CODE_OF_CONDUCT.md +0 -0
  20. {redqueue-0.11.2 → redqueue-0.12.0}/CONTRIBUTING.md +0 -0
  21. {redqueue-0.11.2 → redqueue-0.12.0}/LICENSE +0 -0
  22. {redqueue-0.11.2 → redqueue-0.12.0}/NOTICE +0 -0
  23. {redqueue-0.11.2 → redqueue-0.12.0}/docs/RELEASE.md +0 -0
  24. {redqueue-0.11.2 → redqueue-0.12.0}/examples/README.md +0 -0
  25. {redqueue-0.11.2 → redqueue-0.12.0}/examples/__init__.py +0 -0
  26. {redqueue-0.11.2 → redqueue-0.12.0}/examples/async_list_queue.py +0 -0
  27. {redqueue-0.11.2 → redqueue-0.12.0}/examples/common.py +0 -0
  28. {redqueue-0.11.2 → redqueue-0.12.0}/examples/compatibility_check.py +0 -0
  29. {redqueue-0.11.2 → redqueue-0.12.0}/examples/custom_serializer.py +0 -0
  30. {redqueue-0.11.2 → redqueue-0.12.0}/examples/delayed_tasks.py +0 -0
  31. {redqueue-0.11.2 → redqueue-0.12.0}/examples/monitoring_hooks.py +0 -0
  32. {redqueue-0.11.2 → redqueue-0.12.0}/examples/stream_queue.py +0 -0
  33. {redqueue-0.11.2 → redqueue-0.12.0}/examples/sync_list_queue.py +0 -0
  34. {redqueue-0.11.2 → redqueue-0.12.0}/requirements.txt +0 -0
  35. {redqueue-0.11.2 → redqueue-0.12.0}/scripts/check.py +0 -0
  36. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/__init__.py +0 -0
  37. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/backends/__init__.py +0 -0
  38. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/backends/async_delay.py +0 -0
  39. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/backends/async_stream.py +0 -0
  40. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/backends/base.py +0 -0
  41. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/backends/delay.py +0 -0
  42. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/backends/stream.py +0 -0
  43. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/compat.py +0 -0
  44. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/config.py +0 -0
  45. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/connection.py +0 -0
  46. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/exceptions.py +0 -0
  47. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/message.py +0 -0
  48. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/monitoring.py +0 -0
  49. {redqueue-0.11.2 → redqueue-0.12.0}/src/redqueue/serialization.py +0 -0
  50. {redqueue-0.11.2 → redqueue-0.12.0}/tests/README.md +0 -0
  51. {redqueue-0.11.2 → redqueue-0.12.0}/tests/__init__.py +0 -0
  52. {redqueue-0.11.2 → redqueue-0.12.0}/tests/test_availability.py +0 -0
  53. {redqueue-0.11.2 → redqueue-0.12.0}/tests/test_backend_contracts.py +0 -0
  54. {redqueue-0.11.2 → redqueue-0.12.0}/tests/test_integration_redis.py +0 -0
  55. {redqueue-0.11.2 → redqueue-0.12.0}/tests/test_performance.py +0 -0
  56. {redqueue-0.11.2 → redqueue-0.12.0}/tests/test_real_redis_availability.py +0 -0
  57. {redqueue-0.11.2 → redqueue-0.12.0}/tests/test_real_redis_performance.py +0 -0
@@ -7,24 +7,54 @@ All notable public release changes are documented here.
7
7
  Development versions are tracked separately from formal release versions.
8
8
  开发版本与正式版本分开管理。
9
9
 
10
- ## [0.11.2] - 2026-06-21
10
+ ## [0.12.0] - 2026-06-23
11
+
12
+ ### Added
13
+
14
+ - Added the `redqueue` CLI and `python -m redqueue` module entry point for
15
+ developer debugging.
16
+ - Added CLI commands for Redis compatibility checks, queue statistics, message
17
+ publish, consume with optional ack/nack/retry, delayed scheduling, due
18
+ release, and dead-letter inspection.
19
+ - Added deterministic JSON CLI output and user-facing JSON validation errors.
20
+ - Added CLI unit tests with injected fake Redis and queue clients.
11
21
 
12
22
  ### Fixed
13
23
 
14
- - Fixed cleanup for directly constructed sync clients when owned Redis backend
15
- initialization fails.
16
- - Fixed cleanup for directly constructed async clients when lazy backend
17
- initialization fails.
18
- - Made sync and async client `close()` idempotent for owned Redis clients.
24
+ - Normalized `BRPOPLPUSH` timeout values to integer seconds for Redis versions
25
+ older than 6.2, improving Redis 5.x List consume compatibility.
26
+
27
+ ### 新增
28
+
29
+ - 新增 `redqueue` CLI 和 `python -m redqueue` 模块入口,方便开发者调试。
30
+ - 新增 Redis 兼容性检查、队列统计、消息发布、消费并可选 ack/nack/retry、
31
+ 延迟调度、到期释放和死信查看命令。
32
+ - CLI 输出稳定 JSON,并提供面向用户的 JSON 参数校验错误。
33
+ - 新增基于 fake Redis 和 fake queue client 的 CLI 单元测试。
19
34
 
20
35
  ### 修复
21
36
 
22
- - 修复直接构造同步客户端时,如果 owned Redis 的后端初始化失败,Redis client
23
- 未释放的问题。
24
- - 修复直接构造异步客户端时,如果懒加载后端初始化失败,Redis client 未释放的问题。
25
- - 同步和异步客户端的 `close()` 对 owned Redis client 变为幂等。
37
+ - Redis 6.2 以下版本使用 `BRPOPLPUSH` 时,将 timeout 规范为整数秒,
38
+ 提升 Redis 5.x List 消费兼容性。
39
+
40
+ ## [0.11.2] - 2026-06-21
26
41
 
27
- ## [0.11.1] - 2026-06-21
42
+ ### Fixed
43
+
44
+ - Fixed cleanup for directly constructed sync clients when owned Redis backend
45
+ initialization fails.
46
+ - Fixed cleanup for directly constructed async clients when lazy backend
47
+ initialization fails.
48
+ - Made sync and async client `close()` idempotent for owned Redis clients.
49
+
50
+ ### 修复
51
+
52
+ - 修复直接构造同步客户端时,如果 owned Redis 的后端初始化失败,Redis client
53
+ 未释放的问题。
54
+ - 修复直接构造异步客户端时,如果懒加载后端初始化失败,Redis client 未释放的问题。
55
+ - 同步和异步客户端的 `close()` 对 owned Redis client 变为幂等。
56
+
57
+ ## [0.11.1] - 2026-06-21
28
58
 
29
59
  ### Fixed
30
60
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redqueue
3
- Version: 0.11.2
3
+ Version: 0.12.0
4
4
  Summary: Redis-backed Python message queue library with List, Streams, delayed tasks, and monitoring.
5
5
  Project-URL: Homepage, https://github.com/SpringMirror-pear/redqueue
6
6
  Project-URL: Repository, https://github.com/SpringMirror-pear/redqueue.git
@@ -50,6 +50,7 @@ https://github.com/SpringMirror-pear/redqueue.git
50
50
  - Delayed tasks based on Redis Sorted Set.
51
51
  - Sync client `QueueClient` and async client `AsyncQueueClient`.
52
52
  - Redis connection pool managers for shared sync and async resources.
53
+ - `redqueue` CLI for local debugging and operational checks.
53
54
  - Unified exception hierarchy with structured context.
54
55
  - Monitoring events for publish, consume, ack, nack, retry, dead letter, delay,
55
56
  and backend errors.
@@ -80,12 +81,51 @@ Redis:
80
81
  pip install redqueue
81
82
  ```
82
83
 
84
+ The package installs a `redqueue` command. You can also run it with
85
+ `python -m redqueue` from a source checkout.
86
+
83
87
  For local development:
84
88
 
85
89
  ```bash
86
90
  python -m pip install -r requirements.txt
87
91
  ```
88
92
 
93
+ ## CLI
94
+
95
+ Check Redis compatibility:
96
+
97
+ ```bash
98
+ redqueue check --url redis://127.0.0.1:6379/0
99
+ ```
100
+
101
+ Inspect queue counts:
102
+
103
+ ```bash
104
+ redqueue stats --url redis://127.0.0.1:6379/0 --queue emails
105
+ ```
106
+
107
+ Publish and consume messages:
108
+
109
+ ```bash
110
+ redqueue publish --queue emails --payload '{"to":"user@example.com"}'
111
+ redqueue consume --queue emails --timeout 1 --ack
112
+ ```
113
+
114
+ Delayed task debugging:
115
+
116
+ ```bash
117
+ redqueue delay --queue emails --payload '{"to":"later@example.com"}' --delay-seconds 60
118
+ redqueue schedule-due --queue emails --limit 100
119
+ ```
120
+
121
+ Dead-letter inspection:
122
+
123
+ ```bash
124
+ redqueue dead-letters --queue emails --limit 20
125
+ ```
126
+
127
+ All command output is JSON so it can be piped into scripts or log processors.
128
+
89
129
  ## Quick Start
90
130
 
91
131
  Synchronous List queue:
@@ -14,9 +14,10 @@ https://github.com/SpringMirror-pear/redqueue.git
14
14
  `BRPOPLPUSH`。
15
15
  - 基于 Redis Streams 的消费组后端,Streams 要求 Redis `>=5.0`。
16
16
  - 基于 Redis Sorted Set 的延迟任务。
17
- - 同步客户端 `QueueClient` 与异步客户端 `AsyncQueueClient`。
18
- - 支持同步和异步 Redis 连接池管理器,方便多个客户端共享连接池。
19
- - 带结构化上下文的统一异常体系。
17
+ - 同步客户端 `QueueClient` 与异步客户端 `AsyncQueueClient`。
18
+ - 支持同步和异步 Redis 连接池管理器,方便多个客户端共享连接池。
19
+ - 提供 `redqueue` CLI,用于本地调试和运行时检查。
20
+ - 带结构化上下文的统一异常体系。
20
21
  - 针对发布、消费、确认、拒绝、重试、死信、延迟和后端错误的监控事件。
21
22
  - 通过 `INFO server` 探测 Redis 能力。
22
23
  - Apache License 2.0。
@@ -41,17 +42,56 @@ Redis:
41
42
 
42
43
  ## 安装
43
44
 
44
- ```bash
45
- pip install redqueue
46
- ```
47
-
48
- 本地开发:
45
+ ```bash
46
+ pip install redqueue
47
+ ```
48
+
49
+ 安装后会提供 `redqueue` 命令。从源码目录调试时也可以使用
50
+ `python -m redqueue`。
51
+
52
+ 本地开发:
49
53
 
50
54
  ```bash
51
- python -m pip install -r requirements.txt
52
- ```
53
-
54
- ## 快速开始
55
+ python -m pip install -r requirements.txt
56
+ ```
57
+
58
+ ## CLI
59
+
60
+ 检查 Redis 兼容性:
61
+
62
+ ```bash
63
+ redqueue check --url redis://127.0.0.1:6379/0
64
+ ```
65
+
66
+ 查看队列统计:
67
+
68
+ ```bash
69
+ redqueue stats --url redis://127.0.0.1:6379/0 --queue emails
70
+ ```
71
+
72
+ 发布和消费消息:
73
+
74
+ ```bash
75
+ redqueue publish --queue emails --payload '{"to":"user@example.com"}'
76
+ redqueue consume --queue emails --timeout 1 --ack
77
+ ```
78
+
79
+ 调试延迟任务:
80
+
81
+ ```bash
82
+ redqueue delay --queue emails --payload '{"to":"later@example.com"}' --delay-seconds 60
83
+ redqueue schedule-due --queue emails --limit 100
84
+ ```
85
+
86
+ 查看死信:
87
+
88
+ ```bash
89
+ redqueue dead-letters --queue emails --limit 20
90
+ ```
91
+
92
+ 所有命令都输出 JSON,方便接入脚本和日志处理流程。
93
+
94
+ ## 快速开始
55
95
 
56
96
  同步 List 队列:
57
97
 
@@ -15,9 +15,10 @@ https://github.com/SpringMirror-pear/redqueue.git
15
15
  fallback on older compatible Redis versions.
16
16
  - Redis Streams backend with consumer groups. Streams require Redis `>=5.0`.
17
17
  - Delayed tasks based on Redis Sorted Set.
18
- - Sync client `QueueClient` and async client `AsyncQueueClient`.
19
- - Redis connection pool managers for shared sync and async resources.
20
- - Unified exception hierarchy with structured context.
18
+ - Sync client `QueueClient` and async client `AsyncQueueClient`.
19
+ - Redis connection pool managers for shared sync and async resources.
20
+ - `redqueue` CLI for local debugging and operational checks.
21
+ - Unified exception hierarchy with structured context.
21
22
  - Monitoring events for publish, consume, ack, nack, retry, dead letter, delay,
22
23
  and backend errors.
23
24
  - Redis capability detection from `INFO server`.
@@ -43,17 +44,56 @@ Redis:
43
44
 
44
45
  ## Installation
45
46
 
46
- ```bash
47
- pip install redqueue
48
- ```
49
-
50
- For local development:
47
+ ```bash
48
+ pip install redqueue
49
+ ```
50
+
51
+ The package installs a `redqueue` command. You can also run it with
52
+ `python -m redqueue` from a source checkout.
53
+
54
+ For local development:
51
55
 
52
56
  ```bash
53
- python -m pip install -r requirements.txt
54
- ```
55
-
56
- ## Quick Start
57
+ python -m pip install -r requirements.txt
58
+ ```
59
+
60
+ ## CLI
61
+
62
+ Check Redis compatibility:
63
+
64
+ ```bash
65
+ redqueue check --url redis://127.0.0.1:6379/0
66
+ ```
67
+
68
+ Inspect queue counts:
69
+
70
+ ```bash
71
+ redqueue stats --url redis://127.0.0.1:6379/0 --queue emails
72
+ ```
73
+
74
+ Publish and consume messages:
75
+
76
+ ```bash
77
+ redqueue publish --queue emails --payload '{"to":"user@example.com"}'
78
+ redqueue consume --queue emails --timeout 1 --ack
79
+ ```
80
+
81
+ Delayed task debugging:
82
+
83
+ ```bash
84
+ redqueue delay --queue emails --payload '{"to":"later@example.com"}' --delay-seconds 60
85
+ redqueue schedule-due --queue emails --limit 100
86
+ ```
87
+
88
+ Dead-letter inspection:
89
+
90
+ ```bash
91
+ redqueue dead-letters --queue emails --limit 20
92
+ ```
93
+
94
+ All command output is JSON so it can be piped into scripts or log processors.
95
+
96
+ ## Quick Start
57
97
 
58
98
  Synchronous List queue:
59
99
 
@@ -195,7 +235,7 @@ RedQueue uses a lightweight Git Flow model:
195
235
  ## Documentation
196
236
 
197
237
  - Chinese README: [README-zh-CN.md](README-zh-CN.md)
198
- - API: [docs/API.md](docs/API.md)
238
+ - API: [docs/API.md](docs/API.md)
199
239
  - Examples: [examples/README.md](examples/README.md)
200
240
  - Changelog: [CHANGELOG.md](CHANGELOG.md)
201
241
  - Release process: [docs/RELEASE.md](docs/RELEASE.md)
@@ -205,7 +245,7 @@ RedQueue uses a lightweight Git Flow model:
205
245
 
206
246
  ## Examples
207
247
 
208
- The `examples/` directory contains runnable scripts for synchronous List queues,
248
+ The `examples/` directory contains runnable scripts for synchronous List queues,
209
249
  asynchronous List queues, Streams, delayed tasks, monitoring hooks, custom
210
250
  serializers, and Redis compatibility checks.
211
251
 
@@ -1,8 +1,8 @@
1
1
  # RedQueue API / RedQueue API 文档
2
2
 
3
- This document describes the public API available in RedQueue `0.11.2`.
3
+ This document describes the public API available in RedQueue `0.12.0`.
4
4
 
5
- 本文档描述 RedQueue `0.11.2` 的公开 API。
5
+ 本文档描述 RedQueue `0.12.0` 的公开 API。
6
6
 
7
7
  ## Clients / 客户端
8
8
 
@@ -303,14 +303,45 @@ Monitoring events do not include business payload by default.
303
303
 
304
304
  监控事件默认不包含业务 payload。
305
305
 
306
- ## Redis Capability Detection / Redis 能力探测
307
-
308
- - `RedisVersion`
309
- - `RedisCapabilities`
310
- - `extract_redis_version(info)`
311
- - `detect_capabilities(client)`
312
- - `detect_capabilities_async(client)`
313
-
314
- Streams are rejected with `RedisCompatibilityError` when Redis is below `5.0`.
315
-
316
- 当 Redis 低于 `5.0` 时,启用 Streams 会抛出 `RedisCompatibilityError`。
306
+ ## Redis Capability Detection / Redis 能力探测
307
+
308
+ - `RedisVersion`
309
+ - `RedisCapabilities`
310
+ - `extract_redis_version(info)`
311
+ - `detect_capabilities(client)`
312
+ - `detect_capabilities_async(client)`
313
+
314
+ Streams are rejected with `RedisCompatibilityError` when Redis is below `5.0`.
315
+
316
+ 当 Redis 低于 `5.0` 时,启用 Streams 会抛出 `RedisCompatibilityError`。
317
+
318
+ ## CLI / 命令行工具
319
+
320
+ RedQueue `0.12.0` provides a `redqueue` console command and a
321
+ `python -m redqueue` module entry point for developer diagnostics.
322
+
323
+ RedQueue `0.12.0` 提供 `redqueue` 控制台命令和 `python -m redqueue`
324
+ 模块入口,用于开发者调试。
325
+
326
+ Commands / 命令:
327
+
328
+ - `redqueue check --url redis://127.0.0.1:6379/0`
329
+ - `redqueue stats --queue emails [--backend list|stream]`
330
+ - `redqueue publish --queue emails --payload '{"to":"user@example.com"}'`
331
+ - `redqueue consume --queue emails [--ack|--nack|--retry]`
332
+ - `redqueue delay --queue emails --payload '{"to":"later@example.com"}' --delay-seconds 60`
333
+ - `redqueue schedule-due --queue emails --limit 100`
334
+ - `redqueue dead-letters --queue emails --limit 20`
335
+
336
+ Common options / 通用选项:
337
+
338
+ - `--url`: Redis URL. Defaults to `redis://127.0.0.1:6379/0`.
339
+ - `--queue`: RedQueue queue name.
340
+ - `--backend`: `list` or `stream`.
341
+ - `--namespace`: Redis key namespace. Defaults to `rq`.
342
+ - `--consumer-group`: Streams consumer group. Defaults to `redqueue`.
343
+ - `--consumer-name`: Optional Streams consumer name.
344
+
345
+ Payload and headers are JSON strings. Command responses are stable JSON objects.
346
+
347
+ Payload 和 headers 使用 JSON 字符串。命令响应为稳定 JSON 对象。
@@ -2,9 +2,9 @@
2
2
  requires = ["hatchling==1.30.1"]
3
3
  build-backend = "hatchling.build"
4
4
 
5
- [project]
6
- name = "redqueue"
7
- version = "0.11.2"
5
+ [project]
6
+ name = "redqueue"
7
+ version = "0.12.0"
8
8
  description = "Redis-backed Python message queue library with List, Streams, delayed tasks, and monitoring."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -33,13 +33,16 @@ dependencies = [
33
33
  "redis==6.4.0",
34
34
  ]
35
35
 
36
- [project.urls]
37
- Homepage = "https://github.com/SpringMirror-pear/redqueue"
38
- Repository = "https://github.com/SpringMirror-pear/redqueue.git"
39
- Issues = "https://github.com/SpringMirror-pear/redqueue/issues"
40
-
41
- [project.optional-dependencies]
42
- dev = [
36
+ [project.urls]
37
+ Homepage = "https://github.com/SpringMirror-pear/redqueue"
38
+ Repository = "https://github.com/SpringMirror-pear/redqueue.git"
39
+ Issues = "https://github.com/SpringMirror-pear/redqueue/issues"
40
+
41
+ [project.scripts]
42
+ redqueue = "redqueue.cli:main"
43
+
44
+ [project.optional-dependencies]
45
+ dev = [
43
46
  "pytest==9.1.1; python_version >= '3.10'",
44
47
  "pytest-asyncio==1.4.0; python_version >= '3.10'",
45
48
  "ruff==0.15.18",
@@ -0,0 +1,9 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Author: SpringMirror-pear
3
+
4
+ """Module entry point for ``python -m redqueue``."""
5
+
6
+ from redqueue.cli import main
7
+
8
+ if __name__ == "__main__":
9
+ raise SystemExit(main())
@@ -3,4 +3,4 @@
3
3
 
4
4
  """Package version."""
5
5
 
6
- __version__ = "0.11.2"
6
+ __version__ = "0.12.0"
@@ -56,12 +56,12 @@ class AsyncQueueClient:
56
56
  """
57
57
 
58
58
  self.config = config
59
- self.redis = redis
60
- self.capabilities = capabilities
61
- self._owns_redis = owns_redis
62
- self._closed = False
63
- self.backend: AsyncListBackend | AsyncStreamBackend | None = None
64
- self.delay_backend: AsyncDelayBackend | None = None
59
+ self.redis = redis
60
+ self.capabilities = capabilities
61
+ self._owns_redis = owns_redis
62
+ self._closed = False
63
+ self.backend: AsyncListBackend | AsyncStreamBackend | None = None
64
+ self.delay_backend: AsyncDelayBackend | None = None
65
65
  self.config.monitoring.emit(
66
66
  MonitoringEvent(
67
67
  type=MonitoringEventType.CLIENT_CREATED,
@@ -119,30 +119,30 @@ class AsyncQueueClient:
119
119
  if explicit_owns_redis is None
120
120
  else bool(explicit_owns_redis)
121
121
  )
122
- capabilities = options.pop("capabilities", None)
123
- if capabilities is None:
124
- try:
125
- capabilities = await detect_capabilities_async(
126
- cast(AsyncRedisInfoClient, redis)
127
- )
128
- except Exception:
129
- if owns_redis:
130
- await cls._close_redis(redis)
131
- raise
132
- try:
133
- config = QueueConfig(queue=queue, backend=backend, **options)
134
- except Exception:
135
- if owns_redis:
136
- await cls._close_redis(redis)
137
- raise
138
- client = cls(
139
- config=config,
140
- redis=redis,
141
- capabilities=capabilities,
142
- owns_redis=owns_redis,
143
- )
144
- await client._ensure_backend()
145
- return client
122
+ capabilities = options.pop("capabilities", None)
123
+ if capabilities is None:
124
+ try:
125
+ capabilities = await detect_capabilities_async(
126
+ cast(AsyncRedisInfoClient, redis)
127
+ )
128
+ except Exception:
129
+ if owns_redis:
130
+ await cls._close_redis(redis)
131
+ raise
132
+ try:
133
+ config = QueueConfig(queue=queue, backend=backend, **options)
134
+ except Exception:
135
+ if owns_redis:
136
+ await cls._close_redis(redis)
137
+ raise
138
+ client = cls(
139
+ config=config,
140
+ redis=redis,
141
+ capabilities=capabilities,
142
+ owns_redis=owns_redis,
143
+ )
144
+ await client._ensure_backend()
145
+ return client
146
146
 
147
147
  async def publish(
148
148
  self,
@@ -321,20 +321,20 @@ class AsyncQueueClient:
321
321
 
322
322
  await (await self._ensure_backend()).requeue_dead(message)
323
323
 
324
- async def close(self) -> None:
325
- """Close the async Redis client when this client owns it."""
326
-
327
- if self._closed or not self._owns_redis:
328
- return
329
-
330
- close = getattr(self.redis, "aclose", None) or getattr(
331
- self.redis,
332
- "close",
333
- None,
334
- )
335
- if close is not None:
336
- await self._call_close(close)
337
- self._closed = True
324
+ async def close(self) -> None:
325
+ """Close the async Redis client when this client owns it."""
326
+
327
+ if self._closed or not self._owns_redis:
328
+ return
329
+
330
+ close = getattr(self.redis, "aclose", None) or getattr(
331
+ self.redis,
332
+ "close",
333
+ None,
334
+ )
335
+ if close is not None:
336
+ await self._call_close(close)
337
+ self._closed = True
338
338
 
339
339
  async def __aenter__(self) -> AsyncQueueClient:
340
340
  """Enter an asynchronous resource-management context."""
@@ -364,55 +364,55 @@ class AsyncQueueClient:
364
364
 
365
365
  if self.backend is not None:
366
366
  return self.backend
367
- if self.config.backend_type is BackendType.LIST:
368
- if self.redis is None:
369
- raise TypeError("redis client is required for async List backend")
370
- capabilities = self.capabilities
371
- if capabilities is None:
372
- raise TypeError("Redis capabilities are required before backend use")
373
- try:
374
- self.backend = AsyncListBackend(self.redis, self.config, capabilities)
375
- return self.backend
376
- except Exception:
377
- await self.close()
378
- raise
379
- if self.config.backend_type is BackendType.STREAM:
380
- if self.redis is None:
381
- raise TypeError("redis client is required for async Streams backend")
382
- capabilities = self.capabilities
383
- if capabilities is None:
384
- raise TypeError("Redis capabilities are required before backend use")
385
- try:
386
- self.backend = await AsyncStreamBackend.create(
387
- self.redis,
388
- self.config,
389
- capabilities,
390
- )
391
- return self.backend
392
- except Exception:
393
- await self.close()
394
- raise
395
- raise NotImplementedError(
396
- f"backend {self.config.backend_type.value!r} is not implemented"
397
- )
398
-
399
- @staticmethod
400
- async def _close_redis(redis: Any) -> None:
401
- """Close an async Redis-like object if it exposes a close method."""
402
-
403
- close = getattr(redis, "aclose", None) or getattr(redis, "close", None)
404
- if close is not None:
405
- await AsyncQueueClient._call_close(close)
406
-
407
- @staticmethod
408
- async def _call_close(close: Any) -> None:
409
- """Call a sync or async close method."""
410
-
411
- result = close()
412
- if hasattr(result, "__await__"):
413
- await result
414
-
415
- async def _ensure_delay_backend(self) -> AsyncDelayBackend:
367
+ if self.config.backend_type is BackendType.LIST:
368
+ if self.redis is None:
369
+ raise TypeError("redis client is required for async List backend")
370
+ capabilities = self.capabilities
371
+ if capabilities is None:
372
+ raise TypeError("Redis capabilities are required before backend use")
373
+ try:
374
+ self.backend = AsyncListBackend(self.redis, self.config, capabilities)
375
+ return self.backend
376
+ except Exception:
377
+ await self.close()
378
+ raise
379
+ if self.config.backend_type is BackendType.STREAM:
380
+ if self.redis is None:
381
+ raise TypeError("redis client is required for async Streams backend")
382
+ capabilities = self.capabilities
383
+ if capabilities is None:
384
+ raise TypeError("Redis capabilities are required before backend use")
385
+ try:
386
+ self.backend = await AsyncStreamBackend.create(
387
+ self.redis,
388
+ self.config,
389
+ capabilities,
390
+ )
391
+ return self.backend
392
+ except Exception:
393
+ await self.close()
394
+ raise
395
+ raise NotImplementedError(
396
+ f"backend {self.config.backend_type.value!r} is not implemented"
397
+ )
398
+
399
+ @staticmethod
400
+ async def _close_redis(redis: Any) -> None:
401
+ """Close an async Redis-like object if it exposes a close method."""
402
+
403
+ close = getattr(redis, "aclose", None) or getattr(redis, "close", None)
404
+ if close is not None:
405
+ await AsyncQueueClient._call_close(close)
406
+
407
+ @staticmethod
408
+ async def _call_close(close: Any) -> None:
409
+ """Call a sync or async close method."""
410
+
411
+ result = close()
412
+ if hasattr(result, "__await__"):
413
+ await result
414
+
415
+ async def _ensure_delay_backend(self) -> AsyncDelayBackend:
416
416
  """Return the initialized async delay scheduler, creating it when needed."""
417
417
 
418
418
  if self.delay_backend is not None: