From 82158ce5a69c9c9d7e749c13483a8984abaa496b Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Wed, 22 Nov 2023 03:29:18 -0600 Subject: [PATCH] Avoid using `?` with `get_item` to handle unhashable inputs properly (#1089) Co-authored-by: Samuel Colvin --- src/validators/literal.rs | 5 ++++- tests/validators/test_union.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/validators/literal.rs b/src/validators/literal.rs index 686920cca..a5608e9e1 100644 --- a/src/validators/literal.rs +++ b/src/validators/literal.rs @@ -136,7 +136,10 @@ impl LiteralLookup { } // must be an enum or bytes if let Some(expected_py) = &self.expected_py { - if let Some(v) = expected_py.as_ref(py).get_item(input)? { + // We don't use ? to unpack the result of `get_item` in the next line because unhashable + // inputs will produce a TypeError, which in this case we just want to treat equivalently + // to a failed lookup + if let Ok(Some(v)) = expected_py.as_ref(py).get_item(input) { let id: usize = v.extract().unwrap(); return Ok(Some((input, &self.values[id]))); } diff --git a/tests/validators/test_union.py b/tests/validators/test_union.py index 503a5f387..42c542000 100644 --- a/tests/validators/test_union.py +++ b/tests/validators/test_union.py @@ -771,3 +771,35 @@ class BinaryEnum(IntEnum): assert validator.validate_python(1) is not BinaryEnum.ONE assert validator.validate_python(BinaryEnum.ZERO) is BinaryEnum.ZERO assert validator.validate_python(BinaryEnum.ONE) is BinaryEnum.ONE + + +def test_model_and_literal_union() -> None: + # see https://github.com/pydantic/pydantic/issues/8183 + class ModelA: + pass + + validator = SchemaValidator( + { + 'type': 'union', + 'choices': [ + { + 'type': 'model', + 'cls': ModelA, + 'schema': { + 'type': 'model-fields', + 'fields': { + 'a': {'type': 'model-field', 'schema': {'type': 'int'}}, + }, + }, + }, + {'type': 'literal', 'expected': [True]}, + ], + } + ) + + # validation against Literal[True] fails bc of the unhashable dict + # A ValidationError is raised, not a ValueError, which allows the validation against the union to continue + m = validator.validate_python({'a': 42}) + assert isinstance(m, ModelA) + assert m.a == 42 + assert validator.validate_python(True) is True