forked from RustPython/RustPython
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathiter.rs
More file actions
118 lines (110 loc) · 3.22 KB
/
Copy pathiter.rs
File metadata and controls
118 lines (110 loc) · 3.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use crate::IntoPyObject;
use crate::{
builtins::iter::PySequenceIterator, PyObjectRef, PyResult, PyValue, TryFromObject,
TypeProtocol, VirtualMachine,
};
use std::borrow::Borrow;
use std::ops::Deref;
/// Iterator Protocol
// https://docs.python.org/3/c-api/iter.html
#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct PyIter<T = PyObjectRef>(T)
where
T: Borrow<PyObjectRef>;
impl PyIter<PyObjectRef> {
pub fn into_object(self) -> PyObjectRef {
self.0
}
}
impl<T> PyIter<T>
where
T: Borrow<PyObjectRef>,
{
pub fn new(obj: T) -> Self {
Self(obj)
}
pub fn as_object(&self) -> &PyObjectRef {
self.0.borrow()
}
pub fn next(&self, vm: &VirtualMachine) -> PyResult {
let iternext = {
self.0
.borrow()
.class()
.mro_find_map(|x| x.slots.iternext.load())
.ok_or_else(|| {
vm.new_type_error(format!(
"'{}' object is not an iterator",
self.0.borrow().class().name()
))
})?
};
iternext(self.0.borrow(), vm)
}
}
impl<T> Borrow<PyObjectRef> for PyIter<T>
where
T: Borrow<PyObjectRef>,
{
fn borrow(&self) -> &PyObjectRef {
self.0.borrow()
}
}
impl<T> Deref for PyIter<T>
where
T: Borrow<PyObjectRef>,
{
type Target = PyObjectRef;
fn deref(&self) -> &Self::Target {
self.0.borrow()
}
}
impl IntoPyObject for PyIter<PyObjectRef> {
fn into_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
self.into_object()
}
}
impl TryFromObject for PyIter<PyObjectRef> {
// This helper function is called at multiple places. First, it is called
// in the vm when a for loop is entered. Next, it is used when the builtin
// function 'iter' is called.
fn try_from_object(vm: &VirtualMachine, iter_target: PyObjectRef) -> PyResult<Self> {
let getiter = {
let cls = iter_target.class();
cls.mro_find_map(|x| x.slots.iter.load())
};
if let Some(getiter) = getiter {
let iter = getiter(iter_target, vm)?;
let cls = iter.class();
let is_iter = cls.iter_mro().any(|x| x.slots.iternext.load().is_some());
if is_iter {
drop(cls);
Ok(Self(iter))
} else {
Err(vm.new_type_error(format!(
"iter() returned non-iterator of type '{}'",
cls.name()
)))
}
} else {
vm.get_method_or_type_error(iter_target.clone(), "__getitem__", || {
format!("'{}' object is not iterable", iter_target.class().name())
})?;
Ok(Self(
PySequenceIterator::new(iter_target)
.into_ref(vm)
.into_object(),
))
}
}
}
impl PyObjectRef {
/// Takes an object and returns an iterator for it.
/// This is typically a new iterator but if the argument is an iterator, this
/// returns itself.
pub fn get_iter(self, vm: &VirtualMachine) -> PyResult<PyIter> {
// PyObject_GetIter
PyIter::try_from_object(vm, self)
}
}