Merge pull request #6740 from youknowone/multi-phase-module-init

Multi phase module init
This commit is contained in:
Jeong, YunWon
2026-01-22 12:12:43 +09:00
committed by GitHub
108 changed files with 1230 additions and 1111 deletions

View File

@@ -136,11 +136,6 @@ class PyPicklerTests(AbstractPickleTests, unittest.TestCase):
def test_buffers_error(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_buffers_error()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_builtin_functions(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_builtin_functions()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytearray_memoization(self): # TODO(RUSTPYTHON): Remove this test when it passes
@@ -166,42 +161,6 @@ class PyPicklerTests(AbstractPickleTests, unittest.TestCase):
def test_oob_buffers_writable_to_readonly(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_oob_buffers_writable_to_readonly()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_buffers_error(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_buffers_error()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_builtin_functions(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_builtin_functions()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytearray_memoization(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_bytearray_memoization()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytes_memoization(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_bytes_memoization()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_in_band_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_in_band_buffers()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_oob_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_oob_buffers()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_oob_buffers_writable_to_readonly(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_oob_buffers_writable_to_readonly()
class InMemoryPickleTests(AbstractPickleTests, AbstractUnpickleTests,
BigmemPickleTests, unittest.TestCase):
@@ -250,11 +209,6 @@ class InMemoryPickleTests(AbstractPickleTests, AbstractUnpickleTests,
def test_buffers_error(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_buffers_error()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_builtin_functions(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_builtin_functions()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytearray_memoization(self): # TODO(RUSTPYTHON): Remove this test when it passes

View File

@@ -67,11 +67,6 @@ class OptimizedPickleTests(AbstractPickleTests, unittest.TestCase):
def test_buffers_error(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_buffers_error()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_builtin_functions(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_builtin_functions()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytearray_memoization(self): # TODO(RUSTPYTHON): Remove this test when it passes

View File

@@ -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(|| {

View File

@@ -113,12 +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| {
for (name, init) in rustpython_stdlib::get_module_inits() {
vm.add_native_module(name, init);
}
})
.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");

View File

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

View File

@@ -58,7 +58,7 @@ struct ModuleContext {
name: String,
function_items: FunctionNursery,
attribute_items: ItemNursery,
has_extend_module: bool, // TODO: check if `fn extend_module` exists
has_module_exec: bool,
errors: Vec<syn::Error>,
}
@@ -82,6 +82,12 @@ pub fn impl_pymodule(attr: PunctuatedNestedMeta, module_item: Item) -> Result<To
// collect to context
for item in items.iter_mut() {
// Check if module_exec function is already defined
if let Item::Fn(func) = item
&& func.sig.ident == "module_exec"
{
context.has_module_exec = true;
}
if matches!(item, Item::Impl(_) | Item::Trait(_)) {
// #[pyclass] implementations
continue;
@@ -123,7 +129,7 @@ pub fn impl_pymodule(attr: PunctuatedNestedMeta, module_item: Item) -> Result<To
pub(crate) const DOC: Option<&'static str> = #doc;
},
parse_quote! {
pub(crate) fn __module_def(
pub(crate) fn module_def(
ctx: &::rustpython_vm::Context,
) -> &'static ::rustpython_vm::builtins::PyModuleDef {
DEF.get_or_init(|| {
@@ -133,29 +139,17 @@ pub fn impl_pymodule(attr: PunctuatedNestedMeta, module_item: Item) -> Result<To
methods: METHOD_DEFS,
slots: Default::default(),
};
def.slots.exec = Some(extend_module);
def.slots.exec = Some(module_exec);
def
})
}
},
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);
extend_module(vm, &module).unwrap();
module
}
},
]);
}
if !is_submodule && !context.has_extend_module {
if !is_submodule && !context.has_module_exec {
items.push(parse_quote! {
pub(crate) fn extend_module(vm: &::rustpython_vm::VirtualMachine, module: &::rustpython_vm::Py<::rustpython_vm::builtins::PyModule>) -> ::rustpython_vm::PyResult<()> {
__extend_module(vm, module);
pub(crate) fn module_exec(vm: &::rustpython_vm::VirtualMachine, module: &::rustpython_vm::Py<::rustpython_vm::builtins::PyModule>) -> ::rustpython_vm::PyResult<()> {
__module_exec(vm, module);
Ok(())
}
});
@@ -192,11 +186,10 @@ pub fn impl_pymodule(attr: PunctuatedNestedMeta, module_item: Item) -> Result<To
}
},
parse_quote! {
pub(crate) fn __extend_module(
pub(crate) fn __module_exec(
vm: &::rustpython_vm::VirtualMachine,
module: &::rustpython_vm::Py<::rustpython_vm::builtins::PyModule>,
) {
module.__init_methods(vm).unwrap();
__init_attributes(vm, module);
}
},

View File

@@ -143,8 +143,8 @@ 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 a `make_module` function in the module;
/// this has to be used in a `get_module_inits` to properly register the module.
/// It defines a Python module in the form of a `module_def` function in 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,

View File

@@ -1,7 +1,7 @@
pub(crate) use opcode::make_module;
pub(crate) use _opcode::module_def;
#[pymodule]
mod opcode {
mod _opcode {
use crate::vm::{
AsObject, PyObjectRef, PyResult, VirtualMachine,
builtins::{PyInt, PyIntRef},

View File

@@ -8,18 +8,10 @@
// spell-checker:ignore cantlock commithook foreignkey notnull primarykey gettemppath autoindex convpath
// spell-checker:ignore dbmoved vnode nbytes
use rustpython_vm::{AsObject, PyRef, VirtualMachine, builtins::PyModule};
// pub(crate) use _sqlite::make_module;
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
// TODO: sqlite version check
let module = _sqlite::make_module(vm);
_sqlite::setup_module(module.as_object(), vm);
module
}
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,
@@ -58,8 +50,8 @@ mod _sqlite {
TryFromBorrowedObject, VirtualMachine, atomic_func,
builtins::{
PyBaseException, PyBaseExceptionRef, PyByteArray, PyBytes, PyDict, PyDictRef, PyFloat,
PyInt, PyIntRef, PySlice, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef,
PyUtf8Str, PyUtf8StrRef,
PyInt, PyIntRef, PyModule, PySlice, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType,
PyTypeRef, PyUtf8Str, PyUtf8StrRef,
},
convert::IntoObject,
function::{
@@ -852,26 +844,26 @@ mod _sqlite {
.expect("enable traceback not initialize")
}
pub(super) fn setup_module(module: &PyObject, vm: &VirtualMachine) {
pub(crate) fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
__module_exec(vm, module);
for (name, code) in ERROR_CODES {
let name = vm.ctx.intern_str(*name);
let code = vm.new_pyobj(*code);
module.set_attr(name, code, vm).unwrap();
module.set_attr(name, code, vm)?;
}
setup_module_exceptions(module, vm);
setup_module_exceptions(module.as_object(), vm);
let _ = CONVERTERS.set(vm.ctx.new_dict());
let _ = ADAPTERS.set(vm.ctx.new_dict());
let _ = USER_FUNCTION_EXCEPTION.set(PyAtomicRef::from(None));
let _ = ENABLE_TRACEBACK.set(Radium::new(false));
module
.set_attr("converters", converters().to_owned(), vm)
.unwrap();
module
.set_attr("adapters", adapters().to_owned(), vm)
.unwrap();
module.set_attr("converters", converters().to_owned(), vm)?;
module.set_attr("adapters", adapters().to_owned(), vm)?;
Ok(())
}
#[pyattr]

View File

@@ -1,33 +1,6 @@
// spell-checker:ignore typecode tofile tolist fromfile
use rustpython_vm::{PyRef, VirtualMachine, builtins::PyModule};
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
let module = array::make_module(vm);
let array = module
.get_attr("array", vm)
.expect("Expect array has array type.");
let collections_abc = vm
.import("collections.abc", 0)
.expect("Expect collections exist.");
let abc = collections_abc
.get_attr("abc", vm)
.expect("Expect collections has abc submodule.");
let mutable_sequence = abc
.get_attr("MutableSequence", vm)
.expect("Expect collections.abc has MutableSequence type.");
let register = &mutable_sequence
.get_attr("register", vm)
.expect("Expect collections.abc.MutableSequence has register method.");
register
.call((array,), vm)
.expect("Expect collections.abc.MutableSequence.register(array.array) not fail.");
module
}
pub(crate) use array::module_def;
#[pymodule(name = "array")]
mod array {
@@ -1658,4 +1631,25 @@ mod array {
};
PyArray::from(array).into_ref_with_type(vm, cls)
}
// Register array.array as collections.abc.MutableSequence
pub(crate) fn module_exec(
vm: &VirtualMachine,
module: &Py<crate::vm::builtins::PyModule>,
) -> PyResult<()> {
__module_exec(vm, module);
let array_type = module
.get_attr("array", vm)
.expect("array module has array type");
// vm.import returns the top-level module, so we need to get abc submodule
let collections_abc = vm.import("collections.abc", 0)?;
let abc = collections_abc.get_attr("abc", vm)?;
let mutable_sequence = abc.get_attr("MutableSequence", vm)?;
let register = mutable_sequence.get_attr("register", vm)?;
register.call((array_type,), vm)?;
Ok(())
}
}

View File

@@ -1,7 +1,7 @@
// spell-checker:ignore hexlify unhexlify uuencodes CRCTAB rlecode rledecode
pub(super) use decl::crc32;
pub(crate) use decl::make_module;
pub(crate) use decl::module_def;
use rustpython_vm::{VirtualMachine, builtins::PyBaseExceptionRef, convert::ToPyException};
const PAD: u8 = 61u8;

View File

@@ -1,4 +1,4 @@
pub(crate) use _bisect::make_module;
pub(crate) use _bisect::module_def;
#[pymodule]
mod _bisect {

View File

@@ -1,6 +1,6 @@
// spell-checker:ignore usedforsecurity HASHXOF
pub(crate) use _blake2::make_module;
pub(crate) use _blake2::module_def;
#[pymodule]
mod _blake2 {

View File

@@ -1,6 +1,6 @@
// spell-checker:ignore compresslevel
pub(crate) use _bz2::make_module;
pub(crate) use _bz2::module_def;
#[pymodule]
mod _bz2 {

View File

@@ -1,4 +1,4 @@
pub(crate) use cmath::make_module;
pub(crate) use cmath::module_def;
#[pymodule]
mod cmath {

View File

@@ -1,20 +1,9 @@
use crate::vm::{PyRef, VirtualMachine, builtins::PyModule, class::StaticType};
pub(crate) use _contextvars::module_def;
use crate::vm::PyRef;
use _contextvars::PyContext;
use core::cell::RefCell;
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
let module = _contextvars::make_module(vm);
let token_type = module.get_attr("Token", vm).unwrap();
token_type
.set_attr(
"MISSING",
_contextvars::ContextTokenMissing::static_type().to_owned(),
vm,
)
.unwrap();
module
}
thread_local! {
// TODO: Vec doesn't seem to match copy behavior
static CONTEXTS: RefCell<Vec<PyRef<PyContext>>> = RefCell::default();
@@ -643,4 +632,17 @@ mod _contextvars {
fn copy_context(vm: &VirtualMachine) -> PyContext {
PyContext::current(vm).copy()
}
// Set Token.MISSING attribute
pub(crate) fn module_exec(
vm: &VirtualMachine,
module: &Py<crate::vm::builtins::PyModule>,
) -> PyResult<()> {
__module_exec(vm, module);
let token_type = module.get_attr("Token", vm)?;
token_type.set_attr("MISSING", ContextTokenMissing::static_type().to_owned(), vm)?;
Ok(())
}
}

View File

@@ -1,4 +1,4 @@
pub(crate) use _csv::make_module;
pub(crate) use _csv::module_def;
#[pymodule]
mod _csv {

View File

@@ -1,4 +1,4 @@
pub(crate) use decl::make_module;
pub(crate) use decl::module_def;
#[allow(static_mut_refs)] // TODO: group code only with static mut refs
#[pymodule(name = "faulthandler")]

View File

@@ -1,6 +1,6 @@
// spell-checker:disable
pub(crate) use fcntl::make_module;
pub(crate) use fcntl::module_def;
#[pymodule]
mod fcntl {

View File

@@ -1,4 +1,4 @@
pub(crate) use gc::make_module;
pub(crate) use gc::module_def;
#[pymodule]
mod gc {

View File

@@ -1,5 +1,5 @@
// spell-checker:disable
pub(crate) use grp::make_module;
pub(crate) use grp::module_def;
#[pymodule]
mod grp {

View File

@@ -1,6 +1,6 @@
// spell-checker:ignore usedforsecurity HASHXOF
pub(crate) use _hashlib::make_module;
pub(crate) use _hashlib::module_def;
#[pymodule]
pub mod _hashlib {

View File

@@ -1,4 +1,4 @@
pub(crate) use _json::make_module;
pub(crate) use _json::module_def;
mod machinery;
#[pymodule]

View File

@@ -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::StdlibInitFunc};
use alloc::borrow::Cow;
use crate::vm::{Context, builtins};
pub fn get_module_inits() -> impl Iterator<Item = (Cow<'static, str>, StdlibInitFunc)> {
macro_rules! modules {
{
$(
#[cfg($cfg:meta)]
{ $( $key:expr => $val:expr),* $(,)? }
)*
} => {{
[
$(
$(#[cfg($cfg)] (Cow::<'static, str>::from($key), Box::new($val) as StdlibInitFunc),)*
)*
]
.into_iter()
}};
}
modules! {
#[cfg(all())]
{
"array" => array::make_module,
"binascii" => binascii::make_module,
"_bisect" => bisect::make_module,
"_bz2" => bz2::make_module,
"cmath" => cmath::make_module,
"_contextvars" => contextvars::make_module,
"_csv" => csv::make_module,
"faulthandler" => faulthandler::make_module,
"gc" => gc::make_module,
"_hashlib" => hashlib::make_module,
"_sha1" => sha1::make_module,
"_sha3" => sha3::make_module,
"_sha256" => sha256::make_module,
"_sha512" => sha512::make_module,
"_md5" => md5::make_module,
"_blake2" => blake2::make_module,
"_json" => json::make_module,
"math" => math::make_module,
"pyexpat" => pyexpat::make_module,
"_opcode" => opcode::make_module,
"_random" => random::make_module,
"_statistics" => statistics::make_module,
"_struct" => pystruct::make_module,
"unicodedata" => unicodedata::make_module,
"zlib" => zlib::make_module,
"_statistics" => statistics::make_module,
"_suggestions" => suggestions::make_module,
// crate::vm::sysmodule::sysconfigdata_name() => sysconfigdata::make_module,
}
/// 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 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::make_module,
}
#[cfg(any(unix, windows, target_os = "wasi"))]
{
"select" => select::make_module,
}
#[cfg(not(target_arch = "wasm32"))]
{
"_multiprocessing" => multiprocessing::make_module,
"_socket" => socket::make_module,
}
#[cfg(not(any(target_os = "android", target_arch = "wasm32")))]
{
"_lzma" => lzma::make_module,
}
#[cfg(all(feature = "sqlite", not(any(target_os = "android", target_arch = "wasm32"))))]
{
"_sqlite3" => sqlite::make_module,
}
#[cfg(all(not(target_arch = "wasm32"), feature = "ssl-rustls"))]
{
"_ssl" => ssl::make_module,
}
#[cfg(all(not(target_arch = "wasm32"), feature = "ssl-openssl"))]
{
"_ssl" => openssl::make_module,
}
#[cfg(windows)]
{
"_overlapped" => overlapped::make_module,
}
// Unix-only
#[cfg(unix)]
{
"_posixsubprocess" => posixsubprocess::make_module,
}
#[cfg(all(unix, not(target_os = "redox"), not(target_os = "android")))]
{
"_posixshmem" => posixshmem::make_module,
}
#[cfg(any(unix, windows))]
{
"mmap" => mmap::make_module,
}
#[cfg(all(unix, not(target_os = "redox")))]
{
"syslog" => syslog::make_module,
"resource" => resource::make_module,
}
#[cfg(all(unix, not(any(target_os = "ios", target_os = "redox"))))]
{
"termios" => termios::make_module,
}
fcntl::module_def(ctx),
gc::module_def(ctx),
#[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))]
{
"grp" => grp::make_module,
}
#[cfg(target_os = "macos")]
{
"_scproxy" => scproxy::make_module,
}
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "windows", target_arch = "wasm32", target_os = "redox")))]
{
"_uuid" => uuid::make_module,
}
grp::module_def(ctx),
hashlib::module_def(ctx),
json::module_def(ctx),
#[cfg(not(any(target_os = "ios", target_arch = "wasm32")))]
{
"_locale" => locale::make_module,
}
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::make_module,
}
}
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),
]
}

View File

@@ -1,6 +1,6 @@
// spell-checker:ignore abday abmon yesexpr noexpr CRNCYSTR RADIXCHAR AMPM THOUSEP
pub(crate) use _locale::make_module;
pub(crate) use _locale::module_def;
#[cfg(windows)]
#[repr(C)]

View File

@@ -1,6 +1,6 @@
// spell-checker:ignore ARMTHUMB
pub(crate) use _lzma::make_module;
pub(crate) use _lzma::module_def;
#[pymodule]
mod _lzma {

View File

@@ -1,4 +1,4 @@
pub(crate) use math::make_module;
pub(crate) use math::module_def;
use crate::vm::{VirtualMachine, builtins::PyBaseExceptionRef};

View File

@@ -1,4 +1,4 @@
pub(crate) use _md5::make_module;
pub(crate) use _md5::module_def;
#[pymodule]
mod _md5 {

View File

@@ -1,6 +1,6 @@
// spell-checker:disable
//! mmap module
pub(crate) use mmap::make_module;
pub(crate) use mmap::module_def;
#[pymodule]
mod mmap {

View File

@@ -1,4 +1,4 @@
pub(crate) use _multiprocessing::make_module;
pub(crate) use _multiprocessing::module_def;
#[cfg(windows)]
#[pymodule]

View File

@@ -23,32 +23,25 @@ cfg_if::cfg_if! {
}
}
use crate::vm::{PyRef, VirtualMachine, builtins::PyModule};
pub(crate) use _ssl::module_def;
use openssl_probe::ProbeResult;
use std::sync::LazyLock;
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
// if openssl is vendored, it doesn't know the locations
// of system certificates - cache the probe result now.
#[cfg(openssl_vendored)]
LazyLock::force(&PROBE);
_ssl::make_module(vm)
}
// define our own copy of ProbeResult so we can handle the vendor case
// easily, without having to have a bunch of cfgs
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 {
@@ -67,8 +60,8 @@ mod _ssl {
vm::{
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
builtins::{
PyBaseException, PyBaseExceptionRef, PyBytesRef, PyListRef, PyStrRef, PyType,
PyWeak,
PyBaseException, PyBaseExceptionRef, PyBytesRef, PyListRef, PyModule, PyStrRef,
PyType, PyWeak,
},
class_or_notimplemented,
convert::ToPyException,
@@ -104,6 +97,16 @@ mod _ssl {
// Import certificate types from parent module
use super::cert::{self, cert_to_certificate, cert_to_py};
pub(crate) fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
// 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(&super::PROBE);
__module_exec(vm, module);
Ok(())
}
// Re-export PySSLCertificate to make it available in the _ssl module
// It will be automatically exposed to Python via #[pyclass]
#[allow(unused_imports)]

View File

@@ -1,6 +1,6 @@
// spell-checker:disable
pub(crate) use _overlapped::make_module;
pub(crate) use _overlapped::module_def;
#[allow(non_snake_case)]
#[pymodule]

View File

@@ -1,5 +1,5 @@
#[cfg(all(unix, not(target_os = "redox"), not(target_os = "android")))]
pub(crate) use _posixshmem::make_module;
pub(crate) use _posixshmem::module_def;
#[cfg(all(unix, not(target_os = "redox"), not(target_os = "android")))]
#[pymodule]

View File

@@ -22,7 +22,7 @@ use alloc::ffi::CString;
use core::{convert::Infallible as Never, ffi::CStr, marker::PhantomData, ops::Deref};
pub(crate) use _posixsubprocess::make_module;
pub(crate) use _posixsubprocess::module_def;
#[pymodule]
mod _posixsubprocess {

View File

@@ -2,18 +2,7 @@
// spell-checker: ignore libexpat
use crate::vm::{PyRef, VirtualMachine, builtins::PyModule, extend_module};
pub fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
let module = _pyexpat::make_module(vm);
extend_module!(vm, &module, {
"errors" => _errors::make_module(vm),
"model" => _model::make_module(vm),
});
module
}
pub(crate) use _pyexpat::module_def;
macro_rules! create_property {
($ctx: expr, $attributes: expr, $name: expr, $class: expr, $element: ident) => {
@@ -52,7 +41,8 @@ macro_rules! create_bool_property {
mod _pyexpat {
use crate::vm::{
Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
builtins::{PyBytesRef, PyStr, PyStrRef, PyType},
builtins::{PyBytesRef, PyModule, PyStr, PyStrRef, PyType},
extend_module,
function::ArgBytesLike,
function::{Either, IntoFuncArgs, OptionalArg},
};
@@ -60,6 +50,21 @@ mod _pyexpat {
use std::io::Cursor;
use xml::reader::XmlEvent;
pub(crate) fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
__module_exec(vm, module);
// Add submodules
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(())
}
type MutableObject = PyRwLock<PyObjectRef>;
#[pyattr(name = "version_info")]

View File

@@ -5,7 +5,7 @@
//! Use this rust module to do byte packing:
//! <https://docs.rs/byteorder/1.2.6/byteorder/>
pub(crate) use _struct::make_module;
pub(crate) use _struct::module_def;
#[pymodule]
pub(crate) mod _struct {

View File

@@ -1,6 +1,6 @@
//! Random module.
pub(crate) use _random::make_module;
pub(crate) use _random::module_def;
#[pymodule]
mod _random {

View File

@@ -1,4 +1,4 @@
pub(crate) use re::make_module;
pub(crate) use re::module_def;
#[pymodule]
mod re {

View File

@@ -1,6 +1,6 @@
// spell-checker:disable
pub(crate) use resource::make_module;
pub(crate) use resource::module_def;
#[pymodule]
mod resource {

View File

@@ -1,4 +1,4 @@
pub(crate) use _scproxy::make_module;
pub(crate) use _scproxy::module_def;
#[pymodule]
mod _scproxy {

View File

@@ -1,25 +1,13 @@
// spell-checker:disable
pub(crate) use decl::module_def;
use crate::vm::{
PyObject, PyObjectRef, PyRef, PyResult, TryFromObject, VirtualMachine, builtins::PyListRef,
builtins::PyModule,
PyObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine, builtins::PyListRef,
};
use core::mem;
use std::io;
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
#[cfg(windows)]
crate::vm::windows::init_winsock();
#[cfg(unix)]
{
use crate::vm::class::PyClassImpl;
decl::poll::PyPoll::make_class(&vm.ctx);
}
decl::make_module(vm)
}
#[cfg(unix)]
mod platform {
pub use libc::{FD_ISSET, FD_SET, FD_SETSIZE, FD_ZERO, fd_set, select, timeval};
@@ -221,13 +209,27 @@ fn sec_to_timeval(sec: f64) -> timeval {
mod decl {
use super::*;
use crate::vm::{
PyObjectRef, PyResult, VirtualMachine,
builtins::PyTypeRef,
Py, PyObjectRef, PyResult, VirtualMachine,
builtins::{PyModule, PyTypeRef},
convert::ToPyException,
function::{Either, OptionalOption},
stdlib::time,
};
pub(crate) fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
#[cfg(windows)]
crate::vm::windows::init_winsock();
#[cfg(unix)]
{
use crate::vm::class::PyClassImpl;
poll::PyPoll::make_class(&vm.ctx);
}
__module_exec(vm, module);
Ok(())
}
#[pyattr]
fn error(vm: &VirtualMachine) -> PyTypeRef {
vm.ctx.exceptions.os_error.to_owned()
@@ -545,7 +547,7 @@ mod decl {
pub(super) mod epoll {
use super::*;
use crate::vm::{
Py, PyPayload,
Py, PyPayload, PyRef,
builtins::PyType,
common::lock::{PyRwLock, PyRwLockReadGuard},
convert::{IntoPyException, ToPyObject},

View File

@@ -1,4 +1,4 @@
pub(crate) use _sha1::make_module;
pub(crate) use _sha1::module_def;
#[pymodule]
mod _sha1 {

View File

@@ -1,14 +1,7 @@
use crate::vm::{PyRef, VirtualMachine, builtins::PyModule};
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
let _ = vm.import("_hashlib", 0);
_sha256::make_module(vm)
}
#[pymodule]
mod _sha256 {
use crate::hashlib::_hashlib::{HashArgs, local_sha224, local_sha256};
use crate::vm::{PyPayload, PyResult, VirtualMachine};
use crate::vm::{Py, PyPayload, PyResult, VirtualMachine, builtins::PyModule};
#[pyfunction]
fn sha224(args: HashArgs, vm: &VirtualMachine) -> PyResult {
@@ -19,4 +12,12 @@ mod _sha256 {
fn sha256(args: HashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_sha256(args).into_pyobject(vm))
}
pub(crate) fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
let _ = vm.import("_hashlib", 0);
__module_exec(vm, module);
Ok(())
}
}
pub(crate) use _sha256::module_def;

View File

@@ -1,4 +1,4 @@
pub(crate) use _sha3::make_module;
pub(crate) use _sha3::module_def;
#[pymodule]
mod _sha3 {

View File

@@ -1,14 +1,7 @@
use crate::vm::{PyRef, VirtualMachine, builtins::PyModule};
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
let _ = vm.import("_hashlib", 0);
_sha512::make_module(vm)
}
#[pymodule]
mod _sha512 {
use crate::hashlib::_hashlib::{HashArgs, local_sha384, local_sha512};
use crate::vm::{PyPayload, PyResult, VirtualMachine};
use crate::vm::{Py, PyPayload, PyResult, VirtualMachine, builtins::PyModule};
#[pyfunction]
fn sha384(args: HashArgs, vm: &VirtualMachine) -> PyResult {
@@ -19,4 +12,12 @@ mod _sha512 {
fn sha512(args: HashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_sha512(args).into_pyobject(vm))
}
pub(crate) fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
let _ = vm.import("_hashlib", 0);
__module_exec(vm, module);
Ok(())
}
}
pub(crate) use _sha512::module_def;

View File

@@ -1,27 +1,32 @@
// spell-checker:disable
use crate::vm::{PyRef, VirtualMachine, builtins::PyModule};
pub(crate) use _socket::module_def;
#[cfg(feature = "ssl")]
pub(super) use _socket::{PySocket, SelectKind, sock_select, timeout_error_msg};
pub fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
#[cfg(windows)]
crate::vm::windows::init_winsock();
_socket::make_module(vm)
}
#[pymodule]
mod _socket {
use crate::common::lock::{PyMappedRwLockReadGuard, PyRwLock, PyRwLockReadGuard};
use crate::vm::{
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
builtins::{PyBaseExceptionRef, PyListRef, PyOSError, PyStrRef, PyTupleRef, PyTypeRef},
builtins::{
PyBaseExceptionRef, PyListRef, PyModule, PyOSError, PyStrRef, PyTupleRef, PyTypeRef,
},
common::os::ErrorExt,
convert::{IntoPyException, ToPyObject, TryFromBorrowedObject, TryFromObject},
function::{ArgBytesLike, ArgMemoryBuffer, Either, FsPath, OptionalArg, OptionalOption},
types::{Constructor, DefaultConstructor, Initializer, Representable},
utils::ToCString,
};
pub(crate) fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
#[cfg(windows)]
crate::vm::windows::init_winsock();
__module_exec(vm, module);
Ok(())
}
use core::{
mem::MaybeUninit,
net::{Ipv4Addr, Ipv6Addr, SocketAddr},

View File

@@ -25,7 +25,7 @@ mod compat;
// SSL exception types (shared with openssl backend)
mod error;
pub(crate) use _ssl::make_module;
pub(crate) use _ssl::module_def;
#[allow(non_snake_case)]
#[allow(non_upper_case_globals)]

View File

@@ -1,4 +1,4 @@
pub(crate) use _statistics::make_module;
pub(crate) use _statistics::module_def;
#[pymodule]
mod _statistics {

View File

@@ -1,4 +1,4 @@
pub(crate) use _suggestions::make_module;
pub(crate) use _suggestions::module_def;
#[pymodule]
mod _suggestions {

View File

@@ -1,6 +1,6 @@
// spell-checker:ignore logoption openlog setlogmask upto NDELAY ODELAY
pub(crate) use syslog::make_module;
pub(crate) use syslog::module_def;
#[pymodule(name = "syslog")]
mod syslog {

View File

@@ -1,6 +1,6 @@
// spell-checker:disable
pub(crate) use self::termios::make_module;
pub(crate) use self::termios::module_def;
#[pymodule]
mod termios {

View File

@@ -1,6 +1,6 @@
// spell-checker:ignore createcommand
pub(crate) use self::_tkinter::make_module;
pub(crate) use self::_tkinter::module_def;
#[pymodule]
mod _tkinter {

View File

@@ -4,35 +4,12 @@
// spell-checker:ignore nfkc unistr unidata
pub(crate) use unicodedata::module_def;
use crate::vm::{
PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, builtins::PyModule,
builtins::PyStr, convert::TryFromBorrowedObject,
PyObject, PyResult, VirtualMachine, builtins::PyStr, convert::TryFromBorrowedObject,
};
pub fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
let module = unicodedata::make_module(vm);
let ucd: PyObjectRef = unicodedata::Ucd::new(unic_ucd_age::UNICODE_VERSION)
.into_ref(&vm.ctx)
.into();
for attr in [
"category",
"lookup",
"name",
"bidirectional",
"east_asian_width",
"normalize",
"mirrored",
] {
crate::vm::extend_module!(vm, &module, {
attr => ucd.get_attr(attr, vm).unwrap(),
});
}
module
}
enum NormalizeForm {
Nfc,
Nfkc,
@@ -60,7 +37,8 @@ impl<'a> TryFromBorrowedObject<'a> for NormalizeForm {
#[pymodule]
mod unicodedata {
use crate::vm::{
PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, builtins::PyStrRef,
Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
builtins::{PyModule, PyStrRef},
function::OptionalArg,
};
use itertools::Itertools;
@@ -73,6 +51,27 @@ mod unicodedata {
use unic_ucd_category::GeneralCategory;
use unicode_bidi_mirroring::is_mirroring;
pub(crate) fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
__module_exec(vm, module);
// Add UCD methods as module-level functions
let ucd: PyObjectRef = Ucd::new(UNICODE_VERSION).into_ref(&vm.ctx).into();
for attr in [
"category",
"lookup",
"name",
"bidirectional",
"east_asian_width",
"normalize",
"mirrored",
] {
module.set_attr(attr, ucd.get_attr(attr, vm)?, vm)?;
}
Ok(())
}
#[pyattr]
#[pyclass(name = "UCD")]
#[derive(Debug, PyPayload)]

View File

@@ -1,4 +1,4 @@
pub(crate) use _uuid::make_module;
pub(crate) use _uuid::module_def;
#[pymodule]
mod _uuid {

View File

@@ -1,6 +1,6 @@
// spell-checker:ignore compressobj decompressobj zdict chunksize zlibmodule miniz chunker
pub(crate) use zlib::make_module;
pub(crate) use zlib::module_def;
#[pymodule]
mod zlib {

View File

@@ -49,7 +49,7 @@ pub use mappingproxy::PyMappingProxy;
pub(crate) mod memory;
pub use memory::PyMemoryView;
pub(crate) mod module;
pub use module::{PyModule, PyModuleDef};
pub use module::{PyModule, PyModuleDef, PyModuleSlots};
pub(crate) mod namespace;
pub use namespace::PyNamespace;
pub(crate) mod object;

View File

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

View File

@@ -87,16 +87,36 @@ pub fn import_frozen(vm: &VirtualMachine, module_name: &str) -> PyResult {
}
pub fn import_builtin(vm: &VirtualMachine, module_name: &str) -> PyResult {
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);
let sys_modules = vm.sys_module.get_attr("modules", vm)?;
sys_modules.set_item(module_name, module.as_object().to_owned(), vm)?;
Ok(module.into())
// Check if already in sys.modules (handles recursive imports)
if let Ok(module) = sys_modules.get_item(module_name, vm) {
return Ok(module);
}
// Try multi-phase init first (preferred for modules that import other modules)
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 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());
}
// 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")]

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
pub use atexit::_run_exitfuncs;
pub(crate) use atexit::make_module;
pub(crate) use atexit::module_def;
#[pymodule]
mod atexit {

View File

@@ -2,7 +2,7 @@
//!
//! Implements the list of [builtin Python functions](https://docs.python.org/3/library/builtins.html).
use crate::{Py, VirtualMachine, builtins::PyModule, class::PyClassImpl};
pub(crate) use builtins::{__module_def, DOC};
pub(crate) use builtins::{DOC, module_def};
pub use builtins::{ascii, print, reversed};
#[pymodule]
@@ -1100,7 +1100,8 @@ pub fn init_module(vm: &VirtualMachine, module: &Py<PyModule>) {
crate::protocol::VecBuffer::make_class(&vm.ctx);
builtins::extend_module(vm, module).unwrap();
module.__init_methods(vm).unwrap();
builtins::module_exec(vm, module).unwrap();
let debug_mode: bool = vm.state.config.settings.optimize == 0;
// Create dynamic ExceptionGroup with multiple inheritance (BaseExceptionGroup + Exception)

View File

@@ -1,4 +1,4 @@
pub(crate) use _codecs::make_module;
pub(crate) use _codecs::module_def;
#[pymodule]
mod _codecs {

View File

@@ -1,4 +1,4 @@
pub(crate) use _collections::make_module;
pub(crate) use _collections::module_def;
#[pymodule]
mod _collections {

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
pub(crate) use _functools::make_module;
pub(crate) use _functools::module_def;
#[pymodule]
mod _functools {

View File

@@ -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,7 +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())
}
#[pyfunction]
@@ -119,19 +119,44 @@ mod _imp {
let sys_modules = vm.sys_module.get_attr("modules", vm).unwrap();
let name: PyStrRef = spec.get_attr("name", vm)?.try_into_value(vm)?;
let module = if let Ok(module) = sys_modules.get_item(&*name, vm) {
module
} else if let Some(make_module_func) = vm.state.module_inits.get(name.as_str()) {
make_module_func(vm).into()
} else {
vm.ctx.none()
};
Ok(module)
// Check sys.modules first
if let Ok(module) = sys_modules.get_item(&*name, vm) {
return Ok(module);
}
// Try multi-phase init modules first (they need special handling)
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)
};
// 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)?;
// Phase 2: Call exec slot (can safely import other modules now)
if let Some(exec) = def.slots.exec {
exec(vm, &module)?;
}
return Ok(module.into());
}
Ok(vm.ctx.none())
}
#[pyfunction]
fn exec_builtin(_mod: PyRef<PyModule>) -> i32 {
// TODO: Should we do something here?
// For multi-phase init modules, exec is already called in create_builtin
0
}

View File

@@ -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::extend_module(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]

View File

@@ -1,4 +1,4 @@
pub(crate) use decl::make_module;
pub(crate) use decl::module_def;
#[pymodule(name = "itertools")]
mod decl {

View File

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

View File

@@ -62,95 +62,66 @@ mod winapi;
#[cfg(windows)]
mod winreg;
use crate::{PyRef, VirtualMachine, builtins::PyModule};
use alloc::borrow::Cow;
use std::collections::HashMap;
use crate::{Context, builtins::PyModuleDef};
pub type StdlibInitFunc = Box<py_dyn_fn!(dyn Fn(&VirtualMachine) -> PyRef<PyModule>)>;
pub type StdlibMap = HashMap<Cow<'static, str>, StdlibInitFunc, 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,
}
}
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),
]
}

View File

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

View File

@@ -1,4 +1,4 @@
pub(crate) use _operator::make_module;
pub(crate) use _operator::module_def;
#[pymodule]
mod _operator {

View File

@@ -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;
@@ -906,9 +907,11 @@ pub(super) mod _os {
#[pyarg(any, default)]
#[pystruct_sequence(skip)]
pub st_blocks: i64,
#[cfg(windows)]
#[pyarg(any, default)]
#[pystruct_sequence(skip)]
pub st_reparse_tag: u32,
#[cfg(windows)]
#[pyarg(any, default)]
#[pystruct_sequence(skip)]
pub st_file_attributes: u32,
@@ -943,13 +946,8 @@ pub(super) mod _os {
#[cfg(windows)]
let st_reparse_tag = stat.st_reparse_tag;
#[cfg(not(windows))]
let st_reparse_tag = 0;
#[cfg(windows)]
let st_file_attributes = stat.st_file_attributes;
#[cfg(not(windows))]
let st_file_attributes = 0;
// On Windows, combine st_ino and st_ino_high into a 128-bit value
// like _pystat_l128_from_l64_l64
@@ -986,7 +984,9 @@ pub(super) mod _os {
st_blksize,
#[cfg(not(windows))]
st_blocks,
#[cfg(windows)]
st_reparse_tag,
#[cfg(windows)]
st_file_attributes,
}
}
@@ -1897,21 +1897,21 @@ impl SupportFunc {
}
}
pub fn extend_module(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)?;
}
}
@@ -1921,6 +1921,8 @@ pub fn extend_module(vm: &VirtualMachine, module: &Py<PyModule>) {
"supports_follow_symlinks" => supports_follow_symlinks,
"error" => vm.ctx.exceptions.os_error.to_owned(),
});
Ok(())
}
#[cfg(not(windows))]

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
// spell-checker:disable
pub(crate) use pwd::make_module;
pub(crate) use pwd::module_def;
#[pymodule]
mod pwd {

View File

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

View File

@@ -1,4 +1,4 @@
pub(crate) use _sre::make_module;
pub(crate) use _sre::module_def;
#[pymodule]
mod _sre {

View File

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

View File

@@ -1,7 +1,7 @@
/* String builtin module
*/
pub(crate) use _string::make_module;
pub(crate) use _string::module_def;
#[pymodule]
mod _string {

View File

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

View File

@@ -1,8 +1,6 @@
use crate::{Py, PyResult, VirtualMachine, builtins::PyModule, convert::ToPyObject};
pub(crate) use sys::{
__module_def, DOC, MAXSIZE, RUST_MULTIARCH, UnraisableHookArgsData, multiarch,
};
pub(crate) use sys::{DOC, MAXSIZE, RUST_MULTIARCH, UnraisableHookArgsData, module_def, multiarch};
#[pymodule(name = "_jit")]
mod sys_jit {
@@ -80,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")]
@@ -96,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"
@@ -168,9 +166,11 @@ mod sys {
#[pyattr]
fn builtin_module_names(vm: &VirtualMachine) -> PyTupleRef {
let mut module_names: Vec<_> = vm.state.module_inits.keys().cloned().collect();
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,7 +1609,8 @@ mod sys {
}
pub(crate) fn init_module(vm: &VirtualMachine, module: &Py<PyModule>, builtins: &Py<PyModule>) {
sys::extend_module(vm, module).unwrap();
module.__init_methods(vm).unwrap();
sys::module_exec(vm, module).unwrap();
let modules = vm.ctx.new_dict();
modules
@@ -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),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
pub(crate) use _warnings::make_module;
pub(crate) use _warnings::module_def;
use crate::{Py, PyResult, VirtualMachine, builtins::PyType};

View File

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

View File

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

View File

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

View File

@@ -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_modules(rustpython_stdlib::get_module_inits());
/// }).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::*;

View File

@@ -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,7 +102,7 @@ struct ExceptionStack {
pub struct PyGlobalState {
pub config: PyConfig,
pub module_inits: stdlib::StdlibMap,
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>,
@@ -144,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
@@ -158,8 +158,8 @@ impl VirtualMachine {
};
// Hard-core modules:
let builtins = new_module(stdlib::builtins::__module_def(&ctx));
let sys_module = new_module(stdlib::sys::__module_def(&ctx));
let builtins = new_module(stdlib::builtins::module_def(&ctx));
let sys_module = new_module(stdlib::sys::module_def(&ctx));
let import_func = ctx.none();
let profile_func = RefCell::new(ctx.none());
@@ -169,23 +169,7 @@ impl VirtualMachine {
const { RefCell::new([const { None }; signal::NSIG]) },
));
let module_inits = stdlib::get_module_inits();
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,
@@ -200,34 +184,7 @@ impl VirtualMachine {
signal_handlers,
signal_rx: None,
repr_guards: RefCell::default(),
state: PyRc::new(PyGlobalState {
config,
module_inits,
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()),
@@ -245,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()),
@@ -275,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 {
@@ -470,34 +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`]
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);
}
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);
}
/// 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);
@@ -1381,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_modules(rustpython_stdlib::get_module_inits());
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",
);
});
}

View File

@@ -1,6 +1,4 @@
use rustpython_vm::VirtualMachine;
pub(crate) use _browser::make_module;
pub(crate) use _browser::module_def;
#[pymodule]
mod _browser {
@@ -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("_browser".to_owned(), Box::new(make_module));
vm.add_frozen(py_freeze!(dir = "Lib"));
}

View File

@@ -1,5 +1,4 @@
pub(crate) use _js::{PyJsValue, PyPromise};
use rustpython_vm::VirtualMachine;
#[pymodule]
mod _js {
@@ -621,8 +620,4 @@ mod _js {
}
}
pub(crate) use _js::make_module;
pub fn setup_js_module(vm: &mut VirtualMachine) {
vm.add_native_module("_js".to_owned(), Box::new(make_module));
}
pub(crate) use _js::module_def;

View File

@@ -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::*;

Some files were not shown because too many files have changed in this diff Show More