schema icon indicating copy to clipboard operation
schema copied to clipboard

Union of keys within a dict?

Open chancecarey opened this issue 6 years ago • 5 comments

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.

chancecarey avatar Jan 05 '19 22:01 chancecarey

@cmcarey is this related to https://github.com/keleshev/schema/pull/169?

rmorshea avatar Jan 09 '19 01:01 rmorshea

@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!

chancecarey avatar Jan 09 '19 12:01 chancecarey

@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)
    )
)

rmorshea avatar Jan 19 '19 19:01 rmorshea

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 ;-)

vallsv avatar Oct 28 '19 22:10 vallsv

The difference is that ignore_extra_keys doesn't return the extra keys in the validated data.

skorokithakis avatar Oct 28 '19 22:10 skorokithakis