pyo3/conversions/std/
set.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::{
9        any::PyAnyMethods,
10        frozenset::PyFrozenSetMethods,
11        set::{new_from_iter, try_new_from_iter, PySetMethods},
12        PyFrozenSet, PySet,
13    },
14    FromPyObject, PyAny, PyErr, PyObject, PyResult, Python,
15};
16#[allow(deprecated)]
17use crate::{IntoPy, ToPyObject};
18
19#[allow(deprecated)]
20impl<T, S> ToPyObject for collections::HashSet<T, S>
21where
22    T: hash::Hash + Eq + ToPyObject,
23    S: hash::BuildHasher + Default,
24{
25    fn to_object(&self, py: Python<'_>) -> PyObject {
26        new_from_iter(py, self)
27            .expect("Failed to create Python set from HashSet")
28            .into()
29    }
30}
31
32#[allow(deprecated)]
33impl<T> ToPyObject for collections::BTreeSet<T>
34where
35    T: hash::Hash + Eq + ToPyObject,
36{
37    fn to_object(&self, py: Python<'_>) -> PyObject {
38        new_from_iter(py, self)
39            .expect("Failed to create Python set from BTreeSet")
40            .into()
41    }
42}
43
44#[allow(deprecated)]
45impl<K, S> IntoPy<PyObject> for collections::HashSet<K, S>
46where
47    K: IntoPy<PyObject> + Eq + hash::Hash,
48    S: hash::BuildHasher + Default,
49{
50    fn into_py(self, py: Python<'_>) -> PyObject {
51        new_from_iter(py, self.into_iter().map(|item| item.into_py(py)))
52            .expect("Failed to create Python set from HashSet")
53            .into()
54    }
55}
56
57impl<'py, K, S> IntoPyObject<'py> for collections::HashSet<K, S>
58where
59    K: IntoPyObject<'py> + Eq + hash::Hash,
60    S: hash::BuildHasher + Default,
61{
62    type Target = PySet;
63    type Output = Bound<'py, Self::Target>;
64    type Error = PyErr;
65
66    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
67        try_new_from_iter(py, self)
68    }
69
70    #[cfg(feature = "experimental-inspect")]
71    fn type_output() -> TypeInfo {
72        TypeInfo::set_of(K::type_output())
73    }
74}
75
76impl<'a, 'py, K, H> IntoPyObject<'py> for &'a collections::HashSet<K, H>
77where
78    &'a K: IntoPyObject<'py> + Eq + hash::Hash,
79    K: 'a, // MSRV
80    H: hash::BuildHasher,
81{
82    type Target = PySet;
83    type Output = Bound<'py, Self::Target>;
84    type Error = PyErr;
85
86    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
87        try_new_from_iter(py, self.iter())
88    }
89
90    #[cfg(feature = "experimental-inspect")]
91    fn type_output() -> TypeInfo {
92        TypeInfo::set_of(<&K>::type_output())
93    }
94}
95
96impl<'py, K, S> FromPyObject<'py> for collections::HashSet<K, S>
97where
98    K: FromPyObject<'py> + cmp::Eq + hash::Hash,
99    S: hash::BuildHasher + Default,
100{
101    fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
102        match ob.downcast::<PySet>() {
103            Ok(set) => set.iter().map(|any| any.extract()).collect(),
104            Err(err) => {
105                if let Ok(frozen_set) = ob.downcast::<PyFrozenSet>() {
106                    frozen_set.iter().map(|any| any.extract()).collect()
107                } else {
108                    Err(PyErr::from(err))
109                }
110            }
111        }
112    }
113
114    #[cfg(feature = "experimental-inspect")]
115    fn type_input() -> TypeInfo {
116        TypeInfo::set_of(K::type_input())
117    }
118}
119
120#[allow(deprecated)]
121impl<K> IntoPy<PyObject> for collections::BTreeSet<K>
122where
123    K: IntoPy<PyObject> + cmp::Ord,
124{
125    fn into_py(self, py: Python<'_>) -> PyObject {
126        new_from_iter(py, self.into_iter().map(|item| item.into_py(py)))
127            .expect("Failed to create Python set from BTreeSet")
128            .into()
129    }
130}
131
132impl<'py, K> IntoPyObject<'py> for collections::BTreeSet<K>
133where
134    K: IntoPyObject<'py> + cmp::Ord,
135{
136    type Target = PySet;
137    type Output = Bound<'py, Self::Target>;
138    type Error = PyErr;
139
140    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
141        try_new_from_iter(py, self)
142    }
143
144    #[cfg(feature = "experimental-inspect")]
145    fn type_output() -> TypeInfo {
146        TypeInfo::set_of(K::type_output())
147    }
148}
149
150impl<'a, 'py, K> IntoPyObject<'py> for &'a collections::BTreeSet<K>
151where
152    &'a K: IntoPyObject<'py> + cmp::Ord,
153    K: 'a,
154{
155    type Target = PySet;
156    type Output = Bound<'py, Self::Target>;
157    type Error = PyErr;
158
159    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
160        try_new_from_iter(py, self.iter())
161    }
162
163    #[cfg(feature = "experimental-inspect")]
164    fn type_output() -> TypeInfo {
165        TypeInfo::set_of(<&K>::type_output())
166    }
167}
168
169impl<'py, K> FromPyObject<'py> for collections::BTreeSet<K>
170where
171    K: FromPyObject<'py> + cmp::Ord,
172{
173    fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
174        match ob.downcast::<PySet>() {
175            Ok(set) => set.iter().map(|any| any.extract()).collect(),
176            Err(err) => {
177                if let Ok(frozen_set) = ob.downcast::<PyFrozenSet>() {
178                    frozen_set.iter().map(|any| any.extract()).collect()
179                } else {
180                    Err(PyErr::from(err))
181                }
182            }
183        }
184    }
185
186    #[cfg(feature = "experimental-inspect")]
187    fn type_input() -> TypeInfo {
188        TypeInfo::set_of(K::type_input())
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use crate::types::{any::PyAnyMethods, PyFrozenSet, PySet};
195    use crate::{IntoPyObject, PyObject, Python};
196    use std::collections::{BTreeSet, HashSet};
197
198    #[test]
199    fn test_extract_hashset() {
200        Python::with_gil(|py| {
201            let set = PySet::new(py, [1, 2, 3, 4, 5]).unwrap();
202            let hash_set: HashSet<usize> = set.extract().unwrap();
203            assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
204
205            let set = PyFrozenSet::new(py, [1, 2, 3, 4, 5]).unwrap();
206            let hash_set: HashSet<usize> = set.extract().unwrap();
207            assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
208        });
209    }
210
211    #[test]
212    fn test_extract_btreeset() {
213        Python::with_gil(|py| {
214            let set = PySet::new(py, [1, 2, 3, 4, 5]).unwrap();
215            let hash_set: BTreeSet<usize> = set.extract().unwrap();
216            assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
217
218            let set = PyFrozenSet::new(py, [1, 2, 3, 4, 5]).unwrap();
219            let hash_set: BTreeSet<usize> = set.extract().unwrap();
220            assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
221        });
222    }
223
224    #[test]
225    #[allow(deprecated)]
226    fn test_set_into_py() {
227        use crate::IntoPy;
228        Python::with_gil(|py| {
229            let bt: BTreeSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
230            let hs: HashSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
231
232            let bto: PyObject = bt.clone().into_py(py);
233            let hso: PyObject = hs.clone().into_py(py);
234
235            assert_eq!(bt, bto.extract(py).unwrap());
236            assert_eq!(hs, hso.extract(py).unwrap());
237        });
238    }
239
240    #[test]
241    fn test_set_into_pyobject() {
242        Python::with_gil(|py| {
243            let bt: BTreeSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
244            let hs: HashSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
245
246            let bto = (&bt).into_pyobject(py).unwrap();
247            let hso = (&hs).into_pyobject(py).unwrap();
248
249            assert_eq!(bt, bto.extract().unwrap());
250            assert_eq!(hs, hso.extract().unwrap());
251        });
252    }
253}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here