Initial Commit

This commit is contained in:
Mark Gregory
2026-10-21 13:04:35 +01:00
commit 2c86cd435c
4963 changed files with 779038 additions and 0 deletions
@@ -0,0 +1,75 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
import contextlib
import functools
import inspect
from collections.abc import Callable, Iterator
from typing import TYPE_CHECKING, Generic, TypeVar
V = TypeVar("V")
R = TypeVar("R") # Return type
Pargs = TypeVar("Pargs") # Generic type for arguments
Pkwargs = TypeVar("Pkwargs") # Generic type for arguments
# We don't actually depend on typing_extensions but we can use it in CI with this conditional
# import. ParamSpec can be imported directly from typing after python 3.9 is dropped
# https://peps.python.org/pep-0612/.
if TYPE_CHECKING:
from typing_extensions import ParamSpec
P = ParamSpec("P") # Generic type for all arguments
class _AgnosticContextManager(
contextlib._GeneratorContextManager[R],
Generic[R],
): # pylint: disable=protected-access
"""Context manager that can decorate both async and sync functions.
This is an overridden version of the contextlib._GeneratorContextManager
class that will decorate async functions with an async context manager
to end the span AFTER the entire async function coroutine finishes.
Else it will report near zero spans durations for async functions.
We are overriding the contextlib._GeneratorContextManager class as
reimplementing it is a lot of code to maintain and this class (even if it's
marked as protected) doesn't seems like to be evolving a lot.
For more information, see:
https://github.com/open-telemetry/opentelemetry-python/pull/3633
"""
def __enter__(self) -> R:
"""Reimplementing __enter__ to avoid the type error.
The original __enter__ method returns Any type, but we want to return R.
"""
del self.args, self.kwds, self.func # type: ignore
try:
return next(self.gen) # type: ignore
except StopIteration:
raise RuntimeError("generator didn't yield") from None
def __call__(self, func: V) -> V: # pyright: ignore [reportIncompatibleMethodOverride]
if inspect.iscoroutinefunction(func):
@functools.wraps(func) # type: ignore
async def async_wrapper(*args: Pargs, **kwargs: Pkwargs) -> R: # pyright: ignore [reportInvalidTypeVarUse]
with self._recreate_cm(): # type: ignore
return await func(*args, **kwargs) # type: ignore
return async_wrapper # type: ignore
return super().__call__(func) # type: ignore
def _agnosticcontextmanager(
func: "Callable[P, Iterator[R]]",
) -> "Callable[P, _AgnosticContextManager[R]]":
@functools.wraps(func)
def helper(*args: Pargs, **kwargs: Pkwargs) -> _AgnosticContextManager[R]: # pyright: ignore [reportInvalidTypeVarUse]
return _AgnosticContextManager(func, args, kwargs) # pyright: ignore [reportArgumentType]
# Ignoring the type to keep the original signature of the function
return helper # type: ignore[return-value]
@@ -0,0 +1,61 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
"""
Caching and compatibility wrapper for standard library ``importlib.metadata``.
This module caches ``entry_points()`` to avoid reloading entry points from disk on every call.
It also normalizes minor differences across python versions 3.10+. References to issues:
- https://github.com/open-telemetry/opentelemetry-python/pull/4735
- https://github.com/open-telemetry/opentelemetry-python/pull/5203
"""
import itertools
from functools import cache
from importlib.metadata import (
Distribution,
EntryPoint,
EntryPoints,
PackageNotFoundError,
distributions,
requires,
version,
)
from importlib.metadata import entry_points as original_entry_points
from typing import Any
def _as_entry_points(eps: Any) -> EntryPoints:
# Python versions greater than 3.11 return EntryPoints.
if isinstance(eps, EntryPoints):
return eps
# In Python 3.10 and 3.11 entry_points() returns
# a dict-like SelectableGroups object.
# Use dict.values() instead of eps.values() to avoid the DeprecationWarning
# that SelectableGroups raises when calling .values().
if isinstance(eps, dict):
return EntryPoints(itertools.chain.from_iterable(dict.values(eps)))
# This case should be unreachable, but is included as a fallback.
return EntryPoints(ep for group in eps.groups for ep in eps.select(group=group))
@cache
def _original_entry_points_cached() -> EntryPoints:
return _as_entry_points(original_entry_points())
def entry_points(**params) -> EntryPoints:
return _original_entry_points_cached().select(**params)
__all__ = [
"Distribution",
"EntryPoint",
"EntryPoints",
"PackageNotFoundError",
"distributions",
"entry_points",
"requires",
"version",
]
@@ -0,0 +1,36 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
from collections.abc import Callable
from threading import Lock
class Once:
"""Execute a function exactly once and block all callers until the function returns
Same as golang's `sync.Once <https://pkg.go.dev/sync#Once>`_
"""
def __init__(self) -> None:
self._lock = Lock()
self._done = False
def do_once(self, func: Callable[[], None]) -> bool:
"""Execute ``func`` if it hasn't been executed or return.
Will block until ``func`` has been called by one thread.
Returns:
Whether or not ``func`` was executed in this call
"""
# fast path, try to avoid locking
if self._done:
return False
with self._lock:
if not self._done:
func()
self._done = True
return True
return False
@@ -0,0 +1,42 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
from logging import getLogger
from os import environ
from typing import TYPE_CHECKING, TypeVar, cast
if TYPE_CHECKING:
from opentelemetry.metrics import MeterProvider
from opentelemetry.trace import TracerProvider
Provider = TypeVar("Provider", "TracerProvider", "MeterProvider")
logger = getLogger(__name__)
def _load_provider(provider_environment_variable: str, provider: str) -> Provider: # type: ignore[type-var]
# pylint: disable=import-outside-toplevel,no-name-in-module
from opentelemetry.util._importlib_metadata import ( # noqa: PLC0415
entry_points,
)
try:
provider_name = cast(
str,
environ.get(provider_environment_variable, f"default_{provider}"),
)
return cast(
Provider,
next( # type: ignore
iter( # type: ignore
entry_points( # type: ignore
group=f"opentelemetry_{provider}",
name=provider_name,
)
)
).load()(),
)
except Exception: # pylint: disable=broad-exception-caught
logger.exception("Failed to load configured provider %s", provider)
raise
@@ -0,0 +1,93 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
from collections.abc import Mapping
from logging import getLogger
from re import compile, split
from urllib.parse import unquote
from typing_extensions import deprecated
_logger = getLogger(__name__)
# The following regexes reference this spec: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#specifying-headers-via-environment-variables
# Optional whitespace
_OWS = r"[ \t]*"
# A key contains printable US-ASCII characters except: SP and "(),/:;<=>?@[\]{}
_KEY_FORMAT = r"[\x21\x23-\x27\x2a\x2b\x2d\x2e\x30-\x39\x41-\x5a\x5e-\x7a\x7c\x7e]+"
# A value contains a URL-encoded UTF-8 string. The encoded form can contain any
# printable US-ASCII characters (0x20-0x7f) other than SP, DEL, and ",;/
_VALUE_FORMAT = r"[\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]*"
# Like above with SP included
_LIBERAL_VALUE_FORMAT = r"[\x20\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]*"
# A key-value is key=value, with optional whitespace surrounding key and value
_KEY_VALUE_FORMAT = rf"{_OWS}{_KEY_FORMAT}{_OWS}={_OWS}{_VALUE_FORMAT}{_OWS}"
_HEADER_PATTERN = compile(_KEY_VALUE_FORMAT)
_LIBERAL_HEADER_PATTERN = compile(rf"{_OWS}{_KEY_FORMAT}{_OWS}={_OWS}{_LIBERAL_VALUE_FORMAT}{_OWS}")
_DELIMITER_PATTERN = compile(r"[ \t]*,[ \t]*")
_BAGGAGE_PROPERTY_FORMAT = rf"{_KEY_VALUE_FORMAT}|{_OWS}{_KEY_FORMAT}{_OWS}"
_INVALID_HEADER_ERROR_MESSAGE_STRICT_TEMPLATE = (
"Header format invalid! Header values in environment variables must be "
"URL encoded per the OpenTelemetry Protocol Exporter specification: %s"
)
_INVALID_HEADER_ERROR_MESSAGE_LIBERAL_TEMPLATE = (
"Header format invalid! Header values in environment variables must be "
"URL encoded per the OpenTelemetry Protocol Exporter specification or "
"a comma separated list of name=value occurrences: %s"
)
# pylint: disable=invalid-name
@deprecated("You should use parse_env_headers. Deprecated since version 1.15.0.")
def parse_headers(s: str) -> Mapping[str, str]:
return parse_env_headers(s)
def parse_env_headers(s: str, liberal: bool = False) -> Mapping[str, str]:
"""
Parse ``s``, which is a ``str`` instance containing HTTP headers encoded
for use in ENV variables per the W3C Baggage HTTP header format at
https://www.w3.org/TR/baggage/#baggage-http-header-format, except that
additional semi-colon delimited metadata is not supported.
If ``liberal`` is True we try to parse ``s`` anyway to be more compatible
with other languages SDKs that accept non URL-encoded headers by default.
"""
headers: dict[str, str] = {}
headers_list: list[str] = split(_DELIMITER_PATTERN, s)
for header in headers_list:
if not header: # empty string
continue
header_match = _HEADER_PATTERN.fullmatch(header.strip())
if not header_match and not liberal:
_logger.warning(_INVALID_HEADER_ERROR_MESSAGE_STRICT_TEMPLATE, header)
continue
if header_match:
match_string: str = header_match.string
# value may contain any number of `=`
name, value = match_string.split("=", 1)
name = unquote(name).strip().lower()
value = unquote(value).strip()
headers[name] = value
else:
# this is not url-encoded and does not match the spec but we decided to be
# liberal in what we accept to match other languages SDKs behaviour
liberal_header_match = _LIBERAL_HEADER_PATTERN.fullmatch(header.strip())
if not liberal_header_match:
_logger.warning(_INVALID_HEADER_ERROR_MESSAGE_LIBERAL_TEMPLATE, header)
continue
liberal_match_string: str = liberal_header_match.string
# value may contain any number of `=`
name, value = liberal_match_string.split("=", 1)
name = name.strip().lower()
value = value.strip()
headers[name] = value
return headers
@@ -0,0 +1,28 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
from collections.abc import Mapping, Sequence
from typing import TypeAlias
# This is the implementation of the "Any" type as specified by the specifications of OpenTelemetry data model for logs.
# For more details, refer to the OTel specification:
# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#type-any
AnyValue: TypeAlias = str | bool | int | float | bytes | Sequence["AnyValue"] | Mapping[str, "AnyValue"] | None
AttributeValue: TypeAlias = AnyValue
Attributes = Mapping[str, AnyValue] | None
# Deprecated type, do not use.
AttributesAsKey = tuple[
tuple[
str,
str
| bool
| int
| float
| tuple[str | None, ...]
| tuple[bool | None, ...]
| tuple[int | None, ...]
| tuple[float | None, ...],
],
...,
]