SIGN IN SIGN UP

opentelemetry-configuration: treat present-null config value as empty mapping (#5454)

* Treat present-null config value as empty mapping (#5451)

A YAML mapping key present with an empty (null) value parses to `None`,
which the config conversion layer could not distinguish from an absent
key. Since sampler and resource-detector type dispatch selects a type via
`is not None`, writing a leaf node as `always_on:` (rather than
`always_on: {}`) produced an all-`None` config and failed with
"Unsupported sampler type in config"; detectors written as `- service:`
were silently skipped.

Per the declarative-config spec a key present with a null value on a
`dict[str, Any]`-typed node means "select this with an empty config", so
coerce such a `None` to an empty mapping in `_convert_value`. Scalar and
dataclass fields keep `None`, so an absent optional section stays unset.

* Extend present-null coercion to empty-constructible dataclasses (#5451)

Address review feedback: the empty-value issue affects not only dict-aliased
nodes (sampler/detector leaves) but any dataclass config that can be
instantiated with no arguments, e.g. a metric `console:` exporter written
without an explicit `console: {}`.

_convert_value now also coerces a present null to a defaulted dataclass
instance when the annotated type is a dataclass whose fields are all
optional. Dataclasses with required fields, and scalar fields, keep None so
an absent optional section stays unset and no TypeError is raised.

Adds regression coverage for the null-valued metric console exporter and for
the required-field guard.

* Reuse dict/dataclass handling for present-null coercion (#5451)

Adopt @emdneto's suggestion to substitute an empty mapping and let the
existing dict/dataclass conversion path build it, instead of a separate
coercion helper.

Keep an "all fields optional" guard (_is_empty_constructible_dataclass):
21 config dataclasses have required fields (e.g. BatchSpanProcessor.exporter,
PeriodicMetricReader.exporter), and coercing a present null into those via
cls() would raise TypeError. Leaving them None routes to the existing
"Unsupported type" ConfigurationError instead.

* Test the guard with the real jaeger_remote_development case (#5451)

Replace the synthetic required-field dataclass fixture with the one real
node that is schema-nullable yet has required fields:
ExperimentalJaegerRemoteSampler (endpoint, initial_sampler).

Unlike batch/periodic (schema type: object, so a null is rejected before
conversion), jaeger_remote_development is schema type: [object, null], so
jaeger_remote_development: passes validation and reaches the conversion
layer. Coercing it would raise TypeError; the guard leaves it None.

Adds an end-to-end loader test proving the schema accepts the null and
conversion survives, alongside a sibling console: node that is still
coerced to {}.

* Reuse the _load helper with an optional yaml arg (#5451)

Address review: instead of re-implementing the tempfile write/load/unlink
dance in the new test, give TestConfigLoaderEndToEnd._load an optional yaml
parameter (defaulting to the class _YAML) and pass the jaeger config to it.
A
Allen Kim committed
4ea521a73c0f7ad67b7c04dff1e1dc6d00324a69
Parent: b74d45f
Committed by GitHub <noreply@github.com> on 7/24/2026, 2:33:42 PM