iii-sdk 0.14.0.dev2__tar.gz → 0.16.0.dev1__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.
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/PKG-INFO +2 -2
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/pyproject.toml +5 -2
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/__init__.py +41 -18
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/iii.py +4 -4
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/iii_constants.py +1 -20
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_baggage_span_processor.py +1 -1
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_context_propagation.py +1 -2
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_hold_process.py +6 -6
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_http_external_functions_integration.py +2 -2
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_iii_registration_dedup.py +7 -7
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_init_api.py +3 -3
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_logger_function_ids.py +1 -1
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_logger_otel.py +8 -10
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_payload.py +1 -1
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_register_function_args.py +2 -2
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_span_ops.py +1 -1
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_sync_api.py +2 -2
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_telemetry.py +6 -6
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_telemetry_exporters.py +2 -2
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_telemetry_types.py +1 -1
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_trace_helpers.py +1 -1
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/uv.lock +75 -3
- iii_sdk-0.14.0.dev2/src/iii/baggage_span_processor.py +0 -42
- iii_sdk-0.14.0.dev2/src/iii/logger.py +0 -184
- iii_sdk-0.14.0.dev2/src/iii/payload.py +0 -92
- iii_sdk-0.14.0.dev2/src/iii/span_ops.py +0 -38
- iii_sdk-0.14.0.dev2/src/iii/telemetry.py +0 -571
- iii_sdk-0.14.0.dev2/src/iii/telemetry_exporters.py +0 -457
- iii_sdk-0.14.0.dev2/src/iii/telemetry_types.py +0 -46
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/.gitignore +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/README.md +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/channels.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/errors.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/format_utils.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/iii_types.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/otel_worker_gauges.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/state.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/stream.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/triggers.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/types.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/utils.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/src/iii/worker_metrics.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/conftest.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_api_triggers.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_async_api.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_bridge.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_channel_close_delay.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_data_channels.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_errors.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_format_utils.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_healthcheck.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_invocation_exception.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_middleware.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_pubsub.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_queue_integration.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_rbac_workers.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_state.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_stream_models.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_streams.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_streams_runtime_annotations.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_trigger_metadata.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_trigger_registration_error.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_utils.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_worker_metadata.py +0 -0
- {iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_worker_metrics.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iii-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.16.0.dev1
|
|
4
4
|
Summary: III SDK for Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/iii-hq/iii
|
|
6
6
|
Project-URL: Repository, https://github.com/iii-hq/iii
|
|
@@ -14,8 +14,8 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: iii-observability==0.13.0.dev1
|
|
17
18
|
Requires-Dist: opentelemetry-api>=1.25
|
|
18
|
-
Requires-Dist: opentelemetry-sdk>=1.25
|
|
19
19
|
Requires-Dist: pydantic>=2.0
|
|
20
20
|
Requires-Dist: websockets>=12.0
|
|
21
21
|
Provides-Extra: dev
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "iii-sdk"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.16.0.dev1"
|
|
8
8
|
description = "III SDK for Python"
|
|
9
9
|
authors = [{ name = "III" }]
|
|
10
10
|
license = { text = "Apache-2.0" }
|
|
@@ -23,13 +23,16 @@ dependencies = [
|
|
|
23
23
|
"websockets>=12.0",
|
|
24
24
|
"pydantic>=2.0",
|
|
25
25
|
"opentelemetry-api>=1.25",
|
|
26
|
-
"
|
|
26
|
+
"iii-observability==0.13.0.dev1",
|
|
27
27
|
]
|
|
28
28
|
|
|
29
29
|
[project.urls]
|
|
30
30
|
Homepage = "https://github.com/iii-hq/iii"
|
|
31
31
|
Repository = "https://github.com/iii-hq/iii"
|
|
32
32
|
|
|
33
|
+
[tool.uv.sources]
|
|
34
|
+
iii-observability = { path = "../observability", editable = true }
|
|
35
|
+
|
|
33
36
|
[project.optional-dependencies]
|
|
34
37
|
dev = [
|
|
35
38
|
"pytest>=8.0",
|
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
"""III SDK for Python."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from iii_observability import (
|
|
4
|
+
DEFAULT_ALLOWLIST,
|
|
5
|
+
REDACTED_PLACEHOLDER,
|
|
6
|
+
BaggageSpanProcessor,
|
|
7
|
+
Logger,
|
|
8
|
+
OtelConfig,
|
|
9
|
+
ReconnectionConfig,
|
|
10
|
+
current_span_id,
|
|
11
|
+
current_span_is_recording,
|
|
12
|
+
current_trace_id,
|
|
13
|
+
execute_traced_request,
|
|
14
|
+
extract_baggage,
|
|
15
|
+
extract_traceparent,
|
|
16
|
+
flush_otel,
|
|
17
|
+
init_otel,
|
|
18
|
+
inject_baggage,
|
|
19
|
+
inject_traceparent,
|
|
20
|
+
record_span_event,
|
|
21
|
+
redact,
|
|
22
|
+
redact_and_truncate,
|
|
23
|
+
resolve_max_bytes_from_env,
|
|
24
|
+
set_current_span_attribute,
|
|
25
|
+
set_current_span_error,
|
|
26
|
+
shutdown_otel,
|
|
27
|
+
with_span,
|
|
28
|
+
)
|
|
29
|
+
|
|
4
30
|
from .channels import ChannelReader, ChannelWriter
|
|
5
31
|
from .errors import IIIForbiddenError, IIIInvocationError, IIITimeoutError
|
|
6
32
|
from .format_utils import extract_request_format, extract_response_format, python_type_to_format
|
|
7
33
|
from .iii import TriggerAction, register_worker
|
|
8
|
-
from .iii_constants import FunctionRef, InitOptions,
|
|
34
|
+
from .iii_constants import FunctionRef, InitOptions, TelemetryOptions
|
|
9
35
|
from .iii_types import (
|
|
10
36
|
AuthInput,
|
|
11
37
|
AuthResult,
|
|
@@ -31,19 +57,6 @@ from .iii_types import (
|
|
|
31
57
|
TriggerActionVoid,
|
|
32
58
|
TriggerRequest,
|
|
33
59
|
)
|
|
34
|
-
from .logger import Logger
|
|
35
|
-
from .payload import (
|
|
36
|
-
REDACTED_PLACEHOLDER,
|
|
37
|
-
redact,
|
|
38
|
-
redact_and_truncate,
|
|
39
|
-
resolve_max_bytes_from_env,
|
|
40
|
-
)
|
|
41
|
-
from .span_ops import (
|
|
42
|
-
current_span_is_recording,
|
|
43
|
-
record_span_event,
|
|
44
|
-
set_current_span_attribute,
|
|
45
|
-
set_current_span_error,
|
|
46
|
-
)
|
|
47
60
|
from .stream import (
|
|
48
61
|
IStream,
|
|
49
62
|
StreamChangeEvent,
|
|
@@ -53,7 +66,6 @@ from .stream import (
|
|
|
53
66
|
StreamJoinLeaveTriggerConfig,
|
|
54
67
|
StreamTriggerConfig,
|
|
55
68
|
)
|
|
56
|
-
from .telemetry_types import OtelConfig
|
|
57
69
|
from .triggers import Trigger, TriggerConfig, TriggerHandler, TriggerTypeRef
|
|
58
70
|
from .types import (
|
|
59
71
|
ApiRequest,
|
|
@@ -72,13 +84,24 @@ __all__ = [
|
|
|
72
84
|
"BaggageSpanProcessor",
|
|
73
85
|
"DEFAULT_ALLOWLIST",
|
|
74
86
|
"REDACTED_PLACEHOLDER",
|
|
87
|
+
"current_span_id",
|
|
75
88
|
"current_span_is_recording",
|
|
89
|
+
"current_trace_id",
|
|
90
|
+
"execute_traced_request",
|
|
91
|
+
"extract_baggage",
|
|
92
|
+
"extract_traceparent",
|
|
93
|
+
"flush_otel",
|
|
94
|
+
"init_otel",
|
|
95
|
+
"inject_baggage",
|
|
96
|
+
"inject_traceparent",
|
|
76
97
|
"record_span_event",
|
|
77
|
-
"set_current_span_attribute",
|
|
78
|
-
"set_current_span_error",
|
|
79
98
|
"redact",
|
|
80
99
|
"redact_and_truncate",
|
|
81
100
|
"resolve_max_bytes_from_env",
|
|
101
|
+
"set_current_span_attribute",
|
|
102
|
+
"set_current_span_error",
|
|
103
|
+
"shutdown_otel",
|
|
104
|
+
"with_span",
|
|
82
105
|
# Channels
|
|
83
106
|
"ChannelReader",
|
|
84
107
|
"ChannelWriter",
|
|
@@ -14,6 +14,7 @@ from importlib.metadata import version
|
|
|
14
14
|
from typing import Any, Awaitable, Callable, Coroutine, TypeVar, cast
|
|
15
15
|
|
|
16
16
|
import websockets
|
|
17
|
+
from iii_observability import OtelConfig
|
|
17
18
|
from websockets.asyncio.client import ClientConnection
|
|
18
19
|
|
|
19
20
|
from .channels import ChannelReader, ChannelWriter
|
|
@@ -54,7 +55,6 @@ from .stream import (
|
|
|
54
55
|
StreamListInput,
|
|
55
56
|
StreamSetInput,
|
|
56
57
|
)
|
|
57
|
-
from .telemetry_types import OtelConfig
|
|
58
58
|
from .triggers import Trigger, TriggerConfig, TriggerHandler, TriggerTypeRef
|
|
59
59
|
from .types import Channel, RemoteFunctionData, RemoteTriggerTypeData, is_channel_ref
|
|
60
60
|
|
|
@@ -224,7 +224,7 @@ class III:
|
|
|
224
224
|
from an async context.
|
|
225
225
|
"""
|
|
226
226
|
self._running = True
|
|
227
|
-
from .telemetry import attach_event_loop, init_otel
|
|
227
|
+
from iii_observability.telemetry import attach_event_loop, init_otel
|
|
228
228
|
|
|
229
229
|
loop = asyncio.get_running_loop()
|
|
230
230
|
otel_cfg: OtelConfig | None = None
|
|
@@ -280,7 +280,7 @@ class III:
|
|
|
280
280
|
|
|
281
281
|
self._set_connection_state("disconnected")
|
|
282
282
|
|
|
283
|
-
from .telemetry import shutdown_otel_async
|
|
283
|
+
from iii_observability.telemetry import shutdown_otel_async
|
|
284
284
|
|
|
285
285
|
await shutdown_otel_async()
|
|
286
286
|
|
|
@@ -499,7 +499,7 @@ class III:
|
|
|
499
499
|
tracer = trace.get_tracer("iii-python-sdk")
|
|
500
500
|
import os
|
|
501
501
|
|
|
502
|
-
from
|
|
502
|
+
from iii_observability import redact_and_truncate, resolve_max_bytes_from_env
|
|
503
503
|
|
|
504
504
|
trace_payloads = os.environ.get("III_DISABLE_TRACE_PAYLOADS", "").lower() not in (
|
|
505
505
|
"1",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from typing import Any, Callable, Literal
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
from iii_observability import OtelConfig, ReconnectionConfig
|
|
7
7
|
|
|
8
8
|
IIIConnectionState = Literal["disconnected", "connecting", "connected", "reconnecting", "failed"]
|
|
9
9
|
|
|
@@ -13,25 +13,6 @@ DEFAULT_INVOCATION_TIMEOUT_MS = 30000
|
|
|
13
13
|
MAX_QUEUE_SIZE = 1000
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
@dataclass
|
|
17
|
-
class ReconnectionConfig:
|
|
18
|
-
"""Configuration for WebSocket reconnection behavior.
|
|
19
|
-
|
|
20
|
-
Attributes:
|
|
21
|
-
initial_delay_ms: Starting delay in milliseconds. Default ``1000``.
|
|
22
|
-
max_delay_ms: Maximum delay cap in milliseconds. Default ``30000``.
|
|
23
|
-
backoff_multiplier: Exponential backoff multiplier. Default ``2.0``.
|
|
24
|
-
jitter_factor: Random jitter factor (0--1). Default ``0.3``.
|
|
25
|
-
max_retries: Maximum retry attempts. ``-1`` for infinite. Default ``-1``.
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
initial_delay_ms: int = 1000
|
|
29
|
-
max_delay_ms: int = 30000
|
|
30
|
-
backoff_multiplier: float = 2.0
|
|
31
|
-
jitter_factor: float = 0.3
|
|
32
|
-
max_retries: int = -1
|
|
33
|
-
|
|
34
|
-
|
|
35
16
|
DEFAULT_RECONNECTION_CONFIG = ReconnectionConfig()
|
|
36
17
|
|
|
37
18
|
|
|
@@ -10,7 +10,7 @@ from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
|
|
|
10
10
|
)
|
|
11
11
|
from opentelemetry.sdk.trace.sampling import ALWAYS_OFF
|
|
12
12
|
|
|
13
|
-
from
|
|
13
|
+
from iii_observability import (
|
|
14
14
|
DEFAULT_ALLOWLIST,
|
|
15
15
|
BaggageSpanProcessor,
|
|
16
16
|
)
|
|
@@ -7,8 +7,7 @@ import pytest
|
|
|
7
7
|
import iii.iii as iii_module
|
|
8
8
|
from iii.iii import III
|
|
9
9
|
from iii.iii_constants import InitOptions
|
|
10
|
-
from
|
|
11
|
-
from iii.telemetry_types import OtelConfig
|
|
10
|
+
from iii_observability import OtelConfig, init_otel, shutdown_otel
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
@pytest.fixture(autouse=True)
|
|
@@ -5,8 +5,8 @@ from iii.iii import III
|
|
|
5
5
|
|
|
6
6
|
def test_background_thread_is_not_daemon(monkeypatch) -> None:
|
|
7
7
|
"""The background event-loop thread must be non-daemon so it keeps the process alive."""
|
|
8
|
-
monkeypatch.setattr("
|
|
9
|
-
monkeypatch.setattr("
|
|
8
|
+
monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
|
|
9
|
+
monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
|
|
10
10
|
|
|
11
11
|
async def fake_do_connect(self: III) -> None:
|
|
12
12
|
return None
|
|
@@ -23,8 +23,8 @@ def test_background_thread_is_not_daemon(monkeypatch) -> None:
|
|
|
23
23
|
|
|
24
24
|
def test_shutdown_stops_background_thread(monkeypatch) -> None:
|
|
25
25
|
"""After shutdown(), the background thread should stop within a reasonable timeout."""
|
|
26
|
-
monkeypatch.setattr("
|
|
27
|
-
monkeypatch.setattr("
|
|
26
|
+
monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
|
|
27
|
+
monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
|
|
28
28
|
|
|
29
29
|
async def fake_do_connect(self: III) -> None:
|
|
30
30
|
return None
|
|
@@ -43,8 +43,8 @@ def test_shutdown_stops_background_thread(monkeypatch) -> None:
|
|
|
43
43
|
|
|
44
44
|
def test_shutdown_async_stops_background_thread(monkeypatch) -> None:
|
|
45
45
|
"""After shutdown_async(), the background thread should also stop."""
|
|
46
|
-
monkeypatch.setattr("
|
|
47
|
-
monkeypatch.setattr("
|
|
46
|
+
monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
|
|
47
|
+
monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
|
|
48
48
|
|
|
49
49
|
async def fake_do_connect(self: III) -> None:
|
|
50
50
|
return None
|
{iii_sdk-0.14.0.dev2 → iii_sdk-0.16.0.dev1}/tests/test_http_external_functions_integration.py
RENAMED
|
@@ -157,8 +157,8 @@ def _make_fake_ws_env(monkeypatch: pytest.MonkeyPatch) -> list[dict[str, Any]]:
|
|
|
157
157
|
return FakeWs()
|
|
158
158
|
|
|
159
159
|
monkeypatch.setattr(iii_module.websockets, "connect", fake_connect)
|
|
160
|
-
monkeypatch.setattr("
|
|
161
|
-
monkeypatch.setattr("
|
|
160
|
+
monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
|
|
161
|
+
monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
|
|
162
162
|
monkeypatch.setattr(iii_module.III, "_register_worker_metadata", lambda self: None)
|
|
163
163
|
return sent
|
|
164
164
|
|
|
@@ -17,7 +17,7 @@ def reset_otel():
|
|
|
17
17
|
# III.connect() calls init_otel() which sets global providers;
|
|
18
18
|
# reset them so subsequent test files start with a clean slate.
|
|
19
19
|
try:
|
|
20
|
-
from
|
|
20
|
+
from iii_observability import shutdown_otel
|
|
21
21
|
|
|
22
22
|
shutdown_otel()
|
|
23
23
|
except Exception:
|
|
@@ -73,8 +73,8 @@ def test_preconnect_registration_sent_once(monkeypatch: pytest.MonkeyPatch) -> N
|
|
|
73
73
|
return ws
|
|
74
74
|
|
|
75
75
|
monkeypatch.setattr(iii_module.websockets, "connect", fake_connect)
|
|
76
|
-
monkeypatch.setattr("
|
|
77
|
-
monkeypatch.setattr("
|
|
76
|
+
monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
|
|
77
|
+
monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
|
|
78
78
|
monkeypatch.setattr(iii_module.III, "_register_worker_metadata", lambda self: None)
|
|
79
79
|
|
|
80
80
|
client = III("ws://fake")
|
|
@@ -110,8 +110,8 @@ def test_reconnect_replays_durable_state_once_per_connection(
|
|
|
110
110
|
return ws
|
|
111
111
|
|
|
112
112
|
monkeypatch.setattr(iii_module.websockets, "connect", fake_connect)
|
|
113
|
-
monkeypatch.setattr("
|
|
114
|
-
monkeypatch.setattr("
|
|
113
|
+
monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
|
|
114
|
+
monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
|
|
115
115
|
monkeypatch.setattr(iii_module.III, "_register_worker_metadata", lambda self: None)
|
|
116
116
|
|
|
117
117
|
client = III("ws://fake")
|
|
@@ -153,8 +153,8 @@ def test_call_void_queued_while_disconnected_flushes_after_connect(
|
|
|
153
153
|
return ws
|
|
154
154
|
|
|
155
155
|
monkeypatch.setattr(iii_module.websockets, "connect", fake_connect)
|
|
156
|
-
monkeypatch.setattr("
|
|
157
|
-
monkeypatch.setattr("
|
|
156
|
+
monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
|
|
157
|
+
monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
|
|
158
158
|
monkeypatch.setattr(iii_module.III, "_register_worker_metadata", lambda self: None)
|
|
159
159
|
|
|
160
160
|
client = III("ws://fake")
|
|
@@ -11,8 +11,8 @@ def test_register_worker_returns_iii_instance(monkeypatch) -> None:
|
|
|
11
11
|
async def fake_do_connect(self: III) -> None:
|
|
12
12
|
return None
|
|
13
13
|
|
|
14
|
-
monkeypatch.setattr("
|
|
15
|
-
monkeypatch.setattr("
|
|
14
|
+
monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
|
|
15
|
+
monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
|
|
16
16
|
monkeypatch.setattr(III, "_do_connect", fake_do_connect)
|
|
17
17
|
|
|
18
18
|
client = register_worker("ws://fake")
|
|
@@ -28,7 +28,7 @@ def test_register_worker_is_sync() -> None:
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
def test_connect_consumes_otel_from_init_options(monkeypatch) -> None:
|
|
31
|
-
import
|
|
31
|
+
import iii_observability.telemetry as telemetry
|
|
32
32
|
|
|
33
33
|
captured = {"config": None}
|
|
34
34
|
|
|
@@ -7,7 +7,7 @@ import pytest
|
|
|
7
7
|
|
|
8
8
|
@pytest.fixture(autouse=True)
|
|
9
9
|
def reset_otel():
|
|
10
|
-
from
|
|
10
|
+
from iii_observability import shutdown_otel
|
|
11
11
|
|
|
12
12
|
yield
|
|
13
13
|
shutdown_otel()
|
|
@@ -49,9 +49,7 @@ def _setup_in_memory_log_provider():
|
|
|
49
49
|
|
|
50
50
|
def test_logger_emits_otel_record_when_initialized():
|
|
51
51
|
"""Logger.info emits an OTel LogRecord with severity INFO when OTel is active."""
|
|
52
|
-
from
|
|
53
|
-
from iii.telemetry import init_otel
|
|
54
|
-
from iii.telemetry_types import OtelConfig
|
|
52
|
+
from iii_observability import Logger, OtelConfig, init_otel
|
|
55
53
|
|
|
56
54
|
log_exporter = _setup_in_memory_log_provider()
|
|
57
55
|
init_otel(OtelConfig(enabled=True, logs_enabled=False)) # skip EngineLogExporter
|
|
@@ -68,11 +66,11 @@ def test_logger_emits_otel_record_when_initialized():
|
|
|
68
66
|
def test_logger_emits_warn_severity():
|
|
69
67
|
from opentelemetry._logs import SeverityNumber
|
|
70
68
|
|
|
71
|
-
from
|
|
69
|
+
from iii_observability import Logger
|
|
72
70
|
|
|
73
71
|
log_exporter = _setup_in_memory_log_provider()
|
|
74
72
|
|
|
75
|
-
with patch("
|
|
73
|
+
with patch("iii_observability.logger._is_initialized", return_value=True):
|
|
76
74
|
logger = Logger()
|
|
77
75
|
logger.warn("watch out")
|
|
78
76
|
|
|
@@ -86,7 +84,7 @@ def test_logger_attaches_trace_context_from_active_span():
|
|
|
86
84
|
from opentelemetry import trace
|
|
87
85
|
from opentelemetry.sdk.trace import TracerProvider
|
|
88
86
|
|
|
89
|
-
from
|
|
87
|
+
from iii_observability import Logger
|
|
90
88
|
|
|
91
89
|
log_exporter = _setup_in_memory_log_provider()
|
|
92
90
|
|
|
@@ -94,7 +92,7 @@ def test_logger_attaches_trace_context_from_active_span():
|
|
|
94
92
|
trace.set_tracer_provider(tracer_provider)
|
|
95
93
|
tracer = tracer_provider.get_tracer("test")
|
|
96
94
|
|
|
97
|
-
with patch("
|
|
95
|
+
with patch("iii_observability.logger._is_initialized", return_value=True):
|
|
98
96
|
with tracer.start_as_current_span("test-span") as span:
|
|
99
97
|
logger = Logger(service_name="fn1")
|
|
100
98
|
logger.info("inside span")
|
|
@@ -110,11 +108,11 @@ def test_logger_attaches_trace_context_from_active_span():
|
|
|
110
108
|
|
|
111
109
|
def test_logger_no_trace_context_outside_span():
|
|
112
110
|
"""Logger emits zero trace_id/span_id when no active span."""
|
|
113
|
-
from
|
|
111
|
+
from iii_observability import Logger
|
|
114
112
|
|
|
115
113
|
log_exporter = _setup_in_memory_log_provider()
|
|
116
114
|
|
|
117
|
-
with patch("
|
|
115
|
+
with patch("iii_observability.logger._is_initialized", return_value=True):
|
|
118
116
|
logger = Logger(service_name="fn1")
|
|
119
117
|
logger.info("outside span")
|
|
120
118
|
|
|
@@ -44,8 +44,8 @@ def _patch_ws(monkeypatch: pytest.MonkeyPatch) -> FakeWebSocket:
|
|
|
44
44
|
return ws
|
|
45
45
|
|
|
46
46
|
monkeypatch.setattr(iii_module.websockets, "connect", fake_connect)
|
|
47
|
-
monkeypatch.setattr("
|
|
48
|
-
monkeypatch.setattr("
|
|
47
|
+
monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
|
|
48
|
+
monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
|
|
49
49
|
monkeypatch.setattr(iii_module.III, "_register_worker_metadata", lambda self: None)
|
|
50
50
|
return ws
|
|
51
51
|
|
|
@@ -6,7 +6,7 @@ from opentelemetry.sdk.trace.export import SimpleSpanProcessor
|
|
|
6
6
|
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
|
|
7
7
|
from opentelemetry.trace import StatusCode
|
|
8
8
|
|
|
9
|
-
from
|
|
9
|
+
from iii_observability import (
|
|
10
10
|
current_span_is_recording,
|
|
11
11
|
record_span_event,
|
|
12
12
|
set_current_span_attribute,
|
|
@@ -46,8 +46,8 @@ def _patch_ws(monkeypatch: pytest.MonkeyPatch) -> FakeWebSocket:
|
|
|
46
46
|
return ws
|
|
47
47
|
|
|
48
48
|
monkeypatch.setattr(iii_module.websockets, "connect", fake_connect)
|
|
49
|
-
monkeypatch.setattr("
|
|
50
|
-
monkeypatch.setattr("
|
|
49
|
+
monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
|
|
50
|
+
monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
|
|
51
51
|
monkeypatch.setattr(iii_module.III, "_register_worker_metadata", lambda self: None)
|
|
52
52
|
return ws
|
|
53
53
|
|
|
@@ -4,8 +4,8 @@ import urllib.request
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
7
|
+
from iii_observability.telemetry import _get_tracer, _is_initialized, init_otel, shutdown_otel, shutdown_otel_async
|
|
8
|
+
from iii_observability import OtelConfig
|
|
9
9
|
|
|
10
10
|
# URLLibInstrumentor patches OpenerDirector.open, not urlopen directly
|
|
11
11
|
ORIGINAL_OPENER_OPEN = urllib.request.OpenerDirector.open
|
|
@@ -87,8 +87,8 @@ def test_shutdown_without_init_is_safe():
|
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
def test_telemetry_apis_importable_from_submodules():
|
|
90
|
-
from
|
|
91
|
-
from
|
|
90
|
+
from iii_observability.telemetry import _get_tracer, _is_initialized, init_otel, shutdown_otel
|
|
91
|
+
from iii_observability import OtelConfig
|
|
92
92
|
|
|
93
93
|
assert callable(init_otel)
|
|
94
94
|
assert callable(shutdown_otel)
|
|
@@ -103,7 +103,7 @@ def test_init_configures_engine_span_exporter():
|
|
|
103
103
|
from opentelemetry.sdk.trace import TracerProvider
|
|
104
104
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
105
105
|
|
|
106
|
-
from
|
|
106
|
+
from iii_observability.telemetry_exporters import EngineSpanExporter
|
|
107
107
|
|
|
108
108
|
init_otel(OtelConfig(enabled=True))
|
|
109
109
|
provider = trace.get_tracer_provider()
|
|
@@ -139,7 +139,7 @@ def test_shutdown_closes_connection():
|
|
|
139
139
|
import asyncio
|
|
140
140
|
from unittest.mock import AsyncMock, patch
|
|
141
141
|
|
|
142
|
-
from
|
|
142
|
+
from iii_observability.telemetry_exporters import SharedEngineConnection
|
|
143
143
|
|
|
144
144
|
with (
|
|
145
145
|
patch.object(SharedEngineConnection, "start"),
|
|
@@ -6,7 +6,7 @@ from unittest.mock import MagicMock
|
|
|
6
6
|
|
|
7
7
|
import pytest
|
|
8
8
|
|
|
9
|
-
from
|
|
9
|
+
from iii_observability.telemetry_exporters import EngineLogExporter, EngineSpanExporter, SharedEngineConnection
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def _make_mock_connection():
|
|
@@ -184,7 +184,7 @@ def test_serialize_metrics_emits_empty_string_for_missing_scope_version():
|
|
|
184
184
|
the Python OTel SDK. The serializer must emit "" (not JSON null) so the Rust
|
|
185
185
|
engine's String deserializer doesn't reject the payload.
|
|
186
186
|
"""
|
|
187
|
-
from
|
|
187
|
+
from iii_observability.telemetry_exporters import _serialize_metrics
|
|
188
188
|
from opentelemetry.sdk.metrics import MeterProvider
|
|
189
189
|
from opentelemetry.sdk.metrics.export import InMemoryMetricReader
|
|
190
190
|
from opentelemetry.sdk.resources import Resource
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from opentelemetry import trace
|
|
2
2
|
from opentelemetry.sdk.trace import TracerProvider
|
|
3
3
|
|
|
4
|
-
from
|
|
4
|
+
from iii_observability import current_span_id, current_trace_id
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def test_trace_helpers_follow_the_active_span() -> None:
|
|
@@ -194,6 +194,15 @@ wheels = [
|
|
|
194
194
|
{ url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
|
|
195
195
|
]
|
|
196
196
|
|
|
197
|
+
[[package]]
|
|
198
|
+
name = "certifi"
|
|
199
|
+
version = "2026.5.20"
|
|
200
|
+
source = { registry = "https://pypi.org/simple" }
|
|
201
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" }
|
|
202
|
+
wheels = [
|
|
203
|
+
{ url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" },
|
|
204
|
+
]
|
|
205
|
+
|
|
197
206
|
[[package]]
|
|
198
207
|
name = "colorama"
|
|
199
208
|
version = "0.4.6"
|
|
@@ -489,6 +498,43 @@ wheels = [
|
|
|
489
498
|
{ url = "https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl", hash = "sha256:01284878c966508b6d6f1dbff9b6fa607bc062d8261c5c7253cb285b06422a7f", size = 142004, upload-time = "2026-02-09T19:09:40.561Z" },
|
|
490
499
|
]
|
|
491
500
|
|
|
501
|
+
[[package]]
|
|
502
|
+
name = "h11"
|
|
503
|
+
version = "0.16.0"
|
|
504
|
+
source = { registry = "https://pypi.org/simple" }
|
|
505
|
+
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
|
|
506
|
+
wheels = [
|
|
507
|
+
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
|
508
|
+
]
|
|
509
|
+
|
|
510
|
+
[[package]]
|
|
511
|
+
name = "httpcore"
|
|
512
|
+
version = "1.0.9"
|
|
513
|
+
source = { registry = "https://pypi.org/simple" }
|
|
514
|
+
dependencies = [
|
|
515
|
+
{ name = "certifi" },
|
|
516
|
+
{ name = "h11" },
|
|
517
|
+
]
|
|
518
|
+
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
|
|
519
|
+
wheels = [
|
|
520
|
+
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
|
521
|
+
]
|
|
522
|
+
|
|
523
|
+
[[package]]
|
|
524
|
+
name = "httpx"
|
|
525
|
+
version = "0.28.1"
|
|
526
|
+
source = { registry = "https://pypi.org/simple" }
|
|
527
|
+
dependencies = [
|
|
528
|
+
{ name = "anyio" },
|
|
529
|
+
{ name = "certifi" },
|
|
530
|
+
{ name = "httpcore" },
|
|
531
|
+
{ name = "idna" },
|
|
532
|
+
]
|
|
533
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
|
|
534
|
+
wheels = [
|
|
535
|
+
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
|
|
536
|
+
]
|
|
537
|
+
|
|
492
538
|
[[package]]
|
|
493
539
|
name = "idna"
|
|
494
540
|
version = "3.11"
|
|
@@ -498,13 +544,39 @@ wheels = [
|
|
|
498
544
|
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
|
|
499
545
|
]
|
|
500
546
|
|
|
547
|
+
[[package]]
|
|
548
|
+
name = "iii-observability"
|
|
549
|
+
version = "0.13.0.dev1"
|
|
550
|
+
source = { editable = "../observability" }
|
|
551
|
+
dependencies = [
|
|
552
|
+
{ name = "httpx" },
|
|
553
|
+
{ name = "opentelemetry-api" },
|
|
554
|
+
{ name = "opentelemetry-sdk" },
|
|
555
|
+
{ name = "websockets" },
|
|
556
|
+
]
|
|
557
|
+
|
|
558
|
+
[package.metadata]
|
|
559
|
+
requires-dist = [
|
|
560
|
+
{ name = "httpx", specifier = ">=0.27" },
|
|
561
|
+
{ name = "mypy", marker = "extra == 'dev'", specifier = ">=1.8" },
|
|
562
|
+
{ name = "opentelemetry-api", specifier = ">=1.25" },
|
|
563
|
+
{ name = "opentelemetry-sdk", specifier = ">=1.25" },
|
|
564
|
+
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
|
|
565
|
+
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23" },
|
|
566
|
+
{ name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=6.0" },
|
|
567
|
+
{ name = "pytest-httpx", marker = "extra == 'dev'", specifier = ">=0.30" },
|
|
568
|
+
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.2" },
|
|
569
|
+
{ name = "websockets", specifier = ">=12.0" },
|
|
570
|
+
]
|
|
571
|
+
provides-extras = ["dev"]
|
|
572
|
+
|
|
501
573
|
[[package]]
|
|
502
574
|
name = "iii-sdk"
|
|
503
|
-
version = "0.
|
|
575
|
+
version = "0.13.0.dev1"
|
|
504
576
|
source = { editable = "." }
|
|
505
577
|
dependencies = [
|
|
578
|
+
{ name = "iii-observability" },
|
|
506
579
|
{ name = "opentelemetry-api" },
|
|
507
|
-
{ name = "opentelemetry-sdk" },
|
|
508
580
|
{ name = "pydantic" },
|
|
509
581
|
{ name = "websockets" },
|
|
510
582
|
]
|
|
@@ -526,9 +598,9 @@ requires-dist = [
|
|
|
526
598
|
{ name = "aiohttp", marker = "extra == 'dev'", specifier = ">=3.9" },
|
|
527
599
|
{ name = "anyio", marker = "extra == 'dev'", specifier = ">=4.0" },
|
|
528
600
|
{ name = "griffe", marker = "extra == 'dev'", specifier = ">=1.0" },
|
|
601
|
+
{ name = "iii-observability", editable = "../observability" },
|
|
529
602
|
{ name = "mypy", marker = "extra == 'dev'", specifier = ">=1.8" },
|
|
530
603
|
{ name = "opentelemetry-api", specifier = ">=1.25" },
|
|
531
|
-
{ name = "opentelemetry-sdk", specifier = ">=1.25" },
|
|
532
604
|
{ name = "pydantic", specifier = ">=2.0" },
|
|
533
605
|
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
|
|
534
606
|
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23" },
|