From d2162446d0bb6c4b3a3ba5cb1f77889c8100aff8 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 12 Aug 2025 16:33:46 +0200 Subject: [PATCH] python: Fix venv activation in remote projects (#36043) Crux of the issue was that we were checking whether a venv activation script exists on local filesystem, which is obviously wrong for remote projects. This PR also does away with `source` for venv activation in favor of `.`, which is compliant with `sh` Co-authored-by: Lukas Wirth Closes #34648 Release Notes: - Python: fixed activation of virtual environments in terminals for remote projects Co-authored-by: Lukas Wirth --- crates/project/src/terminals.rs | 59 ++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/crates/project/src/terminals.rs b/crates/project/src/terminals.rs index 41d8c4b2fd..5ea7b87fbe 100644 --- a/crates/project/src/terminals.rs +++ b/crates/project/src/terminals.rs @@ -256,7 +256,7 @@ impl Project { let local_path = if is_ssh_terminal { None } else { path.clone() }; - let mut python_venv_activate_command = None; + let mut python_venv_activate_command = Task::ready(None); let (spawn_task, shell) = match kind { TerminalKind::Shell(_) => { @@ -265,6 +265,7 @@ impl Project { python_venv_directory, &settings.detect_venv, &settings.shell, + cx, ); } @@ -419,9 +420,12 @@ impl Project { }) .detach(); - if let Some(activate_command) = python_venv_activate_command { - this.activate_python_virtual_environment(activate_command, &terminal_handle, cx); - } + this.activate_python_virtual_environment( + python_venv_activate_command, + &terminal_handle, + cx, + ); + terminal_handle }) } @@ -539,12 +543,15 @@ impl Project { venv_base_directory: &Path, venv_settings: &VenvSettings, shell: &Shell, - ) -> Option { - let venv_settings = venv_settings.as_option()?; + cx: &mut App, + ) -> Task> { + let Some(venv_settings) = venv_settings.as_option() else { + return Task::ready(None); + }; let activate_keyword = match venv_settings.activate_script { terminal_settings::ActivateScript::Default => match std::env::consts::OS { "windows" => ".", - _ => "source", + _ => ".", }, terminal_settings::ActivateScript::Nushell => "overlay use", terminal_settings::ActivateScript::PowerShell => ".", @@ -589,30 +596,44 @@ impl Project { .join(activate_script_name) .to_string_lossy() .to_string(); - let quoted = shlex::try_quote(&path).ok()?; - smol::block_on(self.fs.metadata(path.as_ref())) - .ok() - .flatten()?; - Some(format!( - "{} {} ; clear{}", - activate_keyword, quoted, line_ending - )) + let is_valid_path = self.resolve_abs_path(path.as_ref(), cx); + cx.background_spawn(async move { + let quoted = shlex::try_quote(&path).ok()?; + if is_valid_path.await.is_some_and(|meta| meta.is_file()) { + Some(format!( + "{} {} ; clear{}", + activate_keyword, quoted, line_ending + )) + } else { + None + } + }) } else { - Some(format!( + Task::ready(Some(format!( "{activate_keyword} {activate_script_name} {name}; clear{line_ending}", name = venv_settings.venv_name - )) + ))) } } fn activate_python_virtual_environment( &self, - command: String, + command: Task>, terminal_handle: &Entity, cx: &mut App, ) { - terminal_handle.update(cx, |terminal, _| terminal.input(command.into_bytes())); + terminal_handle.update(cx, |_, cx| { + cx.spawn(async move |this, cx| { + if let Some(command) = command.await { + this.update(cx, |this, _| { + this.input(command.into_bytes()); + }) + .ok(); + } + }) + .detach() + }); } pub fn local_terminal_handles(&self) -> &Vec> {