Skip to content

MergeableMixin

MergeableMixin adds merge capabilities to any @dataclasses.dataclass.

Basic usage

import dataclasses
from pleroma import MergeableMixin

@dataclasses.dataclass
class ServerConfig(MergeableMixin):
    host: str | None = None
    port: int | None = None
    timeout: int | None = None

base    = ServerConfig(host="0.0.0.0", port=8080, timeout=30)
overlay = ServerConfig(port=9090)

result = ServerConfig.merge([base, overlay])
# ServerConfig(host='0.0.0.0', port=9090, timeout=30)

Instance shorthand

merged_with is equivalent to prepending self to the collection:

result = base.merged_with(overlay)
# identical to ServerConfig.merge([base, overlay])

Merge strategy

The default strategy is last non-None wins: for each field, the value from the rightmost instance that is not None is chosen.

a = ServerConfig(host="a", port=None)
b = ServerConfig(host=None, port=9090)

ServerConfig.merge([a, b])
# ServerConfig(host='a', port=9090, timeout=None)

Strict last-wins

Pass overwrite_none=True to let None values overwrite non-None ones (strict left-to-right last-wins):

ServerConfig.merge([a, b], overwrite_none=True)
# ServerConfig(host=None, port=9090, timeout=None)

Customisation

Changing traversal order

Override _iter_instances to control which instances participate and in what order. pleroma provides predefined strategies (iter_all, iter_reversed, iter_first, iter_last) that cover the most common cases:

import dataclasses
from pleroma import MergeableMixin, iter_reversed

@dataclasses.dataclass
class Config(MergeableMixin):
    host: str | None = None

    @classmethod
    def _iter_instances(cls, instances):
        return iter_reversed(instances)

For custom ordering, implement the override directly:

@dataclasses.dataclass
class PriorityConfig(MergeableMixin):
    level: int
    value: str | None = None

    @classmethod
    def _iter_instances(cls, instances):
        # merge in ascending priority order so higher priority wins
        return iter(sorted(instances, key=lambda c: c.level))

Custom field resolution

Override _merge_two for full control over how two instances are combined:

@dataclasses.dataclass
class TaggedConfig(MergeableMixin):
    tags: list[str] | None = None
    value: str | None = None

    def _merge_two(self, other, *, overwrite_none=False):
        # union tags instead of overwriting
        self_tags = self.tags or []
        other_tags = other.tags or []
        merged_tags = list(dict.fromkeys(self_tags + other_tags)) or None
        base = super()._merge_two(other, overwrite_none=overwrite_none)
        return dataclasses.replace(base, tags=merged_tags)