-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create
RunningKernel
trait to allow for native and remote jupyter k…
…ernels (#20842) Starts setting up a `RunningKernel` trait to make the remote kernel implementation easy to get started with. No release notes until this is all hooked up. Release Notes: - N/A
- Loading branch information
Showing
9 changed files
with
1,600 additions
and
1,088 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
mod native_kernel; | ||
use std::{fmt::Debug, future::Future, path::PathBuf}; | ||
|
||
use futures::{ | ||
channel::mpsc::{self, Receiver}, | ||
future::Shared, | ||
stream, | ||
}; | ||
use gpui::{AppContext, Model, Task}; | ||
use language::LanguageName; | ||
pub use native_kernel::*; | ||
|
||
mod remote_kernels; | ||
use project::{Project, WorktreeId}; | ||
pub use remote_kernels::*; | ||
|
||
use anyhow::Result; | ||
use runtimelib::{ExecutionState, JupyterKernelspec, JupyterMessage, KernelInfoReply}; | ||
use smol::process::Command; | ||
use ui::SharedString; | ||
|
||
pub type JupyterMessageChannel = stream::SelectAll<Receiver<JupyterMessage>>; | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
pub enum KernelSpecification { | ||
Remote(RemoteKernelSpecification), | ||
Jupyter(LocalKernelSpecification), | ||
PythonEnv(LocalKernelSpecification), | ||
} | ||
|
||
impl KernelSpecification { | ||
pub fn name(&self) -> SharedString { | ||
match self { | ||
Self::Jupyter(spec) => spec.name.clone().into(), | ||
Self::PythonEnv(spec) => spec.name.clone().into(), | ||
Self::Remote(spec) => spec.name.clone().into(), | ||
} | ||
} | ||
|
||
pub fn type_name(&self) -> SharedString { | ||
match self { | ||
Self::Jupyter(_) => "Jupyter".into(), | ||
Self::PythonEnv(_) => "Python Environment".into(), | ||
Self::Remote(_) => "Remote".into(), | ||
} | ||
} | ||
|
||
pub fn path(&self) -> SharedString { | ||
SharedString::from(match self { | ||
Self::Jupyter(spec) => spec.path.to_string_lossy().to_string(), | ||
Self::PythonEnv(spec) => spec.path.to_string_lossy().to_string(), | ||
Self::Remote(spec) => spec.url.to_string(), | ||
}) | ||
} | ||
|
||
pub fn language(&self) -> SharedString { | ||
SharedString::from(match self { | ||
Self::Jupyter(spec) => spec.kernelspec.language.clone(), | ||
Self::PythonEnv(spec) => spec.kernelspec.language.clone(), | ||
Self::Remote(spec) => spec.kernelspec.language.clone(), | ||
}) | ||
} | ||
} | ||
|
||
pub fn python_env_kernel_specifications( | ||
project: &Model<Project>, | ||
worktree_id: WorktreeId, | ||
cx: &mut AppContext, | ||
) -> impl Future<Output = Result<Vec<KernelSpecification>>> { | ||
let python_language = LanguageName::new("Python"); | ||
let toolchains = project | ||
.read(cx) | ||
.available_toolchains(worktree_id, python_language, cx); | ||
let background_executor = cx.background_executor().clone(); | ||
|
||
async move { | ||
let toolchains = if let Some(toolchains) = toolchains.await { | ||
toolchains | ||
} else { | ||
return Ok(Vec::new()); | ||
}; | ||
|
||
let kernelspecs = toolchains.toolchains.into_iter().map(|toolchain| { | ||
background_executor.spawn(async move { | ||
let python_path = toolchain.path.to_string(); | ||
|
||
// Check if ipykernel is installed | ||
let ipykernel_check = Command::new(&python_path) | ||
.args(&["-c", "import ipykernel"]) | ||
.output() | ||
.await; | ||
|
||
if ipykernel_check.is_ok() && ipykernel_check.unwrap().status.success() { | ||
// Create a default kernelspec for this environment | ||
let default_kernelspec = JupyterKernelspec { | ||
argv: vec![ | ||
python_path.clone(), | ||
"-m".to_string(), | ||
"ipykernel_launcher".to_string(), | ||
"-f".to_string(), | ||
"{connection_file}".to_string(), | ||
], | ||
display_name: toolchain.name.to_string(), | ||
language: "python".to_string(), | ||
interrupt_mode: None, | ||
metadata: None, | ||
env: None, | ||
}; | ||
|
||
Some(KernelSpecification::PythonEnv(LocalKernelSpecification { | ||
name: toolchain.name.to_string(), | ||
path: PathBuf::from(&python_path), | ||
kernelspec: default_kernelspec, | ||
})) | ||
} else { | ||
None | ||
} | ||
}) | ||
}); | ||
|
||
let kernel_specs = futures::future::join_all(kernelspecs) | ||
.await | ||
.into_iter() | ||
.flatten() | ||
.collect(); | ||
|
||
anyhow::Ok(kernel_specs) | ||
} | ||
} | ||
|
||
pub trait RunningKernel: Send + Debug { | ||
fn request_tx(&self) -> mpsc::Sender<JupyterMessage>; | ||
fn working_directory(&self) -> &PathBuf; | ||
fn execution_state(&self) -> &ExecutionState; | ||
fn set_execution_state(&mut self, state: ExecutionState); | ||
fn kernel_info(&self) -> Option<&KernelInfoReply>; | ||
fn set_kernel_info(&mut self, info: KernelInfoReply); | ||
fn force_shutdown(&mut self) -> anyhow::Result<()>; | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub enum KernelStatus { | ||
Idle, | ||
Busy, | ||
Starting, | ||
Error, | ||
ShuttingDown, | ||
Shutdown, | ||
Restarting, | ||
} | ||
|
||
impl KernelStatus { | ||
pub fn is_connected(&self) -> bool { | ||
match self { | ||
KernelStatus::Idle | KernelStatus::Busy => true, | ||
_ => false, | ||
} | ||
} | ||
} | ||
|
||
impl ToString for KernelStatus { | ||
fn to_string(&self) -> String { | ||
match self { | ||
KernelStatus::Idle => "Idle".to_string(), | ||
KernelStatus::Busy => "Busy".to_string(), | ||
KernelStatus::Starting => "Starting".to_string(), | ||
KernelStatus::Error => "Error".to_string(), | ||
KernelStatus::ShuttingDown => "Shutting Down".to_string(), | ||
KernelStatus::Shutdown => "Shutdown".to_string(), | ||
KernelStatus::Restarting => "Restarting".to_string(), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum Kernel { | ||
RunningKernel(Box<dyn RunningKernel>), | ||
StartingKernel(Shared<Task<()>>), | ||
ErroredLaunch(String), | ||
ShuttingDown, | ||
Shutdown, | ||
Restarting, | ||
} | ||
|
||
impl From<&Kernel> for KernelStatus { | ||
fn from(kernel: &Kernel) -> Self { | ||
match kernel { | ||
Kernel::RunningKernel(kernel) => match kernel.execution_state() { | ||
ExecutionState::Idle => KernelStatus::Idle, | ||
ExecutionState::Busy => KernelStatus::Busy, | ||
}, | ||
Kernel::StartingKernel(_) => KernelStatus::Starting, | ||
Kernel::ErroredLaunch(_) => KernelStatus::Error, | ||
Kernel::ShuttingDown => KernelStatus::ShuttingDown, | ||
Kernel::Shutdown => KernelStatus::Shutdown, | ||
Kernel::Restarting => KernelStatus::Restarting, | ||
} | ||
} | ||
} | ||
|
||
impl Kernel { | ||
pub fn status(&self) -> KernelStatus { | ||
self.into() | ||
} | ||
|
||
pub fn set_execution_state(&mut self, status: &ExecutionState) { | ||
if let Kernel::RunningKernel(running_kernel) = self { | ||
running_kernel.set_execution_state(status.clone()); | ||
} | ||
} | ||
|
||
pub fn set_kernel_info(&mut self, kernel_info: &KernelInfoReply) { | ||
if let Kernel::RunningKernel(running_kernel) = self { | ||
running_kernel.set_kernel_info(kernel_info.clone()); | ||
} | ||
} | ||
|
||
pub fn is_shutting_down(&self) -> bool { | ||
match self { | ||
Kernel::Restarting | Kernel::ShuttingDown => true, | ||
Kernel::RunningKernel(_) | ||
| Kernel::StartingKernel(_) | ||
| Kernel::ErroredLaunch(_) | ||
| Kernel::Shutdown => false, | ||
} | ||
} | ||
} |
Oops, something went wrong.