Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nested tagged unions enumeration #726

Open
toppk opened this issue Aug 27, 2024 · 3 comments
Open

nested tagged unions enumeration #726

toppk opened this issue Aug 27, 2024 · 3 comments

Comments

@toppk
Copy link

toppk commented Aug 27, 2024

Question

I've reviewed other issues related to tagged unions. But I just wanted to see if there is some magic I can use.

In using tagged unions, I kinda have parent objects represented the tag. But of course, when setting up the decoder I cannot just give the parent class, but I have to give a union (i get it). But that goes for the nested structs too. This example focuses on that.

if you look at Title2 vs Title below you'll see what I had to do, in order to get decode to work. Obviously encoding doesn't have such an issue.

I'd wish for example there would be some magic flag that you could tell the decoder to find the union structs on it's own, instead of having to specify it, and most importantly not being able change outer struct to specify a full union rather then the nested struct parent class (e.g. `Inner')

import msgspec

class Base(msgspec.Struct, tag_field='base'): ...

class Inner(msgspec.Struct, tag_field='inner'): ...

class Page(Inner, tag='page'):
    foo: str

class Box(Inner, tag='box'):
    foo: str

class Title(Base, tag='title'):
    item: Inner  # doesn't work

class Title2(Base, tag='title'):
    item: Page | Box # works

obj = Title(item=Page(foo='yes'))

print(msgspec.json.encode(obj).decode())

jstr = '{"base":"title","item":{"inner":"page","foo":"yes"}}'

decoder = msgspec.json.Decoder(Title2)
print (decoder.decode(jstr)) # works

decoder = msgspec.json.Decoder(Title)
print (decoder.decode(jstr)) # doesn't work
@toppk
Copy link
Author

toppk commented Sep 3, 2024

I've thought about my issue and what I'd want a solution to look like. I want to mention #140 because it is a similar problem, but I think over there the issue was different as there was trying to use multiple different top level tagged unions (i'm not sure exactly).

Anyway, my desired solution to this is to have this

class Inner(msgspec.Struct, tag_field='inner', union_from_subclasses=True): ...

the decoder would notice that Inner class is a msgspec.Struct and has union_from_subclasses = True and then replace it magically with functools.reduce(lambda x,y: x| y, Inner.__subclasses__()) or equivalent. This way my typing is clean, and the complexity is built into the decoder.

@toppk
Copy link
Author

toppk commented Sep 3, 2024

i just noticed #663 which is another way to implement similar capability. I'm not sure if my way work work for that user, but their way would likely work for me.

@mishamsk
Copy link

mishamsk commented Sep 4, 2024

@toppk yepp, that's what I was trying to get to. Since @jcrist is clearly busy lately, I'm periodically coming back to consider doing a fork for now. I need this if I'll decide to migrate my commercial project to msgspec... not at that point yet, hence haven't finished what I started as a PoC in #663

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants