Skip to content

Commit c90da02

Browse files
committed
convert tuple iteration to use Py2 in list
1 parent b1ece19 commit c90da02

File tree

3 files changed

+52
-35
lines changed

3 files changed

+52
-35
lines changed

src/input/input_python.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ impl<'a> Input<'a> for PyAny {
566566
fn extract_generic_iterable(&'a self) -> ValResult<GenericIterable<'a>> {
567567
// Handle concrete non-overlapping types first, then abstract types
568568
if let Ok(iterable) = self.downcast::<PyList>() {
569-
Ok(GenericIterable::List(iterable))
569+
Ok(GenericIterable::List(Py2::borrowed_from_gil_ref(&iterable).clone()))
570570
} else if let Ok(iterable) = self.downcast::<PyTuple>() {
571571
Ok(GenericIterable::Tuple(iterable))
572572
} else if let Ok(iterable) = self.downcast::<PySet>() {
@@ -746,6 +746,13 @@ impl BorrowInput for &'_ PyAny {
746746
}
747747
}
748748

749+
impl BorrowInput for Py2<'_, PyAny> {
750+
type Input<'a> = PyAny where Self: 'a;
751+
fn borrow_input(&self) -> &Self::Input<'_> {
752+
self.as_gil_ref()
753+
}
754+
}
755+
749756
/// Best effort check of whether it's likely to make sense to inspect obj for attributes and iterate over it
750757
/// with `obj.dir()`
751758
fn from_attributes_applicable(obj: &PyAny) -> bool {

src/input/return_enums.rs

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::tools::py_err;
2727
use crate::validators::{CombinedValidator, Exactness, ValidationState, Validator};
2828

2929
use super::input_string::StringMapping;
30-
use super::{py_error_on_minusone, Input};
30+
use super::{py_error_on_minusone, BorrowInput, Input};
3131

3232
pub struct ValidationMatch<T>(T, Exactness);
3333

@@ -67,7 +67,7 @@ impl<T> ValidationMatch<T> {
6767
/// This mostly matches python's definition of `Collection`.
6868
#[cfg_attr(debug_assertions, derive(Debug))]
6969
pub enum GenericIterable<'a> {
70-
List(&'a PyList),
70+
List(Py2<'a, PyList>),
7171
Tuple(&'a PyTuple),
7272
Set(&'a PySet),
7373
FrozenSet(&'a PyFrozenSet),
@@ -92,33 +92,37 @@ impl<'a, 'py: 'a> GenericIterable<'a> {
9292
pub fn as_sequence_iterator(
9393
&self,
9494
py: Python<'py>,
95-
) -> PyResult<Box<dyn Iterator<Item = PyResult<&'a PyAny>> + 'a>> {
95+
) -> PyResult<Box<dyn Iterator<Item = PyResult<Py2<'a, PyAny>>> + 'a>> {
9696
match self {
97-
GenericIterable::List(iter) => Ok(Box::new(iter.iter().map(Ok))),
98-
GenericIterable::Tuple(iter) => Ok(Box::new(iter.iter().map(Ok))),
99-
GenericIterable::Set(iter) => Ok(Box::new(iter.iter().map(Ok))),
100-
GenericIterable::FrozenSet(iter) => Ok(Box::new(iter.iter().map(Ok))),
97+
GenericIterable::List(iter) => Ok(Box::new(iter.clone().into_iter().map(Ok))),
98+
GenericIterable::Tuple(iter) => Ok(Box::new(iter.iter().map(Ok).map(pyresult_ref_to_pyresult_py2))),
99+
GenericIterable::Set(iter) => Ok(Box::new(iter.iter().map(Ok).map(pyresult_ref_to_pyresult_py2))),
100+
GenericIterable::FrozenSet(iter) => Ok(Box::new(iter.iter().map(Ok).map(pyresult_ref_to_pyresult_py2))),
101101
// Note that this iterates over only the keys, just like doing iter({}) in Python
102-
GenericIterable::Dict(iter) => Ok(Box::new(iter.iter().map(|(k, _)| Ok(k)))),
103-
GenericIterable::DictKeys(iter) => Ok(Box::new(iter.iter()?)),
104-
GenericIterable::DictValues(iter) => Ok(Box::new(iter.iter()?)),
105-
GenericIterable::DictItems(iter) => Ok(Box::new(iter.iter()?)),
102+
GenericIterable::Dict(iter) => Ok(Box::new(
103+
iter.iter().map(|(k, _)| Ok(k)).map(pyresult_ref_to_pyresult_py2),
104+
)),
105+
GenericIterable::DictKeys(iter) => Ok(Box::new(iter.iter()?.map(pyresult_ref_to_pyresult_py2))),
106+
GenericIterable::DictValues(iter) => Ok(Box::new(iter.iter()?.map(pyresult_ref_to_pyresult_py2))),
107+
GenericIterable::DictItems(iter) => Ok(Box::new(iter.iter()?.map(pyresult_ref_to_pyresult_py2))),
106108
// Note that this iterates over only the keys, just like doing iter({}) in Python
107-
GenericIterable::Mapping(iter) => Ok(Box::new(iter.keys()?.iter()?)),
108-
GenericIterable::PyString(iter) => Ok(Box::new(iter.iter()?)),
109-
GenericIterable::Bytes(iter) => Ok(Box::new(iter.iter()?)),
110-
GenericIterable::PyByteArray(iter) => Ok(Box::new(iter.iter()?)),
111-
GenericIterable::Sequence(iter) => Ok(Box::new(iter.iter()?)),
112-
GenericIterable::Iterator(iter) => Ok(Box::new(iter.iter()?)),
109+
GenericIterable::Mapping(iter) => Ok(Box::new(iter.keys()?.iter()?.map(pyresult_ref_to_pyresult_py2))),
110+
GenericIterable::PyString(iter) => Ok(Box::new(iter.iter()?.map(pyresult_ref_to_pyresult_py2))),
111+
GenericIterable::Bytes(iter) => Ok(Box::new(iter.iter()?.map(pyresult_ref_to_pyresult_py2))),
112+
GenericIterable::PyByteArray(iter) => Ok(Box::new(iter.iter()?.map(pyresult_ref_to_pyresult_py2))),
113+
GenericIterable::Sequence(iter) => Ok(Box::new(iter.iter()?.map(pyresult_ref_to_pyresult_py2))),
114+
GenericIterable::Iterator(iter) => Ok(Box::new(iter.iter()?.map(pyresult_ref_to_pyresult_py2))),
113115
GenericIterable::JsonArray(iter) => Ok(Box::new(iter.iter().map(move |v| {
114-
let v = v.to_object(py);
115-
Ok(v.into_ref(py))
116+
let v = v.to_object(py).attach_into(py);
117+
Ok(v)
116118
}))),
117119
// Note that this iterates over only the keys, just like doing iter({}) in Python, just for consistency
118120
GenericIterable::JsonObject(iter) => Ok(Box::new(
119-
iter.iter().map(move |(k, _)| Ok(k.to_object(py).into_ref(py))),
121+
iter.iter().map(move |(k, _)| Ok(k.to_object(py).attach_into(py))),
120122
)),
121-
GenericIterable::JsonString(s) => Ok(Box::new(PyString::new(py, s).iter()?)),
123+
GenericIterable::JsonString(s) => {
124+
Ok(Box::new(PyString::new(py, s).iter()?.map(pyresult_ref_to_pyresult_py2)))
125+
}
122126
}
123127
}
124128
}
@@ -188,7 +192,7 @@ macro_rules! any_next_error {
188192
#[allow(clippy::too_many_arguments)]
189193
fn validate_iter_to_vec<'a, 's>(
190194
py: Python<'a>,
191-
iter: impl Iterator<Item = PyResult<&'a (impl Input<'a> + 'a)>>,
195+
iter: impl Iterator<Item = PyResult<impl BorrowInput + 'a>>,
192196
capacity: usize,
193197
mut max_length_check: MaxLengthCheck<'a, impl Input<'a>>,
194198
validator: &'s CombinedValidator,
@@ -198,7 +202,7 @@ fn validate_iter_to_vec<'a, 's>(
198202
let mut errors: Vec<ValLineError> = Vec::new();
199203
for (index, item_result) in iter.enumerate() {
200204
let item = item_result.map_err(|e| any_next_error!(py, e, max_length_check.input, index))?;
201-
match validator.validate(py, item, state) {
205+
match validator.validate(py, item.borrow_input(), state) {
202206
Ok(item) => {
203207
max_length_check.incr()?;
204208
output.push(item);
@@ -253,7 +257,7 @@ impl BuildSet for &PyFrozenSet {
253257
fn validate_iter_to_set<'a, 's>(
254258
py: Python<'a>,
255259
set: impl BuildSet,
256-
iter: impl Iterator<Item = PyResult<&'a (impl Input<'a> + 'a)>>,
260+
iter: impl Iterator<Item = PyResult<impl BorrowInput + 'a>>,
257261
input: &'a (impl Input<'a> + 'a),
258262
field_type: &'static str,
259263
max_length: Option<usize>,
@@ -263,7 +267,7 @@ fn validate_iter_to_set<'a, 's>(
263267
let mut errors: Vec<ValLineError> = Vec::new();
264268
for (index, item_result) in iter.enumerate() {
265269
let item = item_result.map_err(|e| any_next_error!(py, e, input, index))?;
266-
match validator.validate(py, item, state) {
270+
match validator.validate(py, item.borrow_input(), state) {
267271
Ok(item) => {
268272
set.build_add(item)?;
269273
if let Some(max_length) = max_length {
@@ -301,14 +305,14 @@ fn validate_iter_to_set<'a, 's>(
301305
fn no_validator_iter_to_vec<'a, 's>(
302306
py: Python<'a>,
303307
input: &'a (impl Input<'a> + 'a),
304-
iter: impl Iterator<Item = PyResult<&'a (impl Input<'a> + 'a)>>,
308+
iter: impl Iterator<Item = PyResult<impl BorrowInput + 'a>>,
305309
mut max_length_check: MaxLengthCheck<'a, impl Input<'a>>,
306310
) -> ValResult<Vec<PyObject>> {
307311
iter.enumerate()
308312
.map(|(index, result)| {
309313
let v = result.map_err(|e| any_next_error!(py, e, input, index))?;
310314
max_length_check.incr()?;
311-
Ok(v.to_object(py))
315+
Ok(v.borrow_input().to_object(py))
312316
})
313317
.collect()
314318
}
@@ -360,7 +364,7 @@ impl<'a> GenericIterable<'a> {
360364
}
361365

362366
match self {
363-
GenericIterable::List(collection) => validate!(collection.iter().map(Ok)),
367+
GenericIterable::List(collection) => validate!(collection.clone().into_iter().map(Ok)),
364368
GenericIterable::Tuple(collection) => validate!(collection.iter().map(Ok)),
365369
GenericIterable::Set(collection) => validate!(collection.iter().map(Ok)),
366370
GenericIterable::FrozenSet(collection) => validate!(collection.iter().map(Ok)),
@@ -389,7 +393,7 @@ impl<'a> GenericIterable<'a> {
389393
}
390394

391395
match self {
392-
GenericIterable::List(collection) => validate_set!(collection.iter().map(Ok)),
396+
GenericIterable::List(collection) => validate_set!(collection.clone().into_iter().map(Ok)),
393397
GenericIterable::Tuple(collection) => validate_set!(collection.iter().map(Ok)),
394398
GenericIterable::Set(collection) => validate_set!(collection.iter().map(Ok)),
395399
GenericIterable::FrozenSet(collection) => validate_set!(collection.iter().map(Ok)),
@@ -412,7 +416,7 @@ impl<'a> GenericIterable<'a> {
412416

413417
match self {
414418
GenericIterable::List(collection) => {
415-
no_validator_iter_to_vec(py, input, collection.iter().map(Ok), max_length_check)
419+
no_validator_iter_to_vec(py, input, collection.clone().into_iter().map(Ok), max_length_check)
416420
}
417421
GenericIterable::Tuple(collection) => {
418422
no_validator_iter_to_vec(py, input, collection.iter().map(Ok), max_length_check)
@@ -1039,3 +1043,8 @@ impl ToPyObject for Int {
10391043
}
10401044
}
10411045
}
1046+
1047+
/// Backwards-compatibility helper while migrating PyO3 API
1048+
fn pyresult_ref_to_pyresult_py2(result: PyResult<&'_ PyAny>) -> PyResult<Py2<'_, PyAny>> {
1049+
result.map(|any| Py2::borrowed_from_gil_ref(&any).clone())
1050+
}

src/validators/tuple.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use pyo3::types::{PyDict, PyList, PyTuple};
44

55
use crate::build_tools::is_strict;
66
use crate::errors::{ErrorType, ErrorTypeDefaults, ValError, ValLineError, ValResult};
7+
use crate::input::BorrowInput;
78
use crate::input::{GenericIterable, Input};
89
use crate::tools::SchemaDict;
910
use crate::validators::Exactness;
@@ -113,7 +114,7 @@ impl BuildValidator for TuplePositionalValidator {
113114
}
114115

115116
#[allow(clippy::too_many_arguments)]
116-
fn validate_tuple_positional<'s, 'data, T: Iterator<Item = PyResult<&'data I>>, I: Input<'data> + 'data>(
117+
fn validate_tuple_positional<'s, 'data, T: Iterator<Item = PyResult<impl BorrowInput + 'data>>>(
117118
py: Python<'data>,
118119
input: &'data impl Input<'data>,
119120
state: &mut ValidationState,
@@ -126,7 +127,7 @@ fn validate_tuple_positional<'s, 'data, T: Iterator<Item = PyResult<&'data I>>,
126127
) -> ValResult<()> {
127128
for (index, validator) in items_validators.iter().enumerate() {
128129
match collection_iter.next() {
129-
Some(result) => match validator.validate(py, result?, state) {
130+
Some(result) => match validator.validate(py, result?.borrow_input(), state) {
130131
Ok(item) => output.push(item),
131132
Err(ValError::LineErrors(line_errors)) => {
132133
errors.extend(line_errors.into_iter().map(|err| err.with_outer_location(index.into())));
@@ -145,7 +146,7 @@ fn validate_tuple_positional<'s, 'data, T: Iterator<Item = PyResult<&'data I>>,
145146
for (index, result) in collection_iter.enumerate() {
146147
let item = result?;
147148
match extras_validator {
148-
Some(ref extras_validator) => match extras_validator.validate(py, item, state) {
149+
Some(ref extras_validator) => match extras_validator.validate(py, item.borrow_input(), state) {
149150
Ok(item) => output.push(item),
150151
Err(ValError::LineErrors(line_errors)) => {
151152
errors.extend(
@@ -222,7 +223,7 @@ impl Validator for TuplePositionalValidator {
222223
}
223224

224225
match collection {
225-
GenericIterable::List(collection_iter) => iter!(collection_iter.iter().map(Ok)),
226+
GenericIterable::List(collection_iter) => iter!(collection_iter.clone().into_iter().map(Ok)),
226227
GenericIterable::Tuple(collection_iter) => iter!(collection_iter.iter().map(Ok)),
227228
GenericIterable::JsonArray(collection_iter) => iter!(collection_iter.iter().map(Ok)),
228229
other => iter!(other.as_sequence_iterator(py)?),

0 commit comments

Comments
 (0)