From a1d43e8d4851cd5ba394c4466232542e50fbcdf8 Mon Sep 17 00:00:00 2001 From: nikhilmantri0902 Date: Thu, 2 Oct 2025 13:24:57 +0530 Subject: [PATCH 01/11] chore: added full trace flags implementation and tests --- .../_internal/trace_encoder/__init__.py | 16 ++++-- .../tests/test_trace_encoder.py | 55 ++++++++++++++++++- .../tests/test_otlp_trace_exporter.py | 10 ++-- 3 files changed, 68 insertions(+), 13 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py index 388d229bab6..3d54a83ac56 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py @@ -105,10 +105,14 @@ def _encode_resource_spans( return pb2_resource_spans -def _span_flags(parent_span_context: Optional[SpanContext]) -> int: - flags = PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK - if parent_span_context and parent_span_context.is_remote: - flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK +def _span_flags(child_trace_flags: int, parent_span_context: Optional[SpanContext]) -> int: + # Lower 8 bits: W3C TraceFlags + flags = int(child_trace_flags) & int(PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) + # Always indicate whether parent remote information is known + flags |= int(PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) + # Set remote bit when applicable + if parent_span_context and getattr(parent_span_context, "is_remote", False): + flags |= int(PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) return flags @@ -130,7 +134,7 @@ def _encode_span(sdk_span: ReadableSpan) -> PB2SPan: dropped_attributes_count=sdk_span.dropped_attributes, dropped_events_count=sdk_span.dropped_events, dropped_links_count=sdk_span.dropped_links, - flags=_span_flags(sdk_span.parent), + flags=_span_flags(getattr(span_context, "trace_flags", 0), sdk_span.parent), ) @@ -161,7 +165,7 @@ def _encode_links(links: Sequence[Link]) -> Sequence[PB2SPan.Link]: span_id=_encode_span_id(link.context.span_id), attributes=_encode_attributes(link.attributes), dropped_attributes_count=link.dropped_attributes, - flags=_span_flags(link.context), + flags=_span_flags(getattr(link.context, "trace_flags", 0), link.context), ) pb2_links.append(encoded_link) return pb2_links diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py index bf78526d7e4..7c145615675 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py @@ -24,6 +24,8 @@ from opentelemetry.exporter.otlp.proto.common._internal.trace_encoder import ( _SPAN_KIND_MAP, _encode_status, + _encode_span, + _encode_links, ) from opentelemetry.exporter.otlp.proto.common.trace_encoder import encode_spans from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ( @@ -43,6 +45,7 @@ from opentelemetry.proto.trace.v1.trace_pb2 import ScopeSpans as PB2ScopeSpans from opentelemetry.proto.trace.v1.trace_pb2 import Span as PB2SPan from opentelemetry.proto.trace.v1.trace_pb2 import Status as PB2Status +from opentelemetry.proto.trace.v1.trace_pb2 import SpanFlags as PB2SpanFlags from opentelemetry.sdk.trace import Event as SDKEvent from opentelemetry.sdk.trace import Resource as SDKResource from opentelemetry.sdk.trace import SpanContext as SDKSpanContext @@ -291,14 +294,14 @@ def get_exhaustive_test_spans( ), ), ], - flags=0x100, + flags=0x101, ) ], status=PB2Status( code=SDKStatusCode.ERROR.value, message="Example description", ), - flags=0x300, + flags=0x301, ) ], ), @@ -501,3 +504,51 @@ def test_encode_status_code_translations(self): code=SDKStatusCode.ERROR.value, ), ) + + +class TestSpanFlagsEncoding(unittest.TestCase): + def test_span_flags_root_unsampled(self): + sc = SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x00) + span = SDKSpan(name="root", context=sc, parent=None) + span.start(); span.end() + pb = _encode_span(span) + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x00 + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 + assert (pb.flags & ~0x3FF) == 0 + + def test_span_flags_root_sampled(self): + sc = SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x01) + span = SDKSpan(name="root", context=sc, parent=None) + span.start(); span.end() + pb = _encode_span(span) + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 + assert (pb.flags & ~0x3FF) == 0 + + def test_span_flags_remote_parent_sampled(self): + parent = SDKSpanContext(0x1, 0x9, is_remote=True) + sc = SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x01) + span = SDKSpan(name="child", context=sc, parent=parent) + span.start(); span.end() + pb = _encode_span(span) + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0 + assert (pb.flags & ~0x3FF) == 0 + + def test_link_flags_local_and_remote(self): + # local sampled link + l1 = SDKLink(SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x01)) + # remote sampled link + l2 = SDKLink(SDKSpanContext(0x1, 0x3, is_remote=True, trace_flags=0x01)) + pb_links = _encode_links([l1, l2]) + assert (pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 + assert (pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 + assert (pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 + assert (pb_links[0].flags & ~0x3FF) == 0 + assert (pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 + assert (pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 + assert (pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0 + assert (pb_links[1].flags & ~0x3FF) == 0 diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py index cbe6298df77..bcf8404f171 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py @@ -466,7 +466,7 @@ def test_translate_spans(self): ), ), ], - flags=0x300, + flags=0x300, # updated below in more focused tests ) ], flags=0x300, @@ -570,10 +570,10 @@ def test_translate_spans_multi(self): ), ), ], - flags=0x300, + flags=0x301, ) ], - flags=0x300, + flags=0x301, ) ], ), @@ -603,7 +603,7 @@ def test_translate_spans_multi(self): OTLPSpan.SpanKind.SPAN_KIND_INTERNAL ), status=Status(code=0, message=""), - flags=0x300, + flags=0x301, ) ], ), @@ -645,7 +645,7 @@ def test_translate_spans_multi(self): OTLPSpan.SpanKind.SPAN_KIND_INTERNAL ), status=Status(code=0, message=""), - flags=0x300, + flags=0x301, ) ], ) From acc6f2e39cdc55738acf0e5acd6fdd4f80ced97a Mon Sep 17 00:00:00 2001 From: nikhilmantri0902 Date: Thu, 2 Oct 2025 13:30:32 +0530 Subject: [PATCH 02/11] chore: added changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90fcb56cb7e..b1961da8efb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#4755](https://github.com/open-telemetry/opentelemetry-python/pull/4755)) - logs: extend Logger.emit to accept separated keyword arguments ([#4737](https://github.com/open-telemetry/opentelemetry-python/pull/4737)) +- otlp exporters (trace): include W3C TraceFlags (bits 0–7) in OTLP `Span.flags` alongside parent isRemote bits (8–9) + ([#4761](https://github.com/open-telemetry/opentelemetry-python/pull/4761)) ## Version 1.37.0/0.58b0 (2025-09-11) From 27a12dcd9c1936c3d23f8aa96c05d7b7ea49016f Mon Sep 17 00:00:00 2001 From: nikhilmantri0902 Date: Fri, 3 Oct 2025 23:37:23 +0530 Subject: [PATCH 03/11] chore: removed getattr method and unnecessay int typecasts --- .../proto/common/_internal/trace_encoder/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py index 3d54a83ac56..9c5cbfe3a2b 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py @@ -107,12 +107,12 @@ def _encode_resource_spans( def _span_flags(child_trace_flags: int, parent_span_context: Optional[SpanContext]) -> int: # Lower 8 bits: W3C TraceFlags - flags = int(child_trace_flags) & int(PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) + flags = child_trace_flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK # Always indicate whether parent remote information is known - flags |= int(PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) + flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK # Set remote bit when applicable - if parent_span_context and getattr(parent_span_context, "is_remote", False): - flags |= int(PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) + if parent_span_context and parent_span_context.is_remote: + flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK return flags @@ -134,7 +134,7 @@ def _encode_span(sdk_span: ReadableSpan) -> PB2SPan: dropped_attributes_count=sdk_span.dropped_attributes, dropped_events_count=sdk_span.dropped_events, dropped_links_count=sdk_span.dropped_links, - flags=_span_flags(getattr(span_context, "trace_flags", 0), sdk_span.parent), + flags=_span_flags(span_context.trace_flags, sdk_span.parent), ) @@ -165,7 +165,7 @@ def _encode_links(links: Sequence[Link]) -> Sequence[PB2SPan.Link]: span_id=_encode_span_id(link.context.span_id), attributes=_encode_attributes(link.attributes), dropped_attributes_count=link.dropped_attributes, - flags=_span_flags(getattr(link.context, "trace_flags", 0), link.context), + flags=_span_flags(link.context.trace_flags, link.context), ) pb2_links.append(encoded_link) return pb2_links From 858e16bf2b6d992702771cd34e3bd2234d7f71d4 Mon Sep 17 00:00:00 2001 From: nikhilmantri0902 Date: Sat, 11 Oct 2025 14:50:09 +0530 Subject: [PATCH 04/11] chore: minor updates as per review --- .../tests/test_trace_encoder.py | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py index 7c145615675..90814e55915 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py @@ -46,6 +46,13 @@ from opentelemetry.proto.trace.v1.trace_pb2 import Span as PB2SPan from opentelemetry.proto.trace.v1.trace_pb2 import Status as PB2Status from opentelemetry.proto.trace.v1.trace_pb2 import SpanFlags as PB2SpanFlags + +# Mask for all currently-defined span flag bits (0-9): lower 8 trace flags + has/is remote bits +ALL_SPAN_FLAGS_MASK = ( + PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + | PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + | PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK +) from opentelemetry.sdk.trace import Event as SDKEvent from opentelemetry.sdk.trace import Resource as SDKResource from opentelemetry.sdk.trace import SpanContext as SDKSpanContext @@ -508,35 +515,32 @@ def test_encode_status_code_translations(self): class TestSpanFlagsEncoding(unittest.TestCase): def test_span_flags_root_unsampled(self): - sc = SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x00) - span = SDKSpan(name="root", context=sc, parent=None) - span.start(); span.end() + span_context = SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x00) + span = SDKSpan(name="root", context=span_context, parent=None) pb = _encode_span(span) assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x00 assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 - assert (pb.flags & ~0x3FF) == 0 + assert (pb.flags & ~ALL_SPAN_FLAGS_MASK) == 0 def test_span_flags_root_sampled(self): - sc = SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x01) - span = SDKSpan(name="root", context=sc, parent=None) - span.start(); span.end() + span_context = SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x01) + span = SDKSpan(name="root", context=span_context, parent=None) pb = _encode_span(span) assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 - assert (pb.flags & ~0x3FF) == 0 + assert (pb.flags & ~ALL_SPAN_FLAGS_MASK) == 0 def test_span_flags_remote_parent_sampled(self): parent = SDKSpanContext(0x1, 0x9, is_remote=True) - sc = SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x01) - span = SDKSpan(name="child", context=sc, parent=parent) - span.start(); span.end() + span_context = SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x01) + span = SDKSpan(name="child", context=span_context, parent=parent) pb = _encode_span(span) assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0 - assert (pb.flags & ~0x3FF) == 0 + assert (pb.flags & ~ALL_SPAN_FLAGS_MASK) == 0 def test_link_flags_local_and_remote(self): # local sampled link @@ -547,8 +551,8 @@ def test_link_flags_local_and_remote(self): assert (pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 assert (pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 assert (pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 - assert (pb_links[0].flags & ~0x3FF) == 0 + assert (pb_links[0].flags & ~ALL_SPAN_FLAGS_MASK) == 0 assert (pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 assert (pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 assert (pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0 - assert (pb_links[1].flags & ~0x3FF) == 0 + assert (pb_links[1].flags & ~ALL_SPAN_FLAGS_MASK) == 0 From 142ba3a67218e63dd2da2117e2076128e0fb9fd1 Mon Sep 17 00:00:00 2001 From: nikhilmantri0902 Date: Wed, 15 Oct 2025 14:12:56 +0530 Subject: [PATCH 05/11] fix: tox lint errors --- .../_internal/trace_encoder/__init__.py | 4 +- .../tests/test_trace_encoder.py | 78 +++++++++++++------ 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py index 9c5cbfe3a2b..4a0984f2ab0 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py @@ -105,7 +105,9 @@ def _encode_resource_spans( return pb2_resource_spans -def _span_flags(child_trace_flags: int, parent_span_context: Optional[SpanContext]) -> int: +def _span_flags( + child_trace_flags: int, parent_span_context: Optional[SpanContext] +) -> int: # Lower 8 bits: W3C TraceFlags flags = child_trace_flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK # Always indicate whether parent remote information is known diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py index 90814e55915..bbbdfa65cf5 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py @@ -23,9 +23,9 @@ ) from opentelemetry.exporter.otlp.proto.common._internal.trace_encoder import ( _SPAN_KIND_MAP, - _encode_status, - _encode_span, _encode_links, + _encode_span, + _encode_status, ) from opentelemetry.exporter.otlp.proto.common.trace_encoder import encode_spans from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ( @@ -44,15 +44,8 @@ ) from opentelemetry.proto.trace.v1.trace_pb2 import ScopeSpans as PB2ScopeSpans from opentelemetry.proto.trace.v1.trace_pb2 import Span as PB2SPan -from opentelemetry.proto.trace.v1.trace_pb2 import Status as PB2Status from opentelemetry.proto.trace.v1.trace_pb2 import SpanFlags as PB2SpanFlags - -# Mask for all currently-defined span flag bits (0-9): lower 8 trace flags + has/is remote bits -ALL_SPAN_FLAGS_MASK = ( - PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK - | PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK - | PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK -) +from opentelemetry.proto.trace.v1.trace_pb2 import Status as PB2Status from opentelemetry.sdk.trace import Event as SDKEvent from opentelemetry.sdk.trace import Resource as SDKResource from opentelemetry.sdk.trace import SpanContext as SDKSpanContext @@ -66,6 +59,13 @@ from opentelemetry.trace.status import Status as SDKStatus from opentelemetry.trace.status import StatusCode as SDKStatusCode +# Mask for all currently-defined span flag bits (0-9): lower 8 trace flags + has/is remote bits +ALL_SPAN_FLAGS_MASK = ( + PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + | PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + | PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK +) + class TestOTLPTraceEncoder(unittest.TestCase): def test_encode_spans(self): @@ -515,44 +515,74 @@ def test_encode_status_code_translations(self): class TestSpanFlagsEncoding(unittest.TestCase): def test_span_flags_root_unsampled(self): - span_context = SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x00) + span_context = SDKSpanContext( + 0x1, 0x2, is_remote=False, trace_flags=0x00 + ) span = SDKSpan(name="root", context=span_context, parent=None) pb = _encode_span(span) assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x00 - assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 + assert ( + pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + ) != 0 assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 assert (pb.flags & ~ALL_SPAN_FLAGS_MASK) == 0 def test_span_flags_root_sampled(self): - span_context = SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x01) + span_context = SDKSpanContext( + 0x1, 0x2, is_remote=False, trace_flags=0x01 + ) span = SDKSpan(name="root", context=span_context, parent=None) pb = _encode_span(span) assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 - assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 + assert ( + pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + ) != 0 assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 assert (pb.flags & ~ALL_SPAN_FLAGS_MASK) == 0 def test_span_flags_remote_parent_sampled(self): parent = SDKSpanContext(0x1, 0x9, is_remote=True) - span_context = SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x01) + span_context = SDKSpanContext( + 0x1, 0x2, is_remote=False, trace_flags=0x01 + ) span = SDKSpan(name="child", context=span_context, parent=parent) pb = _encode_span(span) assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 - assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 + assert ( + pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + ) != 0 assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0 assert (pb.flags & ~ALL_SPAN_FLAGS_MASK) == 0 def test_link_flags_local_and_remote(self): # local sampled link - l1 = SDKLink(SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x01)) + l1 = SDKLink( + SDKSpanContext(0x1, 0x2, is_remote=False, trace_flags=0x01) + ) # remote sampled link - l2 = SDKLink(SDKSpanContext(0x1, 0x3, is_remote=True, trace_flags=0x01)) + l2 = SDKLink( + SDKSpanContext(0x1, 0x3, is_remote=True, trace_flags=0x01) + ) pb_links = _encode_links([l1, l2]) - assert (pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 - assert (pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 - assert (pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 + assert ( + pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + ) == 0x01 + assert ( + pb_links[0].flags + & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + ) != 0 + assert ( + pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK + ) == 0 assert (pb_links[0].flags & ~ALL_SPAN_FLAGS_MASK) == 0 - assert (pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 - assert (pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0 - assert (pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0 + assert ( + pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + ) == 0x01 + assert ( + pb_links[1].flags + & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + ) != 0 + assert ( + pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK + ) != 0 assert (pb_links[1].flags & ~ALL_SPAN_FLAGS_MASK) == 0 From 37312bf43464b0ddaa11a5580483520c5efef245 Mon Sep 17 00:00:00 2001 From: nikhilmantri0902 Date: Wed, 15 Oct 2025 14:31:01 +0530 Subject: [PATCH 06/11] fix: pylint error --- .../tests/test_trace_encoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py index bbbdfa65cf5..4ad722a5d02 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py @@ -60,7 +60,7 @@ from opentelemetry.trace.status import StatusCode as SDKStatusCode # Mask for all currently-defined span flag bits (0-9): lower 8 trace flags + has/is remote bits -ALL_SPAN_FLAGS_MASK = ( +ALL_SPAN_FLAGS_MASK = ( # pylint: disable=no-member PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK | PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK | PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK From 55e7a3addee273b4d429a6a2be4afde081184849 Mon Sep 17 00:00:00 2001 From: nikhilmantri0902 Date: Sun, 19 Oct 2025 14:46:00 +0530 Subject: [PATCH 07/11] fix: added fixes for the failing test cases --- .../_internal/trace_encoder/__init__.py | 20 +++++++++++++++++-- .../tests/test_trace_encoder.py | 2 +- .../tests/test_otlp_trace_exporter.py | 8 ++++---- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py index 4a0984f2ab0..de9ac214f2b 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py @@ -109,7 +109,12 @@ def _span_flags( child_trace_flags: int, parent_span_context: Optional[SpanContext] ) -> int: # Lower 8 bits: W3C TraceFlags - flags = child_trace_flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + # Handle TraceFlags objects, regular ints, and test mocks + try: + flags = int(child_trace_flags) & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + except (TypeError, ValueError): + # If conversion fails (e.g., Mock object), default to 0 + flags = 0 # Always indicate whether parent remote information is known flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK # Set remote bit when applicable @@ -162,12 +167,23 @@ def _encode_links(links: Sequence[Link]) -> Sequence[PB2SPan.Link]: if links: pb2_links = [] for link in links: + # For links, encode trace_flags and is_remote from the link's context + # Handle TraceFlags objects, regular ints, and test mocks + try: + flags = int(link.context.trace_flags) & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + except (TypeError, ValueError): + # If conversion fails (e.g., Mock object), default to 0 + flags = 0 + flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + if link.context.is_remote: + flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK + encoded_link = PB2SPan.Link( trace_id=_encode_trace_id(link.context.trace_id), span_id=_encode_span_id(link.context.span_id), attributes=_encode_attributes(link.attributes), dropped_attributes_count=link.dropped_attributes, - flags=_span_flags(link.context.trace_flags, link.context), + flags=flags, ) pb2_links.append(encoded_link) return pb2_links diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py index 4ad722a5d02..040014f1716 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py @@ -301,7 +301,7 @@ def get_exhaustive_test_spans( ), ), ], - flags=0x101, + flags=0x100, ) ], status=PB2Status( diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py index bcf8404f171..4a50938e28c 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py @@ -570,10 +570,10 @@ def test_translate_spans_multi(self): ), ), ], - flags=0x301, + flags=0x300, ) ], - flags=0x301, + flags=0x300, ) ], ), @@ -603,7 +603,7 @@ def test_translate_spans_multi(self): OTLPSpan.SpanKind.SPAN_KIND_INTERNAL ), status=Status(code=0, message=""), - flags=0x301, + flags=0x300, ) ], ), @@ -645,7 +645,7 @@ def test_translate_spans_multi(self): OTLPSpan.SpanKind.SPAN_KIND_INTERNAL ), status=Status(code=0, message=""), - flags=0x301, + flags=0x300, ) ], ) From fb5aec88854f0fe8bc0b609441355a430614136f Mon Sep 17 00:00:00 2001 From: nikhilmantri0902 Date: Wed, 22 Oct 2025 23:39:54 +0530 Subject: [PATCH 08/11] fix: precommit formatting --- .../proto/common/_internal/trace_encoder/__init__.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py index de9ac214f2b..1482d1f6a4e 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py @@ -111,7 +111,9 @@ def _span_flags( # Lower 8 bits: W3C TraceFlags # Handle TraceFlags objects, regular ints, and test mocks try: - flags = int(child_trace_flags) & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + flags = ( + int(child_trace_flags) & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + ) except (TypeError, ValueError): # If conversion fails (e.g., Mock object), default to 0 flags = 0 @@ -170,14 +172,17 @@ def _encode_links(links: Sequence[Link]) -> Sequence[PB2SPan.Link]: # For links, encode trace_flags and is_remote from the link's context # Handle TraceFlags objects, regular ints, and test mocks try: - flags = int(link.context.trace_flags) & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + flags = ( + int(link.context.trace_flags) + & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + ) except (TypeError, ValueError): # If conversion fails (e.g., Mock object), default to 0 flags = 0 flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK if link.context.is_remote: flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK - + encoded_link = PB2SPan.Link( trace_id=_encode_trace_id(link.context.trace_id), span_id=_encode_span_id(link.context.span_id), From 3be6a7b2212eff893df7407a391853f27376de7f Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Thu, 30 Oct 2025 11:53:41 +0100 Subject: [PATCH 09/11] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9b4d9bb6ed..03ee9f9d2a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#4734](https://github.com/open-telemetry/opentelemetry-python/pull/4734)) - build: bump ruff to 0.14.1 ([#4782](https://github.com/open-telemetry/opentelemetry-python/pull/4782)) +- otlp exporters (trace): include W3C TraceFlags (bits 0–7) in OTLP `Span.flags` alongside parent isRemote bits (8–9) + ([#4761](https://github.com/open-telemetry/opentelemetry-python/pull/4761)) ## Version 1.38.0/0.59b0 (2025-10-16) @@ -23,8 +25,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#4755](https://github.com/open-telemetry/opentelemetry-python/pull/4755)) - logs: extend Logger.emit to accept separated keyword arguments ([#4737](https://github.com/open-telemetry/opentelemetry-python/pull/4737)) -- otlp exporters (trace): include W3C TraceFlags (bits 0–7) in OTLP `Span.flags` alongside parent isRemote bits (8–9) - ([#4761](https://github.com/open-telemetry/opentelemetry-python/pull/4761)) - logs: add warnings for classes that would be deprecated and renamed in 1.39.0 ([#4771](https://github.com/open-telemetry/opentelemetry-python/pull/4771)) From c7a1222f171f895adf0af34d75286ddde6f4784e Mon Sep 17 00:00:00 2001 From: nikhilmantri0902 Date: Sun, 2 Nov 2025 14:45:38 +0530 Subject: [PATCH 10/11] fix: PR comments --- .../_internal/trace_encoder/__init__.py | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py index 1482d1f6a4e..644a0ed0d55 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py @@ -109,13 +109,11 @@ def _span_flags( child_trace_flags: int, parent_span_context: Optional[SpanContext] ) -> int: # Lower 8 bits: W3C TraceFlags - # Handle TraceFlags objects, regular ints, and test mocks + # TraceFlags is an int subclass, but we handle Mock objects in tests try: - flags = ( - int(child_trace_flags) & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK - ) - except (TypeError, ValueError): - # If conversion fails (e.g., Mock object), default to 0 + flags = child_trace_flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + except TypeError: + # If bitwise operation fails (e.g., Mock object in tests), default to 0 flags = 0 # Always indicate whether parent remote information is known flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK @@ -169,26 +167,14 @@ def _encode_links(links: Sequence[Link]) -> Sequence[PB2SPan.Link]: if links: pb2_links = [] for link in links: - # For links, encode trace_flags and is_remote from the link's context - # Handle TraceFlags objects, regular ints, and test mocks - try: - flags = ( - int(link.context.trace_flags) - & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK - ) - except (TypeError, ValueError): - # If conversion fails (e.g., Mock object), default to 0 - flags = 0 - flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK - if link.context.is_remote: - flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK - + # For links, we encode the link's own context (not treating it as parent-child) + # The link context's is_remote indicates if the linked span is from a remote process encoded_link = PB2SPan.Link( trace_id=_encode_trace_id(link.context.trace_id), span_id=_encode_span_id(link.context.span_id), attributes=_encode_attributes(link.attributes), dropped_attributes_count=link.dropped_attributes, - flags=flags, + flags=_span_flags(link.context.trace_flags, link.context), ) pb2_links.append(encoded_link) return pb2_links From 01afe453e3be27b1f3936a519a959d6795120b4a Mon Sep 17 00:00:00 2001 From: nikhilmantri0902 Date: Tue, 4 Nov 2025 00:09:48 +0530 Subject: [PATCH 11/11] fix: failing workflow --- .../tests/test_trace_encoder.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py index 040014f1716..43362f3ae6f 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py @@ -520,11 +520,11 @@ def test_span_flags_root_unsampled(self): ) span = SDKSpan(name="root", context=span_context, parent=None) pb = _encode_span(span) - assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x00 + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x00 # pylint: disable=no-member assert ( - pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK # pylint: disable=no-member ) != 0 - assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 # pylint: disable=no-member assert (pb.flags & ~ALL_SPAN_FLAGS_MASK) == 0 def test_span_flags_root_sampled(self): @@ -533,11 +533,11 @@ def test_span_flags_root_sampled(self): ) span = SDKSpan(name="root", context=span_context, parent=None) pb = _encode_span(span) - assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 # pylint: disable=no-member assert ( - pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK # pylint: disable=no-member ) != 0 - assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) == 0 # pylint: disable=no-member assert (pb.flags & ~ALL_SPAN_FLAGS_MASK) == 0 def test_span_flags_remote_parent_sampled(self): @@ -547,11 +547,11 @@ def test_span_flags_remote_parent_sampled(self): ) span = SDKSpan(name="child", context=span_context, parent=parent) pb = _encode_span(span) - assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK) == 0x01 # pylint: disable=no-member assert ( - pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK # pylint: disable=no-member ) != 0 - assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0 + assert (pb.flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0 # pylint: disable=no-member assert (pb.flags & ~ALL_SPAN_FLAGS_MASK) == 0 def test_link_flags_local_and_remote(self): @@ -565,24 +565,24 @@ def test_link_flags_local_and_remote(self): ) pb_links = _encode_links([l1, l2]) assert ( - pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK # pylint: disable=no-member ) == 0x01 assert ( pb_links[0].flags - & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK # pylint: disable=no-member ) != 0 assert ( - pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK + pb_links[0].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK # pylint: disable=no-member ) == 0 assert (pb_links[0].flags & ~ALL_SPAN_FLAGS_MASK) == 0 assert ( - pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK + pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_TRACE_FLAGS_MASK # pylint: disable=no-member ) == 0x01 assert ( pb_links[1].flags - & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + & PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK # pylint: disable=no-member ) != 0 assert ( - pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK + pb_links[1].flags & PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK # pylint: disable=no-member ) != 0 assert (pb_links[1].flags & ~ALL_SPAN_FLAGS_MASK) == 0