Skip to content

Commit a94401a

Browse files
Solve the knitr auto-printing problem by registering a method for knit_print (#6589)
* Respect shouldPrint when auto-printing from knitr Implementing a method for the knitr::knit_print generic makes it possible to customise the behaviour without looking up the call stack. The current solution only works on R >= 3.6.0 because that's where delayed S3 registration has been introduced. * Delay S3method(knit_print, data.table) for R < 3.6 Use setHook() to ensure that registerS3method() will be called in the same session if 'knitr' is loaded later. Not needed on R >= 3.6.0 where S3method(knitr::knit_print) will do the right thing by itself. * ws-only style * put setHook() in a branch * Position comment on the same line * Restore the still-required #2369 condition * Regression test for #2369 Avoid breaking it again like in #6589 * NEWS entry * Comment the .onLoad condition Co-authored-by: Michael Chirico <[email protected]> * restore unconditional setHook() --------- Co-authored-by: Michael Chirico <[email protected]>
1 parent 98cf24e commit a94401a

File tree

6 files changed

+49
-17
lines changed

6 files changed

+49
-17
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ if (getRversion() >= "4.0.0") {
104104
# version of R (and that is checked in .onLoad with error if not).
105105
export(.rbind.data.table) # only export in R<4.0.0 where it is still used; R-devel now detects it is missing doc, #5600
106106
}
107+
if (getRversion() >= "3.6.0") S3method(knitr::knit_print, data.table) # else manual delayed registration from the onLoad hook
107108
S3method(dim, data.table)
108109
S3method(dimnames, data.table)
109110
S3method("dimnames<-", data.table)

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ rowwiseDT(
115115
116116
14. Added a `data.frame` method for `format_list_item()` to fix error printing data.tables with columns containing 1-column data.frames, [#6592](https://github.com/Rdatatable/data.table/issues/6592). Thanks to @r2evans for the bug report and fix.
117117
118+
15. The auto-printing suppression in `knitr` documents is now done by implementing a method for `knit_print` instead of looking up the call stack, [#6589](https://github.com/Rdatatable/data.table/pull/6589). Thanks to @jangorecki for the report [#6509](https://github.com/Rdatatable/data.table/issues/6509) and @aitap for the fix.
119+
118120
## NOTES
119121
120122
1. Tests run again when some Suggests packages are missing, [#6411](https://github.com/Rdatatable/data.table/issues/6411). Thanks @aadler for the note and @MichaelChirico for the fix.

R/onLoad.R

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@
6666
lockBinding("rbind.data.frame",baseenv())
6767
}
6868
}
69+
if (session_r_version < "3.6.0") { # corresponds to S3method() directive in NAMESPACE
70+
# no delayed registration support for NAMESPACE; perform it manually
71+
if (isNamespaceLoaded("knitr")) {
72+
registerS3method("knit_print", "data.table", knit_print.data.table, envir = asNamespace("knitr"))
73+
}
74+
setHook(packageEvent("knitr", "onLoad"), function(...) {
75+
registerS3method("knit_print", "data.table", knit_print.data.table, envir = asNamespace("knitr"))
76+
})
77+
}
6978

7079
# Set options for the speed boost in v1.8.0 by avoiding 'default' arg of getOption(,default=)
7180
# In fread and fwrite we have moved back to using getOption's default argument since it is unlikely fread and fread will be called in a loop many times, plus they

R/print.data.table.R

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,8 @@ print.data.table = function(x, topn=getOption("datatable.print.topn"),
3232
SYS = sys.calls()
3333
if (length(SYS) <= 2L || # "> DT" auto-print or "> print(DT)" explicit print (cannot distinguish from R 3.2.0 but that's ok)
3434
( length(SYS) >= 3L && is.symbol(thisSYS <- SYS[[length(SYS)-2L]][[1L]]) &&
35-
as.character(thisSYS) == 'source') || # suppress printing from source(echo = TRUE) calls, #2369
36-
( length(SYS) > 3L && is.symbol(thisSYS <- SYS[[length(SYS)-3L]][[1L]]) &&
37-
as.character(thisSYS) %chin% mimicsAutoPrint ) || # suppress printing from knitr, #6509
38-
# In previous versions of knitr, call stack when auto-printing looked like:
39-
# knit_print -> knit_print.default -> normal_print -> print -> print.data.table
40-
# and we detected and avoided that by checking fourth last call in the stack.
41-
# As of September 2024, the call stack can also look like:
42-
# knit_print.default -> normal_print -> render -> evalq -> evalq -> print -> print.data.table
43-
# so we have to check the 7th last call in the stack too.
44-
# Ideally, we would like to return invisibly from DT[, foo := bar] and have knitr respect that, but a flag in
45-
# .Primitive("[") sets all values returned from [.data.table to visible, hence the need for printing hacks later.
46-
( length(SYS) > 6L && is.symbol(thisSYS <- SYS[[length(SYS)-6L]][[1L]]) &&
47-
as.character(thisSYS) %chin% mimicsAutoPrint ) ) {
35+
as.character(thisSYS) == 'source') ) { # suppress printing from source(echo = TRUE) calls, #2369
4836
return(invisible(x))
49-
# is.symbol() temp fix for #1758.
5037
}
5138
}
5239
if (!is.numeric(nrows)) nrows = 100L
@@ -168,9 +155,6 @@ format.data.table = function(x, ..., justify="none") {
168155
do.call(cbind, lapply(x, format_col, ..., justify=justify))
169156
}
170157

171-
mimicsAutoPrint = c("knit_print.default")
172-
# add maybe repr_text.default. See https://github.com/Rdatatable/data.table/issues/933#issuecomment-220237965
173-
174158
shouldPrint = function(x) {
175159
ret = (identical(.global$print, "") || # to save address() calls and adding lots of address strings to R's global cache
176160
address(x)!=.global$print)
@@ -303,3 +287,9 @@ trunc_cols_message = function(not_printed, abbs, class, col.names){
303287
domain=NA
304288
)
305289
}
290+
291+
# Maybe add a method for repr::repr_text. See https://github.com/Rdatatable/data.table/issues/933#issuecomment-220237965
292+
knit_print.data.table <- function(x, ...) {
293+
if (!shouldPrint(x)) return(invisible(x))
294+
NextMethod()
295+
}

tests/autoprint.R

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,15 @@ DT[1,a:=10L][] # yes. ...[] == oops, forgot print(...)
4343
tryCatch(DT[,foo:=ColumnNameTypo], error=function(e) e$message) # error: not found.
4444
DT # yes
4545
DT # yes
46+
47+
# Regression test for auto-printing suppression in source(), #2369
48+
local({
49+
f = tempfile(fileext = ".R")
50+
on.exit(unlink(f))
51+
writeLines(c(
52+
"library(data.table)",
53+
"DT = data.table(a = 1)",
54+
"DT[,a:=1] # not auto-printed"
55+
), f)
56+
source(f, local = TRUE, echo = TRUE)
57+
})

tests/autoprint.Rout.save

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,24 @@ NULL
136136
1: 10
137137
2: 10
138138
>
139+
> # Regression test for auto-printing suppression in source(), #2369
140+
> local({
141+
+ f = tempfile(fileext = ".R")
142+
+ on.exit(unlink(f))
143+
+ writeLines(c(
144+
+ "library(data.table)",
145+
+ "DT = data.table(a = 1)",
146+
+ "DT[,a:=1] # not auto-printed"
147+
+ ), f)
148+
+ source(f, local = TRUE, echo = TRUE)
149+
+ })
150+
151+
> library(data.table)
152+
153+
> DT = data.table(a = 1)
154+
155+
> DT[, `:=`(a, 1)]
156+
>
139157
> proc.time()
140158
user system elapsed
141159
0.223 0.016 0.231

0 commit comments

Comments
 (0)