Skip to content

Commit 17a7c3e

Browse files
Suppress autoprint during := assignment on subclasses of data.table (#6631)
* 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 * Use identical(,print) to check for autoprint * Add tests, including updating broken test * add comment * test withAutoprint() behavior too * NEWS entry * blank line in correct place? --------- Co-authored-by: Ivan K <[email protected]>
1 parent 96e89fa commit 17a7c3e

File tree

4 files changed

+80
-16
lines changed

4 files changed

+80
-16
lines changed

NEWS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ 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.
118+
15. Auto-printing gets some substantial improvements
119+
- 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). The old way was fragile and wound up broken by some implementation changes in {knitr}. Thanks to @jangorecki for the report [#6509](https://github.com/Rdatatable/data.table/issues/6509) and @aitap for the fix.
120+
- `print()` methods for S3 subclasses of data.table (e.g. an object of class `c("my.table", "data.table", "data.frame")`) no longer print where plain data.tables wouldn't, e.g. `myDT[, y := 2]`, [#3029](https://github.com/Rdatatable/data.table/issues/3029). The improved detection of auto-printing scenarios has the added benefit of _allowing_ print in highly explicit statements like `print(DT[, y := 2])`, obviating our recommendation since v1.9.6 to append `[]` to signal "please print me".
119121

120122
16. Joins of `integer64` and `double` columns succeed when the `double` column has lossless `integer64` representation, [#4167](https://github.com/Rdatatable/data.table/issues/4167) and [#6625](https://github.com/Rdatatable/data.table/issues/6625). Previously, this only worked when the double column had lossless _32-bit_ integer representation. Thanks @MichaelChirico for the reports and fix.
121123

R/print.data.table.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ print.data.table = function(x, topn=getOption("datatable.print.topn"),
3030
# Other options investigated (could revisit): Cstack_info(), .Last.value gets set first before autoprint, history(), sys.status(),
3131
# topenv(), inspecting next statement in caller, using clock() at C level to timeout suppression after some number of cycles
3232
SYS = sys.calls()
33-
if (length(SYS) <= 2L || # "> DT" auto-print or "> print(DT)" explicit print (cannot distinguish from R 3.2.0 but that's ok)
33+
if (identical(SYS[[1L]][[1L]], print) || # this is what auto-print looks like, i.e. '> DT' and '> DT[, a:=b]' in the terminal; see #3029.
3434
( length(SYS) >= 3L && is.symbol(thisSYS <- SYS[[length(SYS)-2L]][[1L]]) &&
3535
as.character(thisSYS) == 'source') ) { # suppress printing from source(echo = TRUE) calls, #2369
3636
return(invisible(x))

tests/autoprint.R

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ DT[FALSE,a:=3L] # no
1010
DT[a==4L,a:=5L] # no
1111
DT[a %in% 4:8, a:=5L] # no
1212
DT # yes
13-
print(DT[2,a:=4L]) # no
13+
print(DT[2,a:=4L]) # yes, as of #6631
1414
print(DT) # yes
1515
if (TRUE) DT[2,a:=5L] # no. used to print before v1.9.5
1616
if (TRUE) if (TRUE) DT[2,a:=6L] # no. used to print before v1.9.5
@@ -20,7 +20,7 @@ DT # yes. 2nd time needed, or solutions below
2020
(function(){DT[2,a:=5L];NULL})() # print NULL
2121
DT[] # yes. guaranteed print
2222
(function(){DT[2,a:=5L];NULL})() # print NULL
23-
print(DT) # no. only DT[] is guaranteed print from v1.9.6 and R 3.2.0
23+
print(DT) # yes. restored in #6631 behavior that had changed in 1.9.6.
2424
(function(){DT[2,a:=5L][];NULL})() # print NULL
2525
DT # yes. i) function needs to add [] after last one, so that "DT" alone is guaranteed anyway
2626
(function(){DT[2,a:=5L];DT[];NULL})() # print NULL
@@ -29,9 +29,9 @@ DT2 = data.table(b=3:4) # no
2929
(function(){DT[2,a:=6L];DT2[1,b:=7L];NULL})()
3030
DT # yes. last := was on DT2 not DT
3131
{DT[2,a:=6L];invisible()} # no
32-
print(DT) # no
32+
print(DT) # yes
3333
(function(){print(DT[2,a:=7L]);print(DT);invisible()})() # yes*2
34-
{print(DT[2,a:=8L]);print(DT);invisible()} # yes*1 Not within function so as at prompt
34+
{print(DT[2,a:=8L]);print(DT);invisible()} # yes*2 as at prompt, again as of #6631
3535
DT[1][,a:=9L] # no (was too tricky to detect that DT[1] is a new object). Simple rule is that := always doesn't print
3636
DT[2,a:=10L][1] # yes
3737
DT[1,a:=10L][1,a:=10L] # no
@@ -51,7 +51,25 @@ local({
5151
writeLines(c(
5252
"library(data.table)",
5353
"DT = data.table(a = 1)",
54-
"DT[,a:=1] # not auto-printed"
54+
"DT[,a:=1]" # no
5555
), f)
5656
source(f, local = TRUE, echo = TRUE)
5757
})
58+
59+
# child class of data.table doesn't induce unintended print, #3029
60+
dt <- data.table(x = 1)
61+
class(dt) <- c("foo", "data.table", "data.frame")
62+
print.foo <- function(x, ...) {
63+
NextMethod("print")
64+
}
65+
dt[, y := 1] # no
66+
67+
# withAutoprint() testing (since R3.4.0)
68+
if (!exists("withAutoprint", baseenv())) {
69+
q("no")
70+
}
71+
if (TRUE) withAutoprint({
72+
DT # yes
73+
DT[1L, 1L] # yes
74+
DT[2L, a := 10L] # no
75+
})

tests/autoprint.Rout.save

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

2-
R version 4.3.2 (2023-10-31) -- "Eye Holes"
3-
Copyright (C) 2023 The R Foundation for Statistical Computing
4-
Platform: x86_64-pc-linux-gnu (64-bit)
2+
R Under development (unstable) (2024-12-01 r87412) -- "Unsuffered Consequences"
3+
Copyright (C) 2024 The R Foundation for Statistical Computing
4+
Platform: x86_64-pc-linux-gnu
55

66
R is free software and comes with ABSOLUTELY NO WARRANTY.
77
You are welcome to redistribute it under certain conditions.
@@ -44,7 +44,11 @@ Index: <a>
4444
<int>
4545
1: 1
4646
2: 3
47-
> print(DT[2,a:=4L]) # no
47+
> print(DT[2,a:=4L]) # yes, as of #6631
48+
a
49+
<int>
50+
1: 1
51+
2: 4
4852
> print(DT) # yes
4953
a
5054
<int>
@@ -69,7 +73,11 @@ NULL
6973
2: 5
7074
> (function(){DT[2,a:=5L];NULL})() # print NULL
7175
NULL
72-
> print(DT) # no. only DT[] is guaranteed print from v1.9.6 and R 3.2.0
76+
> print(DT) # yes. restored in #6631 behavior that had changed in 1.9.6.
77+
a
78+
<int>
79+
1: 1
80+
2: 5
7381
> (function(){DT[2,a:=5L][];NULL})() # print NULL
7482
NULL
7583
> DT # yes. i) function needs to add [] after last one, so that "DT" alone is guaranteed anyway
@@ -93,7 +101,11 @@ NULL
93101
1: 1
94102
2: 6
95103
> {DT[2,a:=6L];invisible()} # no
96-
> print(DT) # no
104+
> print(DT) # yes
105+
a
106+
<int>
107+
1: 1
108+
2: 6
97109
> (function(){print(DT[2,a:=7L]);print(DT);invisible()})() # yes*2
98110
a
99111
<int>
@@ -103,7 +115,11 @@ NULL
103115
<int>
104116
1: 1
105117
2: 7
106-
> {print(DT[2,a:=8L]);print(DT);invisible()} # yes*1 Not within function so as at prompt
118+
> {print(DT[2,a:=8L]);print(DT);invisible()} # yes*2 as at prompt, again as of #6631
119+
a
120+
<int>
121+
1: 1
122+
2: 8
107123
a
108124
<int>
109125
1: 1
@@ -143,7 +159,7 @@ NULL
143159
+ writeLines(c(
144160
+ "library(data.table)",
145161
+ "DT = data.table(a = 1)",
146-
+ "DT[,a:=1] # not auto-printed"
162+
+ "DT[,a:=1]" # no
147163
+ ), f)
148164
+ source(f, local = TRUE, echo = TRUE)
149165
+ })
@@ -154,6 +170,34 @@ NULL
154170

155171
> DT[, `:=`(a, 1)]
156172
>
173+
> # child class of data.table doesn't induce unintended print, #3029
174+
> dt <- data.table(x = 1)
175+
> class(dt) <- c("foo", "data.table", "data.frame")
176+
> print.foo <- function(x, ...) {
177+
+ NextMethod("print")
178+
+ }
179+
> dt[, y := 1] # no
180+
>
181+
> # withAutoprint() testing (since R3.4.0)
182+
> if (!exists("withAutoprint", baseenv())) {
183+
+ q("no")
184+
+ }
185+
> if (TRUE) withAutoprint({
186+
+ DT # yes
187+
+ DT[1L, 1L] # yes
188+
+ DT[2L, a := 10L] # no
189+
+ })
190+
> DT
191+
a
192+
<int>
193+
1: 10
194+
2: 10
195+
> DT[1L, 1L]
196+
a
197+
<int>
198+
1: 10
199+
> DT[2L, `:=`(a, 10L)]
200+
>
157201
> proc.time()
158202
user system elapsed
159-
0.223 0.016 0.231
203+
0.182 0.056 0.246

0 commit comments

Comments
 (0)