schema
schema copied to clipboard
Union of keys within a dict?
Wondering if there's a way to handle unions of keys within a dict, e.g. to handle based on type:
{"title": "post", "type": "link", "url": "google.com"}
{"title": "post", "type": "self", "text": "this is a text post"}
I'm aware I could do something like this:
Or(
{"title": str, "type": "link", "url": str},
{"title": str, "type": "self", "text": str}
)
However this falls apart when you want n cases as you have to have 2^(n-1) or cases.
@cmcarey is this related to https://github.com/keleshev/schema/pull/169?
@rmorshea It seems to be related. Would it be possible to compose as such:
s1 = Schema({"title": str})
opt1 = Schema(Or({"type": "link", "url": str}, {"type": "self", "text": str}))
opt2 = Schema(Or({"has_parent": false}, {"has_parent": true, "parent": str}))
s2 = compose(s1, opt1, opt2)
assert s2 == Schema(Or(
{"title": str, "type": "link", "url": str, "has_parent": false},
{"title": str, "type": "link", "url": str, "has_parent": true, "parent": str},
{"title": str, "type": "self", "text": str, "has_parent": false},
{"title": str, "type": "self", "text": str, "has_parent": true, "parent": str}))
(Not sure if you can test equality like that, but you get the point)
Also a good example as it highlights the necessity of having some way to do this. With only two separate option blocks it required four cases, with three separate option blocks it would require eight, four options would require sixteen, etc.
Thanks!
@cmcarey the reason this is difficult is because there is no option within Schema to allow_extra_keys.
If this option existed you wouldn't even need compose to do what you want:
schema = And(
Schema({"title": str}, allow_extra_keys=True),
Or(
Schema({"type": "link", "url": str}, allow_extra_keys=True),
Schema({"type": "self", "text": str}, allow_extra_keys=True)
),
Or(
Schema({"has_parent": false}, allow_extra_keys=True)
Schema({"has_parent": true, "parent": str}, allow_extra_keys=True)
)
)
Instead of
Schema({"has_parent": False}, allow_extra_keys=True)
Can't you use:
{"has_parent": False, Optional(str): object}
? I try to do something somehow similar without much success ;-)
The difference is that ignore_extra_keys doesn't return the extra keys in the validated data.