msgspec icon indicating copy to clipboard operation
msgspec copied to clipboard

Structs with dictionaries with enum keys do not encode

Open ahrnbom opened this issue 10 months ago • 4 comments

Description

On msgspec 0.19.0, I get this behaviour:

from typing import Dict
from enum import Enum
import msgspec


class SomeEnum(Enum):
    EXAMPLE_VALUE = "hello"


class SomeModel(msgspec.Struct):
    values: Dict[SomeEnum, int] = {}


def test_msgspec_logic():
    a = SomeModel()
    a.values[SomeEnum.EXAMPLE_VALUE] = 2
    msgspec.json.encode(a)

if __name__ == "__main__":
    test_msgspec_logic()

I get this error:

(venv) abone@fedora:~/mflu/mflu/tests$ python3 test_models.py 
Traceback (most recent call last):
  File "/home/abone/mflu/mflu/tests/test_models.py", line 20, in <module>
    test_msgspec_logic()
    ~~~~~~~~~~~~~~~~~~^^
  File "/home/abone/mflu/mflu/tests/test_models.py", line 17, in test_msgspec_logic
    msgspec.json.encode(a)
    ~~~~~~~~~~~~~~~~~~~^^^
TypeError: Only dicts with str-like or number-like keys are supported

This used to work in 0.18.6 on Python 3.12, but with 0.19.0 on Python 3.13, it does not.

I am currently making a game that relies a lot on this behaviour, so I will gladly help with narrowing the problem down or whatever else I can do.

ahrnbom avatar Jan 12 '25 07:01 ahrnbom

I just confirmed that this behaviour is tied to msgspec 0.19.0 itself, running 0.19.0 in Python 3.12 has the same bug.

ahrnbom avatar Jan 12 '25 14:01 ahrnbom

I can also produce in Python 3.11, msgspec==0.19.0 fails while msgspec==0.18.6 passes without issue. Decoding also seems to work fine. Seems like a reasonably significant regression

jack-mcivor avatar Jan 12 '25 14:01 jack-mcivor

@ahrnbom

Got curious and tried something:

from typing import Dict
from enum import Enum
import msgspec


# inherit from both str and Enum
class SomeEnum(str, Enum):
    EXAMPLE_VALUE = "hello"


class SomeModel(msgspec.Struct):
    values: Dict[SomeEnum, int] = {}


def test_msgspec_logic():
    a = SomeModel()
    a.values[SomeEnum.EXAMPLE_VALUE] = 2
    msgspec.json.encode(a)

if __name__ == "__main__":
    test_msgspec_logic()

It does not count as a fix since msgspec should still work with native Enum (I guess at least), but inheriting from str (since your enumerator is a string type) could be used as a workaround

jacopoabramo avatar Feb 06 '25 16:02 jacopoabramo

Here's a PR that fixes this particular problem: https://github.com/jcrist/msgspec/pull/838

ahrnbom avatar Apr 25 '25 22:04 ahrnbom