From 554e6cfb51e1b7fa4d237250eae7b539bc808853 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Thu, 26 Feb 2026 16:38:00 +0100 Subject: [PATCH 1/6] feat: Add set_attribute, remove_attribute to global API --- sentry_sdk/api.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index c4e2229938..05e650311f 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -70,6 +70,8 @@ def overload(x: "T") -> "T": "last_event_id", "new_scope", "push_scope", + "remove_attribute", + "set_attribute", "set_context", "set_extra", "set_level", @@ -287,6 +289,28 @@ def push_scope( # noqa: F811 return _ScopeManager() +@scopemethod +def set_attribute(key: str, value: "Any") -> None: + """ + Set an attribute. + + Any attributes-based telemetry (logs, metrics) captured in this scope will + include this attribute. + """ + return get_isolation_scope().set_attribute(key, value) + + +@scopemethod +def remove_attribute(key: str) -> None: + """ + Remove an attribute. + + If the attribute doesn't exist, this function will not have any effect and + it will also not raise an exception. + """ + return get_isolation_scope().remove_attribute(key) + + @scopemethod def set_tag(key: str, value: "Any") -> None: return get_isolation_scope().set_tag(key, value) From 894391e84d752a3cfe197ff9c479e650250bc95b Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Fri, 27 Feb 2026 09:51:15 +0100 Subject: [PATCH 2/6] test --- tests/test_attributes.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_attributes.py b/tests/test_attributes.py index 40b31fa7f1..e4acf8e87f 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -3,6 +3,27 @@ from tests.test_metrics import envelopes_to_metrics +def test_top_level_api(sentry_init, capture_envelopes): + sentry_init() + + envelopes = capture_envelopes() + + sentry_sdk.set_attribute("set", "value") + sentry_sdk.set_attribute("remove", "value") + sentry_sdk.remove_attribute("removed", "value") + # Attempting to remove a nonexistent attribute should not raise + sentry_sdk.remove_attribute("nonexistent", "value") + + sentry_sdk.metrics.count("test", 1) + sentry_sdk.get_client().flush() + + metrics = envelopes_to_metrics(envelopes) + (metric,) = metrics + + assert metric["attributes"]["set"] == "value" + assert "remove" not in metric["attributes"] + + def test_scope_precedence(sentry_init, capture_envelopes): # Order of precedence, from most important to least: # 1. telemetry attributes (directly supplying attributes on creation or using set_attribute) From dc6d7208434071583f58ae7bc53936564111924e Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Fri, 27 Feb 2026 09:58:16 +0100 Subject: [PATCH 3/6] fix --- tests/test_attributes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_attributes.py b/tests/test_attributes.py index e4acf8e87f..3d772d856a 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -10,9 +10,9 @@ def test_top_level_api(sentry_init, capture_envelopes): sentry_sdk.set_attribute("set", "value") sentry_sdk.set_attribute("remove", "value") - sentry_sdk.remove_attribute("removed", "value") + sentry_sdk.remove_attribute("removed") # Attempting to remove a nonexistent attribute should not raise - sentry_sdk.remove_attribute("nonexistent", "value") + sentry_sdk.remove_attribute("nonexistent") sentry_sdk.metrics.count("test", 1) sentry_sdk.get_client().flush() From 354094cd27a37a82249d14b9655f87e4b57c0bc5 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Fri, 27 Feb 2026 10:02:54 +0100 Subject: [PATCH 4/6] . --- sentry_sdk/__init__.py | 2 ++ tests/test_attributes.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/__init__.py b/sentry_sdk/__init__.py index e149418c38..fda2f18dd1 100644 --- a/sentry_sdk/__init__.py +++ b/sentry_sdk/__init__.py @@ -37,6 +37,8 @@ "last_event_id", "new_scope", "push_scope", + "remove_attribute", + "set_attribute", "set_context", "set_extra", "set_level", diff --git a/tests/test_attributes.py b/tests/test_attributes.py index 3d772d856a..e00e520786 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -21,7 +21,7 @@ def test_top_level_api(sentry_init, capture_envelopes): (metric,) = metrics assert metric["attributes"]["set"] == "value" - assert "remove" not in metric["attributes"] + assert "removed" not in metric["attributes"] def test_scope_precedence(sentry_init, capture_envelopes): From 125e1611e6a4f5b9af07f469a9cf841371e245f2 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Fri, 27 Feb 2026 10:14:33 +0100 Subject: [PATCH 5/6] . --- tests/test_attributes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_attributes.py b/tests/test_attributes.py index e00e520786..cac0a520e7 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -9,7 +9,7 @@ def test_top_level_api(sentry_init, capture_envelopes): envelopes = capture_envelopes() sentry_sdk.set_attribute("set", "value") - sentry_sdk.set_attribute("remove", "value") + sentry_sdk.set_attribute("removed", "value") sentry_sdk.remove_attribute("removed") # Attempting to remove a nonexistent attribute should not raise sentry_sdk.remove_attribute("nonexistent") From 1d28ac05c5f5674a51e50494c5ec2f632cbadd7d Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Fri, 27 Feb 2026 10:28:09 +0100 Subject: [PATCH 6/6] make arg names consistent --- sentry_sdk/api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 05e650311f..bea22d8be7 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -290,25 +290,25 @@ def push_scope( # noqa: F811 @scopemethod -def set_attribute(key: str, value: "Any") -> None: +def set_attribute(attribute: str, value: "Any") -> None: """ Set an attribute. Any attributes-based telemetry (logs, metrics) captured in this scope will include this attribute. """ - return get_isolation_scope().set_attribute(key, value) + return get_isolation_scope().set_attribute(attribute, value) @scopemethod -def remove_attribute(key: str) -> None: +def remove_attribute(attribute: str) -> None: """ Remove an attribute. If the attribute doesn't exist, this function will not have any effect and it will also not raise an exception. """ - return get_isolation_scope().remove_attribute(key) + return get_isolation_scope().remove_attribute(attribute) @scopemethod