1use crate::err::{PyErr, PyResult};
2use crate::ffi;
3use crate::ffi_ptr_ext::FfiPtrExt;
4use crate::types::any::PyAnyMethods;
5#[allow(deprecated)]
6use crate::ToPyObject;
7use crate::{Bound, IntoPyObject, PyAny, PyObject, Python};
8use std::convert::Infallible;
9
10#[repr(transparent)]
20pub struct PySlice(PyAny);
21
22pyobject_native_type!(
23 PySlice,
24 ffi::PySliceObject,
25 pyobject_native_static_type_object!(ffi::PySlice_Type),
26 #checkfunction=ffi::PySlice_Check
27);
28
29#[derive(Debug, Eq, PartialEq)]
31pub struct PySliceIndices {
32 pub start: isize,
36 pub stop: isize,
40 pub step: isize,
42 pub slicelength: usize,
44}
45
46impl PySliceIndices {
47 pub fn new(start: isize, stop: isize, step: isize) -> PySliceIndices {
49 PySliceIndices {
50 start,
51 stop,
52 step,
53 slicelength: 0,
54 }
55 }
56}
57
58impl PySlice {
59 pub fn new(py: Python<'_>, start: isize, stop: isize, step: isize) -> Bound<'_, PySlice> {
61 unsafe {
62 ffi::PySlice_New(
63 ffi::PyLong_FromSsize_t(start),
64 ffi::PyLong_FromSsize_t(stop),
65 ffi::PyLong_FromSsize_t(step),
66 )
67 .assume_owned(py)
68 .downcast_into_unchecked()
69 }
70 }
71
72 #[deprecated(since = "0.23.0", note = "renamed to `PySlice::new`")]
74 #[inline]
75 pub fn new_bound(py: Python<'_>, start: isize, stop: isize, step: isize) -> Bound<'_, PySlice> {
76 Self::new(py, start, stop, step)
77 }
78
79 pub fn full(py: Python<'_>) -> Bound<'_, PySlice> {
81 unsafe {
82 ffi::PySlice_New(ffi::Py_None(), ffi::Py_None(), ffi::Py_None())
83 .assume_owned(py)
84 .downcast_into_unchecked()
85 }
86 }
87
88 #[deprecated(since = "0.23.0", note = "renamed to `PySlice::full`")]
90 #[inline]
91 pub fn full_bound(py: Python<'_>) -> Bound<'_, PySlice> {
92 Self::full(py)
93 }
94}
95
96#[doc(alias = "PySlice")]
102pub trait PySliceMethods<'py>: crate::sealed::Sealed {
103 fn indices(&self, length: isize) -> PyResult<PySliceIndices>;
107}
108
109impl<'py> PySliceMethods<'py> for Bound<'py, PySlice> {
110 fn indices(&self, length: isize) -> PyResult<PySliceIndices> {
111 unsafe {
112 let mut slicelength: isize = 0;
113 let mut start: isize = 0;
114 let mut stop: isize = 0;
115 let mut step: isize = 0;
116 let r = ffi::PySlice_GetIndicesEx(
117 self.as_ptr(),
118 length,
119 &mut start,
120 &mut stop,
121 &mut step,
122 &mut slicelength,
123 );
124 if r == 0 {
125 Ok(PySliceIndices {
126 start,
127 stop,
128 step,
129 slicelength: slicelength as _,
131 })
132 } else {
133 Err(PyErr::fetch(self.py()))
134 }
135 }
136 }
137}
138
139#[allow(deprecated)]
140impl ToPyObject for PySliceIndices {
141 fn to_object(&self, py: Python<'_>) -> PyObject {
142 PySlice::new(py, self.start, self.stop, self.step).into()
143 }
144}
145
146impl<'py> IntoPyObject<'py> for PySliceIndices {
147 type Target = PySlice;
148 type Output = Bound<'py, Self::Target>;
149 type Error = Infallible;
150
151 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
152 Ok(PySlice::new(py, self.start, self.stop, self.step))
153 }
154}
155
156impl<'py> IntoPyObject<'py> for &PySliceIndices {
157 type Target = PySlice;
158 type Output = Bound<'py, Self::Target>;
159 type Error = Infallible;
160
161 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
162 Ok(PySlice::new(py, self.start, self.stop, self.step))
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn test_py_slice_new() {
172 Python::with_gil(|py| {
173 let slice = PySlice::new(py, isize::MIN, isize::MAX, 1);
174 assert_eq!(
175 slice.getattr("start").unwrap().extract::<isize>().unwrap(),
176 isize::MIN
177 );
178 assert_eq!(
179 slice.getattr("stop").unwrap().extract::<isize>().unwrap(),
180 isize::MAX
181 );
182 assert_eq!(
183 slice.getattr("step").unwrap().extract::<isize>().unwrap(),
184 1
185 );
186 });
187 }
188
189 #[test]
190 fn test_py_slice_full() {
191 Python::with_gil(|py| {
192 let slice = PySlice::full(py);
193 assert!(slice.getattr("start").unwrap().is_none(),);
194 assert!(slice.getattr("stop").unwrap().is_none(),);
195 assert!(slice.getattr("step").unwrap().is_none(),);
196 assert_eq!(
197 slice.indices(0).unwrap(),
198 PySliceIndices {
199 start: 0,
200 stop: 0,
201 step: 1,
202 slicelength: 0,
203 },
204 );
205 assert_eq!(
206 slice.indices(42).unwrap(),
207 PySliceIndices {
208 start: 0,
209 stop: 42,
210 step: 1,
211 slicelength: 42,
212 },
213 );
214 });
215 }
216
217 #[test]
218 fn test_py_slice_indices_new() {
219 let start = 0;
220 let stop = 0;
221 let step = 0;
222 assert_eq!(
223 PySliceIndices::new(start, stop, step),
224 PySliceIndices {
225 start,
226 stop,
227 step,
228 slicelength: 0
229 }
230 );
231
232 let start = 0;
233 let stop = 100;
234 let step = 10;
235 assert_eq!(
236 PySliceIndices::new(start, stop, step),
237 PySliceIndices {
238 start,
239 stop,
240 step,
241 slicelength: 0
242 }
243 );
244
245 let start = 0;
246 let stop = -10;
247 let step = -1;
248 assert_eq!(
249 PySliceIndices::new(start, stop, step),
250 PySliceIndices {
251 start,
252 stop,
253 step,
254 slicelength: 0
255 }
256 );
257
258 let start = 0;
259 let stop = -10;
260 let step = 20;
261 assert_eq!(
262 PySliceIndices::new(start, stop, step),
263 PySliceIndices {
264 start,
265 stop,
266 step,
267 slicelength: 0
268 }
269 );
270 }
271}