Hi Reed,
I need to stress before giving my answer that no solution can handle
everything. These scenarios will always lead to problems:
* if any of the formal arguments rely on the current state of the call stack
* if any of the formal arguments rely on a variable that is only
defined later in the body of the function
That being said, this function does well to handle other scenarios:
```R
f <- function (...)
{
## replace f0 as needed
wrapped_function <- f0
call <- match.call(wrapped_function, expand.dots = FALSE)
args <- as.list(call)[-1L]
e <- new.env(parent = environment(wrapped_function))
parent_frame <- parent.frame()
formal_args <- formals(wrapped_function)
for (sym in names(formal_args)) {
## if the formal argument is one of the provided arguments
if (i <- match(sym, names(args), 0L)) {
## if the argument is missing, assign as is
if (identical(args[[i]], quote(expr = )))
e[[sym]] <- quote(expr = )
## if the argument is not ..., assign as a promise
else if (sym != "...")
eval(call("delayedAssign", quote(sym), args[[i]],
eval.env = quote(parent_frame), assign.env = quote(e)))
else {
## handle ... separately
## create a variable corresponding to each dot argument,
## then capture them with get("...")
dots <- args[[i]]
for (i in seq_along(dots)) {
sym <- paste0("dd", i)
eval(call("delayedAssign", quote(sym), dots[[i]],
eval.env = quote(parent_frame)))
dots[[i]] <- as.symbol(sym)
}
e[["..."]] <- eval(as.call(c(function(...)
get("..."), dots)))
}
}
else {
## similar to above, but this time evaluate in e not parent_frame
i <- match(sym, names(formal_args), 0L)
if (identical(formal_args[[i]], quote(expr = )))
e[[sym]] <- quote(expr = )
else eval(call("delayedAssign", quote(sym),
formal_args[[i]], eval.env = quote(e), assign.env = quote(e)))
}
}
## you don't need to turn into a list, but you can
args2 <- as.list(e, all.names = TRUE)
list(call = call, args = args, args2 = args2, e = e)
## do whatever else you want to here
}
```
in the test scenario you described, it works:
```R
f0 <- function(x, y = 2 * z, z, a = NULL, b) NULL
a <- 1
x <- f(a, z = 1 + 100)
x$args2
```
produces:
```> f0 <- function(x, y = 2 * z, z, a = NULL, b) NULL
> a <- 1
> x <- f(a, z = 1 + 100)
> x$args2
$x
[1] 1
$y
[1] 202
$z
[1] 101
$a
NULL
$b
>
```
Regards,
Iris
On Sun, Feb 18, 2024 at 3:51?AM Reed A. Cartwright
<racartwright at gmail.com> wrote:>
> I'm wrapping a function in R and I want to record all the arguments
> passed to it, including default values and missing values. I want to
> be able to snoop on function calls in sourced scripts as part of a
> unit testing framework.
>
> I can capture the values fine, but I'm having trouble evaluating them
> as if `force()` had been applied to each of them.
>
> Here is a minimal example:
>
> f0 <- function(x, y = 2 * z, z, a = NULL, b) NULL
>
> f <- function(...) {
> call <- rlang::call_match(fn = f0, defaults = TRUE)
> args <- rlang::call_args(call)
> # do something here to evaluate args as if force() had been called
> # I've tried many things but haven't found a solution that
handled everything
> args
> }
>
> # In the below example args1 and args2 should be the same
> a <- 1
> args1 <- f(a, z = 1 + 100)
>
> args2 <- list( a = 1, y = 202, z = 101, a = NULL, b =
rlang::missing_arg() )
>
> If anyone knows how to get this to work, I would appreciate the help.
>
> Thanks,
> Reed
>
> --
> Reed A. Cartwright, PhD
> Associate Professor of Genomics, Evolution, and Bioinformatics
> School of Life Sciences and The Biodesign Institute
> Arizona State University
> =================> Address: The Biodesign Institute, PO Box 876401,
Tempe, AZ 85287-6401 USA
> Packages: The Biodesign Institute, 1001 S. McAllister Ave, Tempe, AZ
> 85287-6401 USA
> Office: Biodesign B-220C, 1-480-965-9949
> Website: http://cartwrig.ht/
>
> ______________________________________________
> R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide
http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.