From b6a42b6dcd7d5e398bb5b5917e6f072bb625e832 Mon Sep 17 00:00:00 2001 From: emdneto <9735060+emdneto@users.noreply.github.com> Date: Mon, 6 Jul 2026 20:36:53 -0300 Subject: [PATCH 1/3] support in the field name for declarative configuration Signed-off-by: emdneto <9735060+emdneto@users.noreply.github.com> --- .../sdk/_configuration/_conversion.py | 14 +++++++---- .../tests/_configuration/test_conversion.py | 23 +++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_conversion.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_conversion.py index eb89c747a44..297043d67f4 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_conversion.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_conversion.py @@ -77,7 +77,7 @@ def _convert_value(value: Any, type_hint: Any) -> Any: def _dict_to_dataclass(data: Mapping[str, Any], cls: type[_T]) -> _T: """Recursively convert a mapping to a dataclass instance. - For each key in ``data``: + For each (normalized) key in ``data``: - If it matches a known dataclass field, the value is converted according to that field's type annotation (recursing for nested dataclasses). - Unknown keys are passed through as kwargs; classes decorated with @@ -103,11 +103,15 @@ def _dict_to_dataclass(data: Mapping[str, Any], cls: type[_T]) -> _T: kwargs: dict[str, Any] = {} for key, value in data.items(): - if key in known_fields: - type_hint = hints.get(key) - kwargs[key] = _convert_value(value, type_hint) + # OpenTelemetry Configuration schema uses "/" to separate the feature from its + # stability level (e.g. "detection/development"). Normalize to "_" so + # the key matches valid Python dataclass field names. + normalized_key = key.replace("/", "_") + if normalized_key in known_fields: + type_hint = hints.get(normalized_key) + kwargs[normalized_key] = _convert_value(value, type_hint) else: # Unknown key — @_additional_properties decorator will capture it. - kwargs[key] = value + kwargs[normalized_key] = value return cls(**kwargs) diff --git a/opentelemetry-sdk/tests/_configuration/test_conversion.py b/opentelemetry-sdk/tests/_configuration/test_conversion.py index c6cc082e1e3..be674608d2f 100644 --- a/opentelemetry-sdk/tests/_configuration/test_conversion.py +++ b/opentelemetry-sdk/tests/_configuration/test_conversion.py @@ -42,6 +42,12 @@ class _WithEnum: filter: ExemplarFilter | None = None +@dataclass +class _WithSlashField: + detection_development: str | None = None + name: str | None = None + + class TestDictToDataclass(unittest.TestCase): def test_raises_on_non_dataclass(self): # _dict_to_dataclass is internal and assumes cls is a dataclass. @@ -108,3 +114,20 @@ def test_enum_value_already_enum_passes_through(self): {"filter": ExemplarFilter.trace_based}, _WithEnum ) self.assertIs(result.filter, ExemplarFilter.trace_based) + + def test_slash_key_mapped_to_underscore_field(self): + result = _dict_to_dataclass( + {"name": "hello", "detection/development": "some_value"}, + _WithSlashField, + ) + self.assertEqual(result.name, "hello") + self.assertEqual(result.detection_development, "some_value") + + def test_slash_key_normalized_in_additional_properties(self): + result = _dict_to_dataclass( + {"known": "yes", "detection/development": "value"}, _WithExtras + ) + self.assertEqual(result.known, "yes") + self.assertEqual( + result.additional_properties, {"detection_development": "value"} + ) From 7d2690d46982ae8305e7d2554b4a40dfe27b8bb3 Mon Sep 17 00:00:00 2001 From: emdneto <9735060+emdneto@users.noreply.github.com> Date: Mon, 6 Jul 2026 20:40:17 -0300 Subject: [PATCH 2/3] fix example Signed-off-by: emdneto <9735060+emdneto@users.noreply.github.com> --- opentelemetry-sdk/tests/_configuration/test_conversion.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/opentelemetry-sdk/tests/_configuration/test_conversion.py b/opentelemetry-sdk/tests/_configuration/test_conversion.py index be674608d2f..b0deafc38b3 100644 --- a/opentelemetry-sdk/tests/_configuration/test_conversion.py +++ b/opentelemetry-sdk/tests/_configuration/test_conversion.py @@ -125,9 +125,7 @@ def test_slash_key_mapped_to_underscore_field(self): def test_slash_key_normalized_in_additional_properties(self): result = _dict_to_dataclass( - {"known": "yes", "detection/development": "value"}, _WithExtras + {"known": "yes", "foo/bar": "value"}, _WithExtras ) self.assertEqual(result.known, "yes") - self.assertEqual( - result.additional_properties, {"detection_development": "value"} - ) + self.assertEqual(result.additional_properties, {"foo_bar": "value"}) From 3d36916c23b19d4fee1ea06f25e8dff783f33c2d Mon Sep 17 00:00:00 2001 From: emdneto <9735060+emdneto@users.noreply.github.com> Date: Mon, 6 Jul 2026 20:41:20 -0300 Subject: [PATCH 3/3] add changelog Signed-off-by: emdneto <9735060+emdneto@users.noreply.github.com> --- .changelog/5396.fixed | 1 + 1 file changed, 1 insertion(+) create mode 100644 .changelog/5396.fixed diff --git a/.changelog/5396.fixed b/.changelog/5396.fixed new file mode 100644 index 00000000000..35535c1e72d --- /dev/null +++ b/.changelog/5396.fixed @@ -0,0 +1 @@ +`opentelemetry-configuration`: support slashes in the field name for declarative configuration