Skip to content

Commit 1e7af98

Browse files
authored
Merge pull request RustPython#2911 from Snowapril/fix-reversed-iter
Implement methods for `PyReverseSequenceIterator`
2 parents 119df1a + aac16f1 commit 1e7af98

File tree

6 files changed

+66
-72
lines changed

6 files changed

+66
-72
lines changed

Lib/test/test_enumerate.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,6 @@ def test_range_optimization(self):
172172
x = range(1)
173173
self.assertEqual(type(reversed(x)), type(iter(x)))
174174

175-
# TODO: RUSTPYTHON
176-
@unittest.expectedFailure
177175
def test_len(self):
178176
for s in ('hello', tuple('hello'), list('hello'), range(5)):
179177
self.assertEqual(operator.length_hint(reversed(s)), len(s))
@@ -243,8 +241,6 @@ def __len__(self): return 2
243241
b = Blocked()
244242
self.assertRaises(TypeError, reversed, b)
245243

246-
# TODO: RUSTPYTHON
247-
@unittest.expectedFailure
248244
def test_pickle(self):
249245
for data in 'abc', range(5), tuple(enumerate('abc')), range(1,17,5):
250246
self.check_pickle(reversed(data), list(data)[::-1])

Lib/test/test_tuple.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,6 @@ def test_iterator_pickle(self):
361361
d = pickle.dumps(it, proto)
362362
self.assertEqual(self.type2test(it), self.type2test(data)[1:])
363363

364-
# TODO: RUSTPYTHON
365-
@unittest.expectedFailure
366364
def test_reversed_pickle(self):
367365
data = self.type2test([4, 5, 6, 7])
368366
for proto in range(pickle.HIGHEST_PROTOCOL + 1):

vm/src/builtins/enumerate.rs

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ use crossbeam_utils::atomic::AtomicCell;
44
use num_bigint::BigInt;
55
use num_traits::Zero;
66

7-
use super::int::PyIntRef;
7+
use super::int::{try_to_primitive, PyInt, PyIntRef};
8+
use super::iter::{
9+
IterStatus,
10+
IterStatus::{Active, Exhausted},
11+
};
812
use super::pytype::PyTypeRef;
913
use crate::function::OptionalArg;
1014
use crate::slots::PyIter;
@@ -64,7 +68,8 @@ impl PyIter for PyEnumerate {
6468
#[pyclass(module = false, name = "reversed")]
6569
#[derive(Debug)]
6670
pub struct PyReverseSequenceIterator {
67-
pub position: AtomicCell<isize>,
71+
pub position: AtomicCell<usize>,
72+
pub status: AtomicCell<IterStatus>,
6873
pub obj: PyObjectRef,
6974
}
7075

@@ -76,32 +81,77 @@ impl PyValue for PyReverseSequenceIterator {
7681

7782
#[pyimpl(with(PyIter))]
7883
impl PyReverseSequenceIterator {
79-
pub fn new(obj: PyObjectRef, len: isize) -> Self {
84+
pub fn new(obj: PyObjectRef, len: usize) -> Self {
8085
Self {
81-
position: AtomicCell::new(len - 1),
86+
position: AtomicCell::new(len.saturating_sub(1)),
87+
status: AtomicCell::new(if len == 0 { Exhausted } else { Active }),
8288
obj,
8389
}
8490
}
8591

8692
#[pymethod(magic)]
87-
fn length_hint(&self) -> PyResult<isize> {
88-
Ok(self.position.load() + 1)
93+
fn length_hint(&self, vm: &VirtualMachine) -> PyResult<usize> {
94+
Ok(match self.status.load() {
95+
Active => {
96+
let position = self.position.load();
97+
if position > vm.obj_len(&self.obj)? {
98+
0
99+
} else {
100+
position + 1
101+
}
102+
}
103+
Exhausted => 0,
104+
})
105+
}
106+
107+
#[pymethod(magic)]
108+
fn setstate(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
109+
// When we're exhausted, just return.
110+
if let Exhausted = self.status.load() {
111+
return Ok(());
112+
}
113+
let len = vm.obj_len(&self.obj)?;
114+
let pos = state
115+
.payload::<PyInt>()
116+
.ok_or_else(|| vm.new_type_error("an integer is required.".to_owned()))?;
117+
let pos = std::cmp::min(
118+
try_to_primitive(pos.as_bigint(), vm).unwrap_or(0),
119+
len.saturating_sub(1),
120+
);
121+
self.position.store(pos);
122+
Ok(())
123+
}
124+
125+
#[pymethod(magic)]
126+
fn reduce(&self, vm: &VirtualMachine) -> PyResult {
127+
let iter = vm.get_attribute(vm.builtins.clone(), "reversed")?;
128+
Ok(vm.ctx.new_tuple(match self.status.load() {
129+
Exhausted => vec![iter, vm.ctx.new_tuple(vec![vm.ctx.new_tuple(vec![])])],
130+
Active => vec![
131+
iter,
132+
vm.ctx.new_tuple(vec![self.obj.clone()]),
133+
vm.ctx.new_int(self.position.load()),
134+
],
135+
}))
89136
}
90137
}
91138

92139
impl PyIter for PyReverseSequenceIterator {
93140
fn next(zelf: &PyRef<Self>, vm: &VirtualMachine) -> PyResult {
141+
if let Exhausted = zelf.status.load() {
142+
return Err(vm.new_stop_iteration());
143+
}
94144
let pos = zelf.position.fetch_sub(1);
95-
if pos >= 0 {
96-
match zelf.obj.get_item(pos, vm) {
97-
Err(ref e) if e.isinstance(&vm.ctx.exceptions.index_error) => {
98-
Err(vm.new_stop_iteration())
99-
}
100-
// also catches stop_iteration => stop_iteration
101-
ret => ret,
145+
if pos == 0 {
146+
zelf.status.store(Exhausted);
147+
}
148+
match zelf.obj.get_item(pos, vm) {
149+
Err(ref e) if e.isinstance(&vm.ctx.exceptions.index_error) => {
150+
zelf.status.store(Exhausted);
151+
Err(vm.new_stop_iteration())
102152
}
103-
} else {
104-
Err(vm.new_stop_iteration())
153+
// also catches stop_iteration => stop_iteration
154+
ret => ret,
105155
}
106156
}
107157
}

vm/src/builtins/make_module.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ mod decl {
720720
vm.get_method_or_type_error(obj.clone(), "__getitem__", || {
721721
"argument to reversed() must be a sequence".to_owned()
722722
})?;
723-
let len = vm.obj_len(&obj)? as isize;
723+
let len = vm.obj_len(&obj)?;
724724
let obj_iterator = PyReverseSequenceIterator::new(obj, len);
725725
Ok(obj_iterator.into_object(vm))
726726
}

vm/src/builtins/pystr.rs

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -202,45 +202,6 @@ impl PyIter for PyStrIterator {
202202
}
203203
}
204204

205-
#[pyclass(module = false, name = "str_reverseiterator")]
206-
#[derive(Debug)]
207-
pub struct PyStrReverseIterator {
208-
string: PyStrRef,
209-
position: PyAtomic<usize>,
210-
}
211-
212-
impl PyValue for PyStrReverseIterator {
213-
fn class(vm: &VirtualMachine) -> &PyTypeRef {
214-
&vm.ctx.types.str_reverseiterator_type
215-
}
216-
}
217-
218-
#[pyimpl(with(PyIter))]
219-
impl PyStrReverseIterator {}
220-
221-
impl PyIter for PyStrReverseIterator {
222-
fn next(zelf: &PyRef<Self>, vm: &VirtualMachine) -> PyResult {
223-
let value = &*zelf.string.value;
224-
let mut end = zelf.position.load(atomic::Ordering::Relaxed);
225-
loop {
226-
let ch = value[..end]
227-
.chars()
228-
.next_back()
229-
.ok_or_else(|| vm.new_stop_iteration())?;
230-
231-
match zelf.position.compare_exchange_weak(
232-
end,
233-
end - ch.len_utf8(),
234-
atomic::Ordering::Release,
235-
atomic::Ordering::Relaxed,
236-
) {
237-
Ok(_) => break Ok(ch.into_pyobject(vm)),
238-
Err(cur) => end = cur,
239-
}
240-
}
241-
}
242-
}
243-
244205
#[derive(FromArgs)]
245206
struct StrArgs {
246207
#[pyarg(any, optional)]
@@ -1126,14 +1087,6 @@ impl PyStr {
11261087
fn encode(zelf: PyRef<Self>, args: EncodeArgs, vm: &VirtualMachine) -> PyResult<PyBytesRef> {
11271088
encode_string(zelf, args.encoding, args.errors, vm)
11281089
}
1129-
1130-
#[pymethod(magic)]
1131-
fn reversed(zelf: PyRef<Self>) -> PyStrReverseIterator {
1132-
PyStrReverseIterator {
1133-
position: Radium::new(zelf.byte_len()),
1134-
string: zelf,
1135-
}
1136-
}
11371090
}
11381091

11391092
impl PyStrRef {
@@ -1254,7 +1207,6 @@ pub fn init(ctx: &PyContext) {
12541207
PyStr::extend_class(ctx, &ctx.types.str_type);
12551208

12561209
PyStrIterator::extend_class(ctx, &ctx.types.str_iterator_type);
1257-
PyStrReverseIterator::extend_class(ctx, &ctx.types.str_reverseiterator_type);
12581210
}
12591211

12601212
impl PySliceableSequence for PyStr {

vm/src/types.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ pub struct TypeZoo {
7474
pub list_iterator_type: PyTypeRef,
7575
pub list_reverseiterator_type: PyTypeRef,
7676
pub str_iterator_type: PyTypeRef,
77-
pub str_reverseiterator_type: PyTypeRef,
7877
pub dict_keyiterator_type: PyTypeRef,
7978
pub dict_reversekeyiterator_type: PyTypeRef,
8079
pub dict_valueiterator_type: PyTypeRef,
@@ -193,7 +192,6 @@ impl TypeZoo {
193192
longrange_iterator_type: range::PyLongRangeIterator::init_bare_type().clone(),
194193
set_iterator_type: set::PySetIterator::init_bare_type().clone(),
195194
str_iterator_type: pystr::PyStrIterator::init_bare_type().clone(),
196-
str_reverseiterator_type: pystr::PyStrReverseIterator::init_bare_type().clone(),
197195
traceback_type: traceback::PyTraceback::init_bare_type().clone(),
198196
tuple_iterator_type: tuple::PyTupleIterator::init_bare_type().clone(),
199197
weakproxy_type: weakproxy::PyWeakProxy::init_bare_type().clone(),

0 commit comments

Comments
 (0)