mseep-agentops 0.4.18__py3-none-any.whl → 0.4.23__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.
Files changed (153) hide show
  1. agentops/__init__.py +0 -0
  2. agentops/client/api/base.py +28 -30
  3. agentops/client/api/versions/v3.py +29 -25
  4. agentops/client/api/versions/v4.py +87 -46
  5. agentops/client/client.py +98 -29
  6. agentops/client/http/README.md +87 -0
  7. agentops/client/http/http_client.py +126 -172
  8. agentops/config.py +8 -2
  9. agentops/instrumentation/OpenTelemetry.md +133 -0
  10. agentops/instrumentation/README.md +167 -0
  11. agentops/instrumentation/__init__.py +13 -1
  12. agentops/instrumentation/agentic/ag2/__init__.py +18 -0
  13. agentops/instrumentation/agentic/ag2/instrumentor.py +922 -0
  14. agentops/instrumentation/agentic/agno/__init__.py +19 -0
  15. agentops/instrumentation/agentic/agno/attributes/__init__.py +20 -0
  16. agentops/instrumentation/agentic/agno/attributes/agent.py +250 -0
  17. agentops/instrumentation/agentic/agno/attributes/metrics.py +214 -0
  18. agentops/instrumentation/agentic/agno/attributes/storage.py +158 -0
  19. agentops/instrumentation/agentic/agno/attributes/team.py +195 -0
  20. agentops/instrumentation/agentic/agno/attributes/tool.py +210 -0
  21. agentops/instrumentation/agentic/agno/attributes/workflow.py +254 -0
  22. agentops/instrumentation/agentic/agno/instrumentor.py +1313 -0
  23. agentops/instrumentation/agentic/crewai/LICENSE +201 -0
  24. agentops/instrumentation/agentic/crewai/NOTICE.md +10 -0
  25. agentops/instrumentation/agentic/crewai/__init__.py +6 -0
  26. agentops/instrumentation/agentic/crewai/crewai_span_attributes.py +335 -0
  27. agentops/instrumentation/agentic/crewai/instrumentation.py +535 -0
  28. agentops/instrumentation/agentic/crewai/version.py +1 -0
  29. agentops/instrumentation/agentic/google_adk/__init__.py +19 -0
  30. agentops/instrumentation/agentic/google_adk/instrumentor.py +68 -0
  31. agentops/instrumentation/agentic/google_adk/patch.py +767 -0
  32. agentops/instrumentation/agentic/haystack/__init__.py +1 -0
  33. agentops/instrumentation/agentic/haystack/instrumentor.py +186 -0
  34. agentops/instrumentation/agentic/langgraph/__init__.py +3 -0
  35. agentops/instrumentation/agentic/langgraph/attributes.py +54 -0
  36. agentops/instrumentation/agentic/langgraph/instrumentation.py +598 -0
  37. agentops/instrumentation/agentic/langgraph/version.py +1 -0
  38. agentops/instrumentation/agentic/openai_agents/README.md +156 -0
  39. agentops/instrumentation/agentic/openai_agents/SPANS.md +145 -0
  40. agentops/instrumentation/agentic/openai_agents/TRACING_API.md +144 -0
  41. agentops/instrumentation/agentic/openai_agents/__init__.py +30 -0
  42. agentops/instrumentation/agentic/openai_agents/attributes/common.py +549 -0
  43. agentops/instrumentation/agentic/openai_agents/attributes/completion.py +172 -0
  44. agentops/instrumentation/agentic/openai_agents/attributes/model.py +58 -0
  45. agentops/instrumentation/agentic/openai_agents/attributes/tokens.py +275 -0
  46. agentops/instrumentation/agentic/openai_agents/exporter.py +469 -0
  47. agentops/instrumentation/agentic/openai_agents/instrumentor.py +107 -0
  48. agentops/instrumentation/agentic/openai_agents/processor.py +58 -0
  49. agentops/instrumentation/agentic/smolagents/README.md +88 -0
  50. agentops/instrumentation/agentic/smolagents/__init__.py +12 -0
  51. agentops/instrumentation/agentic/smolagents/attributes/agent.py +354 -0
  52. agentops/instrumentation/agentic/smolagents/attributes/model.py +205 -0
  53. agentops/instrumentation/agentic/smolagents/instrumentor.py +286 -0
  54. agentops/instrumentation/agentic/smolagents/stream_wrapper.py +258 -0
  55. agentops/instrumentation/agentic/xpander/__init__.py +15 -0
  56. agentops/instrumentation/agentic/xpander/context.py +112 -0
  57. agentops/instrumentation/agentic/xpander/instrumentor.py +877 -0
  58. agentops/instrumentation/agentic/xpander/trace_probe.py +86 -0
  59. agentops/instrumentation/agentic/xpander/version.py +3 -0
  60. agentops/instrumentation/common/README.md +65 -0
  61. agentops/instrumentation/common/attributes.py +1 -2
  62. agentops/instrumentation/providers/anthropic/__init__.py +24 -0
  63. agentops/instrumentation/providers/anthropic/attributes/__init__.py +23 -0
  64. agentops/instrumentation/providers/anthropic/attributes/common.py +64 -0
  65. agentops/instrumentation/providers/anthropic/attributes/message.py +541 -0
  66. agentops/instrumentation/providers/anthropic/attributes/tools.py +231 -0
  67. agentops/instrumentation/providers/anthropic/event_handler_wrapper.py +90 -0
  68. agentops/instrumentation/providers/anthropic/instrumentor.py +146 -0
  69. agentops/instrumentation/providers/anthropic/stream_wrapper.py +436 -0
  70. agentops/instrumentation/providers/google_genai/README.md +33 -0
  71. agentops/instrumentation/providers/google_genai/__init__.py +24 -0
  72. agentops/instrumentation/providers/google_genai/attributes/__init__.py +25 -0
  73. agentops/instrumentation/providers/google_genai/attributes/chat.py +125 -0
  74. agentops/instrumentation/providers/google_genai/attributes/common.py +88 -0
  75. agentops/instrumentation/providers/google_genai/attributes/model.py +284 -0
  76. agentops/instrumentation/providers/google_genai/instrumentor.py +170 -0
  77. agentops/instrumentation/providers/google_genai/stream_wrapper.py +238 -0
  78. agentops/instrumentation/providers/ibm_watsonx_ai/__init__.py +28 -0
  79. agentops/instrumentation/providers/ibm_watsonx_ai/attributes/__init__.py +27 -0
  80. agentops/instrumentation/providers/ibm_watsonx_ai/attributes/attributes.py +277 -0
  81. agentops/instrumentation/providers/ibm_watsonx_ai/attributes/common.py +104 -0
  82. agentops/instrumentation/providers/ibm_watsonx_ai/instrumentor.py +162 -0
  83. agentops/instrumentation/providers/ibm_watsonx_ai/stream_wrapper.py +302 -0
  84. agentops/instrumentation/providers/mem0/__init__.py +45 -0
  85. agentops/instrumentation/providers/mem0/common.py +377 -0
  86. agentops/instrumentation/providers/mem0/instrumentor.py +270 -0
  87. agentops/instrumentation/providers/mem0/memory.py +430 -0
  88. agentops/instrumentation/providers/openai/__init__.py +21 -0
  89. agentops/instrumentation/providers/openai/attributes/__init__.py +7 -0
  90. agentops/instrumentation/providers/openai/attributes/common.py +55 -0
  91. agentops/instrumentation/providers/openai/attributes/response.py +607 -0
  92. agentops/instrumentation/providers/openai/config.py +36 -0
  93. agentops/instrumentation/providers/openai/instrumentor.py +312 -0
  94. agentops/instrumentation/providers/openai/stream_wrapper.py +941 -0
  95. agentops/instrumentation/providers/openai/utils.py +44 -0
  96. agentops/instrumentation/providers/openai/v0.py +176 -0
  97. agentops/instrumentation/providers/openai/v0_wrappers.py +483 -0
  98. agentops/instrumentation/providers/openai/wrappers/__init__.py +30 -0
  99. agentops/instrumentation/providers/openai/wrappers/assistant.py +277 -0
  100. agentops/instrumentation/providers/openai/wrappers/chat.py +259 -0
  101. agentops/instrumentation/providers/openai/wrappers/completion.py +109 -0
  102. agentops/instrumentation/providers/openai/wrappers/embeddings.py +94 -0
  103. agentops/instrumentation/providers/openai/wrappers/image_gen.py +75 -0
  104. agentops/instrumentation/providers/openai/wrappers/responses.py +191 -0
  105. agentops/instrumentation/providers/openai/wrappers/shared.py +81 -0
  106. agentops/instrumentation/utilities/concurrent_futures/__init__.py +10 -0
  107. agentops/instrumentation/utilities/concurrent_futures/instrumentation.py +206 -0
  108. agentops/integration/callbacks/dspy/__init__.py +11 -0
  109. agentops/integration/callbacks/dspy/callback.py +471 -0
  110. agentops/integration/callbacks/langchain/README.md +59 -0
  111. agentops/integration/callbacks/langchain/__init__.py +15 -0
  112. agentops/integration/callbacks/langchain/callback.py +791 -0
  113. agentops/integration/callbacks/langchain/utils.py +54 -0
  114. agentops/legacy/crewai.md +121 -0
  115. agentops/logging/instrument_logging.py +4 -0
  116. agentops/sdk/README.md +220 -0
  117. agentops/sdk/core.py +75 -32
  118. agentops/sdk/descriptors/classproperty.py +28 -0
  119. agentops/sdk/exporters.py +152 -33
  120. agentops/semconv/README.md +125 -0
  121. agentops/semconv/span_kinds.py +0 -2
  122. agentops/validation.py +102 -63
  123. {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/METADATA +30 -40
  124. mseep_agentops-0.4.23.dist-info/RECORD +178 -0
  125. {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/WHEEL +1 -2
  126. mseep_agentops-0.4.18.dist-info/RECORD +0 -94
  127. mseep_agentops-0.4.18.dist-info/top_level.txt +0 -2
  128. tests/conftest.py +0 -10
  129. tests/unit/client/__init__.py +0 -1
  130. tests/unit/client/test_http_adapter.py +0 -221
  131. tests/unit/client/test_http_client.py +0 -206
  132. tests/unit/conftest.py +0 -54
  133. tests/unit/sdk/__init__.py +0 -1
  134. tests/unit/sdk/instrumentation_tester.py +0 -207
  135. tests/unit/sdk/test_attributes.py +0 -392
  136. tests/unit/sdk/test_concurrent_instrumentation.py +0 -468
  137. tests/unit/sdk/test_decorators.py +0 -763
  138. tests/unit/sdk/test_exporters.py +0 -241
  139. tests/unit/sdk/test_factory.py +0 -1188
  140. tests/unit/sdk/test_internal_span_processor.py +0 -397
  141. tests/unit/sdk/test_resource_attributes.py +0 -35
  142. tests/unit/test_config.py +0 -82
  143. tests/unit/test_context_manager.py +0 -777
  144. tests/unit/test_events.py +0 -27
  145. tests/unit/test_host_env.py +0 -54
  146. tests/unit/test_init_py.py +0 -501
  147. tests/unit/test_serialization.py +0 -433
  148. tests/unit/test_session.py +0 -676
  149. tests/unit/test_user_agent.py +0 -34
  150. tests/unit/test_validation.py +0 -405
  151. {tests → agentops/instrumentation/agentic/openai_agents/attributes}/__init__.py +0 -0
  152. /tests/unit/__init__.py → /agentops/instrumentation/providers/openai/attributes/tools.py +0 -0
  153. {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/licenses/LICENSE +0 -0
@@ -1,206 +0,0 @@
1
- # """Tests for the HttpClient class."""
2
- #
3
- # import pytest
4
- # import requests
5
- # from unittest import mock
6
- # from pytest_mock import MockerFixture
7
- #
8
- # from agentops.client.http.http_client import HttpClient
9
- # from agentops.client.http.http_adapter import AuthenticatedHttpAdapter, BaseHTTPAdapter
10
- # from agentops.client.auth_manager import AuthManager
11
- #
12
- #
13
- # class TestHttpClient:
14
- # """Tests for the HttpClient class."""
15
- #
16
- # def test_get_session_creates_new_session_if_none_exists(self):
17
- # """Test that get_session creates a new session if none exists."""
18
- # # Reset the session to ensure we're testing from a clean state
19
- # HttpClient._session = None
20
- #
21
- # # Call get_session
22
- # session = HttpClient.get_session()
23
- #
24
- # # Verify a session was created
25
- # assert session is not None
26
- # assert isinstance(session, requests.Session)
27
- #
28
- # # Verify the session has the expected adapters
29
- # assert any(isinstance(adapter, BaseHTTPAdapter) for adapter in session.adapters.values())
30
- #
31
- # # Verify the session has the expected headers
32
- # assert "Content-Type" in session.headers
33
- # assert "Connection" in session.headers
34
- # assert "Keep-Alive" in session.headers
35
- #
36
- # def test_get_session_returns_existing_session(self):
37
- # """Test that get_session returns the existing session if one exists."""
38
- # # Create a session
39
- # HttpClient._session = None
40
- # session1 = HttpClient.get_session()
41
- #
42
- # # Call get_session again
43
- # session2 = HttpClient.get_session()
44
- #
45
- # # Verify the same session was returned
46
- # assert session2 is session1
47
- #
48
- # def test_get_authenticated_session_creates_new_session(self):
49
- # """Test that get_authenticated_session creates a new authenticated session."""
50
- # # Call get_authenticated_session
51
- # session = HttpClient.get_authenticated_session(
52
- # endpoint="https://api.example.com",
53
- # api_key="test-api-key"
54
- # )
55
- #
56
- # # Verify a session was created
57
- # assert session is not None
58
- # assert isinstance(session, requests.Session)
59
- #
60
- # # Verify the session has the expected adapters
61
- # assert any(isinstance(adapter, AuthenticatedHttpAdapter) for adapter in session.adapters.values())
62
- #
63
- # # Verify the session has the expected headers
64
- # assert "Content-Type" in session.headers
65
- # assert "Connection" in session.headers
66
- # assert "Keep-Alive" in session.headers
67
- #
68
- # def test_get_authenticated_session_with_custom_token_fetcher(self, mocker: MockerFixture):
69
- # """Test that get_authenticated_session accepts a custom token fetcher."""
70
- # # Create a mock token fetcher
71
- # mock_token_fetcher = mock.Mock(return_value="test-token")
72
- #
73
- # # Call get_authenticated_session with the custom token fetcher
74
- # session = HttpClient.get_authenticated_session(
75
- # endpoint="https://api.example.com",
76
- # api_key="test-api-key",
77
- # token_fetcher=mock_token_fetcher
78
- # )
79
- #
80
- # # Verify a session was created
81
- # assert session is not None
82
- # assert isinstance(session, requests.Session)
83
- #
84
- # # Get the adapter
85
- # adapter = next(adapter for adapter in session.adapters.values()
86
- # if isinstance(adapter, AuthenticatedHttpAdapter))
87
- #
88
- # # Verify the adapter has the custom token fetcher
89
- # assert adapter.token_fetcher is mock_token_fetcher
90
- #
91
- # def test_request_get(self, mocker: MockerFixture):
92
- # """Test that request makes a GET request."""
93
- # # Mock the session
94
- # mock_session = mock.Mock()
95
- # mock_get = mock.Mock()
96
- # mock_session.get = mock_get
97
- #
98
- # # Mock get_session to return our mock session
99
- # mocker.patch.object(HttpClient, "get_session", return_value=mock_session)
100
- #
101
- # # Call request
102
- # HttpClient.request(
103
- # method="get",
104
- # url="https://api.example.com/test",
105
- # headers={"X-Test": "test"},
106
- # timeout=10
107
- # )
108
- #
109
- # # Verify the session method was called with the expected arguments
110
- # mock_get.assert_called_once_with(
111
- # "https://api.example.com/test",
112
- # headers={"X-Test": "test"},
113
- # timeout=10,
114
- # allow_redirects=False
115
- # )
116
- #
117
- # def test_request_post(self, mocker: MockerFixture):
118
- # """Test that request makes a POST request."""
119
- # # Mock the session
120
- # mock_session = mock.Mock()
121
- # mock_post = mock.Mock()
122
- # mock_session.post = mock_post
123
- #
124
- # # Mock get_session to return our mock session
125
- # mocker.patch.object(HttpClient, "get_session", return_value=mock_session)
126
- #
127
- # # Call request
128
- # HttpClient.request(
129
- # method="post",
130
- # url="https://api.example.com/test",
131
- # data={"test": "data"},
132
- # headers={"X-Test": "test"},
133
- # timeout=10
134
- # )
135
- #
136
- # # Verify the session method was called with the expected arguments
137
- # mock_post.assert_called_once_with(
138
- # "https://api.example.com/test",
139
- # json={"test": "data"},
140
- # headers={"X-Test": "test"},
141
- # timeout=10,
142
- # allow_redirects=False
143
- # )
144
- #
145
- # def test_request_put(self, mocker: MockerFixture):
146
- # """Test that request makes a PUT request."""
147
- # # Mock the session
148
- # mock_session = mock.Mock()
149
- # mock_put = mock.Mock()
150
- # mock_session.put = mock_put
151
- #
152
- # # Mock get_session to return our mock session
153
- # mocker.patch.object(HttpClient, "get_session", return_value=mock_session)
154
- #
155
- # # Call request
156
- # HttpClient.request(
157
- # method="put",
158
- # url="https://api.example.com/test",
159
- # data={"test": "data"},
160
- # headers={"X-Test": "test"},
161
- # timeout=10
162
- # )
163
- #
164
- # # Verify the session method was called with the expected arguments
165
- # mock_put.assert_called_once_with(
166
- # "https://api.example.com/test",
167
- # json={"test": "data"},
168
- # headers={"X-Test": "test"},
169
- # timeout=10,
170
- # allow_redirects=False
171
- # )
172
- #
173
- # def test_request_delete(self, mocker: MockerFixture):
174
- # """Test that request makes a DELETE request."""
175
- # # Mock the session
176
- # mock_session = mock.Mock()
177
- # mock_delete = mock.Mock()
178
- # mock_session.delete = mock_delete
179
- #
180
- # # Mock get_session to return our mock session
181
- # mocker.patch.object(HttpClient, "get_session", return_value=mock_session)
182
- #
183
- # # Call request
184
- # HttpClient.request(
185
- # method="delete",
186
- # url="https://api.example.com/test",
187
- # headers={"X-Test": "test"},
188
- # timeout=10
189
- # )
190
- #
191
- # # Verify the session method was called with the expected arguments
192
- # mock_delete.assert_called_once_with(
193
- # "https://api.example.com/test",
194
- # headers={"X-Test": "test"},
195
- # timeout=10,
196
- # allow_redirects=False
197
- # )
198
- #
199
- # def test_request_unsupported_method(self):
200
- # """Test that request raises an error for unsupported methods."""
201
- # # Call request with an unsupported method
202
- # with pytest.raises(ValueError, match="Unsupported HTTP method: patch"):
203
- # HttpClient.request(
204
- # method="patch",
205
- # url="https://api.example.com/test"
206
- # )
tests/unit/conftest.py DELETED
@@ -1,54 +0,0 @@
1
- import uuid
2
-
3
- import pytest
4
- import requests_mock
5
-
6
- from agentops.config import Config
7
- from tests.fixtures.client import * # noqa
8
- from tests.unit.sdk.instrumentation_tester import InstrumentationTester
9
-
10
-
11
- @pytest.fixture
12
- def api_key() -> str:
13
- """Standard API key for testing"""
14
- return "test-api-key"
15
-
16
-
17
- @pytest.fixture
18
- def endpoint() -> str:
19
- """Base API URL"""
20
- return Config().endpoint
21
-
22
-
23
- @pytest.fixture(autouse=True)
24
- def mock_req(endpoint, api_key):
25
- """
26
- Mocks AgentOps backend API requests.
27
- """
28
- with requests_mock.Mocker(real_http=False) as m:
29
- # Map session IDs to their JWTs
30
- m.post(
31
- endpoint + "/v3/auth/token",
32
- json={"token": str(uuid.uuid4()), "project_id": "test-project-id", "api_key": api_key},
33
- )
34
- yield m
35
-
36
-
37
- @pytest.fixture
38
- def noinstrument():
39
- # Tells the client to not instrument LLM calls
40
- yield
41
-
42
-
43
- @pytest.fixture
44
- def mock_config(mocker):
45
- """Mock the Client.configure method"""
46
- return mocker.patch("agentops.client.Client.configure")
47
-
48
-
49
- @pytest.fixture
50
- def instrumentation():
51
- """Fixture for the instrumentation tester."""
52
- tester = InstrumentationTester()
53
- yield tester
54
- tester.reset()
@@ -1 +0,0 @@
1
- # Test package for the SDK
@@ -1,207 +0,0 @@
1
- from typing import Any, Dict, List, Protocol, Tuple, Union
2
- import importlib
3
- import unittest.mock as mock
4
-
5
- from opentelemetry import trace as trace_api
6
- from opentelemetry.sdk.trace import ReadableSpan, Span, TracerProvider
7
- from opentelemetry.sdk.trace.export import SimpleSpanProcessor
8
- from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
9
- from opentelemetry.util.types import Attributes
10
-
11
- from agentops.sdk.core import tracer
12
-
13
-
14
- def create_tracer_provider(
15
- **kwargs,
16
- ) -> Tuple[TracerProvider, InMemorySpanExporter, SimpleSpanProcessor]:
17
- """Helper to create a configured tracer provider.
18
-
19
- Creates and configures a `TracerProvider` with a
20
- `SimpleSpanProcessor` and a `InMemorySpanExporter`.
21
- All the parameters passed are forwarded to the TracerProvider
22
- constructor.
23
-
24
- Returns:
25
- A tuple with the tracer provider, memory exporter, and span processor.
26
- """
27
- tracer_provider = TracerProvider(**kwargs)
28
- memory_exporter = InMemorySpanExporter()
29
-
30
- # Use SimpleSpanProcessor instead of both processors to avoid duplication
31
- span_processor = SimpleSpanProcessor(memory_exporter)
32
- tracer_provider.add_span_processor(span_processor)
33
-
34
- return tracer_provider, memory_exporter, span_processor
35
-
36
-
37
- def reset_trace_globals():
38
- """Reset the global trace state to avoid conflicts."""
39
- # Reset tracer provider
40
- trace_api._TRACER_PROVIDER = None
41
-
42
- # Reload the trace module to clear warning state
43
- importlib.reload(trace_api)
44
-
45
-
46
- class HasAttributesViaProperty(Protocol):
47
- @property
48
- def attributes(self) -> Attributes:
49
- ...
50
-
51
-
52
- class HasAttributesViaAttr(Protocol):
53
- attributes: Attributes
54
-
55
-
56
- HasAttributes = Union[HasAttributesViaProperty, HasAttributesViaAttr]
57
-
58
-
59
- class InstrumentationTester:
60
- """
61
- A utility class for testing instrumentation in the AgentOps SDK.
62
-
63
- This class provides methods for setting up a test environment with
64
- in-memory span exporters, and for asserting properties of spans.
65
- """
66
-
67
- tracer_provider: TracerProvider
68
- memory_exporter: InMemorySpanExporter
69
- span_processor: SimpleSpanProcessor
70
-
71
- def __init__(self):
72
- """Initialize the instrumentation tester."""
73
- # Reset any global state first
74
- reset_trace_globals()
75
-
76
- # Shut down any existing tracing core
77
- # self._shutdown_core()
78
-
79
- # Create a new tracer provider and memory exporter
80
- (
81
- self.tracer_provider,
82
- self.memory_exporter,
83
- self.span_processor,
84
- ) = create_tracer_provider()
85
-
86
- # Set the tracer provider
87
- trace_api.set_tracer_provider(self.tracer_provider)
88
-
89
- # Create a mock for the meter provider
90
- self.mock_meter_provider = mock.MagicMock()
91
-
92
- # Patch the setup_telemetry function to return our test providers
93
- self.setup_telemetry_patcher = mock.patch(
94
- "agentops.sdk.core.setup_telemetry", return_value=(self.tracer_provider, self.mock_meter_provider)
95
- )
96
- self.mock_setup_telemetry = self.setup_telemetry_patcher.start()
97
-
98
- # Reset the tracing core to force reinitialization
99
- core = tracer
100
- core._initialized = False
101
- core._provider = None
102
-
103
- # Initialize the core, which will now use our mocked setup_telemetry
104
- core.initialize()
105
-
106
- self.clear_spans()
107
-
108
- def _shutdown_core(self):
109
- """Safely shut down the tracing core."""
110
- try:
111
- tracer.shutdown()
112
- except Exception as e:
113
- print(f"Warning: Error shutting down tracing core: {e}")
114
-
115
- def clear_spans(self):
116
- """Clear all spans from the memory exporter."""
117
- # Force flush spans
118
- self.span_processor.force_flush()
119
-
120
- # Then clear the memory
121
- self.memory_exporter.clear()
122
- print("Cleared all spans from memory exporter")
123
-
124
- def reset(self):
125
- """Reset the instrumentation tester."""
126
- # Force flush any pending spans
127
- self.span_processor.force_flush()
128
-
129
- # Clear any existing spans
130
- self.clear_spans()
131
-
132
- # Reset global trace state
133
- reset_trace_globals()
134
-
135
- # Set our tracer provider again
136
- trace_api.set_tracer_provider(self.tracer_provider)
137
-
138
- # Shut down and re-initialize the tracing core
139
- self._shutdown_core()
140
-
141
- # Reset the mock setup_telemetry function
142
- self.mock_setup_telemetry.reset_mock()
143
-
144
- # Reset the tracing core to force reinitialization
145
- core = tracer
146
- core._initialized = False
147
- core._provider = None
148
-
149
- # Initialize the core, which will now use our mocked setup_telemetry
150
- core.initialize()
151
-
152
- def __del__(self):
153
- """Clean up resources when the tester is garbage collected."""
154
- try:
155
- # Stop the patcher when the tester is deleted
156
- self.setup_telemetry_patcher.stop()
157
- except Exception:
158
- pass
159
-
160
- def get_finished_spans(self) -> List[ReadableSpan]:
161
- """Get all finished spans."""
162
- # Force flush any pending spans
163
- self.span_processor.force_flush()
164
-
165
- # Get the spans
166
- spans = list(self.memory_exporter.get_finished_spans())
167
- print(f"Instrumentation Tester: Found {len(spans)} finished spans")
168
- for i, span in enumerate(spans):
169
- print(f"Span {i}: name={span.name}, attributes={span.attributes}")
170
- return spans
171
-
172
- def get_spans_by_name(self, name: str) -> List[ReadableSpan]:
173
- """Get all spans with the given name."""
174
- return [span for span in self.get_finished_spans() if span.name == name]
175
-
176
- def get_spans_by_kind(self, kind: str) -> List[ReadableSpan]:
177
- """Get all spans with the given kind."""
178
- return [
179
- span for span in self.get_finished_spans() if span.attributes and span.attributes.get("span.kind") == kind
180
- ]
181
-
182
- @staticmethod
183
- def assert_has_attributes(obj: HasAttributes, attributes: Dict[str, Any]):
184
- """Assert that an object has the given attributes."""
185
- import json
186
-
187
- assert obj.attributes is not None
188
- for key, val in attributes.items():
189
- assert key in obj.attributes, f"Key {key!r} not found in attributes"
190
-
191
- actual_val = obj.attributes[key]
192
-
193
- # Try to handle JSON-serialized values
194
- if isinstance(actual_val, str) and isinstance(val, (list, dict)):
195
- try:
196
- actual_val = json.loads(actual_val)
197
- except json.JSONDecodeError:
198
- pass
199
-
200
- assert actual_val == val, f"Value for key {key!r} does not match: {actual_val} != {val}"
201
-
202
- @staticmethod
203
- def assert_span_instrumented_for(span: Union[Span, ReadableSpan], module):
204
- """Assert that a span is instrumented for the given module."""
205
- assert span.instrumentation_scope is not None
206
- assert span.instrumentation_scope.name == module.__name__
207
- assert span.instrumentation_scope.version == module.__version__