cpython/objects/
list.rs

1// Copyright (c) 2015 Daniel Grunwald
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19use super::object::PyObject;
20use crate::conversion::{FromPyObject, ToPyObject};
21use crate::err::{self, PyErr, PyResult};
22use crate::ffi::{self, Py_ssize_t};
23use crate::python::{PyClone, PyDrop, Python, PythonObject, ToPythonPointer};
24
25/// Represents a Python `list`.
26pub struct PyList(PyObject);
27
28pyobject_newtype!(PyList, PyList_Check, PyList_Type);
29
30impl PyList {
31    /// Construct a new list with the given elements.
32    pub fn new(py: Python, elements: &[PyObject]) -> PyList {
33        unsafe {
34            let ptr = ffi::PyList_New(elements.len() as Py_ssize_t);
35            let t = err::result_from_owned_ptr(py, ptr)
36                .unwrap()
37                .unchecked_cast_into::<PyList>();
38            for (i, e) in elements.iter().enumerate() {
39                ffi::PyList_SetItem(ptr, i as Py_ssize_t, e.steal_ptr(py));
40            }
41            t
42        }
43    }
44
45    /// Gets the length of the list.
46    #[inline]
47    pub fn len(&self, _py: Python) -> usize {
48        // non-negative Py_ssize_t should always fit into Rust usize
49        unsafe { ffi::PyList_Size(self.0.as_ptr()) as usize }
50    }
51
52    /// Gets the item at the specified index.
53    ///
54    /// Panics if the index is out of range.
55    pub fn get_item(&self, py: Python, index: usize) -> PyObject {
56        // TODO: do we really want to panic here?
57        assert!(index < self.len(py));
58        unsafe {
59            PyObject::from_borrowed_ptr(
60                py,
61                ffi::PyList_GetItem(self.0.as_ptr(), index as Py_ssize_t),
62            )
63        }
64    }
65
66    /// Sets the item at the specified index.
67    ///
68    /// Panics if the index is out of range.
69    pub fn set_item(&self, _py: Python, index: usize, item: PyObject) {
70        let r =
71            unsafe { ffi::PyList_SetItem(self.0.as_ptr(), index as Py_ssize_t, item.steal_ptr()) };
72        assert!(r == 0);
73    }
74
75    /// Inserts an item at the specified index.
76    ///
77    /// Panics if the index is out of range.
78    pub fn insert(&self, _py: Python, index: usize, item: PyObject) {
79        let r = unsafe { ffi::PyList_Insert(self.0.as_ptr(), index as Py_ssize_t, item.as_ptr()) };
80        assert!(r == 0);
81    }
82
83    // Old name for `insert`.
84    #[deprecated(since = "0.3.1", note = "use list.insert() instead")]
85    #[doc(hidden)]
86    pub fn insert_item(&self, py: Python, index: usize, item: PyObject) {
87        self.insert(py, index, item);
88    }
89
90    /// Appends an item to the end of the list
91    pub fn append(&self, _py: Python, item: PyObject) {
92        unsafe { ffi::PyList_Append(self.0.as_ptr(), item.as_ptr()) };
93    }
94
95    #[inline]
96    pub fn iter<'a, 'p>(&'a self, py: Python<'p>) -> PyListIterator<'a, 'p> {
97        PyListIterator {
98            py,
99            list: self,
100            index: 0,
101        }
102    }
103}
104
105/// Used by `PyList::iter()`.
106pub struct PyListIterator<'a, 'p> {
107    py: Python<'p>,
108    list: &'a PyList,
109    index: usize,
110}
111
112impl<'a, 'p> Iterator for PyListIterator<'a, 'p> {
113    type Item = PyObject;
114
115    #[inline]
116    fn next(&mut self) -> Option<PyObject> {
117        if self.index < self.list.len(self.py) {
118            let item = self.list.get_item(self.py, self.index);
119            self.index += 1;
120            Some(item)
121        } else {
122            None
123        }
124    }
125
126    // Note: we cannot implement size_hint because the length of the list
127    // might change during the iteration.
128}
129
130/// Converts a Rust slice to a Python `list`.
131///
132/// Note: this conversion can be inefficient since a Python object is created
133/// for each element of the list. For primitive types `T`, consider using
134/// the buffer protocol instead.
135impl<T> ToPyObject for [T]
136where
137    T: ToPyObject,
138{
139    type ObjectType = PyList;
140
141    fn to_py_object(&self, py: Python) -> PyList {
142        unsafe {
143            let ptr = ffi::PyList_New(self.len() as Py_ssize_t);
144            let t = err::cast_from_owned_ptr_or_panic(py, ptr);
145            for (i, e) in self.iter().enumerate() {
146                let obj = e.to_py_object(py).into_object();
147                ffi::PyList_SetItem(ptr, i as Py_ssize_t, obj.steal_ptr());
148            }
149            t
150        }
151    }
152}
153
154/// Converts a Rust slice to a Python `list`.
155///
156/// Note: this conversion can be inefficient since a Python object is created
157/// for each element of the list. For primitive types `T`, consider using
158/// the buffer protocol instead.
159impl<T> ToPyObject for Vec<T>
160where
161    T: ToPyObject,
162{
163    type ObjectType = PyList;
164
165    fn to_py_object(&self, py: Python) -> PyList {
166        self.as_slice().to_py_object(py)
167    }
168
169    fn into_py_object(self, py: Python) -> PyList {
170        unsafe {
171            let ptr = ffi::PyList_New(self.len() as Py_ssize_t);
172            let t = err::cast_from_owned_ptr_or_panic(py, ptr);
173            for (i, e) in self.into_iter().enumerate() {
174                let obj = e.into_py_object(py).into_object();
175                ffi::PyList_SetItem(ptr, i as Py_ssize_t, obj.steal_ptr());
176            }
177            t
178        }
179    }
180}
181
182#[cfg(test)]
183mod test {
184    use crate::conversion::ToPyObject;
185    use crate::objects::PyList;
186    use crate::python::{Python, PythonObject};
187
188    #[test]
189    fn test_len() {
190        let gil = Python::acquire_gil();
191        let py = gil.python();
192        let v = vec![1, 2, 3, 4];
193        let list = v.to_py_object(py);
194        assert_eq!(4, list.len(py));
195    }
196
197    #[test]
198    fn test_get_item() {
199        let gil = Python::acquire_gil();
200        let py = gil.python();
201        let v = vec![2, 3, 5, 7];
202        let list = v.to_py_object(py);
203        assert_eq!(2, list.get_item(py, 0).extract::<i32>(py).unwrap());
204        assert_eq!(3, list.get_item(py, 1).extract::<i32>(py).unwrap());
205        assert_eq!(5, list.get_item(py, 2).extract::<i32>(py).unwrap());
206        assert_eq!(7, list.get_item(py, 3).extract::<i32>(py).unwrap());
207    }
208
209    #[test]
210    fn test_set_item() {
211        let gil = Python::acquire_gil();
212        let py = gil.python();
213        let v = vec![2, 3, 5, 7];
214        let list = v.to_py_object(py);
215        let val = 42i32.to_py_object(py).into_object();
216        assert_eq!(2, list.get_item(py, 0).extract::<i32>(py).unwrap());
217        list.set_item(py, 0, val);
218        assert_eq!(42, list.get_item(py, 0).extract::<i32>(py).unwrap());
219    }
220
221    #[test]
222    fn test_insert() {
223        let gil = Python::acquire_gil();
224        let py = gil.python();
225        let v = vec![2, 3, 5, 7];
226        let list = v.to_py_object(py);
227        let val = 42i32.to_py_object(py).into_object();
228        assert_eq!(4, list.len(py));
229        assert_eq!(2, list.get_item(py, 0).extract::<i32>(py).unwrap());
230        list.insert(py, 0, val);
231        assert_eq!(5, list.len(py));
232        assert_eq!(42, list.get_item(py, 0).extract::<i32>(py).unwrap());
233        assert_eq!(2, list.get_item(py, 1).extract::<i32>(py).unwrap());
234    }
235
236    #[test]
237    fn test_append() {
238        let gil = Python::acquire_gil();
239        let py = gil.python();
240        let v = vec![2, 3, 5, 7];
241        let list = v.to_py_object(py);
242        let val = 42i32.to_py_object(py).into_object();
243        assert_eq!(4, list.len(py));
244        list.append(py, val);
245        assert_eq!(5, list.len(py));
246        assert_eq!(42, list.get_item(py, 4).extract::<i32>(py).unwrap());
247    }
248
249    #[test]
250    fn test_iter() {
251        let gil = Python::acquire_gil();
252        let py = gil.python();
253        let v = vec![2, 3, 5, 7];
254        let list = v.to_py_object(py);
255        let mut idx = 0;
256        for el in list.iter(py) {
257            assert_eq!(v[idx], el.extract::<i32>(py).unwrap());
258            idx += 1;
259        }
260        assert_eq!(idx, v.len());
261    }
262
263    #[test]
264    fn test_extract() {
265        let gil = Python::acquire_gil();
266        let py = gil.python();
267        let v = vec![2, 3, 5, 7];
268        let list = v.to_py_object(py);
269        let v2 = list.into_object().extract::<Vec<i32>>(py).unwrap();
270        assert_eq!(v, v2);
271    }
272}