mirror of https://github.com/mpastell/Weave.jl
413 lines
13 KiB
Julia
413 lines
13 KiB
Julia
"""
|
||
run(doc::WeaveDoc; doctype = "pandoc", plotlib="Gadfly", informat="noweb",
|
||
out_path=:doc, fig_path = "figures", fig_ext = nothing,
|
||
cache_path = "cache", cache = :off)
|
||
|
||
Run code chunks and capture output from parsed document.
|
||
|
||
* `doctype`: see `list_out_formats()`
|
||
* `plotlib`: `"PyPlot"`, `"Gadfly"`, or `"Winston"`
|
||
* `informat`: `"noweb"` of `"markdown"`
|
||
* `out_path`: Path where the output is generated. Can be: `:doc`: Path of the source document, `:pwd`: Julia working directory,
|
||
`"somepath"`: Path as a AbstractString e.g `"/home/mpastell/weaveout"`
|
||
* `fig_path`: where figures will be generated, relative to out_path
|
||
* `fig_ext`: Extension for saved figures e.g. `".pdf"`, `".png"`. Default setting depends on `doctype`.
|
||
* `cache_path`: where of cached output will be saved.
|
||
* `cache`: controls caching of code: `:off` = no caching, `:all` = cache everything,
|
||
`:user` = cache based on chunk options, `:refresh`, run all code chunks and save new cache.
|
||
|
||
**Note:** Run command from terminal and not using IJulia, Juno or ESS, they tend to mess with capturing output.
|
||
"""
|
||
function Base.run(doc::WeaveDoc; doctype = "pandoc", plotlib="Gadfly", informat="noweb",
|
||
out_path=:doc, fig_path = "figures", fig_ext = nothing,
|
||
cache_path = "cache", cache = :off)
|
||
#cache :all, :user, :off, :refresh
|
||
|
||
|
||
doc.cwd = get_cwd(doc, out_path)
|
||
doc.doctype = doctype
|
||
doc.format = formats[doctype]
|
||
set_rc_params(doc.format.formatdict, fig_path, fig_ext)
|
||
|
||
|
||
#New sandbox for each document
|
||
sandbox = "ReportSandBox$(rcParams[:doc_number])"
|
||
eval(parse("module $sandbox\nend"))
|
||
SandBox = eval(parse(sandbox))
|
||
rcParams[:doc_number] += 1
|
||
|
||
init_plotting(plotlib)
|
||
report = Report(doc.cwd, doc.basename, doc.format.formatdict)
|
||
pushdisplay(report)
|
||
|
||
if cache != :off && cache != :refresh
|
||
cached = read_cache(doc, cache_path)
|
||
cached == nothing && info("No cached results found, running code")
|
||
else
|
||
cached = nothing
|
||
end
|
||
|
||
executed = Any[]
|
||
n = length(doc.chunks)
|
||
|
||
for i = 1:n
|
||
chunk = doc.chunks[i]
|
||
result_chunks = run_chunk(chunk, report, SandBox, cached, cache, i)
|
||
#push!(executed, result_chunk)
|
||
executed = [executed; result_chunks]
|
||
end
|
||
|
||
popdisplay(report)
|
||
|
||
if cache != :off
|
||
write_cache(doc, cache_path)
|
||
end
|
||
|
||
#Clear variables from used sandbox
|
||
clear_sandbox(SandBox)
|
||
doc.chunks = executed
|
||
return doc
|
||
end
|
||
|
||
function run_chunk(chunk::CodeChunk, report::Report, SandBox::Module, cached, cache, idx)
|
||
#Defaults, already merged before, this merges format specific things
|
||
options = merge(rcParams[:chunk_defaults], chunk.options)
|
||
merge!(chunk.options, options)
|
||
|
||
if cached != nothing && (cache == :all || (cache ==:user && chunk.options[:cache]))
|
||
result_chunk = restore_chunk(chunk, cached, idx)
|
||
else
|
||
result_chunk = eval_chunk(chunk, report, SandBox)
|
||
end
|
||
end
|
||
|
||
function run_chunk(chunk::DocChunk, report::Report, SandBox::Module, cached, cache, idx)
|
||
return chunk
|
||
end
|
||
|
||
function reset_report(report::Report)
|
||
report.cur_result = ""
|
||
report.figures = AbstractString[]
|
||
report.term_state = :text
|
||
end
|
||
|
||
function run_code(chunk::CodeChunk, report::Report, SandBox::Module)
|
||
expressions = parse_input(chunk.content)
|
||
#@show expressions
|
||
result_no = 1
|
||
results = ChunkOutput[ ]
|
||
|
||
for (str_expr, expr) = expressions
|
||
reset_report(report)
|
||
(obj, out) = capture_output(expr, SandBox, chunk.options[:term], rcParams[:plotlib])
|
||
displayed = report.cur_result #Not needed?
|
||
figures = report.figures #Captured figures
|
||
result = ChunkOutput(str_expr, out, displayed, figures)
|
||
push!(results, result)
|
||
end
|
||
|
||
#Save figures only in the end of chunk for PyPlot
|
||
if rcParams[:plotlib] == "PyPlot"
|
||
savefigs_pyplot(report::Report)
|
||
end
|
||
|
||
return results
|
||
end
|
||
|
||
|
||
function run_block(chunk::CodeChunk, report::Report, SandBox::Module)
|
||
expressions = parse_input(chunk.content)
|
||
#@show expressions
|
||
result_no = 1
|
||
result_chunks = CodeChunk[ ]
|
||
input = ""
|
||
for (str_expr, expr) = expressions
|
||
reset_report(report)
|
||
(obj, out) = capture_output(expr, SandBox)
|
||
|
||
if rcParams[:plotlib] == "Gadfly" && typeof(obj) == Gadfly.Plot
|
||
obj != nothing && display(obj)
|
||
end
|
||
|
||
displayed = report.cur_result #Catch output to text display
|
||
figures = report.figures #Captured figures
|
||
if strip(out) == "" && isempty(figures) && displayed == ""
|
||
input *= str_expr
|
||
else
|
||
@show input
|
||
content = "\n" * input * str_expr
|
||
rchunk = CodeChunk(content, chunk.number, chunk.start_line, chunk.option_AbstractString, copy(chunk.options))
|
||
input = ""
|
||
rchunk.result_no = result_no
|
||
result_no *=1
|
||
rchunk.figures = report.figures
|
||
rchunk.output = out * displayed
|
||
push!(result_chunks, rchunk)
|
||
end
|
||
end
|
||
return result_chunks
|
||
end
|
||
|
||
|
||
function run_term(chunk::CodeChunk, report::Report, SandBox::Module)
|
||
expressions = parse_input(chunk.content)
|
||
#@show expressions
|
||
result_no = 1
|
||
result_chunks = CodeChunk[ ]
|
||
output = ""
|
||
prompt = "\njulia> "
|
||
for (str_expr, expr) = expressions
|
||
reset_report(report)
|
||
(obj, out) = capture_output(expr, SandBox)
|
||
obj != nothing && display(obj)
|
||
displayed = report.cur_result #Catch output to text display
|
||
figures = report.figures #Captured figures
|
||
|
||
if strip(out) == "" && isempty(figures) && displayed == ""
|
||
output *= prompt * str_expr
|
||
else
|
||
content = prompt * output * str_expr
|
||
rchunk = CodeChunk(content, chunk.number, chunk.start_line, chunk.option_AbstractString, copy(chunk.options))
|
||
rchunk.output = content * out * displayed
|
||
@show rchunk.output
|
||
output = ""
|
||
rchunk.result_no = result_no
|
||
result_no *=1
|
||
rchunk.figures = report.figures
|
||
push!(result_chunks, rchunk)
|
||
end
|
||
end
|
||
return result_chunks
|
||
end
|
||
|
||
|
||
function run_term2(code_str, report::Report, SandBox::Module)
|
||
prompt = "\njulia> "
|
||
codestart = "\n\n"*report.formatdict[:codestart]
|
||
|
||
if haskey(report.formatdict, :indent)
|
||
prompt = indent(prompt, report.formatdict[:indent])
|
||
end
|
||
|
||
parsed = parse_input(code_str)
|
||
#Emulate terminal
|
||
n = length(parsed)
|
||
pos = 1 #The first character is extra line end
|
||
while pos < n
|
||
oldpos = pos
|
||
code, pos = parse(code_str, pos)
|
||
|
||
report.term_state == :fig && (report.cur_result*= codestart)
|
||
prompts = AbstractString(prompt, rstrip(code_str[oldpos:(pos-1)]), "\n")
|
||
report.cur_result *= prompts
|
||
report.term_state = :text
|
||
s = eval(SandBox, code)
|
||
s != nothing && display(s)
|
||
end
|
||
|
||
return AbstractString(report.cur_result)
|
||
end
|
||
|
||
function capture_output(expr::Expr, SandBox::Module, term, plotlib)
|
||
oldSTDOUT = STDOUT
|
||
out = nothing
|
||
obj = nothing
|
||
rw, wr = redirect_stdout()
|
||
try
|
||
obj = eval(SandBox, expr)
|
||
if term
|
||
obj != nothing && display(obj)
|
||
elseif plotlib == "Gadfly" && typeof(obj) == Gadfly.Plot
|
||
obj != nothing && display(obj)
|
||
end
|
||
|
||
finally
|
||
redirect_stdout(oldSTDOUT)
|
||
close(wr)
|
||
out = readall(rw)
|
||
close(rw)
|
||
end
|
||
return (obj, out)
|
||
end
|
||
|
||
|
||
#Parse chunk input to array of expressions
|
||
function parse_input(input::AbstractString)
|
||
parsed = Tuple{AbstractString, Expr}[]
|
||
n = length(input)
|
||
pos = 2 #The first character is extra line end
|
||
while pos < n
|
||
oldpos = pos
|
||
code, pos = parse(input, pos)
|
||
push!(parsed, (input[oldpos:pos-1] , code ))
|
||
end
|
||
parsed
|
||
end
|
||
|
||
|
||
function eval_chunk(chunk::CodeChunk, report::Report, SandBox::Module)
|
||
info("Weaving chunk $(chunk.number) from line $(chunk.start_line)")
|
||
|
||
if !chunk.options[:eval]
|
||
chunk.output = ""
|
||
chunk.options[:fig] = false
|
||
return chunk
|
||
end
|
||
|
||
report.fignum = 1
|
||
report.cur_chunk = chunk
|
||
|
||
if haskey(report.formatdict, :out_width) && chunk.options[:out_width] == nothing
|
||
chunk.options[:out_width] = report.formatdict[:out_width]
|
||
end
|
||
|
||
chunk.result = run_code(chunk, report, SandBox)
|
||
if chunk.options[:term]
|
||
chunks = collect_results(chunk, TermResult())
|
||
elseif chunk.options[:results] == "hold"
|
||
chunks = collect_results(chunk, CollectResult())
|
||
else
|
||
chunks = collect_results(chunk, ScriptResult())
|
||
end
|
||
|
||
#else
|
||
# chunk.options[:fig] && (chunk.figures = copy(report.figures))
|
||
#end
|
||
chunks
|
||
end
|
||
|
||
|
||
#function eval_chunk(chunk::DocChunk, report::Report, SandBox)
|
||
# chunk
|
||
#end
|
||
|
||
#Set all variables to nothing
|
||
function clear_sandbox(SandBox::Module)
|
||
for name = names(SandBox, true)
|
||
if name != :eval && name != names(SandBox)[1]
|
||
try eval(SandBox, parse(AbstractString(AbstractString(name), "=nothing"))) end
|
||
end
|
||
end
|
||
end
|
||
|
||
|
||
function get_figname(report::Report, chunk; fignum = nothing)
|
||
figpath = joinpath(report.cwd, chunk.options[:fig_path])
|
||
isdir(figpath) || mkpath(figpath)
|
||
ext = chunk.options[:fig_ext]
|
||
fignum == nothing && (fignum = report.fignum)
|
||
|
||
chunkid = (chunk.options[:name] == nothing) ? chunk.number : chunk.options[:name]
|
||
full_name = joinpath(report.cwd, chunk.options[:fig_path],
|
||
"$(report.basename)_$(chunkid)_$(fignum)$ext")
|
||
rel_name = "$(chunk.options[:fig_path])/$(report.basename)_$(chunkid)_$(fignum)$ext" #Relative path is used in output
|
||
return full_name, rel_name
|
||
end
|
||
|
||
|
||
function init_plotting(plotlib)
|
||
if plotlib == nothing
|
||
rcParams[:chunk_defaults][:fig] = false
|
||
rcParams[:plotlib] = nothing
|
||
else
|
||
l_plotlib = lowercase(plotlib)
|
||
rcParams[:chunk_defaults][:fig] = true
|
||
|
||
if l_plotlib == "winston"
|
||
eval(parse("""include(Pkg.dir("Weave","src","winston.jl"))"""))
|
||
rcParams[:plotlib] = "Winston"
|
||
elseif l_plotlib == "pyplot"
|
||
eval(parse("""include(Pkg.dir("Weave","src","pyplot.jl"))"""))
|
||
rcParams[:plotlib] = "PyPlot"
|
||
elseif l_plotlib == "gadfly"
|
||
eval(parse("""include(Pkg.dir("Weave","src","gadfly.jl"))"""))
|
||
rcParams[:plotlib] = "Gadfly"
|
||
end
|
||
end
|
||
return nothing
|
||
end
|
||
|
||
function get_cwd(doc::WeaveDoc, out_path)
|
||
#Set the output directory
|
||
if out_path == :doc
|
||
cwd = doc.path
|
||
elseif out_path == :pwd
|
||
cwd = pwd()
|
||
else
|
||
cwd = out_path
|
||
end
|
||
return cwd
|
||
end
|
||
|
||
function set_rc_params(formatdict, fig_path, fig_ext)
|
||
if fig_ext == nothing
|
||
rcParams[:chunk_defaults][:fig_ext] = formatdict[:fig_ext]
|
||
else
|
||
rcParams[:chunk_defaults][:fig_ext] = fig_ext
|
||
end
|
||
rcParams[:chunk_defaults][:fig_path] = fig_path
|
||
return nothing
|
||
end
|
||
|
||
function collect_results(chunk::CodeChunk, fmt::ScriptResult)
|
||
content = ""
|
||
result_no = 1
|
||
result_chunks = CodeChunk[ ]
|
||
for r = chunk.result
|
||
if strip(r.stdout) == "" && isempty(r.figures) && r.displayed == ""
|
||
content *= r.code
|
||
else
|
||
content = "\n" * content * r.code
|
||
rchunk = CodeChunk(content, chunk.number, chunk.start_line, chunk.option_AbstractString, copy(chunk.options))
|
||
content = ""
|
||
rchunk.result_no = result_no
|
||
result_no *=1
|
||
rchunk.figures = r.figures
|
||
rchunk.output = r.stdout * r.displayed
|
||
push!(result_chunks, rchunk)
|
||
end
|
||
end
|
||
if content != ""
|
||
startswith(content, "\n") || (content = "\n" * content)
|
||
rchunk = CodeChunk(content, chunk.number, chunk.start_line, chunk.option_AbstractString, copy(chunk.options))
|
||
push!(result_chunks, rchunk)
|
||
end
|
||
|
||
return result_chunks
|
||
end
|
||
|
||
function collect_results(chunk::CodeChunk, fmt::TermResult)
|
||
output = ""
|
||
result_no = 1
|
||
prompt = "\njulia> "
|
||
result_chunks = CodeChunk[ ]
|
||
for r =chunk.result
|
||
output *= prompt * r.code
|
||
output *= r.displayed * r.stdout
|
||
if !isempty(r.figures)
|
||
rchunk = CodeChunk("", chunk.number, chunk.start_line, chunk.option_AbstractString, copy(chunk.options))
|
||
rchunk.output = output
|
||
output = ""
|
||
rchunk.figures = r.figures
|
||
push!(result_chunks, rchunk)
|
||
end
|
||
end
|
||
if output != ""
|
||
rchunk = CodeChunk("", chunk.number, chunk.start_line, chunk.option_AbstractString, copy(chunk.options))
|
||
rchunk.output = output
|
||
push!(result_chunks, rchunk)
|
||
end
|
||
|
||
return result_chunks
|
||
end
|
||
|
||
function collect_results(chunk::CodeChunk, fmt::CollectResult)
|
||
result_no = 1
|
||
for r =chunk.result
|
||
chunk.output *= r.stdout
|
||
chunk.figures = [chunk.figures, r.figures]
|
||
end
|
||
|
||
return [chunk]
|
||
end
|