Search code examples
r-markdownknitrquarto

Is there a way to detect the chunk language when setting knitr chunk options?


Summary - is there any way for the code run when setting knitr chunk options to know the language of the contained chunk?

Detail:

I have a knitr (in fact Quarto) book in which I have both R and Python chunks, where the Python chunks run via Reticulate.

In fact the R chunks are for an R edition of the book, and the Python chunks for a Python edition.

I'd like to tell knitr to eval=FALSE, echo=FALSE for the R chunks, when building the Python edition, and vice versa for the Python edition.

Is there a way for me to detect the chunk language at chunk option evaluation time, in order to do something like this?

# My document

```{r, setup, include=FALSE}
is_py_ed <- TRUE  # Worked out elsewhere.
is_r_ed <- FALSE  # Worked out elsewhere. 
knitr::opts_chunk$set(
  eval=(this_is_python_chunk & is_py_ed) | (this_is_r_chunk & is_r_ed),
  echo=(this_is_python_chunk & is_py_ed) | (this_is_r_chunk & is_r_ed)
)
```

```{python}
# This chunk gets dropped in R edition, kept in Python edition.
```

```{r}
# This chunk gets dropped in Python edition, kept in R edition.
```

Solution

  • You can do this using an option hook and get the current language engine used by knitr to eval and execute the code of the current code chunk using options$engine (where options is a list of chunk options passed to the hook functions).

    ---
    title: "Chunk language"
    format: html
    ---
    
    # My document
    
    ```{r, setup, include=FALSE}
    is_py_ed <- TRUE  # Worked out elsewhere.
    is_r_ed <- FALSE  # Worked out elsewhere.
    
    knitr::opts_chunk$set(echo = "", eval = "")
    
    knitr::opts_hooks$set(
      echo = function(options) {
        if(!is.logical(options$echo)) {
          options$echo <- (options$engine == "python" & is_py_ed) | (options$engine == "R" & is_r_ed) 
        }
        return(options)
      },
      eval = function(options) {
        if(!is.logical(options$eval)) {
          options$eval <- (options$engine == "python" & is_py_ed) | (options$engine == "R" & is_r_ed)
        }
        return(options)
      }
    )
    
    ```
    
    ```{python}
    print("Hello from python")
    ```
    
    ```{r}
    print("Hello from R")
    ```
    
    ```{r, echo=TRUE, eval=TRUE}
    print("Hello from R (evaluated in python edition)")
    ```
    

    Note: As a workaround to enable overriding the chunk option echo and eval locally, I have set them to empty strings globally. Then the option hooks will change the values accordingly if they are not set in the chunk header.


    code chunk execution based on chunk language