Detecting whether shiny runs the R code
Asked Answered
I

3

7

I would like to run R code in two locations, in an Rnw file and as an interactive shiny R markdown document.

Thus, what I need, since interactive shiny components do not work in Rnw files, is a code snippet in R that detects whether to load the interactive code or not.

This seems to work, but it feels like a quick hack:

if (exists("input")) { # input is provided by shiny
    # interactive components like renderPlot for shiny
} else {
    # non-interactive code for Rnw file
}

Is there a stable solution or something like a global variable that I can access that says whether shiny is running at the moment? Or should I check whether the shiny package is loaded?

What's safest?

Irresistible answered 27/9, 2015 at 10:31 Comment(8)
Not sure if I'm understanding you correctly: since you are talking about two separate files, putting the interactive components into the .Rmd and the static components into the .Rnw is not an option?Berns
Providing some sample code of what exactly you have been trying would help. --> reproducible exampleBerns
Well, your suggestion is possible. However, I would like to keep code at one place that belongs together and rather have a default settings that get overridden by an interactive part, which is only triggered in shiny.Irresistible
What kind of sample code would you like to see? I tried to formulate the abstract problem, but I could write an example if anything is unclear :-)Irresistible
Would if (interactive()) do the job? It checks if you are in an interactive session or not. If not (e.g. you are in a document) it would return your non-interactive code.Berns
But it clearly does not indicate, if you are inside of a .Rmd with runtime: shiny. Bummer.Berns
About reproducible code: if you could share code for both kinds of document you would like to make, it would make experimenting easier. (for cases you don't already know the answer)Berns
I updated my formerly deleted answer to reflect my opinion about the topic.Berns
I
3

This information is provided directly via Shiny’s isRunning function.


Outdated answer below:

You can do the following:

shiny_running = function () {
    # Look for `runApp` call somewhere in the call stack.
    frames = sys.frames()
    calls = lapply(sys.calls(), `[[`, 1)
    call_name = function (call)
        if (is.function(call)) '<closure>' else deparse(call)
    call_names = vapply(calls, call_name, character(1))

    target_call = grep('^runApp$', call_names)

    if (length(target_call) == 0)
        return(FALSE)
    
    # Found a function called `runApp`, verify that it’s Shiny’s.
    target_frame = frames[[target_call]]
    namespace_frame = parent.env(target_frame)
    isNamespace(namespace_frame) && environmentName(namespace_frame) == 'shiny'
}

Now you can simply use shiny_running() in code and get a logical value back that indicates whether the document is run as a Shiny app.

This is probably (close to) the best way, according to a discussion on Shiny the mailing list — but do note the caveats mentioned in the discussion.

Adapted from code in the “modules” package.

Alternatively, the following works. It may be better suited for the Shiny/RMarkdown use-case, but requires the existence of the YAML front matter: It works by reading the runtime value from that.

shiny_running = function ()
    identical(rmarkdown::metadata$runtime, 'shiny')
Icono answered 28/9, 2015 at 16:47 Comment(0)
D
15

There is now a function shiny::isRunning().

Dexterdexterity answered 19/2, 2019 at 3:34 Comment(0)
I
3

This information is provided directly via Shiny’s isRunning function.


Outdated answer below:

You can do the following:

shiny_running = function () {
    # Look for `runApp` call somewhere in the call stack.
    frames = sys.frames()
    calls = lapply(sys.calls(), `[[`, 1)
    call_name = function (call)
        if (is.function(call)) '<closure>' else deparse(call)
    call_names = vapply(calls, call_name, character(1))

    target_call = grep('^runApp$', call_names)

    if (length(target_call) == 0)
        return(FALSE)
    
    # Found a function called `runApp`, verify that it’s Shiny’s.
    target_frame = frames[[target_call]]
    namespace_frame = parent.env(target_frame)
    isNamespace(namespace_frame) && environmentName(namespace_frame) == 'shiny'
}

Now you can simply use shiny_running() in code and get a logical value back that indicates whether the document is run as a Shiny app.

This is probably (close to) the best way, according to a discussion on Shiny the mailing list — but do note the caveats mentioned in the discussion.

Adapted from code in the “modules” package.

Alternatively, the following works. It may be better suited for the Shiny/RMarkdown use-case, but requires the existence of the YAML front matter: It works by reading the runtime value from that.

shiny_running = function ()
    identical(rmarkdown::metadata$runtime, 'shiny')
Icono answered 28/9, 2015 at 16:47 Comment(0)
B
2

Update: After Konrad Rudolphs comment I rethought my approach. My original answer can be found down below.

My approach is different from Konrad Rudolphs and maybe different to the OPs initial thoughts. The code speaks for itself:

if (identical(rmarkdown::metadata$runtime, "shiny")) {
  "shinyApp" 
} else {
  "static part"
}

I would not run this code from inside an app but use it as a wrapper around the app. If the code resides within a .Rmd with runtime: shiny in the YAML front matter it will start the app, if not, it will show the static part.

I guess that should do what you wanted, and be as stable as it could get.


My original thought would have been to hard code whether or not you were in an interactive document:

document_is_interactive <- TRUE

if (document_is_interactive) {
    # interactive components like renderPlot for shiny
} else {
    # non-interactive code for Rnw file
}

Although possible, this could lead to problems and would therefore be less stable than other the approach with rmarkdown::metadata$runtime.

Berns answered 27/9, 2015 at 21:30 Comment(7)
“I doubt that mimicking its behavior is more stable than just hard-coding the result” — Actually, why do you think that? Of course it’s more stable, it trivially prevents accidents where the user forgot to update the variable manually. In fact, I think that’s a viable alternative to my answer (which is more general and also works in non-RMarkdown code).Icono
@KonradRudolph maybe it is more stable if you do it correctly. ;) I will make an attempt and update my answer.Berns
Fair enough. See my updated answer. It’s a bit disappointing that Knitr doesn’t give the user access to the YAML front-matter configuration directly but parsing it manually is far from hard, as the code shows.Icono
I found a way without the need to parse the file, see my updated answer.Berns
So have I :p — Actually, I just see that your answer has the same idea, but beware: == will yield the wrong result when the header is not set. Your code tries to circumvent this but is inconsistent when runtime is set, but not to shiny.Icono
I had already read your updated answer and remembered that == is actually not good.Berns
You have the edge. :) But thanks for the comments – I learned quite a bit.Berns

© 2022 - 2024 — McMap. All rights reserved.