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.45k stars 76 forks source link

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

Closed Yolley closed 1 year ago

Yolley commented 1 year 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 1 year 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 1 year 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.