Merge pull request #2559 from RustPython/coolreader18/moduless

Add some os-related modules and improve stdlib_inits
This commit is contained in:
Noah
2021-04-21 22:25:40 -05:00
committed by GitHub
21 changed files with 662 additions and 239 deletions

2
Cargo.lock generated
View File

@@ -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",

View File

@@ -172,7 +172,7 @@ fn generate_field((i, field): (usize, &Field)) -> Result<TokenStream2, Diagnosti
::rustpython_vm::function::ArgumentError::TooFewArgs
},
ParameterKind::KeywordOnly => quote! {
::rustpython_vm::function::ArgumentError::RequiredKeywordArgument(#pyname)
::rustpython_vm::function::ArgumentError::RequiredKeywordArgument(#pyname.to_owned())
},
ParameterKind::Flatten => unreachable!(),
};

View File

@@ -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)
}

View File

@@ -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();

View File

@@ -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<Self> {
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(),
}
}
}

View File

@@ -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]

View File

@@ -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
}
};
}

View File

@@ -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<PyTupleRef> {
fn into_struct_sequence(self, vm: &VirtualMachine) -> PyTupleRef {
self.into_tuple(vm)
.into_ref_with_type(vm, Self::static_type().clone())
.unwrap()
}
#[pymethod(magic)]

109
vm/src/stdlib/fcntl.rs Normal file
View File

@@ -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<Either<BufOrStr, int::PyIntRef>>,
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<Either<Either<PyRwBytesLike, BufOrStr>, i32>>,
mutate_flag: OptionalArg<bool>,
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))
}
}
}
}

View File

@@ -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<py_dyn_fn!(dyn Fn(&VirtualMachine) -> PyObjectRef)>;
pub fn get_module_inits() -> HashMap<String, StdlibInitFunc, ahash::RandomState> {
#[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<Cow<'static, str>, 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
}

View File

@@ -475,13 +475,6 @@ mod _os {
Ok(vm.ctx.new_int(written as u64))
}
#[pyfunction]
fn error(message: OptionalArg<PyStrRef>, 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<Self> {
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<UnameResult> {
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<i32>, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
fn get_terminal_size(
fd: OptionalArg<i32>,
vm: &VirtualMachine,
) -> PyResult<super::_os::PyTerminalSize> {
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<i32>, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
fn get_terminal_size(
fd: OptionalArg<i32>,
vm: &VirtualMachine,
) -> PyResult<super::_os::PyTerminalSize> {
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")]

View File

@@ -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<User> for Passwd {
}
}
fn pwd_getpwnam(name: PyStrRef, vm: &VirtualMachine) -> PyResult {
fn pwd_getpwnam(name: PyStrRef, vm: &VirtualMachine) -> PyResult<Passwd> {
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<Passwd> {
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() };

172
vm/src/stdlib/resource.rs Normal file
View File

@@ -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<libc::rusage> 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<Rusage> {
let res = unsafe {
let mut rusage = mem::MaybeUninit::<libc::rusage>::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<Self> {
let seq = vm.extract_elements::<libc::rlim_t>(&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<Limits> {
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::<libc::rlimit>::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),
})
}
}

View File

@@ -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)
}

View File

@@ -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<WakeupFd> {
// 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::<i32>() 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, {

View File

@@ -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()) };
})
}
}

View File

@@ -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"));

View File

@@ -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,

View File

@@ -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<Either<f64, i64>>, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
fn time_gmtime(secs: OptionalArg<Either<f64, i64>>, vm: &VirtualMachine) -> PyResult<PyStructTime> {
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<Either<f64, i64>>,
vm: &VirtualMachine,
) -> PyResult<PyObjectRef> {
) -> PyResult<PyStructTime> {
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<PyStructTime>, vm: &VirtualMac
Ok(vm.ctx.new_str(formatted_time))
}
fn time_strptime(string: PyStrRef, format: OptionalArg<PyStrRef>, vm: &VirtualMachine) -> PyResult {
fn time_strptime(
string: PyStrRef,
format: OptionalArg<PyStrRef>,
vm: &VirtualMachine,
) -> PyResult<PyStructTime> {
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<PyTupleRef> {
fn tp_new(_cls: PyTypeRef, seq: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
// 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)
}
}

View File

@@ -319,7 +319,7 @@ fn sys_getwindowsversion(vm: &VirtualMachine) -> PyResult<crate::builtins::tuple
sp.into_string()
.map_err(|_| vm.new_os_error("service pack is not ASCII".to_owned()))?
};
WindowsVersion {
Ok(WindowsVersion {
major: version.dwMajorVersion,
minor: version.dwMinorVersion,
build: version.dwBuildNumber,
@@ -335,7 +335,7 @@ fn sys_getwindowsversion(vm: &VirtualMachine) -> PyResult<crate::builtins::tuple
version.dwBuildNumber,
), // TODO Provide accurate version, like CPython impl
}
.into_struct_sequence(vm)
.into_struct_sequence(vm))
}
pub fn get_stdin(vm: &VirtualMachine) -> 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<String> = 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");

View File

@@ -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<String, stdlib::StdlibInitFunc, ahash::RandomState>,
pub stdlib_inits: stdlib::StdlibMap,
pub frozen: HashMap<String, code::FrozenModule, ahash::RandomState>,
pub stacksize: AtomicCell<usize>,
pub thread_count: AtomicCell<usize>,
@@ -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<S>(&mut self, name: S, module: stdlib::StdlibInitFunc)
where
S: Into<Cow<'static, str>>,
{
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`]