From 03d3300de87a213f22788e7cadd8466d87c8cf2c Mon Sep 17 00:00:00 2001 From: sydney-runkle Date: Tue, 21 Nov 2023 12:47:51 -0500 Subject: [PATCH 1/4] avoid using ? with get item to handle unhashable inputs properly --- src/validators/literal.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/validators/literal.rs b/src/validators/literal.rs index 0f1caf601..d71a0ac2a 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 as a + // validation failure + 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]))); } From 7e2d37b1146516cb2e784d2fc2196870dbc28362 Mon Sep 17 00:00:00 2001 From: sydney-runkle Date: Tue, 21 Nov 2023 13:08:11 -0500 Subject: [PATCH 2/4] test addition --- tests/validators/test_union.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/validators/test_union.py b/tests/validators/test_union.py index 503a5f387..fe9914110 100644 --- a/tests/validators/test_union.py +++ b/tests/validators/test_union.py @@ -771,3 +771,32 @@ 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]}, + ], + } + ) + + # should raise ValidationError for Literal check, not TypeError + assert validator.validate_python({'a': 42}) + assert validator.validate_python(True) From b9d6c8a72ac129195fa3ead0b2677c6e06788623 Mon Sep 17 00:00:00 2001 From: sydney-runkle Date: Tue, 21 Nov 2023 13:09:55 -0500 Subject: [PATCH 3/4] updating comment --- tests/validators/test_union.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/validators/test_union.py b/tests/validators/test_union.py index fe9914110..3a1884f3f 100644 --- a/tests/validators/test_union.py +++ b/tests/validators/test_union.py @@ -797,6 +797,7 @@ class ModelA: } ) - # should raise ValidationError for Literal check, not TypeError + # 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 assert validator.validate_python({'a': 42}) assert validator.validate_python(True) From 60c2c3555e867be17b95431274d9809d7fb2b2b9 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Wed, 22 Nov 2023 09:23:05 +0000 Subject: [PATCH 4/4] tweak tests and comment --- src/validators/literal.rs | 6 +++--- tests/validators/test_union.py | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/validators/literal.rs b/src/validators/literal.rs index d71a0ac2a..9f8c25e0f 100644 --- a/src/validators/literal.rs +++ b/src/validators/literal.rs @@ -136,9 +136,9 @@ impl LiteralLookup { } // must be an enum or bytes if let Some(expected_py) = &self.expected_py { - // 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 as a - // validation failure + // 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 3a1884f3f..42c542000 100644 --- a/tests/validators/test_union.py +++ b/tests/validators/test_union.py @@ -799,5 +799,7 @@ class ModelA: # 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 - assert validator.validate_python({'a': 42}) - assert validator.validate_python(True) + m = validator.validate_python({'a': 42}) + assert isinstance(m, ModelA) + assert m.a == 42 + assert validator.validate_python(True) is True