diff options
Diffstat (limited to 'cli/js/40_jupyter.js')
-rw-r--r-- | cli/js/40_jupyter.js | 687 |
1 files changed, 344 insertions, 343 deletions
diff --git a/cli/js/40_jupyter.js b/cli/js/40_jupyter.js index c4b27ad0b..a0a637472 100644 --- a/cli/js/40_jupyter.js +++ b/cli/js/40_jupyter.js @@ -1,4 +1,5 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// deno-lint-ignore-file /* * @module mod @@ -35,184 +36,215 @@ * }, { raw: true }); * ``` */ +{ + const internals = Deno[Deno.internal]; + const core = internals.core; -const core = globalThis.Deno.core; + const $display = Symbol.for("Jupyter.display"); -const internals = globalThis.__bootstrap.internals; + /** Escape copied from https://deno.land/std@0.192.0/html/entities.ts */ + const rawToEntityEntries = [ + ["&", "&"], + ["<", "<"], + [">", ">"], + ['"', """], + ["'", "'"], + ]; -const $display = Symbol.for("Jupyter.display"); + const rawToEntity = new Map(rawToEntityEntries); -/** Escape copied from https://deno.land/std@0.192.0/html/entities.ts */ -const rawToEntityEntries = [ - ["&", "&"], - ["<", "<"], - [">", ">"], - ['"', """], - ["'", "'"], -]; + const rawRe = new RegExp(`[${[...rawToEntity.keys()].join("")}]`, "g"); -const rawToEntity = new Map(rawToEntityEntries); - -const rawRe = new RegExp(`[${[...rawToEntity.keys()].join("")}]`, "g"); - -function escapeHTML(str) { - return str.replaceAll( - rawRe, - (m) => rawToEntity.has(m) ? rawToEntity.get(m) : m, - ); -} + function escapeHTML(str) { + return str.replaceAll( + rawRe, + (m) => rawToEntity.has(m) ? rawToEntity.get(m) : m, + ); + } -/** Duck typing our way to common visualization and tabular libraries */ -/** Vegalite */ -function isVegaLike(obj) { - return obj !== null && typeof obj === "object" && "toSpec" in obj; -} -function extractVega(obj) { - const spec = obj.toSpec(); - if (!("$schema" in spec)) { - return null; + /** Duck typing our way to common visualization and tabular libraries */ + /** Vegalite */ + function isVegaLike(obj) { + return obj !== null && typeof obj === "object" && "toSpec" in obj; } - if (typeof spec !== "object") { - return null; + function extractVega(obj) { + const spec = obj.toSpec(); + if (!("$schema" in spec)) { + return null; + } + if (typeof spec !== "object") { + return null; + } + let mediaType = "application/vnd.vega.v5+json"; + if (spec.$schema === "https://vega.github.io/schema/vega-lite/v4.json") { + mediaType = "application/vnd.vegalite.v4+json"; + } else if ( + spec.$schema === "https://vega.github.io/schema/vega-lite/v5.json" + ) { + mediaType = "application/vnd.vegalite.v5+json"; + } + return { + [mediaType]: spec, + }; } - let mediaType = "application/vnd.vega.v5+json"; - if (spec.$schema === "https://vega.github.io/schema/vega-lite/v4.json") { - mediaType = "application/vnd.vegalite.v4+json"; - } else if ( - spec.$schema === "https://vega.github.io/schema/vega-lite/v5.json" - ) { - mediaType = "application/vnd.vegalite.v5+json"; + /** Polars */ + function isDataFrameLike(obj) { + const isObject = obj !== null && typeof obj === "object"; + if (!isObject) { + return false; + } + const df = obj; + return df.schema !== void 0 && typeof df.schema === "object" && + df.head !== void 0 && typeof df.head === "function" && + df.toRecords !== void 0 && typeof df.toRecords === "function"; } - return { - [mediaType]: spec, - }; -} -/** Polars */ -function isDataFrameLike(obj) { - const isObject = obj !== null && typeof obj === "object"; - if (!isObject) { - return false; + /** + * Map Polars DataType to JSON Schema data types. + * @param dataType - The Polars DataType. + * @returns The corresponding JSON Schema data type. + */ + function mapPolarsTypeToJSONSchema(colType) { + const typeMapping = { + Null: "null", + Bool: "boolean", + Int8: "integer", + Int16: "integer", + Int32: "integer", + Int64: "integer", + UInt8: "integer", + UInt16: "integer", + UInt32: "integer", + UInt64: "integer", + Float32: "number", + Float64: "number", + Date: "string", + Datetime: "string", + Utf8: "string", + Categorical: "string", + List: "array", + Struct: "object", + }; + // These colTypes are weird. When you console.dir or console.log them + // they show a `DataType` field, however you can't access it directly until you + // convert it to JSON + const dataType = colType.toJSON()["DataType"]; + return typeMapping[dataType] || "string"; } - const df = obj; - return df.schema !== void 0 && typeof df.schema === "object" && - df.head !== void 0 && typeof df.head === "function" && - df.toRecords !== void 0 && typeof df.toRecords === "function"; -} -/** - * Map Polars DataType to JSON Schema data types. - * @param dataType - The Polars DataType. - * @returns The corresponding JSON Schema data type. - */ -function mapPolarsTypeToJSONSchema(colType) { - const typeMapping = { - Null: "null", - Bool: "boolean", - Int8: "integer", - Int16: "integer", - Int32: "integer", - Int64: "integer", - UInt8: "integer", - UInt16: "integer", - UInt32: "integer", - UInt64: "integer", - Float32: "number", - Float64: "number", - Date: "string", - Datetime: "string", - Utf8: "string", - Categorical: "string", - List: "array", - Struct: "object", - }; - // These colTypes are weird. When you console.dir or console.log them - // they show a `DataType` field, however you can't access it directly until you - // convert it to JSON - const dataType = colType.toJSON()["DataType"]; - return typeMapping[dataType] || "string"; -} -function extractDataFrame(df) { - const fields = []; - const schema = { - fields, - }; - let data = []; - // Convert DataFrame schema to Tabular DataResource schema - for (const [colName, colType] of Object.entries(df.schema)) { - const dataType = mapPolarsTypeToJSONSchema(colType); - schema.fields.push({ - name: colName, - type: dataType, + function extractDataFrame(df) { + const fields = []; + const schema = { + fields, + }; + let data = []; + // Convert DataFrame schema to Tabular DataResource schema + for (const [colName, colType] of Object.entries(df.schema)) { + const dataType = mapPolarsTypeToJSONSchema(colType); + schema.fields.push({ + name: colName, + type: dataType, + }); + } + // Convert DataFrame data to row-oriented JSON + // + // TODO(rgbkrk): Determine how to get the polars format max rows + // Since pl.setTblRows just sets env var POLARS_FMT_MAX_ROWS, + // we probably just have to pick a number for now. + // + + data = df.head(50).toRecords(); + let htmlTable = "<table>"; + htmlTable += "<thead><tr>"; + schema.fields.forEach((field) => { + htmlTable += `<th>${escapeHTML(String(field.name))}</th>`; + }); + htmlTable += "</tr></thead>"; + htmlTable += "<tbody>"; + df.head(10).toRecords().forEach((row) => { + htmlTable += "<tr>"; + schema.fields.forEach((field) => { + htmlTable += `<td>${escapeHTML(String(row[field.name]))}</td>`; + }); + htmlTable += "</tr>"; }); + htmlTable += "</tbody></table>"; + return { + "application/vnd.dataresource+json": { data, schema }, + "text/html": htmlTable, + }; } - // Convert DataFrame data to row-oriented JSON - // - // TODO(rgbkrk): Determine how to get the polars format max rows - // Since pl.setTblRows just sets env var POLARS_FMT_MAX_ROWS, - // we probably just have to pick a number for now. - // - data = df.head(50).toRecords(); - let htmlTable = "<table>"; - htmlTable += "<thead><tr>"; - schema.fields.forEach((field) => { - htmlTable += `<th>${escapeHTML(String(field.name))}</th>`; - }); - htmlTable += "</tr></thead>"; - htmlTable += "<tbody>"; - df.head(10).toRecords().forEach((row) => { - htmlTable += "<tr>"; - schema.fields.forEach((field) => { - htmlTable += `<td>${escapeHTML(String(row[field.name]))}</td>`; - }); - htmlTable += "</tr>"; - }); - htmlTable += "</tbody></table>"; - return { - "application/vnd.dataresource+json": { data, schema }, - "text/html": htmlTable, - }; -} + /** Canvas */ + function isCanvasLike(obj) { + return obj !== null && typeof obj === "object" && "toDataURL" in obj; + } -/** Canvas */ -function isCanvasLike(obj) { - return obj !== null && typeof obj === "object" && "toDataURL" in obj; -} + /** Possible HTML and SVG Elements */ + function isSVGElementLike(obj) { + return obj !== null && typeof obj === "object" && "outerHTML" in obj && + typeof obj.outerHTML === "string" && obj.outerHTML.startsWith("<svg"); + } -/** Possible HTML and SVG Elements */ -function isSVGElementLike(obj) { - return obj !== null && typeof obj === "object" && "outerHTML" in obj && - typeof obj.outerHTML === "string" && obj.outerHTML.startsWith("<svg"); -} + function isHTMLElementLike(obj) { + return obj !== null && typeof obj === "object" && "outerHTML" in obj && + typeof obj.outerHTML === "string"; + } -function isHTMLElementLike(obj) { - return obj !== null && typeof obj === "object" && "outerHTML" in obj && - typeof obj.outerHTML === "string"; -} + /** Check to see if an object already contains a `Symbol.for("Jupyter.display") */ + function hasDisplaySymbol(obj) { + return obj !== null && typeof obj === "object" && $display in obj && + typeof obj[$display] === "function"; + } -/** Check to see if an object already contains a `Symbol.for("Jupyter.display") */ -function hasDisplaySymbol(obj) { - return obj !== null && typeof obj === "object" && $display in obj && - typeof obj[$display] === "function"; -} + function makeDisplayable(obj) { + return { + [$display]: () => obj, + }; + } -function makeDisplayable(obj) { - return { - [$display]: () => obj, - }; -} + /** + * Format an object for displaying in Deno + * + * @param obj - The object to be displayed + * @returns MediaBundle + */ + async function format(obj) { + if (hasDisplaySymbol(obj)) { + return await obj[$display](); + } + if (typeof obj !== "object") { + return { + "text/plain": Deno[Deno.internal].inspectArgs(["%o", obj], { + colors: !Deno.noColor, + }), + }; + } -/** - * Format an object for displaying in Deno - * - * @param obj - The object to be displayed - * @returns MediaBundle - */ -async function format(obj) { - if (hasDisplaySymbol(obj)) { - return await obj[$display](); - } - if (typeof obj !== "object") { + if (isCanvasLike(obj)) { + const dataURL = obj.toDataURL(); + const parts = dataURL.split(","); + const mime = parts[0].split(":")[1].split(";")[0]; + const data = parts[1]; + return { + [mime]: data, + }; + } + if (isVegaLike(obj)) { + return extractVega(obj); + } + if (isDataFrameLike(obj)) { + return extractDataFrame(obj); + } + if (isSVGElementLike(obj)) { + return { + "image/svg+xml": obj.outerHTML, + }; + } + if (isHTMLElementLike(obj)) { + return { + "text/html": obj.outerHTML, + }; + } return { "text/plain": Deno[Deno.internal].inspectArgs(["%o", obj], { colors: !Deno.noColor, @@ -220,211 +252,180 @@ async function format(obj) { }; } - if (isCanvasLike(obj)) { - const dataURL = obj.toDataURL(); - const parts = dataURL.split(","); - const mime = parts[0].split(":")[1].split(";")[0]; - const data = parts[1]; - return { - [mime]: data, - }; - } - if (isVegaLike(obj)) { - return extractVega(obj); - } - if (isDataFrameLike(obj)) { - return extractDataFrame(obj); - } - if (isSVGElementLike(obj)) { - return { - "image/svg+xml": obj.outerHTML, - }; - } - if (isHTMLElementLike(obj)) { - return { - "text/html": obj.outerHTML, + /** + * This function creates a tagged template function for a given media type. + * The tagged template function takes a template string and returns a displayable object. + * + * @param mediatype - The media type for the tagged template function. + * @returns A function that takes a template string and returns a displayable object. + */ + function createTaggedTemplateDisplayable(mediatype) { + return (strings, ...values) => { + const payload = strings.reduce( + (acc, string, i) => + acc + string + (values[i] !== undefined ? values[i] : ""), + "", + ); + return makeDisplayable({ [mediatype]: payload }); }; } - return { - "text/plain": Deno[Deno.internal].inspectArgs(["%o", obj], { - colors: !Deno.noColor, - }), - }; -} -/** - * This function creates a tagged template function for a given media type. - * The tagged template function takes a template string and returns a displayable object. - * - * @param mediatype - The media type for the tagged template function. - * @returns A function that takes a template string and returns a displayable object. - */ -function createTaggedTemplateDisplayable(mediatype) { - return (strings, ...values) => { - const payload = strings.reduce( - (acc, string, i) => - acc + string + (values[i] !== undefined ? values[i] : ""), - "", - ); - return makeDisplayable({ [mediatype]: payload }); - }; -} - -/** - * Show Markdown in Jupyter frontends with a tagged template function. - * - * Takes a template string and returns a displayable object for Jupyter frontends. - * - * @example - * Create a Markdown view. - * - * ```typescript - * md`# Notebooks in TypeScript via Deno  - * - * * TypeScript ${Deno.version.typescript} - * * V8 ${Deno.version.v8} - * * Deno ${Deno.version.deno} - * - * Interactive compute with Jupyter _built into Deno_! - * ` - * ``` - */ -const md = createTaggedTemplateDisplayable("text/markdown"); + /** + * Show Markdown in Jupyter frontends with a tagged template function. + * + * Takes a template string and returns a displayable object for Jupyter frontends. + * + * @example + * Create a Markdown view. + * + * ```typescript + * md`# Notebooks in TypeScript via Deno  + * + * * TypeScript ${Deno.version.typescript} + * * V8 ${Deno.version.v8} + * * Deno ${Deno.version.deno} + * + * Interactive compute with Jupyter _built into Deno_! + * ` + * ``` + */ + const md = createTaggedTemplateDisplayable("text/markdown"); -/** - * Show HTML in Jupyter frontends with a tagged template function. - * - * Takes a template string and returns a displayable object for Jupyter frontends. - * - * @example - * Create an HTML view. - * ```typescript - * html`<h1>Hello, world!</h1>` - * ``` - */ -const html = createTaggedTemplateDisplayable("text/html"); -/** - * SVG Tagged Template Function. - * - * Takes a template string and returns a displayable object for Jupyter frontends. - * - * Example usage: - * - * svg`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> - * <circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" /> - * </svg>` - */ -const svg = createTaggedTemplateDisplayable("image/svg+xml"); + /** + * Show HTML in Jupyter frontends with a tagged template function. + * + * Takes a template string and returns a displayable object for Jupyter frontends. + * + * @example + * Create an HTML view. + * ```typescript + * html`<h1>Hello, world!</h1>` + * ``` + */ + const html = createTaggedTemplateDisplayable("text/html"); + /** + * SVG Tagged Template Function. + * + * Takes a template string and returns a displayable object for Jupyter frontends. + * + * Example usage: + * + * svg`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> + * <circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" /> + * </svg>` + */ + const svg = createTaggedTemplateDisplayable("image/svg+xml"); -function isMediaBundle(obj) { - if (obj == null || typeof obj !== "object" || Array.isArray(obj)) { - return false; - } - for (const key in obj) { - if (typeof key !== "string") { + function isMediaBundle(obj) { + if (obj == null || typeof obj !== "object" || Array.isArray(obj)) { return false; } + for (const key in obj) { + if (typeof key !== "string") { + return false; + } + } + return true; } - return true; -} -async function formatInner(obj, raw) { - if (raw && isMediaBundle(obj)) { - return obj; - } else { - return await format(obj); + async function formatInner(obj, raw) { + if (raw && isMediaBundle(obj)) { + return obj; + } else { + return await format(obj); + } } -} -internals.jupyter = { formatInner }; + internals.jupyter = { formatInner }; -function enableJupyter() { - const { - op_jupyter_broadcast, - } = core.ensureFastOps(); + function enableJupyter() { + const { + op_jupyter_broadcast, + } = core.ensureFastOps(); - async function broadcast( - msgType, - content, - { metadata = {}, buffers = [] } = {}, - ) { - await op_jupyter_broadcast(msgType, content, metadata, buffers); - } + async function broadcast( + msgType, + content, + { metadata = {}, buffers = [] } = {}, + ) { + await op_jupyter_broadcast(msgType, content, metadata, buffers); + } - async function broadcastResult(executionCount, result) { - try { - if (result === undefined) { - return; + async function broadcastResult(executionCount, result) { + try { + if (result === undefined) { + return; + } + + const data = await format(result); + await broadcast("execute_result", { + execution_count: executionCount, + data, + metadata: {}, + }); + } catch (err) { + if (err instanceof Error) { + const stack = err.stack || ""; + await broadcast("error", { + ename: err.name, + evalue: err.message, + traceback: stack.split("\n"), + }); + } else if (typeof err == "string") { + await broadcast("error", { + ename: "Error", + evalue: err, + traceback: [], + }); + } else { + await broadcast("error", { + ename: "Error", + evalue: + "An error occurred while formatting a result, but it could not be identified", + traceback: [], + }); + } } + } - const data = await format(result); - await broadcast("execute_result", { - execution_count: executionCount, - data, + internals.jupyter.broadcastResult = broadcastResult; + + /** + * Display function for Jupyter Deno Kernel. + * Mimics the behavior of IPython's `display(obj, raw=True)` function to allow + * asynchronous displaying of objects in Jupyter. + * + * @param obj - The object to be displayed + * @param options - Display options + */ + async function display(obj, options = { raw: false, update: false }) { + const bundle = await formatInner(obj, options.raw); + let messageType = "display_data"; + if (options.update) { + messageType = "update_display_data"; + } + let transient = {}; + if (options.display_id) { + transient = { display_id: options.display_id }; + } + await broadcast(messageType, { + data: bundle, metadata: {}, + transient, }); - } catch (err) { - if (err instanceof Error) { - const stack = err.stack || ""; - await broadcast("error", { - ename: err.name, - evalue: err.message, - traceback: stack.split("\n"), - }); - } else if (typeof err == "string") { - await broadcast("error", { - ename: "Error", - evalue: err, - traceback: [], - }); - } else { - await broadcast("error", { - ename: "Error", - evalue: - "An error occurred while formatting a result, but it could not be identified", - traceback: [], - }); - } + return; } - } - internals.jupyter.broadcastResult = broadcastResult; - - /** - * Display function for Jupyter Deno Kernel. - * Mimics the behavior of IPython's `display(obj, raw=True)` function to allow - * asynchronous displaying of objects in Jupyter. - * - * @param obj - The object to be displayed - * @param options - Display options - */ - async function display(obj, options = { raw: false, update: false }) { - const bundle = await formatInner(obj, options.raw); - let messageType = "display_data"; - if (options.update) { - messageType = "update_display_data"; - } - let transient = {}; - if (options.display_id) { - transient = { display_id: options.display_id }; - } - await broadcast(messageType, { - data: bundle, - metadata: {}, - transient, - }); - return; + globalThis.Deno.jupyter = { + broadcast, + display, + format, + md, + html, + svg, + $display, + }; } - globalThis.Deno.jupyter = { - broadcast, - display, - format, - md, - html, - svg, - $display, - }; + internals.enableJupyter = enableJupyter; } - -internals.enableJupyter = enableJupyter; |