jcrist / msgspec

A fast serialization and validation library, with builtin support for JSON, MessagePack, YAML, and TOML
https://jcristharif.com/msgspec/
BSD 3-Clause "New" or "Revised" License
2.3k stars 67 forks source link

How to add multiple Mixins that declare additional fields? #590

Closed Yolley closed 10 months ago

Yolley commented 10 months ago

Question

Some of my structs share common fields that I would like declare as mixins and inherit from these Mixins in Struct implementation like so:

class ActiveMixin:
    active: bool

class CreatedUpdatedMixin:
    created: datetime
    updated: datetime

class ImplementationStruct(msgspec.Struct, ActiveMixin, CreatedUpdatedMixin):
    field_1: str

Fields that were declared in Mixins are not added to __struct_fields__, therefore there are not parsed when I try to decode a JSON with these fields presented. And it doesn't matter whether I change order of inheritance or not.

I've tried to to do differently so that each Mixin inherits from msgspec.Struct

class ActiveMixin(msgspec.Struct):
    active: bool

class CreatedUpdatedMixin(msgspec.Struct):
    created: datetime
    updated: datetime

class ImplementationStruct(ActiveMixin, CreatedUpdatedMixin):
    field_1: str

But now I get an error

    class ImplementationStruct(ActiveMixin, CreatedUpdatedMixin):
TypeError: multiple bases have instance lay-out conflict

So, is it possible to declare Mixins somehow and inherit from them to no not duplicate declaration of the same fields in each Struct?

wikiped commented 10 months ago

@Yolley You might want to have a look at Tagged Unions part of the docs. There is an example of inheritance.

Since Structs behave like builtin classes (which in most cases are C-based as well) - one way to avoid lay-out conflict is to follow Single Inheritance rule for every descendant from a Base class. I.e.:

from msgspec import Struct

class Base(Struct, kw_only=True):  # kw_only=True - is just an example
    """This will ensure all children will have the same ___struct_config__"""
    ...

class TimedStamped(Base):
    created: datetime
    updated datetime

class ActiveStamped(TimeStamped):
    active: bool
Yolley commented 10 months ago

@Yolley You might want to have a look at Tagged Unions part of the docs. There is an example of inheritance.

Since Structs behave like builtin classes (which in most cases are C-based as well) - one way to avoid lay-out conflict is to follow Single Inheritance rule for every descendant from a Base class. I.e.:

from msgspec import Struct

class Base(Struct, kw_only=True):  # kw_only=True - is just an example
    """This will ensure all children will have the same ___struct_config__"""
    ...

class TimedStamped(Base):
    created: datetime
    updated datetime

class ActiveStamped(TimeStamped):
    active: bool

So that's the only way, will need to duplicate field definitions across different base Mixins then. I see, thank you.