Skip to content

Commit e9ac9a3

Browse files
tconley1428claude
andauthored
Add __getattr__ to _ReplaySafeSpan to delegate SDK-specific attributes (#1392)
Instrumentation libraries like opentelemetry-instrumentation-asgi access attributes beyond the Span ABC (e.g. .name, .kind, .resource, .attributes) directly on spans. _ReplaySafeSpan now forwards these via __getattr__ to the wrapped span. Also removes dead self._status assignment in set_status. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 74001f2 commit e9ac9a3

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

temporalio/contrib/opentelemetry/_tracer_provider.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ def __init__(self, span: Span):
3333
self._exception: BaseException | None = None
3434
self._span = span
3535

36+
def __getattr__(self, name: str) -> object:
37+
return getattr(self._span, name)
38+
3639
def end(self, end_time: int | None = None) -> None:
3740
if workflow.in_workflow() and workflow.unsafe.is_replaying_history_events():
3841
# Skip ending spans during workflow replay to avoid duplicate telemetry
@@ -75,7 +78,6 @@ def is_recording(self) -> bool:
7578
def set_status(
7679
self, status: Status | StatusCode, description: str | None = None
7780
) -> None:
78-
self._status = status
7981
self._span.set_status(status, description)
8082

8183
def record_exception(

tests/contrib/opentelemetry/test_opentelemetry_plugin.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,3 +576,34 @@ async def test_otel_tracing_workflow_failure(
576576
assert (
577577
actual_hierarchy == expected_hierarchy
578578
), f"Span hierarchy mismatch.\nExpected:\n{expected_hierarchy}\nActual:\n{actual_hierarchy}"
579+
580+
581+
def test_replay_safe_span_delegates_extra_attributes():
582+
"""Test that _ReplaySafeSpan delegates attribute access to the underlying span.
583+
584+
Concrete span implementations (e.g. opentelemetry.sdk.trace.Span) expose
585+
attributes beyond the Span ABC such as .attributes, .name, .kind, and
586+
.resource. _ReplaySafeSpan must forward these so that instrumentation
587+
libraries that rely on them work correctly.
588+
"""
589+
from opentelemetry.sdk.trace import TracerProvider as SdkTracerProvider
590+
591+
from temporalio.contrib.opentelemetry._tracer_provider import _ReplaySafeSpan
592+
593+
provider = SdkTracerProvider()
594+
tracer = provider.get_tracer("test")
595+
inner_span = tracer.start_span("test-span")
596+
597+
wrapper = _ReplaySafeSpan(inner_span)
598+
599+
# These properties exist on the SDK span but not on the Span ABC
600+
assert wrapper.name == "test-span"
601+
assert wrapper.kind is not None
602+
assert wrapper.resource is not None
603+
assert wrapper.attributes is not None or wrapper.attributes == {}
604+
605+
# Verify that AttributeError is still raised for truly missing attributes
606+
with pytest.raises(AttributeError):
607+
_ = wrapper.nonexistent_attribute_xyz
608+
609+
inner_span.end()

0 commit comments

Comments
 (0)