Skip to content

Commit 4eec00f

Browse files
Forward envvars from JSON payload (#306)
* Forward envvars from JSON payload * Refresh Quarto-provided env vars
1 parent 094d57b commit 4eec00f

File tree

2 files changed

+52
-11
lines changed

2 files changed

+52
-11
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
### Added
11+
12+
- Support new requirement from `quarto` to population environment variables
13+
explicitly in the notebook process [#306].
14+
1015
## [v0.17.3] - 2025-05-19
1116

1217
### Fixed
@@ -460,3 +465,4 @@ caching is enabled. Delete this folder to clear the cache. [#259]
460465
[#303]: https://github.com/PumasAI/QuartoNotebookRunner.jl/issues/303
461466
[#304]: https://github.com/PumasAI/QuartoNotebookRunner.jl/issues/304
462467
[#305]: https://github.com/PumasAI/QuartoNotebookRunner.jl/issues/305
468+
[#306]: https://github.com/PumasAI/QuartoNotebookRunner.jl/issues/306

src/server.jl

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,18 @@ mutable struct File
2424
options = _parsed_options(options)
2525
_, _, file_frontmatter = raw_text_chunks(path)
2626
merged_options = _extract_relevant_options(file_frontmatter, options)
27-
exeflags, env = _exeflags_and_env(merged_options)
27+
exeflags, env, quarto_env = _exeflags_and_env(merged_options)
2828
timeout = _extract_timeout(merged_options)
2929

30-
3130
exe, _exeflags = _julia_exe(exeflags)
32-
worker =
33-
cd(() -> Malt.Worker(; exe, exeflags = _exeflags, env), dirname(path))
31+
worker = cd(
32+
() -> Malt.Worker(;
33+
exe,
34+
exeflags = _exeflags,
35+
env = vcat(env, quarto_env),
36+
),
37+
dirname(path),
38+
)
3439
file = new(
3540
worker,
3641
path,
@@ -104,7 +109,7 @@ function _julia_exe(exeflags)
104109
end
105110

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

157+
# Several QUARTO_* environment variables are passed to the worker process
158+
# via the `env` field rather than via real environment variables. Capture
159+
# these and pass them to the worker process separate from `env` since that
160+
# is used by the worker status printout and we don't want these extra ones
161+
# that the user has not set themselves to show up there.
162+
quarto_env = Base.byteenv(options["env"])
163+
152164
# Ensure that coverage settings are passed to the worker so that worker
153165
# code is tracked correctly during tests.
154166
# Based on https://github.com/JuliaLang/julia/blob/eed18bdf706b7aab15b12f3ba0588e8fafcd4930/base/util.jl#L216-L229.
@@ -171,7 +183,7 @@ function _exeflags_and_env(options)
171183
end
172184
end
173185

174-
return exeflags, env
186+
return exeflags, env, quarto_env
175187
end
176188

177189
struct Server
@@ -205,22 +217,40 @@ function init!(file::File, options::Dict)
205217
end
206218

207219
function refresh!(file::File, options::Dict)
208-
exeflags, env = _exeflags_and_env(options)
220+
exeflags, env, quarto_env = _exeflags_and_env(options)
209221
if exeflags != file.exeflags || env != file.env || !Malt.isrunning(file.worker) # the worker might have been killed on another task
210222
Malt.stop(file.worker)
211223
exe, _exeflags = _julia_exe(exeflags)
212-
file.worker =
213-
cd(() -> Malt.Worker(; exe, exeflags = _exeflags, env), dirname(file.path))
224+
file.worker = cd(
225+
() -> Malt.Worker(; exe, exeflags = _exeflags, env = vcat(env, quarto_env)),
226+
dirname(file.path),
227+
)
214228
file.exe = exe
215229
file.exeflags = exeflags
216230
file.env = env
217231
file.source_code_hash = hash(VERSION)
218232
file.output_chunks = []
219233
init!(file, options)
220234
end
235+
refresh_quarto_env_vars!(file, quarto_env)
221236
remote_eval_fetch_channeled(file.worker, :(refresh!($(options)); revise_hook()))
222237
end
223238

239+
# Environment variables provided by Quarto may change between `quarto render`
240+
# calls. To update them correctly in the worker process, we need to refresh
241+
# them before each run.
242+
function refresh_quarto_env_vars!(file::File, quarto_env)
243+
if !isempty(quarto_env)
244+
remote_eval_fetch_channeled(file.worker, quote
245+
for each in $quarto_env
246+
k, v = Base.splitenv(each)
247+
ENV[k] = v
248+
end
249+
end)
250+
end
251+
return nothing
252+
end
253+
224254
function _cache_file(f::File, source_code_hash)
225255
path = joinpath(dirname(f.path), ".cache")
226256
hs = string(hash(f.worker.manifest_file, source_code_hash); base = 62)
@@ -441,9 +471,11 @@ function _extract_relevant_options(file_frontmatter::Dict, options::Dict)
441471
daemon = daemon_default,
442472
params = params_default,
443473
cache = cache_default,
474+
env = Dict{String,Any}(),
444475
)
445476
else
446477
format = get(D, options, "format")
478+
env = get(D, options, "env")
447479
execute = get(D, format, "execute")
448480
fig_width = get(execute, "fig-width", fig_width_default)
449481
fig_height = get(execute, "fig-height", fig_height_default)
@@ -481,6 +513,7 @@ function _extract_relevant_options(file_frontmatter::Dict, options::Dict)
481513
daemon,
482514
params = params_merged,
483515
cache,
516+
env,
484517
)
485518
end
486519
end
@@ -497,6 +530,7 @@ function _options_template(;
497530
daemon,
498531
params,
499532
cache,
533+
env,
500534
)
501535
D = Dict{String,Any}
502536
return D(
@@ -515,6 +549,7 @@ function _options_template(;
515549
"metadata" => D("julia" => julia),
516550
),
517551
"params" => D(params),
552+
"env" => env,
518553
)
519554
end
520555

@@ -1466,7 +1501,7 @@ function run!(
14661501
chunk_callback = (i, n, c) -> nothing,
14671502
)
14681503
try
1469-
borrow_file!(server, path; optionally_create = true) do file
1504+
borrow_file!(server, path; options, optionally_create = true) do file
14701505
if file.timeout_timer !== nothing
14711506
close(file.timeout_timer)
14721507
file.timeout_timer = nothing
@@ -1606,7 +1641,7 @@ function borrow_file!(
16061641
get(server.workers, apath, nothing)
16071642
end
16081643
if file !== current_file
1609-
return borrow_file!(f, server, apath; optionally_create)
1644+
return borrow_file!(f, server, apath; options, optionally_create)
16101645
else
16111646
return f(file)
16121647
end

0 commit comments

Comments
 (0)