summaryrefslogtreecommitdiff
path: root/cli/tools/jupyter/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/tools/jupyter/mod.rs')
-rw-r--r--cli/tools/jupyter/mod.rs139
1 files changed, 139 insertions, 0 deletions
diff --git a/cli/tools/jupyter/mod.rs b/cli/tools/jupyter/mod.rs
new file mode 100644
index 000000000..b704d58cd
--- /dev/null
+++ b/cli/tools/jupyter/mod.rs
@@ -0,0 +1,139 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use crate::args::Flags;
+use crate::args::JupyterFlags;
+use crate::tools::repl;
+use crate::util::logger;
+use crate::CliFactory;
+use deno_core::anyhow::Context;
+use deno_core::error::AnyError;
+use deno_core::futures::channel::mpsc;
+use deno_core::op;
+use deno_core::resolve_url_or_path;
+use deno_core::serde::Deserialize;
+use deno_core::serde_json;
+use deno_core::Op;
+use deno_core::OpState;
+use deno_runtime::permissions::Permissions;
+use deno_runtime::permissions::PermissionsContainer;
+
+mod install;
+mod jupyter_msg;
+mod server;
+
+pub async fn kernel(
+ flags: Flags,
+ jupyter_flags: JupyterFlags,
+) -> Result<(), AnyError> {
+ if !flags.unstable {
+ eprintln!(
+ "Unstable subcommand 'deno jupyter'. The --unstable flag must be provided."
+ );
+ std::process::exit(70);
+ }
+
+ if !jupyter_flags.install && !jupyter_flags.kernel {
+ install::status()?;
+ return Ok(());
+ }
+
+ if jupyter_flags.install {
+ install::install()?;
+ return Ok(());
+ }
+
+ let connection_filepath = jupyter_flags.conn_file.unwrap();
+
+ // This env var might be set by notebook
+ if std::env::var("DEBUG").is_ok() {
+ logger::init(Some(log::Level::Debug));
+ }
+
+ let factory = CliFactory::from_flags(flags).await?;
+ let cli_options = factory.cli_options();
+ let main_module =
+ resolve_url_or_path("./$deno$jupyter.ts", cli_options.initial_cwd())
+ .unwrap();
+ // TODO(bartlomieju): should we run with all permissions?
+ let permissions = PermissionsContainer::new(Permissions::allow_all());
+ let npm_resolver = factory.npm_resolver().await?.clone();
+ let resolver = factory.resolver().await?.clone();
+ let worker_factory = factory.create_cli_main_worker_factory().await?;
+ let (stdio_tx, stdio_rx) = mpsc::unbounded();
+
+ let conn_file =
+ std::fs::read_to_string(&connection_filepath).with_context(|| {
+ format!("Couldn't read connection file: {:?}", connection_filepath)
+ })?;
+ let spec: ConnectionSpec =
+ serde_json::from_str(&conn_file).with_context(|| {
+ format!(
+ "Connection file is not a valid JSON: {:?}",
+ connection_filepath
+ )
+ })?;
+
+ let mut worker = worker_factory
+ .create_custom_worker(
+ main_module.clone(),
+ permissions,
+ vec![deno_jupyter::init_ops(stdio_tx)],
+ Default::default(),
+ )
+ .await?;
+ worker.setup_repl().await?;
+ let worker = worker.into_main_worker();
+ let repl_session =
+ repl::ReplSession::initialize(cli_options, npm_resolver, resolver, worker)
+ .await?;
+
+ server::JupyterServer::start(spec, stdio_rx, repl_session).await?;
+
+ Ok(())
+}
+
+deno_core::extension!(deno_jupyter,
+ options = {
+ sender: mpsc::UnboundedSender<server::StdioMsg>,
+ },
+ middleware = |op| match op.name {
+ "op_print" => op_print::DECL,
+ _ => op,
+ },
+ state = |state, options| {
+ state.put(options.sender);
+ },
+);
+
+#[op]
+pub fn op_print(
+ state: &mut OpState,
+ msg: String,
+ is_err: bool,
+) -> Result<(), AnyError> {
+ let sender = state.borrow_mut::<mpsc::UnboundedSender<server::StdioMsg>>();
+
+ if is_err {
+ if let Err(err) = sender.unbounded_send(server::StdioMsg::Stderr(msg)) {
+ eprintln!("Failed to send stderr message: {}", err);
+ }
+ return Ok(());
+ }
+
+ if let Err(err) = sender.unbounded_send(server::StdioMsg::Stdout(msg)) {
+ eprintln!("Failed to send stdout message: {}", err);
+ }
+ Ok(())
+}
+
+#[derive(Debug, Deserialize)]
+pub struct ConnectionSpec {
+ ip: String,
+ transport: String,
+ control_port: u32,
+ shell_port: u32,
+ stdin_port: u32,
+ hb_port: u32,
+ iopub_port: u32,
+ key: String,
+}