15.1 Register a custom language engine (*)

You can register a custom language engine via the method knitr::knit_engines$set(). It accepts a function as its input, e.g.,

knitr::knit_engines$set(foo = function(options) {
  # the source code is in options$code; just do whatever
  # you want with it

This registers the foo engine, and you will be able to use a code chunk that starts with ```{foo}.

The engine function has one argument, options, which is a list of chunk options of the code chunk. You can access the source code of the chunk as a character vector in options$code. For example, for the code chunk:

1 + 1
2 + 2

The code element of options would be a character vector c('1 + 1', '2 + 2').

Language engines do not really have to deal with computer languages, but can process any text in a code chunk. First, we show a simple example of an engine that converts the content of a code chunk to uppercase:

knitr::knit_engines$set(upper = function(options) {
  code <- paste(options$code, collapse = "\n")
  if (options$eval) 
    toupper(code) else code

The key is that we apply the function toupper to the “code,” and return the result as a single character string (by concatenating all lines of code by \n). Note that toupper() is applied only when the chunk option eval = TRUE, otherwise the original string is returned. This shows you how to make use of chunk options like eval inside the engine function. Similarly, you may consider adding if (options$results == 'hide') return() to the function body to hide the output when the chunk option results = 'hide'. Below is an example chunk that uses the upper engine, with its output:

Hello, **knitr** engines!


Next we show an example of an alternative Python engine18 named py. This engine is implemented by simply calling the python command via the R function system2():

knitr::knit_engines$set(py = function(options) {
  code <- paste(options$code, collapse = '\n')
  out  <- system2(
    'python', c('-c', shQuote(code)), stdout = TRUE
  knitr::engine_output(options, code, out)

To fully understand the above engine function, you need to know the following:

  1. Given Python code as a character string (code in the above function), we can execute the code via a command-line call python -c 'code'. That is what system2() does. We collect the (text) output by specifying stdout = TRUE in system2().

  2. You can pass the chunk options, source code, and text output to the function knitr::engine_output() to generate the final output. This function deals with common chunk options like echo = FALSE and results = 'hide', so you do not need to take care of these cases.

A lot of language engines in knitr are defined in this way (i.e., using system2() to execute commands corresponding to languages). If you are curious about the technical details, you may check out the source code of most language engines in the R source code here: https://github.com/yihui/knitr/blob/master/R/engine.R.

Now we can use the new engine py, e.g.,

print(1 + 1)
## 2

You can even override existing language engines in knitr via knitr::knit_engines$set(), if you are sure that your versions are necessary or better than the existing ones. Usually we do not recommend that you do this because it may surprise users who are familiar with existing engines, but we want to make you aware of this possibility anyway.

  1. In practice, you should use the built-in python engine instead, which is based on the reticulate package and supports Python code chunks much better (see Section 15.2).↩︎