1use crate::sealed::Sealed;
2use crate::types::PyAnyMethods;
3use crate::{ffi, Bound, PyAny, PyResult, PyTypeInfo, Python};
4#[cfg(RustPython)]
5use crate::{
6 sync::PyOnceLock,
7 types::{PyType, PyTypeMethods},
8 Py,
9};
10
11#[repr(transparent)]
19pub struct PyRange(PyAny);
20
21#[cfg(not(RustPython))]
22pyobject_native_type_core!(PyRange, pyobject_native_static_type_object!(ffi::PyRange_Type), "builtins", "range", #checkfunction=ffi::PyRange_Check);
23
24#[cfg(RustPython)]
25pyobject_native_type_core!(
26 PyRange,
27 |py| {
28 static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
29 TYPE.import(py, "builtins", "range").unwrap().as_type_ptr()
30 },
31 "builtins",
32 "range",
33 #checkfunction=ffi::PyRange_Check
34);
35
36impl<'py> PyRange {
37 pub fn new(py: Python<'py>, start: isize, stop: isize) -> PyResult<Bound<'py, Self>> {
39 Self::new_with_step(py, start, stop, 1)
40 }
41
42 pub fn new_with_step(
44 py: Python<'py>,
45 start: isize,
46 stop: isize,
47 step: isize,
48 ) -> PyResult<Bound<'py, Self>> {
49 unsafe {
50 Ok(Self::type_object(py)
51 .call1((start, stop, step))?
52 .cast_into_unchecked())
53 }
54 }
55}
56
57#[doc(alias = "PyRange")]
63pub trait PyRangeMethods<'py>: Sealed {
64 fn start(&self) -> PyResult<isize>;
66
67 fn stop(&self) -> PyResult<isize>;
69
70 fn step(&self) -> PyResult<isize>;
72}
73
74impl<'py> PyRangeMethods<'py> for Bound<'py, PyRange> {
75 fn start(&self) -> PyResult<isize> {
76 self.getattr(intern!(self.py(), "start"))?.extract()
77 }
78
79 fn stop(&self) -> PyResult<isize> {
80 self.getattr(intern!(self.py(), "stop"))?.extract()
81 }
82
83 fn step(&self) -> PyResult<isize> {
84 self.getattr(intern!(self.py(), "step"))?.extract()
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_py_range_new() {
94 Python::attach(|py| {
95 let range = PyRange::new(py, isize::MIN, isize::MAX).unwrap();
96 assert_eq!(range.start().unwrap(), isize::MIN);
97 assert_eq!(range.stop().unwrap(), isize::MAX);
98 assert_eq!(range.step().unwrap(), 1);
99 });
100 }
101
102 #[test]
103 fn test_py_range_new_with_step() {
104 Python::attach(|py| {
105 let range = PyRange::new_with_step(py, 1, 10, 2).unwrap();
106 assert_eq!(range.start().unwrap(), 1);
107 assert_eq!(range.stop().unwrap(), 10);
108 assert_eq!(range.step().unwrap(), 2);
109 });
110 }
111}