diff --git a/Cargo.lock b/Cargo.lock index e9ef09e9e..a8b5ebc71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2336,6 +2336,8 @@ dependencies = [ [[package]] name = "sre-engine" version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5872399287c284fed4bc773cb7f6041623ac88213774f5e11e89e2131681fc1" dependencies = [ "bitflags", "num_enum", diff --git a/derive/src/from_args.rs b/derive/src/from_args.rs index ca48ca744..8619f1c68 100644 --- a/derive/src/from_args.rs +++ b/derive/src/from_args.rs @@ -172,7 +172,7 @@ fn generate_field((i, field): (usize, &Field)) -> Result quote! { - ::rustpython_vm::function::ArgumentError::RequiredKeywordArgument(#pyname) + ::rustpython_vm::function::ArgumentError::RequiredKeywordArgument(#pyname.to_owned()) }, ParameterKind::Flatten => unreachable!(), }; diff --git a/derive/src/pystructseq.rs b/derive/src/pystructseq.rs index 4628e6153..4f3b4d249 100644 --- a/derive/src/pystructseq.rs +++ b/derive/src/pystructseq.rs @@ -39,6 +39,11 @@ pub(crate) fn impl_pystruct_sequence( ::rustpython_vm::builtins::tuple::PyTuple::_new(items.into_boxed_slice()) } } + impl ::rustpython_vm::pyobject::IntoPyObject for #ty { + fn into_pyobject(self, vm: &::rustpython_vm::VirtualMachine) -> ::rustpython_vm::pyobject::PyObjectRef { + ::rustpython_vm::pyobject::PyStructSequence::into_struct_sequence(self, vm).into_object() + } + } }; Ok(ret) } diff --git a/vm/build.rs b/vm/build.rs index 0fa0fe022..1ad01760b 100644 --- a/vm/build.rs +++ b/vm/build.rs @@ -23,7 +23,7 @@ fn main() { let mut f = std::fs::File::create(env_path).unwrap(); write!( f, - "hashmap! {{ {} }}", + "sysvars! {{ {} }}", std::env::vars_os().format_with(", ", |(k, v), f| f(&format_args!("{:?} => {:?}", k, v))) ) .unwrap(); diff --git a/vm/src/byteslike.rs b/vm/src/byteslike.rs index 464cc3045..eb0b0b078 100644 --- a/vm/src/byteslike.rs +++ b/vm/src/byteslike.rs @@ -1,4 +1,5 @@ use crate::builtins::memory::{try_buffer_from_object, BufferRef}; +use crate::builtins::PyStrRef; use crate::common::borrow::{BorrowedValue, BorrowedValueMut}; use crate::pyobject::{BorrowValue, PyObjectRef, PyResult, TryFromObject}; use crate::vm::VirtualMachine; @@ -124,3 +125,27 @@ impl<'a> BorrowValue<'a> for PyRwBytesLike { self.0.as_contiguous_mut().unwrap() } } + +/// A buffer or utf8 string. Like the `s*` format code for `PyArg_Parse` in CPython. +pub enum BufOrStr { + Buf(PyBytesLike), + Str(PyStrRef), +} + +impl TryFromObject for BufOrStr { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + obj.downcast() + .map(Self::Str) + .or_else(|obj| PyBytesLike::try_from_object(vm, obj).map(Self::Buf)) + } +} + +impl<'a> BorrowValue<'a> for BufOrStr { + type Borrowed = BorrowedValue<'a, [u8]>; + fn borrow_value(&'a self) -> Self::Borrowed { + match self { + Self::Buf(b) => b.borrow_value(), + Self::Str(s) => s.borrow_value().as_bytes().into(), + } + } +} diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 6039d9ba2..3a92aed60 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -17,6 +17,7 @@ #![allow(clippy::upper_case_acronyms)] #![doc(html_logo_url = "https://raw.githubusercontent.com/RustPython/RustPython/master/logo.png")] #![doc(html_root_url = "https://docs.rs/rustpython-vm/")] +#![cfg_attr(target_os = "redox", feature(array_value_iter))] #[cfg(feature = "flame-it")] #[macro_use] diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 847aef3ba..672f7811b 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -256,21 +256,3 @@ cfg_if::cfg_if! { } } } - -/// A modified version of the hashmap! macro from the maplit crate -macro_rules! hashmap { - (@single $($x:tt)*) => (()); - (@count $($rest:expr),*) => (<[()]>::len(&[$(hashmap!(@single $rest)),*])); - - (hasher=$hasher:expr, $($key:expr => $value:expr,)+) => { hashmap!(hasher=$hasher, $($key => $value),+) }; - (hasher=$hasher:expr, $($key:expr => $value:expr),*) => { - { - let _cap = hashmap!(@count $($key),*); - let mut _map = ::std::collections::HashMap::with_capacity_and_hasher(_cap, $hasher); - $( - let _ = _map.insert($key, $value); - )* - _map - } - }; -} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 937a76f58..c5a8fd3e9 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1139,9 +1139,10 @@ pub trait PyStructSequence: StaticType + PyClassImpl + Sized + 'static { fn into_tuple(self, vm: &VirtualMachine) -> PyTuple; - fn into_struct_sequence(self, vm: &VirtualMachine) -> PyResult { + fn into_struct_sequence(self, vm: &VirtualMachine) -> PyTupleRef { self.into_tuple(vm) .into_ref_with_type(vm, Self::static_type().clone()) + .unwrap() } #[pymethod(magic)] diff --git a/vm/src/stdlib/fcntl.rs b/vm/src/stdlib/fcntl.rs new file mode 100644 index 000000000..73c613a0e --- /dev/null +++ b/vm/src/stdlib/fcntl.rs @@ -0,0 +1,109 @@ +pub(crate) use fcntl::make_module; + +#[pymodule] +mod fcntl { + use super::super::os; + use crate::builtins::int; + use crate::byteslike::{BufOrStr, PyRwBytesLike}; + use crate::function::OptionalArg; + use crate::pyobject::{BorrowValue, Either, PyResult}; + use crate::VirtualMachine; + + #[pyattr] + use libc::{FD_CLOEXEC, F_GETFD, F_GETFL, F_SETFD, F_SETFL}; + + #[cfg(not(target_os = "wasi"))] + #[pyattr] + use libc::{F_DUPFD, F_DUPFD_CLOEXEC, F_GETLK, F_SETLK, F_SETLKW}; + + #[cfg(not(any(target_os = "wasi", target_os = "redox")))] + #[pyattr] + use libc::{F_GETOWN, F_RDLCK, F_SETOWN, F_UNLCK, F_WRLCK}; + + #[pyfunction] + fn fcntl( + fd: i32, + cmd: i32, + arg: OptionalArg>, + vm: &VirtualMachine, + ) -> PyResult { + let int = match arg { + OptionalArg::Present(Either::A(arg)) => { + let mut buf = [0u8; 1024]; + let arg_len; + { + let s = arg.borrow_value(); + arg_len = s.len(); + buf.get_mut(..arg_len) + .ok_or_else(|| vm.new_value_error("fcntl string arg too long".to_owned()))? + .copy_from_slice(&*s) + } + let ret = unsafe { libc::fcntl(fd, cmd, buf.as_mut_ptr()) }; + if ret < 0 { + return Err(os::errno_err(vm)); + } + return Ok(vm.ctx.new_bytes(buf[..arg_len].to_vec())); + } + OptionalArg::Present(Either::B(i)) => int::bigint_unsigned_mask(i.borrow_value()), + OptionalArg::Missing => 0, + }; + let ret = unsafe { libc::fcntl(fd, cmd, int as i32) }; + if ret < 0 { + return Err(os::errno_err(vm)); + } + Ok(vm.ctx.new_int(ret)) + } + + #[pyfunction] + fn ioctl( + fd: i32, + request: i32, + arg: OptionalArg, i32>>, + mutate_flag: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + let arg = arg.unwrap_or_else(|| Either::B(0)); + match arg { + Either::A(buf_kind) => { + const BUF_SIZE: usize = 1024; + let mut buf = [0u8; BUF_SIZE + 1]; // nul byte + let mut fill_buf = |b: &[u8]| { + if b.len() > BUF_SIZE { + return Err(vm.new_value_error("fcntl string arg too long".to_owned())); + } + buf[..b.len()].copy_from_slice(b); + Ok(b.len()) + }; + let buf_len = match buf_kind { + Either::A(rw_arg) => { + let mutate_flag = mutate_flag.unwrap_or(true); + let mut arg_buf = rw_arg.borrow_value(); + if mutate_flag { + let ret = + unsafe { libc::ioctl(fd, request as _, arg_buf.as_mut_ptr()) }; + if ret < 0 { + return Err(os::errno_err(vm)); + } + return Ok(vm.ctx.new_int(ret)); + } + // treat like an immutable buffer + fill_buf(&arg_buf)? + } + Either::B(ro_buf) => fill_buf(&ro_buf.borrow_value())?, + }; + let ret = unsafe { libc::ioctl(fd, request as _, buf.as_mut_ptr()) }; + if ret < 0 { + return Err(os::errno_err(vm)); + } + Ok(vm.ctx.new_bytes(buf[..buf_len].to_vec())) + } + Either::B(i) => { + let ret = unsafe { libc::ioctl(fd, request as _, i) }; + if ret < 0 { + return Err(os::errno_err(vm)); + } + Ok(vm.ctx.new_int(ret)) + } + } + } +} diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index ffa90764f..497a219c6 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -1,5 +1,6 @@ use crate::pyobject::PyObjectRef; use crate::vm::VirtualMachine; +use std::borrow::Cow; use std::collections::HashMap; pub mod array; @@ -48,6 +49,8 @@ mod os; #[cfg(not(target_arch = "wasm32"))] mod faulthandler; +#[cfg(any(unix, target_os = "wasi"))] +mod fcntl; #[cfg(windows)] mod msvcrt; #[cfg(not(target_arch = "wasm32"))] @@ -56,6 +59,9 @@ mod multiprocessing; mod posixsubprocess; #[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))] mod pwd; +// libc is missing constants on redox +#[cfg(all(unix, not(target_os = "redox")))] +mod resource; #[cfg(not(target_arch = "wasm32"))] mod select; #[cfg(not(target_arch = "wasm32"))] @@ -71,108 +77,116 @@ mod winreg; pub type StdlibInitFunc = Box PyObjectRef)>; -pub fn get_module_inits() -> HashMap { - #[allow(unused_mut)] - let mut modules = hashmap! { - hasher = ahash::RandomState::default(), - "array".to_owned() => Box::new(array::make_module) as StdlibInitFunc, - "atexit".to_owned() => Box::new(atexit::make_module), - "binascii".to_owned() => Box::new(binascii::make_module), - "_collections".to_owned() => Box::new(collections::make_module), - "_csv".to_owned() => Box::new(csv::make_module), - "dis".to_owned() => Box::new(dis::make_module), - "errno".to_owned() => Box::new(errno::make_module), - "_functools".to_owned() => Box::new(functools::make_module), - "hashlib".to_owned() => Box::new(hashlib::make_module), - "itertools".to_owned() => Box::new(itertools::make_module), - "_io".to_owned() => Box::new(io::make_module), - "_json".to_owned() => Box::new(json::make_module), - "marshal".to_owned() => Box::new(marshal::make_module), - "math".to_owned() => Box::new(math::make_module), - "_operator".to_owned() => Box::new(operator::make_module), - "_platform".to_owned() => Box::new(platform::make_module), - "regex_crate".to_owned() => Box::new(re::make_module), - "_random".to_owned() => Box::new(random::make_module), - "_serde_json".to_owned() => Box::new(serde_json::make_module), - "_sre".to_owned() => Box::new(sre::make_module), - "_string".to_owned() => Box::new(string::make_module), - "_struct".to_owned() => Box::new(pystruct::make_module), - "time".to_owned() => Box::new(time_module::make_module), - "_weakref".to_owned() => Box::new(weakref::make_module), - "_imp".to_owned() => Box::new(imp::make_module), - "unicodedata".to_owned() => Box::new(unicodedata::make_module), - "_warnings".to_owned() => Box::new(warnings::make_module), - "zlib".to_owned() => Box::new(zlib::make_module), - crate::sysmodule::sysconfigdata_name() => Box::new(sysconfigdata::make_module), - }; +pub type StdlibMap = HashMap, StdlibInitFunc, ahash::RandomState>; - // Insert parser related modules: - #[cfg(feature = "rustpython-ast")] - { - modules.insert( - "_ast".to_owned(), - Box::new(ast::make_module) as StdlibInitFunc, - ); +pub fn get_module_inits() -> StdlibMap { + macro_rules! modules { + { + $( + #[cfg($cfg:meta)] + { $( $key:expr => $val:expr),* $(,)? } + )* + } => {{ + let iter = std::array::IntoIter::new([ + $( + $(#[cfg($cfg)] (Cow::<'static, str>::from($key), Box::new($val) as StdlibInitFunc),)* + )* + ]); + iter.collect() + }}; } - - #[cfg(feature = "rustpython-parser")] - { - modules.insert("keyword".to_owned(), Box::new(keyword::make_module)); - } - - // Insert compiler related modules: - #[cfg(feature = "rustpython-compiler")] - { - modules.insert("symtable".to_owned(), Box::new(symtable::make_module)); - } - - #[cfg(any(unix, windows, target_os = "wasi"))] - modules.insert(os::MODULE_NAME.to_owned(), Box::new(os::make_module)); - - // disable some modules on WASM - #[cfg(not(target_arch = "wasm32"))] - { - modules.insert("_socket".to_owned(), Box::new(socket::make_module)); - modules.insert( - "_multiprocessing".to_owned(), - Box::new(multiprocessing::make_module), - ); - modules.insert("_signal".to_owned(), Box::new(signal::make_module)); - modules.insert("select".to_owned(), Box::new(select::make_module)); + modules! { + #[cfg(all())] + { + "array" => array::make_module, + "atexit" => atexit::make_module, + "binascii" => binascii::make_module, + "_collections" => collections::make_module, + "_csv" => csv::make_module, + "dis" => dis::make_module, + "errno" => errno::make_module, + "_functools" => functools::make_module, + "hashlib" => hashlib::make_module, + "itertools" => itertools::make_module, + "_io" => io::make_module, + "_json" => json::make_module, + "marshal" => marshal::make_module, + "math" => math::make_module, + "_operator" => operator::make_module, + "_platform" => platform::make_module, + "regex_crate" => re::make_module, + "_random" => random::make_module, + "_serde_json" => serde_json::make_module, + "_sre" => sre::make_module, + "_string" => string::make_module, + "_struct" => pystruct::make_module, + "time" => time_module::make_module, + "_weakref" => weakref::make_module, + "_imp" => imp::make_module, + "unicodedata" => unicodedata::make_module, + "_warnings" => warnings::make_module, + "zlib" => zlib::make_module, + crate::sysmodule::sysconfigdata_name() => sysconfigdata::make_module, + } + // parser related modules: + #[cfg(feature = "rustpython-ast")] + { + "_ast" => ast::make_module, + } + #[cfg(feature = "rustpython-parser")] + { + "keyword" => keyword::make_module, + } + // compiler related modules: + #[cfg(feature = "rustpython-compiler")] + { + "symtable" => symtable::make_module, + } + #[cfg(any(unix, windows, target_os = "wasi"))] + { + os::MODULE_NAME => os::make_module, + } + #[cfg(any(unix, target_os = "wasi"))] + { + "fcntl" => fcntl::make_module, + } + // disable some modules on WASM + #[cfg(not(target_arch = "wasm32"))] + { + "_socket" => socket::make_module, + "_multiprocessing" => multiprocessing::make_module, + "_signal" => signal::make_module, + "select" => select::make_module, + "faulthandler" => faulthandler::make_module, + } #[cfg(feature = "ssl")] - modules.insert("_ssl".to_owned(), Box::new(ssl::make_module)); - #[cfg(feature = "threading")] - modules.insert("_thread".to_owned(), Box::new(thread::make_module)); - modules.insert( - "faulthandler".to_owned(), - Box::new(faulthandler::make_module), - ); + { + "_ssl" => ssl::make_module, + } + #[cfg(all(feature = "threading", not(target_arch = "wasm32")))] + { + "_thread" => thread::make_module, + } + // Unix-only + #[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))] + { + "pwd" => pwd::make_module, + } + #[cfg(all(unix, not(target_os = "redox")))] + { + "termios" => termios::make_module, + "resource" => resource::make_module, + } + #[cfg(unix)] + { + "_posixsubprocess" => posixsubprocess::make_module, + } + // Windows-only + #[cfg(windows)] + { + "msvcrt" => msvcrt::make_module, + "_winapi" => winapi::make_module, + "winreg" => winreg::make_module, + } } - - // Unix-only - #[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))] - { - modules.insert("pwd".to_owned(), Box::new(pwd::make_module)); - } - - #[cfg(all(unix, not(target_os = "redox")))] - modules.insert("termios".to_owned(), Box::new(termios::make_module)); - - #[cfg(unix)] - { - modules.insert( - "_posixsubprocess".to_owned(), - Box::new(posixsubprocess::make_module), - ); - } - - // Windows-only - #[cfg(windows)] - { - modules.insert("msvcrt".to_owned(), Box::new(msvcrt::make_module)); - modules.insert("_winapi".to_owned(), Box::new(winapi::make_module)); - modules.insert("winreg".to_owned(), Box::new(winreg::make_module)); - } - - modules } diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 01a3a2760..4361500e2 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -475,13 +475,6 @@ mod _os { Ok(vm.ctx.new_int(written as u64)) } - #[pyfunction] - fn error(message: OptionalArg, vm: &VirtualMachine) -> PyResult { - let msg = message.map_or("".to_owned(), |msg| msg.borrow_value().to_owned()); - - Err(vm.new_os_error(msg)) - } - #[pyfunction] fn fsync(fd: i64, vm: &VirtualMachine) -> PyResult<()> { let file = rust_file(fd); @@ -793,10 +786,6 @@ mod _os { #[pyimpl(with(PyStructSequence))] impl StatResult { - fn into_obj(self, vm: &VirtualMachine) -> PyObjectRef { - self.into_struct_sequence(vm).unwrap().into_object() - } - fn from_metadata(meta: fs::Metadata) -> io::Result { let (st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, ctime); #[cfg(windows)] @@ -919,7 +908,7 @@ mod _os { } }; meta.and_then(StatResult::from_metadata) - .map(|stat| stat.into_obj(vm)) + .map(|stat| stat.into_pyobject(vm)) .map_err(|err| err.into_pyexception(vm)) } @@ -1274,7 +1263,7 @@ mod _os { Ok(()) } - #[cfg(unix)] + #[cfg(all(unix, not(any(target_os = "redox", target_os = "android"))))] #[pyfunction] fn getloadavg(vm: &VirtualMachine) -> PyResult<(f64, f64, f64)> { let mut loadavg = [0f64; 3]; @@ -1418,6 +1407,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { "supports_fd" => supports_fd.into_object(), "supports_dir_fd" => supports_dir_fd.into_object(), "supports_follow_symlinks" => supports_follow_symlinks.into_object(), + "error" => vm.ctx.exceptions.os_error.clone(), }); module @@ -2112,14 +2102,10 @@ mod posix { } #[pyimpl(with(PyStructSequence))] - impl UnameResult { - fn into_obj(self, vm: &VirtualMachine) -> PyObjectRef { - self.into_struct_sequence(vm).unwrap().into_object() - } - } + impl UnameResult {} #[pyfunction] - fn uname(vm: &VirtualMachine) -> PyResult { + fn uname(vm: &VirtualMachine) -> PyResult { let info = uname::uname().map_err(|err| err.into_pyexception(vm))?; Ok(UnameResult { sysname: info.sysname, @@ -2127,8 +2113,7 @@ mod posix { release: info.release, version: info.version, machine: info.machine, - } - .into_obj(vm)) + }) } #[pyfunction] @@ -2473,23 +2458,23 @@ mod posix { } #[pyfunction] - fn get_terminal_size(fd: OptionalArg, vm: &VirtualMachine) -> PyResult { + fn get_terminal_size( + fd: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { let (columns, lines) = { - #[cfg(unix)] - { - nix::ioctl_read_bad!(winsz, libc::TIOCGWINSZ, libc::winsize); - let mut w = libc::winsize { - ws_row: 0, - ws_col: 0, - ws_xpixel: 0, - ws_ypixel: 0, - }; - unsafe { winsz(fd.unwrap_or(libc::STDOUT_FILENO), &mut w) } - .map_err(|err| err.into_pyexception(vm))?; - (w.ws_col.into(), w.ws_row.into()) - } + nix::ioctl_read_bad!(winsz, libc::TIOCGWINSZ, libc::winsize); + let mut w = libc::winsize { + ws_row: 0, + ws_col: 0, + ws_xpixel: 0, + ws_ypixel: 0, + }; + unsafe { winsz(fd.unwrap_or(libc::STDOUT_FILENO), &mut w) } + .map_err(|err| err.into_pyexception(vm))?; + (w.ws_col.into(), w.ws_row.into()) }; - super::_os::PyTerminalSize { columns, lines }.into_struct_sequence(vm) + Ok(super::_os::PyTerminalSize { columns, lines }) } // from libstd: @@ -2818,36 +2803,37 @@ mod nt { } #[pyfunction] - fn get_terminal_size(fd: OptionalArg, vm: &VirtualMachine) -> PyResult { + fn get_terminal_size( + fd: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { let (columns, lines) = { - { - use winapi::um::{handleapi, processenv, winbase, wincon}; - let stdhandle = match fd { - OptionalArg::Present(0) => winbase::STD_INPUT_HANDLE, - OptionalArg::Present(1) | OptionalArg::Missing => winbase::STD_OUTPUT_HANDLE, - OptionalArg::Present(2) => winbase::STD_ERROR_HANDLE, - _ => return Err(vm.new_value_error("bad file descriptor".to_owned())), - }; - let h = unsafe { processenv::GetStdHandle(stdhandle) }; - if h.is_null() { - return Err(vm.new_os_error("handle cannot be retrieved".to_owned())); - } - if h == handleapi::INVALID_HANDLE_VALUE { - return Err(errno_err(vm)); - } - let mut csbi = wincon::CONSOLE_SCREEN_BUFFER_INFO::default(); - let ret = unsafe { wincon::GetConsoleScreenBufferInfo(h, &mut csbi) }; - if ret == 0 { - return Err(errno_err(vm)); - } - let w = csbi.srWindow; - ( - (w.Right - w.Left + 1) as usize, - (w.Bottom - w.Top + 1) as usize, - ) + use winapi::um::{handleapi, processenv, winbase, wincon}; + let stdhandle = match fd { + OptionalArg::Present(0) => winbase::STD_INPUT_HANDLE, + OptionalArg::Present(1) | OptionalArg::Missing => winbase::STD_OUTPUT_HANDLE, + OptionalArg::Present(2) => winbase::STD_ERROR_HANDLE, + _ => return Err(vm.new_value_error("bad file descriptor".to_owned())), + }; + let h = unsafe { processenv::GetStdHandle(stdhandle) }; + if h.is_null() { + return Err(vm.new_os_error("handle cannot be retrieved".to_owned())); } + if h == handleapi::INVALID_HANDLE_VALUE { + return Err(errno_err(vm)); + } + let mut csbi = wincon::CONSOLE_SCREEN_BUFFER_INFO::default(); + let ret = unsafe { wincon::GetConsoleScreenBufferInfo(h, &mut csbi) }; + if ret == 0 { + return Err(errno_err(vm)); + } + let w = csbi.srWindow; + ( + (w.Right - w.Left + 1) as usize, + (w.Bottom - w.Top + 1) as usize, + ) }; - super::_os::PyTerminalSize { columns, lines }.into_struct_sequence(vm) + Ok(super::_os::PyTerminalSize { columns, lines }) } #[cfg(target_env = "msvc")] diff --git a/vm/src/stdlib/pwd.rs b/vm/src/stdlib/pwd.rs index 07a793ec1..cdcfd2d42 100644 --- a/vm/src/stdlib/pwd.rs +++ b/vm/src/stdlib/pwd.rs @@ -1,7 +1,9 @@ use crate::builtins::int::PyIntRef; use crate::builtins::pystr::PyStrRef; use crate::exceptions::IntoPyException; -use crate::pyobject::{BorrowValue, PyClassImpl, PyObjectRef, PyResult, PyStructSequence}; +use crate::pyobject::{ + BorrowValue, IntoPyObject, PyClassImpl, PyObjectRef, PyResult, PyStructSequence, +}; use crate::vm::VirtualMachine; use std::convert::TryFrom; use std::ptr::NonNull; @@ -46,9 +48,9 @@ impl From for Passwd { } } -fn pwd_getpwnam(name: PyStrRef, vm: &VirtualMachine) -> PyResult { +fn pwd_getpwnam(name: PyStrRef, vm: &VirtualMachine) -> PyResult { match User::from_name(name.borrow_value()).map_err(|err| err.into_pyexception(vm))? { - Some(user) => Ok(Passwd::from(user).into_struct_sequence(vm)?.into_object()), + Some(user) => Ok(Passwd::from(user)), None => { let name_repr = vm.to_repr(name.as_object())?; let message = vm @@ -59,14 +61,14 @@ fn pwd_getpwnam(name: PyStrRef, vm: &VirtualMachine) -> PyResult { } } -fn pwd_getpwuid(uid: PyIntRef, vm: &VirtualMachine) -> PyResult { +fn pwd_getpwuid(uid: PyIntRef, vm: &VirtualMachine) -> PyResult { let uid_t = libc::uid_t::try_from(uid.borrow_value()).map(unistd::Uid::from_raw); let user = match uid_t { Ok(uid) => User::from_uid(uid).map_err(|err| err.into_pyexception(vm))?, Err(_) => None, }; match user { - Some(user) => Ok(Passwd::from(user).into_struct_sequence(vm)?.into_object()), + Some(user) => Ok(Passwd::from(user)), None => { let message = vm .ctx @@ -86,7 +88,7 @@ fn pwd_getpwall(vm: &VirtualMachine) -> PyResult { unsafe { libc::setpwent() }; while let Some(ptr) = NonNull::new(unsafe { libc::getpwent() }) { let user = User::from(unsafe { ptr.as_ref() }); - let passwd = Passwd::from(user).into_struct_sequence(vm)?.into_object(); + let passwd = Passwd::from(user).into_pyobject(vm); list.push(passwd); } unsafe { libc::endpwent() }; diff --git a/vm/src/stdlib/resource.rs b/vm/src/stdlib/resource.rs new file mode 100644 index 000000000..f04b342b9 --- /dev/null +++ b/vm/src/stdlib/resource.rs @@ -0,0 +1,172 @@ +pub(crate) use resource::make_module; + +#[pymodule] +mod resource { + use super::super::os; + use crate::exceptions::IntoPyException; + use crate::pyobject::{IntoPyObject, PyObjectRef, PyResult, PyStructSequence, TryFromObject}; + use crate::VirtualMachine; + use std::{io, mem}; + + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + use libc::RLIMIT_NLIMITS as RLIM_NLIMITS; + } else if #[cfg(target_os = "android")] { + // TODO: this should be in libc soon + const RLIM_NLIMITS: i32 = 16; + } else { + // in bsd-ish platforms, this constant isn't abi-stable across os versions, so we just + // pick a high number so we don't get false positive ValueErrors and just bubble up the + // EINVAL that get/setrlimit return on an invalid resource + const RLIM_NLIMITS: i32 = 256; + } + } + + // TODO: RLIMIT_OFILE, + #[pyattr] + use libc::{ + RLIMIT_AS, RLIMIT_CORE, RLIMIT_CPU, RLIMIT_DATA, RLIMIT_FSIZE, RLIMIT_MEMLOCK, + RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_RSS, RLIMIT_STACK, RLIM_INFINITY, + }; + + #[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] + #[pyattr] + use libc::{RLIMIT_MSGQUEUE, RLIMIT_NICE, RLIMIT_RTPRIO, RLIMIT_SIGPENDING}; + // TODO: I think this is supposed to be defined for all linux_like? + #[cfg(target_os = "linux")] + #[pyattr] + use libc::RLIMIT_RTTIME; + + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + #[pyattr] + use libc::{RLIMIT_NPTS, RLIMIT_SBSIZE, RLIMIT_SWAP}; + + #[cfg(any(target_os = "freebsd", target_os = "solaris", target_os = "illumos"))] + #[pyattr] + use libc::{RLIMIT_NPTS, RLIMIT_SBSIZE, RLIMIT_SWAP, RLIMIT_VMEM}; + + #[pyattr] + #[pyclass(name = "struct_rusage")] + #[derive(PyStructSequence)] + struct Rusage { + ru_utime: f64, + ru_stime: f64, + ru_maxrss: libc::c_long, + ru_ixrss: libc::c_long, + ru_idrss: libc::c_long, + ru_isrss: libc::c_long, + ru_minflt: libc::c_long, + ru_majflt: libc::c_long, + ru_nswap: libc::c_long, + ru_inblock: libc::c_long, + ru_oublock: libc::c_long, + ru_msgsnd: libc::c_long, + ru_msgrcv: libc::c_long, + ru_nsignals: libc::c_long, + ru_nvcsw: libc::c_long, + ru_nivcsw: libc::c_long, + } + + #[pyimpl(with(PyStructSequence))] + impl Rusage {} + + impl From for Rusage { + fn from(rusage: libc::rusage) -> Self { + let tv = |tv: libc::timeval| tv.tv_sec as f64 + (tv.tv_usec as f64 / 1_000_000.0); + Rusage { + ru_utime: tv(rusage.ru_utime), + ru_stime: tv(rusage.ru_utime), + ru_maxrss: rusage.ru_maxrss, + ru_ixrss: rusage.ru_ixrss, + ru_idrss: rusage.ru_idrss, + ru_isrss: rusage.ru_isrss, + ru_minflt: rusage.ru_minflt, + ru_majflt: rusage.ru_majflt, + ru_nswap: rusage.ru_nswap, + ru_inblock: rusage.ru_inblock, + ru_oublock: rusage.ru_oublock, + ru_msgsnd: rusage.ru_msgsnd, + ru_msgrcv: rusage.ru_msgrcv, + ru_nsignals: rusage.ru_nsignals, + ru_nvcsw: rusage.ru_nvcsw, + ru_nivcsw: rusage.ru_nivcsw, + } + } + } + + #[pyfunction] + fn getrusage(who: i32, vm: &VirtualMachine) -> PyResult { + let res = unsafe { + let mut rusage = mem::MaybeUninit::::uninit(); + if libc::getrusage(who, rusage.as_mut_ptr()) == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(rusage.assume_init()) + } + }; + res.map(Rusage::from).map_err(|e| { + if e.kind() == io::ErrorKind::InvalidInput { + vm.new_value_error("invalid who parameter".to_owned()) + } else { + e.into_pyexception(vm) + } + }) + } + + struct Limits(libc::rlimit); + impl TryFromObject for Limits { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + let seq = vm.extract_elements::(&obj)?; + match *seq { + [cur, max] => Ok(Self(libc::rlimit { + rlim_cur: cur & RLIM_INFINITY, + rlim_max: max & RLIM_INFINITY, + })), + _ => Err(vm.new_value_error("expected a tuple of 2 integers".to_owned())), + } + } + } + impl IntoPyObject for Limits { + fn into_pyobject(self, vm: &VirtualMachine) -> PyObjectRef { + (self.0.rlim_cur, self.0.rlim_max).into_pyobject(vm) + } + } + + #[pyfunction] + fn getrlimit(resource: i32, vm: &VirtualMachine) -> PyResult { + if resource < 0 || resource >= RLIM_NLIMITS as _ { + return Err(vm.new_value_error("invalid resource specified".to_owned())); + } + let rlimit = unsafe { + let mut rlimit = mem::MaybeUninit::::uninit(); + if libc::getrlimit(resource as _, rlimit.as_mut_ptr()) == -1 { + return Err(os::errno_err(vm)); + } + rlimit.assume_init() + }; + Ok(Limits(rlimit)) + } + + #[pyfunction] + fn setrlimit(resource: i32, limits: Limits, vm: &VirtualMachine) -> PyResult<()> { + if resource < 0 || resource >= RLIM_NLIMITS as _ { + return Err(vm.new_value_error("invalid resource specified".to_owned())); + } + let res = unsafe { + if libc::setrlimit(resource as _, &limits.0) == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + }; + res.map_err(|e| match e.kind() { + io::ErrorKind::InvalidInput => { + vm.new_value_error("current limit exceeds maximum limit".to_owned()) + } + io::ErrorKind::PermissionDenied => { + vm.new_value_error("not allowed to raise maximum limit".to_owned()) + } + _ => e.into_pyexception(vm), + }) + } +} diff --git a/vm/src/stdlib/select.rs b/vm/src/stdlib/select.rs index 7f697a314..b3e5b9341 100644 --- a/vm/src/stdlib/select.rs +++ b/vm/src/stdlib/select.rs @@ -3,10 +3,7 @@ use crate::vm::VirtualMachine; use std::io; pub(crate) fn make_module(vm: &VirtualMachine) -> PyObjectRef { - #[cfg(windows)] - { - let _ = unsafe { winapi::um::winsock2::WSAStartup(0x0101, &mut std::mem::zeroed()) }; - } + super::socket::init_winsock(); decl::make_module(vm) } diff --git a/vm/src/stdlib/signal.rs b/vm/src/stdlib/signal.rs index 0076db8d4..2dff0e749 100644 --- a/vm/src/stdlib/signal.rs +++ b/vm/src/stdlib/signal.rs @@ -1,7 +1,8 @@ +use crate::exceptions::IntoPyException; use crate::pyobject::{PyObjectRef, PyResult, TryFromObject}; use crate::vm::{VirtualMachine, NSIG}; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::{self, AtomicBool, Ordering}; #[cfg(unix)] use nix::unistd::alarm as sig_alarm; @@ -23,9 +24,37 @@ static TRIGGERS: [AtomicBool; NSIG] = [ATOMIC_FALSE; NSIG]; static ANY_TRIGGERED: AtomicBool = AtomicBool::new(false); +cfg_if::cfg_if! { + if #[cfg(windows)] { + use winapi::um::winsock2; + type WakeupFd = libc::SOCKET; + const INVALID_WAKEUP: WakeupFd = (-1isize) as usize; + static WAKEUP: atomic::AtomicUsize = atomic::AtomicUsize::new(INVALID_WAKEUP); + // windows doesn't use the same fds for files and sockets like windows does, so we need + // this to know whether to send() or write() + static WAKEUP_IS_SOCKET: AtomicBool = AtomicBool::new(false); + } else { + type WakeupFd = i32; + const INVALID_WAKEUP: WakeupFd = -1; + static WAKEUP: atomic::AtomicI32 = atomic::AtomicI32::new(INVALID_WAKEUP); + } +} + extern "C" fn run_signal(signum: i32) { - ANY_TRIGGERED.store(true, Ordering::Relaxed); TRIGGERS[signum as usize].store(true, Ordering::Relaxed); + ANY_TRIGGERED.store(true, Ordering::SeqCst); + let wakeup_fd = WAKEUP.load(Ordering::Relaxed); + if wakeup_fd != INVALID_WAKEUP { + let sigbyte = signum as u8; + #[cfg(windows)] + if WAKEUP_IS_SOCKET.load(Ordering::Relaxed) { + let _res = + unsafe { winsock2::send(wakeup_fd, &sigbyte as *const u8 as *const _, 1, 0) }; + return; + } + let _res = unsafe { libc::write(wakeup_fd as _, &sigbyte as *const u8 as *const _, 1) }; + // TODO: handle _res < 1, support warn_on_full_buffer + } } fn assert_in_range(signum: i32, vm: &VirtualMachine) -> PyResult<()> { @@ -36,6 +65,11 @@ fn assert_in_range(signum: i32, vm: &VirtualMachine) -> PyResult<()> { } } +#[cfg(all(unix, not(target_os = "redox")))] +extern "C" { + fn siginterrupt(sig: i32, flag: i32) -> i32; +} + fn _signal_signal( signalnum: i32, handler: PyObjectRef, @@ -65,13 +99,8 @@ fn _signal_signal( return Err(vm.new_os_error("Failed to set signal".to_owned())); } #[cfg(all(unix, not(target_os = "redox")))] - { - extern "C" { - fn siginterrupt(sig: i32, flag: i32); - } - unsafe { - siginterrupt(signalnum, 1); - } + unsafe { + siginterrupt(signalnum, 1); } let old_handler = std::mem::replace( @@ -145,6 +174,78 @@ fn _signal_default_int_handler( Err(vm.new_exception_empty(vm.ctx.exceptions.keyboard_interrupt.clone())) } +#[derive(FromArgs)] +struct SetWakeupFdArgs { + #[pyarg(any)] + fd: WakeupFd, + #[pyarg(named, default = "true")] + warn_on_full_buffer: bool, +} + +fn _signal_set_wakeup_fd(args: SetWakeupFdArgs, vm: &VirtualMachine) -> PyResult { + // TODO: implement warn_on_full_buffer + let _ = args.warn_on_full_buffer; + let fd = args.fd; + + if vm.signal_handlers.is_none() { + return Err(vm.new_value_error("signal only works in main thread".to_owned())); + } + + #[cfg(windows)] + let is_socket = if fd != INVALID_WAKEUP { + super::socket::init_winsock(); + let mut res = 0i32; + let mut res_size = std::mem::size_of::() as i32; + let res = unsafe { + winsock2::getsockopt( + fd, + winsock2::SOL_SOCKET, + winsock2::SO_ERROR, + &mut res as *mut i32 as *mut _, + &mut res_size, + ) + }; + // if getsockopt succeeded, fd is for sure a socket + let is_socket = res == 0; + if !is_socket { + let err = std::io::Error::last_os_error(); + // if getsockopt failed for some other reason, throw + if err.raw_os_error() != Some(winsock2::WSAENOTSOCK) { + return Err(err.into_pyexception(vm)); + } + } + is_socket + } else { + false + }; + #[cfg(not(windows))] + if fd != INVALID_WAKEUP { + use nix::fcntl; + let oflags = fcntl::fcntl(fd, fcntl::F_GETFL).map_err(|e| e.into_pyexception(vm))?; + let nonblock = fcntl::OFlag::from_bits_truncate(oflags).contains(fcntl::OFlag::O_NONBLOCK); + if !nonblock { + return Err(vm.new_value_error(format!("the fd {} must be in non-blocking mode", fd))); + } + } + + let old_fd = WAKEUP.swap(fd, Ordering::Relaxed); + #[cfg(windows)] + WAKEUP_IS_SOCKET.store(is_socket, Ordering::Relaxed); + + Ok(old_fd) +} + +#[cfg(all(unix, not(target_os = "redox")))] +fn _signal_siginterrupt(signum: i32, flag: i32, vm: &VirtualMachine) -> PyResult<()> { + assert_in_range(signum, vm)?; + let res = unsafe { siginterrupt(signum, flag) }; + if res < 0 { + Err(super::os::errno_err(vm)) + } else { + Ok(()) + } +} + pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { let ctx = &vm.ctx; @@ -156,6 +257,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { let module = py_module!(vm, "_signal", { "signal" => named_function!(ctx, _signal, signal), "getsignal" => named_function!(ctx, _signal, getsignal), + "set_wakeup_fd" => named_function!(ctx, _signal, set_wakeup_fd), "SIG_DFL" => sig_dfl.clone(), "SIG_IGN" => sig_ign.clone(), "SIGABRT" => ctx.new_int(libc::SIGABRT as u8), @@ -220,6 +322,11 @@ fn extend_module_platform_specific(vm: &VirtualMachine, module: &PyObjectRef) { "SIGSYS" => ctx.new_int(libc::SIGSYS as u8), }); + #[cfg(not(target_os = "redox"))] + extend_module!(vm, module, { + "siginterrupt" => named_function!(ctx, _signal, siginterrupt), + }); + #[cfg(not(any(target_os = "macos", target_os = "openbsd", target_os = "freebsd")))] { extend_module!(vm, module, { diff --git a/vm/src/stdlib/socket.rs b/vm/src/stdlib/socket.rs index d176a71e0..5d4a35501 100644 --- a/vm/src/stdlib/socket.rs +++ b/vm/src/stdlib/socket.rs @@ -1077,6 +1077,8 @@ rustpython_common::static_cell! { } pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { + init_winsock(); + let ctx = &vm.ctx; let socket_timeout = TIMEOUT_ERROR .get_or_init(|| { @@ -1184,3 +1186,13 @@ fn extend_module_platform_specific(vm: &VirtualMachine, module: &PyObjectRef) { "sethostname" => named_function!(ctx, _socket, sethostname), }); } + +pub fn init_winsock() { + #[cfg(windows)] + { + static WSA_INIT: parking_lot::Once = parking_lot::Once::new(); + WSA_INIT.call_once(|| { + let _ = unsafe { winapi::um::winsock2::WSAStartup(0x0101, &mut std::mem::zeroed()) }; + }) + } +} diff --git a/vm/src/stdlib/sysconfigdata.rs b/vm/src/stdlib/sysconfigdata.rs index 5b15e9b1c..cefc0dac7 100644 --- a/vm/src/stdlib/sysconfigdata.rs +++ b/vm/src/stdlib/sysconfigdata.rs @@ -1,19 +1,21 @@ -use crate::pyobject::{ItemProtocol, PyObjectRef}; +use crate::pyobject::{IntoPyObject, ItemProtocol, PyObjectRef}; use crate::VirtualMachine; use crate::sysmodule::MULTIARCH; pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { let vars = vm.ctx.new_dict(); - macro_rules! hashmap { + macro_rules! sysvars { ($($key:literal => $value:expr),*$(,)?) => {{ - $(vars.set_item($key, vm.ctx.new_str($value), vm).unwrap();)* + $(vars.set_item($key, $value.into_pyobject(vm), vm).unwrap();)* }}; } - hashmap! { + sysvars! { // fake shared module extension "EXT_SUFFIX" => format!(".rustpython-{}", MULTIARCH), "MULTIARCH" => MULTIARCH, + // enough for tests to stop expecting urandom() to fail after restricting file resources + "HAVE_GETRANDOM" => 1, } include!(concat!(env!("OUT_DIR"), "/env_vars.rs")); diff --git a/vm/src/stdlib/termios.rs b/vm/src/stdlib/termios.rs index 50ebf93ac..b671e46d0 100644 --- a/vm/src/stdlib/termios.rs +++ b/vm/src/stdlib/termios.rs @@ -10,6 +10,9 @@ mod termios { use std::convert::TryFrom; use termios::Termios; + // TODO: more ioctl numbers + #[pyattr] + use libc::{TIOCGWINSZ, TIOCSWINSZ}; #[pyattr] use termios::{ os::target::NCCS, B0, B110, B1200, B134, B150, B1800, B19200, B200, B2400, B300, B38400, diff --git a/vm/src/stdlib/time_module.rs b/vm/src/stdlib/time_module.rs index 267fc6457..e1876e9a6 100644 --- a/vm/src/stdlib/time_module.rs +++ b/vm/src/stdlib/time_module.rs @@ -9,7 +9,6 @@ use chrono::{Datelike, Timelike}; use crate::builtins::pystr::PyStrRef; use crate::builtins::pytype::PyTypeRef; -use crate::builtins::tuple::PyTupleRef; use crate::function::OptionalArg; use crate::pyobject::{ BorrowValue, Either, PyClassImpl, PyObjectRef, PyResult, PyStructSequence, TryFromObject, @@ -90,23 +89,23 @@ fn pyobj_to_naive_date_time( } /// https://docs.python.org/3/library/time.html?highlight=gmtime#time.gmtime -fn time_gmtime(secs: OptionalArg>, vm: &VirtualMachine) -> PyResult { +fn time_gmtime(secs: OptionalArg>, vm: &VirtualMachine) -> PyResult { let default = chrono::offset::Utc::now().naive_utc(); let instant = match secs { OptionalArg::Present(secs) => pyobj_to_naive_date_time(secs, vm)?, OptionalArg::Missing => default, }; - Ok(PyStructTime::new(vm, instant, 0).into_obj(vm)) + Ok(PyStructTime::new(vm, instant, 0)) } fn time_localtime( secs: OptionalArg>, vm: &VirtualMachine, -) -> PyResult { +) -> PyResult { let instant = optional_or_localtime(secs, vm)?; // TODO: isdst flag must be valid value here // https://docs.python.org/3/library/time.html#time.localtime - Ok(PyStructTime::new(vm, instant, -1).into_obj(vm)) + Ok(PyStructTime::new(vm, instant, -1)) } fn time_mktime(t: PyStructTime, vm: &VirtualMachine) -> PyResult { @@ -154,14 +153,18 @@ fn time_strftime(format: PyStrRef, t: OptionalArg, vm: &VirtualMac Ok(vm.ctx.new_str(formatted_time)) } -fn time_strptime(string: PyStrRef, format: OptionalArg, vm: &VirtualMachine) -> PyResult { +fn time_strptime( + string: PyStrRef, + format: OptionalArg, + vm: &VirtualMachine, +) -> PyResult { let format = match format { OptionalArg::Present(ref format) => format.borrow_value(), OptionalArg::Missing => "%a %b %H:%M:%S %Y", }; let instant = NaiveDateTime::parse_from_str(string.borrow_value(), format) .map_err(|e| vm.new_value_error(format!("Parse error: {:?}", e)))?; - Ok(PyStructTime::new(vm, instant, -1).into_obj(vm)) + Ok(PyStructTime::new(vm, instant, -1)) } #[pyclass(module = "time", name = "struct_time")] @@ -217,14 +220,10 @@ impl PyStructTime { Ok(dt) } - fn into_obj(self, vm: &VirtualMachine) -> PyObjectRef { - self.into_struct_sequence(vm).unwrap().into_object() - } - #[pyslot] - fn tp_new(_cls: PyTypeRef, seq: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn tp_new(_cls: PyTypeRef, seq: PyObjectRef, vm: &VirtualMachine) -> PyResult { // cls is ignorable because this is not a basetype - Self::try_from_object(vm, seq)?.into_struct_sequence(vm) + Self::try_from_object(vm, seq) } } diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 42b5cc969..29c3e3a86 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -319,7 +319,7 @@ fn sys_getwindowsversion(vm: &VirtualMachine) -> PyResult PyResult PyResult { @@ -497,23 +497,19 @@ pub(crate) fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: Py let ctx = &vm.ctx; let _flags_type = SysFlags::make_class(ctx); - let flags = SysFlags::from_settings(&vm.state.settings) - .into_struct_sequence(vm) - .unwrap(); + let flags = SysFlags::from_settings(&vm.state.settings).into_struct_sequence(vm); let _version_info_type = version::VersionInfo::make_class(ctx); - let version_info = version::VersionInfo::VERSION - .into_struct_sequence(vm) - .unwrap(); + let version_info = version::VersionInfo::VERSION.into_struct_sequence(vm); let _hash_info_type = PyHashInfo::make_class(ctx); - let hash_info = PyHashInfo::INFO.into_struct_sequence(vm).unwrap(); + let hash_info = PyHashInfo::INFO.into_struct_sequence(vm); let _float_info_type = PyFloatInfo::make_class(ctx); - let float_info = PyFloatInfo::INFO.into_struct_sequence(vm).unwrap(); + let float_info = PyFloatInfo::INFO.into_struct_sequence(vm); let _int_info_type = PyIntInfo::make_class(ctx); - let int_info = PyIntInfo::INFO.into_struct_sequence(vm).unwrap(); + let int_info = PyIntInfo::INFO.into_struct_sequence(vm); // TODO Add crate version to this namespace let implementation = py_namespace!(vm, { @@ -634,12 +630,16 @@ setprofile() -- set the global profiling function setrecursionlimit() -- set the max recursion depth for the interpreter settrace() -- set the global debug tracing function "; - let mut module_names: Vec = vm.state.stdlib_inits.keys().cloned().collect(); - module_names.push("sys".to_owned()); - module_names.push("builtins".to_owned()); + let mut module_names: Vec<_> = vm.state.stdlib_inits.keys().cloned().collect(); + module_names.push("sys".into()); + module_names.push("builtins".into()); module_names.sort(); - let builtin_module_names = - ctx.new_tuple(module_names.into_iter().map(|n| ctx.new_str(n)).collect()); + let builtin_module_names = ctx.new_tuple( + module_names + .into_iter() + .map(|n| ctx.new_str(n.into_owned())) + .collect(), + ); let modules = ctx.new_dict(); let prefix = option_env!("RUSTPYTHON_PREFIX").unwrap_or("/usr/local"); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index a0d363046..9acf5e817 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -4,6 +4,7 @@ //! https://github.com/ProgVal/pythonvm-rust/blob/master/src/processor/mod.rs //! +use std::borrow::Cow; use std::cell::{Cell, Ref, RefCell}; use std::collections::{HashMap, HashSet}; use std::fmt; @@ -115,7 +116,7 @@ pub(crate) mod thread { pub struct PyGlobalState { pub settings: PySettings, - pub stdlib_inits: HashMap, + pub stdlib_inits: stdlib::StdlibMap, pub frozen: HashMap, pub stacksize: AtomicCell, pub thread_count: AtomicCell, @@ -361,10 +362,13 @@ impl VirtualMachine { } /// Can only be used in the initialization closure passed to [`Interpreter::new_with_init`] - pub fn add_native_module(&mut self, name: String, module: stdlib::StdlibInitFunc) { + pub fn add_native_module(&mut self, name: S, module: stdlib::StdlibInitFunc) + where + S: Into>, + { let state = PyRc::get_mut(&mut self.state) .expect("can't add_native_module when there are multiple threads"); - state.stdlib_inits.insert(name, module); + state.stdlib_inits.insert(name.into(), module); } /// Can only be used in the initialization closure passed to [`Interpreter::new_with_init`]