diff --git a/Cargo.lock b/Cargo.lock index 16941b8265a..6e07767c6df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3143,7 +3143,6 @@ dependencies = [ "malachite-base", "malachite-bigint", "malachite-q", - "nix 0.30.1", "num-complex", "num-traits", "parking_lot", @@ -3152,8 +3151,6 @@ dependencies = [ "rustpython-wtf8", "siphasher", "unicode_names2 2.0.0", - "widestring", - "windows-sys 0.61.2", ] [[package]] @@ -3222,6 +3219,19 @@ dependencies = [ "phf 0.13.1", ] +[[package]] +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", +] + [[package]] name = "rustpython-jit" version = "0.5.0" @@ -3409,6 +3419,7 @@ dependencies = [ "rustls-platform-verifier", "rustpython-common", "rustpython-derive", + "rustpython-host_env", "rustpython-ruff_python_ast", "rustpython-ruff_python_parser", "rustpython-ruff_source_file", @@ -3488,6 +3499,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 cbc9c8f042f..bd46b0fceae 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/clippy.toml b/crates/common/clippy.toml new file mode 100644 index 00000000000..a83f3d6ddda --- /dev/null +++ b/crates/common/clippy.toml @@ -0,0 +1,32 @@ +disallowed-methods = [ + { path = "std::fs::read", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::write", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::read_to_string", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::read_dir", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::create_dir", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::create_dir_all", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::remove_file", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::remove_dir", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::metadata", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::symlink_metadata", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::canonicalize", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::File::open", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::File::create", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::OpenOptions::open", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::env::var", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::var_os", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::set_var", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::remove_var", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::vars", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::vars_os", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::current_dir", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::set_current_dir", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::temp_dir", reason = "use rustpython_host_env for host environment access" }, + { path = "std::process::Command::new", reason = "use rustpython_host_env for host process access" }, + { path = "std::process::exit", reason = "use rustpython_host_env for host process access" }, + { path = "std::process::abort", reason = "use rustpython_host_env for host process access" }, + { path = "std::process::id", reason = "use rustpython_host_env for host process access" }, + { path = "std::net::TcpStream::connect", reason = "use rustpython_host_env for host network access" }, + { path = "std::net::TcpListener::bind", reason = "use rustpython_host_env for host network access" }, + { path = "std::net::UdpSocket::bind", reason = "use rustpython_host_env for host network access" }, +] diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index e514c17541f..53a8e0d752b 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -1,37 +1,26 @@ //! A crate to hold types and functions common to all rustpython components. #![cfg_attr(not(feature = "std"), no_std)] +#![deny(clippy::disallowed_methods)] 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/common/src/refcount.rs b/crates/common/src/refcount.rs index 4d871a67a0d..d17f5ccd9f3 100644 --- a/crates/common/src/refcount.rs +++ b/crates/common/src/refcount.rs @@ -15,6 +15,10 @@ const WEAK_COUNT: usize = 1 << STRONG_WIDTH; #[inline(never)] #[cold] +#[allow( + clippy::disallowed_methods, + reason = "refcount overflow must preserve upstream abort semantics" +)] fn refcount_overflow() -> ! { #[cfg(feature = "std")] std::process::abort(); diff --git a/crates/host_env/Cargo.toml b/crates/host_env/Cargo.toml new file mode 100644 index 00000000000..e826a8baaf4 --- /dev/null +++ b/crates/host_env/Cargo.toml @@ -0,0 +1,41 @@ +[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(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] +workspace = true diff --git a/crates/host_env/clippy.toml b/crates/host_env/clippy.toml new file mode 100644 index 00000000000..5539ef094fe --- /dev/null +++ b/crates/host_env/clippy.toml @@ -0,0 +1 @@ +disallowed-methods = [] 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/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/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/fs.rs b/crates/host_env/src/fs.rs new file mode 100644 index 00000000000..911bd4575b9 --- /dev/null +++ b/crates/host_env/src/fs.rs @@ -0,0 +1,59 @@ +use std::{ + fs::{self, File, Metadata, ReadDir}, + io, + path::Path, +}; + +pub fn open(path: impl AsRef) -> io::Result { + File::open(path) +} + +pub fn read(path: impl AsRef) -> io::Result> { + fs::read(path) +} + +pub fn read_to_string(path: impl AsRef) -> io::Result { + fs::read_to_string(path) +} + +pub fn read_dir(path: impl AsRef) -> io::Result { + fs::read_dir(path) +} + +pub fn create_dir_all(path: impl AsRef) -> io::Result<()> { + fs::create_dir_all(path) +} + +pub fn remove_dir(path: impl AsRef) -> io::Result<()> { + fs::remove_dir(path) +} + +pub fn remove_file(path: impl AsRef) -> io::Result<()> { + fs::remove_file(path) +} + +pub fn metadata(path: impl AsRef) -> io::Result { + fs::metadata(path) +} + +pub fn symlink_metadata(path: impl AsRef) -> io::Result { + fs::symlink_metadata(path) +} + +pub fn open_write(path: impl AsRef) -> io::Result { + fs::OpenOptions::new().write(true).open(path) +} + +pub fn canonicalize(path: impl AsRef) -> io::Result { + fs::canonicalize(path) +} + +#[cfg(windows)] +pub fn open_write_with_custom_flags(path: impl AsRef, flags: u32) -> io::Result { + use std::os::windows::fs::OpenOptionsExt; + + fs::OpenOptions::new() + .write(true) + .custom_flags(flags) + .open(path) +} diff --git a/crates/host_env/src/lib.rs b/crates/host_env/src/lib.rs new file mode 100644 index 00000000000..80c2109a46f --- /dev/null +++ b/crates/host_env/src/lib.rs @@ -0,0 +1,42 @@ +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(any(not(target_arch = "wasm32"), target_os = "wasi"))] +pub mod fs; + +#[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/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/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/common/src/os.rs b/crates/host_env/src/os.rs similarity index 88% rename from crates/common/src/os.rs rename to crates/host_env/src/os.rs index 2a318f477e5..9d5aac5178b 100644 --- a/crates/common/src/os.rs +++ b/crates/host_env/src/os.rs @@ -2,7 +2,13 @@ // TODO: we can move more os-specific bindings/interfaces from stdlib::{os, posix, nt} to here use core::str::Utf8Error; -use std::{io, process::ExitCode}; +use std::{ + env, + ffi::{OsStr, OsString}, + io, + path::PathBuf, + process::ExitCode, +}; /// Convert exit code to std::process::ExitCode /// @@ -22,6 +28,56 @@ pub fn exit_code(code: u32) -> ExitCode { ExitCode::from(code as u8) } +pub fn current_dir() -> io::Result { + env::current_dir() +} + +pub fn temp_dir() -> PathBuf { + env::temp_dir() +} + +pub fn var(key: &str) -> Result { + env::var(key) +} + +pub fn var_os(key: impl AsRef) -> Option { + env::var_os(key) +} + +pub fn vars_os() -> env::VarsOs { + env::vars_os() +} + +pub fn vars() -> env::Vars { + env::vars() +} + +/// # Safety +/// The caller must ensure no other threads can concurrently read or write +/// the process environment while this mutation is performed. +pub unsafe fn set_var(key: impl AsRef, value: impl AsRef) { + unsafe { env::set_var(key, value) }; +} + +/// # Safety +/// The caller must ensure no other threads can concurrently read or write +/// the process environment while this mutation is performed. +pub unsafe fn remove_var(key: impl AsRef) { + unsafe { env::remove_var(key) }; +} + +pub fn set_current_dir(path: impl AsRef) -> io::Result<()> { + env::set_current_dir(path) +} + +pub fn process_id() -> u32 { + std::process::id() +} + +pub fn exit(code: i32) -> ! { + std::process::exit(code) +} + pub trait ErrorExt { fn posix_errno(&self) -> i32; } diff --git a/crates/host_env/src/posix.rs b/crates/host_env/src/posix.rs new file mode 100644 index 00000000000..f3a5bbf4dcf --- /dev/null +++ b/crates/host_env/src/posix.rs @@ -0,0 +1,88 @@ +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(()) +} + +#[cfg(target_os = "macos")] +pub fn get_number_of_os_threads() -> isize { + type MachPortT = libc::c_uint; + type KernReturnT = libc::c_int; + type MachMsgTypeNumberT = libc::c_uint; + type ThreadActArrayT = *mut MachPortT; + const KERN_SUCCESS: KernReturnT = 0; + unsafe extern "C" { + fn mach_task_self() -> MachPortT; + fn task_for_pid( + task: MachPortT, + pid: libc::c_int, + target_task: *mut MachPortT, + ) -> KernReturnT; + fn task_threads( + target_task: MachPortT, + act_list: *mut ThreadActArrayT, + act_list_cnt: *mut MachMsgTypeNumberT, + ) -> KernReturnT; + fn vm_deallocate( + target_task: MachPortT, + address: libc::uintptr_t, + size: libc::uintptr_t, + ) -> KernReturnT; + } + + let self_task = unsafe { mach_task_self() }; + let mut proc_task: MachPortT = 0; + if unsafe { task_for_pid(self_task, libc::getpid(), &mut proc_task) } == KERN_SUCCESS { + let mut threads: ThreadActArrayT = core::ptr::null_mut(); + let mut n_threads: MachMsgTypeNumberT = 0; + if unsafe { task_threads(proc_task, &mut threads, &mut n_threads) } == KERN_SUCCESS { + if !threads.is_null() { + let _ = unsafe { + vm_deallocate( + self_task, + threads as libc::uintptr_t, + (n_threads as usize * core::mem::size_of::()) as libc::uintptr_t, + ) + }; + } + return n_threads as isize; + } + } + 0 +} + +#[cfg(target_os = "linux")] +pub fn get_number_of_os_threads() -> isize { + use std::io::Read as _; + + let mut file = match crate::fs::open("/proc/self/stat") { + Ok(f) => f, + Err(_) => return 0, + }; + let mut buf = [0u8; 160]; + let n = match file.read(&mut buf) { + Ok(n) => n, + Err(_) => return 0, + }; + let line = match core::str::from_utf8(&buf[..n]) { + Ok(s) => s, + Err(_) => return 0, + }; + if let Some(field) = line.split_whitespace().nth(19) { + return field.parse::().unwrap_or(0); + } + 0 +} + +#[cfg(not(any(target_os = "macos", target_os = "linux")))] +pub fn get_number_of_os_threads() -> isize { + 0 +} 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..b643d9c2e39 --- /dev/null +++ b/crates/host_env/src/time.rs @@ -0,0 +1,41 @@ +use core::time::Duration; +use std::time::{SystemTime, SystemTimeError, 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() -> Result { + SystemTime::now().duration_since(UNIX_EPOCH) +} + +#[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/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..6971a54f992 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/build.rs b/crates/stdlib/build.rs index 95c34c4fb3c..5a06d24a269 100644 --- a/crates/stdlib/build.rs +++ b/crates/stdlib/build.rs @@ -1,3 +1,8 @@ +#![allow( + clippy::disallowed_methods, + reason = "build scripts cannot use rustpython-host_env" +)] + // spell-checker:ignore ossl osslconf fn main() { diff --git a/crates/stdlib/clippy.toml b/crates/stdlib/clippy.toml new file mode 100644 index 00000000000..a83f3d6ddda --- /dev/null +++ b/crates/stdlib/clippy.toml @@ -0,0 +1,32 @@ +disallowed-methods = [ + { path = "std::fs::read", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::write", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::read_to_string", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::read_dir", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::create_dir", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::create_dir_all", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::remove_file", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::remove_dir", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::metadata", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::symlink_metadata", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::canonicalize", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::File::open", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::File::create", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::OpenOptions::open", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::env::var", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::var_os", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::set_var", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::remove_var", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::vars", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::vars_os", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::current_dir", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::set_current_dir", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::temp_dir", reason = "use rustpython_host_env for host environment access" }, + { path = "std::process::Command::new", reason = "use rustpython_host_env for host process access" }, + { path = "std::process::exit", reason = "use rustpython_host_env for host process access" }, + { path = "std::process::abort", reason = "use rustpython_host_env for host process access" }, + { path = "std::process::id", reason = "use rustpython_host_env for host process access" }, + { path = "std::net::TcpStream::connect", reason = "use rustpython_host_env for host network access" }, + { path = "std::net::TcpListener::bind", reason = "use rustpython_host_env for host network access" }, + { path = "std::net::UdpSocket::bind", reason = "use rustpython_host_env for host network access" }, +] diff --git a/crates/stdlib/src/faulthandler.rs b/crates/stdlib/src/faulthandler.rs index 9c4373c312e..e562a382345 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 @@ -587,7 +587,7 @@ mod decl { } // Fallback - std::process::exit(1); + rustpython_host_env::os::exit(1); } // Windows vectored exception handler (faulthandler.c:417-480) @@ -880,7 +880,7 @@ mod decl { } if exit { - std::process::exit(1); + rustpython_host_env::os::exit(1); } } 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/lib.rs b/crates/stdlib/src/lib.rs index 941de413bfc..6d91500227f 100644 --- a/crates/stdlib/src/lib.rs +++ b/crates/stdlib/src/lib.rs @@ -1,6 +1,6 @@ // to allow `mod foo {}` in foo.rs; clippy thinks this is a mistake/misunderstanding of // how `mod` works, but we want this sometimes for pymodule declarations - +#![deny(clippy::disallowed_methods)] #![allow(clippy::module_inception)] #[macro_use] 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 88e616aa5d3..9c7161d8048 100644 --- a/crates/stdlib/src/openssl.rs +++ b/crates/stdlib/src/openssl.rs @@ -1671,17 +1671,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 @@ -1880,7 +1880,7 @@ mod _ssl { const PEM_BUFSIZE: usize = 1024; // Read key file data - let key_data = std::fs::read(key_file_path) + let key_data = rustpython_host_env::fs::read(key_file_path) .map_err(|e| crate::vm::convert::ToPyException::to_pyexception(&e, vm))?; let pkey = if let Some(ref pw_obj) = password { diff --git a/crates/stdlib/src/openssl/cert.rs b/crates/stdlib/src/openssl/cert.rs index b63d824a837..4f0e222f14f 100644 --- a/crates/stdlib/src/openssl/cert.rs +++ b/crates/stdlib/src/openssl/cert.rs @@ -359,7 +359,7 @@ pub(crate) mod ssl_cert { #[pyfunction] pub(crate) fn _test_decode_cert(path: FsPath, vm: &VirtualMachine) -> PyResult { let path = path.to_path_buf(vm)?; - let pem = std::fs::read(path).map_err(|e| e.to_pyexception(vm))?; + let pem = rustpython_host_env::fs::read(path).map_err(|e| e.to_pyexception(vm))?; let x509 = X509::from_pem(&pem).map_err(|e| convert_openssl_error(vm, e))?; cert_to_py(vm, &x509, false) } 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..91fdf4aafbc 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::shm; #[derive(FromArgs)] struct ShmOpenArgs { @@ -27,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/posixsubprocess.rs b/crates/stdlib/src/posixsubprocess.rs index fec2ceb16d5..01b0d466ffc 100644 --- a/crates/stdlib/src/posixsubprocess.rs +++ b/crates/stdlib/src/posixsubprocess.rs @@ -248,7 +248,7 @@ fn exec(args: &ForkExecArgs<'_>, procargs: ProcArgs<'_>, vm: &VirtualMachine) -> // errno is written in hex format let _ = write!(pipe, "OSError:{:x}:{}", e as i32, ctx.as_msg()); } - std::process::exit(255) + rustpython_host_env::os::exit(255) } } } 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 d3fe59144ef..f14e6ab62d7 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(()) } @@ -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, @@ -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/ssl.rs b/crates/stdlib/src/ssl.rs index 06c0010a79d..51f5bc042f4 100644 --- a/crates/stdlib/src/ssl.rs +++ b/crates/stdlib/src/ssl.rs @@ -1766,8 +1766,8 @@ mod _ssl { // Validate that the file contains DH parameters // Read the file and check for DH PARAMETERS header - let contents = - std::fs::read_to_string(&path_str).map_err(|e| vm.new_os_error(e.to_string()))?; + let contents = rustpython_host_env::fs::read_to_string(&path_str) + .map_err(|e| vm.new_os_error(e.to_string()))?; if !contents.contains("BEGIN DH PARAMETERS") && !contents.contains("BEGIN X9.42 DH PARAMETERS") @@ -2154,7 +2154,7 @@ mod _ssl { path: &str, vm: &VirtualMachine, ) -> PyResult>> { - let data = std::fs::read(path).map_err(|e| match e.kind() { + let data = rustpython_host_env::fs::read(path).map_err(|e| match e.kind() { std::io::ErrorKind::NotFound | std::io::ErrorKind::PermissionDenied => { e.into_pyexception(vm) } @@ -4913,7 +4913,7 @@ mod _ssl { fn _test_decode_cert(path: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult { // Read certificate file let path_str = path.as_str(); - let cert_data = std::fs::read(path_str).map_err(|e| { + let cert_data = rustpython_host_env::fs::read(path_str).map_err(|e| { vm.new_os_error(format!( "Failed to read certificate file {}: {}", path_str, e diff --git a/crates/stdlib/src/ssl/cert.rs b/crates/stdlib/src/ssl/cert.rs index cd39972cf41..0ae71208c5c 100644 --- a/crates/stdlib/src/ssl/cert.rs +++ b/crates/stdlib/src/ssl/cert.rs @@ -639,7 +639,7 @@ impl<'a> CertLoader<'a> { /// /// Returns statistics about loaded certificates pub fn load_from_file(&mut self, path: &str) -> Result { - let contents = std::fs::read(path)?; + let contents = rustpython_host_env::fs::read(path)?; self.load_from_bytes(&contents) } @@ -648,7 +648,7 @@ impl<'a> CertLoader<'a> { /// Reads all files in the directory and attempts to parse them as certificates. /// Invalid files are silently skipped (matches OpenSSL capath behavior). pub fn load_from_dir(&mut self, dir_path: &str) -> Result { - let entries = std::fs::read_dir(dir_path)?; + let entries = rustpython_host_env::fs::read_dir(dir_path)?; let mut stats = CertStats::default(); for entry in entries { @@ -658,7 +658,7 @@ impl<'a> CertLoader<'a> { // Skip directories and process all files // OpenSSL capath uses hash-based naming like "4e1295a3.0" if path.is_file() - && let Ok(contents) = std::fs::read(&path) + && let Ok(contents) = rustpython_host_env::fs::read(&path) { // Ignore errors for individual files (some may not be certs) if let Ok(file_stats) = self.load_from_bytes(&contents) { @@ -1129,7 +1129,7 @@ pub(super) fn load_cert_chain_from_file( password: Option<&str>, ) -> Result<(Vec>, PrivateKeyDer<'static>), Box> { // Load certificate file - preserve io::Error for errno - let cert_contents = std::fs::read(cert_path)?; + let cert_contents = rustpython_host_env::fs::read(cert_path)?; // Parse certificates (PEM format) let mut cert_cursor = std::io::Cursor::new(&cert_contents); @@ -1142,7 +1142,7 @@ pub(super) fn load_cert_chain_from_file( } // Load private key file - preserve io::Error for errno - let key_contents = std::fs::read(key_path)?; + let key_contents = rustpython_host_env::fs::read(key_path)?; // Parse private key (supports PKCS8, RSA, EC formats) let private_key = if let Some(pwd) = password { 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 de402724434..919b4ff702a 100644 --- a/crates/stdlib/src/termios.rs +++ b/crates/stdlib/src/termios.rs @@ -7,10 +7,9 @@ mod termios { use crate::vm::{ PyObjectRef, PyResult, TryFromObject, VirtualMachine, builtins::{PyBaseExceptionRef, PyBytes, PyInt, PyListRef, PyTypeRef}, - common::os::ErrorExt, convert::ToPyObject, }; - 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/stdlib/src/tkinter.rs b/crates/stdlib/src/tkinter.rs index b258002c129..b19468a7faa 100644 --- a/crates/stdlib/src/tkinter.rs +++ b/crates/stdlib/src/tkinter.rs @@ -1,3 +1,8 @@ +#![allow( + clippy::disallowed_methods, + reason = "tkinter environment setup still uses direct host APIs until later extraction" +)] + // spell-checker:ignore createcommand pub(crate) use self::_tkinter::module_def; diff --git a/crates/vm/Cargo.toml b/crates/vm/Cargo.toml index b721418a4cc..1537a8fd517 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/build.rs b/crates/vm/build.rs index f76bf3f5cbd..f38ae993568 100644 --- a/crates/vm/build.rs +++ b/crates/vm/build.rs @@ -1,3 +1,8 @@ +#![allow( + clippy::disallowed_methods, + reason = "build scripts cannot use rustpython-host_env" +)] + use itertools::Itertools; use std::{env, io::prelude::*, path::PathBuf, process::Command}; diff --git a/crates/vm/clippy.toml b/crates/vm/clippy.toml new file mode 100644 index 00000000000..a83f3d6ddda --- /dev/null +++ b/crates/vm/clippy.toml @@ -0,0 +1,32 @@ +disallowed-methods = [ + { path = "std::fs::read", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::write", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::read_to_string", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::read_dir", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::create_dir", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::create_dir_all", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::remove_file", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::remove_dir", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::metadata", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::symlink_metadata", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::canonicalize", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::File::open", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::File::create", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::fs::OpenOptions::open", reason = "use rustpython_host_env for host filesystem access" }, + { path = "std::env::var", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::var_os", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::set_var", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::remove_var", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::vars", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::vars_os", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::current_dir", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::set_current_dir", reason = "use rustpython_host_env for host environment access" }, + { path = "std::env::temp_dir", reason = "use rustpython_host_env for host environment access" }, + { path = "std::process::Command::new", reason = "use rustpython_host_env for host process access" }, + { path = "std::process::exit", reason = "use rustpython_host_env for host process access" }, + { path = "std::process::abort", reason = "use rustpython_host_env for host process access" }, + { path = "std::process::id", reason = "use rustpython_host_env for host process access" }, + { path = "std::net::TcpStream::connect", reason = "use rustpython_host_env for host network access" }, + { path = "std::net::TcpListener::bind", reason = "use rustpython_host_env for host network access" }, + { path = "std::net::UdpSocket::bind", reason = "use rustpython_host_env for host network access" }, +] diff --git a/crates/vm/src/builtins/code.rs b/crates/vm/src/builtins/code.rs index aafbe196a07..dd5cb14f84b 100644 --- a/crates/vm/src/builtins/code.rs +++ b/crates/vm/src/builtins/code.rs @@ -2,12 +2,14 @@ use super::{PyBytesRef, PyStrRef, PyTupleRef, PyType, set::PyFrozenSet}; use crate::common::lock::PyMutex; +#[cfg(feature = "host_env")] +use crate::convert::ToPyException; use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, builtins::PyStrInterned, bytecode::{self, AsBag, BorrowedConstant, CodeFlags, Constant, ConstantBag, Instruction}, class::{PyClassImpl, StaticType}, - convert::{ToPyException, ToPyObject}, + convert::ToPyObject, frozen, function::OptionalArg, types::{Comparable, Constructor, Hashable, Representable}, @@ -517,12 +519,13 @@ impl PyCode { Self::new_ref_with_bag(vm, code.decode(PyVmBag(vm))) } + #[cfg(feature = "host_env")] pub fn from_pyc_path(path: &std::path::Path, vm: &VirtualMachine) -> PyResult> { let name = match path.file_stem() { Some(stem) => stem.display().to_string(), None => "".to_owned(), }; - let content = std::fs::read(path).map_err(|e| e.to_pyexception(vm))?; + let content = crate::host_env::fs::read(path).map_err(|e| e.to_pyexception(vm))?; Self::from_pyc( &content, Some(&name), @@ -531,6 +534,10 @@ impl PyCode { vm, ) } + #[cfg(not(feature = "host_env"))] + pub fn from_pyc_path(_path: &std::path::Path, vm: &VirtualMachine) -> PyResult> { + Err(vm.new_runtime_error("loading a pyc file requires the `host_env` feature".to_owned())) + } pub fn from_pyc( pyc_bytes: &[u8], name: Option<&str>, diff --git a/crates/vm/src/exceptions.rs b/crates/vm/src/exceptions.rs index 1f82c3cd72c..ffab36bd01d 100644 --- a/crates/vm/src/exceptions.rs +++ b/crates/vm/src/exceptions.rs @@ -17,10 +17,9 @@ use crate::{ }; use crossbeam_utils::atomic::AtomicCell; use itertools::Itertools; -use std::{ - collections::HashSet, - io::{self, BufRead, BufReader}, -}; +#[cfg(feature = "host_env")] +use std::io::{BufRead, BufReader}; +use std::{collections::HashSet, io}; pub use super::exception_group::exception_group; @@ -366,6 +365,7 @@ impl VirtualMachine { } } +#[cfg(feature = "host_env")] fn print_source_line( output: &mut W, filename: &str, @@ -373,7 +373,7 @@ fn print_source_line( ) -> Result<(), W::Error> { // TODO: use io.open() method instead, when available, according to https://github.com/python/cpython/blob/main/Python/traceback.c#L393 // TODO: support different encodings - let file = match std::fs::File::open(filename) { + let file = match crate::host_env::fs::open(filename) { Ok(file) => file, Err(_) => return Ok(()), }; @@ -392,6 +392,15 @@ fn print_source_line( Ok(()) } +#[cfg(not(feature = "host_env"))] +fn print_source_line( + _output: &mut W, + _filename: &str, + _lineno: usize, +) -> Result<(), W::Error> { + Ok(()) +} + /// Print exception occurrence location from traceback element fn write_traceback_entry( output: &mut W, @@ -1380,7 +1389,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 +1946,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/getpath.rs b/crates/vm/src/getpath.rs index 31fa0617b45..789fc72f62a 100644 --- a/crates/vm/src/getpath.rs +++ b/crates/vm/src/getpath.rs @@ -120,7 +120,7 @@ pub fn init_path_config(settings: &Settings) -> Paths { // In this case: // - sys.executable should be the launcher path (where user invoked Python) // - sys._base_executable should be the real Python executable - let exe_dir = if let Ok(launcher) = env::var("__PYVENV_LAUNCHER__") { + let exe_dir = if let Ok(launcher) = crate::host_env::os::var("__PYVENV_LAUNCHER__") { paths.executable = launcher.clone(); paths.base_executable = real_executable; PathBuf::from(&launcher).parent().map(PathBuf::from) @@ -370,8 +370,9 @@ fn get_executable_path() -> Option { } /// Parse pyvenv.cfg and extract the 'home' key value +#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))] fn parse_pyvenv_home(pyvenv_cfg: &Path) -> Option { - let content = std::fs::read_to_string(pyvenv_cfg).ok()?; + let content = crate::host_env::fs::read_to_string(pyvenv_cfg).ok()?; for line in content.lines() { if let Some((key, value)) = line.split_once('=') @@ -384,6 +385,11 @@ fn parse_pyvenv_home(pyvenv_cfg: &Path) -> Option { None } +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] +fn parse_pyvenv_home(_pyvenv_cfg: &Path) -> Option { + None +} + #[cfg(test)] mod tests { use super::*; @@ -399,7 +405,10 @@ mod tests { #[test] fn test_search_up() { // Test with a path that doesn't have any landmarks - let result = search_up_file(std::env::temp_dir(), &["nonexistent_landmark_xyz"]); + let result = search_up_file( + crate::host_env::os::temp_dir(), + &["nonexistent_landmark_xyz"], + ); assert!(result.is_none()); } diff --git a/crates/vm/src/import.rs b/crates/vm/src/import.rs index f8f41c12081..09033dd84f8 100644 --- a/crates/vm/src/import.rs +++ b/crates/vm/src/import.rs @@ -340,7 +340,7 @@ pub(crate) fn is_possibly_shadowing_path(origin: &str, vm: &VirtualMachine) -> b }; let cmp_path = if sys_path_0.is_empty() { - match std::env::current_dir() { + match crate::host_env::os::current_dir() { Ok(d) => d.to_string_lossy().to_string(), Err(_) => return false, } diff --git a/crates/vm/src/lib.rs b/crates/vm/src/lib.rs index 21762466f13..5e2e88a074a 100644 --- a/crates/vm/src/lib.rs +++ b/crates/vm/src/lib.rs @@ -8,6 +8,7 @@ // to allow `mod foo {}` in foo.rs; clippy thinks this is a mistake/misunderstanding of // how `mod` works, but we want this sometimes for pymodule declarations +#![deny(clippy::disallowed_methods)] #![allow(clippy::module_inception)] // we want to mirror python naming conventions when defining python structs, so that does mean // uppercase acronyms, e.g. TextIOWrapper instead of TextIoWrapper @@ -105,6 +106,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/readline.rs b/crates/vm/src/readline.rs index d62d520ecbd..cf82c3ba1ff 100644 --- a/crates/vm/src/readline.rs +++ b/crates/vm/src/readline.rs @@ -105,18 +105,34 @@ mod rustyline_readline { } pub fn load_history(&mut self, path: &Path) -> OtherResult<()> { - self.repl.load_history(path)?; - Ok(()) + #[cfg(not(feature = "host_env"))] + { + let _ = path; + Err(io::Error::other("history requires the `host_env` feature").into()) + } + #[cfg(feature = "host_env")] + { + self.repl.load_history(path)?; + Ok(()) + } } pub fn save_history(&mut self, path: &Path) -> OtherResult<()> { - if !path.exists() - && let Some(parent) = path.parent() + #[cfg(not(feature = "host_env"))] { - std::fs::create_dir_all(parent)?; + let _ = path; + Err(io::Error::other("history requires the `host_env` feature").into()) + } + #[cfg(feature = "host_env")] + { + if !path.exists() + && let Some(parent) = path.parent() + { + crate::host_env::fs::create_dir_all(parent)?; + } + self.repl.save_history(path)?; + Ok(()) } - self.repl.save_history(path)?; - Ok(()) } pub fn add_history_entry(&mut self, entry: &str) -> OtherResult<()> { 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..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( @@ -404,16 +385,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 665ff0d9e58..9edb740cdb0 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,8 @@ 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}; #[pyattr] @@ -200,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] @@ -223,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 93364ea3596..f10de1238d5 100644 --- a/crates/vm/src/stdlib/msvcrt.rs +++ b/crates/vm/src/stdlib/msvcrt.rs @@ -7,10 +7,11 @@ mod msvcrt { use crate::{ PyRef, PyResult, VirtualMachine, builtins::{PyBytes, PyStrRef}, - common::{crt_fd, suppress_iph}, convert::IntoPyException, + 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 5dd4cf4f001..809e26249c3 100644 --- a/crates/vm/src/stdlib/nt.rs +++ b/crates/vm/src/stdlib/nt.rs @@ -10,18 +10,19 @@ 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}, }; 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 std::{io, os::windows::ffi::OsStringExt}; use windows_sys::Win32::{ Foundation::{self, INVALID_HANDLE_VALUE}, Storage::FileSystem, @@ -235,7 +236,7 @@ pub(crate) mod module { fn environ(vm: &VirtualMachine) -> PyDictRef { let environ = vm.ctx.new_dict(); - for (key, value) in env::vars() { + for (key, value) in crate::host_env::os::vars() { // Skip hidden Windows environment variables (e.g., =C:, =D:, =ExitCode) // These are internal cmd.exe bookkeeping variables that store per-drive // current directories and cannot be reliably modified via _wputenv(). @@ -250,7 +251,7 @@ pub(crate) mod module { #[pyfunction] fn _create_environ(vm: &VirtualMachine) -> PyDictRef { let environ = vm.ctx.new_dict(); - for (key, value) in env::vars() { + for (key, value) in crate::host_env::os::vars() { if key.starts_with('=') { continue; } @@ -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] @@ -411,7 +352,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 +516,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 +611,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 +702,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 +719,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 +2097,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..429fef19eeb 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -3,20 +3,20 @@ 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}; +use std::{io, path::Path}; pub(crate) fn fs_metadata>( path: P, follow_symlink: bool, -) -> io::Result { +) -> io::Result { if follow_symlink { - fs::metadata(path.as_ref()) + crate::host_env::fs::metadata(path.as_ref()) } else { - fs::symlink_metadata(path.as_ref()) + crate::host_env::fs::symlink_metadata(path.as_ref()) } } @@ -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,7 +176,8 @@ pub(super) mod _os { use core::time::Duration; use crossbeam_utils::atomic::AtomicCell; use rustpython_common::wtf8::Wtf8Buf; - use std::{env, fs, fs::OpenOptions, io, path::PathBuf, time::SystemTime}; + use rustpython_host_env::suppress_iph; + use std::{fs, io, path::PathBuf, time::SystemTime}; const OPEN_DIR_FD: bool = cfg!(not(any(windows, target_os = "redox"))); pub(crate) const MKDIR_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(()) @@ -368,7 +366,7 @@ pub(super) mod _os { #[pyfunction] fn mkdirs(path: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { let os_path = vm.fsencode(&path)?; - fs::create_dir_all(&*os_path).map_err(|err| err.into_pyexception(vm)) + crate::host_env::fs::create_dir_all(&*os_path).map_err(|err| err.into_pyexception(vm)) } #[cfg(not(windows))] @@ -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(()) @@ -391,14 +389,16 @@ pub(super) mod _os { } #[cfg(target_os = "redox")] let [] = dir_fd.0; - fs::remove_dir(&path).map_err(|err| OSErrorBuilder::with_filename(&err, path, vm)) + crate::host_env::fs::remove_dir(&path) + .map_err(|err| OSErrorBuilder::with_filename(&err, path, vm)) } #[cfg(windows)] #[pyfunction] fn rmdir(path: OsPath, dir_fd: DirFd<'_, 0>, vm: &VirtualMachine) -> PyResult<()> { let [] = dir_fd.0; - fs::remove_dir(&path).map_err(|err| OSErrorBuilder::with_filename(&err, path, vm)) + crate::host_env::fs::remove_dir(&path) + .map_err(|err| OSErrorBuilder::with_filename(&err, path, vm)) } const LISTDIR_FD: bool = cfg!(all(unix, not(target_os = "redox"))); @@ -413,7 +413,7 @@ pub(super) mod _os { .unwrap_or_else(|| OsPathOrFd::Path(OsPath::new_str("."))); let list = match path { OsPathOrFd::Path(path) => { - let dir_iter = match fs::read_dir(&path) { + let dir_iter = match crate::host_env::fs::read_dir(&path) { Ok(iter) => iter, Err(err) => { return Err(OSErrorBuilder::with_filename(&err, path, vm)); @@ -437,7 +437,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 +490,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", @@ -543,7 +543,7 @@ pub(super) mod _os { let key = super::bytes_as_os_str(key, vm)?; let value = super::bytes_as_os_str(value, vm)?; // SAFETY: requirements forwarded from the caller - unsafe { env::set_var(key, value) }; + unsafe { crate::host_env::os::set_var(key, value) }; Ok(()) } @@ -595,7 +595,7 @@ pub(super) mod _os { } let key = super::bytes_as_os_str(key, vm)?; // SAFETY: requirements forwarded from the caller - unsafe { env::remove_var(key) }; + unsafe { crate::host_env::os::remove_var(key) }; Ok(()) } @@ -1124,7 +1124,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)), @@ -1187,7 +1187,7 @@ pub(super) mod _os { .unwrap_or_else(|| OsPathOrFd::Path(OsPath::new_str("."))); match path { OsPathOrFd::Path(path) => { - let entries = fs::read_dir(&path.path) + let entries = crate::host_env::fs::read_dir(&path.path) .map_err(|err| OSErrorBuilder::with_filename(&err, path.clone(), vm))?; Ok(ScandirIterator { entries: PyRwLock::new(Some(entries)), @@ -1400,7 +1400,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 +1414,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, @@ -1473,7 +1473,7 @@ pub(super) mod _os { } fn curdir_inner(vm: &VirtualMachine) -> PyResult { - env::current_dir().map_err(|err| err.into_pyexception(vm)) + crate::host_env::os::current_dir().map_err(|err| err.into_pyexception(vm)) } #[pyfunction] @@ -1488,7 +1488,7 @@ pub(super) mod _os { #[pyfunction] fn chdir(path: OsPath, vm: &VirtualMachine) -> PyResult<()> { - env::set_current_dir(&path.path) + crate::host_env::os::set_current_dir(&path.path) .map_err(|err| OSErrorBuilder::with_filename(&err, path, vm))?; #[cfg(windows)] @@ -1501,7 +1501,7 @@ pub(super) mod _os { use std::os::windows::ffi::OsStrExt; use windows_sys::Win32::System::Environment::SetEnvironmentVariableW; - if let Ok(cwd) = env::current_dir() { + if let Ok(cwd) = crate::host_env::os::current_dir() { let cwd_str = cwd.as_os_str(); let mut cwd_wide: Vec = cwd_str.encode_wide().collect(); @@ -1558,7 +1558,7 @@ pub(super) mod _os { // https://github.com/WebAssembly/wasi-libc/blob/wasi-sdk-21/libc-bottom-half/getpid/getpid.c 42 } else { - std::process::id() + crate::host_env::os::process_id() }; vm.ctx.new_int(pid).into() } @@ -1571,7 +1571,7 @@ pub(super) mod _os { #[pyfunction] fn _exit(code: i32) { - std::process::exit(code) + crate::host_env::os::exit(code) } #[pyfunction] @@ -1687,7 +1687,8 @@ pub(super) mod _os { let src_path = match follow_symlinks.into_option() { Some(true) => { // Explicit follow_symlinks=True: resolve symlinks - fs::canonicalize(&src.path).unwrap_or_else(|_| PathBuf::from(src.path.clone())) + crate::host_env::fs::canonicalize(&src.path) + .unwrap_or_else(|_| PathBuf::from(src.path.clone())) } Some(false) | None => { // Default or explicit no-follow: native hard_link behavior @@ -1835,7 +1836,7 @@ pub(super) mod _os { } #[cfg(windows)] { - use std::{fs::OpenOptions, os::windows::prelude::*}; + use std::os::windows::prelude::*; type DWORD = u32; use windows_sys::Win32::{Foundation::FILETIME, Storage::FileSystem}; @@ -1859,11 +1860,11 @@ pub(super) mod _os { let acc = ft(acc); let modif = ft(modif); - let f = OpenOptions::new() - .write(true) - .custom_flags(windows_sys::Win32::Storage::FileSystem::FILE_FLAG_BACKUP_SEMANTICS) - .open(&path) - .map_err(|err| OSErrorBuilder::with_filename(&err, path.clone(), vm))?; + let f = crate::host_env::fs::open_write_with_custom_flags( + &path, + windows_sys::Win32::Storage::FileSystem::FILE_FLAG_BACKUP_SEMANTICS, + ) + .map_err(|err| OSErrorBuilder::with_filename(&err, path.clone(), vm))?; let ret = unsafe { FileSystem::SetFileTime(f.as_raw_handle() as _, core::ptr::null(), &acc, &modif) @@ -2050,7 +2051,7 @@ pub(super) mod _os { let path = OsPath::try_from_object(vm, path)?; // TODO: just call libc::truncate() on POSIX - let f = match OpenOptions::new().write(true).open(&path) { + let f = match crate::host_env::fs::open_write(&path) { Ok(f) => f, Err(e) => return Err(error(vm, e, path)), }; diff --git a/crates/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs index f1f4ddeaac8..b97801d2362 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, @@ -53,9 +42,9 @@ 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, + fs, io, os::fd::{AsFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}, }; use strum::IntoEnumIterator; @@ -483,7 +472,7 @@ pub mod module { ) })?; - let metadata = match fs::metadata(&path.path) { + let metadata = match crate::host_env::fs::metadata(&path.path) { Ok(m) => m, // If the file doesn't exist, return False for any access check Err(_) => return Ok(false), @@ -511,7 +500,7 @@ pub mod module { #[pyattr] fn environ(vm: &VirtualMachine) -> PyDictRef { let environ = vm.ctx.new_dict(); - for (key, value) in env::vars_os() { + for (key, value) in crate::host_env::os::vars_os() { let key: PyObjectRef = vm.ctx.new_bytes(key.into_vec()).into(); let value: PyObjectRef = vm.ctx.new_bytes(value.into_vec()).into(); environ.set_item(&*key, value, vm).unwrap(); @@ -523,7 +512,7 @@ pub mod module { #[pyfunction] fn _create_environ(vm: &VirtualMachine) -> PyDictRef { let environ = vm.ctx.new_dict(); - for (key, value) in env::vars_os() { + for (key, value) in crate::host_env::os::vars_os() { let key: PyObjectRef = vm.ctx.new_bytes(key.into_vec()).into(); let value: PyObjectRef = vm.ctx.new_bytes(value.into_vec()).into(); environ.set_item(&*key, value, vm).unwrap(); @@ -574,7 +563,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(()) @@ -582,7 +571,8 @@ pub mod module { } #[cfg(target_os = "redox")] let [] = dir_fd.0; - fs::remove_file(&path).map_err(|err| OSErrorBuilder::with_filename(&err, path, vm)) + crate::host_env::fs::remove_file(&path) + .map_err(|err| OSErrorBuilder::with_filename(&err, path, vm)) } #[cfg(not(target_os = "redox"))] @@ -876,79 +866,7 @@ pub mod module { /// Best-effort number of OS threads in this process. /// Returns <= 0 when unavailable. fn get_number_of_os_threads() -> isize { - #[cfg(target_os = "macos")] - { - type MachPortT = libc::c_uint; - type KernReturnT = libc::c_int; - type MachMsgTypeNumberT = libc::c_uint; - type ThreadActArrayT = *mut MachPortT; - const KERN_SUCCESS: KernReturnT = 0; - unsafe extern "C" { - fn mach_task_self() -> MachPortT; - fn task_for_pid( - task: MachPortT, - pid: libc::c_int, - target_task: *mut MachPortT, - ) -> KernReturnT; - fn task_threads( - target_task: MachPortT, - act_list: *mut ThreadActArrayT, - act_list_cnt: *mut MachMsgTypeNumberT, - ) -> KernReturnT; - fn vm_deallocate( - target_task: MachPortT, - address: libc::uintptr_t, - size: libc::uintptr_t, - ) -> KernReturnT; - } - - let self_task = unsafe { mach_task_self() }; - let mut proc_task: MachPortT = 0; - if unsafe { task_for_pid(self_task, libc::getpid(), &mut proc_task) } == KERN_SUCCESS { - let mut threads: ThreadActArrayT = core::ptr::null_mut(); - let mut n_threads: MachMsgTypeNumberT = 0; - if unsafe { task_threads(proc_task, &mut threads, &mut n_threads) } == KERN_SUCCESS - { - if !threads.is_null() { - let _ = unsafe { - vm_deallocate( - self_task, - threads as libc::uintptr_t, - (n_threads as usize * core::mem::size_of::()) - as libc::uintptr_t, - ) - }; - } - return n_threads as isize; - } - } - 0 - } - #[cfg(target_os = "linux")] - { - use std::io::Read as _; - let mut file = match std::fs::File::open("/proc/self/stat") { - Ok(f) => f, - Err(_) => return 0, - }; - let mut buf = [0u8; 160]; - let n = match file.read(&mut buf) { - Ok(n) => n, - Err(_) => return 0, - }; - let line = match core::str::from_utf8(&buf[..n]) { - Ok(s) => s, - Err(_) => return 0, - }; - if let Some(field) = line.split_whitespace().nth(19) { - return field.parse::().unwrap_or(0); - } - 0 - } - #[cfg(not(any(target_os = "macos", target_os = "linux")))] - { - 0 - } + rustpython_host_env::posix::get_number_of_os_threads() } /// Warn if forking from a multi-threaded process. @@ -1859,7 +1777,7 @@ pub mod module { } else { // env=None means use the current environment - env::vars_os() + crate::host_env::os::vars_os() .map(|(k, v)| { let mut entry = k.into_vec(); entry.push(b'='); @@ -2598,9 +2516,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) @@ -2626,7 +2544,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..c50134a33a4 100644 --- a/crates/vm/src/stdlib/posix_compat.rs +++ b/crates/vm/src/stdlib/posix_compat.rs @@ -13,7 +13,7 @@ pub(crate) mod module { ospath::OsPath, stdlib::os::{_os, DirFd, SupportFunc, TargetIsDirectory}, }; - use std::{env, fs}; + use std::fs; #[pyfunction] pub(super) fn access(_path: PyStrRef, _mode: u8, vm: &VirtualMachine) -> PyResult { @@ -46,10 +46,10 @@ 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() { + for (key, value) in crate::host_env::os::vars_os() { let key: PyObjectRef = vm.ctx.new_bytes(key.into_vec()).into(); let value: PyObjectRef = vm.ctx.new_bytes(value.into_vec()).into(); environ.set_item(&*key, value, vm).unwrap(); diff --git a/crates/vm/src/stdlib/sys.rs b/crates/vm/src/stdlib/sys.rs index 72379494ddb..fdf66b457bc 100644 --- a/crates/vm/src/stdlib/sys.rs +++ b/crates/vm/src/stdlib/sys.rs @@ -53,7 +53,7 @@ mod sys { use core::sync::atomic::Ordering; use num_traits::ToPrimitive; use std::{ - env::{self, VarError}, + env, io::{IsTerminal, Read, Write}, }; @@ -871,15 +871,18 @@ mod sys { #[pyfunction(name = "__breakpointhook__")] #[pyfunction] pub fn breakpointhook(args: FuncArgs, vm: &VirtualMachine) -> PyResult { - let env_var = std::env::var("PYTHONBREAKPOINT") + #[cfg(feature = "host_env")] + let env_var = crate::host_env::os::var("PYTHONBREAKPOINT") .and_then(|env_var| { if env_var.is_empty() { - Err(VarError::NotPresent) + Err(std::env::VarError::NotPresent) } else { Ok(env_var) } }) .unwrap_or_else(|_| "pdb.set_trace".to_owned()); + #[cfg(not(feature = "host_env"))] + let env_var = "pdb.set_trace".to_owned(); if env_var.eq("0") { return Ok(vm.ctx.none()); @@ -1091,7 +1094,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 3f2c3ba8288..a630e3ac506 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] @@ -477,24 +475,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 { @@ -678,7 +658,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(), @@ -1428,7 +1408,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/mod.rs b/crates/vm/src/vm/mod.rs index 882ffe5f54e..267382ed34d 100644 --- a/crates/vm/src/vm/mod.rs +++ b/crates/vm/src/vm/mod.rs @@ -536,7 +536,7 @@ impl StopTheWorldState { #[cfg(all(unix, feature = "threading"))] pub(super) fn stw_trace_enabled() -> bool { static ENABLED: std::sync::OnceLock = std::sync::OnceLock::new(); - *ENABLED.get_or_init(|| std::env::var_os("RUSTPYTHON_STW_TRACE").is_some()) + *ENABLED.get_or_init(|| crate::host_env::os::var_os("RUSTPYTHON_STW_TRACE").is_some()) } #[cfg(all(unix, feature = "threading"))] @@ -788,8 +788,8 @@ impl VirtualMachine { #[cfg(feature = "encodings")] fn import_encodings(&mut self) -> PyResult<()> { self.import("encodings", 0).map_err(|import_err| { - let rustpythonpath_env = std::env::var("RUSTPYTHONPATH").ok(); - let pythonpath_env = std::env::var("PYTHONPATH").ok(); + let rustpythonpath_env = crate::host_env::os::var("RUSTPYTHONPATH").ok(); + let pythonpath_env = crate::host_env::os::var("PYTHONPATH").ok(); let env_set = rustpythonpath_env.as_ref().is_some() || pythonpath_env.as_ref().is_some(); let path_contains_env = self.state.config.paths.module_search_paths.iter().any(|s| { Some(s.as_str()) == rustpythonpath_env.as_deref() || Some(s.as_str()) == pythonpath_env.as_deref() diff --git a/crates/vm/src/vm/python_run.rs b/crates/vm/src/vm/python_run.rs index 2f6f0bbee01..26000b5e11f 100644 --- a/crates/vm/src/vm/python_run.rs +++ b/crates/vm/src/vm/python_run.rs @@ -105,7 +105,7 @@ mod file_run { if path != "" { set_main_loader(module_dict, path, "SourceFileLoader", self)?; } - match std::fs::read_to_string(path) { + match crate::host_env::fs::read_to_string(path) { Ok(source) => { let code_obj = self .compile(&source, compiler::Mode::Exec, path.to_owned()) @@ -160,7 +160,7 @@ mod file_run { return Ok(false); } - let mut file = std::fs::File::open(path)?; + let mut file = crate::host_env::fs::open(path)?; let mut buf = [0u8; 2]; use std::io::Read; 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<()> {