mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 20:29:11 +00:00
feat(jupyter): allow to install and maintain multiple kernels (#29249)
Some checks are pending
ci / test release macos-aarch64 (push) Blocked by required conditions
ci / bench release linux-x86_64 (push) Blocked by required conditions
ci / lint debug linux-x86_64 (push) Blocked by required conditions
ci / lint debug macos-x86_64 (push) Blocked by required conditions
ci / lint debug windows-x86_64 (push) Blocked by required conditions
ci / test debug linux-x86_64 (push) Blocked by required conditions
ci / test release linux-x86_64 (push) Blocked by required conditions
ci / test debug macos-x86_64 (push) Blocked by required conditions
ci / test release macos-x86_64 (push) Blocked by required conditions
ci / test debug windows-x86_64 (push) Blocked by required conditions
ci / test release windows-x86_64 (push) Blocked by required conditions
ci / pre-build (push) Waiting to run
ci / test debug linux-aarch64 (push) Blocked by required conditions
ci / test release linux-aarch64 (push) Blocked by required conditions
ci / test debug macos-aarch64 (push) Blocked by required conditions
ci / build wasm32 (push) Blocked by required conditions
ci / publish canary (push) Blocked by required conditions
Some checks are pending
ci / test release macos-aarch64 (push) Blocked by required conditions
ci / bench release linux-x86_64 (push) Blocked by required conditions
ci / lint debug linux-x86_64 (push) Blocked by required conditions
ci / lint debug macos-x86_64 (push) Blocked by required conditions
ci / lint debug windows-x86_64 (push) Blocked by required conditions
ci / test debug linux-x86_64 (push) Blocked by required conditions
ci / test release linux-x86_64 (push) Blocked by required conditions
ci / test debug macos-x86_64 (push) Blocked by required conditions
ci / test release macos-x86_64 (push) Blocked by required conditions
ci / test debug windows-x86_64 (push) Blocked by required conditions
ci / test release windows-x86_64 (push) Blocked by required conditions
ci / pre-build (push) Waiting to run
ci / test debug linux-aarch64 (push) Blocked by required conditions
ci / test release linux-aarch64 (push) Blocked by required conditions
ci / test debug macos-aarch64 (push) Blocked by required conditions
ci / build wasm32 (push) Blocked by required conditions
ci / publish canary (push) Blocked by required conditions
This commit changes the `deno jupyter` subcommand: - `deno jupyter` now accepts additional `--name` argument to allow installing and maintaing multiple kernelsspec - useful when one wants to install a stable kernel and a debug/canary kernel - `deno jupyter --install` now accepts additional `--display` argument to allow customizing display name of the kernel - the default one is "Deno" - `deno jupyter --install` no longer blindly installs kernelspec, instead it first checks if a kernelspec already exists and if so, returns an error suggesting to use `--force` flag - `deno jupyter --help` no longer shows `--unstable` flag Closes https://github.com/denoland/deno/issues/29219 Closes https://github.com/denoland/deno/issues/29220
This commit is contained in:
parent
9b0707baa2
commit
5ea90de199
7 changed files with 223 additions and 29 deletions
|
@ -278,8 +278,11 @@ pub struct JSONReferenceFlags {
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct JupyterFlags {
|
pub struct JupyterFlags {
|
||||||
pub install: bool,
|
pub install: bool,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub display: Option<String>,
|
||||||
pub kernel: bool,
|
pub kernel: bool,
|
||||||
pub conn_file: Option<String>,
|
pub conn_file: Option<String>,
|
||||||
|
pub force: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
@ -2723,14 +2726,37 @@ fn json_reference_subcommand() -> Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn jupyter_subcommand() -> Command {
|
fn jupyter_subcommand() -> Command {
|
||||||
command("jupyter", "Deno kernel for Jupyter notebooks", UnstableArgsConfig::ResolutionAndRuntime)
|
command("jupyter", "Deno kernel for Jupyter notebooks", UnstableArgsConfig::None)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("install")
|
Arg::new("install")
|
||||||
.long("install")
|
.long("install")
|
||||||
.help("Installs kernelspec, requires 'jupyter' command to be available.")
|
.help("Install a kernelspec")
|
||||||
.conflicts_with("kernel")
|
.conflicts_with("kernel")
|
||||||
.action(ArgAction::SetTrue)
|
.action(ArgAction::SetTrue)
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("name")
|
||||||
|
.long("name")
|
||||||
|
.short('n')
|
||||||
|
.help(cstr!("Set a name for the kernel (defaults to 'deno'). <p(245)>Useful when maintaing multiple Deno kernels.</>"))
|
||||||
|
.value_parser(value_parser!(String))
|
||||||
|
.conflicts_with("kernel")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("display")
|
||||||
|
.long("display")
|
||||||
|
.short('d')
|
||||||
|
.help(cstr!("Set a display name for the kernel (defaults to 'Deno'). <p(245)>Useful when maintaing multiple Deno kernels.</>"))
|
||||||
|
.value_parser(value_parser!(String))
|
||||||
|
.requires("install")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("force")
|
||||||
|
.long("force")
|
||||||
|
.help("Force installation of a kernel, overwriting previously existing kernelspec")
|
||||||
|
.requires("install")
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("kernel")
|
Arg::new("kernel")
|
||||||
.long("kernel")
|
.long("kernel")
|
||||||
|
@ -5163,11 +5189,17 @@ fn jupyter_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
||||||
let conn_file = matches.remove_one::<String>("conn");
|
let conn_file = matches.remove_one::<String>("conn");
|
||||||
let kernel = matches.get_flag("kernel");
|
let kernel = matches.get_flag("kernel");
|
||||||
let install = matches.get_flag("install");
|
let install = matches.get_flag("install");
|
||||||
|
let display = matches.remove_one::<String>("display");
|
||||||
|
let name = matches.remove_one::<String>("name");
|
||||||
|
let force = matches.get_flag("force");
|
||||||
|
|
||||||
flags.subcommand = DenoSubcommand::Jupyter(JupyterFlags {
|
flags.subcommand = DenoSubcommand::Jupyter(JupyterFlags {
|
||||||
install,
|
install,
|
||||||
kernel,
|
kernel,
|
||||||
conn_file,
|
conn_file,
|
||||||
|
name,
|
||||||
|
display,
|
||||||
|
force,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11340,6 +11372,9 @@ mod tests {
|
||||||
install: false,
|
install: false,
|
||||||
kernel: false,
|
kernel: false,
|
||||||
conn_file: None,
|
conn_file: None,
|
||||||
|
name: None,
|
||||||
|
display: None,
|
||||||
|
force: false,
|
||||||
}),
|
}),
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
}
|
}
|
||||||
|
@ -11353,6 +11388,65 @@ mod tests {
|
||||||
install: true,
|
install: true,
|
||||||
kernel: false,
|
kernel: false,
|
||||||
conn_file: None,
|
conn_file: None,
|
||||||
|
name: None,
|
||||||
|
display: None,
|
||||||
|
force: false,
|
||||||
|
}),
|
||||||
|
..Flags::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let r = flags_from_vec(svec!["deno", "jupyter", "--install", "--force"]);
|
||||||
|
assert_eq!(
|
||||||
|
r.unwrap(),
|
||||||
|
Flags {
|
||||||
|
subcommand: DenoSubcommand::Jupyter(JupyterFlags {
|
||||||
|
install: true,
|
||||||
|
kernel: false,
|
||||||
|
conn_file: None,
|
||||||
|
name: None,
|
||||||
|
display: None,
|
||||||
|
force: true,
|
||||||
|
}),
|
||||||
|
..Flags::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let r = flags_from_vec(svec![
|
||||||
|
"deno",
|
||||||
|
"jupyter",
|
||||||
|
"--install",
|
||||||
|
"--name",
|
||||||
|
"debugdeno",
|
||||||
|
"--display",
|
||||||
|
"Deno (debug)"
|
||||||
|
]);
|
||||||
|
assert_eq!(
|
||||||
|
r.unwrap(),
|
||||||
|
Flags {
|
||||||
|
subcommand: DenoSubcommand::Jupyter(JupyterFlags {
|
||||||
|
install: true,
|
||||||
|
kernel: false,
|
||||||
|
conn_file: None,
|
||||||
|
name: Some("debugdeno".to_string()),
|
||||||
|
display: Some("Deno (debug)".to_string()),
|
||||||
|
force: false,
|
||||||
|
}),
|
||||||
|
..Flags::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let r = flags_from_vec(svec!["deno", "jupyter", "-n", "debugdeno",]);
|
||||||
|
assert_eq!(
|
||||||
|
r.unwrap(),
|
||||||
|
Flags {
|
||||||
|
subcommand: DenoSubcommand::Jupyter(JupyterFlags {
|
||||||
|
install: false,
|
||||||
|
kernel: false,
|
||||||
|
conn_file: None,
|
||||||
|
name: Some("debugdeno".to_string()),
|
||||||
|
display: None,
|
||||||
|
force: false,
|
||||||
}),
|
}),
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
}
|
}
|
||||||
|
@ -11372,6 +11466,9 @@ mod tests {
|
||||||
install: false,
|
install: false,
|
||||||
kernel: true,
|
kernel: true,
|
||||||
conn_file: Some(String::from("path/to/conn/file")),
|
conn_file: Some(String::from("path/to/conn/file")),
|
||||||
|
name: None,
|
||||||
|
display: None,
|
||||||
|
force: false,
|
||||||
}),
|
}),
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
}
|
}
|
||||||
|
@ -11389,6 +11486,12 @@ mod tests {
|
||||||
r.unwrap_err();
|
r.unwrap_err();
|
||||||
let r = flags_from_vec(svec!["deno", "jupyter", "--install", "--kernel",]);
|
let r = flags_from_vec(svec!["deno", "jupyter", "--install", "--kernel",]);
|
||||||
r.unwrap_err();
|
r.unwrap_err();
|
||||||
|
let r = flags_from_vec(svec!["deno", "jupyter", "--display", "deno"]);
|
||||||
|
r.unwrap_err();
|
||||||
|
let r = flags_from_vec(svec!["deno", "jupyter", "--kernel", "--display"]);
|
||||||
|
r.unwrap_err();
|
||||||
|
let r = flags_from_vec(svec!["deno", "jupyter", "--force"]);
|
||||||
|
r.unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -11733,6 +11836,9 @@ mod tests {
|
||||||
install: false,
|
install: false,
|
||||||
kernel: false,
|
kernel: false,
|
||||||
conn_file: None,
|
conn_file: None,
|
||||||
|
name: None,
|
||||||
|
display: None,
|
||||||
|
force: false,
|
||||||
}),
|
}),
|
||||||
unstable_config: UnstableConfig {
|
unstable_config: UnstableConfig {
|
||||||
bare_node_builtins: true,
|
bare_node_builtins: true,
|
||||||
|
|
|
@ -3,27 +3,51 @@
|
||||||
use std::env::current_exe;
|
use std::env::current_exe;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use deno_core::anyhow::bail;
|
||||||
|
use deno_core::anyhow::Context;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
use deno_core::serde_json::json;
|
use deno_core::serde_json::json;
|
||||||
use jupyter_runtime::dirs::user_data_dir;
|
|
||||||
|
static TEST_ENV_VAR_NAME: &str = "DENO_TEST_JUPYTER_PATH";
|
||||||
|
|
||||||
const DENO_ICON_32: &[u8] = include_bytes!("./resources/deno-logo-32x32.png");
|
const DENO_ICON_32: &[u8] = include_bytes!("./resources/deno-logo-32x32.png");
|
||||||
const DENO_ICON_64: &[u8] = include_bytes!("./resources/deno-logo-64x64.png");
|
const DENO_ICON_64: &[u8] = include_bytes!("./resources/deno-logo-64x64.png");
|
||||||
const DENO_ICON_SVG: &[u8] = include_bytes!("./resources/deno-logo-svg.svg");
|
const DENO_ICON_SVG: &[u8] = include_bytes!("./resources/deno-logo-svg.svg");
|
||||||
|
|
||||||
pub fn status() -> Result<(), AnyError> {
|
fn get_user_data_dir() -> Result<PathBuf, AnyError> {
|
||||||
let user_data_dir = user_data_dir()?;
|
Ok(if let Some(env_var) = std::env::var_os(TEST_ENV_VAR_NAME) {
|
||||||
|
PathBuf::from(env_var)
|
||||||
|
} else {
|
||||||
|
jupyter_runtime::dirs::user_data_dir()?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
let kernel_spec_dir_path = user_data_dir.join("kernels").join("deno");
|
pub fn status(maybe_name: Option<&str>) -> Result<(), AnyError> {
|
||||||
|
let user_data_dir = get_user_data_dir()?;
|
||||||
|
|
||||||
|
let kernel_name = maybe_name.unwrap_or("deno");
|
||||||
|
let kernel_spec_dir_path = user_data_dir.join("kernels").join(kernel_name);
|
||||||
let kernel_spec_path = kernel_spec_dir_path.join("kernel.json");
|
let kernel_spec_path = kernel_spec_dir_path.join("kernel.json");
|
||||||
|
|
||||||
if kernel_spec_path.exists() {
|
if kernel_spec_path.exists() {
|
||||||
log::info!("✅ Deno kernel already installed");
|
log::info!(
|
||||||
|
"✅ Deno kernel already installed at {}",
|
||||||
|
kernel_spec_dir_path.display()
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
log::warn!("ℹ️ Deno kernel is not yet installed, run `deno jupyter --install` to set it up");
|
let mut install_cmd = "deno jupyter --install".to_string();
|
||||||
|
if let Some(name) = maybe_name {
|
||||||
|
install_cmd.push_str(" --name ");
|
||||||
|
install_cmd.push_str(name);
|
||||||
|
}
|
||||||
|
log::warn!(
|
||||||
|
"ℹ️ Deno kernel is not yet installed, run `{}` to set it up",
|
||||||
|
install_cmd
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,29 +63,69 @@ fn install_icon(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install() -> Result<(), AnyError> {
|
pub fn install(
|
||||||
let user_data_dir = user_data_dir()?;
|
maybe_name: Option<&str>,
|
||||||
let kernel_dir = user_data_dir.join("kernels").join("deno");
|
maybe_display_name: Option<&str>,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<(), AnyError> {
|
||||||
|
let user_data_dir = get_user_data_dir()?;
|
||||||
|
|
||||||
std::fs::create_dir_all(&kernel_dir)?;
|
let kernel_name = maybe_name.unwrap_or("deno");
|
||||||
|
let kernel_spec_dir_path = user_data_dir.join("kernels").join(kernel_name);
|
||||||
|
let kernel_spec_path = kernel_spec_dir_path.join("kernel.json");
|
||||||
|
|
||||||
let kernel_json_path = kernel_dir.join("kernel.json");
|
std::fs::create_dir_all(&kernel_spec_dir_path).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"Failed to create kernel directory at {}",
|
||||||
|
kernel_spec_dir_path.display()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if kernel_spec_path.exists() && !force {
|
||||||
|
bail!(
|
||||||
|
"Deno kernel already exists at {}, run again with `--force` to overwrite it",
|
||||||
|
kernel_spec_dir_path.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let display_name = maybe_display_name.unwrap_or("Deno");
|
||||||
|
let current_exe_path = current_exe()
|
||||||
|
.context("Failed to get current executable path")?
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
// TODO(bartlomieju): add remaining fields as per
|
// TODO(bartlomieju): add remaining fields as per
|
||||||
// https://jupyter-client.readthedocs.io/en/stable/kernels.html#kernel-specs
|
// https://jupyter-client.readthedocs.io/en/stable/kernels.html#kernel-specs
|
||||||
// FIXME(bartlomieju): replace `current_exe` before landing?
|
|
||||||
let json_data = json!({
|
let json_data = json!({
|
||||||
"argv": [current_exe().unwrap().to_string_lossy(), "jupyter", "--kernel", "--conn", "{connection_file}"],
|
"argv": [current_exe_path, "jupyter", "--kernel", "--conn", "{connection_file}"],
|
||||||
"display_name": "Deno",
|
"display_name": display_name,
|
||||||
"language": "typescript",
|
"language": "typescript",
|
||||||
});
|
});
|
||||||
|
|
||||||
let f = std::fs::File::create(kernel_json_path)?;
|
let f = std::fs::File::create(&kernel_spec_path).with_context(|| {
|
||||||
serde_json::to_writer_pretty(f, &json_data)?;
|
format!(
|
||||||
install_icon(&kernel_dir, "logo-32x32.png", DENO_ICON_32)?;
|
"Failed to create kernelspec file at {}",
|
||||||
install_icon(&kernel_dir, "logo-64x64.png", DENO_ICON_64)?;
|
kernel_spec_path.display()
|
||||||
install_icon(&kernel_dir, "logo-svg.svg", DENO_ICON_SVG)?;
|
)
|
||||||
|
})?;
|
||||||
|
serde_json::to_writer_pretty(f, &json_data).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"Failed to write kernelspec file at {}",
|
||||||
|
kernel_spec_path.display()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let failed_icon_fn =
|
||||||
|
|| format!("Failed to copy icon to {}", kernel_spec_dir_path.display());
|
||||||
|
install_icon(&kernel_spec_dir_path, "logo-32x32.png", DENO_ICON_32)
|
||||||
|
.with_context(failed_icon_fn)?;
|
||||||
|
install_icon(&kernel_spec_dir_path, "logo-64x64.png", DENO_ICON_64)
|
||||||
|
.with_context(failed_icon_fn)?;
|
||||||
|
install_icon(&kernel_spec_dir_path, "logo-svg.svg", DENO_ICON_SVG)
|
||||||
|
.with_context(failed_icon_fn)?;
|
||||||
|
|
||||||
log::info!("✅ Deno kernelspec installed successfully.");
|
log::info!(
|
||||||
|
"✅ Deno kernelspec installed successfully at {}.",
|
||||||
|
kernel_spec_dir_path.display()
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,12 +49,16 @@ pub async fn kernel(
|
||||||
);
|
);
|
||||||
|
|
||||||
if !jupyter_flags.install && !jupyter_flags.kernel {
|
if !jupyter_flags.install && !jupyter_flags.kernel {
|
||||||
install::status()?;
|
install::status(jupyter_flags.name.as_deref())?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if jupyter_flags.install {
|
if jupyter_flags.install {
|
||||||
install::install()?;
|
install::install(
|
||||||
|
jupyter_flags.name.as_deref(),
|
||||||
|
jupyter_flags.display.as_deref(),
|
||||||
|
jupyter_flags.force,
|
||||||
|
)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,24 @@
|
||||||
{
|
{
|
||||||
"args": "jupyter --install",
|
"tempDir": true,
|
||||||
"output": "install_command.out",
|
|
||||||
"envs": {
|
"envs": {
|
||||||
"PATH": ""
|
"PATH": "",
|
||||||
|
"DENO_TEST_JUPYTER_PATH": "$PWD"
|
||||||
},
|
},
|
||||||
"exitCode": 0
|
"steps": [{
|
||||||
|
"args": "jupyter --install",
|
||||||
|
"output": "install_command.out",
|
||||||
|
"exitCode": 0
|
||||||
|
}, {
|
||||||
|
"args": "jupyter --install",
|
||||||
|
"output": "already_installed.out",
|
||||||
|
"exitCode": 1
|
||||||
|
}, {
|
||||||
|
"args": "jupyter --install --name devdeno",
|
||||||
|
"output": "install_name.out",
|
||||||
|
"exitCode": 0
|
||||||
|
}, {
|
||||||
|
"args": "jupyter --install --force",
|
||||||
|
"output": "install_command.out",
|
||||||
|
"exitCode": 0
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Warning "deno jupyter" is unstable and might change in the future.
|
||||||
|
error: Deno kernel already exists at [WILDLINE]deno, run again with `--force` to overwrite it
|
|
@ -1,2 +1,2 @@
|
||||||
Warning "deno jupyter" is unstable and might change in the future.
|
Warning "deno jupyter" is unstable and might change in the future.
|
||||||
✅ Deno kernelspec installed successfully.
|
✅ Deno kernelspec installed successfully at [WILDLINE]deno.
|
||||||
|
|
2
tests/specs/jupyter/install_command/install_name.out
Normal file
2
tests/specs/jupyter/install_command/install_name.out
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Warning "deno jupyter" is unstable and might change in the future.
|
||||||
|
✅ Deno kernelspec installed successfully at [WILDLINE]devdeno.
|
Loading…
Add table
Add a link
Reference in a new issue