I am using cli::cli_abort
to handle errors.
As context for an error message, I am trying to print the contents of a matrix. I can achieve this via capture.output
, though when the message is processed by cli::cli_abort
the whitespace gets collapsed and you can no longer see the matrix columns easily.
The following reproducible example (hopefully!) illustrates:
do_something_with_matrix <- function(x) {
# provide a way to format the matrix as a string vector
format_mtx <- function(x, method = c("collapse", "preserve")) {
method <- match.arg(method)
mtx_lines <- if (method == "collapse") {
c("Matrix values [Note: spaces get collapsed, hard-to-read columns]:",
capture.output(x))
} else {
out_txt <- gsub(" ", "_", capture.output(x))
c("Matrix values [Note: spaces replaced with '_' to prevent collapsing]:",
out_txt)
}
names(mtx_lines) <- rep("i", length(mtx_lines))
mtx_lines
}
# check the input
if (isTRUE(any(x < 0))) {
# display the matrix, replacing spaces with '_' to stop them collapsing
cli::cli_inform(c(
format_mtx(x, "preserve")
))
# abort since input is invalid
cli::cli_abort(c(
"{.var x} must contain only positive values",
"x" = "You've supplied a matrix with one or more negative values.",
# display the matrix with spaces as-is (they will be collapsed)
format_mtx(x)
))
}
# do <whatever> with x [...]
}
# --- --- ---
# Example usage
# create a matrix
mtx_data <- matrix(c( 6.62, 3.33, 1.94,
-4.1, 3.52, 0.5,
1.94, 0.5, 1.95),
3, 3)
do_something_with_matrix(mtx_data)
# ℹ Matrix values [Note: spaces replaced with '_' to prevent collapsing]:
# ℹ _____[,1]__[,2]_[,3]
# ℹ [1,]_6.62_-4.10_1.94
# ℹ [2,]_3.33__3.52_0.50
# ℹ [3,]_1.94__0.50_1.95
# Error in `do_something_with_matrix()`:
# ! `x` must contain only positive values
# ✖ You've supplied a matrix with one or more negative values.
# ℹ Matrix values [Note: spaces get collapsed, hard-to-read columns]:
# ℹ [,1] [,2] [,3]
# ℹ [1,] 6.62 -4.10 1.94
# ℹ [2,] 3.33 3.52 0.50
# ℹ [3,] 1.94 0.50 1.95
# Run `rlang::last_error()` to see where the error occurred.
I have searched for a way to do this but not yet managed to come up with anything. I wondered if the use_cli_format
argument to rlang::abort
might help, but looking at the code for cli::cli_abort
shows that this arg is set to TRUE so I can't change it (anyway I'm not sure if that could help):
> cli::cli_abort
# function (message, ..., .envir = parent.frame(), call = .envir)
# {
# message[] <- vcapply(message, format_inline, .envir = .envir)
# rlang::abort(message, ..., call = call, use_cli_format = TRUE)
# }
# <bytecode: 0x5562464367c8>
# <environment: namespace:cli>
Is there a way to preserve the spaces/columns in the printed matrix values? Or would there be an altogether better way to display the user-supplied matrix in the event of an error?
Since posting this question I realised that a way to achieve what I wanted to do would be to simply call rlang::abort
directly.
I have tried to check if there is any recommendation against doing this when using the cli
package but if so I have not yet found any info to indicate it. So I am posting a possible solution to my original problem, using rlang::abort and setting use_cli_format = FALSE
:
library(rlang)
library(cli)
do_something_with_matrix <- function(x) {
# Format the matrix as a named string vector where the names indicate info
# items for the abort message
format_mtx <- function(x) {
mtx_lines <- capture.output(print(x))
names(mtx_lines) <- rep("i", length(mtx_lines))
mtx_lines
}
# Check the input
if (isTRUE(any(x < 0))) {
# Abort since input is invalid
rlang::abort(c(
cli::cli_fmt(
cli::cli_text("Matrix {.var x} must contain only positive values")
),
"x" = "You've supplied a matrix with one or more negative values.",
"i" = "Your matrix:",
format_mtx(x)
), use_cli_format = FALSE)
}
# Do <whatever> with x [...]
}
# --- --- ---
# Example usage
# Create a matrix containing an invalid value
mtx_data <- matrix(c( 6.62, 3.33, 1.94,
-4.1, 3.52, 0.5,
1.94, 0.5, 1.95),
3, 3)
do_something_with_matrix(mtx_data)
# => Produces output with readable matrix columns:
#
# ! Matrix `x` must contain only positive values
# ✖ You've supplied a matrix with one or more negative values.
# ℹ Your matrix:
# ℹ [,1] [,2] [,3]
# ℹ [1,] 6.62 -4.10 1.94
# ℹ [2,] 3.33 3.52 0.50
# ℹ [3,] 1.94 0.50 1.95
# Run `rlang::last_trace()` to see where the error occurred.