diff --git a/src/ffi/abstract_.rs b/src/ffi/abstract_.rs index 651e6fac1d3..e4ee4eadfe9 100644 --- a/src/ffi/abstract_.rs +++ b/src/ffi/abstract_.rs @@ -3,12 +3,6 @@ use crate::ffi::pyport::Py_ssize_t; use std::os::raw::{c_char, c_int}; use std::ptr; -#[cfg_attr(windows, link(name = "pythonXY"))] -extern "C" { - #[cfg_attr(PyPy, link_name = "PyPySequence_Type")] - pub static mut PySequence_Type: PyTypeObject; -} - extern "C" { #[cfg(PyPy)] #[link_name = "PyPyObject_DelAttrString"] diff --git a/src/instance.rs b/src/instance.rs index acc3f0ef153..84e186098ed 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -3,7 +3,7 @@ use crate::conversion::{PyTryFrom, ToBorrowedObject}; use crate::err::{PyDowncastError, PyErr, PyResult}; use crate::gil; use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell}; -use crate::types::{PyDict, PyTuple}; +use crate::types::{PyDict, PyTuple, PySequence}; use crate::{ ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass, PyClassInitializer, PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject, @@ -128,6 +128,32 @@ where } } +impl Py { + /// Borrows a GIL-bound reference to the PySequence. By binding to the GIL lifetime, this + /// allows the GIL-bound reference to not require `Python` for any of its methods. + /// + /// ``` + /// # use pyo3::prelude::*; + /// # use pyo3::types::PyList; + /// # Python::with_gil(|py| { + /// let list: Py = PyList::empty(py).cast_as::().unwrap().into(); + /// let seq: &PySequence = list.as_ref(py); + /// assert_eq!(seq.len().unwrap(), 0); + /// # }); + /// ``` + pub fn as_ref<'py>(&'py self, _py: Python<'py>) -> &'py PySequence { + 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) -> &PySequence { + unsafe { py.from_owned_ptr(self.into_ptr()) } + } +} + impl Py where T: PyClass, diff --git a/src/types/sequence.rs b/src/types/sequence.rs index 4ef37c5c825..6fa2ff8bdee 100644 --- a/src/types/sequence.rs +++ b/src/types/sequence.rs @@ -1,6 +1,6 @@ // Copyright (c) 2017-present PyO3 Project and Contributors -use crate::err::{self, PyErr, PyResult}; +use crate::err::{self, PyDowncastError, PyErr, PyResult}; use crate::ffi::{self, Py_ssize_t}; use crate::instance::PyNativeType; use crate::types::{PyAny, PyList, PyTuple}; @@ -11,7 +11,6 @@ use crate::{FromPyObject, PyTryFrom, ToBorrowedObject}; #[repr(transparent)] pub struct PySequence(PyAny); pyobject_native_type_named!(PySequence); -pyobject_native_type_info!(PySequence, ffi::PySequence_Type, Some("builtins"), #checkfunction=ffi::PySequence_Check); pyobject_native_type_extract!(PySequence); impl PySequence { @@ -294,6 +293,29 @@ where } } +impl<'v> PyTryFrom<'v> for PySequence { + fn try_from>(value: V) -> Result<&'v PySequence, PyDowncastError<'v>> { + let value = value.into(); + unsafe { + if ffi::PySequence_Check(value.as_ptr()) != 0 { + Ok(::try_from_unchecked(value)) + } else { + Err(PyDowncastError::new(value, "Sequence")) + } + } + } + + fn try_from_exact>(value: V) -> Result<&'v PySequence, PyDowncastError<'v>> { + ::try_from(value) + } + + #[inline] + unsafe fn try_from_unchecked>(value: V) -> &'v PySequence { + let ptr = value.into() as *const _ as *const PySequence; + &*ptr + } +} + fn extract_sequence<'s, T>(obj: &'s PyAny) -> PyResult> where T: FromPyObject<'s>, @@ -312,6 +334,7 @@ mod test { use crate::AsPyPointer; use crate::Python; use crate::{Py, PyObject, PyTryFrom, ToPyObject}; + use crate::types::PyList; fn get_object() -> PyObject { // Convenience function for getting a single unique object @@ -641,7 +664,7 @@ mod test { } #[test] - fn test_as_empty() { + fn test_is_empty() { let gil = Python::acquire_gil(); let py = gil.python(); let list = vec![1].to_object(py); @@ -654,11 +677,11 @@ mod test { } #[test] - fn test_is_ref() { + fn test_as_ref() { let gil = Python::acquire_gil(); let py = gil.python(); - let list = vec!["foo"].to_object(py); - let seq: Py = list.cast_as::(py).unwrap().into(); - assert!(seq.as_ref(py).extract::>().unwrap() == ["foo"]); + let seq: Py = PyList::empty(py).cast_as::().unwrap().into(); + let seq_ref: &PySequence = seq.as_ref(py); + assert_eq!(seq_ref.len().unwrap(), 0); } }