cuneus 0.2.11__tar.gz → 0.2.13__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.
- {cuneus-0.2.11 → cuneus-0.2.13}/PKG-INFO +2 -1
- {cuneus-0.2.11 → cuneus-0.2.13}/pyproject.toml +2 -1
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/core/application.py +22 -2
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/core/exceptions.py +8 -3
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/core/extensions.py +7 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/core/settings.py +4 -1
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/ext/otel.py +15 -15
- {cuneus-0.2.11 → cuneus-0.2.13}/uv.lock +53 -1
- {cuneus-0.2.11 → cuneus-0.2.13}/.gitignore +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/.python-version +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/Makefile +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/README.md +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/examples/my_app/__init__.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/examples/my_app/main.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/examples/pyproject.toml +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/__init__.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/cli.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/core/__init__.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/core/logging.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/dependencies.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/ext/__init__.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/ext/database.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/ext/health.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/ext/server.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/py.typed +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/src/cuneus/utils.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/tests/cli/test_cli.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/tests/cli/testapp/__init__.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/tests/cli/testapp/main.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/tests/cli/testapp/pyproject.toml +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/tests/ext/test_database.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/tests/ext/test_health.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/tests/ext/test_otel.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/tests/test_dependencies.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/tests/test_exceptions.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/tests/test_extensions.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/tests/test_integration.py +0 -0
- {cuneus-0.2.11 → cuneus-0.2.13}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cuneus
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.13
|
|
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
|
|
@@ -32,6 +32,7 @@ Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
|
32
32
|
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
33
33
|
Requires-Dist: opentelemetry-api; extra == 'dev'
|
|
34
34
|
Requires-Dist: opentelemetry-instrumentation; extra == 'dev'
|
|
35
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi; extra == 'dev'
|
|
35
36
|
Requires-Dist: opentelemetry-sdk; extra == 'dev'
|
|
36
37
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
37
38
|
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cuneus"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.13"
|
|
4
4
|
description = "ASGI application wrapper"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Robert Myers", email = "robert@julython.org" }]
|
|
@@ -25,6 +25,7 @@ dev = [
|
|
|
25
25
|
"opentelemetry-sdk",
|
|
26
26
|
"opentelemetry-api",
|
|
27
27
|
"opentelemetry-instrumentation",
|
|
28
|
+
"opentelemetry-instrumentation-fastapi",
|
|
28
29
|
"pytest>=8.0",
|
|
29
30
|
"pytest-asyncio>=0.23",
|
|
30
31
|
"pytest-cov>=4.0",
|
|
@@ -16,7 +16,14 @@ from starlette.middleware import Middleware
|
|
|
16
16
|
from .settings import Settings
|
|
17
17
|
from .exceptions import ExceptionExtension
|
|
18
18
|
from .logging import LoggingExtension
|
|
19
|
-
from .extensions import
|
|
19
|
+
from .extensions import (
|
|
20
|
+
Extension,
|
|
21
|
+
HasCLI,
|
|
22
|
+
HasExceptionHandler,
|
|
23
|
+
HasMiddleware,
|
|
24
|
+
HasPostAppHook,
|
|
25
|
+
HasRoutes,
|
|
26
|
+
)
|
|
20
27
|
from ..ext.health import HealthExtension
|
|
21
28
|
from ..ext.server import ServerExtension
|
|
22
29
|
|
|
@@ -88,6 +95,10 @@ def build_app(
|
|
|
88
95
|
|
|
89
96
|
settings = settings or Settings()
|
|
90
97
|
|
|
98
|
+
# Grab defaults for a few FastAPI args:
|
|
99
|
+
debug: bool = fastapi_kwargs.pop("debug", settings.debug)
|
|
100
|
+
version: str = fastapi_kwargs.pop("version", settings.version)
|
|
101
|
+
|
|
91
102
|
all_inputs = (*DEFAULTS, *extensions) if include_defaults else extensions
|
|
92
103
|
|
|
93
104
|
all_extensions = [_instantiate_extension(ext, settings) for ext in all_inputs]
|
|
@@ -125,11 +136,20 @@ def build_app(
|
|
|
125
136
|
logger.debug(f"Adding cli commands from {ext_name}")
|
|
126
137
|
ext.register_cli(app_cli)
|
|
127
138
|
|
|
128
|
-
app = FastAPI(
|
|
139
|
+
app = FastAPI(
|
|
140
|
+
debug=debug,
|
|
141
|
+
version=version,
|
|
142
|
+
lifespan=lifespan,
|
|
143
|
+
middleware=middleware,
|
|
144
|
+
**fastapi_kwargs,
|
|
145
|
+
)
|
|
129
146
|
|
|
130
147
|
# Preform post app initialization extension customization
|
|
131
148
|
for ext in all_extensions:
|
|
132
149
|
ext_name = ext.__class__.__name__
|
|
150
|
+
if isinstance(ext, HasPostAppHook):
|
|
151
|
+
logger.debug(f"Running post app hook from {ext_name}")
|
|
152
|
+
ext.post_app_hook(app)
|
|
133
153
|
if isinstance(ext, HasExceptionHandler):
|
|
134
154
|
logger.debug(f"Loading exception handlers from {ext_name}")
|
|
135
155
|
ext.add_exception_handler(app)
|
|
@@ -158,9 +158,12 @@ class ExceptionExtension(BaseExtension):
|
|
|
158
158
|
|
|
159
159
|
def add_exception_handler(self, app: FastAPI) -> None:
|
|
160
160
|
app.add_exception_handler(AppException, self._handle_app_exception) # type: ignore[arg-type]
|
|
161
|
-
|
|
161
|
+
if self.settings.handle_unknown_errors: # pragma: no branch
|
|
162
|
+
app.add_exception_handler(Exception, self._handle_unexpected_exception)
|
|
162
163
|
|
|
163
|
-
def _handle_app_exception(
|
|
164
|
+
def _handle_app_exception(
|
|
165
|
+
self, request: Request, exc: AppException
|
|
166
|
+
) -> JSONResponse:
|
|
164
167
|
if exc.status_code >= 500 and self.settings.log_server_errors:
|
|
165
168
|
log.exception("server_error", error_code=exc.error_code)
|
|
166
169
|
else:
|
|
@@ -178,7 +181,9 @@ class ExceptionExtension(BaseExtension):
|
|
|
178
181
|
headers=headers,
|
|
179
182
|
)
|
|
180
183
|
|
|
181
|
-
def _handle_unexpected_exception(
|
|
184
|
+
def _handle_unexpected_exception(
|
|
185
|
+
self, request: Request, exc: Exception
|
|
186
|
+
) -> JSONResponse:
|
|
182
187
|
log.exception("unexpected_error", exc_info=exc)
|
|
183
188
|
response: dict[str, Any] = {
|
|
184
189
|
"error": {
|
|
@@ -74,6 +74,13 @@ class HasRoutes(Protocol):
|
|
|
74
74
|
def add_routes(self, app: FastAPI) -> None: ... # pragma: no cover
|
|
75
75
|
|
|
76
76
|
|
|
77
|
+
@runtime_checkable
|
|
78
|
+
class HasPostAppHook(Protocol):
|
|
79
|
+
"""Extension that modifies app after creation."""
|
|
80
|
+
|
|
81
|
+
def post_app_hook(self, app: FastAPI) -> None: ... # pragma: no cover
|
|
82
|
+
|
|
83
|
+
|
|
77
84
|
class BaseExtension:
|
|
78
85
|
"""
|
|
79
86
|
Base class for extensions with explicit startup/shutdown hooks.
|
|
@@ -50,14 +50,17 @@ class Settings(CuneusBaseSettings):
|
|
|
50
50
|
app_name: str = "app"
|
|
51
51
|
app_module: str = "app.main:app"
|
|
52
52
|
cli_module: str = "app.main:cli"
|
|
53
|
+
|
|
54
|
+
# FastAPI settings
|
|
53
55
|
debug: bool = False
|
|
54
|
-
version: str
|
|
56
|
+
version: str = "0.1.0"
|
|
55
57
|
|
|
56
58
|
# logging
|
|
57
59
|
log_level: str = "INFO"
|
|
58
60
|
log_json: bool = False
|
|
59
61
|
log_server_errors: bool = True
|
|
60
62
|
request_id_header: str = "X-Request-ID"
|
|
63
|
+
handle_unknown_errors: bool = True
|
|
61
64
|
|
|
62
65
|
# health
|
|
63
66
|
health_enabled: bool = True
|
|
@@ -103,7 +103,8 @@ class OTelExtension(BaseExtension, HasMiddleware):
|
|
|
103
103
|
excluded_paths: Paths to exclude from tracing
|
|
104
104
|
"""
|
|
105
105
|
|
|
106
|
-
_tracer_provider: TracerProvider
|
|
106
|
+
_tracer_provider: TracerProvider | None = None
|
|
107
|
+
_meter_provider: MeterProvider | None = None
|
|
107
108
|
|
|
108
109
|
def __init__(
|
|
109
110
|
self,
|
|
@@ -117,7 +118,7 @@ class OTelExtension(BaseExtension, HasMiddleware):
|
|
|
117
118
|
|
|
118
119
|
async def startup(self, registry: svcs.Registry, app: FastAPI) -> dict[str, Any]:
|
|
119
120
|
if not self.settings.enabled:
|
|
120
|
-
logger.
|
|
121
|
+
logger.debug("OpenTelemetry disabled")
|
|
121
122
|
return {}
|
|
122
123
|
|
|
123
124
|
resource = Resource.create(
|
|
@@ -146,20 +147,17 @@ class OTelExtension(BaseExtension, HasMiddleware):
|
|
|
146
147
|
self._tracer_provider.get_tracer(self.settings.service_name),
|
|
147
148
|
)
|
|
148
149
|
|
|
149
|
-
self._setup_auto_instrumentation()
|
|
150
|
-
|
|
151
150
|
logger.info(
|
|
152
151
|
"OpenTelemetry tracing started",
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
"exporters": len(self._span_exporters),
|
|
156
|
-
},
|
|
152
|
+
service=self.settings.service_name,
|
|
153
|
+
num_exporters=len(self._span_exporters),
|
|
157
154
|
)
|
|
158
155
|
|
|
159
156
|
if self.settings.metrics_enabled:
|
|
160
157
|
meter_provider = MeterProvider(resource=resource)
|
|
161
158
|
metrics.set_meter_provider(meter_provider)
|
|
162
|
-
|
|
159
|
+
self._meter_provider = meter_provider
|
|
160
|
+
registry.register_value(MeterProvider, self._meter_provider)
|
|
163
161
|
|
|
164
162
|
return {"tracer_provider": self._tracer_provider}
|
|
165
163
|
|
|
@@ -179,14 +177,9 @@ class OTelExtension(BaseExtension, HasMiddleware):
|
|
|
179
177
|
)
|
|
180
178
|
]
|
|
181
179
|
|
|
182
|
-
def
|
|
180
|
+
def post_app_hook(self, app: FastAPI) -> None:
|
|
183
181
|
"""Setup auto-instrumentation based on settings."""
|
|
184
182
|
instrumentors = [
|
|
185
|
-
(
|
|
186
|
-
self.settings.instrument_fastapi,
|
|
187
|
-
"opentelemetry.instrumentation.fastapi",
|
|
188
|
-
"FastAPIInstrumentor",
|
|
189
|
-
),
|
|
190
183
|
(
|
|
191
184
|
self.settings.instrument_sqlalchemy,
|
|
192
185
|
"opentelemetry.instrumentation.sqlalchemy",
|
|
@@ -208,6 +201,13 @@ class OTelExtension(BaseExtension, HasMiddleware):
|
|
|
208
201
|
if enabled:
|
|
209
202
|
self._try_instrument(module, class_name)
|
|
210
203
|
|
|
204
|
+
if self.settings.instrument_fastapi:
|
|
205
|
+
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
|
|
206
|
+
|
|
207
|
+
inst = FastAPIInstrumentor()
|
|
208
|
+
inst.instrument_app(app)
|
|
209
|
+
logger.debug("FastAPIInstrumentor auto-instrumentation enabled")
|
|
210
|
+
|
|
211
211
|
def _try_instrument(self, module: str, class_name: str) -> None:
|
|
212
212
|
try:
|
|
213
213
|
mod = importlib.import_module(module)
|
|
@@ -87,6 +87,15 @@ wheels = [
|
|
|
87
87
|
{ url = "https://files.pythonhosted.org/packages/2f/f5/c36551e93acba41a59939ae6a0fb77ddb3f2e8e8caa716410c65f7341f72/asgi_lifespan-2.1.0-py3-none-any.whl", hash = "sha256:ed840706680e28428c01e14afb3875d7d76d3206f3d5b2f2294e059b5c23804f", size = 10895, upload-time = "2023-03-28T17:35:47.772Z" },
|
|
88
88
|
]
|
|
89
89
|
|
|
90
|
+
[[package]]
|
|
91
|
+
name = "asgiref"
|
|
92
|
+
version = "3.11.1"
|
|
93
|
+
source = { registry = "https://pypi.org/simple" }
|
|
94
|
+
sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550, upload-time = "2026-02-03T13:30:14.33Z" }
|
|
95
|
+
wheels = [
|
|
96
|
+
{ url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" },
|
|
97
|
+
]
|
|
98
|
+
|
|
90
99
|
[[package]]
|
|
91
100
|
name = "async-timeout"
|
|
92
101
|
version = "5.0.1"
|
|
@@ -412,7 +421,7 @@ toml = [
|
|
|
412
421
|
|
|
413
422
|
[[package]]
|
|
414
423
|
name = "cuneus"
|
|
415
|
-
version = "0.2.
|
|
424
|
+
version = "0.2.13"
|
|
416
425
|
source = { editable = "." }
|
|
417
426
|
dependencies = [
|
|
418
427
|
{ name = "click" },
|
|
@@ -445,6 +454,7 @@ dev = [
|
|
|
445
454
|
{ name = "mypy" },
|
|
446
455
|
{ name = "opentelemetry-api" },
|
|
447
456
|
{ name = "opentelemetry-instrumentation" },
|
|
457
|
+
{ name = "opentelemetry-instrumentation-fastapi" },
|
|
448
458
|
{ name = "opentelemetry-sdk" },
|
|
449
459
|
{ name = "pytest" },
|
|
450
460
|
{ name = "pytest-asyncio" },
|
|
@@ -472,6 +482,7 @@ requires-dist = [
|
|
|
472
482
|
{ name = "mypy", marker = "extra == 'dev'", specifier = ">=1.8" },
|
|
473
483
|
{ name = "opentelemetry-api", marker = "extra == 'dev'" },
|
|
474
484
|
{ name = "opentelemetry-instrumentation", marker = "extra == 'dev'" },
|
|
485
|
+
{ name = "opentelemetry-instrumentation-fastapi", marker = "extra == 'dev'" },
|
|
475
486
|
{ name = "opentelemetry-sdk", marker = "extra == 'dev'" },
|
|
476
487
|
{ name = "pydantic", specifier = ">=2.0" },
|
|
477
488
|
{ name = "pydantic-settings", specifier = ">=2.0" },
|
|
@@ -1032,6 +1043,38 @@ wheels = [
|
|
|
1032
1043
|
{ url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" },
|
|
1033
1044
|
]
|
|
1034
1045
|
|
|
1046
|
+
[[package]]
|
|
1047
|
+
name = "opentelemetry-instrumentation-asgi"
|
|
1048
|
+
version = "0.60b1"
|
|
1049
|
+
source = { registry = "https://pypi.org/simple" }
|
|
1050
|
+
dependencies = [
|
|
1051
|
+
{ name = "asgiref" },
|
|
1052
|
+
{ name = "opentelemetry-api" },
|
|
1053
|
+
{ name = "opentelemetry-instrumentation" },
|
|
1054
|
+
{ name = "opentelemetry-semantic-conventions" },
|
|
1055
|
+
{ name = "opentelemetry-util-http" },
|
|
1056
|
+
]
|
|
1057
|
+
sdist = { url = "https://files.pythonhosted.org/packages/77/db/851fa88db7441da82d50bd80f2de5ee55213782e25dc858e04d0c9961d60/opentelemetry_instrumentation_asgi-0.60b1.tar.gz", hash = "sha256:16bfbe595cd24cda309a957456d0fc2523f41bc7b076d1f2d7e98a1ad9876d6f", size = 26107, upload-time = "2025-12-11T13:36:47.015Z" }
|
|
1058
|
+
wheels = [
|
|
1059
|
+
{ url = "https://files.pythonhosted.org/packages/76/76/1fb94367cef64420d2171157a6b9509582873bd09a6afe08a78a8d1f59d9/opentelemetry_instrumentation_asgi-0.60b1-py3-none-any.whl", hash = "sha256:d48def2dbed10294c99cfcf41ebbd0c414d390a11773a41f472d20000fcddc25", size = 16933, upload-time = "2025-12-11T13:35:40.462Z" },
|
|
1060
|
+
]
|
|
1061
|
+
|
|
1062
|
+
[[package]]
|
|
1063
|
+
name = "opentelemetry-instrumentation-fastapi"
|
|
1064
|
+
version = "0.60b1"
|
|
1065
|
+
source = { registry = "https://pypi.org/simple" }
|
|
1066
|
+
dependencies = [
|
|
1067
|
+
{ name = "opentelemetry-api" },
|
|
1068
|
+
{ name = "opentelemetry-instrumentation" },
|
|
1069
|
+
{ name = "opentelemetry-instrumentation-asgi" },
|
|
1070
|
+
{ name = "opentelemetry-semantic-conventions" },
|
|
1071
|
+
{ name = "opentelemetry-util-http" },
|
|
1072
|
+
]
|
|
1073
|
+
sdist = { url = "https://files.pythonhosted.org/packages/9c/e7/e7e5e50218cf488377209d85666b182fa2d4928bf52389411ceeee1b2b60/opentelemetry_instrumentation_fastapi-0.60b1.tar.gz", hash = "sha256:de608955f7ff8eecf35d056578346a5365015fd7d8623df9b1f08d1c74769c01", size = 24958, upload-time = "2025-12-11T13:36:59.35Z" }
|
|
1074
|
+
wheels = [
|
|
1075
|
+
{ url = "https://files.pythonhosted.org/packages/7d/cc/6e808328ba54662e50babdcab21138eae4250bc0fddf67d55526a615a2ca/opentelemetry_instrumentation_fastapi-0.60b1-py3-none-any.whl", hash = "sha256:af94b7a239ad1085fc3a820ecf069f67f579d7faf4c085aaa7bd9b64eafc8eaf", size = 13478, upload-time = "2025-12-11T13:36:00.811Z" },
|
|
1076
|
+
]
|
|
1077
|
+
|
|
1035
1078
|
[[package]]
|
|
1036
1079
|
name = "opentelemetry-sdk"
|
|
1037
1080
|
version = "1.39.1"
|
|
@@ -1059,6 +1102,15 @@ wheels = [
|
|
|
1059
1102
|
{ url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" },
|
|
1060
1103
|
]
|
|
1061
1104
|
|
|
1105
|
+
[[package]]
|
|
1106
|
+
name = "opentelemetry-util-http"
|
|
1107
|
+
version = "0.60b1"
|
|
1108
|
+
source = { registry = "https://pypi.org/simple" }
|
|
1109
|
+
sdist = { url = "https://files.pythonhosted.org/packages/50/fc/c47bb04a1d8a941a4061307e1eddfa331ed4d0ab13d8a9781e6db256940a/opentelemetry_util_http-0.60b1.tar.gz", hash = "sha256:0d97152ca8c8a41ced7172d29d3622a219317f74ae6bb3027cfbdcf22c3cc0d6", size = 11053, upload-time = "2025-12-11T13:37:25.115Z" }
|
|
1110
|
+
wheels = [
|
|
1111
|
+
{ url = "https://files.pythonhosted.org/packages/16/5c/d3f1733665f7cd582ef0842fb1d2ed0bc1fba10875160593342d22bba375/opentelemetry_util_http-0.60b1-py3-none-any.whl", hash = "sha256:66381ba28550c91bee14dcba8979ace443444af1ed609226634596b4b0faf199", size = 8947, upload-time = "2025-12-11T13:36:37.151Z" },
|
|
1112
|
+
]
|
|
1113
|
+
|
|
1062
1114
|
[[package]]
|
|
1063
1115
|
name = "packaging"
|
|
1064
1116
|
version = "25.0"
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|