Okay, so this was really bugging me...
I still can't recall how I did it, but I wrote a rudimentary solution here (weird we can't upload .R files here... someone should fix that!):
Let this file be demo_no_namespace
,
f <- function() {
Sys.sleep(5)
}
res <- tryCatch(withTimeout(f(), timeout = 0.1),
error = function(e) {
capture.output(e)
})
We have two functions here which will need namespaces: withTimeout()
which comes from R.utils
and capture.output
which comes from utils
.
Then you can use the following script which leverages the lintr
package to parse the source file into its constitute parts. I (or you) could certainly write something which more cleanly parses out the function calls (or you could just assume any character string which has a letter, number, underscore, or period followed by a parentheses is a function name (though you'd probably want to also check for spaces, e.g. sum (x)
, as typos like that happen... Anyway, I didn't feel like writing my own parse code and I had experience with using lintr
for other things so I used it as a jumping off point. Someone else can probably suggest a refinement which only requires base R and is a little cleaner.
require(lintr)
filename <- "demo_no_namespace.R"
prepend_ns <- function(filename) {
se <- lintr::get_source_expressions(filename)$expressions
extract_full_parse <- function(se) {
f_idx <- which(vapply(se,
function(l) {
any(names(l) == "full_parsed_content")
},
logical(1)))
se[[f_idx]][["full_parsed_content"]]
}
fpc <- extract_full_parse(se)
fcall <- c("SYMBOL_FUNCTION_CALL", "SPECIAL")
functions <- fpc[fpc$token %in% fcall, ]$text
ns <- vapply(functions, find, character(1))
ns <- gsub("package:", "", ns[grepl("package:(?!base$)", ns, perl = TRUE)])
ns[seq_along(ns)] <- paste(ns, names(ns), sep = "::")
filetext <- readLines(filename)
for (n in names(ns)) {
filetext <- gsub(paste0("(?<!:)", n),
ns[n],
filetext,
perl = TRUE)
}
writeLines(filetext, con = filename)
}
So, this does is get all the function call elements from a file, looks up the namespace for each function (NOTE: you could and almost certainly will run into issues with function name collisions so be careful applying this globally! Work slowly and check your efforts at each step and always make a full backup if you're not using version control... <sidenote: always use version control!>), ignores functions in the base
package and global environment (you could always add more packages to ignore if you find yourself having the name collision issues mentioned above), makes a character vector of package::function
elements, reads the file into a character vector, then finally replaces every function
with package::function
as long as the function doesn't already have a namespace prepended.
This is not meant to be a perfect or comprehensive solution and it, unfortunately, uses a function from outside of Base R, but it should provide you a launching off point from which you can quickly update files to call functions explicitly from their respective namespaces.
Hope that helps...
Best!