debugger: Install debugpy into user's venv if there's one selected (#35617)

Closes #35388


Release Notes:

- debugger: Fixed Python debug sessions failing to launch due to a
missing debugpy installation. Debugpy is now installed into user's venv
if there's one available.
This commit is contained in:
Piotr Osiewicz 2025-08-05 00:37:06 +02:00 committed by GitHub
parent 182edbf526
commit 91bbdb7002
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -126,38 +126,42 @@ impl PythonDebugAdapter {
}
None
}
const BINARY_DIR: &str = if cfg!(target_os = "windows") {
"Scripts"
} else {
"bin"
};
async fn base_venv(&self, delegate: &dyn DapDelegate) -> Result<Arc<Path>, String> {
const BINARY_DIR: &str = if cfg!(target_os = "windows") {
"Scripts"
} else {
"bin"
};
self.python_venv_base
.get_or_init(move || async move {
let venv_base = Self::ensure_venv(delegate)
.await
.map_err(|e| format!("{e}"))?;
let pip_path = venv_base.join(BINARY_DIR).join("pip3");
let installation_succeeded = util::command::new_smol_command(pip_path.as_path())
.arg("install")
.arg("debugpy")
.arg("-U")
.output()
.await
.map_err(|e| format!("{e}"))?
.status
.success();
if !installation_succeeded {
return Err("debugpy installation failed".into());
}
Self::install_debugpy_into_venv(&venv_base).await?;
Ok(venv_base)
})
.await
.clone()
}
async fn install_debugpy_into_venv(venv_path: &Path) -> Result<(), String> {
let pip_path = venv_path.join(Self::BINARY_DIR).join("pip3");
let installation_succeeded = util::command::new_smol_command(pip_path.as_path())
.arg("install")
.arg("debugpy")
.arg("-U")
.output()
.await
.map_err(|e| format!("{e}"))?
.status
.success();
if !installation_succeeded {
return Err("debugpy installation failed".into());
}
Ok(())
}
async fn get_installed_binary(
&self,
delegate: &Arc<dyn DapDelegate>,
@ -629,11 +633,22 @@ impl DebugAdapter for PythonDebugAdapter {
.await;
}
let base_path = config
.config
.get("cwd")
.and_then(|cwd| {
cwd.as_str()
.map(Path::new)?
.strip_prefix(delegate.worktree_root_path())
.ok()
})
.unwrap_or_else(|| "".as_ref())
.into();
let toolchain = delegate
.toolchain_store()
.active_toolchain(
delegate.worktree_id(),
Arc::from("".as_ref()),
base_path,
language::LanguageName::new(Self::LANGUAGE_NAME),
cx,
)
@ -641,6 +656,10 @@ impl DebugAdapter for PythonDebugAdapter {
if let Some(toolchain) = &toolchain {
if let Some(path) = Path::new(&toolchain.path.to_string()).parent() {
if let Some(parent) = path.parent() {
Self::install_debugpy_into_venv(parent).await.ok();
}
let debugpy_path = path.join("debugpy");
if delegate.fs().is_file(&debugpy_path).await {
log::debug!(