karrio-server 2025.5rc17__py3-none-any.whl → 2025.5rc19__py3-none-any.whl
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.
- karrio/server/VERSION +1 -1
- karrio/server/lib/otel_huey.py +146 -0
- karrio/server/settings/apm.py +125 -0
- karrio/server/settings/workers.py +16 -0
- {karrio_server-2025.5rc17.dist-info → karrio_server-2025.5rc19.dist-info}/METADATA +10 -1
- {karrio_server-2025.5rc17.dist-info → karrio_server-2025.5rc19.dist-info}/RECORD +9 -8
- {karrio_server-2025.5rc17.dist-info → karrio_server-2025.5rc19.dist-info}/WHEEL +0 -0
- {karrio_server-2025.5rc17.dist-info → karrio_server-2025.5rc19.dist-info}/entry_points.txt +0 -0
- {karrio_server-2025.5rc17.dist-info → karrio_server-2025.5rc19.dist-info}/top_level.txt +0 -0
karrio/server/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2025.
|
|
1
|
+
2025.5rc19
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OpenTelemetry instrumentation for Huey task queue.
|
|
3
|
+
|
|
4
|
+
This module provides tracing support for Huey tasks, enabling distributed tracing
|
|
5
|
+
across API requests and background tasks.
|
|
6
|
+
"""
|
|
7
|
+
import functools
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Any, Callable, Dict, Optional
|
|
10
|
+
|
|
11
|
+
from opentelemetry import trace, context, propagate
|
|
12
|
+
from opentelemetry.trace import Status, StatusCode
|
|
13
|
+
from opentelemetry.semconv.trace import SpanAttributes
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class HueyInstrumentor:
|
|
19
|
+
"""Instrumentation for Huey task queue."""
|
|
20
|
+
|
|
21
|
+
_instance = None
|
|
22
|
+
_instrumented = False
|
|
23
|
+
|
|
24
|
+
def __new__(cls):
|
|
25
|
+
if cls._instance is None:
|
|
26
|
+
cls._instance = super().__new__(cls)
|
|
27
|
+
return cls._instance
|
|
28
|
+
|
|
29
|
+
def instrument(self, huey_instance=None):
|
|
30
|
+
"""
|
|
31
|
+
Instrument Huey for OpenTelemetry tracing.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
huey_instance: The Huey instance to instrument. If None, will try to
|
|
35
|
+
import from Django settings.
|
|
36
|
+
"""
|
|
37
|
+
if self._instrumented:
|
|
38
|
+
logger.debug("Huey already instrumented")
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
if huey_instance is None:
|
|
43
|
+
from django.conf import settings
|
|
44
|
+
huey_instance = settings.HUEY
|
|
45
|
+
|
|
46
|
+
# Wrap the task decorator
|
|
47
|
+
original_task = huey_instance.task
|
|
48
|
+
huey_instance.task = self._wrap_task_decorator(original_task, huey_instance)
|
|
49
|
+
|
|
50
|
+
# Wrap periodic tasks
|
|
51
|
+
if hasattr(huey_instance, 'periodic_task'):
|
|
52
|
+
original_periodic = huey_instance.periodic_task
|
|
53
|
+
huey_instance.periodic_task = self._wrap_task_decorator(original_periodic, huey_instance)
|
|
54
|
+
|
|
55
|
+
self._instrumented = True
|
|
56
|
+
logger.info("Huey instrumented for OpenTelemetry")
|
|
57
|
+
|
|
58
|
+
except Exception as e:
|
|
59
|
+
logger.warning(f"Failed to instrument Huey: {e}")
|
|
60
|
+
|
|
61
|
+
def _wrap_task_decorator(self, original_decorator: Callable, huey_instance) -> Callable:
|
|
62
|
+
"""Wrap the Huey task decorator to add tracing."""
|
|
63
|
+
|
|
64
|
+
@functools.wraps(original_decorator)
|
|
65
|
+
def wrapped_decorator(*args, **kwargs):
|
|
66
|
+
decorated = original_decorator(*args, **kwargs)
|
|
67
|
+
|
|
68
|
+
def task_wrapper(fn):
|
|
69
|
+
task_fn = decorated(fn)
|
|
70
|
+
|
|
71
|
+
@functools.wraps(task_fn)
|
|
72
|
+
def traced_task(*task_args, **task_kwargs):
|
|
73
|
+
tracer = trace.get_tracer(__name__)
|
|
74
|
+
|
|
75
|
+
# Extract trace context from task kwargs if present
|
|
76
|
+
trace_context = task_kwargs.pop('_otel_context', None)
|
|
77
|
+
if trace_context:
|
|
78
|
+
ctx = propagate.extract(trace_context)
|
|
79
|
+
token = context.attach(ctx)
|
|
80
|
+
else:
|
|
81
|
+
token = None
|
|
82
|
+
|
|
83
|
+
# Start span for the task
|
|
84
|
+
task_name = fn.__name__
|
|
85
|
+
with tracer.start_as_current_span(
|
|
86
|
+
f"huey.task.{task_name}",
|
|
87
|
+
kind=trace.SpanKind.CONSUMER,
|
|
88
|
+
) as span:
|
|
89
|
+
try:
|
|
90
|
+
# Set span attributes
|
|
91
|
+
span.set_attribute("messaging.system", "huey")
|
|
92
|
+
span.set_attribute("messaging.destination", task_name)
|
|
93
|
+
span.set_attribute("messaging.operation", "process")
|
|
94
|
+
span.set_attribute("task.name", task_name)
|
|
95
|
+
|
|
96
|
+
# Execute the task
|
|
97
|
+
result = task_fn(*task_args, **task_kwargs)
|
|
98
|
+
span.set_status(Status(StatusCode.OK))
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
except Exception as e:
|
|
102
|
+
span.set_status(Status(StatusCode.ERROR, str(e)))
|
|
103
|
+
span.record_exception(e)
|
|
104
|
+
raise
|
|
105
|
+
finally:
|
|
106
|
+
if token:
|
|
107
|
+
context.detach(token)
|
|
108
|
+
|
|
109
|
+
# Preserve original attributes
|
|
110
|
+
traced_task.task = task_fn.task if hasattr(task_fn, 'task') else task_fn
|
|
111
|
+
if hasattr(task_fn, '__name__'):
|
|
112
|
+
traced_task.__name__ = task_fn.__name__
|
|
113
|
+
if hasattr(task_fn, '__module__'):
|
|
114
|
+
traced_task.__module__ = task_fn.__module__
|
|
115
|
+
|
|
116
|
+
return traced_task
|
|
117
|
+
|
|
118
|
+
return task_wrapper
|
|
119
|
+
|
|
120
|
+
return wrapped_decorator
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def inject_trace_context(task_kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
|
124
|
+
"""
|
|
125
|
+
Inject current trace context into task kwargs for propagation.
|
|
126
|
+
|
|
127
|
+
This should be called when enqueuing a task to propagate the trace context
|
|
128
|
+
to the background worker.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
task_kwargs: The kwargs to be passed to the task
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Updated kwargs with trace context
|
|
135
|
+
"""
|
|
136
|
+
carrier = {}
|
|
137
|
+
propagate.inject(carrier)
|
|
138
|
+
if carrier:
|
|
139
|
+
task_kwargs['_otel_context'] = carrier
|
|
140
|
+
return task_kwargs
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def instrument_huey():
|
|
144
|
+
"""Convenience function to instrument Huey."""
|
|
145
|
+
instrumentor = HueyInstrumentor()
|
|
146
|
+
instrumentor.instrument()
|
karrio/server/settings/apm.py
CHANGED
|
@@ -56,3 +56,128 @@ if SENTRY_DSN:
|
|
|
56
56
|
# django.contrib.auth) you may enable sending PII data.
|
|
57
57
|
send_default_pii=True,
|
|
58
58
|
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# OpenTelemetry Configuration
|
|
62
|
+
OTEL_ENABLED = config("OTEL_ENABLED", default=False, cast=bool)
|
|
63
|
+
OTEL_SERVICE_NAME = config("OTEL_SERVICE_NAME", default="karrio-api")
|
|
64
|
+
OTEL_EXPORTER_OTLP_ENDPOINT = config("OTEL_EXPORTER_OTLP_ENDPOINT", default=None)
|
|
65
|
+
OTEL_EXPORTER_OTLP_PROTOCOL = config("OTEL_EXPORTER_OTLP_PROTOCOL", default="grpc")
|
|
66
|
+
OTEL_EXPORTER_OTLP_HEADERS = config("OTEL_EXPORTER_OTLP_HEADERS", default="")
|
|
67
|
+
OTEL_TRACES_EXPORTER = config("OTEL_TRACES_EXPORTER", default="otlp")
|
|
68
|
+
OTEL_METRICS_EXPORTER = config("OTEL_METRICS_EXPORTER", default="otlp")
|
|
69
|
+
OTEL_LOGS_EXPORTER = config("OTEL_LOGS_EXPORTER", default="otlp")
|
|
70
|
+
OTEL_RESOURCE_ATTRIBUTES = config("OTEL_RESOURCE_ATTRIBUTES", default="")
|
|
71
|
+
OTEL_ENVIRONMENT = config("OTEL_ENVIRONMENT", default=config("ENV", default="production"))
|
|
72
|
+
|
|
73
|
+
# Only initialize OpenTelemetry if enabled and endpoint is configured
|
|
74
|
+
if OTEL_ENABLED and OTEL_EXPORTER_OTLP_ENDPOINT:
|
|
75
|
+
import logging
|
|
76
|
+
from opentelemetry import trace, metrics
|
|
77
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
78
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
79
|
+
from opentelemetry.sdk.metrics import MeterProvider
|
|
80
|
+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
|
|
81
|
+
from opentelemetry.sdk.resources import Resource, SERVICE_NAME, SERVICE_VERSION
|
|
82
|
+
from opentelemetry.instrumentation.django import DjangoInstrumentor
|
|
83
|
+
from opentelemetry.instrumentation.requests import RequestsInstrumentor
|
|
84
|
+
from opentelemetry.instrumentation.logging import LoggingInstrumentor
|
|
85
|
+
from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor
|
|
86
|
+
from opentelemetry.instrumentation.redis import RedisInstrumentor
|
|
87
|
+
|
|
88
|
+
# Import appropriate exporter based on protocol
|
|
89
|
+
if OTEL_EXPORTER_OTLP_PROTOCOL == "grpc":
|
|
90
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
91
|
+
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
|
|
92
|
+
else: # http/protobuf
|
|
93
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
94
|
+
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
|
|
95
|
+
|
|
96
|
+
# Parse headers if provided
|
|
97
|
+
headers = {}
|
|
98
|
+
if OTEL_EXPORTER_OTLP_HEADERS:
|
|
99
|
+
for header_pair in OTEL_EXPORTER_OTLP_HEADERS.split(","):
|
|
100
|
+
if "=" in header_pair:
|
|
101
|
+
key, value = header_pair.split("=", 1)
|
|
102
|
+
headers[key.strip()] = value.strip()
|
|
103
|
+
|
|
104
|
+
# Parse resource attributes
|
|
105
|
+
resource_attributes = {
|
|
106
|
+
SERVICE_NAME: OTEL_SERVICE_NAME,
|
|
107
|
+
SERVICE_VERSION: config("VERSION", default="unknown"),
|
|
108
|
+
"environment": OTEL_ENVIRONMENT,
|
|
109
|
+
"deployment.environment": OTEL_ENVIRONMENT,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if OTEL_RESOURCE_ATTRIBUTES:
|
|
113
|
+
for attr_pair in OTEL_RESOURCE_ATTRIBUTES.split(","):
|
|
114
|
+
if "=" in attr_pair:
|
|
115
|
+
key, value = attr_pair.split("=", 1)
|
|
116
|
+
resource_attributes[key.strip()] = value.strip()
|
|
117
|
+
|
|
118
|
+
# Create resource
|
|
119
|
+
resource = Resource(attributes=resource_attributes)
|
|
120
|
+
|
|
121
|
+
# Configure Trace Provider
|
|
122
|
+
trace_provider = TracerProvider(resource=resource)
|
|
123
|
+
trace.set_tracer_provider(trace_provider)
|
|
124
|
+
|
|
125
|
+
# Configure span exporter
|
|
126
|
+
span_exporter = OTLPSpanExporter(
|
|
127
|
+
endpoint=OTEL_EXPORTER_OTLP_ENDPOINT,
|
|
128
|
+
headers=headers if headers else None,
|
|
129
|
+
)
|
|
130
|
+
span_processor = BatchSpanProcessor(span_exporter)
|
|
131
|
+
trace_provider.add_span_processor(span_processor)
|
|
132
|
+
|
|
133
|
+
# Configure Metrics Provider
|
|
134
|
+
metric_exporter = OTLPMetricExporter(
|
|
135
|
+
endpoint=OTEL_EXPORTER_OTLP_ENDPOINT,
|
|
136
|
+
headers=headers if headers else None,
|
|
137
|
+
)
|
|
138
|
+
metric_reader = PeriodicExportingMetricReader(
|
|
139
|
+
exporter=metric_exporter,
|
|
140
|
+
export_interval_millis=30000, # Export metrics every 30 seconds
|
|
141
|
+
)
|
|
142
|
+
meter_provider = MeterProvider(
|
|
143
|
+
resource=resource,
|
|
144
|
+
metric_readers=[metric_reader],
|
|
145
|
+
)
|
|
146
|
+
metrics.set_meter_provider(meter_provider)
|
|
147
|
+
|
|
148
|
+
# Instrument Django
|
|
149
|
+
DjangoInstrumentor().instrument(
|
|
150
|
+
is_sql_commentor_enabled=True, # Add trace context to SQL queries
|
|
151
|
+
request_hook=lambda span, request: span.set_attribute("http.client_ip", request.META.get("REMOTE_ADDR", "")),
|
|
152
|
+
response_hook=lambda span, request, response: span.set_attribute("http.response.size", len(response.content) if hasattr(response, 'content') else 0),
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Instrument other libraries
|
|
156
|
+
RequestsInstrumentor().instrument() # HTTP client requests
|
|
157
|
+
LoggingInstrumentor().instrument(set_logging_format=True) # Add trace context to logs
|
|
158
|
+
|
|
159
|
+
# Instrument database if PostgreSQL is used
|
|
160
|
+
if config("DATABASE_ENGINE", default="").endswith("postgresql"):
|
|
161
|
+
try:
|
|
162
|
+
Psycopg2Instrumentor().instrument()
|
|
163
|
+
except Exception:
|
|
164
|
+
pass # Psycopg2 might not be installed
|
|
165
|
+
|
|
166
|
+
# Instrument Redis if configured
|
|
167
|
+
if config("REDIS_HOST", default=None):
|
|
168
|
+
try:
|
|
169
|
+
RedisInstrumentor().instrument()
|
|
170
|
+
except Exception:
|
|
171
|
+
pass # Redis might not be installed
|
|
172
|
+
|
|
173
|
+
# Instrument Huey task queue (temporarily disabled due to compatibility issues)
|
|
174
|
+
# try:
|
|
175
|
+
# from karrio.server.lib.otel_huey import instrument_huey
|
|
176
|
+
# instrument_huey()
|
|
177
|
+
# except Exception as e:
|
|
178
|
+
# logger = logging.getLogger(__name__)
|
|
179
|
+
# logger.warning(f"Failed to instrument Huey: {e}")
|
|
180
|
+
|
|
181
|
+
# Log that OpenTelemetry is enabled
|
|
182
|
+
logger = logging.getLogger(__name__)
|
|
183
|
+
logger.info(f"OpenTelemetry enabled: Service={OTEL_SERVICE_NAME}, Endpoint={OTEL_EXPORTER_OTLP_ENDPOINT}")
|
|
@@ -51,3 +51,19 @@ else:
|
|
|
51
51
|
filename=WORKER_DB_FILE_NAME,
|
|
52
52
|
**({"immediate": WORKER_IMMEDIATE_MODE} if WORKER_IMMEDIATE_MODE else {}),
|
|
53
53
|
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# Apply OpenTelemetry instrumentation to Huey if enabled
|
|
57
|
+
OTEL_ENABLED = decouple.config("OTEL_ENABLED", default=False, cast=bool)
|
|
58
|
+
OTEL_EXPORTER_OTLP_ENDPOINT = decouple.config("OTEL_EXPORTER_OTLP_ENDPOINT", default=None)
|
|
59
|
+
|
|
60
|
+
if OTEL_ENABLED and OTEL_EXPORTER_OTLP_ENDPOINT:
|
|
61
|
+
try:
|
|
62
|
+
# Import and apply instrumentation to the Huey instance
|
|
63
|
+
from karrio.server.lib.otel_huey import HueyInstrumentor
|
|
64
|
+
instrumentor = HueyInstrumentor()
|
|
65
|
+
instrumentor.instrument(HUEY)
|
|
66
|
+
except Exception as e:
|
|
67
|
+
import logging
|
|
68
|
+
logger = logging.getLogger(__name__)
|
|
69
|
+
logger.warning(f"Failed to instrument Huey in worker settings: {e}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: karrio_server
|
|
3
|
-
Version: 2025.
|
|
3
|
+
Version: 2025.5rc19
|
|
4
4
|
Summary: Multi-carrier shipping API
|
|
5
5
|
Author-email: karrio <hello@karrio.io>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -33,6 +33,15 @@ Requires-Dist: python-decouple
|
|
|
33
33
|
Requires-Dist: karrio_server_core
|
|
34
34
|
Requires-Dist: sentry-sdk
|
|
35
35
|
Requires-Dist: whitenoise
|
|
36
|
+
Requires-Dist: opentelemetry-instrumentation-django
|
|
37
|
+
Requires-Dist: opentelemetry-api
|
|
38
|
+
Requires-Dist: opentelemetry-sdk
|
|
39
|
+
Requires-Dist: opentelemetry-exporter-otlp
|
|
40
|
+
Requires-Dist: opentelemetry-instrumentation-requests
|
|
41
|
+
Requires-Dist: opentelemetry-instrumentation-logging
|
|
42
|
+
Requires-Dist: opentelemetry-instrumentation-psycopg2
|
|
43
|
+
Requires-Dist: opentelemetry-instrumentation-redis
|
|
44
|
+
Requires-Dist: opentelemetry-semantic-conventions
|
|
36
45
|
|
|
37
46
|
# <a href="https://karrio.io" target="_blank"><img alt="Karrio" src="https://docs.karrio.io/img/logo.svg" height="50px" /></a>
|
|
38
47
|
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
karrio/server/VERSION,sha256=
|
|
1
|
+
karrio/server/VERSION,sha256=sr-32UZkYuBmbyg3HAaDl4O8VhbDcZwfgiCiLqyBUkk,10
|
|
2
2
|
karrio/server/__init__.py,sha256=iOEMwnlORWezdO8-2vxBIPSR37D7JGjluZ8f55vzxls,81
|
|
3
3
|
karrio/server/__main__.py,sha256=hy2-Zb2wSVe_Pu6zWZ-BhiM4rBaGZ3D16HSuhudygqg,632
|
|
4
4
|
karrio/server/asgi.py,sha256=LsZYMWo8U9zURVPdHnvUsziOhMjdCdQoD2-gMJbS2U0,462
|
|
5
5
|
karrio/server/workers.py,sha256=wOlWmXC7zRJV86IfbQnfUZsnCIvvWtXzFHH_EIkg1J0,179
|
|
6
6
|
karrio/server/wsgi.py,sha256=SpWqkEYlMsj89_znZ8p8IjH3EgTVRWRq_9eS8t64dMw,403
|
|
7
|
+
karrio/server/lib/otel_huey.py,sha256=6MP6vX6b6x6RPF2K1m8B8L8S9GK1Q3vANmLidsxh65k,5428
|
|
7
8
|
karrio/server/settings/__init__.py,sha256=iw-NBcReOnDYpnvSEBdYDfV7jC0040jYdupnmSdElec,866
|
|
8
|
-
karrio/server/settings/apm.py,sha256=
|
|
9
|
+
karrio/server/settings/apm.py,sha256=963moZDKcwEelUh1AmLQY1RRsm5qk3neugJXxk6fBY0,7322
|
|
9
10
|
karrio/server/settings/base.py,sha256=iryAK2fL_mul9UjHp4lU57S0Lzk-AEvpUSSf0_E1E9U,20375
|
|
10
11
|
karrio/server/settings/cache.py,sha256=BCFIjbLKBgYs34i099Z_i8EC55lRF39oJfsPHoXq3vc,1112
|
|
11
12
|
karrio/server/settings/constance.py,sha256=wKi7u-NORAPjIJMIbl3k5kPRkUA6jJJWe9kxsV3aoeA,7113
|
|
12
13
|
karrio/server/settings/debug.py,sha256=fFyK2XX47UGeK0eRNSV-9ZLaComay5QvJW0692vaH98,527
|
|
13
14
|
karrio/server/settings/email.py,sha256=bHBLKM_v3HTkmjrz_Msdj_Z7_kMzAb7i6pvJCZuzu1E,1403
|
|
14
|
-
karrio/server/settings/workers.py,sha256=
|
|
15
|
+
karrio/server/settings/workers.py,sha256=xoXvaZgxKKVlvB3xJgq0PFCTEhNQf9rte_Qy6BUSY28,2360
|
|
15
16
|
karrio/server/static/extra/branding/android-chrome-192x192.png,sha256=qSwEKKBtk4udSHb0OWGC5-jfkP5cVpULDD1Cdkuk8PU,7005
|
|
16
17
|
karrio/server/static/extra/branding/android-chrome-512x512.png,sha256=YFwVPnPChO30tTuyrwSc_z548H7C6OFXh4CCu2krvHA,21062
|
|
17
18
|
karrio/server/static/extra/branding/favicon-16x16.png,sha256=wkELpij29bIvvKr5sDcjfNeYvj7i0yk-__bJbZckEK8,1085
|
|
@@ -72,8 +73,8 @@ karrio/server/templates/admin/base_site.html,sha256=kbcdvehXZ1EHaw07JL7fSZmjrnVM
|
|
|
72
73
|
karrio/server/templates/openapi/openapi.html,sha256=3ApCZ5pE6Wjv7CJllVbqD2WiDQuLy-BFS-IIHurXhBY,1133
|
|
73
74
|
karrio/server/urls/__init__.py,sha256=Ah-XqaqRsfecQgCGRHjxmXe8O7a0avq5ocU90tkVwQI,1998
|
|
74
75
|
karrio/server/urls/jwt.py,sha256=QN2L-EpUEQCF2UGYPu_VVlA49Fc0BtcY7Ov3-xpp7_U,6772
|
|
75
|
-
karrio_server-2025.
|
|
76
|
-
karrio_server-2025.
|
|
77
|
-
karrio_server-2025.
|
|
78
|
-
karrio_server-2025.
|
|
79
|
-
karrio_server-2025.
|
|
76
|
+
karrio_server-2025.5rc19.dist-info/METADATA,sha256=aQuJAF1GCpbIxds8rUfklOJcYJ23MrQYC6w2OzOPkZY,4283
|
|
77
|
+
karrio_server-2025.5rc19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
78
|
+
karrio_server-2025.5rc19.dist-info/entry_points.txt,sha256=c2eftt6MpJjyp0OFv1OmO9nUYSDemt9fGq_RDdvpGLw,55
|
|
79
|
+
karrio_server-2025.5rc19.dist-info/top_level.txt,sha256=D1D7x8R3cTfjF_15mfiO7wCQ5QMtuM4x8GaPr7z5i78,12
|
|
80
|
+
karrio_server-2025.5rc19.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|