Skip to content

Commit d2c4418

Browse files
committed
introduce PyConfig
1 parent d6638c7 commit d2c4418

File tree

14 files changed

+146
-209
lines changed

14 files changed

+146
-209
lines changed

crates/stdlib/src/syslog.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ mod syslog {
2626
use libc::{LOG_AUTHPRIV, LOG_CRON, LOG_PERROR};
2727

2828
fn get_argv(vm: &VirtualMachine) -> Option<PyStrRef> {
29-
if let Some(argv) = vm.state.settings.argv.first()
29+
if let Some(argv) = vm.state.config.settings.argv.first()
3030
&& !argv.is_empty()
3131
{
3232
return Some(

crates/vm/src/getpath.rs

Lines changed: 56 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -2,71 +2,64 @@
22
//!
33
//! This module provides path calculation logic but implemented directly in Rust.
44
//!
5-
//! The main entry point is `init_path_config()` which should be called
6-
//! before interpreter initialization to populate Settings with path info.
5+
//! The main entry point is `init_path_config()` which computes Paths from Settings.
76
87
use std::env;
98
use std::path::{Path, PathBuf};
109

1110
use crate::version;
12-
use crate::vm::Settings;
11+
use crate::vm::{Paths, Settings};
1312

14-
/// Initialize path configuration in Settings (like getpath.py)
13+
/// Compute path configuration from Settings
1514
///
1615
/// This function should be called before interpreter initialization.
17-
/// It computes executable, base_executable, prefix, and module_search_paths.
18-
pub fn init_path_config(settings: &mut Settings) {
19-
// Skip if already configured
20-
if settings.module_search_paths_set {
21-
return;
22-
}
23-
16+
/// It returns a Paths struct with all computed path values.
17+
pub fn init_path_config(settings: &Settings) -> Paths {
2418
// 1. Compute executable path
25-
if settings.executable.is_none() {
26-
settings.executable = get_executable_path().map(|p| p.to_string_lossy().into_owned());
27-
}
28-
29-
let exe_path = settings
30-
.executable
31-
.as_ref()
32-
.map(PathBuf::from)
33-
.or_else(get_executable_path);
34-
35-
// 2. Compute base_executable (for venv support)
36-
if settings.base_executable.is_none() {
37-
settings.base_executable = compute_base_executable(exe_path.as_deref());
38-
}
39-
40-
// 3. Compute prefix paths (with fallbacks to ensure all values are set)
41-
let (prefix, base_prefix) = compute_prefixes(exe_path.as_deref());
42-
let default_prefix = || {
43-
std::option_env!("RUSTPYTHON_PREFIX")
44-
.map(String::from)
45-
.unwrap_or_else(|| if cfg!(windows) { "C:" } else { "/usr/local" }.to_owned())
19+
let executable = get_executable_path()
20+
.map(|p| p.to_string_lossy().into_owned())
21+
.unwrap_or_default();
22+
23+
let exe_path = if executable.is_empty() {
24+
None
25+
} else {
26+
Some(PathBuf::from(&executable))
4627
};
4728

48-
if settings.prefix.is_none() {
49-
settings.prefix = Some(prefix.clone().unwrap_or_else(default_prefix));
50-
}
51-
if settings.base_prefix.is_none() {
52-
settings.base_prefix = Some(
53-
base_prefix
54-
.clone()
55-
.or_else(|| prefix.clone())
56-
.unwrap_or_else(default_prefix),
57-
);
58-
}
59-
if settings.exec_prefix.is_none() {
60-
settings.exec_prefix = settings.prefix.clone();
61-
}
62-
if settings.base_exec_prefix.is_none() {
63-
settings.base_exec_prefix = settings.base_prefix.clone();
29+
// 2. Compute base_executable (for venv support)
30+
let base_executable =
31+
compute_base_executable(exe_path.as_deref()).unwrap_or_else(|| executable.clone());
32+
33+
// 3. Compute prefix paths
34+
let (prefix_opt, base_prefix_opt) = compute_prefixes(exe_path.as_deref());
35+
let prefix = prefix_opt.unwrap_or_else(default_prefix);
36+
let base_prefix = base_prefix_opt.unwrap_or_else(|| prefix.clone());
37+
38+
// 4. Build module_search_paths
39+
let module_search_paths = compute_module_search_paths(settings, &base_prefix);
40+
41+
Paths {
42+
executable,
43+
base_executable,
44+
prefix: prefix.clone(),
45+
base_prefix: base_prefix.clone(),
46+
exec_prefix: prefix,
47+
base_exec_prefix: base_prefix,
48+
module_search_paths,
6449
}
50+
}
6551

66-
// 4. Build module_search_paths (use settings.base_prefix which is now guaranteed to be set)
67-
settings.module_search_paths =
68-
compute_module_search_paths(settings, settings.base_prefix.as_deref());
69-
settings.module_search_paths_set = true;
52+
/// Get default prefix value
53+
fn default_prefix() -> String {
54+
std::option_env!("RUSTPYTHON_PREFIX")
55+
.map(String::from)
56+
.unwrap_or_else(|| {
57+
if cfg!(windows) {
58+
"C:".to_owned()
59+
} else {
60+
"/usr/local".to_owned()
61+
}
62+
})
7063
}
7164

7265
/// Compute base_executable from executable path
@@ -84,10 +77,6 @@ fn compute_base_executable(exe_path: Option<&Path>) -> Option<String> {
8477
let home_path = PathBuf::from(&venv_home);
8578
let exe_name = exe_path.file_name()?;
8679
let base_exe = home_path.join(exe_name);
87-
if base_exe.exists() {
88-
return Some(base_exe.to_string_lossy().into_owned());
89-
}
90-
// Fallback: just return the home directory path with exe name
9180
return Some(base_exe.to_string_lossy().into_owned());
9281
}
9382

@@ -100,10 +89,8 @@ fn compute_prefixes(exe_path: Option<&Path>) -> (Option<String>, Option<String>)
10089
let Some(exe_path) = exe_path else {
10190
return (None, None);
10291
};
103-
104-
let exe_dir = match exe_path.parent() {
105-
Some(d) => d,
106-
None => return (None, None),
92+
let Some(exe_dir) = exe_path.parent() else {
93+
return (None, None);
10794
};
10895

10996
// Check if we're in a venv
@@ -124,60 +111,19 @@ fn compute_prefixes(exe_path: Option<&Path>) -> (Option<String>, Option<String>)
124111
}
125112

126113
/// Build the complete module_search_paths (sys.path)
127-
fn compute_module_search_paths(settings: &Settings, base_prefix: Option<&str>) -> Vec<String> {
114+
fn compute_module_search_paths(settings: &Settings, base_prefix: &str) -> Vec<String> {
128115
let mut paths = Vec::new();
129116

130117
// 1. Add paths from path_list (PYTHONPATH/RUSTPYTHONPATH)
131118
paths.extend(settings.path_list.iter().cloned());
132119

133120
// 2. Add zip stdlib path
134-
if let Some(base_prefix) = base_prefix {
135-
let platlibdir = "lib";
136-
let zip_name = format!("rustpython{}{}", version::MAJOR, version::MINOR);
137-
let zip_path = PathBuf::from(base_prefix).join(platlibdir).join(&zip_name);
138-
paths.push(zip_path.to_string_lossy().into_owned());
139-
}
140-
141-
paths
142-
}
143-
144-
/// Get the zip stdlib path to add to sys.path
145-
///
146-
/// Returns a path like `/usr/local/lib/rustpython313` or
147-
/// `/path/to/venv/lib/rustpython313` for virtual environments.
148-
pub fn get_zip_stdlib_path() -> Option<String> {
149-
// ZIP_LANDMARK pattern: {platlibdir}/{impl_name}{VERSION_MAJOR}{VERSION_MINOR}
150121
let platlibdir = "lib";
151122
let zip_name = format!("rustpython{}{}", version::MAJOR, version::MINOR);
123+
let zip_path = PathBuf::from(base_prefix).join(platlibdir).join(&zip_name);
124+
paths.push(zip_path.to_string_lossy().into_owned());
152125

153-
let base_prefix = get_base_prefix()?;
154-
let zip_path = base_prefix.join(platlibdir).join(&zip_name);
155-
156-
Some(zip_path.to_string_lossy().into_owned())
157-
}
158-
159-
/// Get the base prefix directory
160-
///
161-
/// For installed Python: parent of the bin directory
162-
/// For venv: the 'home' value from pyvenv.cfg
163-
fn get_base_prefix() -> Option<PathBuf> {
164-
let exe_path = get_executable_path()?;
165-
let exe_dir = exe_path.parent()?;
166-
167-
// Check if we're in a venv by looking for pyvenv.cfg
168-
if let Some(venv_home) = get_venv_home(&exe_path) {
169-
// venv_home is the directory containing the base Python
170-
// Go up one level to get the prefix (e.g., /usr/local from /usr/local/bin)
171-
let home_path = PathBuf::from(&venv_home);
172-
if let Some(parent) = home_path.parent() {
173-
return Some(parent.to_path_buf());
174-
}
175-
return Some(home_path);
176-
}
177-
178-
// Not in venv: go up from bin/ to get prefix
179-
// e.g., /usr/local/bin/rustpython -> /usr/local
180-
exe_dir.parent().map(|p| p.to_path_buf())
126+
paths
181127
}
182128

183129
/// Get the current executable path
@@ -230,8 +176,10 @@ mod tests {
230176
use super::*;
231177

232178
#[test]
233-
fn test_zip_stdlib_path_format() {
234-
// Just verify it returns something and doesn't panic
235-
let _path = get_zip_stdlib_path();
179+
fn test_init_path_config() {
180+
let settings = Settings::default();
181+
let paths = init_path_config(&settings);
182+
// Just verify it doesn't panic and returns valid paths
183+
assert!(!paths.prefix.is_empty());
236184
}
237185
}

crates/vm/src/import.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ fn remove_importlib_frames_inner(
206206
// TODO: This function should do nothing on verbose mode.
207207
// TODO: Fix this function after making PyTraceback.next mutable
208208
pub fn remove_importlib_frames(vm: &VirtualMachine, exc: &Py<PyBaseException>) {
209-
if vm.state.settings.verbose != 0 {
209+
if vm.state.config.settings.verbose != 0 {
210210
return;
211211
}
212212

crates/vm/src/stdlib/builtins.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ mod builtins {
132132

133133
let optimize: i32 = args.optimize.map_or(Ok(-1), |v| v.try_to_primitive(vm))?;
134134
let optimize: u8 = if optimize == -1 {
135-
vm.state.settings.optimize
135+
vm.state.config.settings.optimize
136136
} else {
137137
optimize
138138
.try_into()
@@ -1080,7 +1080,7 @@ pub fn init_module(vm: &VirtualMachine, module: &Py<PyModule>) {
10801080

10811081
builtins::extend_module(vm, module).unwrap();
10821082

1083-
let debug_mode: bool = vm.state.settings.optimize == 0;
1083+
let debug_mode: bool = vm.state.config.settings.optimize == 0;
10841084
// Create dynamic ExceptionGroup with multiple inheritance (BaseExceptionGroup + Exception)
10851085
let exception_group = crate::exception_group::exception_group();
10861086

crates/vm/src/stdlib/imp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ mod _imp {
9191
#[pyattr]
9292
fn check_hash_based_pycs(vm: &VirtualMachine) -> PyStrRef {
9393
vm.ctx
94-
.new_str(vm.state.settings.check_hash_pycs_mode.to_string())
94+
.new_str(vm.state.config.settings.check_hash_pycs_mode.to_string())
9595
}
9696

9797
#[pyfunction]

crates/vm/src/stdlib/io.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2398,7 +2398,9 @@ mod _io {
23982398
*data = None;
23992399

24002400
let encoding = match args.encoding {
2401-
None if vm.state.settings.utf8_mode > 0 => identifier_utf8!(vm, utf_8).to_owned(),
2401+
None if vm.state.config.settings.utf8_mode > 0 => {
2402+
identifier_utf8!(vm, utf_8).to_owned()
2403+
}
24022404
Some(enc) if enc.as_str() != "locale" => enc,
24032405
_ => {
24042406
// None without utf8_mode or "locale" encoding

crates/vm/src/stdlib/signal.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ pub(crate) mod _signal {
114114
module: &Py<crate::builtins::PyModule>,
115115
vm: &VirtualMachine,
116116
) {
117-
if vm.state.settings.install_signal_handlers {
117+
if vm.state.config.settings.install_signal_handlers {
118118
let sig_dfl = vm.new_pyobj(SIG_DFL as u8);
119119
let sig_ign = vm.new_pyobj(SIG_IGN as u8);
120120

0 commit comments

Comments
 (0)