diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index dd90fb07e..eb8fdad26 100644 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -890,7 +890,7 @@ class BaseTest: self.assertRaises(TypeError, a.__setitem__, slice(0, 1), b) # TODO: RUSTPYTHON - @unittest.expectedFailure + # @unittest.expectedFailure def test_extended_set_del_slice(self): indices = (0, None, 1, 3, 19, 100, sys.maxsize, -1, -2, -31, -100) for start in indices: @@ -1214,7 +1214,7 @@ class NumberTest(BaseTest): self.assertEqual(a[-1000:-2000:-2], array.array(self.typecode, [])) # TODO: RUSTPYTHON - @unittest.expectedFailure + # @unittest.expectedFailure def test_delslice(self): a = array.array(self.typecode, range(5)) del a[::2] diff --git a/tests/snippets/stdlib_array.py b/tests/snippets/stdlib_array.py index 9bdd92598..516572013 100644 --- a/tests/snippets/stdlib_array.py +++ b/tests/snippets/stdlib_array.py @@ -34,3 +34,11 @@ a[0:0:9999999999] = c assert a == array(T, [100, 1, 2, 3, 4, 5, 6, 7, 8, 100]) a[0:0:-9999999999] = c assert a == array(T, [100, 1, 2, 3, 4, 5, 6, 7, 8, 100]) +del a[::9999999999] +assert a == array(T, [1, 2, 3, 4, 5, 6, 7, 8, 100]) +del a[::-9999999999] +assert a == array(T, [1, 2, 3, 4, 5, 6, 7, 8]) +del a[0:0:9999999999] +assert a == array(T, [1, 2, 3, 4, 5, 6, 7, 8]) +del a[0:0:-9999999999] +assert a == array(T, [1, 2, 3, 4, 5, 6, 7, 8]) diff --git a/vm/src/stdlib/array.rs b/vm/src/stdlib/array.rs index e322561f7..6938980b3 100644 --- a/vm/src/stdlib/array.rs +++ b/vm/src/stdlib/array.rs @@ -344,7 +344,79 @@ macro_rules! def_array_enum { } fn delitem_by_slice(&mut self, slice: PySliceRef, vm: &VirtualMachine) -> PyResult<()> { - Err(vm.new_not_implemented_error("FIX NOW".to_owned())) + let start = slice.start_index(vm)?; + let stop = slice.stop_index(vm)?; + let step = slice.step_index(vm)?.unwrap_or_else(BigInt::one); + + if step.is_zero() { + return Err(vm.new_value_error("slice step cannot be zero".to_owned())); + } + + match self { + $(ArrayContentType::$n(elements) => { + if step == BigInt::one() { + let range = get_slice_range(&start, &stop, elements.len()); + if range.start < range.end { + elements.drain(range); + } + return Ok(()); + } + + let (start, stop, step, is_negative_step) = if step.is_negative() { + ( + stop.map(|x| if x == -BigInt::one() { + elements.len() + BigInt::one() + } else { + x + 1 + }), + start.map(|x| if x == -BigInt::one() { + BigInt::from(elements.len()) + } else { + x + 1 + }), + -step, + true + ) + } else { + (start, stop, step, false) + }; + + let range = get_slice_range(&start, &stop, elements.len()); + if range.start >= range.end { + return Ok(()); + } + + // step is not negative here + if let Some(step) = step.to_usize() { + let mut indexes = if is_negative_step { + itertools::Either::Left(range.clone().rev().step_by(step).rev()).peekable() + } else { + itertools::Either::Right(range.clone().step_by(step)).peekable() + }; + + let mut deleted = 0; + + // passing whole range, swap or overlap + for i in range.clone() { + if indexes.peek() == Some(&i) { + indexes.next(); + deleted += 1; + } else { + elements.swap(i - deleted, i); + } + } + // then drain (the values to delete should now be contiguous at the end of the range) + elements.drain((range.end - deleted)..range.end); + } else { + // edge case, step is too big for usize + // same behaviour as CPython + elements.remove( + if is_negative_step { range.end -1 } else { range.start } + ); + } + Ok(()) + })* + } } fn repr(&self, _vm: &VirtualMachine) -> PyResult { @@ -596,7 +668,7 @@ impl PyArray { fn delitem(&self, needle: Either, vm: &VirtualMachine) -> PyResult<()> { match needle { Either::A(i) => self.borrow_value_mut().delitem_by_idx(i, vm), - Either::B(slice) => self.borrow_value_mut().delitem_by_slice(slice, vm) + Either::B(slice) => self.borrow_value_mut().delitem_by_slice(slice, vm), } }