debugger: Detect debugpy from virtual env (#31211)

Remco Smits created

Release Notes:

- Debugger Beta: Detect debugpy from virtual env

Change summary

crates/dap_adapters/src/python.rs | 49 +++++++++++++++++++++++---------
1 file changed, 35 insertions(+), 14 deletions(-)

Detailed changes

crates/dap_adapters/src/python.rs 🔗

@@ -6,9 +6,14 @@ use dap::{
 };
 use gpui::{AsyncApp, SharedString};
 use json_dotpath::DotPaths;
-use language::LanguageName;
+use language::{LanguageName, Toolchain};
 use serde_json::Value;
-use std::{collections::HashMap, ffi::OsStr, path::PathBuf, sync::OnceLock};
+use std::{
+    collections::HashMap,
+    ffi::OsStr,
+    path::{Path, PathBuf},
+    sync::OnceLock,
+};
 use util::ResultExt;
 
 #[derive(Default)]
@@ -87,7 +92,7 @@ impl PythonDebugAdapter {
         delegate: &Arc<dyn DapDelegate>,
         config: &DebugTaskDefinition,
         user_installed_path: Option<PathBuf>,
-        cx: &mut AsyncApp,
+        toolchain: Option<Toolchain>,
     ) -> Result<DebugAdapterBinary> {
         const BINARY_NAMES: [&str; 3] = ["python3", "python", "py"];
         let tcp_connection = config.tcp_connection.clone().unwrap_or_default();
@@ -106,16 +111,6 @@ impl PythonDebugAdapter {
             .context("Debugpy directory not found")?
         };
 
-        let toolchain = delegate
-            .toolchain_store()
-            .active_toolchain(
-                delegate.worktree_id(),
-                Arc::from("".as_ref()),
-                language::LanguageName::new(Self::LANGUAGE_NAME),
-                cx,
-            )
-            .await;
-
         let python_path = if let Some(toolchain) = toolchain {
             Some(toolchain.path.to_string())
         } else {
@@ -563,6 +558,32 @@ impl DebugAdapter for PythonDebugAdapter {
         user_installed_path: Option<PathBuf>,
         cx: &mut AsyncApp,
     ) -> Result<DebugAdapterBinary> {
+        let toolchain = delegate
+            .toolchain_store()
+            .active_toolchain(
+                delegate.worktree_id(),
+                Arc::from("".as_ref()),
+                language::LanguageName::new(Self::LANGUAGE_NAME),
+                cx,
+            )
+            .await;
+
+        if let Some(toolchain) = &toolchain {
+            if let Some(path) = Path::new(&toolchain.path.to_string()).parent() {
+                let debugpy_path = path.join("debugpy");
+                if smol::fs::metadata(&debugpy_path).await.is_ok() {
+                    return self
+                        .get_installed_binary(
+                            delegate,
+                            &config,
+                            Some(debugpy_path.to_path_buf()),
+                            Some(toolchain.clone()),
+                        )
+                        .await;
+                }
+            }
+        }
+
         if self.checked.set(()).is_ok() {
             delegate.output_to_console(format!("Checking latest version of {}...", self.name()));
             if let Some(version) = self.fetch_latest_adapter_version(delegate).await.log_err() {
@@ -570,7 +591,7 @@ impl DebugAdapter for PythonDebugAdapter {
             }
         }
 
-        self.get_installed_binary(delegate, &config, user_installed_path, cx)
+        self.get_installed_binary(delegate, &config, user_installed_path, toolchain)
             .await
     }
 }