Skip to content

Commit

Permalink
close #2226: add an error handler to improve YAML error message (#2294)
Browse files Browse the repository at this point in the history
Co-authored-by: Yihui Xie <[email protected]>
  • Loading branch information
pedropark99 and yihui authored Oct 24, 2023
1 parent ba8d9fb commit 8725408
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 16 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGES IN knitr VERSION 1.45

## NEW FEATURES

- Improved the error message to contain more specific information when YAML chunk options could not be parsed (thanks, @pedropark99, #2294).

## BUG FIXES

- Special characters in the chunk option `fig.alt` are properly escaped now (thanks, @jay-sf, #2290).
Expand Down
6 changes: 3 additions & 3 deletions R/concordance.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@

# record input/output lines numbers in Rnw/tex and filenames
knit_concord = new_defaults(list(
inlines = NULL, outlines = NULL, infile = NULL, outfile = NULL
inlines = NULL, outlines = NULL, infile = NULL, outfile = NULL, block = NULL
))

# do not consider child mode for concordance
concord_mode = function() {
opts_knit$get('concordance') && !child_mode()
}

current_lines = function(i) {
current_lines = function(i = knit_concord$get('block')) {
# a helpr function to return line numbers for block i
n = knit_concord$get('inlines')
n1 = sum(head(n, i)); n0 = n1 - n[i] + 2
c(min(n0, n1), n1)
paste(c(min(n0, n1), n1), collapse = '-')
}

# generate concordance for RStudio
Expand Down
23 changes: 15 additions & 8 deletions R/output.R
Original file line number Diff line number Diff line change
Expand Up @@ -311,20 +311,18 @@ process_file = function(text, output) {
}
if (progress && is.function(pb$update)) pb$update(i)
group = groups[[i]]
res[i] = withCallingHandlers(
knit_concord$set(block = i)
res[i] = handle_error(
withCallingHandlers(
if (tangle) process_tangle(group) else process_group(group),
error = function(e) if (xfun::pkg_available('rlang', '1.0.0')) rlang::entrace(e)
),
error = function(e) {
function(e, loc) {
setwd(wd)
write_utf8(res, output %n% stdout())
message(
'\nQuitting from lines ', paste(current_lines(i), collapse = '-'),
if (labels[i] != '') sprintf(' [%s]', labels[i]),
sprintf(' (%s)', knit_concord$get('infile'))
)
}
paste0('\nQuitting from lines ', loc)
},
if (labels[i] != '') sprintf(' [%s]', labels[i])
)
}

Expand All @@ -337,6 +335,15 @@ process_file = function(text, output) {
res
}

# if an expr throws an an error, message the location of the error if possible
handle_error = function(expr, handler, label = '') {
withCallingHandlers(expr, error = function(e) {
# return a string to point out the error location
loc = paste0(current_lines(), label, sprintf(' (%s)', knit_concord$get('infile')))
message(one_string(handler(e, loc)))
})
}

auto_out_name = function(input, ext = tolower(file_ext(input))) {
base = sans_ext(input)
name = if (opts_knit$get('tangle')) c(base, '.R') else
Expand Down
19 changes: 17 additions & 2 deletions R/parser.R
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ split_file = function(lines, set.preamble = TRUE, patterns = knit_patterns$get()
knit_concord$set(inlines = sapply(groups, length)) # input line numbers for concordance

# parse 'em all
lapply(groups, function(g) {
lapply(seq_along(groups), function(i) {
knit_concord$set(block = i)
g = groups[[i]]
block = grepl(chunk.begin, g[1])
if (!set.preamble && !parent_mode()) {
return(if (block) '' else g) # only need to remove chunks to get pure preamble
Expand Down Expand Up @@ -302,7 +304,20 @@ partition_chunk = function(engine, code) {
meta = substr(src, nchar(s1) + 1, nchar(src) - nchar(s2))
# see if the metadata looks like YAML or CSV
if (grepl('^[^ :]+:($|\\s)', meta[1])) {
meta = yaml::yaml.load(meta, handlers = list(expr = parse_only))
meta = handle_error(
yaml::yaml.load(meta, handlers = list(expr = parse_only)),
function(e, loc) {
x = e$message
r = 'line (\\d+), column (\\d+)'
m = regmatches(x, regexec(r, x, perl = TRUE))[[1]]
if (length(m) < 3) return()
m = as.integer(m[-1]) # c(row, col)
c(
sprintf('Failed to parse YAML inside code chunk at lines %s:', loc), '',
append(meta, paste0(strrep(' ', m[2]), '^~~~~~'), m[1]), ''
)
}
)
if (!is.list(meta) || length(names(meta)) == 0) {
warning('Invalid YAML option format in chunk: \n', one_string(meta), '\n')
meta = list()
Expand Down
4 changes: 1 addition & 3 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -1141,9 +1141,7 @@ str_split = function(x, split, ...) {
# methods to update/close it
txt_pb = function(total, labels) {
if (getOption('knitr.progress.linenums', FALSE)) {
nums = sapply(seq_along(labels), function(i) {
paste(current_lines(i), collapse = '-')
})
nums = sapply(seq_along(labels), current_lines)
labels = sprintf(
'%s%s%s:%s', labels, ifelse(labels == '', '', ' @ '),
knit_concord$get('infile'), nums
Expand Down

0 comments on commit 8725408

Please sign in to comment.