mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Implement Py_mod_create slot support in multi-phase init
This commit is contained in:
1 parent
bc02b2318c
commit
20376451eb
62 files changed
+964
-906
No files matched your search
@@ -24,10 +24,9 @@ fn bench_rustpython_code(b: &mut Bencher, name: &str, source: &str) {
|
||||
settings.path_list.push("Lib/".to_string());
|
||||
settings.write_bytecode = false;
|
||||
settings.user_site_directory = false;
|
||||
Interpreter::with_init(settings, |vm| {
|
||||
vm.add_native_modules(rustpython_stdlib::get_module_inits());
|
||||
})
|
||||
.enter(|vm| {
|
||||
let builder = Interpreter::builder(settings);
|
||||
let defs = rustpython_stdlib::stdlib_module_defs(&builder.ctx);
|
||||
builder.add_native_modules(&defs).build().enter(|vm| {
|
||||
// Note: bench_cpython is both compiling and executing the code.
|
||||
// As such we compile the code in the benchmark loop as well.
|
||||
b.iter(|| {
|
||||
|
||||
@@ -113,10 +113,10 @@ fn bench_rustpython_code(group: &mut BenchmarkGroup<WallTime>, bench: &MicroBenc
|
||||
settings.write_bytecode = false;
|
||||
settings.user_site_directory = false;
|
||||
|
||||
Interpreter::with_init(settings, |vm| {
|
||||
vm.add_native_module_defs(rustpython_stdlib::get_module_defs());
|
||||
})
|
||||
.enter(|vm| {
|
||||
let builder = Interpreter::builder(settings);
|
||||
let defs = rustpython_stdlib::stdlib_module_defs(&builder.ctx);
|
||||
let interp = builder.add_native_modules(&defs).build();
|
||||
interp.enter(|vm| {
|
||||
let setup_code = vm
|
||||
.compile(&bench.setup, Mode::Exec, bench.name.to_owned())
|
||||
.expect("Error compiling setup code");
|
||||
|
||||
@@ -5,8 +5,10 @@ use alloc::fmt;
|
||||
use core::cmp;
|
||||
use std::{ffi, io};
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::fd::AsFd;
|
||||
#[cfg(not(windows))]
|
||||
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||
use std::os::fd::{AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::BorrowedHandle;
|
||||
|
||||
|
||||
@@ -144,18 +144,6 @@ pub fn impl_pymodule(attr: PunctuatedNestedMeta, module_item: Item) -> Result<To
|
||||
})
|
||||
}
|
||||
},
|
||||
parse_quote! {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn make_module(
|
||||
vm: &::rustpython_vm::VirtualMachine
|
||||
) -> ::rustpython_vm::PyRef<::rustpython_vm::builtins::PyModule> {
|
||||
use ::rustpython_vm::PyPayload;
|
||||
let module = ::rustpython_vm::builtins::PyModule::from_def(module_def(&vm.ctx)).into_ref(&vm.ctx);
|
||||
__init_dict(vm, &module);
|
||||
module_exec(vm, &module).unwrap();
|
||||
module
|
||||
}
|
||||
},
|
||||
]);
|
||||
}
|
||||
if !is_submodule && !context.has_module_exec {
|
||||
@@ -202,7 +190,6 @@ pub fn impl_pymodule(attr: PunctuatedNestedMeta, module_item: Item) -> Result<To
|
||||
vm: &::rustpython_vm::VirtualMachine,
|
||||
module: &::rustpython_vm::Py<::rustpython_vm::builtins::PyModule>,
|
||||
) {
|
||||
module.__init_methods(vm).unwrap();
|
||||
__init_attributes(vm, module);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -144,7 +144,7 @@ pub fn pyexception(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
||||
/// This attribute must be applied to an inline module.
|
||||
/// It defines a Python module in the form of a `module_def` function in the module;
|
||||
/// this has to be used in a `get_module_defs` to properly register the module.
|
||||
/// this has to be used in a `add_native_module` to properly register the module.
|
||||
/// Additionally, this macro defines 'MODULE_NAME' and 'DOC' in the module.
|
||||
/// # Arguments
|
||||
/// - `name`: the name of the python module,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
pub(crate) use opcode::module_def;
|
||||
pub(crate) use _opcode::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod opcode {
|
||||
mod _opcode {
|
||||
use crate::vm::{
|
||||
AsObject, PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::{PyInt, PyIntRef},
|
||||
@@ -8,10 +8,10 @@
|
||||
// spell-checker:ignore cantlock commithook foreignkey notnull primarykey gettemppath autoindex convpath
|
||||
// spell-checker:ignore dbmoved vnode nbytes
|
||||
|
||||
pub(crate) use _sqlite::module_def;
|
||||
pub(crate) use _sqlite3::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod _sqlite {
|
||||
mod _sqlite3 {
|
||||
use libsqlite3_sys::{
|
||||
SQLITE_BLOB, SQLITE_DETERMINISTIC, SQLITE_FLOAT, SQLITE_INTEGER, SQLITE_NULL,
|
||||
SQLITE_OPEN_CREATE, SQLITE_OPEN_READWRITE, SQLITE_OPEN_URI, SQLITE_TEXT, SQLITE_TRACE_STMT,
|
||||
+81
-125
@@ -38,10 +38,10 @@ mod json;
|
||||
#[cfg(not(any(target_os = "ios", target_arch = "wasm32")))]
|
||||
mod locale;
|
||||
|
||||
mod _opcode;
|
||||
mod math;
|
||||
#[cfg(any(unix, windows))]
|
||||
mod mmap;
|
||||
mod opcode;
|
||||
mod pyexpat;
|
||||
mod pystruct;
|
||||
mod random;
|
||||
@@ -65,6 +65,11 @@ mod posixshmem;
|
||||
#[cfg(unix)]
|
||||
mod posixsubprocess;
|
||||
// libc is missing constants on redox
|
||||
#[cfg(all(
|
||||
feature = "sqlite",
|
||||
not(any(target_os = "android", target_arch = "wasm32"))
|
||||
))]
|
||||
mod _sqlite3;
|
||||
#[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))]
|
||||
mod grp;
|
||||
#[cfg(windows)]
|
||||
@@ -75,11 +80,6 @@ mod resource;
|
||||
mod scproxy;
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
mod select;
|
||||
#[cfg(all(
|
||||
feature = "sqlite",
|
||||
not(any(target_os = "android", target_arch = "wasm32"))
|
||||
))]
|
||||
mod sqlite;
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "ssl-openssl"))]
|
||||
mod openssl;
|
||||
@@ -105,131 +105,87 @@ mod tkinter;
|
||||
use rustpython_common as common;
|
||||
use rustpython_vm as vm;
|
||||
|
||||
use crate::vm::{builtins, stdlib::StdlibDefFunc};
|
||||
use alloc::borrow::Cow;
|
||||
use crate::vm::{Context, builtins};
|
||||
|
||||
/// Returns module definitions for multi-phase init modules.
|
||||
/// These modules are added to sys.modules BEFORE their exec function runs,
|
||||
/// allowing safe circular imports.
|
||||
pub fn get_module_defs() -> impl Iterator<Item = (Cow<'static, str>, StdlibDefFunc)> {
|
||||
macro_rules! modules {
|
||||
{
|
||||
$(
|
||||
#[cfg($cfg:meta)]
|
||||
{ $( $key:expr => $val:expr),* $(,)? }
|
||||
)*
|
||||
} => {{
|
||||
[
|
||||
$(
|
||||
$(#[cfg($cfg)] (Cow::<'static, str>::from($key), $val as StdlibDefFunc),)*
|
||||
)*
|
||||
]
|
||||
.into_iter()
|
||||
}};
|
||||
}
|
||||
modules! {
|
||||
#[cfg(all())]
|
||||
{
|
||||
"array" => array::module_def,
|
||||
"binascii" => binascii::module_def,
|
||||
"_bisect" => bisect::module_def,
|
||||
"_blake2" => blake2::module_def,
|
||||
"_bz2" => bz2::module_def,
|
||||
"cmath" => cmath::module_def,
|
||||
"_contextvars" => contextvars::module_def,
|
||||
"_csv" => csv::module_def,
|
||||
"faulthandler" => faulthandler::module_def,
|
||||
"gc" => gc::module_def,
|
||||
"_hashlib" => hashlib::module_def,
|
||||
"_json" => json::module_def,
|
||||
"math" => math::module_def,
|
||||
"_md5" => md5::module_def,
|
||||
"_opcode" => opcode::module_def,
|
||||
"_random" => random::module_def,
|
||||
"_sha1" => sha1::module_def,
|
||||
"_sha256" => sha256::module_def,
|
||||
"_sha3" => sha3::module_def,
|
||||
"_sha512" => sha512::module_def,
|
||||
"_statistics" => statistics::module_def,
|
||||
"_struct" => pystruct::module_def,
|
||||
"_suggestions" => suggestions::module_def,
|
||||
"zlib" => zlib::module_def,
|
||||
"pyexpat" => pyexpat::module_def,
|
||||
"unicodedata" => unicodedata::module_def,
|
||||
}
|
||||
pub fn stdlib_module_defs(ctx: &Context) -> Vec<&'static builtins::PyModuleDef> {
|
||||
vec![
|
||||
_opcode::module_def(ctx),
|
||||
array::module_def(ctx),
|
||||
binascii::module_def(ctx),
|
||||
bisect::module_def(ctx),
|
||||
blake2::module_def(ctx),
|
||||
bz2::module_def(ctx),
|
||||
cmath::module_def(ctx),
|
||||
contextvars::module_def(ctx),
|
||||
csv::module_def(ctx),
|
||||
faulthandler::module_def(ctx),
|
||||
#[cfg(any(unix, target_os = "wasi"))]
|
||||
{
|
||||
"fcntl" => fcntl::module_def,
|
||||
}
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
{
|
||||
"select" => select::module_def,
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
"_multiprocessing" => multiprocessing::module_def,
|
||||
"_socket" => socket::module_def,
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_arch = "wasm32")))]
|
||||
{
|
||||
"_lzma" => lzma::module_def,
|
||||
}
|
||||
#[cfg(all(feature = "sqlite", not(any(target_os = "android", target_arch = "wasm32"))))]
|
||||
{
|
||||
"_sqlite3" => sqlite::module_def,
|
||||
}
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "ssl-rustls"))]
|
||||
{
|
||||
"_ssl" => ssl::module_def,
|
||||
}
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "ssl-openssl"))]
|
||||
{
|
||||
"_ssl" => openssl::module_def,
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
"_overlapped" => overlapped::module_def,
|
||||
}
|
||||
#[cfg(unix)]
|
||||
{
|
||||
"_posixsubprocess" => posixsubprocess::module_def,
|
||||
}
|
||||
#[cfg(all(unix, not(target_os = "redox"), not(target_os = "android")))]
|
||||
{
|
||||
"_posixshmem" => posixshmem::module_def,
|
||||
}
|
||||
#[cfg(any(unix, windows))]
|
||||
{
|
||||
"mmap" => mmap::module_def,
|
||||
}
|
||||
#[cfg(all(unix, not(target_os = "redox")))]
|
||||
{
|
||||
"syslog" => syslog::module_def,
|
||||
"resource" => resource::module_def,
|
||||
}
|
||||
#[cfg(all(unix, not(any(target_os = "ios", target_os = "redox"))))]
|
||||
{
|
||||
"termios" => termios::module_def,
|
||||
}
|
||||
fcntl::module_def(ctx),
|
||||
gc::module_def(ctx),
|
||||
#[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))]
|
||||
{
|
||||
"grp" => grp::module_def,
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
"_scproxy" => scproxy::module_def,
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "windows", target_arch = "wasm32", target_os = "redox")))]
|
||||
{
|
||||
"_uuid" => uuid::module_def,
|
||||
}
|
||||
grp::module_def(ctx),
|
||||
hashlib::module_def(ctx),
|
||||
json::module_def(ctx),
|
||||
#[cfg(not(any(target_os = "ios", target_arch = "wasm32")))]
|
||||
{
|
||||
"_locale" => locale::module_def,
|
||||
}
|
||||
locale::module_def(ctx),
|
||||
#[cfg(not(any(target_os = "android", target_arch = "wasm32")))]
|
||||
lzma::module_def(ctx),
|
||||
math::module_def(ctx),
|
||||
md5::module_def(ctx),
|
||||
#[cfg(any(unix, windows))]
|
||||
mmap::module_def(ctx),
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
multiprocessing::module_def(ctx),
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "ssl-openssl"))]
|
||||
openssl::module_def(ctx),
|
||||
#[cfg(windows)]
|
||||
overlapped::module_def(ctx),
|
||||
#[cfg(unix)]
|
||||
posixsubprocess::module_def(ctx),
|
||||
#[cfg(all(unix, not(target_os = "redox"), not(target_os = "android")))]
|
||||
posixshmem::module_def(ctx),
|
||||
pyexpat::module_def(ctx),
|
||||
pystruct::module_def(ctx),
|
||||
random::module_def(ctx),
|
||||
#[cfg(all(unix, not(target_os = "redox")))]
|
||||
resource::module_def(ctx),
|
||||
#[cfg(target_os = "macos")]
|
||||
scproxy::module_def(ctx),
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
select::module_def(ctx),
|
||||
sha1::module_def(ctx),
|
||||
sha256::module_def(ctx),
|
||||
sha3::module_def(ctx),
|
||||
sha512::module_def(ctx),
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
socket::module_def(ctx),
|
||||
#[cfg(all(
|
||||
feature = "sqlite",
|
||||
not(any(target_os = "android", target_arch = "wasm32"))
|
||||
))]
|
||||
_sqlite3::module_def(ctx),
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "ssl-rustls"))]
|
||||
ssl::module_def(ctx),
|
||||
statistics::module_def(ctx),
|
||||
suggestions::module_def(ctx),
|
||||
#[cfg(all(unix, not(target_os = "redox")))]
|
||||
syslog::module_def(ctx),
|
||||
#[cfg(all(unix, not(any(target_os = "ios", target_os = "redox"))))]
|
||||
termios::module_def(ctx),
|
||||
#[cfg(feature = "tkinter")]
|
||||
{
|
||||
"_tkinter" => tkinter::module_def,
|
||||
}
|
||||
}
|
||||
tkinter::module_def(ctx),
|
||||
unicodedata::module_def(ctx),
|
||||
#[cfg(not(any(
|
||||
target_os = "android",
|
||||
target_os = "ios",
|
||||
target_os = "windows",
|
||||
target_arch = "wasm32",
|
||||
target_os = "redox"
|
||||
)))]
|
||||
uuid::module_def(ctx),
|
||||
zlib::module_def(ctx),
|
||||
]
|
||||
}
|
||||
@@ -33,20 +33,18 @@ use std::sync::LazyLock;
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(openssl_vendored)] {
|
||||
static PROBE: LazyLock<ProbeResult> = LazyLock::new(openssl_probe::probe);
|
||||
fn probe() -> &'static ProbeResult { &PROBE }
|
||||
} else {
|
||||
static EMPTY_PROBE: LazyLock<ProbeResult> = LazyLock::new(|| ProbeResult { cert_file: None, cert_dir: vec![] });
|
||||
fn probe() -> &'static ProbeResult {
|
||||
&EMPTY_PROBE
|
||||
}
|
||||
static PROBE: LazyLock<ProbeResult> = LazyLock::new(|| ProbeResult { cert_file: None, cert_dir: vec![] });
|
||||
}
|
||||
}
|
||||
|
||||
fn probe() -> &'static ProbeResult {
|
||||
&PROBE
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[pymodule(with(cert::ssl_cert, ssl_error::ssl_error, ossl101, ossl111, windows))]
|
||||
mod _ssl {
|
||||
#[cfg(openssl_vendored)]
|
||||
use super::PROBE;
|
||||
use super::{bio, probe};
|
||||
|
||||
// Import error types and helpers used in this module (others are exposed via pymodule(with(...)))
|
||||
@@ -103,7 +101,7 @@ mod _ssl {
|
||||
// if openssl is vendored, it doesn't know the locations
|
||||
// of system certificates - cache the probe result now.
|
||||
#[cfg(openssl_vendored)]
|
||||
std::sync::LazyLock::force(&PROBE);
|
||||
std::sync::LazyLock::force(&super::PROBE);
|
||||
|
||||
__module_exec(vm, module);
|
||||
Ok(())
|
||||
|
||||
@@ -42,6 +42,7 @@ mod _pyexpat {
|
||||
use crate::vm::{
|
||||
Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
|
||||
builtins::{PyBytesRef, PyModule, PyStr, PyStrRef, PyType},
|
||||
extend_module,
|
||||
function::ArgBytesLike,
|
||||
function::{Either, IntoFuncArgs, OptionalArg},
|
||||
};
|
||||
@@ -53,10 +54,13 @@ mod _pyexpat {
|
||||
__module_exec(vm, module);
|
||||
|
||||
// Add submodules
|
||||
let model = PyModule::from_def(super::_model::module_def(&vm.ctx)).into_ref(&vm.ctx);
|
||||
let errors = PyModule::from_def(super::_errors::module_def(&vm.ctx)).into_ref(&vm.ctx);
|
||||
module.set_attr("model", model, vm)?;
|
||||
module.set_attr("errors", errors, vm)?;
|
||||
let model = super::_model::module_def(&vm.ctx).create_module(vm)?;
|
||||
let errors = super::_errors::module_def(&vm.ctx).create_module(vm)?;
|
||||
|
||||
extend_module!(vm, module, {
|
||||
"model" => model,
|
||||
"errors" => errors,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -41,6 +41,46 @@ impl core::fmt::Debug for PyModuleSlots {
|
||||
}
|
||||
}
|
||||
|
||||
impl PyModuleDef {
|
||||
/// Create a module from this definition (Phase 1 of multi-phase init).
|
||||
///
|
||||
/// This performs:
|
||||
/// 1. Create module object (using create slot if provided)
|
||||
/// 2. Initialize module dict from def
|
||||
/// 3. Add methods to module
|
||||
///
|
||||
/// Does NOT add to sys.modules or call exec slot.
|
||||
pub fn create_module(&'static self, vm: &VirtualMachine) -> PyResult<PyRef<PyModule>> {
|
||||
use crate::PyPayload;
|
||||
|
||||
// Create module (use create slot if provided, else default creation)
|
||||
let module = if let Some(create) = self.slots.create {
|
||||
// Custom module creation
|
||||
let spec = vm.ctx.new_str(self.name.as_str());
|
||||
create(vm, spec.as_object(), self)?
|
||||
} else {
|
||||
// Default module creation
|
||||
PyModule::from_def(self).into_ref(&vm.ctx)
|
||||
};
|
||||
|
||||
// Initialize module dict and methods
|
||||
PyModule::__init_dict_from_def(vm, &module);
|
||||
module.__init_methods(vm)?;
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
/// Execute the module's exec slot (Phase 2 of multi-phase init).
|
||||
///
|
||||
/// Calls the exec slot if present. Returns Ok(()) if no exec slot.
|
||||
pub fn exec_module(&'static self, vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
|
||||
if let Some(exec) = self.slots.exec {
|
||||
exec(vm, module)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)] // avoid Default implementation
|
||||
#[pyclass(module = false, name = "module")]
|
||||
#[derive(Debug)]
|
||||
|
||||
+12
-23
@@ -87,9 +87,6 @@ pub fn import_frozen(vm: &VirtualMachine, module_name: &str) -> PyResult {
|
||||
}
|
||||
|
||||
pub fn import_builtin(vm: &VirtualMachine, module_name: &str) -> PyResult {
|
||||
use crate::PyPayload;
|
||||
use crate::builtins::PyModule;
|
||||
|
||||
let sys_modules = vm.sys_module.get_attr("modules", vm)?;
|
||||
|
||||
// Check if already in sys.modules (handles recursive imports)
|
||||
@@ -98,36 +95,28 @@ pub fn import_builtin(vm: &VirtualMachine, module_name: &str) -> PyResult {
|
||||
}
|
||||
|
||||
// Try multi-phase init first (preferred for modules that import other modules)
|
||||
if let Some(def_func) = vm.state.module_defs.get(module_name) {
|
||||
let def = def_func(&vm.ctx);
|
||||
|
||||
// Phase 1: Create module from definition
|
||||
let module = PyModule::from_def(def).into_ref(&vm.ctx);
|
||||
|
||||
// Initialize module dict using proper method
|
||||
PyModule::__init_dict_from_def(vm, &module);
|
||||
if let Some(&def) = vm.state.module_defs.get(module_name) {
|
||||
// Phase 1: Create and initialize module
|
||||
let module = def.create_module(vm)?;
|
||||
|
||||
// Add to sys.modules BEFORE exec (critical for circular import handling)
|
||||
sys_modules.set_item(module_name, module.clone().into(), vm)?;
|
||||
|
||||
// Phase 2: Call exec slot (can safely import other modules now)
|
||||
if let Some(exec) = def.slots.exec {
|
||||
exec(vm, &module)?;
|
||||
// If exec fails, remove the partially-initialized module from sys.modules
|
||||
if let Err(e) = def.exec_module(vm, &module) {
|
||||
let _ = sys_modules.del_item(module_name, vm);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
return Ok(module.into());
|
||||
}
|
||||
|
||||
// Fall back to legacy single-phase init
|
||||
let make_module_func = vm.state.module_inits.get(module_name).ok_or_else(|| {
|
||||
vm.new_import_error(
|
||||
format!("Cannot import builtin module {module_name}"),
|
||||
vm.ctx.new_str(module_name),
|
||||
)
|
||||
})?;
|
||||
let module = make_module_func(vm);
|
||||
sys_modules.set_item(module_name, module.as_object().to_owned(), vm)?;
|
||||
Ok(module.into())
|
||||
// Module not found in module_defs
|
||||
Err(vm.new_import_error(
|
||||
format!("Cannot import builtin module {module_name}"),
|
||||
vm.ctx.new_str(module_name),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
|
||||
@@ -99,7 +99,7 @@ pub use self::object::{
|
||||
AsObject, Py, PyAtomicRef, PyExact, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact,
|
||||
PyResult, PyWeakRef,
|
||||
};
|
||||
pub use self::vm::{Context, Interpreter, Settings, VirtualMachine};
|
||||
pub use self::vm::{Context, Interpreter, InterpreterBuilder, Settings, VirtualMachine};
|
||||
|
||||
pub use rustpython_common as common;
|
||||
pub use rustpython_compiler_core::{bytecode, frozen};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! This module provides the C implementation of Abstract Base Classes (ABCs)
|
||||
//! as defined in PEP 3119.
|
||||
|
||||
pub(crate) use _abc::make_module;
|
||||
pub(crate) use _abc::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod _abc {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
//! `ast` standard module for abstract syntax trees.
|
||||
|
||||
//!
|
||||
//! This module makes use of the parser logic, and translates all ast nodes
|
||||
//! into python ast.AST objects.
|
||||
|
||||
pub(crate) use python::_ast::module_def;
|
||||
|
||||
mod pyast;
|
||||
|
||||
use crate::builtins::{PyInt, PyStr};
|
||||
use crate::stdlib::ast::module::{Mod, ModInteractive};
|
||||
use crate::stdlib::ast::node::BoxedSlice;
|
||||
use crate::stdlib::ast::python::_ast;
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
|
||||
TryFromObject, VirtualMachine,
|
||||
@@ -365,9 +367,3 @@ pub const PY_COMPILE_FLAGS_MASK: i32 = PY_COMPILE_FLAG_AST_ONLY
|
||||
| CO_FUTURE_BARRY_AS_BDFL
|
||||
| CO_FUTURE_GENERATOR_STOP
|
||||
| CO_FUTURE_ANNOTATIONS;
|
||||
|
||||
pub fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
let module = _ast::make_module(vm);
|
||||
pyast::extend_module_nodes(vm, &module);
|
||||
module
|
||||
}
|
||||
@@ -96,4 +96,13 @@ pub(crate) mod _ast {
|
||||
|
||||
#[pyattr(name = "PyCF_TYPE_COMMENTS")]
|
||||
use super::PY_CF_TYPE_COMMENTS;
|
||||
|
||||
pub(crate) fn module_exec(
|
||||
vm: &VirtualMachine,
|
||||
module: &Py<crate::builtins::PyModule>,
|
||||
) -> PyResult<()> {
|
||||
__module_exec(vm, module);
|
||||
super::super::pyast::extend_module_nodes(vm, module);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
pub use atexit::_run_exitfuncs;
|
||||
pub(crate) use atexit::make_module;
|
||||
pub(crate) use atexit::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod atexit {
|
||||
|
||||
@@ -1100,6 +1100,7 @@ pub fn init_module(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
|
||||
crate::protocol::VecBuffer::make_class(&vm.ctx);
|
||||
|
||||
module.__init_methods(vm).unwrap();
|
||||
builtins::module_exec(vm, module).unwrap();
|
||||
|
||||
let debug_mode: bool = vm.state.config.settings.optimize == 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub(crate) use _codecs::make_module;
|
||||
pub(crate) use _codecs::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod _codecs {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub(crate) use _collections::make_module;
|
||||
pub(crate) use _collections::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod _collections {
|
||||
|
||||
@@ -10,8 +10,8 @@ mod structure;
|
||||
mod union;
|
||||
|
||||
use crate::{
|
||||
AsObject, Py, PyObjectRef, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyModule, PyStr, PyType},
|
||||
AsObject, Py, PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::{PyStr, PyType},
|
||||
class::PyClassImpl,
|
||||
types::TypeDataRef,
|
||||
};
|
||||
@@ -85,32 +85,9 @@ impl PyType {
|
||||
}
|
||||
|
||||
// Dynamic type check helpers for PyCData
|
||||
// These check if an object's type's metaclass is a subclass of a specific metaclass
|
||||
pub(crate) use _ctypes::module_def;
|
||||
|
||||
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
let module = _ctypes::make_module(vm);
|
||||
let ctx = &vm.ctx;
|
||||
PyCSimpleType::make_class(ctx);
|
||||
array::PyCArrayType::make_class(ctx);
|
||||
pointer::PyCPointerType::make_class(ctx);
|
||||
structure::PyCStructType::make_class(ctx);
|
||||
union::PyCUnionType::make_class(ctx);
|
||||
function::PyCFuncPtrType::make_class(ctx);
|
||||
extend_module!(vm, &module, {
|
||||
"_CData" => PyCData::make_class(ctx),
|
||||
"_SimpleCData" => PyCSimple::make_class(ctx),
|
||||
"Array" => PyCArray::make_class(ctx),
|
||||
"CField" => PyCField::make_class(ctx),
|
||||
"CFuncPtr" => function::PyCFuncPtr::make_class(ctx),
|
||||
"_Pointer" => PyCPointer::make_class(ctx),
|
||||
"_pointer_type_cache" => ctx.new_dict(),
|
||||
"_array_type_cache" => ctx.new_dict(),
|
||||
"Structure" => PyCStructure::make_class(ctx),
|
||||
"CThunkObject" => function::PyCThunk::make_class(ctx),
|
||||
"Union" => PyCUnion::make_class(ctx),
|
||||
});
|
||||
module
|
||||
}
|
||||
// These check if an object's type's metaclass is a subclass of a specific metaclass
|
||||
|
||||
/// Size of long double - platform dependent
|
||||
/// x86_64 macOS/Linux: 16 bytes (80-bit extended + padding)
|
||||
@@ -1300,4 +1277,37 @@ pub(crate) mod _ctypes {
|
||||
|
||||
Ok(S_OK)
|
||||
}
|
||||
|
||||
pub(crate) fn module_exec(
|
||||
vm: &VirtualMachine,
|
||||
module: &Py<crate::builtins::PyModule>,
|
||||
) -> PyResult<()> {
|
||||
use super::*;
|
||||
|
||||
__module_exec(vm, module);
|
||||
|
||||
let ctx = &vm.ctx;
|
||||
PyCSimpleType::make_class(ctx);
|
||||
array::PyCArrayType::make_class(ctx);
|
||||
pointer::PyCPointerType::make_class(ctx);
|
||||
structure::PyCStructType::make_class(ctx);
|
||||
union::PyCUnionType::make_class(ctx);
|
||||
function::PyCFuncPtrType::make_class(ctx);
|
||||
|
||||
extend_module!(vm, module, {
|
||||
"_CData" => PyCData::make_class(ctx),
|
||||
"_SimpleCData" => PyCSimple::make_class(ctx),
|
||||
"Array" => PyCArray::make_class(ctx),
|
||||
"CField" => PyCField::make_class(ctx),
|
||||
"CFuncPtr" => function::PyCFuncPtr::make_class(ctx),
|
||||
"_Pointer" => PyCPointer::make_class(ctx),
|
||||
"_pointer_type_cache" => ctx.new_dict(),
|
||||
"_array_type_cache" => ctx.new_dict(),
|
||||
"Structure" => PyCStructure::make_class(ctx),
|
||||
"CThunkObject" => function::PyCThunk::make_class(ctx),
|
||||
"Union" => PyCUnion::make_class(ctx),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,26 @@
|
||||
// spell-checker:disable
|
||||
|
||||
use crate::{PyRef, VirtualMachine, builtins::PyModule};
|
||||
pub(crate) use errno_mod::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod errno {}
|
||||
#[pymodule(name = "errno")]
|
||||
mod errno_mod {
|
||||
use crate::{Py, PyResult, VirtualMachine, builtins::PyModule};
|
||||
|
||||
pub fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
let module = errno::make_module(vm);
|
||||
let errorcode = vm.ctx.new_dict();
|
||||
extend_module!(vm, &module, {
|
||||
"errorcode" => errorcode.clone(),
|
||||
});
|
||||
for (name, code) in ERROR_CODES {
|
||||
let name = vm.ctx.intern_str(*name);
|
||||
let code = vm.new_pyobj(*code);
|
||||
errorcode
|
||||
.set_item(&*code, name.to_owned().into(), vm)
|
||||
.unwrap();
|
||||
module.set_attr(name, code, vm).unwrap();
|
||||
pub(crate) fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
|
||||
__module_exec(vm, module);
|
||||
|
||||
let errorcode = vm.ctx.new_dict();
|
||||
extend_module!(vm, module, {
|
||||
"errorcode" => errorcode.clone(),
|
||||
});
|
||||
for (name, code) in super::ERROR_CODES {
|
||||
let name = vm.ctx.intern_str(*name);
|
||||
let code = vm.new_pyobj(*code);
|
||||
errorcode.set_item(&*code, name.to_owned().into(), vm)?;
|
||||
module.set_attr(name, code, vm)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
module
|
||||
}
|
||||
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub(crate) use _functools::make_module;
|
||||
pub(crate) use _functools::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod _functools {
|
||||
|
||||
+15
-20
@@ -1,6 +1,6 @@
|
||||
use crate::frozen::FrozenModule;
|
||||
use crate::{VirtualMachine, builtins::PyBaseExceptionRef};
|
||||
pub(crate) use _imp::make_module;
|
||||
pub(crate) use _imp::module_def;
|
||||
|
||||
pub use crate::vm::resolve_frozen_alias;
|
||||
|
||||
@@ -84,7 +84,7 @@ fn find_frozen(name: &str, vm: &VirtualMachine) -> Result<FrozenModule, FrozenEr
|
||||
#[pymodule(with(lock))]
|
||||
mod _imp {
|
||||
use crate::{
|
||||
PyObjectRef, PyRef, PyResult, VirtualMachine,
|
||||
PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyBytesRef, PyCode, PyMemoryView, PyModule, PyStrRef},
|
||||
function::OptionalArg,
|
||||
import, version,
|
||||
@@ -106,8 +106,7 @@ mod _imp {
|
||||
|
||||
#[pyfunction]
|
||||
fn is_builtin(name: PyStrRef, vm: &VirtualMachine) -> bool {
|
||||
vm.state.module_inits.contains_key(name.as_str())
|
||||
|| vm.state.module_defs.contains_key(name.as_str())
|
||||
vm.state.module_defs.contains_key(name.as_str())
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
@@ -117,9 +116,6 @@ mod _imp {
|
||||
|
||||
#[pyfunction]
|
||||
fn create_builtin(spec: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
use crate::PyPayload;
|
||||
use crate::builtins::PyModule;
|
||||
|
||||
let sys_modules = vm.sys_module.get_attr("modules", vm).unwrap();
|
||||
let name: PyStrRef = spec.get_attr("name", vm)?.try_into_value(vm)?;
|
||||
|
||||
@@ -129,14 +125,20 @@ mod _imp {
|
||||
}
|
||||
|
||||
// Try multi-phase init modules first (they need special handling)
|
||||
if let Some(def_func) = vm.state.module_defs.get(name.as_str()) {
|
||||
let def = def_func(&vm.ctx);
|
||||
if let Some(&def) = vm.state.module_defs.get(name.as_str()) {
|
||||
// Phase 1: Create module (use create slot if provided, else default creation)
|
||||
let module = if let Some(create) = def.slots.create {
|
||||
// Custom module creation
|
||||
create(vm, &spec, def)?
|
||||
} else {
|
||||
// Default module creation
|
||||
PyModule::from_def(def).into_ref(&vm.ctx)
|
||||
};
|
||||
|
||||
// Phase 1: Create module from definition
|
||||
let module = PyModule::from_def(def).into_ref(&vm.ctx);
|
||||
|
||||
// Initialize module dict using proper method
|
||||
// Initialize module dict and methods
|
||||
// Corresponds to PyModule_FromDefAndSpec: md_def, _add_methods_to_object, PyModule_SetDocString
|
||||
PyModule::__init_dict_from_def(vm, &module);
|
||||
module.__init_methods(vm)?;
|
||||
|
||||
// Add to sys.modules BEFORE exec (critical for circular import handling)
|
||||
sys_modules.set_item(&*name, module.clone().into(), vm)?;
|
||||
@@ -149,13 +151,6 @@ mod _imp {
|
||||
return Ok(module.into());
|
||||
}
|
||||
|
||||
// Fall back to legacy single-phase init
|
||||
if let Some(make_module_func) = vm.state.module_inits.get(name.as_str()) {
|
||||
let module = make_module_func(vm);
|
||||
sys_modules.set_item(&*name, module.clone().into(), vm)?;
|
||||
return Ok(module.into());
|
||||
}
|
||||
|
||||
Ok(vm.ctx.none())
|
||||
}
|
||||
|
||||
|
||||
+19
-19
@@ -1,6 +1,8 @@
|
||||
/*
|
||||
* I/O core tools.
|
||||
*/
|
||||
pub(crate) use _io::module_def;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))] {
|
||||
use crate::common::crt_fd::Offset;
|
||||
@@ -19,7 +21,7 @@ cfg_if::cfg_if! {
|
||||
}
|
||||
|
||||
use crate::{
|
||||
PyObjectRef, PyRef, PyResult, TryFromObject, VirtualMachine,
|
||||
PyObjectRef, PyResult, TryFromObject, VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyModule},
|
||||
common::os::ErrorExt,
|
||||
convert::{IntoPyException, ToPyException},
|
||||
@@ -89,23 +91,6 @@ impl IntoPyException for std::io::Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
let ctx = &vm.ctx;
|
||||
|
||||
let module = _io::make_module(vm);
|
||||
|
||||
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
|
||||
fileio::module_exec(vm, &module).unwrap();
|
||||
|
||||
let unsupported_operation = _io::unsupported_operation().to_owned();
|
||||
extend_module!(vm, &module, {
|
||||
"UnsupportedOperation" => unsupported_operation,
|
||||
"BlockingIOError" => ctx.exceptions.blocking_io_error.to_owned(),
|
||||
});
|
||||
|
||||
module
|
||||
}
|
||||
|
||||
// not used on all platforms
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
@@ -4743,8 +4728,23 @@ mod _io {
|
||||
assert_eq!(buffered.getvalue(), data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
|
||||
// Call auto-generated initialization first
|
||||
__module_exec(vm, module);
|
||||
|
||||
// Initialize FileIO types on non-WASM platforms
|
||||
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
|
||||
super::fileio::module_exec(vm, module)?;
|
||||
|
||||
let unsupported_operation = unsupported_operation().to_owned();
|
||||
extend_module!(vm, module, {
|
||||
"UnsupportedOperation" => unsupported_operation,
|
||||
"BlockingIOError" => vm.ctx.exceptions.blocking_io_error.to_owned(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
// disable FileIO on WASM
|
||||
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
|
||||
#[pymodule]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub(crate) use decl::make_module;
|
||||
pub(crate) use decl::module_def;
|
||||
|
||||
#[pymodule(name = "itertools")]
|
||||
mod decl {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// spell-checker:ignore pyfrozen pycomplex
|
||||
pub(crate) use decl::make_module;
|
||||
pub(crate) use decl::module_def;
|
||||
|
||||
#[pymodule(name = "marshal")]
|
||||
mod decl {
|
||||
|
||||
+56
-104
@@ -62,114 +62,66 @@ mod winapi;
|
||||
#[cfg(windows)]
|
||||
mod winreg;
|
||||
|
||||
use crate::{Context, PyRef, VirtualMachine, builtins::PyModule, builtins::PyModuleDef};
|
||||
use alloc::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use crate::{Context, builtins::PyModuleDef};
|
||||
|
||||
/// Legacy single-phase init: function that creates and populates a module in one step
|
||||
pub type StdlibInitFunc = Box<py_dyn_fn!(dyn Fn(&VirtualMachine) -> PyRef<PyModule>)>;
|
||||
|
||||
/// Multi-phase init: function that returns a module definition
|
||||
/// The import machinery will:
|
||||
/// 1. Create module from def
|
||||
/// 2. Add to sys.modules
|
||||
/// 3. Call exec slot (which can safely import other modules)
|
||||
pub type StdlibDefFunc = fn(&Context) -> &'static PyModuleDef;
|
||||
|
||||
pub type StdlibMap = HashMap<Cow<'static, str>, StdlibInitFunc, ahash::RandomState>;
|
||||
pub type StdlibDefMap = HashMap<Cow<'static, str>, StdlibDefFunc, ahash::RandomState>;
|
||||
|
||||
pub fn get_module_inits() -> StdlibMap {
|
||||
macro_rules! modules {
|
||||
{
|
||||
$(
|
||||
#[cfg($cfg:meta)]
|
||||
{ $( $key:expr => $val:expr),* $(,)? }
|
||||
)*
|
||||
} => {{
|
||||
let modules = [
|
||||
$(
|
||||
$(#[cfg($cfg)] (Cow::<'static, str>::from($key), Box::new($val) as StdlibInitFunc),)*
|
||||
)*
|
||||
];
|
||||
modules.into_iter().collect()
|
||||
}};
|
||||
}
|
||||
modules! {
|
||||
#[cfg(all())]
|
||||
{
|
||||
"_abc" => _abc::make_module,
|
||||
"atexit" => atexit::make_module,
|
||||
"_codecs" => codecs::make_module,
|
||||
"_collections" => collections::make_module,
|
||||
"errno" => errno::make_module,
|
||||
"_functools" => functools::make_module,
|
||||
"itertools" => itertools::make_module,
|
||||
"_io" => io::make_module,
|
||||
"marshal" => marshal::make_module,
|
||||
"_operator" => operator::make_module,
|
||||
"_signal" => signal::make_module,
|
||||
"_sre" => sre::make_module,
|
||||
"_stat" => stat::make_module,
|
||||
"_sysconfig" => sysconfig::make_module,
|
||||
"_string" => string::make_module,
|
||||
"time" => time::make_module,
|
||||
"_typing" => typing::make_module,
|
||||
"_weakref" => weakref::make_module,
|
||||
"_imp" => imp::make_module,
|
||||
"_warnings" => warnings::make_module,
|
||||
sys::sysconfigdata_name() => sysconfigdata::make_module,
|
||||
}
|
||||
// parser related modules:
|
||||
/// Returns module definitions for multi-phase init modules.
|
||||
///
|
||||
/// These modules use multi-phase initialization pattern:
|
||||
/// 1. Create module from def and add to sys.modules
|
||||
/// 2. Call exec slot (can safely import other modules without circular import issues)
|
||||
pub fn builtin_module_defs(ctx: &Context) -> Vec<&'static PyModuleDef> {
|
||||
vec![
|
||||
_abc::module_def(ctx),
|
||||
#[cfg(feature = "ast")]
|
||||
{
|
||||
"_ast" => ast::make_module,
|
||||
}
|
||||
// compiler related modules:
|
||||
#[cfg(feature = "compiler")]
|
||||
{
|
||||
"_symtable" => symtable::make_module,
|
||||
}
|
||||
#[cfg(any(unix, target_os = "wasi"))]
|
||||
{
|
||||
"posix" => posix::make_module,
|
||||
// "fcntl" => fcntl::make_module,
|
||||
}
|
||||
#[cfg(feature = "threading")]
|
||||
{
|
||||
"_thread" => thread::make_module,
|
||||
}
|
||||
// Unix-only
|
||||
#[cfg(all(
|
||||
unix,
|
||||
not(any(target_os = "ios", target_os = "wasi", target_os = "redox"))
|
||||
))]
|
||||
{
|
||||
"pwd" => pwd::make_module,
|
||||
}
|
||||
// Windows-only
|
||||
#[cfg(windows)]
|
||||
{
|
||||
"nt" => nt::make_module,
|
||||
"msvcrt" => msvcrt::make_module,
|
||||
"_winapi" => winapi::make_module,
|
||||
"winreg" => winreg::make_module,
|
||||
}
|
||||
ast::module_def(ctx),
|
||||
atexit::module_def(ctx),
|
||||
codecs::module_def(ctx),
|
||||
collections::module_def(ctx),
|
||||
#[cfg(all(
|
||||
any(target_os = "linux", target_os = "macos", target_os = "windows"),
|
||||
not(any(target_env = "musl", target_env = "sgx"))
|
||||
))]
|
||||
{
|
||||
"_ctypes" => ctypes::make_module,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns module definitions for multi-phase init modules.
|
||||
/// These modules use CPython's two-phase initialization pattern:
|
||||
/// 1. Create module from def and add to sys.modules
|
||||
/// 2. Call exec slot (can safely import other modules without circular import issues)
|
||||
pub fn get_module_defs() -> StdlibDefMap {
|
||||
// Currently empty - modules will be migrated to multi-phase init as needed
|
||||
HashMap::default()
|
||||
ctypes::module_def(ctx),
|
||||
errno::module_def(ctx),
|
||||
functools::module_def(ctx),
|
||||
imp::module_def(ctx),
|
||||
io::module_def(ctx),
|
||||
itertools::module_def(ctx),
|
||||
marshal::module_def(ctx),
|
||||
#[cfg(windows)]
|
||||
msvcrt::module_def(ctx),
|
||||
#[cfg(windows)]
|
||||
nt::module_def(ctx),
|
||||
operator::module_def(ctx),
|
||||
#[cfg(any(unix, target_os = "wasi"))]
|
||||
posix::module_def(ctx),
|
||||
#[cfg(all(
|
||||
any(not(target_arch = "wasm32"), target_os = "wasi"),
|
||||
not(any(unix, windows))
|
||||
))]
|
||||
posix::module_def(ctx),
|
||||
#[cfg(all(
|
||||
unix,
|
||||
not(any(target_os = "ios", target_os = "wasi", target_os = "redox"))
|
||||
))]
|
||||
pwd::module_def(ctx),
|
||||
signal::module_def(ctx),
|
||||
sre::module_def(ctx),
|
||||
stat::module_def(ctx),
|
||||
string::module_def(ctx),
|
||||
#[cfg(feature = "compiler")]
|
||||
symtable::module_def(ctx),
|
||||
sysconfigdata::module_def(ctx),
|
||||
sysconfig::module_def(ctx),
|
||||
#[cfg(feature = "threading")]
|
||||
thread::module_def(ctx),
|
||||
time::module_def(ctx),
|
||||
typing::module_def(ctx),
|
||||
warnings::module_def(ctx),
|
||||
weakref::module_def(ctx),
|
||||
#[cfg(windows)]
|
||||
winapi::module_def(ctx),
|
||||
#[cfg(windows)]
|
||||
winreg::module_def(ctx),
|
||||
]
|
||||
}
|
||||
@@ -1,19 +1,12 @@
|
||||
// spell-checker:disable
|
||||
|
||||
use crate::{PyRef, VirtualMachine, builtins::PyModule};
|
||||
|
||||
pub(crate) use module::module_def;
|
||||
pub use module::raw_set_handle_inheritable;
|
||||
|
||||
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
let module = module::make_module(vm);
|
||||
super::os::module_exec(vm, &module);
|
||||
module
|
||||
}
|
||||
|
||||
#[pymodule(name = "nt", with(super::os::_os))]
|
||||
pub(crate) mod module {
|
||||
use crate::{
|
||||
PyResult, TryFromObject, VirtualMachine,
|
||||
Py, PyResult, TryFromObject, VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyDictRef, PyListRef, PyStrRef, PyTupleRef},
|
||||
common::{crt_fd, suppress_iph, windows::ToWideString},
|
||||
convert::ToPyException,
|
||||
@@ -1923,4 +1916,13 @@ pub(crate) mod module {
|
||||
pub(crate) fn support_funcs() -> Vec<SupportFunc> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
pub(crate) fn module_exec(
|
||||
vm: &VirtualMachine,
|
||||
module: &Py<crate::builtins::PyModule>,
|
||||
) -> PyResult<()> {
|
||||
__module_exec(vm, module);
|
||||
super::super::os::module_exec(vm, module)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
pub(crate) use _operator::make_module;
|
||||
pub(crate) use _operator::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod _operator {
|
||||
|
||||
@@ -149,6 +149,8 @@ pub(super) mod _os {
|
||||
use super::{DirFd, FollowSymlinks, SupportFunc};
|
||||
#[cfg(windows)]
|
||||
use crate::common::windows::ToWideString;
|
||||
#[cfg(any(unix, windows))]
|
||||
use crate::utils::ToCString;
|
||||
use crate::{
|
||||
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
|
||||
builtins::{
|
||||
@@ -168,7 +170,6 @@ pub(super) mod _os {
|
||||
protocol::PyIterReturn,
|
||||
recursion::ReprGuard,
|
||||
types::{IterNext, Iterable, PyStructSequence, Representable, SelfIter},
|
||||
utils::ToCString,
|
||||
vm::VirtualMachine,
|
||||
};
|
||||
use core::time::Duration;
|
||||
@@ -1896,21 +1897,21 @@ impl SupportFunc {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
pub fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
|
||||
let support_funcs = _os::support_funcs();
|
||||
let supports_fd = PySet::default().into_ref(&vm.ctx);
|
||||
let supports_dir_fd = PySet::default().into_ref(&vm.ctx);
|
||||
let supports_follow_symlinks = PySet::default().into_ref(&vm.ctx);
|
||||
for support in support_funcs {
|
||||
let func_obj = module.get_attr(support.name, vm).unwrap();
|
||||
let func_obj = module.get_attr(support.name, vm)?;
|
||||
if support.fd.unwrap_or(false) {
|
||||
supports_fd.clone().add(func_obj.clone(), vm).unwrap();
|
||||
supports_fd.clone().add(func_obj.clone(), vm)?;
|
||||
}
|
||||
if support.dir_fd.unwrap_or(false) {
|
||||
supports_dir_fd.clone().add(func_obj.clone(), vm).unwrap();
|
||||
supports_dir_fd.clone().add(func_obj.clone(), vm)?;
|
||||
}
|
||||
if support.follow_symlinks.unwrap_or(false) {
|
||||
supports_follow_symlinks.clone().add(func_obj, vm).unwrap();
|
||||
supports_follow_symlinks.clone().add(func_obj, vm)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1920,6 +1921,8 @@ pub fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
"supports_follow_symlinks" => supports_follow_symlinks,
|
||||
"error" => vm.ctx.exceptions.os_error.to_owned(),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// spell-checker:disable
|
||||
|
||||
use crate::{PyRef, VirtualMachine, builtins::PyModule};
|
||||
use std::os::fd::BorrowedFd;
|
||||
|
||||
pub(crate) use module::module_def;
|
||||
|
||||
pub fn set_inheritable(fd: BorrowedFd<'_>, inheritable: bool) -> nix::Result<()> {
|
||||
use nix::fcntl;
|
||||
let flags = fcntl::FdFlag::from_bits_truncate(fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD)?);
|
||||
@@ -14,12 +15,6 @@ pub fn set_inheritable(fd: BorrowedFd<'_>, inheritable: bool) -> nix::Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
let module = module::make_module(vm);
|
||||
super::os::module_exec(vm, &module);
|
||||
module
|
||||
}
|
||||
|
||||
#[pymodule(name = "posix", with(super::os::_os))]
|
||||
pub mod module {
|
||||
use crate::{
|
||||
@@ -2606,4 +2601,13 @@ pub mod module {
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
pub(crate) fn module_exec(
|
||||
vm: &VirtualMachine,
|
||||
module: &Py<crate::builtins::PyModule>,
|
||||
) -> PyResult<()> {
|
||||
__module_exec(vm, module);
|
||||
super::super::os::module_exec(vm, module)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,13 @@
|
||||
// spell-checker:disable
|
||||
|
||||
//! `posix` compatible module for `not(any(unix, windows))`
|
||||
use crate::{PyRef, VirtualMachine, builtins::PyModule};
|
||||
|
||||
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
let module = module::make_module(vm);
|
||||
super::os::module_exec(vm, &module);
|
||||
module
|
||||
}
|
||||
pub(crate) use module::module_def;
|
||||
|
||||
#[pymodule(name = "posix", with(super::os::_os))]
|
||||
pub(crate) mod module {
|
||||
use crate::{
|
||||
PyObjectRef, PyResult, VirtualMachine,
|
||||
Py, PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::PyStrRef,
|
||||
convert::IntoPyException,
|
||||
ospath::OsPath,
|
||||
@@ -71,4 +66,13 @@ pub(crate) mod module {
|
||||
pub(crate) fn support_funcs() -> Vec<SupportFunc> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
pub(crate) fn module_exec(
|
||||
vm: &VirtualMachine,
|
||||
module: &Py<crate::builtins::PyModule>,
|
||||
) -> PyResult<()> {
|
||||
__module_exec(vm, module);
|
||||
super::super::os::module_exec(vm, module)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// spell-checker:disable
|
||||
|
||||
pub(crate) use pwd::make_module;
|
||||
pub(crate) use pwd::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod pwd {
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
// spell-checker:disable
|
||||
|
||||
use crate::{PyRef, VirtualMachine, builtins::PyModule};
|
||||
|
||||
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
let module = _signal::make_module(vm);
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
_signal::init_signal_handlers(&module, vm);
|
||||
|
||||
module
|
||||
}
|
||||
pub(crate) use _signal::module_def;
|
||||
|
||||
#[pymodule]
|
||||
pub(crate) mod _signal {
|
||||
#[cfg(any(unix, windows))]
|
||||
use crate::{
|
||||
Py,
|
||||
convert::{IntoPyException, TryFromBorrowedObject},
|
||||
};
|
||||
use crate::{PyObjectRef, PyResult, VirtualMachine, signal};
|
||||
use crate::convert::{IntoPyException, TryFromBorrowedObject};
|
||||
use crate::{Py, PyObjectRef, PyResult, VirtualMachine, signal};
|
||||
#[cfg(unix)]
|
||||
use crate::{
|
||||
builtins::PyTypeRef,
|
||||
@@ -682,4 +670,16 @@ pub(crate) mod _signal {
|
||||
// TODO: handle _res < 1, support warn_on_full_buffer
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn module_exec(
|
||||
vm: &VirtualMachine,
|
||||
module: &Py<crate::builtins::PyModule>,
|
||||
) -> PyResult<()> {
|
||||
__module_exec(vm, module);
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
init_signal_handlers(module, vm);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
pub(crate) use _sre::make_module;
|
||||
pub(crate) use _sre::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod _sre {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{PyRef, VirtualMachine, builtins::PyModule};
|
||||
pub(crate) use _stat::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod stat {
|
||||
mod _stat {
|
||||
// Use libc::mode_t for Mode to match the system's definition
|
||||
#[cfg(unix)]
|
||||
type Mode = libc::mode_t;
|
||||
@@ -522,7 +522,3 @@ mod stat {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
stat::make_module(vm)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/* String builtin module
|
||||
*/
|
||||
|
||||
pub(crate) use _string::make_module;
|
||||
pub(crate) use _string::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod _string {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
pub(crate) use symtable::make_module;
|
||||
pub(crate) use _symtable::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod symtable {
|
||||
mod _symtable {
|
||||
use crate::{
|
||||
PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyDictRef, PyStrRef},
|
||||
|
||||
@@ -78,7 +78,7 @@ mod sys {
|
||||
#[pyattr(name = "abiflags")]
|
||||
const ABIFLAGS_ATTR: &str = "t"; // 't' for free-threaded (no GIL)
|
||||
// Internal constant used for sysconfigdata_name
|
||||
pub(crate) const ABIFLAGS: &str = "t";
|
||||
pub const ABIFLAGS: &str = "t";
|
||||
#[pyattr(name = "api_version")]
|
||||
const API_VERSION: u32 = 0x0; // what C api?
|
||||
#[pyattr(name = "copyright")]
|
||||
@@ -94,7 +94,7 @@ mod sys {
|
||||
#[pyattr(name = "maxunicode")]
|
||||
const MAXUNICODE: u32 = core::char::MAX as u32;
|
||||
#[pyattr(name = "platform")]
|
||||
pub(crate) const PLATFORM: &str = {
|
||||
pub const PLATFORM: &str = {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_os = "linux")] {
|
||||
"linux"
|
||||
@@ -166,11 +166,11 @@ mod sys {
|
||||
|
||||
#[pyattr]
|
||||
fn builtin_module_names(vm: &VirtualMachine) -> PyTupleRef {
|
||||
let mut module_names: Vec<_> = vm.state.module_inits.keys().cloned().collect();
|
||||
// Also include multi-phase init modules
|
||||
module_names.extend(vm.state.module_defs.keys().cloned());
|
||||
module_names.push("sys".into());
|
||||
module_names.push("builtins".into());
|
||||
let mut module_names: Vec<String> =
|
||||
vm.state.module_defs.keys().map(|&s| s.to_owned()).collect();
|
||||
module_names.push("sys".to_owned());
|
||||
module_names.push("builtins".to_owned());
|
||||
|
||||
module_names.sort();
|
||||
vm.ctx.new_tuple(
|
||||
module_names
|
||||
@@ -1609,6 +1609,7 @@ mod sys {
|
||||
}
|
||||
|
||||
pub(crate) fn init_module(vm: &VirtualMachine, module: &Py<PyModule>, builtins: &Py<PyModule>) {
|
||||
module.__init_methods(vm).unwrap();
|
||||
sys::module_exec(vm, module).unwrap();
|
||||
|
||||
let modules = vm.ctx.new_dict();
|
||||
@@ -1620,7 +1621,8 @@ pub(crate) fn init_module(vm: &VirtualMachine, module: &Py<PyModule>, builtins:
|
||||
.unwrap();
|
||||
|
||||
// Create sys._jit submodule
|
||||
let jit_module = sys_jit::make_module(vm);
|
||||
let jit_def = sys_jit::module_def(&vm.ctx);
|
||||
let jit_module = jit_def.create_module(vm).unwrap();
|
||||
|
||||
extend_module!(vm, module, {
|
||||
"__doc__" => sys::DOC.to_owned().to_pyobject(vm),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
pub(crate) use sysconfig::make_module;
|
||||
pub(crate) use _sysconfig::module_def;
|
||||
|
||||
#[pymodule(name = "_sysconfig")]
|
||||
pub(crate) mod sysconfig {
|
||||
#[pymodule]
|
||||
pub(crate) mod _sysconfig {
|
||||
use crate::{VirtualMachine, builtins::PyDictRef, convert::ToPyObject};
|
||||
|
||||
#[pyfunction]
|
||||
|
||||
@@ -1,11 +1,29 @@
|
||||
// spell-checker: words LDSHARED ARFLAGS CPPFLAGS CCSHARED BASECFLAGS BLDSHARED
|
||||
|
||||
pub(crate) use _sysconfigdata::make_module;
|
||||
pub(crate) use _sysconfigdata::module_def;
|
||||
|
||||
#[pymodule]
|
||||
pub(crate) mod _sysconfigdata {
|
||||
use crate::stdlib::sys::{RUST_MULTIARCH, multiarch};
|
||||
use crate::{VirtualMachine, builtins::PyDictRef, convert::ToPyObject};
|
||||
mod _sysconfigdata {
|
||||
use crate::stdlib::sys::{RUST_MULTIARCH, multiarch, sysconfigdata_name};
|
||||
use crate::{
|
||||
Py, PyResult, VirtualMachine,
|
||||
builtins::{PyDictRef, PyModule},
|
||||
convert::ToPyObject,
|
||||
};
|
||||
|
||||
fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
|
||||
// Set build_time_vars attribute
|
||||
let build_time_vars = build_time_vars(vm);
|
||||
module.set_attr("build_time_vars", build_time_vars, vm)?;
|
||||
|
||||
// Ensure the module is registered under the platform-specific name
|
||||
// (import_builtin() already handles this, but double-check for safety)
|
||||
let sys_modules = vm.sys_module.get_attr("modules", vm)?;
|
||||
let sysconfigdata_name = sysconfigdata_name();
|
||||
sys_modules.set_item(sysconfigdata_name.as_str(), module.to_owned().into(), vm)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
fn build_time_vars(vm: &VirtualMachine) -> PyDictRef {
|
||||
|
||||
@@ -4,7 +4,7 @@ pub(crate) use _thread::after_fork_child;
|
||||
#[cfg_attr(target_arch = "wasm32", allow(unused_imports))]
|
||||
pub(crate) use _thread::{
|
||||
CurrentFrameSlot, HandleEntry, RawRMutex, ShutdownEntry, get_all_current_frames, get_ident,
|
||||
init_main_thread_ident, make_module,
|
||||
init_main_thread_ident, module_def,
|
||||
};
|
||||
|
||||
#[pymodule]
|
||||
|
||||
@@ -3,18 +3,10 @@
|
||||
|
||||
// See also:
|
||||
// https://docs.python.org/3/library/time.html
|
||||
use crate::{PyRef, VirtualMachine, builtins::PyModule};
|
||||
|
||||
pub use decl::time;
|
||||
|
||||
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
#[cfg(not(target_env = "msvc"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
unsafe {
|
||||
c_tzset()
|
||||
};
|
||||
decl::make_module(vm)
|
||||
}
|
||||
pub(crate) use decl::module_def;
|
||||
|
||||
#[cfg(not(target_env = "msvc"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -34,7 +26,7 @@ unsafe extern "C" {
|
||||
#[pymodule(name = "time", with(platform))]
|
||||
mod decl {
|
||||
use crate::{
|
||||
AsObject, PyObjectRef, PyResult, VirtualMachine,
|
||||
AsObject, Py, PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::{PyStrRef, PyTypeRef, PyUtf8StrRef},
|
||||
function::{Either, FuncArgs, OptionalArg},
|
||||
types::{PyStructSequence, struct_sequence_new},
|
||||
@@ -581,6 +573,20 @@ mod decl {
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use super::platform::*;
|
||||
|
||||
pub(crate) fn module_exec(
|
||||
vm: &VirtualMachine,
|
||||
module: &Py<crate::builtins::PyModule>,
|
||||
) -> PyResult<()> {
|
||||
#[cfg(not(target_env = "msvc"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
unsafe {
|
||||
super::c_tzset()
|
||||
};
|
||||
|
||||
__module_exec(vm, module);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// spell-checker:ignore typevarobject funcobj
|
||||
use crate::{Context, PyPayload, PyRef, VirtualMachine, class::PyClassImpl, stdlib::PyModule};
|
||||
use crate::{Context, class::PyClassImpl};
|
||||
|
||||
pub use crate::stdlib::typevar::{
|
||||
Generic, ParamSpec, ParamSpecArgs, ParamSpecKwargs, TypeVar, TypeVarTuple,
|
||||
set_typeparam_default,
|
||||
};
|
||||
pub(crate) use decl::module_def;
|
||||
pub use decl::*;
|
||||
|
||||
/// Initialize typing types (call extend_class)
|
||||
@@ -12,32 +13,12 @@ pub fn init(ctx: &Context) {
|
||||
NoDefault::extend_class(ctx, ctx.types.typing_no_default_type);
|
||||
}
|
||||
|
||||
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
let module = decl::make_module(vm);
|
||||
TypeVar::make_class(&vm.ctx);
|
||||
ParamSpec::make_class(&vm.ctx);
|
||||
TypeVarTuple::make_class(&vm.ctx);
|
||||
ParamSpecArgs::make_class(&vm.ctx);
|
||||
ParamSpecKwargs::make_class(&vm.ctx);
|
||||
Generic::make_class(&vm.ctx);
|
||||
extend_module!(vm, &module, {
|
||||
"NoDefault" => vm.ctx.typing_no_default.clone(),
|
||||
"TypeVar" => TypeVar::class(&vm.ctx).to_owned(),
|
||||
"ParamSpec" => ParamSpec::class(&vm.ctx).to_owned(),
|
||||
"TypeVarTuple" => TypeVarTuple::class(&vm.ctx).to_owned(),
|
||||
"ParamSpecArgs" => ParamSpecArgs::class(&vm.ctx).to_owned(),
|
||||
"ParamSpecKwargs" => ParamSpecKwargs::class(&vm.ctx).to_owned(),
|
||||
"Generic" => Generic::class(&vm.ctx).to_owned(),
|
||||
"Union" => vm.ctx.types.union_type.to_owned(),
|
||||
});
|
||||
module
|
||||
}
|
||||
|
||||
#[pymodule(name = "_typing")]
|
||||
pub(crate) mod decl {
|
||||
use crate::{
|
||||
Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
|
||||
builtins::{PyStrRef, PyTupleRef, PyType, PyTypeRef, pystr::AsPyStr, type_},
|
||||
class::PyClassImpl,
|
||||
function::{FuncArgs, IntoFuncArgs},
|
||||
protocol::PyNumberMethods,
|
||||
types::{AsNumber, Constructor, Representable},
|
||||
@@ -206,4 +187,33 @@ pub(crate) mod decl {
|
||||
// &AS_MAPPING
|
||||
// }
|
||||
// }
|
||||
|
||||
pub(crate) fn module_exec(
|
||||
vm: &VirtualMachine,
|
||||
module: &Py<crate::builtins::PyModule>,
|
||||
) -> PyResult<()> {
|
||||
__module_exec(vm, module);
|
||||
|
||||
use super::{Generic, ParamSpec, ParamSpecArgs, ParamSpecKwargs, TypeVar, TypeVarTuple};
|
||||
|
||||
TypeVar::make_class(&vm.ctx);
|
||||
ParamSpec::make_class(&vm.ctx);
|
||||
TypeVarTuple::make_class(&vm.ctx);
|
||||
ParamSpecArgs::make_class(&vm.ctx);
|
||||
ParamSpecKwargs::make_class(&vm.ctx);
|
||||
Generic::make_class(&vm.ctx);
|
||||
|
||||
extend_module!(vm, module, {
|
||||
"NoDefault" => vm.ctx.typing_no_default.clone(),
|
||||
"TypeVar" => TypeVar::class(&vm.ctx).to_owned(),
|
||||
"ParamSpec" => ParamSpec::class(&vm.ctx).to_owned(),
|
||||
"TypeVarTuple" => TypeVarTuple::class(&vm.ctx).to_owned(),
|
||||
"ParamSpecArgs" => ParamSpecArgs::class(&vm.ctx).to_owned(),
|
||||
"ParamSpecKwargs" => ParamSpecKwargs::class(&vm.ctx).to_owned(),
|
||||
"Generic" => Generic::class(&vm.ctx).to_owned(),
|
||||
"Union" => vm.ctx.types.union_type.to_owned(),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
pub(crate) use _warnings::make_module;
|
||||
pub(crate) use _warnings::module_def;
|
||||
|
||||
use crate::{Py, PyResult, VirtualMachine, builtins::PyType};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! - [python weakref module](https://docs.python.org/3/library/weakref.html)
|
||||
//! - [rust weak struct](https://doc.rust-lang.org/std/rc/struct.Weak.html)
|
||||
//!
|
||||
pub(crate) use _weakref::make_module;
|
||||
pub(crate) use _weakref::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod _weakref {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// spell-checker:disable
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
pub(crate) use _winapi::make_module;
|
||||
pub(crate) use _winapi::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod _winapi {
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
// spell-checker:disable
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::{PyRef, VirtualMachine, builtins::PyModule};
|
||||
|
||||
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
winreg::make_module(vm)
|
||||
}
|
||||
pub(crate) use winreg::module_def;
|
||||
|
||||
#[pymodule]
|
||||
mod winreg {
|
||||
|
||||
+337
-24
@@ -1,7 +1,276 @@
|
||||
use super::{Context, PyConfig, VirtualMachine, setting::Settings, thread};
|
||||
use crate::{PyResult, getpath, stdlib::atexit, vm::PyBaseExceptionRef};
|
||||
use super::{Context, PyConfig, PyGlobalState, VirtualMachine, setting::Settings, thread};
|
||||
use crate::{
|
||||
PyResult, builtins, common::rc::PyRc, frozen::FrozenModule, getpath, py_freeze, stdlib::atexit,
|
||||
vm::PyBaseExceptionRef,
|
||||
};
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
type InitFunc = Box<dyn FnOnce(&mut VirtualMachine)>;
|
||||
|
||||
/// Configuration builder for constructing an Interpreter.
|
||||
///
|
||||
/// This is the preferred way to configure and create an interpreter with custom modules.
|
||||
/// Modules must be registered before the interpreter is built,
|
||||
/// similar to CPython's `PyImport_AppendInittab` which must be called before `Py_Initialize`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rustpython_vm::Interpreter;
|
||||
///
|
||||
/// let builder = Interpreter::builder(Default::default());
|
||||
/// // In practice, add stdlib: builder.add_native_modules(&stdlib_module_defs(&builder.ctx))
|
||||
/// let interp = builder.build();
|
||||
/// ```
|
||||
pub struct InterpreterBuilder {
|
||||
settings: Settings,
|
||||
pub ctx: PyRc<Context>,
|
||||
module_defs: Vec<&'static builtins::PyModuleDef>,
|
||||
frozen_modules: Vec<(&'static str, FrozenModule)>,
|
||||
init_hooks: Vec<InitFunc>,
|
||||
}
|
||||
|
||||
/// Private helper to initialize a VM with settings, context, and custom initialization.
|
||||
fn initialize_main_vm<F>(
|
||||
settings: Settings,
|
||||
ctx: PyRc<Context>,
|
||||
module_defs: Vec<&'static builtins::PyModuleDef>,
|
||||
frozen_modules: Vec<(&'static str, FrozenModule)>,
|
||||
init_hooks: Vec<InitFunc>,
|
||||
init: F,
|
||||
) -> (VirtualMachine, PyRc<PyGlobalState>)
|
||||
where
|
||||
F: FnOnce(&mut VirtualMachine),
|
||||
{
|
||||
use crate::codecs::CodecsRegistry;
|
||||
use crate::common::hash::HashSecret;
|
||||
use crate::common::lock::PyMutex;
|
||||
use crate::warn::WarningsState;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
let paths = getpath::init_path_config(&settings);
|
||||
let config = PyConfig::new(settings, paths);
|
||||
|
||||
crate::types::TypeZoo::extend(&ctx);
|
||||
crate::exceptions::ExceptionZoo::extend(&ctx);
|
||||
|
||||
// Build module_defs map from builtin modules + additional modules
|
||||
let mut all_module_defs: std::collections::BTreeMap<
|
||||
&'static str,
|
||||
&'static builtins::PyModuleDef,
|
||||
> = crate::stdlib::builtin_module_defs(&ctx)
|
||||
.into_iter()
|
||||
.chain(module_defs)
|
||||
.map(|def| (def.name.as_str(), def))
|
||||
.collect();
|
||||
|
||||
// Register sysconfigdata under platform-specific name as well
|
||||
if let Some(&sysconfigdata_def) = all_module_defs.get("_sysconfigdata") {
|
||||
use std::sync::OnceLock;
|
||||
static SYSCONFIGDATA_NAME: OnceLock<&'static str> = OnceLock::new();
|
||||
let leaked_name = *SYSCONFIGDATA_NAME.get_or_init(|| {
|
||||
let name = crate::stdlib::sys::sysconfigdata_name();
|
||||
Box::leak(name.into_boxed_str())
|
||||
});
|
||||
all_module_defs.insert(leaked_name, sysconfigdata_def);
|
||||
}
|
||||
|
||||
// Create hash secret
|
||||
let seed = match config.settings.hash_seed {
|
||||
Some(seed) => seed,
|
||||
None => super::process_hash_secret_seed(),
|
||||
};
|
||||
let hash_secret = HashSecret::new(seed);
|
||||
|
||||
// Create codec registry and warnings state
|
||||
let codec_registry = CodecsRegistry::new(&ctx);
|
||||
let warnings = WarningsState::init_state(&ctx);
|
||||
|
||||
// Create int_max_str_digits
|
||||
let int_max_str_digits = AtomicCell::new(match config.settings.int_max_str_digits {
|
||||
-1 => 4300,
|
||||
other => other,
|
||||
} as usize);
|
||||
|
||||
// Initialize frozen modules (core + user-provided)
|
||||
let mut frozen: std::collections::HashMap<&'static str, FrozenModule, ahash::RandomState> =
|
||||
core_frozen_inits().collect();
|
||||
frozen.extend(frozen_modules);
|
||||
|
||||
// Create PyGlobalState
|
||||
let global_state = PyRc::new(PyGlobalState {
|
||||
config,
|
||||
module_defs: all_module_defs,
|
||||
frozen,
|
||||
stacksize: AtomicCell::new(0),
|
||||
thread_count: AtomicCell::new(0),
|
||||
hash_secret,
|
||||
atexit_funcs: PyMutex::default(),
|
||||
codec_registry,
|
||||
finalizing: AtomicBool::new(false),
|
||||
warnings,
|
||||
override_frozen_modules: AtomicCell::new(0),
|
||||
before_forkers: PyMutex::default(),
|
||||
after_forkers_child: PyMutex::default(),
|
||||
after_forkers_parent: PyMutex::default(),
|
||||
int_max_str_digits,
|
||||
switch_interval: AtomicCell::new(0.005),
|
||||
global_trace_func: PyMutex::default(),
|
||||
global_profile_func: PyMutex::default(),
|
||||
#[cfg(feature = "threading")]
|
||||
main_thread_ident: AtomicCell::new(0),
|
||||
#[cfg(feature = "threading")]
|
||||
thread_frames: parking_lot::Mutex::new(std::collections::HashMap::new()),
|
||||
#[cfg(feature = "threading")]
|
||||
thread_handles: parking_lot::Mutex::new(Vec::new()),
|
||||
#[cfg(feature = "threading")]
|
||||
shutdown_handles: parking_lot::Mutex::new(Vec::new()),
|
||||
});
|
||||
|
||||
// Create VM with the global state
|
||||
// Note: Don't clone here - init_hooks need exclusive access to mutate state
|
||||
let mut vm = VirtualMachine::new(ctx, global_state);
|
||||
|
||||
// Execute initialization hooks (can mutate vm.state)
|
||||
for hook in init_hooks {
|
||||
hook(&mut vm);
|
||||
}
|
||||
|
||||
// Call custom init function (can mutate vm.state)
|
||||
init(&mut vm);
|
||||
|
||||
vm.initialize();
|
||||
|
||||
// Clone global_state for Interpreter after all initialization is done
|
||||
let global_state = vm.state.clone();
|
||||
(vm, global_state)
|
||||
}
|
||||
|
||||
impl InterpreterBuilder {
|
||||
/// Create a new interpreter configuration with default settings.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
settings: Settings::default(),
|
||||
ctx: Context::genesis().clone(),
|
||||
module_defs: Vec::new(),
|
||||
frozen_modules: Vec::new(),
|
||||
init_hooks: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set custom settings for the interpreter.
|
||||
///
|
||||
/// If called multiple times, only the last settings will be used.
|
||||
pub fn settings(mut self, settings: Settings) -> Self {
|
||||
self.settings = settings;
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a single native module definition.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rustpython_vm::{Interpreter, builtins::PyModuleDef};
|
||||
///
|
||||
/// let builder = Interpreter::builder(Default::default());
|
||||
/// // Note: In practice, use module_def from your #[pymodule]
|
||||
/// // let def = mymodule::module_def(&builder.ctx);
|
||||
/// // let interp = builder.add_native_module(def).build();
|
||||
/// let interp = builder.build();
|
||||
/// ```
|
||||
pub fn add_native_module(self, def: &'static builtins::PyModuleDef) -> Self {
|
||||
self.add_native_modules(&[def])
|
||||
}
|
||||
|
||||
/// Add multiple native module definitions.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rustpython_vm::Interpreter;
|
||||
///
|
||||
/// let builder = Interpreter::builder(Default::default());
|
||||
/// // In practice, use module_defs from rustpython_stdlib:
|
||||
/// // let defs = rustpython_stdlib::stdlib_module_defs(&builder.ctx);
|
||||
/// // let interp = builder.add_native_modules(&defs).build();
|
||||
/// let interp = builder.build();
|
||||
/// ```
|
||||
pub fn add_native_modules(mut self, defs: &[&'static builtins::PyModuleDef]) -> Self {
|
||||
self.module_defs.extend_from_slice(defs);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a custom initialization hook.
|
||||
///
|
||||
/// Hooks are executed in the order they are added during interpreter creation.
|
||||
/// This function will be called after modules are registered but before
|
||||
/// the VM is initialized, allowing for additional customization.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rustpython_vm::Interpreter;
|
||||
///
|
||||
/// let interp = Interpreter::builder(Default::default())
|
||||
/// .init_hook(|vm| {
|
||||
/// // Custom initialization
|
||||
/// })
|
||||
/// .build();
|
||||
/// ```
|
||||
pub fn init_hook<F>(mut self, init: F) -> Self
|
||||
where
|
||||
F: FnOnce(&mut VirtualMachine) + 'static,
|
||||
{
|
||||
self.init_hooks.push(Box::new(init));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add frozen modules to the interpreter.
|
||||
///
|
||||
/// Frozen modules are Python modules compiled into the binary.
|
||||
/// This method accepts any iterator of (name, FrozenModule) pairs.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rustpython_vm::Interpreter;
|
||||
///
|
||||
/// let interp = Interpreter::builder(Default::default())
|
||||
/// // In practice: .add_frozen_modules(rustpython_pylib::FROZEN_STDLIB)
|
||||
/// .build();
|
||||
/// ```
|
||||
pub fn add_frozen_modules<I>(mut self, frozen: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = (&'static str, FrozenModule)>,
|
||||
{
|
||||
self.frozen_modules.extend(frozen);
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the interpreter.
|
||||
///
|
||||
/// This consumes the configuration and returns a fully initialized Interpreter.
|
||||
pub fn build(self) -> Interpreter {
|
||||
let (vm, global_state) = initialize_main_vm(
|
||||
self.settings,
|
||||
self.ctx,
|
||||
self.module_defs,
|
||||
self.frozen_modules,
|
||||
self.init_hooks,
|
||||
|_| {}, // No additional init needed
|
||||
);
|
||||
Interpreter { global_state, vm }
|
||||
}
|
||||
|
||||
/// Alias for `build()` for compatibility with the `interpreter()` pattern.
|
||||
pub fn interpreter(self) -> Interpreter {
|
||||
self.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for InterpreterBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// The general interface for the VM
|
||||
///
|
||||
/// # Examples
|
||||
@@ -21,43 +290,49 @@ use core::sync::atomic::Ordering;
|
||||
/// });
|
||||
/// ```
|
||||
pub struct Interpreter {
|
||||
pub global_state: PyRc<PyGlobalState>,
|
||||
vm: VirtualMachine,
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
/// Create a new interpreter configuration builder.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rustpython_vm::Interpreter;
|
||||
///
|
||||
/// let builder = Interpreter::builder(Default::default());
|
||||
/// // In practice, add stdlib: builder.add_native_modules(&stdlib_module_defs(&builder.ctx))
|
||||
/// let interp = builder.build();
|
||||
/// ```
|
||||
pub fn builder(settings: Settings) -> InterpreterBuilder {
|
||||
InterpreterBuilder::new().settings(settings)
|
||||
}
|
||||
|
||||
/// This is a bare unit to build up an interpreter without the standard library.
|
||||
/// To create an interpreter with the standard library with the `rustpython` crate, use `rustpython::InterpreterConfig`.
|
||||
/// To create an interpreter with the standard library with the `rustpython` crate, use `rustpython::InterpreterBuilder`.
|
||||
/// To create an interpreter without the `rustpython` crate, but only with `rustpython-vm`,
|
||||
/// try to build one from the source code of `InterpreterConfig`. It will not be a one-liner but it also will not be too hard.
|
||||
/// try to build one from the source code of `InterpreterBuilder`. It will not be a one-liner but it also will not be too hard.
|
||||
pub fn without_stdlib(settings: Settings) -> Self {
|
||||
Self::with_init(settings, |_| {})
|
||||
}
|
||||
|
||||
/// Create with initialize function taking mutable vm reference.
|
||||
/// ```
|
||||
/// use rustpython_vm::Interpreter;
|
||||
/// Interpreter::with_init(Default::default(), |vm| {
|
||||
/// // put this line to add stdlib to the vm
|
||||
/// // vm.add_native_module_defs(rustpython_stdlib::get_module_defs());
|
||||
/// }).enter(|vm| {
|
||||
/// vm.run_code_string(vm.new_scope_with_builtins(), "print(1)", "<...>".to_owned());
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// Note: This is a legacy API. To add stdlib, use `Interpreter::builder()` instead.
|
||||
pub fn with_init<F>(settings: Settings, init: F) -> Self
|
||||
where
|
||||
F: FnOnce(&mut VirtualMachine),
|
||||
{
|
||||
// Compute path configuration from settings
|
||||
let paths = getpath::init_path_config(&settings);
|
||||
let config = PyConfig::new(settings, paths);
|
||||
|
||||
let ctx = Context::genesis();
|
||||
crate::types::TypeZoo::extend(ctx);
|
||||
crate::exceptions::ExceptionZoo::extend(ctx);
|
||||
let mut vm = VirtualMachine::new(config, ctx.clone());
|
||||
init(&mut vm);
|
||||
vm.initialize();
|
||||
Self { vm }
|
||||
let (vm, global_state) = initialize_main_vm(
|
||||
settings,
|
||||
Context::genesis().clone(),
|
||||
Vec::new(), // No module_defs
|
||||
Vec::new(), // No frozen_modules
|
||||
Vec::new(), // No init_hooks
|
||||
init,
|
||||
);
|
||||
Self { global_state, vm }
|
||||
}
|
||||
|
||||
/// Run a function with the main virtual machine and return a PyResult of the result.
|
||||
@@ -157,6 +432,44 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
fn core_frozen_inits() -> impl Iterator<Item = (&'static str, FrozenModule)> {
|
||||
let iter = core::iter::empty();
|
||||
macro_rules! ext_modules {
|
||||
($iter:ident, $($t:tt)*) => {
|
||||
let $iter = $iter.chain(py_freeze!($($t)*));
|
||||
};
|
||||
}
|
||||
|
||||
// keep as example but use file one now
|
||||
// ext_modules!(
|
||||
// iter,
|
||||
// source = "initialized = True; print(\"Hello world!\")\n",
|
||||
// module_name = "__hello__",
|
||||
// );
|
||||
|
||||
// Python modules that the vm calls into, but are not actually part of the stdlib. They could
|
||||
// in theory be implemented in Rust, but are easiest to do in Python for one reason or another.
|
||||
// Includes _importlib_bootstrap and _importlib_bootstrap_external
|
||||
ext_modules!(
|
||||
iter,
|
||||
dir = "./Lib/python_builtins",
|
||||
crate_name = "rustpython_compiler_core"
|
||||
);
|
||||
|
||||
// core stdlib Python modules that the vm calls into, but are still used in Python
|
||||
// application code, e.g. copyreg
|
||||
// FIXME: Initializing core_modules here results duplicated frozen module generation for core_modules.
|
||||
// We need a way to initialize this modules for both `Interpreter::without_stdlib()` and `InterpreterBuilder::new().init_stdlib().interpreter()`
|
||||
// #[cfg(not(feature = "freeze-stdlib"))]
|
||||
ext_modules!(
|
||||
iter,
|
||||
dir = "./Lib/core_modules",
|
||||
crate_name = "rustpython_compiler_core"
|
||||
);
|
||||
|
||||
iter
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
+41
-174
@@ -19,7 +19,7 @@ mod vm_ops;
|
||||
use crate::{
|
||||
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
|
||||
builtins::{
|
||||
PyBaseExceptionRef, PyDict, PyDictRef, PyInt, PyList, PyModule, PyStr, PyStrInterned,
|
||||
self, PyBaseExceptionRef, PyDict, PyDictRef, PyInt, PyList, PyModule, PyStr, PyStrInterned,
|
||||
PyStrRef, PyTypeRef,
|
||||
code::PyCode,
|
||||
dict::{PyDictItems, PyDictKeys, PyDictValues},
|
||||
@@ -56,7 +56,7 @@ use std::{
|
||||
};
|
||||
|
||||
pub use context::Context;
|
||||
pub use interpreter::Interpreter;
|
||||
pub use interpreter::{Interpreter, InterpreterBuilder};
|
||||
pub(crate) use method::PyMethod;
|
||||
pub use setting::{CheckHashPycsMode, Paths, PyConfig, Settings};
|
||||
|
||||
@@ -102,8 +102,7 @@ struct ExceptionStack {
|
||||
|
||||
pub struct PyGlobalState {
|
||||
pub config: PyConfig,
|
||||
pub module_inits: stdlib::StdlibMap,
|
||||
pub module_defs: stdlib::StdlibDefMap,
|
||||
pub module_defs: std::collections::BTreeMap<&'static str, &'static builtins::PyModuleDef>,
|
||||
pub frozen: HashMap<&'static str, FrozenModule, ahash::RandomState>,
|
||||
pub stacksize: AtomicCell<usize>,
|
||||
pub thread_count: AtomicCell<usize>,
|
||||
@@ -145,7 +144,7 @@ pub fn process_hash_secret_seed() -> u32 {
|
||||
|
||||
impl VirtualMachine {
|
||||
/// Create a new `VirtualMachine` structure.
|
||||
fn new(config: PyConfig, ctx: PyRc<Context>) -> Self {
|
||||
pub(crate) fn new(ctx: PyRc<Context>, state: PyRc<PyGlobalState>) -> Self {
|
||||
flame_guard!("new VirtualMachine");
|
||||
|
||||
// make a new module without access to the vm; doesn't
|
||||
@@ -170,24 +169,7 @@ impl VirtualMachine {
|
||||
const { RefCell::new([const { None }; signal::NSIG]) },
|
||||
));
|
||||
|
||||
let module_inits = stdlib::get_module_inits();
|
||||
let module_defs = stdlib::get_module_defs();
|
||||
|
||||
let seed = match config.settings.hash_seed {
|
||||
Some(seed) => seed,
|
||||
None => process_hash_secret_seed(),
|
||||
};
|
||||
let hash_secret = HashSecret::new(seed);
|
||||
|
||||
let codec_registry = CodecsRegistry::new(&ctx);
|
||||
|
||||
let warnings = WarningsState::init_state(&ctx);
|
||||
|
||||
let int_max_str_digits = AtomicCell::new(match config.settings.int_max_str_digits {
|
||||
-1 => 4300,
|
||||
other => other,
|
||||
} as usize);
|
||||
let mut vm = Self {
|
||||
let vm = Self {
|
||||
builtins,
|
||||
sys_module,
|
||||
ctx,
|
||||
@@ -202,35 +184,7 @@ impl VirtualMachine {
|
||||
signal_handlers,
|
||||
signal_rx: None,
|
||||
repr_guards: RefCell::default(),
|
||||
state: PyRc::new(PyGlobalState {
|
||||
config,
|
||||
module_inits,
|
||||
module_defs,
|
||||
frozen: HashMap::default(),
|
||||
stacksize: AtomicCell::new(0),
|
||||
thread_count: AtomicCell::new(0),
|
||||
hash_secret,
|
||||
atexit_funcs: PyMutex::default(),
|
||||
codec_registry,
|
||||
finalizing: AtomicBool::new(false),
|
||||
warnings,
|
||||
override_frozen_modules: AtomicCell::new(0),
|
||||
before_forkers: PyMutex::default(),
|
||||
after_forkers_child: PyMutex::default(),
|
||||
after_forkers_parent: PyMutex::default(),
|
||||
int_max_str_digits,
|
||||
switch_interval: AtomicCell::new(0.005),
|
||||
global_trace_func: PyMutex::default(),
|
||||
global_profile_func: PyMutex::default(),
|
||||
#[cfg(feature = "threading")]
|
||||
main_thread_ident: AtomicCell::new(0),
|
||||
#[cfg(feature = "threading")]
|
||||
thread_frames: parking_lot::Mutex::new(HashMap::new()),
|
||||
#[cfg(feature = "threading")]
|
||||
thread_handles: parking_lot::Mutex::new(Vec::new()),
|
||||
#[cfg(feature = "threading")]
|
||||
shutdown_handles: parking_lot::Mutex::new(Vec::new()),
|
||||
}),
|
||||
state,
|
||||
initialized: false,
|
||||
recursion_depth: Cell::new(0),
|
||||
c_stack_soft_limit: Cell::new(Self::calculate_c_stack_soft_limit()),
|
||||
@@ -248,9 +202,6 @@ impl VirtualMachine {
|
||||
panic!("Interpreters in same process must share the hash seed");
|
||||
}
|
||||
|
||||
let frozen = core_frozen_inits().collect();
|
||||
PyRc::get_mut(&mut vm.state).unwrap().frozen = frozen;
|
||||
|
||||
vm.builtins.init_dict(
|
||||
vm.ctx.intern_str("builtins"),
|
||||
Some(vm.ctx.intern_str(stdlib::builtins::DOC.unwrap()).to_owned()),
|
||||
@@ -278,7 +229,7 @@ impl VirtualMachine {
|
||||
});
|
||||
|
||||
let guide_message = if cfg!(feature = "freeze-stdlib") {
|
||||
"`rustpython_pylib` maybe not set while using `freeze-stdlib` feature. Try using `rustpython::InterpreterConfig::init_stdlib` or manually call `vm.add_frozen(rustpython_pylib::FROZEN_STDLIB)` in `rustpython_vm::Interpreter::with_init`."
|
||||
"`rustpython_pylib` may not be set while using `freeze-stdlib` feature. Try using `rustpython::InterpreterBuilder::init_stdlib` or manually call `builder.add_frozen_modules(rustpython_pylib::FROZEN_STDLIB)` in `rustpython_vm::Interpreter::builder()`."
|
||||
} else if !env_set {
|
||||
"Neither RUSTPYTHONPATH nor PYTHONPATH is set. Try setting one of them to the stdlib directory."
|
||||
} else if path_contains_env {
|
||||
@@ -473,53 +424,6 @@ impl VirtualMachine {
|
||||
self.initialized = true;
|
||||
}
|
||||
|
||||
fn state_mut(&mut self) -> &mut PyGlobalState {
|
||||
PyRc::get_mut(&mut self.state)
|
||||
.expect("there should not be multiple threads while a user has a mut ref to a vm")
|
||||
}
|
||||
|
||||
/// Can only be used in the initialization closure passed to [`Interpreter::with_init`]
|
||||
#[deprecated(note = "use add_native_module_def(s) with multi-phase module initialization")]
|
||||
pub fn add_native_module<S>(&mut self, name: S, module: stdlib::StdlibInitFunc)
|
||||
where
|
||||
S: Into<Cow<'static, str>>,
|
||||
{
|
||||
self.state_mut().module_inits.insert(name.into(), module);
|
||||
}
|
||||
|
||||
#[deprecated(note = "use add_native_module_defs with multi-phase module initialization")]
|
||||
pub fn add_native_modules<I>(&mut self, iter: I)
|
||||
where
|
||||
I: IntoIterator<Item = (Cow<'static, str>, stdlib::StdlibInitFunc)>,
|
||||
{
|
||||
self.state_mut().module_inits.extend(iter);
|
||||
}
|
||||
|
||||
/// Add a module definition for multi-phase initialization.
|
||||
/// These modules are added to sys.modules BEFORE their exec function runs,
|
||||
/// allowing safe circular imports.
|
||||
pub fn add_native_module_def<S>(&mut self, name: S, def_func: stdlib::StdlibDefFunc)
|
||||
where
|
||||
S: Into<Cow<'static, str>>,
|
||||
{
|
||||
self.state_mut().module_defs.insert(name.into(), def_func);
|
||||
}
|
||||
|
||||
pub fn add_native_module_defs<I>(&mut self, iter: I)
|
||||
where
|
||||
I: IntoIterator<Item = (Cow<'static, str>, stdlib::StdlibDefFunc)>,
|
||||
{
|
||||
self.state_mut().module_defs.extend(iter);
|
||||
}
|
||||
|
||||
/// Can only be used in the initialization closure passed to [`Interpreter::with_init`]
|
||||
pub fn add_frozen<I>(&mut self, frozen: I)
|
||||
where
|
||||
I: IntoIterator<Item = (&'static str, FrozenModule)>,
|
||||
{
|
||||
self.state_mut().frozen.extend(frozen);
|
||||
}
|
||||
|
||||
/// Set the custom signal channel for the interpreter
|
||||
pub fn set_user_signal_channel(&mut self, signal_rx: signal::UserSignalReceiver) {
|
||||
self.signal_rx = Some(signal_rx);
|
||||
@@ -1403,89 +1307,52 @@ pub fn resolve_frozen_alias(name: &str) -> &str {
|
||||
}
|
||||
}
|
||||
|
||||
fn core_frozen_inits() -> impl Iterator<Item = (&'static str, FrozenModule)> {
|
||||
let iter = core::iter::empty();
|
||||
macro_rules! ext_modules {
|
||||
($iter:ident, $($t:tt)*) => {
|
||||
let $iter = $iter.chain(py_freeze!($($t)*));
|
||||
};
|
||||
}
|
||||
|
||||
// keep as example but use file one now
|
||||
// ext_modules!(
|
||||
// iter,
|
||||
// source = "initialized = True; print(\"Hello world!\")\n",
|
||||
// module_name = "__hello__",
|
||||
// );
|
||||
|
||||
// Python modules that the vm calls into, but are not actually part of the stdlib. They could
|
||||
// in theory be implemented in Rust, but are easiest to do in Python for one reason or another.
|
||||
// Includes _importlib_bootstrap and _importlib_bootstrap_external
|
||||
ext_modules!(
|
||||
iter,
|
||||
dir = "./Lib/python_builtins",
|
||||
crate_name = "rustpython_compiler_core"
|
||||
);
|
||||
|
||||
// core stdlib Python modules that the vm calls into, but are still used in Python
|
||||
// application code, e.g. copyreg
|
||||
// FIXME: Initializing core_modules here results duplicated frozen module generation for core_modules.
|
||||
// We need a way to initialize this modules for both `Interpreter::without_stdlib()` and `InterpreterConfig::new().init_stdlib().interpreter()`
|
||||
// #[cfg(not(feature = "freeze-stdlib"))]
|
||||
ext_modules!(
|
||||
iter,
|
||||
dir = "./Lib/core_modules",
|
||||
crate_name = "rustpython_compiler_core"
|
||||
);
|
||||
|
||||
iter
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_frozen() {
|
||||
use rustpython_vm as vm;
|
||||
|
||||
vm::Interpreter::with_init(Default::default(), |vm| {
|
||||
// vm.add_native_module_defs(rustpython_stdlib::get_module_defs());
|
||||
vm.add_frozen(rustpython_vm::py_freeze!(
|
||||
vm::Interpreter::builder(Default::default())
|
||||
.add_frozen_modules(rustpython_vm::py_freeze!(
|
||||
dir = "../../extra_tests/snippets"
|
||||
));
|
||||
})
|
||||
.enter(|vm| {
|
||||
let scope = vm.new_scope_with_builtins();
|
||||
))
|
||||
.build()
|
||||
.enter(|vm| {
|
||||
let scope = vm.new_scope_with_builtins();
|
||||
|
||||
let source = "from dir_module.dir_module_inner import value2";
|
||||
let code_obj = vm
|
||||
.compile(source, vm::compiler::Mode::Exec, "<embedded>".to_owned())
|
||||
.map_err(|err| vm.new_syntax_error(&err, Some(source)))
|
||||
.unwrap();
|
||||
let source = "from dir_module.dir_module_inner import value2";
|
||||
let code_obj = vm
|
||||
.compile(source, vm::compiler::Mode::Exec, "<embedded>".to_owned())
|
||||
.map_err(|err| vm.new_syntax_error(&err, Some(source)))
|
||||
.unwrap();
|
||||
|
||||
if let Err(e) = vm.run_code_obj(code_obj, scope) {
|
||||
vm.print_exception(e);
|
||||
panic!();
|
||||
}
|
||||
})
|
||||
if let Err(e) = vm.run_code_obj(code_obj, scope) {
|
||||
vm.print_exception(e);
|
||||
panic!();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frozen_origname_matches() {
|
||||
use rustpython_vm as vm;
|
||||
|
||||
vm::Interpreter::with_init(Default::default(), |_vm| {}).enter(|vm| {
|
||||
let check = |name, expected| {
|
||||
let module = import::import_frozen(vm, name).unwrap();
|
||||
let origname: PyStrRef = module
|
||||
.get_attr("__origname__", vm)
|
||||
.unwrap()
|
||||
.try_into_value(vm)
|
||||
.unwrap();
|
||||
assert_eq!(origname.as_str(), expected);
|
||||
};
|
||||
vm::Interpreter::builder(Default::default())
|
||||
.build()
|
||||
.enter(|vm| {
|
||||
let check = |name, expected| {
|
||||
let module = import::import_frozen(vm, name).unwrap();
|
||||
let origname: PyStrRef = module
|
||||
.get_attr("__origname__", vm)
|
||||
.unwrap()
|
||||
.try_into_value(vm)
|
||||
.unwrap();
|
||||
assert_eq!(origname.as_str(), expected);
|
||||
};
|
||||
|
||||
check("_frozen_importlib", "importlib._bootstrap");
|
||||
check(
|
||||
"_frozen_importlib_external",
|
||||
"importlib._bootstrap_external",
|
||||
);
|
||||
});
|
||||
check("_frozen_importlib", "importlib._bootstrap");
|
||||
check(
|
||||
"_frozen_importlib_external",
|
||||
"importlib._bootstrap_external",
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
use rustpython_vm::VirtualMachine;
|
||||
|
||||
pub(crate) use _browser::module_def;
|
||||
|
||||
#[pymodule]
|
||||
@@ -257,8 +255,3 @@ mod _browser {
|
||||
Ok(PyPromise::from_future(future).into_pyobject(vm))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_browser_module(vm: &mut VirtualMachine) {
|
||||
vm.add_native_module_def("_browser".to_owned(), module_def);
|
||||
vm.add_frozen(py_freeze!(dir = "Lib"));
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
pub(crate) use _js::{PyJsValue, PyPromise};
|
||||
use rustpython_vm::VirtualMachine;
|
||||
|
||||
#[pymodule]
|
||||
mod _js {
|
||||
@@ -622,7 +621,3 @@ mod _js {
|
||||
}
|
||||
|
||||
pub(crate) use _js::module_def;
|
||||
|
||||
pub fn setup_js_module(vm: &mut VirtualMachine) {
|
||||
vm.add_native_module_def("_js".to_owned(), module_def);
|
||||
}
|
||||
@@ -11,7 +11,6 @@ extern crate rustpython_vm;
|
||||
|
||||
use js_sys::{Reflect, WebAssembly::RuntimeError};
|
||||
use std::panic;
|
||||
pub use vm_class::add_init_func;
|
||||
pub(crate) use vm_class::weak_vm;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
|
||||
+27
-30
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
browser_module::setup_browser_module,
|
||||
browser_module,
|
||||
convert::{self, PyResultExt},
|
||||
js_module, wasm_builtins,
|
||||
};
|
||||
@@ -37,55 +37,52 @@ mod _window {
|
||||
|
||||
impl StoredVirtualMachine {
|
||||
fn new(id: String, inject_browser_module: bool) -> StoredVirtualMachine {
|
||||
let mut scope = None;
|
||||
let mut settings = Settings::default();
|
||||
settings.allow_external_library = false;
|
||||
let interp = Interpreter::with_init(settings, |vm| {
|
||||
#[cfg(feature = "freeze-stdlib")]
|
||||
vm.add_native_module_defs(rustpython_stdlib::get_module_defs());
|
||||
|
||||
#[cfg(feature = "freeze-stdlib")]
|
||||
vm.add_frozen(rustpython_pylib::FROZEN_STDLIB);
|
||||
let mut builder = Interpreter::builder(settings);
|
||||
|
||||
vm.wasm_id = Some(id);
|
||||
#[cfg(feature = "freeze-stdlib")]
|
||||
{
|
||||
let defs = rustpython_stdlib::stdlib_module_defs(&builder.ctx);
|
||||
builder = builder
|
||||
.add_native_modules(&defs)
|
||||
.add_frozen_modules(rustpython_pylib::FROZEN_STDLIB);
|
||||
}
|
||||
|
||||
js_module::setup_js_module(vm);
|
||||
if inject_browser_module {
|
||||
vm.add_native_module_def("_window".to_owned(), _window::module_def);
|
||||
setup_browser_module(vm);
|
||||
}
|
||||
// Add wasm-specific modules
|
||||
let js_def = js_module::module_def(&builder.ctx);
|
||||
builder = builder.add_native_module(js_def);
|
||||
|
||||
VM_INIT_FUNCS.with_borrow(|funcs| {
|
||||
for f in funcs {
|
||||
f(vm)
|
||||
}
|
||||
});
|
||||
if inject_browser_module {
|
||||
let window_def = _window::module_def(&builder.ctx);
|
||||
let browser_def = browser_module::module_def(&builder.ctx);
|
||||
builder = builder
|
||||
.add_native_modules(&[window_def, browser_def])
|
||||
.add_frozen_modules(rustpython_vm::py_freeze!(dir = "Lib"));
|
||||
}
|
||||
|
||||
scope = Some(vm.new_scope_with_builtins());
|
||||
});
|
||||
let interp = builder
|
||||
.init_hook(move |vm| {
|
||||
vm.wasm_id = Some(id);
|
||||
})
|
||||
.build();
|
||||
|
||||
let scope = interp.enter(|vm| vm.new_scope_with_builtins());
|
||||
|
||||
StoredVirtualMachine {
|
||||
interp,
|
||||
scope: scope.unwrap(),
|
||||
scope,
|
||||
held_objects: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a hook to add builtins or frozen modules to the RustPython VirtualMachine while it's
|
||||
/// initializing.
|
||||
pub fn add_init_func(f: fn(&mut VirtualMachine)) {
|
||||
VM_INIT_FUNCS.with_borrow_mut(|funcs| funcs.push(f))
|
||||
}
|
||||
|
||||
// It's fine that it's thread local, since WASM doesn't even have threads yet. thread_local!
|
||||
// probably gets compiled down to a normal-ish static variable, like Atomic* types do:
|
||||
// https://rustwasm.github.io/2018/10/24/multithreading-rust-and-wasm.html#atomic-instructions
|
||||
thread_local! {
|
||||
static STORED_VMS: RefCell<HashMap<String, Rc<StoredVirtualMachine>>> = RefCell::default();
|
||||
static VM_INIT_FUNCS: RefCell<Vec<fn(&mut VirtualMachine)>> = const {
|
||||
RefCell::new(Vec::new())
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_vm_id(vm: &VirtualMachine) -> &str {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// spell-checker:ignore aheui
|
||||
/// Setting up a project with a frozen stdlib can be done *either* by using `rustpython::InterpreterConfig` or `rustpython_vm::Interpreter::with_init`.
|
||||
/// See each function for example.
|
||||
///
|
||||
/// See also: `aheui-rust.md` for freezing your own package.
|
||||
//! Setting up a project with a frozen stdlib can be done *either* by using `rustpython::InterpreterBuilder` or `rustpython_vm::Interpreter::builder`.
|
||||
//! See each function for example.
|
||||
//!
|
||||
//! See also: `aheui-rust.md` for freezing your own package.
|
||||
|
||||
use rustpython::InterpreterBuilderExt;
|
||||
use rustpython_vm::{PyResult, VirtualMachine};
|
||||
|
||||
fn run(keyword: &str, vm: &VirtualMachine) -> PyResult<()> {
|
||||
@@ -17,21 +18,19 @@ fn run(keyword: &str, vm: &VirtualMachine) -> PyResult<()> {
|
||||
}
|
||||
|
||||
fn interpreter_with_config() {
|
||||
let interpreter = rustpython::InterpreterConfig::new()
|
||||
let interpreter = rustpython::InterpreterBuilder::new()
|
||||
.init_stdlib()
|
||||
.interpreter();
|
||||
// Use interpreter.enter to reuse the same interpreter later
|
||||
interpreter.run(|vm| run("rustpython::InterpreterConfig", vm));
|
||||
interpreter.run(|vm| run("rustpython::InterpreterBuilder", vm));
|
||||
}
|
||||
|
||||
fn interpreter_with_vm() {
|
||||
let interpreter = rustpython_vm::Interpreter::with_init(Default::default(), |vm| {
|
||||
// This is unintuitive, but the stdlib is out of the vm crate.
|
||||
// Any suggestion to improve this is welcome.
|
||||
vm.add_frozen(rustpython_pylib::FROZEN_STDLIB);
|
||||
});
|
||||
let interpreter = rustpython_vm::Interpreter::builder(Default::default())
|
||||
.add_frozen_modules(rustpython_pylib::FROZEN_STDLIB)
|
||||
.build();
|
||||
// Use interpreter.enter to reuse the same interpreter later
|
||||
interpreter.run(|vm| run("rustpython_vm::Interpreter::with_init", vm));
|
||||
interpreter.run(|vm| run("rustpython_vm::Interpreter::builder", vm));
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use rustpython::InterpreterBuilderExt;
|
||||
use rustpython::vm::{
|
||||
PyObject, PyPayload, PyResult, TryFromBorrowedObject, VirtualMachine, pyclass, pymodule,
|
||||
};
|
||||
|
||||
pub fn main() {
|
||||
let interp = rustpython::InterpreterConfig::new()
|
||||
.init_stdlib()
|
||||
.init_hook(Box::new(|vm| {
|
||||
vm.add_native_module_def("rust_py_module".to_owned(), rust_py_module::module_def);
|
||||
}))
|
||||
.interpreter();
|
||||
let builder = rustpython::Interpreter::builder(Default::default());
|
||||
let def = rust_py_module::module_def(&builder.ctx);
|
||||
let interp = builder.init_stdlib().add_native_module(def).build();
|
||||
|
||||
interp.enter(|vm| {
|
||||
vm.insert_sys_path(vm.new_pyobj("examples"))
|
||||
|
||||
@@ -42,9 +42,9 @@ gen()
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let interp = vm::Interpreter::with_init(Default::default(), |vm| {
|
||||
vm.add_native_module_defs(rustpython_stdlib::get_module_defs());
|
||||
});
|
||||
let builder = vm::Interpreter::builder(Default::default());
|
||||
let defs = rustpython_stdlib::stdlib_module_defs(&builder.ctx);
|
||||
let interp = builder.add_native_modules(&defs).build();
|
||||
let result = py_main(&interp);
|
||||
vm::common::os::exit_code(interp.run(|_vm| result))
|
||||
}
|
||||
@@ -19,9 +19,9 @@ fn main() -> ExitCode {
|
||||
// Add standard library path
|
||||
let mut settings = vm::Settings::default();
|
||||
settings.path_list.push("Lib".to_owned());
|
||||
let interp = vm::Interpreter::with_init(settings, |vm| {
|
||||
vm.add_native_module_defs(rustpython_stdlib::get_module_defs());
|
||||
});
|
||||
let builder = vm::Interpreter::builder(settings);
|
||||
let defs = rustpython_stdlib::stdlib_module_defs(&builder.ctx);
|
||||
let interp = builder.add_native_modules(&defs).build();
|
||||
let result = py_main(&interp);
|
||||
let result = result.map(|result| {
|
||||
println!("name: {result}");
|
||||
|
||||
+25
-120
@@ -1,139 +1,44 @@
|
||||
use rustpython_vm::{
|
||||
Interpreter, PyRef, Settings, VirtualMachine, builtins::PyModule, stdlib::StdlibDefFunc,
|
||||
};
|
||||
use rustpython_vm::InterpreterBuilder;
|
||||
|
||||
pub type InitHook = Box<dyn FnOnce(&mut VirtualMachine)>;
|
||||
|
||||
/// The convenient way to create [rustpython_vm::Interpreter] with stdlib and other stuffs.
|
||||
///
|
||||
/// Basic usage:
|
||||
/// ```
|
||||
/// let interpreter = rustpython::InterpreterConfig::new()
|
||||
/// .init_stdlib()
|
||||
/// .interpreter();
|
||||
/// ```
|
||||
///
|
||||
/// To override [rustpython_vm::Settings]:
|
||||
/// ```
|
||||
/// use rustpython_vm::Settings;
|
||||
/// // Override your settings here.
|
||||
/// let mut settings = Settings::default();
|
||||
/// settings.debug = 1;
|
||||
/// // You may want to add paths to `rustpython_vm::Settings::path_list` to allow import python libraries.
|
||||
/// settings.path_list.push("Lib".to_owned()); // add standard library directory
|
||||
/// settings.path_list.push("".to_owned()); // add current working directory
|
||||
/// let interpreter = rustpython::InterpreterConfig::new()
|
||||
/// .settings(settings)
|
||||
/// .interpreter();
|
||||
/// ```
|
||||
///
|
||||
/// To add native modules:
|
||||
/// ```
|
||||
/// use rustpython_vm::pymodule;
|
||||
///
|
||||
/// #[pymodule]
|
||||
/// mod your_module {}
|
||||
///
|
||||
/// let interpreter = rustpython::InterpreterConfig::new()
|
||||
/// .init_stdlib()
|
||||
/// .add_native_module_def(
|
||||
/// "your_module_name".to_owned(),
|
||||
/// your_module::module_def,
|
||||
/// )
|
||||
/// .interpreter();
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct InterpreterConfig {
|
||||
settings: Option<Settings>,
|
||||
init_hooks: Vec<InitHook>,
|
||||
}
|
||||
|
||||
impl InterpreterConfig {
|
||||
/// Creates a new interpreter configuration with default settings.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Builds the interpreter with the current configuration.
|
||||
pub fn interpreter(self) -> Interpreter {
|
||||
let settings = self.settings.unwrap_or_default();
|
||||
Interpreter::with_init(settings, |vm| {
|
||||
for hook in self.init_hooks {
|
||||
hook(vm);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets custom settings for the interpreter.
|
||||
///
|
||||
/// If called multiple times, only the last settings will be used.
|
||||
pub fn settings(mut self, settings: Settings) -> Self {
|
||||
self.settings = Some(settings);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a custom initialization hook.
|
||||
///
|
||||
/// Hooks are executed in the order they are added during interpreter creation.
|
||||
pub fn init_hook(mut self, hook: InitHook) -> Self {
|
||||
self.init_hooks.push(hook);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a native module definition to the interpreter.
|
||||
pub fn add_native_module_def(self, name: String, def_func: StdlibDefFunc) -> Self {
|
||||
self.init_hook(Box::new(move |vm| vm.add_native_module_def(name, def_func)))
|
||||
}
|
||||
|
||||
/// Adds a native module to the interpreter.
|
||||
#[deprecated(note = "use add_native_module_def with multi-phase module initialization")]
|
||||
#[allow(deprecated)]
|
||||
pub fn add_native_module(
|
||||
self,
|
||||
name: String,
|
||||
make_module: fn(&VirtualMachine) -> PyRef<PyModule>,
|
||||
) -> Self {
|
||||
self.init_hook(Box::new(move |vm| {
|
||||
vm.add_native_module(name, Box::new(make_module))
|
||||
}))
|
||||
}
|
||||
|
||||
/// Initializes the Python standard library.
|
||||
/// Extension trait for InterpreterBuilder to add rustpython-specific functionality.
|
||||
pub trait InterpreterBuilderExt {
|
||||
/// Initialize the Python standard library.
|
||||
///
|
||||
/// Requires the `stdlib` feature to be enabled.
|
||||
#[cfg(feature = "stdlib")]
|
||||
pub fn init_stdlib(self) -> Self {
|
||||
self.init_hook(Box::new(init_stdlib))
|
||||
fn init_stdlib(self) -> Self;
|
||||
}
|
||||
|
||||
impl InterpreterBuilderExt for InterpreterBuilder {
|
||||
#[cfg(feature = "stdlib")]
|
||||
fn init_stdlib(self) -> Self {
|
||||
let defs = rustpython_stdlib::stdlib_module_defs(&self.ctx);
|
||||
let builder = self.add_native_modules(&defs);
|
||||
|
||||
#[cfg(feature = "freeze-stdlib")]
|
||||
let builder = builder
|
||||
.add_frozen_modules(rustpython_pylib::FROZEN_STDLIB)
|
||||
.init_hook(set_frozen_stdlib_dir);
|
||||
|
||||
#[cfg(not(feature = "freeze-stdlib"))]
|
||||
let builder = builder.init_hook(setup_dynamic_stdlib);
|
||||
|
||||
builder
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes all standard library modules for the given VM.
|
||||
#[cfg(feature = "stdlib")]
|
||||
pub fn init_stdlib(vm: &mut VirtualMachine) {
|
||||
vm.add_native_module_defs(rustpython_stdlib::get_module_defs());
|
||||
|
||||
#[cfg(feature = "freeze-stdlib")]
|
||||
setup_frozen_stdlib(vm);
|
||||
|
||||
#[cfg(not(feature = "freeze-stdlib"))]
|
||||
setup_dynamic_stdlib(vm);
|
||||
}
|
||||
|
||||
/// Setup frozen standard library (compiled into the binary)
|
||||
/// Set stdlib_dir for frozen standard library
|
||||
#[cfg(all(feature = "stdlib", feature = "freeze-stdlib"))]
|
||||
fn setup_frozen_stdlib(vm: &mut VirtualMachine) {
|
||||
fn set_frozen_stdlib_dir(vm: &mut crate::VirtualMachine) {
|
||||
use rustpython_vm::common::rc::PyRc;
|
||||
|
||||
vm.add_frozen(rustpython_pylib::FROZEN_STDLIB);
|
||||
|
||||
// Set stdlib_dir to the frozen stdlib path
|
||||
let state = PyRc::get_mut(&mut vm.state).unwrap();
|
||||
state.config.paths.stdlib_dir = Some(rustpython_pylib::LIB_PATH.to_owned());
|
||||
}
|
||||
|
||||
/// Setup dynamic standard library loading from filesystem
|
||||
#[cfg(all(feature = "stdlib", not(feature = "freeze-stdlib")))]
|
||||
fn setup_dynamic_stdlib(vm: &mut VirtualMachine) {
|
||||
fn setup_dynamic_stdlib(vm: &mut crate::VirtualMachine) {
|
||||
use rustpython_vm::common::rc::PyRc;
|
||||
|
||||
let state = PyRc::get_mut(&mut vm.state).unwrap();
|
||||
|
||||
+24
-19
@@ -1,23 +1,29 @@
|
||||
//! This is the `rustpython` binary. If you're looking to embed RustPython into your application,
|
||||
//! you're likely looking for the [`rustpython_vm`] crate.
|
||||
//!
|
||||
//! You can install `rustpython` with `cargo install rustpython`, or if you'd like to inject your
|
||||
//! own native modules you can make a binary crate that depends on the `rustpython` crate (and
|
||||
//! You can install `rustpython` with `cargo install rustpython`. If you'd like to inject your
|
||||
//! own native modules, you can make a binary crate that depends on the `rustpython` crate (and
|
||||
//! probably [`rustpython_vm`], too), and make a `main.rs` that looks like:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use rustpython::{InterpreterBuilder, InterpreterBuilderExt};
|
||||
//! use rustpython_vm::{pymodule, py_freeze};
|
||||
//! fn main() {
|
||||
//! rustpython::run(|vm| {
|
||||
//! vm.add_native_module_def("my_mod".to_owned(), my_mod::module_def);
|
||||
//! vm.add_frozen(py_freeze!(source = "def foo(): pass", module_name = "other_thing"));
|
||||
//! });
|
||||
//!
|
||||
//! fn main() -> std::process::ExitCode {
|
||||
//! let builder = InterpreterBuilder::new().init_stdlib();
|
||||
//! // Add a native module using builder.ctx
|
||||
//! let my_mod_def = my_mod::module_def(&builder.ctx);
|
||||
//! let builder = builder
|
||||
//! .add_native_module(my_mod_def)
|
||||
//! // Add a frozen module
|
||||
//! .add_frozen_modules(py_freeze!(source = "def foo(): pass", module_name = "other_thing"));
|
||||
//!
|
||||
//! rustpython::run(builder)
|
||||
//! }
|
||||
//!
|
||||
//! #[pymodule]
|
||||
//! mod my_mod {
|
||||
//! use rustpython_vm::builtins::PyStrRef;
|
||||
//TODO: use rustpython_vm::prelude::*;
|
||||
//!
|
||||
//! #[pyfunction]
|
||||
//! fn do_thing(x: i32) -> i32 {
|
||||
@@ -54,8 +60,8 @@ use std::env;
|
||||
use std::io::IsTerminal;
|
||||
use std::process::ExitCode;
|
||||
|
||||
pub use interpreter::InterpreterConfig;
|
||||
pub use rustpython_vm as vm;
|
||||
pub use interpreter::InterpreterBuilderExt;
|
||||
pub use rustpython_vm::{self as vm, Interpreter, InterpreterBuilder};
|
||||
pub use settings::{InstallPipMode, RunMode, parse_opts};
|
||||
pub use shell::run_shell;
|
||||
|
||||
@@ -69,7 +75,11 @@ compile_error!(
|
||||
|
||||
/// The main cli of the `rustpython` interpreter. This function will return `std::process::ExitCode`
|
||||
/// based on the return code of the python code ran through the cli.
|
||||
pub fn run(init: impl FnOnce(&mut VirtualMachine) + 'static) -> ExitCode {
|
||||
///
|
||||
/// **Note**: This function provides no way to further initialize the VM after the builder is applied.
|
||||
/// All VM initialization (adding native modules, init hooks, etc.) must be done through the
|
||||
/// [`InterpreterBuilder`] parameter before calling this function.
|
||||
pub fn run(mut builder: InterpreterBuilder) -> ExitCode {
|
||||
env_logger::init();
|
||||
|
||||
// NOTE: This is not a WASI convention. But it will be convenient since POSIX shell always defines it.
|
||||
@@ -101,14 +111,9 @@ pub fn run(init: impl FnOnce(&mut VirtualMachine) + 'static) -> ExitCode {
|
||||
}
|
||||
}
|
||||
|
||||
let mut config = InterpreterConfig::new().settings(settings);
|
||||
#[cfg(feature = "stdlib")]
|
||||
{
|
||||
config = config.init_stdlib();
|
||||
}
|
||||
config = config.init_hook(Box::new(init));
|
||||
builder = builder.settings(settings);
|
||||
|
||||
let interp = config.interpreter();
|
||||
let interp = builder.interpreter();
|
||||
let exitcode = interp.run(move |vm| run_rustpython(vm, run_mode));
|
||||
|
||||
rustpython_vm::common::os::exit_code(exitcode)
|
||||
@@ -351,7 +356,7 @@ mod tests {
|
||||
use rustpython_vm::Interpreter;
|
||||
|
||||
fn interpreter() -> Interpreter {
|
||||
InterpreterConfig::new().init_stdlib().interpreter()
|
||||
InterpreterBuilder::new().init_stdlib().interpreter()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+8
-1
@@ -1,3 +1,10 @@
|
||||
use rustpython::{InterpreterBuilder, InterpreterBuilderExt};
|
||||
|
||||
pub fn main() -> std::process::ExitCode {
|
||||
rustpython::run(|_vm| {})
|
||||
let mut config = InterpreterBuilder::new();
|
||||
#[cfg(feature = "stdlib")]
|
||||
{
|
||||
config = config.init_stdlib();
|
||||
}
|
||||
rustpython::run(config)
|
||||
}
|
||||
Reference in new issue
Block a user