Skip to main content
Version: 2.4 (prerelease)

Migrate tuple usage to 2.4

OmegaConf 2.4 preserves native Python tuples as structurally immutable TupleConfig values. Earlier releases converted them to mutable ListConfig values. This is a breaking change if your code assumes every OmegaConf sequence is a list or mutates a config created from a tuple.

What changed​

>>> from omegaconf import OmegaConf
>>> cfg = OmegaConf.create({"coords": (1, 2)})
>>> OmegaConf.is_tuple(cfg.coords)
True
>>> OmegaConf.is_sequence(cfg.coords)
True
>>> OmegaConf.is_list(cfg.coords)
False

A TupleConfig does not allow elements to be inserted, removed, or replaced. Nested mutable containers remain mutable. OmegaConf.to_container() converts it back to a native tuple.

Choose the intended sequence type​

If the value should be mutable, create it from a list:

>>> cfg = OmegaConf.create({"coords": list((1, 2))})
>>> cfg.coords.append(3)
>>> list(cfg.coords)
[1, 2, 3]

For a structured config, annotate mutable sequences as list[T] or typing.List[T]. If the value is conceptually a tuple, keep the tuple input or use a tuple annotation. Replace the complete value through its mutable parent when needed:

>>> cfg = OmegaConf.create({"coords": (1, 2)})
>>> cfg.coords = (1, 2, 3)
>>> tuple(cfg.coords)
(1, 2, 3)

An untyped tuple accepts any length on replacement. A fixed annotation such as tuple[int, int] requires two elements; tuple[int, ...] permits any number of integers.

Update sequence checks​

Use OmegaConf.is_tuple(value) for tuple-specific behavior, OmegaConf.is_list(value) for mutable-list behavior, and OmegaConf.is_sequence(value) when either kind of sequence is accepted. Code that used isinstance(value, ListConfig) for general indexed traversal should usually use OmegaConf.is_sequence(value).

Check code that appends to config sequences, assigns their elements, or expects tuple input to become a list. Tuple semantics remain experimental in 2.4; feedback is welcome in issue #392.