Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Lib/test/test_cmd_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -1369,7 +1369,6 @@ def test_presite(self):
proc = assert_python_ok("-X", f"presite={entrypoint}", "-c", "pass")
self.assertEqual(proc.out.rstrip(), b"presite func")

@unittest.expectedFailure # TODO: RUSTPYTHON; explicit PYTHONHOME startup path configuration and fatal encodings failure reporting are not implemented
def test_dump_path_config(self):
# gh-151253: At the first import (import encodings) during Python
# startup, if the import fails, dump the Python path configuration.
Expand Down
57 changes: 53 additions & 4 deletions crates/vm/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ pub struct VirtualMachine {
pub state: PyRc<PyGlobalState>,
pub initialized: bool,
pub(crate) import_bootstrap_complete: bool,
startup_error: Option<PyBaseExceptionRef>,
recursion_depth: Cell<usize>,
/// Depth of native recursion that pushes no Python frame, counted only
/// where the stack pointer cannot be read. Everywhere else the native
Expand Down Expand Up @@ -1372,6 +1373,7 @@ impl VirtualMachine {
state,
initialized: false,
import_bootstrap_complete: false,
startup_error: None,
recursion_depth: Cell::new(0),
#[cfg(any(miri, target_env = "musl"))]
native_recursion_depth: Cell::new(0),
Expand Down Expand Up @@ -1409,6 +1411,9 @@ impl VirtualMachine {
#[cfg(feature = "encodings")]
fn import_encodings(&mut self) -> PyResult<()> {
self.import("encodings", 0).map_err(|import_err| {
if self.state.config.settings.home.is_some() {
return import_err;
}
let rustpythonpath_env = crate::host_env::os::var("RUSTPYTHONPATH").ok();
let pythonpath_env = crate::host_env::os::var("PYTHONPATH").ok();
let env_set = rustpythonpath_env.as_ref().is_some() || pythonpath_env.as_ref().is_some();
Expand Down Expand Up @@ -1668,10 +1673,14 @@ impl VirtualMachine {
#[cfg(feature = "encodings")]
if _expect_stdlib {
if let Err(e) = self.import_encodings() {
eprintln!(
"encodings initialization failed. Only utf-8 encoding will be supported."
);
self.print_exception(&e);
if self.state.config.settings.home.is_some() {
self.startup_error = Some(e);
} else {
eprintln!(
"encodings initialization failed. Only utf-8 encoding will be supported."
);
self.print_exception(&e);
}
}
} else {
// Here may not be the best place to give general `path_list` advice,
Expand All @@ -1686,6 +1695,46 @@ impl VirtualMachine {
self.initialized = true;
}

/// Report an invalid explicit standard-library configuration to a command-line
/// host. Embedding applications may continue using the bootstrap codecs without calling this.
pub fn check_stdlib_initialization(&self) -> PyResult<()> {
let Some(error) = &self.startup_error else {
return Ok(());
};
let settings = &self.state.config.settings;
let paths = &self.state.config.paths;
let repr = |value: &str| {
self.ctx
.new_str(value)
.repr(self)
.unwrap_or_else(|_| "?".to_owned())
};
eprintln!("Python path configuration:");
eprintln!(
" PYTHONHOME = {}",
settings
.home
.as_deref()
.map_or_else(|| "(not set)".to_owned(), repr)
);
eprintln!(" isolated = {}", u8::from(settings.isolated));
eprintln!(" environment = {}", u8::from(!settings.ignore_environment));
eprintln!(" sys.executable = {}", repr(&paths.executable));
eprintln!(" sys.prefix = {}", repr(&paths.prefix));
eprintln!(" sys.exec_prefix = {}", repr(&paths.exec_prefix));
eprintln!(" sys.path = [");
for path in &paths.module_search_paths {
eprintln!(" {},", repr(path));
}
eprintln!(" ]");
// A startup import failure is fatal even if the imported module raised
// SystemExit(0) or SystemExit(None). Keep its exception as the cause,
// without passing its exit code to ordinary user-code finalization.
let startup_error = self.new_runtime_error("Failed to import encodings module");
startup_error.set_cause(Some(error.clone()));
Err(startup_error)
}

/// Apply configured global lazy imports after startup imports, including site.
/// Hosts that perform their own main-module setup can call this at that boundary.
pub fn apply_startup_lazy_imports(&self) {
Expand Down
1 change: 1 addition & 0 deletions crates/vm/src/vm/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1481,6 +1481,7 @@ impl VirtualMachine {
state: self.state.clone(),
initialized: self.initialized,
import_bootstrap_complete: self.import_bootstrap_complete,
startup_error: self.startup_error.clone(),
recursion_depth: Cell::new(0),
#[cfg(any(miri, target_env = "musl"))]
native_recursion_depth: Cell::new(0),
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ fn run_rustpython(vm: &VirtualMachine, run_mode: RunMode) -> u32 {

// pymain_run_python
fn pymain_run_python(vm: &VirtualMachine, run_mode: RunMode) -> PyResult<u32> {
vm.check_stdlib_initialization()?;
let scope = vm.new_scope_with_main()?;

// The native warnings state already holds the default filters. Import the
Expand Down
Loading