From c0b12c0c8e1b70c1972ee5f1f0665d4cf7704df6 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Sun, 12 Apr 2026 04:53:11 +0900 Subject: [PATCH 1/2] Create rustpython-host-env crate; move host abstractions out of common Move os, crt_fd, fileutils, windows, macros modules from rustpython-common into the new rustpython-host-env crate. This isolates host OS API wrappers behind a crate boundary with zero Python runtime dependency. - Add crates/host_env to workspace - Drop nix, windows-sys, widestring deps from common - Wire vm and stdlib to depend on rustpython-host-env - Migrate all imports from common::{os,crt_fd,fileutils,windows} to rustpython_host_env:: --- Cargo.lock | 17 +++++++++-- Cargo.toml | 1 + crates/common/Cargo.toml | 14 --------- crates/common/src/lib.rs | 12 -------- crates/host_env/Cargo.toml | 32 ++++++++++++++++++++ crates/{common => host_env}/src/crt_fd.rs | 0 crates/{common => host_env}/src/fileutils.rs | 0 crates/host_env/src/lib.rs | 16 ++++++++++ crates/{common => host_env}/src/macros.rs | 0 crates/{common => host_env}/src/os.rs | 0 crates/{common => host_env}/src/windows.rs | 0 crates/stdlib/Cargo.toml | 1 + crates/stdlib/src/faulthandler.rs | 2 +- crates/stdlib/src/mmap.rs | 4 +-- crates/stdlib/src/openssl.rs | 22 +++++++------- crates/stdlib/src/overlapped.rs | 2 +- crates/stdlib/src/posixshmem.rs | 8 ++--- crates/stdlib/src/socket.rs | 12 ++++---- crates/stdlib/src/termios.rs | 2 +- crates/vm/Cargo.toml | 1 + crates/vm/src/exceptions.rs | 4 +-- crates/vm/src/function/fspath.rs | 2 +- crates/vm/src/lib.rs | 1 + crates/vm/src/ospath.rs | 2 +- crates/vm/src/stdlib/_codecs.rs | 6 ++-- crates/vm/src/stdlib/_ctypes/function.rs | 6 ++-- crates/vm/src/stdlib/_io.rs | 9 +++--- crates/vm/src/stdlib/_signal.rs | 7 +++-- crates/vm/src/stdlib/_winapi.rs | 3 +- crates/vm/src/stdlib/msvcrt.rs | 2 +- crates/vm/src/stdlib/nt.rs | 18 +++++------ crates/vm/src/stdlib/os.rs | 32 +++++++++----------- crates/vm/src/stdlib/posix.rs | 10 +++--- crates/vm/src/stdlib/posix_compat.rs | 2 +- crates/vm/src/stdlib/sys.rs | 2 +- crates/vm/src/stdlib/time.rs | 4 +-- crates/vm/src/stdlib/winreg.rs | 2 +- crates/vm/src/stdlib/winsound.rs | 2 +- crates/vm/src/vm/vm_new.rs | 2 +- crates/vm/src/windows.rs | 12 ++++---- examples/generator.rs | 2 +- examples/package_embed.rs | 2 +- src/lib.rs | 2 +- 43 files changed, 158 insertions(+), 122 deletions(-) create mode 100644 crates/host_env/Cargo.toml rename crates/{common => host_env}/src/crt_fd.rs (100%) rename crates/{common => host_env}/src/fileutils.rs (100%) create mode 100644 crates/host_env/src/lib.rs rename crates/{common => host_env}/src/macros.rs (100%) rename crates/{common => host_env}/src/os.rs (100%) rename crates/{common => host_env}/src/windows.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 20336689a32..c4753ecd21f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3142,7 +3142,6 @@ dependencies = [ "malachite-base", "malachite-bigint", "malachite-q", - "nix 0.30.1", "num-complex", "num-traits", "parking_lot", @@ -3151,8 +3150,6 @@ dependencies = [ "rustpython-wtf8", "siphasher", "unicode_names2 2.0.0", - "widestring", - "windows-sys 0.61.2", ] [[package]] @@ -3221,6 +3218,18 @@ dependencies = [ "phf 0.13.1", ] +[[package]] +name = "rustpython-host-env" +version = "0.5.0" +dependencies = [ + "libc", + "nix 0.30.1", + "num-traits", + "rustpython-wtf8", + "widestring", + "windows-sys 0.61.2", +] + [[package]] name = "rustpython-jit" version = "0.5.0" @@ -3407,6 +3416,7 @@ dependencies = [ "rustls-platform-verifier", "rustpython-common", "rustpython-derive", + "rustpython-host-env", "rustpython-ruff_python_ast", "rustpython-ruff_python_parser", "rustpython-ruff_source_file", @@ -3486,6 +3496,7 @@ dependencies = [ "rustpython-compiler", "rustpython-compiler-core", "rustpython-derive", + "rustpython-host-env", "rustpython-jit", "rustpython-literal", "rustpython-ruff_python_ast", diff --git a/Cargo.toml b/Cargo.toml index 7bd8b8f3374..2df67583ad7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,6 +145,7 @@ rustpython-compiler-core = { path = "crates/compiler-core", version = "0.5.0" } rustpython-compiler = { path = "crates/compiler", version = "0.5.0" } rustpython-codegen = { path = "crates/codegen", version = "0.5.0" } rustpython-common = { path = "crates/common", version = "0.5.0" } +rustpython-host-env = { path = "crates/host_env", version = "0.5.0" } rustpython-derive = { path = "crates/derive", version = "0.5.0" } rustpython-derive-impl = { path = "crates/derive-impl", version = "0.5.0" } rustpython-jit = { path = "crates/jit", version = "0.5.0" } diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 555336f059a..d93ac6b9ecd 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -36,19 +36,5 @@ lock_api = "0.4" siphasher = "1" num-complex.workspace = true -[target.'cfg(unix)'.dependencies] -nix = { workspace = true } - -[target.'cfg(windows)'.dependencies] -widestring = { workspace = true } -windows-sys = { workspace = true, features = [ - "Win32_Foundation", - "Win32_Networking_WinSock", - "Win32_Storage_FileSystem", - "Win32_System_Ioctl", - "Win32_System_LibraryLoader", - "Win32_System_SystemServices", -] } - [lints] workspace = true diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index e514c17541f..f3dca3689ff 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -4,34 +4,22 @@ extern crate alloc; -#[macro_use] -mod macros; -pub use macros::*; - pub mod atomic; pub mod borrow; pub mod boxvec; pub mod cformat; -#[cfg(all(feature = "std", any(unix, windows, target_os = "wasi")))] -pub mod crt_fd; pub mod encodings; -#[cfg(all(feature = "std", any(not(target_arch = "wasm32"), target_os = "wasi")))] -pub mod fileutils; pub mod float_ops; pub mod format; pub mod hash; pub mod int; pub mod linked_list; pub mod lock; -#[cfg(feature = "std")] -pub mod os; pub mod rand; pub mod rc; pub mod refcount; pub mod static_cell; pub mod str; -#[cfg(all(feature = "std", windows))] -pub mod windows; pub use rustpython_wtf8 as wtf8; diff --git a/crates/host_env/Cargo.toml b/crates/host_env/Cargo.toml new file mode 100644 index 00000000000..d405f794c7d --- /dev/null +++ b/crates/host_env/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "rustpython-host-env" +description = "Host OS API abstractions for RustPython" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +rustpython-wtf8 = { workspace = true } + +libc = { workspace = true } +num-traits = { workspace = true } + +[target.'cfg(unix)'.dependencies] +nix = { workspace = true } + +[target.'cfg(windows)'.dependencies] +widestring = { workspace = true } +windows-sys = { workspace = true, features = [ + "Win32_Foundation", + "Win32_Networking_WinSock", + "Win32_Storage_FileSystem", + "Win32_System_Ioctl", + "Win32_System_LibraryLoader", + "Win32_System_SystemServices", +] } + +[lints] +workspace = true diff --git a/crates/common/src/crt_fd.rs b/crates/host_env/src/crt_fd.rs similarity index 100% rename from crates/common/src/crt_fd.rs rename to crates/host_env/src/crt_fd.rs diff --git a/crates/common/src/fileutils.rs b/crates/host_env/src/fileutils.rs similarity index 100% rename from crates/common/src/fileutils.rs rename to crates/host_env/src/fileutils.rs diff --git a/crates/host_env/src/lib.rs b/crates/host_env/src/lib.rs new file mode 100644 index 00000000000..b2c4ddacf6d --- /dev/null +++ b/crates/host_env/src/lib.rs @@ -0,0 +1,16 @@ +extern crate alloc; + +#[macro_use] +mod macros; +pub use macros::*; + +pub mod os; + +#[cfg(any(unix, windows, target_os = "wasi"))] +pub mod crt_fd; + +#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))] +pub mod fileutils; + +#[cfg(windows)] +pub mod windows; diff --git a/crates/common/src/macros.rs b/crates/host_env/src/macros.rs similarity index 100% rename from crates/common/src/macros.rs rename to crates/host_env/src/macros.rs diff --git a/crates/common/src/os.rs b/crates/host_env/src/os.rs similarity index 100% rename from crates/common/src/os.rs rename to crates/host_env/src/os.rs diff --git a/crates/common/src/windows.rs b/crates/host_env/src/windows.rs similarity index 100% rename from crates/common/src/windows.rs rename to crates/host_env/src/windows.rs diff --git a/crates/stdlib/Cargo.toml b/crates/stdlib/Cargo.toml index f828507d6cf..5739a3f4a97 100644 --- a/crates/stdlib/Cargo.toml +++ b/crates/stdlib/Cargo.toml @@ -30,6 +30,7 @@ flame-it = ["flame"] rustpython-derive = { workspace = true } rustpython-vm = { workspace = true, default-features = false, features = ["compiler"]} rustpython-common = { workspace = true } +rustpython-host-env = { workspace = true } ruff_python_parser = { workspace = true } ruff_python_ast = { workspace = true } diff --git a/crates/stdlib/src/faulthandler.rs b/crates/stdlib/src/faulthandler.rs index 9c4373c312e..96e024e4c62 100644 --- a/crates/stdlib/src/faulthandler.rs +++ b/crates/stdlib/src/faulthandler.rs @@ -13,7 +13,7 @@ mod decl { use core::time::Duration; use parking_lot::{Condvar, Mutex}; #[cfg(any(unix, windows))] - use rustpython_common::os::{get_errno, set_errno}; + use rustpython_host_env::os::{get_errno, set_errno}; use std::thread; /// fault_handler_t diff --git a/crates/stdlib/src/mmap.rs b/crates/stdlib/src/mmap.rs index a99d0cc131d..c492c960750 100644 --- a/crates/stdlib/src/mmap.rs +++ b/crates/stdlib/src/mmap.rs @@ -30,10 +30,10 @@ mod mmap { #[cfg(unix)] use nix::{sys::stat::fstat, unistd}; #[cfg(unix)] - use rustpython_common::crt_fd; + use rustpython_host_env::crt_fd; #[cfg(windows)] - use rustpython_common::suppress_iph; + use rustpython_host_env::suppress_iph; #[cfg(windows)] use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle, RawHandle}; #[cfg(windows)] diff --git a/crates/stdlib/src/openssl.rs b/crates/stdlib/src/openssl.rs index 00461f1b468..572fb96e354 100644 --- a/crates/stdlib/src/openssl.rs +++ b/crates/stdlib/src/openssl.rs @@ -1668,17 +1668,17 @@ mod _ssl { // Open the file using fopen (cross-platform) let fp = - rustpython_common::fileutils::fopen(path.as_path(), "rb").map_err(|e| { - match e.kind() { - std::io::ErrorKind::NotFound => vm - .new_os_subtype_error( - vm.ctx.exceptions.file_not_found_error.to_owned(), - Some(libc::ENOENT), - e.to_string(), - ) - .upcast(), - _ => vm.new_os_error(e.to_string()), - } + rustpython_host_env::fileutils::fopen(path.as_path(), "rb").map_err(|e| match e + .kind() + { + std::io::ErrorKind::NotFound => vm + .new_os_subtype_error( + vm.ctx.exceptions.file_not_found_error.to_owned(), + Some(libc::ENOENT), + e.to_string(), + ) + .upcast(), + _ => vm.new_os_error(e.to_string()), })?; // Read DH parameters diff --git a/crates/stdlib/src/overlapped.rs b/crates/stdlib/src/overlapped.rs index 76d18cb7a9a..2230991b643 100644 --- a/crates/stdlib/src/overlapped.rs +++ b/crates/stdlib/src/overlapped.rs @@ -283,7 +283,7 @@ mod _overlapped { } else { err }; - let errno = crate::vm::common::os::winerror_to_errno(err as i32); + let errno = rustpython_host_env::os::winerror_to_errno(err as i32); let message = std::io::Error::from_raw_os_error(err as i32).to_string(); let exc = vm.new_errno_error(errno, message); let _ = exc diff --git a/crates/stdlib/src/posixshmem.rs b/crates/stdlib/src/posixshmem.rs index f5481619bba..da1a8cf1d89 100644 --- a/crates/stdlib/src/posixshmem.rs +++ b/crates/stdlib/src/posixshmem.rs @@ -6,12 +6,10 @@ pub(crate) use _posixshmem::module_def; mod _posixshmem { use alloc::ffi::CString; - use crate::{ - common::os::errno_io_error, - vm::{ - FromArgs, PyResult, VirtualMachine, builtins::PyUtf8StrRef, convert::IntoPyException, - }, + use crate::vm::{ + FromArgs, PyResult, VirtualMachine, builtins::PyUtf8StrRef, convert::IntoPyException, }; + use rustpython_host_env::os::errno_io_error; #[derive(FromArgs)] struct ShmOpenArgs { diff --git a/crates/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs index d3fe59144ef..fe2aa6be3da 100644 --- a/crates/stdlib/src/socket.rs +++ b/crates/stdlib/src/socket.rs @@ -14,7 +14,6 @@ mod _socket { PyBaseExceptionRef, PyListRef, PyModule, PyOSError, PyStrRef, PyTupleRef, PyTypeRef, PyUtf8StrRef, }, - common::os::ErrorExt, convert::{IntoPyException, ToPyObject, TryFromBorrowedObject, TryFromObject}, function::{ ArgBytesLike, ArgIntoFloat, ArgMemoryBuffer, ArgStrOrBytesLike, Either, FsPath, @@ -23,6 +22,7 @@ mod _socket { types::{Constructor, DefaultConstructor, Destructor, Initializer, Representable}, utils::ToCString, }; + use rustpython_host_env::os::ErrorExt; pub(crate) fn module_exec(vm: &VirtualMachine, module: &Py) -> PyResult<()> { #[cfg(windows)] @@ -2270,7 +2270,7 @@ mod _socket { ) }; if ret < 0 { - return Err(crate::common::os::errno_io_error().into()); + return Err(rustpython_host_env::os::errno_io_error().into()); } Ok(vm.ctx.new_int(flag).into()) } else { @@ -2291,7 +2291,7 @@ mod _socket { ) }; if ret < 0 { - return Err(crate::common::os::errno_io_error().into()); + return Err(rustpython_host_env::os::errno_io_error().into()); } buf.truncate(buflen as usize); Ok(vm.ctx.new_bytes(buf).into()) @@ -2332,7 +2332,7 @@ mod _socket { } }; if ret < 0 { - Err(crate::common::os::errno_io_error().into()) + Err(rustpython_host_env::os::errno_io_error().into()) } else { Ok(()) } @@ -3084,7 +3084,7 @@ mod _socket { fn if_nametoindex(name: FsPath, vm: &VirtualMachine) -> PyResult { let name = name.to_cstring(vm)?; // in case 'if_nametoindex' does not set errno - crate::common::os::set_errno(libc::ENODEV); + rustpython_host_env::os::set_errno(libc::ENODEV); let ret = unsafe { c::if_nametoindex(name.as_ptr() as _) }; if ret == 0 { Err(vm.new_last_errno_error()) @@ -3098,7 +3098,7 @@ mod _socket { fn if_indextoname(index: IfIndex, vm: &VirtualMachine) -> PyResult { let mut buf = [0; c::IF_NAMESIZE + 1]; // in case 'if_indextoname' does not set errno - crate::common::os::set_errno(libc::ENXIO); + rustpython_host_env::os::set_errno(libc::ENXIO); let ret = unsafe { c::if_indextoname(index, buf.as_mut_ptr()) }; if ret.is_null() { Err(vm.new_last_errno_error()) diff --git a/crates/stdlib/src/termios.rs b/crates/stdlib/src/termios.rs index de402724434..8f7963c159f 100644 --- a/crates/stdlib/src/termios.rs +++ b/crates/stdlib/src/termios.rs @@ -7,9 +7,9 @@ mod termios { use crate::vm::{ PyObjectRef, PyResult, TryFromObject, VirtualMachine, builtins::{PyBaseExceptionRef, PyBytes, PyInt, PyListRef, PyTypeRef}, - common::os::ErrorExt, convert::ToPyObject, }; + use rustpython_host_env::os::ErrorExt; use termios::Termios; // TODO: more ioctl numbers diff --git a/crates/vm/Cargo.toml b/crates/vm/Cargo.toml index b721418a4cc..0d02a89a889 100644 --- a/crates/vm/Cargo.toml +++ b/crates/vm/Cargo.toml @@ -33,6 +33,7 @@ rustpython-compiler = { workspace = true, optional = true } rustpython-codegen = { workspace = true, optional = true } rustpython-common = { workspace = true } rustpython-derive = { workspace = true } +rustpython-host-env = { workspace = true } rustpython-jit = { workspace = true, optional = true } ruff_python_ast = { workspace = true, optional = true } diff --git a/crates/vm/src/exceptions.rs b/crates/vm/src/exceptions.rs index 1f82c3cd72c..6a90a160ff5 100644 --- a/crates/vm/src/exceptions.rs +++ b/crates/vm/src/exceptions.rs @@ -1380,7 +1380,7 @@ impl IntoPyException for OSErrorBuilder { impl ToOSErrorBuilder for std::io::Error { fn to_os_error_builder(&self, vm: &VirtualMachine) -> OSErrorBuilder { - use crate::common::os::ErrorExt; + use crate::host_env::os::ErrorExt; let errno = self.posix_errno(); #[cfg(windows)] @@ -1937,7 +1937,7 @@ pub(super) mod types { .as_ref() .and_then(|w| w.downcast_ref::()) .and_then(|w| w.try_to_primitive::(vm).ok()) - .map(crate::common::os::winerror_to_errno) + .map(crate::host_env::os::winerror_to_errno) { let errno_obj = vm.new_pyobj(errno); let _ = unsafe { exc.errno.swap(Some(errno_obj.clone())) }; diff --git a/crates/vm/src/function/fspath.rs b/crates/vm/src/function/fspath.rs index 732fd0ca35a..c3ec1221627 100644 --- a/crates/vm/src/function/fspath.rs +++ b/crates/vm/src/function/fspath.rs @@ -123,7 +123,7 @@ impl FsPath { } pub fn bytes_as_os_str<'a>(b: &'a [u8], vm: &VirtualMachine) -> PyResult<&'a std::ffi::OsStr> { - rustpython_common::os::bytes_as_os_str(b) + rustpython_host_env::os::bytes_as_os_str(b) .map_err(|_| vm.new_unicode_decode_error("can't decode path for utf-8")) } } diff --git a/crates/vm/src/lib.rs b/crates/vm/src/lib.rs index 21762466f13..78669d2fce2 100644 --- a/crates/vm/src/lib.rs +++ b/crates/vm/src/lib.rs @@ -105,6 +105,7 @@ pub use self::vm::{Context, Interpreter, InterpreterBuilder, Settings, VirtualMa pub use rustpython_common as common; pub use rustpython_compiler_core::{bytecode, frozen}; +pub use rustpython_host_env as host_env; pub use rustpython_literal as literal; #[doc(hidden)] diff --git a/crates/vm/src/ospath.rs b/crates/vm/src/ospath.rs index d3123a87acb..15cda4b82b5 100644 --- a/crates/vm/src/ospath.rs +++ b/crates/vm/src/ospath.rs @@ -1,4 +1,4 @@ -use rustpython_common::crt_fd; +use rustpython_host_env::crt_fd; use crate::{ AsObject, PyObjectRef, PyResult, VirtualMachine, diff --git a/crates/vm/src/stdlib/_codecs.rs b/crates/vm/src/stdlib/_codecs.rs index 39ebb3599bd..9253823d459 100644 --- a/crates/vm/src/stdlib/_codecs.rs +++ b/crates/vm/src/stdlib/_codecs.rs @@ -387,7 +387,7 @@ mod _codecs_windows { #[pyfunction] fn mbcs_encode(args: MbcsEncodeArgs, vm: &VirtualMachine) -> PyResult<(Vec, usize)> { - use crate::common::windows::ToWideString; + use crate::host_env::windows::ToWideString; use windows_sys::Win32::Globalization::{ CP_ACP, WC_NO_BEST_FIT_CHARS, WideCharToMultiByte, }; @@ -573,7 +573,7 @@ mod _codecs_windows { #[pyfunction] fn oem_encode(args: OemEncodeArgs, vm: &VirtualMachine) -> PyResult<(Vec, usize)> { - use crate::common::windows::ToWideString; + use crate::host_env::windows::ToWideString; use windows_sys::Win32::Globalization::{ CP_OEMCP, WC_NO_BEST_FIT_CHARS, WideCharToMultiByte, }; @@ -1049,7 +1049,7 @@ mod _codecs_windows { args: CodePageEncodeArgs, vm: &VirtualMachine, ) -> PyResult<(Vec, usize)> { - use crate::common::windows::ToWideString; + use crate::host_env::windows::ToWideString; if args.code_page < 0 { return Err(vm.new_value_error("invalid code page number")); diff --git a/crates/vm/src/stdlib/_ctypes/function.rs b/crates/vm/src/stdlib/_ctypes/function.rs index e28fd91abbc..9136304c296 100644 --- a/crates/vm/src/stdlib/_ctypes/function.rs +++ b/crates/vm/src/stdlib/_ctypes/function.rs @@ -2154,7 +2154,7 @@ unsafe extern "C" fn thunk_callback( // Swap errno before call if FUNCFLAG_USE_ERRNO is set let use_errno = userdata.flags & StgInfoFlags::FUNCFLAG_USE_ERRNO.bits() != 0; let saved_errno = if use_errno { - let current = rustpython_common::os::get_errno(); + let current = rustpython_host_env::os::get_errno(); // TODO: swap with ctypes stored errno (thread-local) Some(current) } else { @@ -2175,10 +2175,10 @@ unsafe extern "C" fn thunk_callback( // Swap errno back after call if use_errno { - let _current = rustpython_common::os::get_errno(); + let _current = rustpython_host_env::os::get_errno(); // TODO: store current errno to ctypes storage if let Some(saved) = saved_errno { - rustpython_common::os::set_errno(saved); + rustpython_host_env::os::set_errno(saved); } } diff --git a/crates/vm/src/stdlib/_io.rs b/crates/vm/src/stdlib/_io.rs index c238dda3725..15111952d56 100644 --- a/crates/vm/src/stdlib/_io.rs +++ b/crates/vm/src/stdlib/_io.rs @@ -7,7 +7,7 @@ pub(crate) use _io::reinit_std_streams_after_fork; cfg_if::cfg_if! { if #[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))] { - use crate::common::crt_fd::Offset; + use rustpython_host_env::crt_fd::Offset; } else { type Offset = i64; } @@ -5323,11 +5323,12 @@ mod _io { #[pymodule] mod fileio { use super::{_io::*, Offset, iobase_finalize}; + use crate::host_env::crt_fd; use crate::{ AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, builtins::{PyBaseExceptionRef, PyUtf8Str, PyUtf8StrRef}, - common::{crt_fd, wtf8::Wtf8Buf}, + common::wtf8::Wtf8Buf, convert::{IntoPyException, ToPyException}, exceptions::OSErrorBuilder, function::{ArgBytesLike, ArgMemoryBuffer, OptionalArg, OptionalOption}, @@ -5554,7 +5555,7 @@ mod fileio { // TODO: _Py_set_inheritable - let fd_fstat = crate::common::fileutils::fstat(fd); + let fd_fstat = rustpython_host_env::fileutils::fstat(fd); #[cfg(windows)] { @@ -6017,7 +6018,7 @@ mod winconsoleio { const BUFMAX: usize = 32 * 1024 * 1024; fn handle_from_fd(fd: i32) -> HANDLE { - unsafe { rustpython_common::suppress_iph!(libc::get_osfhandle(fd)) as HANDLE } + unsafe { rustpython_host_env::suppress_iph!(libc::get_osfhandle(fd)) as HANDLE } } fn is_invalid_handle(handle: HANDLE) -> bool { diff --git a/crates/vm/src/stdlib/_signal.rs b/crates/vm/src/stdlib/_signal.rs index a69d766ce51..eddf8ce8e46 100644 --- a/crates/vm/src/stdlib/_signal.rs +++ b/crates/vm/src/stdlib/_signal.rs @@ -404,16 +404,17 @@ pub(crate) mod _signal { let fd_i32 = i32::try_from(fd).map_err(|_| vm.new_value_error("invalid fd"))?; // Verify the fd is valid by trying to fstat it let borrowed_fd = - unsafe { crate::common::crt_fd::Borrowed::try_borrow_raw(fd_i32) } + unsafe { rustpython_host_env::crt_fd::Borrowed::try_borrow_raw(fd_i32) } .map_err(|e| e.into_pyexception(vm))?; - crate::common::fileutils::fstat(borrowed_fd).map_err(|e| e.into_pyexception(vm))?; + rustpython_host_env::fileutils::fstat(borrowed_fd) + .map_err(|e| e.into_pyexception(vm))?; } is_socket } else { false }; #[cfg(unix)] - if let Ok(fd) = unsafe { crate::common::crt_fd::Borrowed::try_borrow_raw(fd) } { + if let Ok(fd) = unsafe { rustpython_host_env::crt_fd::Borrowed::try_borrow_raw(fd) } { use nix::fcntl; let oflags = fcntl::fcntl(fd, fcntl::F_GETFL).map_err(|e| e.into_pyexception(vm))?; let nonblock = diff --git a/crates/vm/src/stdlib/_winapi.rs b/crates/vm/src/stdlib/_winapi.rs index b210badd00a..bc560eb92bc 100644 --- a/crates/vm/src/stdlib/_winapi.rs +++ b/crates/vm/src/stdlib/_winapi.rs @@ -8,7 +8,7 @@ mod _winapi { use crate::{ Py, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, builtins::PyStrRef, - common::{lock::PyMutex, windows::ToWideString}, + common::lock::PyMutex, convert::{ToPyException, ToPyResult}, function::{ArgMapping, ArgSequence, OptionalArg}, types::Constructor, @@ -16,6 +16,7 @@ mod _winapi { }; use core::ptr::{null, null_mut}; use rustpython_common::wtf8::Wtf8Buf; + use rustpython_host_env::windows::ToWideString; use windows_sys::Win32::Foundation::{HANDLE, MAX_PATH}; #[pyattr] diff --git a/crates/vm/src/stdlib/msvcrt.rs b/crates/vm/src/stdlib/msvcrt.rs index 93364ea3596..46beb306fc6 100644 --- a/crates/vm/src/stdlib/msvcrt.rs +++ b/crates/vm/src/stdlib/msvcrt.rs @@ -7,8 +7,8 @@ mod msvcrt { use crate::{ PyRef, PyResult, VirtualMachine, builtins::{PyBytes, PyStrRef}, - common::{crt_fd, suppress_iph}, convert::IntoPyException, + host_env::{crt_fd, suppress_iph}, }; use itertools::Itertools; use std::os::windows::io::AsRawHandle; diff --git a/crates/vm/src/stdlib/nt.rs b/crates/vm/src/stdlib/nt.rs index 5dd4cf4f001..97f5b310238 100644 --- a/crates/vm/src/stdlib/nt.rs +++ b/crates/vm/src/stdlib/nt.rs @@ -10,10 +10,10 @@ pub(crate) mod module { builtins::{ PyBaseExceptionRef, PyBytes, PyDictRef, PyListRef, PyStr, PyStrRef, PyTupleRef, }, - common::{crt_fd, suppress_iph, windows::ToWideString}, convert::ToPyException, exceptions::OSErrorBuilder, function::{ArgMapping, Either, OptionalArg}, + host_env::{crt_fd, suppress_iph, windows::ToWideString}, ospath::{OsPath, OsPathOrFd}, stdlib::os::{_os, DirFd, SupportFunc, TargetIsDirectory}, }; @@ -411,7 +411,7 @@ pub(crate) mod module { /// Uses FindFirstFileW to get the name as stored on the filesystem. #[pyfunction] fn _findfirstfile(path: OsPath, vm: &VirtualMachine) -> PyResult { - use crate::common::windows::ToWideString; + use crate::host_env::windows::ToWideString; use std::os::windows::ffi::OsStringExt; use windows_sys::Win32::Storage::FileSystem::{ FindClose, FindFirstFileW, WIN32_FIND_DATAW, @@ -575,10 +575,10 @@ pub(crate) mod module { /// _testFileTypeByName - test file type by path name fn _test_file_type_by_name(path: &std::path::Path, tested_type: u32) -> bool { - use crate::common::fileutils::windows::{ + use crate::host_env::fileutils::windows::{ FILE_INFO_BY_NAME_CLASS, get_file_information_by_name, }; - use crate::common::windows::ToWideString; + use crate::host_env::windows::ToWideString; use windows_sys::Win32::Foundation::{CloseHandle, INVALID_HANDLE_VALUE}; use windows_sys::Win32::Storage::FileSystem::{ CreateFileW, FILE_ATTRIBUTE_REPARSE_POINT, FILE_FLAG_BACKUP_SEMANTICS, @@ -670,10 +670,10 @@ pub(crate) mod module { /// _testFileExistsByName - test if path exists fn _test_file_exists_by_name(path: &std::path::Path, follow_links: bool) -> bool { - use crate::common::fileutils::windows::{ + use crate::host_env::fileutils::windows::{ FILE_INFO_BY_NAME_CLASS, get_file_information_by_name, }; - use crate::common::windows::ToWideString; + use crate::host_env::windows::ToWideString; use windows_sys::Win32::Foundation::{CloseHandle, INVALID_HANDLE_VALUE}; use windows_sys::Win32::Storage::FileSystem::{ CreateFileW, FILE_ATTRIBUTE_REPARSE_POINT, FILE_FLAG_BACKUP_SEMANTICS, @@ -761,7 +761,7 @@ pub(crate) mod module { fn _test_file_type(path_or_fd: &OsPathOrFd<'_>, tested_type: u32) -> bool { match path_or_fd { OsPathOrFd::Fd(fd) => { - if let Ok(handle) = crate::common::crt_fd::as_handle(*fd) { + if let Ok(handle) = crate::host_env::crt_fd::as_handle(*fd) { use std::os::windows::io::AsRawHandle; _test_file_type_by_handle(handle.as_raw_handle() as _, tested_type, true) } else { @@ -778,7 +778,7 @@ pub(crate) mod module { match path_or_fd { OsPathOrFd::Fd(fd) => { - if let Ok(handle) = crate::common::crt_fd::as_handle(*fd) { + if let Ok(handle) = crate::host_env::crt_fd::as_handle(*fd) { use std::os::windows::io::AsRawHandle; let file_type = unsafe { GetFileType(handle.as_raw_handle() as _) }; // GetFileType(hfile) != FILE_TYPE_UNKNOWN || !GetLastError() @@ -2156,7 +2156,7 @@ pub(crate) mod module { /// returns the substitute name from reparse data which includes the prefix #[pyfunction] fn readlink(path: OsPath, vm: &VirtualMachine) -> PyResult { - use crate::common::windows::ToWideString; + use crate::host_env::windows::ToWideString; use windows_sys::Win32::Foundation::CloseHandle; use windows_sys::Win32::Storage::FileSystem::{ CreateFileW, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index 949fda8252a..e31017f0682 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -3,9 +3,9 @@ use crate::{ AsObject, Py, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, builtins::{PyModule, PySet}, - common::crt_fd, convert::{IntoPyException, ToPyException, ToPyObject}, function::{ArgumentError, FromArgs, FuncArgs}, + host_env::crt_fd, }; use std::{fs, io, path::Path}; @@ -101,7 +101,7 @@ pub(super) struct FollowSymlinks( #[cfg(not(windows))] fn bytes_as_os_str<'a>(b: &'a [u8], vm: &VirtualMachine) -> PyResult<&'a std::ffi::OsStr> { - rustpython_common::os::bytes_as_os_str(b) + rustpython_host_env::os::bytes_as_os_str(b) .map_err(|_| vm.new_unicode_decode_error("can't decode path for utf-8")) } @@ -152,8 +152,9 @@ impl ToPyObject for crt_fd::Borrowed<'_> { #[pymodule(sub)] pub(super) mod _os { use super::{DirFd, FollowSymlinks, SupportFunc}; + use crate::host_env::fileutils::StatStruct; #[cfg(windows)] - use crate::common::windows::ToWideString; + use crate::host_env::windows::ToWideString; #[cfg(any(unix, windows))] use crate::utils::ToCString; use crate::{ @@ -161,15 +162,11 @@ pub(super) mod _os { builtins::{ PyBytesRef, PyGenericAlias, PyIntRef, PyStrRef, PyTuple, PyTupleRef, PyTypeRef, }, - common::{ - crt_fd, - fileutils::StatStruct, - lock::{OnceCell, PyRwLock}, - suppress_iph, - }, + common::lock::{OnceCell, PyRwLock}, convert::{IntoPyException, ToPyObject}, exceptions::{OSErrorBuilder, ToOSErrorBuilder}, function::{ArgBytesLike, ArgMemoryBuffer, FsPath, FuncArgs, OptionalArg}, + host_env::crt_fd, ospath::{OsPath, OsPathOrFd, OutputMode, PathConverter}, protocol::PyIterReturn, recursion::ReprGuard, @@ -179,6 +176,7 @@ pub(super) mod _os { use core::time::Duration; use crossbeam_utils::atomic::AtomicCell; use rustpython_common::wtf8::Wtf8Buf; + use rustpython_host_env::suppress_iph; use std::{env, fs, fs::OpenOptions, io, path::PathBuf, time::SystemTime}; const OPEN_DIR_FD: bool = cfg!(not(any(windows, target_os = "redox"))); @@ -349,7 +347,7 @@ pub(super) mod _os { if let Some(fd) = dir_fd.raw_opt() { let res = unsafe { libc::mkdirat(fd, c_path.as_ptr(), mode as _) }; return if res < 0 { - let err = crate::common::os::errno_io_error(); + let err = crate::host_env::os::errno_io_error(); Err(OSErrorBuilder::with_filename(&err, path, vm)) } else { Ok(()) @@ -359,7 +357,7 @@ pub(super) mod _os { let [] = dir_fd.0; let res = unsafe { libc::mkdir(c_path.as_ptr(), mode as _) }; if res < 0 { - let err = crate::common::os::errno_io_error(); + let err = crate::host_env::os::errno_io_error(); return Err(OSErrorBuilder::with_filename(&err, path, vm)); } Ok(()) @@ -383,7 +381,7 @@ pub(super) mod _os { let c_path = path.clone().into_cstring(vm)?; let res = unsafe { libc::unlinkat(fd, c_path.as_ptr(), libc::AT_REMOVEDIR) }; return if res < 0 { - let err = crate::common::os::errno_io_error(); + let err = crate::host_env::os::errno_io_error(); Err(OSErrorBuilder::with_filename(&err, path, vm)) } else { Ok(()) @@ -437,7 +435,7 @@ pub(super) mod _os { } #[cfg(all(unix, not(target_os = "redox")))] { - use rustpython_common::os::ffi::OsStrExt; + use rustpython_host_env::os::ffi::OsStrExt; use std::os::unix::io::IntoRawFd; let new_fd = nix::unistd::dup(fno).map_err(|e| e.into_pyexception(vm))?; let raw_fd = new_fd.into_raw_fd(); @@ -490,7 +488,7 @@ pub(super) mod _os { /// Check if wide string length exceeds Windows environment variable limit. #[cfg(windows)] fn check_env_var_len(wide_len: usize, vm: &VirtualMachine) -> PyResult<()> { - use crate::common::windows::_MAX_ENV; + use crate::host_env::windows::_MAX_ENV; if wide_len > _MAX_ENV + 1 { return Err(vm.new_value_error(format!( "the environment variable is longer than {_MAX_ENV} characters", @@ -1124,7 +1122,7 @@ pub(super) mod _os { #[cfg(all(unix, not(target_os = "redox")))] impl IterNext for ScandirIteratorFd { fn next(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { - use rustpython_common::os::ffi::OsStrExt; + use rustpython_host_env::os::ffi::OsStrExt; let mut guard = zelf.dir.lock(); let dir = match guard.as_mut() { None => return Ok(PyIterReturn::StopIteration(None)), @@ -1400,7 +1398,7 @@ pub(super) mod _os { let [] = dir_fd.0; match file { OsPathOrFd::Path(path) => crate::windows::win32_xstat(&path.path, follow_symlinks.0), - OsPathOrFd::Fd(fd) => crate::common::fileutils::fstat(fd), + OsPathOrFd::Fd(fd) => crate::host_env::fileutils::fstat(fd), } .map(Some) } @@ -1414,7 +1412,7 @@ pub(super) mod _os { let mut stat = core::mem::MaybeUninit::uninit(); let ret = match file { OsPathOrFd::Path(path) => { - use rustpython_common::os::ffi::OsStrExt; + use rustpython_host_env::os::ffi::OsStrExt; let path = path.as_ref().as_os_str().as_bytes(); let path = match alloc::ffi::CString::new(path) { Ok(x) => x, diff --git a/crates/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs index 8cde18a47ba..b8c57f04e42 100644 --- a/crates/vm/src/stdlib/posix.rs +++ b/crates/vm/src/stdlib/posix.rs @@ -53,7 +53,7 @@ pub mod module { fcntl, unistd::{self, Gid, Pid, Uid}, }; - use rustpython_common::os::ffi::OsStringExt; + use rustpython_host_env::os::ffi::OsStringExt; use std::{ env, fs, io, os::fd::{AsFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}, @@ -573,7 +573,7 @@ pub mod module { let c_path = path.clone().into_cstring(vm)?; let res = unsafe { libc::unlinkat(fd, c_path.as_ptr(), 0) }; return if res < 0 { - let err = crate::common::os::errno_io_error(); + let err = crate::host_env::os::errno_io_error(); Err(OSErrorBuilder::with_filename(&err, path, vm)) } else { Ok(()) @@ -2597,9 +2597,9 @@ pub mod module { #[pyfunction] fn sysconf(name: SysconfName, vm: &VirtualMachine) -> PyResult { - crate::common::os::set_errno(0); + crate::host_env::os::set_errno(0); let r = unsafe { libc::sysconf(name.0) }; - if r == -1 && crate::common::os::get_errno() != 0 { + if r == -1 && crate::host_env::os::get_errno() != 0 { return Err(vm.new_last_errno_error()); } Ok(r) @@ -2625,7 +2625,7 @@ pub mod module { struct SendFileArgs<'fd> { out_fd: BorrowedFd<'fd>, in_fd: BorrowedFd<'fd>, - offset: crate::common::crt_fd::Offset, + offset: rustpython_host_env::crt_fd::Offset, count: i64, #[cfg(target_os = "macos")] #[pyarg(any, optional)] diff --git a/crates/vm/src/stdlib/posix_compat.rs b/crates/vm/src/stdlib/posix_compat.rs index 89d3d94d7b2..2b4b70c2457 100644 --- a/crates/vm/src/stdlib/posix_compat.rs +++ b/crates/vm/src/stdlib/posix_compat.rs @@ -46,7 +46,7 @@ pub(crate) mod module { #[cfg(target_os = "wasi")] #[pyattr] fn environ(vm: &VirtualMachine) -> crate::builtins::PyDictRef { - use rustpython_common::os::ffi::OsStringExt; + use rustpython_host_env::os::ffi::OsStringExt; let environ = vm.ctx.new_dict(); for (key, value) in env::vars_os() { diff --git a/crates/vm/src/stdlib/sys.rs b/crates/vm/src/stdlib/sys.rs index 72379494ddb..64d16a2c9c7 100644 --- a/crates/vm/src/stdlib/sys.rs +++ b/crates/vm/src/stdlib/sys.rs @@ -1091,7 +1091,7 @@ mod sys { #[cfg(windows)] fn get_kernel32_version() -> std::io::Result<(u32, u32, u32)> { - use crate::common::windows::ToWideString; + use crate::host_env::windows::ToWideString; unsafe { // Create a wide string for "kernel32.dll" let module_name: Vec = std::ffi::OsStr::new("kernel32.dll").to_wide_with_nul(); diff --git a/crates/vm/src/stdlib/time.rs b/crates/vm/src/stdlib/time.rs index 9235d8c89b7..13d353fb35c 100644 --- a/crates/vm/src/stdlib/time.rs +++ b/crates/vm/src/stdlib/time.rs @@ -687,7 +687,7 @@ mod decl { loop { let mut out = vec![0u16; size]; let written = unsafe { - rustpython_common::suppress_iph!(wcsftime( + rustpython_host_env::suppress_iph!(wcsftime( out.as_mut_ptr(), out.len(), fmt_wide.as_ptr(), @@ -1448,7 +1448,7 @@ mod platform { pub(super) fn win_mktime(t: &StructTimeData, vm: &VirtualMachine) -> PyResult { let mut tm = super::decl::tm_from_struct_time(t, vm)?; - let timestamp = unsafe { rustpython_common::suppress_iph!(c_mktime(&mut tm)) }; + let timestamp = unsafe { rustpython_host_env::suppress_iph!(c_mktime(&mut tm)) }; if timestamp == -1 && tm.tm_wday == -1 { return Err(vm.new_overflow_error("mktime argument out of range")); } diff --git a/crates/vm/src/stdlib/winreg.rs b/crates/vm/src/stdlib/winreg.rs index 264d14327da..89e4cb8e8aa 100644 --- a/crates/vm/src/stdlib/winreg.rs +++ b/crates/vm/src/stdlib/winreg.rs @@ -7,9 +7,9 @@ pub(crate) use winreg::module_def; mod winreg { use crate::builtins::{PyInt, PyStr, PyTuple, PyTypeRef}; use crate::common::hash::PyHash; - use crate::common::windows::ToWideString; use crate::convert::TryFromObject; use crate::function::FuncArgs; + use crate::host_env::windows::ToWideString; use crate::object::AsObject; use crate::protocol::PyNumberMethods; use crate::types::{AsNumber, Hashable}; diff --git a/crates/vm/src/stdlib/winsound.rs b/crates/vm/src/stdlib/winsound.rs index 0ca2e9a2258..fea00d10c63 100644 --- a/crates/vm/src/stdlib/winsound.rs +++ b/crates/vm/src/stdlib/winsound.rs @@ -18,8 +18,8 @@ mod win32 { #[pymodule] mod winsound { use crate::builtins::{PyBytes, PyStr}; - use crate::common::windows::ToWideString; use crate::convert::{IntoPyException, TryFromBorrowedObject}; + use crate::host_env::windows::ToWideString; use crate::protocol::PyBuffer; use crate::{AsObject, PyObjectRef, PyResult, VirtualMachine}; diff --git a/crates/vm/src/vm/vm_new.rs b/crates/vm/src/vm/vm_new.rs index fb40268c569..63de652ac7a 100644 --- a/crates/vm/src/vm/vm_new.rs +++ b/crates/vm/src/vm/vm_new.rs @@ -249,7 +249,7 @@ impl VirtualMachine { /// On windows, CRT errno are expected to be handled by this function. /// This is identical to `new_last_os_error` on non-Windows platforms. pub fn new_last_errno_error(&self) -> PyBaseExceptionRef { - let err = crate::common::os::errno_io_error(); + let err = crate::host_env::os::errno_io_error(); err.to_pyexception(self) } diff --git a/crates/vm/src/windows.rs b/crates/vm/src/windows.rs index ec9ac8f18fb..fe109c95ee5 100644 --- a/crates/vm/src/windows.rs +++ b/crates/vm/src/windows.rs @@ -1,4 +1,4 @@ -use crate::common::fileutils::{ +use crate::host_env::fileutils::{ StatStruct, windows::{FILE_INFO_BY_NAME_CLASS, get_file_information_by_name}, }; @@ -6,7 +6,7 @@ use crate::{ PyObjectRef, PyResult, TryFromObject, VirtualMachine, convert::{ToPyObject, ToPyResult}, }; -use rustpython_common::windows::ToWideString; +use rustpython_host_env::windows::ToWideString; use std::ffi::OsStr; use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE}; @@ -103,8 +103,8 @@ const S_IFMT: u16 = libc::S_IFMT as u16; const S_IFDIR: u16 = libc::S_IFDIR as u16; const S_IFREG: u16 = libc::S_IFREG as u16; const S_IFCHR: u16 = libc::S_IFCHR as u16; -const S_IFLNK: u16 = crate::common::fileutils::windows::S_IFLNK as u16; -const S_IFIFO: u16 = crate::common::fileutils::windows::S_IFIFO as u16; +const S_IFLNK: u16 = crate::host_env::fileutils::windows::S_IFLNK as u16; +const S_IFIFO: u16 = crate::host_env::fileutils::windows::S_IFIFO as u16; /// FILE_ATTRIBUTE_TAG_INFO structure for GetFileInformationByHandleEx #[repr(C)] @@ -141,7 +141,7 @@ fn attribute_data_to_stat( basic_info: Option<&windows_sys::Win32::Storage::FileSystem::FILE_BASIC_INFO>, id_info: Option<&windows_sys::Win32::Storage::FileSystem::FILE_ID_INFO>, ) -> StatStruct { - use crate::common::fileutils::windows::SECS_BETWEEN_EPOCHS; + use crate::host_env::fileutils::windows::SECS_BETWEEN_EPOCHS; use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT; let mut st_mode = attributes_to_mode(info.dwFileAttributes); @@ -513,7 +513,7 @@ fn win32_xstat_impl(path: &OsStr, traverse: bool) -> std::io::Result || (!traverse && is_reparse_tag_name_surrogate(stat_info.ReparseTag)) { let mut result = - crate::common::fileutils::windows::stat_basic_info_to_stat(&stat_info); + crate::host_env::fileutils::windows::stat_basic_info_to_stat(&stat_info); // If st_ino is 0, fall through to slow path to get proper file ID if result.st_ino != 0 || result.st_ino_high != 0 { result.update_st_mode_from_path(path, stat_info.FileAttributes); diff --git a/examples/generator.rs b/examples/generator.rs index 55841c767a1..f695f094681 100644 --- a/examples/generator.rs +++ b/examples/generator.rs @@ -46,5 +46,5 @@ fn main() -> ExitCode { let defs = rustpython_stdlib::stdlib_module_defs(&builder.ctx); let interp = builder.add_native_modules(&defs).build(); let result = py_main(&interp); - vm::common::os::exit_code(interp.run(|_vm| result)) + vm::host_env::os::exit_code(interp.run(|_vm| result)) } diff --git a/examples/package_embed.rs b/examples/package_embed.rs index bb2f29e3f5f..cf2593facd8 100644 --- a/examples/package_embed.rs +++ b/examples/package_embed.rs @@ -26,5 +26,5 @@ fn main() -> ExitCode { let result = result.map(|result| { println!("name: {result}"); }); - vm::common::os::exit_code(interp.run(|_vm| result)) + vm::host_env::os::exit_code(interp.run(|_vm| result)) } diff --git a/src/lib.rs b/src/lib.rs index 02b514b6e17..b9ddcc3aeb0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,7 +120,7 @@ pub fn run(mut builder: InterpreterBuilder) -> ExitCode { let interp = builder.interpreter(); let exitcode = interp.run(move |vm| run_rustpython(vm, run_mode)); - rustpython_vm::common::os::exit_code(exitcode) + rustpython_vm::host_env::os::exit_code(exitcode) } fn get_pip(scope: Scope, vm: &VirtualMachine) -> PyResult<()> { From ee341813bdafe58d45e2a7f05e51a87e9ae1d14c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 20:14:22 +0000 Subject: [PATCH 2/2] refactor: extract host helpers Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/48d1e64d-37ce-409f-b511-8e61a349665c Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --- Cargo.lock | 7 +- Cargo.toml | 2 +- crates/host_env/Cargo.toml | 11 +- crates/host_env/src/fcntl.rs | 75 +++++++++++++ crates/host_env/src/lib.rs | 24 +++++ crates/host_env/src/msvcrt.rs | 126 ++++++++++++++++++++++ crates/host_env/src/nt.rs | 73 +++++++++++++ crates/host_env/src/posix.rs | 13 +++ crates/host_env/src/select.rs | 176 ++++++++++++++++++++++++++++++ crates/host_env/src/shm.rs | 23 ++++ crates/host_env/src/signal.rs | 17 +++ crates/host_env/src/syslog.rs | 68 ++++++++++++ crates/host_env/src/termios.rs | 23 ++++ crates/host_env/src/time.rs | 46 ++++++++ crates/host_env/src/winapi.rs | 17 +++ crates/stdlib/Cargo.toml | 2 +- crates/stdlib/src/fcntl.rs | 54 +++------- crates/stdlib/src/posixshmem.rs | 20 +--- crates/stdlib/src/select.rs | 183 +------------------------------- crates/stdlib/src/socket.rs | 12 +-- crates/stdlib/src/syslog.rs | 55 ++-------- crates/stdlib/src/termios.rs | 17 ++- crates/vm/Cargo.toml | 2 +- crates/vm/src/stdlib/_signal.rs | 23 +--- crates/vm/src/stdlib/_winapi.rs | 9 +- crates/vm/src/stdlib/msvcrt.rs | 99 ++++------------- crates/vm/src/stdlib/nt.rs | 69 +----------- crates/vm/src/stdlib/posix.rs | 13 +-- crates/vm/src/stdlib/time.rs | 48 +++------ 29 files changed, 793 insertions(+), 514 deletions(-) create mode 100644 crates/host_env/src/fcntl.rs create mode 100644 crates/host_env/src/msvcrt.rs create mode 100644 crates/host_env/src/nt.rs create mode 100644 crates/host_env/src/posix.rs create mode 100644 crates/host_env/src/select.rs create mode 100644 crates/host_env/src/shm.rs create mode 100644 crates/host_env/src/signal.rs create mode 100644 crates/host_env/src/syslog.rs create mode 100644 crates/host_env/src/termios.rs create mode 100644 crates/host_env/src/time.rs create mode 100644 crates/host_env/src/winapi.rs diff --git a/Cargo.lock b/Cargo.lock index c4753ecd21f..2d567375977 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3219,13 +3219,14 @@ dependencies = [ ] [[package]] -name = "rustpython-host-env" +name = "rustpython-host_env" version = "0.5.0" dependencies = [ "libc", "nix 0.30.1", "num-traits", "rustpython-wtf8", + "termios", "widestring", "windows-sys 0.61.2", ] @@ -3416,7 +3417,7 @@ dependencies = [ "rustls-platform-verifier", "rustpython-common", "rustpython-derive", - "rustpython-host-env", + "rustpython-host_env", "rustpython-ruff_python_ast", "rustpython-ruff_python_parser", "rustpython-ruff_source_file", @@ -3496,7 +3497,7 @@ dependencies = [ "rustpython-compiler", "rustpython-compiler-core", "rustpython-derive", - "rustpython-host-env", + "rustpython-host_env", "rustpython-jit", "rustpython-literal", "rustpython-ruff_python_ast", diff --git a/Cargo.toml b/Cargo.toml index 2df67583ad7..88169444ff1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,7 +145,7 @@ rustpython-compiler-core = { path = "crates/compiler-core", version = "0.5.0" } rustpython-compiler = { path = "crates/compiler", version = "0.5.0" } rustpython-codegen = { path = "crates/codegen", version = "0.5.0" } rustpython-common = { path = "crates/common", version = "0.5.0" } -rustpython-host-env = { path = "crates/host_env", version = "0.5.0" } +rustpython-host_env = { path = "crates/host_env", version = "0.5.0" } rustpython-derive = { path = "crates/derive", version = "0.5.0" } rustpython-derive-impl = { path = "crates/derive-impl", version = "0.5.0" } rustpython-jit = { path = "crates/jit", version = "0.5.0" } diff --git a/crates/host_env/Cargo.toml b/crates/host_env/Cargo.toml index d405f794c7d..e826a8baaf4 100644 --- a/crates/host_env/Cargo.toml +++ b/crates/host_env/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rustpython-host-env" +name = "rustpython-host_env" description = "Host OS API abstractions for RustPython" version.workspace = true authors.workspace = true @@ -17,15 +17,24 @@ num-traits = { workspace = true } [target.'cfg(unix)'.dependencies] nix = { workspace = true } +[target.'cfg(all(unix, not(target_os = "ios"), not(target_os = "redox")))'.dependencies] +termios = "0.3.3" + [target.'cfg(windows)'.dependencies] widestring = { workspace = true } windows-sys = { workspace = true, features = [ "Win32_Foundation", + "Win32_Globalization", "Win32_Networking_WinSock", "Win32_Storage_FileSystem", + "Win32_System_Console", + "Win32_System_Diagnostics_Debug", "Win32_System_Ioctl", "Win32_System_LibraryLoader", + "Win32_System_SystemInformation", "Win32_System_SystemServices", + "Win32_System_Threading", + "Win32_System_Time", ] } [lints] diff --git a/crates/host_env/src/fcntl.rs b/crates/host_env/src/fcntl.rs new file mode 100644 index 00000000000..b4dba53fa3d --- /dev/null +++ b/crates/host_env/src/fcntl.rs @@ -0,0 +1,75 @@ +use std::io; + +pub fn fcntl_int(fd: i32, cmd: i32, arg: i32) -> io::Result { + let ret = unsafe { libc::fcntl(fd, cmd, arg) }; + if ret < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(ret) + } +} + +pub fn fcntl_with_bytes(fd: i32, cmd: i32, arg: &mut [u8]) -> io::Result { + let ret = unsafe { libc::fcntl(fd, cmd, arg.as_mut_ptr()) }; + if ret < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(ret) + } +} + +/// # Safety +/// +/// `arg` must be a valid pointer for the `request` passed to `ioctl` and must +/// satisfy the platform ABI requirements for that request. +pub unsafe fn ioctl_ptr( + fd: i32, + request: libc::c_ulong, + arg: *mut libc::c_void, +) -> io::Result { + let ret = unsafe { libc::ioctl(fd, request as _, arg) }; + if ret < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(ret) + } +} + +pub fn ioctl_int(fd: i32, request: libc::c_ulong, arg: i32) -> io::Result { + let ret = unsafe { libc::ioctl(fd, request as _, arg) }; + if ret < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(ret) + } +} + +#[cfg(not(any(target_os = "wasi", target_os = "redox")))] +pub fn flock(fd: i32, operation: i32) -> io::Result { + let ret = unsafe { libc::flock(fd, operation) }; + if ret < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(ret) + } +} + +#[cfg(not(any(target_os = "wasi", target_os = "redox")))] +pub fn lockf(fd: i32, cmd: i32, lock: &libc::flock) -> io::Result { + let ret = unsafe { + libc::fcntl( + fd, + if (cmd & libc::LOCK_NB) != 0 { + libc::F_SETLK + } else { + libc::F_SETLKW + }, + lock, + ) + }; + if ret < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(ret) + } +} diff --git a/crates/host_env/src/lib.rs b/crates/host_env/src/lib.rs index b2c4ddacf6d..6ae544c0147 100644 --- a/crates/host_env/src/lib.rs +++ b/crates/host_env/src/lib.rs @@ -14,3 +14,27 @@ pub mod fileutils; #[cfg(windows)] pub mod windows; + +#[cfg(any(unix, target_os = "wasi"))] +pub mod fcntl; +#[cfg(any(unix, windows, target_os = "wasi"))] +pub mod select; +#[cfg(unix)] +pub mod syslog; +#[cfg(all(unix, not(target_os = "redox"), not(target_os = "ios")))] +pub mod termios; + +#[cfg(unix)] +pub mod posix; +#[cfg(all(unix, not(target_os = "redox"), not(target_os = "android")))] +pub mod shm; +#[cfg(unix)] +pub mod signal; +pub mod time; + +#[cfg(windows)] +pub mod msvcrt; +#[cfg(windows)] +pub mod nt; +#[cfg(windows)] +pub mod winapi; diff --git a/crates/host_env/src/msvcrt.rs b/crates/host_env/src/msvcrt.rs new file mode 100644 index 00000000000..00a599e3944 --- /dev/null +++ b/crates/host_env/src/msvcrt.rs @@ -0,0 +1,126 @@ +use alloc::{string::String, vec::Vec}; +use std::io; + +use crate::crt_fd; +use windows_sys::Win32::System::Diagnostics::Debug; + +pub const LK_UNLCK: i32 = 0; +pub const LK_LOCK: i32 = 1; +pub const LK_NBLCK: i32 = 2; +pub const LK_RLCK: i32 = 3; +pub const LK_NBRLCK: i32 = 4; + +unsafe extern "C" { + fn _getch() -> i32; + fn _getwch() -> u32; + fn _getche() -> i32; + fn _getwche() -> u32; + fn _putch(c: u32) -> i32; + fn _putwch(c: u16) -> u32; + fn _ungetch(c: i32) -> i32; + fn _ungetwch(c: u32) -> u32; + fn _locking(fd: i32, mode: i32, nbytes: i64) -> i32; + fn _heapmin() -> i32; + fn _kbhit() -> i32; + fn _setmode(fd: crt_fd::Borrowed<'_>, flags: i32) -> i32; +} + +pub fn setmode_binary(fd: crt_fd::Borrowed<'_>) { + unsafe { suppress_iph!(_setmode(fd, libc::O_BINARY)) }; +} + +pub fn getch() -> Vec { + vec![unsafe { _getch() } as u8] +} + +pub fn getwch() -> String { + let value = unsafe { _getwch() }; + char::from_u32(value) + .unwrap_or_else(|| panic!("invalid unicode {value:#x} from _getwch")) + .to_string() +} + +pub fn getche() -> Vec { + vec![unsafe { _getche() } as u8] +} + +pub fn getwche() -> String { + let value = unsafe { _getwche() }; + char::from_u32(value) + .unwrap_or_else(|| panic!("invalid unicode {value:#x} from _getwche")) + .to_string() +} + +pub fn putch(c: u8) { + unsafe { suppress_iph!(_putch(c.into())) }; +} + +pub fn putwch(c: char) { + unsafe { suppress_iph!(_putwch(c as u16)) }; +} + +pub fn ungetch(c: u8) -> io::Result<()> { + let ret = unsafe { suppress_iph!(_ungetch(c as i32)) }; + if ret == -1 { + Err(io::Error::from_raw_os_error(libc::ENOSPC)) + } else { + Ok(()) + } +} + +pub fn ungetwch(c: char) -> io::Result<()> { + let ret = unsafe { suppress_iph!(_ungetwch(c as u32)) }; + if ret == 0xFFFF { + Err(io::Error::from_raw_os_error(libc::ENOSPC)) + } else { + Ok(()) + } +} + +pub fn kbhit() -> i32 { + unsafe { _kbhit() } +} + +pub fn locking(fd: i32, mode: i32, nbytes: i64) -> io::Result<()> { + let ret = unsafe { suppress_iph!(_locking(fd, mode, nbytes)) }; + if ret == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } +} + +pub fn heapmin() -> io::Result<()> { + let ret = unsafe { suppress_iph!(_heapmin()) }; + if ret == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } +} + +pub fn setmode(fd: crt_fd::Borrowed<'_>, flags: i32) -> io::Result { + let ret = unsafe { suppress_iph!(_setmode(fd, flags)) }; + if ret == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(ret) + } +} + +pub fn open_osfhandle(handle: isize, flags: i32) -> io::Result { + let ret = unsafe { suppress_iph!(libc::open_osfhandle(handle, flags)) }; + if ret == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(ret) + } +} + +pub fn get_error_mode() -> u32 { + unsafe { suppress_iph!(Debug::GetErrorMode()) } +} + +pub fn set_error_mode(mode: Debug::THREAD_ERROR_MODE) -> u32 { + unsafe { suppress_iph!(Debug::SetErrorMode(mode)) } +} diff --git a/crates/host_env/src/nt.rs b/crates/host_env/src/nt.rs new file mode 100644 index 00000000000..c6771aad40a --- /dev/null +++ b/crates/host_env/src/nt.rs @@ -0,0 +1,73 @@ +// cspell:ignore hchmod +use std::{ffi::OsStr, io, os::windows::io::AsRawHandle}; + +use crate::{crt_fd, windows::ToWideString}; +use windows_sys::Win32::{ + Foundation::HANDLE, + Storage::FileSystem::{ + FILE_ATTRIBUTE_READONLY, FILE_BASIC_INFO, FileBasicInfo, GetFileAttributesW, + GetFileInformationByHandleEx, INVALID_FILE_ATTRIBUTES, SetFileAttributesW, + SetFileInformationByHandle, + }, +}; + +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn win32_hchmod(handle: HANDLE, mode: u32, write_bit: u32) -> io::Result<()> { + let mut info: FILE_BASIC_INFO = unsafe { core::mem::zeroed() }; + let ret = unsafe { + GetFileInformationByHandleEx( + handle, + FileBasicInfo, + (&mut info as *mut FILE_BASIC_INFO).cast(), + core::mem::size_of::() as u32, + ) + }; + if ret == 0 { + return Err(io::Error::last_os_error()); + } + + if mode & write_bit != 0 { + info.FileAttributes &= !FILE_ATTRIBUTE_READONLY; + } else { + info.FileAttributes |= FILE_ATTRIBUTE_READONLY; + } + + let ret = unsafe { + SetFileInformationByHandle( + handle, + FileBasicInfo, + (&info as *const FILE_BASIC_INFO).cast(), + core::mem::size_of::() as u32, + ) + }; + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } +} + +pub fn fchmod(fd: i32, mode: u32, write_bit: u32) -> io::Result<()> { + let borrowed = unsafe { crt_fd::Borrowed::borrow_raw(fd) }; + let handle = crt_fd::as_handle(borrowed)?; + win32_hchmod(handle.as_raw_handle() as HANDLE, mode, write_bit) +} + +pub fn win32_lchmod(path: &OsStr, mode: u32, write_bit: u32) -> io::Result<()> { + let wide = path.to_wide_with_nul(); + let attr = unsafe { GetFileAttributesW(wide.as_ptr()) }; + if attr == INVALID_FILE_ATTRIBUTES { + return Err(io::Error::last_os_error()); + } + let new_attr = if mode & write_bit != 0 { + attr & !FILE_ATTRIBUTE_READONLY + } else { + attr | FILE_ATTRIBUTE_READONLY + }; + let ret = unsafe { SetFileAttributesW(wide.as_ptr(), new_attr) }; + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } +} diff --git a/crates/host_env/src/posix.rs b/crates/host_env/src/posix.rs new file mode 100644 index 00000000000..e624db10a15 --- /dev/null +++ b/crates/host_env/src/posix.rs @@ -0,0 +1,13 @@ +use std::os::fd::BorrowedFd; + +pub fn set_inheritable(fd: BorrowedFd<'_>, inheritable: bool) -> nix::Result<()> { + use nix::fcntl; + + let flags = fcntl::FdFlag::from_bits_truncate(fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD)?); + let mut new_flags = flags; + new_flags.set(fcntl::FdFlag::FD_CLOEXEC, !inheritable); + if flags != new_flags { + fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFD(new_flags))?; + } + Ok(()) +} diff --git a/crates/host_env/src/select.rs b/crates/host_env/src/select.rs new file mode 100644 index 00000000000..e5ddc4284f7 --- /dev/null +++ b/crates/host_env/src/select.rs @@ -0,0 +1,176 @@ +use core::mem::MaybeUninit; +use std::io; + +#[cfg(unix)] +mod platform { + pub use libc::{FD_ISSET, FD_SET, FD_SETSIZE, FD_ZERO, fd_set, select, timeval}; + pub use std::os::unix::io::RawFd; + + pub const fn check_err(x: i32) -> bool { + x < 0 + } +} + +#[allow(non_snake_case)] +#[cfg(windows)] +mod platform { + pub use WinSock::{FD_SET as fd_set, FD_SETSIZE, SOCKET as RawFd, TIMEVAL as timeval, select}; + use windows_sys::Win32::Networking::WinSock; + + pub unsafe fn FD_SET(fd: RawFd, set: *mut fd_set) { + let mut slot = unsafe { (&raw mut (*set).fd_array).cast::() }; + let fd_count = unsafe { (*set).fd_count }; + for _ in 0..fd_count { + if unsafe { *slot } == fd { + return; + } + slot = unsafe { slot.add(1) }; + } + if fd_count < FD_SETSIZE { + unsafe { + *slot = fd as RawFd; + (*set).fd_count += 1; + } + } + } + + pub unsafe fn FD_ZERO(set: *mut fd_set) { + unsafe { (*set).fd_count = 0 }; + } + + pub unsafe fn FD_ISSET(fd: RawFd, set: *mut fd_set) -> bool { + use WinSock::__WSAFDIsSet; + unsafe { __WSAFDIsSet(fd as _, set) != 0 } + } + + pub fn check_err(x: i32) -> bool { + x == WinSock::SOCKET_ERROR + } +} + +#[cfg(target_os = "wasi")] +mod platform { + pub use libc::{FD_SETSIZE, timeval}; + pub use std::os::fd::RawFd; + + pub const fn check_err(x: i32) -> bool { + x < 0 + } + + #[repr(C)] + pub struct fd_set { + __nfds: usize, + __fds: [libc::c_int; FD_SETSIZE], + } + + #[allow(non_snake_case)] + pub unsafe fn FD_ISSET(fd: RawFd, set: *const fd_set) -> bool { + let set = unsafe { &*set }; + for p in &set.__fds[..set.__nfds] { + if *p == fd { + return true; + } + } + false + } + + #[allow(non_snake_case)] + pub unsafe fn FD_SET(fd: RawFd, set: *mut fd_set) { + let set = unsafe { &mut *set }; + for p in &set.__fds[..set.__nfds] { + if *p == fd { + return; + } + } + let n = set.__nfds; + assert!(n < set.__fds.len(), "fd_set full"); + set.__fds[n] = fd; + set.__nfds = n + 1; + } + + #[allow(non_snake_case)] + pub unsafe fn FD_ZERO(set: *mut fd_set) { + unsafe { (*set).__nfds = 0 }; + } + + unsafe extern "C" { + pub fn select( + nfds: libc::c_int, + readfds: *mut fd_set, + writefds: *mut fd_set, + errorfds: *mut fd_set, + timeout: *const timeval, + ) -> libc::c_int; + } +} + +pub use platform::{RawFd, timeval}; + +#[repr(transparent)] +pub struct FdSet(MaybeUninit); + +impl FdSet { + pub fn new() -> Self { + let mut fdset = MaybeUninit::zeroed(); + unsafe { platform::FD_ZERO(fdset.as_mut_ptr()) }; + Self(fdset) + } + + pub fn insert(&mut self, fd: RawFd) { + unsafe { platform::FD_SET(fd, self.0.as_mut_ptr()) }; + } + + pub fn contains(&mut self, fd: RawFd) -> bool { + unsafe { platform::FD_ISSET(fd, self.0.as_mut_ptr()) } + } + + pub fn clear(&mut self) { + unsafe { platform::FD_ZERO(self.0.as_mut_ptr()) }; + } + + pub fn highest(&mut self) -> Option { + (0..platform::FD_SETSIZE as RawFd) + .rev() + .find(|&fd| self.contains(fd)) + } +} + +impl Default for FdSet { + fn default() -> Self { + Self::new() + } +} + +pub fn select( + nfds: libc::c_int, + readfds: &mut FdSet, + writefds: &mut FdSet, + errfds: &mut FdSet, + timeout: Option<&mut timeval>, +) -> io::Result { + let timeout = match timeout { + Some(tv) => tv as *mut timeval, + None => core::ptr::null_mut(), + }; + let ret = unsafe { + platform::select( + nfds, + readfds.0.as_mut_ptr(), + writefds.0.as_mut_ptr(), + errfds.0.as_mut_ptr(), + timeout, + ) + }; + if platform::check_err(ret) { + Err(io::Error::last_os_error()) + } else { + Ok(ret) + } +} + +pub fn sec_to_timeval(sec: f64) -> timeval { + timeval { + tv_sec: sec.trunc() as _, + tv_usec: (sec.fract() * 1e6) as _, + } +} diff --git a/crates/host_env/src/shm.rs b/crates/host_env/src/shm.rs new file mode 100644 index 00000000000..08a2ae9787c --- /dev/null +++ b/crates/host_env/src/shm.rs @@ -0,0 +1,23 @@ +use core::ffi::CStr; +use std::io; + +pub fn shm_open(name: &CStr, flags: libc::c_int, mode: libc::c_uint) -> io::Result { + #[cfg(target_os = "freebsd")] + let mode = mode.try_into().unwrap(); + + let fd = unsafe { libc::shm_open(name.as_ptr(), flags, mode) }; + if fd == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(fd) + } +} + +pub fn shm_unlink(name: &CStr) -> io::Result<()> { + let ret = unsafe { libc::shm_unlink(name.as_ptr()) }; + if ret == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } +} diff --git a/crates/host_env/src/signal.rs b/crates/host_env/src/signal.rs new file mode 100644 index 00000000000..13a7206c68a --- /dev/null +++ b/crates/host_env/src/signal.rs @@ -0,0 +1,17 @@ +pub fn timeval_to_double(tv: &libc::timeval) -> f64 { + tv.tv_sec as f64 + (tv.tv_usec as f64 / 1_000_000.0) +} + +pub fn double_to_timeval(val: f64) -> libc::timeval { + libc::timeval { + tv_sec: val.trunc() as _, + tv_usec: (val.fract() * 1_000_000.0) as _, + } +} + +pub fn itimerval_to_tuple(it: &libc::itimerval) -> (f64, f64) { + ( + timeval_to_double(&it.it_value), + timeval_to_double(&it.it_interval), + ) +} diff --git a/crates/host_env/src/syslog.rs b/crates/host_env/src/syslog.rs new file mode 100644 index 00000000000..67c10f6f21a --- /dev/null +++ b/crates/host_env/src/syslog.rs @@ -0,0 +1,68 @@ +use alloc::boxed::Box; +use core::ffi::CStr; +use std::{ + os::raw::c_char, + sync::{OnceLock, RwLock}, +}; + +#[derive(Debug)] +enum GlobalIdent { + Explicit(Box), + Implicit, +} + +impl GlobalIdent { + fn as_ptr(&self) -> *const c_char { + match self { + Self::Explicit(cstr) => cstr.as_ptr(), + Self::Implicit => core::ptr::null(), + } + } +} + +fn global_ident() -> &'static RwLock> { + static IDENT: OnceLock>> = OnceLock::new(); + IDENT.get_or_init(|| RwLock::new(None)) +} + +pub fn is_open() -> bool { + global_ident() + .read() + .expect("syslog lock poisoned") + .is_some() +} + +pub fn openlog(ident: Option>, logoption: i32, facility: i32) { + let ident = match ident { + Some(ident) => GlobalIdent::Explicit(ident), + None => GlobalIdent::Implicit, + }; + let mut locked_ident = global_ident().write().expect("syslog lock poisoned"); + unsafe { libc::openlog(ident.as_ptr(), logoption, facility) }; + *locked_ident = Some(ident); +} + +pub fn syslog(priority: i32, msg: &CStr) { + let cformat = c"%s"; + unsafe { libc::syslog(priority, cformat.as_ptr(), msg.as_ptr()) }; +} + +pub fn closelog() { + if is_open() { + let mut locked_ident = global_ident().write().expect("syslog lock poisoned"); + unsafe { libc::closelog() }; + *locked_ident = None; + } +} + +pub fn setlogmask(maskpri: i32) -> i32 { + unsafe { libc::setlogmask(maskpri) } +} + +pub const fn log_mask(pri: i32) -> i32 { + pri << 1 +} + +pub const fn log_upto(pri: i32) -> i32 { + (1 << (pri + 1)) - 1 +} diff --git a/crates/host_env/src/termios.rs b/crates/host_env/src/termios.rs new file mode 100644 index 00000000000..76bcd0c9f01 --- /dev/null +++ b/crates/host_env/src/termios.rs @@ -0,0 +1,23 @@ +pub fn tcgetattr(fd: i32) -> std::io::Result<::termios::Termios> { + ::termios::Termios::from_fd(fd) +} + +pub fn tcsetattr(fd: i32, when: i32, termios: &::termios::Termios) -> std::io::Result<()> { + ::termios::tcsetattr(fd, when, termios) +} + +pub fn tcsendbreak(fd: i32, duration: i32) -> std::io::Result<()> { + ::termios::tcsendbreak(fd, duration) +} + +pub fn tcdrain(fd: i32) -> std::io::Result<()> { + ::termios::tcdrain(fd) +} + +pub fn tcflush(fd: i32, queue: i32) -> std::io::Result<()> { + ::termios::tcflush(fd, queue) +} + +pub fn tcflow(fd: i32, action: i32) -> std::io::Result<()> { + ::termios::tcflow(fd, action) +} diff --git a/crates/host_env/src/time.rs b/crates/host_env/src/time.rs new file mode 100644 index 00000000000..2050356e406 --- /dev/null +++ b/crates/host_env/src/time.rs @@ -0,0 +1,46 @@ +use core::time::Duration; +use std::{ + io, + time::{SystemTime, UNIX_EPOCH}, +}; + +pub const SEC_TO_MS: i64 = 1000; +pub const MS_TO_US: i64 = 1000; +pub const SEC_TO_US: i64 = SEC_TO_MS * MS_TO_US; +pub const US_TO_NS: i64 = 1000; +pub const MS_TO_NS: i64 = MS_TO_US * US_TO_NS; +pub const SEC_TO_NS: i64 = SEC_TO_MS * MS_TO_NS; +pub const NS_TO_MS: i64 = 1000 * 1000; +pub const NS_TO_US: i64 = 1000; + +pub fn duration_since_system_now() -> io::Result { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(io::Error::other) +} + +#[cfg(target_env = "msvc")] +#[cfg(not(target_arch = "wasm32"))] +pub fn get_tz_info() -> windows_sys::Win32::System::Time::TIME_ZONE_INFORMATION { + let mut info = unsafe { core::mem::zeroed() }; + unsafe { windows_sys::Win32::System::Time::GetTimeZoneInformation(&mut info) }; + info +} + +#[cfg(any(unix, windows))] +pub fn asctime_from_tm(tm: &libc::tm) -> String { + const WDAY_NAME: [&str; 7] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + const MON_NAME: [&str; 12] = [ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + ]; + format!( + "{} {}{:>3} {:02}:{:02}:{:02} {}", + WDAY_NAME[tm.tm_wday as usize], + MON_NAME[tm.tm_mon as usize], + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + tm.tm_year + 1900 + ) +} diff --git a/crates/host_env/src/winapi.rs b/crates/host_env/src/winapi.rs new file mode 100644 index 00000000000..26f46969315 --- /dev/null +++ b/crates/host_env/src/winapi.rs @@ -0,0 +1,17 @@ +use windows_sys::Win32::Foundation::HANDLE; + +pub fn get_acp() -> u32 { + unsafe { windows_sys::Win32::Globalization::GetACP() } +} + +pub fn get_current_process() -> HANDLE { + unsafe { windows_sys::Win32::System::Threading::GetCurrentProcess() } +} + +pub fn get_last_error() -> u32 { + unsafe { windows_sys::Win32::Foundation::GetLastError() } +} + +pub fn get_version() -> u32 { + unsafe { windows_sys::Win32::System::SystemInformation::GetVersion() } +} diff --git a/crates/stdlib/Cargo.toml b/crates/stdlib/Cargo.toml index 5739a3f4a97..6971a54f992 100644 --- a/crates/stdlib/Cargo.toml +++ b/crates/stdlib/Cargo.toml @@ -30,7 +30,7 @@ flame-it = ["flame"] rustpython-derive = { workspace = true } rustpython-vm = { workspace = true, default-features = false, features = ["compiler"]} rustpython-common = { workspace = true } -rustpython-host-env = { workspace = true } +rustpython-host_env = { workspace = true } ruff_python_parser = { workspace = true } ruff_python_ast = { workspace = true } diff --git a/crates/stdlib/src/fcntl.rs b/crates/stdlib/src/fcntl.rs index 0f75a09ba0f..6d27b3cee57 100644 --- a/crates/stdlib/src/fcntl.rs +++ b/crates/stdlib/src/fcntl.rs @@ -4,6 +4,8 @@ pub(crate) use fcntl::module_def; #[pymodule] mod fcntl { + use rustpython_host_env::fcntl as host_fcntl; + use crate::vm::{ PyResult, VirtualMachine, builtins::PyIntRef, @@ -73,19 +75,15 @@ mod fcntl { .ok_or_else(|| vm.new_value_error("fcntl string arg too long"))? .copy_from_slice(&s) } - let ret = unsafe { libc::fcntl(fd, cmd, buf.as_mut_ptr()) }; - if ret < 0 { - return Err(vm.new_last_errno_error()); - } + host_fcntl::fcntl_with_bytes(fd, cmd, &mut buf[..arg_len]) + .map_err(|_| vm.new_last_errno_error())?; return Ok(vm.ctx.new_bytes(buf[..arg_len].to_vec()).into()); } OptionalArg::Present(Either::B(i)) => i.as_u32_mask(), OptionalArg::Missing => 0, }; - let ret = unsafe { libc::fcntl(fd, cmd, int as i32) }; - if ret < 0 { - return Err(vm.new_last_errno_error()); - } + let ret = + host_fcntl::fcntl_int(fd, cmd, int as i32).map_err(|_| vm.new_last_errno_error())?; Ok(vm.new_pyobj(ret)) } @@ -118,11 +116,10 @@ mod fcntl { let mutate_flag = mutate_flag.unwrap_or(true); let mut arg_buf = rw_arg.borrow_buf_mut(); if mutate_flag { - let ret = - unsafe { libc::ioctl(fd, request as _, arg_buf.as_mut_ptr()) }; - if ret < 0 { - return Err(vm.new_last_errno_error()); + let ret = unsafe { + host_fcntl::ioctl_ptr(fd, request, arg_buf.as_mut_ptr().cast()) } + .map_err(|_| vm.new_last_errno_error())?; return Ok(vm.ctx.new_int(ret).into()); } // treat like an immutable buffer @@ -130,17 +127,13 @@ mod fcntl { } Either::B(ro_buf) => fill_buf(&ro_buf.borrow_bytes())?, }; - let ret = unsafe { libc::ioctl(fd, request as _, buf.as_mut_ptr()) }; - if ret < 0 { - return Err(vm.new_last_errno_error()); - } + unsafe { host_fcntl::ioctl_ptr(fd, request, buf.as_mut_ptr().cast()) } + .map_err(|_| vm.new_last_errno_error())?; Ok(vm.ctx.new_bytes(buf[..buf_len].to_vec()).into()) } Either::B(i) => { - let ret = unsafe { libc::ioctl(fd, request as _, i) }; - if ret < 0 { - return Err(vm.new_last_errno_error()); - } + let ret = + host_fcntl::ioctl_int(fd, request, i).map_err(|_| vm.new_last_errno_error())?; Ok(vm.ctx.new_int(ret).into()) } } @@ -150,11 +143,7 @@ mod fcntl { #[cfg(not(any(target_os = "wasi", target_os = "redox")))] #[pyfunction] fn flock(_io::Fildes(fd): _io::Fildes, operation: i32, vm: &VirtualMachine) -> PyResult { - let ret = unsafe { libc::flock(fd, operation) }; - // TODO: add support for platforms that don't have a builtin `flock` syscall - if ret < 0 { - return Err(vm.new_last_errno_error()); - } + let ret = host_fcntl::flock(fd, operation).map_err(|_| vm.new_last_errno_error())?; Ok(vm.ctx.new_int(ret).into()) } @@ -201,20 +190,7 @@ mod fcntl { .map_err(|e| vm.new_overflow_error(format!("{e}")))?, OptionalArg::Missing => 0, }; - let ret = unsafe { - libc::fcntl( - fd, - if (cmd & libc::LOCK_NB) != 0 { - libc::F_SETLK - } else { - libc::F_SETLKW - }, - &l, - ) - }; - if ret < 0 { - return Err(vm.new_last_errno_error()); - } + let ret = host_fcntl::lockf(fd, cmd, &l).map_err(|_| vm.new_last_errno_error())?; Ok(vm.ctx.new_int(ret).into()) } } diff --git a/crates/stdlib/src/posixshmem.rs b/crates/stdlib/src/posixshmem.rs index da1a8cf1d89..91fdf4aafbc 100644 --- a/crates/stdlib/src/posixshmem.rs +++ b/crates/stdlib/src/posixshmem.rs @@ -9,7 +9,7 @@ mod _posixshmem { use crate::vm::{ FromArgs, PyResult, VirtualMachine, builtins::PyUtf8StrRef, convert::IntoPyException, }; - use rustpython_host_env::os::errno_io_error; + use rustpython_host_env::shm; #[derive(FromArgs)] struct ShmOpenArgs { @@ -25,26 +25,12 @@ mod _posixshmem { fn shm_open(args: ShmOpenArgs, vm: &VirtualMachine) -> PyResult { let name = CString::new(args.name.as_str()).map_err(|e| e.into_pyexception(vm))?; let mode: libc::c_uint = args.mode as _; - #[cfg(target_os = "freebsd")] - let mode = mode.try_into().unwrap(); - // SAFETY: `name` is a NUL-terminated string and `shm_open` does not write through it. - let fd = unsafe { libc::shm_open(name.as_ptr(), args.flags, mode) }; - if fd == -1 { - Err(errno_io_error().into_pyexception(vm)) - } else { - Ok(fd) - } + shm::shm_open(name.as_c_str(), args.flags, mode).map_err(|e| e.into_pyexception(vm)) } #[pyfunction] fn shm_unlink(name: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<()> { let name = CString::new(name.as_str()).map_err(|e| e.into_pyexception(vm))?; - // SAFETY: `name` is a valid NUL-terminated string and `shm_unlink` only reads it. - let ret = unsafe { libc::shm_unlink(name.as_ptr()) }; - if ret == -1 { - Err(errno_io_error().into_pyexception(vm)) - } else { - Ok(()) - } + shm::shm_unlink(name.as_c_str()).map_err(|e| e.into_pyexception(vm)) } } diff --git a/crates/stdlib/src/select.rs b/crates/stdlib/src/select.rs index b52144247f7..1cfe22e7c61 100644 --- a/crates/stdlib/src/select.rs +++ b/crates/stdlib/src/select.rs @@ -5,119 +5,9 @@ pub(crate) use decl::module_def; use crate::vm::{ PyObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine, builtins::PyListRef, }; -use core::mem; +use rustpython_host_env::select::{self as host_select, FdSet, RawFd}; use std::io; -#[cfg(unix)] -mod platform { - pub use libc::{FD_ISSET, FD_SET, FD_SETSIZE, FD_ZERO, fd_set, select, timeval}; - pub use std::os::unix::io::RawFd; - - pub const fn check_err(x: i32) -> bool { - x < 0 - } -} - -#[allow(non_snake_case)] -#[cfg(windows)] -mod platform { - pub use WinSock::{FD_SET as fd_set, FD_SETSIZE, SOCKET as RawFd, TIMEVAL as timeval, select}; - use windows_sys::Win32::Networking::WinSock; - - // based off winsock2.h: https://gist.github.com/piscisaureus/906386#file-winsock2-h-L128-L141 - - pub unsafe fn FD_SET(fd: RawFd, set: *mut fd_set) { - unsafe { - let mut slot = (&raw mut (*set).fd_array).cast::(); - let fd_count = (*set).fd_count; - for _ in 0..fd_count { - if *slot == fd { - return; - } - slot = slot.add(1); - } - // slot == &fd_array[fd_count] at this point - if fd_count < FD_SETSIZE { - *slot = fd as RawFd; - (*set).fd_count += 1; - } - } - } - - pub unsafe fn FD_ZERO(set: *mut fd_set) { - unsafe { (*set).fd_count = 0 }; - } - - pub unsafe fn FD_ISSET(fd: RawFd, set: *mut fd_set) -> bool { - use WinSock::__WSAFDIsSet; - unsafe { __WSAFDIsSet(fd as _, set) != 0 } - } - - pub fn check_err(x: i32) -> bool { - x == WinSock::SOCKET_ERROR - } -} - -#[cfg(target_os = "wasi")] -mod platform { - pub use libc::{FD_SETSIZE, timeval}; - pub use std::os::fd::RawFd; - - pub fn check_err(x: i32) -> bool { - x < 0 - } - - #[repr(C)] - pub struct fd_set { - __nfds: usize, - __fds: [libc::c_int; FD_SETSIZE], - } - - #[allow(non_snake_case)] - pub unsafe fn FD_ISSET(fd: RawFd, set: *const fd_set) -> bool { - let set = unsafe { &*set }; - let n = set.__nfds; - for p in &set.__fds[..n] { - if *p == fd { - return true; - } - } - false - } - - #[allow(non_snake_case)] - pub unsafe fn FD_SET(fd: RawFd, set: *mut fd_set) { - let set = unsafe { &mut *set }; - let n = set.__nfds; - for p in &set.__fds[..n] { - if *p == fd { - return; - } - } - set.__nfds = n + 1; - set.__fds[n] = fd; - } - - #[allow(non_snake_case)] - pub unsafe fn FD_ZERO(set: *mut fd_set) { - let set = unsafe { &mut *set }; - set.__nfds = 0; - } - - unsafe extern "C" { - pub fn select( - nfds: libc::c_int, - readfds: *mut fd_set, - writefds: *mut fd_set, - errorfds: *mut fd_set, - timeout: *const timeval, - ) -> libc::c_int; - } -} - -use platform::RawFd; -pub use platform::timeval; - #[derive(Traverse)] struct Selectable { obj: PyObjectRef, @@ -139,72 +29,6 @@ impl TryFromObject for Selectable { } } -// Keep it in a MaybeUninit, since on windows FD_ZERO doesn't actually zero the whole thing -#[repr(transparent)] -pub struct FdSet(mem::MaybeUninit); - -impl FdSet { - pub fn new() -> Self { - // it's just ints, and all the code that's actually - // interacting with it is in C, so it's safe to zero - let mut fdset = core::mem::MaybeUninit::zeroed(); - unsafe { platform::FD_ZERO(fdset.as_mut_ptr()) }; - Self(fdset) - } - - pub fn insert(&mut self, fd: RawFd) { - unsafe { platform::FD_SET(fd, self.0.as_mut_ptr()) }; - } - - pub fn contains(&mut self, fd: RawFd) -> bool { - unsafe { platform::FD_ISSET(fd, self.0.as_mut_ptr()) } - } - - pub fn clear(&mut self) { - unsafe { platform::FD_ZERO(self.0.as_mut_ptr()) }; - } - - pub fn highest(&mut self) -> Option { - (0..platform::FD_SETSIZE as RawFd) - .rev() - .find(|&i| self.contains(i)) - } -} - -pub fn select( - nfds: libc::c_int, - readfds: &mut FdSet, - writefds: &mut FdSet, - errfds: &mut FdSet, - timeout: Option<&mut timeval>, -) -> io::Result { - let timeout = match timeout { - Some(tv) => tv as *mut timeval, - None => core::ptr::null_mut(), - }; - let ret = unsafe { - platform::select( - nfds, - readfds.0.as_mut_ptr(), - writefds.0.as_mut_ptr(), - errfds.0.as_mut_ptr(), - timeout, - ) - }; - if platform::check_err(ret) { - Err(io::Error::last_os_error()) - } else { - Ok(ret) - } -} - -fn sec_to_timeval(sec: f64) -> timeval { - timeval { - tv_sec: sec.trunc() as _, - tv_usec: (sec.fract() * 1e6) as _, - } -} - #[pymodule(name = "select")] mod decl { use super::*; @@ -279,8 +103,9 @@ mod decl { .map_or(0, |n| n + 1) as _; loop { - let mut tv = timeout.map(sec_to_timeval); - let res = vm.allow_threads(|| super::select(nfds, &mut r, &mut w, &mut x, tv.as_mut())); + let mut tv = timeout.map(host_select::sec_to_timeval); + let res = + vm.allow_threads(|| host_select::select(nfds, &mut r, &mut w, &mut x, tv.as_mut())); match res { Ok(_) => break, diff --git a/crates/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs index fe2aa6be3da..f14e6ab62d7 100644 --- a/crates/stdlib/src/socket.rs +++ b/crates/stdlib/src/socket.rs @@ -2787,13 +2787,13 @@ mod _socket { } #[cfg(windows)] { - use crate::select; + use rustpython_host_env::select as host_select; let fd = sock_fileno(sock); - let mut reads = select::FdSet::new(); - let mut writes = select::FdSet::new(); - let mut errs = select::FdSet::new(); + let mut reads = host_select::FdSet::new(); + let mut writes = host_select::FdSet::new(); + let mut errs = host_select::FdSet::new(); let fd = fd as usize; match kind { @@ -2805,12 +2805,12 @@ mod _socket { } } - let mut interval = interval.map(|dur| select::timeval { + let mut interval = interval.map(|dur| host_select::timeval { tv_sec: dur.as_secs() as _, tv_usec: dur.subsec_micros() as _, }); - select::select( + host_select::select( fd as i32 + 1, &mut reads, &mut writes, diff --git a/crates/stdlib/src/syslog.rs b/crates/stdlib/src/syslog.rs index 8f446a8e161..fab82f14f56 100644 --- a/crates/stdlib/src/syslog.rs +++ b/crates/stdlib/src/syslog.rs @@ -4,15 +4,13 @@ pub(crate) use syslog::module_def; #[pymodule(name = "syslog")] mod syslog { - use crate::common::lock::PyRwLock; use crate::vm::{ PyObjectRef, PyPayload, PyResult, VirtualMachine, builtins::{PyStr, PyStrRef}, function::{OptionalArg, OptionalOption}, utils::ToCString, }; - use core::ffi::CStr; - use std::os::raw::c_char; + use rustpython_host_env::syslog as host_syslog; #[pyattr] use libc::{ @@ -41,28 +39,6 @@ mod syslog { None } - #[derive(Debug)] - enum GlobalIdent { - Explicit(Box), - Implicit, - } - - impl GlobalIdent { - fn as_ptr(&self) -> *const c_char { - match self { - Self::Explicit(cstr) => cstr.as_ptr(), - Self::Implicit => core::ptr::null(), - } - } - } - - fn global_ident() -> &'static PyRwLock> { - rustpython_common::static_cell! { - static IDENT: PyRwLock>; - }; - IDENT.get_or_init(|| PyRwLock::new(None)) - } - #[derive(Default, FromArgs)] struct OpenLogArgs { #[pyarg(any, optional)] @@ -83,16 +59,7 @@ mod syslog { } .map(|ident| ident.into_boxed_c_str()); - let ident = match ident { - Some(ident) => GlobalIdent::Explicit(ident), - None => GlobalIdent::Implicit, - }; - - { - let mut locked_ident = global_ident().write(); - unsafe { libc::openlog(ident.as_ptr(), logoption, facility) }; - *locked_ident = Some(ident); - } + host_syslog::openlog(ident, logoption, facility); Ok(()) } @@ -111,38 +78,34 @@ mod syslog { None => (LOG_INFO, args.priority.try_into_value(vm)?), }; - if global_ident().read().is_none() { + if !host_syslog::is_open() { openlog(OpenLogArgs::default(), vm)?; } - let (cformat, cmsg) = ("%s".to_cstring(vm)?, msg.to_cstring(vm)?); - unsafe { libc::syslog(priority, cformat.as_ptr(), cmsg.as_ptr()) }; + let cmsg = msg.to_cstring(vm)?; + host_syslog::syslog(priority, cmsg.as_c_str()); Ok(()) } #[pyfunction] fn closelog() { - if global_ident().read().is_some() { - let mut locked_ident = global_ident().write(); - unsafe { libc::closelog() }; - *locked_ident = None; - } + host_syslog::closelog(); } #[pyfunction] fn setlogmask(maskpri: i32) -> i32 { - unsafe { libc::setlogmask(maskpri) } + host_syslog::setlogmask(maskpri) } #[inline] #[pyfunction(name = "LOG_MASK")] const fn log_mask(pri: i32) -> i32 { - pri << 1 + host_syslog::log_mask(pri) } #[inline] #[pyfunction(name = "LOG_UPTO")] const fn log_upto(pri: i32) -> i32 { - (1 << (pri + 1)) - 1 + host_syslog::log_upto(pri) } } diff --git a/crates/stdlib/src/termios.rs b/crates/stdlib/src/termios.rs index 8f7963c159f..919b4ff702a 100644 --- a/crates/stdlib/src/termios.rs +++ b/crates/stdlib/src/termios.rs @@ -9,8 +9,7 @@ mod termios { builtins::{PyBaseExceptionRef, PyBytes, PyInt, PyListRef, PyTypeRef}, convert::ToPyObject, }; - use rustpython_host_env::os::ErrorExt; - use termios::Termios; + use rustpython_host_env::{os::ErrorExt, termios as host_termios}; // TODO: more ioctl numbers // NOTE: B2500000, B3000000, B3500000, B4000000, and CIBAUD @@ -171,7 +170,7 @@ mod termios { #[pyfunction] fn tcgetattr(fd: i32, vm: &VirtualMachine) -> PyResult> { - let termios = Termios::from_fd(fd).map_err(|e| termios_error(e, vm))?; + let termios = host_termios::tcgetattr(fd).map_err(|e| termios_error(e, vm))?; let noncanon = (termios.c_lflag & termios::ICANON) == 0; let cc = termios .c_cc @@ -200,7 +199,7 @@ mod termios { <&[PyObjectRef; 7]>::try_from(&*attributes.borrow_vec()) .map_err(|_| vm.new_type_error("tcsetattr, arg 3: must be 7 element list"))? .clone(); - let mut termios = Termios::from_fd(fd).map_err(|e| termios_error(e, vm))?; + let mut termios = host_termios::tcgetattr(fd).map_err(|e| termios_error(e, vm))?; termios.c_iflag = iflag.try_into_value(vm)?; termios.c_oflag = oflag.try_into_value(vm)?; termios.c_cflag = cflag.try_into_value(vm)?; @@ -231,32 +230,32 @@ mod termios { }; } - termios::tcsetattr(fd, when, &termios).map_err(|e| termios_error(e, vm))?; + host_termios::tcsetattr(fd, when, &termios).map_err(|e| termios_error(e, vm))?; Ok(()) } #[pyfunction] fn tcsendbreak(fd: i32, duration: i32, vm: &VirtualMachine) -> PyResult<()> { - termios::tcsendbreak(fd, duration).map_err(|e| termios_error(e, vm))?; + host_termios::tcsendbreak(fd, duration).map_err(|e| termios_error(e, vm))?; Ok(()) } #[pyfunction] fn tcdrain(fd: i32, vm: &VirtualMachine) -> PyResult<()> { - termios::tcdrain(fd).map_err(|e| termios_error(e, vm))?; + host_termios::tcdrain(fd).map_err(|e| termios_error(e, vm))?; Ok(()) } #[pyfunction] fn tcflush(fd: i32, queue: i32, vm: &VirtualMachine) -> PyResult<()> { - termios::tcflush(fd, queue).map_err(|e| termios_error(e, vm))?; + host_termios::tcflush(fd, queue).map_err(|e| termios_error(e, vm))?; Ok(()) } #[pyfunction] fn tcflow(fd: i32, action: i32, vm: &VirtualMachine) -> PyResult<()> { - termios::tcflow(fd, action).map_err(|e| termios_error(e, vm))?; + host_termios::tcflow(fd, action).map_err(|e| termios_error(e, vm))?; Ok(()) } diff --git a/crates/vm/Cargo.toml b/crates/vm/Cargo.toml index 0d02a89a889..1537a8fd517 100644 --- a/crates/vm/Cargo.toml +++ b/crates/vm/Cargo.toml @@ -33,7 +33,7 @@ rustpython-compiler = { workspace = true, optional = true } rustpython-codegen = { workspace = true, optional = true } rustpython-common = { workspace = true } rustpython-derive = { workspace = true } -rustpython-host-env = { workspace = true } +rustpython-host_env = { workspace = true } rustpython-jit = { workspace = true, optional = true } ruff_python_ast = { workspace = true, optional = true } diff --git a/crates/vm/src/stdlib/_signal.rs b/crates/vm/src/stdlib/_signal.rs index eddf8ce8e46..0d9dfad311d 100644 --- a/crates/vm/src/stdlib/_signal.rs +++ b/crates/vm/src/stdlib/_signal.rs @@ -13,6 +13,8 @@ pub(crate) mod _signal { function::{ArgIntoFloat, OptionalArg}, }; use core::sync::atomic::{self, Ordering}; + #[cfg(unix)] + use rustpython_host_env::signal::{double_to_timeval, itimerval_to_tuple}; #[cfg(any(unix, windows))] use libc::sighandler_t; @@ -280,27 +282,6 @@ pub(crate) mod _signal { Ok(()) } - #[cfg(unix)] - fn timeval_to_double(tv: &libc::timeval) -> f64 { - tv.tv_sec as f64 + (tv.tv_usec as f64 / 1_000_000.0) - } - - #[cfg(unix)] - fn double_to_timeval(val: f64) -> libc::timeval { - libc::timeval { - tv_sec: val.trunc() as _, - tv_usec: ((val.fract()) * 1_000_000.0) as _, - } - } - - #[cfg(unix)] - fn itimerval_to_tuple(it: &libc::itimerval) -> (f64, f64) { - ( - timeval_to_double(&it.it_value), - timeval_to_double(&it.it_interval), - ) - } - #[cfg(unix)] #[pyfunction] fn setitimer( diff --git a/crates/vm/src/stdlib/_winapi.rs b/crates/vm/src/stdlib/_winapi.rs index bc560eb92bc..6fb3dc1bffa 100644 --- a/crates/vm/src/stdlib/_winapi.rs +++ b/crates/vm/src/stdlib/_winapi.rs @@ -16,6 +16,7 @@ mod _winapi { }; use core::ptr::{null, null_mut}; use rustpython_common::wtf8::Wtf8Buf; + use rustpython_host_env::winapi as host_winapi; use rustpython_host_env::windows::ToWideString; use windows_sys::Win32::Foundation::{HANDLE, MAX_PATH}; @@ -201,12 +202,12 @@ mod _winapi { #[pyfunction] fn GetACP() -> u32 { - unsafe { windows_sys::Win32::Globalization::GetACP() } + host_winapi::get_acp() } #[pyfunction] fn GetCurrentProcess() -> WinHandle { - WinHandle(unsafe { windows_sys::Win32::System::Threading::GetCurrentProcess() }) + WinHandle(host_winapi::get_current_process()) } #[pyfunction] @@ -224,12 +225,12 @@ mod _winapi { #[pyfunction] fn GetLastError() -> u32 { - unsafe { windows_sys::Win32::Foundation::GetLastError() } + host_winapi::get_last_error() } #[pyfunction] fn GetVersion() -> u32 { - unsafe { windows_sys::Win32::System::SystemInformation::GetVersion() } + host_winapi::get_version() } #[derive(FromArgs)] diff --git a/crates/vm/src/stdlib/msvcrt.rs b/crates/vm/src/stdlib/msvcrt.rs index 46beb306fc6..f10de1238d5 100644 --- a/crates/vm/src/stdlib/msvcrt.rs +++ b/crates/vm/src/stdlib/msvcrt.rs @@ -8,9 +8,10 @@ mod msvcrt { PyRef, PyResult, VirtualMachine, builtins::{PyBytes, PyStrRef}, convert::IntoPyException, - host_env::{crt_fd, suppress_iph}, + host_env::crt_fd, }; use itertools::Itertools; + use rustpython_host_env::msvcrt as host_msvcrt; use std::os::windows::io::AsRawHandle; use windows_sys::Win32::System::Diagnostics::Debug; @@ -21,54 +22,36 @@ mod msvcrt { }; pub fn setmode_binary(fd: crt_fd::Borrowed<'_>) { - unsafe { suppress_iph!(_setmode(fd, libc::O_BINARY)) }; - } - - unsafe extern "C" { - fn _getch() -> i32; - fn _getwch() -> u32; - fn _getche() -> i32; - fn _getwche() -> u32; - fn _putch(c: u32) -> i32; - fn _putwch(c: u16) -> u32; - fn _ungetch(c: i32) -> i32; - fn _ungetwch(c: u32) -> u32; - fn _locking(fd: i32, mode: i32, nbytes: i64) -> i32; - fn _heapmin() -> i32; - fn _kbhit() -> i32; + host_msvcrt::setmode_binary(fd); } // Locking mode constants #[pyattr] - const LK_UNLCK: i32 = 0; // Unlock + const LK_UNLCK: i32 = host_msvcrt::LK_UNLCK; // Unlock #[pyattr] - const LK_LOCK: i32 = 1; // Lock (blocking) + const LK_LOCK: i32 = host_msvcrt::LK_LOCK; // Lock (blocking) #[pyattr] - const LK_NBLCK: i32 = 2; // Non-blocking lock + const LK_NBLCK: i32 = host_msvcrt::LK_NBLCK; // Non-blocking lock #[pyattr] - const LK_RLCK: i32 = 3; // Lock for reading (same as LK_LOCK) + const LK_RLCK: i32 = host_msvcrt::LK_RLCK; // Lock for reading (same as LK_LOCK) #[pyattr] - const LK_NBRLCK: i32 = 4; // Non-blocking lock for reading (same as LK_NBLCK) + const LK_NBRLCK: i32 = host_msvcrt::LK_NBRLCK; // Non-blocking lock for reading (same as LK_NBLCK) #[pyfunction] fn getch() -> Vec { - let c = unsafe { _getch() }; - vec![c as u8] + host_msvcrt::getch() } #[pyfunction] fn getwch() -> String { - let c = unsafe { _getwch() }; - char::from_u32(c).unwrap().to_string() + host_msvcrt::getwch() } #[pyfunction] fn getche() -> Vec { - let c = unsafe { _getche() }; - vec![c as u8] + host_msvcrt::getche() } #[pyfunction] fn getwche() -> String { - let c = unsafe { _getwche() }; - char::from_u32(c).unwrap().to_string() + host_msvcrt::getwche() } #[pyfunction] fn putch(b: PyRef, vm: &VirtualMachine) -> PyResult<()> { @@ -76,7 +59,7 @@ mod msvcrt { b.as_bytes().iter().exactly_one().map_err(|_| { vm.new_type_error("putch() argument must be a byte string of length 1") })?; - unsafe { suppress_iph!(_putch(c.into())) }; + host_msvcrt::putch(c); Ok(()) } #[pyfunction] @@ -86,7 +69,7 @@ mod msvcrt { .chars() .exactly_one() .map_err(|_| vm.new_type_error("putch() argument must be a string of length 1"))?; - unsafe { suppress_iph!(_putwch(c as u16)) }; + host_msvcrt::putwch(c); Ok(()) } @@ -95,13 +78,7 @@ mod msvcrt { let &c = b.as_bytes().iter().exactly_one().map_err(|_| { vm.new_type_error("ungetch() argument must be a byte string of length 1") })?; - let ret = unsafe { suppress_iph!(_ungetch(c as i32)) }; - if ret == -1 { - // EOF returned means the buffer is full - Err(vm.new_os_error(libc::ENOSPC)) - } else { - Ok(()) - } + host_msvcrt::ungetch(c).map_err(|e| e.into_pyexception(vm)) } #[pyfunction] @@ -110,62 +87,32 @@ mod msvcrt { s.expect_str().chars().exactly_one().map_err(|_| { vm.new_type_error("ungetwch() argument must be a string of length 1") })?; - let ret = unsafe { suppress_iph!(_ungetwch(c as u32)) }; - if ret == 0xFFFF { - // WEOF returned means the buffer is full - Err(vm.new_os_error(libc::ENOSPC)) - } else { - Ok(()) - } + host_msvcrt::ungetwch(c).map_err(|e| e.into_pyexception(vm)) } #[pyfunction] fn kbhit() -> i32 { - unsafe { _kbhit() } + host_msvcrt::kbhit() } #[pyfunction] fn locking(fd: i32, mode: i32, nbytes: i64, vm: &VirtualMachine) -> PyResult<()> { - let ret = unsafe { suppress_iph!(_locking(fd, mode, nbytes)) }; - if ret == -1 { - Err(vm.new_last_errno_error()) - } else { - Ok(()) - } + host_msvcrt::locking(fd, mode, nbytes).map_err(|e| e.into_pyexception(vm)) } #[pyfunction] fn heapmin(vm: &VirtualMachine) -> PyResult<()> { - let ret = unsafe { suppress_iph!(_heapmin()) }; - if ret == -1 { - Err(vm.new_last_errno_error()) - } else { - Ok(()) - } - } - - unsafe extern "C" { - fn _setmode(fd: crt_fd::Borrowed<'_>, flags: i32) -> i32; + host_msvcrt::heapmin().map_err(|e| e.into_pyexception(vm)) } #[pyfunction] fn setmode(fd: crt_fd::Borrowed<'_>, flags: i32, vm: &VirtualMachine) -> PyResult { - let flags = unsafe { suppress_iph!(_setmode(fd, flags)) }; - if flags == -1 { - Err(vm.new_last_errno_error()) - } else { - Ok(flags) - } + host_msvcrt::setmode(fd, flags).map_err(|e| e.into_pyexception(vm)) } #[pyfunction] fn open_osfhandle(handle: isize, flags: i32, vm: &VirtualMachine) -> PyResult { - let ret = unsafe { suppress_iph!(libc::open_osfhandle(handle, flags)) }; - if ret == -1 { - Err(vm.new_last_errno_error()) - } else { - Ok(ret) - } + host_msvcrt::open_osfhandle(handle, flags).map_err(|e| e.into_pyexception(vm)) } #[pyfunction] @@ -178,12 +125,12 @@ mod msvcrt { #[allow(non_snake_case)] #[pyfunction] fn GetErrorMode() -> u32 { - unsafe { suppress_iph!(Debug::GetErrorMode()) } + host_msvcrt::get_error_mode() } #[allow(non_snake_case)] #[pyfunction] fn SetErrorMode(mode: Debug::THREAD_ERROR_MODE, _: &VirtualMachine) -> u32 { - unsafe { suppress_iph!(Debug::SetErrorMode(mode)) } + host_msvcrt::set_error_mode(mode) } } diff --git a/crates/vm/src/stdlib/nt.rs b/crates/vm/src/stdlib/nt.rs index 97f5b310238..992ca580ee4 100644 --- a/crates/vm/src/stdlib/nt.rs +++ b/crates/vm/src/stdlib/nt.rs @@ -20,6 +20,7 @@ pub(crate) mod module { use core::mem::MaybeUninit; use libc::intptr_t; use rustpython_common::wtf8::Wtf8Buf; + use rustpython_host_env::nt as host_nt; use std::os::windows::io::AsRawHandle; use std::{env, io, os::windows::ffi::OsStringExt}; use windows_sys::Win32::{ @@ -274,76 +275,16 @@ pub(crate) mod module { const S_IWRITE: u32 = 128; fn win32_hchmod(handle: Foundation::HANDLE, mode: u32, vm: &VirtualMachine) -> PyResult<()> { - use windows_sys::Win32::Storage::FileSystem::{ - FILE_BASIC_INFO, FileBasicInfo, GetFileInformationByHandleEx, - SetFileInformationByHandle, - }; - - // Get current file info - let mut info: FILE_BASIC_INFO = unsafe { core::mem::zeroed() }; - let ret = unsafe { - GetFileInformationByHandleEx( - handle, - FileBasicInfo, - &mut info as *mut _ as *mut _, - core::mem::size_of::() as u32, - ) - }; - if ret == 0 { - return Err(vm.new_last_os_error()); - } - - // Modify readonly attribute based on S_IWRITE bit - if mode & S_IWRITE != 0 { - info.FileAttributes &= !FileSystem::FILE_ATTRIBUTE_READONLY; - } else { - info.FileAttributes |= FileSystem::FILE_ATTRIBUTE_READONLY; - } - - // Set the new attributes - let ret = unsafe { - SetFileInformationByHandle( - handle, - FileBasicInfo, - &info as *const _ as *const _, - core::mem::size_of::() as u32, - ) - }; - if ret == 0 { - return Err(vm.new_last_os_error()); - } - - Ok(()) + host_nt::win32_hchmod(handle, mode, S_IWRITE).map_err(|e| e.to_pyexception(vm)) } fn fchmod_impl(fd: i32, mode: u32, vm: &VirtualMachine) -> PyResult<()> { - // Get Windows HANDLE from fd - let borrowed = unsafe { crt_fd::Borrowed::borrow_raw(fd) }; - let handle = crt_fd::as_handle(borrowed).map_err(|e| e.to_pyexception(vm))?; - let hfile = handle.as_raw_handle() as Foundation::HANDLE; - win32_hchmod(hfile, mode, vm) + host_nt::fchmod(fd, mode, S_IWRITE).map_err(|e| e.to_pyexception(vm)) } fn win32_lchmod(path: &OsPath, mode: u32, vm: &VirtualMachine) -> PyResult<()> { - use windows_sys::Win32::Storage::FileSystem::{GetFileAttributesW, SetFileAttributesW}; - - let wide = path.to_wide_cstring(vm)?; - let attr = unsafe { GetFileAttributesW(wide.as_ptr()) }; - if attr == FileSystem::INVALID_FILE_ATTRIBUTES { - let err = io::Error::last_os_error(); - return Err(OSErrorBuilder::with_filename(&err, path.clone(), vm)); - } - let new_attr = if mode & S_IWRITE != 0 { - attr & !FileSystem::FILE_ATTRIBUTE_READONLY - } else { - attr | FileSystem::FILE_ATTRIBUTE_READONLY - }; - let ret = unsafe { SetFileAttributesW(wide.as_ptr(), new_attr) }; - if ret == 0 { - let err = io::Error::last_os_error(); - return Err(OSErrorBuilder::with_filename(&err, path.clone(), vm)); - } - Ok(()) + host_nt::win32_lchmod(path.path.as_os_str(), mode, S_IWRITE) + .map_err(|err| OSErrorBuilder::with_filename(&err, path.clone(), vm)) } #[pyfunction] diff --git a/crates/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs index b8c57f04e42..d5a8b702faa 100644 --- a/crates/vm/src/stdlib/posix.rs +++ b/crates/vm/src/stdlib/posix.rs @@ -1,19 +1,8 @@ // spell-checker:disable -use std::os::fd::BorrowedFd; - pub(crate) use module::module_def; -pub fn set_inheritable(fd: BorrowedFd<'_>, inheritable: bool) -> nix::Result<()> { - use nix::fcntl; - let flags = fcntl::FdFlag::from_bits_truncate(fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD)?); - let mut new_flags = flags; - new_flags.set(fcntl::FdFlag::FD_CLOEXEC, !inheritable); - if flags != new_flags { - fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFD(new_flags))?; - } - Ok(()) -} +pub use rustpython_host_env::posix::set_inheritable; #[pymodule(name = "posix", with( super::os::_os, diff --git a/crates/vm/src/stdlib/time.rs b/crates/vm/src/stdlib/time.rs index 13d353fb35c..80b88865e66 100644 --- a/crates/vm/src/stdlib/time.rs +++ b/crates/vm/src/stdlib/time.rs @@ -41,9 +41,12 @@ mod decl { naive::{NaiveDate, NaiveDateTime, NaiveTime}, }; use core::time::Duration; + #[cfg(any(unix, windows))] + use rustpython_host_env::time::asctime_from_tm; + use rustpython_host_env::time::{self as host_time}; #[cfg(target_env = "msvc")] #[cfg(not(target_arch = "wasm32"))] - use windows_sys::Win32::System::Time::{GetTimeZoneInformation, TIME_ZONE_INFORMATION}; + use windows_sys::Win32::System::Time::TIME_ZONE_INFORMATION; #[cfg(windows)] unsafe extern "C" { @@ -56,27 +59,24 @@ mod decl { } #[allow(dead_code)] - pub(super) const SEC_TO_MS: i64 = 1000; + pub(super) const SEC_TO_MS: i64 = host_time::SEC_TO_MS; #[allow(dead_code)] - pub(super) const MS_TO_US: i64 = 1000; + pub(super) const MS_TO_US: i64 = host_time::MS_TO_US; #[allow(dead_code)] - pub(super) const SEC_TO_US: i64 = SEC_TO_MS * MS_TO_US; + pub(super) const SEC_TO_US: i64 = host_time::SEC_TO_US; #[allow(dead_code)] - pub(super) const US_TO_NS: i64 = 1000; + pub(super) const US_TO_NS: i64 = host_time::US_TO_NS; #[allow(dead_code)] - pub(super) const MS_TO_NS: i64 = MS_TO_US * US_TO_NS; + pub(super) const MS_TO_NS: i64 = host_time::MS_TO_NS; #[allow(dead_code)] - pub(super) const SEC_TO_NS: i64 = SEC_TO_MS * MS_TO_NS; + pub(super) const SEC_TO_NS: i64 = host_time::SEC_TO_NS; #[allow(dead_code)] - pub(super) const NS_TO_MS: i64 = 1000 * 1000; + pub(super) const NS_TO_MS: i64 = host_time::NS_TO_MS; #[allow(dead_code)] - pub(super) const NS_TO_US: i64 = 1000; + pub(super) const NS_TO_US: i64 = host_time::NS_TO_US; fn duration_since_system_now(vm: &VirtualMachine) -> PyResult { - use std::time::{SystemTime, UNIX_EPOCH}; - - SystemTime::now() - .duration_since(UNIX_EPOCH) + host_time::duration_since_system_now() .map_err(|e| vm.new_value_error(format!("Time error: {e:?}"))) } @@ -218,9 +218,7 @@ mod decl { #[cfg(target_env = "msvc")] #[cfg(not(target_arch = "wasm32"))] pub(super) fn get_tz_info() -> TIME_ZONE_INFORMATION { - let mut info: TIME_ZONE_INFORMATION = unsafe { core::mem::zeroed() }; - unsafe { GetTimeZoneInformation(&mut info) }; - info + host_time::get_tz_info() } // #[pyfunction] @@ -486,24 +484,6 @@ mod decl { } } - #[cfg(any(unix, windows))] - fn asctime_from_tm(tm: &libc::tm) -> String { - const WDAY_NAME: [&str; 7] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - const MON_NAME: [&str; 12] = [ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", - ]; - format!( - "{} {}{:>3} {:02}:{:02}:{:02} {}", - WDAY_NAME[tm.tm_wday as usize], - MON_NAME[tm.tm_mon as usize], - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - tm.tm_year + 1900 - ) - } - #[cfg(not(any(unix, windows)))] impl OptionalArg { fn naive_or_local(self, vm: &VirtualMachine) -> PyResult {