summaryrefslogtreecommitdiff
path: root/cli/tools/jupyter/server.rs
diff options
context:
space:
mode:
authorKyle Kelley <rgbkrk@gmail.com>2023-10-12 15:32:38 -0700
committerGitHub <noreply@github.com>2023-10-12 22:32:38 +0000
commit48e695a2c89edad6e4880e7decfdb36d524f8279 (patch)
tree3107db3c04775a9627515b04243551957a6d0127 /cli/tools/jupyter/server.rs
parentc464cd7073c761780b3170a48542c387560e3f26 (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/server.rs')
-rw-r--r--cli/tools/jupyter/server.rs111
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);