-
Notifications
You must be signed in to change notification settings - Fork 92
Expand file tree
/
Copy pathutils_api-tweak.R
More file actions
216 lines (181 loc) · 6.92 KB
/
Copy pathutils_api-tweak.R
File metadata and controls
216 lines (181 loc) · 6.92 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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#' Tweak a future function by adjusting its default arguments
#'
#' @param strategy A future backend or the name of one.
#'
#' @param penvir The environment used when searching for a future
#' function by its name.
#'
#' @return a future function.
#'
#' @seealso
#' Use [plan()] to set a future to become the
#' new default strategy.
#'
#' @rdname plan
#' @export
tweak <- function(strategy, ..., penvir = parent.frame()) UseMethod("tweak")
#' @export
tweak.character <- function(strategy, ..., penvir = parent.frame()) {
parts <- strsplit(strategy, split = "::", fixed = TRUE)[[1]]
nparts <- length(parts)
if (nparts == 2) {
envir <- getNamespace(parts[[1]])
s <- parts[[2]]
if (!exists(s, mode = "function", envir = envir, inherits = TRUE)) {
stopf("No such backend for futures: %s", sQuote(strategy))
}
strategy <- get(s, mode = "function", envir = envir, inherits = TRUE)
} else {
## Search attached packages and the 'future' package
## for a future function with this name
envirs <- list(penvir, future = getNamespace("future"), NULL)
for (envir in envirs) {
## Reached the end? Nothing found.
if (is.null(envir)) {
stopf("No such backend for futures: %s", sQuote(strategy))
}
if (exists(strategy, mode = "function", envir = envir, inherits = TRUE)) {
strategy <- get(strategy, mode = "function", envir = envir, inherits = TRUE)
break
}
}
}
## Sanity check
stop_if_not(is.function(strategy))
tweak(strategy, ..., penvir = penvir)
}
#' @export
tweak.future <- function(strategy, ..., penvir = parent.frame()) {
args <- list(...)
## Nothing to tweak?
if (length(args) == 0L)
return(strategy)
names <- names(args)
if (is.null(names)) {
stop("Additional arguments to tweak() must be named")
}
## Identify arguments that must not be tweaked
untweakable <- character(0L)
tweakable <- character(0L)
## (a) All future strategies inherits from the 'future' class
untweakable <- c(attr(future, "untweakable", exact = TRUE), untweakable)
tweakable <- c(eval(attr(future, "tweakable", exact = TRUE)), tweakable)
## (b) All future strategies inherits from the 'Future' class
untweakable <- c(attr(Future, "untweakable", exact = TRUE), untweakable)
tweakable <- c(eval(attr(Future, "tweakable", exact = TRUE)), tweakable)
## (c) All future strategies inherits from the 'FutureBackend' class
untweakable <- c(attr(FutureBackend, "untweakable", exact = TRUE),untweakable)
tweakable <- c(eval(attr(FutureBackend, "tweakable", exact = TRUE)), tweakable)
## Sanity check
conflicts <- intersect(tweakable, untweakable)
stopifnot(length(conflicts) == 0)
## (d) Others that are specific to this future strategy, if any
untweakable_plan <- tweakable_plan <- character(0L)
for (class in class(strategy)) {
if (class == "future") break
if (!exists(class, mode = "function")) next
fcn <- get(class, mode = "function")
if (!inherits(fcn, "future")) next
untweakable_class <- attr(fcn, "untweakable", exact = TRUE)
tweakable_class <- eval(attr(fcn, "tweakable", exact = TRUE))
untweakable_plan <- c(untweakable_class, untweakable_plan)
tweakable_plan <- c(tweakable_class, tweakable_plan)
}
untweakable <- c(untweakable_plan, untweakable)
tweakable <- c(tweakable_plan, tweakable)
## (e) Add temporary, secret option for disabling these checks in case to
## give users some time to sort out legacy mistakes
untweakable <- getOption("future.tweak.untweakable", untweakable)
if (any(names %in% untweakable)) {
untweakable <- intersect(names, untweakable)
untweakable <- commaq(untweakable)
stopf("Detected arguments that must not be set via plan() or tweak(): %s",
untweakable)
}
## Arguments 'earlySignal' is deprecated
if ("earlySignal" %in% names) {
deprecateArgument("plan", "earlySignal", args[["earlySignal"]])
}
## formals()<- drops any attributes including class
attrs <- attributes(strategy)
class <- class(strategy)
## Reset 'backend', if set
attrs[["backend"]] <- NULL
if (identical(attrs[["init"]], "done")) attrs[["init"]] <- TRUE
## Tweak arguments
formals <- names(formals(strategy))
known <- c(formals, names(formals(future)), tweakable)
unknown <- setdiff(names, known)
if (length(unknown) > 0L) {
warnf("Detected %d unknown future arguments: %s", length(unknown), commaq(unknown))
}
strategy2 <- function(...) NULL
args2 <- args
for (kk in seq_along(args)) {
name <- names[kk]
value <- args[[name]]
if (is.call(value)) {
## enquote()
value <- as.call(list(quote(quote), value))
}
formals(strategy2)[name] <- list(value)
args2[[name]] <- as.symbol(name)
}
## Arguments 'envir' and 'workers' must exist in the wrapper, if
## they exist in the "future" function
formals2 <- names(formals(strategy2))
for (name in c("workers", "envir")) {
if (is.element(name, formals) && !is.element(name, formals2)) {
formals(strategy2) <- c(formals(strategy2), formals(strategy)[name])
args2[[name]] <- as.symbol(name)
}
}
## Construct the call strategy(..., <named 'args2' arguments>), where
## 'args2' is a named list of argument symbols/values to splice in.
body(strategy2) <- as.call(c(list(quote(strategy), quote(...)), args2))
## Avoid strategy2() depending on the calling frame, which would cause it
## to pick up package dependencies from there, which then are attached on
## the future worker.
environment(strategy2) <- new.env(parent = environment(strategy))
environment(strategy2)[["strategy"]] <- strategy
## Restore attributes including class
attributes(strategy2) <- attrs
## Append whatever tweaks were made
args <- c(attr(strategy, "tweaks"), args)
attr(strategy2, "tweaks") <- args
## Flag that it is tweaked
class(strategy2) <- c("tweaked", class)
strategy2
} ## tweak()
#' @export
tweak.function <- function(strategy, ...) {
strategy_name <- NULL
## Try to find the name of the function
env <- environment(strategy)
env_name <- environmentName(env)
if (nchar(env_name) == 0) env_name <- "<unknown>"
names <- ls(envir = env, all.names = TRUE)
if (length(names) > 0) {
is_fcn <- sapply(names, FUN = exists, mode = "function",
envir = env, inherits = FALSE)
names <- names[is_fcn]
if (length(names) > 0) {
for (name in names) {
fcn <- get(name, mode = "function", envir = env, inherits = FALSE)
if (identical(fcn, strategy)) {
strategy_name <- sprintf("%s::%s", env_name, name)
break
}
}
}
}
msg <- "Trying to use non-future function"
if (!is.null(strategy_name)) {
msg <- sprintf("%s %s", msg, sQuote(strategy_name))
} else if (nzchar(env_name)) {
msg <- sprintf("%s from environment / package %s", msg, sQuote(env_name))
}
args <- deparse(args(strategy), width.cutoff = 500L)[1L]
msg <- sprintf("%s: %s { ... }", msg, args)
stop(msg)
}