diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 9a121d5f1..c82a35a76 100644 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -25,7 +25,10 @@ class ArraySubclassWithKwargs(array.array): def __init__(self, typecode, newarg=None): array.array.__init__(self) -typecodes = 'ubBhHiIlLfdqQ' +# TODO: RUSTPYTHON +# We did not support typecode u for unicode yet +# typecodes = 'ubBhHiIlLfdqQ' +typecodes = 'bBhHiIlLfdqQ' class MiscTest(unittest.TestCase): @@ -497,8 +500,6 @@ class BaseTest: b = array.array(self.typecode, a) self.assertEqual(a, b) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_repr(self): a = array.array(self.typecode, 2*self.example) self.assertEqual(a, eval(repr(a), {"array": array.array})) @@ -801,8 +802,6 @@ class BaseTest: self.assertEqual(list(a[start:stop:step]), list(a)[start:stop:step]) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_setslice(self): a = array.array(self.typecode, self.example) a[:1] = a @@ -1235,8 +1234,6 @@ class NumberTest(BaseTest): a = array.array(self.typecode, range(10)) del a[9::1<<333] - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_assignment(self): a = array.array(self.typecode, range(10)) a[::2] = array.array(self.typecode, [42]*5) diff --git a/tests/snippets/stdlib_array.py b/tests/snippets/stdlib_array.py index 8d4b9d85e..4b7174540 100644 --- a/tests/snippets/stdlib_array.py +++ b/tests/snippets/stdlib_array.py @@ -32,3 +32,17 @@ def test_float_with_integer_input(): assert f == array("f", [-2, 0, 2, 3, 4]) test_float_with_integer_input() + +# slice assignment step overflow behaviour test +T = 'I' +a = array(T, range(10)) +b = array(T, [100]) +a[::9999999999] = b +assert a == array(T, [100, 1, 2, 3, 4, 5, 6, 7, 8, 9]) +a[::-9999999999] = b +assert a == array(T, [100, 1, 2, 3, 4, 5, 6, 7, 8, 100]) +c = array(T) +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]) diff --git a/vm/src/bytesinner.rs b/vm/src/bytesinner.rs index 46438d8b5..dc42c900b 100644 --- a/vm/src/bytesinner.rs +++ b/vm/src/bytesinner.rs @@ -252,13 +252,11 @@ impl PyBytesInner { let mut res = String::with_capacity(self.elements.len()); for i in self.elements.iter() { match i { - 0..=8 => res.push_str(&format!("\\x0{}", i)), 9 => res.push_str("\\t"), 10 => res.push_str("\\n"), - 11 => res.push_str(&format!("\\x0{:x}", i)), 13 => res.push_str("\\r"), 32..=126 => res.push(*(i) as char), - _ => res.push_str(&format!("\\x{:x}", i)), + _ => res.push_str(&format!("\\x{:02x}", i)), } } res diff --git a/vm/src/stdlib/array.rs b/vm/src/stdlib/array.rs index c5a6e0a64..ff674a241 100644 --- a/vm/src/stdlib/array.rs +++ b/vm/src/stdlib/array.rs @@ -5,18 +5,21 @@ use crate::common::cell::{ use crate::function::OptionalArg; use crate::obj::objbytes::PyBytesRef; use crate::obj::objfloat::try_float; -use crate::obj::objsequence::PySliceableSequence; +use crate::obj::objsequence::{get_slice_range, PySliceableSequence}; use crate::obj::objslice::PySliceRef; use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; use crate::obj::{objbool, objiter}; use crate::pyobject::{ - BorrowValue, Either, IntoPyObject, PyArithmaticValue, PyClassImpl, PyComparisonValue, - PyIterable, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, + BorrowValue, Either, IdProtocol, IntoPyObject, PyArithmaticValue, PyClassImpl, + PyComparisonValue, PyIterable, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, + TypeProtocol, }; use crate::VirtualMachine; use crossbeam_utils::atomic::AtomicCell; use itertools::Itertools; +use num_bigint::BigInt; +use num_traits::{One, Signed, ToPrimitive, Zero}; use std::fmt; struct ArrayTypeSpecifierError { @@ -34,7 +37,7 @@ impl fmt::Display for ArrayTypeSpecifierError { macro_rules! def_array_enum { ($(($n:ident, $t:ident, $c:literal)),*$(,)?) => { - #[derive(Debug)] + #[derive(Debug, Clone)] enum ArrayContentType { $($n(Vec<$t>),)* } @@ -230,18 +233,128 @@ macro_rules! def_array_enum { Either::B(slice) => self.getitem_by_slice(slice, vm), } } + + fn setitem_by_slice(&mut self, slice: PySliceRef, items: &ArrayContentType, vm: &VirtualMachine) -> PyResult<()> { + let start = slice.start_index(vm)?; + let stop = slice.stop_index(vm)?; + let step = slice.step_index(vm)?.unwrap_or_else(BigInt::one); - fn setitem(&mut self, needle: Either, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - match needle { - Either::A(i) => { - let i = self.idx(i, "array assignment", vm)?; - match self { - $(ArrayContentType::$n(v) => { v[i] = $t::try_into_from_object(vm, value)? },)* - } - Ok(()) - } - Either::B(_slice) => Err(vm.new_not_implemented_error("array slice is not implemented".to_owned())), + if step.is_zero() { + return Err(vm.new_value_error("slice step cannot be zero".to_owned())); } + + match self { + $(ArrayContentType::$n(elements) => if let ArrayContentType::$n(items) = items { + if step == BigInt::one() { + let range = get_slice_range(&start, &stop, elements.len()); + let range = if range.end < range.start { + range.start..range.start + } else { + range + }; + elements.splice(range, items.iter().cloned()); + 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()); + let range = if range.end < range.start { + range.start..range.start + } else { + range + }; + + // step is not negative here + if let Some(step) = step.to_usize() { + let slicelen = if range.end > range.start { + (range.end - range.start - 1) / step + 1 + } else { + 0 + }; + + if slicelen == items.len() { + if is_negative_step { + for (i, &item) in range.rev().step_by(step).zip(items) { + elements[i] = item; + } + } else { + for (i, &item) in range.step_by(step).zip(items) { + elements[i] = item; + } + } + Ok(()) + } else { + Err(vm.new_value_error(format!( + "attempt to assign sequence of size {} to extended slice of size {}", + items.len(), slicelen + ))) + } + } else { + // edge case, step is too big for usize + // same behaviour as CPython + let slicelen = if range.start < range.end { 1 } else { 0 }; + if match items.len() { + 0 => slicelen == 0, + 1 => { + elements[ + if is_negative_step { range.end - 1 } else { range.start } + ] = items[0]; + true + }, + _ => false, + } { + Ok(()) + } else { + Err(vm.new_value_error(format!( + "attempt to assign sequence of size {} to extended slice of size {}", + items.len(), slicelen + ))) + } + } + } else { + Err(vm.new_type_error("bad argument type for built-in operation".to_owned())) + },)* + } + } + + fn setitem_by_idx(&mut self, i: isize, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + let i = self.idx(i, "array assignment", vm)?; + match self { + $(ArrayContentType::$n(v) => { v[i] = TryFromObject::try_from_object(vm, value)? },)* + } + Ok(()) + } + + fn repr(&self, _vm: &VirtualMachine) -> PyResult { + // we don't need ReprGuard here + let s = match self { + $(ArrayContentType::$n(v) => { + if v.is_empty() { + format!("array('{}')", $c) + } else { + format!("array('{}', [{}])", $c, v.iter().format(", ")) + } + })* + }; + Ok(s) } fn iter<'a>(&'a self, vm: &'a VirtualMachine) -> impl Iterator + 'a { @@ -479,12 +592,41 @@ impl PyArray { #[pymethod(magic)] fn setitem( - &self, + zelf: PyRef, needle: Either, obj: PyObjectRef, vm: &VirtualMachine, ) -> PyResult<()> { - self.borrow_value_mut().setitem(needle, obj, vm) + match needle { + Either::A(i) => zelf.borrow_value_mut().setitem_by_idx(i, obj, vm), + Either::B(slice) => { + let cloned; + let guard; + let items = if zelf.is(&obj) { + cloned = zelf.borrow_value().clone(); + &cloned + } else { + match obj.payload::() { + Some(array) => { + guard = array.borrow_value(); + &*guard + } + None => { + return Err(vm.new_type_error(format!( + "can only assign array (not \"{}\") to array slice", + obj.class().name + ))); + } + } + }; + zelf.borrow_value_mut().setitem_by_slice(slice, items, vm) + } + } + } + + #[pymethod(name = "__repr__")] + fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { + zelf.borrow_value().repr(vm) } #[pymethod(name = "__eq__")] diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 6e398be46..6062e9d23 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -719,6 +719,14 @@ mod _os { std::process::exit(code) } + #[pyfunction] + fn abort() { + extern "C" { + fn abort(); + } + unsafe { abort() } + } + #[pyfunction] fn urandom(size: usize, vm: &VirtualMachine) -> PyResult> { let mut buf = vec![0u8; size]; diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index c8d2b381d..7eea1d507 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -579,6 +579,23 @@ fn socket_gethostbyaddr( )) } +#[cfg(not(target_os = "redox"))] +fn socket_gethostbyname(name: PyStringRef, vm: &VirtualMachine) -> PyResult { + match socket_gethostbyaddr(name, vm) { + Ok((_, _, hosts)) => { + let lst = vm.extract_elements::(&hosts)?; + Ok(lst.get(0).unwrap().to_string()) + } + Err(_) => { + let error_type = vm.class("_socket", "gaierror"); + Err(vm.new_exception_msg( + error_type, + "nodename nor servname provided, or not known".to_owned(), + )) + } + } +} + fn get_addr(vm: &VirtualMachine, addr: T) -> PyResult where T: ToSocketAddrs, @@ -707,6 +724,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { extend_module!(vm, module, { "getaddrinfo" => ctx.new_function(socket_getaddrinfo), "gethostbyaddr" => ctx.new_function(socket_gethostbyaddr), + "gethostbyname" => ctx.new_function(socket_gethostbyname), }); extend_module_platform_specific(vm, &module);