pyo3/types/
range.rs

1use crate::sealed::Sealed;
2use crate::types::PyAnyMethods;
3use crate::{ffi, Bound, PyAny, PyResult, PyTypeInfo, Python};
4
5/// Represents a Python `range`.
6///
7/// Values of this type are accessed via PyO3's smart pointers, e.g. as
8/// [`Py<PyRange>`][crate::Py] or [`Bound<'py, PyRange>`][Bound].
9///
10/// For APIs available on `range` objects, see the [`PyRangeMethods`] trait which is implemented for
11/// [`Bound<'py, PyRange>`][Bound].
12#[repr(transparent)]
13pub struct PyRange(PyAny);
14
15pyobject_native_type_core!(PyRange, pyobject_native_static_type_object!(ffi::PyRange_Type), #checkfunction=ffi::PyRange_Check);
16
17impl<'py> PyRange {
18    /// Creates a new Python `range` object with a default step of 1.
19    pub fn new(py: Python<'py>, start: isize, stop: isize) -> PyResult<Bound<'py, Self>> {
20        Self::new_with_step(py, start, stop, 1)
21    }
22
23    /// Creates a new Python `range` object with a specified step.
24    pub fn new_with_step(
25        py: Python<'py>,
26        start: isize,
27        stop: isize,
28        step: isize,
29    ) -> PyResult<Bound<'py, Self>> {
30        unsafe {
31            Ok(Self::type_object(py)
32                .call1((start, stop, step))?
33                .downcast_into_unchecked())
34        }
35    }
36}
37
38/// Implementation of functionality for [`PyRange`].
39///
40/// These methods are defined for the `Bound<'py, PyRange>` smart pointer, so to use method call
41/// syntax these methods are separated into a trait, because stable Rust does not yet support
42/// `arbitrary_self_types`.
43#[doc(alias = "PyRange")]
44pub trait PyRangeMethods<'py>: Sealed {
45    /// Returns the start of the range.
46    fn start(&self) -> PyResult<isize>;
47
48    /// Returns the exclusive end of the range.
49    fn stop(&self) -> PyResult<isize>;
50
51    /// Returns the step of the range.
52    fn step(&self) -> PyResult<isize>;
53}
54
55impl<'py> PyRangeMethods<'py> for Bound<'py, PyRange> {
56    fn start(&self) -> PyResult<isize> {
57        self.getattr(intern!(self.py(), "start"))?.extract()
58    }
59
60    fn stop(&self) -> PyResult<isize> {
61        self.getattr(intern!(self.py(), "stop"))?.extract()
62    }
63
64    fn step(&self) -> PyResult<isize> {
65        self.getattr(intern!(self.py(), "step"))?.extract()
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_py_range_new() {
75        Python::with_gil(|py| {
76            let range = PyRange::new(py, isize::MIN, isize::MAX).unwrap();
77            assert_eq!(range.start().unwrap(), isize::MIN);
78            assert_eq!(range.stop().unwrap(), isize::MAX);
79            assert_eq!(range.step().unwrap(), 1);
80        });
81    }
82
83    #[test]
84    fn test_py_range_new_with_step() {
85        Python::with_gil(|py| {
86            let range = PyRange::new_with_step(py, 1, 10, 2).unwrap();
87            assert_eq!(range.start().unwrap(), 1);
88            assert_eq!(range.stop().unwrap(), 10);
89            assert_eq!(range.step().unwrap(), 2);
90        });
91    }
92}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here