1
- use pyo3:: prelude:: * ;
2
1
use pyo3:: types:: { PyDict , PyFrozenSet , PySet } ;
2
+ use pyo3:: { ffi, prelude:: * , AsPyPointer } ;
3
3
4
4
use crate :: build_tools:: SchemaDict ;
5
5
use crate :: errors:: { ErrorType , ValResult } ;
6
6
use crate :: input:: iterator:: { validate_iterator, IterableValidationChecks , LengthConstraints } ;
7
- use crate :: input:: { GenericIterable , Input } ;
7
+ use crate :: input:: { py_error_on_minusone , GenericIterable , Input } ;
8
8
use crate :: recursion_guard:: RecursionGuard ;
9
9
10
10
use super :: any:: AnyValidator ;
@@ -24,10 +24,11 @@ struct IntoSetValidator {
24
24
name : String ,
25
25
}
26
26
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 ( ) ) ) }
31
32
}
32
33
33
34
impl IntoSetValidator {
@@ -67,18 +68,10 @@ impl IntoSetValidator {
67
68
extra : & Extra ,
68
69
definitions : & ' data Definitions < CombinedValidator > ,
69
70
recursion_guard : & ' s mut RecursionGuard ,
70
- // small breakage of encapsulation to avoid a lot of code duplication / macros
71
- set_type : SetType ,
72
71
) -> 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) ;
77
73
78
- let field_type = match set_type {
79
- SetType :: FrozenSet => "Frozenset" ,
80
- SetType :: Set => "Set" ,
81
- } ;
74
+ let field_type = "Set" ;
82
75
83
76
let generic_iterable = input. extract_iterable ( ) . map_err ( |_| create_err ( input) ) ?;
84
77
@@ -97,9 +90,22 @@ impl IntoSetValidator {
97
90
let len = |output : & & ' data PySet | output. len ( ) ;
98
91
let mut write = |output : & mut & ' data PySet , ob : PyObject | output. add ( ob) ;
99
92
100
- match ( generic_iterable, strict, set_type ) {
93
+ match ( generic_iterable, strict) {
101
94
// 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 (
103
109
py,
104
110
input,
105
111
extra,
@@ -112,7 +118,72 @@ impl IntoSetValidator {
112
118
& mut write,
113
119
& len,
114
120
) ?,
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 (
116
187
py,
117
188
input,
118
189
extra,
@@ -125,7 +196,7 @@ impl IntoSetValidator {
125
196
& mut write,
126
197
& len,
127
198
) ?,
128
- ( GenericIterable :: FrozenSet ( iter) , _, SetType :: FrozenSet ) => validate_iterator (
199
+ ( GenericIterable :: FrozenSet ( iter) , _) => validate_iterator (
129
200
py,
130
201
input,
131
202
extra,
@@ -148,9 +219,8 @@ impl IntoSetValidator {
148
219
| GenericIterable :: Dict ( _)
149
220
| GenericIterable :: Mapping ( _) ,
150
221
false ,
151
- _,
152
222
) => 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) {
154
224
Ok ( iter) => validate_iterator (
155
225
py,
156
226
input,
@@ -221,10 +291,9 @@ impl Validator for FrozenSetValidator {
221
291
definitions : & ' data Definitions < CombinedValidator > ,
222
292
recursion_guard : & ' s mut RecursionGuard ,
223
293
) -> 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) )
228
297
}
229
298
230
299
fn different_strict_behavior (
@@ -272,10 +341,9 @@ impl Validator for SetValidator {
272
341
definitions : & ' data Definitions < CombinedValidator > ,
273
342
recursion_guard : & ' s mut RecursionGuard ,
274
343
) -> 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) )
279
347
}
280
348
281
349
fn different_strict_behavior (
0 commit comments