diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index b4119305f99..183caa898ef 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1358,7 +1358,6 @@ def test_open_default_encoding(self): os.environ.clear() os.environ.update(old_environ) - @unittest.expectedFailureIfWindows('TODO: RUSTPYTHON Windows') @support.requires_subprocess() def test_open_non_inheritable(self): fileobj = open(__file__, encoding="utf-8") diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index c86d910eef6..6f111b2ffcd 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2102,7 +2102,6 @@ def test_urandom_failure(self): """ assert_python_ok('-c', code) - @unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; on Windows (ModuleNotFoundError: No module named 'os')") def test_urandom_fd_closed(self): # Issue #21207: urandom() should reopen its fd to /dev/urandom if # closed. @@ -4621,7 +4620,6 @@ def test_process_cpu_count_affinity(self): # FD inheritance check is only useful for systems with process support. @support.requires_subprocess() class FDInheritanceTests(unittest.TestCase): - @unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.get_inheritable not implemented yet for all platforms') def test_get_set_inheritable(self): fd = os.open(__file__, os.O_RDONLY) self.addCleanup(os.close, fd) @@ -4682,7 +4680,6 @@ def test_get_set_inheritable_badf(self): os.set_inheritable(fd, False) self.assertEqual(ctx.exception.errno, errno.EBADF) - @unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.get_inheritable not implemented yet for all platforms') def test_open(self): fd = os.open(__file__, os.O_RDONLY) self.addCleanup(os.close, fd) @@ -4696,7 +4693,6 @@ def test_pipe(self): self.assertEqual(os.get_inheritable(rfd), False) self.assertEqual(os.get_inheritable(wfd), False) - @unittest.skipIf(sys.platform == 'win32', 'TODO: RUSTPYTHON; os.dup on windows') def test_dup(self): fd1 = os.open(__file__, os.O_RDONLY) self.addCleanup(os.close, fd1) @@ -4705,13 +4701,11 @@ def test_dup(self): self.addCleanup(os.close, fd2) self.assertEqual(os.get_inheritable(fd2), False) - @unittest.skipIf(sys.platform == 'win32', 'TODO: RUSTPYTHON; os.dup on windows') def test_dup_standard_stream(self): fd = os.dup(1) self.addCleanup(os.close, fd) self.assertGreater(fd, 0) - @unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.dup not implemented yet for all platforms') @unittest.skipUnless(sys.platform == 'win32', 'win32-specific test') def test_dup_nul(self): # os.dup() was creating inheritable fds for character files. diff --git a/crates/vm/src/stdlib/nt.rs b/crates/vm/src/stdlib/nt.rs index e2d4b41cc3c..0464fa42f30 100644 --- a/crates/vm/src/stdlib/nt.rs +++ b/crates/vm/src/stdlib/nt.rs @@ -14,7 +14,7 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef { pub(crate) mod module { use crate::{ PyResult, TryFromObject, VirtualMachine, - builtins::{PyDictRef, PyListRef, PyStrRef, PyTupleRef}, + builtins::{PyBaseExceptionRef, PyDictRef, PyListRef, PyStrRef, PyTupleRef}, common::{crt_fd, os::last_os_error, suppress_iph}, convert::ToPyException, function::{Either, OptionalArg}, @@ -391,6 +391,13 @@ pub(crate) mod module { } } + #[pyfunction] + fn get_inheritable(fd: i32, vm: &VirtualMachine) -> PyResult { + let borrowed = unsafe { crt_fd::Borrowed::borrow_raw(fd) }; + let handle = crt_fd::as_handle(borrowed).map_err(|e| e.to_pyexception(vm))?; + get_handle_inheritable(handle.as_raw_handle() as _, vm) + } + #[pyfunction] fn getlogin(vm: &VirtualMachine) -> PyResult { let mut buffer = [0u16; 257]; @@ -477,6 +484,15 @@ pub(crate) mod module { unsafe extern "C" { fn _umask(mask: i32) -> i32; + fn _dup(fd: i32) -> i32; + fn _dup2(fd: i32, fd2: i32) -> i32; + } + + /// Close fd and convert error to PyException (PEP 446 cleanup) + #[cold] + fn close_fd_and_raise(fd: i32, err: std::io::Error, vm: &VirtualMachine) -> PyBaseExceptionRef { + let _ = unsafe { crt_fd::Owned::from_raw(fd) }; + err.to_pyexception(vm) } #[pyfunction] @@ -489,6 +505,45 @@ pub(crate) mod module { } } + #[pyfunction] + fn dup(fd: i32, vm: &VirtualMachine) -> PyResult { + let fd2 = unsafe { suppress_iph!(_dup(fd)) }; + if fd2 < 0 { + return Err(errno_err(vm)); + } + let borrowed = unsafe { crt_fd::Borrowed::borrow_raw(fd2) }; + let handle = crt_fd::as_handle(borrowed).map_err(|e| close_fd_and_raise(fd2, e, vm))?; + raw_set_handle_inheritable(handle.as_raw_handle() as _, false) + .map_err(|e| close_fd_and_raise(fd2, e, vm))?; + Ok(fd2) + } + + #[derive(FromArgs)] + struct Dup2Args { + #[pyarg(positional)] + fd: i32, + #[pyarg(positional)] + fd2: i32, + #[pyarg(any, default = true)] + inheritable: bool, + } + + #[pyfunction] + fn dup2(args: Dup2Args, vm: &VirtualMachine) -> PyResult { + let result = unsafe { suppress_iph!(_dup2(args.fd, args.fd2)) }; + if result < 0 { + return Err(errno_err(vm)); + } + if !args.inheritable { + let borrowed = unsafe { crt_fd::Borrowed::borrow_raw(args.fd2) }; + let handle = + crt_fd::as_handle(borrowed).map_err(|e| close_fd_and_raise(args.fd2, e, vm))?; + raw_set_handle_inheritable(handle.as_raw_handle() as _, false) + .map_err(|e| close_fd_and_raise(args.fd2, e, vm))?; + } + Ok(args.fd2) + } + pub(crate) fn support_funcs() -> Vec { Vec::new() }