rstudio / rmarkdown

Dynamic Documents for R
https://rmarkdown.rstudio.com
GNU General Public License v3.0
2.83k stars 971 forks source link

Adding `"paged_df"` to a `data.frame` class stops the data frame from printing to console when `{rmarkdown}` is loaded #2562

Open markfairbanks opened 6 days ago

markfairbanks commented 6 days ago

Adding "paged_df" to a data.frame subclass stops the data frame from printing to console when {rmarkdown} is loaded.

This issue occurs when using library(rmarkdown) but also prevents things from working with the {reprex} package.

This is a new issue, not sure what changed. I found out about it due to this issue here. I add "paged_df" as a subclass of a tidytable found in the print method here so paged printing would work in Rmarkdown. Not sure if there is a better/recommended way to allow paged printing of a data frame subclass like a tidytable?

There's not output here when using reprex::reprex()

library(tidytable)

tidytable(x = 1:3, y = 1:3)

Created on 2024-07-01 with reprex v2.1.0

xfun::session_info('rmarkdown')
#> R version 4.3.1 (2023-06-16)
#> Platform: aarch64-apple-darwin20 (64-bit)
#> Running under: macOS Sonoma 14.5
#> 
#> Locale: en_US.UTF-8 / en_US.UTF-8 / en_US.UTF-8 / C / en_US.UTF-8 / en_US.UTF-8
#> 
#> Package version:
#>   base64enc_0.1.3   bslib_0.7.0       cachem_1.1.0      cli_3.6.3        
#>   digest_0.6.36     evaluate_0.24.0   fastmap_1.2.0     fontawesome_0.5.2
#>   fs_1.6.4          glue_1.7.0.9000   graphics_4.3.1    grDevices_4.3.1  
#>   highr_0.11        htmltools_0.5.8.1 jquerylib_0.1.4   jsonlite_1.8.8   
#>   knitr_1.47        lifecycle_1.0.4   memoise_2.0.1     methods_4.3.1    
#>   mime_0.12         R6_2.5.1          rappdirs_0.3.3    rlang_1.1.4      
#>   rmarkdown_2.27    sass_0.4.9        stats_4.3.1       tinytex_0.51     
#>   tools_4.3.1       utils_4.3.1       xfun_0.45         yaml_2.3.8       
#> 
#> Pandoc version: 3.1.1

Created on 2024-07-01 with reprex v2.1.0

-->

Checklist

When filing a bug report, please check the boxes below to confirm that you have provided us with the information we need. Have you:

cderv commented 6 days ago

This is a new issue, not sure what changed.

Do you remember when it was working ? Which version of the tools ?

This is really puzzling to me it was working before...

Let's discuss why this is happening.

I believe you get the issue because your print method is returning invisible(x) and so it is not catch by knitr / evaluate output.

The printing does correctly happens, meaning print.page_df for rmarkdown is called, but not used by evaluation.

This all happens in evaluate handlers https://github.com/yihui/knitr/blob/4a02625e0dce1b88f1c90396790e1eb40401a779/R/utils.R#L863-L881

So using invisible() but also calling print within another print() as you currently do print.tidytable will mess this up I believe. If you remove invisible(x) in print.tidytable() this works too.

diff --git a/R/print.R b/R/print.R
index d6a3d201..3959cb95 100644
--- a/R/print.R
+++ b/R/print.R
@@ -3,7 +3,6 @@
 print.tidytable <- function(x, ..., n = NULL, width = NULL, n_extra = NULL) {
   y <- set_class(x, print_class("tidytable_print"))
   print(y, ..., n = n, width = width, n_extra = n_extra)
-  invisible(x)
 }

 #' @export

I did a few tests, and this is happening at the evaluate level too

> str(evaluate::evaluate(input = "library(rmarkdown)\nlibrary(tidytable)\ntidytable(x = 1:3, y = 1:3)"))
List of 3
 $ :List of 1
  ..$ src: chr "library(rmarkdown)\n"
  ..- attr(*, "class")= chr "source"
 $ :List of 1
  ..$ src: chr "library(tidytable)\n"
  ..- attr(*, "class")= chr "source"
 $ :List of 1
  ..$ src: chr "tidytable(x = 1:3, y = 1:3)"
  ..- attr(*, "class")= chr "source"

See how there is no result captured when rmarkdown is making the print method available

We have one results showing when no rmarkdown is loaded,

> str(evaluate::evaluate(input = "suppressPackageStartupMessages(library(tidytable))\ntidytable(x = 1:3, y = 1:3)"))
List of 3
 $ :List of 1
  ..$ src: chr "suppressPackageStartupMessages(library(tidytable))\n"
  ..- attr(*, "class")= chr "source"
 $ :List of 1
  ..$ src: chr "tidytable(x = 1:3, y = 1:3)"
  ..- attr(*, "class")= chr "source"
 $ : chr "# A tidytable: 3 × 2\n      x     y\n  <int> <int>\n1     1     1\n2     2     2\n3     3     3\n"

So maybe this is specific to how print.paged_df is working - it is supposed to in evaluation results to be sewed using sew.knit_asis method. 🤔

I am still wondering about this nested print() of object, and how evaluate works.

@yihui you know all this better than me - do you confirm this what is happening when the method defined is

print.tidytable <- function(x, ..., n = NULL, width = NULL, n_extra = NULL) {
  y <- set_class(x, print_class("tidytable_print"))
  print(y, ..., n = n, width = width, n_extra = n_extra)
  invisible(x)
}

and that this is how it work ? Or is there an issue somewhere.

I am surprised this was working before as mentioned .... 🤔

@markfairbanks regarding printing in knitr context, you can have custom knit_print() method too. You can read more on knit_print() method at https://cran.r-project.org/web/packages/knitr/vignettes/knit_print.html

Not sure if there is a better/recommended way to allow paged printing of a data frame subclass like a tidytable?

rmarkdown::paged_table() is an exported function which is the one handling options and class for paged_df. Usually this can be used to build a pagetable from a dataframe.

Though adding the class is working ok.

markfairbanks commented 3 days ago

Do you remember when it was working ? Which version of the tools ?

I'm not sure unfortunately. It's been working fine for a few years but broke semi-recently.

You can read more on knit_print() method at

rmarkdown::paged_table() is an exported function which is the one handling options and class for paged_df. Usually this can be used to build a pagetable from a dataframe. Though adding the class is working ok.

I've tried all sorts of things but nothing I do seems to allow me to define the tidytable/data.table subclass that prints a paged table when paged.print = TRUE. I tried permanently attaching "paged_df" as a subclass and it no longer prints normally in console.

Is there a simple way to define "if paged.print = TRUE use paged printing, otherwise use normal print"?

Feel free to close this issue by the way as maybe this is more of a Stack Overflow type question - but if you happen to know what I need to do that'd be great.

cderv commented 3 days ago

I'll think about this. To understand full context, out of curiosity, why return invisible(x) in your print method ?

markfairbanks commented 3 days ago

I can't remember to be honest 😅. I think a contributor recommended it and it was part of the hack to get paged printing to work, but it's possible it was unnecessary to add.

Edit: If I remove invisible(x) it doesn't help with this issue - reprex::reprex() still doesn't print anything

markfairbanks commented 10 hours ago

So I reset printing to get back to basics. Essentially I have a data frame which uses printing from pillar (like tibble does) by using "tbl" as a subclass. I do not define a print.tidytable() method anymore. Printing now works properly in console and reprex::reprex() works.

But now paged printing doesn't work.

library(tidytable)

df <- tidytable(x = 1:3, y = c("a", "a", "b"))

df
#> # A tidytable: 3 × 2
#>       x y    
#>   <int> <chr>
#> 1     1 a    
#> 2     2 a    
#> 3     3 b

class(df)
#> [1] "tidytable"  "tbl"        "data.table" "data.frame"