diff options
author | Kyle Kelley <rgbkrk@gmail.com> | 2023-10-12 15:32:38 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-12 22:32:38 +0000 |
commit | 48e695a2c89edad6e4880e7decfdb36d524f8279 (patch) | |
tree | 3107db3c04775a9627515b04243551957a6d0127 /cli/tools/jupyter | |
parent | c464cd7073c761780b3170a48542c387560e3f26 (diff) |
feat(unstable): add Deno.jupyter.display API (#20819)
This brings in [`display`](https://github.com/rgbkrk/display.js) as part
of the `Deno.jupyter` namespace.
Additionally these APIs were added:
- "Deno.jupyter.md"
- "Deno.jupyter.html"
- "Deno.jupyter.svg"
- "Deno.jupyter.format"
These APIs greatly extend capabilities of rendering output in Jupyter
notebooks.
---------
Co-authored-by: Bartek IwaĆczuk <biwanczuk@gmail.com>
Diffstat (limited to 'cli/tools/jupyter')
-rw-r--r-- | cli/tools/jupyter/server.rs | 111 |
1 files changed, 17 insertions, 94 deletions
diff --git a/cli/tools/jupyter/server.rs b/cli/tools/jupyter/server.rs index 2c7bea9d2..6175a33e9 100644 --- a/cli/tools/jupyter/server.rs +++ b/cli/tools/jupyter/server.rs @@ -388,21 +388,9 @@ impl JupyterServer { } = evaluate_response.value; if exception_details.is_none() { - let output = - get_jupyter_display_or_eval_value(&mut self.repl_session, &result) - .await?; - // Don't bother sending `execute_result` reply if the MIME bundle is empty - if !output.is_empty() { - msg - .new_message("execute_result") - .with_content(json!({ - "execution_count": self.execution_count, - "data": output, - "metadata": {}, - })) - .send(&mut *self.iopub_socket.lock().await) - .await?; - } + publish_result(&mut self.repl_session, &result, self.execution_count) + .await?; + msg .new_reply() .with_content(json!({ @@ -543,32 +531,33 @@ fn kernel_info() -> serde_json::Value { }) } -async fn get_jupyter_display( +async fn publish_result( session: &mut repl::ReplSession, evaluate_result: &cdp::RemoteObject, + execution_count: usize, ) -> Result<Option<HashMap<String, serde_json::Value>>, AnyError> { + let arg0 = cdp::CallArgument { + value: Some(serde_json::Value::Number(execution_count.into())), + unserializable_value: None, + object_id: None, + }; + + let arg1 = cdp::CallArgument::from(evaluate_result); + let response = session .post_message_with_event_loop( "Runtime.callFunctionOn", Some(json!({ - "functionDeclaration": r#"async function (object) { - if (typeof object[Symbol.for("Jupyter.display")] !== "function") { - return null; - } - - try { - const representation = await object[Symbol.for("Jupyter.display")](); - return JSON.stringify(representation); - } catch { - return null; - } + "functionDeclaration": r#"async function (execution_count, result) { + await Deno[Deno.internal].jupyter.broadcastResult(execution_count, result); }"#, - "arguments": [cdp::CallArgument::from(evaluate_result)], + "arguments": [arg0, arg1], "executionContextId": session.context_id, "awaitPromise": true, })), ) .await?; + let response: cdp::CallFunctionOnResponse = serde_json::from_value(response)?; if let Some(exception_details) = &response.exception_details { @@ -578,75 +567,9 @@ async fn get_jupyter_display( return Ok(None); } - match response.result.value { - Some(serde_json::Value::String(json_str)) => { - let Ok(data) = - serde_json::from_str::<HashMap<String, serde_json::Value>>(&json_str) - else { - eprintln!("Unexpected response from Jupyter.display: {json_str}"); - return Ok(None); - }; - - if !data.is_empty() { - return Ok(Some(data)); - } - } - Some(serde_json::Value::Null) => { - // Object did not have the Jupyter display spec - return Ok(None); - } - _ => { - eprintln!( - "Unexpected response from Jupyter.display: {:?}", - response.result - ) - } - } - Ok(None) } -async fn get_jupyter_display_or_eval_value( - session: &mut repl::ReplSession, - evaluate_result: &cdp::RemoteObject, -) -> Result<HashMap<String, serde_json::Value>, AnyError> { - // Printing "undefined" generates a lot of noise, so let's skip - // these. - if evaluate_result.kind == "undefined" { - return Ok(HashMap::default()); - } - - // If the response is a primitive value we don't need to try and format - // Jupyter response. - if evaluate_result.object_id.is_some() { - if let Some(data) = get_jupyter_display(session, evaluate_result).await? { - return Ok(data); - } - } - - let response = session - .call_function_on_args( - format!( - r#"function (object) {{ - try {{ - return {0}.inspectArgs(["%o", object], {{ colors: !{0}.noColor }}); - }} catch (err) {{ - return {0}.inspectArgs(["%o", err]); - }} - }}"#, - *repl::REPL_INTERNALS_NAME - ), - &[evaluate_result.clone()], - ) - .await?; - let mut data = HashMap::default(); - if let Some(value) = response.result.value { - data.insert("text/plain".to_string(), value); - } - - Ok(data) -} - // TODO(bartlomieju): dedup with repl::editor fn get_expr_from_line_at_pos(line: &str, cursor_pos: usize) -> &str { let start = line[..cursor_pos].rfind(is_word_boundary).unwrap_or(0); |