Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/5396.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`opentelemetry-configuration`: support slashes in the field name for declarative configuration
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Comment on lines +109 to 113

@xrmx xrmx Jul 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT on using a double underscore instead just to be a bit more wary of name collisions?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xrmx The generated dataclass fields currently use single underscores, so if we do that, we need to modify the schema generation to represent the known_fields with double underscores as well. I can implement the collision detection to raise a TypeError in case of collisions

# Unknown key — @_additional_properties decorator will capture it.
kwargs[key] = value
kwargs[normalized_key] = value

return cls(**kwargs)
21 changes: 21 additions & 0 deletions opentelemetry-sdk/tests/_configuration/test_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -108,3 +114,18 @@ 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", "foo/bar": "value"}, _WithExtras
)
self.assertEqual(result.known, "yes")
self.assertEqual(result.additional_properties, {"foo_bar": "value"})
Loading