diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index c79bee4703..4d65c4318c 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -135,7 +135,7 @@ an Abstract Syntax Tree (AST): - The Parser relies on `LALRPOP`, a Rust parser generator framework. The LALRPOP definition of Python's grammar is in `parser/src/python.lalrpop`. - More information on parsers and a tutorial can be found in the - [LALRPOP book](https://lalrpop.github.io/lalrpop/README.html). + [LALRPOP book](https://lalrpop.github.io/lalrpop/). - AST: `ast/` implements in Rust the Python types and expressions represented by the AST nodes. diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index ead224c6a2..cce00c8f59 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -1169,8 +1169,6 @@ def test_product_issue_25021(self): p.__setstate__((0, 0, 0x1000)) # will access tuple element 1 if not clamped self.assertRaises(StopIteration, next, p) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_repeat(self): self.assertEqual(list(repeat(object='a', times=3)), ['a', 'a', 'a']) self.assertEqual(lzip(range(3),repeat('a')), @@ -1197,8 +1195,6 @@ def test_repeat(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): self.pickletest(proto, repeat(object='a', times=10)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_repeat_with_negative_times(self): self.assertEqual(repr(repeat('a', -1)), "repeat('a', 0)") self.assertEqual(repr(repeat('a', -2)), "repeat('a', 0)") @@ -2181,15 +2177,11 @@ def test_tee(self): class LengthTransparency(unittest.TestCase): - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_repeat(self): self.assertEqual(operator.length_hint(repeat(None, 50)), 50) self.assertEqual(operator.length_hint(repeat(None, 0)), 0) self.assertEqual(operator.length_hint(repeat(None), 12), 12) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_repeat_with_negative_times(self): self.assertEqual(operator.length_hint(repeat(None, -1)), 0) self.assertEqual(operator.length_hint(repeat(None, -2)), 0) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index a024dd1675..f508f6e581 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1878,10 +1878,6 @@ def helper(self): if hasattr(os, f): self.check(getattr(os, f)) - # TODO: RUSTPYTHON; io.FileIO(fd) should check if the fd passed is valid - if f == "fdopen": - # this is test_fdopen - helper = unittest.expectedFailure(helper) return helper for f in singles: diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index 8d26c5e1a4..af5c02dcbc 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -201,6 +201,7 @@ impl PySetInner { PySetIterator { dict: PyRc::clone(&self.content), + elements: self.content.keys(), size_info: AtomicCell::new(set_size), } } @@ -763,6 +764,7 @@ struct SetSizeInfo { #[pyclass(module = false, name = "set_iterator")] pub(crate) struct PySetIterator { dict: PyRc, + elements: Vec, size_info: AtomicCell, } @@ -796,8 +798,10 @@ impl PyIter for PySetIterator { if let Some(set_size) = size_info.size { if set_size == zelf.dict.len() { let index = size_info.position; - let keys = zelf.dict.keys(); - let item = keys.get(index).ok_or_else(|| vm.new_stop_iteration())?; + let item = zelf + .elements + .get(index) + .ok_or_else(|| vm.new_stop_iteration())?; size_info.position += 1; zelf.size_info.store(size_info); return Ok(item.clone()); diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index a1b86fd5df..d18d8be303 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -8,6 +8,9 @@ cfg_if::cfg_if! { type Offset = i64; } } + +#[cfg(unix)] +use crate::stdlib::os::{errno_err, PathOrFd}; use crate::VirtualMachine; use crate::{PyObjectRef, PyResult, TryFromObject}; pub(crate) use _io::io_open as open; @@ -3571,6 +3574,12 @@ mod _io { } } + // check file descriptor validity + #[cfg(unix)] + if let Ok(PathOrFd::Fd(fd)) = PathOrFd::try_from_object(vm, file.clone()) { + nix::fcntl::fcntl(fd, nix::fcntl::F_GETFD).map_err(|_| errno_err(vm))?; + } + // Construct a FileIO (subclass of RawIOBase) // This is subsequently consumed by a Buffered Class. let file_io_class = { diff --git a/vm/src/stdlib/itertools.rs b/vm/src/stdlib/itertools.rs index f323814545..54938b3b2f 100644 --- a/vm/src/stdlib/itertools.rs +++ b/vm/src/stdlib/itertools.rs @@ -260,7 +260,7 @@ mod decl { #[derive(Debug)] struct PyItertoolsRepeat { object: PyObjectRef, - times: Option>, + times: Option>, } impl PyValue for PyItertoolsRepeat { @@ -269,40 +269,81 @@ mod decl { } } - #[pyimpl(with(PyIter))] + #[derive(FromArgs)] + struct PyRepeatNewArgs { + #[pyarg(any)] + object: PyObjectRef, + #[pyarg(any, optional)] + times: OptionalArg, + } + + #[pyimpl(with(PyIter), flags(BASETYPE))] impl PyItertoolsRepeat { #[pyslot] fn tp_new( cls: PyTypeRef, - object: PyObjectRef, - times: OptionalArg, + PyRepeatNewArgs { object, times }: PyRepeatNewArgs, vm: &VirtualMachine, ) -> PyResult> { - let times = times - .into_option() - .map(|int| PyRwLock::new(int.as_bigint().clone())); - + let times = match times.into_option() { + Some(int) => { + let val = int.as_bigint(); + if *val > BigInt::from(isize::MAX) { + return Err(vm.new_overflow_error("Cannot fit in isize.".to_owned())); + } + // times always >= 0. + Some(PyRwLock::new(val.to_usize().unwrap_or(0))) + } + None => None, + }; PyItertoolsRepeat { object, times }.into_ref_with_type(vm, cls) } #[pymethod(name = "__length_hint__")] - fn length_hint(&self, vm: &VirtualMachine) -> PyObjectRef { + fn length_hint(&self, vm: &VirtualMachine) -> PyResult { match self.times { - Some(ref times) => vm.ctx.new_int(times.read().clone()), - None => vm.ctx.new_int(0), + Some(ref times) => Ok(vm.ctx.new_int(*times.read())), + // Return TypeError, length_hint picks this up and returns the default. + None => Err(vm.new_type_error("length of unsized object.".to_owned())), } } + + #[pymethod(magic)] + fn reduce(zelf: PyRef, vm: &VirtualMachine) -> PyResult { + let cls = zelf.clone_class().into_pyobject(vm); + Ok(match zelf.times { + Some(ref times) => vm.ctx.new_tuple(vec![ + cls, + vm.ctx.new_tuple(vec![ + zelf.object.clone(), + vm.ctx.new_int(*times.read()).into_pyobject(vm), + ]), + ]), + None => vm + .ctx + .new_tuple(vec![cls, vm.ctx.new_tuple(vec![zelf.object.clone()])]), + }) + } + + #[pymethod(magic)] + fn repr(&self, vm: &VirtualMachine) -> PyResult { + let mut fmt = format!("{}", vm.to_repr(&self.object)?); + if let Some(ref times) = self.times { + fmt.push_str(&format!(", {}", times.read())); + } + Ok(format!("repeat({})", fmt)) + } } + impl PyIter for PyItertoolsRepeat { fn next(zelf: &PyRef, vm: &VirtualMachine) -> PyResult { if let Some(ref times) = zelf.times { let mut times = times.write(); - if !times.is_positive() { + if *times == 0 { return Err(vm.new_stop_iteration()); } *times -= 1; } - Ok(zelf.object.clone()) } } diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index e3b8537107..24a883cf11 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -217,7 +217,7 @@ impl TryFromObject for PyPathLike { } } -enum PathOrFd { +pub(crate) enum PathOrFd { Path(PyPathLike), Fd(i32), }