Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix global eval: false #174

Merged
merged 3 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions src/server.jl
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ function _extract_relevant_options(file_frontmatter::Dict, options::Dict)
fig_format_default = get(file_frontmatter, "fig-format", nothing)
fig_dpi_default = get(file_frontmatter, "fig-dpi", nothing)
error_default = get(get(D, file_frontmatter, "execute"), "error", true)
eval_default = get(get(D, file_frontmatter, "execute"), "eval", true)
daemon_default = get(get(D, file_frontmatter, "execute"), "daemon", true)

pandoc_to_default = nothing
Expand All @@ -185,6 +186,7 @@ function _extract_relevant_options(file_frontmatter::Dict, options::Dict)
fig_format = fig_format_default,
fig_dpi = fig_dpi_default,
error = error_default,
eval = eval_default,
pandoc_to = pandoc_to_default,
julia = julia_default,
daemon = daemon_default,
Expand All @@ -198,6 +200,7 @@ function _extract_relevant_options(file_frontmatter::Dict, options::Dict)
fig_format = get(execute, "fig-format", fig_format_default)
fig_dpi = get(execute, "fig-dpi", fig_dpi_default)
error = get(execute, "error", error_default)
eval = get(execute, "eval", eval_default)
daemon = get(execute, "daemon", daemon_default)

pandoc = get(D, format, "pandoc")
Expand All @@ -221,6 +224,7 @@ function _extract_relevant_options(file_frontmatter::Dict, options::Dict)
fig_format,
fig_dpi,
error,
eval,
pandoc_to,
julia = julia_merged,
daemon,
Expand All @@ -235,6 +239,7 @@ function _options_template(;
fig_format,
fig_dpi,
error,
eval,
pandoc_to,
julia,
daemon,
Expand All @@ -249,6 +254,7 @@ function _options_template(;
"fig-format" => fig_format,
"fig-dpi" => fig_dpi,
"error" => error,
"eval" => eval,
"daemon" => daemon,
),
"pandoc" => D("to" => pandoc_to),
Expand Down Expand Up @@ -310,6 +316,8 @@ raw_markdown_chunks(file::File) =
raw_markdown_chunks(path::String) =
raw_markdown_chunks_from_string(path, read(path, String))

struct Unset end

function raw_markdown_chunks_from_string(path::String, markdown::String)
raw_chunks = []
pars = Parser()
Expand All @@ -333,8 +341,8 @@ function raw_markdown_chunks_from_string(path::String, markdown::String)
# all other options seem to be quarto-rendering related, like where to put figure captions etc.
source = node.literal
cell_options = extract_cell_options(source; file = path, line = line)
evaluate = get(cell_options, "eval", true)
if !(evaluate isa Bool)
evaluate = get(cell_options, "eval", Unset())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the logic really need an Unset rather than just inheriting the global setting as the cell default? Or is there some more subtle logic going on here that I'm not seeing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could have tried to route the options through the functions that extract the cell options but that would have been a larger change because those are currently independent. And I didn't take the usual nothing because that's a value that you can also set in YAML, and I wanted to error for invalid values instead I think, so I chose to make a new value that's outside of the YAML set.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that would have been a larger change because those are currently independent

How much larger? Larger changes aren't always the wrong thing to do. If logically these options aren't independent then adjusting the code so that there is dependence between them makes sense to me.

Copy link
Collaborator Author

@jkrumbiegel jkrumbiegel Sep 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cell parsing can be independent from global settings. If you do the extra work to route the global options into parsing that saves you only from having to use this one placeholder value for "nothing was specified here". A cell with eval: true should be evaluated in a eval: false global regime, but the default is also implicit eval: true. So one needs to disambiguate implicit and explicit true. I do that with this placeholder. We could also decide that the implicit true is actually eval: null or whatever that is in YAML (eval = nothing on the Julia side). Right now, I think it would error if the user wrote that. But we could make it so that just means whatever the global default is. Then we don't need Unset. That would be fine with me as well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just stick with what's here then for now.

if !(evaluate isa Union{Bool,Unset})
error(
"Cannot handle an `eval` code cell option with value $(repr(evaluate)), only true or false.",
)
Expand Down Expand Up @@ -446,8 +454,8 @@ function raw_script_chunks(path::String)
if type == :code

cell_options = extract_cell_options(source; file = path, line = start_line)
evaluate = get(cell_options, "eval", true)
if !(evaluate isa Bool)
evaluate = get(cell_options, "eval", Unset())
if !(evaluate isa Union{Bool,Unset})
error(
"Cannot handle an `eval` code cell option with value $(repr(evaluate)), only true or false.",
)
Expand Down Expand Up @@ -570,6 +578,10 @@ function Base.showerror(io::IO, e::EvaluationError)
end
end

should_eval(chunk, global_eval::Bool) =
chunk.type === :code &&
(chunk.evaluate === true || (chunk.evaluate === Unset() && global_eval))

"""
evaluate_raw_cells!(f::File, chunks::Vector)

Expand All @@ -593,6 +605,7 @@ function evaluate_raw_cells!(

error_metadata = NamedTuple{(:kind, :file, :traceback),Tuple{Symbol,String,String}}[]
allow_error_global = options["format"]["execute"]["error"]
global_eval::Bool = options["format"]["execute"]["eval"]

wd = try
pwd()
Expand All @@ -601,12 +614,12 @@ function evaluate_raw_cells!(
end
header = "Running $(relpath(f.path, wd))"

chunks_to_evaluate = sum(c -> c.type === :code && c.evaluate, chunks)
chunks_to_evaluate = sum(c -> should_eval(c, global_eval), chunks)
ith_chunk_to_evaluate = 1

@maybe_progress showprogress "$header" for (nth, chunk) in enumerate(chunks)
if chunk.type === :code
if !chunk.evaluate
if !should_eval(chunk, global_eval)
# Cells that are not evaluated are not executed, but they are
# still included in the notebook.
push!(
Expand Down
21 changes: 21 additions & 0 deletions test/examples/evalfalse.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: Cell Options
engine: julia
execute:
eval: false
---

```{julia}
println("shouldn't run")
```

```{julia}
#| eval: false
println("shouldn't run either")
```

```{julia}
#| eval: true
println("should run")
```

10 changes: 10 additions & 0 deletions test/testsets/evalfalse.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
include("../utilities/prelude.jl")

test_example(joinpath(@__DIR__, "../examples/evalfalse.qmd")) do json
cells = json["cells"]
@test length(cells) == 7
@test isempty(cells[2]["outputs"])
@test isempty(cells[4]["outputs"])
@test !isempty(cells[6]["outputs"])
@test occursin("should run", cells[6]["outputs"][1]["text"])
end
Loading