Deferring a function call when knitr ends

Is there a way to execute a function (without calling it explicitly) just before knitr finishes running? Something like on.exit() that I could run when rendering an R markdown or Quarto file. I was hoping to do that with withr::defer() but I realise that it needs to be followed by an explicit withr::deferred_run() call when used in the global environment.

For context, I'd like to call a function many times in an R markdown document. The function modifies an external file. Instead of reading and writing the file in every call, I'd like to read the file when knitr starts, add its content in a cache, modify it there and write the file when knitr ends. The function is in a local package, I was thinking to initialise the reading of the file and defer the writing in an .onLoad() call.

What is you workflow ? How are you calling the rendering ?

Asking because I believe what you want to do is equivalent to having a startup chunk that reads the file, and a last chunk that will do the writing. Everything in between is in the same knitting process. Doing something at the start of knitting = first chunk, and something last = last chunk.

If this does not work for you, you could define a custom format using output_format() in which you can find an on_exit field - but this will be later than after knit - the callback function would trigger when rmarkdown::render() exits. output_format_dependency() is a way to add that in a function from a chunk, instead of an output format. Quite advanced though.

Which function are you referring to here ?

I'm writing a manuscript and have quite a few R packages to cite. Instead of copy pasting the bibtex citation to a .bib file (or Zotero), adding the reference manually and calling packageVersion() every time, I created a function to automate the process. The function gets the package citation, adds it to the .bib file and generates a preformatted string with the package version and reference.

I find it quite convenient and since I don't know any tool doing this, I was thinking to make a tiny package out of it. I'd like to optimise the function first though. Instead of reading and writing the .bib file every time the function is called, I was thinking to store the content of the file when loading the package, modify the file in place when calling the function and writing the file at the end of the knitting (the writing must not happen later otherwise the references won't work because they wouldn't be in the .bib file yet).

The point is to simplify things so I don't want to have to write chunks at the beginning and end of the document.

I've just tried withr::defer() again and managed to make it work in the end.

.onLoad <- function(...) {
  if (isTRUE(getOption('knitr.in.progress'))) {
    withr::defer(fun())
  }
}

I think my problem was .onLoad() not being initialized properly in my tests.

1 Like

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.