jcrist / msgspec

A fast serialization and validation library, with builtin support for JSON, MessagePack, YAML, and TOML
https://jcristharif.com/msgspec/
BSD 3-Clause "New" or "Revised" License
2.43k stars 75 forks source link

Converting an object / dict to a struct where the struct contains a Raw field fails with "Expected `any`, got ..." #699

Closed indrat closed 1 month ago

indrat commented 5 months ago

Description

Description

Converting using msgspec.convert from an object with from_attributes=True to a struct that contains a Raw field doesn't appear to work.

Example

Given a struct like:

class Wrapper(msgspec.Struct):
    result: msgspec.Raw | msgspec.UnsetType = msgspec.UNSET

That is used to deserialise json successfully, e.g b'{"result": {"status": 0}}' => Wrapper(result=<msgspec.Raw object at 0x1028b3bb0>)

However going from an object or dict and trying to msgspec.convert it into a Wrapper doesn't appear to work or am I doing something wrong?

from types import SimpleNamespace
import msgspec

class Wrapper(msgspec.Struct):
    result: msgspec.Raw | msgspec.UnsetType = msgspec.UNSET

wrapped = {"status": 0}
raw_bytes = msgspec.json.encode(wrapped)
raw = msgspec.Raw(raw_bytes)

try:
    print("wrapped: raw")
    ns_a = SimpleNamespace(result=raw)
    print(ns_a)
    # expected that this would work?
    print(msgspec.convert(ns_a, type=Wrapper, from_attributes=True))
except Exception as e:
    print(e) # Expected `any`, got `msgspec.Raw` - at `$.result`

try:
    print("wrapped: nested namespace")
    ns_a = SimpleNamespace(result=SimpleNamespace(status=0))
    print(ns_a)
    # didn't really expect that this would work
    print(msgspec.convert(ns_a, type=Wrapper, from_attributes=True))
except Exception as e:
    print(e) # Expected `any`, got `types.SimpleNamespace` - at `$.result`

try:
    print("wrapped: bytes")
    ns_a = SimpleNamespace(result=raw_bytes)
    print(ns_a)
    # didn't really expect that this would work but thought it might
    print(msgspec.convert(ns_a, type=Wrapper, from_attributes=True))
except Exception as e:
    print(e) # Expected `any`, got `bytes` - at `$.result`

Expected

    ns_a = SimpleNamespace(result=msgspec.Raw(b'{"status":0}'))
    print(msgspec.convert(ns_a, type=Wrapper, from_attributes=True))
    # Wrapper(result=<msgspec.Raw object at 0x1028b3bb0>)`