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