1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
#' A condition (message, warning, or error) that occurred while orchestrating a future
#'
#' While \emph{orchestrating} (creating, launching, querying, collection)
#' futures, unexpected run-time errors (and other types of conditions) may
#' occur. Such conditions are coerced to a corresponding FutureCondition
#' class to help distinguish them from conditions that occur due to the
#' \emph{evaluation} of the future.
#'
#' @param message A message.
#'
#' @param call The call stack that led up to the condition.
#'
#' @param future The \link{Future} involved.
#'
#' @return An object of class FutureCondition which inherits from class
#' \link[base:conditions]{condition} and FutureMessage, FutureWarning,
#' and FutureError all inherits from FutureCondition.
#' Moreover, a FutureError inherits from \link[base:conditions]{error},
#' a FutureWarning from \link[base:conditions]{warning}, and
#' a FutureMessage from \link[base:conditions]{message}.
#'
#' @export
#' @keywords internal
FutureCondition <- function(message, call = NULL, future = NULL) {
## Support different types of input
## NOTE: We could turn this into an S3 method. /HB 2016-07-01
if (inherits(message, "Future")) {
future <- message
result <- future$result
if (inherits(result, "FutureResult")) {
cond <- result$condition
} else {
## BACKWARD COMPATIBILITY
cond <- future$value
}
stop_if_not(inherits(cond, "condition"))
message <- conditionMessage(cond)
} else if (inherits(message, "condition")) {
cond <- message
message <- conditionMessage(cond)
}
if (is.null(message)) {
stop("INTERNAL ERROR: Trying to set up a FutureCondition with message = NULL")
}
message <- as.character(message)
if (length(message) != 1L) {
stop("INTERNAL ERROR: Trying to set up a FutureCondition with length(message) != 1L: ", length(message))
}
## Create a condition object
structure(list(message = message, call = call),
class = c("FutureCondition", "condition"),
future = future)
}
#' @importFrom utils tail
#' @export
print.FutureCondition <- function(x, ...) {
NextMethod()
future <- attr(x, "future", exact = TRUE)
## DEPRECATED / BACKWARD COMPATIBILITY: FutureError(..., output)
output <- attr(x, "output", exact = TRUE)
if (!is.null(future) || !is.null(output)) {
cat("\n\nDEBUG: BEGIN TROUBLESHOOTING HELP\n")
if (!is.null(future)) {
cat("Future involved:\n")
print(future)
cat("\n")
}
result <- future$result
if (inherits(result, "FutureResult")) {
cond <- result$condition
} else {
## BACKWARD COMPATIBILITY
cond <- future$value
}
if (inherits(cond, "condition")) {
fcalls <- result$calls
if (is.null(fcalls)) fcalls <- cond$traceback ## BACKWARD COMPATIBILITY
if (!is.null(fcalls)) {
cat("Future call stack:\n")
print(fcalls)
cat("\n")
}
}
## DEPRECATED / BACKWARD COMPATIBILITY: FutureError(..., output)
if (!is.null(output)) {
cat("Captured output:\n")
cat(tail(output, n = 30L), sep = "\n")
cat("\n\n")
}
cat("DEBUG: END TROUBLESHOOTING HELP\n")
}
invisible(x)
} ## print()
#' @rdname FutureCondition
#' @export
FutureMessage <- function(message, call = NULL, future = NULL) {
cond <- FutureCondition(message = message, call = call, future = future)
class(cond) <- c("FutureMessage", "message", class(cond))
cond
}
#' @rdname FutureCondition
#' @export
FutureWarning <- function(message, call = NULL, future = NULL) {
cond <- FutureCondition(message = message, call = call, future = future)
class(cond) <- c("FutureWarning", "warning", class(cond))
cond
}
#' @param output (DEPRECATED - don't use!) only for backward compatibility
#'
#' @rdname FutureCondition
#' @export
FutureError <- function(message, call = NULL, future = NULL, output = NULL) {
cond <- FutureCondition(message = message, call = call, future = future)
## TODO: Remove usage of 'simpleError'. Various packages' tests use this.
class(cond) <- c("FutureError", "simpleError", "error", class(cond))
## DEPREACTED
if (!is.null(output)) {
.Deprecated(msg = "Argument 'output' of FutureError is deprecated")
attr(cond, "output") <- output
}
cond
}
#' @param hint (optional) A string with a suggestion on what might be wrong.
#'
#' @rdname FutureCondition
#' @export
UnexpectedFutureResultError <- function(future, hint = NULL) {
label <- future$label
if (is.null(label)) label <- "<none>"
expr <- hexpr(future$expr)
result <- future$result
result_string <- hpaste(as.character(result))
if (length(result_string) == 0L)
result_string <- ""
else if (nchar(result_string) > 512L)
result_string <- paste(substr(result_string, start = 1L, stop = 512L),
"...")
msg <- sprintf("Unexpected result (of class %s != %s) retrieved for %s future (label = %s, expression = %s): %s",
sQuote(class(result)[1]), sQuote("FutureResult"),
class(future)[1], sQuote(label), sQuote(expr),
result_string)
if (!is.null(hint)) msg <- sprintf("%s. %s", msg, hint)
cond <- FutureError(msg, future = future)
class(cond) <- c("UnexpectedFutureResultError", class(cond))
cond
}
|