R/cedta.R

Defines functions cedta .any_eval_calls_in_stack

cedta.override = NULL  # If no need arises, will deprecate.

cedta.pkgEvalsUserCode = c("gWidgetsWWW","statET","FastRWeb","slidify","rmarkdown","knitr","ezknitr","IRkernel", "rtvs")
# These packages run user code in their own environment and thus do not
# themselves Depend or Import data.table. knitr's eval is passed envir=globalenv() so doesn't
# need to be listed here currently, but we include it in case it decides to change that.
# The others create their own environment to eval in.
# The packages might use data.frame syntax elsewhere in their package code so we just want
# the eval (only) of user code to be data.table-aware.

# If a new package needs to be added to this vector, a user may add to it using :
#   assignInNamespace("cedta.pkgEvalsUserCode", c(data.table:::cedta.pkgEvalsUserCode,"<nsname>"), "data.table")
# But please let us know so we can add the package to this vector in the package upstream, so other
# users don't have to tread the same path. Then you can remove your assignInNamepace() call.

# Packages may also set a variable in their namespace :
#   .datatable.aware = TRUE
# which makes them data.table-aware optionally and possibly variably.
# http://stackoverflow.com/a/13131555/403310
# .datatable.aware is not in data.table's namespace and it is not intended to ever be added here. Otherwise
#   package authors could set it using assignInNamespace and then not revert its value properly which would
#   cause subsequent calls from other packages to fail.

# .datatable.aware = TRUE or .datatable.aware = FALSE can also be set directly in the calling frame,
# e.g. at the start of a particular function to make it data.table-aware in an otherwise unaware package
# or vice versa.

# nocov start: very hard to reach this within our test suite -- the call stack within a call generated by e.g. knitr
# for loop, not any(vapply_1b(.)), to allow early exit
.any_eval_calls_in_stack <- function() {
  calls = sys.calls()
  # likelier to be close to the end of the call stack, right?
  for (ii in length(calls):1) { # rev(seq_len(length(calls)))? See https://bugs.r-project.org/show_bug.cgi?id=18406.
    the_call <- calls[[ii]][[1L]]
    if (is.name(the_call) && (the_call %chin% c("eval", "evalq"))) return(TRUE)
  }
  return(FALSE)
}
# nocov end

# cedta = Calling Environment Data.Table-Aware
cedta = function(n=2L) {
  # Calling Environment Data Table Aware
  env = parent.frame(n)
  if (isTRUEorFALSE(env$.datatable.aware)) { # dtplyr#184, #5654
    return(env$.datatable.aware)
  }
  ns = topenv(env)
  if (!isNamespace(ns)) {
    # e.g. DT queries at the prompt (.GlobalEnv) and knitr's eval(,envir=globalenv()) but not DF[...] inside knitr::kable v1.6
    return(TRUE)
  }
  nsname = getNamespaceName(ns)
  ans = nsname=="data.table" ||
    "data.table" %chin% names(getNamespaceImports(ns)) ||   # most common and recommended cases first for speed
    (nsname=="utils" &&
      (exists("debugger.look", parent.frame(n+1L)) ||
      (length(sc<-sys.calls())>=8L && sc[[length(sc)-7L]] %iscall% 'example')) ) || # 'example' for #2972
    (nsname=="base" && all(c("FUN", "X") %chin% ls(parent.frame(n)))) || # lapply
    (nsname %chin% cedta.pkgEvalsUserCode && .any_eval_calls_in_stack()) ||
    nsname %chin% cedta.override ||
    isTRUE(ns$.datatable.aware) ||  # As of Sep 2018: RCAS, caretEnsemble, dtplyr, rstanarm, rbokeh, CEMiTool, rqdatatable, RImmPort, BPRMeth, rlist
    tryCatch("data.table" %chin% get(".Depends",paste("package",nsname,sep=":"),inherits=FALSE),error=function(e)FALSE)  # both ns$.Depends and get(.Depends,ns) are not sufficient
  if (!ans && getOption("datatable.verbose")) {
    # nocov start
    catf("cedta decided '%s' wasn't data.table aware. Here is call stack with [[1L]] applied:\n", nsname)
    print(sapply(sys.calls(), "[[", 1L))
    # nocov end
    # so we can trace the namespace name that may need to be added (very unusually)
  }
  ans
}
Rdatatable/data.table documentation built on April 23, 2024, 3:32 a.m.