|
|
|
@ -405,6 +405,12 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type) |
|
|
|
inputfile = realpath(abspath(inputfile)) |
|
|
|
inputfile = realpath(abspath(inputfile)) |
|
|
|
mkpath(outputdir) |
|
|
|
mkpath(outputdir) |
|
|
|
outputdir = realpath(abspath(outputdir)) |
|
|
|
outputdir = realpath(abspath(outputdir)) |
|
|
|
|
|
|
|
isdir(outputdir) || error("not a directory: $(outputdir)") |
|
|
|
|
|
|
|
ext = type === (:nb) ? ".ipynb" : ".$(type)" |
|
|
|
|
|
|
|
outputfile = joinpath(outputdir, config["name"]::String * ext) |
|
|
|
|
|
|
|
if inputfile == outputfile |
|
|
|
|
|
|
|
throw(ArgumentError("outputfile (`$outputfile`) is identical to inputfile (`$inputfile`)")) |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
output_thing = type === (:md) ? "markdown page" : |
|
|
|
output_thing = type === (:md) ? "markdown page" : |
|
|
|
type === (:nb) ? "notebook" : |
|
|
|
type === (:nb) ? "notebook" : |
|
|
|
@ -414,7 +420,8 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type) |
|
|
|
# Add some information for passing around Literate methods |
|
|
|
# Add some information for passing around Literate methods |
|
|
|
config["literate_inputfile"] = inputfile |
|
|
|
config["literate_inputfile"] = inputfile |
|
|
|
config["literate_outputdir"] = outputdir |
|
|
|
config["literate_outputdir"] = outputdir |
|
|
|
config["literate_ext"] = type === (:nb) ? ".ipynb" : ".$(type)" |
|
|
|
config["literate_ext"] = ext |
|
|
|
|
|
|
|
config["literate_outputfile"] = outputfile |
|
|
|
|
|
|
|
|
|
|
|
# read content |
|
|
|
# read content |
|
|
|
content = read(inputfile, String) |
|
|
|
content = read(inputfile, String) |
|
|
|
@ -445,13 +452,7 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
function write_result(content, config; print=print) |
|
|
|
function write_result(content, config; print=print) |
|
|
|
inputfile = config["literate_inputfile"] |
|
|
|
outputfile = config["literate_outputfile"] |
|
|
|
outputdir = config["literate_outputdir"] |
|
|
|
|
|
|
|
isdir(outputdir) || error("not a directory: $(outputdir)") |
|
|
|
|
|
|
|
outputfile = joinpath(outputdir, config["name"]::String * config["literate_ext"]) |
|
|
|
|
|
|
|
if inputfile == outputfile |
|
|
|
|
|
|
|
throw(ArgumentError("outputfile (`$outputfile`) is identical to inputfile (`$inputfile`)")) |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
@info "writing result to `$(Base.contractuser(outputfile))`" |
|
|
|
@info "writing result to `$(Base.contractuser(outputfile))`" |
|
|
|
open(outputfile, "w") do io |
|
|
|
open(outputfile, "w") do io |
|
|
|
print(io, content) |
|
|
|
print(io, content) |
|
|
|
@ -543,10 +544,13 @@ function markdown(inputfile, outputdir=pwd(); config::AbstractDict=Dict(), kwarg |
|
|
|
write(iocode, codefence.second, '\n') |
|
|
|
write(iocode, codefence.second, '\n') |
|
|
|
any(write_line, chunk.lines) && write(iomd, seekstart(iocode)) |
|
|
|
any(write_line, chunk.lines) && write(iomd, seekstart(iocode)) |
|
|
|
if execute |
|
|
|
if execute |
|
|
|
execute_markdown!(iomd, sb, join(chunk.lines, '\n'), outputdir; |
|
|
|
cd(config["literate_outputdir"]) do |
|
|
|
inputfile=config["literate_inputfile"], |
|
|
|
execute_markdown!(iomd, sb, join(chunk.lines, '\n'), outputdir; |
|
|
|
flavor=config["flavor"], |
|
|
|
inputfile=config["literate_inputfile"], |
|
|
|
image_formats=config["image_formats"]) |
|
|
|
fake_source=config["literate_outputfile"], |
|
|
|
|
|
|
|
flavor=config["flavor"], |
|
|
|
|
|
|
|
image_formats=config["image_formats"]) |
|
|
|
|
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
write(iomd, '\n') # add a newline between each chunk |
|
|
|
write(iomd, '\n') # add a newline between each chunk |
|
|
|
@ -561,10 +565,10 @@ function markdown(inputfile, outputdir=pwd(); config::AbstractDict=Dict(), kwarg |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
function execute_markdown!(io::IO, sb::Module, block::String, outputdir; |
|
|
|
function execute_markdown!(io::IO, sb::Module, block::String, outputdir; |
|
|
|
inputfile::String="<unknown>", flavor::AbstractFlavor, |
|
|
|
inputfile::String, fake_source::String, |
|
|
|
image_formats::Vector) |
|
|
|
flavor::AbstractFlavor, image_formats::Vector) |
|
|
|
# TODO: Deal with explicit display(...) calls |
|
|
|
# TODO: Deal with explicit display(...) calls |
|
|
|
r, str, _ = execute_block(sb, block; inputfile=inputfile) |
|
|
|
r, str, _ = execute_block(sb, block; inputfile=inputfile, fake_source=fake_source) |
|
|
|
# issue #101: consecutive codefenced blocks need newline |
|
|
|
# issue #101: consecutive codefenced blocks need newline |
|
|
|
# issue #144: quadruple backticks allow for triple backticks in the output |
|
|
|
# issue #144: quadruple backticks allow for triple backticks in the output |
|
|
|
plain_fence = "\n````\n" => "\n````" |
|
|
|
plain_fence = "\n````\n" => "\n````" |
|
|
|
@ -698,7 +702,8 @@ function jupyter_notebook(chunks, config) |
|
|
|
@info "executing notebook `$(config["name"] * ".ipynb")`" |
|
|
|
@info "executing notebook `$(config["name"] * ".ipynb")`" |
|
|
|
try |
|
|
|
try |
|
|
|
cd(config["literate_outputdir"]) do |
|
|
|
cd(config["literate_outputdir"]) do |
|
|
|
nb = execute_notebook(nb; inputfile=config["literate_inputfile"]) |
|
|
|
nb = execute_notebook(nb; inputfile=config["literate_inputfile"], |
|
|
|
|
|
|
|
fake_source=config["literate_outputfile"]) |
|
|
|
end |
|
|
|
end |
|
|
|
catch err |
|
|
|
catch err |
|
|
|
@error "error when executing notebook based on input file: " * |
|
|
|
@error "error when executing notebook based on input file: " * |
|
|
|
@ -709,7 +714,7 @@ function jupyter_notebook(chunks, config) |
|
|
|
return nb |
|
|
|
return nb |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
function execute_notebook(nb; inputfile::String="<unknown>") |
|
|
|
function execute_notebook(nb; inputfile::String, fake_source::String) |
|
|
|
sb = sandbox() |
|
|
|
sb = sandbox() |
|
|
|
execution_count = 0 |
|
|
|
execution_count = 0 |
|
|
|
for cell in nb["cells"] |
|
|
|
for cell in nb["cells"] |
|
|
|
@ -717,7 +722,7 @@ function execute_notebook(nb; inputfile::String="<unknown>") |
|
|
|
execution_count += 1 |
|
|
|
execution_count += 1 |
|
|
|
cell["execution_count"] = execution_count |
|
|
|
cell["execution_count"] = execution_count |
|
|
|
block = join(cell["source"]) |
|
|
|
block = join(cell["source"]) |
|
|
|
r, str, display_dicts = execute_block(sb, block; inputfile=inputfile) |
|
|
|
r, str, display_dicts = execute_block(sb, block; inputfile=inputfile, fake_source=fake_source) |
|
|
|
|
|
|
|
|
|
|
|
# str should go into stream |
|
|
|
# str should go into stream |
|
|
|
if !isempty(str) |
|
|
|
if !isempty(str) |
|
|
|
@ -799,7 +804,7 @@ function Base.display(ld::LiterateDisplay, mime::MIME, x) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
# Execute a code-block in a module and capture stdout/stderr and the result |
|
|
|
# Execute a code-block in a module and capture stdout/stderr and the result |
|
|
|
function execute_block(sb::Module, block::String; inputfile::String="<unknown>") |
|
|
|
function execute_block(sb::Module, block::String; inputfile::String, fake_source::String) |
|
|
|
@debug """execute_block($sb, block) |
|
|
|
@debug """execute_block($sb, block) |
|
|
|
``` |
|
|
|
``` |
|
|
|
$(block) |
|
|
|
$(block) |
|
|
|
@ -815,13 +820,13 @@ function execute_block(sb::Module, block::String; inputfile::String="<unknown>") |
|
|
|
# `rethrow = Union{}` means that we try-catch all the exceptions thrown in the do-block |
|
|
|
# `rethrow = Union{}` means that we try-catch all the exceptions thrown in the do-block |
|
|
|
# and return them via the return value (they get handled below). |
|
|
|
# and return them via the return value (they get handled below). |
|
|
|
c = IOCapture.capture(rethrow = Union{}) do |
|
|
|
c = IOCapture.capture(rethrow = Union{}) do |
|
|
|
include_string(sb, block) |
|
|
|
include_string(sb, block, fake_source) |
|
|
|
end |
|
|
|
end |
|
|
|
popdisplay(disp) # IOCapture.capture has a try-catch so should always end up here |
|
|
|
popdisplay(disp) # IOCapture.capture has a try-catch so should always end up here |
|
|
|
if c.error |
|
|
|
if c.error |
|
|
|
error(""" |
|
|
|
error(""" |
|
|
|
$(sprint(showerror, c.value)) |
|
|
|
$(sprint(showerror, c.value)) |
|
|
|
when executing the following code block in file `$(Base.contractuser(inputfile))` |
|
|
|
when executing the following code block from inputfile `$(Base.contractuser(inputfile))` |
|
|
|
|
|
|
|
|
|
|
|
```julia |
|
|
|
```julia |
|
|
|
$block |
|
|
|
$block |
|
|
|
|