Skip to content

Commit

Permalink
:= suppressed in a more reliable way not depending on trigger depth. …
Browse files Browse the repository at this point in the history
…Both if(TRUE)DT[,:=] and `:=` in knitr should now be suppressed. See README for one downside, though. Closes #869.
  • Loading branch information
mattdowle committed Oct 8, 2014
1 parent e011351 commit 342d6ba
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 31 deletions.
29 changes: 13 additions & 16 deletions R/data.table.R
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,7 @@ dim.data.table <- function(x) {

.global = new.env() # thanks to: http://stackoverflow.com/a/12605694/403310
setPackageName("data.table",.global)
.global$print = TRUE
.global$depthtrigger =
if (exists(".global",.GlobalEnv)) {
9L # this is development
} else {
3L # normal value when package is loaded in 2.14+ (where functions are compiled in namespace). data.table depends on R >= 2.14.0
}
.global$print = ""

.SD = .N = .I = .GRP = .BY = NULL
# These are exported to prevent NOTEs from R CMD check, and checkUsage via compiler.
Expand All @@ -78,11 +72,15 @@ print.data.table = function(x,
nrows=getOption("datatable.print.nrows"), # (100) under this the whole (small) table is printed, unless topn is provided
row.names = TRUE, ...)
{
if (!.global$print) {
# := in [.data.table sets print=FALSE, when appropriate, to suppress := autoprinting at the console
.global$print = TRUE
if (.global$print != "" &&
length(SYS<-last(sys.calls()))>=2 &&
typeof(SYS[[2]]) == "list" && # auto-printing; e.g. not explicit print. When explicit print, this is symbol or language
address(x) == .global$print ) {
# := in [.data.table sets print=address(x), when appropriate, to suppress next autoprint at the console. See FAQ 2.22
.global$print = ""
return(invisible())
}
.global$print = ""
if (!is.numeric(nrows)) nrows = 100L
if (!is.infinite(nrows)) nrows = as.integer(nrows)
if (nrows <= 0L) return(invisible()) # ability to turn off printing
Expand Down Expand Up @@ -357,10 +355,10 @@ chmatch2 <- function(x, table, nomatch=NA_integer_) {
if (!is.logical(which) || length(which)>1) stop("'which' must be a logical vector length 1. Either FALSE, TRUE or NA.")
if ((isTRUE(which)||is.na(which)) && !missing(j)) stop("'which' is ",which," (meaning return row numbers) but 'j' is also supplied. Either you need row numbers or the result of j, but only one type of result can be returned.")
if (!is.na(nomatch) && is.na(which)) stop("which=NA with nomatch=0 would always return an empty vector. Please change or remove either which or nomatch.")
.global$print=""
if (missing(i) && missing(j)) {
# ...[] == oops at console, forgot print(...)
# or some kind of dynamic construction that has edge case of no contents inside [...]
.global$print=TRUE
return(x)
}
if (!with && missing(j)) stop("j must be provided when with=FALSE")
Expand Down Expand Up @@ -878,11 +876,10 @@ chmatch2 <- function(x, table, nomatch=NA_integer_) {
suppPrint = identity
if (length(av) && av[1L] == ":=") {
if (identical(attr(x,".data.table.locked"),TRUE)) stop(".SD is locked. Using := in .SD's j is reserved for possible future use; a tortuously flexible way to modify by group. Use := in j directly to modify by group by reference.")
if (Cstack_info()[["eval_depth"]] <= .global$depthtrigger) {
suppPrint = function(x) { .global$print=FALSE; x }
# Suppress print when returns ok not on error, bug #2376. Thanks to: http://stackoverflow.com/a/13606880/403310
# All appropriate returns following this point are wrapped i.e. return(suppPrint(x)).
}
suppPrint = function(x) { .global$print=address(x); x }
# Suppress print when returns ok not on error, bug #2376. Thanks to: http://stackoverflow.com/a/13606880/403310
# All appropriate returns following this point are wrapped; i.e. return(suppPrint(x)).

# FR #4996 - verbose message and return when a join matches nothing with `:=` in j
if (byjoin & !notjoin) {
# Note: !notjoin is here only until the notjoin is implemented as a "proper" byjoin
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@

#### NEW FEATURES

1. `if (TRUE) DT[,LHS:=RHS]` now doesn't print (thanks to Jureiss, [#869](https://github.com/Rdatatable/data.table/issues/869) and `:=` should not longer print in knitr. To get this to work we've had to live with one downside: if a `:=` is used inside a function with no `DT[]` before the end of the function, then the next time `DT` is typed at the prompt, nothing will be printed. A repeated `DT` will print. To avoid this: include a `DT[]` after the last `:=` in your function. If that is not possible (e.g., it's not a function you can change) then `print(DT)` and `DT[]` at the prompt are guaranteed to print. As before, adding an extra `[]` on the end of `:=` query is a recommended idiom to update and then print; e.g. `> DT[,foo:=3L][]`

#### BUG FIXES

1. `as.data.table.list` with list input having 0-length items, e.g. `x = list(a=integer(0), b=3:4)`. `as.data.table(x)` recycles item `a` with `NA`s to fit the length of the longer column `b` (length=2), as before now, but with an additional warning message that the item has been recycled with `NA`. Closes [#847](https://github.com/Rdatatable/data.table/issues/847). Thanks to @tvinodr for the report. This was a regression from 1.9.2.

2. In `DT[i, j]` when `i` returns all `FALSE`, some edge cases where `j` contained length-0 values (ex: `integer(0)`) did not return an empty data.table, as it should. Closes [#758](https://github.com/Rdatatable/data.table/issues/758) and [#813](https://github.com/Rdatatable/data.table/issues/813). Thanks to @tunaaa and @nigmastar for the nice reproducible reports.
2. `DT[i, j]` when `i` returns all `FALSE` and `j` contains some length-0 values (ex: `integer(0)`) now returns an empty data.table as it should. Closes [#758](https://github.com/Rdatatable/data.table/issues/758) and [#813](https://github.com/Rdatatable/data.table/issues/813). Thanks to @tunaaa and @nigmastar for the nice reproducible reports.

3. `allow.cartesian` is ignored during joins when:
* `i` has no duplicates and `mult="all"`. Closes [#742](https://github.com/Rdatatable/data.table/issues/742). Thanks to @nigmastar for the report.
Expand Down
23 changes: 18 additions & 5 deletions tests/autoprint.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,35 @@ DT[2,a:=3L] # no
DT # yes
print(DT[2,a:=4L]) # yes
print(DT) # yes
(function(){DT[2,a:=5L];NULL})() # no
DT # yes
if (TRUE) DT[2,a:=5L] # no. used to print before v1.9.5
if (TRUE) if (TRUE) DT[2,a:=6L] # no. used to print before v1.9.5
(function(){DT[2,a:=5L];NULL})() # print NULL
DT # no (from v1.9.5+). := suppresses next auto print (can't distinguish just "DT" symbol alone at the prompt)
DT # yes. 2nd time needed, or solutions below
(function(){DT[2,a:=5L];NULL})() # print NULL
DT[] # yes. guaranteed print
(function(){DT[2,a:=5L];NULL})() # print NULL
print(DT) # yes. guaranteed print
(function(){DT[2,a:=5L][];NULL})() # print NULL
DT # yes. i) function needs to add [] after last one, so that "DT" alone is guaranteed anyway
(function(){DT[2,a:=5L];DT[];NULL})() # print NULL
DT # yes. ii) or as a separate DT[] after the last := inside the function
DT2 = data.table(b=3:4) # no
(function(){DT[2,a:=6L];DT2[1,b:=7L];NULL})()
DT # yes. last := was on DT2 not DT
{DT[2,a:=6L];invisible()} # no
print(DT) # yes
(function(){print(DT[2,a:=7L]);print(DT);invisible()})() # yes*2
{print(DT[2,a:=8L]);print(DT);invisible()} # yes*2
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
DT[2,a:=10L][1] # yes (because eval depth is above trigger in the := here via nested `[.data.table` calls, iiuc).
DT[2,a:=10L][1] # yes
DT[1,a:=10L][1,a:=10L] # no
DT[,a:=as.integer(a)] # no
DT[1,a:=as.integer(a)] # no
DT[1,a:=10L][] # yes. ...[] == oops, forgot print(...)

# Test that error in := doesn't suppress next valid print, bug #2376
assign("depthtrigger", 20L, data.table:::.global) # try() adds 10 levels of depth. But we need try() otherwise this script would stop.
try(DT[,foo:=ColumnNameTypo]) # error: not found.
DT # yes (if we didn't change depthtrigger above this would print for depth reason and not test it)
DT # yes
DT # yes

50 changes: 41 additions & 9 deletions tests/autoprint.Rout.save
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@

R version 2.15.1 (2012-06-22) -- "Roasted Marshmallows"
Copyright (C) 2012 The R Foundation for Statistical Computing
ISBN 3-900051-07-0
R version 3.1.1 (2014-07-10) -- "Sock it to Me"
Copyright (C) 2014 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)

R is free software and comes with ABSOLUTELY NO WARRANTY.
Expand Down Expand Up @@ -41,12 +40,46 @@ Loading required package: data.table
a
1: 1
2: 4
> (function(){DT[2,a:=5L];NULL})() # no
> if (TRUE) DT[2,a:=5L] # no. used to print before v1.9.5
> if (TRUE) if (TRUE) DT[2,a:=6L] # no. used to print before v1.9.5
> (function(){DT[2,a:=5L];NULL})() # print NULL
NULL
> DT # yes
> DT # no (from v1.9.5+). := suppresses next auto print (can't distinguish just "DT" symbol alone at the prompt)
> DT # yes. 2nd time needed, or solutions below
a
1: 1
2: 5
> (function(){DT[2,a:=5L];NULL})() # print NULL
NULL
> DT[] # yes. guaranteed print
a
1: 1
2: 5
> (function(){DT[2,a:=5L];NULL})() # print NULL
NULL
> print(DT) # yes. guaranteed print
a
1: 1
2: 5
> (function(){DT[2,a:=5L][];NULL})() # print NULL
NULL
> DT # yes. i) function needs to add [] after last one, so that "DT" alone is guaranteed anyway
a
1: 1
2: 5
> (function(){DT[2,a:=5L];DT[];NULL})() # print NULL
NULL
> DT # yes. ii) or as a separate DT[] after the last := inside the function
a
1: 1
2: 5
> DT2 = data.table(b=3:4) # no
> (function(){DT[2,a:=6L];DT2[1,b:=7L];NULL})()
NULL
> DT # yes. last := was on DT2 not DT
a
1: 1
2: 6
> {DT[2,a:=6L];invisible()} # no
> print(DT) # yes
a
Expand All @@ -67,7 +100,7 @@ NULL
1: 1
2: 8
> 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
> DT[2,a:=10L][1] # yes (because eval depth is above trigger in the := here via nested `[.data.table` calls, iiuc).
> DT[2,a:=10L][1] # yes
a
1: 1
> DT[1,a:=10L][1,a:=10L] # no
Expand All @@ -79,10 +112,9 @@ NULL
2: 10
>
> # Test that error in := doesn't suppress next valid print, bug #2376
> assign("depthtrigger", 20L, data.table:::.global) # try() adds 10 levels of depth. But we need try() otherwise this script would stop.
> try(DT[,foo:=ColumnNameTypo]) # error: not found.
Error in eval(expr, envir, enclos) : object 'ColumnNameTypo' not found
> DT # yes (if we didn't change depthtrigger above this would print for depth reason and not test it)
> DT # yes
a
1: 10
2: 10
Expand All @@ -94,4 +126,4 @@ Error in eval(expr, envir, enclos) : object 'ColumnNameTypo' not found
>
> proc.time()
user system elapsed
1.740 0.096 1.810
3.14 0.10 3.22

0 comments on commit 342d6ba

Please sign in to comment.