66import pytest
77
88import sentry_sdk
9+ from sentry_sdk .traces import SegmentSource
910
1011minimum_python_38 = pytest .mark .skipif (
1112 sys .version_info < (3 , 8 ), reason = "Asyncio tests need Python >= 3.8"
@@ -43,9 +44,9 @@ def test_start_span(sentry_init, capture_envelopes):
4344
4445 events = capture_envelopes ()
4546
46- with sentry_sdk .traces .start_span (name = "segment" ):
47- with sentry_sdk .traces .start_span (name = "child" ):
48- ...
47+ with sentry_sdk .traces .start_span (name = "segment" ) as segment :
48+ with sentry_sdk .traces .start_span (name = "child" ) as child :
49+ assert child . segment == segment
4950
5051 sentry_sdk .get_client ().flush ()
5152 spans = envelopes_to_spans (events )
@@ -85,6 +86,7 @@ def test_start_span_no_context_manager(sentry_init, capture_envelopes):
8586 segment .start ()
8687 child = sentry_sdk .traces .start_span (name = "child" )
8788 child .start ()
89+ assert child .segment == segment
8890 child .finish ()
8991 segment .finish ()
9092
@@ -113,6 +115,37 @@ def test_start_span_no_context_manager(sentry_init, capture_envelopes):
113115 assert segment ["status" ] == "ok"
114116
115117
118+ def test_span_sampled_at_start (sentry_init , capture_envelopes ):
119+ # Test that if a span is created without the context manager, it is sampled
120+ # at .start() time rather then creation time
121+
122+ def traces_sampler (sampling_context ):
123+ assert "delayed_attribute" in sampling_context ["attributes" ]
124+ assert sampling_context ["attributes" ]["delayed_attribute" ] == 12
125+ return 1.0
126+
127+ sentry_init (
128+ traces_sampler = traces_sampler ,
129+ _experiments = {"trace_lifecycle" : "stream" },
130+ )
131+
132+ events = capture_envelopes ()
133+
134+ segment = sentry_sdk .traces .start_span (name = "segment" )
135+ segment .set_attribute ("delayed_attribute" , 12 )
136+ segment .start ()
137+ segment .finish ()
138+
139+ sentry_sdk .get_client ().flush ()
140+ spans = envelopes_to_spans (events )
141+
142+ assert len (spans ) == 1
143+ (segment ,) = spans
144+
145+ assert segment ["name" ] == "segment"
146+ assert segment ["attributes" ]["delayed_attribute" ] == 12
147+
148+
116149def test_start_span_attributes (sentry_init , capture_envelopes ):
117150 sentry_init (
118151 traces_sample_rate = 1.0 ,
@@ -165,6 +198,79 @@ def traces_sampler(sampling_context):
165198 assert span ["attributes" ]["my_attribute" ] == "my_value"
166199
167200
201+ def test_span_attributes (sentry_init , capture_envelopes ):
202+ sentry_init (
203+ traces_sample_rate = 1.0 ,
204+ _experiments = {"trace_lifecycle" : "stream" },
205+ )
206+
207+ events = capture_envelopes ()
208+
209+ class Class :
210+ pass
211+
212+ with sentry_sdk .traces .start_span (
213+ name = "segment" , attributes = {"attribute1" : "value" }
214+ ) as span :
215+ assert span .get_attributes ()["attribute1" ] == "value"
216+ span .set_attribute ("attribute2" , 47 )
217+ span .remove_attribute ("attribute1" )
218+ span .set_attributes ({"attribute3" : 4.5 , "attribute4" : False })
219+ assert "attribute1" not in span .get_attributes ()
220+ attributes = span .get_attributes ()
221+ assert attributes ["attribute2" ] == 47
222+ assert attributes ["attribute3" ] == 4.5
223+ assert attributes ["attribute4" ] is False
224+
225+ sentry_sdk .get_client ().flush ()
226+ spans = envelopes_to_spans (events )
227+
228+ assert len (spans ) == 1
229+ (span ,) = spans
230+
231+ assert span ["name" ] == "segment"
232+ assert "attribute1" not in span ["attributes" ]
233+ assert span ["attributes" ]["attribute2" ] == 47
234+ assert span ["attributes" ]["attribute3" ] == 4.5
235+ assert span ["attributes" ]["attribute4" ] is False
236+
237+
238+ def test_span_attributes_serialize_early (sentry_init , capture_envelopes ):
239+ sentry_init (
240+ traces_sample_rate = 1.0 ,
241+ _experiments = {"trace_lifecycle" : "stream" },
242+ )
243+
244+ events = capture_envelopes ()
245+
246+ class Class :
247+ pass
248+
249+ with sentry_sdk .traces .start_span (name = "span" ) as span :
250+ span .set_attributes (
251+ {
252+ # arrays of different types will be serialized
253+ "attribute1" : [123 , "text" ],
254+ # so will custom class instances
255+ "attribute2" : Class (),
256+ }
257+ )
258+ attributes = span .get_attributes ()
259+ assert isinstance (attributes ["attribute1" ], str )
260+ assert attributes ["attribute1" ] == "[123, 'text']"
261+ assert isinstance (attributes ["attribute2" ], str )
262+ assert "Class" in attributes ["attribute2" ]
263+
264+ sentry_sdk .get_client ().flush ()
265+ spans = envelopes_to_spans (events )
266+
267+ assert len (spans ) == 1
268+ (span ,) = spans
269+
270+ assert span ["attributes" ]["attribute1" ] == "[123, 'text']"
271+ assert "Class" in span ["attributes" ]["attribute2" ]
272+
273+
168274def test_traces_sampler_drops_span (sentry_init , capture_envelopes ):
169275 def traces_sampler (sampling_context ):
170276 assert "attributes" in sampling_context
@@ -432,7 +538,6 @@ def traced_function(): ...
432538
433539
434540@minimum_python_38
435- @pytest .mark .asyncio
436541def test_trace_decorator_async (sentry_init , capture_envelopes ):
437542 sentry_init (
438543 traces_sample_rate = 1.0 ,
@@ -459,6 +564,50 @@ async def traced_function(): ...
459564 assert span ["status" ] == "ok"
460565
461566
567+ def test_set_span_op (sentry_init , capture_envelopes ):
568+ sentry_init (
569+ traces_sample_rate = 1.0 ,
570+ _experiments = {"trace_lifecycle" : "stream" },
571+ )
572+
573+ events = capture_envelopes ()
574+
575+ with sentry_sdk .traces .start_span (name = "span" ) as span :
576+ span .set_op ("function" )
577+ assert span .get_attributes ()["sentry.op" ] == "function"
578+
579+ sentry_sdk .get_client ().flush ()
580+ spans = envelopes_to_spans (events )
581+
582+ assert len (spans ) == 1
583+ (span ,) = spans
584+
585+ assert span ["name" ] == "span"
586+ assert span ["attributes" ]["sentry.op" ] == "function"
587+
588+
589+ def test_set_span_source (sentry_init , capture_envelopes ):
590+ sentry_init (
591+ traces_sample_rate = 1.0 ,
592+ _experiments = {"trace_lifecycle" : "stream" },
593+ )
594+
595+ events = capture_envelopes ()
596+
597+ with sentry_sdk .traces .start_span (name = "span" ) as span :
598+ span .set_source (SegmentSource .TASK )
599+ assert span .get_attributes ()["sentry.span.source" ] == SegmentSource .TASK .value
600+
601+ sentry_sdk .get_client ().flush ()
602+ spans = envelopes_to_spans (events )
603+
604+ assert len (spans ) == 1
605+ (span ,) = spans
606+
607+ assert span ["name" ] == "span"
608+ assert span ["attributes" ]["sentry.span.source" ] == SegmentSource .TASK .value
609+
610+
462611def test_transport_format (sentry_init , capture_envelopes ):
463612 sentry_init (
464613 server_name = "test-server" ,
0 commit comments