R: checking required / required arguments

I want to be able to validate all arguments without defaults specified when calling a function in R. This seems like a smart thing to do to me as it avoids a function that doesn't fire later (which might be after a lot of processing) when the value is missing.

One way to accomplish this would be to write an instruction if (missing(arg))

for each argument, but that requires the arguments to the function and the instructions above to be consistent with each other, so I'm looking for a better solution.

I am currently using the following function, which works in most cases, but not all.

# check for required arguments by getting arguments for the
# definition of the calling function and comparing to the arguments 
# in the function call
check_required_args <- function () {
    def    <- sys.function(-1)
    f_args <- formals(def)
    f_args <- f_args[sapply(f_args, is.name)]  # remove arguments with defaults
    f_args <- names(f_args)
    f_args <- f_args[f_args != '...'] # remove ellipsis argument if present

    call   <- match.call(definition=def, call=sys.call(-1))
    f_name <- call[1]
    c_args <- names(as.list(call[-1]))

    for(n in f_args) {
        if (!n %in% c_args) {
            stop("Argument '", n, "' missing from call to function ", 
                f_name, "()", call.=FALSE)
        }
    }
}

f <- function(a, b, c=2) check_required_args()
f(a=1) # should fail (missing argument b)
f(2, 3) # should work
f(2, c=5) # should fail (missing argument b)
f(2, 3, 4) # should work


f <- function(a, b, ...) f2(a, b, ...)
f2 <- function(a, b, c, ...) check_required_args()

f2(a=1, b=2, c=3) # should work
f2(a=1, b=2) # should fail (missing argument c for function f2)
f(a=1, b=2, c=3) # should work
f(a=1, b=2) # should fail  (missing argument c for function f2)

      

Can this feature be improved to work in all these cases? If there is no more suitable solution to perform this check?

+3


source to share


1 answer


Maybe this?

check_required_args <- function (fun = sys.function(-1), ncall = 3) {
  f_args <- formals(fun)
  f_args <- f_args[vapply(f_args, is.symbol, FUN.VALUE = TRUE)]
  f_args <- names(f_args)
  f_args <- setdiff(f_args, "...")
  test <- vapply(f_args, 
                 function(x) missingArg(as.name(x), envir = parent.frame(ncall), eval = TRUE), 
                 FUN.VALUE = TRUE)
  stopifnot(!any(test))
  return(invisible(NULL))     
}


f <- function(a, b, c=2) {
  check_required_args()
  return("Hello!")
}
f(a=1) # should fail (missing argument b)
#Error: !any(test) is not TRUE

f(2, 3) # should work
#[1] "Hello!"

f(2, c=5) # should fail (missing argument b)
# Error: !any(test) is not TRUE

f(2, 3, 4) # should work
#[1] "Hello!"

x <- 1
f(a=x, 3)
#[1] "Hello!"


f <- function(a, b, ...) f2(a, b, ...)
f2 <- function(a, b, c, ...) {
  check_required_args()
  return("Hello!")
}

f2(a=1, b=2, c=3) # should work
#[1] "Hello!"

f2(a=1, b=2) # should fail (missing argument c for function f2)
#Error: !any(test) is not TRUE 

f(a=1, b=2, c=3) # should work
#[1] "Hello!"

f(a=1, b=2) # should fail  (missing argument c for function f2)
#Error: !any(test) is not TRUE 

      

Edit:



You can use get

to check for availability:

check_required_args <- function (fun = sys.function(-1), ncall = 3) {
  f_args <- formals(fun)
  f_args <- f_args[vapply(f_args, is.symbol, FUN.VALUE=TRUE)]
  f_args <- names(f_args)
  f_args <- setdiff(f_args, "...")
  test <- lapply(f_args, 
                 function(x) {
                   get(x, envir = parent.frame(ncall), inherits = TRUE)
                   return(NULL)
                   })
  #possibly use a for loop instead
  #wrap in tryCatch for customized error messages

}

f <- function(a, b, ...) f2(a, b, ...)
f2 <- function(a, b, c, ...) {
  check_required_args()
  return("Hello!")
}

f(c=2)
#Error in get(x, envir = parent.frame(ncall), inherits = TRUE) : 
#  argument "a" is missing, with no default 

      

If you don't want to check incoming frames, install inherits = FALSE

.

+3


source







All Articles