-
-
Notifications
You must be signed in to change notification settings - Fork 114
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
Documentation does not explain unstruct_collection_overrides' input #513
Comments
Howdy! Sorry to hear you had issues with this. Documentation is always a work in progress. Would you care to submit a PR for the docs dealing with this, to help the next person? I'll gladly review and merge it.
So what exactly are you trying to accomplish? Forcing a consistent ordering of sets of frozen attrs classes in json? I love a challenge. If you're willing to make the attrs classes orderable, you might try something like this: from typing import get_args
from attrs import AttrsInstance, frozen, has
from cattrs import Converter
from cattrs._compat import is_mutable_set
c = Converter()
@frozen(order=True)
class A:
x: int = 0
y: int = 0
def unstructure_sets(val: set[AttrsInstance]) -> list:
return [c.unstructure(v) for v in sorted(val)]
c.register_unstructure_hook_func(
lambda t: is_mutable_set(t) and has(get_args(t)[0]), unstructure_sets
)
print(c.unstructure({A(x=1), A(x=2), A(x=3)}, unstructure_as=set[A])) (This can be made faster by adding some complexity.) |
Aha! That is indeed what I was trying to do. I think the magic input here is the nearly undocumented I don't mind writing up a doc PR, but I don't think I understand how the unstructuring procedure works. Presumably it takes the tree of objects and unstructures from the leaves up? That is, the unstructuring procedure for collections is "unstructure all the contents, put them in a collection of the same kind, and then use |
I think the following achieves what I was aiming for: def unstructure_set(val: set) -> list:
vals = list(val)
try:
return [c.unstructure(v) for v in sorted(vals)]
except TypeError as e:
print(f"Whoops, can't sort: {e}")
return [c.unstructure(v) for v in vals]
c = Converter()
c.register_unstructure_hook_func(
lambda t: issubclass(t, set)
or issubclass(getattr(t, "__origin__", type(None)), set),
unstructure_set,
)
print(c.unstructure({1, 2, -1}))
@frozen(order=True)
class A:
x: int = 0
y: int = 0
print(c.unstructure({A(x=4), A(x=2), A(x=3)}))
print(c.unstructure({A(x=4), A(x=2), A(x=3)}, unstructure_as=set[A]))
@frozen
class B:
x: int = 0
y: int = 0
print(c.unstructure({B(x=4), B(x=2), B(x=3)}))
print(c.unstructure({B(x=4), B(x=2), B(x=3)}, unstructure_as=set[B])) That is, I don't think I can do this with Dicts are amenable to a similar approach, I think, but they can also be handled at the JSON level with In practice I ran into another complexity: one of my objects has an |
Are you wrapping cattrs in a framework of sorts? I've done that a lot and have almost always been able to know exactly what I was unstructuring, although that might not be the case for you unless you lean on type hinting a lot. I played around with your example; instead of trying to sort and then dealing with a possible exception, I tried this: c = Converter()
def unstructure_ordered_set(val: set) -> list:
return c.unstructure(sorted(val))
c.register_unstructure_hook_func(
lambda t: issubclass(getattr(t, "__origin__", type(None)), set)
and is_orderable(t.__args__[0]),
unstructure_ordered_set,
)
def is_orderable(type: type) -> bool:
return type.__lt__ != object.__lt__ Obviously it's not the same as yours (only works for orderable classes) but it seems to work ;) |
Description
I am trying to unstructure sets into something that JSON can cope with and that is canonical (equal for any two equal sets; set iteration order can differ for equal sets). I tried
unstruct_collection_overrides={Set: sorted}
and obtained a very confusing error ("< not supported between dict and dict"). It turns out that the problem is that the functions inunstruct_collection_overrides
receive a generator that emits the unstructured contents of the collection (at least for Set; I'm not sure for dicts). Since the objects in my set are frozen attrs objects, they themselves work fine in a Set (and being sorted), but once unstructured they become dictionaries, which can't be sorted.[Edited to add: my conclusion is that this isn't really possible; that's not the bug, that's just an unfortunate limitation.]
The problem: the documentation at https://catt.rs/en/stable/unstructuring.html#customizing-collection-unstructuring never specifies what the unstructuring functions receive as input. Neither does the docstring for Converter (https://catt.rs/en/stable/cattrs.html#cattrs.Converter). A little explanation would have helped tremendously in tracking down the mysterious error.
What I Did
gives
as opposed to some kind of canonically sorted list.
The text was updated successfully, but these errors were encountered: