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