-
Notifications
You must be signed in to change notification settings - Fork 1k
Expand file tree
/
Copy pathcedta.R
More file actions
100 lines (84 loc) · 4.24 KB
/
cedta.R
File metadata and controls
100 lines (84 loc) · 4.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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) {
# likelier to be close to the end of the call stack, right?
for (ii in length(calls):1) { # nolint: seq_linter. rev(seq_len(length(calls)))? See https://bugs.r-project.org/show_bug.cgi?id=18406.
if (calls[[ii]] %iscall% c("eval", "evalq")) return(TRUE)
}
FALSE
}
# nocov end
.any_sd_queries_in_stack = function(calls) {
for (ii in length(calls):1) { # nolint: seq_linter. As above.
if (!calls[[ii]] %iscall% "[") next
the_lhs = calls[[ii]][[2L]]
if (!is.name(the_lhs) || the_lhs != ".SD") next
return(TRUE)
}
FALSE
}
# in a helper to promote readability
# NB: put the most common and recommended cases first for speed
.cedta_impl_ = function(ns, n) {
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)
if (nsname == "data.table") return(TRUE)
if ("data.table" %chin% names(getNamespaceImports(ns))) return(TRUE) # nocov
if (isTRUE(ns$.datatable.aware)) return(TRUE) # nocov
sc = sys.calls()
if (nsname == "utils") {
if (exists("debugger.look", parent.frame(n+1L))) return(TRUE) # nocov
# 'example' for #2972
if (length(sc) >= 8L && sc[[length(sc) - 7L]] %iscall% 'example') return(TRUE) # nocov
}
if (nsname == "base") {
if (all(c("FUN", "X") %chin% ls(parent.frame(n)))) return(TRUE) # lapply
if (.any_sd_queries_in_stack(sc)) return(TRUE) # e.g. lapply() where "piped-in" j= arg has .SD[]
}
if (nsname %chin% cedta.pkgEvalsUserCode && .any_eval_calls_in_stack(sc)) return(TRUE) # nocov
# both ns$.Depends and get(.Depends,ns) are not sufficient
pkg_ns = paste("package", nsname, sep=":")
tryCatch("data.table" %chin% get(".Depends", pkg_ns, inherits=FALSE), error=function(e) FALSE)
}
# 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)
ans = .cedta_impl_(ns, n+1L)
if (!ans && getOption("datatable.verbose")) {
# nocov start
catf("cedta decided '%s' wasn't data.table aware. Here is call stack with [[1L]] applied:\n", getNamespaceName(ns))
print(sapply(sys.calls(), `[[`, 1L))
# nocov end
# so we can trace the namespace name that may need to be added (very unusually)
}
ans
}