simple-module-hosting 0.0.14__tar.gz → 0.0.15__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 (51) hide show
  1. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/PKG-INFO +3 -3
  2. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/pyproject.toml +3 -3
  3. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/_observability.py +12 -4
  4. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/logging.py +3 -0
  5. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_logging.py +31 -0
  6. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/.gitignore +0 -0
  7. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/LICENSE +0 -0
  8. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/README.md +0 -0
  9. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/__init__.py +0 -0
  10. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/__main__.py +0 -0
  11. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/_error_handlers.py +0 -0
  12. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/_host_services.py +0 -0
  13. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/_hydrate_step.py +0 -0
  14. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/_inertia_setup.py +0 -0
  15. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/_inertia_shared.py +0 -0
  16. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/_phase_helpers.py +0 -0
  17. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/app_builder.py +0 -0
  18. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/bootstrap_settings.py +0 -0
  19. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/health.py +0 -0
  20. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/host_cli.py +0 -0
  21. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/host_settings.py +0 -0
  22. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/i18n_deps.py +0 -0
  23. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/i18n_manifest.py +0 -0
  24. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/i18n_middleware.py +0 -0
  25. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/inertia_deps.py +0 -0
  26. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/inertia_utils.py +0 -0
  27. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/manifest.py +0 -0
  28. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/middleware.py +0 -0
  29. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/migrations.py +0 -0
  30. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/permissions.py +0 -0
  31. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/py.typed +0 -0
  32. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/redirects.py +0 -0
  33. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/simple_module_hosting/settings.py +0 -0
  34. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_app.py +0 -0
  35. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_check_migrations.py +0 -0
  36. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_health.py +0 -0
  37. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_host_cli.py +0 -0
  38. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_hosting_permissions.py +0 -0
  39. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_i18n_manifest.py +0 -0
  40. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_inertia_i18n_shared_props.py +0 -0
  41. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_lifespan_order.py +0 -0
  42. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_locale_middleware.py +0 -0
  43. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_manifest.py +0 -0
  44. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_middleware_order.py +0 -0
  45. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_redirects.py +0 -0
  46. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_session_cookie_security.py +0 -0
  47. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_settings_i18n.py +0 -0
  48. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_settings_secrets.py +0 -0
  49. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_strict_discovery_wiring.py +0 -0
  50. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_tenant_middleware.py +0 -0
  51. {simple_module_hosting-0.0.14 → simple_module_hosting-0.0.15}/tests/test_translator_dep.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simple_module_hosting
3
- Version: 0.0.14
3
+ Version: 0.0.15
4
4
  Summary: FastAPI + Inertia.js host runtime for simple_module — app_builder, middleware stack, CLI (sm / simple-module), scaffolding
5
5
  Project-URL: Homepage, https://github.com/antosubash/simple_module_python
6
6
  Project-URL: Repository, https://github.com/antosubash/simple_module_python
@@ -26,8 +26,8 @@ Requires-Dist: fastapi-inertia>=1.0
26
26
  Requires-Dist: fastapi>=0.115
27
27
  Requires-Dist: httpx>=0.27
28
28
  Requires-Dist: jinja2>=3.1
29
- Requires-Dist: simple-module-core==0.0.14
30
- Requires-Dist: simple-module-db==0.0.14
29
+ Requires-Dist: simple-module-core==0.0.15
30
+ Requires-Dist: simple-module-db==0.0.15
31
31
  Requires-Dist: starlette>=0.44
32
32
  Requires-Dist: tomlkit>=0.13
33
33
  Requires-Dist: uvicorn[standard]>=0.34
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "simple_module_hosting"
3
- version = "0.0.14"
3
+ version = "0.0.15"
4
4
  description = "FastAPI + Inertia.js host runtime for simple_module — app_builder, middleware stack, CLI (sm / simple-module), scaffolding"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -26,8 +26,8 @@ dependencies = [
26
26
  "fastapi-inertia>=1.0",
27
27
  "httpx>=0.27",
28
28
  "jinja2>=3.1",
29
- "simple_module_core==0.0.14",
30
- "simple_module_db==0.0.14",
29
+ "simple_module_core==0.0.15",
30
+ "simple_module_db==0.0.15",
31
31
  "starlette>=0.44",
32
32
  "tomlkit>=0.13",
33
33
  "uvicorn[standard]>=0.34",
@@ -26,10 +26,16 @@ _QUIET_PREFIXES = ("/health", "/static/")
26
26
  class CorrelationIdMiddleware:
27
27
  """Generate or propagate a correlation ID for every request.
28
28
 
29
- Reads the incoming ``X-Correlation-ID`` header (or generates a UUID4) and
30
- stores it in a :class:`~contextvars.ContextVar` so that every log record
31
- emitted during the request automatically includes the ID. The same value
32
- is echoed back in the response header.
29
+ Reads the incoming ``X-Correlation-ID`` header (or generates a UUID4) and:
30
+
31
+ * stores it in the ``simple_module_hosting.logging.correlation_id``
32
+ ContextVar so the stdlib logging filter (or a user-supplied structlog
33
+ processor — see ``docs/framework/middleware.md``) picks it up with no
34
+ per-handler plumbing;
35
+ * exposes it on ``request.state.correlation_id`` for handlers that
36
+ prefer the request object over the contextvar;
37
+ * echoes the value back as the ``X-Correlation-ID`` response header so
38
+ clients can cross-reference their request with server-side logs.
33
39
  """
34
40
 
35
41
  HEADER = "X-Correlation-ID"
@@ -43,6 +49,8 @@ class CorrelationIdMiddleware:
43
49
  return
44
50
 
45
51
  cid = Headers(scope=scope).get(self.HEADER) or uuid.uuid4().hex
52
+ # Skip allocating a Request wrapper — downstream Request(scope).state reads this same dict.
53
+ scope.setdefault("state", {})["correlation_id"] = cid
46
54
 
47
55
  async def send_with_header(message: Message) -> None:
48
56
  if message["type"] == _MSG_RESPONSE_START:
@@ -38,6 +38,9 @@ class JsonFormatter(logging.Formatter):
38
38
  "entity",
39
39
  "entity_id",
40
40
  "db_duration_ms",
41
+ # Bound by log filters on Celery / job-runner workers.
42
+ "task_id",
43
+ "task_name",
41
44
  )
42
45
 
43
46
  def format(self, record: logging.LogRecord) -> str:
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import logging
7
+ from typing import TYPE_CHECKING
7
8
 
8
9
  import httpx
9
10
  from simple_module_hosting.logging import (
@@ -12,6 +13,13 @@ from simple_module_hosting.logging import (
12
13
  correlation_id,
13
14
  setup_logging,
14
15
  )
16
+ from simple_module_hosting.middleware import CorrelationIdMiddleware
17
+ from starlette.applications import Starlette
18
+ from starlette.responses import JSONResponse
19
+ from starlette.routing import Route
20
+
21
+ if TYPE_CHECKING:
22
+ from starlette.requests import Request
15
23
 
16
24
  # ── JsonFormatter ──────────────────────────────────────────────────────
17
25
 
@@ -174,6 +182,29 @@ class TestCorrelationIdMiddleware:
174
182
  r2 = await client.get("/health")
175
183
  assert r1.headers["x-correlation-id"] != r2.headers["x-correlation-id"]
176
184
 
185
+ async def test_state_contextvar_and_header_agree(self):
186
+ # Background tasks read the ContextVar; handlers read request.state;
187
+ # clients read the response header — all three must agree per request.
188
+ async def echo(request: Request) -> JSONResponse:
189
+ return JSONResponse(
190
+ {
191
+ "state": request.state.correlation_id,
192
+ "contextvar": correlation_id.get(""),
193
+ }
194
+ )
195
+
196
+ app = Starlette(routes=[Route("/echo", echo)])
197
+ app.add_middleware(CorrelationIdMiddleware)
198
+
199
+ transport = httpx.ASGITransport(app=app)
200
+ async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as c:
201
+ resp = await c.get("/echo", headers={CorrelationIdMiddleware.HEADER: "trace-xyz"})
202
+
203
+ assert resp.headers[CorrelationIdMiddleware.HEADER] == "trace-xyz"
204
+ body = resp.json()
205
+ assert body["state"] == "trace-xyz"
206
+ assert body["contextvar"] == "trace-xyz"
207
+
177
208
 
178
209
  # ── Request logging middleware (integration) ────────────────────────────
179
210