pyo3/conversions/std/
map.rs

1use std::{cmp, collections, hash};
2
3#[cfg(feature = "experimental-inspect")]
4use crate::inspect::types::TypeInfo;
5use crate::{
6    conversion::IntoPyObject,
7    instance::Bound,
8    types::{any::PyAnyMethods, dict::PyDictMethods, PyDict},
9    FromPyObject, PyAny, PyErr, PyObject, Python,
10};
11#[allow(deprecated)]
12use crate::{IntoPy, ToPyObject};
13
14#[allow(deprecated)]
15impl<K, V, H> ToPyObject for collections::HashMap<K, V, H>
16where
17    K: hash::Hash + cmp::Eq + ToPyObject,
18    V: ToPyObject,
19    H: hash::BuildHasher,
20{
21    fn to_object(&self, py: Python<'_>) -> PyObject {
22        let dict = PyDict::new(py);
23        for (k, v) in self {
24            dict.set_item(k.to_object(py), v.to_object(py)).unwrap();
25        }
26        dict.into_any().unbind()
27    }
28}
29
30#[allow(deprecated)]
31impl<K, V> ToPyObject for collections::BTreeMap<K, V>
32where
33    K: cmp::Eq + ToPyObject,
34    V: ToPyObject,
35{
36    fn to_object(&self, py: Python<'_>) -> PyObject {
37        let dict = PyDict::new(py);
38        for (k, v) in self {
39            dict.set_item(k.to_object(py), v.to_object(py)).unwrap();
40        }
41        dict.into_any().unbind()
42    }
43}
44
45#[allow(deprecated)]
46impl<K, V, H> IntoPy<PyObject> for collections::HashMap<K, V, H>
47where
48    K: hash::Hash + cmp::Eq + IntoPy<PyObject>,
49    V: IntoPy<PyObject>,
50    H: hash::BuildHasher,
51{
52    fn into_py(self, py: Python<'_>) -> PyObject {
53        let dict = PyDict::new(py);
54        for (k, v) in self {
55            dict.set_item(k.into_py(py), v.into_py(py)).unwrap();
56        }
57        dict.into_any().unbind()
58    }
59}
60
61impl<'py, K, V, H> IntoPyObject<'py> for collections::HashMap<K, V, H>
62where
63    K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
64    V: IntoPyObject<'py>,
65    H: hash::BuildHasher,
66{
67    type Target = PyDict;
68    type Output = Bound<'py, Self::Target>;
69    type Error = PyErr;
70
71    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
72        let dict = PyDict::new(py);
73        for (k, v) in self {
74            dict.set_item(k, v)?;
75        }
76        Ok(dict)
77    }
78
79    #[cfg(feature = "experimental-inspect")]
80    fn type_output() -> TypeInfo {
81        TypeInfo::dict_of(K::type_output(), V::type_output())
82    }
83}
84
85impl<'a, 'py, K, V, H> IntoPyObject<'py> for &'a collections::HashMap<K, V, H>
86where
87    &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
88    &'a V: IntoPyObject<'py>,
89    K: 'a, // MSRV
90    V: 'a, // MSRV
91    H: hash::BuildHasher,
92{
93    type Target = PyDict;
94    type Output = Bound<'py, Self::Target>;
95    type Error = PyErr;
96
97    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
98        let dict = PyDict::new(py);
99        for (k, v) in self {
100            dict.set_item(k, v)?;
101        }
102        Ok(dict)
103    }
104
105    #[cfg(feature = "experimental-inspect")]
106    fn type_output() -> TypeInfo {
107        TypeInfo::dict_of(<&K>::type_output(), <&V>::type_output())
108    }
109}
110
111#[allow(deprecated)]
112impl<K, V> IntoPy<PyObject> for collections::BTreeMap<K, V>
113where
114    K: cmp::Eq + IntoPy<PyObject>,
115    V: IntoPy<PyObject>,
116{
117    fn into_py(self, py: Python<'_>) -> PyObject {
118        let dict = PyDict::new(py);
119        for (k, v) in self {
120            dict.set_item(k.into_py(py), v.into_py(py)).unwrap();
121        }
122        dict.into_any().unbind()
123    }
124}
125
126impl<'py, K, V> IntoPyObject<'py> for collections::BTreeMap<K, V>
127where
128    K: IntoPyObject<'py> + cmp::Eq,
129    V: IntoPyObject<'py>,
130{
131    type Target = PyDict;
132    type Output = Bound<'py, Self::Target>;
133    type Error = PyErr;
134
135    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
136        let dict = PyDict::new(py);
137        for (k, v) in self {
138            dict.set_item(k, v)?;
139        }
140        Ok(dict)
141    }
142
143    #[cfg(feature = "experimental-inspect")]
144    fn type_output() -> TypeInfo {
145        TypeInfo::dict_of(K::type_output(), V::type_output())
146    }
147}
148
149impl<'a, 'py, K, V> IntoPyObject<'py> for &'a collections::BTreeMap<K, V>
150where
151    &'a K: IntoPyObject<'py> + cmp::Eq,
152    &'a V: IntoPyObject<'py>,
153    K: 'a,
154    V: 'a,
155{
156    type Target = PyDict;
157    type Output = Bound<'py, Self::Target>;
158    type Error = PyErr;
159
160    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
161        let dict = PyDict::new(py);
162        for (k, v) in self {
163            dict.set_item(k, v)?;
164        }
165        Ok(dict)
166    }
167
168    #[cfg(feature = "experimental-inspect")]
169    fn type_output() -> TypeInfo {
170        TypeInfo::dict_of(<&K>::type_output(), <&V>::type_output())
171    }
172}
173
174impl<'py, K, V, S> FromPyObject<'py> for collections::HashMap<K, V, S>
175where
176    K: FromPyObject<'py> + cmp::Eq + hash::Hash,
177    V: FromPyObject<'py>,
178    S: hash::BuildHasher + Default,
179{
180    fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
181        let dict = ob.downcast::<PyDict>()?;
182        let mut ret = collections::HashMap::with_capacity_and_hasher(dict.len(), S::default());
183        for (k, v) in dict {
184            ret.insert(k.extract()?, v.extract()?);
185        }
186        Ok(ret)
187    }
188
189    #[cfg(feature = "experimental-inspect")]
190    fn type_input() -> TypeInfo {
191        TypeInfo::mapping_of(K::type_input(), V::type_input())
192    }
193}
194
195impl<'py, K, V> FromPyObject<'py> for collections::BTreeMap<K, V>
196where
197    K: FromPyObject<'py> + cmp::Ord,
198    V: FromPyObject<'py>,
199{
200    fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
201        let dict = ob.downcast::<PyDict>()?;
202        let mut ret = collections::BTreeMap::new();
203        for (k, v) in dict {
204            ret.insert(k.extract()?, v.extract()?);
205        }
206        Ok(ret)
207    }
208
209    #[cfg(feature = "experimental-inspect")]
210    fn type_input() -> TypeInfo {
211        TypeInfo::mapping_of(K::type_input(), V::type_input())
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218    use std::collections::{BTreeMap, HashMap};
219
220    #[test]
221    fn test_hashmap_to_python() {
222        Python::with_gil(|py| {
223            let mut map = HashMap::<i32, i32>::new();
224            map.insert(1, 1);
225
226            let py_map = (&map).into_pyobject(py).unwrap();
227
228            assert!(py_map.len() == 1);
229            assert!(
230                py_map
231                    .get_item(1)
232                    .unwrap()
233                    .unwrap()
234                    .extract::<i32>()
235                    .unwrap()
236                    == 1
237            );
238            assert_eq!(map, py_map.extract().unwrap());
239        });
240    }
241
242    #[test]
243    fn test_btreemap_to_python() {
244        Python::with_gil(|py| {
245            let mut map = BTreeMap::<i32, i32>::new();
246            map.insert(1, 1);
247
248            let py_map = (&map).into_pyobject(py).unwrap();
249
250            assert!(py_map.len() == 1);
251            assert!(
252                py_map
253                    .get_item(1)
254                    .unwrap()
255                    .unwrap()
256                    .extract::<i32>()
257                    .unwrap()
258                    == 1
259            );
260            assert_eq!(map, py_map.extract().unwrap());
261        });
262    }
263
264    #[test]
265    fn test_hashmap_into_python() {
266        Python::with_gil(|py| {
267            let mut map = HashMap::<i32, i32>::new();
268            map.insert(1, 1);
269
270            let py_map = map.into_pyobject(py).unwrap();
271
272            assert!(py_map.len() == 1);
273            assert!(
274                py_map
275                    .get_item(1)
276                    .unwrap()
277                    .unwrap()
278                    .extract::<i32>()
279                    .unwrap()
280                    == 1
281            );
282        });
283    }
284
285    #[test]
286    fn test_btreemap_into_py() {
287        Python::with_gil(|py| {
288            let mut map = BTreeMap::<i32, i32>::new();
289            map.insert(1, 1);
290
291            let py_map = map.into_pyobject(py).unwrap();
292
293            assert!(py_map.len() == 1);
294            assert!(
295                py_map
296                    .get_item(1)
297                    .unwrap()
298                    .unwrap()
299                    .extract::<i32>()
300                    .unwrap()
301                    == 1
302            );
303        });
304    }
305}