From 61135b715440a137cfc78e1fa5308f9241960d15 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 22 May 2025 08:59:42 -0700 Subject: [PATCH 1/4] fix: move 'py lifetimes --- Cargo.toml | 2 +- src/ser.rs | 34 +++++++++++++++++----------------- tests/test_custom_types.rs | 12 ++++++------ 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b9d4df2..d636384 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "pythonize" version = "0.25.0" authors = ["David Hewitt <1939362+davidhewitt@users.noreply.github.com>"] edition = "2021" -rust-version = "1.63" +rust-version = "1.65" license = "MIT" description = "Serde Serializer & Deserializer from Rust <--> Python, backed by PyO3." homepage = "https://github.com/davidhewitt/pythonize" diff --git a/src/ser.rs b/src/ser.rs index 513a8e2..8fde104 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -11,22 +11,22 @@ use crate::error::{PythonizeError, Result}; // TODO: move 'py lifetime into builder once GATs are available in MSRV /// Trait for types which can represent a Python mapping -pub trait PythonizeMappingType<'py> { +pub trait PythonizeMappingType { /// Builder type for Python mappings - type Builder; + type Builder<'py>; /// Create a builder for a Python mapping - fn builder(py: Python<'py>, len: Option) -> PyResult; + fn builder<'py>(py: Python<'py>, len: Option) -> PyResult>; /// Adds the key-value item to the mapping being built - fn push_item( - builder: &mut Self::Builder, + fn push_item<'py>( + builder: &mut Self::Builder<'py>, key: Bound<'py, PyAny>, value: Bound<'py, PyAny>, ) -> PyResult<()>; /// Build the Python mapping - fn finish(builder: Self::Builder) -> PyResult>; + fn finish<'py>(builder: Self::Builder<'py>) -> PyResult>; } // TODO: move 'py lifetime into builder once GATs are available in MSRV @@ -65,29 +65,29 @@ pub trait PythonizeListType: Sized { /// Custom types for serialization pub trait PythonizeTypes<'py> { /// Python map type (should be representable as python mapping) - type Map: PythonizeMappingType<'py>; + type Map: PythonizeMappingType; /// Python (struct-like) named map type (should be representable as python mapping) type NamedMap: PythonizeNamedMappingType<'py>; /// Python sequence type (should be representable as python sequence) type List: PythonizeListType; } -impl<'py> PythonizeMappingType<'py> for PyDict { - type Builder = Bound<'py, Self>; +impl PythonizeMappingType for PyDict { + type Builder<'py> = Bound<'py, Self>; - fn builder(py: Python<'py>, _len: Option) -> PyResult { + fn builder<'py>(py: Python<'py>, _len: Option) -> PyResult> { Ok(Self::new(py)) } - fn push_item( - builder: &mut Self::Builder, + fn push_item<'py>( + builder: &mut Self::Builder<'py>, key: Bound<'py, PyAny>, value: Bound<'py, PyAny>, ) -> PyResult<()> { builder.set_item(key, value) } - fn finish(builder: Self::Builder) -> PyResult> { + fn finish<'py>(builder: Self::Builder<'py>) -> PyResult> { Ok(builder.into_mapping()) } } @@ -99,15 +99,15 @@ impl<'py> PythonizeMappingType<'py> for PyDict { /// This adapter is commonly applied to use the same unnamed mapping type for /// both [`PythonizeTypes::Map`] and [`PythonizeTypes::NamedMap`] while only /// implementing [`PythonizeMappingType`]. -pub struct PythonizeUnnamedMappingAdapter<'py, T: PythonizeMappingType<'py>> { +pub struct PythonizeUnnamedMappingAdapter<'py, T: PythonizeMappingType> { _unnamed: T, _marker: PhantomData<&'py ()>, } -impl<'py, T: PythonizeMappingType<'py>> PythonizeNamedMappingType<'py> +impl<'py, T: PythonizeMappingType> PythonizeNamedMappingType<'py> for PythonizeUnnamedMappingAdapter<'py, T> { - type Builder = >::Builder; + type Builder = ::Builder<'py>; fn builder(py: Python<'py>, len: usize, _name: &'static str) -> PyResult { ::builder(py, Some(len)) @@ -237,7 +237,7 @@ pub struct PythonStructDictSerializer<'py, P: PythonizeTypes<'py>> { #[doc(hidden)] pub struct PythonMapSerializer<'py, P: PythonizeTypes<'py>> { py: Python<'py>, - builder: >::Builder, + builder: ::Builder<'py>, key: Option>, _types: PhantomData

, } diff --git a/tests/test_custom_types.rs b/tests/test_custom_types.rs index d311c14..d3d3299 100644 --- a/tests/test_custom_types.rs +++ b/tests/test_custom_types.rs @@ -107,10 +107,10 @@ impl CustomDict { } } -impl<'py> PythonizeMappingType<'py> for CustomDict { - type Builder = Bound<'py, CustomDict>; +impl PythonizeMappingType for CustomDict { + type Builder<'py> = Bound<'py, CustomDict>; - fn builder(py: Python<'py>, len: Option) -> PyResult { + fn builder<'py>(py: Python<'py>, len: Option) -> PyResult> { Bound::new( py, CustomDict { @@ -119,15 +119,15 @@ impl<'py> PythonizeMappingType<'py> for CustomDict { ) } - fn push_item( - builder: &mut Self::Builder, + fn push_item<'py>( + builder: &mut Self::Builder<'py>, key: Bound<'py, PyAny>, value: Bound<'py, PyAny>, ) -> PyResult<()> { unsafe { builder.downcast_unchecked::() }.set_item(key, value) } - fn finish(builder: Self::Builder) -> PyResult> { + fn finish<'py>(builder: Self::Builder<'py>) -> PyResult> { Ok(unsafe { builder.into_any().downcast_into_unchecked() }) } } From 9b56559ba46a2dd55ab5c080c285696ed7639f3e Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 27 May 2025 12:49:51 -0700 Subject: [PATCH 2/4] did I gat it right? --- src/ser.rs | 87 ++++++++++++++-------------- tests/test_custom_types.rs | 26 +++++---- tests/test_with_serde_path_to_err.rs | 4 +- 3 files changed, 61 insertions(+), 56 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index 8fde104..c5fcdb6 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -13,7 +13,7 @@ use crate::error::{PythonizeError, Result}; /// Trait for types which can represent a Python mapping pub trait PythonizeMappingType { /// Builder type for Python mappings - type Builder<'py>; + type Builder<'py>: 'py; /// Create a builder for a Python mapping fn builder<'py>(py: Python<'py>, len: Option) -> PyResult>; @@ -31,22 +31,26 @@ pub trait PythonizeMappingType { // TODO: move 'py lifetime into builder once GATs are available in MSRV /// Trait for types which can represent a Python mapping and have a name -pub trait PythonizeNamedMappingType<'py> { +pub trait PythonizeNamedMappingType { /// Builder type for Python mappings with a name - type Builder; + type Builder<'py>: 'py; /// Create a builder for a Python mapping with a name - fn builder(py: Python<'py>, len: usize, name: &'static str) -> PyResult; + fn builder<'py>( + py: Python<'py>, + len: usize, + name: &'static str, + ) -> PyResult>; /// Adds the field to the named mapping being built - fn push_field( - builder: &mut Self::Builder, + fn push_field<'py>( + builder: &mut Self::Builder<'py>, name: Bound<'py, PyString>, value: Bound<'py, PyAny>, ) -> PyResult<()>; /// Build the Python mapping - fn finish(builder: Self::Builder) -> PyResult>; + fn finish<'py>(builder: Self::Builder<'py>) -> PyResult>; } /// Trait for types which can represent a Python sequence @@ -63,11 +67,11 @@ pub trait PythonizeListType: Sized { // TODO: remove 'py lifetime once GATs are available in MSRV /// Custom types for serialization -pub trait PythonizeTypes<'py> { +pub trait PythonizeTypes { /// Python map type (should be representable as python mapping) type Map: PythonizeMappingType; /// Python (struct-like) named map type (should be representable as python mapping) - type NamedMap: PythonizeNamedMappingType<'py>; + type NamedMap: PythonizeNamedMappingType; /// Python sequence type (should be representable as python sequence) type List: PythonizeListType; } @@ -98,31 +102,32 @@ impl PythonizeMappingType for PyDict { /// /// This adapter is commonly applied to use the same unnamed mapping type for /// both [`PythonizeTypes::Map`] and [`PythonizeTypes::NamedMap`] while only -/// implementing [`PythonizeMappingType`]. -pub struct PythonizeUnnamedMappingAdapter<'py, T: PythonizeMappingType> { +// /// implementing [`PythonizeMappingType`]. +pub struct PythonizeUnnamedMappingAdapter { _unnamed: T, - _marker: PhantomData<&'py ()>, } -impl<'py, T: PythonizeMappingType> PythonizeNamedMappingType<'py> - for PythonizeUnnamedMappingAdapter<'py, T> -{ - type Builder = ::Builder<'py>; +impl PythonizeNamedMappingType for PythonizeUnnamedMappingAdapter { + type Builder<'py> = T::Builder<'py>; - fn builder(py: Python<'py>, len: usize, _name: &'static str) -> PyResult { - ::builder(py, Some(len)) + fn builder<'py>( + py: Python<'py>, + len: usize, + _name: &'static str, + ) -> PyResult> { + T::builder(py, Some(len)) } - fn push_field( - builder: &mut Self::Builder, + fn push_field<'py>( + builder: &mut Self::Builder<'py>, name: Bound<'py, PyString>, value: Bound<'py, PyAny>, ) -> PyResult<()> { - ::push_item(builder, name.into_any(), value) + T::push_item(builder, name.into_any(), value) } - fn finish(builder: Self::Builder) -> PyResult> { - ::finish(builder) + fn finish<'py>(builder: Self::Builder<'py>) -> PyResult> { + T::finish(builder) } } @@ -154,9 +159,9 @@ impl PythonizeListType for PyTuple { pub struct PythonizeDefault; -impl<'py> PythonizeTypes<'py> for PythonizeDefault { +impl PythonizeTypes for PythonizeDefault { type Map = PyDict; - type NamedMap = PythonizeUnnamedMappingAdapter<'py, PyDict>; + type NamedMap = PythonizeUnnamedMappingAdapter; type List = PyList; } @@ -173,7 +178,7 @@ where pub fn pythonize_custom<'py, P, T>(py: Python<'py>, value: &T) -> Result> where T: ?Sized + Serialize, - P: PythonizeTypes<'py>, + P: PythonizeTypes, { value.serialize(Pythonizer::custom::

(py)) } @@ -221,28 +226,28 @@ pub struct PythonTupleVariantSerializer<'py, P> { } #[doc(hidden)] -pub struct PythonStructVariantSerializer<'py, P: PythonizeTypes<'py>> { +pub struct PythonStructVariantSerializer<'py, P: PythonizeTypes> { name: &'static str, variant: &'static str, inner: PythonStructDictSerializer<'py, P>, } #[doc(hidden)] -pub struct PythonStructDictSerializer<'py, P: PythonizeTypes<'py>> { +pub struct PythonStructDictSerializer<'py, P: PythonizeTypes> { py: Python<'py>, - builder: >::Builder, + builder: ::Builder<'py>, _types: PhantomData

, } #[doc(hidden)] -pub struct PythonMapSerializer<'py, P: PythonizeTypes<'py>> { +pub struct PythonMapSerializer<'py, P: PythonizeTypes> { py: Python<'py>, builder: ::Builder<'py>, key: Option>, _types: PhantomData

, } -impl<'py, P: PythonizeTypes<'py>> Pythonizer<'py, P> { +impl<'py, P: PythonizeTypes> Pythonizer<'py, P> { /// The default implementation for serialisation functions. #[inline] fn serialise_default(self, v: T) -> Result> @@ -256,7 +261,7 @@ impl<'py, P: PythonizeTypes<'py>> Pythonizer<'py, P> { } } -impl<'py, P: PythonizeTypes<'py>> ser::Serializer for Pythonizer<'py, P> { +impl<'py, P: PythonizeTypes> ser::Serializer for Pythonizer<'py, P> { type Ok = Bound<'py, PyAny>; type Error = PythonizeError; type SerializeSeq = PythonCollectionSerializer<'py, P>; @@ -464,7 +469,7 @@ impl<'py, P: PythonizeTypes<'py>> ser::Serializer for Pythonizer<'py, P> { } } -impl<'py, P: PythonizeTypes<'py>> ser::SerializeSeq for PythonCollectionSerializer<'py, P> { +impl<'py, P: PythonizeTypes> ser::SerializeSeq for PythonCollectionSerializer<'py, P> { type Ok = Bound<'py, PyAny>; type Error = PythonizeError; @@ -482,7 +487,7 @@ impl<'py, P: PythonizeTypes<'py>> ser::SerializeSeq for PythonCollectionSerializ } } -impl<'py, P: PythonizeTypes<'py>> ser::SerializeTuple for PythonCollectionSerializer<'py, P> { +impl<'py, P: PythonizeTypes> ser::SerializeTuple for PythonCollectionSerializer<'py, P> { type Ok = Bound<'py, PyAny>; type Error = PythonizeError; @@ -498,7 +503,7 @@ impl<'py, P: PythonizeTypes<'py>> ser::SerializeTuple for PythonCollectionSerial } } -impl<'py, P: PythonizeTypes<'py>> ser::SerializeTupleStruct for PythonCollectionSerializer<'py, P> { +impl<'py, P: PythonizeTypes> ser::SerializeTupleStruct for PythonCollectionSerializer<'py, P> { type Ok = Bound<'py, PyAny>; type Error = PythonizeError; @@ -514,9 +519,7 @@ impl<'py, P: PythonizeTypes<'py>> ser::SerializeTupleStruct for PythonCollection } } -impl<'py, P: PythonizeTypes<'py>> ser::SerializeTupleVariant - for PythonTupleVariantSerializer<'py, P> -{ +impl<'py, P: PythonizeTypes> ser::SerializeTupleVariant for PythonTupleVariantSerializer<'py, P> { type Ok = Bound<'py, PyAny>; type Error = PythonizeError; @@ -538,7 +541,7 @@ impl<'py, P: PythonizeTypes<'py>> ser::SerializeTupleVariant } } -impl<'py, P: PythonizeTypes<'py>> ser::SerializeMap for PythonMapSerializer<'py, P> { +impl<'py, P: PythonizeTypes> ser::SerializeMap for PythonMapSerializer<'py, P> { type Ok = Bound<'py, PyAny>; type Error = PythonizeError; @@ -569,7 +572,7 @@ impl<'py, P: PythonizeTypes<'py>> ser::SerializeMap for PythonMapSerializer<'py, } } -impl<'py, P: PythonizeTypes<'py>> ser::SerializeStruct for PythonStructDictSerializer<'py, P> { +impl<'py, P: PythonizeTypes> ser::SerializeStruct for PythonStructDictSerializer<'py, P> { type Ok = Bound<'py, PyAny>; type Error = PythonizeError; @@ -590,9 +593,7 @@ impl<'py, P: PythonizeTypes<'py>> ser::SerializeStruct for PythonStructDictSeria } } -impl<'py, P: PythonizeTypes<'py>> ser::SerializeStructVariant - for PythonStructVariantSerializer<'py, P> -{ +impl<'py, P: PythonizeTypes> ser::SerializeStructVariant for PythonStructVariantSerializer<'py, P> { type Ok = Bound<'py, PyAny>; type Error = PythonizeError; diff --git a/tests/test_custom_types.rs b/tests/test_custom_types.rs index d3d3299..32c5768 100644 --- a/tests/test_custom_types.rs +++ b/tests/test_custom_types.rs @@ -58,9 +58,9 @@ impl PythonizeListType for CustomList { } struct PythonizeCustomList; -impl<'py> PythonizeTypes<'py> for PythonizeCustomList { +impl<'py> PythonizeTypes for PythonizeCustomList { type Map = PyDict; - type NamedMap = PythonizeUnnamedMappingAdapter<'py, PyDict>; + type NamedMap = PythonizeUnnamedMappingAdapter; type List = CustomList; } @@ -133,9 +133,9 @@ impl PythonizeMappingType for CustomDict { } struct PythonizeCustomDict; -impl<'py> PythonizeTypes<'py> for PythonizeCustomDict { +impl<'py> PythonizeTypes for PythonizeCustomDict { type Map = CustomDict; - type NamedMap = PythonizeUnnamedMappingAdapter<'py, CustomDict>; + type NamedMap = PythonizeUnnamedMappingAdapter; type List = PyTuple; } @@ -215,10 +215,14 @@ impl NamedCustomDict { } } -impl<'py> PythonizeNamedMappingType<'py> for NamedCustomDict { - type Builder = Bound<'py, NamedCustomDict>; +impl PythonizeNamedMappingType for NamedCustomDict { + type Builder<'py> = Bound<'py, NamedCustomDict>; - fn builder(py: Python<'py>, len: usize, name: &'static str) -> PyResult { + fn builder<'py>( + py: Python<'py>, + len: usize, + name: &'static str, + ) -> PyResult> { Bound::new( py, NamedCustomDict { @@ -228,21 +232,21 @@ impl<'py> PythonizeNamedMappingType<'py> for NamedCustomDict { ) } - fn push_field( - builder: &mut Self::Builder, + fn push_field<'py>( + builder: &mut Self::Builder<'py>, name: Bound<'py, pyo3::types::PyString>, value: Bound<'py, PyAny>, ) -> PyResult<()> { unsafe { builder.downcast_unchecked::() }.set_item(name, value) } - fn finish(builder: Self::Builder) -> PyResult> { + fn finish<'py>(builder: Self::Builder<'py>) -> PyResult> { Ok(unsafe { builder.into_any().downcast_into_unchecked() }) } } struct PythonizeNamedCustomDict; -impl<'py> PythonizeTypes<'py> for PythonizeNamedCustomDict { +impl<'py> PythonizeTypes for PythonizeNamedCustomDict { type Map = CustomDict; type NamedMap = NamedCustomDict; type List = PyTuple; diff --git a/tests/test_with_serde_path_to_err.rs b/tests/test_with_serde_path_to_err.rs index 5f2b970..9c3b688 100644 --- a/tests/test_with_serde_path_to_err.rs +++ b/tests/test_with_serde_path_to_err.rs @@ -13,9 +13,9 @@ struct Root { root_map: BTreeMap>, } -impl<'py, T> PythonizeTypes<'py> for Root { +impl<'py, T> PythonizeTypes for Root { type Map = PyDict; - type NamedMap = PythonizeUnnamedMappingAdapter<'py, PyDict>; + type NamedMap = PythonizeUnnamedMappingAdapter; type List = PyList; } From 51dac8ba2a29f85c168825ebb011311781d94168 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 27 May 2025 12:50:26 -0700 Subject: [PATCH 3/4] remove todos? --- src/ser.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index c5fcdb6..a0dc89d 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -9,7 +9,6 @@ use serde::{ser, Serialize}; use crate::error::{PythonizeError, Result}; -// TODO: move 'py lifetime into builder once GATs are available in MSRV /// Trait for types which can represent a Python mapping pub trait PythonizeMappingType { /// Builder type for Python mappings @@ -29,7 +28,6 @@ pub trait PythonizeMappingType { fn finish<'py>(builder: Self::Builder<'py>) -> PyResult>; } -// TODO: move 'py lifetime into builder once GATs are available in MSRV /// Trait for types which can represent a Python mapping and have a name pub trait PythonizeNamedMappingType { /// Builder type for Python mappings with a name @@ -65,7 +63,6 @@ pub trait PythonizeListType: Sized { U: ExactSizeIterator; } -// TODO: remove 'py lifetime once GATs are available in MSRV /// Custom types for serialization pub trait PythonizeTypes { /// Python map type (should be representable as python mapping) From 77dddae721f6e5b4553c642f5a76e28800283ca1 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 27 May 2025 12:52:56 -0700 Subject: [PATCH 4/4] fix comment --- src/ser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ser.rs b/src/ser.rs index a0dc89d..dce22e3 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -99,7 +99,7 @@ impl PythonizeMappingType for PyDict { /// /// This adapter is commonly applied to use the same unnamed mapping type for /// both [`PythonizeTypes::Map`] and [`PythonizeTypes::NamedMap`] while only -// /// implementing [`PythonizeMappingType`]. +/// implementing [`PythonizeMappingType`]. pub struct PythonizeUnnamedMappingAdapter { _unnamed: T, }