Repository: open-telemetry/opentelemetry-python

Issue:
is_valid is currently a method on the SpanContext, the spec defines it as a bool flag 
To be compliant with the tracing spec, we need a `is_valid` to be a boolean flag rather than a method on the `SpanContext`

https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#spancontext


Discussion:
In the linked document it is simply described as "[an API that returns a boolean value [...]](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#isvalid)". What would be the difference between a flag and a method here? Should it simply be defined as an attribute during instantiation, as is the case with `is_remote`?
@ffe4 right, the discrepancy between `is_remote` and `is_valid` is what I meant to address with this issue, as they're defined in the spec as the same thing, but they're implemented differently.

Resolving patch:
diff --git a/opentelemetry-api/src/opentelemetry/trace/span.py b/opentelemetry-api/src/opentelemetry/trace/span.py
--- a/opentelemetry-api/src/opentelemetry/trace/span.py
+++ b/opentelemetry-api/src/opentelemetry/trace/span.py
@@ -187,6 +187,10 @@ def __init__(
         self.trace_flags = trace_flags
         self.trace_state = trace_state
         self.is_remote = is_remote
+        self.is_valid = (
+            self.trace_id != INVALID_TRACE_ID
+            and self.span_id != INVALID_SPAN_ID
+        )
 
     def __repr__(self) -> str:
         return (
@@ -199,20 +203,6 @@ def __repr__(self) -> str:
             self.is_remote,
         )
 
-    def is_valid(self) -> bool:
-        """Get whether this `SpanContext` is valid.
-
-        A `SpanContext` is said to be invalid if its trace ID or span ID is
-        invalid (i.e. ``0``).
-
-        Returns:
-            True if the `SpanContext` is valid, false otherwise.
-        """
-        return (
-            self.trace_id != INVALID_TRACE_ID
-            and self.span_id != INVALID_SPAN_ID
-        )
-
 
 class DefaultSpan(Span):
     """The default Span that is used when no Span implementation is available.
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
@@ -774,7 +774,7 @@ def start_span(  # pylint: disable=too-many-locals
         ):
             raise TypeError("parent must be a Span, SpanContext or None.")
 
-        if parent_context is None or not parent_context.is_valid():
+        if parent_context is None or not parent_context.is_valid:
             parent = parent_context = None
             trace_id = generate_trace_id()
             trace_flags = None


Test patch:
diff --git a/opentelemetry-api/tests/trace/test_defaultspan.py b/opentelemetry-api/tests/trace/test_defaultspan.py
--- a/opentelemetry-api/tests/trace/test_defaultspan.py
+++ b/opentelemetry-api/tests/trace/test_defaultspan.py
@@ -32,4 +32,4 @@ def test_ctor(self):
     def test_invalid_span(self):
         self.assertIsNotNone(trace.INVALID_SPAN)
         self.assertIsNotNone(trace.INVALID_SPAN.get_context())
-        self.assertFalse(trace.INVALID_SPAN.get_context().is_valid())
+        self.assertFalse(trace.INVALID_SPAN.get_context().is_valid)
diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py
--- a/opentelemetry-sdk/tests/trace/test_trace.py
+++ b/opentelemetry-sdk/tests/trace/test_trace.py
@@ -165,7 +165,7 @@ def test_start_span_invalid_spancontext(self):
         new_span = tracer.start_span(
             "root", parent=trace_api.INVALID_SPAN_CONTEXT
         )
-        self.assertTrue(new_span.context.is_valid())
+        self.assertTrue(new_span.context.is_valid)
         self.assertIsNone(new_span.parent)
 
     def test_instrumentation_info(self):