diff --git a/DESCRIPTION b/DESCRIPTION
index 9a7f5c78..57db7519 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -37,6 +37,7 @@ Remotes: plotly/dash-html-components@17da1f4,
License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
+KeepSource: true
RoxygenNote: 6.1.1
Roxygen: list(markdown = TRUE)
URL: https://github.com/plotly/dashR
diff --git a/R/dash.R b/R/dash.R
index 00ad4c6f..1b074b1c 100644
--- a/R/dash.R
+++ b/R/dash.R
@@ -525,18 +525,18 @@ Dash <- R6::R6Class(
private$updateReloadHash()
private$index()
- viewer <- getOption("viewer")
+ use_viewer <- !(is.null(getOption("viewer"))) && (dynGet("use_viewer") == TRUE)
host <- dynGet("host")
port <- dynGet("port")
app_url <- paste0("http://", host, ":", port)
- if (!is.null(viewer) && host %in% c("localhost", "127.0.0.1"))
+ if (use_viewer && host %in% c("localhost", "127.0.0.1"))
rstudioapi::viewer(app_url)
- else {
+ else if (use_viewer) {
warning("RStudio viewer not supported; ensure that host is 'localhost' or '127.0.0.1' and that you are using RStudio to run your app. Opening default browser...")
utils::browseURL(app_url)
- }
+ }
})
# user-facing fields
@@ -611,7 +611,7 @@ Dash <- R6::R6Class(
port = Sys.getenv('DASH_PORT', 8050),
block = TRUE,
showcase = FALSE,
- viewer = FALSE,
+ use_viewer = FALSE,
dev_tools_prune_errors = TRUE,
debug = FALSE,
dev_tools_ui = NULL,
diff --git a/R/utils.R b/R/utils.R
index cafabb1b..99db66d1 100644
--- a/R/utils.R
+++ b/R/utils.R
@@ -684,7 +684,7 @@ encode_plotly <- function(layout_objs) {
# so that it is pretty printed to stderr()
printCallStack <- function(call_stack, header=TRUE) {
if (header) {
- write(crayon::yellow$bold(" ### DashR Traceback (most recent/innermost call last) ###"), stderr())
+ write(crayon::yellow$bold(" ### Dash for R Traceback (most recent/innermost call last) ###"), stderr())
}
write(
crayon::white(
@@ -694,7 +694,9 @@ printCallStack <- function(call_stack, header=TRUE) {
call_stack
),
": ",
- call_stack
+ call_stack,
+ " ",
+ lapply(call_stack, attr, "flineref")
)
),
stderr()
@@ -707,7 +709,7 @@ stackTraceToHTML <- function(call_stack,
if(is.null(call_stack)) {
return(NULL)
}
- header <- " ### DashR Traceback (most recent/innermost call last) ###"
+ header <- " ### Dash for R Traceback (most recent/innermost call last) ###"
formattedStack <- c(paste0(
" ",
@@ -716,6 +718,8 @@ stackTraceToHTML <- function(call_stack,
),
": ",
call_stack,
+ " ",
+ lapply(call_stack, attr, "lineref"),
collapse="
"
)
)
@@ -761,7 +765,19 @@ getStackTrace <- function(expr, debug = FALSE, prune_errors = TRUE) {
}
functionsAsList <- lapply(calls, function(completeCall) {
- currentCall <- completeCall[[1]]
+ # avoid attempting to cast closures as strings, which will fail
+ # some calls in the stack are symbol (name) objects, while others
+ # are calls, which must be deparsed; the first element in the vector
+ # should be the function signature
+ if (is.name(completeCall[[1]]))
+ currentCall <- as.character(completeCall[[1]])
+ else if (is.call(completeCall[[1]]))
+ currentCall <- deparse(completeCall)[1]
+ else
+ currentCall <- completeCall[[1]]
+
+ attr(currentCall, "flineref") <- getLineWithError(completeCall, formatted=TRUE)
+ attr(currentCall, "lineref") <- getLineWithError(completeCall, formatted=FALSE)
if (is.function(currentCall) & !is.primitive(currentCall)) {
constructedCall <- paste0(" function(",
@@ -813,18 +829,16 @@ getStackTrace <- function(expr, debug = FALSE, prune_errors = TRUE) {
functionsAsList <- removeHandlers(functionsAsList)
}
- # use deparse in case the call throwing the error is a symbol,
- # since this cannot be "printed" without deparsing the call
warning(call. = FALSE, immediate. = TRUE, sprintf("Execution error in %s: %s",
- deparse(functionsAsList[[length(functionsAsList)]]),
+ functionsAsList[[length(functionsAsList)]],
conditionMessage(e)))
stack_message <- stackTraceToHTML(functionsAsList,
- deparse(functionsAsList[[length(functionsAsList)]]),
+ functionsAsList[[length(functionsAsList)]],
conditionMessage(e))
assign("stack_message", value=stack_message,
- envir=sys.frame(1)$private)
+ envir=sys.frame(countEnclosingFrames("private"))$private)
printCallStack(functionsAsList)
}
@@ -836,8 +850,22 @@ getStackTrace <- function(expr, debug = FALSE, prune_errors = TRUE) {
)
} else {
evalq(expr)
- }
}
+}
+
+getLineWithError <- function(currentCall, formatted=TRUE) {
+ srcref <- attr(currentCall, "srcref", exact = TRUE)
+ if (!is.null(srcref) & !(getAppPath()==FALSE)) {
+ # filename
+ srcfile <- attr(srcref, "srcfile", exact = TRUE)
+ # line number
+ context <- sprintf("-- %s, Line %s", srcfile$filename, srcref[[1]])
+ if (formatted)
+ context <- crayon::yellow$italic(context)
+ return(context)
+ } else
+ ""
+}
# This helper function drops error
# handling functions from the call
@@ -938,12 +966,12 @@ getAppPath <- function() {
cmd_args <- commandArgs(trailingOnly = FALSE)
file_argument <- "--file="
matched_arg <- grep(file_argument, cmd_args)
-
+
# if app is instantiated via Rscript, cmd_args should contain path
if (length(matched_arg) > 0) {
# Rscript
return(normalizePath(sub(file_argument, "", cmd_args[matched_arg])))
- }
+ }
# if app is instantiated via source(), sys.frames should contain path
else if (!is.null(sys.frames()[[1]]$ofile)) {
return(normalizePath(sys.frames()[[1]]$ofile))
@@ -969,25 +997,33 @@ setModtimeAsAttr <- function(path) {
}
}
+countEnclosingFrames <- function(object) {
+ for (i in 1:sys.nframe()) {
+ objs <- ls(envir=sys.frame(i))
+ if (object %in% objs)
+ return(i)
+ }
+}
+
changedAssets <- function(before, after) {
# identify files that used to exist in the asset map,
- # but which have been removed
+ # but which have been removed
deletedElements <- before[which(is.na(match(before, after)))]
-
+
# identify files which were added since the last refresh
addedElements <- after[which(is.na(match(after, before)))]
-
+
# identify any items that have been updated since the last
# refresh based on modification time attributes set in map
- #
+ #
# in R, attributes are discarded when subsetting, so it's
# necessary to subset the attributes being compared instead.
# here we only compare objects which overlap
before_modtimes <-attributes(before)$modtime[before %in% after]
after_modtimes <- attributes(after)$modtime[after %in% before]
-
+
changedElements <- after[which(after_modtimes > before_modtimes)]
-
+
if (length(deletedElements) == 0) {
deletedElements <- NULL
}
@@ -1005,13 +1041,13 @@ changedAssets <- function(before, after) {
)
}
-dashLogger <- function(event = NULL,
- message = NULL,
- request = NULL,
- time = Sys.time(),
+dashLogger <- function(event = NULL,
+ message = NULL,
+ request = NULL,
+ time = Sys.time(),
...) {
orange <- crayon::make_style("orange")
-
+
# dashLogger is being called from within fiery, and the Fire() object generator
# is called from a private method within the Dash() R6 class; this makes
# accessing variables set within Dash's private fields somewhat complicated
@@ -1019,22 +1055,22 @@ dashLogger <- function(event = NULL,
# the following line retrieves the value of the silence_route_logging parameter,
# which is nearly 20 frames up the stack; if it's not found, we'll assume FALSE
silence_routes_logging <- dynGet("self", ifnotfound = FALSE)$config$silence_routes_logging
-
+
if (!is.null(event)) {
msg <- sprintf("%s: %s", event, message)
-
- msg <- switch(event, error = crayon::red(msg), warning = crayon::yellow(msg),
+
+ msg <- switch(event, error = crayon::red(msg), warning = crayon::yellow(msg),
message = crayon::blue(msg), msg)
-
+
# assign the status group for color coding
if (event == "request") {
- status_group <- as.integer(cut(request$respond()$status,
+ status_group <- as.integer(cut(request$respond()$status,
breaks = c(100, 200, 300, 400, 500, 600), right = FALSE))
-
- msg <- switch(status_group, crayon::blue$bold(msg), crayon::green$bold(msg),
+
+ msg <- switch(status_group, crayon::blue$bold(msg), crayon::green$bold(msg),
crayon::cyan$bold(msg), orange$bold(msg), crayon::red$bold(msg))
}
-
+
# if log messages are suppressed, report only server stop/start messages, errors, and warnings
# otherwise, print everything to console
if (event %in% c("start", "stop", "error", "warning") || !(silence_routes_logging)) {
@@ -1043,7 +1079,7 @@ dashLogger <- function(event = NULL,
}
}
}
-
+
clientsideFunction <- function(namespace, function_name) {
return(list(namespace=namespace, function_name=function_name))
}