iii-sdk 0.19.0.dev4__tar.gz → 0.19.2__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 (61) hide show
  1. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/PKG-INFO +2 -2
  2. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/pyproject.toml +2 -2
  3. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/iii.py +6 -1
  4. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/iii_constants.py +3 -0
  5. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_api_triggers.py +58 -0
  6. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_worker_metadata.py +12 -0
  7. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/.gitignore +0 -0
  8. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/README.md +0 -0
  9. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/__init__.py +0 -0
  10. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/channels.py +0 -0
  11. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/errors.py +0 -0
  12. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/format_utils.py +0 -0
  13. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/helpers.py +0 -0
  14. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/iii_types.py +0 -0
  15. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/otel_worker_gauges.py +0 -0
  16. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/state.py +0 -0
  17. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/stream.py +0 -0
  18. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/triggers.py +0 -0
  19. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/types.py +0 -0
  20. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/utils.py +0 -0
  21. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/src/iii/worker_metrics.py +0 -0
  22. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/conftest.py +0 -0
  23. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_async_api.py +0 -0
  24. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_baggage_span_processor.py +0 -0
  25. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_bridge.py +0 -0
  26. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_channel_close_delay.py +0 -0
  27. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_context_propagation.py +0 -0
  28. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_data_channels.py +0 -0
  29. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_errors.py +0 -0
  30. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_format_utils.py +0 -0
  31. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_healthcheck.py +0 -0
  32. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_helpers.py +0 -0
  33. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_hold_process.py +0 -0
  34. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_http_external_functions_integration.py +0 -0
  35. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_iii_registration_dedup.py +0 -0
  36. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_init_api.py +0 -0
  37. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_invocation_exception.py +0 -0
  38. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_logger_function_ids.py +0 -0
  39. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_logger_otel.py +0 -0
  40. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_middleware.py +0 -0
  41. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_payload.py +0 -0
  42. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_pubsub.py +0 -0
  43. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_queue_integration.py +0 -0
  44. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_rbac_workers.py +0 -0
  45. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_register_function_args.py +0 -0
  46. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_span_ops.py +0 -0
  47. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_state.py +0 -0
  48. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_stream_models.py +0 -0
  49. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_streams.py +0 -0
  50. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_streams_runtime_annotations.py +0 -0
  51. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_sync_api.py +0 -0
  52. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_telemetry.py +0 -0
  53. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_telemetry_exporters.py +0 -0
  54. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_telemetry_types.py +0 -0
  55. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_trace_helpers.py +0 -0
  56. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_trigger_metadata.py +0 -0
  57. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_trigger_registration_error.py +0 -0
  58. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_trigger_type_lifecycle.py +0 -0
  59. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_utils.py +0 -0
  60. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/tests/test_worker_metrics.py +0 -0
  61. {iii_sdk-0.19.0.dev4 → iii_sdk-0.19.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iii-sdk
3
- Version: 0.19.0.dev4
3
+ Version: 0.19.2
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,7 +14,7 @@ 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.19.0.dev4
17
+ Requires-Dist: iii-observability==0.19.2
18
18
  Requires-Dist: opentelemetry-api>=1.25
19
19
  Requires-Dist: pydantic>=2.0
20
20
  Requires-Dist: websockets>=12.0
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "iii-sdk"
7
- version = "0.19.0.dev4"
7
+ version = "0.19.2"
8
8
  description = "III SDK for Python"
9
9
  authors = [{ name = "III" }]
10
10
  license = { text = "Apache-2.0" }
@@ -23,7 +23,7 @@ dependencies = [
23
23
  "websockets>=12.0",
24
24
  "pydantic>=2.0",
25
25
  "opentelemetry-api>=1.25",
26
- "iii-observability==0.19.0.dev4",
26
+ "iii-observability==0.19.2",
27
27
  ]
28
28
 
29
29
  [project.urls]
@@ -1235,7 +1235,7 @@ class III:
1235
1235
  ),
1236
1236
  }
1237
1237
 
1238
- return {
1238
+ metadata: dict[str, Any] = {
1239
1239
  "runtime": "python",
1240
1240
  "version": sdk_version,
1241
1241
  "name": worker_name,
@@ -1244,6 +1244,11 @@ class III:
1244
1244
  "isolation": os.environ.get("III_ISOLATION") or None,
1245
1245
  "telemetry": telemetry,
1246
1246
  }
1247
+ # Optional, like the other SDKs: only send `description` when set so the
1248
+ # engine's `#[serde(default)]` field stays absent otherwise.
1249
+ if self._options.worker_description:
1250
+ metadata["description"] = self._options.worker_description
1251
+ return metadata
1247
1252
 
1248
1253
  def _register_worker_metadata(self) -> None:
1249
1254
  msg = InvokeFunctionMessage(
@@ -47,6 +47,8 @@ class InitOptions:
47
47
 
48
48
  Attributes:
49
49
  worker_name: Display name for this worker. Defaults to ``hostname:pid``.
50
+ worker_description: One-line, human/LLM-readable summary of what this
51
+ worker does. Surfaces in ``engine::workers::list`` / ``engine::workers::info``.
50
52
  enable_metrics_reporting: Enable worker metrics via OpenTelemetry. Default ``True``.
51
53
  invocation_timeout_ms: Default timeout for ``trigger()`` in milliseconds. Default ``30000``.
52
54
  reconnection_config: WebSocket reconnection behavior.
@@ -56,6 +58,7 @@ class InitOptions:
56
58
  """
57
59
 
58
60
  worker_name: str | None = None
61
+ worker_description: str | None = None
59
62
  enable_metrics_reporting: bool = True
60
63
  invocation_timeout_ms: int = DEFAULT_INVOCATION_TIMEOUT_MS
61
64
  reconnection_config: ReconnectionConfig | None = None
@@ -136,6 +136,64 @@ async def test_raw_json_request_body(engine_http_url, iii_client: III):
136
136
  trigger.unregister()
137
137
 
138
138
 
139
+ @pytest.mark.asyncio
140
+ async def test_conflicting_route_structure_is_rejected(engine_http_url, iii_client: III, caplog):
141
+ """Two routes with identical structure but swapped path-param names must not
142
+ crash the engine: the first keeps serving and the second is rejected with a
143
+ logged registration error."""
144
+ caplog.set_level("ERROR", logger="iii")
145
+
146
+ def handler(input_data):
147
+ return {"status_code": 200, "body": {"ok": True}}
148
+
149
+ # First route registers normally.
150
+ fn_a = iii_client.register_function("test.api.conflict.a.py", handler)
151
+ trig_a = iii_client.register_trigger(
152
+ {
153
+ "type": "http",
154
+ "function_id": "test.api.conflict.a.py",
155
+ "config": {
156
+ "api_path": "test/py/conflict/:listId/:userId",
157
+ "http_method": "GET",
158
+ },
159
+ }
160
+ )
161
+
162
+ # Second route has the same axum shape with swapped param names -> conflict.
163
+ fn_b = iii_client.register_function("test.api.conflict.b.py", handler)
164
+ trig_b = iii_client.register_trigger(
165
+ {
166
+ "type": "http",
167
+ "function_id": "test.api.conflict.b.py",
168
+ "config": {
169
+ "api_path": "test/py/conflict/:userId/:listId",
170
+ "http_method": "GET",
171
+ },
172
+ }
173
+ )
174
+
175
+ # Give the engine time to process both registrations and reply.
176
+ time.sleep(0.5)
177
+
178
+ # Engine stayed alive and the first route still serves — no panic.
179
+ async with aiohttp.ClientSession() as session:
180
+ async with session.get(f"{engine_http_url}/test/py/conflict/list1/user1") as resp:
181
+ assert resp.status == 200
182
+ data = await resp.json()
183
+ assert data["ok"] is True
184
+
185
+ # The conflicting registration was surfaced as an error. The engine rejects
186
+ # whichever route it processes second (wire order is not guaranteed), so assert on
187
+ # the conflict message rather than a specific route or the random trigger id.
188
+ messages = [record.getMessage() for record in caplog.records]
189
+ assert any("conflicts with already-registered route" in m for m in messages), messages
190
+
191
+ fn_a.unregister()
192
+ trig_a.unregister()
193
+ fn_b.unregister()
194
+ trig_b.unregister()
195
+
196
+
139
197
  @pytest.mark.asyncio
140
198
  async def test_path_parameters(engine_http_url, iii_client: III):
141
199
  """Verify path parameters are extracted correctly."""
@@ -47,6 +47,18 @@ def test_get_worker_metadata_forwards_iii_isolation_env_var(
47
47
  assert metadata["isolation"] == "docker"
48
48
 
49
49
 
50
+ def test_get_worker_metadata_omits_description_when_unset() -> None:
51
+ metadata = _call_metadata_method()
52
+
53
+ assert "description" not in metadata
54
+
55
+
56
+ def test_get_worker_metadata_includes_worker_description_when_set() -> None:
57
+ metadata = _call_metadata_method(InitOptions(worker_description="resizes images"))
58
+
59
+ assert metadata["description"] == "resizes images"
60
+
61
+
50
62
  @requires_tomllib
51
63
  def test_detect_project_name_reads_pyproject_name(tmp_path: Path) -> None:
52
64
  (tmp_path / "pyproject.toml").write_text('[project]\nname = "my-pkg"\n')
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes