From e5c70a2a20590d782da4e282bf5d88fb63cf8a98 Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:52:31 -0700 Subject: [PATCH 01/10] api: conditionally load entrypoints for OTEL_PYTHON_CONTEXT This prevents unnecessarily importing the entry points library, reducing the memory footprint of loading the API by about a MB (27%): |Scenario | Memory before | Memory after | delta | | - | - | - | - | |Import API only | 5.43 MB | 4.49 MB | -940 KB | |API + get_tracer (no-op) | 5.05 MB | 4.10 MB | -950 KB | Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --- .../src/opentelemetry/context/__init__.py | 35 ++++++------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py index 9287d5b401e..6f8862b5a49 100644 --- a/opentelemetry-api/src/opentelemetry/context/__init__.py +++ b/opentelemetry-api/src/opentelemetry/context/__init__.py @@ -15,15 +15,15 @@ from __future__ import annotations import logging +import os import typing from contextvars import Token -from os import environ from uuid import uuid4 # pylint: disable=wrong-import-position from opentelemetry.context.context import Context, _RuntimeContext # noqa +from opentelemetry.context.contextvars_context import ContextVarsRuntimeContext from opentelemetry.environment_variables import OTEL_PYTHON_CONTEXT -from opentelemetry.util._importlib_metadata import entry_points logger = logging.getLogger(__name__) @@ -34,37 +34,22 @@ def _load_runtime_context() -> _RuntimeContext: Returns: An instance of RuntimeContext. """ + configured_context = os.environ.get(OTEL_PYTHON_CONTEXT) + if not configured_context: + return ContextVarsRuntimeContext() - # FIXME use a better implementation of a configuration manager - # to avoid having to get configuration values straight from - # environment variables - default_context = "contextvars_context" - - configured_context = environ.get(OTEL_PYTHON_CONTEXT, default_context) # type: str + from opentelemetry.util._importlib_metadata import entry_points try: - return next( # type: ignore - iter( # type: ignore - entry_points( # type: ignore - group="opentelemetry_context", - name=configured_context, - ) - ) + return next( + iter(entry_points(group="opentelemetry_context", name=configured_context)) ).load()() except Exception: # pylint: disable=broad-exception-caught logger.exception( - "Failed to load context: %s, fallback to %s", + "Failed to load context: %s, falling back to contextvars_context", configured_context, - default_context, ) - return next( # type: ignore - iter( # type: ignore - entry_points( # type: ignore - group="opentelemetry_context", - name=default_context, - ) - ) - ).load()() + return ContextVarsRuntimeContext() _RUNTIME_CONTEXT = _load_runtime_context() From 4024ef7ac4d9d92da10311a0a072cc67baeb762b Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Mon, 27 Apr 2026 09:48:31 -0700 Subject: [PATCH 02/10] add ignore rule to import lint, update changelog Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --- CHANGELOG.md | 1 + opentelemetry-api/src/opentelemetry/context/__init__.py | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 614f240d4ee..273ae7c0179 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#5120](https://github.com/open-telemetry/opentelemetry-python/pull/5120)) - Add WeaverLiveCheck test util ([#5088](https://github.com/open-telemetry/opentelemetry-python/pull/5088)) +- `opentelemetry-api`: conditionallly import entrypoints for `opentelemetry_context` only if the `OTEL_PYTHON_CONTEXT` env variable is defined, return `ContextVarsRuntimeContext` otherwise. ## Version 1.41.0/0.62b0 (2026-04-09) diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py index 6f8862b5a49..8339c2caa35 100644 --- a/opentelemetry-api/src/opentelemetry/context/__init__.py +++ b/opentelemetry-api/src/opentelemetry/context/__init__.py @@ -38,6 +38,7 @@ def _load_runtime_context() -> _RuntimeContext: if not configured_context: return ContextVarsRuntimeContext() + # pylint: disable=import-outside-toplevel,no-name-in-module from opentelemetry.util._importlib_metadata import entry_points try: From 2861d762f44d76862243c577280dff4f1b52f3a7 Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Mon, 27 Apr 2026 11:49:10 -0700 Subject: [PATCH 03/10] fix precommit Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --- .../src/opentelemetry/context/__init__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py index 8339c2caa35..f9eaeeb7536 100644 --- a/opentelemetry-api/src/opentelemetry/context/__init__.py +++ b/opentelemetry-api/src/opentelemetry/context/__init__.py @@ -39,11 +39,17 @@ def _load_runtime_context() -> _RuntimeContext: return ContextVarsRuntimeContext() # pylint: disable=import-outside-toplevel,no-name-in-module - from opentelemetry.util._importlib_metadata import entry_points + from opentelemetry.util._importlib_metadata import ( # noqa: PLC0415 + entry_points, + ) try: return next( - iter(entry_points(group="opentelemetry_context", name=configured_context)) + iter( + entry_points( + group="opentelemetry_context", name=configured_context + ) + ) ).load()() except Exception: # pylint: disable=broad-exception-caught logger.exception( From 460af4d239aff3afe474309825400c460c575224 Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Mon, 27 Apr 2026 13:00:00 -0700 Subject: [PATCH 04/10] clean up changelog Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 273ae7c0179..706ccfbfb77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#5120](https://github.com/open-telemetry/opentelemetry-python/pull/5120)) - Add WeaverLiveCheck test util ([#5088](https://github.com/open-telemetry/opentelemetry-python/pull/5088)) -- `opentelemetry-api`: conditionallly import entrypoints for `opentelemetry_context` only if the `OTEL_PYTHON_CONTEXT` env variable is defined, return `ContextVarsRuntimeContext` otherwise. +- `opentelemetry-api`: conditionallly import entrypoints for `opentelemetry_context` only if the `OTEL_PYTHON_CONTEXT` env variable is defined, return `ContextVarsRuntimeContext` otherwise + ([#5144](https://github.com/open-telemetry/opentelemetry-python/pull/5144)) ## Version 1.41.0/0.62b0 (2026-04-09) From 9152c35182f299c0705e772696cb71210d96980d Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Thu, 14 May 2026 08:17:12 -0700 Subject: [PATCH 05/10] use changelog fragment Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --- .changelog/5144.changed | 1 + CHANGELOG.md | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 .changelog/5144.changed diff --git a/.changelog/5144.changed b/.changelog/5144.changed new file mode 100644 index 00000000000..99b227b1caf --- /dev/null +++ b/.changelog/5144.changed @@ -0,0 +1 @@ +`opentelemetry-api`: conditionallly import entrypoints for `opentelemetry_context` only if the `OTEL_PYTHON_CONTEXT` env variable is defined, return `ContextVarsRuntimeContext` otherwise diff --git a/CHANGELOG.md b/CHANGELOG.md index abb76fba387..4875ec59176 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,8 +62,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#5119](https://github.com/open-telemetry/opentelemetry-python/pull/5119)) - `opentelemetry-sdk`: only load entrypoints for resource detectors if they are configured via `OTEL_EXPERIMENTAL_RESOURCE_DETECTORS` ([#5145](https://github.com/open-telemetry/opentelemetry-python/pull/5145)) -- `opentelemetry-api`: conditionallly import entrypoints for `opentelemetry_context` only if the `OTEL_PYTHON_CONTEXT` env variable is defined, return `ContextVarsRuntimeContext` otherwise - ([#5144](https://github.com/open-telemetry/opentelemetry-python/pull/5144)) - `opentelemetry-exporter-otlp-json-common`: add 'opentelemetry-exporter-otlp-json-common' package for OTLP JSON exporters ([#4996](https://github.com/open-telemetry/opentelemetry-python/pull/4996)) - `opentelemetry-exporter-otlp-proto-grpc`: make retryable gRPC error codes configurable for gRPC exporters From aba01702db7193b08d957e2e1378006809b201b1 Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Tue, 19 May 2026 12:37:33 -0700 Subject: [PATCH 06/10] apply the same pattern to propagators and providers Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --- .../src/opentelemetry/propagate/__init__.py | 84 +++++++++++-------- .../src/opentelemetry/util/_providers.py | 7 +- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/propagate/__init__.py b/opentelemetry-api/src/opentelemetry/propagate/__init__.py index 207f639a3f4..95d833c0dfb 100644 --- a/opentelemetry-api/src/opentelemetry/propagate/__init__.py +++ b/opentelemetry-api/src/opentelemetry/propagate/__init__.py @@ -63,7 +63,6 @@ def example_route(): from opentelemetry.context.context import Context from opentelemetry.environment_variables import OTEL_PROPAGATORS from opentelemetry.propagators import composite, textmap -from opentelemetry.util._importlib_metadata import entry_points logger = getLogger(__name__) @@ -109,46 +108,57 @@ def inject( get_global_textmap().inject(carrier, context=context, setter=setter) -propagators: list[textmap.TextMapPropagator] = [] - -# Single use variable here to hack black and make lint pass -environ_propagators = environ.get( - OTEL_PROPAGATORS, - "tracecontext,baggage", -) - - -for propagator in environ_propagators.split(","): - propagator = propagator.strip() - if propagator.lower() == "none": - logger.debug( - "OTEL_PROPAGATORS environment variable contains none, removing all propagators" +def _load_propagators() -> textmap.TextMapPropagator: + configured = environ.get(OTEL_PROPAGATORS) + if not configured: + # pylint: disable=import-outside-toplevel,no-name-in-module + from opentelemetry.baggage.propagation import ( # noqa: PLC0415 + W3CBaggagePropagator, ) - propagators = [] - break - try: - propagators.append( - next( # type: ignore - iter( # type: ignore - entry_points( # type: ignore[misc] - group="opentelemetry_propagator", - name=propagator, - ) - ) - ).load()() + # pylint: disable=import-outside-toplevel,no-name-in-module + from opentelemetry.trace.propagation.tracecontext import ( # noqa: PLC0415 + TraceContextTextMapPropagator, ) - except StopIteration: - raise ValueError( - f"Propagator {propagator} not found. It is either misspelled or not installed." - ) - except Exception: # pylint: disable=broad-exception-caught - logger.exception("Failed to load propagator: %s", propagator) - raise + return composite.CompositePropagator( + [TraceContextTextMapPropagator(), W3CBaggagePropagator()] + ) -_HTTP_TEXT_FORMAT: textmap.TextMapPropagator = composite.CompositePropagator( - propagators -) + # pylint: disable=import-outside-toplevel,no-name-in-module + from opentelemetry.util._importlib_metadata import ( # noqa: PLC0415 + entry_points, + ) + + propagators: list[textmap.TextMapPropagator] = [] + for propagator in configured.split(","): + propagator = propagator.strip() + if propagator.lower() == "none": + logger.debug( + "OTEL_PROPAGATORS environment variable contains none, removing all propagators" + ) + return composite.CompositePropagator([]) + try: + propagators.append( + next( # type: ignore + iter( # type: ignore + entry_points( # type: ignore[misc] + group="opentelemetry_propagator", + name=propagator, + ) + ) + ).load()() + ) + except StopIteration: + raise ValueError( + f"Propagator {propagator} not found. It is either misspelled or not installed." + ) + except Exception: # pylint: disable=broad-exception-caught + logger.exception("Failed to load propagator: %s", propagator) + raise + return composite.CompositePropagator(propagators) + + +_HTTP_TEXT_FORMAT: textmap.TextMapPropagator = _load_propagators() def get_global_textmap() -> textmap.TextMapPropagator: diff --git a/opentelemetry-api/src/opentelemetry/util/_providers.py b/opentelemetry-api/src/opentelemetry/util/_providers.py index ac801ba3eba..37cc823e395 100644 --- a/opentelemetry-api/src/opentelemetry/util/_providers.py +++ b/opentelemetry-api/src/opentelemetry/util/_providers.py @@ -5,8 +5,6 @@ from os import environ from typing import TYPE_CHECKING, TypeVar, cast -from opentelemetry.util._importlib_metadata import entry_points - if TYPE_CHECKING: from opentelemetry.metrics import MeterProvider from opentelemetry.trace import TracerProvider @@ -19,6 +17,11 @@ 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, From 7affb497578635717e4e42b67a6b47658184fcd2 Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Tue, 19 May 2026 18:50:24 -0700 Subject: [PATCH 07/10] precommit Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --- opentelemetry-api/src/opentelemetry/propagate/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/opentelemetry-api/src/opentelemetry/propagate/__init__.py b/opentelemetry-api/src/opentelemetry/propagate/__init__.py index 95d833c0dfb..6e0fff7f53c 100644 --- a/opentelemetry-api/src/opentelemetry/propagate/__init__.py +++ b/opentelemetry-api/src/opentelemetry/propagate/__init__.py @@ -115,6 +115,7 @@ def _load_propagators() -> textmap.TextMapPropagator: from opentelemetry.baggage.propagation import ( # noqa: PLC0415 W3CBaggagePropagator, ) + # pylint: disable=import-outside-toplevel,no-name-in-module from opentelemetry.trace.propagation.tracecontext import ( # noqa: PLC0415 TraceContextTextMapPropagator, From ab548731a1ae100ac55f670eb684935e347c57d7 Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Wed, 20 May 2026 12:52:27 -0700 Subject: [PATCH 08/10] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .changelog/5144.changed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/5144.changed b/.changelog/5144.changed index 99b227b1caf..dee40b7472d 100644 --- a/.changelog/5144.changed +++ b/.changelog/5144.changed @@ -1 +1 @@ -`opentelemetry-api`: conditionallly import entrypoints for `opentelemetry_context` only if the `OTEL_PYTHON_CONTEXT` env variable is defined, return `ContextVarsRuntimeContext` otherwise +`opentelemetry-api`: conditionally import entrypoints for `opentelemetry_context` only if the `OTEL_PYTHON_CONTEXT` env variable is defined, return `ContextVarsRuntimeContext` otherwise From 88791260e75a218613fcf24575b8be79514551cf Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Wed, 20 May 2026 14:59:09 -0700 Subject: [PATCH 09/10] put public symbols back Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --- opentelemetry-api/src/opentelemetry/propagate/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/opentelemetry-api/src/opentelemetry/propagate/__init__.py b/opentelemetry-api/src/opentelemetry/propagate/__init__.py index 6e0fff7f53c..0b1215133db 100644 --- a/opentelemetry-api/src/opentelemetry/propagate/__init__.py +++ b/opentelemetry-api/src/opentelemetry/propagate/__init__.py @@ -161,6 +161,14 @@ def _load_propagators() -> textmap.TextMapPropagator: _HTTP_TEXT_FORMAT: textmap.TextMapPropagator = _load_propagators() +# Deprecated: propagators, environ_propagators and propagator names were never intendended to be part of the public API. +propagators: list[textmap.TextMapPropagator] = [] + +environ_propagators = environ.get(OTEL_PROPAGATORS, "tracecontext,baggage") + +for propagator in environ_propagators.split(","): # type: ignore[assignment] + propagator = propagator.strip() + def get_global_textmap() -> textmap.TextMapPropagator: return _HTTP_TEXT_FORMAT From 861cf106b501b4fb3d8beb8c5eb5e4321f54453d Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Wed, 20 May 2026 15:04:47 -0700 Subject: [PATCH 10/10] rename vars Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --- .../src/opentelemetry/propagate/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/propagate/__init__.py b/opentelemetry-api/src/opentelemetry/propagate/__init__.py index 0b1215133db..d53a1b91d30 100644 --- a/opentelemetry-api/src/opentelemetry/propagate/__init__.py +++ b/opentelemetry-api/src/opentelemetry/propagate/__init__.py @@ -130,33 +130,33 @@ def _load_propagators() -> textmap.TextMapPropagator: entry_points, ) - propagators: list[textmap.TextMapPropagator] = [] - for propagator in configured.split(","): - propagator = propagator.strip() - if propagator.lower() == "none": + _propagators: list[textmap.TextMapPropagator] = [] + for _propagator in configured.split(","): + _propagator = _propagator.strip() + if _propagator.lower() == "none": logger.debug( "OTEL_PROPAGATORS environment variable contains none, removing all propagators" ) return composite.CompositePropagator([]) try: - propagators.append( + _propagators.append( next( # type: ignore iter( # type: ignore entry_points( # type: ignore[misc] group="opentelemetry_propagator", - name=propagator, + name=_propagator, ) ) ).load()() ) except StopIteration: raise ValueError( - f"Propagator {propagator} not found. It is either misspelled or not installed." + f"Propagator {_propagator} not found. It is either misspelled or not installed." ) except Exception: # pylint: disable=broad-exception-caught - logger.exception("Failed to load propagator: %s", propagator) + logger.exception("Failed to load propagator: %s", _propagator) raise - return composite.CompositePropagator(propagators) + return composite.CompositePropagator(_propagators) _HTTP_TEXT_FORMAT: textmap.TextMapPropagator = _load_propagators()