From c30fabddeec442a94f48a4fe4234a548d3a4a9ec Mon Sep 17 00:00:00 2001 From: hyperbora Date: Sun, 30 Aug 2020 09:30:13 +0900 Subject: [PATCH 1/9] add gethostbyname function --- vm/src/stdlib/socket.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) 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); From d2397f440c4c0e20799a9ae6758ade49b31f3086 Mon Sep 17 00:00:00 2001 From: minoring Date: Sun, 30 Aug 2020 14:10:12 +0900 Subject: [PATCH 2/9] Implement os.abort --- vm/src/stdlib/os.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index df7cdbc7e..5b099c959 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -714,6 +714,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]; From 1922725af5bf6e3375500f40273e006f1bfca45d Mon Sep 17 00:00:00 2001 From: Kangzhi Shi Date: Sun, 30 Aug 2020 16:51:52 +0200 Subject: [PATCH 3/9] Implement array setitem by slice --- Lib/test/test_array.py | 9 +-- tests/snippets/stdlib_array.py | 16 +++- vm/src/stdlib/array.rs | 142 +++++++++++++++++++++++++++++---- 3 files changed, 147 insertions(+), 20 deletions(-) diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 9a121d5f1..2e3019744 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): @@ -801,8 +804,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 +1236,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 0ef0969ff..9bdd92598 100644 --- a/tests/snippets/stdlib_array.py +++ b/tests/snippets/stdlib_array.py @@ -19,4 +19,18 @@ a = array("b", [0, 1, 2, 3]) b = a assert a.__ne__(b) is False b = array("B", [3, 2, 1, 0]) -assert a.__ne__(b) is True \ No newline at end of file +assert a.__ne__(b) is True + +# 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/stdlib/array.rs b/vm/src/stdlib/array.rs index d01773e9d..ab3d74c2b 100644 --- a/vm/src/stdlib/array.rs +++ b/vm/src/stdlib/array.rs @@ -4,19 +4,21 @@ use crate::common::cell::{ }; use crate::function::OptionalArg; use crate::obj::objbytes::PyBytesRef; -use crate::obj::objsequence::PySliceableSequence; +use crate::obj::objsequence::{PySliceableSequence, get_slice_range}; 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, + PyIterable, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol }; use crate::VirtualMachine; use crossbeam_utils::atomic::AtomicCell; use itertools::Itertools; use std::fmt; +use num_bigint::BigInt; +use num_traits::{Zero, One, Signed, ToPrimitive}; struct ArrayTypeSpecifierError { _priv: (), @@ -33,7 +35,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>),)* } @@ -223,17 +225,113 @@ macro_rules! def_array_enum { } } - 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] = TryFromObject::try_from_object(vm, value)? },)* - } - Ok(()) - } - Either::B(_slice) => Err(vm.new_not_implemented_error("array slice is not implemented".to_owned())), + 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); + + 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); + 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 iter<'a>(&'a self, vm: &'a VirtualMachine) -> impl Iterator + 'a { @@ -440,7 +538,23 @@ impl PyArray { obj: PyObjectRef, vm: &VirtualMachine, ) -> PyResult<()> { - self.borrow_value_mut().setitem(needle, obj, vm) + match needle { + Either::A(i) => self.borrow_value_mut().setitem_by_idx(i, obj, vm), + Either::B(slice) => { + let items = match obj.payload::() { + // TODO: is there a way to check ref equality between [self] and [obj] + // so we can do clone() only when they are referring the same PyObject + Some(array) => array.borrow_value().clone(), + None => { + return Err(vm.new_type_error(format!( + "can only assign array (not \"{}\") to array slice", + obj.class().name + ))); + } + }; + self.borrow_value_mut().setitem_by_slice(slice, items, vm) + } + } } #[pymethod(name = "__eq__")] From ff7c8c94f76f19114fd1b31823f31fb460d5236d Mon Sep 17 00:00:00 2001 From: Kangzhi Shi Date: Sun, 30 Aug 2020 17:17:17 +0200 Subject: [PATCH 4/9] fix rustfmt --- vm/src/stdlib/array.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vm/src/stdlib/array.rs b/vm/src/stdlib/array.rs index ab3d74c2b..80e52db0b 100644 --- a/vm/src/stdlib/array.rs +++ b/vm/src/stdlib/array.rs @@ -4,21 +4,21 @@ use crate::common::cell::{ }; use crate::function::OptionalArg; use crate::obj::objbytes::PyBytesRef; -use crate::obj::objsequence::{PySliceableSequence, get_slice_range}; +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 + PyIterable, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; use crate::VirtualMachine; use crossbeam_utils::atomic::AtomicCell; use itertools::Itertools; -use std::fmt; use num_bigint::BigInt; -use num_traits::{Zero, One, Signed, ToPrimitive}; +use num_traits::{One, Signed, ToPrimitive, Zero}; +use std::fmt; struct ArrayTypeSpecifierError { _priv: (), From 187afa4e4a7efc08c07e60ccdb5ebc1a229776e4 Mon Sep 17 00:00:00 2001 From: Kangzhi Shi Date: Sun, 30 Aug 2020 19:28:26 +0200 Subject: [PATCH 5/9] Implement array __repr__ --- vm/src/stdlib/array.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/vm/src/stdlib/array.rs b/vm/src/stdlib/array.rs index d01773e9d..0ff310d40 100644 --- a/vm/src/stdlib/array.rs +++ b/vm/src/stdlib/array.rs @@ -236,6 +236,20 @@ macro_rules! def_array_enum { } } + 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().map(|x| x.to_string()).join(", ")) + } + })* + }; + Ok(s) + } + fn iter<'a>(&'a self, vm: &'a VirtualMachine) -> impl Iterator + 'a { let mut i = 0; std::iter::from_fn(move || { @@ -443,6 +457,11 @@ impl PyArray { self.borrow_value_mut().setitem(needle, obj, vm) } + #[pymethod(name = "__repr__")] + fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { + zelf.borrow_value().repr(vm) + } + #[pymethod(name = "__eq__")] fn eq(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { let lhs = self.borrow_value(); From 4d888e15c107964cac3dfb07d94ec367aa7aec2e Mon Sep 17 00:00:00 2001 From: Kangzhi Shi Date: Sun, 30 Aug 2020 21:27:37 +0200 Subject: [PATCH 6/9] Remove expectedFailure from array test_repr --- Lib/test/test_array.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 9a121d5f1..a50d1d516 100644 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -497,8 +497,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})) From 6068ab13142f6a1c98221228e111f5ad1e573ed8 Mon Sep 17 00:00:00 2001 From: Kangzhi Shi Date: Sun, 30 Aug 2020 21:32:58 +0200 Subject: [PATCH 7/9] Replace map->join with Itertools::format --- vm/src/stdlib/array.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/stdlib/array.rs b/vm/src/stdlib/array.rs index 0ff310d40..6d0985776 100644 --- a/vm/src/stdlib/array.rs +++ b/vm/src/stdlib/array.rs @@ -243,7 +243,7 @@ macro_rules! def_array_enum { if v.is_empty() { format!("array('{}')", $c) } else { - format!("array('{}', [{}])", $c, v.iter().map(|x| x.to_string()).join(", ")) + format!("array('{}', [{}])", $c, v.iter().format(", ")) } })* }; From aa378b9af7d37ed1b769c15ae15ea21ff076d442 Mon Sep 17 00:00:00 2001 From: hyperbora Date: Mon, 31 Aug 2020 14:19:05 +0900 Subject: [PATCH 8/9] Troubleshooting displaying bytes in the wrong hexadecimal number in certain cases. --- vm/src/bytesinner.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 From 5a21bc2b618cd7826ac78262c33a1a2466fbea25 Mon Sep 17 00:00:00 2001 From: Kangzhi Shi Date: Mon, 31 Aug 2020 08:49:27 +0200 Subject: [PATCH 9/9] Clone only when necessary --- vm/src/stdlib/array.rs | 45 +++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/vm/src/stdlib/array.rs b/vm/src/stdlib/array.rs index 80e52db0b..8773914c4 100644 --- a/vm/src/stdlib/array.rs +++ b/vm/src/stdlib/array.rs @@ -10,8 +10,9 @@ 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; @@ -225,7 +226,7 @@ macro_rules! def_array_enum { } } - fn setitem_by_slice(&mut self, slice: PySliceRef, items: ArrayContentType, vm: &VirtualMachine) -> PyResult<()> { + 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); @@ -243,7 +244,7 @@ macro_rules! def_array_enum { } else { range }; - elements.splice(range, items); + elements.splice(range, items.iter().cloned()); return Ok(()); } @@ -283,11 +284,11 @@ macro_rules! def_array_enum { if slicelen == items.len() { if is_negative_step { - for (i, item) in range.rev().step_by(step).zip(items) { + 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) { + for (i, &item) in range.step_by(step).zip(items) { elements[i] = item; } } @@ -533,26 +534,34 @@ impl PyArray { #[pymethod(magic)] fn setitem( - &self, + zelf: PyRef, needle: Either, obj: PyObjectRef, vm: &VirtualMachine, ) -> PyResult<()> { match needle { - Either::A(i) => self.borrow_value_mut().setitem_by_idx(i, obj, vm), + Either::A(i) => zelf.borrow_value_mut().setitem_by_idx(i, obj, vm), Either::B(slice) => { - let items = match obj.payload::() { - // TODO: is there a way to check ref equality between [self] and [obj] - // so we can do clone() only when they are referring the same PyObject - Some(array) => array.borrow_value().clone(), - None => { - return Err(vm.new_type_error(format!( - "can only assign array (not \"{}\") to array slice", - obj.class().name - ))); + 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 + ))); + } } }; - self.borrow_value_mut().setitem_by_slice(slice, items, vm) + zelf.borrow_value_mut().setitem_by_slice(slice, items, vm) } } }