cuneus 0.2.10__tar.gz → 0.2.11__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 (38) hide show
  1. {cuneus-0.2.10 → cuneus-0.2.11}/PKG-INFO +1 -1
  2. {cuneus-0.2.10 → cuneus-0.2.11}/pyproject.toml +1 -1
  3. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/cli.py +5 -2
  4. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/core/application.py +8 -7
  5. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/core/logging.py +3 -4
  6. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/ext/otel.py +10 -17
  7. {cuneus-0.2.10 → cuneus-0.2.11}/.gitignore +0 -0
  8. {cuneus-0.2.10 → cuneus-0.2.11}/.python-version +0 -0
  9. {cuneus-0.2.10 → cuneus-0.2.11}/Makefile +0 -0
  10. {cuneus-0.2.10 → cuneus-0.2.11}/README.md +0 -0
  11. {cuneus-0.2.10 → cuneus-0.2.11}/examples/my_app/__init__.py +0 -0
  12. {cuneus-0.2.10 → cuneus-0.2.11}/examples/my_app/main.py +0 -0
  13. {cuneus-0.2.10 → cuneus-0.2.11}/examples/pyproject.toml +0 -0
  14. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/__init__.py +0 -0
  15. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/core/__init__.py +0 -0
  16. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/core/exceptions.py +0 -0
  17. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/core/extensions.py +0 -0
  18. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/core/settings.py +0 -0
  19. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/dependencies.py +0 -0
  20. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/ext/__init__.py +0 -0
  21. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/ext/database.py +0 -0
  22. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/ext/health.py +0 -0
  23. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/ext/server.py +0 -0
  24. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/py.typed +0 -0
  25. {cuneus-0.2.10 → cuneus-0.2.11}/src/cuneus/utils.py +0 -0
  26. {cuneus-0.2.10 → cuneus-0.2.11}/tests/cli/test_cli.py +0 -0
  27. {cuneus-0.2.10 → cuneus-0.2.11}/tests/cli/testapp/__init__.py +0 -0
  28. {cuneus-0.2.10 → cuneus-0.2.11}/tests/cli/testapp/main.py +0 -0
  29. {cuneus-0.2.10 → cuneus-0.2.11}/tests/cli/testapp/pyproject.toml +0 -0
  30. {cuneus-0.2.10 → cuneus-0.2.11}/tests/ext/test_database.py +0 -0
  31. {cuneus-0.2.10 → cuneus-0.2.11}/tests/ext/test_health.py +0 -0
  32. {cuneus-0.2.10 → cuneus-0.2.11}/tests/ext/test_otel.py +0 -0
  33. {cuneus-0.2.10 → cuneus-0.2.11}/tests/test_dependencies.py +0 -0
  34. {cuneus-0.2.10 → cuneus-0.2.11}/tests/test_exceptions.py +0 -0
  35. {cuneus-0.2.10 → cuneus-0.2.11}/tests/test_extensions.py +0 -0
  36. {cuneus-0.2.10 → cuneus-0.2.11}/tests/test_integration.py +0 -0
  37. {cuneus-0.2.10 → cuneus-0.2.11}/tests/test_utils.py +0 -0
  38. {cuneus-0.2.10 → cuneus-0.2.11}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuneus
3
- Version: 0.2.10
3
+ Version: 0.2.11
4
4
  Summary: ASGI application wrapper
5
5
  Project-URL: Homepage, https://github.com/rmyers/cuneus
6
6
  Project-URL: Documentation, https://github.com/rmyers/cuneus#readme
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cuneus"
3
- version = "0.2.10"
3
+ version = "0.2.11"
4
4
  description = "ASGI application wrapper"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "Robert Myers", email = "robert@julython.org" }]
@@ -8,12 +8,15 @@ from .core.settings import Settings, ensure_project_in_path
8
8
  from .utils import import_from_string
9
9
 
10
10
 
11
- def get_user_cli(config: Settings = Settings()) -> click.Group | None:
11
+ def get_user_cli(config: Settings | None = None) -> click.Group | None:
12
12
  """Load CLI from config."""
13
+ config = config or Settings()
13
14
  try:
14
15
  return cast(click.Group, import_from_string(config.cli_module))
15
16
  except (ImportError, AttributeError) as e:
16
- click.echo(f"Warning: Could not load CLI from {config.cli_module}: {e}", err=True)
17
+ click.echo(
18
+ f"Warning: Could not load CLI from {config.cli_module}: {e}", err=True
19
+ )
17
20
  return None
18
21
 
19
22
 
@@ -4,9 +4,6 @@ cuneus - The wedge stone that locks the arch together.
4
4
  Lightweight lifespan management for FastAPI applications.
5
5
  """
6
6
 
7
- from __future__ import annotations
8
-
9
- import inspect
10
7
  from contextlib import AsyncExitStack, asynccontextmanager
11
8
  from typing import Any, AsyncIterator, Callable
12
9
 
@@ -41,7 +38,9 @@ class ExtensionConflictError(Exception):
41
38
  pass
42
39
 
43
40
 
44
- def _instantiate_extension(ext: ExtensionInput, settings: Settings | None = None) -> Extension:
41
+ def _instantiate_extension(
42
+ ext: ExtensionInput, settings: Settings | None = None
43
+ ) -> Extension:
45
44
  if isinstance(ext, type) or callable(ext):
46
45
  try:
47
46
  return ext(settings=settings)
@@ -67,13 +66,13 @@ def build_app(
67
66
  from myapp.extensions import DatabaseExtension
68
67
 
69
68
  settings = Settings()
70
- app, cli = build_app(
69
+ app, cli, lifespan = build_app(
71
70
  SettingsExtension(settings),
72
71
  DatabaseExtension(settings),
73
72
  title="Args are passed to FastAPI",
74
73
  )
75
74
 
76
- __all__ = ["app", "cli"]
75
+ __all__ = ["app", "cli", "lifespan"]
77
76
 
78
77
  Testing:
79
78
  from myapp import app, lifespan
@@ -95,7 +94,9 @@ def build_app(
95
94
 
96
95
  @svcs.fastapi.lifespan
97
96
  @asynccontextmanager
98
- async def lifespan(app: FastAPI, registry: svcs.Registry) -> AsyncIterator[dict[str, Any]]:
97
+ async def lifespan(
98
+ app: FastAPI, registry: svcs.Registry
99
+ ) -> AsyncIterator[dict[str, Any]]:
99
100
  async with AsyncExitStack() as stack:
100
101
  state: dict[str, Any] = {}
101
102
 
@@ -8,11 +8,10 @@ import logging
8
8
  import shutil
9
9
  import time
10
10
  import uuid
11
- from typing import Any, Awaitable, Callable
11
+ from typing import Awaitable, Callable
12
12
 
13
13
  import structlog
14
- import svcs
15
- from fastapi import FastAPI, Request, Response
14
+ from fastapi import Request, Response
16
15
  from starlette.middleware.base import BaseHTTPMiddleware
17
16
  from starlette.middleware import Middleware
18
17
  from starlette.types import ASGIApp
@@ -33,7 +32,6 @@ def configure_structlog(settings: Settings | None = None) -> None:
33
32
  structlog.stdlib.add_logger_name,
34
33
  structlog.stdlib.PositionalArgumentsFormatter(),
35
34
  structlog.processors.TimeStamper(fmt="iso", utc=True),
36
- structlog.processors.StackInfoRenderer(),
37
35
  structlog.processors.UnicodeDecoder(),
38
36
  ]
39
37
 
@@ -76,6 +74,7 @@ def configure_structlog(settings: Settings | None = None) -> None:
76
74
 
77
75
  # Quiet noisy loggers
78
76
  logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
77
+ logging.getLogger("svcs").setLevel(logging.INFO)
79
78
 
80
79
 
81
80
  class LoggingExtension(BaseExtension):
@@ -1,8 +1,7 @@
1
- # cuneus/ext/otel.py
2
1
  from __future__ import annotations
3
2
 
4
- from contextlib import asynccontextmanager
5
- from typing import Any, AsyncIterator, Callable
3
+ import importlib
4
+ from typing import Any, Callable
6
5
 
7
6
  import structlog
8
7
  import svcs
@@ -116,14 +115,10 @@ class OTelExtension(BaseExtension, HasMiddleware):
116
115
  self._span_exporters = span_exporters or []
117
116
  self._span_processors = span_processors or []
118
117
 
119
- @asynccontextmanager
120
- async def register(
121
- self, registry: svcs.Registry, app: FastAPI
122
- ) -> AsyncIterator[dict[str, Any]]:
118
+ async def startup(self, registry: svcs.Registry, app: FastAPI) -> dict[str, Any]:
123
119
  if not self.settings.enabled:
124
120
  logger.info("OpenTelemetry disabled")
125
- yield {}
126
- return
121
+ return {}
127
122
 
128
123
  resource = Resource.create(
129
124
  {
@@ -166,12 +161,12 @@ class OTelExtension(BaseExtension, HasMiddleware):
166
161
  metrics.set_meter_provider(meter_provider)
167
162
  registry.register_value(MeterProvider, meter_provider)
168
163
 
169
- try:
170
- yield {"tracer_provider": self._tracer_provider}
171
- finally:
172
- if self._tracer_provider:
173
- self._tracer_provider.shutdown()
174
- logger.info("OpenTelemetry shutdown")
164
+ return {"tracer_provider": self._tracer_provider}
165
+
166
+ async def shutdown(self, app: FastAPI) -> None:
167
+ if self._tracer_provider:
168
+ self._tracer_provider.shutdown()
169
+ logger.info("OpenTelemetry shutdown")
175
170
 
176
171
  def middleware(self) -> list[Middleware]:
177
172
  if not self.settings.enabled or not self.settings.traces_enabled:
@@ -215,8 +210,6 @@ class OTelExtension(BaseExtension, HasMiddleware):
215
210
 
216
211
  def _try_instrument(self, module: str, class_name: str) -> None:
217
212
  try:
218
- import importlib
219
-
220
213
  mod = importlib.import_module(module)
221
214
  instrumentor = getattr(mod, class_name)()
222
215
  instrumentor.instrument()
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