1use crate::sealed::Sealed;
2use crate::types::PyAnyMethods;
3use crate::{ffi, Bound, PyAny, PyResult, PyTypeInfo, Python};
4
5#[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 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 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#[doc(alias = "PyRange")]
44pub trait PyRangeMethods<'py>: Sealed {
45 fn start(&self) -> PyResult<isize>;
47
48 fn stop(&self) -> PyResult<isize>;
50
51 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}