Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- `opentelemetry-sdk`: add propagator plugin loading to declarative file configuration via the `opentelemetry_propagator` entry point group, matching the spec's PluginComponentProvider mechanism
([#5098](https://github.com/open-telemetry/opentelemetry-python/pull/5098))
- `opentelemetry-api`: Enforce W3C Baggage size limits on outbound propagation in `W3CBaggagePropagator.inject()`. Previously only inbound extraction enforced limits; now inject also caps entries at 180, individual pairs at 4096 bytes, and total header at 8192 bytes per the W3C Baggage spec. The extract path max_pairs limit now counts all size-valid entries rather than only successfully parsed ones.
([#5163](https://github.com/open-telemetry/opentelemetry-python/pull/5163))
- `opentelemetry-sdk`: add `additional_properties` support to generated config models via custom `datamodel-codegen` template, enabling plugin/custom component names to flow through typed dataclasses
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,37 @@
TraceContextTextMapPropagator,
)


def _load_entry_point_propagator(name: str) -> TextMapPropagator:
"""Load and instantiate a propagator by name."""
return load_entry_point("opentelemetry_propagator", name)()
# Propagators bundled with the SDK — no entry point lookup needed.
_PROPAGATOR_REGISTRY: dict = {
"tracecontext": lambda _: TraceContextTextMapPropagator(),
"baggage": lambda _: W3CBaggagePropagator(),
}


def _propagators_from_textmap_config(
config: TextMapPropagatorConfig,
) -> list[TextMapPropagator]:
"""Resolve a single TextMapPropagator config entry to a list of propagators."""
"""Resolve a TextMapPropagator config to a list of propagators.

Known names (tracecontext, baggage) are bootstrapped directly via
_PROPAGATOR_REGISTRY. Known schema fields not in the registry (b3, b3multi)
and unknown plugin names from additional_properties are loaded via the
``opentelemetry_propagator`` entry point group.
"""
result: list[TextMapPropagator] = []
if config.tracecontext is not None:
result.append(TraceContextTextMapPropagator())
if config.baggage is not None:
result.append(W3CBaggagePropagator())
if config.b3 is not None:
result.append(_load_entry_point_propagator("b3"))
if config.b3multi is not None:
result.append(_load_entry_point_propagator("b3multi"))
for name in _PROPAGATOR_REGISTRY:
if getattr(config, name, None) is not None:
result.append(_PROPAGATOR_REGISTRY[name](getattr(config, name)))

# Known schema fields not in registry (b3, b3multi) — loaded via entry point
for name in ("b3", "b3multi"):
if getattr(config, name, None) is not None:
result.append(load_entry_point("opentelemetry_propagator", name)())

# Plugin propagators from additional_properties
for name in config.additional_properties:
result.append(load_entry_point("opentelemetry_propagator", name)())

return result


Expand Down Expand Up @@ -85,7 +97,7 @@ def create_propagator(
name = name.strip()
if not name or name.lower() == "none":
continue
propagator = _load_entry_point_propagator(name)
propagator = load_entry_point("opentelemetry_propagator", name)()
propagators.setdefault(type(propagator), propagator)

return CompositePropagator(list(propagators.values()))
Expand Down
34 changes: 34 additions & 0 deletions opentelemetry-sdk/tests/_configuration/test_propagator.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,40 @@ def test_unknown_composite_list_propagator_raises(self):
with self.assertRaises(ConfigurationError):
create_propagator(config)

def test_plugin_propagator_via_entry_point(self):
mock_propagator = MagicMock()
mock_ep = MagicMock()
mock_ep.load.return_value = lambda: mock_propagator

with patch(
"opentelemetry.sdk._configuration._common.entry_points",
return_value=[mock_ep],
):
config = PropagatorConfig(
composite=[
# pylint: disable=unexpected-keyword-arg
TextMapPropagatorConfig(my_custom_propagator={})
]
)
result = create_propagator(config)

self.assertEqual(len(result._propagators), 1) # type: ignore[attr-defined]
self.assertIs(result._propagators[0], mock_propagator) # type: ignore[attr-defined]

def test_unknown_composite_propagator_raises(self):
with patch(
"opentelemetry.sdk._configuration._common.entry_points",
return_value=[],
):
config = PropagatorConfig(
composite=[
# pylint: disable=unexpected-keyword-arg
TextMapPropagatorConfig(nonexistent={})
]
)
with self.assertRaises(ConfigurationError):
create_propagator(config)


class TestConfigurePropagator(unittest.TestCase):
def test_configure_propagator_calls_set_global_textmap(self):
Expand Down
Loading