Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added

- Support new requirement from `quarto` to population environment variables
explicitly in the notebook process [#306].

## [v0.17.3] - 2025-05-19

### Fixed
Expand Down Expand Up @@ -460,3 +465,4 @@ caching is enabled. Delete this folder to clear the cache. [#259]
[#303]: https://github.com/PumasAI/QuartoNotebookRunner.jl/issues/303
[#304]: https://github.com/PumasAI/QuartoNotebookRunner.jl/issues/304
[#305]: https://github.com/PumasAI/QuartoNotebookRunner.jl/issues/305
[#306]: https://github.com/PumasAI/QuartoNotebookRunner.jl/issues/306
57 changes: 46 additions & 11 deletions src/server.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ mutable struct File
options = _parsed_options(options)
_, _, file_frontmatter = raw_text_chunks(path)
merged_options = _extract_relevant_options(file_frontmatter, options)
exeflags, env = _exeflags_and_env(merged_options)
exeflags, env, quarto_env = _exeflags_and_env(merged_options)
timeout = _extract_timeout(merged_options)


exe, _exeflags = _julia_exe(exeflags)
worker =
cd(() -> Malt.Worker(; exe, exeflags = _exeflags, env), dirname(path))
worker = cd(
() -> Malt.Worker(;
exe,
exeflags = _exeflags,
env = vcat(env, quarto_env),
),
dirname(path),
)
file = new(
worker,
path,
Expand Down Expand Up @@ -104,7 +109,7 @@ function _julia_exe(exeflags)
end

function _extract_timeout(merged_options)
daemon = merged_options["format"]["execute"]["daemon"]
daemon = something(merged_options["format"]["execute"]["daemon"], true)
if daemon === true
300.0 # match quarto's default timeout of 300 seconds
elseif daemon === false
Expand Down Expand Up @@ -149,6 +154,13 @@ function _exeflags_and_env(options)
# if exeflags already contains '--color=no', the 'no' will prevail
pushfirst!(exeflags, "--color=yes")

# Several QUARTO_* environment variables are passed to the worker process
# via the `env` field rather than via real environment variables. Capture
# these and pass them to the worker process separate from `env` since that
# is used by the worker status printout and we don't want these extra ones
# that the user has not set themselves to show up there.
quarto_env = Base.byteenv(options["env"])

# Ensure that coverage settings are passed to the worker so that worker
# code is tracked correctly during tests.
# Based on https://github.com/JuliaLang/julia/blob/eed18bdf706b7aab15b12f3ba0588e8fafcd4930/base/util.jl#L216-L229.
Expand All @@ -171,7 +183,7 @@ function _exeflags_and_env(options)
end
end

return exeflags, env
return exeflags, env, quarto_env
end

struct Server
Expand Down Expand Up @@ -205,22 +217,40 @@ function init!(file::File, options::Dict)
end

function refresh!(file::File, options::Dict)
exeflags, env = _exeflags_and_env(options)
exeflags, env, quarto_env = _exeflags_and_env(options)
if exeflags != file.exeflags || env != file.env || !Malt.isrunning(file.worker) # the worker might have been killed on another task
Malt.stop(file.worker)
exe, _exeflags = _julia_exe(exeflags)
file.worker =
cd(() -> Malt.Worker(; exe, exeflags = _exeflags, env), dirname(file.path))
file.worker = cd(
() -> Malt.Worker(; exe, exeflags = _exeflags, env = vcat(env, quarto_env)),
dirname(file.path),
)
file.exe = exe
file.exeflags = exeflags
file.env = env
file.source_code_hash = hash(VERSION)
file.output_chunks = []
init!(file, options)
end
refresh_quarto_env_vars!(file, quarto_env)
remote_eval_fetch_channeled(file.worker, :(refresh!($(options)); revise_hook()))
end

# Environment variables provided by Quarto may change between `quarto render`
# calls. To update them correctly in the worker process, we need to refresh
# them before each run.
function refresh_quarto_env_vars!(file::File, quarto_env)
if !isempty(quarto_env)
remote_eval_fetch_channeled(file.worker, quote
for each in $quarto_env
k, v = Base.splitenv(each)
ENV[k] = v
end
end)
end
return nothing
end

function _cache_file(f::File, source_code_hash)
path = joinpath(dirname(f.path), ".cache")
hs = string(hash(f.worker.manifest_file, source_code_hash); base = 62)
Expand Down Expand Up @@ -441,9 +471,11 @@ function _extract_relevant_options(file_frontmatter::Dict, options::Dict)
daemon = daemon_default,
params = params_default,
cache = cache_default,
env = Dict{String,Any}(),
)
else
format = get(D, options, "format")
env = get(D, options, "env")
execute = get(D, format, "execute")
fig_width = get(execute, "fig-width", fig_width_default)
fig_height = get(execute, "fig-height", fig_height_default)
Expand Down Expand Up @@ -481,6 +513,7 @@ function _extract_relevant_options(file_frontmatter::Dict, options::Dict)
daemon,
params = params_merged,
cache,
env,
)
end
end
Expand All @@ -497,6 +530,7 @@ function _options_template(;
daemon,
params,
cache,
env,
)
D = Dict{String,Any}
return D(
Expand All @@ -515,6 +549,7 @@ function _options_template(;
"metadata" => D("julia" => julia),
),
"params" => D(params),
"env" => env,
)
end

Expand Down Expand Up @@ -1466,7 +1501,7 @@ function run!(
chunk_callback = (i, n, c) -> nothing,
)
try
borrow_file!(server, path; optionally_create = true) do file
borrow_file!(server, path; options, optionally_create = true) do file
if file.timeout_timer !== nothing
close(file.timeout_timer)
file.timeout_timer = nothing
Expand Down Expand Up @@ -1606,7 +1641,7 @@ function borrow_file!(
get(server.workers, apath, nothing)
end
if file !== current_file
return borrow_file!(f, server, apath; optionally_create)
return borrow_file!(f, server, apath; options, optionally_create)
else
return f(file)
end
Expand Down
Loading