better stack traces (e.g. for plotting and help_files_with_topic)
This is what currently happens if a ggplot object raises an error:
library(ggplot2)
data <- data.frame(x = letters[1:5], y = runif(5))
ggplot(data, aes(x, y)) + geom_bar()
Error: stat_count() must not be used with a y aesthetic.
Traceback:
1. prepare_mimebundle(obj, .self$handle_display_error)
2. repr_text(obj)
3. repr_text.default(obj)
4. paste(capture.output(print(obj)), collapse = "\n")
5. capture.output(print(obj))
6. evalVis(expr)
7. withVisible(eval(expr, pf))
8. eval(expr, pf)
9. eval(expr, envir, enclos)
10. print(obj)
11. print.ggplot(obj)
12. ggplot_build(x)
13. by_layer(function(l, d) l$compute_statistic(d, panel))
14. f(l = layers[[i]], d = data[[i]])
15. l$compute_statistic(d, panel)
16. f(..., self = self)
17. self$stat$setup_params(data, self$stat_params)
18. f(...)
19. stop("stat_count() must not be used with a y aesthetic.", call. = FALSE)
It seems that ggplot plots are build by
returning a ggplot object -> repr_text.default does print -> evaluate registers a plot -> calls the handler for a plot.
IMO:
- this shouldn't spit out a traceback
- do not do a plot building in repr_text
So implement repr_xxx.ggplot (and other high level plotting libs)?
Or ask for a repr_text.ggplot and repr_png.ggplot in ggplot itself?
Or wrap the print in the repr_text.default in a tryCatch...
This seems to be the easiest fix:
repr_text.default <- function(obj, ...) {
tryCatch({
paste(capture.output(print(obj)), collapse = '\n')
}, error = function(e) paste("ERROR:", e))
}
But on my system, it still returns some plot, at least there is a big space below the error message.
Yep, this is returned:
<body><div style="width: 504pt; height: 504pt;"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="504pt" height="504pt" viewBox="0 0 504 504" version="1.1" style="width: 100%; height: 100%;">
<g id="surface2">
</g>
</svg></div></body>
And a black plot picture if svg is removed...
knitr refuses to knit a document with that error (or, if I specify {r error=TRUE}, it also has a big space under the code), RStudio does not print anything...
IMO, the one workaround would be to specify all relevant repr_xxx.ggplot, but that wouldn't help us here: we would have to build the plot in repr_text to catch and display the error, so it's basically build the plot 3 instead of 2 times (repr_text, svg, png) and just display errors in repr_text :-(
The other workaround would be to somehow find out if a recorded plot is empty and then simple return NULL...
-> I will add a PR with only the above tryCatch workaround and then have a look if there is any way to catch the recordedplot stuff...
This seems to be a way to test for empty recordedPlot objetcs:
> library(ggplot2)
> data <- data.frame(x = letters[1:5], y = runif(5))
> plot(1:10)
> full <- recordPlot()
> length(full[[1]])
[1] 8
> ggplot(data, aes(x, y)) + geom_point()
> full <- recordPlot()
> length(full[[1]])
[1] 3
> ggplot(data, aes(x, y)) + geom_bar()
Error: stat_count() must not be used with a y aesthetic.
> length(empty[[1]])
[1] 2
If the object passed to repr_xxx.recodedPlot(obj) has more than two things in obj[[1]], then it seems to be a legitimate plot... But I'm not sure that there are no plots with less than 3 things... seems to be a bit risky... But maybe someone else has a better test...
I removed the 0.5 milestone as I think this is ok to release with until we have found a solution we all find agreeable :-)
further discussion in the PR #46