Skip to content

Commit 6e6f474

Browse files
fix: loosen constraints on optionally_keyed_by for msgspec
1 parent b161f65 commit 6e6f474

2 files changed

Lines changed: 24 additions & 9 deletions

File tree

src/taskgraph/util/schema.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import re
77
from collections.abc import Mapping
88
from functools import reduce
9-
from typing import Literal, Optional, Union
9+
from typing import Any, Literal, Optional, Union
1010

1111
import msgspec
1212
import voluptuous
@@ -85,9 +85,11 @@ def optionally_keyed_by(*arguments, use_msgspec=False):
8585
if use_msgspec:
8686
# msgspec implementation - return type hints
8787
_type = arguments[-1]
88+
if _type is object:
89+
return object
8890
fields = arguments[:-1]
8991
bykeys = [Literal[f"by-{field}"] for field in fields]
90-
return Union[_type, dict[UnionTypes(*bykeys), dict[str, _type]]]
92+
return Union[_type, dict[UnionTypes(*bykeys), dict[str, Any]]]
9193
else:
9294
# voluptuous implementation - return validator function
9395
schema = arguments[-1]

test/test_util_schema.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,10 @@ def test_optionally_keyed_by():
290290
"by-foo": {"a": "b", "c": "d"}
291291
}
292292

293-
with pytest.raises(msgspec.ValidationError):
294-
msgspec.convert({"by-foo": {"a": 1, "c": "d"}}, typ)
293+
# Inner dict values are Any, so mixed types are accepted
294+
assert msgspec.convert({"by-foo": {"a": 1, "c": "d"}}, typ) == {
295+
"by-foo": {"a": 1, "c": "d"}
296+
}
295297

296298
with pytest.raises(msgspec.ValidationError):
297299
msgspec.convert({"by-bar": {"a": "b"}}, typ)
@@ -305,11 +307,22 @@ def test_optionally_keyed_by_mulitple_keys():
305307
}
306308
assert msgspec.convert({"by-bar": {"x": "y"}}, typ) == {"by-bar": {"x": "y"}}
307309

308-
with pytest.raises(msgspec.ValidationError):
309-
msgspec.convert({"by-foo": {"a": 123, "c": "d"}}, typ)
310-
311-
with pytest.raises(msgspec.ValidationError):
312-
msgspec.convert({"by-bar": {"a": 1}}, typ)
310+
# Inner dict values are Any, so mixed types are accepted
311+
assert msgspec.convert({"by-foo": {"a": 123, "c": "d"}}, typ) == {
312+
"by-foo": {"a": 123, "c": "d"}
313+
}
314+
assert msgspec.convert({"by-bar": {"a": 1}}, typ) == {"by-bar": {"a": 1}}
313315

314316
with pytest.raises(msgspec.ValidationError):
315317
msgspec.convert({"by-unknown": {"a": "b"}}, typ)
318+
319+
320+
def test_optionally_keyed_by_object_passthrough():
321+
"""When the type argument is `object`, optionally_keyed_by returns object directly."""
322+
typ = optionally_keyed_by("foo", object, use_msgspec=True)
323+
assert typ is object
324+
# object accepts anything via msgspec.convert
325+
assert msgspec.convert("hello", typ) == "hello"
326+
assert msgspec.convert(42, typ) == 42
327+
assert msgspec.convert({"by-foo": {"a": "b"}}, typ) == {"by-foo": {"a": "b"}}
328+
assert msgspec.convert({"arbitrary": "dict"}, typ) == {"arbitrary": "dict"}

0 commit comments

Comments
 (0)