py_executer_lib/path.rs
1use crate::warning_println;
2use std::path::PathBuf;
3use std::process::{Command, Stdio};
4
5/// Finds the native Python executable path.
6///
7/// If `uv_path` is empty, it uses `which` or `where` command to find the native Python executable.
8/// If the command is successful, it returns the path of the Python executable.
9/// If the command is not successful, it returns an empty string.
10///
11/// If `uv_path` is not empty, it returns an empty string.
12///
13/// # Platform-specific
14///
15/// On Unix-like systems, it uses `which` command.
16///
17/// On Windows, it uses `where` command.
18pub fn get_python_native_path(uv_path: &String) -> String {
19 if uv_path.is_empty() {
20 #[cfg(not(target_os = "windows"))]
21 let find_executable = "which";
22
23 // For Windows
24 #[cfg(target_os = "windows")]
25 let find_executable = "where";
26
27 let output = Command::new(find_executable)
28 .arg("python3")
29 .output()
30 .unwrap();
31 if output.status.success() {
32 String::from_utf8(output.stdout)
33 .unwrap_or("".to_string())
34 .trim()
35 .to_string()
36 } else {
37 "".to_string()
38 }
39 } else {
40 "".to_string()
41 }
42}
43
44/// Finds a virtual environment path.
45///
46/// # Errors
47///
48/// The function returns an `Err` if the provided venv path does not exist or if the Python executable
49/// under the venv path does not exist.
50///
51/// # Platform-specific
52///
53/// On Unix-like systems, it uses `which` command.
54///
55/// On Windows, it uses `where` command.
56///
57/// # Arguments
58///
59/// * `runtime_path`: The runtime path of the current directory.
60/// * `uv_path`: The path of the uv executable.
61/// * `python_native_path`: The path of the native Python executable.
62/// * `quiet`: If `true`, suppresses warnings and errors.
63/// * `clean`: If `true`, will clean the created uv-managed .venv and config files after execution.
64/// * `files_to_clean`: A vector of paths to clean.
65///
66/// # Returns
67///
68/// The path of the found virtual environment.
69pub fn get_venv_path(
70 runtime_path: PathBuf,
71 uv_path: String,
72 python_native_path: String,
73 quiet: bool,
74 clean: bool,
75 files_to_clean: &mut Vec<PathBuf>,
76) -> PathBuf {
77 let possible_venv_dir_names = ["venv", ".venv"];
78 possible_venv_dir_names
79 .iter()
80 .map(|name| runtime_path.join(name))
81 .find(|path| path.exists())
82 .unwrap_or_else(|| {
83 prepare_venv(
84 quiet,
85 &runtime_path,
86 &uv_path,
87 &python_native_path,
88 clean,
89 files_to_clean,
90 )
91 })
92}
93
94fn prepare_venv(
95 quiet: bool,
96 runtime_path: &PathBuf,
97 uv_path: &String,
98 python_native_path: &String,
99 clean: bool,
100 files_to_clean: &mut Vec<PathBuf>,
101) -> PathBuf {
102 if !quiet {
103 warning_println!(
104 "No venv found in {}, will generate one",
105 runtime_path.display()
106 );
107 }
108 let new_venv_path = runtime_path.join(".venv");
109 let _ = Command::new(if uv_path.is_empty() {
110 &python_native_path
111 } else {
112 &uv_path
113 })
114 .args(["venv", &new_venv_path.to_str().unwrap()])
115 .stdout(if quiet {
116 Stdio::null()
117 } else {
118 Stdio::inherit()
119 })
120 .stderr(if quiet {
121 Stdio::null()
122 } else {
123 Stdio::inherit()
124 })
125 .output()
126 .unwrap();
127 if clean {
128 files_to_clean.push(new_venv_path.clone());
129 }
130 new_venv_path
131}