()
| 23 | } |
| 24 | |
| 25 | fn run() -> Result<u32, Box<dyn core::error::Error>> { |
| 26 | // 1. Get own executable path |
| 27 | let exe_path = env::current_exe()?; |
| 28 | let exe_name = exe_path |
| 29 | .file_name() |
| 30 | .ok_or("Failed to get executable name")? |
| 31 | .to_string_lossy(); |
| 32 | |
| 33 | // 2. Determine target executable name based on launcher name |
| 34 | // pythonw.exe / venvwlauncher -> pythonw.exe (GUI, no console) |
| 35 | // python.exe / venvlauncher -> python.exe (console) |
| 36 | let exe_name_lower = exe_name.to_lowercase(); |
| 37 | let target_exe = if exe_name_lower.contains("pythonw") || exe_name_lower.contains("venvw") { |
| 38 | "pythonw.exe" |
| 39 | } else { |
| 40 | "python.exe" |
| 41 | }; |
| 42 | |
| 43 | // 3. Find pyvenv.cfg |
| 44 | // The launcher is in Scripts/ directory, pyvenv.cfg is in parent (venv root) |
| 45 | let scripts_dir = exe_path.parent().ok_or("Failed to get Scripts directory")?; |
| 46 | let venv_dir = scripts_dir.parent().ok_or("Failed to get venv directory")?; |
| 47 | let cfg_path = venv_dir.join("pyvenv.cfg"); |
| 48 | |
| 49 | if !cfg_path.exists() { |
| 50 | return Err(format!("pyvenv.cfg not found: {}", cfg_path.display()).into()); |
| 51 | } |
| 52 | |
| 53 | // 4. Parse home= from pyvenv.cfg |
| 54 | let home = read_home(&cfg_path)?; |
| 55 | |
| 56 | // 5. Locate python executable in home directory |
| 57 | let python_path = PathBuf::from(&home).join(target_exe); |
| 58 | if !python_path.exists() { |
| 59 | return Err(format!("Python not found: {}", python_path.display()).into()); |
| 60 | } |
| 61 | |
| 62 | // 6. Set __PYVENV_LAUNCHER__ environment variable |
| 63 | // This tells Python it was launched from a venv |
| 64 | // SAFETY: We are in a single-threaded context (program entry point) |
| 65 | unsafe { |
| 66 | env::set_var("__PYVENV_LAUNCHER__", &exe_path); |
| 67 | } |
| 68 | |
| 69 | // 7. Launch Python with same arguments |
| 70 | let args: Vec<String> = env::args().skip(1).collect(); |
| 71 | launch_process(&python_path, &args) |
| 72 | } |
| 73 | |
| 74 | /// Parse the `home=` value from pyvenv.cfg |
| 75 | fn read_home(cfg_path: &Path) -> Result<String, Box<dyn core::error::Error>> { |
no test coverage detected