From 7e5f771d332bd4ae2633e91d79855a5b50e45bb5 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Fri, 10 Jul 2026 14:32:11 +0200 Subject: [PATCH 1/2] fix(tracing): Set sentry.op to function in new trace decorator --- sentry_sdk/tracing_utils.py | 7 +++++-- tests/tracing/test_decorator.py | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index c2e34a795b..60779e30c1 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -1113,6 +1113,9 @@ def span_decorator(f: "Any") -> "Any": """ Decorator to create a span for the given function. """ + new_attributes = attributes or {} + if "sentry.op" not in new_attributes: + new_attributes["sentry.op"] = OP.FUNCTION @functools.wraps(f) async def async_wrapper(*args: "Any", **kwargs: "Any") -> "Any": @@ -1127,7 +1130,7 @@ async def async_wrapper(*args: "Any", **kwargs: "Any") -> "Any": span_name = name or qualname_from_function(f) or "" with start_streaming_span( - name=span_name, attributes=attributes, active=active + name=span_name, attributes=new_attributes, active=active ): result = await f(*args, **kwargs) return result @@ -1150,7 +1153,7 @@ def sync_wrapper(*args: "Any", **kwargs: "Any") -> "Any": span_name = name or qualname_from_function(f) or "" with start_streaming_span( - name=span_name, attributes=attributes, active=active + name=span_name, attributes=new_attributes, active=active ): return f(*args, **kwargs) diff --git a/tests/tracing/test_decorator.py b/tests/tracing/test_decorator.py index d6f6ca87ed..9852d950ab 100644 --- a/tests/tracing/test_decorator.py +++ b/tests/tracing/test_decorator.py @@ -110,6 +110,7 @@ def traced_function(): span["name"] == "test_decorator.test_trace_decorator_span_streaming..traced_function" ) + assert span["attributes"]["sentry.op"] == "function" assert span["status"] == "ok" @@ -136,6 +137,7 @@ def traced_function(): assert span["name"] == "traced" assert span["attributes"]["traced.attribute"] == 123 + assert span["attributes"]["sentry.op"] == "function" assert span["status"] == "ok" @@ -193,6 +195,7 @@ async def traced_function(): span["name"] == "test_decorator.test_trace_decorator_async_span_streaming..traced_function" ) + assert span["attributes"]["sentry.op"] == "function" assert span["status"] == "ok" @@ -222,6 +225,7 @@ async def traced_function(): assert span["name"] == "traced" assert span["attributes"]["traced.attribute"] == 123 + assert span["attributes"]["sentry.op"] == "function" assert span["status"] == "ok" From 6528ec723cec0a9058711314c229b34a52f28962 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Fri, 10 Jul 2026 14:49:50 +0200 Subject: [PATCH 2/2] fix(tracing): Avoid UnboundLocalError in streaming span decorator Copy attributes dict instead of using `or` to prevent rebinding the closure variable. Co-Authored-By: Claude Opus 4.6 --- sentry_sdk/tracing_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 60779e30c1..f4929ef1a7 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -1113,7 +1113,7 @@ def span_decorator(f: "Any") -> "Any": """ Decorator to create a span for the given function. """ - new_attributes = attributes or {} + new_attributes = dict(attributes) if attributes else {} if "sentry.op" not in new_attributes: new_attributes["sentry.op"] = OP.FUNCTION