Skip to content

Types inheriting from GenericAlias types (such as Mapping) cannot be decoded. #957

@mwaddoups

Description

@mwaddoups

Description

If you try to make a generic class that inherits from one of the _SpecialGenericAlias types (such as Mapping), the type inference fails to work.

A minimal example below (Python 3.13, msgspec 0.20.0):

from collections.abc import Mapping
from dataclasses import dataclass

import msgspec


@dataclass
class Bar[T](Mapping[str, T]):
    data: dict[str, T]

    def __getitem__(self, x):
        return self.data[x]

    def __len__(self):
        return len(self.data)

    def __iter__(self):
        return iter(self.data)


x: Bar[int] = Bar({"x": 3})

msgspec.msgpack.decode(msgspec.msgpack.encode(x), type=Bar[int])

with traceback

Traceback (most recent call last):
  File "<censored>", line 31, in <module>
    msgspec.msgpack.decode(msgspec.msgpack.encode(x), type=Bar[int])
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/venv/lib/python3.13/site-packages/msgspec/_utils.py", line 252, in get_dataclass_info
    hints = get_class_annotations(obj)
  File "/opt/venv/lib/python3.13/site-packages/msgspec/_utils.py", line 149, in get_class_annotations
    mro, typevar_mappings = _get_class_mro_and_typevar_mappings(obj)
                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
  File "/opt/venv/lib/python3.13/site-packages/msgspec/_utils.py", line 126, in _get_class_mro_and_typevar_mappings
    inner(obj, {})
    ~~~~~^^^^^^^^^
  File "/opt/venv/lib/python3.13/site-packages/msgspec/_utils.py", line 124, in inner
    inner(b, new_scope)
    ~~~~~^^^^^^^^^^^^^^
  File "/opt/venv/lib/python3.13/site-packages/msgspec/_utils.py", line 116, in inner
    params = cls.__parameters__
             ^^^^^^^^^^^^^^^^^^
AttributeError: type object 'Mapping' has no attribute '__parameters__'

I believe this is caused by the logic in inner(...) in the utils. For this, we look up __origin__ - however, in these specific cases, the types library has typing.Mapping as a typing._SpecialGenericAlias, which has different behaviour - it's __origin__ is not typing.Mapping, it's collections.abc.Mapping. This has no generic type parameters so inference fails.

This looks extremely tough to wade through so not sure what a good workaround is here - not a typing expert!

I'll note briefly that this behaviour goes away if it's not generic in inheritance, but it still fails if I use msgspec.Struct and a combined metaclass from StructMeta and ABCMeta so is not an artifact of dataclasses or anything, I think.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions