Convert the functions of an R script to package::function format

Maybe this question was asked many times before me, unfortunately I haven’t found a solution by searching the forums and internet.

Is there any tool which can help converting the function definitions included in an R script to a format required in packages? I mean the conversion from function() to package::function() format.
I don’t expect a fail-proof solution, just one which is solving the majority of cases and reducing the burden of typing. I assume that the package names can be identified from the library() lines of the original script.

There is a way to do it, but for the life of me I can't remember how. I did it a few months back on a package I am working on and I think there might be some package with an addin that does it.

Regardless, as I understand it, it is often better/easier to use the @importFrom roxygen2 tag at the top of your package files. (One issue I ran into using package::function() everywhere is when I nested multiple functions I frequently ran afoul of having many lines growing much longer than 80 characters, reducing overall readability.)

e.g.:

#' @importFrom knitr purl

Which lets you use purl(x) instead of knitr:purl(x) while using Imports: knitr instead of Depends: knitr in your Descriptions file.

Hadley's R Packages text covers this in a bit more detail.

If/when I remember or find how I automatically prepended the package information I'll update my answer here (as I write this, I think I might have actually rolled my own solution...)

1 Like

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!

2 Likes

it is possible this package could help you with this task

It will look for function calls and try to add the prefix to generate pkg::fun form. Take care as it will modify your script.

This other one offers the same in its pretty_* functions

They both have RStudio addins.

Hope it helps

5 Likes

^ This! :clap: :clap: :clap:

Ha! After taking a look at this, it's exactly what I used when I wanted to add explicit namespaces to the functions in my package. My Google-Fu is usually strong, but for some reason I wasn't finding it this morning... :flushed: :angry: :anguished:

Kudos!

2 Likes

Thank you for your recommendations.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.