jcrist / msgspec

A fast serialization and validation library, with builtin support for JSON, MessagePack, YAML, and TOML

Home Page:https://jcristharif.com/msgspec/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Subclasses of frozen Structs causing mypy error: `Cannot inherit non-frozen dataclass from a frozen one`

wikiped opened this issue · comments

Description

With the following code:

import msgspec as ms


class Base(ms.Struct, frozen=True):
    ...


class Child(Base):
    ...


assert Child.__struct_config__.frozen is True

In a file: frozen.py

And the following command:

python -m mypy ./frozen.py

Outputs a false positive:

<...>frozen.py:8: error: Cannot inherit non-frozen dataclass from a frozen one  [misc]
Found 1 error in 1 file (checked 1 source file)

In a similar mypy issue the cause has been attributed to pydantic's use of dataclass_transform which might be having similar effects here.

This has been tested with:

mypy: 1.9.0 (compiled: yes)
msgspec: 0.18.6

Yeah, this is a restriction of how dataclass_transform works (PEP 681). Since we want to support both mypy and pyright, I'm unwilling to fix this by writing a custom mypy extension - we want to follow the upstream python standards alone. You have two options:

  • pass frozen=True to the definition of Child as well (this is what I recommend)
  • Add type: ignore on the same line as class Child. This will ignore that error in mypy, but won't catch issues where you try to mutate Child.

We run into the same option-isn't-inherited issue with kw_only (which we made non-inherited to match what mypy/pyright expect). In that case I'm considering adding a new base class like KWStruct that defines the default to be kw_only=True, which would work better with dataclass_transform for cases where you want all subclasses to be keyword-only.

We could do the same here and define a FrozenStruct base class that defaults to frozen=True. If we do this, we might also want to change frozen to be non-inherited to match the dataclass_transform implementation.

Thanks for quick feedback.

I have had considered two options you mentioned, but both look like an "inconvience" and therefore was hoping to find "a better way" to address this.

The FrozenStruct idea (and KWStruct) sounds like both developer and linter friendly way to solve this.