Weave.jl/src/run.jl

413 lines
13 KiB
Julia
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

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