Skip to content

Commit

Permalink
Merge pull request #9258 from quarto-dev/bugfix/9255
Browse files Browse the repository at this point in the history
Jupyter - support cells where typeof cell.source === "string"
  • Loading branch information
cscheid authored Apr 2, 2024
2 parents f1441f7 + 21295ed commit b6907df
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 20 deletions.
1 change: 1 addition & 0 deletions news/changelog-1.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ All changes included in 1.5:
- ([#8919](https://github.com/quarto-dev/quarto-cli/issues/8919)): Ensure enough backticks in `quarto convert` from `.ipynb` to `.qmd` files.
- ([#8998](https://github.com/quarto-dev/quarto-cli/issues/8998)): Interpret slide separation markers `---` correctly when creating the `.ipynb` intermediate notebook from a `.qmd` file.
- ([#9133](https://github.com/quarto-dev/quarto-cli/issues/9133)): Fix issue with Jupyter engine when using paths containing special characters.
- ([#9255](https://github.com/quarto-dev/quarto-cli/issues/9255)): Support cell source fields of type `string`.

## Website Listings

Expand Down
18 changes: 15 additions & 3 deletions src/command/convert/jupyter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import { partitionCellOptions } from "../../core/lib/partition-cell-options.ts";
import { Metadata } from "../../config/types.ts";
import { jupyterKernelspec } from "../../core/jupyter/kernels.ts";
import { fixupFrontMatter } from "../../core/jupyter/jupyter-fixups.ts";
import {
jupyterCellSrcAsLines,
jupyterCellSrcAsStr,
} from "../../core/jupyter/jupyter-shared.ts";

export async function markdownToJupyterNotebook(
file: string,
Expand Down Expand Up @@ -67,7 +71,9 @@ export async function jupyterNotebookToMarkdown(
case "raw":
// see if this is the front matter
if (frontMatter === undefined) {
frontMatter = partitionYamlFrontMatter(cell.source.join(""))?.yaml;
frontMatter = partitionYamlFrontMatter(
jupyterCellSrcAsStr(cell),
)?.yaml;
if (!frontMatter) {
md.push(...mdFromRawCell(cellWithOptions));
}
Expand Down Expand Up @@ -145,8 +151,11 @@ async function mdFromCodeCell(
}

// determine the largest number of backticks in the cell

const maxBackticks = Math.max(
...cell.source.map((line) => line.match(/^`+/g)?.[0].length || 0),
...jupyterCellSrcAsLines(cell).map((line) =>
line.match(/^`+/g)?.[0].length || 0
),
2,
);
const backticks = "`".repeat(maxBackticks + 1);
Expand All @@ -155,7 +164,10 @@ async function mdFromCodeCell(
const md: string[] = [backticks + "{" + language + "}\n"];

// partition
const { yaml, source } = await partitionCellOptions(language, cell.source);
const { yaml, source } = await partitionCellOptions(
language,
jupyterCellSrcAsLines(cell),
);
const options = yaml ? yaml as JupyterCellOptions : {};

if (!includeIds) {
Expand Down
12 changes: 6 additions & 6 deletions src/core/jupyter/jupyter-filters.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/*
* jupyter-filters.ts
*
* Copyright (C) 2020-2022 Posit Software, PBC
*
*/
* jupyter-filters.ts
*
* Copyright (C) 2020-2022 Posit Software, PBC
*/

import { existsSync } from "fs/exists.ts";
import { basename, dirname, isAbsolute, join } from "../../deno_ral/path.ts";
Expand All @@ -19,6 +18,7 @@ import {
} from "./filtered-notebook-cache.ts";
import { fixupFrontMatter } from "./jupyter-fixups.ts";
import { JupyterNotebook } from "./types.ts";
import { jupyterCellSrcAsStr } from "./jupyter-shared.ts";

export async function markdownFromNotebookFile(file: string, format?: Format) {
// read file with any filters
Expand All @@ -36,7 +36,7 @@ export function markdownFromNotebookJSON(nb: JupyterNotebook) {

const markdown = nb.cells.reduce((md, cell) => {
if (["markdown", "raw"].includes(cell.cell_type)) {
return md + "\n" + cell.source.join("") + "\n";
return md + "\n" + jupyterCellSrcAsStr(cell) + "\n";
} else {
return md;
}
Expand Down
9 changes: 7 additions & 2 deletions src/core/jupyter/jupyter-fixups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import { lines } from "../lib/text.ts";
import { markdownWithExtractedHeading } from "../pandoc/pandoc-partition.ts";
import { partitionYamlFrontMatter, readYamlFromMarkdown } from "../yaml.ts";
import { JupyterNotebook, JupyterOutput } from "./types.ts";
import {
jupyterCellSrcAsLines,
jupyterCellSrcAsStr,
} from "./jupyter-shared.ts";

export function fixupStreams(nb: JupyterNotebook): JupyterNotebook {
for (const cell of nb.cells) {
Expand Down Expand Up @@ -155,7 +159,8 @@ export function fixupFrontMatter(nb: JupyterNotebook): JupyterNotebook {
let partitioned: { yaml: string; markdown: string } | undefined;
const frontMatterCellIndex = nb.cells.findIndex((cell) => {
if (cell.cell_type === "raw" || cell.cell_type === "markdown") {
partitioned = partitionYamlFrontMatter(cell.source.join("")) || undefined;
partitioned = partitionYamlFrontMatter(jupyterCellSrcAsStr(cell)) ||
undefined;
if (partitioned) {
cell.cell_type = "raw";
return true;
Expand All @@ -179,7 +184,7 @@ export function fixupFrontMatter(nb: JupyterNotebook): JupyterNotebook {
if (cell.cell_type === "markdown") {
const { lines, headingText, contentBeforeHeading } =
markdownWithExtractedHeading(
nbLines(cell.source).join(""),
nbLines(jupyterCellSrcAsLines(cell)).join(""),
);
if (headingText && !contentBeforeHeading) {
title = headingText;
Expand Down
15 changes: 14 additions & 1 deletion src/core/jupyter/jupyter-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,21 @@ import { pathWithForwardSlashes } from "../path.ts";

import { pythonExecForCaps } from "./exec.ts";
import { jupyterKernelspecs } from "./kernels.ts";
import { JupyterCapabilities, JupyterKernelspec } from "./types.ts";
import {
JupyterCapabilities,
JupyterCell,
JupyterKernelspec,
} from "./types.ts";
import { isEnvDir } from "./capabilities.ts";
import { lines } from "../lib/text.ts";

export const jupyterCellSrcAsStr = (cell: JupyterCell) => {
return typeof cell.source === "string" ? cell.source : cell.source.join("");
};

export const jupyterCellSrcAsLines = (cell: JupyterCell) => {
return typeof cell.source === "string" ? lines(cell.source) : cell.source;
};

export async function jupyterCapabilitiesMessage(
caps: JupyterCapabilities,
Expand Down
26 changes: 19 additions & 7 deletions src/core/jupyter/jupyter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ import {
resolveUserExpressions,
userExpressionsFromCell,
} from "./jupyter-inline.ts";
import {
jupyterCellSrcAsLines,
jupyterCellSrcAsStr,
} from "./jupyter-shared.ts";

export const kQuartoMimeType = "quarto_mimetype";
export const kQuartoOutputOrder = "quarto_order";
Expand Down Expand Up @@ -349,7 +353,7 @@ export async function quartoMdToJupyter(
}
if (cell_type === "raw" && frontMatter) {
// delete 'jupyter' metadata since we've already transferred it
const yaml = readYamlFromMarkdown(cell.source.join(""));
const yaml = readYamlFromMarkdown(jupyterCellSrcAsStr(cell));
if (yaml.jupyter) {
delete yaml.jupyter;
// write the cell only if there is metadata to write
Expand All @@ -371,9 +375,12 @@ export async function quartoMdToJupyter(
}
} else if (cell_type === "code") {
// see if there is embedded metadata we should forward into the cell metadata
const cellSrcLines = typeof cell.source === "string"
? lines(cell.source)
: cell.source;
const { yaml, source } = partitionCellOptions(
kernelspec.language.toLowerCase(),
cell.source,
cellSrcLines,
);
if (yaml && !Array.isArray(yaml) && typeof yaml === "object") {
// use label as id if necessary
Expand Down Expand Up @@ -412,7 +419,10 @@ export async function quartoMdToJupyter(
}

// if the source is empty then don't add it
cell.source = trimEmptyLines(cell.source);
const cellSrcLines = typeof cell.source === "string"
? lines(cell.source)
: cell.source;
cell.source = trimEmptyLines(cellSrcLines);
if (cell.source.length > 0) {
nb.cells.push(cell);
}
Expand Down Expand Up @@ -777,7 +787,9 @@ export async function jupyterToMarkdown(
// If this is the front matter cell, don't wrap it in
// a cell envelope, as it need to be remain discoverable
if (frontMatter === undefined) {
frontMatter = partitionYamlFrontMatter(cell.source.join(""))?.yaml;
frontMatter = partitionYamlFrontMatter(
jupyterCellSrcAsStr(cell),
)?.yaml;
if (frontMatter) {
markdownOptions.preserveCellMetadata = false;
}
Expand Down Expand Up @@ -858,7 +870,7 @@ export function jupyterCellWithOptions(
): JupyterCellWithOptions {
const { yaml, optionsSource, source } = partitionCellOptions(
language,
cell.source,
jupyterCellSrcAsLines(cell),
);

// read any options defined in cell metadata
Expand Down Expand Up @@ -1026,7 +1038,7 @@ export function mdFromRawCell(

const mimeType = cell.metadata?.[kCellRawMimeType];
if (mimeType) {
const rawOutput = mdRawOutput(mimeType, cell.source);
const rawOutput = mdRawOutput(mimeType, jupyterCellSrcAsLines(cell));
if (rawOutput) {
return rawCellEnvelope(cell.id, rawOutput);
}
Expand Down Expand Up @@ -1402,7 +1414,7 @@ async function mdFromCodeCell(
if (includeCode(cell, options) || options.preserveCodeCellYaml) {
const fenced = echoFenced(cell, options);
const ticks = "`".repeat(
Math.max(countTicks(cell.source) + 1, fenced ? 4 : 3),
Math.max(countTicks(jupyterCellSrcAsLines(cell)) + 1, fenced ? 4 : 3),
);

md.push(ticks + " {");
Expand Down
2 changes: 1 addition & 1 deletion src/core/jupyter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export interface JupyterCell {
cell_type: "markdown" | "code" | "raw";
execution_count?: null | number;
metadata: JupyterCellMetadata;
source: string[];
source: string | string[];
attachments?: Record<string, Record<string, string>>;
outputs?: JupyterOutput[];
}
Expand Down
1 change: 1 addition & 0 deletions tests/docs/smoke-all/2024/04/02/9255.ipynb

Large diffs are not rendered by default.

0 comments on commit b6907df

Please sign in to comment.