Skip to content

Commit c2b11f6

Browse files
codebotenMikeGoldsmithCopilot
authored
api: conditionally load entrypoints for OTEL_PYTHON_CONTEXT (open-telemetry#5144)
* 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> * add ignore rule to import lint, update changelog Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * fix precommit Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * clean up changelog Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * use changelog fragment Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * apply the same pattern to propagators and providers Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * precommit Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * put public symbols back Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * rename vars Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --------- Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> Co-authored-by: Mike Goldsmith <goldsmith.mike@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 05ee71b commit c2b11f6

4 files changed

Lines changed: 75 additions & 60 deletions

File tree

.changelog/5144.changed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`opentelemetry-api`: conditionally import entrypoints for `opentelemetry_context` only if the `OTEL_PYTHON_CONTEXT` env variable is defined, return `ContextVarsRuntimeContext` otherwise

opentelemetry-api/src/opentelemetry/context/__init__.py

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
from __future__ import annotations
55

66
import logging
7+
import os
78
from contextvars import Token
8-
from os import environ
99
from uuid import uuid4
1010

1111
# pylint: disable=wrong-import-position
1212
from opentelemetry.context.context import Context, _RuntimeContext # noqa
13+
from opentelemetry.context.contextvars_context import ContextVarsRuntimeContext
1314
from opentelemetry.environment_variables import OTEL_PYTHON_CONTEXT
14-
from opentelemetry.util._importlib_metadata import entry_points
1515

1616
logger = logging.getLogger(__name__)
1717

@@ -22,37 +22,29 @@ def _load_runtime_context() -> _RuntimeContext:
2222
Returns:
2323
An instance of RuntimeContext.
2424
"""
25+
configured_context = os.environ.get(OTEL_PYTHON_CONTEXT)
26+
if not configured_context:
27+
return ContextVarsRuntimeContext()
2528

26-
# FIXME use a better implementation of a configuration manager
27-
# to avoid having to get configuration values straight from
28-
# environment variables
29-
default_context = "contextvars_context"
30-
31-
configured_context = environ.get(OTEL_PYTHON_CONTEXT, default_context) # type: str
29+
# pylint: disable=import-outside-toplevel,no-name-in-module
30+
from opentelemetry.util._importlib_metadata import ( # noqa: PLC0415
31+
entry_points,
32+
)
3233

3334
try:
34-
return next( # type: ignore
35-
iter( # type: ignore
36-
entry_points( # type: ignore
37-
group="opentelemetry_context",
38-
name=configured_context,
35+
return next(
36+
iter(
37+
entry_points(
38+
group="opentelemetry_context", name=configured_context
3939
)
4040
)
4141
).load()()
4242
except Exception: # pylint: disable=broad-exception-caught
4343
logger.exception(
44-
"Failed to load context: %s, fallback to %s",
44+
"Failed to load context: %s, falling back to contextvars_context",
4545
configured_context,
46-
default_context,
4746
)
48-
return next( # type: ignore
49-
iter( # type: ignore
50-
entry_points( # type: ignore
51-
group="opentelemetry_context",
52-
name=default_context,
53-
)
54-
)
55-
).load()()
47+
return ContextVarsRuntimeContext()
5648

5749

5850
_RUNTIME_CONTEXT = _load_runtime_context()

opentelemetry-api/src/opentelemetry/propagate/__init__.py

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ def example_route():
6363
from opentelemetry.context.context import Context
6464
from opentelemetry.environment_variables import OTEL_PROPAGATORS
6565
from opentelemetry.propagators import composite, textmap
66-
from opentelemetry.util._importlib_metadata import entry_points
6766

6867
logger = getLogger(__name__)
6968

@@ -109,46 +108,66 @@ def inject(
109108
get_global_textmap().inject(carrier, context=context, setter=setter)
110109

111110

112-
propagators: list[textmap.TextMapPropagator] = []
113-
114-
# Single use variable here to hack black and make lint pass
115-
environ_propagators = environ.get(
116-
OTEL_PROPAGATORS,
117-
"tracecontext,baggage",
118-
)
111+
def _load_propagators() -> textmap.TextMapPropagator:
112+
configured = environ.get(OTEL_PROPAGATORS)
113+
if not configured:
114+
# pylint: disable=import-outside-toplevel,no-name-in-module
115+
from opentelemetry.baggage.propagation import ( # noqa: PLC0415
116+
W3CBaggagePropagator,
117+
)
119118

119+
# pylint: disable=import-outside-toplevel,no-name-in-module
120+
from opentelemetry.trace.propagation.tracecontext import ( # noqa: PLC0415
121+
TraceContextTextMapPropagator,
122+
)
120123

121-
for propagator in environ_propagators.split(","):
122-
propagator = propagator.strip()
123-
if propagator.lower() == "none":
124-
logger.debug(
125-
"OTEL_PROPAGATORS environment variable contains none, removing all propagators"
124+
return composite.CompositePropagator(
125+
[TraceContextTextMapPropagator(), W3CBaggagePropagator()]
126126
)
127-
propagators = []
128-
break
129-
try:
130-
propagators.append(
131-
next( # type: ignore
132-
iter( # type: ignore
133-
entry_points( # type: ignore[misc]
134-
group="opentelemetry_propagator",
135-
name=propagator,
127+
128+
# pylint: disable=import-outside-toplevel,no-name-in-module
129+
from opentelemetry.util._importlib_metadata import ( # noqa: PLC0415
130+
entry_points,
131+
)
132+
133+
_propagators: list[textmap.TextMapPropagator] = []
134+
for _propagator in configured.split(","):
135+
_propagator = _propagator.strip()
136+
if _propagator.lower() == "none":
137+
logger.debug(
138+
"OTEL_PROPAGATORS environment variable contains none, removing all propagators"
139+
)
140+
return composite.CompositePropagator([])
141+
try:
142+
_propagators.append(
143+
next( # type: ignore
144+
iter( # type: ignore
145+
entry_points( # type: ignore[misc]
146+
group="opentelemetry_propagator",
147+
name=_propagator,
148+
)
136149
)
137-
)
138-
).load()()
139-
)
140-
except StopIteration:
141-
raise ValueError(
142-
f"Propagator {propagator} not found. It is either misspelled or not installed."
143-
)
144-
except Exception: # pylint: disable=broad-exception-caught
145-
logger.exception("Failed to load propagator: %s", propagator)
146-
raise
150+
).load()()
151+
)
152+
except StopIteration:
153+
raise ValueError(
154+
f"Propagator {_propagator} not found. It is either misspelled or not installed."
155+
)
156+
except Exception: # pylint: disable=broad-exception-caught
157+
logger.exception("Failed to load propagator: %s", _propagator)
158+
raise
159+
return composite.CompositePropagator(_propagators)
160+
161+
162+
_HTTP_TEXT_FORMAT: textmap.TextMapPropagator = _load_propagators()
147163

164+
# Deprecated: propagators, environ_propagators and propagator names were never intendended to be part of the public API.
165+
propagators: list[textmap.TextMapPropagator] = []
166+
167+
environ_propagators = environ.get(OTEL_PROPAGATORS, "tracecontext,baggage")
148168

149-
_HTTP_TEXT_FORMAT: textmap.TextMapPropagator = composite.CompositePropagator(
150-
propagators
151-
)
169+
for propagator in environ_propagators.split(","): # type: ignore[assignment]
170+
propagator = propagator.strip()
152171

153172

154173
def get_global_textmap() -> textmap.TextMapPropagator:

opentelemetry-api/src/opentelemetry/util/_providers.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
from os import environ
66
from typing import TYPE_CHECKING, TypeVar, cast
77

8-
from opentelemetry.util._importlib_metadata import entry_points
9-
108
if TYPE_CHECKING:
119
from opentelemetry.metrics import MeterProvider
1210
from opentelemetry.trace import TracerProvider
@@ -19,6 +17,11 @@
1917
def _load_provider(
2018
provider_environment_variable: str, provider: str
2119
) -> Provider: # type: ignore[type-var]
20+
# pylint: disable=import-outside-toplevel,no-name-in-module
21+
from opentelemetry.util._importlib_metadata import ( # noqa: PLC0415
22+
entry_points,
23+
)
24+
2225
try:
2326
provider_name = cast(
2427
str,

0 commit comments

Comments
 (0)