Skip to content

Commit eead8f2

Browse files
authored
Added error handling with html display (#117)
* Implemented error handling and initial html display * Improved html display and tests for error handling
1 parent 51ea08c commit eead8f2

File tree

7 files changed

+88
-11
lines changed

7 files changed

+88
-11
lines changed

src/Weave.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ Weave an input document to output file.
6868
* `cache_path`: where of cached output will be saved.
6969
* `cache`: controls caching of code: `:off` = no caching, `:all` = cache everything,
7070
`:user` = cache based on chunk options, `:refresh`, run all code chunks and save new cache.
71+
* `throw_errors` if `false` errors are included in output document and the whole document is
72+
executed. if `true` errors are thrown when they occur.
7173
* `template` : Template (file path) for md2html or md2tex formats.
7274
* `highlight_theme` : Theme (Highlights.AbstractTheme) for used syntax highlighting
7375
* `css` : CSS (file path) used for md2html format
@@ -79,6 +81,7 @@ function weave(source ; doctype = :auto, plotlib=:auto,
7981
informat=:auto, out_path=:doc, args = Dict(),
8082
fig_path = "figures", fig_ext = nothing,
8183
cache_path = "cache", cache=:off,
84+
throw_errors = false,
8285
template = nothing, highlight_theme = nothing, css = nothing,
8386
latex_cmd = "xelatex")
8487

@@ -91,7 +94,8 @@ function weave(source ; doctype = :auto, plotlib=:auto,
9194
try
9295
doc = run(doc, doctype = doctype, plotlib=plotlib,
9396
out_path=out_path, args = args,
94-
fig_path = fig_path, fig_ext = fig_ext, cache_path = cache_path, cache=cache)
97+
fig_path = fig_path, fig_ext = fig_ext, cache_path = cache_path, cache=cache,
98+
throw_errors = throw_errors)
9599
formatted = format(doc)
96100

97101
outname = get_outname(out_path, doc)

src/display_methods.jl

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ mutable struct Report <: Display
1515
mimetypes::Array{AbstractString}
1616
first_plot::Bool
1717
header_script::String
18+
throw_errors::Bool
1819
end
1920

20-
function Report(cwd, basename, formatdict, mimetypes)
21-
Report(cwd, basename, formatdict, "", "", "", 1, AbstractString[], :text, nothing, mimetypes, true, "")
21+
function Report(cwd, basename, formatdict, mimetypes, throw_errors)
22+
Report(cwd, basename, formatdict, "", "", "", 1, AbstractString[], :text, nothing,
23+
mimetypes, true, "", throw_errors)
2224
end
2325

2426

@@ -34,13 +36,14 @@ function Base.display(report::Report, data)
3436
if mimewritable(m, data)
3537
try
3638
if !istextmime(m)
37-
Compat.invokelatest(display, report, m, data)
39+
Compat.invokelatest(display, report, m, data)
3840
elseif report.cur_chunk.options[:term]
3941
Compat.invokelatest(display, report, "text/plain", data)
40-
else
42+
else
4143
Compat.invokelatest(display, report, m, data)
4244
end
4345
catch e
46+
throw(e)
4447
warn("Failed to display data in \"$m\" format")
4548
continue
4649
end
@@ -71,6 +74,20 @@ function Base.display(report::Report, m::MIME"text/plain", data)
7174
println(s)
7275
end
7376

77+
function Base.display(report::Report, m::MIME"text/plain", data::Exception)
78+
println("Error: " * sprint(showerror, data))
79+
end
80+
81+
function Base.display(report::Report, m::MIME"text/html", data::Exception)
82+
report.rich_output = sprint(show, m, data)
83+
end
84+
85+
function Base.show(io, m::MIME"text/html", data::Exception)
86+
println(io ,"<pre class=\"julia-error\">")
87+
println(io, Base.Markdown.htmlesc("ERROR: " * sprint(showerror, data)))
88+
println(io ,"</pre>")
89+
end
90+
7491
#Catch "rich_output"
7592
function Base.display(report::Report, m::MIME"text/html", data)
7693
s = reprmime(m, data)
@@ -88,7 +105,6 @@ function Base.display(report::Report, m::MIME"text/latex", data)
88105
report.rich_output *= "\n" * s
89106
end
90107

91-
92108
"""Add saved figure name to results and return the name"""
93109
function add_figure(report::Report, data, m, ext)
94110
chunk = report.cur_chunk

src/run.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Run code chunks and capture output from parsed document.
2222
"""
2323
function Base.run(doc::WeaveDoc; doctype = :auto, plotlib=:auto,
2424
out_path=:doc, args=Dict(), fig_path = "figures", fig_ext = nothing,
25-
cache_path = "cache", cache = :off)
25+
cache_path = "cache", cache = :off, throw_errors=false)
2626
#cache :all, :user, :off, :refresh
2727

2828
doc.cwd = get_cwd(doc, out_path)
@@ -60,7 +60,7 @@ function Base.run(doc::WeaveDoc; doctype = :auto, plotlib=:auto,
6060
rcParams[:plotlib_set] = false
6161
plotlib == :auto || init_plotting(plotlib)
6262

63-
report = Report(doc.cwd, doc.basename, doc.format.formatdict, mimetypes)
63+
report = Report(doc.cwd, doc.basename, doc.format.formatdict, mimetypes, throw_errors)
6464
pushdisplay(report)
6565

6666
if cache != :off && cache != :refresh
@@ -204,7 +204,7 @@ function run_code(chunk::CodeChunk, report::Report, SandBox::Module)
204204
lastline = (result_no == N)
205205
rcParams[:plotlib_set] || detect_plotlib(chunk) #Try to autodetect plotting library
206206
(obj, out) = capture_output(expr, SandBox, chunk.options[:term],
207-
chunk.options[:display], rcParams[:plotlib], lastline)
207+
chunk.options[:display], rcParams[:plotlib], lastline, report.throw_errors)
208208
figures = report.figures #Captured figures
209209
result = ChunkOutput(str_expr, out, report.cur_result, report.rich_output, figures)
210210
report.rich_output = ""
@@ -223,7 +223,7 @@ end
223223
getstdout() = Base.STDOUT
224224

225225
function capture_output(expr, SandBox::Module, term, disp, plotlib,
226-
lastline)
226+
lastline, throw_errors=false)
227227
#oldSTDOUT = STDOUT
228228
oldSTDOUT = getstdout()
229229
out = nothing
@@ -243,6 +243,10 @@ function capture_output(expr, SandBox::Module, term, disp, plotlib,
243243
elseif lastline && obj != nothing
244244
(expr.head != :toplevel && expr.head != :(=)) && display(obj)
245245
end
246+
catch E
247+
throw_errors && throw(E)
248+
display(E)
249+
warn("ERROR: $(typeof(E)) occurred, including output in Weaved document")
246250
finally
247251
redirect_stdout(oldSTDOUT)
248252
close(wr)

templates/pandoc_skeleton.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,10 @@ pre.sourceCode.julia {
518518
border-radius: 4px;
519519
}
520520

521+
pre.julia-error {
522+
color : red
523+
}
524+
521525
code,
522526
kbd,
523527
pre,

templates/skeleton_css.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,10 @@ pre.sourceCode.julia {
515515
border-radius: 4px;
516516
}
517517

518+
pre.julia-error {
519+
color : red
520+
}
521+
518522
code,
519523
kbd,
520524
pre,

test/errors_test.jl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using Weave
2+
using Base.Test
3+
4+
s1= """
5+
6+
```julia
7+
using NonExisting
8+
```
9+
10+
```julia
11+
x =
12+
```
13+
14+
15+
```julia;term=true
16+
plot(x)
17+
y = 10
18+
print(y
19+
```
20+
21+
"""
22+
23+
p1 = Weave.parse_doc(s1, "markdown")
24+
doc = Weave.WeaveDoc("dummy1.jmd", p1, Dict())
25+
doc1 = Weave.run(doc, doctype = "pandoc")
26+
27+
@test doc1.chunks[1].output == "Error: ArgumentError: Module NonExisting not found in current path.\nRun `Pkg.add(\"NonExisting\")` to install the NonExisting package.\n"
28+
@test doc1.chunks[2].output == "Error: syntax: incomplete: premature end of input\n"
29+
@test doc1.chunks[3].output == "\njulia> plot(x)\nError: UndefVarError: plot not defined\n\njulia> y = 10\n10\n\njulia> print(y\nError: syntax: incomplete: premature end of input\n"
30+
31+
try
32+
doc2 = Weave.run(doc, doctype = "pandoc", throw_errors = true)
33+
catch E
34+
@test typeof(E) == ArgumentError
35+
@test E.msg == "Module NonExisting not found in current path.\nRun `Pkg.add(\"NonExisting\")` to install the NonExisting package."
36+
end
37+
38+
doc = Weave.WeaveDoc("dummy1.jmd", p1, Dict())
39+
doc3 = Weave.run(doc, doctype = "md2html")
40+
@test doc3.chunks[1].rich_output == "<pre class=\"julia-error\">\nERROR: ArgumentError: Module NonExisting not found in current path.\nRun &#96;Pkg.add&#40;&quot;NonExisting&quot;&#41;&#96; to install the NonExisting package.\n</pre>\n"
41+
@test doc3.chunks[2].rich_output == "<pre class=\"julia-error\">\nERROR: syntax: incomplete: premature end of input\n</pre>\n"
42+
@test doc3.chunks[3].output == "\njulia> plot(x)\nError: UndefVarError: plot not defined\n\njulia> y = 10\n10\n\njulia> print(y\nError: syntax: incomplete: premature end of input\n"
43+
@test doc3.chunks[3].rich_output == ""

test/runtests.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ using Base.Test
44
info("Test: Chunk options")
55
include("chunk_options.jl")
66

7+
info("Testing error handling")
8+
include("errors_test.jl")
9+
710
info("Test: Converting")
811
include("convert_test.jl")
912

@@ -32,4 +35,3 @@ include("chunk_opts_gadfly.jl")
3235
info("Test: Weaving with Plots.jl")
3336
include("plotsjl_test.jl")
3437
include("publish_test.jl")
35-

0 commit comments

Comments
 (0)