Skip to content

Fix for when obj is UnionType in _utils._apply_params #769

Closed

Description

@treykeown

Description

Hi. I'm using Python 3.13 and installing the package directly from this git repo.

I had an issue with _utils._apply_params - I don't have a minimal example, but here's the traceback:

...
  File ".../mycode.py", line 264, in decode
    return msgspec.msgpack.decode(item, **kwargs)
    ^^^^^^^^^^^^^^^
  File ".../.venv/lib/python3.13/site-packages/msgspec/_utils.py", line 111, in get_class_annotations
    mro, typevar_mappings = _get_class_mro_and_typevar_mappings(obj)
      ^^^^^^^^^^^^^^^^^
  File ".../.venv/lib/python3.13/site-packages/msgspec/_utils.py", line 88, in _get_class_mro_and_typevar_mappings
    inner(obj, {})
      ~~~^^^^^^^^^
  File ".../.venv/lib/python3.13/site-packages/msgspec/_utils.py", line 86, in inner
    inner(b, new_scope)
    ^^^^^^^
  File ".../.venv/lib/python3.13/site-packages/msgspec/_utils.py", line 79, in inner
    args = tuple(_apply_params(a, scope) for a in c.__args__)
    ^^^^^^^^^^^
  File ".../.venv/lib/python3.13/site-packages/msgspec/_utils.py", line 79, in <genexpr>
    args = tuple(_apply_params(a, scope) for a in c.__args__)
    ^^^^^^^^^^^
  File ".../.venv/lib/python3.13/site-packages/msgspec/_utils.py", line 55, in _apply_params
    args = tuple(mapping.get(p, p) for p in params)
    ^^^^^^^^^^^^^^^
TypeError: 'getset_descriptor' object is not iterable

I found that this was due to _utils._apply_params being called with obj == types.UnionType. UnionType returns a descriptor for __parameters__.

>>> type(int | str).__parameters__
<attribute '__parameters__' of 'types.UnionType' objects>

I've pasted my workaround below:

def _apply_params(obj, mapping):
    import types
    if (params := getattr(obj, "__parameters__", None)) and (obj != types.UnionType):
        args = tuple(mapping.get(p, p) for p in params)
        return obj[args]
    elif isinstance(obj, typing.TypeVar):
        return mapping.get(obj, obj)
    return obj

Activity

  1. jcrist commented on Nov 12, 2024

    @jcrist
    Member

    Thanks for opening this - can you please spend the time producing a minimal example? Without an example demonstrating the failure we have no way to check if the fix is correct.

  2. treykeown commented on Nov 15, 2024

    @treykeown
    Author
    from types import UnionType
    
    import msgspec
    
    if __name__ == "__main__":
        class Base[T](msgspec.Struct, frozen=True, kw_only=True, tag=True): ...
    
        class Buggy(Base[UnionType], frozen=True, kw_only=True): ...
        # This works fine:
        # from typing import Union
        # class Buggy(Base[Union], frozen=True, kw_only=True): ...
    
        class Container(msgspec.Struct, frozen=True):
            item: Buggy
    
        enc = msgspec.msgpack.encode(Container(item=Buggy()))
        msgspec.msgpack.decode(enc, type=Container)
  3. jcrist commented on Nov 15, 2024

    @jcrist
    Member

    Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions