Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dataclass inheritance doesn't work when base class is decoded first #598

Closed
clint-enki opened this issue Dec 4, 2023 · 1 comment · Fixed by #599
Closed

Dataclass inheritance doesn't work when base class is decoded first #598

clint-enki opened this issue Dec 4, 2023 · 1 comment · Fixed by #599

Comments

@clint-enki
Copy link

clint-enki commented Dec 4, 2023

Description

When using inheritance in dataclasses, msgspec decodes a derived class as it's base class when the base class is decoded once beforehand. I've made an minimal example to replicate the issue.

a.py:

from dataclasses import dataclass


@dataclass
class Base:
    a: int


@dataclass
class Foo(Base):
    b: int

b.py:

import msgspec


def test_decode():
    # Base class decoded first
    from a import Base

    x = Base(a=1)
    ser = msgspec.json.encode(x)
    dser = msgspec.json.decode(ser, type=Base)
    assert x == dser

    from a import Foo

    x = Foo(a=1, b=2)
    ser = msgspec.json.encode(x)
    dser = msgspec.json.decode(ser, type=Foo)
    assert x == dser


if __name__ == "__main__":
    test_decode()

Running pytest:

pytest b.py
===================================================== test session starts =====================================================
platform linux -- Python 3.11.6, pytest-7.4.3, pluggy-1.3.0
rootdir: /tmp/
plugins: anyio-4.0.0
collected 2 items

b.py F                                                                                                                  [100%]
========================================================== FAILURES ===========================================================
_________________________________________________________ test_decode _________________________________________________________

    def test_decode():
        from a import Base

        x = Base(a=1)
        ser = msgspec.yaml.encode(x)
        dser = msgspec.yaml.decode(ser, type=Base)
        assert x == dser

        from a import Foo

        x = Foo(a=1, b=2)
        ser = msgspec.yaml.encode(x)
        dser = msgspec.yaml.decode(ser, type=Foo)
>       assert x == dser
E       assert Foo(a=1, b=2) == Base(a=1)

b.py:20: AssertionError
=================================================== short test summary info ===================================================
FAILED b.py::test_decode - assert Foo(a=1, b=2) == Base(a=1)
====================================================== 1 failed in 0.04s ======================================================

However, if the derived class is decoded first then the base class is correctly decoded. This works perfectly:

def test_decode():
    # derived class decoded first
    from a import Foo

    x = Foo(a=1, b=2)
    ser = msgspec.yaml.encode(x)
    dser = msgspec.yaml.decode(ser, type=Foo)
    assert x == dser

    from a import Base

    x = Base(a=1)
    ser = msgspec.yaml.encode(x)
    dser = msgspec.yaml.decode(ser, type=Base)
    assert x == dser

Tested with:

  • Python 3.11.6 and 3.9.18
  • msgspec 0.18.4
@jcrist
Copy link
Owner

jcrist commented Dec 4, 2023

Thanks for the excellent issue report - this was a subtle issue in our caching implementation. Fixed in #599.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants