Skip to content

Commit

Permalink
Proof of concept to rethrow yaml parsing error in _pkgdown.yml
Browse files Browse the repository at this point in the history
  • Loading branch information
olivroy committed May 18, 2024
1 parent 8513981 commit e409a55
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 3 deletions.
90 changes: 87 additions & 3 deletions R/package.R
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,97 @@ pkgdown_config_path <- function(path) {
)
}

read_meta <- function(path) {
path <- pkgdown_config_path(path)
read_meta <- function(path, check_path = TRUE) {
if (check_path) {
# check_path = FALSE can be used to supply a direct path to
# read_meta instead of a pkgdown object.
path <- pkgdown_config_path(path)
}

# If parsing fails, it will say Error in read_meta()
call <- current_env()
if (is.null(path)) {
yaml <- list()
} else {
yaml <- yaml::yaml.load_file(path) %||% list()
yaml <- withCallingHandlers(
yaml::yaml.load_file(path),
error = function(e) {
yaml_error_msg <- conditionMessage(e)
# Create a clickable path, in case we end up
# just rethrowing the original message
yaml_error_msg <- gsub(
"\\(([^\\)]+)\\)",
"({.path \\1})",
yaml_error_msg
)

# trying to extract the parsing error type?
# like block mapping, block collection?
what <- regmatches(
yaml_error_msg,
m = regexpr("block\\s[[:alpha:]]+", yaml_error_msg)
)
# search for all occurences of digits (1 to 4 digits long)
# I would not see a _pkgdown.yml file containing >9999 lines.
error_locations <- regmatches(
yaml_error_msg,
m = gregexpr("\\d{1,4}", yaml_error_msg)
)
error_locations <- unlist(error_locations, use.names = FALSE)
if (length(error_locations) != 4 || !is_string(what)) {
# can't parse the error message properly,
# just rethrow it cli style
cli::cli_abort(yaml_error_msg, call = call)
}
# yaml throws its message like this:
# ! (./pkgdown/_pkgdown.yml) Parser error: while parsing
# a block mapping at line 1, column 1
# did not find expected key at line 9, column 3
block_line <- error_locations[1]
block_col <- error_locations[2]
error_line <- error_locations[3]
error_col <- error_locations[4]

# try to identify the faulty block / field name to add a hint
block_name <- withCallingHandlers(
readLines(path, warn = FALSE, n = block_line, encoding = "UTF-8"),

Check warning on line 234 in R/package.R

View workflow job for this annotation

GitHub Actions / lint

file=R/package.R,line=234,col=11,[undesirable_function_linter] Function "readLines" is undesirable. As an alternative, use read_lines().
error = function(e) NULL
)
if (!is.null(block_name)) {
block_name <- block_name[length(block_name)]
block_name <- gsub(":.*$", ":", block_name)
}

block_pos <- cli::style_hyperlink(
paste0(block_line),
url = paste0("file://", path),
params = list(
line = as.integer(block_line),
col = as.integer(block_col)
)
)
error_pos <- cli::style_hyperlink(
paste0(error_line),
url = paste0("file://", path),
params = list(
line = as.integer(error_line),
col = as.integer(error_col)
)
)
cli::cli_abort(
c(
"!" = "The {.field {block_name}} field failed to parse.",
"i" = "Error occured between lines {block_pos} and {error_pos}.",
"i" = "Edit {.path {path}} to fix the problem.",
# repeat the yaml error
"i" = "Did not find the expected keys at line {error_pos} while parsing a {what}."
),
call = call,
parent = e
)
}
)
yaml <- yaml %||% list()
}

yaml
Expand Down
13 changes: 13 additions & 0 deletions tests/testthat/_snaps/package.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,16 @@
! template.bootstrap must be 3 or 5, not 1.
i Edit _pkgdown.yml to fix the problem.

# read_meta() errors gracefully if _pkgdown.yml failed to parse

Code
read_meta(file, check_path = FALSE)
Condition
Error in `read_meta()`:
! The url: field failed to parse.
i Error occured between lines 1 and 9.
i Edit 'assets/bad-yaml/_pkgdown.yml' to fix the problem.
i Did not find the expected keys at line 9 while parsing a block mapping.
Caused by error in `yaml.load()`:
! (assets/bad-yaml/_pkgdown.yml) Parser error: while parsing a block mapping at line 1, column 1 did not find expected key at line 9, column 3

16 changes: 16 additions & 0 deletions tests/testthat/assets/bad-yaml/_pkgdown.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
url: https://pkgdown.r-lib.org

home:
title: Build websites for R packages

authors:
Jay Hesselberth:
href: https://hesselberthlab.org
Maëlle Salmon:
href: https://masalmon.eu
Hadley Wickham:
href: http://hadley.nz
Posit Software, PBC:
href: https://www.posit.co
html: >-
<img src='https://www.tidyverse.org/posit-logo.svg' alt='Posit' width='62' height='16' style="margin-bottom: 3px;" />
11 changes: 11 additions & 0 deletions tests/testthat/test-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,14 @@ test_that("titles don't get autolinked code", {
rd <- rd_text("\\title{\\code{foo()}}", fragment = FALSE)
expect_equal(extract_title(rd), "<code>foo()</code>")
})

test_that("read_meta() errors gracefully if _pkgdown.yml failed to parse", {
file <- test_path("assets", "bad-yaml", "_pkgdown.yml")
expect_snapshot(
error = TRUE,
read_meta(
file,
check_path = FALSE
)
)
})

0 comments on commit e409a55

Please sign in to comment.