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}