diff --git a/Cargo.lock b/Cargo.lock index deb0dba0dac2f766d243a21801902bee66d156b1..64f0a4b41f04a11b5813ba8e371bc1587d12a0ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9321,6 +9321,7 @@ dependencies = [ "pet-fs", "pet-poetry", "pet-reporter", + "pet-virtualenv", "pretty_assertions", "project", "regex", diff --git a/Cargo.toml b/Cargo.toml index 5cad901d18d5832f0d593c6c436613e507cdabe1..d6f74e2685c3c350f3d4a415148dce4c96dfd49f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -584,6 +584,7 @@ pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", re pet-pixi = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" } pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" } pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" } +pet-virtualenv = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" } portable-pty = "0.9.0" postage = { version = "0.5", features = ["futures-traits"] } pretty_assertions = { version = "1.3.0", features = ["unstable"] } diff --git a/crates/languages/Cargo.toml b/crates/languages/Cargo.toml index 7ebafd8fdd9e2310c207d47a7d911516a498d00c..f08c548ddcb10a311e1d5b29a9bf50a7b5bc4fb1 100644 --- a/crates/languages/Cargo.toml +++ b/crates/languages/Cargo.toml @@ -57,6 +57,7 @@ pet-core.workspace = true pet-fs.workspace = true pet-poetry.workspace = true pet-reporter.workspace = true +pet-virtualenv.workspace = true pet.workspace = true project.workspace = true regex.workspace = true diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 27af3eea17c9858d11e01cd7187975e804736ba2..d989f4d6dc22d2e58752d6d635b62091d8bd63d6 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -16,6 +16,7 @@ use node_runtime::{NodeRuntime, VersionStrategy}; use pet_core::Configuration; use pet_core::os_environment::Environment; use pet_core::python_environment::{PythonEnvironment, PythonEnvironmentKind}; +use pet_virtualenv::is_virtualenv_dir; use project::Fs; use project::lsp_store::language_server_settings; use serde_json::{Value, json}; @@ -900,6 +901,21 @@ fn python_module_name_from_relative_path(relative_path: &str) -> String { .to_string() } +fn is_python_env_global(k: &PythonEnvironmentKind) -> bool { + matches!( + k, + PythonEnvironmentKind::Homebrew + | PythonEnvironmentKind::Pyenv + | PythonEnvironmentKind::GlobalPaths + | PythonEnvironmentKind::MacPythonOrg + | PythonEnvironmentKind::MacCommandLineTools + | PythonEnvironmentKind::LinuxGlobal + | PythonEnvironmentKind::MacXCode + | PythonEnvironmentKind::WindowsStore + | PythonEnvironmentKind::WindowsRegistry + ) +} + fn python_env_kind_display(k: &PythonEnvironmentKind) -> &'static str { match k { PythonEnvironmentKind::Conda => "Conda", @@ -966,6 +982,26 @@ async fn get_worktree_venv_declaration(worktree_root: &Path) -> Option { Some(venv_name.trim().to_string()) } +fn get_venv_parent_dir(env: &PythonEnvironment) -> Option { + // If global, we aren't a virtual environment + if let Some(kind) = env.kind + && is_python_env_global(&kind) + { + return None; + } + + // Check to be sure we are a virtual environment using pet's most generic + // virtual environment type, VirtualEnv + let venv = env + .executable + .as_ref() + .and_then(|p| p.parent()) + .and_then(|p| p.parent()) + .filter(|p| is_virtualenv_dir(p))?; + + venv.parent().map(|parent| parent.to_path_buf()) +} + #[async_trait] impl ToolchainLister for PythonToolchainProvider { async fn list( @@ -1025,11 +1061,15 @@ impl ToolchainLister for PythonToolchainProvider { }); // Compare project paths against worktree root - let proj_ordering = || match (&lhs.project, &rhs.project) { - (Some(l), Some(r)) => (r == &wr).cmp(&(l == &wr)), - (Some(l), None) if l == &wr => Ordering::Less, - (None, Some(r)) if r == &wr => Ordering::Greater, - _ => Ordering::Equal, + let proj_ordering = || { + let lhs_project = lhs.project.clone().or_else(|| get_venv_parent_dir(lhs)); + let rhs_project = rhs.project.clone().or_else(|| get_venv_parent_dir(rhs)); + match (&lhs_project, &rhs_project) { + (Some(l), Some(r)) => (r == &wr).cmp(&(l == &wr)), + (Some(l), None) if l == &wr => Ordering::Less, + (None, Some(r)) if r == &wr => Ordering::Greater, + _ => Ordering::Equal, + } }; // Compare environment priorities