Skip to content

Commit

Permalink
Implement the same for PyIterator.
Browse files Browse the repository at this point in the history
  • Loading branch information
moriyoshi committed Jun 26, 2021
1 parent 3cdba10 commit a050004
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 4 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add support for `#[pyclass(extends=Exception)]`. [#1591](https://github.com/PyO3/pyo3/pull/1591)
- Add support for extracting `PathBuf` from `pathlib.Path`. [#1654](https://github.com/PyO3/pyo3/pull/1654)
- Add `#[pyo3(text_signature = "...")]` syntax for setting text signature. [#1658](https://github.com/PyO3/pyo3/pull/1658)
- Add impl for `Py::as_ref()` and `Py::into_ref()` to `PySequence` and `PyIterator`. [#1682](https://github.com/PyO3/pyo3/pull/1682)

### Changed
- Allow only one `#[pymethods]` block per `#[pyclass]` by default, to simplify the proc macro implementations. Add `multiple-pymethods` feature to opt-in to the more complex full behavior. [#1457](https://github.com/PyO3/pyo3/pull/1457)
Expand Down Expand Up @@ -86,7 +87,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Fix segfault when dereferencing `ffi::PyDateTimeAPI` without the GIL. [#1563](https://github.com/PyO3/pyo3/pull/1563)
- Fix memory leak when converting to u128 and i128. [#1638](https://github.com/PyO3/pyo3/pull/1638)
- Fix segfault when calling `PyList::get_item` with negative indices. [#1668](https://github.com/PyO3/pyo3/pull/1668)
- Fix inability to call `Py::as_ref()` against `PySequence`. [#1682](https://github.com/PyO3/pyo3/pull/1682)

## [0.13.2] - 2021-02-12
### Packaging
Expand Down
48 changes: 45 additions & 3 deletions src/types/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
//
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython

use crate::{ffi, AsPyPointer, PyAny, PyErr, PyNativeType, PyResult, Python};
use crate::{ffi, AsPyPointer, IntoPyPointer, PyAny, PyErr, PyNativeType, PyResult, Python};
#[cfg(any(not(Py_LIMITED_API), Py_3_8))]
use crate::{PyDowncastError, PyTryFrom};
use crate::{Py, PyDowncastError, PyTryFrom};

/// A Python iterator object.
///
Expand Down Expand Up @@ -93,6 +93,22 @@ impl<'v> PyTryFrom<'v> for PyIterator {
}
}

impl Py<PyIterator> {
/// Borrows a GIL-bound reference to the PyIterator. By binding to the GIL lifetime, this
/// allows the GIL-bound reference to not require `Python` for any of its methods.
pub fn as_ref<'py>(&'py self, _py: Python<'py>) -> &'py PyIterator {
let any = self.as_ptr() as *const PyAny;
unsafe { PyNativeType::unchecked_downcast(&*any) }
}

/// Similar to [`as_ref`](#method.as_ref), and also consumes this `Py` and registers the
/// Python object reference in PyO3's object storage. The reference count for the Python
/// object will not be decreased until the GIL lifetime ends.
pub fn into_ref(self, py: Python) -> &PyIterator {
unsafe { py.from_owned_ptr(self.into_ptr()) }
}
}

#[cfg(test)]
mod tests {
use super::PyIterator;
Expand All @@ -101,7 +117,7 @@ mod tests {
use crate::types::{PyDict, PyList};
#[cfg(any(not(Py_LIMITED_API), Py_3_8))]
use crate::{Py, PyAny, PyTryFrom};
use crate::{Python, ToPyObject};
use crate::{PyResult, Python, ToPyObject};
use indoc::indoc;

#[test]
Expand Down Expand Up @@ -213,4 +229,30 @@ mod tests {
let iter: &PyIterator = PyIterator::try_from(obj.as_ref(py)).unwrap();
assert_eq!(obj, iter.into());
}

#[test]
fn test_as_ref() {
let gil = Python::acquire_gil();
let py = gil.python();
let iter: Py<PyIterator> = PyList::empty(py).to_object(py).as_ref(py).iter().unwrap().into();
let mut iter_ref: &PyIterator = iter.as_ref(py);
assert!(matches!(iter_ref.next(), Option::<PyResult::<&PyAny>>::None));
}

#[test]
fn test_into_ref() {
let gil = Python::acquire_gil();
let py = gil.python();
let l = PyList::empty(py).to_object(py);
let bare_ref = l.as_ref(py).iter().unwrap();
assert_eq!(bare_ref.get_refcnt(), 1);
let iter: Py<PyIterator> = bare_ref.into();
assert_eq!(bare_ref.get_refcnt(), 2);
Python::with_gil(|py| {
let mut iter_ref = iter.into_ref(py);
assert!(matches!(iter_ref.next(), Option::<PyResult::<&PyAny>>::None));
assert_eq!(iter_ref.get_refcnt(), 2);
});
assert_eq!(bare_ref.get_refcnt(), 2);
}
}

0 comments on commit a050004

Please sign in to comment.