RustPython / RustPython/RustPython

Provide `os` module for `wasm32-unknown-unknown` target

Aperta
#7,363 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Lingua principale
Rust
Stelle
22.4k
Fork
1.5k
Merge medio
16h 33m
PR unite (30g)
165

Descrizione

Summary

Currently, RustPython does not provide the os module for wasm32-unknown-unknown. The module is gated behind the host_env feature, and the wasm32 example project disables it (default-features = false, features = ["compiler"]). However, CPython's Emscripten build provides an os module with a reduced function set, and many Python packages do import os at the top level. Without the os module, even os.path.join() (pure Python) fails with ImportError.

Motivation

  • Many Python packages unconditionally import os at the top level — they currently fail entirely on wasm32-unknown-unknown
  • os.path functions (join, dirname, basename, etc.) are pure Python and would work fine if the module existed
  • Constants like os.sep, os.name, os.O_RDONLY are useful even without real I/O
  • Functions like os.fspath(), os.getpid(), os.strerror() can return sensible values
  • CPython's Emscripten build takes this approach — provide the module, let unsupported operations raise OSError at runtime

Problem Analysis

The os module (specifically _os in os.rs) fails to compile on wasm32-unknown-unknown due to several dependencies:

1. crt_fd.rs depends on libc
  • mod c { pub(super) use libc::*; }libc exports nothing for wasm32-unknown-unknown
  • Functions like c::open(), c::close(), c::read(), c::write(), c::fsync(), c::ftruncate() don't exist
  • Constants like c::EBADF, c::off_t don't exist
  • Currently gated: #[cfg(all(feature = "std", any(unix, windows, target_os = "wasi")))] in lib.rs
2. fileutils.rs depends on libc::stat
  • pub use libc::stat as StatStruct on non-windows — no libc::stat on wasm32
  • fstat() uses libc::fstat() — doesn't exist
  • Currently gated: #[cfg(all(feature = "std", any(not(target_arch = "wasm32"), target_os = "wasi")))]
3. os.rs uses libc directly
  • #[pyattr] use libc::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY} — empty on wasm32
  • libc::EINTR in read/write retry loops
  • libc::isatty() in isatty()
  • libc::strerror() in strerror()
  • libc::lseek() in lseek()
  • libc::nl_langinfo() in device_encoding()
  • libc::stat/lstat/fstat/fstatat in stat_inner()
4. num_cpus crate — not available for wasm32-unknown-unknown
  • Used in cpu_count()
5. os_open() is gated behind cfg(any(unix, windows, target_os = "wasi"))

Proposed Solution

Approach: Compile-only support — the os module compiles and is importable on wasm32-unknown-unknown. Functions that can't work on wasm32 will raise OSError at runtime (since std::fs returns errors). Constants and path functions work normally.

Files to Modify
crates/common/src/lib.rs

Expand cfg gates to include wasm32-unknown-unknown:

// crt_fd: include wasm32
#[cfg(all(feature = "std", any(unix, windows, target_os = "wasi", target_arch = "wasm32")))]
pub mod crt_fd;

// fileutils: remove wasm32 exclusion
#[cfg(feature = "std")]
pub mod fileutils;
crates/common/src/crt_fd.rs

Add a wasm32-specific mod c block (similar to the existing Windows-specific sections):

#[cfg(all(target_arch = "wasm32", not(any(unix, windows, target_os = "wasi"))))]
mod c {
    pub type off_t = i64;
    pub type c_int = i32;
    pub type c_char = i8;
    pub const EBADF: c_int = 9;

    // Stub functions that always return -1
    pub unsafe fn open(_: *const c_char, _: c_int, _: c_int) -> c_int { -1 }
    pub unsafe fn close(_: c_int) -> c_int { -1 }
    pub unsafe fn read(_: c_int, _: *mut u8, _: usize) -> isize { -1 }
    pub unsafe fn write(_: c_int, _: *const u8, _: usize) -> isize { -1 }
    pub unsafe fn fsync(_: c_int) -> c_int { -1 }
    pub unsafe fn ftruncate(_: c_int, _: off_t) -> c_int { -1 }
}

std::os::fd types (OwnedFd, BorrowedFd, RawFd) are available on wasm32 since Rust 1.84, so the existing #[cfg(not(windows))] imports work.

crates/common/src/fileutils.rs

Add wasm32-specific StatStruct and fstat stub:

#[cfg(not(any(unix, windows, target_os = "wasi")))]
pub struct StatStruct { /* minimal fields matching libc::stat layout */ }

#[cfg(not(any(unix, windows, target_os = "wasi")))]
pub fn fstat(_: crate::crt_fd::Borrowed<'_>) -> std::io::Result<StatStruct> {
    Err(std::io::Error::new(std::io::ErrorKind::Unsupported, "fstat not supported"))
}
crates/vm/src/stdlib/os.rs

~10 changes needed:

  • O_* constants: Define manually for wasm32 (O_RDONLY=0, O_WRONLY=1, O_RDWR=2, etc.)
  • open/os_open: Add wasm32 path returning error
  • read/write: Replace libc::EINTR with cfg-gated constant
  • isatty: Return false on wasm32
  • strerror: Basic error-number-to-string mapping
  • lseek: Return error on wasm32
  • stat_inner: Use std::fs::metadata or return error
  • StatResultData::from_stat: Handle wasm32 StatStruct fields
  • cpu_count: Return 1 on wasm32
  • device_encoding: Return "UTF-8" on wasm32
  • utime_impl: Return "not supported" error on wasm32
crates/vm/src/stdlib/posix_compat.rs

Add environ for wasm32 (currently only defined for target_os = "wasi"):

#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
#[pyattr]
fn environ(vm: &VirtualMachine) -> crate::builtins::PyDictRef {
    vm.ctx.new_dict()  // Empty dict — no env vars on bare wasm32
}
Cfg Pattern

The recurring condition is:

#[cfg(all(target_arch = "wasm32", not(any(unix, windows, target_os = "wasi"))))]

This matches wasm32-unknown-unknown specifically (not emscripten, not wasi, not windows).

Verification Plan

  1. cargo check --target wasm32-unknown-unknown -p rustpython-vm --features "compiler,host_env" — must compile
  2. cargo test -p rustpython-vm on host — no regressions
  3. Update example_projects/wasm32_without_js/ to optionally enable host_env
  4. Runtime: build wasm32 binary with import os; print(os.name) — should print "posix" without error

References

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia dai gate cfg e dal codice specifico della piattaforma in crates/common/src/lib.rs, crates/common/src/crt_fd.rs e crates/common/src/fileutils.rs, quindi esamina i percorsi che dipendono da libc in crates/vm/src/stdlib/os.rs e posix_compat.rs. Esegui cargo check --target wasm32-unknown-unknown -p rustpython-vm --features "compiler,host_env" e cargo test -p rustpython-vm. Il lavoro è completato quando il target compila, i test dell’host continuano a superare l’esecuzione e import os funziona con il comportamento a runtime indicato.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
rust, wasm
Ambito
build-system, compilers
Tipo di issue
Funzionalità
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Ferma
Chiarezza
Specificata chiaramente
Idoneità per principianti
35/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.