From 2d02ffaeebb26d35e3c2ba32c1be6d70b7ebe668 Mon Sep 17 00:00:00 2001 From: skitsy24 Date: Sat, 25 Apr 2026 09:34:52 -0700 Subject: [PATCH 1/3] added check for promise to autoprint for R3.5 --- R/print.data.table.R | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/R/print.data.table.R b/R/print.data.table.R index 88ff4ea505..ba6c7b77c8 100644 --- a/R/print.data.table.R +++ b/R/print.data.table.R @@ -22,6 +22,7 @@ print.data.table = function(x, topn=getOption("datatable.print.topn"), stopifnot(isTRUEorFALSE(class)) if (col.names == "none" && class) warningf("Column classes will be suppressed when col.names is 'none'") + if (!shouldPrint(x)) { # := in [.data.table sets .global$print=address(x) to suppress the next print i.e., like <- does. See FAQ 2.22 and README item in v1.9.5 # The issue is distinguishing "> DT" (after a previous := in a function) from "> DT[,foo:=1]". To print.data.table(), there @@ -31,7 +32,21 @@ print.data.table = function(x, topn=getOption("datatable.print.topn"), # Other options investigated (could revisit): Cstack_info(), .Last.value gets set first before autoprint, history(), sys.status(), # topenv(), inspecting next statement in caller, using clock() at C level to timeout suppression after some number of cycles SYS = sys.calls() - 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. + # is_print_call detects whether print() was called explicitly (e.g., print(DT)) + # vs. auto-print (e.g., just typing DT at the prompt). This distinction matters + # because := assignments should suppress auto-print but not explicit print() calls. + is_print_call = FALSE + if (identical(SYS[[1L]][[1L]], print)) { + is_print_call = TRUE + } else if (typeof(SYS[[1L]][[1L]]) == "promise") { + # in R 3.4 and R 3.5, auto-print uses a promise to reference base::print due to lazy loading + # safely evaluate promise to get the actual function + evaluated = tryCatch(eval(SYS[[1L]][[1L]]), error = function(e) NULL) + if (identical(evaluated, print)) { + is_print_call = TRUE + } + } + if (is_print_call || # this is what auto-print looks like, i.e. '> DT' and '> DT[, a:=b]' in the terminal; see #3029. ( length(SYS) >= 3L && is.symbol(thisSYS <- SYS[[length(SYS)-2L]][[1L]]) && as.character(thisSYS) == 'source') ) { # suppress printing from source(echo = TRUE) calls, #2369 return(invisible(x)) From af7736d769f85b7471804c1d2b10ce52c7656cde Mon Sep 17 00:00:00 2001 From: skitsy24 Date: Sat, 25 Apr 2026 10:00:45 -0700 Subject: [PATCH 2/3] adjusted comment on change --- R/print.data.table.R | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/R/print.data.table.R b/R/print.data.table.R index ba6c7b77c8..6cd712abd4 100644 --- a/R/print.data.table.R +++ b/R/print.data.table.R @@ -32,9 +32,8 @@ print.data.table = function(x, topn=getOption("datatable.print.topn"), # Other options investigated (could revisit): Cstack_info(), .Last.value gets set first before autoprint, history(), sys.status(), # topenv(), inspecting next statement in caller, using clock() at C level to timeout suppression after some number of cycles SYS = sys.calls() - # is_print_call detects whether print() was called explicitly (e.g., print(DT)) - # vs. auto-print (e.g., just typing DT at the prompt). This distinction matters - # because := assignments should suppress auto-print but not explicit print() calls. + # is_print_call detects whether print() was called either explicitly or through autoprint, + # is wrapped in a promise or not to account for R 3.4/3.5. is_print_call = FALSE if (identical(SYS[[1L]][[1L]], print)) { is_print_call = TRUE From 194ef2bffd434660761f9aa63f1d5638deb77b56 Mon Sep 17 00:00:00 2001 From: skitsy24 Date: Sun, 26 Apr 2026 15:07:29 -0700 Subject: [PATCH 3/3] added nocov for R 3.4/5 fix and TODO --- R/print.data.table.R | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/R/print.data.table.R b/R/print.data.table.R index 6cd712abd4..bf7f782473 100644 --- a/R/print.data.table.R +++ b/R/print.data.table.R @@ -32,19 +32,25 @@ print.data.table = function(x, topn=getOption("datatable.print.topn"), # Other options investigated (could revisit): Cstack_info(), .Last.value gets set first before autoprint, history(), sys.status(), # topenv(), inspecting next statement in caller, using clock() at C level to timeout suppression after some number of cycles SYS = sys.calls() + + # TODO(R>=3.6): Remove this branch once the minimal supported R version is raised. No need for is_print_call. Just + # identical(SYS[[1L]][[1L]], print) + # is_print_call detects whether print() was called either explicitly or through autoprint, # is wrapped in a promise or not to account for R 3.4/3.5. is_print_call = FALSE if (identical(SYS[[1L]][[1L]], print)) { is_print_call = TRUE - } else if (typeof(SYS[[1L]][[1L]]) == "promise") { + } + # nocov start + else if (typeof(SYS[[1L]][[1L]]) == "promise") { # in R 3.4 and R 3.5, auto-print uses a promise to reference base::print due to lazy loading # safely evaluate promise to get the actual function evaluated = tryCatch(eval(SYS[[1L]][[1L]]), error = function(e) NULL) if (identical(evaluated, print)) { is_print_call = TRUE } - } + } # nocov end if (is_print_call || # this is what auto-print looks like, i.e. '> DT' and '> DT[, a:=b]' in the terminal; see #3029. ( length(SYS) >= 3L && is.symbol(thisSYS <- SYS[[length(SYS)-2L]][[1L]]) && as.character(thisSYS) == 'source') ) { # suppress printing from source(echo = TRUE) calls, #2369