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.
- agentops/__init__.py +0 -0
- agentops/client/api/base.py +28 -30
- agentops/client/api/versions/v3.py +29 -25
- agentops/client/api/versions/v4.py +87 -46
- agentops/client/client.py +98 -29
- agentops/client/http/README.md +87 -0
- agentops/client/http/http_client.py +126 -172
- agentops/config.py +8 -2
- agentops/instrumentation/OpenTelemetry.md +133 -0
- agentops/instrumentation/README.md +167 -0
- agentops/instrumentation/__init__.py +13 -1
- agentops/instrumentation/agentic/ag2/__init__.py +18 -0
- agentops/instrumentation/agentic/ag2/instrumentor.py +922 -0
- agentops/instrumentation/agentic/agno/__init__.py +19 -0
- agentops/instrumentation/agentic/agno/attributes/__init__.py +20 -0
- agentops/instrumentation/agentic/agno/attributes/agent.py +250 -0
- agentops/instrumentation/agentic/agno/attributes/metrics.py +214 -0
- agentops/instrumentation/agentic/agno/attributes/storage.py +158 -0
- agentops/instrumentation/agentic/agno/attributes/team.py +195 -0
- agentops/instrumentation/agentic/agno/attributes/tool.py +210 -0
- agentops/instrumentation/agentic/agno/attributes/workflow.py +254 -0
- agentops/instrumentation/agentic/agno/instrumentor.py +1313 -0
- agentops/instrumentation/agentic/crewai/LICENSE +201 -0
- agentops/instrumentation/agentic/crewai/NOTICE.md +10 -0
- agentops/instrumentation/agentic/crewai/__init__.py +6 -0
- agentops/instrumentation/agentic/crewai/crewai_span_attributes.py +335 -0
- agentops/instrumentation/agentic/crewai/instrumentation.py +535 -0
- agentops/instrumentation/agentic/crewai/version.py +1 -0
- agentops/instrumentation/agentic/google_adk/__init__.py +19 -0
- agentops/instrumentation/agentic/google_adk/instrumentor.py +68 -0
- agentops/instrumentation/agentic/google_adk/patch.py +767 -0
- agentops/instrumentation/agentic/haystack/__init__.py +1 -0
- agentops/instrumentation/agentic/haystack/instrumentor.py +186 -0
- agentops/instrumentation/agentic/langgraph/__init__.py +3 -0
- agentops/instrumentation/agentic/langgraph/attributes.py +54 -0
- agentops/instrumentation/agentic/langgraph/instrumentation.py +598 -0
- agentops/instrumentation/agentic/langgraph/version.py +1 -0
- agentops/instrumentation/agentic/openai_agents/README.md +156 -0
- agentops/instrumentation/agentic/openai_agents/SPANS.md +145 -0
- agentops/instrumentation/agentic/openai_agents/TRACING_API.md +144 -0
- agentops/instrumentation/agentic/openai_agents/__init__.py +30 -0
- agentops/instrumentation/agentic/openai_agents/attributes/common.py +549 -0
- agentops/instrumentation/agentic/openai_agents/attributes/completion.py +172 -0
- agentops/instrumentation/agentic/openai_agents/attributes/model.py +58 -0
- agentops/instrumentation/agentic/openai_agents/attributes/tokens.py +275 -0
- agentops/instrumentation/agentic/openai_agents/exporter.py +469 -0
- agentops/instrumentation/agentic/openai_agents/instrumentor.py +107 -0
- agentops/instrumentation/agentic/openai_agents/processor.py +58 -0
- agentops/instrumentation/agentic/smolagents/README.md +88 -0
- agentops/instrumentation/agentic/smolagents/__init__.py +12 -0
- agentops/instrumentation/agentic/smolagents/attributes/agent.py +354 -0
- agentops/instrumentation/agentic/smolagents/attributes/model.py +205 -0
- agentops/instrumentation/agentic/smolagents/instrumentor.py +286 -0
- agentops/instrumentation/agentic/smolagents/stream_wrapper.py +258 -0
- agentops/instrumentation/agentic/xpander/__init__.py +15 -0
- agentops/instrumentation/agentic/xpander/context.py +112 -0
- agentops/instrumentation/agentic/xpander/instrumentor.py +877 -0
- agentops/instrumentation/agentic/xpander/trace_probe.py +86 -0
- agentops/instrumentation/agentic/xpander/version.py +3 -0
- agentops/instrumentation/common/README.md +65 -0
- agentops/instrumentation/common/attributes.py +1 -2
- agentops/instrumentation/providers/anthropic/__init__.py +24 -0
- agentops/instrumentation/providers/anthropic/attributes/__init__.py +23 -0
- agentops/instrumentation/providers/anthropic/attributes/common.py +64 -0
- agentops/instrumentation/providers/anthropic/attributes/message.py +541 -0
- agentops/instrumentation/providers/anthropic/attributes/tools.py +231 -0
- agentops/instrumentation/providers/anthropic/event_handler_wrapper.py +90 -0
- agentops/instrumentation/providers/anthropic/instrumentor.py +146 -0
- agentops/instrumentation/providers/anthropic/stream_wrapper.py +436 -0
- agentops/instrumentation/providers/google_genai/README.md +33 -0
- agentops/instrumentation/providers/google_genai/__init__.py +24 -0
- agentops/instrumentation/providers/google_genai/attributes/__init__.py +25 -0
- agentops/instrumentation/providers/google_genai/attributes/chat.py +125 -0
- agentops/instrumentation/providers/google_genai/attributes/common.py +88 -0
- agentops/instrumentation/providers/google_genai/attributes/model.py +284 -0
- agentops/instrumentation/providers/google_genai/instrumentor.py +170 -0
- agentops/instrumentation/providers/google_genai/stream_wrapper.py +238 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/__init__.py +28 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/__init__.py +27 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/attributes.py +277 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/common.py +104 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/instrumentor.py +162 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/stream_wrapper.py +302 -0
- agentops/instrumentation/providers/mem0/__init__.py +45 -0
- agentops/instrumentation/providers/mem0/common.py +377 -0
- agentops/instrumentation/providers/mem0/instrumentor.py +270 -0
- agentops/instrumentation/providers/mem0/memory.py +430 -0
- agentops/instrumentation/providers/openai/__init__.py +21 -0
- agentops/instrumentation/providers/openai/attributes/__init__.py +7 -0
- agentops/instrumentation/providers/openai/attributes/common.py +55 -0
- agentops/instrumentation/providers/openai/attributes/response.py +607 -0
- agentops/instrumentation/providers/openai/config.py +36 -0
- agentops/instrumentation/providers/openai/instrumentor.py +312 -0
- agentops/instrumentation/providers/openai/stream_wrapper.py +941 -0
- agentops/instrumentation/providers/openai/utils.py +44 -0
- agentops/instrumentation/providers/openai/v0.py +176 -0
- agentops/instrumentation/providers/openai/v0_wrappers.py +483 -0
- agentops/instrumentation/providers/openai/wrappers/__init__.py +30 -0
- agentops/instrumentation/providers/openai/wrappers/assistant.py +277 -0
- agentops/instrumentation/providers/openai/wrappers/chat.py +259 -0
- agentops/instrumentation/providers/openai/wrappers/completion.py +109 -0
- agentops/instrumentation/providers/openai/wrappers/embeddings.py +94 -0
- agentops/instrumentation/providers/openai/wrappers/image_gen.py +75 -0
- agentops/instrumentation/providers/openai/wrappers/responses.py +191 -0
- agentops/instrumentation/providers/openai/wrappers/shared.py +81 -0
- agentops/instrumentation/utilities/concurrent_futures/__init__.py +10 -0
- agentops/instrumentation/utilities/concurrent_futures/instrumentation.py +206 -0
- agentops/integration/callbacks/dspy/__init__.py +11 -0
- agentops/integration/callbacks/dspy/callback.py +471 -0
- agentops/integration/callbacks/langchain/README.md +59 -0
- agentops/integration/callbacks/langchain/__init__.py +15 -0
- agentops/integration/callbacks/langchain/callback.py +791 -0
- agentops/integration/callbacks/langchain/utils.py +54 -0
- agentops/legacy/crewai.md +121 -0
- agentops/logging/instrument_logging.py +4 -0
- agentops/sdk/README.md +220 -0
- agentops/sdk/core.py +75 -32
- agentops/sdk/descriptors/classproperty.py +28 -0
- agentops/sdk/exporters.py +152 -33
- agentops/semconv/README.md +125 -0
- agentops/semconv/span_kinds.py +0 -2
- agentops/validation.py +102 -63
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/METADATA +30 -40
- mseep_agentops-0.4.23.dist-info/RECORD +178 -0
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/WHEEL +1 -2
- mseep_agentops-0.4.18.dist-info/RECORD +0 -94
- mseep_agentops-0.4.18.dist-info/top_level.txt +0 -2
- tests/conftest.py +0 -10
- tests/unit/client/__init__.py +0 -1
- tests/unit/client/test_http_adapter.py +0 -221
- tests/unit/client/test_http_client.py +0 -206
- tests/unit/conftest.py +0 -54
- tests/unit/sdk/__init__.py +0 -1
- tests/unit/sdk/instrumentation_tester.py +0 -207
- tests/unit/sdk/test_attributes.py +0 -392
- tests/unit/sdk/test_concurrent_instrumentation.py +0 -468
- tests/unit/sdk/test_decorators.py +0 -763
- tests/unit/sdk/test_exporters.py +0 -241
- tests/unit/sdk/test_factory.py +0 -1188
- tests/unit/sdk/test_internal_span_processor.py +0 -397
- tests/unit/sdk/test_resource_attributes.py +0 -35
- tests/unit/test_config.py +0 -82
- tests/unit/test_context_manager.py +0 -777
- tests/unit/test_events.py +0 -27
- tests/unit/test_host_env.py +0 -54
- tests/unit/test_init_py.py +0 -501
- tests/unit/test_serialization.py +0 -433
- tests/unit/test_session.py +0 -676
- tests/unit/test_user_agent.py +0 -34
- tests/unit/test_validation.py +0 -405
- {tests → agentops/instrumentation/agentic/openai_agents/attributes}/__init__.py +0 -0
- /tests/unit/__init__.py → /agentops/instrumentation/providers/openai/attributes/tools.py +0 -0
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/licenses/LICENSE +0 -0
tests/unit/sdk/test_factory.py
DELETED
@@ -1,1188 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
import inspect
|
3
|
-
import pytest
|
4
|
-
|
5
|
-
from agentops.sdk.decorators.factory import create_entity_decorator
|
6
|
-
from agentops.semconv import SpanKind
|
7
|
-
from agentops.semconv.span_attributes import SpanAttributes
|
8
|
-
from tests.unit.sdk.instrumentation_tester import InstrumentationTester
|
9
|
-
from agentops.sdk.core import tracer
|
10
|
-
|
11
|
-
|
12
|
-
class TestFactoryModule:
|
13
|
-
"""Comprehensive tests for the factory.py module functionality."""
|
14
|
-
|
15
|
-
def test_create_entity_decorator_factory_function(self):
|
16
|
-
"""Test that create_entity_decorator returns a callable decorator."""
|
17
|
-
decorator = create_entity_decorator("test_kind")
|
18
|
-
assert callable(decorator)
|
19
|
-
|
20
|
-
# Test that it can be used as a decorator
|
21
|
-
@decorator
|
22
|
-
def test_function():
|
23
|
-
return "test"
|
24
|
-
|
25
|
-
assert test_function() == "test"
|
26
|
-
|
27
|
-
def test_decorator_with_parameters(self):
|
28
|
-
"""Test decorator with explicit parameters."""
|
29
|
-
decorator = create_entity_decorator("test_kind")
|
30
|
-
|
31
|
-
@decorator(name="custom_name", version="1.0", tags=["tag1", "tag2"])
|
32
|
-
def test_function():
|
33
|
-
return "test"
|
34
|
-
|
35
|
-
assert test_function() == "test"
|
36
|
-
|
37
|
-
def test_decorator_partial_application(self):
|
38
|
-
"""Test that decorator can be partially applied."""
|
39
|
-
decorator = create_entity_decorator("test_kind")
|
40
|
-
partial_decorator = decorator(name="partial_name", version="2.0")
|
41
|
-
|
42
|
-
@partial_decorator
|
43
|
-
def test_function():
|
44
|
-
return "test"
|
45
|
-
|
46
|
-
assert test_function() == "test"
|
47
|
-
|
48
|
-
def test_class_decoration_basic(self, instrumentation: InstrumentationTester):
|
49
|
-
"""Test basic class decoration functionality."""
|
50
|
-
decorator = create_entity_decorator("test_kind")
|
51
|
-
|
52
|
-
@decorator
|
53
|
-
class TestClass:
|
54
|
-
def __init__(self, value=42):
|
55
|
-
self.value = value
|
56
|
-
|
57
|
-
# Test instantiation
|
58
|
-
instance = TestClass(100)
|
59
|
-
assert instance.value == 100
|
60
|
-
|
61
|
-
# Note: The current factory implementation has a bug where class decoration
|
62
|
-
# creates spans but doesn't properly end them, so no spans are recorded
|
63
|
-
spans = instrumentation.get_finished_spans()
|
64
|
-
assert len(spans) == 0
|
65
|
-
|
66
|
-
def test_class_decoration_with_parameters(self, instrumentation: InstrumentationTester):
|
67
|
-
"""Test class decoration with explicit parameters."""
|
68
|
-
decorator = create_entity_decorator("test_kind")
|
69
|
-
|
70
|
-
@decorator(name="CustomClass", version="1.0", tags={"env": "test"})
|
71
|
-
class TestClass:
|
72
|
-
def __init__(self, value=42):
|
73
|
-
self.value = value
|
74
|
-
|
75
|
-
instance = TestClass(100)
|
76
|
-
assert instance.value == 100
|
77
|
-
|
78
|
-
# Note: The current factory implementation has a bug where class decoration
|
79
|
-
# creates spans but doesn't properly end them, so no spans are recorded
|
80
|
-
spans = instrumentation.get_finished_spans()
|
81
|
-
assert len(spans) == 0
|
82
|
-
|
83
|
-
def test_class_metadata_preservation(self):
|
84
|
-
"""Test that class metadata is preserved after decoration."""
|
85
|
-
decorator = create_entity_decorator("test_kind")
|
86
|
-
|
87
|
-
@decorator
|
88
|
-
class TestClass:
|
89
|
-
"""Test class docstring."""
|
90
|
-
|
91
|
-
def __init__(self):
|
92
|
-
pass
|
93
|
-
|
94
|
-
assert TestClass.__name__ == "TestClass"
|
95
|
-
# The qualname will include the test function context, which is expected
|
96
|
-
assert "TestClass" in TestClass.__qualname__
|
97
|
-
assert TestClass.__module__ == TestClass.__module__ # Should be preserved
|
98
|
-
assert TestClass.__doc__ == "Test class docstring."
|
99
|
-
|
100
|
-
def test_async_context_manager_normal_flow(self, instrumentation: InstrumentationTester):
|
101
|
-
"""Test async context manager with normal flow."""
|
102
|
-
decorator = create_entity_decorator("test_kind")
|
103
|
-
|
104
|
-
@decorator
|
105
|
-
class TestClass:
|
106
|
-
def __init__(self):
|
107
|
-
self.value = 42
|
108
|
-
|
109
|
-
async def test_async_context():
|
110
|
-
async with TestClass() as instance:
|
111
|
-
assert instance.value == 42
|
112
|
-
assert hasattr(instance, "_agentops_active_span")
|
113
|
-
assert instance._agentops_active_span is not None
|
114
|
-
return "success"
|
115
|
-
|
116
|
-
result = asyncio.run(test_async_context())
|
117
|
-
assert result == "success"
|
118
|
-
|
119
|
-
spans = instrumentation.get_finished_spans()
|
120
|
-
assert len(spans) == 1
|
121
|
-
|
122
|
-
def test_async_context_manager_exception_flow(self, instrumentation: InstrumentationTester):
|
123
|
-
"""Test async context manager with exception flow."""
|
124
|
-
decorator = create_entity_decorator("test_kind")
|
125
|
-
|
126
|
-
@decorator
|
127
|
-
class TestClass:
|
128
|
-
def __init__(self):
|
129
|
-
self.value = 42
|
130
|
-
|
131
|
-
async def test_async_context_with_exception():
|
132
|
-
try:
|
133
|
-
async with TestClass() as instance:
|
134
|
-
assert instance.value == 42
|
135
|
-
raise ValueError("Test exception")
|
136
|
-
except ValueError:
|
137
|
-
return "exception_handled"
|
138
|
-
|
139
|
-
result = asyncio.run(test_async_context_with_exception())
|
140
|
-
assert result == "exception_handled"
|
141
|
-
|
142
|
-
spans = instrumentation.get_finished_spans()
|
143
|
-
assert len(spans) == 1
|
144
|
-
|
145
|
-
def test_async_context_manager_reuse(self, instrumentation: InstrumentationTester):
|
146
|
-
"""Test that async context manager can be reused."""
|
147
|
-
decorator = create_entity_decorator("test_kind")
|
148
|
-
|
149
|
-
@decorator
|
150
|
-
class TestClass:
|
151
|
-
def __init__(self):
|
152
|
-
self.value = 42
|
153
|
-
|
154
|
-
async def test_reuse():
|
155
|
-
instance = TestClass()
|
156
|
-
|
157
|
-
# First use
|
158
|
-
async with instance as inst1:
|
159
|
-
assert inst1.value == 42
|
160
|
-
|
161
|
-
# Second use - should work with existing span
|
162
|
-
async with instance as inst2:
|
163
|
-
assert inst2.value == 42
|
164
|
-
assert inst2 is instance
|
165
|
-
|
166
|
-
asyncio.run(test_reuse())
|
167
|
-
|
168
|
-
spans = instrumentation.get_finished_spans()
|
169
|
-
# The current implementation creates a span for __init__ and another for the async context
|
170
|
-
assert len(spans) == 2
|
171
|
-
|
172
|
-
def test_sync_function_decoration(self, instrumentation: InstrumentationTester):
|
173
|
-
"""Test synchronous function decoration."""
|
174
|
-
decorator = create_entity_decorator("test_kind")
|
175
|
-
|
176
|
-
@decorator
|
177
|
-
def test_function(x, y=10):
|
178
|
-
return x + y
|
179
|
-
|
180
|
-
result = test_function(5, y=15)
|
181
|
-
assert result == 20
|
182
|
-
|
183
|
-
spans = instrumentation.get_finished_spans()
|
184
|
-
assert len(spans) == 1
|
185
|
-
span = spans[0]
|
186
|
-
assert span.name == "test_function.test_kind"
|
187
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
188
|
-
|
189
|
-
def test_async_function_decoration(self, instrumentation: InstrumentationTester):
|
190
|
-
"""Test asynchronous function decoration."""
|
191
|
-
decorator = create_entity_decorator("test_kind")
|
192
|
-
|
193
|
-
@decorator
|
194
|
-
async def test_async_function(x, y=10):
|
195
|
-
await asyncio.sleep(0.01)
|
196
|
-
return x + y
|
197
|
-
|
198
|
-
result = asyncio.run(test_async_function(5, y=15))
|
199
|
-
assert result == 20
|
200
|
-
|
201
|
-
spans = instrumentation.get_finished_spans()
|
202
|
-
assert len(spans) == 1
|
203
|
-
span = spans[0]
|
204
|
-
assert span.name == "test_async_function.test_kind"
|
205
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
206
|
-
|
207
|
-
def test_generator_function_decoration(self, instrumentation: InstrumentationTester):
|
208
|
-
"""Test generator function decoration."""
|
209
|
-
decorator = create_entity_decorator("test_kind")
|
210
|
-
|
211
|
-
@decorator
|
212
|
-
def test_generator(count):
|
213
|
-
for i in range(count):
|
214
|
-
yield f"item_{i}"
|
215
|
-
|
216
|
-
results = list(test_generator(3))
|
217
|
-
assert results == ["item_0", "item_1", "item_2"]
|
218
|
-
|
219
|
-
spans = instrumentation.get_finished_spans()
|
220
|
-
assert len(spans) == 1
|
221
|
-
span = spans[0]
|
222
|
-
assert span.name == "test_generator.test_kind"
|
223
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
224
|
-
|
225
|
-
def test_async_generator_function_decoration(self, instrumentation: InstrumentationTester):
|
226
|
-
"""Test async generator function decoration."""
|
227
|
-
decorator = create_entity_decorator("test_kind")
|
228
|
-
|
229
|
-
@decorator
|
230
|
-
async def test_async_generator(count):
|
231
|
-
for i in range(count):
|
232
|
-
await asyncio.sleep(0.01)
|
233
|
-
yield f"async_item_{i}"
|
234
|
-
|
235
|
-
async def collect_results():
|
236
|
-
results = []
|
237
|
-
async for item in test_async_generator(3):
|
238
|
-
results.append(item)
|
239
|
-
return results
|
240
|
-
|
241
|
-
results = asyncio.run(collect_results())
|
242
|
-
assert results == ["async_item_0", "async_item_1", "async_item_2"]
|
243
|
-
|
244
|
-
spans = instrumentation.get_finished_spans()
|
245
|
-
assert len(spans) == 1
|
246
|
-
span = spans[0]
|
247
|
-
assert span.name == "test_async_generator.test_kind"
|
248
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
249
|
-
|
250
|
-
def test_session_entity_kind_sync(self, instrumentation: InstrumentationTester):
|
251
|
-
"""Test SESSION entity kind with sync function."""
|
252
|
-
decorator = create_entity_decorator("session")
|
253
|
-
|
254
|
-
@decorator
|
255
|
-
def test_session_function():
|
256
|
-
return "session_result"
|
257
|
-
|
258
|
-
result = test_session_function()
|
259
|
-
assert result == "session_result"
|
260
|
-
|
261
|
-
spans = instrumentation.get_finished_spans()
|
262
|
-
assert len(spans) == 1
|
263
|
-
span = spans[0]
|
264
|
-
assert span.name == "test_session_function.session"
|
265
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "session"
|
266
|
-
|
267
|
-
def test_session_entity_kind_async(self, instrumentation: InstrumentationTester):
|
268
|
-
"""Test SESSION entity kind with async function."""
|
269
|
-
decorator = create_entity_decorator("session")
|
270
|
-
|
271
|
-
@decorator
|
272
|
-
async def test_session_async_function():
|
273
|
-
await asyncio.sleep(0.01)
|
274
|
-
return "session_async_result"
|
275
|
-
|
276
|
-
result = asyncio.run(test_session_async_function())
|
277
|
-
assert result == "session_async_result"
|
278
|
-
|
279
|
-
spans = instrumentation.get_finished_spans()
|
280
|
-
assert len(spans) == 1
|
281
|
-
span = spans[0]
|
282
|
-
assert span.name == "test_session_async_function.session"
|
283
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "session"
|
284
|
-
|
285
|
-
def test_session_entity_kind_generator_warning(self, caplog, instrumentation: InstrumentationTester):
|
286
|
-
"""Test that SESSION entity kind logs warning for generators."""
|
287
|
-
# TODO: This test should assert that a warning is logged, but logger capture is complex due to custom logger setup.
|
288
|
-
# For now, we only assert the correct span behavior.
|
289
|
-
decorator = create_entity_decorator("session")
|
290
|
-
|
291
|
-
@decorator
|
292
|
-
def test_session_generator():
|
293
|
-
yield "session_generator_item"
|
294
|
-
|
295
|
-
# The decorator should return a generator, not None
|
296
|
-
# For session decorators, the generator logic falls through to the regular generator handling
|
297
|
-
generator = test_session_generator()
|
298
|
-
results = list(generator)
|
299
|
-
assert results == ["session_generator_item"]
|
300
|
-
|
301
|
-
spans = instrumentation.get_finished_spans()
|
302
|
-
assert len(spans) == 1
|
303
|
-
|
304
|
-
def test_tool_entity_kind_with_cost(self, instrumentation: InstrumentationTester):
|
305
|
-
"""Test tool entity kind with cost parameter."""
|
306
|
-
decorator = create_entity_decorator("tool")
|
307
|
-
|
308
|
-
@decorator(cost=0.05)
|
309
|
-
def test_tool_function():
|
310
|
-
return "tool_result"
|
311
|
-
|
312
|
-
result = test_tool_function()
|
313
|
-
assert result == "tool_result"
|
314
|
-
|
315
|
-
spans = instrumentation.get_finished_spans()
|
316
|
-
assert len(spans) == 1
|
317
|
-
span = spans[0]
|
318
|
-
assert span.name == "test_tool_function.tool"
|
319
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "tool"
|
320
|
-
assert span.attributes.get("gen_ai.usage.total_cost") == 0.05
|
321
|
-
|
322
|
-
def test_guardrail_entity_kind_with_spec(self, instrumentation: InstrumentationTester):
|
323
|
-
"""Test guardrail entity kind with spec parameter."""
|
324
|
-
decorator = create_entity_decorator("guardrail")
|
325
|
-
|
326
|
-
@decorator(spec="input")
|
327
|
-
def test_guardrail_function():
|
328
|
-
return "guardrail_result"
|
329
|
-
|
330
|
-
result = test_guardrail_function()
|
331
|
-
assert result == "guardrail_result"
|
332
|
-
|
333
|
-
spans = instrumentation.get_finished_spans()
|
334
|
-
assert len(spans) == 1
|
335
|
-
span = spans[0]
|
336
|
-
assert span.name == "test_guardrail_function.guardrail"
|
337
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "guardrail"
|
338
|
-
assert span.attributes.get("agentops.guardrail.spec") == "input"
|
339
|
-
|
340
|
-
def test_guardrail_entity_kind_with_output_spec(self, instrumentation: InstrumentationTester):
|
341
|
-
"""Test guardrail entity kind with output spec parameter."""
|
342
|
-
decorator = create_entity_decorator("guardrail")
|
343
|
-
|
344
|
-
@decorator(spec="output")
|
345
|
-
def test_guardrail_output_function():
|
346
|
-
return "guardrail_output_result"
|
347
|
-
|
348
|
-
result = test_guardrail_output_function()
|
349
|
-
assert result == "guardrail_output_result"
|
350
|
-
|
351
|
-
spans = instrumentation.get_finished_spans()
|
352
|
-
assert len(spans) == 1
|
353
|
-
span = spans[0]
|
354
|
-
assert span.name == "test_guardrail_output_function.guardrail"
|
355
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "guardrail"
|
356
|
-
assert span.attributes.get("agentops.guardrail.spec") == "output"
|
357
|
-
|
358
|
-
def test_guardrail_entity_kind_with_invalid_spec(self, instrumentation: InstrumentationTester):
|
359
|
-
"""Test guardrail entity kind with invalid spec parameter."""
|
360
|
-
decorator = create_entity_decorator("guardrail")
|
361
|
-
|
362
|
-
@decorator(spec="invalid")
|
363
|
-
def test_guardrail_invalid_function():
|
364
|
-
return "guardrail_invalid_result"
|
365
|
-
|
366
|
-
result = test_guardrail_invalid_function()
|
367
|
-
assert result == "guardrail_invalid_result"
|
368
|
-
|
369
|
-
spans = instrumentation.get_finished_spans()
|
370
|
-
assert len(spans) == 1
|
371
|
-
span = spans[0]
|
372
|
-
assert span.name == "test_guardrail_invalid_function.guardrail"
|
373
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "guardrail"
|
374
|
-
# Should not have the spec attribute for invalid spec
|
375
|
-
assert "agentops.decorator.guardrail.spec" not in span.attributes
|
376
|
-
|
377
|
-
def test_tags_parameter_list(self, instrumentation: InstrumentationTester):
|
378
|
-
"""Test tags parameter with list."""
|
379
|
-
decorator = create_entity_decorator("test_kind")
|
380
|
-
|
381
|
-
@decorator(tags=["tag1", "tag2", "tag3"])
|
382
|
-
def test_function_with_tags():
|
383
|
-
return "tagged_result"
|
384
|
-
|
385
|
-
result = test_function_with_tags()
|
386
|
-
assert result == "tagged_result"
|
387
|
-
|
388
|
-
spans = instrumentation.get_finished_spans()
|
389
|
-
assert len(spans) == 1
|
390
|
-
span = spans[0]
|
391
|
-
assert span.name == "test_function_with_tags.test_kind"
|
392
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
393
|
-
# Tags should be recorded in the span attributes
|
394
|
-
|
395
|
-
def test_tags_parameter_dict(self, instrumentation: InstrumentationTester):
|
396
|
-
"""Test tags parameter with dictionary."""
|
397
|
-
decorator = create_entity_decorator("test_kind")
|
398
|
-
|
399
|
-
@decorator(tags={"env": "test", "version": "1.0"})
|
400
|
-
def test_function_with_dict_tags():
|
401
|
-
return "dict_tagged_result"
|
402
|
-
|
403
|
-
result = test_function_with_dict_tags()
|
404
|
-
assert result == "dict_tagged_result"
|
405
|
-
|
406
|
-
spans = instrumentation.get_finished_spans()
|
407
|
-
assert len(spans) == 1
|
408
|
-
span = spans[0]
|
409
|
-
assert span.name == "test_function_with_dict_tags.test_kind"
|
410
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
411
|
-
|
412
|
-
def test_version_parameter(self, instrumentation: InstrumentationTester):
|
413
|
-
"""Test version parameter."""
|
414
|
-
decorator = create_entity_decorator("test_kind")
|
415
|
-
|
416
|
-
@decorator(version="2.1.0")
|
417
|
-
def test_function_with_version():
|
418
|
-
return "versioned_result"
|
419
|
-
|
420
|
-
result = test_function_with_version()
|
421
|
-
assert result == "versioned_result"
|
422
|
-
|
423
|
-
spans = instrumentation.get_finished_spans()
|
424
|
-
assert len(spans) == 1
|
425
|
-
span = spans[0]
|
426
|
-
assert span.name == "test_function_with_version.test_kind"
|
427
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
428
|
-
|
429
|
-
def test_function_with_exception(self, instrumentation: InstrumentationTester):
|
430
|
-
"""Test function decoration with exception handling."""
|
431
|
-
decorator = create_entity_decorator("test_kind")
|
432
|
-
|
433
|
-
@decorator
|
434
|
-
def test_function_with_exception():
|
435
|
-
raise ValueError("Test exception")
|
436
|
-
|
437
|
-
with pytest.raises(ValueError, match="Test exception"):
|
438
|
-
test_function_with_exception()
|
439
|
-
|
440
|
-
spans = instrumentation.get_finished_spans()
|
441
|
-
assert len(spans) == 1
|
442
|
-
span = spans[0]
|
443
|
-
assert span.name == "test_function_with_exception.test_kind"
|
444
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
445
|
-
|
446
|
-
def test_async_function_with_exception(self, instrumentation: InstrumentationTester):
|
447
|
-
"""Test async function decoration with exception handling."""
|
448
|
-
decorator = create_entity_decorator("test_kind")
|
449
|
-
|
450
|
-
@decorator
|
451
|
-
async def test_async_function_with_exception():
|
452
|
-
await asyncio.sleep(0.01)
|
453
|
-
raise RuntimeError("Async test exception")
|
454
|
-
|
455
|
-
with pytest.raises(RuntimeError, match="Async test exception"):
|
456
|
-
asyncio.run(test_async_function_with_exception())
|
457
|
-
|
458
|
-
spans = instrumentation.get_finished_spans()
|
459
|
-
assert len(spans) == 1
|
460
|
-
span = spans[0]
|
461
|
-
assert span.name == "test_async_function_with_exception.test_kind"
|
462
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
463
|
-
|
464
|
-
def test_class_init_with_exception(self, instrumentation: InstrumentationTester):
|
465
|
-
"""Test class decoration with exception in __init__."""
|
466
|
-
decorator = create_entity_decorator("test_kind")
|
467
|
-
|
468
|
-
@decorator
|
469
|
-
class TestClassWithException:
|
470
|
-
def __init__(self, should_raise=False):
|
471
|
-
if should_raise:
|
472
|
-
raise ValueError("Init exception")
|
473
|
-
self.value = 42
|
474
|
-
|
475
|
-
# Normal instantiation
|
476
|
-
instance = TestClassWithException(should_raise=False)
|
477
|
-
assert instance.value == 42
|
478
|
-
|
479
|
-
# Exception during instantiation
|
480
|
-
with pytest.raises(ValueError, match="Init exception"):
|
481
|
-
TestClassWithException(should_raise=True)
|
482
|
-
|
483
|
-
spans = instrumentation.get_finished_spans()
|
484
|
-
# Only one span should be created (for the successful instantiation)
|
485
|
-
# The failed instantiation doesn't create a span because the exception is raised before span creation
|
486
|
-
assert len(spans) == 1
|
487
|
-
|
488
|
-
def test_tracer_not_initialized(self, instrumentation: InstrumentationTester):
|
489
|
-
"""Test behavior when tracer is not initialized."""
|
490
|
-
# We can't directly set tracer.initialized as it's a read-only property
|
491
|
-
# Instead, we'll test that the decorator works when tracer is not initialized
|
492
|
-
# by temporarily mocking the tracer.initialized property
|
493
|
-
|
494
|
-
decorator = create_entity_decorator("test_kind")
|
495
|
-
|
496
|
-
@decorator
|
497
|
-
def test_function_no_tracer():
|
498
|
-
return "no_tracer_result"
|
499
|
-
|
500
|
-
# This should work normally since tracer is initialized in tests
|
501
|
-
result = test_function_no_tracer()
|
502
|
-
assert result == "no_tracer_result"
|
503
|
-
|
504
|
-
# Should create spans normally
|
505
|
-
spans = instrumentation.get_finished_spans()
|
506
|
-
assert len(spans) == 1
|
507
|
-
|
508
|
-
def test_complex_parameter_combination(self, instrumentation: InstrumentationTester):
|
509
|
-
"""Test decorator with all parameters combined."""
|
510
|
-
decorator = create_entity_decorator("tool")
|
511
|
-
|
512
|
-
@decorator(
|
513
|
-
name="complex_function", version="3.0.0", tags={"env": "test", "component": "test"}, cost=0.1, spec="input"
|
514
|
-
)
|
515
|
-
def test_complex_function(x, y):
|
516
|
-
return x * y
|
517
|
-
|
518
|
-
result = test_complex_function(5, 6)
|
519
|
-
assert result == 30
|
520
|
-
|
521
|
-
spans = instrumentation.get_finished_spans()
|
522
|
-
assert len(spans) == 1
|
523
|
-
span = spans[0]
|
524
|
-
assert span.name == "complex_function.tool"
|
525
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "tool"
|
526
|
-
assert span.attributes.get("gen_ai.usage.total_cost") == 0.1
|
527
|
-
|
528
|
-
def test_method_decoration(self, instrumentation: InstrumentationTester):
|
529
|
-
"""Test decoration of class methods."""
|
530
|
-
decorator = create_entity_decorator("test_kind")
|
531
|
-
|
532
|
-
class TestClass:
|
533
|
-
def __init__(self):
|
534
|
-
self.value = 0
|
535
|
-
|
536
|
-
@decorator
|
537
|
-
def test_method(self, increment):
|
538
|
-
self.value += increment
|
539
|
-
return self.value
|
540
|
-
|
541
|
-
instance = TestClass()
|
542
|
-
result = instance.test_method(5)
|
543
|
-
assert result == 5
|
544
|
-
assert instance.value == 5
|
545
|
-
|
546
|
-
spans = instrumentation.get_finished_spans()
|
547
|
-
assert len(spans) == 1
|
548
|
-
span = spans[0]
|
549
|
-
assert span.name == "test_method.test_kind"
|
550
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
551
|
-
|
552
|
-
def test_static_method_decoration(self, instrumentation: InstrumentationTester):
|
553
|
-
"""Test decoration of static methods."""
|
554
|
-
decorator = create_entity_decorator("test_kind")
|
555
|
-
|
556
|
-
class TestClass:
|
557
|
-
@staticmethod
|
558
|
-
@decorator
|
559
|
-
def test_static_method(x, y):
|
560
|
-
return x + y
|
561
|
-
|
562
|
-
result = TestClass.test_static_method(3, 4)
|
563
|
-
assert result == 7
|
564
|
-
|
565
|
-
spans = instrumentation.get_finished_spans()
|
566
|
-
assert len(spans) == 1
|
567
|
-
span = spans[0]
|
568
|
-
assert span.name == "test_static_method.test_kind"
|
569
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
570
|
-
|
571
|
-
def test_class_method_decoration(self, instrumentation: InstrumentationTester):
|
572
|
-
"""Test decoration of class methods."""
|
573
|
-
decorator = create_entity_decorator("test_kind")
|
574
|
-
|
575
|
-
class TestClass:
|
576
|
-
class_value = 100
|
577
|
-
|
578
|
-
@classmethod
|
579
|
-
@decorator
|
580
|
-
def test_class_method(cls, increment):
|
581
|
-
cls.class_value += increment
|
582
|
-
return cls.class_value
|
583
|
-
|
584
|
-
result = TestClass.test_class_method(50)
|
585
|
-
assert result == 150
|
586
|
-
assert TestClass.class_value == 150
|
587
|
-
|
588
|
-
spans = instrumentation.get_finished_spans()
|
589
|
-
assert len(spans) == 1
|
590
|
-
span = spans[0]
|
591
|
-
assert span.name == "test_class_method.test_kind"
|
592
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
593
|
-
|
594
|
-
def test_nested_decorators(self, instrumentation: InstrumentationTester):
|
595
|
-
"""Test multiple decorators applied to the same function."""
|
596
|
-
decorator1 = create_entity_decorator("kind1")
|
597
|
-
decorator2 = create_entity_decorator("kind2")
|
598
|
-
|
599
|
-
@decorator1
|
600
|
-
@decorator2
|
601
|
-
def test_nested_function():
|
602
|
-
return "nested_result"
|
603
|
-
|
604
|
-
result = test_nested_function()
|
605
|
-
assert result == "nested_result"
|
606
|
-
|
607
|
-
spans = instrumentation.get_finished_spans()
|
608
|
-
assert len(spans) == 2 # Should create spans for both decorators
|
609
|
-
|
610
|
-
# Check that both spans were created with correct names
|
611
|
-
span_names = [span.name for span in spans]
|
612
|
-
assert "test_nested_function.kind2" in span_names
|
613
|
-
assert "test_nested_function.kind1" in span_names
|
614
|
-
|
615
|
-
span_kinds = [span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) for span in spans]
|
616
|
-
assert "kind1" in span_kinds
|
617
|
-
assert "kind2" in span_kinds
|
618
|
-
|
619
|
-
def test_decorator_with_lambda(self, instrumentation: InstrumentationTester):
|
620
|
-
"""Test decorator with lambda function."""
|
621
|
-
decorator = create_entity_decorator("test_kind")
|
622
|
-
|
623
|
-
test_lambda = decorator(lambda x: x * 2)
|
624
|
-
|
625
|
-
result = test_lambda(5)
|
626
|
-
assert result == 10
|
627
|
-
|
628
|
-
spans = instrumentation.get_finished_spans()
|
629
|
-
assert len(spans) == 1
|
630
|
-
span = spans[0]
|
631
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
632
|
-
|
633
|
-
def test_decorator_with_builtin_function(self, instrumentation: InstrumentationTester):
|
634
|
-
"""Test decorator with built-in function (should work but may not create spans)."""
|
635
|
-
decorator = create_entity_decorator("test_kind")
|
636
|
-
|
637
|
-
# This should not raise an error, but may not create spans due to built-in function limitations
|
638
|
-
decorated_len = decorator(len)
|
639
|
-
|
640
|
-
result = decorated_len([1, 2, 3, 4, 5])
|
641
|
-
assert result == 5
|
642
|
-
|
643
|
-
# Built-in functions may not be instrumented the same way
|
644
|
-
_ = instrumentation.get_finished_spans()
|
645
|
-
# The behavior may vary depending on the implementation
|
646
|
-
|
647
|
-
def test_decorator_with_coroutine_function(self, instrumentation: InstrumentationTester):
|
648
|
-
"""Test decorator with coroutine function."""
|
649
|
-
decorator = create_entity_decorator("test_kind")
|
650
|
-
|
651
|
-
@decorator
|
652
|
-
async def test_coroutine():
|
653
|
-
await asyncio.sleep(0.01)
|
654
|
-
return "coroutine_result"
|
655
|
-
|
656
|
-
# Test that it's actually a coroutine function
|
657
|
-
assert asyncio.iscoroutinefunction(test_coroutine)
|
658
|
-
|
659
|
-
result = asyncio.run(test_coroutine())
|
660
|
-
assert result == "coroutine_result"
|
661
|
-
|
662
|
-
spans = instrumentation.get_finished_spans()
|
663
|
-
assert len(spans) == 1
|
664
|
-
span = spans[0]
|
665
|
-
assert span.name == "test_coroutine.test_kind"
|
666
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
667
|
-
|
668
|
-
def test_decorator_with_async_generator_function(self, instrumentation: InstrumentationTester):
|
669
|
-
"""Test decorator with async generator function."""
|
670
|
-
decorator = create_entity_decorator("test_kind")
|
671
|
-
|
672
|
-
@decorator
|
673
|
-
async def test_async_gen():
|
674
|
-
for i in range(3):
|
675
|
-
await asyncio.sleep(0.01)
|
676
|
-
yield f"async_gen_item_{i}"
|
677
|
-
|
678
|
-
# Test that it's actually an async generator function
|
679
|
-
assert inspect.isasyncgenfunction(test_async_gen)
|
680
|
-
|
681
|
-
async def collect_async_gen():
|
682
|
-
results = []
|
683
|
-
async for item in test_async_gen():
|
684
|
-
results.append(item)
|
685
|
-
return results
|
686
|
-
|
687
|
-
results = asyncio.run(collect_async_gen())
|
688
|
-
assert results == ["async_gen_item_0", "async_gen_item_1", "async_gen_item_2"]
|
689
|
-
|
690
|
-
spans = instrumentation.get_finished_spans()
|
691
|
-
assert len(spans) == 1
|
692
|
-
span = spans[0]
|
693
|
-
assert span.name == "test_async_gen.test_kind"
|
694
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
695
|
-
|
696
|
-
def test_decorator_with_generator_function(self, instrumentation: InstrumentationTester):
|
697
|
-
"""Test decorator with generator function."""
|
698
|
-
decorator = create_entity_decorator("test_kind")
|
699
|
-
|
700
|
-
@decorator
|
701
|
-
def test_gen():
|
702
|
-
for i in range(3):
|
703
|
-
yield f"gen_item_{i}"
|
704
|
-
|
705
|
-
# Test that it's actually a generator function
|
706
|
-
assert inspect.isgeneratorfunction(test_gen)
|
707
|
-
|
708
|
-
results = list(test_gen())
|
709
|
-
assert results == ["gen_item_0", "gen_item_1", "gen_item_2"]
|
710
|
-
|
711
|
-
spans = instrumentation.get_finished_spans()
|
712
|
-
assert len(spans) == 1
|
713
|
-
span = spans[0]
|
714
|
-
assert span.name == "test_gen.test_kind"
|
715
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
716
|
-
|
717
|
-
def test_decorator_with_kwargs_only_function(self, instrumentation: InstrumentationTester):
|
718
|
-
"""Test decorator with function that only accepts kwargs."""
|
719
|
-
decorator = create_entity_decorator("test_kind")
|
720
|
-
|
721
|
-
@decorator
|
722
|
-
def test_kwargs_only(**kwargs):
|
723
|
-
return sum(kwargs.values())
|
724
|
-
|
725
|
-
result = test_kwargs_only(a=1, b=2, c=3)
|
726
|
-
assert result == 6
|
727
|
-
|
728
|
-
spans = instrumentation.get_finished_spans()
|
729
|
-
assert len(spans) == 1
|
730
|
-
span = spans[0]
|
731
|
-
assert span.name == "test_kwargs_only.test_kind"
|
732
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
733
|
-
|
734
|
-
def test_decorator_with_args_only_function(self, instrumentation: InstrumentationTester):
|
735
|
-
"""Test decorator with function that only accepts args."""
|
736
|
-
decorator = create_entity_decorator("test_kind")
|
737
|
-
|
738
|
-
@decorator
|
739
|
-
def test_args_only(*args):
|
740
|
-
return sum(args)
|
741
|
-
|
742
|
-
result = test_args_only(1, 2, 3, 4, 5)
|
743
|
-
assert result == 15
|
744
|
-
|
745
|
-
spans = instrumentation.get_finished_spans()
|
746
|
-
assert len(spans) == 1
|
747
|
-
span = spans[0]
|
748
|
-
assert span.name == "test_args_only.test_kind"
|
749
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
750
|
-
|
751
|
-
def test_decorator_with_mixed_args_function(self, instrumentation: InstrumentationTester):
|
752
|
-
"""Test decorator with function that accepts both args and kwargs."""
|
753
|
-
decorator = create_entity_decorator("test_kind")
|
754
|
-
|
755
|
-
@decorator
|
756
|
-
def test_mixed_args(x, y, *args, **kwargs):
|
757
|
-
return x + y + sum(args) + sum(kwargs.values())
|
758
|
-
|
759
|
-
result = test_mixed_args(1, 2, 3, 4, a=5, b=6)
|
760
|
-
assert result == 21 # 1 + 2 + 3 + 4 + 5 + 6
|
761
|
-
|
762
|
-
spans = instrumentation.get_finished_spans()
|
763
|
-
assert len(spans) == 1
|
764
|
-
span = spans[0]
|
765
|
-
assert span.name == "test_mixed_args.test_kind"
|
766
|
-
assert span.attributes.get(SpanAttributes.AGENTOPS_SPAN_KIND) == "test_kind"
|
767
|
-
|
768
|
-
def test_class_input_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
769
|
-
"""Test exception handling when recording class input fails."""
|
770
|
-
decorator = create_entity_decorator("test_kind")
|
771
|
-
|
772
|
-
# Create a class that will cause _record_entity_input to fail
|
773
|
-
@decorator
|
774
|
-
class TestClass:
|
775
|
-
def __init__(self, value=42):
|
776
|
-
# Create an object that will cause serialization to fail
|
777
|
-
self.value = value
|
778
|
-
self.bad_object = object() # This will cause serialization issues
|
779
|
-
|
780
|
-
# The exception should be caught and logged
|
781
|
-
instance = TestClass(100)
|
782
|
-
assert instance.value == 100
|
783
|
-
# Note: The actual exception might not be logged in the current implementation
|
784
|
-
# but the coverage will show if the exception handling path was executed
|
785
|
-
|
786
|
-
def test_class_output_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
787
|
-
"""Test exception handling when recording class output fails."""
|
788
|
-
decorator = create_entity_decorator("test_kind")
|
789
|
-
|
790
|
-
@decorator
|
791
|
-
class TestClass:
|
792
|
-
def __init__(self):
|
793
|
-
self.value = 42
|
794
|
-
# Create an object that will cause serialization to fail
|
795
|
-
self.bad_object = object()
|
796
|
-
|
797
|
-
async def test_async_context():
|
798
|
-
async with TestClass():
|
799
|
-
return "success"
|
800
|
-
|
801
|
-
result = asyncio.run(test_async_context())
|
802
|
-
assert result == "success"
|
803
|
-
# Note: The actual exception might not be logged in the current implementation
|
804
|
-
# but the coverage will show if the exception handling path was executed
|
805
|
-
|
806
|
-
def test_session_generator_implementation(self, instrumentation: InstrumentationTester, caplog):
|
807
|
-
"""Test the session generator implementation that was previously not implemented."""
|
808
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
809
|
-
|
810
|
-
@decorator
|
811
|
-
def test_session_generator():
|
812
|
-
yield 1
|
813
|
-
yield 2
|
814
|
-
yield 3
|
815
|
-
|
816
|
-
results = list(test_session_generator())
|
817
|
-
assert results == [1, 2, 3]
|
818
|
-
|
819
|
-
# The warning should be logged, but the exact message might vary
|
820
|
-
# Just verify that the function works and creates spans
|
821
|
-
spans = instrumentation.get_finished_spans()
|
822
|
-
assert len(spans) == 1
|
823
|
-
|
824
|
-
def test_session_async_generator_implementation(self, instrumentation: InstrumentationTester, caplog):
|
825
|
-
"""Test the session async generator implementation."""
|
826
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
827
|
-
|
828
|
-
@decorator
|
829
|
-
async def test_session_async_generator():
|
830
|
-
yield 1
|
831
|
-
yield 2
|
832
|
-
yield 3
|
833
|
-
|
834
|
-
async def collect_results():
|
835
|
-
results = []
|
836
|
-
async for item in test_session_async_generator():
|
837
|
-
results.append(item)
|
838
|
-
return results
|
839
|
-
|
840
|
-
results = asyncio.run(collect_results())
|
841
|
-
assert results == [1, 2, 3]
|
842
|
-
|
843
|
-
# The warning should be logged, but the exact message might vary
|
844
|
-
# Just verify that the function works and creates spans
|
845
|
-
spans = instrumentation.get_finished_spans()
|
846
|
-
assert len(spans) == 1
|
847
|
-
|
848
|
-
def test_session_generator_input_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
849
|
-
"""Test exception handling when recording session generator input fails."""
|
850
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
851
|
-
|
852
|
-
@decorator
|
853
|
-
def test_session_generator():
|
854
|
-
# Create an object that will cause serialization to fail
|
855
|
-
_ = object() # This will cause serialization issues
|
856
|
-
yield 1
|
857
|
-
yield 2
|
858
|
-
|
859
|
-
results = list(test_session_generator())
|
860
|
-
assert results == [1, 2]
|
861
|
-
# Note: The actual exception might not be logged in the current implementation
|
862
|
-
# but the coverage will show if the exception handling path was executed
|
863
|
-
|
864
|
-
def test_session_async_generator_input_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
865
|
-
"""Test exception handling when recording session async generator input fails."""
|
866
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
867
|
-
|
868
|
-
@decorator
|
869
|
-
async def test_session_async_generator():
|
870
|
-
# Create an object that will cause serialization to fail
|
871
|
-
_ = object() # This will cause serialization issues
|
872
|
-
yield 1
|
873
|
-
yield 2
|
874
|
-
|
875
|
-
async def collect_results():
|
876
|
-
results = []
|
877
|
-
async for item in test_session_async_generator():
|
878
|
-
results.append(item)
|
879
|
-
return results
|
880
|
-
|
881
|
-
results = asyncio.run(collect_results())
|
882
|
-
assert results == [1, 2]
|
883
|
-
# Note: The actual exception might not be logged in the current implementation
|
884
|
-
# but the coverage will show if the exception handling path was executed
|
885
|
-
|
886
|
-
def test_session_async_trace_start_failure(self, instrumentation: InstrumentationTester, caplog):
|
887
|
-
"""Test handling when trace start fails for session async function."""
|
888
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
889
|
-
|
890
|
-
# Mock tracer.start_trace to return None
|
891
|
-
with pytest.MonkeyPatch().context() as m:
|
892
|
-
m.setattr(tracer, "start_trace", lambda *args, **kwargs: None)
|
893
|
-
|
894
|
-
@decorator
|
895
|
-
async def test_session_async_function():
|
896
|
-
return "success"
|
897
|
-
|
898
|
-
result = asyncio.run(test_session_async_function())
|
899
|
-
assert result == "success"
|
900
|
-
# The error message should be logged, but the exact format might vary
|
901
|
-
# Just verify that the function works when trace start fails
|
902
|
-
|
903
|
-
def test_session_async_input_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
904
|
-
"""Test exception handling when recording session async input fails."""
|
905
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
906
|
-
|
907
|
-
@decorator
|
908
|
-
async def test_session_async_function():
|
909
|
-
# Create an object that will cause serialization to fail
|
910
|
-
_ = object() # This will cause serialization issues
|
911
|
-
return "success"
|
912
|
-
|
913
|
-
result = asyncio.run(test_session_async_function())
|
914
|
-
assert result == "success"
|
915
|
-
# Note: The actual exception might not be logged in the current implementation
|
916
|
-
# but the coverage will show if the exception handling path was executed
|
917
|
-
|
918
|
-
def test_session_async_output_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
919
|
-
"""Test exception handling when recording session async output fails."""
|
920
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
921
|
-
|
922
|
-
@decorator
|
923
|
-
async def test_session_async_function():
|
924
|
-
# Return an object that will cause serialization to fail
|
925
|
-
return object()
|
926
|
-
|
927
|
-
result = asyncio.run(test_session_async_function())
|
928
|
-
assert result is not None
|
929
|
-
# Note: The actual exception might not be logged in the current implementation
|
930
|
-
# but the coverage will show if the exception handling path was executed
|
931
|
-
|
932
|
-
def test_session_async_exception_handling(self, instrumentation: InstrumentationTester):
|
933
|
-
"""Test exception handling in session async function."""
|
934
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
935
|
-
|
936
|
-
@decorator
|
937
|
-
async def test_session_async_function():
|
938
|
-
raise ValueError("Test exception")
|
939
|
-
|
940
|
-
with pytest.raises(ValueError, match="Test exception"):
|
941
|
-
asyncio.run(test_session_async_function())
|
942
|
-
|
943
|
-
# Should end trace with "Indeterminate" state
|
944
|
-
spans = instrumentation.get_finished_spans()
|
945
|
-
assert len(spans) == 1
|
946
|
-
|
947
|
-
def test_session_async_finally_block(self, instrumentation: InstrumentationTester, caplog):
|
948
|
-
"""Test finally block handling in session async function."""
|
949
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
950
|
-
|
951
|
-
@decorator
|
952
|
-
async def test_session_async_function():
|
953
|
-
return "success"
|
954
|
-
|
955
|
-
result = asyncio.run(test_session_async_function())
|
956
|
-
assert result == "success"
|
957
|
-
|
958
|
-
# Should not log warning about trace not being ended since it was ended properly
|
959
|
-
assert "not explicitly ended" not in caplog.text
|
960
|
-
|
961
|
-
def test_session_sync_trace_start_failure(self, instrumentation: InstrumentationTester, caplog):
|
962
|
-
"""Test handling when trace start fails for session sync function."""
|
963
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
964
|
-
|
965
|
-
# Mock tracer.start_trace to return None
|
966
|
-
with pytest.MonkeyPatch().context() as m:
|
967
|
-
m.setattr(tracer, "start_trace", lambda *args, **kwargs: None)
|
968
|
-
|
969
|
-
@decorator
|
970
|
-
def test_session_sync_function():
|
971
|
-
return "success"
|
972
|
-
|
973
|
-
result = test_session_sync_function()
|
974
|
-
assert result == "success"
|
975
|
-
# The error message should be logged, but the exact format might vary
|
976
|
-
# Just verify that the function works when trace start fails
|
977
|
-
|
978
|
-
def test_session_sync_input_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
979
|
-
"""Test exception handling when recording session sync input fails."""
|
980
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
981
|
-
|
982
|
-
@decorator
|
983
|
-
def test_session_sync_function():
|
984
|
-
# Create an object that will cause serialization to fail
|
985
|
-
_ = object() # This will cause serialization issues
|
986
|
-
return "success"
|
987
|
-
|
988
|
-
result = test_session_sync_function()
|
989
|
-
assert result == "success"
|
990
|
-
# Note: The actual exception might not be logged in the current implementation
|
991
|
-
# but the coverage will show if the exception handling path was executed
|
992
|
-
|
993
|
-
def test_session_sync_output_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
994
|
-
"""Test exception handling when recording session sync output fails."""
|
995
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
996
|
-
|
997
|
-
@decorator
|
998
|
-
def test_session_sync_function():
|
999
|
-
# Return an object that will cause serialization to fail
|
1000
|
-
return object()
|
1001
|
-
|
1002
|
-
result = test_session_sync_function()
|
1003
|
-
assert result is not None
|
1004
|
-
# Note: The actual exception might not be logged in the current implementation
|
1005
|
-
# but the coverage will show if the exception handling path was executed
|
1006
|
-
|
1007
|
-
def test_session_sync_exception_handling(self, instrumentation: InstrumentationTester):
|
1008
|
-
"""Test exception handling in session sync function."""
|
1009
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
1010
|
-
|
1011
|
-
@decorator
|
1012
|
-
def test_session_sync_function():
|
1013
|
-
raise ValueError("Test exception")
|
1014
|
-
|
1015
|
-
with pytest.raises(ValueError, match="Test exception"):
|
1016
|
-
test_session_sync_function()
|
1017
|
-
|
1018
|
-
# Should end trace with "Indeterminate" state
|
1019
|
-
spans = instrumentation.get_finished_spans()
|
1020
|
-
assert len(spans) == 1
|
1021
|
-
|
1022
|
-
def test_session_sync_finally_block(self, instrumentation: InstrumentationTester, caplog):
|
1023
|
-
"""Test finally block handling in session sync function."""
|
1024
|
-
decorator = create_entity_decorator(SpanKind.SESSION)
|
1025
|
-
|
1026
|
-
@decorator
|
1027
|
-
def test_session_sync_function():
|
1028
|
-
return "success"
|
1029
|
-
|
1030
|
-
result = test_session_sync_function()
|
1031
|
-
assert result == "success"
|
1032
|
-
|
1033
|
-
# Should not log warning about trace not being ended since it was ended properly
|
1034
|
-
assert "not explicitly ended" not in caplog.text
|
1035
|
-
|
1036
|
-
def test_generator_input_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
1037
|
-
"""Test exception handling when recording generator input fails."""
|
1038
|
-
decorator = create_entity_decorator("test_kind")
|
1039
|
-
|
1040
|
-
@decorator
|
1041
|
-
def test_generator():
|
1042
|
-
# Create an object that will cause serialization to fail
|
1043
|
-
_ = object() # This will cause serialization issues
|
1044
|
-
yield 1
|
1045
|
-
yield 2
|
1046
|
-
|
1047
|
-
results = list(test_generator())
|
1048
|
-
assert results == [1, 2]
|
1049
|
-
# Note: The actual exception might not be logged in the current implementation
|
1050
|
-
# but the coverage will show if the exception handling path was executed
|
1051
|
-
|
1052
|
-
def test_async_generator_input_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
1053
|
-
"""Test exception handling when recording async generator input fails."""
|
1054
|
-
decorator = create_entity_decorator("test_kind")
|
1055
|
-
|
1056
|
-
@decorator
|
1057
|
-
async def test_async_generator():
|
1058
|
-
# Create an object that will cause serialization to fail
|
1059
|
-
_ = object() # This will cause serialization issues
|
1060
|
-
yield 1
|
1061
|
-
yield 2
|
1062
|
-
|
1063
|
-
async def collect_results():
|
1064
|
-
results = []
|
1065
|
-
async for item in test_async_generator():
|
1066
|
-
results.append(item)
|
1067
|
-
return results
|
1068
|
-
|
1069
|
-
results = asyncio.run(collect_results())
|
1070
|
-
assert results == [1, 2]
|
1071
|
-
# Note: The actual exception might not be logged in the current implementation
|
1072
|
-
# but the coverage will show if the exception handling path was executed
|
1073
|
-
|
1074
|
-
def test_async_function_input_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
1075
|
-
"""Test exception handling when recording async function input fails."""
|
1076
|
-
decorator = create_entity_decorator("test_kind")
|
1077
|
-
|
1078
|
-
@decorator
|
1079
|
-
async def test_async_function():
|
1080
|
-
# Create an object that will cause serialization to fail
|
1081
|
-
_ = object() # This will cause serialization issues
|
1082
|
-
return "success"
|
1083
|
-
|
1084
|
-
result = asyncio.run(test_async_function())
|
1085
|
-
assert result == "success"
|
1086
|
-
# Note: The actual exception might not be logged in the current implementation
|
1087
|
-
# but the coverage will show if the exception handling path was executed
|
1088
|
-
|
1089
|
-
def test_async_function_output_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
1090
|
-
"""Test exception handling when recording async function output fails."""
|
1091
|
-
decorator = create_entity_decorator("test_kind")
|
1092
|
-
|
1093
|
-
@decorator
|
1094
|
-
async def test_async_function():
|
1095
|
-
# Return an object that will cause serialization to fail
|
1096
|
-
return object()
|
1097
|
-
|
1098
|
-
result = asyncio.run(test_async_function())
|
1099
|
-
assert result is not None
|
1100
|
-
# Note: The actual exception might not be logged in the current implementation
|
1101
|
-
# but the coverage will show if the exception handling path was executed
|
1102
|
-
|
1103
|
-
def test_async_function_execution_exception(self, instrumentation: InstrumentationTester, caplog):
|
1104
|
-
"""Test exception handling in async function execution."""
|
1105
|
-
decorator = create_entity_decorator("test_kind")
|
1106
|
-
|
1107
|
-
@decorator
|
1108
|
-
async def test_async_function():
|
1109
|
-
raise ValueError("Test exception")
|
1110
|
-
|
1111
|
-
with pytest.raises(ValueError, match="Test exception"):
|
1112
|
-
asyncio.run(test_async_function())
|
1113
|
-
|
1114
|
-
# The error should be logged, but the exact message might vary
|
1115
|
-
# Just verify that the exception is handled properly
|
1116
|
-
spans = instrumentation.get_finished_spans()
|
1117
|
-
assert len(spans) == 1
|
1118
|
-
|
1119
|
-
def test_sync_function_input_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
1120
|
-
"""Test exception handling when recording sync function input fails."""
|
1121
|
-
decorator = create_entity_decorator("test_kind")
|
1122
|
-
|
1123
|
-
@decorator
|
1124
|
-
def test_sync_function():
|
1125
|
-
# Create an object that will cause serialization to fail
|
1126
|
-
_ = object() # This will cause serialization issues
|
1127
|
-
return "success"
|
1128
|
-
|
1129
|
-
result = test_sync_function()
|
1130
|
-
assert result == "success"
|
1131
|
-
# Note: The actual exception might not be logged in the current implementation
|
1132
|
-
# but the coverage will show if the exception handling path was executed
|
1133
|
-
|
1134
|
-
def test_sync_function_output_recording_exception(self, instrumentation: InstrumentationTester, caplog):
|
1135
|
-
"""Test exception handling when recording sync function output fails."""
|
1136
|
-
decorator = create_entity_decorator("test_kind")
|
1137
|
-
|
1138
|
-
@decorator
|
1139
|
-
def test_sync_function():
|
1140
|
-
# Return an object that will cause serialization to fail
|
1141
|
-
return object()
|
1142
|
-
|
1143
|
-
result = test_sync_function()
|
1144
|
-
assert result is not None
|
1145
|
-
# Note: The actual exception might not be logged in the current implementation
|
1146
|
-
# but the coverage will show if the exception handling path was executed
|
1147
|
-
|
1148
|
-
def test_sync_function_execution_exception(self, instrumentation: InstrumentationTester, caplog):
|
1149
|
-
"""Test exception handling in sync function execution."""
|
1150
|
-
decorator = create_entity_decorator("test_kind")
|
1151
|
-
|
1152
|
-
@decorator
|
1153
|
-
def test_sync_function():
|
1154
|
-
raise ValueError("Test exception")
|
1155
|
-
|
1156
|
-
with pytest.raises(ValueError, match="Test exception"):
|
1157
|
-
test_sync_function()
|
1158
|
-
|
1159
|
-
# The error should be logged, but the exact message might vary
|
1160
|
-
# Just verify that the exception is handled properly
|
1161
|
-
spans = instrumentation.get_finished_spans()
|
1162
|
-
assert len(spans) == 1
|
1163
|
-
|
1164
|
-
def test_class_del_method_coverage(self, instrumentation: InstrumentationTester):
|
1165
|
-
"""Test that __del__ method is called when object is garbage collected."""
|
1166
|
-
decorator = create_entity_decorator("test_kind")
|
1167
|
-
|
1168
|
-
@decorator
|
1169
|
-
class TestClass:
|
1170
|
-
def __init__(self):
|
1171
|
-
self.value = 42
|
1172
|
-
|
1173
|
-
# Create instance and let it go out of scope to trigger __del__
|
1174
|
-
def create_and_destroy():
|
1175
|
-
instance = TestClass()
|
1176
|
-
assert instance.value == 42
|
1177
|
-
# The __del__ method should be called when instance goes out of scope
|
1178
|
-
|
1179
|
-
create_and_destroy()
|
1180
|
-
|
1181
|
-
# Force garbage collection to trigger __del__
|
1182
|
-
import gc
|
1183
|
-
|
1184
|
-
gc.collect()
|
1185
|
-
|
1186
|
-
# The __del__ method should have been called, but we can't easily test this
|
1187
|
-
# since it's called during garbage collection. The coverage will show if the
|
1188
|
-
# lines were executed.
|