Skip to content

Commit ca14e05

Browse files
Copilotyouknowone
andauthored
refactor: extract remaining host env helpers
Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/d96f57e1-b196-4460-9983-97d5ff118835 Co-authored-by: youknowone <[email protected]>
1 parent facc616 commit ca14e05

File tree

20 files changed

+277
-221
lines changed

20 files changed

+277
-221
lines changed

crates/common/src/refcount.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,13 @@ const STRONG: usize = (1 << STRONG_WIDTH) - 1;
1313
const COUNT: usize = 1;
1414
const WEAK_COUNT: usize = 1 << STRONG_WIDTH;
1515

16-
#[allow(
17-
clippy::disallowed_methods,
18-
reason = "refcount overflow must abort immediately under std"
19-
)]
2016
#[inline(never)]
2117
#[cold]
2218
fn refcount_overflow() -> ! {
2319
#[cfg(feature = "std")]
24-
std::process::abort();
20+
unsafe {
21+
libc::abort()
22+
};
2523
#[cfg(not(feature = "std"))]
2624
core::panic!("refcount overflow");
2725
}

crates/host_env/src/fileutils.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
// Python/fileutils.c in CPython
22
#![allow(non_snake_case)]
33

4+
use std::{
5+
fs::{self, File, Metadata, ReadDir},
6+
io,
7+
path::Path,
8+
};
9+
410
#[cfg(not(windows))]
511
pub use libc::stat as StatStruct;
612

@@ -20,6 +26,46 @@ pub fn fstat(fd: crate::crt_fd::Borrowed<'_>) -> std::io::Result<StatStruct> {
2026
}
2127
}
2228

29+
pub fn open(path: impl AsRef<Path>) -> io::Result<File> {
30+
File::open(path)
31+
}
32+
33+
pub fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
34+
fs::read(path)
35+
}
36+
37+
pub fn read_to_string(path: impl AsRef<Path>) -> io::Result<String> {
38+
fs::read_to_string(path)
39+
}
40+
41+
pub fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
42+
fs::read_dir(path)
43+
}
44+
45+
pub fn create_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
46+
fs::create_dir_all(path)
47+
}
48+
49+
pub fn remove_dir(path: impl AsRef<Path>) -> io::Result<()> {
50+
fs::remove_dir(path)
51+
}
52+
53+
pub fn remove_file(path: impl AsRef<Path>) -> io::Result<()> {
54+
fs::remove_file(path)
55+
}
56+
57+
pub fn metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
58+
fs::metadata(path)
59+
}
60+
61+
pub fn symlink_metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
62+
fs::symlink_metadata(path)
63+
}
64+
65+
pub fn open_write(path: impl AsRef<Path>) -> io::Result<File> {
66+
fs::OpenOptions::new().write(true).open(path)
67+
}
68+
2369
#[cfg(windows)]
2470
pub mod windows {
2571
use crate::crt_fd;

crates/host_env/src/os.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22
// TODO: we can move more os-specific bindings/interfaces from stdlib::{os, posix, nt} to here
33

44
use core::str::Utf8Error;
5-
use std::{io, process::ExitCode};
5+
use std::{
6+
env,
7+
ffi::{OsStr, OsString},
8+
io,
9+
path::PathBuf,
10+
process::ExitCode,
11+
};
612

713
/// Convert exit code to std::process::ExitCode
814
///
@@ -22,6 +28,46 @@ pub fn exit_code(code: u32) -> ExitCode {
2228
ExitCode::from(code as u8)
2329
}
2430

31+
pub fn current_dir() -> io::Result<PathBuf> {
32+
env::current_dir()
33+
}
34+
35+
pub fn temp_dir() -> PathBuf {
36+
env::temp_dir()
37+
}
38+
39+
pub fn var(key: &str) -> Result<String, env::VarError> {
40+
env::var(key)
41+
}
42+
43+
pub fn var_os(key: impl AsRef<OsStr>) -> Option<OsString> {
44+
env::var_os(key)
45+
}
46+
47+
pub fn vars_os() -> env::VarsOs {
48+
env::vars_os()
49+
}
50+
51+
pub fn set_var(key: impl AsRef<OsStr>, value: impl AsRef<OsStr>) {
52+
unsafe { env::set_var(key, value) };
53+
}
54+
55+
pub fn remove_var(key: impl AsRef<OsStr>) {
56+
unsafe { env::remove_var(key) };
57+
}
58+
59+
pub fn set_current_dir(path: impl AsRef<std::path::Path>) -> io::Result<()> {
60+
env::set_current_dir(path)
61+
}
62+
63+
pub fn process_id() -> u32 {
64+
std::process::id()
65+
}
66+
67+
pub fn exit(code: i32) -> ! {
68+
std::process::exit(code)
69+
}
70+
2571
pub trait ErrorExt {
2672
fn posix_errno(&self) -> i32;
2773
}

crates/host_env/src/posix.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,78 @@ pub fn set_inheritable(fd: BorrowedFd<'_>, inheritable: bool) -> nix::Result<()>
1111
}
1212
Ok(())
1313
}
14+
15+
#[cfg(target_os = "macos")]
16+
pub fn get_number_of_os_threads() -> isize {
17+
type MachPortT = libc::c_uint;
18+
type KernReturnT = libc::c_int;
19+
type MachMsgTypeNumberT = libc::c_uint;
20+
type ThreadActArrayT = *mut MachPortT;
21+
const KERN_SUCCESS: KernReturnT = 0;
22+
unsafe extern "C" {
23+
fn mach_task_self() -> MachPortT;
24+
fn task_for_pid(
25+
task: MachPortT,
26+
pid: libc::c_int,
27+
target_task: *mut MachPortT,
28+
) -> KernReturnT;
29+
fn task_threads(
30+
target_task: MachPortT,
31+
act_list: *mut ThreadActArrayT,
32+
act_list_cnt: *mut MachMsgTypeNumberT,
33+
) -> KernReturnT;
34+
fn vm_deallocate(
35+
target_task: MachPortT,
36+
address: libc::uintptr_t,
37+
size: libc::uintptr_t,
38+
) -> KernReturnT;
39+
}
40+
41+
let self_task = unsafe { mach_task_self() };
42+
let mut proc_task: MachPortT = 0;
43+
if unsafe { task_for_pid(self_task, libc::getpid(), &mut proc_task) } == KERN_SUCCESS {
44+
let mut threads: ThreadActArrayT = core::ptr::null_mut();
45+
let mut n_threads: MachMsgTypeNumberT = 0;
46+
if unsafe { task_threads(proc_task, &mut threads, &mut n_threads) } == KERN_SUCCESS {
47+
if !threads.is_null() {
48+
let _ = unsafe {
49+
vm_deallocate(
50+
self_task,
51+
threads as libc::uintptr_t,
52+
(n_threads as usize * core::mem::size_of::<MachPortT>()) as libc::uintptr_t,
53+
)
54+
};
55+
}
56+
return n_threads as isize;
57+
}
58+
}
59+
0
60+
}
61+
62+
#[cfg(target_os = "linux")]
63+
pub fn get_number_of_os_threads() -> isize {
64+
use std::io::Read as _;
65+
66+
let mut file = match crate::fileutils::open("/proc/self/stat") {
67+
Ok(f) => f,
68+
Err(_) => return 0,
69+
};
70+
let mut buf = [0u8; 160];
71+
let n = match file.read(&mut buf) {
72+
Ok(n) => n,
73+
Err(_) => return 0,
74+
};
75+
let line = match core::str::from_utf8(&buf[..n]) {
76+
Ok(s) => s,
77+
Err(_) => return 0,
78+
};
79+
if let Some(field) = line.split_whitespace().nth(19) {
80+
return field.parse::<isize>().unwrap_or(0);
81+
}
82+
0
83+
}
84+
85+
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
86+
pub fn get_number_of_os_threads() -> isize {
87+
0
88+
}

crates/stdlib/src/faulthandler.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
#![allow(
2-
clippy::disallowed_methods,
3-
reason = "faulthandler fallback exits still use direct host APIs until later extraction"
4-
)]
5-
61
pub(crate) use decl::module_def;
72

83
#[allow(static_mut_refs)] // TODO: group code only with static mut refs
@@ -592,7 +587,7 @@ mod decl {
592587
}
593588

594589
// Fallback
595-
std::process::exit(1);
590+
rustpython_host_env::os::exit(1);
596591
}
597592

598593
// Windows vectored exception handler (faulthandler.c:417-480)
@@ -885,7 +880,7 @@ mod decl {
885880
}
886881

887882
if exit {
888-
std::process::exit(1);
883+
rustpython_host_env::os::exit(1);
889884
}
890885
}
891886

crates/stdlib/src/openssl.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
#![allow(
2-
clippy::disallowed_methods,
3-
reason = "remaining openssl file access has not been extracted into rustpython-host-env yet"
4-
)]
5-
61
// spell-checker:disable
72

83
mod cert;
@@ -1882,7 +1877,7 @@ mod _ssl {
18821877
const PEM_BUFSIZE: usize = 1024;
18831878

18841879
// Read key file data
1885-
let key_data = std::fs::read(key_file_path)
1880+
let key_data = rustpython_host_env::fileutils::read(key_file_path)
18861881
.map_err(|e| crate::vm::convert::ToPyException::to_pyexception(&e, vm))?;
18871882

18881883
let pkey = if let Some(ref pw_obj) = password {

crates/stdlib/src/openssl/cert.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
#![allow(
2-
clippy::disallowed_methods,
3-
reason = "remaining openssl certificate file access has not been extracted into rustpython-host-env yet"
4-
)]
5-
61
pub(super) use ssl_cert::{PySSLCertificate, cert_to_certificate, cert_to_py, obj2txt};
72

83
// Certificate type for SSL module
@@ -364,7 +359,7 @@ pub(crate) mod ssl_cert {
364359
#[pyfunction]
365360
pub(crate) fn _test_decode_cert(path: FsPath, vm: &VirtualMachine) -> PyResult {
366361
let path = path.to_path_buf(vm)?;
367-
let pem = std::fs::read(path).map_err(|e| e.to_pyexception(vm))?;
362+
let pem = rustpython_host_env::fileutils::read(path).map_err(|e| e.to_pyexception(vm))?;
368363
let x509 = X509::from_pem(&pem).map_err(|e| convert_openssl_error(vm, e))?;
369364
cert_to_py(vm, &x509, false)
370365
}

crates/stdlib/src/posixsubprocess.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
#![allow(
2-
clippy::disallowed_methods,
3-
reason = "posixsubprocess exit path still uses direct host APIs until later extraction"
4-
)]
5-
61
// spell-checker:disable
72

83
use crate::vm::{
@@ -253,7 +248,7 @@ fn exec(args: &ForkExecArgs<'_>, procargs: ProcArgs<'_>, vm: &VirtualMachine) ->
253248
// errno is written in hex format
254249
let _ = write!(pipe, "OSError:{:x}:{}", e as i32, ctx.as_msg());
255250
}
256-
std::process::exit(255)
251+
rustpython_host_env::os::exit(255)
257252
}
258253
}
259254
}

crates/stdlib/src/ssl.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
#![allow(
2-
clippy::disallowed_methods,
3-
reason = "remaining ssl file access has not been extracted into rustpython-host-env yet"
4-
)]
51
// spell-checker: ignore ssleof aesccm aesgcm capath getblocking setblocking ENDTLS TLSEXT
62

73
//! Pure Rust SSL/TLS implementation using rustls
@@ -1770,8 +1766,8 @@ mod _ssl {
17701766

17711767
// Validate that the file contains DH parameters
17721768
// Read the file and check for DH PARAMETERS header
1773-
let contents =
1774-
std::fs::read_to_string(&path_str).map_err(|e| vm.new_os_error(e.to_string()))?;
1769+
let contents = rustpython_host_env::fileutils::read_to_string(&path_str)
1770+
.map_err(|e| vm.new_os_error(e.to_string()))?;
17751771

17761772
if !contents.contains("BEGIN DH PARAMETERS")
17771773
&& !contents.contains("BEGIN X9.42 DH PARAMETERS")
@@ -2158,7 +2154,7 @@ mod _ssl {
21582154
path: &str,
21592155
vm: &VirtualMachine,
21602156
) -> PyResult<Option<CertificateRevocationListDer<'static>>> {
2161-
let data = std::fs::read(path).map_err(|e| match e.kind() {
2157+
let data = rustpython_host_env::fileutils::read(path).map_err(|e| match e.kind() {
21622158
std::io::ErrorKind::NotFound | std::io::ErrorKind::PermissionDenied => {
21632159
e.into_pyexception(vm)
21642160
}
@@ -4917,7 +4913,7 @@ mod _ssl {
49174913
fn _test_decode_cert(path: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
49184914
// Read certificate file
49194915
let path_str = path.as_str();
4920-
let cert_data = std::fs::read(path_str).map_err(|e| {
4916+
let cert_data = rustpython_host_env::fileutils::read(path_str).map_err(|e| {
49214917
vm.new_os_error(format!(
49224918
"Failed to read certificate file {}: {}",
49234919
path_str, e

crates/stdlib/src/ssl/cert.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
// cspell: ignore accessdescs
22

3-
#![allow(
4-
clippy::disallowed_methods,
5-
reason = "remaining certificate file access has not been extracted into rustpython-host-env yet"
6-
)]
7-
83
//! Certificate parsing, validation, and conversion utilities for SSL/TLS
94
//!
105
//! This module provides reusable functions for working with X.509 certificates:
@@ -644,7 +639,7 @@ impl<'a> CertLoader<'a> {
644639
///
645640
/// Returns statistics about loaded certificates
646641
pub fn load_from_file(&mut self, path: &str) -> Result<CertStats, std::io::Error> {
647-
let contents = std::fs::read(path)?;
642+
let contents = rustpython_host_env::fileutils::read(path)?;
648643
self.load_from_bytes(&contents)
649644
}
650645

@@ -653,7 +648,7 @@ impl<'a> CertLoader<'a> {
653648
/// Reads all files in the directory and attempts to parse them as certificates.
654649
/// Invalid files are silently skipped (matches OpenSSL capath behavior).
655650
pub fn load_from_dir(&mut self, dir_path: &str) -> Result<CertStats, std::io::Error> {
656-
let entries = std::fs::read_dir(dir_path)?;
651+
let entries = rustpython_host_env::fileutils::read_dir(dir_path)?;
657652
let mut stats = CertStats::default();
658653

659654
for entry in entries {
@@ -663,7 +658,7 @@ impl<'a> CertLoader<'a> {
663658
// Skip directories and process all files
664659
// OpenSSL capath uses hash-based naming like "4e1295a3.0"
665660
if path.is_file()
666-
&& let Ok(contents) = std::fs::read(&path)
661+
&& let Ok(contents) = rustpython_host_env::fileutils::read(&path)
667662
{
668663
// Ignore errors for individual files (some may not be certs)
669664
if let Ok(file_stats) = self.load_from_bytes(&contents) {
@@ -1134,7 +1129,7 @@ pub(super) fn load_cert_chain_from_file(
11341129
password: Option<&str>,
11351130
) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>), Box<dyn core::error::Error>> {
11361131
// Load certificate file - preserve io::Error for errno
1137-
let cert_contents = std::fs::read(cert_path)?;
1132+
let cert_contents = rustpython_host_env::fileutils::read(cert_path)?;
11381133

11391134
// Parse certificates (PEM format)
11401135
let mut cert_cursor = std::io::Cursor::new(&cert_contents);
@@ -1147,7 +1142,7 @@ pub(super) fn load_cert_chain_from_file(
11471142
}
11481143

11491144
// Load private key file - preserve io::Error for errno
1150-
let key_contents = std::fs::read(key_path)?;
1145+
let key_contents = rustpython_host_env::fileutils::read(key_path)?;
11511146

11521147
// Parse private key (supports PKCS8, RSA, EC formats)
11531148
let private_key = if let Some(pwd) = password {

0 commit comments

Comments
 (0)