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};
9#[cfg(RustPython)]
10use crate::{
11 sync::PyOnceLock,
12 types::{PyType, PyTypeMethods},
13 Py,
14};
15use crate::{Bound, IntoPyObject, PyAny, Python};
16use core::convert::Infallible;
17
18#[repr(transparent)]
28pub struct PySlice(PyAny);
29
30#[cfg(not(RustPython))]
31pyobject_native_type!(
32 PySlice,
33 ffi::PySliceObject,
34 pyobject_native_static_type_object!(ffi::PySlice_Type),
35 "builtins",
36 "slice",
37 #checkfunction=ffi::PySlice_Check
38);
39
40#[cfg(RustPython)]
41pyobject_native_type!(
42 PySlice,
43 ffi::PySliceObject,
44 |py| {
45 static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
46 TYPE.import(py, "builtins", "slice").unwrap().as_type_ptr()
47 },
48 "builtins",
49 "slice",
50 #checkfunction=ffi::PySlice_Check
51);
52
53#[derive(Debug, Eq, PartialEq)]
55pub struct PySliceIndices {
56 pub start: isize,
60 pub stop: isize,
64 pub step: isize,
66 pub slicelength: usize,
68}
69
70impl PySliceIndices {
71 pub fn new(start: isize, stop: isize, step: isize) -> PySliceIndices {
73 PySliceIndices {
74 start,
75 stop,
76 step,
77 slicelength: 0,
78 }
79 }
80}
81
82impl PySlice {
83 pub fn new(py: Python<'_>, start: isize, stop: isize, step: isize) -> Bound<'_, PySlice> {
85 unsafe {
86 ffi::PySlice_New(
87 ffi::PyLong_FromSsize_t(start),
88 ffi::PyLong_FromSsize_t(stop),
89 ffi::PyLong_FromSsize_t(step),
90 )
91 .assume_owned(py)
92 .cast_into_unchecked()
93 }
94 }
95
96 pub fn full(py: Python<'_>) -> Bound<'_, PySlice> {
98 unsafe {
99 ffi::PySlice_New(ffi::Py_None(), ffi::Py_None(), ffi::Py_None())
100 .assume_owned(py)
101 .cast_into_unchecked()
102 }
103 }
104}
105
106#[doc(alias = "PySlice")]
112pub trait PySliceMethods<'py>: crate::sealed::Sealed {
113 fn indices(&self, length: isize) -> PyResult<PySliceIndices>;
117}
118
119impl<'py> PySliceMethods<'py> for Bound<'py, PySlice> {
120 fn indices(&self, length: isize) -> PyResult<PySliceIndices> {
121 unsafe {
122 let mut slicelength: isize = 0;
123 let mut start: isize = 0;
124 let mut stop: isize = 0;
125 let mut step: isize = 0;
126 let r = ffi::PySlice_GetIndicesEx(
127 self.as_ptr(),
128 length,
129 &mut start,
130 &mut stop,
131 &mut step,
132 &mut slicelength,
133 );
134 if r == 0 {
135 Ok(PySliceIndices {
136 start,
137 stop,
138 step,
139 slicelength: slicelength as _,
141 })
142 } else {
143 Err(PyErr::fetch(self.py()))
144 }
145 }
146 }
147}
148
149impl<'py> IntoPyObject<'py> for PySliceIndices {
150 type Target = PySlice;
151 type Output = Bound<'py, Self::Target>;
152 type Error = Infallible;
153
154 #[cfg(feature = "experimental-inspect")]
155 const OUTPUT_TYPE: PyStaticExpr = PySlice::TYPE_HINT;
156
157 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
158 Ok(PySlice::new(py, self.start, self.stop, self.step))
159 }
160}
161
162impl<'py> IntoPyObject<'py> for &PySliceIndices {
163 type Target = PySlice;
164 type Output = Bound<'py, Self::Target>;
165 type Error = Infallible;
166
167 #[cfg(feature = "experimental-inspect")]
168 const OUTPUT_TYPE: PyStaticExpr = PySlice::TYPE_HINT;
169
170 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
171 Ok(PySlice::new(py, self.start, self.stop, self.step))
172 }
173}
174
175impl<'py> TryFrom<Bound<'py, PyRange>> for Bound<'py, PySlice> {
176 type Error = PyErr;
177
178 fn try_from(range: Bound<'py, PyRange>) -> Result<Self, Self::Error> {
179 Ok(PySlice::new(
180 range.py(),
181 range.start()?,
182 range.stop()?,
183 range.step()?,
184 ))
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 use crate::types::PyAnyMethods as _;
192
193 #[test]
194 fn test_py_slice_new() {
195 Python::attach(|py| {
196 let slice = PySlice::new(py, isize::MIN, isize::MAX, 1);
197 assert_eq!(
198 slice.getattr("start").unwrap().extract::<isize>().unwrap(),
199 isize::MIN
200 );
201 assert_eq!(
202 slice.getattr("stop").unwrap().extract::<isize>().unwrap(),
203 isize::MAX
204 );
205 assert_eq!(
206 slice.getattr("step").unwrap().extract::<isize>().unwrap(),
207 1
208 );
209 });
210 }
211
212 #[test]
213 fn test_py_slice_full() {
214 Python::attach(|py| {
215 let slice = PySlice::full(py);
216 assert!(slice.getattr("start").unwrap().is_none(),);
217 assert!(slice.getattr("stop").unwrap().is_none(),);
218 assert!(slice.getattr("step").unwrap().is_none(),);
219 assert_eq!(
220 slice.indices(0).unwrap(),
221 PySliceIndices {
222 start: 0,
223 stop: 0,
224 step: 1,
225 slicelength: 0,
226 },
227 );
228 assert_eq!(
229 slice.indices(42).unwrap(),
230 PySliceIndices {
231 start: 0,
232 stop: 42,
233 step: 1,
234 slicelength: 42,
235 },
236 );
237 });
238 }
239
240 #[test]
241 fn test_py_slice_indices_new() {
242 let start = 0;
243 let stop = 0;
244 let step = 0;
245 assert_eq!(
246 PySliceIndices::new(start, stop, step),
247 PySliceIndices {
248 start,
249 stop,
250 step,
251 slicelength: 0
252 }
253 );
254
255 let start = 0;
256 let stop = 100;
257 let step = 10;
258 assert_eq!(
259 PySliceIndices::new(start, stop, step),
260 PySliceIndices {
261 start,
262 stop,
263 step,
264 slicelength: 0
265 }
266 );
267
268 let start = 0;
269 let stop = -10;
270 let step = -1;
271 assert_eq!(
272 PySliceIndices::new(start, stop, step),
273 PySliceIndices {
274 start,
275 stop,
276 step,
277 slicelength: 0
278 }
279 );
280
281 let start = 0;
282 let stop = -10;
283 let step = 20;
284 assert_eq!(
285 PySliceIndices::new(start, stop, step),
286 PySliceIndices {
287 start,
288 stop,
289 step,
290 slicelength: 0
291 }
292 );
293 }
294}