about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/opentelemetry/trace
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/opentelemetry/trace')
-rw-r--r--.venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py646
-rw-r--r--.venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__init__.py51
-rw-r--r--.venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/tracecontext.py118
-rw-r--r--.venv/lib/python3.12/site-packages/opentelemetry/trace/py.typed0
-rw-r--r--.venv/lib/python3.12/site-packages/opentelemetry/trace/span.py608
-rw-r--r--.venv/lib/python3.12/site-packages/opentelemetry/trace/status.py82
6 files changed, 1505 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py b/.venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py
new file mode 100644
index 00000000..73087e95
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py
@@ -0,0 +1,646 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+The OpenTelemetry tracing API describes the classes used to generate
+distributed traces.
+
+The :class:`.Tracer` class controls access to the execution context, and
+manages span creation. Each operation in a trace is represented by a
+:class:`.Span`, which records the start, end time, and metadata associated with
+the operation.
+
+This module provides abstract (i.e. unimplemented) classes required for
+tracing, and a concrete no-op :class:`.NonRecordingSpan` that allows applications
+to use the API package alone without a supporting implementation.
+
+To get a tracer, you need to provide the package name from which you are
+calling the tracer APIs to OpenTelemetry by calling `TracerProvider.get_tracer`
+with the calling module name and the version of your package.
+
+The tracer supports creating spans that are "attached" or "detached" from the
+context. New spans are "attached" to the context in that they are
+created as children of the currently active span, and the newly-created span
+can optionally become the new active span::
+
+    from opentelemetry import trace
+
+    tracer = trace.get_tracer(__name__)
+
+    # Create a new root span, set it as the current span in context
+    with tracer.start_as_current_span("parent"):
+        # Attach a new child and update the current span
+        with tracer.start_as_current_span("child"):
+            do_work():
+        # Close child span, set parent as current
+    # Close parent span, set default span as current
+
+When creating a span that's "detached" from the context the active span doesn't
+change, and the caller is responsible for managing the span's lifetime::
+
+    # Explicit parent span assignment is done via the Context
+    from opentelemetry.trace import set_span_in_context
+
+    context = set_span_in_context(parent)
+    child = tracer.start_span("child", context=context)
+
+    try:
+        do_work(span=child)
+    finally:
+        child.end()
+
+Applications should generally use a single global TracerProvider, and use
+either implicit or explicit context propagation consistently throughout.
+
+.. versionadded:: 0.1.0
+.. versionchanged:: 0.3.0
+    `TracerProvider` was introduced and the global ``tracer`` getter was
+    replaced by ``tracer_provider``.
+.. versionchanged:: 0.5.0
+    ``tracer_provider`` was replaced by `get_tracer_provider`,
+    ``set_preferred_tracer_provider_implementation`` was replaced by
+    `set_tracer_provider`.
+"""
+
+import os
+import typing
+from abc import ABC, abstractmethod
+from enum import Enum
+from logging import getLogger
+from typing import Iterator, Optional, Sequence, cast
+
+from deprecated import deprecated
+
+from opentelemetry import context as context_api
+from opentelemetry.attributes import BoundedAttributes
+from opentelemetry.context.context import Context
+from opentelemetry.environment_variables import OTEL_PYTHON_TRACER_PROVIDER
+from opentelemetry.trace.propagation import (
+    _SPAN_KEY,
+    get_current_span,
+    set_span_in_context,
+)
+from opentelemetry.trace.span import (
+    DEFAULT_TRACE_OPTIONS,
+    DEFAULT_TRACE_STATE,
+    INVALID_SPAN,
+    INVALID_SPAN_CONTEXT,
+    INVALID_SPAN_ID,
+    INVALID_TRACE_ID,
+    NonRecordingSpan,
+    Span,
+    SpanContext,
+    TraceFlags,
+    TraceState,
+    format_span_id,
+    format_trace_id,
+)
+from opentelemetry.trace.status import Status, StatusCode
+from opentelemetry.util import types
+from opentelemetry.util._decorator import _agnosticcontextmanager
+from opentelemetry.util._once import Once
+from opentelemetry.util._providers import _load_provider
+
+logger = getLogger(__name__)
+
+
+class _LinkBase(ABC):
+    def __init__(self, context: "SpanContext") -> None:
+        self._context = context
+
+    @property
+    def context(self) -> "SpanContext":
+        return self._context
+
+    @property
+    @abstractmethod
+    def attributes(self) -> types.Attributes:
+        pass
+
+
+class Link(_LinkBase):
+    """A link to a `Span`. The attributes of a Link are immutable.
+
+    Args:
+        context: `SpanContext` of the `Span` to link to.
+        attributes: Link's attributes.
+    """
+
+    def __init__(
+        self,
+        context: "SpanContext",
+        attributes: types.Attributes = None,
+    ) -> None:
+        super().__init__(context)
+        self._attributes = attributes
+
+    @property
+    def attributes(self) -> types.Attributes:
+        return self._attributes
+
+    @property
+    def dropped_attributes(self) -> int:
+        if isinstance(self._attributes, BoundedAttributes):
+            return self._attributes.dropped
+        return 0
+
+
+_Links = Optional[Sequence[Link]]
+
+
+class SpanKind(Enum):
+    """Specifies additional details on how this span relates to its parent span.
+
+    Note that this enumeration is experimental and likely to change. See
+    https://github.com/open-telemetry/opentelemetry-specification/pull/226.
+    """
+
+    #: Default value. Indicates that the span is used internally in the
+    # application.
+    INTERNAL = 0
+
+    #: Indicates that the span describes an operation that handles a remote
+    # request.
+    SERVER = 1
+
+    #: Indicates that the span describes a request to some remote service.
+    CLIENT = 2
+
+    #: Indicates that the span describes a producer sending a message to a
+    #: broker. Unlike client and server, there is usually no direct critical
+    #: path latency relationship between producer and consumer spans.
+    PRODUCER = 3
+
+    #: Indicates that the span describes a consumer receiving a message from a
+    #: broker. Unlike client and server, there is usually no direct critical
+    #: path latency relationship between producer and consumer spans.
+    CONSUMER = 4
+
+
+class TracerProvider(ABC):
+    @abstractmethod
+    def get_tracer(
+        self,
+        instrumenting_module_name: str,
+        instrumenting_library_version: typing.Optional[str] = None,
+        schema_url: typing.Optional[str] = None,
+        attributes: typing.Optional[types.Attributes] = None,
+    ) -> "Tracer":
+        """Returns a `Tracer` for use by the given instrumentation library.
+
+        For any two calls it is undefined whether the same or different
+        `Tracer` instances are returned, even for different library names.
+
+        This function may return different `Tracer` types (e.g. a no-op tracer
+        vs.  a functional tracer).
+
+        Args:
+            instrumenting_module_name: The uniquely identifiable name for instrumentation
+                scope, such as instrumentation library, package, module or class name.
+                ``__name__`` may not be used as this can result in
+                different tracer names if the tracers are in different files.
+                It is better to use a fixed string that can be imported where
+                needed and used consistently as the name of the tracer.
+
+                This should *not* be the name of the module that is
+                instrumented but the name of the module doing the instrumentation.
+                E.g., instead of ``"requests"``, use
+                ``"opentelemetry.instrumentation.requests"``.
+
+            instrumenting_library_version: Optional. The version string of the
+                instrumenting library.  Usually this should be the same as
+                ``importlib.metadata.version(instrumenting_library_name)``.
+
+            schema_url: Optional. Specifies the Schema URL of the emitted telemetry.
+            attributes: Optional. Specifies the attributes of the emitted telemetry.
+        """
+
+
+class NoOpTracerProvider(TracerProvider):
+    """The default TracerProvider, used when no implementation is available.
+
+    All operations are no-op.
+    """
+
+    def get_tracer(
+        self,
+        instrumenting_module_name: str,
+        instrumenting_library_version: typing.Optional[str] = None,
+        schema_url: typing.Optional[str] = None,
+        attributes: typing.Optional[types.Attributes] = None,
+    ) -> "Tracer":
+        # pylint:disable=no-self-use,unused-argument
+        return NoOpTracer()
+
+
+@deprecated(version="1.9.0", reason="You should use NoOpTracerProvider")  # type: ignore
+class _DefaultTracerProvider(NoOpTracerProvider):
+    """The default TracerProvider, used when no implementation is available.
+
+    All operations are no-op.
+    """
+
+
+class ProxyTracerProvider(TracerProvider):
+    def get_tracer(
+        self,
+        instrumenting_module_name: str,
+        instrumenting_library_version: typing.Optional[str] = None,
+        schema_url: typing.Optional[str] = None,
+        attributes: typing.Optional[types.Attributes] = None,
+    ) -> "Tracer":
+        if _TRACER_PROVIDER:
+            return _TRACER_PROVIDER.get_tracer(
+                instrumenting_module_name,
+                instrumenting_library_version,
+                schema_url,
+                attributes,
+            )
+        return ProxyTracer(
+            instrumenting_module_name,
+            instrumenting_library_version,
+            schema_url,
+            attributes,
+        )
+
+
+class Tracer(ABC):
+    """Handles span creation and in-process context propagation.
+
+    This class provides methods for manipulating the context, creating spans,
+    and controlling spans' lifecycles.
+    """
+
+    @abstractmethod
+    def start_span(
+        self,
+        name: str,
+        context: Optional[Context] = None,
+        kind: SpanKind = SpanKind.INTERNAL,
+        attributes: types.Attributes = None,
+        links: _Links = None,
+        start_time: Optional[int] = None,
+        record_exception: bool = True,
+        set_status_on_exception: bool = True,
+    ) -> "Span":
+        """Starts a span.
+
+        Create a new span. Start the span without setting it as the current
+        span in the context. To start the span and use the context in a single
+        method, see :meth:`start_as_current_span`.
+
+        By default the current span in the context will be used as parent, but an
+        explicit context can also be specified, by passing in a `Context` containing
+        a current `Span`. If there is no current span in the global `Context` or in
+        the specified context, the created span will be a root span.
+
+        The span can be used as a context manager. On exiting the context manager,
+        the span's end() method will be called.
+
+        Example::
+
+            # trace.get_current_span() will be used as the implicit parent.
+            # If none is found, the created span will be a root instance.
+            with tracer.start_span("one") as child:
+                child.add_event("child's event")
+
+        Args:
+            name: The name of the span to be created.
+            context: An optional Context containing the span's parent. Defaults to the
+                global context.
+            kind: The span's kind (relationship to parent). Note that is
+                meaningful even if there is no parent.
+            attributes: The span's attributes.
+            links: Links span to other spans
+            start_time: Sets the start time of a span
+            record_exception: Whether to record any exceptions raised within the
+                context as error event on the span.
+            set_status_on_exception: Only relevant if the returned span is used
+                in a with/context manager. Defines whether the span status will
+                be automatically set to ERROR when an uncaught exception is
+                raised in the span with block. The span status won't be set by
+                this mechanism if it was previously set manually.
+
+        Returns:
+            The newly-created span.
+        """
+
+    @_agnosticcontextmanager
+    @abstractmethod
+    def start_as_current_span(
+        self,
+        name: str,
+        context: Optional[Context] = None,
+        kind: SpanKind = SpanKind.INTERNAL,
+        attributes: types.Attributes = None,
+        links: _Links = None,
+        start_time: Optional[int] = None,
+        record_exception: bool = True,
+        set_status_on_exception: bool = True,
+        end_on_exit: bool = True,
+    ) -> Iterator["Span"]:
+        """Context manager for creating a new span and set it
+        as the current span in this tracer's context.
+
+        Exiting the context manager will call the span's end method,
+        as well as return the current span to its previous value by
+        returning to the previous context.
+
+        Example::
+
+            with tracer.start_as_current_span("one") as parent:
+                parent.add_event("parent's event")
+                with tracer.start_as_current_span("two") as child:
+                    child.add_event("child's event")
+                    trace.get_current_span()  # returns child
+                trace.get_current_span()      # returns parent
+            trace.get_current_span()          # returns previously active span
+
+        This is a convenience method for creating spans attached to the
+        tracer's context. Applications that need more control over the span
+        lifetime should use :meth:`start_span` instead. For example::
+
+            with tracer.start_as_current_span(name) as span:
+                do_work()
+
+        is equivalent to::
+
+            span = tracer.start_span(name)
+            with opentelemetry.trace.use_span(span, end_on_exit=True):
+                do_work()
+
+        This can also be used as a decorator::
+
+            @tracer.start_as_current_span("name")
+            def function():
+                ...
+
+            function()
+
+        Args:
+            name: The name of the span to be created.
+            context: An optional Context containing the span's parent. Defaults to the
+                global context.
+            kind: The span's kind (relationship to parent). Note that is
+                meaningful even if there is no parent.
+            attributes: The span's attributes.
+            links: Links span to other spans
+            start_time: Sets the start time of a span
+            record_exception: Whether to record any exceptions raised within the
+                context as error event on the span.
+            set_status_on_exception: Only relevant if the returned span is used
+                in a with/context manager. Defines whether the span status will
+                be automatically set to ERROR when an uncaught exception is
+                raised in the span with block. The span status won't be set by
+                this mechanism if it was previously set manually.
+            end_on_exit: Whether to end the span automatically when leaving the
+                context manager.
+
+        Yields:
+            The newly-created span.
+        """
+
+
+class ProxyTracer(Tracer):
+    # pylint: disable=W0222,signature-differs
+    def __init__(
+        self,
+        instrumenting_module_name: str,
+        instrumenting_library_version: typing.Optional[str] = None,
+        schema_url: typing.Optional[str] = None,
+        attributes: typing.Optional[types.Attributes] = None,
+    ):
+        self._instrumenting_module_name = instrumenting_module_name
+        self._instrumenting_library_version = instrumenting_library_version
+        self._schema_url = schema_url
+        self._attributes = attributes
+        self._real_tracer: Optional[Tracer] = None
+        self._noop_tracer = NoOpTracer()
+
+    @property
+    def _tracer(self) -> Tracer:
+        if self._real_tracer:
+            return self._real_tracer
+
+        if _TRACER_PROVIDER:
+            self._real_tracer = _TRACER_PROVIDER.get_tracer(
+                self._instrumenting_module_name,
+                self._instrumenting_library_version,
+                self._schema_url,
+                self._attributes,
+            )
+            return self._real_tracer
+        return self._noop_tracer
+
+    def start_span(self, *args, **kwargs) -> Span:  # type: ignore
+        return self._tracer.start_span(*args, **kwargs)  # type: ignore
+
+    @_agnosticcontextmanager  # type: ignore
+    def start_as_current_span(self, *args, **kwargs) -> Iterator[Span]:
+        with self._tracer.start_as_current_span(*args, **kwargs) as span:  # type: ignore
+            yield span
+
+
+class NoOpTracer(Tracer):
+    """The default Tracer, used when no Tracer implementation is available.
+
+    All operations are no-op.
+    """
+
+    def start_span(
+        self,
+        name: str,
+        context: Optional[Context] = None,
+        kind: SpanKind = SpanKind.INTERNAL,
+        attributes: types.Attributes = None,
+        links: _Links = None,
+        start_time: Optional[int] = None,
+        record_exception: bool = True,
+        set_status_on_exception: bool = True,
+    ) -> "Span":
+        return INVALID_SPAN
+
+    @_agnosticcontextmanager
+    def start_as_current_span(
+        self,
+        name: str,
+        context: Optional[Context] = None,
+        kind: SpanKind = SpanKind.INTERNAL,
+        attributes: types.Attributes = None,
+        links: _Links = None,
+        start_time: Optional[int] = None,
+        record_exception: bool = True,
+        set_status_on_exception: bool = True,
+        end_on_exit: bool = True,
+    ) -> Iterator["Span"]:
+        yield INVALID_SPAN
+
+
+@deprecated(version="1.9.0", reason="You should use NoOpTracer")  # type: ignore
+class _DefaultTracer(NoOpTracer):
+    """The default Tracer, used when no Tracer implementation is available.
+
+    All operations are no-op.
+    """
+
+
+_TRACER_PROVIDER_SET_ONCE = Once()
+_TRACER_PROVIDER: Optional[TracerProvider] = None
+_PROXY_TRACER_PROVIDER = ProxyTracerProvider()
+
+
+def get_tracer(
+    instrumenting_module_name: str,
+    instrumenting_library_version: typing.Optional[str] = None,
+    tracer_provider: Optional[TracerProvider] = None,
+    schema_url: typing.Optional[str] = None,
+    attributes: typing.Optional[types.Attributes] = None,
+) -> "Tracer":
+    """Returns a `Tracer` for use by the given instrumentation library.
+
+    This function is a convenience wrapper for
+    opentelemetry.trace.TracerProvider.get_tracer.
+
+    If tracer_provider is omitted the current configured one is used.
+    """
+    if tracer_provider is None:
+        tracer_provider = get_tracer_provider()
+    return tracer_provider.get_tracer(
+        instrumenting_module_name,
+        instrumenting_library_version,
+        schema_url,
+        attributes,
+    )
+
+
+def _set_tracer_provider(tracer_provider: TracerProvider, log: bool) -> None:
+    def set_tp() -> None:
+        global _TRACER_PROVIDER  # pylint: disable=global-statement
+        _TRACER_PROVIDER = tracer_provider
+
+    did_set = _TRACER_PROVIDER_SET_ONCE.do_once(set_tp)
+
+    if log and not did_set:
+        logger.warning("Overriding of current TracerProvider is not allowed")
+
+
+def set_tracer_provider(tracer_provider: TracerProvider) -> None:
+    """Sets the current global :class:`~.TracerProvider` object.
+
+    This can only be done once, a warning will be logged if any further attempt
+    is made.
+    """
+    _set_tracer_provider(tracer_provider, log=True)
+
+
+def get_tracer_provider() -> TracerProvider:
+    """Gets the current global :class:`~.TracerProvider` object."""
+    if _TRACER_PROVIDER is None:
+        # if a global tracer provider has not been set either via code or env
+        # vars, return a proxy tracer provider
+        if OTEL_PYTHON_TRACER_PROVIDER not in os.environ:
+            return _PROXY_TRACER_PROVIDER
+
+        tracer_provider: TracerProvider = _load_provider(
+            OTEL_PYTHON_TRACER_PROVIDER, "tracer_provider"
+        )
+        _set_tracer_provider(tracer_provider, log=False)
+    # _TRACER_PROVIDER will have been set by one thread
+    return cast("TracerProvider", _TRACER_PROVIDER)
+
+
+@_agnosticcontextmanager
+def use_span(
+    span: Span,
+    end_on_exit: bool = False,
+    record_exception: bool = True,
+    set_status_on_exception: bool = True,
+) -> Iterator[Span]:
+    """Takes a non-active span and activates it in the current context.
+
+    Args:
+        span: The span that should be activated in the current context.
+        end_on_exit: Whether to end the span automatically when leaving the
+            context manager scope.
+        record_exception: Whether to record any exceptions raised within the
+            context as error event on the span.
+        set_status_on_exception: Only relevant if the returned span is used
+            in a with/context manager. Defines whether the span status will
+            be automatically set to ERROR when an uncaught exception is
+            raised in the span with block. The span status won't be set by
+            this mechanism if it was previously set manually.
+    """
+    try:
+        token = context_api.attach(context_api.set_value(_SPAN_KEY, span))
+        try:
+            yield span
+        finally:
+            context_api.detach(token)
+
+    # Record only exceptions that inherit Exception class but not BaseException, because
+    # classes that directly inherit BaseException are not technically errors, e.g. GeneratorExit.
+    # See https://github.com/open-telemetry/opentelemetry-python/issues/4484
+    except Exception as exc:  # pylint: disable=broad-exception-caught
+        if isinstance(span, Span) and span.is_recording():
+            # Record the exception as an event
+            if record_exception:
+                span.record_exception(exc)
+
+            # Set status in case exception was raised
+            if set_status_on_exception:
+                span.set_status(
+                    Status(
+                        status_code=StatusCode.ERROR,
+                        description=f"{type(exc).__name__}: {exc}",
+                    )
+                )
+
+        # This causes parent spans to set their status to ERROR and to record
+        # an exception as an event if a child span raises an exception even if
+        # such child span was started with both record_exception and
+        # set_status_on_exception attributes set to False.
+        raise
+
+    finally:
+        if end_on_exit:
+            span.end()
+
+
+__all__ = [
+    "DEFAULT_TRACE_OPTIONS",
+    "DEFAULT_TRACE_STATE",
+    "INVALID_SPAN",
+    "INVALID_SPAN_CONTEXT",
+    "INVALID_SPAN_ID",
+    "INVALID_TRACE_ID",
+    "NonRecordingSpan",
+    "Link",
+    "Span",
+    "SpanContext",
+    "SpanKind",
+    "TraceFlags",
+    "TraceState",
+    "TracerProvider",
+    "Tracer",
+    "format_span_id",
+    "format_trace_id",
+    "get_current_span",
+    "get_tracer",
+    "get_tracer_provider",
+    "set_tracer_provider",
+    "set_span_in_context",
+    "use_span",
+    "Status",
+    "StatusCode",
+]
diff --git a/.venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__init__.py b/.venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__init__.py
new file mode 100644
index 00000000..d3529e17
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__init__.py
@@ -0,0 +1,51 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import Optional
+
+from opentelemetry.context import create_key, get_value, set_value
+from opentelemetry.context.context import Context
+from opentelemetry.trace.span import INVALID_SPAN, Span
+
+SPAN_KEY = "current-span"
+_SPAN_KEY = create_key("current-span")
+
+
+def set_span_in_context(
+    span: Span, context: Optional[Context] = None
+) -> Context:
+    """Set the span in the given context.
+
+    Args:
+        span: The Span to set.
+        context: a Context object. if one is not passed, the
+            default current context is used instead.
+    """
+    ctx = set_value(_SPAN_KEY, span, context=context)
+    return ctx
+
+
+def get_current_span(context: Optional[Context] = None) -> Span:
+    """Retrieve the current span.
+
+    Args:
+        context: A Context object. If one is not passed, the
+            default current context is used instead.
+
+    Returns:
+        The Span set in the context if it exists. INVALID_SPAN otherwise.
+    """
+    span = get_value(_SPAN_KEY, context=context)
+    if span is None or not isinstance(span, Span):
+        return INVALID_SPAN
+    return span
diff --git a/.venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/tracecontext.py b/.venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/tracecontext.py
new file mode 100644
index 00000000..af16a08f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/tracecontext.py
@@ -0,0 +1,118 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import re
+import typing
+
+from opentelemetry import trace
+from opentelemetry.context.context import Context
+from opentelemetry.propagators import textmap
+from opentelemetry.trace import format_span_id, format_trace_id
+from opentelemetry.trace.span import TraceState
+
+
+class TraceContextTextMapPropagator(textmap.TextMapPropagator):
+    """Extracts and injects using w3c TraceContext's headers."""
+
+    _TRACEPARENT_HEADER_NAME = "traceparent"
+    _TRACESTATE_HEADER_NAME = "tracestate"
+    _TRACEPARENT_HEADER_FORMAT = (
+        "^[ \t]*([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})"
+        + "(-.*)?[ \t]*$"
+    )
+    _TRACEPARENT_HEADER_FORMAT_RE = re.compile(_TRACEPARENT_HEADER_FORMAT)
+
+    def extract(
+        self,
+        carrier: textmap.CarrierT,
+        context: typing.Optional[Context] = None,
+        getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter,
+    ) -> Context:
+        """Extracts SpanContext from the carrier.
+
+        See `opentelemetry.propagators.textmap.TextMapPropagator.extract`
+        """
+        if context is None:
+            context = Context()
+
+        header = getter.get(carrier, self._TRACEPARENT_HEADER_NAME)
+
+        if not header:
+            return context
+
+        match = re.search(self._TRACEPARENT_HEADER_FORMAT_RE, header[0])
+        if not match:
+            return context
+
+        version: str = match.group(1)
+        trace_id: str = match.group(2)
+        span_id: str = match.group(3)
+        trace_flags: str = match.group(4)
+
+        if trace_id == "0" * 32 or span_id == "0" * 16:
+            return context
+
+        if version == "00":
+            if match.group(5):  # type: ignore
+                return context
+        if version == "ff":
+            return context
+
+        tracestate_headers = getter.get(carrier, self._TRACESTATE_HEADER_NAME)
+        if tracestate_headers is None:
+            tracestate = None
+        else:
+            tracestate = TraceState.from_header(tracestate_headers)
+
+        span_context = trace.SpanContext(
+            trace_id=int(trace_id, 16),
+            span_id=int(span_id, 16),
+            is_remote=True,
+            trace_flags=trace.TraceFlags(int(trace_flags, 16)),
+            trace_state=tracestate,
+        )
+        return trace.set_span_in_context(
+            trace.NonRecordingSpan(span_context), context
+        )
+
+    def inject(
+        self,
+        carrier: textmap.CarrierT,
+        context: typing.Optional[Context] = None,
+        setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter,
+    ) -> None:
+        """Injects SpanContext into the carrier.
+
+        See `opentelemetry.propagators.textmap.TextMapPropagator.inject`
+        """
+        span = trace.get_current_span(context)
+        span_context = span.get_span_context()
+        if span_context == trace.INVALID_SPAN_CONTEXT:
+            return
+        traceparent_string = f"00-{format_trace_id(span_context.trace_id)}-{format_span_id(span_context.span_id)}-{span_context.trace_flags:02x}"
+        setter.set(carrier, self._TRACEPARENT_HEADER_NAME, traceparent_string)
+        if span_context.trace_state:
+            tracestate_string = span_context.trace_state.to_header()
+            setter.set(
+                carrier, self._TRACESTATE_HEADER_NAME, tracestate_string
+            )
+
+    @property
+    def fields(self) -> typing.Set[str]:
+        """Returns a set with the fields set in `inject`.
+
+        See
+        `opentelemetry.propagators.textmap.TextMapPropagator.fields`
+        """
+        return {self._TRACEPARENT_HEADER_NAME, self._TRACESTATE_HEADER_NAME}
diff --git a/.venv/lib/python3.12/site-packages/opentelemetry/trace/py.typed b/.venv/lib/python3.12/site-packages/opentelemetry/trace/py.typed
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/opentelemetry/trace/py.typed
diff --git a/.venv/lib/python3.12/site-packages/opentelemetry/trace/span.py b/.venv/lib/python3.12/site-packages/opentelemetry/trace/span.py
new file mode 100644
index 00000000..6e54dfc7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/opentelemetry/trace/span.py
@@ -0,0 +1,608 @@
+import abc
+import logging
+import re
+import types as python_types
+import typing
+import warnings
+
+from opentelemetry.trace.status import Status, StatusCode
+from opentelemetry.util import types
+
+# The key MUST begin with a lowercase letter or a digit,
+# and can only contain lowercase letters (a-z), digits (0-9),
+# underscores (_), dashes (-), asterisks (*), and forward slashes (/).
+# For multi-tenant vendor scenarios, an at sign (@) can be used to
+# prefix the vendor name. Vendors SHOULD set the tenant ID
+# at the beginning of the key.
+
+# key = ( lcalpha ) 0*255( lcalpha / DIGIT / "_" / "-"/ "*" / "/" )
+# key = ( lcalpha / DIGIT ) 0*240( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ) "@" lcalpha 0*13( lcalpha / DIGIT / "_" / "-"/ "*" / "/" )
+# lcalpha = %x61-7A ; a-z
+
+_KEY_FORMAT = (
+    r"[a-z][_0-9a-z\-\*\/]{0,255}|"
+    r"[a-z0-9][_0-9a-z\-\*\/]{0,240}@[a-z][_0-9a-z\-\*\/]{0,13}"
+)
+_KEY_PATTERN = re.compile(_KEY_FORMAT)
+
+# The value is an opaque string containing up to 256 printable
+# ASCII [RFC0020] characters (i.e., the range 0x20 to 0x7E)
+# except comma (,) and (=).
+# value    = 0*255(chr) nblk-chr
+# nblk-chr = %x21-2B / %x2D-3C / %x3E-7E
+# chr      = %x20 / nblk-chr
+
+_VALUE_FORMAT = (
+    r"[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]"
+)
+_VALUE_PATTERN = re.compile(_VALUE_FORMAT)
+
+
+_TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS = 32
+_delimiter_pattern = re.compile(r"[ \t]*,[ \t]*")
+_member_pattern = re.compile(f"({_KEY_FORMAT})(=)({_VALUE_FORMAT})[ \t]*")
+_logger = logging.getLogger(__name__)
+
+
+def _is_valid_pair(key: str, value: str) -> bool:
+    return (
+        isinstance(key, str)
+        and _KEY_PATTERN.fullmatch(key) is not None
+        and isinstance(value, str)
+        and _VALUE_PATTERN.fullmatch(value) is not None
+    )
+
+
+class Span(abc.ABC):
+    """A span represents a single operation within a trace."""
+
+    @abc.abstractmethod
+    def end(self, end_time: typing.Optional[int] = None) -> None:
+        """Sets the current time as the span's end time.
+
+        The span's end time is the wall time at which the operation finished.
+
+        Only the first call to `end` should modify the span, and
+        implementations are free to ignore or raise on further calls.
+        """
+
+    @abc.abstractmethod
+    def get_span_context(self) -> "SpanContext":
+        """Gets the span's SpanContext.
+
+        Get an immutable, serializable identifier for this span that can be
+        used to create new child spans.
+
+        Returns:
+            A :class:`opentelemetry.trace.SpanContext` with a copy of this span's immutable state.
+        """
+
+    @abc.abstractmethod
+    def set_attributes(
+        self, attributes: typing.Mapping[str, types.AttributeValue]
+    ) -> None:
+        """Sets Attributes.
+
+        Sets Attributes with the key and value passed as arguments dict.
+
+        Note: The behavior of `None` value attributes is undefined, and hence
+        strongly discouraged. It is also preferred to set attributes at span
+        creation, instead of calling this method later since samplers can only
+        consider information already present during span creation.
+        """
+
+    @abc.abstractmethod
+    def set_attribute(self, key: str, value: types.AttributeValue) -> None:
+        """Sets an Attribute.
+
+        Sets a single Attribute with the key and value passed as arguments.
+
+        Note: The behavior of `None` value attributes is undefined, and hence
+        strongly discouraged. It is also preferred to set attributes at span
+        creation, instead of calling this method later since samplers can only
+        consider information already present during span creation.
+        """
+
+    @abc.abstractmethod
+    def add_event(
+        self,
+        name: str,
+        attributes: types.Attributes = None,
+        timestamp: typing.Optional[int] = None,
+    ) -> None:
+        """Adds an `Event`.
+
+        Adds a single `Event` with the name and, optionally, a timestamp and
+        attributes passed as arguments. Implementations should generate a
+        timestamp if the `timestamp` argument is omitted.
+        """
+
+    def add_link(  # pylint: disable=no-self-use
+        self,
+        context: "SpanContext",
+        attributes: types.Attributes = None,
+    ) -> None:
+        """Adds a `Link`.
+
+        Adds a single `Link` with the `SpanContext` of the span to link to and,
+        optionally, attributes passed as arguments. Implementations may ignore
+        calls with an invalid span context if both attributes and TraceState
+        are empty.
+
+        Note: It is preferred to add links at span creation, instead of calling
+        this method later since samplers can only consider information already
+        present during span creation.
+        """
+        warnings.warn(
+            "Span.add_link() not implemented and will be a no-op. "
+            "Use opentelemetry-sdk >= 1.23 to add links after span creation"
+        )
+
+    @abc.abstractmethod
+    def update_name(self, name: str) -> None:
+        """Updates the `Span` name.
+
+        This will override the name provided via :func:`opentelemetry.trace.Tracer.start_span`.
+
+        Upon this update, any sampling behavior based on Span name will depend
+        on the implementation.
+        """
+
+    @abc.abstractmethod
+    def is_recording(self) -> bool:
+        """Returns whether this span will be recorded.
+
+        Returns true if this Span is active and recording information like
+        events with the add_event operation and attributes using set_attribute.
+        """
+
+    @abc.abstractmethod
+    def set_status(
+        self,
+        status: typing.Union[Status, StatusCode],
+        description: typing.Optional[str] = None,
+    ) -> None:
+        """Sets the Status of the Span. If used, this will override the default
+        Span status.
+        """
+
+    @abc.abstractmethod
+    def record_exception(
+        self,
+        exception: BaseException,
+        attributes: types.Attributes = None,
+        timestamp: typing.Optional[int] = None,
+        escaped: bool = False,
+    ) -> None:
+        """Records an exception as a span event."""
+
+    def __enter__(self) -> "Span":
+        """Invoked when `Span` is used as a context manager.
+
+        Returns the `Span` itself.
+        """
+        return self
+
+    def __exit__(
+        self,
+        exc_type: typing.Optional[typing.Type[BaseException]],
+        exc_val: typing.Optional[BaseException],
+        exc_tb: typing.Optional[python_types.TracebackType],
+    ) -> None:
+        """Ends context manager and calls `end` on the `Span`."""
+
+        self.end()
+
+
+class TraceFlags(int):
+    """A bitmask that represents options specific to the trace.
+
+    The only supported option is the "sampled" flag (``0x01``). If set, this
+    flag indicates that the trace may have been sampled upstream.
+
+    See the `W3C Trace Context - Traceparent`_ spec for details.
+
+    .. _W3C Trace Context - Traceparent:
+        https://www.w3.org/TR/trace-context/#trace-flags
+    """
+
+    DEFAULT = 0x00
+    SAMPLED = 0x01
+
+    @classmethod
+    def get_default(cls) -> "TraceFlags":
+        return cls(cls.DEFAULT)
+
+    @property
+    def sampled(self) -> bool:
+        return bool(self & TraceFlags.SAMPLED)
+
+
+DEFAULT_TRACE_OPTIONS = TraceFlags.get_default()
+
+
+class TraceState(typing.Mapping[str, str]):
+    """A list of key-value pairs representing vendor-specific trace info.
+
+    Keys and values are strings of up to 256 printable US-ASCII characters.
+    Implementations should conform to the `W3C Trace Context - Tracestate`_
+    spec, which describes additional restrictions on valid field values.
+
+    .. _W3C Trace Context - Tracestate:
+        https://www.w3.org/TR/trace-context/#tracestate-field
+    """
+
+    def __init__(
+        self,
+        entries: typing.Optional[
+            typing.Sequence[typing.Tuple[str, str]]
+        ] = None,
+    ) -> None:
+        self._dict = {}  # type: dict[str, str]
+        if entries is None:
+            return
+        if len(entries) > _TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS:
+            _logger.warning(
+                "There can't be more than %s key/value pairs.",
+                _TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS,
+            )
+            return
+
+        for key, value in entries:
+            if _is_valid_pair(key, value):
+                if key in self._dict:
+                    _logger.warning("Duplicate key: %s found.", key)
+                    continue
+                self._dict[key] = value
+            else:
+                _logger.warning(
+                    "Invalid key/value pair (%s, %s) found.", key, value
+                )
+
+    def __contains__(self, item: object) -> bool:
+        return item in self._dict
+
+    def __getitem__(self, key: str) -> str:
+        return self._dict[key]
+
+    def __iter__(self) -> typing.Iterator[str]:
+        return iter(self._dict)
+
+    def __len__(self) -> int:
+        return len(self._dict)
+
+    def __repr__(self) -> str:
+        pairs = [
+            f"{{key={key}, value={value}}}"
+            for key, value in self._dict.items()
+        ]
+        return str(pairs)
+
+    def add(self, key: str, value: str) -> "TraceState":
+        """Adds a key-value pair to tracestate. The provided pair should
+        adhere to w3c tracestate identifiers format.
+
+        Args:
+            key: A valid tracestate key to add
+            value: A valid tracestate value to add
+
+        Returns:
+            A new TraceState with the modifications applied.
+
+            If the provided key-value pair is invalid or results in tracestate
+            that violates tracecontext specification, they are discarded and
+            same tracestate will be returned.
+        """
+        if not _is_valid_pair(key, value):
+            _logger.warning(
+                "Invalid key/value pair (%s, %s) found.", key, value
+            )
+            return self
+        # There can be a maximum of 32 pairs
+        if len(self) >= _TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS:
+            _logger.warning("There can't be more 32 key/value pairs.")
+            return self
+        # Duplicate entries are not allowed
+        if key in self._dict:
+            _logger.warning("The provided key %s already exists.", key)
+            return self
+        new_state = [(key, value)] + list(self._dict.items())
+        return TraceState(new_state)
+
+    def update(self, key: str, value: str) -> "TraceState":
+        """Updates a key-value pair in tracestate. The provided pair should
+        adhere to w3c tracestate identifiers format.
+
+        Args:
+            key: A valid tracestate key to update
+            value: A valid tracestate value to update for key
+
+        Returns:
+            A new TraceState with the modifications applied.
+
+            If the provided key-value pair is invalid or results in tracestate
+            that violates tracecontext specification, they are discarded and
+            same tracestate will be returned.
+        """
+        if not _is_valid_pair(key, value):
+            _logger.warning(
+                "Invalid key/value pair (%s, %s) found.", key, value
+            )
+            return self
+        prev_state = self._dict.copy()
+        prev_state.pop(key, None)
+        new_state = [(key, value), *prev_state.items()]
+        return TraceState(new_state)
+
+    def delete(self, key: str) -> "TraceState":
+        """Deletes a key-value from tracestate.
+
+        Args:
+            key: A valid tracestate key to remove key-value pair from tracestate
+
+        Returns:
+            A new TraceState with the modifications applied.
+
+            If the provided key-value pair is invalid or results in tracestate
+            that violates tracecontext specification, they are discarded and
+            same tracestate will be returned.
+        """
+        if key not in self._dict:
+            _logger.warning("The provided key %s doesn't exist.", key)
+            return self
+        prev_state = self._dict.copy()
+        prev_state.pop(key)
+        new_state = list(prev_state.items())
+        return TraceState(new_state)
+
+    def to_header(self) -> str:
+        """Creates a w3c tracestate header from a TraceState.
+
+        Returns:
+            A string that adheres to the w3c tracestate
+            header format.
+        """
+        return ",".join(key + "=" + value for key, value in self._dict.items())
+
+    @classmethod
+    def from_header(cls, header_list: typing.List[str]) -> "TraceState":
+        """Parses one or more w3c tracestate header into a TraceState.
+
+        Args:
+            header_list: one or more w3c tracestate headers.
+
+        Returns:
+            A valid TraceState that contains values extracted from
+            the tracestate header.
+
+            If the format of one headers is illegal, all values will
+            be discarded and an empty tracestate will be returned.
+
+            If the number of keys is beyond the maximum, all values
+            will be discarded and an empty tracestate will be returned.
+        """
+        pairs = {}  # type: dict[str, str]
+        for header in header_list:
+            members: typing.List[str] = re.split(_delimiter_pattern, header)
+            for member in members:
+                # empty members are valid, but no need to process further.
+                if not member:
+                    continue
+                match = _member_pattern.fullmatch(member)
+                if not match:
+                    _logger.warning(
+                        "Member doesn't match the w3c identifiers format %s",
+                        member,
+                    )
+                    return cls()
+                groups: typing.Tuple[str, ...] = match.groups()
+                key, _eq, value = groups
+                # duplicate keys are not legal in header
+                if key in pairs:
+                    return cls()
+                pairs[key] = value
+        return cls(list(pairs.items()))
+
+    @classmethod
+    def get_default(cls) -> "TraceState":
+        return cls()
+
+    def keys(self) -> typing.KeysView[str]:
+        return self._dict.keys()
+
+    def items(self) -> typing.ItemsView[str, str]:
+        return self._dict.items()
+
+    def values(self) -> typing.ValuesView[str]:
+        return self._dict.values()
+
+
+DEFAULT_TRACE_STATE = TraceState.get_default()
+_TRACE_ID_MAX_VALUE = 2**128 - 1
+_SPAN_ID_MAX_VALUE = 2**64 - 1
+
+
+class SpanContext(
+    typing.Tuple[int, int, bool, "TraceFlags", "TraceState", bool]
+):
+    """The state of a Span to propagate between processes.
+
+    This class includes the immutable attributes of a :class:`.Span` that must
+    be propagated to a span's children and across process boundaries.
+
+    Args:
+        trace_id: The ID of the trace that this span belongs to.
+        span_id: This span's ID.
+        is_remote: True if propagated from a remote parent.
+        trace_flags: Trace options to propagate.
+        trace_state: Tracing-system-specific info to propagate.
+    """
+
+    def __new__(
+        cls,
+        trace_id: int,
+        span_id: int,
+        is_remote: bool,
+        trace_flags: typing.Optional["TraceFlags"] = DEFAULT_TRACE_OPTIONS,
+        trace_state: typing.Optional["TraceState"] = DEFAULT_TRACE_STATE,
+    ) -> "SpanContext":
+        if trace_flags is None:
+            trace_flags = DEFAULT_TRACE_OPTIONS
+        if trace_state is None:
+            trace_state = DEFAULT_TRACE_STATE
+
+        is_valid = (
+            INVALID_TRACE_ID < trace_id <= _TRACE_ID_MAX_VALUE
+            and INVALID_SPAN_ID < span_id <= _SPAN_ID_MAX_VALUE
+        )
+
+        return tuple.__new__(
+            cls,
+            (trace_id, span_id, is_remote, trace_flags, trace_state, is_valid),
+        )
+
+    def __getnewargs__(
+        self,
+    ) -> typing.Tuple[int, int, bool, "TraceFlags", "TraceState"]:
+        return (
+            self.trace_id,
+            self.span_id,
+            self.is_remote,
+            self.trace_flags,
+            self.trace_state,
+        )
+
+    @property
+    def trace_id(self) -> int:
+        return self[0]  # pylint: disable=unsubscriptable-object
+
+    @property
+    def span_id(self) -> int:
+        return self[1]  # pylint: disable=unsubscriptable-object
+
+    @property
+    def is_remote(self) -> bool:
+        return self[2]  # pylint: disable=unsubscriptable-object
+
+    @property
+    def trace_flags(self) -> "TraceFlags":
+        return self[3]  # pylint: disable=unsubscriptable-object
+
+    @property
+    def trace_state(self) -> "TraceState":
+        return self[4]  # pylint: disable=unsubscriptable-object
+
+    @property
+    def is_valid(self) -> bool:
+        return self[5]  # pylint: disable=unsubscriptable-object
+
+    def __setattr__(self, *args: str) -> None:
+        _logger.debug(
+            "Immutable type, ignoring call to set attribute", stack_info=True
+        )
+
+    def __delattr__(self, *args: str) -> None:
+        _logger.debug(
+            "Immutable type, ignoring call to set attribute", stack_info=True
+        )
+
+    def __repr__(self) -> str:
+        return f"{type(self).__name__}(trace_id=0x{format_trace_id(self.trace_id)}, span_id=0x{format_span_id(self.span_id)}, trace_flags=0x{self.trace_flags:02x}, trace_state={self.trace_state!r}, is_remote={self.is_remote})"
+
+
+class NonRecordingSpan(Span):
+    """The Span that is used when no Span implementation is available.
+
+    All operations are no-op except context propagation.
+    """
+
+    def __init__(self, context: "SpanContext") -> None:
+        self._context = context
+
+    def get_span_context(self) -> "SpanContext":
+        return self._context
+
+    def is_recording(self) -> bool:
+        return False
+
+    def end(self, end_time: typing.Optional[int] = None) -> None:
+        pass
+
+    def set_attributes(
+        self, attributes: typing.Mapping[str, types.AttributeValue]
+    ) -> None:
+        pass
+
+    def set_attribute(self, key: str, value: types.AttributeValue) -> None:
+        pass
+
+    def add_event(
+        self,
+        name: str,
+        attributes: types.Attributes = None,
+        timestamp: typing.Optional[int] = None,
+    ) -> None:
+        pass
+
+    def add_link(
+        self,
+        context: "SpanContext",
+        attributes: types.Attributes = None,
+    ) -> None:
+        pass
+
+    def update_name(self, name: str) -> None:
+        pass
+
+    def set_status(
+        self,
+        status: typing.Union[Status, StatusCode],
+        description: typing.Optional[str] = None,
+    ) -> None:
+        pass
+
+    def record_exception(
+        self,
+        exception: BaseException,
+        attributes: types.Attributes = None,
+        timestamp: typing.Optional[int] = None,
+        escaped: bool = False,
+    ) -> None:
+        pass
+
+    def __repr__(self) -> str:
+        return f"NonRecordingSpan({self._context!r})"
+
+
+INVALID_SPAN_ID = 0x0000000000000000
+INVALID_TRACE_ID = 0x00000000000000000000000000000000
+INVALID_SPAN_CONTEXT = SpanContext(
+    trace_id=INVALID_TRACE_ID,
+    span_id=INVALID_SPAN_ID,
+    is_remote=False,
+    trace_flags=DEFAULT_TRACE_OPTIONS,
+    trace_state=DEFAULT_TRACE_STATE,
+)
+INVALID_SPAN = NonRecordingSpan(INVALID_SPAN_CONTEXT)
+
+
+def format_trace_id(trace_id: int) -> str:
+    """Convenience trace ID formatting method
+    Args:
+        trace_id: Trace ID int
+
+    Returns:
+        The trace ID as 32-byte hexadecimal string
+    """
+    return format(trace_id, "032x")
+
+
+def format_span_id(span_id: int) -> str:
+    """Convenience span ID formatting method
+    Args:
+        span_id: Span ID int
+
+    Returns:
+        The span ID as 16-byte hexadecimal string
+    """
+    return format(span_id, "016x")
diff --git a/.venv/lib/python3.12/site-packages/opentelemetry/trace/status.py b/.venv/lib/python3.12/site-packages/opentelemetry/trace/status.py
new file mode 100644
index 00000000..ada7fa1e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/opentelemetry/trace/status.py
@@ -0,0 +1,82 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import enum
+import logging
+import typing
+
+logger = logging.getLogger(__name__)
+
+
+class StatusCode(enum.Enum):
+    """Represents the canonical set of status codes of a finished Span."""
+
+    UNSET = 0
+    """The default status."""
+
+    OK = 1
+    """The operation has been validated by an Application developer or Operator to have completed successfully."""
+
+    ERROR = 2
+    """The operation contains an error."""
+
+
+class Status:
+    """Represents the status of a finished Span.
+
+    Args:
+        status_code: The canonical status code that describes the result
+            status of the operation.
+        description: An optional description of the status.
+    """
+
+    def __init__(
+        self,
+        status_code: StatusCode = StatusCode.UNSET,
+        description: typing.Optional[str] = None,
+    ):
+        self._status_code = status_code
+        self._description = None
+
+        if description:
+            if not isinstance(description, str):
+                logger.warning("Invalid status description type, expected str")
+                return
+            if status_code is not StatusCode.ERROR:
+                logger.warning(
+                    "description should only be set when status_code is set to StatusCode.ERROR"
+                )
+                return
+
+        self._description = description
+
+    @property
+    def status_code(self) -> StatusCode:
+        """Represents the canonical status code of a finished Span."""
+        return self._status_code
+
+    @property
+    def description(self) -> typing.Optional[str]:
+        """Status description"""
+        return self._description
+
+    @property
+    def is_ok(self) -> bool:
+        """Returns false if this represents an error, true otherwise."""
+        return self.is_unset or self._status_code is StatusCode.OK
+
+    @property
+    def is_unset(self) -> bool:
+        """Returns true if unset, false otherwise."""
+        return self._status_code is StatusCode.UNSET