diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 3a7d943f401..fe6b71cba27 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -737,6 +737,13 @@ impl<'a> PyClassImplsBuilder<'a> { _pyo3::IntoPy::into_py(_pyo3::Py::new(py, self).unwrap(), py) } } + + impl _pyo3::IntoPyObject for #cls { + type Target = #cls; + fn into_py(self, py: _pyo3::Python) -> _pyo3::Py<#cls> { + _pyo3::Py::new(py, self).unwrap() + } + } } } else { quote! {} diff --git a/src/callback.rs b/src/callback.rs index 3794b9e5906..87e00aafb92 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -6,8 +6,8 @@ use crate::err::{PyErr, PyResult}; use crate::exceptions::PyOverflowError; use crate::ffi::{self, Py_hash_t}; use crate::panic::PanicException; -use crate::{GILPool, IntoPyPointer}; -use crate::{IntoPy, PyObject, Python}; +use crate::{GILPool, IntoPyObject, IntoPyPointer}; +use crate::{PyObject, Python}; use std::any::Any; use std::os::raw::c_int; use std::panic::UnwindSafe; @@ -53,7 +53,7 @@ where impl IntoPyCallbackOutput<*mut ffi::PyObject> for T where - T: IntoPy, + T: IntoPyObject, { #[inline] fn convert(self, py: Python<'_>) -> PyResult<*mut ffi::PyObject> { @@ -118,11 +118,11 @@ impl IntoPyCallbackOutput for usize { impl IntoPyCallbackOutput for T where - T: IntoPy, + T: IntoPyObject, { #[inline] fn convert(self, py: Python<'_>) -> PyResult { - Ok(self.into_py(py)) + Ok(self.into_object(py)) } } diff --git a/src/conversion.rs b/src/conversion.rs index 6dffae77905..e881f98321b 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -184,6 +184,98 @@ pub trait IntoPy: Sized { fn into_py(self, py: Python<'_>) -> T; } +/// Defines a conversion from a Rust type to a Python object. +/// +/// It functions similarly to std's [`Into`](std::convert::Into) trait, +/// but requires a [GIL token](Python) as an argument. +/// Many functions and traits internal to PyO3 require this trait as a bound, +/// so a lack of this trait can manifest itself in different error messages. +/// +/// # Examples +/// ## With `#[pyclass]` +/// The easiest way to implement `IntoPy` is by exposing a struct as a native Python object +/// by annotating it with [`#[pyclass]`](crate::prelude::pyclass). +/// +/// ```rust +/// use pyo3::prelude::*; +/// +/// #[pyclass] +/// struct Number { +/// #[pyo3(get, set)] +/// value: i32, +/// } +/// ``` +/// Python code will see this as an instance of the `Number` class with a `value` attribute. +/// +/// ## Conversion to a Python object +/// +/// However, it may not be desirable to expose the existence of `Number` to Python code. +/// `IntoPy` allows us to define a conversion to an appropriate Python object. +/// ```rust +/// use pyo3::prelude::*; +/// +/// struct Number { +/// value: i32, +/// } +/// +/// impl IntoPy for Number { +/// fn into_py(self, py: Python<'_>) -> PyObject { +/// // delegates to i32's IntoPy implementation. +/// self.value.into_py(py) +/// } +/// } +/// ``` +/// Python code will see this as an `int` object. +/// +/// ## Dynamic conversion into Python objects. +/// It is also possible to return a different Python object depending on some condition. +/// This is useful for types like enums that can carry different types. +/// +/// ```rust +/// use pyo3::prelude::*; +/// +/// enum Value { +/// Integer(i32), +/// String(String), +/// None, +/// } +/// +/// impl IntoPy for Value { +/// fn into_py(self, py: Python<'_>) -> PyObject { +/// match self { +/// Self::Integer(val) => val.into_py(py), +/// Self::String(val) => val.into_py(py), +/// Self::None => py.None(), +/// } +/// } +/// } +/// # fn main() { +/// # Python::with_gil(|py| { +/// # let v = Value::Integer(73).into_py(py); +/// # let v = v.extract::(py).unwrap(); +/// # +/// # let v = Value::String("foo".into()).into_py(py); +/// # let v = v.extract::(py).unwrap(); +/// # +/// # let v = Value::None.into_py(py); +/// # let v = v.extract::>>(py).unwrap(); +/// # }); +/// # } +/// ``` +/// Python code will see this as any of the `int`, `string` or `None` objects. +#[cfg_attr(docsrs, doc(alias = "IntoPyCallbackOutput"))] +pub trait IntoPyObject: Sized { + type Target; + + /// Performs the conversion. + fn into_py(self, py: Python<'_>) -> Py; + + /// Performs the conversion. + fn into_object(self, py: Python<'_>) -> PyObject { + unsafe { Py::from_owned_ptr(py, self.into_py(py).into_ptr()) } + } +} + /// `FromPyObject` is implemented by various types that can be extracted from /// a Python object reference. /// @@ -244,6 +336,17 @@ where } } +impl IntoPyObject for Option +where + T: IntoPyObject, +{ + type Target = PyAny; + + fn into_py(self, py: Python<'_>) -> PyObject { + self.map_or_else(|| py.None(), |val| val.into_object(py)) + } +} + /// `()` is converted to Python `None`. impl ToPyObject for () { fn to_object(&self, py: Python<'_>) -> PyObject { @@ -431,6 +534,13 @@ impl IntoPy> for () { } } +impl IntoPyObject for () { + type Target = PyTuple; + fn into_py(self, py: Python<'_>) -> Py { + PyTuple::empty(py).into() + } +} + /// Raw level conversion between `*mut ffi::PyObject` and PyO3 types. /// /// # Safety diff --git a/src/conversions/array.rs b/src/conversions/array.rs index 1b630a8aa00..b82301b96ef 100644 --- a/src/conversions/array.rs +++ b/src/conversions/array.rs @@ -3,9 +3,12 @@ use crate::{exceptions, PyErr}; #[cfg(min_const_generics)] mod min_const_generics { use super::invalid_sequence_length; - use crate::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, PyTryFrom, Python, ToPyObject}; + use crate::{ + types::PyList, FromPyObject, IntoPyObject, Py, PyAny, PyObject, PyResult, PyTryFrom, + Python, ToPyObject, + }; - impl IntoPy for [T; N] + impl crate::IntoPy for [T; N] where T: ToPyObject, { @@ -14,6 +17,16 @@ mod min_const_generics { } } + impl IntoPyObject for [T; N] + where + T: ToPyObject, + { + type Target = PyList; + fn into_py(self, py: Python<'_>) -> Py { + PyList::new(py, self.as_ref()).into() + } + } + impl<'a, T, const N: usize> FromPyObject<'a> for [T; N] where T: FromPyObject<'a>, diff --git a/src/conversions/hashbrown.rs b/src/conversions/hashbrown.rs index da08e6adfa4..6401c9bc198 100644 --- a/src/conversions/hashbrown.rs +++ b/src/conversions/hashbrown.rs @@ -52,6 +52,22 @@ where } } +impl IntoPyObject for hashbrown::HashMap +where + K: hash::Hash + cmp::Eq + IntoPyObject, + V: IntoPyObject, + H: hash::BuildHasher, +{ + type Target = PyDict; + + fn into_py(self, py: Python<'_>) -> Py { + let iter = self + .into_iter() + .map(|(k, v)| (k.into_py(py), v.into_py(py))); + IntoPyDict::into_py_dict(iter, py) + } +} + impl<'source, K, V, S> FromPyObject<'source> for hashbrown::HashMap where K: FromPyObject<'source> + cmp::Eq + hash::Hash, diff --git a/src/conversions/indexmap.rs b/src/conversions/indexmap.rs index 0e18cad7da5..0bbadbfb98d 100644 --- a/src/conversions/indexmap.rs +++ b/src/conversions/indexmap.rs @@ -121,6 +121,21 @@ where } } +impl IntoPyObject for indexmap::IndexMap +where + K: hash::Hash + cmp::Eq + IntoPyObject, + V: IntoPyObject, + H: hash::BuildHasher, +{ + type Target = PyDict; + fn into_py(self, py: Python<'_>) -> PyObject { + let iter = self + .into_iter() + .map(|(k, v)| (k.into_py(py), v.into_py(py))); + IntoPyDict::into_py_dict(iter, py).into() + } +} + impl<'source, K, V, S> FromPyObject<'source> for indexmap::IndexMap where K: FromPyObject<'source> + cmp::Eq + hash::Hash, diff --git a/src/conversions/osstr.rs b/src/conversions/osstr.rs index 21ad0cab6c1..165503ff8f2 100644 --- a/src/conversions/osstr.rs +++ b/src/conversions/osstr.rs @@ -2,7 +2,7 @@ use crate::types::PyString; #[cfg(windows)] use crate::PyErr; use crate::{ - ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyObject, PyResult, PyTryFrom, Python, + ffi, AsPyPointer, FromPyObject, IntoPyObject, Py, PyAny, PyObject, PyResult, PyTryFrom, Python, ToPyObject, }; use std::borrow::Cow; @@ -12,43 +12,7 @@ use std::os::raw::c_char; impl ToPyObject for OsStr { fn to_object(&self, py: Python<'_>) -> PyObject { - // If the string is UTF-8, take the quick and easy shortcut - if let Some(valid_utf8_path) = self.to_str() { - return valid_utf8_path.to_object(py); - } - - // All targets besides windows support the std::os::unix::ffi::OsStrExt API: - // https://doc.rust-lang.org/src/std/sys_common/mod.rs.html#59 - #[cfg(not(windows))] - { - #[cfg(target_os = "wasi")] - let bytes = std::os::wasi::ffi::OsStrExt::as_bytes(self); - #[cfg(not(target_os = "wasi"))] - let bytes = std::os::unix::ffi::OsStrExt::as_bytes(self); - - let ptr = bytes.as_ptr() as *const c_char; - let len = bytes.len() as ffi::Py_ssize_t; - unsafe { - // DecodeFSDefault automatically chooses an appropriate decoding mechanism to - // parse os strings losslessly (i.e. surrogateescape most of the time) - let pystring = ffi::PyUnicode_DecodeFSDefaultAndSize(ptr, len); - PyObject::from_owned_ptr(py, pystring) - } - } - - #[cfg(windows)] - { - let wstr: Vec = std::os::windows::ffi::OsStrExt::encode_wide(self).collect(); - - unsafe { - // This will not panic because the data from encode_wide is well-formed Windows - // string data - PyObject::from_owned_ptr( - py, - ffi::PyUnicode_FromWideChar(wstr.as_ptr(), wstr.len() as ffi::Py_ssize_t), - ) - } - } + self.into_object(py) } } @@ -110,13 +74,57 @@ impl FromPyObject<'_> for OsString { } } -impl IntoPy for &'_ OsStr { +impl crate::IntoPy for &'_ OsStr { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { self.to_object(py) } } +impl IntoPyObject for &'_ OsStr { + type Target = PyString; + #[inline] + fn into_py(self, py: Python<'_>) -> Py { + // If the string is UTF-8, take the quick and easy shortcut + if let Some(valid_utf8_path) = self.to_str() { + return valid_utf8_path.into_py(py); + } + + // All targets besides windows support the std::os::unix::ffi::OsStrExt API: + // https://doc.rust-lang.org/src/std/sys_common/mod.rs.html#59 + #[cfg(not(windows))] + { + #[cfg(target_os = "wasi")] + let bytes = std::os::wasi::ffi::OsStrExt::as_bytes(self); + #[cfg(not(target_os = "wasi"))] + let bytes = std::os::unix::ffi::OsStrExt::as_bytes(self); + + let ptr = bytes.as_ptr() as *const c_char; + let len = bytes.len() as ffi::Py_ssize_t; + unsafe { + // DecodeFSDefault automatically chooses an appropriate decoding mechanism to + // parse os strings losslessly (i.e. surrogateescape most of the time) + let pystring = ffi::PyUnicode_DecodeFSDefaultAndSize(ptr, len); + Py::from_owned_ptr(py, pystring) + } + } + + #[cfg(windows)] + { + let wstr: Vec = std::os::windows::ffi::OsStrExt::encode_wide(self).collect(); + + unsafe { + // This will not panic because the data from encode_wide is well-formed Windows + // string data + Py::from_owned_ptr( + py, + ffi::PyUnicode_FromWideChar(wstr.as_ptr(), wstr.len() as ffi::Py_ssize_t), + ) + } + } + } +} + impl ToPyObject for Cow<'_, OsStr> { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { @@ -124,13 +132,21 @@ impl ToPyObject for Cow<'_, OsStr> { } } -impl IntoPy for Cow<'_, OsStr> { +impl crate::IntoPy for Cow<'_, OsStr> { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { self.to_object(py) } } +impl IntoPyObject for Cow<'_, OsStr> { + type Target = PyString; + #[inline] + fn into_py(self, py: Python<'_>) -> Py { + (*self).into_py(py) + } +} + impl ToPyObject for OsString { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { @@ -138,18 +154,32 @@ impl ToPyObject for OsString { } } -impl IntoPy for OsString { +impl crate::IntoPy for OsString { fn into_py(self, py: Python<'_>) -> PyObject { self.to_object(py) } } -impl<'a> IntoPy for &'a OsString { +impl IntoPyObject for OsString { + type Target = PyString; + fn into_py(self, py: Python<'_>) -> Py { + (*self).into_py(py) + } +} + +impl<'a> crate::IntoPy for &'a OsString { fn into_py(self, py: Python<'_>) -> PyObject { self.to_object(py) } } +impl<'a> IntoPyObject for &'a OsString { + type Target = PyString; + fn into_py(self, py: Python<'_>) -> Py { + (&**self).into_py(py) + } +} + #[cfg(test)] mod tests { use crate::{types::PyString, IntoPy, PyObject, Python, ToPyObject}; diff --git a/src/conversions/path.rs b/src/conversions/path.rs index a5f04a5db38..e985bc74d2c 100644 --- a/src/conversions/path.rs +++ b/src/conversions/path.rs @@ -1,5 +1,5 @@ -use crate::types::PyType; -use crate::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject}; +use crate::types::{PyString, PyType}; +use crate::{FromPyObject, IntoPyObject, Py, PyAny, PyObject, PyResult, Python, ToPyObject}; use std::borrow::Cow; use std::ffi::OsString; use std::path::{Path, PathBuf}; @@ -32,13 +32,21 @@ impl FromPyObject<'_> for PathBuf { } } -impl<'a> IntoPy for &'a Path { +impl<'a> crate::IntoPy for &'a Path { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { self.as_os_str().to_object(py) } } +impl<'a> IntoPyObject for &'a Path { + type Target = PyString; + #[inline] + fn into_py(self, py: Python<'_>) -> Py { + self.as_os_str().into_py(py) + } +} + impl<'a> ToPyObject for Cow<'a, Path> { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { @@ -46,13 +54,21 @@ impl<'a> ToPyObject for Cow<'a, Path> { } } -impl<'a> IntoPy for Cow<'a, Path> { +impl<'a> crate::IntoPy for Cow<'a, Path> { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { self.to_object(py) } } +impl<'a> IntoPyObject for Cow<'a, Path> { + type Target = PyString; + #[inline] + fn into_py(self, py: Python<'_>) -> Py { + (*self).into_py(py) + } +} + impl ToPyObject for PathBuf { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { @@ -60,18 +76,32 @@ impl ToPyObject for PathBuf { } } -impl IntoPy for PathBuf { +impl crate::IntoPy for PathBuf { fn into_py(self, py: Python<'_>) -> PyObject { self.into_os_string().to_object(py) } } -impl<'a> IntoPy for &'a PathBuf { +impl IntoPyObject for PathBuf { + type Target = PyString; + fn into_py(self, py: Python<'_>) -> Py { + self.into_os_string().into_py(py) + } +} + +impl<'a> crate::IntoPy for &'a PathBuf { fn into_py(self, py: Python<'_>) -> PyObject { self.as_os_str().to_object(py) } } +impl<'a> IntoPyObject for &'a PathBuf { + type Target = PyString; + fn into_py(self, py: Python<'_>) -> Py { + self.as_os_str().into_py(py) + } +} + #[cfg(test)] mod tests { use crate::{types::PyString, IntoPy, PyObject, Python, ToPyObject}; diff --git a/src/err/err_state.rs b/src/err/err_state.rs index ecebeb5a622..cc8b0ef4a18 100644 --- a/src/err/err_state.rs +++ b/src/err/err_state.rs @@ -3,7 +3,7 @@ use crate::{ ffi, type_object::PyTypeObject, types::{PyTraceback, PyType}, - AsPyPointer, IntoPy, IntoPyPointer, Py, PyObject, Python, + AsPyPointer, IntoPyObject, IntoPyPointer, Py, PyObject, Python, }; #[derive(Clone)] @@ -38,10 +38,10 @@ pub trait PyErrArguments: Send + Sync { impl PyErrArguments for T where - T: IntoPy + Send + Sync, + T: IntoPyObject + Send + Sync, { fn arguments(self, py: Python<'_>) -> PyObject { - self.into_py(py) + self.into_object(py) } } diff --git a/src/err/mod.rs b/src/err/mod.rs index eed6798ad58..cd9c78bed7d 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -8,7 +8,8 @@ use crate::{ ffi, }; use crate::{ - AsPyPointer, IntoPy, IntoPyPointer, Py, PyAny, PyObject, Python, ToBorrowedObject, ToPyObject, + AsPyPointer, IntoPy, IntoPyObject, IntoPyPointer, Py, PyAny, PyObject, Python, + ToBorrowedObject, ToPyObject, }; use std::borrow::Cow; use std::cell::UnsafeCell; @@ -651,15 +652,29 @@ impl IntoPy for PyErr { } } +impl IntoPyObject for PyErr { + type Target = PyAny; + fn into_py(self, py: Python<'_>) -> PyObject { + self.into_value(py).into() + } +} + impl ToPyObject for PyErr { fn to_object(&self, py: Python<'_>) -> PyObject { - self.clone_ref(py).into_py(py) + self.clone_ref(py).into_object(py) } } impl<'a> IntoPy for &'a PyErr { fn into_py(self, py: Python<'_>) -> PyObject { - self.clone_ref(py).into_py(py) + self.clone_ref(py).into_object(py) + } +} + +impl<'a> IntoPyObject for &'a PyErr { + type Target = PyAny; + fn into_py(self, py: Python<'_>) -> PyObject { + self.clone_ref(py).into_object(py) } } diff --git a/src/instance.rs b/src/instance.rs index a8c0997bf1f..174be2645a8 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1,13 +1,13 @@ // Copyright (c) 2017-present PyO3 Project and Contributors use crate::conversion::{PyTryFrom, ToBorrowedObject}; use crate::err::{self, PyDowncastError, PyErr, PyResult}; -use crate::gil; use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell}; use crate::types::{PyDict, PyTuple}; use crate::{ - ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass, PyClassInitializer, - PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject, + ffi, AsPyPointer, FromPyObject, IntoPyPointer, PyAny, PyClass, PyClassInitializer, PyRef, + PyRefMut, PyTypeInfo, Python, ToPyObject, }; +use crate::{gil, IntoPyObject}; use std::marker::PhantomData; use std::mem; use std::ptr::NonNull; @@ -602,7 +602,7 @@ impl Py { pub fn call( &self, py: Python<'_>, - args: impl IntoPy>, + args: impl IntoPyObject, kwargs: Option<&PyDict>, ) -> PyResult { let args = args.into_py(py).into_ptr(); @@ -620,7 +620,11 @@ impl Py { /// Calls the object with only positional arguments. /// /// This is equivalent to the Python expression `self(*args)`. - pub fn call1(&self, py: Python<'_>, args: impl IntoPy>) -> PyResult { + pub fn call1( + &self, + py: Python<'_>, + args: impl IntoPyObject, + ) -> PyResult { self.call(py, args, None) } @@ -647,7 +651,7 @@ impl Py { &self, py: Python<'_>, name: &str, - args: impl IntoPy>, + args: impl IntoPyObject, kwargs: Option<&PyDict>, ) -> PyResult { name.with_borrowed_ptr(py, |name| unsafe { @@ -672,7 +676,7 @@ impl Py { &self, py: Python<'_>, name: &str, - args: impl IntoPy>, + args: impl IntoPyObject, ) -> PyResult { self.call_method(py, name, args, None) } @@ -811,7 +815,7 @@ impl ToPyObject for Py { } } -impl IntoPy for Py { +impl crate::IntoPy for Py { /// Converts a `Py` instance to `PyObject`. /// Consumes `self` without calling `Py_DECREF()`. #[inline] @@ -820,6 +824,14 @@ impl IntoPy for Py { } } +impl IntoPyObject for Py { + type Target = T; + #[inline] + fn into_py(self, _py: Python<'_>) -> Py { + self + } +} + impl AsPyPointer for Py { /// Gets the underlying FFI pointer, returns a borrowed pointer. #[inline] diff --git a/src/lib.rs b/src/lib.rs index 2ed3d3c1311..2bfeacfdf1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -295,8 +295,8 @@ //! [`Ungil`]: crate::marker::Ungil pub use crate::class::*; pub use crate::conversion::{ - AsPyPointer, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, - ToBorrowedObject, ToPyObject, + AsPyPointer, FromPyObject, FromPyPointer, IntoPy, IntoPyObject, IntoPyPointer, PyTryFrom, + PyTryInto, ToBorrowedObject, ToPyObject, }; pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyResult}; #[cfg(not(PyPy))] diff --git a/src/prelude.rs b/src/prelude.rs index d7b65902542..77f888ece15 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,7 +11,7 @@ //! ``` pub use crate::conversion::{ - FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject, + FromPyObject, IntoPyObject, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject, }; pub use crate::err::{PyErr, PyResult}; pub use crate::gil::GILGuard; diff --git a/src/pycell.rs b/src/pycell.rs index 8e9038e9f0b..f2e2de34460 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -184,7 +184,7 @@ use crate::{ type_object::get_tp_free, PyTypeInfo, }; -use crate::{ffi, IntoPy, PyErr, PyNativeType, PyObject, PyResult, Python}; +use crate::{ffi, IntoPyObject, Py, PyErr, PyNativeType, PyObject, PyResult, Python}; use std::cell::{Cell, UnsafeCell}; use std::fmt; use std::mem::ManuallyDrop; @@ -504,6 +504,13 @@ impl ToPyObject for &PyCell { } } +impl IntoPyObject for &PyCell { + type Target = T; + fn into_py(self, py: Python<'_>) -> Py { + unsafe { Py::from_borrowed_ptr(py, self.as_ptr()) } + } +} + impl AsRef for PyCell { fn as_ref(&self) -> &PyAny { unsafe { self.py().from_borrowed_ptr(self.as_ptr()) } @@ -677,7 +684,14 @@ impl<'p, T: PyClass> Drop for PyRef<'p, T> { } } -impl IntoPy for PyRef<'_, T> { +impl crate::IntoPy for PyRef<'_, T> { + fn into_py(self, py: Python<'_>) -> PyObject { + unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } + } +} + +impl IntoPyObject for PyRef<'_, T> { + type Target = PyAny; fn into_py(self, py: Python<'_>) -> PyObject { unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } } @@ -775,7 +789,14 @@ impl<'p, T: PyClass> Drop for PyRefMut<'p, T> { } } -impl IntoPy for PyRefMut<'_, T> { +impl crate::IntoPy for PyRefMut<'_, T> { + fn into_py(self, py: Python<'_>) -> PyObject { + unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } + } +} + +impl IntoPyObject for PyRefMut<'_, T> { + type Target = PyAny; fn into_py(self, py: Python<'_>) -> PyObject { unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } } diff --git a/src/pyclass.rs b/src/pyclass.rs index eb2bb5c9595..c2eb759678c 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -7,7 +7,7 @@ use crate::{ assign_sequence_item_from_mapping, get_sequence_item_from_mapping, tp_dealloc, PyClassDict, PyClassImpl, PyClassItems, PyClassWeakRef, }, - IntoPy, IntoPyPointer, PyCell, PyErr, PyMethodDefType, PyNativeType, PyObject, PyResult, + IntoPyObject, IntoPyPointer, PyCell, PyErr, PyMethodDefType, PyNativeType, PyObject, PyResult, PyTypeInfo, Python, }; use std::{ @@ -495,24 +495,24 @@ impl IntoPyCallbackOutput<*mut ffi::PyObject> for PyIterNextOutput { impl IntoPyCallbackOutput for IterNextOutput where - T: IntoPy, - U: IntoPy, + T: IntoPyObject, + U: IntoPyObject, { fn convert(self, py: Python<'_>) -> PyResult { match self { - IterNextOutput::Yield(o) => Ok(IterNextOutput::Yield(o.into_py(py))), - IterNextOutput::Return(o) => Ok(IterNextOutput::Return(o.into_py(py))), + IterNextOutput::Yield(o) => Ok(IterNextOutput::Yield(o.into_object(py))), + IterNextOutput::Return(o) => Ok(IterNextOutput::Return(o.into_object(py))), } } } impl IntoPyCallbackOutput for Option where - T: IntoPy, + T: IntoPyObject, { fn convert(self, py: Python<'_>) -> PyResult { match self { - Some(o) => Ok(PyIterNextOutput::Yield(o.into_py(py))), + Some(o) => Ok(PyIterNextOutput::Yield(o.into_object(py))), None => Ok(PyIterNextOutput::Return(py.None())), } } @@ -544,24 +544,24 @@ impl IntoPyCallbackOutput<*mut ffi::PyObject> for PyIterANextOutput { impl IntoPyCallbackOutput for IterANextOutput where - T: IntoPy, - U: IntoPy, + T: IntoPyObject, + U: IntoPyObject, { fn convert(self, py: Python<'_>) -> PyResult { match self { - IterANextOutput::Yield(o) => Ok(IterANextOutput::Yield(o.into_py(py))), - IterANextOutput::Return(o) => Ok(IterANextOutput::Return(o.into_py(py))), + IterANextOutput::Yield(o) => Ok(IterANextOutput::Yield(o.into_object(py))), + IterANextOutput::Return(o) => Ok(IterANextOutput::Return(o.into_object(py))), } } } impl IntoPyCallbackOutput for Option where - T: IntoPy, + T: IntoPyObject, { fn convert(self, py: Python<'_>) -> PyResult { match self { - Some(o) => Ok(PyIterANextOutput::Yield(o.into_py(py))), + Some(o) => Ok(PyIterANextOutput::Yield(o.into_object(py))), None => Ok(PyIterANextOutput::Return(py.None())), } } diff --git a/src/types/any.rs b/src/types/any.rs index 9ba9b515944..4d83d9286b7 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -1,12 +1,12 @@ use crate::class::basic::CompareOp; use crate::conversion::{ - AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, ToBorrowedObject, ToPyObject, + AsPyPointer, FromPyObject, IntoPyPointer, PyTryFrom, ToBorrowedObject, ToPyObject, }; use crate::err::{PyDowncastError, PyErr, PyResult}; use crate::exceptions::PyTypeError; use crate::type_object::PyTypeObject; use crate::types::{PyDict, PyIterator, PyList, PyString, PyTuple, PyType}; -use crate::{err, ffi, Py, PyNativeType, PyObject, Python}; +use crate::{err, ffi, IntoPyObject, PyNativeType, PyObject, Python}; use std::cell::UnsafeCell; use std::cmp::Ordering; use std::os::raw::c_int; @@ -402,7 +402,7 @@ impl PyAny { /// This is equivalent to the Python expression `self(*args, **kwargs)`. pub fn call( &self, - args: impl IntoPy>, + args: impl IntoPyObject, kwargs: Option<&PyDict>, ) -> PyResult<&PyAny> { let args = args.into_py(self.py()).into_ptr(); @@ -480,7 +480,7 @@ impl PyAny { /// value = add(1,2) /// assert value == 3 /// ``` - pub fn call1(&self, args: impl IntoPy>) -> PyResult<&PyAny> { + pub fn call1(&self, args: impl IntoPyObject) -> PyResult<&PyAny> { self.call(args, None) } @@ -516,7 +516,7 @@ impl PyAny { pub fn call_method( &self, name: &str, - args: impl IntoPy>, + args: impl IntoPyObject, kwargs: Option<&PyDict>, ) -> PyResult<&PyAny> { name.with_borrowed_ptr(self.py(), |name| unsafe { @@ -607,7 +607,11 @@ impl PyAny { /// list_.insert(1,2) /// assert list_ == [1,2,3,4] /// ``` - pub fn call_method1(&self, name: &str, args: impl IntoPy>) -> PyResult<&PyAny> { + pub fn call_method1( + &self, + name: &str, + args: impl IntoPyObject, + ) -> PyResult<&PyAny> { self.call_method(name, args, None) } diff --git a/src/types/boolobject.rs b/src/types/boolobject.rs index b7126bc044c..9643b7f8feb 100644 --- a/src/types/boolobject.rs +++ b/src/types/boolobject.rs @@ -1,7 +1,7 @@ // Copyright (c) 2017-present PyO3 Project and Contributors use crate::{ - ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyObject, PyResult, PyTryFrom, Python, - ToPyObject, + ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyObject, Py, PyAny, PyObject, PyResult, PyTryFrom, + Python, ToPyObject, }; /// Represents a Python `bool`. @@ -48,6 +48,14 @@ impl IntoPy for bool { } } +impl IntoPyObject for bool { + type Target = PyBool; + #[inline] + fn into_py(self, py: Python<'_>) -> Py { + PyBool::new(py, self).into() + } +} + /// Converts a Python `bool` to a Rust `bool`. /// /// Fails with `TypeError` if the input is not a Python `bool`. diff --git a/src/types/bytes.rs b/src/types/bytes.rs index 44bb2fd90b3..7effaa33789 100644 --- a/src/types/bytes.rs +++ b/src/types/bytes.rs @@ -1,5 +1,5 @@ use crate::{ - ffi, AsPyPointer, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, PyTryFrom, Python, + ffi, AsPyPointer, FromPyObject, IntoPyObject, Py, PyAny, PyObject, PyResult, PyTryFrom, Python, ToPyObject, }; use std::ops::Index; @@ -124,12 +124,19 @@ impl> Index for PyBytes { } } -impl<'a> IntoPy for &'a [u8] { +impl<'a> crate::IntoPy for &'a [u8] { fn into_py(self, py: Python<'_>) -> PyObject { PyBytes::new(py, self).to_object(py) } } +impl<'a> IntoPyObject for &'a [u8] { + type Target = PyBytes; + fn into_py(self, py: Python<'_>) -> Py { + PyBytes::new(py, self).into() + } +} + impl<'a> FromPyObject<'a> for &'a [u8] { fn extract(obj: &'a PyAny) -> PyResult { Ok(::try_from(obj)?.as_bytes()) diff --git a/src/types/dict.rs b/src/types/dict.rs index 794fbea7151..60f0a38afdb 100644 --- a/src/types/dict.rs +++ b/src/types/dict.rs @@ -5,8 +5,8 @@ use crate::types::{PyAny, PyList}; #[cfg(not(PyPy))] use crate::IntoPyPointer; use crate::{ - ffi, AsPyPointer, FromPyObject, IntoPy, PyObject, PyTryFrom, Python, ToBorrowedObject, - ToPyObject, + ffi, AsPyPointer, FromPyObject, IntoPyObject, Py, PyObject, PyTryFrom, Python, + ToBorrowedObject, ToPyObject, }; use std::collections::{BTreeMap, HashMap}; use std::ptr::NonNull; @@ -251,10 +251,10 @@ where } } -impl IntoPy for collections::HashMap +impl crate::IntoPy for collections::HashMap where - K: hash::Hash + cmp::Eq + IntoPy, - V: IntoPy, + K: hash::Hash + cmp::Eq + crate::IntoPy, + V: crate::IntoPy, H: hash::BuildHasher, { fn into_py(self, py: Python<'_>) -> PyObject { @@ -265,10 +265,25 @@ where } } -impl IntoPy for collections::BTreeMap +impl IntoPyObject for collections::HashMap where - K: cmp::Eq + IntoPy, - V: IntoPy, + K: hash::Hash + cmp::Eq + IntoPyObject, + V: IntoPyObject, + H: hash::BuildHasher, +{ + type Target = PyDict; + fn into_py(self, py: Python<'_>) -> Py { + let iter = self + .into_iter() + .map(|(k, v)| (k.into_py(py), v.into_py(py))); + IntoPyDict::into_py_dict(iter, py).into() + } +} + +impl crate::IntoPy for collections::BTreeMap +where + K: cmp::Eq + crate::IntoPy, + V: crate::IntoPy, { fn into_py(self, py: Python<'_>) -> PyObject { let iter = self @@ -278,6 +293,20 @@ where } } +impl IntoPyObject for collections::BTreeMap +where + K: cmp::Eq + IntoPyObject, + V: IntoPyObject, +{ + type Target = PyDict; + fn into_py(self, py: Python<'_>) -> Py { + let iter = self + .into_iter() + .map(|(k, v)| (k.into_py(py), v.into_py(py))); + IntoPyDict::into_py_dict(iter, py).into() + } +} + /// Conversion trait that allows a sequence of tuples to be converted into `PyDict` /// Primary use case for this trait is `call` and `call_method` methods as keywords argument. pub trait IntoPyDict { diff --git a/src/types/floatob.rs b/src/types/floatob.rs index 3079c61bbb8..359be61f26a 100644 --- a/src/types/floatob.rs +++ b/src/types/floatob.rs @@ -2,7 +2,8 @@ // // based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython use crate::{ - ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject, + ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyObject, Py, PyAny, PyErr, PyObject, PyResult, + Python, ToPyObject, }; use std::os::raw::c_double; @@ -46,6 +47,13 @@ impl IntoPy for f64 { } } +impl IntoPyObject for f64 { + type Target = PyFloat; + fn into_py(self, py: Python<'_>) -> Py { + PyFloat::new(py, self).into() + } +} + impl<'source> FromPyObject<'source> for f64 { // PyFloat_AsDouble returns -1.0 upon failure #![allow(clippy::float_cmp)] @@ -74,6 +82,13 @@ impl IntoPy for f32 { } } +impl IntoPyObject for f32 { + type Target = PyFloat; + fn into_py(self, py: Python<'_>) -> Py { + PyFloat::new(py, f64::from(self)).into() + } +} + impl<'source> FromPyObject<'source> for f32 { fn extract(obj: &'source PyAny) -> PyResult { Ok(obj.extract::()? as f32) diff --git a/src/types/list.rs b/src/types/list.rs index ab69859e9fd..f6c99d8d8cc 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -9,8 +9,8 @@ use crate::ffi::{self, Py_ssize_t}; use crate::internal_tricks::get_ssize_index; use crate::types::PySequence; use crate::{ - AsPyPointer, IntoPy, IntoPyPointer, Py, PyAny, PyObject, PyTryFrom, Python, ToBorrowedObject, - ToPyObject, + AsPyPointer, IntoPy, IntoPyObject, IntoPyPointer, Py, PyAny, PyObject, PyTryFrom, Python, + ToBorrowedObject, ToPyObject, }; /// Represents a Python `list`. @@ -361,6 +361,18 @@ where } } +impl IntoPyObject for Vec +where + T: IntoPyObject, +{ + type Target = PyList; + fn into_py(self, py: Python<'_>) -> Py { + let mut iter = self.into_iter().map(|e| e.into_object(py)); + let list = new_from_iter(py, &mut iter); + list.into() + } +} + #[cfg(test)] mod tests { use crate::types::PyList; diff --git a/src/types/mod.rs b/src/types/mod.rs index ad79d062b9a..0c470e42d42 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -65,6 +65,15 @@ macro_rules! pyobject_native_type_base( unsafe { $crate::PyObject::from_borrowed_ptr(py, self.as_ptr()) } } } + + impl<$($generics,)*> $crate::IntoPyObject for &'_ $name { + type Target = $name; + #[inline] + fn into_py(self, py: $crate::Python<'_>) -> $crate::Py<$name> { + use $crate::AsPyPointer; + unsafe { $crate::Py::from_borrowed_ptr(py, self.as_ptr()) } + } + } }; ); diff --git a/src/types/module.rs b/src/types/module.rs index 2838734b473..c7c6344790b 100644 --- a/src/types/module.rs +++ b/src/types/module.rs @@ -4,12 +4,12 @@ use crate::callback::IntoPyCallbackOutput; use crate::err::{PyErr, PyResult}; -use crate::exceptions; use crate::ffi; use crate::pyclass::PyClass; use crate::type_object::PyTypeObject; use crate::types::{PyAny, PyCFunction, PyDict, PyList, PyString}; -use crate::{AsPyPointer, IntoPy, PyObject, Python}; +use crate::{exceptions, IntoPyObject}; +use crate::{AsPyPointer, PyObject, Python}; use std::ffi::{CStr, CString}; use std::str; @@ -67,7 +67,7 @@ impl PyModule { /// import antigravity /// ``` pub fn import<'p>(py: Python<'p>, name: &str) -> PyResult<&'p PyModule> { - let name: PyObject = name.into_py(py); + let name = name.into_py(py); unsafe { py.from_owned_ptr_or_err(ffi::PyImport_Import(name.as_ptr())) } } @@ -241,12 +241,12 @@ impl PyModule { /// ``` pub fn add(&self, name: &str, value: V) -> PyResult<()> where - V: IntoPy, + V: IntoPyObject, { self.index()? .append(name) .expect("could not append __name__ to __all__"); - self.setattr(name, value.into_py(self.py())) + self.setattr(name, value.into_object(self.py())) } /// Adds a new class to the module. diff --git a/src/types/num.rs b/src/types/num.rs index 75af9a19193..1fa32b4f6a8 100644 --- a/src/types/num.rs +++ b/src/types/num.rs @@ -3,8 +3,8 @@ // based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython use crate::{ - exceptions, ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, - ToPyObject, + exceptions, ffi, AsPyPointer, FromPyObject, IntoPyObject, Py, PyAny, PyErr, PyObject, PyResult, + Python, ToPyObject, }; use std::convert::TryFrom; use std::i64; @@ -15,11 +15,17 @@ macro_rules! int_fits_larger_int { impl ToPyObject for $rust_type { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - (*self as $larger_type).into_py(py) + (*self as $larger_type).into_object(py) } } - impl IntoPy for $rust_type { + impl $crate::IntoPy for $rust_type { fn into_py(self, py: Python<'_>) -> PyObject { + (self as $larger_type).into_object(py) + } + } + impl IntoPyObject for $rust_type { + type Target = PyLong; + fn into_py(self, py: Python<'_>) -> Py { (self as $larger_type).into_py(py) } } @@ -52,11 +58,17 @@ macro_rules! int_fits_c_long { unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(*self as c_long)) } } } - impl IntoPy for $rust_type { + impl $crate::IntoPy for $rust_type { fn into_py(self, py: Python<'_>) -> PyObject { unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(self as c_long)) } } } + impl IntoPyObject for $rust_type { + type Target = PyLong; + fn into_py(self, py: Python<'_>) -> Py { + unsafe { Py::from_owned_ptr(py, ffi::PyLong_FromLong(self as c_long)) } + } + } impl<'source> FromPyObject<'source> for $rust_type { fn extract(obj: &'source PyAny) -> PyResult { @@ -86,12 +98,18 @@ macro_rules! int_convert_u64_or_i64 { unsafe { PyObject::from_owned_ptr(py, $pylong_from_ll_or_ull(*self)) } } } - impl IntoPy for $rust_type { + impl $crate::IntoPy for $rust_type { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { unsafe { PyObject::from_owned_ptr(py, $pylong_from_ll_or_ull(self)) } } } + impl IntoPyObject for $rust_type { + type Target = PyLong; + fn into_py(self, py: Python<'_>) -> Py { + unsafe { Py::from_owned_ptr(py, ffi::PyLong_FromLong(self as c_long)) } + } + } impl<'source> FromPyObject<'source> for $rust_type { fn extract(ob: &'source PyAny) -> PyResult<$rust_type> { let ptr = ob.as_ptr(); @@ -153,10 +171,10 @@ mod fast_128bit_int_conversion { impl ToPyObject for $rust_type { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - (*self).into_py(py) + (*self).into_object(py) } } - impl IntoPy for $rust_type { + impl $crate::IntoPy for $rust_type { fn into_py(self, py: Python<'_>) -> PyObject { unsafe { // Always use little endian @@ -171,6 +189,22 @@ mod fast_128bit_int_conversion { } } } + impl IntoPyObject for $rust_type { + type Target = PyLong; + fn into_py(self, py: Python<'_>) -> Py { + unsafe { + // Always use little endian + let bytes = self.to_le_bytes(); + let obj = ffi::_PyLong_FromByteArray( + bytes.as_ptr() as *const std::os::raw::c_uchar, + bytes.len(), + 1, + $is_signed, + ); + Py::from_owned_ptr(py, obj) + } + } + } impl<'source> FromPyObject<'source> for $rust_type { fn extract(ob: &'source PyAny) -> PyResult<$rust_type> { @@ -216,7 +250,7 @@ mod slow_128bit_int_conversion { } } - impl IntoPy for $rust_type { + impl $crate::IntoPy for $rust_type { fn into_py(self, py: Python<'_>) -> PyObject { let lower = self as u64; let upper = (self >> SHIFT) as $half_type; @@ -235,6 +269,26 @@ mod slow_128bit_int_conversion { } } } + impl IntoPyObject for $rust_type { + type Target = PyLong; + fn into_py(self, py: Python<'_>) -> Py { + let lower = self as u64; + let upper = (self >> SHIFT) as $half_type; + unsafe { + let shifted = PyObject::from_owned_ptr( + py, + ffi::PyNumber_Lshift( + upper.into_py(py).as_ptr(), + SHIFT.into_py(py).as_ptr(), + ), + ); + Py::from_owned_ptr( + py, + ffi::PyNumber_Or(shifted.as_ptr(), lower.into_py(py).as_ptr()), + ) + } + } + } impl<'source> FromPyObject<'source> for $rust_type { fn extract(ob: &'source PyAny) -> PyResult<$rust_type> { diff --git a/src/types/set.rs b/src/types/set.rs index 63dc7c7e24e..ead91493df2 100644 --- a/src/types/set.rs +++ b/src/types/set.rs @@ -5,7 +5,8 @@ use crate::err::{self, PyErr, PyResult}; #[cfg(Py_LIMITED_API)] use crate::types::PyIterator; use crate::{ - ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyObject, Python, ToBorrowedObject, ToPyObject, + ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyObject, Py, PyAny, PyObject, Python, + ToBorrowedObject, ToPyObject, }; use std::cmp; use std::collections::{BTreeSet, HashSet}; @@ -237,6 +238,23 @@ where } } +impl IntoPyObject for HashSet +where + K: IntoPyObject + Eq + hash::Hash, + S: hash::BuildHasher + Default, +{ + type Target = PySet; + fn into_py(self, py: Python<'_>) -> Py { + let set = PySet::empty(py).expect("Failed to construct empty set"); + { + for val in self { + set.add(val.into_py(py)).expect("Failed to add to set"); + } + } + set.into() + } +} + impl<'source, K, S> FromPyObject<'source> for HashSet where K: FromPyObject<'source> + cmp::Eq + hash::Hash, @@ -263,6 +281,22 @@ where } } +impl IntoPyObject for BTreeSet +where + K: IntoPyObject + cmp::Ord, +{ + type Target = PySet; + fn into_py(self, py: Python<'_>) -> Py { + let set = PySet::empty(py).expect("Failed to construct empty set"); + { + for val in self { + set.add(val.into_py(py)).expect("Failed to add to set"); + } + } + set.into() + } +} + impl<'source, K> FromPyObject<'source> for BTreeSet where K: FromPyObject<'source> + cmp::Ord, diff --git a/src/types/string.rs b/src/types/string.rs index 4e906f8ce51..6cd5a628616 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -4,8 +4,8 @@ use crate::exceptions::PyUnicodeDecodeError; use crate::types::PyBytes; use crate::{ - ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyObject, PyResult, PyTryFrom, Python, - ToPyObject, + ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyObject, Py, PyAny, PyObject, PyResult, PyTryFrom, + Python, ToPyObject, }; use std::borrow::Cow; use std::os::raw::c_char; @@ -298,6 +298,14 @@ impl<'a> IntoPy for &'a str { } } +impl IntoPyObject for &'_ str { + type Target = PyString; + #[inline] + fn into_py(self, py: Python<'_>) -> Py { + PyString::new(py, self).into() + } +} + /// Converts a Rust `Cow<'_, str>` to a Python object. /// See `PyString::new` for details on the conversion. impl<'a> ToPyObject for Cow<'a, str> { @@ -314,6 +322,14 @@ impl IntoPy for Cow<'_, str> { } } +impl IntoPyObject for Cow<'_, str> { + type Target = PyString; + #[inline] + fn into_py(self, py: Python<'_>) -> Py { + PyString::new(py, &self).into() + } +} + /// Converts a Rust `String` to a Python object. /// See `PyString::new` for details on the conversion. impl ToPyObject for String { @@ -325,7 +341,7 @@ impl ToPyObject for String { impl ToPyObject for char { fn to_object(&self, py: Python<'_>) -> PyObject { - self.into_py(py) + self.into_object(py) } } @@ -336,12 +352,27 @@ impl IntoPy for char { } } +impl IntoPyObject for char { + type Target = PyString; + fn into_py(self, py: Python<'_>) -> Py { + let mut bytes = [0u8; 4]; + PyString::new(py, self.encode_utf8(&mut bytes)).into() + } +} + impl IntoPy for String { fn into_py(self, py: Python<'_>) -> PyObject { PyString::new(py, &self).into() } } +impl IntoPyObject for String { + type Target = PyString; + fn into_py(self, py: Python<'_>) -> Py { + PyString::new(py, &self).into() + } +} + impl<'a> IntoPy for &'a String { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { @@ -349,6 +380,14 @@ impl<'a> IntoPy for &'a String { } } +impl IntoPyObject for &'_ String { + type Target = PyString; + #[inline] + fn into_py(self, py: Python<'_>) -> Py { + PyString::new(py, self).into() + } +} + /// Allows extracting strings from Python objects. /// Accepts Python `str` and `unicode` objects. impl<'source> FromPyObject<'source> for &'source str { diff --git a/src/types/tuple.rs b/src/types/tuple.rs index 86454542063..9af2ed80bc4 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -6,8 +6,8 @@ use crate::ffi::{self, Py_ssize_t}; use crate::internal_tricks::get_ssize_index; use crate::types::PySequence; use crate::{ - exceptions, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, Py, PyAny, PyErr, PyObject, - PyResult, PyTryFrom, Python, ToBorrowedObject, ToPyObject, + exceptions, AsPyPointer, FromPyObject, IntoPy, IntoPyObject, IntoPyPointer, Py, PyAny, PyErr, + PyObject, PyResult, PyTryFrom, Python, ToBorrowedObject, ToPyObject, }; #[inline] @@ -333,6 +333,18 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+ } } + impl <$($T: IntoPy),+> IntoPyObject for ($($T,)+) { + type Target = PyTuple; + fn into_py(self, py: Python<'_>) -> Py { + unsafe { + let ptr = ffi::PyTuple_New($length); + let ret = Py::from_owned_ptr(py, ptr); + $(ffi::PyTuple_SetItem(ptr, $n, self.$n.into_py(py).into_ptr());)+ + ret + } + } + } + impl<'s, $($T: FromPyObject<'s>),+> FromPyObject<'s> for ($($T,)+) { fn extract(obj: &'s PyAny) -> PyResult { diff --git a/tests/test_arithmetics.rs b/tests/test_arithmetics.rs index 0c4cd7667fc..ce51e9dade1 100644 --- a/tests/test_arithmetics.rs +++ b/tests/test_arithmetics.rs @@ -471,8 +471,8 @@ impl RichComparisons2 { fn __richcmp__(&self, other: &PyAny, op: CompareOp) -> PyObject { match op { - CompareOp::Eq => true.into_py(other.py()), - CompareOp::Ne => false.into_py(other.py()), + CompareOp::Eq => true.into_object(other.py()), + CompareOp::Ne => false.into_object(other.py()), _ => other.py().NotImplemented(), } } diff --git a/tests/test_buffer_protocol.rs b/tests/test_buffer_protocol.rs index a92529015ac..efc3b44f3ba 100644 --- a/tests/test_buffer_protocol.rs +++ b/tests/test_buffer_protocol.rs @@ -112,7 +112,7 @@ fn test_buffer_referenced() { let input = vec![b' ', b'2', b'3']; let gil = Python::acquire_gil(); let py = gil.python(); - let instance: PyObject = TestBufferClass { + let instance = TestBufferClass { vec: input.clone(), drop_called: drop_called.clone(), } diff --git a/tests/test_class_conversion.rs b/tests/test_class_conversion.rs index a564750a8e3..bd1087150e1 100644 --- a/tests/test_class_conversion.rs +++ b/tests/test_class_conversion.rs @@ -122,9 +122,9 @@ fn test_polymorphic_container_does_not_accept_other_types() { let setattr = |value: PyObject| p.as_ref(py).setattr("inner", value); - assert!(setattr(1i32.into_py(py)).is_err()); + assert!(setattr(1i32.into_object(py)).is_err()); assert!(setattr(py.None()).is_err()); - assert!(setattr((1i32, 2i32).into_py(py)).is_err()); + assert!(setattr((1i32, 2i32).into_object(py)).is_err()); } #[test] diff --git a/tests/test_frompyobject.rs b/tests/test_frompyobject.rs index 7215ba82e24..2be39a243e6 100644 --- a/tests/test_frompyobject.rs +++ b/tests/test_frompyobject.rs @@ -77,7 +77,7 @@ fn test_transparent_named_field_struct() { let test = "test".into_py(py); let b: B = FromPyObject::extract(test.as_ref(py)).expect("Failed to extract B from String"); assert_eq!(b.test, "test"); - let test: PyObject = 1.into_py(py); + let test = 1i32.into_py(py); let b = B::extract(test.as_ref(py)); assert!(b.is_err()); }); @@ -161,10 +161,10 @@ pub struct Tuple(String, usize); #[test] fn test_tuple_struct() { Python::with_gil(|py| { - let tup = PyTuple::new(py, &[1.into_py(py), "test".into_py(py)]); + let tup = PyTuple::new(py, &[1i32.into_object(py), "test".into_object(py)]); let tup = Tuple::extract(tup.as_ref()); assert!(tup.is_err()); - let tup = PyTuple::new(py, &["test".into_py(py), 1.into_py(py)]); + let tup = PyTuple::new(py, &["test".into_object(py), 1i32.into_object(py)]); let tup = Tuple::extract(tup.as_ref()).expect("Failed to extract Tuple from PyTuple"); assert_eq!(tup.0, "test"); assert_eq!(tup.1, 1); @@ -177,7 +177,7 @@ pub struct TransparentTuple(String); #[test] fn test_transparent_tuple_struct() { Python::with_gil(|py| { - let tup: PyObject = 1.into_py(py); + let tup = 1i32.into_py(py); let tup = TransparentTuple::extract(tup.as_ref(py)); assert!(tup.is_err()); let test = "test".into_py(py); @@ -249,7 +249,7 @@ fn test_struct_nested_type_errors_with_generics() { #[test] fn test_transparent_struct_error_message() { Python::with_gil(|py| { - let tup: PyObject = 1.into_py(py); + let tup = 1i32.into_py(py); let tup = B::extract(tup.as_ref(py)); assert!(tup.is_err()); assert_eq!( @@ -263,7 +263,7 @@ fn test_transparent_struct_error_message() { #[test] fn test_tuple_struct_error_message() { Python::with_gil(|py| { - let tup: PyObject = (1, "test").into_py(py); + let tup = (1, "test").into_py(py); let tup = Tuple::extract(tup.as_ref(py)); assert!(tup.is_err()); assert_eq!( @@ -277,7 +277,7 @@ fn test_tuple_struct_error_message() { #[test] fn test_transparent_tuple_error_message() { Python::with_gil(|py| { - let tup: PyObject = 1.into_py(py); + let tup = 1i32.into_py(py); let tup = TransparentTuple::extract(tup.as_ref(py)); assert!(tup.is_err()); assert_eq!( @@ -323,7 +323,7 @@ pub struct PyBool { #[test] fn test_enum() { Python::with_gil(|py| { - let tup = PyTuple::new(py, &[1.into_py(py), "test".into_py(py)]); + let tup = PyTuple::new(py, &[1i32.into_object(py), "test".into_object(py)]); let f = Foo::extract(tup.as_ref()).expect("Failed to extract Foo from tuple"); match f { Foo::TupleVar(test, test2) => { @@ -344,7 +344,7 @@ fn test_enum() { _ => panic!("Expected extracting Foo::StructVar, got {:?}", f), } - let int: PyObject = 1.into_py(py); + let int = 1i32.into_py(py); let f = Foo::extract(int.as_ref(py)).expect("Failed to extract Foo from int"); match f { Foo::TransparentTuple(test) => assert_eq!(test, 1), diff --git a/tests/test_mapping.rs b/tests/test_mapping.rs index d9efb77288c..3dbee84a0dc 100644 --- a/tests/test_mapping.rs +++ b/tests/test_mapping.rs @@ -61,7 +61,7 @@ impl Mapping { fn get(&self, py: Python<'_>, key: &str, default: Option) -> Option { self.index .get(key) - .map(|value| value.into_py(py)) + .map(|value| value.into_object(py)) .or(default) } } diff --git a/tests/test_pyfunction.rs b/tests/test_pyfunction.rs index ab80d4a0fa5..406565f9fe2 100644 --- a/tests/test_pyfunction.rs +++ b/tests/test_pyfunction.rs @@ -285,12 +285,12 @@ fn test_closure() { .iter() .map(|elem| { if let Ok(i) = elem.extract::() { - (i + 1).into_py(py) + (i + 1).into_object(py) } else if let Ok(f) = elem.extract::() { - (2. * f).into_py(py) + (2. * f).into_object(py) } else if let Ok(mut s) = elem.extract::() { s.push_str("-py"); - s.into_py(py) + s.into_object(py) } else { panic!("unexpected argument type for {:?}", elem) } diff --git a/tests/test_pyself.rs b/tests/test_pyself.rs index 5195f5e16d5..0f663ecf6b8 100644 --- a/tests/test_pyself.rs +++ b/tests/test_pyself.rs @@ -91,7 +91,7 @@ fn reader() -> Reader { fn test_nested_iter() { let gil = Python::acquire_gil(); let py = gil.python(); - let reader: PyObject = reader().into_py(py); + let reader = reader().into_py(py); py_assert!( py, reader, @@ -103,7 +103,7 @@ fn test_nested_iter() { fn test_clone_ref() { let gil = Python::acquire_gil(); let py = gil.python(); - let reader: PyObject = reader().into_py(py); + let reader = reader().into_py(py); py_assert!(py, reader, "reader == reader.clone_ref()"); py_assert!(py, reader, "reader == reader.clone_ref_with_py()"); } diff --git a/tests/test_sequence.rs b/tests/test_sequence.rs index c87247a2a11..888d6e12c4f 100644 --- a/tests/test_sequence.rs +++ b/tests/test_sequence.rs @@ -263,7 +263,7 @@ fn test_generic_list_get() { let gil = Python::acquire_gil(); let py = gil.python(); - let list: PyObject = GenericList { + let list = GenericList { items: [1, 2, 3].iter().map(|i| i.to_object(py)).collect(), } .into_py(py);