Skip to content

Commit e6f9bb3

Browse files
committed
Craete a FrozenSet directly
1 parent 1f5a98b commit e6f9bb3

File tree

2 files changed

+98
-31
lines changed

2 files changed

+98
-31
lines changed

src/input/iterator.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ pub fn map_iter_error<'data>(
150150
}
151151

152152
#[allow(clippy::too_many_arguments)]
153-
#[inline(always)]
154153
pub fn validate_iterator<'s, 'data, V, O, W, L>(
155154
py: Python<'data>,
156155
input: &'data impl Input<'data>,

src/validators/sets.rs

Lines changed: 98 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use pyo3::prelude::*;
21
use pyo3::types::{PyDict, PyFrozenSet, PySet};
2+
use pyo3::{ffi, prelude::*, AsPyPointer};
33

44
use crate::build_tools::SchemaDict;
55
use crate::errors::{ErrorType, ValResult};
66
use crate::input::iterator::{validate_iterator, IterableValidationChecks, LengthConstraints};
7-
use crate::input::{GenericIterable, Input};
7+
use crate::input::{py_error_on_minusone, GenericIterable, Input};
88
use crate::recursion_guard::RecursionGuard;
99

1010
use super::any::AnyValidator;
@@ -24,10 +24,11 @@ struct IntoSetValidator {
2424
name: String,
2525
}
2626

27-
#[derive(Debug, Clone, Copy)]
28-
enum SetType {
29-
FrozenSet,
30-
Set,
27+
fn frozen_set_add<K>(set: &PyFrozenSet, key: K) -> PyResult<()>
28+
where
29+
K: ToPyObject,
30+
{
31+
unsafe { py_error_on_minusone(set.py(), ffi::PySet_Add(set.as_ptr(), key.to_object(set.py()).as_ptr())) }
3132
}
3233

3334
impl IntoSetValidator {
@@ -67,18 +68,10 @@ impl IntoSetValidator {
6768
extra: &Extra,
6869
definitions: &'data Definitions<CombinedValidator>,
6970
recursion_guard: &'s mut RecursionGuard,
70-
// small breakage of encapsulation to avoid a lot of code duplication / macros
71-
set_type: SetType,
7271
) -> ValResult<'data, &'data PySet> {
73-
let create_err = |input| match set_type {
74-
SetType::FrozenSet => ValError::new(ErrorType::FrozenSetType, input),
75-
SetType::Set => ValError::new(ErrorType::SetType, input),
76-
};
72+
let create_err = |input| ValError::new(ErrorType::SetType, input);
7773

78-
let field_type = match set_type {
79-
SetType::FrozenSet => "Frozenset",
80-
SetType::Set => "Set",
81-
};
74+
let field_type = "Set";
8275

8376
let generic_iterable = input.extract_iterable().map_err(|_| create_err(input))?;
8477

@@ -97,9 +90,22 @@ impl IntoSetValidator {
9790
let len = |output: &&'data PySet| output.len();
9891
let mut write = |output: &mut &'data PySet, ob: PyObject| output.add(ob);
9992

100-
match (generic_iterable, strict, set_type) {
93+
match (generic_iterable, strict) {
10194
// Always allow actual lists or JSON arrays
102-
(GenericIterable::JsonArray(iter), _, _) => validate_iterator(
95+
(GenericIterable::JsonArray(iter), _) => validate_iterator(
96+
py,
97+
input,
98+
extra,
99+
definitions,
100+
recursion_guard,
101+
&mut checks,
102+
iter.iter().map(Ok),
103+
&self.item_validator,
104+
&mut output,
105+
&mut write,
106+
&len,
107+
)?,
108+
(GenericIterable::Set(iter), _) => validate_iterator(
103109
py,
104110
input,
105111
extra,
@@ -112,7 +118,72 @@ impl IntoSetValidator {
112118
&mut write,
113119
&len,
114120
)?,
115-
(GenericIterable::Set(iter), _, SetType::Set) => validate_iterator(
121+
// If not in strict mode we also accept any iterable except str, bytes or mappings
122+
// This may seem counterintuitive since a Mapping is a less generic type than an arbitrary
123+
// iterable (which we do accept) but doing something like `x: list[int] = {1: 'a'}` is commonly
124+
// a mistake, so we don't parse it by default
125+
(
126+
GenericIterable::String(_)
127+
| GenericIterable::Bytes(_)
128+
| GenericIterable::Dict(_)
129+
| GenericIterable::Mapping(_),
130+
false,
131+
) => return Err(create_err(input)),
132+
(generic_iterable, false) => match generic_iterable.into_sequence_iterator(py) {
133+
Ok(iter) => validate_iterator(
134+
py,
135+
input,
136+
extra,
137+
definitions,
138+
recursion_guard,
139+
&mut checks,
140+
iter,
141+
&self.item_validator,
142+
&mut output,
143+
&mut write,
144+
&len,
145+
)?,
146+
Err(_) => return Err(create_err(input)),
147+
},
148+
_ => return Err(create_err(input)),
149+
};
150+
151+
Ok(output)
152+
}
153+
154+
#[allow(clippy::too_many_arguments)]
155+
pub fn validate_into_frozenset<'s, 'data>(
156+
&'s self,
157+
py: Python<'data>,
158+
input: &'data impl Input<'data>,
159+
extra: &Extra,
160+
definitions: &'data Definitions<CombinedValidator>,
161+
recursion_guard: &'s mut RecursionGuard,
162+
) -> ValResult<'data, &'data PyFrozenSet> {
163+
let create_err = |input| ValError::new(ErrorType::FrozenSetType, input);
164+
165+
let field_type = "Frozenset";
166+
167+
let generic_iterable = input.extract_iterable().map_err(|_| create_err(input))?;
168+
169+
let strict = extra.strict.unwrap_or(self.strict);
170+
171+
let length_constraints = LengthConstraints {
172+
min_length: self.min_length,
173+
max_length: self.max_length,
174+
max_input_length: self.generator_max_length,
175+
};
176+
177+
let mut checks = IterableValidationChecks::new(false, length_constraints, field_type);
178+
179+
let mut output = PyFrozenSet::empty(py)?;
180+
181+
let len = |output: &&'data PyFrozenSet| output.len();
182+
let mut write = |output: &mut &'data PyFrozenSet, ob: PyObject| frozen_set_add(output, ob);
183+
184+
match (generic_iterable, strict) {
185+
// Always allow actual lists or JSON arrays
186+
(GenericIterable::JsonArray(iter), _) => validate_iterator(
116187
py,
117188
input,
118189
extra,
@@ -125,7 +196,7 @@ impl IntoSetValidator {
125196
&mut write,
126197
&len,
127198
)?,
128-
(GenericIterable::FrozenSet(iter), _, SetType::FrozenSet) => validate_iterator(
199+
(GenericIterable::FrozenSet(iter), _) => validate_iterator(
129200
py,
130201
input,
131202
extra,
@@ -148,9 +219,8 @@ impl IntoSetValidator {
148219
| GenericIterable::Dict(_)
149220
| GenericIterable::Mapping(_),
150221
false,
151-
_,
152222
) => return Err(create_err(input)),
153-
(generic_iterable, false, _) => match generic_iterable.into_sequence_iterator(py) {
223+
(generic_iterable, false) => match generic_iterable.into_sequence_iterator(py) {
154224
Ok(iter) => validate_iterator(
155225
py,
156226
input,
@@ -221,10 +291,9 @@ impl Validator for FrozenSetValidator {
221291
definitions: &'data Definitions<CombinedValidator>,
222292
recursion_guard: &'s mut RecursionGuard,
223293
) -> ValResult<'data, PyObject> {
224-
let set = self
225-
.inner
226-
.validate_into_set(py, input, extra, definitions, recursion_guard, SetType::FrozenSet)?;
227-
Ok(PyFrozenSet::new(py, set)?.into_py(py))
294+
self.inner
295+
.validate_into_frozenset(py, input, extra, definitions, recursion_guard)
296+
.map(|v| v.into_py(py))
228297
}
229298

230299
fn different_strict_behavior(
@@ -272,10 +341,9 @@ impl Validator for SetValidator {
272341
definitions: &'data Definitions<CombinedValidator>,
273342
recursion_guard: &'s mut RecursionGuard,
274343
) -> ValResult<'data, PyObject> {
275-
let set = self
276-
.inner
277-
.validate_into_set(py, input, extra, definitions, recursion_guard, SetType::Set)?;
278-
Ok(set.into_py(py))
344+
self.inner
345+
.validate_into_set(py, input, extra, definitions, recursion_guard)
346+
.map(|v| v.into_py(py))
279347
}
280348

281349
fn different_strict_behavior(

0 commit comments

Comments
 (0)