Skip to content

Commit 302b3bb

Browse files
committed
Merge branch 'master' into pyclass-new-layout
2 parents c57177a + 95d045f commit 302b3bb

File tree

7 files changed

+123
-18
lines changed

7 files changed

+123
-18
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1212
* The blanket implementations for `FromPyObject` for `&T` and `&mut T` are no longer specializable. Implement `PyTryFrom` for your type to control the behavior of `FromPyObject::extract()` for your types.
1313
* The implementation for `IntoPy<U> for T` where `U: FromPy<T>` is no longer specializable. Control the behavior of this via the implementation of `FromPy`.
1414

15+
### Added
16+
17+
* Implemented `IntoIterator` for `PySet` and `PyFrozenSet`. [#716](https://github.com/PyO3/pyo3/pull/716)
18+
19+
### Fixed
20+
21+
* Clear error indicator when the exception is handled on the Rust side. [#719](https://github.com/PyO3/pyo3/pull/719)
22+
1523
## [0.8.5]
1624

1725
* Support for `#[name = "foo"]` attribute for `#[pyfunction]` and in `#[pymethods]`. [#692](https://github.com/PyO3/pyo3/pull/692)

src/ffi/setobject.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,13 @@ extern "C" {
8282
pub fn PySet_Add(set: *mut PyObject, key: *mut PyObject) -> c_int;
8383
#[cfg_attr(PyPy, link_name = "PyPySet_Pop")]
8484
pub fn PySet_Pop(set: *mut PyObject) -> *mut PyObject;
85+
86+
#[cfg(not(Py_LIMITED_API))]
87+
#[cfg_attr(PyPy, link_name = "_PySet_NextEntry")]
88+
pub fn _PySet_NextEntry(
89+
set: *mut PyObject,
90+
pos: *mut Py_ssize_t,
91+
key: *mut *mut PyObject,
92+
hash: *mut super::Py_hash_t,
93+
) -> c_int;
8594
}

src/types/dict.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,23 +155,20 @@ impl PyDict {
155155
}
156156
}
157157

158-
/// Returns a iterator of (key, value) pairs in this dictionary
159-
/// Note that it's unsafe to use when the dictionary might be changed
160-
/// by other python code.
158+
/// Returns a iterator of (key, value) pairs in this dictionary.
159+
///
160+
/// Note that it's unsafe to use when the dictionary might be changed by other code.
161161
pub fn iter(&self) -> PyDictIterator {
162-
let py = self.py();
163162
PyDictIterator {
164-
dict: self.to_object(py),
163+
dict: self.as_ref(),
165164
pos: 0,
166-
py,
167165
}
168166
}
169167
}
170168

171169
pub struct PyDictIterator<'py> {
172-
dict: PyObject,
170+
dict: &'py PyAny,
173171
pos: isize,
174-
py: Python<'py>,
175172
}
176173

177174
impl<'py> Iterator for PyDictIterator<'py> {
@@ -183,7 +180,7 @@ impl<'py> Iterator for PyDictIterator<'py> {
183180
let mut key: *mut ffi::PyObject = std::ptr::null_mut();
184181
let mut value: *mut ffi::PyObject = std::ptr::null_mut();
185182
if ffi::PyDict_Next(self.dict.as_ptr(), &mut self.pos, &mut key, &mut value) != 0 {
186-
let py = self.py;
183+
let py = self.dict.py();
187184
Some((py.from_borrowed_ptr(key), py.from_borrowed_ptr(value)))
188185
} else {
189186
None

src/types/iterator.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ impl<'p> PyIterator<'p> {
4242
let ptr = ffi::PyObject_GetIter(obj.as_ptr());
4343
// Returns NULL if an object cannot be iterated.
4444
if ptr.is_null() {
45+
PyErr::fetch(py);
4546
return Err(PyDowncastError);
4647
}
4748

src/types/list.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ impl PyList {
113113
})
114114
}
115115

116-
/// Returns an iterator over the tuple items.
116+
/// Returns an iterator over this list items.
117117
pub fn iter(&self) -> PyListIterator {
118118
PyListIterator {
119119
list: self,

src/types/set.rs

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::ffi;
66
use crate::instance::PyNativeType;
77
use crate::internal_tricks::Unsendable;
88
use crate::object::PyObject;
9+
use crate::types::PyAny;
910
use crate::AsPyPointer;
1011
use crate::Python;
1112
use crate::{ToBorrowedObject, ToPyObject};
@@ -97,7 +98,57 @@ impl PySet {
9798

9899
/// Remove and return an arbitrary element from the set
99100
pub fn pop(&self) -> Option<PyObject> {
100-
unsafe { PyObject::from_owned_ptr_or_opt(self.py(), ffi::PySet_Pop(self.as_ptr())) }
101+
let element =
102+
unsafe { PyObject::from_owned_ptr_or_err(self.py(), ffi::PySet_Pop(self.as_ptr())) };
103+
match element {
104+
Ok(e) => Some(e),
105+
Err(_) => None,
106+
}
107+
}
108+
109+
/// Returns an iterator of values in this set.
110+
///
111+
/// Note that it can be unsafe to use when the set might be changed by other code.
112+
#[cfg(not(Py_LIMITED_API))]
113+
pub fn iter(&self) -> PySetIterator {
114+
PySetIterator {
115+
set: self.as_ref(),
116+
pos: 0,
117+
}
118+
}
119+
}
120+
121+
#[cfg(not(Py_LIMITED_API))]
122+
pub struct PySetIterator<'py> {
123+
set: &'py super::PyAny,
124+
pos: isize,
125+
}
126+
127+
#[cfg(not(Py_LIMITED_API))]
128+
impl<'py> Iterator for PySetIterator<'py> {
129+
type Item = &'py super::PyAny;
130+
131+
#[inline]
132+
fn next(&mut self) -> Option<Self::Item> {
133+
unsafe {
134+
let mut key: *mut ffi::PyObject = std::ptr::null_mut();
135+
let mut hash: ffi::Py_hash_t = 0;
136+
if ffi::_PySet_NextEntry(self.set.as_ptr(), &mut self.pos, &mut key, &mut hash) != 0 {
137+
Some(self.set.py().from_borrowed_ptr(key))
138+
} else {
139+
None
140+
}
141+
}
142+
}
143+
}
144+
145+
#[cfg(not(Py_LIMITED_API))]
146+
impl<'a> std::iter::IntoIterator for &'a PySet {
147+
type Item = &'a PyAny;
148+
type IntoIter = PySetIterator<'a>;
149+
150+
fn into_iter(self) -> Self::IntoIter {
151+
self.iter()
101152
}
102153
}
103154

@@ -171,15 +222,34 @@ impl PyFrozenSet {
171222
}
172223
})
173224
}
225+
226+
/// Returns an iterator of values in this frozen set.
227+
///
228+
/// Note that it can be unsafe to use when the set might be changed by other code.
229+
#[cfg(not(Py_LIMITED_API))]
230+
pub fn iter(&self) -> PySetIterator {
231+
self.into_iter()
232+
}
233+
}
234+
235+
#[cfg(not(Py_LIMITED_API))]
236+
impl<'a> std::iter::IntoIterator for &'a PyFrozenSet {
237+
type Item = &'a PyAny;
238+
type IntoIter = PySetIterator<'a>;
239+
240+
fn into_iter(self) -> Self::IntoIter {
241+
PySetIterator {
242+
set: self.as_ref(),
243+
pos: 0,
244+
}
245+
}
174246
}
175247

176248
#[cfg(test)]
177249
mod test {
178250
use super::{PyFrozenSet, PySet};
179251
use crate::instance::AsPyRef;
180-
use crate::objectprotocol::ObjectProtocol;
181-
use crate::Python;
182-
use crate::{PyTryFrom, ToPyObject};
252+
use crate::{ObjectProtocol, PyTryFrom, Python, ToPyObject};
183253
use std::collections::HashSet;
184254

185255
#[test]
@@ -264,6 +334,9 @@ mod test {
264334
assert!(val.is_some());
265335
let val2 = set.pop();
266336
assert!(val2.is_none());
337+
assert!(py
338+
.eval("print('Exception state should not be set.')", None, None)
339+
.is_ok());
267340
}
268341

269342
#[test]
@@ -272,8 +345,15 @@ mod test {
272345
let py = gil.python();
273346

274347
let set = PySet::new(py, &[1]).unwrap();
275-
for el in set.iter().unwrap() {
276-
assert_eq!(1i32, el.unwrap().extract::<i32>().unwrap());
348+
349+
// iter method
350+
for el in set.iter() {
351+
assert_eq!(1i32, el.extract().unwrap());
352+
}
353+
354+
// intoiterator iteration
355+
for el in set {
356+
assert_eq!(1i32, el.extract().unwrap());
277357
}
278358
}
279359

@@ -311,8 +391,15 @@ mod test {
311391
let py = gil.python();
312392

313393
let set = PyFrozenSet::new(py, &[1]).unwrap();
314-
for el in set.iter().unwrap() {
315-
assert_eq!(1i32, el.unwrap().extract::<i32>().unwrap());
394+
395+
// iter method
396+
for el in set.iter() {
397+
assert_eq!(1i32, el.extract::<i32>().unwrap());
398+
}
399+
400+
// intoiterator iteration
401+
for el in set {
402+
assert_eq!(1i32, el.extract::<i32>().unwrap());
316403
}
317404
}
318405
}

tests/test_various.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,7 @@ fn incorrect_iter() {
178178
let int_ref = int.as_ref(py);
179179
// Should not segfault.
180180
assert!(int_ref.iter().is_err());
181+
assert!(py
182+
.eval("print('Exception state should not be set.')", None, None)
183+
.is_ok());
181184
}

0 commit comments

Comments
 (0)