# install.packages('pak')
::pak('mjfrigaard/shinypak') pak
16 Debugging apps
During regular development, Posit Workbench’s interactive debugger lets us inspect variables and expressions and execute the code line-by-line. In Shiny functions, the debugger lets us track the execution of reactive expressions and observers, which allows us to unravel reactivity-related issues that are often difficult to diagnose.
Launch app with the shinypak
package:
launch('25.0_debug-error')
16.1 Debugging modules
The contents of your Shiny app-package can quickly become a complicated and intertwined combination of functions: utility, modules, UI, server, etc. I like to display the relationship between the functions with abstract syntax trees:1
For example, we know scatter_plot()
is called from within the scatter plot display module function:
█─mod_scatter_display_server
└─█─scatter_plot
And mod_scatter_display_server()
is called within movies_server()
:
█─movies_server
├─█─mod_scatter_display_server
│ └─█─scatter_plot
└─█─mod_var_input_server
Which is called from inside sap
:
█─launch_app
├─█─movies_ui
│ ├─█─mod_var_input_ui
│ └─█─mod_scatter_display_ui
└─█─movies_server
├─█─mod_scatter_display_server
│ └─█─scatter_plot
└─█─mod_var_input_server
I find these abstract folder trees helpful when I’m debugging or testing Shiny functions. I can use them to try and anticipate the application call stack (especially when I end up with multiple utility functions or nested modules).
We’ll add browser()
and observe()
in the movies_server()
function to capture the behaviors of both modules:
Launch app with the shinypak
package:
launch('25.1_debug-selected_vars')
<- function(input, output, session) {
movies_server
observe({
browser()
<- mod_var_input_server("vars")
selected_vars
mod_scatter_display_server("plot", var_inputs = selected_vars)
})
}
- 1
-
Observer scope
- 2
- Activate debugger
Then we’ll load the package and display the app in the Viewer pane (below the Console):
Ctrl/Cmd + Shift + L
ℹ Loading sap
launch_app(options = list(test.mode = FALSE), run = 'p')
ℹ shinyViewerType set to pane
The application launches, but browser()
pauses the execution of the modules and activates the IDE’s debugger. This allows us to view the objects that are available in movies_server()
before the variables are passed to the graph rendering functions:
In the Source pane, we can see the call to browser()
highlighted (Browse[1]>
tells us the location in the browser()
function).
browser()
was called inside observe()
, the execution will pause, and we can interactively examine values
In the debugger, we want to confirm the returned values from the variable input module, selected_vars
, which requires us to execute the next two lines of code:
1]> n
Browse[2]> n Browse[
selected_vars
Inside movies_server()
:
mod_var_input_server()
collects the following values and returns a reactive list (selected_vars
):
- Three variable names
x
,y
,z
- Graph aesthetics
alpha
andsize
- An optional plot title
plot_title
When we inspect selected_vars
in the debugger console (without parentheses) we see the method (i.e., the reactive list of inputs), and not the actual values:
2]> selected_vars Browse[
reactive({
list(y = input$y, x = input$x,
z = input$z, alpha = input$alpha,
size = input$size,
plot_title = input$plot_title)
})
If we check selected_vars()
(with parentheses) in the debugger, we see this contains the values from the variable input module:
2]> selected_vars() Browse[
$y
[1] "audience_score"
$x
[1] "imdb_rating"
$z
[1] "mpaa_rating"
$alpha
[1] 0.5
$size
[1] 2
$plot_title
[1] ""
These two steps confirm that the UI values are being collected by the variable input module and stored in selected_vars
, so the error must be coming from inside the scatter plot display module.
16.2 Module communication
Launch app with the shinypak
package:
launch('25.2_debug-var_inputs')
We’ll repeat a similar process in mod_scatter_display_server()
, but include the call to observe(browser())
after moduleServer()
. Then we’ll load the package and run the application again:
<- function(id, var_inputs) {
mod_scatter_display_server moduleServer(id, function(input, output, session) {
observe({
browser()
# module code
})
}) }
- 1
-
Wrap
browser()
inobserve()
and place after the call tomoduleServer()
Ctrl/Cmd + Shift + L
ℹ Loading sap
launch_app(options = list(test.mode = FALSE), run = 'p')
Inside the module, we want to confirm var_inputs()
is being created correctly from the var_inputs
object in movies_server()
.
selected_vars
is the input for mod_scatter_display_server()
(as var_inputs
)
var_inputs
is converted to the reactiveinputs
inputs
is passed toscatter_plot()
insiderenderPlot()
2]> var_inputs() Browse[
$y
[1] "audience_score"
$x
[1] "imdb_rating"
$z
[1] "mpaa_rating"
$alpha
[1] 0.5
$size
[1] 2
$plot_title
[1] ""
16.2.1 Verify variable inputs
Inside the scatter plot display module, the var_inputs
argument is used to create a reactive input()
object for the graph created by scatter_plot()
:
<- reactive({
inputs <- tools::toTitleCase(var_inputs()$plot_title)
plot_title list(
x = var_inputs()$x,
y = var_inputs()$y,
z = var_inputs()$z,
alpha = var_inputs()$alpha,
size = var_inputs()$size,
plot_title = plot_title
) })
- 1
-
Variable inputs (from
selected_vars
)
- 2
-
inputs()
forscatter_plot()
Now that we’ve confirmed var_inputs()
has been created, we’ll verify the values are passed correctly from var_inputs()
to inputs()
(which is used to create the scatter plot).
To do this, we’ll progress through the module function (using n
in the debugger console or by clicking Next) until the inputs()
reactive has been created,
n
in the debugger or click Next to progress through the function
2]> inputs() Browse[
$x
[1] "imdb_rating"
$y
[1] "audience_score"
$z
[1] "mpaa_rating"
$alpha
[1] 0.5
$size
[1] 2
$plot_title
[1] ""
These two steps have shown us 1) the modules are communicating properly, and 2) the scatter plot display module contains the list of reactive values needed to render the graph.
Create abstract syntax trees with the
ast()
function from thelobstr
package.↩︎