stemtrace 0.3.1__tar.gz → 0.3.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 (139) hide show
  1. {stemtrace-0.3.1 → stemtrace-0.3.2}/.bumpversion.toml +1 -1
  2. {stemtrace-0.3.1 → stemtrace-0.3.2}/CHANGELOG.md +5 -0
  3. {stemtrace-0.3.1 → stemtrace-0.3.2}/PKG-INFO +2 -2
  4. {stemtrace-0.3.1 → stemtrace-0.3.2}/README.md +1 -1
  5. {stemtrace-0.3.1 → stemtrace-0.3.2}/pyproject.toml +1 -1
  6. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/__init__.py +4 -1
  7. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/library/transports/redis.py +29 -0
  8. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/package.json +1 -1
  9. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_transports.py +38 -1
  10. {stemtrace-0.3.1 → stemtrace-0.3.2}/uv.lock +1 -1
  11. {stemtrace-0.3.1 → stemtrace-0.3.2}/.dockerignore +0 -0
  12. {stemtrace-0.3.1 → stemtrace-0.3.2}/.github/dependabot.yml +0 -0
  13. {stemtrace-0.3.1 → stemtrace-0.3.2}/.github/workflows/ci.yml +0 -0
  14. {stemtrace-0.3.1 → stemtrace-0.3.2}/.github/workflows/e2e.yml +0 -0
  15. {stemtrace-0.3.1 → stemtrace-0.3.2}/.github/workflows/release.yml +0 -0
  16. {stemtrace-0.3.1 → stemtrace-0.3.2}/.gitignore +0 -0
  17. {stemtrace-0.3.1 → stemtrace-0.3.2}/.pre-commit-config.yaml +0 -0
  18. {stemtrace-0.3.1 → stemtrace-0.3.2}/.python-version +0 -0
  19. {stemtrace-0.3.1 → stemtrace-0.3.2}/CONTRIBUTING.md +0 -0
  20. {stemtrace-0.3.1 → stemtrace-0.3.2}/Dockerfile +0 -0
  21. {stemtrace-0.3.1 → stemtrace-0.3.2}/Dockerfile.e2e +0 -0
  22. {stemtrace-0.3.1 → stemtrace-0.3.2}/LICENSE +0 -0
  23. {stemtrace-0.3.1 → stemtrace-0.3.2}/Makefile +0 -0
  24. {stemtrace-0.3.1 → stemtrace-0.3.2}/build_ui.py +0 -0
  25. {stemtrace-0.3.1 → stemtrace-0.3.2}/docker-compose.e2e.rabbitmq.yml +0 -0
  26. {stemtrace-0.3.1 → stemtrace-0.3.2}/docker-compose.e2e.yml +0 -0
  27. {stemtrace-0.3.1 → stemtrace-0.3.2}/docker-compose.rabbitmq.yml +0 -0
  28. {stemtrace-0.3.1 → stemtrace-0.3.2}/docker-compose.yml +0 -0
  29. {stemtrace-0.3.1 → stemtrace-0.3.2}/docs/screenshots/task_details.png +0 -0
  30. {stemtrace-0.3.1 → stemtrace-0.3.2}/docs/screenshots/unregistered.png +0 -0
  31. {stemtrace-0.3.1 → stemtrace-0.3.2}/docs/screenshots/workflow.png +0 -0
  32. {stemtrace-0.3.1 → stemtrace-0.3.2}/examples/celery_app.py +0 -0
  33. {stemtrace-0.3.1 → stemtrace-0.3.2}/examples/fastapi_integration.py +0 -0
  34. {stemtrace-0.3.1 → stemtrace-0.3.2}/examples/with_auth.py +0 -0
  35. {stemtrace-0.3.1 → stemtrace-0.3.2}/examples/with_login.py +0 -0
  36. {stemtrace-0.3.1 → stemtrace-0.3.2}/progress.md +0 -0
  37. {stemtrace-0.3.1 → stemtrace-0.3.2}/scripts/wait_for_http.py +0 -0
  38. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/core/__init__.py +0 -0
  39. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/core/events.py +0 -0
  40. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/core/exceptions.py +0 -0
  41. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/core/graph.py +0 -0
  42. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/core/ports.py +0 -0
  43. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/library/__init__.py +0 -0
  44. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/library/bootsteps.py +0 -0
  45. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/library/config.py +0 -0
  46. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/library/scrubbing.py +0 -0
  47. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/library/signals.py +0 -0
  48. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/library/transports/__init__.py +0 -0
  49. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/library/transports/memory.py +0 -0
  50. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/library/transports/rabbitmq.py +0 -0
  51. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/py.typed +0 -0
  52. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/__init__.py +0 -0
  53. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/__main__.py +0 -0
  54. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/api/__init__.py +0 -0
  55. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/api/routes.py +0 -0
  56. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/api/schemas.py +0 -0
  57. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/api/websocket.py +0 -0
  58. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/consumer.py +0 -0
  59. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/fastapi/__init__.py +0 -0
  60. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/fastapi/auth.py +0 -0
  61. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/fastapi/extension.py +0 -0
  62. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/fastapi/form_auth.py +0 -0
  63. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/fastapi/login_routes.py +0 -0
  64. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/fastapi/router.py +0 -0
  65. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/store.py +0 -0
  66. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/__init__.py +0 -0
  67. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/biome.json +0 -0
  68. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/index.html +0 -0
  69. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/package-lock.json +0 -0
  70. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/playwright.config.ts +0 -0
  71. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/postcss.config.js +0 -0
  72. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/api/client.ts +0 -0
  73. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/api/queries.ts +0 -0
  74. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/DateRangePicker.tsx +0 -0
  75. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/Filters.tsx +0 -0
  76. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/TaskGraph.tsx +0 -0
  77. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/TaskList.tsx +0 -0
  78. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/TaskStateBadge.tsx +0 -0
  79. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/TaskTimeline.tsx +0 -0
  80. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/TimelineBar.tsx +0 -0
  81. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/Tooltip.tsx +0 -0
  82. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/hooks/WebSocketContext.tsx +0 -0
  83. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/hooks/useInfiniteScroll.ts +0 -0
  84. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/index.css +0 -0
  85. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/main.tsx +0 -0
  86. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/__root.tsx +0 -0
  87. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/graph.$rootId.tsx +0 -0
  88. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/graphs.tsx +0 -0
  89. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/index.tsx +0 -0
  90. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/registry.tsx +0 -0
  91. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/tasks.$taskId.tsx +0 -0
  92. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/workers.tsx +0 -0
  93. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/utils/format.ts +0 -0
  94. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/vite-env.d.ts +0 -0
  95. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/fixtures/mock-api.ts +0 -0
  96. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/fixtures/mock-data.ts +0 -0
  97. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/graphs.spec.ts +0 -0
  98. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/registry.spec.ts +0 -0
  99. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/task-detail.spec.ts +0 -0
  100. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/tasks.spec.ts +0 -0
  101. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/workers.spec.ts +0 -0
  102. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tsconfig.json +0 -0
  103. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tsconfig.tsbuildinfo +0 -0
  104. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/vite.config.ts +0 -0
  105. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/static.py +0 -0
  106. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/ui/templates/login.html +0 -0
  107. {stemtrace-0.3.1 → stemtrace-0.3.2}/src/stemtrace/server/websocket.py +0 -0
  108. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/__init__.py +0 -0
  109. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/conftest.py +0 -0
  110. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/e2e/__init__.py +0 -0
  111. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/e2e/conftest.py +0 -0
  112. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/e2e/tasks.py +0 -0
  113. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/e2e/test_api_e2e.py +0 -0
  114. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/e2e/test_workers_e2e.py +0 -0
  115. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/integration/__init__.py +0 -0
  116. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/integration/test_embedded_static.py +0 -0
  117. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/integration/test_fastapi_integration.py +0 -0
  118. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/integration/test_rabbitmq_transport.py +0 -0
  119. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/integration/test_redis_transport.py +0 -0
  120. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/integration/test_static_ui.py +0 -0
  121. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/integration/test_workers_refresh.py +0 -0
  122. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/__init__.py +0 -0
  123. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_auth.py +0 -0
  124. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_bootsteps.py +0 -0
  125. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_config.py +0 -0
  126. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_consumer.py +0 -0
  127. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_events.py +0 -0
  128. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_examples.py +0 -0
  129. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_exceptions.py +0 -0
  130. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_form_auth.py +0 -0
  131. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_form_login.py +0 -0
  132. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_graph.py +0 -0
  133. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_init.py +0 -0
  134. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_routes.py +0 -0
  135. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_scrubbing.py +0 -0
  136. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_signals.py +0 -0
  137. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_store.py +0 -0
  138. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_ui_static_prefix.py +0 -0
  139. {stemtrace-0.3.1 → stemtrace-0.3.2}/tests/unit/test_websocket.py +0 -0
@@ -1,5 +1,5 @@
1
1
  [tool.bumpversion]
2
- current_version = "0.3.1"
2
+ current_version = "0.3.2"
3
3
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
4
4
  serialize = ["{major}.{minor}.{patch}"]
5
5
  commit = true
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.2] - 2026-02-28
11
+
12
+ ### Fixed
13
+ - Redis transport: normalize `ssl_cert_reqs` URL parameter from `CERT_REQUIRED`/`CERT_NONE`/`CERT_OPTIONAL` to lowercase values expected by redis-py
14
+
10
15
  ## [0.3.1] - 2026-01-04
11
16
 
12
17
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stemtrace
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: A lightweight Celery task flow visualizer
5
5
  Project-URL: Homepage, https://github.com/iansokolskyi/stemtrace
6
6
  Project-URL: Documentation, https://github.com/iansokolskyi/stemtrace#readme
@@ -56,7 +56,7 @@ Description-Content-Type: text/markdown
56
56
 
57
57
  **Zero-infrastructure Celery task flow visualizer**
58
58
 
59
- [![PyPI version](https://img.shields.io/badge/pypi-v0.3.1-darklime)](https://pypi.org/project/stemtrace)
59
+ [![PyPI version](https://img.shields.io/badge/pypi-v0.3.2-darklime)](https://pypi.org/project/stemtrace)
60
60
  [![Python](https://img.shields.io/pypi/pyversions/stemtrace.svg)](https://pypi.org/project/stemtrace/)
61
61
  [![CI](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml/badge.svg)](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml)
62
62
  [![codecov](https://codecov.io/gh/iansokolskyi/stemtrace/graph/badge.svg)](https://codecov.io/gh/iansokolskyi/stemtrace)
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Zero-infrastructure Celery task flow visualizer**
4
4
 
5
- [![PyPI version](https://img.shields.io/badge/pypi-v0.3.1-darklime)](https://pypi.org/project/stemtrace)
5
+ [![PyPI version](https://img.shields.io/badge/pypi-v0.3.2-darklime)](https://pypi.org/project/stemtrace)
6
6
  [![Python](https://img.shields.io/pypi/pyversions/stemtrace.svg)](https://pypi.org/project/stemtrace/)
7
7
  [![CI](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml/badge.svg)](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml)
8
8
  [![codecov](https://codecov.io/gh/iansokolskyi/stemtrace/graph/badge.svg)](https://codecov.io/gh/iansokolskyi/stemtrace)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stemtrace"
3
- version = "0.3.1"
3
+ version = "0.3.2"
4
4
  description = "A lightweight Celery task flow visualizer"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -43,7 +43,7 @@ if TYPE_CHECKING:
43
43
  from celery import Celery
44
44
  from fastapi import FastAPI
45
45
 
46
- __version__ = "0.3.1"
46
+ __version__ = "0.3.2"
47
47
  __all__ = [
48
48
  "ConfigurationError",
49
49
  "StemtraceConfig",
@@ -92,6 +92,7 @@ def init_worker(
92
92
  scrub_sensitive_data: bool = True,
93
93
  additional_sensitive_keys: frozenset[str] | None = None,
94
94
  safe_keys: frozenset[str] | None = None,
95
+ max_data_size: int = 10240,
95
96
  ) -> None:
96
97
  """Initialize stemtrace for Celery worker instrumentation.
97
98
 
@@ -105,6 +106,7 @@ def init_worker(
105
106
  scrub_sensitive_data: Whether to scrub sensitive keys (default: True).
106
107
  additional_sensitive_keys: Extra keys to treat as sensitive.
107
108
  safe_keys: Keys to never scrub (overrides sensitive).
109
+ max_data_size: Maximum size in bytes for serialized data (default: 10KB).
108
110
 
109
111
  Raises:
110
112
  ConfigurationError: If no broker URL can be determined.
@@ -127,6 +129,7 @@ def init_worker(
127
129
  scrub_sensitive_data=scrub_sensitive_data,
128
130
  additional_sensitive_keys=additional_sensitive_keys or frozenset(),
129
131
  safe_keys=safe_keys or frozenset(),
132
+ max_data_size=max_data_size,
130
133
  )
131
134
  set_config(config)
132
135
 
@@ -21,6 +21,34 @@ logger = logging.getLogger(__name__)
21
21
  # Type alias for all events that can be consumed from the stream
22
22
  StreamEvent = TaskEvent | WorkerEvent
23
23
 
24
+ # Mapping from CERT_* constants (used in URLs) to lowercase values expected by redis-py
25
+ _SSL_CERT_REQS_MAP = {
26
+ "CERT_NONE": "none",
27
+ "CERT_OPTIONAL": "optional",
28
+ "CERT_REQUIRED": "required",
29
+ }
30
+
31
+
32
+ def _normalize_redis_ssl_params(url: str) -> str:
33
+ """Normalize ssl_cert_reqs in a Redis URL from CERT_* to lowercase.
34
+
35
+ redis-py expects lowercase values (none/optional/required) but URLs
36
+ commonly use the Python ssl module constants (CERT_NONE/CERT_REQUIRED).
37
+ """
38
+ from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
39
+
40
+ parsed = urlparse(url)
41
+ if not parsed.query:
42
+ return url
43
+ params = parse_qs(parsed.query)
44
+ if "ssl_cert_reqs" not in params:
45
+ return url
46
+ raw = params["ssl_cert_reqs"][0]
47
+ normalized = _SSL_CERT_REQS_MAP.get(raw, raw)
48
+ params["ssl_cert_reqs"] = [normalized]
49
+ new_query = urlencode(params, doseq=True)
50
+ return urlunparse(parsed._replace(query=new_query))
51
+
24
52
 
25
53
  class RedisTransport:
26
54
  """Redis Streams-based event transport (XADD/XREAD)."""
@@ -129,6 +157,7 @@ class RedisTransport:
129
157
  """Create transport from Redis URL."""
130
158
  from redis import Redis as RedisClient
131
159
 
160
+ url = _normalize_redis_ssl_params(url)
132
161
  return cls(
133
162
  client=RedisClient.from_url(url, decode_responses=False),
134
163
  prefix=prefix,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stemtrace-ui",
3
3
  "private": true,
4
- "version": "0.3.1",
4
+ "version": "0.3.2",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
@@ -15,7 +15,10 @@ from stemtrace.core.exceptions import UnsupportedBrokerError
15
15
  from stemtrace.library.transports import get_transport
16
16
  from stemtrace.library.transports.memory import MemoryTransport
17
17
  from stemtrace.library.transports.rabbitmq import RabbitMQTransport
18
- from stemtrace.library.transports.redis import RedisTransport
18
+ from stemtrace.library.transports.redis import (
19
+ RedisTransport,
20
+ _normalize_redis_ssl_params,
21
+ )
19
22
 
20
23
 
21
24
  @pytest.fixture
@@ -201,6 +204,40 @@ class TestGetTransport:
201
204
  assert transport.ttl == 7200
202
205
 
203
206
 
207
+ class TestNormalizeRedisSslParams:
208
+ """Tests for _normalize_redis_ssl_params URL normalization."""
209
+
210
+ def test_cert_required_normalized(self) -> None:
211
+ url = "rediss://:pass@host:6379/1?ssl_cert_reqs=CERT_REQUIRED"
212
+ assert "ssl_cert_reqs=required" in _normalize_redis_ssl_params(url)
213
+
214
+ def test_cert_none_normalized(self) -> None:
215
+ url = "rediss://:pass@host:6379/0?ssl_cert_reqs=CERT_NONE"
216
+ assert "ssl_cert_reqs=none" in _normalize_redis_ssl_params(url)
217
+
218
+ def test_cert_optional_normalized(self) -> None:
219
+ url = "rediss://:pass@host:6379/0?ssl_cert_reqs=CERT_OPTIONAL"
220
+ assert "ssl_cert_reqs=optional" in _normalize_redis_ssl_params(url)
221
+
222
+ def test_already_lowercase_unchanged(self) -> None:
223
+ url = "rediss://:pass@host:6379/0?ssl_cert_reqs=required"
224
+ assert "ssl_cert_reqs=required" in _normalize_redis_ssl_params(url)
225
+
226
+ def test_no_query_params_unchanged(self) -> None:
227
+ url = "rediss://:pass@host:6379/0"
228
+ assert _normalize_redis_ssl_params(url) == url
229
+
230
+ def test_no_ssl_cert_reqs_unchanged(self) -> None:
231
+ url = "rediss://:pass@host:6379/0?timeout=5"
232
+ assert _normalize_redis_ssl_params(url) == url
233
+
234
+ def test_other_params_preserved(self) -> None:
235
+ url = "rediss://:pass@host:6379/1?ssl_cert_reqs=CERT_REQUIRED&timeout=5"
236
+ result = _normalize_redis_ssl_params(url)
237
+ assert "ssl_cert_reqs=required" in result
238
+ assert "timeout=5" in result
239
+
240
+
204
241
  class TestRedisTransport:
205
242
  """Tests for RedisTransport with mocked Redis client."""
206
243
 
@@ -1595,7 +1595,7 @@ wheels = [
1595
1595
 
1596
1596
  [[package]]
1597
1597
  name = "stemtrace"
1598
- version = "0.3.1"
1598
+ version = "0.3.2"
1599
1599
  source = { editable = "." }
1600
1600
  dependencies = [
1601
1601
  { name = "celery" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes