RustPython / RustPython/RustPython

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

Aberta
#7,363 0 comentários 0 reações 0 responsáveis Ver no GitHub

Ninguém assumiu esta issue ainda.

Linguagem predominante
Rust
Estrelas
22.4k
Forks
1.5k
Merge médio
16h 33min
PRs com merge (30d)
165

Descrição

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

Guia de contribuição

Abrir o guia de contribuição

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Direção de pesquisa

Comece pelos gates cfg e pelo código específico da plataforma em crates/common/src/lib.rs, crates/common/src/crt_fd.rs e crates/common/src/fileutils.rs; depois, inspecione os caminhos dependentes de libc em crates/vm/src/stdlib/os.rs e posix_compat.rs. Execute cargo check --target wasm32-unknown-unknown -p rustpython-vm --features "compiler,host_env" e cargo test -p rustpython-vm. Considera-se concluído quando o target compilar, os testes do host continuarem passando e import os funcionar com o comportamento em tempo de execução indicado.

Escrita pelo modelo de indexação a partir do texto da issue.

Avaliação

Stack de tecnologia
rust, wasm
Domínio
build-system, compilers
Tipo de issue
Funcionalidade
Dificuldade
5/5
Tempo estimado
Mais de uma semana
Status de atividade
Estagnada
Clareza
Claramente especificada
Facilidade para iniciantes
35/100

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.