GeezerVison is showing github.com — Align builtin iterator controls and callback lifetimes (#9699) · RustPython/RustPython@1f6c4dc
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: crates/vm/src/builtins/bytearray.rs +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -1504,7 +1504,7 @@ impl AsSequence for PyByteArray { | 1504 | 1504 | PyByteArray::sequence_downcast(seq) | 1505 | 1505 | .borrow_buf() | 1506 | 1506 | .getitem_by_index(vm, i) | 1507 | | - .map(|x| vm.ctx.new_bytes(vec![x]).into()) | | 1507 | + .map(|x| vm.ctx.new_int(x).into()) | 1508 | 1508 | }), | 1509 | 1509 | ass_item: atomic_func!(|seq, i, value, vm| { | 1510 | 1510 | let zelf = PyByteArray::sequence_downcast(seq); | |
Collapse file Copy file name to clipboardExpand all lines: crates/vm/src/builtins/bytes.rs +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -698,7 +698,7 @@ impl AsSequence for PyBytes { | 698 | 698 | PyBytes::sequence_downcast(seq) | 699 | 699 | .as_bytes() | 700 | 700 | .getitem_by_index(vm, i) | 701 | | - .map(|x| vm.ctx.new_bytes(vec![x]).into()) | | 701 | + .map(|x| vm.ctx.new_int(x).into()) | 702 | 702 | }), | 703 | 703 | contains: atomic_func!(|seq, other, vm| { | 704 | 704 | PyBytes::sequence_downcast(seq).__contains__(other.to_owned(), vm) | |
Collapse file Copy file name to clipboardExpand all lines: crates/vm/src/builtins/enumerate.rs +99-28Lines changed: 99 additions & 28 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | use super::{ | 2 | | - IterStatus, PositionIterInternal, PyGenericAlias, PyTupleRef, PyType, PyTypeRef, | 3 | | - iter::builtins_reversed, locked_rev_next, | | 2 | + PyGenericAlias, PyTupleRef, PyType, PyTypeRef, | | 3 | + iter::{SequenceIter, SequenceIterDirection}, | 4 | 4 | }; | 5 | | -use crate::common::lock::{PyMutex, PyRwLock}; | | 5 | +use crate::common::lock::PyRwLock; | 6 | 6 | use crate::{ | 7 | 7 | AsObject, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, | 8 | 8 | class::PyClassImpl, | 9 | | - function::ArgIndex, | | 9 | + function::{ArgIndex, FuncArgs}, | 10 | 10 | protocol::{PyIter, PyIterReturn}, | 11 | 11 | raise_if_stop, | 12 | | - types::{Constructor, IterNext, Iterable, SelfIter}, | | 12 | + types::{Constructor, IterNext, Iterable, SelfIter, drop_kwargs_if_init_overridden}, | 13 | 13 | }; | 14 | 14 | use malachite_bigint::BigInt; | 15 | 15 | use num_traits::ToPrimitive; | @@ -139,7 +139,7 @@ impl IterNext for PyEnumerate { | 139 | 139 | #[pyclass(module = false, name = "reversed", traverse)] | 140 | 140 | #[derive(Debug)] | 141 | 141 | pub(crate) struct PyReverseSequenceIterator { | 142 | | - internal: PyMutex<PositionIterInternal<PyObjectRef>>, | | 142 | + internal: SequenceIter, | 143 | 143 | } | 144 | 144 |
| 145 | 145 | impl PyPayload for PyReverseSequenceIterator { | @@ -150,50 +150,121 @@ impl PyPayload for PyReverseSequenceIterator { | 150 | 150 | } | 151 | 151 |
| 152 | 152 | impl PyReverseSequenceIterator { | | 153 | + pub(crate) fn from_object( | | 154 | + cls: PyTypeRef, | | 155 | + sequence: &PyObjectRef, | | 156 | + vm: &VirtualMachine, | | 157 | + ) -> PyResult { | | 158 | + // Keep the original sequence alive while binding, calling, and releasing | | 159 | + // the special method, including when it does not retain its receiver. | | 160 | + if let Some(method) = vm.get_method(sequence.clone(), identifier!(vm, __reversed__)) { | | 161 | + let method = method?; | | 162 | + if vm.is_none(&method) { | | 163 | + return Err(Self::not_reversible(sequence, vm)); | | 164 | + } | | 165 | + let result = method.call((), vm); | | 166 | + drop(method); | | 167 | + return result; | | 168 | + } | | 169 | + | | 170 | + Self::py_new( | | 171 | + &cls, | | 172 | + ReversedArgs { | | 173 | + object: sequence.clone(), | | 174 | + }, | | 175 | + vm, | | 176 | + )? | | 177 | + .into_ref_with_type(vm, cls) | | 178 | + .map(Into::into) | | 179 | + } | | 180 | + | | 181 | + fn not_reversible( | | 182 | + sequence: &PyObjectRef, | | 183 | + vm: &VirtualMachine, | | 184 | + ) -> crate::builtins::PyBaseExceptionRef { | | 185 | + vm.new_type_error(format!( | | 186 | + "'{}' object is not reversible", | | 187 | + sequence.class().slot_name() | | 188 | + )) | | 189 | + } | | 190 | + | 153 | 191 | pub(crate) const fn new(obj: PyObjectRef, len: usize) -> Self { | 154 | | - let position = len.saturating_sub(1); | | 192 | + let position = len as isize - 1; | 155 | 193 | Self { | 156 | | - internal: PyMutex::new(PositionIterInternal::new(obj, position)), | | 194 | + internal: SequenceIter::new(obj, position, SequenceIterDirection::Reverse), | 157 | 195 | } | 158 | 196 | } | 159 | 197 | } | 160 | 198 |
| 161 | | -#[pyclass(with(IterNext, Iterable))] | | 199 | +#[derive(FromArgs)] | | 200 | +pub(crate) struct ReversedArgs { | | 201 | + #[pyarg(positional)] | | 202 | + object: PyObjectRef, | | 203 | +} | | 204 | + | | 205 | +impl Constructor for PyReverseSequenceIterator { | | 206 | + type Args = ReversedArgs; | | 207 | + | | 208 | + fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult { | | 209 | + // A subtype's custom initializer may consume keywords, but the exact | | 210 | + // type and subtypes inheriting its initializer reject them first. | | 211 | + // Retain the original arguments through callbacks even if keywords | | 212 | + // are removed from the constructor's view of the call. | | 213 | + let constructor_args = | | 214 | + drop_kwargs_if_init_overridden(&cls, vm.ctx.types.reverse_iter_type, args.clone()); | | 215 | + if !constructor_args.kwargs.is_empty() { | | 216 | + return Err(vm.new_type_error("reversed() takes no keyword arguments")); | | 217 | + } | | 218 | + let ReversedArgs { object } = constructor_args.bind_for(vm, "reversed")?; | | 219 | + let result = Self::from_object(cls, &object, vm); | | 220 | + drop(object); | | 221 | + drop(args); | | 222 | + result | | 223 | + } | | 224 | + | | 225 | + fn py_new( | | 226 | + _cls: &Py<PyType>, | | 227 | + Self::Args { object }: Self::Args, | | 228 | + vm: &VirtualMachine, | | 229 | + ) -> PyResult<Self> { | | 230 | + // The slot handles arbitrary __reversed__ results before constructing | | 231 | + // a payload. Checking sequence support must not bind __getitem__. | | 232 | + let protocol = object.sequence_unchecked(); | | 233 | + if !protocol.check() { | | 234 | + return Err(Self::not_reversible(&object, vm)); | | 235 | + } | | 236 | + let len = protocol.length(vm)?; | | 237 | + Ok(Self::new(object, len)) | | 238 | + } | | 239 | +} | | 240 | + | | 241 | +#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))] | 162 | 242 | impl Py<PyReverseSequenceIterator> { | 163 | 243 | #[pymethod] | 164 | 244 | fn __length_hint__(&self, vm: &VirtualMachine) -> PyResult<usize> { | 165 | | - let internal = self.internal.lock(); | 166 | | - if let IterStatus::Active(obj) = &internal.status | 167 | | - && internal.position <= obj.length(vm)? | 168 | | - { | 169 | | - return Ok(internal.position + 1); | 170 | | - } | 171 | | - Ok(0) | | 245 | + vm.with_recursion(" in __length_hint__", || { | | 246 | + self.internal | | 247 | + .length_hint(vm) | | 248 | + .map(|length| length.unwrap_or(0)) | | 249 | + }) | 172 | 250 | } | 173 | 251 |
| 174 | 252 | #[pymethod] | 175 | 253 | fn __setstate__(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { | 176 | | - self.internal.lock().set_state(&state, |_, pos| pos, vm) | | 254 | + self.internal.set_state(&state, vm) | 177 | 255 | } | 178 | 256 |
| 179 | 257 | #[pymethod] | 180 | | - fn __reduce__(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> { | 181 | | - let func = builtins_reversed(vm)?; | 182 | | - Ok(self.internal.lock().reduce( | 183 | | - func, | 184 | | - |x| x.clone(), | 185 | | - |vm| vm.ctx.empty_tuple.clone().into(), | 186 | | - vm, | 187 | | - )) | | 258 | + fn __reduce__(&self, vm: &VirtualMachine) -> PyTupleRef { | | 259 | + let func = self.class().to_owned().into(); | | 260 | + self.internal.reduce(func, vm) | 188 | 261 | } | 189 | 262 | } | 190 | 263 |
| 191 | 264 | impl SelfIter for PyReverseSequenceIterator {} | 192 | 265 | impl IterNext for PyReverseSequenceIterator { | 193 | 266 | fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> { | 194 | | - locked_rev_next(&zelf.internal, |obj, pos| { | 195 | | - PyIterReturn::from_getitem_result(obj.get_item(&pos, vm), vm) | 196 | | - }) | | 267 | + zelf.internal.next(vm) | 197 | 268 | } | 198 | 269 | } | 199 | 270 |
| |
Collapse file Copy file name to clipboardExpand all lines: crates/vm/src/builtins/filter.rs +18-17Lines changed: 18 additions & 17 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | use super::{PyType, PyTypeRef}; | 2 | 2 | use crate::{ | 3 | | - Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, | | 3 | + AsObject, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, | 4 | 4 | class::PyClassImpl, | 5 | 5 | protocol::{PyIter, PyIterReturn}, | 6 | 6 | raise_if_stop, | @@ -26,7 +26,7 @@ pub struct FilterArgs { | 26 | 26 | #[pyarg(positional)] | 27 | 27 | function: PyObjectRef, | 28 | 28 | #[pyarg(positional)] | 29 | | - iterable: PyIter, | | 29 | + iterable: PyObjectRef, | 30 | 30 | } | 31 | 31 |
| 32 | 32 | impl Constructor for PyFilter { | @@ -35,25 +35,25 @@ impl Constructor for PyFilter { | 35 | 35 |
| 36 | 36 | fn py_new( | 37 | 37 | _cls: &Py<PyType>, | 38 | | - Self::Args { | 39 | | - function, | 40 | | - iterable: iterator, | 41 | | - }: Self::Args, | 42 | | - _vm: &VirtualMachine, | | 38 | + Self::Args { function, iterable }: Self::Args, | | 39 | + vm: &VirtualMachine, | 43 | 40 | ) -> PyResult<Self> { | 44 | | - Ok(Self { | 45 | | - predicate: function, | | 41 | + let result = iterable.get_iter(vm).map(|iterator| Self { | | 42 | + predicate: function.clone(), | 46 | 43 | iterator, | 47 | | - }) | | 44 | + }); | | 45 | + drop(iterable); | | 46 | + drop(function); | | 47 | + result | 48 | 48 | } | 49 | 49 | } | 50 | 50 |
| 51 | 51 | #[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))] | 52 | 52 | impl Py<PyFilter> { | 53 | 53 | #[pymethod] | 54 | | - fn __reduce__(&self, vm: &VirtualMachine) -> (PyTypeRef, (PyObjectRef, PyIter)) { | | 54 | + fn __reduce__(&self) -> (PyTypeRef, (PyObjectRef, PyIter)) { | 55 | 55 | ( | 56 | | - vm.ctx.types.filter_type.to_owned(), | | 56 | + self.class().to_owned(), | 57 | 57 | (self.predicate.clone(), self.iterator.clone()), | 58 | 58 | ) | 59 | 59 | } | @@ -66,17 +66,18 @@ impl IterNext for PyFilter { | 66 | 66 | let predicate = &zelf.predicate; | 67 | 67 | loop { | 68 | 68 | let next_obj = raise_if_stop!(zelf.iterator.next(vm)?); | 69 | | - let predicate_value = if vm.is_none(predicate) { | 70 | | - next_obj.clone() | | 69 | + let is_true = if vm.is_none(predicate) || predicate.is(vm.ctx.types.bool_type) { | | 70 | + next_obj.try_to_bool(vm)? | 71 | 71 | } else { | 72 | 72 | // the predicate itself can raise StopIteration which does stop the filter iteration | 73 | | - raise_if_stop!(PyIterReturn::from_pyresult( | | 73 | + let predicate_value = raise_if_stop!(PyIterReturn::from_pyresult( | 74 | 74 | predicate.call((next_obj.clone(),), vm), | 75 | 75 | vm | 76 | | - )?) | | 76 | + )?); | | 77 | + predicate_value.try_to_bool(vm)? | 77 | 78 | }; | 78 | 79 |
| 79 | | - if predicate_value.try_to_bool(vm)? { | | 80 | + if is_true { | 80 | 81 | return Ok(PyIterReturn::Return(next_obj)); | 81 | 82 | } | 82 | 83 | } | |
|