cuneus 0.2.12__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.12 → cuneus-0.2.13}/PKG-INFO +2 -1
- {cuneus-0.2.12 → cuneus-0.2.13}/pyproject.toml +2 -1
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/core/application.py +22 -2
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/core/exceptions.py +8 -3
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/core/extensions.py +7 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/core/settings.py +4 -1
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/ext/otel.py +6 -14
- {cuneus-0.2.12 → cuneus-0.2.13}/uv.lock +53 -1
- {cuneus-0.2.12 → cuneus-0.2.13}/.gitignore +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/.python-version +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/Makefile +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/README.md +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/examples/my_app/__init__.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/examples/my_app/main.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/examples/pyproject.toml +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/__init__.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/cli.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/core/__init__.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/core/logging.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/dependencies.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/ext/__init__.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/ext/database.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/ext/health.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/ext/server.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/py.typed +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/src/cuneus/utils.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/tests/cli/test_cli.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/tests/cli/testapp/__init__.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/tests/cli/testapp/main.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/tests/cli/testapp/pyproject.toml +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/tests/ext/test_database.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/tests/ext/test_health.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/tests/ext/test_otel.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/tests/test_dependencies.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/tests/test_exceptions.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/tests/test_extensions.py +0 -0
- {cuneus-0.2.12 → cuneus-0.2.13}/tests/test_integration.py +0 -0
- {cuneus-0.2.12 → 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,7 @@ 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
107
|
_meter_provider: MeterProvider | None = None
|
|
108
108
|
|
|
109
109
|
def __init__(
|
|
@@ -118,7 +118,7 @@ class OTelExtension(BaseExtension, HasMiddleware):
|
|
|
118
118
|
|
|
119
119
|
async def startup(self, registry: svcs.Registry, app: FastAPI) -> dict[str, Any]:
|
|
120
120
|
if not self.settings.enabled:
|
|
121
|
-
logger.
|
|
121
|
+
logger.debug("OpenTelemetry disabled")
|
|
122
122
|
return {}
|
|
123
123
|
|
|
124
124
|
resource = Resource.create(
|
|
@@ -147,14 +147,10 @@ class OTelExtension(BaseExtension, HasMiddleware):
|
|
|
147
147
|
self._tracer_provider.get_tracer(self.settings.service_name),
|
|
148
148
|
)
|
|
149
149
|
|
|
150
|
-
self._setup_auto_instrumentation(app)
|
|
151
|
-
|
|
152
150
|
logger.info(
|
|
153
151
|
"OpenTelemetry tracing started",
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
"exporters": len(self._span_exporters),
|
|
157
|
-
},
|
|
152
|
+
service=self.settings.service_name,
|
|
153
|
+
num_exporters=len(self._span_exporters),
|
|
158
154
|
)
|
|
159
155
|
|
|
160
156
|
if self.settings.metrics_enabled:
|
|
@@ -181,7 +177,7 @@ class OTelExtension(BaseExtension, HasMiddleware):
|
|
|
181
177
|
)
|
|
182
178
|
]
|
|
183
179
|
|
|
184
|
-
def
|
|
180
|
+
def post_app_hook(self, app: FastAPI) -> None:
|
|
185
181
|
"""Setup auto-instrumentation based on settings."""
|
|
186
182
|
instrumentors = [
|
|
187
183
|
(
|
|
@@ -209,11 +205,7 @@ class OTelExtension(BaseExtension, HasMiddleware):
|
|
|
209
205
|
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
|
|
210
206
|
|
|
211
207
|
inst = FastAPIInstrumentor()
|
|
212
|
-
inst.instrument_app(
|
|
213
|
-
app,
|
|
214
|
-
tracer_provider=self._tracer_provider,
|
|
215
|
-
meter_provider=self._meter_provider,
|
|
216
|
-
)
|
|
208
|
+
inst.instrument_app(app)
|
|
217
209
|
logger.debug("FastAPIInstrumentor auto-instrumentation enabled")
|
|
218
210
|
|
|
219
211
|
def _try_instrument(self, module: str, class_name: str) -> None:
|
|
@@ -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
|