Skip to content

Commit ccd07fa

Browse files
committed
refactor: simplify omit handling in UnionValidator and remove unused MaybeOmits struct
1 parent 4160dc6 commit ccd07fa

2 files changed

Lines changed: 25 additions & 21 deletions

File tree

pydantic-core/src/validators/union.rs

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl UnionValidator {
110110
let old_fields_set_count = state.fields_set_count;
111111

112112
let mut errors = MaybeErrors::new(self.custom_error.as_ref());
113-
let mut omits = MaybeOmits::new();
113+
let mut should_omit = false;
114114

115115
let mut best_match: Option<(Py<PyAny>, Exactness, Option<usize>)> = None;
116116

@@ -159,7 +159,7 @@ impl UnionValidator {
159159
},
160160
Err(ValError::Omit) => {
161161
if best_match.is_none() {
162-
omits.push(choice, label.as_deref());
162+
should_omit = true;
163163
}
164164
}
165165
Err(ValError::LineErrors(lines)) => {
@@ -184,7 +184,7 @@ impl UnionValidator {
184184
return Ok(best_match);
185185
}
186186
// if there were no successful matches, but there was at least one omit, return omit instead of errors
187-
if best_match.is_none() && !omits.is_empty() {
187+
if best_match.is_none() && should_omit {
188188
return Err(ValError::Omit);
189189
}
190190
// no matches, build errors
@@ -289,24 +289,6 @@ impl<'a> MaybeErrors<'a> {
289289
}
290290
}
291291

292-
struct MaybeOmits<'a> {
293-
omits: SmallVec<[(&'a CombinedValidator, Option<&'a str>); SMALL_UNION_THRESHOLD]>,
294-
}
295-
296-
impl<'a> MaybeOmits<'a> {
297-
fn new() -> Self {
298-
Self { omits: SmallVec::new() }
299-
}
300-
301-
fn push(&mut self, choice: &'a CombinedValidator, label: Option<&'a str>) {
302-
self.omits.push((choice, label));
303-
}
304-
305-
fn is_empty(&self) -> bool {
306-
self.omits.is_empty()
307-
}
308-
}
309-
310292
#[derive(Debug)]
311293
pub struct TaggedUnionValidator {
312294
discriminator: Discriminator,

tests/types/test_union.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,25 @@ class Container2(pydantic.BaseModel):
7474
# as nested unions were individually attempted with each of strict and lax checking,
7575
# and the discriminators also incurred an extra attempt at each check level too.
7676
assert MyModel.field_a_serializer_calls == 2
77+
78+
79+
def test_list_union_omit():
80+
OmitList = list[pydantic.OnErrorOmit[int] | pydantic.OnErrorOmit[bool]]
81+
ta = pydantic.TypeAdapter(OmitList)
82+
assert ta.validate_python([1, 'True', 'foo', '2', False, 'bar']) == [1, True, 2, False]
83+
84+
85+
def test_typed_dict_union_omit():
86+
class TD(TypedDict):
87+
# arguably this should fail on schema build since `x` is a
88+
# required field, and setting the type to `OnErrorOmit`
89+
# effectively makes it optional.
90+
x: pydantic.OnErrorOmit[int] | pydantic.OnErrorOmit[bool]
91+
92+
ta = pydantic.TypeAdapter(TD)
93+
assert ta.validate_python({'x': 1}) == {'x': 1}
94+
assert ta.validate_python({'x': 'True'}) == {'x': True}
95+
96+
# test to document the behaviour that if all options in the union fail,
97+
# the field is omitted, even if it is required in the TypedDict.
98+
assert ta.validate_python({'x': 'foo'}) == {}

0 commit comments

Comments
 (0)