Browse Source

Fix cwd and apparent source file when executing markdown. (#195)

pull/203/head
Fredrik Ekre 4 years ago committed by GitHub
parent
commit
53a9d26ff8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      CHANGELOG.md
  2. 47
      src/Literate.jl
  3. 8
      test/runtests.jl

5
CHANGELOG.md

@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [Unreleased]
### Fixed
- Set current working directory for markdown execution to the output directory, just like
notebook execution. ([#195][github-195])
- Set the apparent source file to the output file for markdown and notebook execution.
([#195][github-195])
## [2.13.1] - 2022-04-12 ## [2.13.1] - 2022-04-12
### Fixed ### Fixed

47
src/Literate.jl

@ -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

8
test/runtests.jl

@ -824,6 +824,10 @@ end end
print("a: ", a); nothing #hide print("a: ", a); nothing #hide
#- #-
47 #hide 47 #hide
#-
(@__DIR__) == pwd() ? "cwd correct" : "cwd incorrect"
#-
basename(@__FILE__)
""") """)
Literate.markdown(inputfile, outdir; execute=true) Literate.markdown(inputfile, outdir; execute=true)
markdown = read(joinpath(outdir, "inputfile.md"), String) markdown = read(joinpath(outdir, "inputfile.md"), String)
@ -846,6 +850,8 @@ end end
@test !occursin("```julia\n47 #hide\n```", markdown) # line with `#hide` removed @test !occursin("```julia\n47 #hide\n```", markdown) # line with `#hide` removed
@test !occursin("```julia\n```", markdown) # no empty code block @test !occursin("```julia\n```", markdown) # no empty code block
@test occursin("```\n47\n```", markdown) # return value (even though line/block removed) @test occursin("```\n47\n```", markdown) # return value (even though line/block removed)
@test occursin("```\n\"cwd correct\"\n```", markdown) # Correct cwd (@__DIR__)
@test occursin("```\n\"inputfile.md\"\n```", markdown) # Correct source file (@__FILE__)
# FranklinFlavor # FranklinFlavor
Literate.markdown(inputfile, outdir; execute=true, flavor=Literate.FranklinFlavor()) Literate.markdown(inputfile, outdir; execute=true, flavor=Literate.FranklinFlavor())
@ -1179,7 +1185,7 @@ end end
err err
end) end)
@test isa(r, ErrorException) @test isa(r, ErrorException)
@test occursin("when executing the following code block in file ", r.msg) @test occursin("when executing the following code block from inputfile ", r.msg)
@test occursin(inputfile, r.msg) @test occursin(inputfile, r.msg)
# verify that inputfile exists # verify that inputfile exists

Loading…
Cancel
Save