iii-sdk 0.11.6.dev2__tar.gz → 0.11.6.dev4__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 (59) hide show
  1. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/PKG-INFO +1 -1
  2. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/pyproject.toml +1 -1
  3. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/iii.py +40 -1
  4. iii_sdk-0.11.6.dev4/tests/test_worker_metadata.py +113 -0
  5. iii_sdk-0.11.6.dev2/tests/test_worker_metadata.py +0 -33
  6. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/.gitignore +0 -0
  7. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/README.md +0 -0
  8. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/__init__.py +0 -0
  9. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/channels.py +0 -0
  10. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/errors.py +0 -0
  11. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/format_utils.py +0 -0
  12. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/iii_constants.py +0 -0
  13. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/iii_types.py +0 -0
  14. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/logger.py +0 -0
  15. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/otel_worker_gauges.py +0 -0
  16. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/state.py +0 -0
  17. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/stream.py +0 -0
  18. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/telemetry.py +0 -0
  19. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/telemetry_exporters.py +0 -0
  20. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/telemetry_types.py +0 -0
  21. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/triggers.py +0 -0
  22. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/types.py +0 -0
  23. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/utils.py +0 -0
  24. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/src/iii/worker_metrics.py +0 -0
  25. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/conftest.py +0 -0
  26. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_api_triggers.py +0 -0
  27. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_async_api.py +0 -0
  28. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_bridge.py +0 -0
  29. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_channel_close_delay.py +0 -0
  30. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_context_propagation.py +0 -0
  31. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_data_channels.py +0 -0
  32. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_errors.py +0 -0
  33. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_format_utils.py +0 -0
  34. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_healthcheck.py +0 -0
  35. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_hold_process.py +0 -0
  36. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_http_external_functions_integration.py +0 -0
  37. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_iii_registration_dedup.py +0 -0
  38. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_init_api.py +0 -0
  39. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_invocation_exception.py +0 -0
  40. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_logger_function_ids.py +0 -0
  41. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_logger_otel.py +0 -0
  42. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_middleware.py +0 -0
  43. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_pubsub.py +0 -0
  44. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_queue_integration.py +0 -0
  45. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_rbac_workers.py +0 -0
  46. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_register_function_args.py +0 -0
  47. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_state.py +0 -0
  48. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_stream_models.py +0 -0
  49. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_streams.py +0 -0
  50. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_streams_runtime_annotations.py +0 -0
  51. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_sync_api.py +0 -0
  52. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_telemetry.py +0 -0
  53. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_telemetry_exporters.py +0 -0
  54. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_telemetry_types.py +0 -0
  55. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_trace_helpers.py +0 -0
  56. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_trigger_metadata.py +0 -0
  57. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_utils.py +0 -0
  58. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/tests/test_worker_metrics.py +0 -0
  59. {iii_sdk-0.11.6.dev2 → iii_sdk-0.11.6.dev4}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iii-sdk
3
- Version: 0.11.6.dev2
3
+ Version: 0.11.6.dev4
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "iii-sdk"
7
- version = "0.11.6.dev2"
7
+ version = "0.11.6.dev4"
8
8
  description = "III SDK for Python"
9
9
  authors = [{ name = "III" }]
10
10
  license = { text = "Apache-2.0" }
@@ -77,6 +77,42 @@ def _resolve_format(fmt: Any) -> Any | None:
77
77
  return fmt
78
78
 
79
79
 
80
+ def _detect_project_name(cwd: str | None = None) -> str | None:
81
+ """Return a project identifier for telemetry, derived from the current working directory.
82
+
83
+ Reads ``[project] name`` from ``pyproject.toml`` if present at ``cwd``;
84
+ otherwise falls back to the basename of ``cwd``. Returns ``None`` only
85
+ when both signals are unavailable (e.g. cwd is the filesystem root, or
86
+ the Python runtime has no TOML parser and no readable cwd basename).
87
+
88
+ No directory walking — only inspects ``cwd`` itself, so the SDK never
89
+ reads files outside the user's explicit working directory.
90
+ """
91
+ try:
92
+ cwd = cwd or os.getcwd()
93
+ manifest = os.path.join(cwd, "pyproject.toml")
94
+ if os.path.isfile(manifest):
95
+ import importlib
96
+
97
+ try:
98
+ tomllib = importlib.import_module("tomllib") # Python 3.11+
99
+ except ImportError:
100
+ tomllib = None
101
+ if tomllib is not None:
102
+ with open(manifest, "rb") as fh:
103
+ data = tomllib.load(fh)
104
+ name = data.get("project", {}).get("name")
105
+ if isinstance(name, str) and name.strip():
106
+ return name.strip()
107
+ except Exception:
108
+ pass
109
+
110
+ if not cwd:
111
+ return None
112
+ base = os.path.basename(cwd).strip()
113
+ return base or None
114
+
115
+
80
116
  class _TraceContextError(Exception):
81
117
  """Wraps a handler exception with the response traceparent from the active span."""
82
118
 
@@ -1163,7 +1199,10 @@ class III:
1163
1199
 
1164
1200
  telemetry: dict[str, Any] = {
1165
1201
  "language": language,
1166
- "project_name": telemetry_opts.project_name if telemetry_opts else None,
1202
+ "project_name": (
1203
+ (telemetry_opts.project_name if telemetry_opts else None)
1204
+ or _detect_project_name()
1205
+ ),
1167
1206
  "framework": (telemetry_opts.framework if telemetry_opts else None) or "iii-py",
1168
1207
  "amplitude_api_key": (
1169
1208
  telemetry_opts.amplitude_api_key if telemetry_opts else None
@@ -0,0 +1,113 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ import pytest
9
+
10
+ from iii import InitOptions
11
+ from iii.iii import III, _detect_project_name
12
+ from iii.iii_constants import TelemetryOptions
13
+
14
+ # pyproject.toml parsing relies on stdlib tomllib (Python 3.11+).
15
+ # On 3.10 the SDK intentionally falls back to the cwd basename, so
16
+ # tests that assert the parsed name are skipped on that runtime.
17
+ requires_tomllib = pytest.mark.skipif(
18
+ sys.version_info < (3, 11),
19
+ reason="pyproject.toml parsing requires stdlib tomllib (Python 3.11+)",
20
+ )
21
+
22
+
23
+ def _call_metadata_method(options: InitOptions | None = None) -> dict[str, object]:
24
+ stub = III.__new__(III)
25
+ stub._options = options or InitOptions()
26
+ return stub._get_worker_metadata()
27
+
28
+
29
+ def test_get_worker_metadata_isolation_is_none_when_env_unset(
30
+ monkeypatch: pytest.MonkeyPatch,
31
+ ) -> None:
32
+ monkeypatch.delenv("III_ISOLATION", raising=False)
33
+
34
+ metadata = _call_metadata_method()
35
+
36
+ assert "isolation" in metadata
37
+ assert metadata["isolation"] is None
38
+
39
+
40
+ def test_get_worker_metadata_forwards_iii_isolation_env_var(
41
+ monkeypatch: pytest.MonkeyPatch,
42
+ ) -> None:
43
+ monkeypatch.setenv("III_ISOLATION", "docker")
44
+
45
+ metadata = _call_metadata_method()
46
+
47
+ assert metadata["isolation"] == "docker"
48
+
49
+
50
+ @requires_tomllib
51
+ def test_detect_project_name_reads_pyproject_name(tmp_path: Path) -> None:
52
+ (tmp_path / "pyproject.toml").write_text('[project]\nname = "my-pkg"\n')
53
+
54
+ assert _detect_project_name(str(tmp_path)) == "my-pkg"
55
+
56
+
57
+ def test_detect_project_name_falls_back_to_cwd_basename_when_no_manifest(
58
+ tmp_path: Path,
59
+ ) -> None:
60
+ assert _detect_project_name(str(tmp_path)) == tmp_path.name
61
+
62
+
63
+ def test_detect_project_name_falls_back_to_cwd_basename_when_name_missing(
64
+ tmp_path: Path,
65
+ ) -> None:
66
+ (tmp_path / "pyproject.toml").write_text('[project]\nversion = "1.0.0"\n')
67
+
68
+ assert _detect_project_name(str(tmp_path)) == tmp_path.name
69
+
70
+
71
+ def test_detect_project_name_falls_back_to_cwd_basename_when_pyproject_malformed(
72
+ tmp_path: Path,
73
+ ) -> None:
74
+ (tmp_path / "pyproject.toml").write_text("not valid toml [[[")
75
+
76
+ assert _detect_project_name(str(tmp_path)) == tmp_path.name
77
+
78
+
79
+ @requires_tomllib
80
+ def test_get_worker_metadata_auto_detects_project_name_from_pyproject(
81
+ tmp_path: Path,
82
+ monkeypatch: pytest.MonkeyPatch,
83
+ ) -> None:
84
+ (tmp_path / "pyproject.toml").write_text('[project]\nname = "auto-detected-pkg"\n')
85
+ monkeypatch.chdir(tmp_path)
86
+
87
+ metadata = _call_metadata_method()
88
+
89
+ assert metadata["telemetry"]["project_name"] == "auto-detected-pkg" # type: ignore[index]
90
+
91
+
92
+ def test_get_worker_metadata_user_provided_project_name_wins(
93
+ tmp_path: Path,
94
+ monkeypatch: pytest.MonkeyPatch,
95
+ ) -> None:
96
+ (tmp_path / "pyproject.toml").write_text('[project]\nname = "auto-detected-pkg"\n')
97
+ monkeypatch.chdir(tmp_path)
98
+ options = InitOptions(telemetry=TelemetryOptions(project_name="explicit-override"))
99
+
100
+ metadata = _call_metadata_method(options)
101
+
102
+ assert metadata["telemetry"]["project_name"] == "explicit-override" # type: ignore[index]
103
+
104
+
105
+ def test_get_worker_metadata_falls_back_to_cwd_basename_without_pyproject(
106
+ tmp_path: Path,
107
+ monkeypatch: pytest.MonkeyPatch,
108
+ ) -> None:
109
+ monkeypatch.chdir(tmp_path)
110
+
111
+ metadata = _call_metadata_method()
112
+
113
+ assert metadata["telemetry"]["project_name"] == tmp_path.name # type: ignore[index]
@@ -1,33 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import pytest
4
-
5
- from iii import InitOptions
6
- from iii.iii import III
7
-
8
-
9
- def _call_metadata_method() -> dict[str, object]:
10
- stub = III.__new__(III)
11
- stub._options = InitOptions()
12
- return stub._get_worker_metadata()
13
-
14
-
15
- def test_get_worker_metadata_isolation_is_none_when_env_unset(
16
- monkeypatch: pytest.MonkeyPatch,
17
- ) -> None:
18
- monkeypatch.delenv("III_ISOLATION", raising=False)
19
-
20
- metadata = _call_metadata_method()
21
-
22
- assert "isolation" in metadata
23
- assert metadata["isolation"] is None
24
-
25
-
26
- def test_get_worker_metadata_forwards_iii_isolation_env_var(
27
- monkeypatch: pytest.MonkeyPatch,
28
- ) -> None:
29
- monkeypatch.setenv("III_ISOLATION", "docker")
30
-
31
- metadata = _call_metadata_method()
32
-
33
- assert metadata["isolation"] == "docker"
File without changes
File without changes
File without changes