From be06c9a293b6ffdd627fbc225ed91767126733f2 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:42:45 +0200 Subject: [PATCH 01/21] add `IntoPyObject` for `bool` --- src/types/boolobject.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/types/boolobject.rs b/src/types/boolobject.rs index e678ca2c601..e5dc050a679 100644 --- a/src/types/boolobject.rs +++ b/src/types/boolobject.rs @@ -7,6 +7,8 @@ use crate::{ }; use super::any::PyAnyMethods; +use crate::conversion::IntoPyObject; +use std::convert::Infallible; /// Represents a Python `bool`. /// @@ -170,6 +172,28 @@ impl IntoPy for bool { } } +impl<'py> IntoPyObject<'py> for bool { + type Target = PyBool; + type Output = Borrowed<'py, 'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + Ok(PyBool::new(py, self)) + } +} + +impl<'py> IntoPyObject<'py> for &bool { + type Target = PyBool; + type Output = Borrowed<'py, 'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + /// Converts a Python `bool` to a Rust `bool`. /// /// Fails with `TypeError` if the input is not a Python `bool`. From a4e5f5f063d779bb9c170d80d503fd1cbc7a8ef6 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:43:23 +0200 Subject: [PATCH 02/21] add `IntoPyObject` for `PyErr` --- src/err/mod.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/err/mod.rs b/src/err/mod.rs index bb5c80f2ca0..fbf346b4caf 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -15,8 +15,10 @@ use std::ffi::CString; mod err_state; mod impls; +use crate::conversion::IntoPyObject; pub use err_state::PyErrArguments; use err_state::{PyErrState, PyErrStateLazyFnOutput, PyErrStateNormalized}; +use std::convert::Infallible; /// Represents a Python exception. /// @@ -800,6 +802,28 @@ impl<'a> IntoPy for &'a PyErr { } } +impl<'py> IntoPyObject<'py> for PyErr { + type Target = PyBaseException; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + Ok(self.into_value(py).into_bound(py)) + } +} + +impl<'py> IntoPyObject<'py> for &PyErr { + type Target = PyBaseException; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + self.clone_ref(py).into_pyobject(py) + } +} + struct PyDowncastErrorArguments { from: Py, to: Cow<'static, str>, From e51ab11b922a7c269610bd039d80fba7540761b6 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:43:43 +0200 Subject: [PATCH 03/21] add `IntoPyObject` for `&Duration` and `&SystemTime` --- src/conversions/std/time.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/conversions/std/time.rs b/src/conversions/std/time.rs index 4c5c56e4cac..e16d17cff23 100755 --- a/src/conversions/std/time.rs +++ b/src/conversions/std/time.rs @@ -123,6 +123,20 @@ impl<'py> IntoPyObject<'py> for Duration { } } +impl<'py> IntoPyObject<'py> for &Duration { + #[cfg(not(Py_LIMITED_API))] + type Target = PyDelta; + #[cfg(Py_LIMITED_API)] + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + // Conversions between SystemTime and datetime do not rely on the floating point timestamp of the // timestamp/fromtimestamp APIs to avoid possible precision loss but goes through the // timedelta/std::time::Duration types by taking for reference point the UNIX epoch. @@ -171,6 +185,17 @@ impl<'py> IntoPyObject<'py> for SystemTime { } } +impl<'py> IntoPyObject<'py> for &SystemTime { + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + fn unix_epoch_py(py: Python<'_>) -> &PyObject { static UNIX_EPOCH: GILOnceCell = GILOnceCell::new(); UNIX_EPOCH From 42c748e9ef711d73494f4f97eb28cd265e8bd4cc Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:44:27 +0200 Subject: [PATCH 04/21] add `IntoPyObject` for string references --- src/conversions/std/string.rs | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/conversions/std/string.rs b/src/conversions/std/string.rs index 92d0f13babe..a6ea913a4ff 100644 --- a/src/conversions/std/string.rs +++ b/src/conversions/std/string.rs @@ -47,11 +47,23 @@ impl<'py> IntoPyObject<'py> for &str { type Output = Bound<'py, Self::Target>; type Error = Infallible; + #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { Ok(PyString::new(py, self)) } } +impl<'py> IntoPyObject<'py> for &&str { + type Target = PyString; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + /// Converts a Rust `Cow<'_, str>` to a Python object. /// See `PyString::new` for details on the conversion. impl ToPyObject for Cow<'_, str> { @@ -78,11 +90,23 @@ impl<'py> IntoPyObject<'py> for Cow<'_, str> { type Output = Bound<'py, Self::Target>; type Error = Infallible; + #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { (*self).into_pyobject(py) } } +impl<'py> IntoPyObject<'py> for &Cow<'_, str> { + type Target = PyString; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (&**self).into_pyobject(py) + } +} + /// Converts a Rust `String` to a Python object. /// See `PyString::new` for details on the conversion. impl ToPyObject for String { @@ -121,6 +145,17 @@ impl<'py> IntoPyObject<'py> for char { } } +impl<'py> IntoPyObject<'py> for &char { + type Target = PyString; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + impl IntoPy for String { fn into_py(self, py: Python<'_>) -> PyObject { PyString::new(py, &self).into() @@ -159,6 +194,7 @@ impl<'py> IntoPyObject<'py> for &String { type Output = Bound<'py, Self::Target>; type Error = Infallible; + #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { Ok(PyString::new(py, self)) } From efa2b8490e9ce6e739ca8d2ed729b5c35fbb7b6d Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:44:55 +0200 Subject: [PATCH 05/21] add `IntoPyObject` for set references --- src/conversions/hashbrown.rs | 25 ++++++++++++++++++- src/conversions/std/set.rs | 47 +++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/conversions/hashbrown.rs b/src/conversions/hashbrown.rs index d09205fce40..8f10d0b7652 100644 --- a/src/conversions/hashbrown.rs +++ b/src/conversions/hashbrown.rs @@ -118,7 +118,7 @@ where impl<'py, K, H> IntoPyObject<'py> for hashbrown::HashSet where - K: hash::Hash + cmp::Eq + IntoPyObject<'py>, + K: IntoPyObject<'py> + cmp::Eq + hash::Hash, H: hash::BuildHasher, PyErr: From, { @@ -139,6 +139,29 @@ where } } +impl<'a, 'py, K, H> IntoPyObject<'py> for &'a hashbrown::HashSet +where + &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash, + &'a H: hash::BuildHasher, + PyErr: From<<&'a K as IntoPyObject<'py>>::Error>, +{ + type Target = PySet; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject(self, py: Python<'py>) -> Result { + try_new_from_iter( + py, + self.into_iter().map(|item| { + item.into_pyobject(py) + .map(BoundObject::into_any) + .map(BoundObject::unbind) + .map_err(Into::into) + }), + ) + } +} + impl<'py, K, S> FromPyObject<'py> for hashbrown::HashSet where K: FromPyObject<'py> + cmp::Eq + hash::Hash, diff --git a/src/conversions/std/set.rs b/src/conversions/std/set.rs index 165194bbafc..b15013b81ba 100644 --- a/src/conversions/std/set.rs +++ b/src/conversions/std/set.rs @@ -77,6 +77,29 @@ where } } +impl<'a, 'py, K, H> IntoPyObject<'py> for &'a collections::HashSet +where + &'a K: IntoPyObject<'py> + Eq + hash::Hash, + &'a H: hash::BuildHasher, + PyErr: From<<&'a K as IntoPyObject<'py>>::Error>, +{ + type Target = PySet; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject(self, py: Python<'py>) -> Result { + try_new_from_iter( + py, + self.iter().map(|item| { + item.into_pyobject(py) + .map(BoundObject::into_any) + .map(BoundObject::unbind) + .map_err(Into::into) + }), + ) + } +} + impl<'py, K, S> FromPyObject<'py> for collections::HashSet where K: FromPyObject<'py> + cmp::Eq + hash::Hash, @@ -119,7 +142,7 @@ where impl<'py, K> IntoPyObject<'py> for collections::BTreeSet where - K: IntoPyObject<'py> + Eq + hash::Hash, + K: IntoPyObject<'py> + cmp::Ord, PyErr: From, { type Target = PySet; @@ -139,6 +162,28 @@ where } } +impl<'a, 'py, K> IntoPyObject<'py> for &'a collections::BTreeSet +where + &'a K: IntoPyObject<'py> + cmp::Ord, + PyErr: From<<&'a K as IntoPyObject<'py>>::Error>, +{ + type Target = PySet; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject(self, py: Python<'py>) -> Result { + try_new_from_iter( + py, + self.iter().map(|item| { + item.into_pyobject(py) + .map(BoundObject::into_any) + .map(BoundObject::unbind) + .map_err(Into::into) + }), + ) + } +} + impl<'py, K> FromPyObject<'py> for collections::BTreeSet where K: FromPyObject<'py> + cmp::Ord, From 486cac480c56a1ef9ab5ea1e9bb054d9398cc476 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:45:22 +0200 Subject: [PATCH 06/21] add `IntoPyObject` for `&Option` --- src/conversions/std/option.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/conversions/std/option.rs b/src/conversions/std/option.rs index 2ef00acb550..38eaebd499b 100644 --- a/src/conversions/std/option.rs +++ b/src/conversions/std/option.rs @@ -44,6 +44,20 @@ where } } +impl<'a, 'py, T> IntoPyObject<'py> for &'a Option +where + &'a T: IntoPyObject<'py>, +{ + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = <&'a T as IntoPyObject<'py>>::Error; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + self.as_ref().into_pyobject(py) + } +} + impl<'py, T> FromPyObject<'py> for Option where T: FromPyObject<'py>, From 7e3324660079e84266d8969a53e3b86445767b9f Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:45:37 +0200 Subject: [PATCH 07/21] add `IntoPyObject` for map references --- src/conversions/hashbrown.rs | 25 +++++++++++++++++- src/conversions/indexmap.rs | 25 +++++++++++++++++- src/conversions/std/map.rs | 49 ++++++++++++++++++++++++++++++++++-- 3 files changed, 95 insertions(+), 4 deletions(-) diff --git a/src/conversions/hashbrown.rs b/src/conversions/hashbrown.rs index 8f10d0b7652..7104fe35679 100644 --- a/src/conversions/hashbrown.rs +++ b/src/conversions/hashbrown.rs @@ -56,7 +56,7 @@ where impl<'py, K, V, H> IntoPyObject<'py> for hashbrown::HashMap where - K: hash::Hash + cmp::Eq + IntoPyObject<'py>, + K: IntoPyObject<'py> + cmp::Eq + hash::Hash, V: IntoPyObject<'py>, H: hash::BuildHasher, PyErr: From + From, @@ -77,6 +77,29 @@ where } } +impl<'a, 'py, K, V, H> IntoPyObject<'py> for &'a hashbrown::HashMap +where + &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash, + &'a V: IntoPyObject<'py>, + H: hash::BuildHasher, + PyErr: From<<&'a K as IntoPyObject<'py>>::Error> + From<<&'a V as IntoPyObject<'py>>::Error>, +{ + type Target = PyDict; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject(self, py: Python<'py>) -> Result { + let dict = PyDict::new(py); + for (k, v) in self { + dict.set_item( + k.into_pyobject(py)?.into_bound(), + v.into_pyobject(py)?.into_bound(), + )?; + } + Ok(dict) + } +} + impl<'py, K, V, S> FromPyObject<'py> for hashbrown::HashMap where K: FromPyObject<'py> + cmp::Eq + hash::Hash, diff --git a/src/conversions/indexmap.rs b/src/conversions/indexmap.rs index 3725bab70fc..bd45d4d3164 100644 --- a/src/conversions/indexmap.rs +++ b/src/conversions/indexmap.rs @@ -119,7 +119,7 @@ where impl<'py, K, V, H> IntoPyObject<'py> for indexmap::IndexMap where - K: hash::Hash + cmp::Eq + IntoPyObject<'py>, + K: IntoPyObject<'py> + cmp::Eq + hash::Hash, V: IntoPyObject<'py>, H: hash::BuildHasher, PyErr: From + From, @@ -140,6 +140,29 @@ where } } +impl<'a, 'py, K, V, H> IntoPyObject<'py> for &'a indexmap::IndexMap +where + &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash, + &'a V: IntoPyObject<'py>, + H: hash::BuildHasher, + PyErr: From<<&'a K as IntoPyObject<'py>>::Error> + From<<&'a V as IntoPyObject<'py>>::Error>, +{ + type Target = PyDict; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject(self, py: Python<'py>) -> Result { + let dict = PyDict::new(py); + for (k, v) in self { + dict.set_item( + k.into_pyobject(py)?.into_bound(), + v.into_pyobject(py)?.into_bound(), + )?; + } + Ok(dict) + } +} + impl<'py, K, V, S> FromPyObject<'py> for indexmap::IndexMap where K: FromPyObject<'py> + cmp::Eq + hash::Hash, diff --git a/src/conversions/std/map.rs b/src/conversions/std/map.rs index 2a966177693..f19b5671d7a 100644 --- a/src/conversions/std/map.rs +++ b/src/conversions/std/map.rs @@ -51,7 +51,7 @@ where impl<'py, K, V, H> IntoPyObject<'py> for collections::HashMap where - K: hash::Hash + cmp::Eq + IntoPyObject<'py>, + K: IntoPyObject<'py> + cmp::Eq + hash::Hash, V: IntoPyObject<'py>, H: hash::BuildHasher, PyErr: From + From, @@ -72,6 +72,29 @@ where } } +impl<'a, 'py, K, V, H> IntoPyObject<'py> for &'a collections::HashMap +where + &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash, + &'a V: IntoPyObject<'py>, + &'a H: hash::BuildHasher, + PyErr: From<<&'a K as IntoPyObject<'py>>::Error> + From<<&'a V as IntoPyObject<'py>>::Error>, +{ + type Target = PyDict; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject(self, py: Python<'py>) -> Result { + let dict = PyDict::new(py); + for (k, v) in self { + dict.set_item( + k.into_pyobject(py)?.into_bound(), + v.into_pyobject(py)?.into_bound(), + )?; + } + Ok(dict) + } +} + impl IntoPy for collections::BTreeMap where K: cmp::Eq + IntoPy, @@ -92,7 +115,7 @@ where impl<'py, K, V> IntoPyObject<'py> for collections::BTreeMap where - K: cmp::Eq + IntoPyObject<'py>, + K: IntoPyObject<'py> + cmp::Eq, V: IntoPyObject<'py>, PyErr: From + From, { @@ -112,6 +135,28 @@ where } } +impl<'a, 'py, K, V> IntoPyObject<'py> for &'a collections::BTreeMap +where + &'a K: IntoPyObject<'py> + cmp::Eq, + &'a V: IntoPyObject<'py>, + PyErr: From<<&'a K as IntoPyObject<'py>>::Error> + From<<&'a V as IntoPyObject<'py>>::Error>, +{ + type Target = PyDict; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject(self, py: Python<'py>) -> Result { + let dict = PyDict::new(py); + for (k, v) in self { + dict.set_item( + k.into_pyobject(py)?.into_bound(), + v.into_pyobject(py)?.into_bound(), + )?; + } + Ok(dict) + } +} + impl<'py, K, V, S> FromPyObject<'py> for collections::HashMap where K: FromPyObject<'py> + cmp::Eq + hash::Hash, From f1c461dc43b75fae5a329874e17ac14e3bc70c60 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:45:56 +0200 Subject: [PATCH 08/21] add `IntoPyObject` for `&Cell` --- src/conversions/std/cell.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/conversions/std/cell.rs b/src/conversions/std/cell.rs index c3ea4d97bab..75a8a13a787 100644 --- a/src/conversions/std/cell.rs +++ b/src/conversions/std/cell.rs @@ -22,6 +22,18 @@ impl<'py, T: Copy + IntoPyObject<'py>> IntoPyObject<'py> for Cell { type Output = T::Output; type Error = T::Error; + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + self.get().into_pyobject(py) + } +} + +impl<'a, 'py, T: Copy + IntoPyObject<'py>> IntoPyObject<'py> for &'a Cell { + type Target = T::Target; + type Output = T::Output; + type Error = T::Error; + + #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { self.get().into_pyobject(py) } From 759753a4eb417f7f354acb586bb8980ea1c63cf6 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:46:08 +0200 Subject: [PATCH 09/21] add `IntoPyObject` for ipaddr references --- src/conversions/std/ipaddr.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/conversions/std/ipaddr.rs b/src/conversions/std/ipaddr.rs index a7fd7fde241..1efcb508646 100755 --- a/src/conversions/std/ipaddr.rs +++ b/src/conversions/std/ipaddr.rs @@ -56,6 +56,17 @@ impl<'py> IntoPyObject<'py> for Ipv4Addr { } } +impl<'py> IntoPyObject<'py> for &Ipv4Addr { + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + impl ToPyObject for Ipv6Addr { fn to_object(&self, py: Python<'_>) -> PyObject { static IPV6_ADDRESS: GILOnceCell> = GILOnceCell::new(); @@ -81,6 +92,17 @@ impl<'py> IntoPyObject<'py> for Ipv6Addr { } } +impl<'py> IntoPyObject<'py> for &Ipv6Addr { + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + impl ToPyObject for IpAddr { fn to_object(&self, py: Python<'_>) -> PyObject { match self { @@ -109,6 +131,17 @@ impl<'py> IntoPyObject<'py> for IpAddr { } } +impl<'py> IntoPyObject<'py> for &IpAddr { + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + #[cfg(test)] mod test_ipaddr { use std::str::FromStr; From 45ec3277270e3787f044743cb33f836125abfb2e Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:46:46 +0200 Subject: [PATCH 10/21] add `IntoPyObject` for `&Decimal` --- src/conversions/rust_decimal.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/conversions/rust_decimal.rs b/src/conversions/rust_decimal.rs index c7056dd8a0e..d41acdc706c 100644 --- a/src/conversions/rust_decimal.rs +++ b/src/conversions/rust_decimal.rs @@ -115,6 +115,17 @@ impl<'py> IntoPyObject<'py> for Decimal { } } +impl<'py> IntoPyObject<'py> for &Decimal { + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + #[cfg(test)] mod test_rust_decimal { use super::*; From d4c113cea6ac2765a614b67fa9534462673ff489 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:47:04 +0200 Subject: [PATCH 11/21] add `IntoPyObject` for `Ratio` --- src/conversions/num_rational.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/conversions/num_rational.rs b/src/conversions/num_rational.rs index 3843af95344..b7eb9d1d0f2 100644 --- a/src/conversions/num_rational.rs +++ b/src/conversions/num_rational.rs @@ -43,11 +43,14 @@ //! assert fraction + 5 == fraction_plus_five //! ``` +use crate::conversion::IntoPyObject; use crate::ffi; use crate::sync::GILOnceCell; use crate::types::any::PyAnyMethods; use crate::types::PyType; -use crate::{Bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject}; +use crate::{ + Bound, FromPyObject, IntoPy, Py, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject, +}; #[cfg(feature = "num-bigint")] use num_bigint::BigInt; @@ -95,6 +98,27 @@ macro_rules! rational_conversion { self.to_object(py) } } + + impl<'py> IntoPyObject<'py> for Ratio<$int> { + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (&self).into_pyobject(py) + } + } + + impl<'py> IntoPyObject<'py> for &Ratio<$int> { + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject(self, py: Python<'py>) -> Result { + get_fraction_cls(py)?.call1((self.numer().clone(), self.denom().clone())) + } + } }; } rational_conversion!(i8); From 8fd3b68c70fc4253073f9611c07384944cea32c4 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:47:18 +0200 Subject: [PATCH 12/21] add `IntoPyObject` for `&Complex` --- src/conversions/num_complex.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/conversions/num_complex.rs b/src/conversions/num_complex.rs index 645d704c672..ee6a3d46ec7 100644 --- a/src/conversions/num_complex.rs +++ b/src/conversions/num_complex.rs @@ -154,6 +154,18 @@ macro_rules! complex_conversion { } } + #[cfg_attr(docsrs, doc(cfg(feature = "num-complex")))] + impl<'py> crate::conversion::IntoPyObject<'py> for &Complex<$float> { + type Target = PyComplex; + type Output = Bound<'py, Self::Target>; + type Error = std::convert::Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } + } + #[cfg_attr(docsrs, doc(cfg(feature = "num-complex")))] impl FromPyObject<'_> for Complex<$float> { fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult> { From 8a90b6ea49612dbfa9dd321e72719a58096e651b Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:47:40 +0200 Subject: [PATCH 13/21] add `IntoPyObject` for bigint references --- src/conversions/num_bigint.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/conversions/num_bigint.rs b/src/conversions/num_bigint.rs index 5ac3bf9ef61..6a2bc732a85 100644 --- a/src/conversions/num_bigint.rs +++ b/src/conversions/num_bigint.rs @@ -141,6 +141,18 @@ macro_rules! bigint_conversion { type Output = Bound<'py, Self::Target>; type Error = PyErr; + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (&self).into_pyobject(py) + } + } + + #[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))] + impl<'py> IntoPyObject<'py> for &$rust_ty { + type Target = PyInt; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + #[cfg(not(Py_LIMITED_API))] fn into_pyobject(self, py: Python<'py>) -> Result { use crate::ffi_ptr_ext::FfiPtrExt; From 0349a5fa6d792005652b24bffa41651c687b53f6 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:48:00 +0200 Subject: [PATCH 14/21] add `IntoPyObject` for `&Either` --- src/conversions/either.rs | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/conversions/either.rs b/src/conversions/either.rs index 2dd79ba3807..3d4aeafa32b 100644 --- a/src/conversions/either.rs +++ b/src/conversions/either.rs @@ -67,12 +67,40 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "either")))] -impl<'py, L, R, E1, E2> IntoPyObject<'py> for Either +impl<'py, L, R> IntoPyObject<'py> for Either where - L: IntoPyObject<'py, Error = E1>, - R: IntoPyObject<'py, Error = E2>, - E1: Into, - E2: Into, + L: IntoPyObject<'py>, + R: IntoPyObject<'py>, + L::Error: Into, + R::Error: Into, +{ + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject(self, py: Python<'py>) -> Result { + match self { + Either::Left(l) => l + .into_pyobject(py) + .map(BoundObject::into_any) + .map(BoundObject::into_bound) + .map_err(Into::into), + Either::Right(r) => r + .into_pyobject(py) + .map(BoundObject::into_any) + .map(BoundObject::into_bound) + .map_err(Into::into), + } + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "either")))] +impl<'a, 'py, L, R> IntoPyObject<'py> for &'a Either +where + &'a L: IntoPyObject<'py>, + &'a R: IntoPyObject<'py>, + <&'a L as IntoPyObject<'py>>::Error: Into, + <&'a R as IntoPyObject<'py>>::Error: Into, { type Target = PyAny; type Output = Bound<'py, Self::Target>; From ca37cccceca1248d91198527bfc69ac14dbe6fb8 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:48:32 +0200 Subject: [PATCH 15/21] add `IntoPyObject` for chrono references --- src/conversions/chrono.rs | 98 ++++++++++++++++++++++++++++++++++++ src/conversions/chrono_tz.rs | 11 ++++ 2 files changed, 109 insertions(+) diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs index 25c5acd9963..8e598e20ef2 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -152,6 +152,20 @@ impl<'py> IntoPyObject<'py> for Duration { } } +impl<'py> IntoPyObject<'py> for &Duration { + #[cfg(Py_LIMITED_API)] + type Target = PyAny; + #[cfg(not(Py_LIMITED_API))] + type Target = PyDelta; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + impl FromPyObject<'_> for Duration { fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { // Python size are much lower than rust size so we do not need bound checks. @@ -231,6 +245,20 @@ impl<'py> IntoPyObject<'py> for NaiveDate { } } +impl<'py> IntoPyObject<'py> for &NaiveDate { + #[cfg(Py_LIMITED_API)] + type Target = PyAny; + #[cfg(not(Py_LIMITED_API))] + type Target = PyDate; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + impl FromPyObject<'_> for NaiveDate { fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { #[cfg(not(Py_LIMITED_API))] @@ -309,6 +337,20 @@ impl<'py> IntoPyObject<'py> for NaiveTime { } } +impl<'py> IntoPyObject<'py> for &NaiveTime { + #[cfg(Py_LIMITED_API)] + type Target = PyAny; + #[cfg(not(Py_LIMITED_API))] + type Target = PyTime; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + impl FromPyObject<'_> for NaiveTime { fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { #[cfg(not(Py_LIMITED_API))] @@ -372,6 +414,20 @@ impl<'py> IntoPyObject<'py> for NaiveDateTime { } } +impl<'py> IntoPyObject<'py> for &NaiveDateTime { + #[cfg(Py_LIMITED_API)] + type Target = PyAny; + #[cfg(not(Py_LIMITED_API))] + type Target = PyDateTime; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + impl FromPyObject<'_> for NaiveDateTime { fn extract_bound(dt: &Bound<'_, PyAny>) -> PyResult { #[cfg(not(Py_LIMITED_API))] @@ -419,6 +475,20 @@ impl<'py, Tz: TimeZone> IntoPyObject<'py> for DateTime { type Output = Bound<'py, Self::Target>; type Error = PyErr; + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (&self).into_pyobject(py) + } +} + +impl<'py, Tz: TimeZone> IntoPyObject<'py> for &DateTime { + #[cfg(Py_LIMITED_API)] + type Target = PyAny; + #[cfg(not(Py_LIMITED_API))] + type Target = PyDateTime; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + fn into_pyobject(self, py: Python<'py>) -> Result { let tz = self.offset().fix().into_pyobject(py)?; let DateArgs { year, month, day } = (&self.naive_local().date()).into(); @@ -531,6 +601,20 @@ impl<'py> IntoPyObject<'py> for FixedOffset { } } +impl<'py> IntoPyObject<'py> for &FixedOffset { + #[cfg(Py_LIMITED_API)] + type Target = PyAny; + #[cfg(not(Py_LIMITED_API))] + type Target = PyTzInfo; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + impl FromPyObject<'_> for FixedOffset { /// Convert python tzinfo to rust [`FixedOffset`]. /// @@ -594,6 +678,20 @@ impl<'py> IntoPyObject<'py> for Utc { } } +impl<'py> IntoPyObject<'py> for &Utc { + #[cfg(Py_LIMITED_API)] + type Target = PyAny; + #[cfg(not(Py_LIMITED_API))] + type Target = PyTzInfo; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + impl FromPyObject<'_> for Utc { fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { let py_utc = timezone_utc_bound(ob.py()); diff --git a/src/conversions/chrono_tz.rs b/src/conversions/chrono_tz.rs index 88bf39de64a..cb8baf0448a 100644 --- a/src/conversions/chrono_tz.rs +++ b/src/conversions/chrono_tz.rs @@ -75,6 +75,17 @@ impl<'py> IntoPyObject<'py> for Tz { } } +impl<'py> IntoPyObject<'py> for &Tz { + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + impl FromPyObject<'_> for Tz { fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { Tz::from_str( From 7e2f69063a12d2090c5085be29e770990827a7b0 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:48:51 +0200 Subject: [PATCH 16/21] add `IntoPyObject` for `&SmallVec` --- src/conversions/smallvec.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/conversions/smallvec.rs b/src/conversions/smallvec.rs index 091cbfc48ee..bffa54d00b9 100644 --- a/src/conversions/smallvec.rs +++ b/src/conversions/smallvec.rs @@ -76,6 +76,22 @@ where } } +impl<'a, 'py, A> IntoPyObject<'py> for &'a SmallVec +where + A: Array, + &'a A::Item: IntoPyObject<'py>, + PyErr: From<<&'a A::Item as IntoPyObject<'py>>::Error>, +{ + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + self.as_slice().into_pyobject(py) + } +} + impl<'py, A> FromPyObject<'py> for SmallVec where A: Array, From e25c1a36ad6260c16b853e46527f0fdbed538a48 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:49:59 +0200 Subject: [PATCH 17/21] add `IntoPyObject` for `&[T; N]` --- src/conversions/std/array.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/conversions/std/array.rs b/src/conversions/std/array.rs index 2780868ae04..5645d87cfe5 100644 --- a/src/conversions/std/array.rs +++ b/src/conversions/std/array.rs @@ -56,6 +56,21 @@ where } } +impl<'a, 'py, T, const N: usize> IntoPyObject<'py> for &'a [T; N] +where + &'a T: IntoPyObject<'py>, + PyErr: From<<&'a T as IntoPyObject<'py>>::Error>, +{ + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + self.as_slice().into_pyobject(py) + } +} + impl ToPyObject for [T; N] where T: ToPyObject, From 0b84ec25dd33d3b75877d43da6c89a54dd351374 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:50:28 +0200 Subject: [PATCH 18/21] add `IntoPyObject` for path and osstr references --- src/conversions/std/osstr.rs | 14 ++++++++++++++ src/conversions/std/path.rs | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/conversions/std/osstr.rs b/src/conversions/std/osstr.rs index 3904010d35a..dc757717973 100644 --- a/src/conversions/std/osstr.rs +++ b/src/conversions/std/osstr.rs @@ -145,11 +145,23 @@ impl<'py> IntoPyObject<'py> for Cow<'_, OsStr> { type Output = Bound<'py, Self::Target>; type Error = Infallible; + #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { (*self).into_pyobject(py) } } +impl<'py> IntoPyObject<'py> for &Cow<'_, OsStr> { + type Target = PyString; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (&**self).into_pyobject(py) + } +} + impl ToPyObject for OsString { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { @@ -168,6 +180,7 @@ impl<'py> IntoPyObject<'py> for OsString { type Output = Bound<'py, Self::Target>; type Error = Infallible; + #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { self.as_os_str().into_pyobject(py) } @@ -184,6 +197,7 @@ impl<'py> IntoPyObject<'py> for &OsString { type Output = Bound<'py, Self::Target>; type Error = Infallible; + #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { self.as_os_str().into_pyobject(py) } diff --git a/src/conversions/std/path.rs b/src/conversions/std/path.rs index 1540c852f05..7e7a5bdc17a 100644 --- a/src/conversions/std/path.rs +++ b/src/conversions/std/path.rs @@ -37,6 +37,7 @@ impl<'py> IntoPyObject<'py> for &Path { type Output = Bound<'py, Self::Target>; type Error = Infallible; + #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { self.as_os_str().into_pyobject(py) } @@ -61,6 +62,18 @@ impl<'py> IntoPyObject<'py> for Cow<'_, Path> { type Output = Bound<'py, Self::Target>; type Error = Infallible; + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + self.as_os_str().into_pyobject(py) + } +} + +impl<'py> IntoPyObject<'py> for &Cow<'_, Path> { + type Target = PyString; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { self.as_os_str().into_pyobject(py) } @@ -84,6 +97,7 @@ impl<'py> IntoPyObject<'py> for PathBuf { type Output = Bound<'py, Self::Target>; type Error = Infallible; + #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { self.as_os_str().into_pyobject(py) } @@ -100,6 +114,7 @@ impl<'py> IntoPyObject<'py> for &PathBuf { type Output = Bound<'py, Self::Target>; type Error = Infallible; + #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { self.as_os_str().into_pyobject(py) } From 8846de5129c6cd77723b786bfae9e79fe06739d1 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:52:43 +0200 Subject: [PATCH 19/21] add `IntoPyObject` for `&Borrowed` --- src/conversion.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/conversion.rs b/src/conversion.rs index 82710cf16fb..d909382bdb9 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -291,6 +291,16 @@ impl<'a, 'py, T> IntoPyObject<'py> for Borrowed<'a, 'py, T> { } } +impl<'a, 'py, T> IntoPyObject<'py> for &Borrowed<'a, 'py, T> { + type Target = T; + type Output = Borrowed<'a, 'py, Self::Target>; + type Error = Infallible; + + fn into_pyobject(self, _py: Python<'py>) -> Result { + Ok(*self) + } +} + impl<'py, T> IntoPyObject<'py> for Py { type Target = T; type Output = Bound<'py, Self::Target>; From cb1f3e488691ebb3fd9fe33e8172f08b914e1a75 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 16 Aug 2024 20:34:46 +0200 Subject: [PATCH 20/21] bless ui test --- tests/ui/missing_intopy.stderr | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/ui/missing_intopy.stderr b/tests/ui/missing_intopy.stderr index 58ddbff0f22..f7dcca419bf 100644 --- a/tests/ui/missing_intopy.stderr +++ b/tests/ui/missing_intopy.stderr @@ -8,14 +8,14 @@ error[E0277]: `Blah` cannot be converted to a Python object = note: if you do not wish to have a corresponding Python type, implement it manually = note: if you do not own `Blah` you can perform a manual conversion to one of the types in `pyo3::types::*` = help: the following other types implement trait `IntoPyObject<'py>`: + &&str + &'a BTreeMap + &'a BTreeSet + &'a Cell + &'a HashMap + &'a HashSet + &'a Option &'a Py - &'a PyRef<'py, T> - &'a PyRefMut<'py, T> - &'a Vec - &'a [T] - &'a pyo3::Bound<'py, T> - &OsStr - &OsString and $N others note: required by a bound in `UnknownReturnType::::wrap` --> src/impl_/wrap.rs From 2ab5a01581894f1498e73395cff36f094e5e5210 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Sat, 17 Aug 2024 14:43:26 +0200 Subject: [PATCH 21/21] implement `ToPyObject` and `IntoPy` in terms of `IntoPyObject` --- src/conversions/chrono.rs | 120 +++++-------------------- src/conversions/chrono_tz.rs | 12 +-- src/conversions/num_bigint.rs | 62 +------------ src/conversions/num_rational.rs | 10 +-- src/conversions/rust_decimal.rs | 14 +-- src/conversions/std/ipaddr.rs | 27 ++---- src/conversions/std/num.rs | 149 +++++++++++++++++++++++++------- src/conversions/std/osstr.rs | 15 ++-- src/conversions/std/path.rs | 17 ++-- src/conversions/std/string.rs | 24 ++--- src/conversions/std/time.rs | 99 +++++++-------------- src/err/mod.rs | 9 +- src/types/boolobject.rs | 14 +-- src/types/float.rs | 58 ++++++++++++- 14 files changed, 290 insertions(+), 340 deletions(-) diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs index 8e598e20ef2..44cee349c2f 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -61,49 +61,16 @@ use chrono::{ }; impl ToPyObject for Duration { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - // Total number of days - let days = self.num_days(); - // Remainder of seconds - let secs_dur = *self - Duration::days(days); - let secs = secs_dur.num_seconds(); - // Fractional part of the microseconds - let micros = (secs_dur - Duration::seconds(secs_dur.num_seconds())) - .num_microseconds() - // This should never panic since we are just getting the fractional - // part of the total microseconds, which should never overflow. - .unwrap(); - - #[cfg(not(Py_LIMITED_API))] - { - // We do not need to check the days i64 to i32 cast from rust because - // python will panic with OverflowError. - // We pass true as the `normalize` parameter since we'd need to do several checks here to - // avoid that, and it shouldn't have a big performance impact. - // The seconds and microseconds cast should never overflow since it's at most the number of seconds per day - PyDelta::new_bound( - py, - days.try_into().unwrap_or(i32::MAX), - secs.try_into().unwrap(), - micros.try_into().unwrap(), - true, - ) - .expect("failed to construct delta") - .into() - } - #[cfg(Py_LIMITED_API)] - { - DatetimeTypes::get(py) - .timedelta - .call1(py, (days, secs, micros)) - .expect("failed to construct datetime.timedelta") - } + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for Duration { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -199,27 +166,16 @@ impl FromPyObject<'_> for Duration { } impl ToPyObject for NaiveDate { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - let DateArgs { year, month, day } = self.into(); - #[cfg(not(Py_LIMITED_API))] - { - PyDate::new_bound(py, year, month, day) - .expect("failed to construct date") - .into() - } - #[cfg(Py_LIMITED_API)] - { - DatetimeTypes::get(py) - .date - .call1(py, (year, month, day)) - .expect("failed to construct datetime.date") - } + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for NaiveDate { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -275,33 +231,16 @@ impl FromPyObject<'_> for NaiveDate { } impl ToPyObject for NaiveTime { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - let TimeArgs { - hour, - min, - sec, - micro, - truncated_leap_second, - } = self.into(); - #[cfg(not(Py_LIMITED_API))] - let time = - PyTime::new_bound(py, hour, min, sec, micro, None).expect("Failed to construct time"); - #[cfg(Py_LIMITED_API)] - let time = DatetimeTypes::get(py) - .time - .bind(py) - .call1((hour, min, sec, micro)) - .expect("failed to construct datetime.time"); - if truncated_leap_second { - warn_truncated_leap_second(&time); - } - time.into() + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for NaiveTime { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -367,14 +306,16 @@ impl FromPyObject<'_> for NaiveTime { } impl ToPyObject for NaiveDateTime { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - naive_datetime_to_py_datetime(py, self, None) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for NaiveDateTime { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -549,31 +490,16 @@ impl FromPyObject<'py>> FromPyObject<'_> for DateTime) -> PyObject { - let seconds_offset = self.local_minus_utc(); - - #[cfg(not(Py_LIMITED_API))] - { - let td = PyDelta::new_bound(py, 0, seconds_offset, 0, true) - .expect("failed to construct timedelta"); - timezone_from_offset(&td) - .expect("Failed to construct PyTimezone") - .into() - } - #[cfg(Py_LIMITED_API)] - { - let td = Duration::seconds(seconds_offset.into()).into_py(py); - DatetimeTypes::get(py) - .timezone - .call1(py, (td,)) - .expect("failed to construct datetime.timezone") - } + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for FixedOffset { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -647,14 +573,16 @@ impl FromPyObject<'_> for FixedOffset { } impl ToPyObject for Utc { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - timezone_utc_bound(py).into() + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for Utc { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } diff --git a/src/conversions/chrono_tz.rs b/src/conversions/chrono_tz.rs index cb8baf0448a..52c156eaba2 100644 --- a/src/conversions/chrono_tz.rs +++ b/src/conversions/chrono_tz.rs @@ -45,20 +45,16 @@ use chrono_tz::Tz; use std::str::FromStr; impl ToPyObject for Tz { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - static ZONE_INFO: GILOnceCell> = GILOnceCell::new(); - ZONE_INFO - .get_or_try_init_type_ref(py, "zoneinfo", "ZoneInfo") - .unwrap() - .call1((self.name(),)) - .unwrap() - .unbind() + self.into_pyobject(py).unwrap().unbind() } } impl IntoPy for Tz { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().unbind() } } diff --git a/src/conversions/num_bigint.rs b/src/conversions/num_bigint.rs index 6a2bc732a85..3c699868c52 100644 --- a/src/conversions/num_bigint.rs +++ b/src/conversions/num_bigint.rs @@ -47,8 +47,6 @@ //! assert n + 1 == value //! ``` -#[cfg(not(Py_LIMITED_API))] -use crate::ffi_ptr_ext::FfiPtrExt; #[cfg(Py_LIMITED_API)] use crate::types::{bytes::PyBytesMethods, PyBytes}; use crate::{ @@ -69,69 +67,17 @@ macro_rules! bigint_conversion { ($rust_ty: ty, $is_signed: literal, $to_bytes: path) => { #[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))] impl ToPyObject for $rust_ty { - #[cfg(not(Py_LIMITED_API))] - fn to_object(&self, py: Python<'_>) -> PyObject { - let bytes = $to_bytes(self); - #[cfg(not(Py_3_13))] - { - unsafe { - ffi::_PyLong_FromByteArray( - bytes.as_ptr().cast(), - bytes.len(), - 1, - $is_signed.into(), - ) - .assume_owned(py) - .unbind() - } - } - #[cfg(Py_3_13)] - { - if $is_signed { - unsafe { - ffi::PyLong_FromNativeBytes( - bytes.as_ptr().cast(), - bytes.len(), - ffi::Py_ASNATIVEBYTES_LITTLE_ENDIAN, - ) - .assume_owned(py) - } - } else { - unsafe { - ffi::PyLong_FromUnsignedNativeBytes( - bytes.as_ptr().cast(), - bytes.len(), - ffi::Py_ASNATIVEBYTES_LITTLE_ENDIAN, - ) - .assume_owned(py) - } - } - .unbind() - } - } - - #[cfg(Py_LIMITED_API)] + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - let bytes = $to_bytes(self); - let bytes_obj = PyBytes::new(py, &bytes); - let kwargs = if $is_signed { - let kwargs = crate::types::PyDict::new(py); - kwargs.set_item(crate::intern!(py, "signed"), true).unwrap(); - Some(kwargs) - } else { - None - }; - py.get_type::() - .call_method("from_bytes", (bytes_obj, "little"), kwargs.as_ref()) - .expect("int.from_bytes() failed during to_object()") // FIXME: #1813 or similar - .into() + self.into_pyobject(py).unwrap().into_any().unbind() } } #[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))] impl IntoPy for $rust_ty { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } diff --git a/src/conversions/num_rational.rs b/src/conversions/num_rational.rs index b7eb9d1d0f2..146e9973119 100644 --- a/src/conversions/num_rational.rs +++ b/src/conversions/num_rational.rs @@ -85,17 +85,15 @@ macro_rules! rational_conversion { } impl ToPyObject for Ratio<$int> { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - let fraction_cls = get_fraction_cls(py).expect("failed to load fractions.Fraction"); - let ret = fraction_cls - .call1((self.numer().clone(), self.denom().clone())) - .expect("failed to call fractions.Fraction(value)"); - ret.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for Ratio<$int> { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } diff --git a/src/conversions/rust_decimal.rs b/src/conversions/rust_decimal.rs index d41acdc706c..8dbecad9fa5 100644 --- a/src/conversions/rust_decimal.rs +++ b/src/conversions/rust_decimal.rs @@ -83,22 +83,16 @@ fn get_decimal_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> { } impl ToPyObject for Decimal { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - // TODO: handle error gracefully when ToPyObject can error - // look up the decimal.Decimal - let dec_cls = get_decimal_cls(py).expect("failed to load decimal.Decimal"); - // now call the constructor with the Rust Decimal string-ified - // to not be lossy - let ret = dec_cls - .call1((self.to_string(),)) - .expect("failed to call decimal.Decimal(value)"); - ret.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for Decimal { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } diff --git a/src/conversions/std/ipaddr.rs b/src/conversions/std/ipaddr.rs index 1efcb508646..8c308ead3b8 100755 --- a/src/conversions/std/ipaddr.rs +++ b/src/conversions/std/ipaddr.rs @@ -32,14 +32,9 @@ impl FromPyObject<'_> for IpAddr { } impl ToPyObject for Ipv4Addr { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - static IPV4_ADDRESS: GILOnceCell> = GILOnceCell::new(); - IPV4_ADDRESS - .get_or_try_init_type_ref(py, "ipaddress", "IPv4Address") - .expect("failed to load ipaddress.IPv4Address") - .call1((u32::from_be_bytes(self.octets()),)) - .expect("failed to construct ipaddress.IPv4Address") - .unbind() + self.into_pyobject(py).unwrap().unbind() } } @@ -68,14 +63,9 @@ impl<'py> IntoPyObject<'py> for &Ipv4Addr { } impl ToPyObject for Ipv6Addr { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - static IPV6_ADDRESS: GILOnceCell> = GILOnceCell::new(); - IPV6_ADDRESS - .get_or_try_init_type_ref(py, "ipaddress", "IPv6Address") - .expect("failed to load ipaddress.IPv6Address") - .call1((u128::from_be_bytes(self.octets()),)) - .expect("failed to construct ipaddress.IPv6Address") - .unbind() + self.into_pyobject(py).unwrap().unbind() } } @@ -104,17 +94,16 @@ impl<'py> IntoPyObject<'py> for &Ipv6Addr { } impl ToPyObject for IpAddr { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - match self { - IpAddr::V4(ip) => ip.to_object(py), - IpAddr::V6(ip) => ip.to_object(py), - } + self.into_pyobject(py).unwrap().unbind() } } impl IntoPy for IpAddr { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().unbind() } } diff --git a/src/conversions/std/num.rs b/src/conversions/std/num.rs index 61c666a1cfe..954aebb14a3 100644 --- a/src/conversions/std/num.rs +++ b/src/conversions/std/num.rs @@ -21,12 +21,13 @@ 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.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for $rust_type { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - (self as $larger_type).into_py(py) + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] @@ -90,13 +91,13 @@ macro_rules! int_convert_u64_or_i64 { impl ToPyObject for $rust_type { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_owned_ptr(py, $pylong_from_ll_or_ull(*self)) } + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for $rust_type { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_owned_ptr(py, $pylong_from_ll_or_ull(self)) } + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] @@ -117,6 +118,16 @@ macro_rules! int_convert_u64_or_i64 { } } } + impl<'py> IntoPyObject<'py> for &$rust_type { + type Target = PyInt; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } + } impl FromPyObject<'_> for $rust_type { fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<$rust_type> { extract_int!(obj, !0, $pylong_as_ll_or_ull, $force_index_call) @@ -133,13 +144,15 @@ macro_rules! int_convert_u64_or_i64 { macro_rules! int_fits_c_long { ($rust_type:ty) => { impl ToPyObject for $rust_type { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(*self as c_long)) } + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for $rust_type { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(self as c_long)) } + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] @@ -167,6 +180,7 @@ macro_rules! int_fits_c_long { type Output = Bound<'py, Self::Target>; type Error = Infallible; + #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { (*self).into_pyobject(py) } @@ -188,13 +202,15 @@ macro_rules! int_fits_c_long { } impl ToPyObject for u8 { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(*self as c_long)) } + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for u8 { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(self as c_long)) } + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] fn type_output() -> TypeInfo { @@ -305,23 +321,39 @@ mod fast_128bit_int_conversion { impl ToPyObject for $rust_type { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - (*self).into_py(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for $rust_type { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { + self.into_pyobject(py).unwrap().into_any().unbind() + } + + #[cfg(feature = "experimental-inspect")] + fn type_output() -> TypeInfo { + TypeInfo::builtin("int") + } + } + + impl<'py> IntoPyObject<'py> for $rust_type { + type Target = PyInt; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + fn into_pyobject(self, py: Python<'py>) -> Result { #[cfg(not(Py_3_13))] { let bytes = self.to_le_bytes(); unsafe { - ffi::_PyLong_FromByteArray( + Ok(ffi::_PyLong_FromByteArray( bytes.as_ptr().cast(), bytes.len(), 1, $is_signed.into(), ) .assume_owned(py) - .unbind() + .downcast_into_unchecked()) } } #[cfg(Py_3_13)] @@ -330,30 +362,37 @@ mod fast_128bit_int_conversion { if $is_signed { unsafe { - ffi::PyLong_FromNativeBytes( + Ok(ffi::PyLong_FromNativeBytes( bytes.as_ptr().cast(), bytes.len(), ffi::Py_ASNATIVEBYTES_NATIVE_ENDIAN, ) .assume_owned(py) + .downcast_into_unchecked()) } } else { unsafe { - ffi::PyLong_FromUnsignedNativeBytes( + Ok(ffi::PyLong_FromUnsignedNativeBytes( bytes.as_ptr().cast(), bytes.len(), ffi::Py_ASNATIVEBYTES_NATIVE_ENDIAN, ) .assume_owned(py) + .downcast_into_unchecked()) } } - .unbind() } } + } - #[cfg(feature = "experimental-inspect")] - fn type_output() -> TypeInfo { - TypeInfo::builtin("int") + impl<'py> IntoPyObject<'py> for &$rust_type { + type Target = PyInt; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) } } @@ -428,25 +467,14 @@ mod slow_128bit_int_conversion { impl ToPyObject for $rust_type { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - (*self).into_py(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for $rust_type { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - let lower = (self as u64).into_py(py); - let upper = ((self >> SHIFT) as $half_type).into_py(py); - let shift = SHIFT.into_py(py); - unsafe { - let shifted = PyObject::from_owned_ptr( - py, - ffi::PyNumber_Lshift(upper.as_ptr(), shift.as_ptr()), - ); - PyObject::from_owned_ptr( - py, - ffi::PyNumber_Or(shifted.as_ptr(), lower.as_ptr()), - ) - } + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] @@ -455,6 +483,37 @@ mod slow_128bit_int_conversion { } } + impl<'py> IntoPyObject<'py> for $rust_type { + type Target = PyInt; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + fn into_pyobject(self, py: Python<'py>) -> Result { + let lower = (self as u64).into_pyobject(py)?; + let upper = ((self >> SHIFT) as $half_type).into_pyobject(py)?; + let shift = SHIFT.into_pyobject(py)?; + unsafe { + let shifted = + ffi::PyNumber_Lshift(upper.as_ptr(), shift.as_ptr()).assume_owned(py); + + Ok(ffi::PyNumber_Or(shifted.as_ptr(), lower.as_ptr()) + .assume_owned(py) + .downcast_into_unchecked()) + } + } + } + + impl<'py> IntoPyObject<'py> for &$rust_type { + type Target = PyInt; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } + } + impl FromPyObject<'_> for $rust_type { fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> { let py = ob.py(); @@ -503,14 +562,38 @@ fn err_if_invalid_value( macro_rules! nonzero_int_impl { ($nonzero_type:ty, $primitive_type:ty) => { impl ToPyObject for $nonzero_type { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - self.get().to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for $nonzero_type { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.get().into_py(py) + self.into_pyobject(py).unwrap().into_any().unbind() + } + } + + impl<'py> IntoPyObject<'py> for $nonzero_type { + type Target = PyInt; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + self.get().into_pyobject(py) + } + } + + impl<'py> IntoPyObject<'py> for &$nonzero_type { + type Target = PyInt; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) } } diff --git a/src/conversions/std/osstr.rs b/src/conversions/std/osstr.rs index dc757717973..c140ef703d4 100644 --- a/src/conversions/std/osstr.rs +++ b/src/conversions/std/osstr.rs @@ -9,6 +9,7 @@ use std::convert::Infallible; use std::ffi::{OsStr, OsString}; impl ToPyObject for OsStr { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { self.into_pyobject(py).unwrap().into_any().unbind() } @@ -122,21 +123,21 @@ impl FromPyObject<'_> for OsString { impl IntoPy for &'_ OsStr { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl ToPyObject for Cow<'_, OsStr> { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - (self as &OsStr).to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for Cow<'_, OsStr> { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -165,13 +166,14 @@ impl<'py> IntoPyObject<'py> for &Cow<'_, OsStr> { impl ToPyObject for OsString { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - (self as &OsStr).to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for OsString { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -187,8 +189,9 @@ impl<'py> IntoPyObject<'py> for OsString { } impl<'a> IntoPy for &'a OsString { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } diff --git a/src/conversions/std/path.rs b/src/conversions/std/path.rs index 7e7a5bdc17a..758c4bbe198 100644 --- a/src/conversions/std/path.rs +++ b/src/conversions/std/path.rs @@ -10,8 +10,9 @@ use std::ffi::OsString; use std::path::{Path, PathBuf}; impl ToPyObject for Path { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - self.as_os_str().to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -28,7 +29,7 @@ impl FromPyObject<'_> for PathBuf { impl<'a> IntoPy for &'a Path { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.as_os_str().to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -46,14 +47,14 @@ impl<'py> IntoPyObject<'py> for &Path { impl<'a> ToPyObject for Cow<'a, Path> { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - self.as_os_str().to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl<'a> IntoPy for Cow<'a, Path> { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -82,13 +83,14 @@ impl<'py> IntoPyObject<'py> for &Cow<'_, Path> { impl ToPyObject for PathBuf { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - self.as_os_str().to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for PathBuf { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.into_os_string().to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -104,8 +106,9 @@ impl<'py> IntoPyObject<'py> for PathBuf { } impl<'a> IntoPy for &'a PathBuf { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.as_os_str().to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } diff --git a/src/conversions/std/string.rs b/src/conversions/std/string.rs index a6ea913a4ff..02688641e78 100644 --- a/src/conversions/std/string.rs +++ b/src/conversions/std/string.rs @@ -14,14 +14,14 @@ use crate::{ impl ToPyObject for str { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - PyString::new(py, self).into() + self.into_pyobject(py).unwrap().into_any().unbind() } } impl<'a> IntoPy for &'a str { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - PyString::new(py, self).into() + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] @@ -33,7 +33,7 @@ impl<'a> IntoPy for &'a str { impl<'a> IntoPy> for &'a str { #[inline] fn into_py(self, py: Python<'_>) -> Py { - PyString::new(py, self).into() + self.into_pyobject(py).unwrap().unbind() } #[cfg(feature = "experimental-inspect")] @@ -69,14 +69,14 @@ impl<'py> IntoPyObject<'py> for &&str { impl ToPyObject for Cow<'_, str> { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - PyString::new(py, self).into() + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for Cow<'_, str> { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] @@ -112,20 +112,21 @@ impl<'py> IntoPyObject<'py> for &Cow<'_, str> { impl ToPyObject for String { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - PyString::new(py, self).into() + self.into_pyobject(py).unwrap().into_any().unbind() } } impl ToPyObject for char { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - self.into_py(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for char { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - let mut bytes = [0u8; 4]; - PyString::new(py, self.encode_utf8(&mut bytes)).into() + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] @@ -157,8 +158,9 @@ impl<'py> IntoPyObject<'py> for &char { } impl IntoPy for String { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - PyString::new(py, &self).into() + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] @@ -180,7 +182,7 @@ impl<'py> IntoPyObject<'py> for String { impl<'a> IntoPy for &'a String { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - PyString::new(py, self).into() + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] diff --git a/src/conversions/std/time.rs b/src/conversions/std/time.rs index e16d17cff23..5623297c161 100755 --- a/src/conversions/std/time.rs +++ b/src/conversions/std/time.rs @@ -53,40 +53,16 @@ impl FromPyObject<'_> for Duration { } impl ToPyObject for Duration { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - let days = self.as_secs() / SECONDS_PER_DAY; - let seconds = self.as_secs() % SECONDS_PER_DAY; - let microseconds = self.subsec_micros(); - - #[cfg(not(Py_LIMITED_API))] - { - PyDelta::new_bound( - py, - days.try_into() - .expect("Too large Rust duration for timedelta"), - seconds.try_into().unwrap(), - microseconds.try_into().unwrap(), - false, - ) - .expect("failed to construct timedelta (overflow?)") - .into() - } - #[cfg(Py_LIMITED_API)] - { - static TIMEDELTA: GILOnceCell> = GILOnceCell::new(); - TIMEDELTA - .get_or_try_init_type_ref(py, "datetime", "timedelta") - .unwrap() - .call1((days, seconds, microseconds)) - .unwrap() - .into() - } + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for Duration { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -146,7 +122,7 @@ impl<'py> IntoPyObject<'py> for &Duration { impl FromPyObject<'_> for SystemTime { fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { let duration_since_unix_epoch: Duration = obj - .call_method1(intern!(obj.py(), "__sub__"), (unix_epoch_py(obj.py()),))? + .call_method1(intern!(obj.py(), "__sub__"), (unix_epoch_py(obj.py())?,))? .extract()?; UNIX_EPOCH .checked_add(duration_since_unix_epoch) @@ -157,17 +133,16 @@ impl FromPyObject<'_> for SystemTime { } impl ToPyObject for SystemTime { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - let duration_since_unix_epoch = self.duration_since(UNIX_EPOCH).unwrap().into_py(py); - unix_epoch_py(py) - .call_method1(py, intern!(py, "__add__"), (duration_since_unix_epoch,)) - .unwrap() + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for SystemTime { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.to_object(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } @@ -179,7 +154,7 @@ impl<'py> IntoPyObject<'py> for SystemTime { fn into_pyobject(self, py: Python<'py>) -> Result { let duration_since_unix_epoch = self.duration_since(UNIX_EPOCH).unwrap().into_pyobject(py)?; - unix_epoch_py(py) + unix_epoch_py(py)? .bind(py) .call_method1(intern!(py, "__add__"), (duration_since_unix_epoch,)) } @@ -196,41 +171,29 @@ impl<'py> IntoPyObject<'py> for &SystemTime { } } -fn unix_epoch_py(py: Python<'_>) -> &PyObject { +fn unix_epoch_py(py: Python<'_>) -> PyResult<&PyObject> { static UNIX_EPOCH: GILOnceCell = GILOnceCell::new(); - UNIX_EPOCH - .get_or_try_init(py, || { - #[cfg(not(Py_LIMITED_API))] - { - Ok::<_, PyErr>( - PyDateTime::new_bound( - py, - 1970, - 1, - 1, - 0, - 0, - 0, - 0, - Some(&timezone_utc_bound(py)), - )? + UNIX_EPOCH.get_or_try_init(py, || { + #[cfg(not(Py_LIMITED_API))] + { + Ok::<_, PyErr>( + PyDateTime::new_bound(py, 1970, 1, 1, 0, 0, 0, 0, Some(&timezone_utc_bound(py)))? .into(), - ) - } - #[cfg(Py_LIMITED_API)] - { - let datetime = py.import("datetime")?; - let utc = datetime.getattr("timezone")?.getattr("utc")?; - Ok::<_, PyErr>( - datetime - .getattr("datetime")? - .call1((1970, 1, 1, 0, 0, 0, 0, utc)) - .unwrap() - .into(), - ) - } - }) - .unwrap() + ) + } + #[cfg(Py_LIMITED_API)] + { + let datetime = py.import("datetime")?; + let utc = datetime.getattr("timezone")?.getattr("utc")?; + Ok::<_, PyErr>( + datetime + .getattr("datetime")? + .call1((1970, 1, 1, 0, 0, 0, 0, utc)) + .unwrap() + .into(), + ) + } + }) } #[cfg(test)] diff --git a/src/err/mod.rs b/src/err/mod.rs index fbf346b4caf..68d207c8fb8 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -785,20 +785,23 @@ impl std::fmt::Display for PyErr { impl std::error::Error for PyErr {} impl IntoPy for PyErr { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.into_value(py).into() + self.into_pyobject(py).unwrap().into_any().unbind() } } impl ToPyObject for PyErr { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - self.clone_ref(py).into_py(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } impl<'a> IntoPy for &'a PyErr { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - self.clone_ref(py).into_py(py) + self.into_pyobject(py).unwrap().into_any().unbind() } } diff --git a/src/types/boolobject.rs b/src/types/boolobject.rs index e5dc050a679..8f211a27a54 100644 --- a/src/types/boolobject.rs +++ b/src/types/boolobject.rs @@ -8,6 +8,7 @@ use crate::{ use super::any::PyAnyMethods; use crate::conversion::IntoPyObject; +use crate::BoundObject; use std::convert::Infallible; /// Represents a Python `bool`. @@ -147,23 +148,14 @@ impl PartialEq> for &'_ bool { impl ToPyObject for bool { #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - unsafe { - PyObject::from_borrowed_ptr( - py, - if *self { - ffi::Py_True() - } else { - ffi::Py_False() - }, - ) - } + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for bool { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - PyBool::new(py, self).into_py(py) + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] diff --git a/src/types/float.rs b/src/types/float.rs index 70c5e1cd9e3..5e637af3b62 100644 --- a/src/types/float.rs +++ b/src/types/float.rs @@ -1,10 +1,12 @@ use super::any::PyAnyMethods; +use crate::conversion::IntoPyObject; #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::{ ffi, ffi_ptr_ext::FfiPtrExt, instance::Bound, Borrowed, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject, }; +use std::convert::Infallible; use std::os::raw::c_double; /// Represents a Python `float` object. @@ -73,14 +75,16 @@ impl<'py> PyFloatMethods<'py> for Bound<'py, PyFloat> { } impl ToPyObject for f64 { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - PyFloat::new(py, *self).into() + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for f64 { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - PyFloat::new(py, self).into() + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] @@ -89,6 +93,28 @@ impl IntoPy for f64 { } } +impl<'py> IntoPyObject<'py> for f64 { + type Target = PyFloat; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + Ok(PyFloat::new(py, self)) + } +} + +impl<'py> IntoPyObject<'py> for &f64 { + type Target = PyFloat; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + impl<'py> FromPyObject<'py> for f64 { // PyFloat_AsDouble returns -1.0 upon failure #![allow(clippy::float_cmp)] @@ -120,14 +146,16 @@ impl<'py> FromPyObject<'py> for f64 { } impl ToPyObject for f32 { + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - PyFloat::new(py, f64::from(*self)).into() + self.into_pyobject(py).unwrap().into_any().unbind() } } impl IntoPy for f32 { + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { - PyFloat::new(py, f64::from(self)).into() + self.into_pyobject(py).unwrap().into_any().unbind() } #[cfg(feature = "experimental-inspect")] @@ -136,6 +164,28 @@ impl IntoPy for f32 { } } +impl<'py> IntoPyObject<'py> for f32 { + type Target = PyFloat; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + Ok(PyFloat::new(py, self.into())) + } +} + +impl<'py> IntoPyObject<'py> for &f32 { + type Target = PyFloat; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + #[inline] + fn into_pyobject(self, py: Python<'py>) -> Result { + (*self).into_pyobject(py) + } +} + impl<'py> FromPyObject<'py> for f32 { fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { Ok(obj.extract::()? as f32)