Skip to content

Commit df049d4

Browse files
committed
Fix regression with keep-ipynb (#12780)
backport of #12793 - update fileInformationCache based on `keep-ipynb` information - requires to always pass ProjectContext in ExecuteOptions - requires to ensure fileInformationCache is not cloned by safeCloneDeep This allows us to always get the global fileInformationCache from the project context to get / set information in it from different contexts. - Add tests for project context and single file context
1 parent b0863ff commit df049d4

File tree

13 files changed

+89
-19
lines changed

13 files changed

+89
-19
lines changed

news/changelog-1.7.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## In this release
44

5+
- ([#12780](https://github.com/quarto-dev/quarto-cli/issues/12780)): `keep-ipynb: true` now works again correctly and intermediate `.quarto_ipynb` is not removed.
6+
57
## In previous releases
68

79
- ([#6607](https://github.com/quarto-dev/quarto-cli/issues/6607)): Add missing beamer template update for beamer theme options: `colorthemeoptions`, `fontthemeoptions`, `innerthemeoptions` and `outerthemeoptions`.

src/command/render/render-contexts.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ import {
7171
kProjectType,
7272
ProjectContext,
7373
} from "../../project/types.ts";
74-
import { isHtmlDashboardOutput, isHtmlOutput } from "../../config/format.ts";
75-
import { formatHasBootstrap } from "../../format/html/format-html-info.ts";
7674
import { warnOnce } from "../../core/log.ts";
7775
import { dirAndStem } from "../../core/path.ts";
7876
import { fileExecutionEngineAndTarget } from "../../execute/engine.ts";
@@ -88,6 +86,8 @@ import {
8886
} from "../../core/pandoc/pandoc-formats.ts";
8987
import { ExtensionContext } from "../../extension/types.ts";
9088
import { NotebookContext } from "../../render/notebook/notebook-types.ts";
89+
import { isHtmlDashboardOutput, isHtmlOutput } from "../../config/format.ts";
90+
import { formatHasBootstrap } from "../../format/html/format-html-info.ts";
9191

9292
export async function resolveFormatsFromMetadata(
9393
metadata: Metadata,
@@ -297,7 +297,7 @@ export async function renderContexts(
297297

298298
// if this isn't for execute then cleanup context
299299
if (!forExecute && engine.executeTargetSkipped) {
300-
engine.executeTargetSkipped(target, formats[formatKey].format);
300+
engine.executeTargetSkipped(target, formats[formatKey].format, project);
301301
}
302302
}
303303
return contexts;

src/command/render/render-files.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,11 @@ export async function renderExecute(
199199

200200
// notify engine that we skipped execute
201201
if (context.engine.executeTargetSkipped) {
202-
context.engine.executeTargetSkipped(context.target, context.format);
202+
context.engine.executeTargetSkipped(
203+
context.target,
204+
context.format,
205+
context.project,
206+
);
203207
}
204208

205209
// return results

src/core/jupyter/jupyter-embed.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ import {
4343
JupyterCellOutput,
4444
} from "../jupyter/types.ts";
4545

46-
import { dirname, extname, join, basename, isAbsolute } from "../../deno_ral/path.ts";
46+
import {
47+
basename,
48+
dirname,
49+
extname,
50+
isAbsolute,
51+
join,
52+
} from "../../deno_ral/path.ts";
4753
import { languages } from "../handlers/base.ts";
4854
import {
4955
extractJupyterWidgetDependencies,
@@ -596,6 +602,7 @@ async function getCachedNotebookInfo(
596602
quiet: flags.quiet,
597603
previewServer: context.options.previewServer,
598604
handledLanguages: languages(),
605+
project: context.project,
599606
};
600607

601608
const [dir, stem] = dirAndStem(nbAddress.path);

src/execute/jupyter/jupyter.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { readYamlFromMarkdown } from "../../core/yaml.ts";
1717
import { isInteractiveSession } from "../../core/platform.ts";
1818
import { partitionMarkdown } from "../../core/pandoc/pandoc-partition.ts";
1919

20-
import { dirAndStem, normalizePath, removeIfExists } from "../../core/path.ts";
20+
import { dirAndStem, normalizePath } from "../../core/path.ts";
2121
import { runningInCI } from "../../core/ci-info.ts";
2222

2323
import {
@@ -109,7 +109,10 @@ import {
109109
import { jupyterCapabilities } from "../../core/jupyter/capabilities.ts";
110110
import { runExternalPreviewServer } from "../../preview/preview-server.ts";
111111
import { onCleanup } from "../../core/cleanup.ts";
112-
import { projectOutputDir } from "../../project/project-shared.ts";
112+
import {
113+
ensureFileInformationCache,
114+
projectOutputDir,
115+
} from "../../project/project-shared.ts";
113116
import { assert } from "testing/asserts";
114117

115118
export const jupyterEngine: ExecutionEngine = {
@@ -436,7 +439,7 @@ export const jupyterEngine: ExecutionEngine = {
436439

437440
// if it's a transient notebook then remove it
438441
// (unless keep-ipynb was specified)
439-
cleanupNotebook(options.target, options.format);
442+
cleanupNotebook(options.target, options.format, options.project);
440443

441444
// Create markdown from the result
442445
const outputs = result.cellOutputs.map((output) => output.markdown);
@@ -713,12 +716,17 @@ async function disableDaemonForNotebook(target: ExecutionTarget) {
713716
return false;
714717
}
715718

716-
function cleanupNotebook(target: ExecutionTarget, format: Format) {
717-
// remove transient notebook if appropriate
719+
function cleanupNotebook(
720+
target: ExecutionTarget,
721+
format: Format,
722+
project: ProjectContext,
723+
) {
724+
// Make notebook non-transient when keep-ipynb is set
718725
const data = target.data as JupyterTargetData;
719-
if (data.transient) {
720-
if (!format.execute[kKeepIpynb]) {
721-
removeIfExists(target.input);
726+
const cached = ensureFileInformationCache(project, target.source);
727+
if (data.transient && format.execute[kKeepIpynb]) {
728+
if (cached.target && cached.target.data) {
729+
(cached.target.data as JupyterTargetData).transient = false;
722730
}
723731
}
724732
}

src/execute/types.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ export interface ExecutionEngine {
4949
format: Format,
5050
) => Format;
5151
execute: (options: ExecuteOptions) => Promise<ExecuteResult>;
52-
executeTargetSkipped?: (target: ExecutionTarget, format: Format) => void;
52+
executeTargetSkipped?: (
53+
target: ExecutionTarget,
54+
format: Format,
55+
project: ProjectContext,
56+
) => void;
5357
dependencies: (options: DependenciesOptions) => Promise<DependenciesResult>;
5458
postprocess: (options: PostProcessOptions) => Promise<void>;
5559
canFreeze: boolean;
@@ -89,7 +93,7 @@ export interface ExecuteOptions {
8993
quiet?: boolean;
9094
previewServer?: boolean;
9195
handledLanguages: string[]; // list of languages handled by cell language handlers, after the execution engine
92-
project?: ProjectContext;
96+
project: ProjectContext;
9397
}
9498

9599
// result of execution

src/project/project-context.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import { projectResourceFiles } from "./project-resources.ts";
7070

7171
import {
7272
cleanupFileInformationCache,
73+
FileInformationCacheMap,
7374
ignoreFieldsForProjectType,
7475
normalizeFormatYaml,
7576
projectConfigFile,
@@ -272,7 +273,7 @@ export async function projectContext(
272273
dir: join(dir, ".quarto"),
273274
prefix: "quarto-session-temp",
274275
});
275-
const fileInformationCache = new Map();
276+
const fileInformationCache = new FileInformationCacheMap();
276277
const result: ProjectContext = {
277278
resolveBrand: async (fileName?: string) =>
278279
projectResolveBrand(result, fileName),
@@ -368,7 +369,7 @@ export async function projectContext(
368369
dir: join(dir, ".quarto"),
369370
prefix: "quarto-session-temp",
370371
});
371-
const fileInformationCache = new Map();
372+
const fileInformationCache = new FileInformationCacheMap();
372373
const result: ProjectContext = {
373374
resolveBrand: async (fileName?: string) =>
374375
projectResolveBrand(result, fileName),
@@ -443,7 +444,7 @@ export async function projectContext(
443444
dir: join(originalDir, ".quarto"),
444445
prefix: "quarto-session-temp",
445446
});
446-
const fileInformationCache = new Map();
447+
const fileInformationCache = new FileInformationCacheMap();
447448
const context: ProjectContext = {
448449
resolveBrand: async (fileName?: string) =>
449450
projectResolveBrand(context, fileName),

src/project/project-shared.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import {
5353
} from "../resources/types/schema-types.ts";
5454
import { Brand } from "../core/brand/brand.ts";
5555
import { assert } from "testing/asserts";
56+
import { Cloneable } from "../core/safe-clone-deep.ts";
5657

5758
export function projectExcludeDirs(context: ProjectContext): string[] {
5859
const outputDir = projectOutputDir(context);
@@ -633,6 +634,15 @@ export async function projectResolveBrand(
633634
}
634635
}
635636

637+
// Create a class that extends Map and implements Cloneable
638+
export class FileInformationCacheMap extends Map<string, FileInformation>
639+
implements Cloneable<Map<string, FileInformation>> {
640+
clone(): Map<string, FileInformation> {
641+
// Return the same instance (reference) instead of creating a clone
642+
return this;
643+
}
644+
}
645+
636646
export function cleanupFileInformationCache(project: ProjectContext) {
637647
project.fileInformationCache.forEach((entry) => {
638648
if (entry?.target?.data) {

src/project/types/single-file/single-file.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { MappedString } from "../../../core/mapped-text.ts";
2121
import { fileExecutionEngineAndTarget } from "../../../execute/engine.ts";
2222
import {
2323
cleanupFileInformationCache,
24+
FileInformationCacheMap,
2425
projectFileMetadata,
2526
projectResolveBrand,
2627
projectResolveFullMarkdownForFile,
@@ -49,7 +50,7 @@ export async function singleFileProjectContext(
4950
notebookContext,
5051
environment: () => environmentMemoizer(result),
5152
renderFormats,
52-
fileInformationCache: new Map(),
53+
fileInformationCache: new FileInformationCacheMap(),
5354
fileExecutionEngineAndTarget: (
5455
file: string,
5556
) => {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/.quarto/

0 commit comments

Comments
 (0)