Create rustpython-host-env (#7582)

* Create rustpython-host-env crate; move host abstractions out of common

Move os, crt_fd, fileutils, windows, macros modules from
rustpython-common into the new rustpython-host-env crate.
This isolates host OS API wrappers behind a crate boundary
with zero Python runtime dependency.

- Add crates/host_env to workspace
- Drop nix, windows-sys, widestring deps from common
- Wire vm and stdlib to depend on rustpython-host-env
- Migrate all imports from common::{os,crt_fd,fileutils,windows}
  to rustpython_host_env::

* refactor: extract host helpers

Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/48d1e64d-37ce-409f-b511-8e61a349665c

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* lint: enforce direct host API boundaries

Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/97225fb7-7b3d-4197-a77c-eb44aead5b13

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* refactor: extract remaining host env helpers

Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/d96f57e1-b196-4460-9983-97d5ff118835

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* fix: clean extracted host env follow-up

Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/d96f57e1-b196-4460-9983-97d5ff118835

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* fix: document env mutation safety

Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/d96f57e1-b196-4460-9983-97d5ff118835

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* refactor: split host fs helpers from fileutils

Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/c57424c5-0e1d-490a-82b3-2d2f6c8cf2cd

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* fix: resolve latest host env ci regressions

Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/899eb717-ebc6-4a4a-870c-2a15c5f33e02

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* fix: resolve remaining windows clippy host fs calls

Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/12f32740-8173-4b10-a1d6-00b29e90a8ec

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* host_env

---------

Co-authored-by: Jeong, YunWon <jeong@youknowone.org>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-04-21 00:13:25 +09:00
1 parent 9140ef583a
commit d5921d16af
77 files changed
+1373 -777

No files matched your search

Generated
+15 -3
View File
@@ -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",
+1
View File
@@ -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" }
-14
View File
@@ -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
+32
View File
@@ -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" },
]
+1 -12
View File
@@ -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;
+4
View File
@@ -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();
+41
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
disallowed-methods = []
File renamed without changes.
+75
View File
@@ -0,0 +1,75 @@
use std::io;
pub fn fcntl_int(fd: i32, cmd: i32, arg: i32) -> io::Result<i32> {
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<i32> {
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<i32> {
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<i32> {
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<i32> {
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<i32> {
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)
}
}
File renamed without changes.
+59
View File
@@ -0,0 +1,59 @@
use std::{
fs::{self, File, Metadata, ReadDir},
io,
path::Path,
};
pub fn open(path: impl AsRef<Path>) -> io::Result<File> {
File::open(path)
}
pub fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
fs::read(path)
}
pub fn read_to_string(path: impl AsRef<Path>) -> io::Result<String> {
fs::read_to_string(path)
}
pub fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
fs::read_dir(path)
}
pub fn create_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
fs::create_dir_all(path)
}
pub fn remove_dir(path: impl AsRef<Path>) -> io::Result<()> {
fs::remove_dir(path)
}
pub fn remove_file(path: impl AsRef<Path>) -> io::Result<()> {
fs::remove_file(path)
}
pub fn metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
fs::metadata(path)
}
pub fn symlink_metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
fs::symlink_metadata(path)
}
pub fn open_write(path: impl AsRef<Path>) -> io::Result<File> {
fs::OpenOptions::new().write(true).open(path)
}
pub fn canonicalize(path: impl AsRef<Path>) -> io::Result<std::path::PathBuf> {
fs::canonicalize(path)
}
#[cfg(windows)]
pub fn open_write_with_custom_flags(path: impl AsRef<Path>, flags: u32) -> io::Result<File> {
use std::os::windows::fs::OpenOptionsExt;
fs::OpenOptions::new()
.write(true)
.custom_flags(flags)
.open(path)
}
+42
View File
@@ -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;
File renamed without changes.
+126
View File
@@ -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<u8> {
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<u8> {
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<i32> {
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<i32> {
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)) }
}
+73
View File
@@ -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::<FILE_BASIC_INFO>() 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::<FILE_BASIC_INFO>() 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(())
}
}
@@ -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<PathBuf> {
env::current_dir()
}
pub fn temp_dir() -> PathBuf {
env::temp_dir()
}
pub fn var(key: &str) -> Result<String, env::VarError> {
env::var(key)
}
pub fn var_os(key: impl AsRef<OsStr>) -> Option<OsString> {
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<OsStr>, value: impl AsRef<OsStr>) {
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<OsStr>) {
unsafe { env::remove_var(key) };
}
pub fn set_current_dir(path: impl AsRef<std::path::Path>) -> 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;
}
+88
View File
@@ -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::<MachPortT>()) 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::<isize>().unwrap_or(0);
}
0
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
pub fn get_number_of_os_threads() -> isize {
0
}
+176
View File
@@ -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::<RawFd>() };
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<platform::fd_set>);
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<RawFd> {
(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<i32> {
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 _,
}
}
+23
View File
@@ -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<libc::c_int> {
#[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(())
}
}
+17
View File
@@ -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),
)
}
+68
View File
@@ -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<CStr>),
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<Option<GlobalIdent>> {
static IDENT: OnceLock<RwLock<Option<GlobalIdent>>> = 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<Box<CStr>>, 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
}
+23
View File
@@ -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)
}
+41
View File
@@ -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<Duration, SystemTimeError> {
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
)
}
+17
View File
@@ -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() }
}
File renamed without changes.
+1
View File
@@ -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 }
+5
View File
@@ -1,3 +1,8 @@
#![allow(
clippy::disallowed_methods,
reason = "build scripts cannot use rustpython-host_env"
)]
// spell-checker:ignore ossl osslconf
fn main() {
+32
View File
@@ -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" },
]
+3 -3
View File
@@ -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);
}
}
+15 -39
View File
@@ -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())
}
}
+1 -1
View File
@@ -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]
+2 -2
View File
@@ -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)]
+12 -12
View File
@@ -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 {
+1 -1
View File
@@ -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)
}
+1 -1
View File
@@ -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
+5 -21
View File
@@ -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<libc::c_int> {
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))
}
}
+1 -1
View File
@@ -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)
}
}
}
+4 -179
View File
@@ -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::<RawFd>();
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<platform::fd_set>);
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<RawFd> {
(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<i32> {
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,
+12 -12
View File
@@ -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<PyModule>) -> 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<IfIndex> {
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<String> {
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())
+4 -4
View File
@@ -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<Option<CertificateRevocationListDer<'static>>> {
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<PyObjectRef> {
// 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
+5 -5
View File
@@ -639,7 +639,7 @@ impl<'a> CertLoader<'a> {
///
/// Returns statistics about loaded certificates
pub fn load_from_file(&mut self, path: &str) -> Result<CertStats, std::io::Error> {
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<CertStats, std::io::Error> {
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<CertificateDer<'static>>, PrivateKeyDer<'static>), Box<dyn core::error::Error>> {
// 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 {
+9 -46
View File
@@ -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<CStr>),
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<Option<GlobalIdent>> {
rustpython_common::static_cell! {
static IDENT: PyRwLock<Option<GlobalIdent>>;
};
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)
}
}
+8 -9
View File
@@ -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<Vec<PyObjectRef>> {
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(())
}
+5
View File
@@ -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;
+1
View File
@@ -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 }
+5
View File
@@ -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};
+32
View File
@@ -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" },
]
+9 -2
View File
@@ -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<PyRef<Self>> {
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<PyRef<Self>> {
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>,
+16 -7
View File
@@ -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<W: Write>(
output: &mut W,
filename: &str,
@@ -373,7 +373,7 @@ fn print_source_line<W: Write>(
) -> 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<W: Write>(
Ok(())
}
#[cfg(not(feature = "host_env"))]
fn print_source_line<W: Write>(
_output: &mut W,
_filename: &str,
_lineno: usize,
) -> Result<(), W::Error> {
Ok(())
}
/// Print exception occurrence location from traceback element
fn write_traceback_entry<W: Write>(
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::<crate::builtins::PyInt>())
.and_then(|w| w.try_to_primitive::<i32>(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())) };
+1 -1
View File
@@ -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"))
}
}
+12 -3
View File
@@ -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<PathBuf> {
}
/// 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<String> {
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<String> {
None
}
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
fn parse_pyvenv_home(_pyvenv_cfg: &Path) -> Option<String> {
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());
}
+1 -1
View File
@@ -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,
}
+2
View File
@@ -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)]
+1 -1
View File
@@ -1,4 +1,4 @@
use rustpython_common::crt_fd;
use rustpython_host_env::crt_fd;
use crate::{
AsObject, PyObjectRef, PyResult, VirtualMachine,
+23 -7
View File
@@ -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<()> {
+3 -3
View File
@@ -387,7 +387,7 @@ mod _codecs_windows {
#[pyfunction]
fn mbcs_encode(args: MbcsEncodeArgs, vm: &VirtualMachine) -> PyResult<(Vec<u8>, 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<u8>, 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<u8>, 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"));
+3 -3
View File
@@ -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);
}
}
+5 -4
View File
@@ -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 {
+6 -24
View File
@@ -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 =
+7 -5
View File
@@ -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)]
+23 -76
View File
@@ -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<u8> {
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<u8> {
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<PyBytes>, 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<i32> {
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<i32> {
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)
}
}
+17 -76
View File
@@ -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::<FILE_BASIC_INFO>() 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::<FILE_BASIC_INFO>() 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<PyStrRef> {
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,
+43 -42
View File
@@ -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<P: AsRef<Path>>(
path: P,
follow_symlink: bool,
) -> io::Result<fs::Metadata> {
) -> io::Result<std::fs::Metadata> {
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<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
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<PathBuf> {
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<u16> = 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)),
};
+14 -96
View File
@@ -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::<MachPortT>())
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::<isize>().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<libc::c_long> {
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)]
+3 -3
View File
@@ -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<bool> {
@@ -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();
+7 -4
View File
@@ -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<u16> = std::ffi::OsStr::new("kernel32.dll").to_wide_with_nul();
+16 -36
View File
@@ -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<Duration> {
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<StructTimeData> {
fn naive_or_local(self, vm: &VirtualMachine) -> PyResult<NaiveDateTime> {
@@ -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<f64> {
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"));
}
+1 -1
View File
@@ -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};
+1 -1
View File
@@ -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};
+3 -3
View File
@@ -536,7 +536,7 @@ impl StopTheWorldState {
#[cfg(all(unix, feature = "threading"))]
pub(super) fn stw_trace_enabled() -> bool {
static ENABLED: std::sync::OnceLock<bool> = 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()
+2 -2
View File
@@ -105,7 +105,7 @@ mod file_run {
if path != "<stdin>" {
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;
+1 -1
View File
@@ -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)
}
+6 -6
View File
@@ -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<StatStruct>
|| (!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);
+1 -1
View File
@@ -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))
}
+1 -1
View File
@@ -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))
}
+1 -1
View File
@@ -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<()> {