Function Argument Validation for R (favr) provides tools for the succinct validation of function arguments with clear error messaging.
abortifnot()andabortif()for general validation.check()for general validation using tidy evaluation.check_with()for data-masked validation using tidy evaluation.walk_check()for applying a check to each element of a vector.check_class()andcheck_inherits()for class validation.
Numerous other strongly typed check_*() functions are provided for
specific types of validation, including:
Validate specific types:
check_integer(),check_character(),check_null(), etc.check_scalar_integer(),check_scalar_character(),check_scalar_logical(), etc.check_array()andcheck_matrix()for the ‘implicit’ types of array and matrix, respectively.
Validate specific S3 types:
check_factor(),check_date(),check_posixct(), etc.check_data.frame(),check_tibble(),check_data.table(), etc.check_vctr()andcheck_list_of()for their respective vctrs classes.
Validate OOP types:
check_s3(),check_s4(),check_s7()andcheck_r6()for their respective OOP types.
Modify check behaviour:
bare()to also check for bare objects (i.e. objects with no class attribute) in the type check functions, or bare S3 objects (where the expected S3 class is first in the class attribute vector) in the S3 type check functions.at_least(),at_most(), andin_range()to also check for ranges in length/number of rows/number of columns.
Validate specific scalar values:
check_true(),check_false(),check_bool()andcheck_string().
Validate the lack of forbidden values:
check_no_na(),check_finite(),check_unique()andcheck_nzchar().
Validate object properties:
check_length(),check_nrow(),check_ncol(),check_size(),check_non_empty()andcheck_named().
Validate file and directory existence:
check_dir(),check_file()andcheck_ext().
Build checks in the style of favr:
s3_vec_check()ands3_df_check()for developers to create their own S3 type checks.
Install the latest version of favr from CRAN.
install.packages("favr")To get a bug fix or to use a feature from the development version, you can install the development version of favr from GitHub.
# install.packages("pak")
pak::pak("LJ-Jenkins/favr")General validation:
library(favr, warn.conflicts = FALSE)
x <- c(1, 2, 3)
y <- c("a", "b", "c")
abortifnot(x < 4, nchar(y) > 1)
#> Error:
#> ! `nchar(y) > 1` is not TRUE.
abortifnot(
"{.var x} must be length {.val {5}}, but is length {.val {length(x)}}." = length(x) == 5,
is.character(y)
)
#> Error:
#> ! `x` must be length 5, but is length 3.
abortifnot(
is.numeric(x),
is.numeric(y),
message = "{.var x} and {.var y} must be {.cls numeric}."
)
#> Error:
#> ! `x` and `y` must be <numeric>.General validation with tidy evaluation:
inject_msg <- "{.var x} must contain negative values."
check(is.character(y), {{ inject_msg }} := x < 0)
#> Error:
#> ! `x` must contain negative values.
check(is.character(y), !!inject_msg := x < 0)
#> Error:
#> ! `x` must contain negative values.
inject_args <- list("{.var y} must all have 2 nchars." = nchar(y) == 2)
check(is.numeric(x), !!!inject_args)
#> Error:
#> ! `y` must all have 2 nchars.Data-masked validation:
data <- list(a = c("a", "b", "c"), b = 1:3)
# `check_with()` user-supplied messages are eval'd in the data mask context.
check_with(data,
"{.var a} must all have 1 nchars." = nchar(a) == 1,
"{.var b} must be length {.val 5}, but is length {.val {length(b)}}." = length(b) == 5
)
#> Error:
#> ! `b` must be length "5", but is length 3.
b <- c("a", "b", "c")
check_with(data, is.numeric(.data$b), is.numeric(.env$b))
#> Error:
#> ! `is.numeric(.env$b)` is not TRUE.Walking a check over a vector:
x <- list(1, 2, my_el = "3", 4)
walk_check(x, is.numeric)
#> Error:
#> ! Check result for `.x[['my_el']]` (index: 3) is not TRUE.Class validation:
x <- structure(1:3, class = "a_class")
check_class(x, "my_class")
#> Error:
#> ! `x` must be class <my_class>, but is class <a_class>.
class(x) <- c("b_class", class(x))
check_inherits(x, "my_class")
#> Error:
#> ! `x` must inherit from <my_class>, but is class <b_class/a_class>.Specific type validation:
x <- c(1, 2, 3)
check_integer(x)
#> Error:
#> ! `x` must be an <integer> vector, not a <double> vector.
check_scalar_double(x)
#> Error:
#> ! `x` must be a scalar <double>, but it is of length 3.
check_s3(x)
#> Error:
#> ! `x` must be an <S3> object, not <numeric>.
df <- data.frame(x = 1:3, y = 1:3)
check_s3(df)
check_tibble(df)
#> Error:
#> ! `df` must inherit from <tbl_df>, but is class <data.frame>.
# the `bare()` modifier can be used to ensure bare objects.
check_integer(factor(1))
check_integer(bare(factor(1)))
#> Error:
#> ! `factor(1)` must be a bare <integer>, but it is of class <factor>.
class(df) <- c("my_class", "tbl_df", "tbl", class(df))
check_tibble(df)
check_tibble(bare(df))
#> Error:
#> ! `df` must be a bare <tbl_df>, but it is of class <my_class>.
# length modifiers can be used on `n` to specify length ranges.
check_double(x, n = 2)
#> Error:
#> ! `x` must be a <double> vector of length 2, not 3.
check_double(x, n = at_least(4))
#> Error:
#> ! `x` must be a <double> vector of at least length 4, but it is of
#> length 3.
check_double(x, n = at_most(2))
#> Error:
#> ! `x` must be a <double> vector of at most length 2, but it is of length
#> 3.
check_double(x, n = in_range(1, 2))
#> Error:
#> ! `x` must be a <double> vector of a length between 1 and 2, but it is
#> of length 3.
check_tibble(df, nrow = 2)
#> Error:
#> ! `df` must be a <tbl_df> with 2 rows, not 3.
check_tibble(df, ncol = at_least(3))
#> Error:
#> ! `df` must be a <tbl_df> with at least 3 columns, but it has 2.
check_tibble(df, nrow = at_most(2))
#> Error:
#> ! `df` must be a <tbl_df> with at most 2 rows, but it has 3.
check_tibble(df, ncol = in_range(3, 5))
#> Error:
#> ! `df` must be a <tbl_df> with 3 to 5 columns, but it has 2.Ensure no forbidden values:
x <- c(1, 2, 1, NA)
check_no_na(x)
#> Error:
#> ! `x` must not contain NA values.
check_finite(x)
#> Error:
#> ! `x` must not contain non-finite values.
check_unique(x)
#> Error:
#> ! `x` must have unique elements. Duplicates: 1.
x <- c("a", "b", "")
check_nzchar(x)
#> Error:
#> ! `x` must not contain empty strings.
x <- c("a", "b", " ")
check_nzchar(x, allow_all_ws = FALSE)
#> Error:
#> ! `x` must not contain all whitespace elements.Check object properties:
x <- c(1, 2, 3)
check_length(x, 2)
#> Error:
#> ! `x` must be of length 2, not 3.
check_size(x, at_most(1))
#> Error:
#> ! `x` must be of at most size 1, but it is of size 3.
df <- data.frame(x = 1:3, y = 1:3)
check_nrow(df, 2)
#> Error:
#> ! `df` must have 2 rows, not 3.
check_ncol(df, in_range(3, 5))
#> Error:
#> ! `df` must have 3 to 5 columns, but it has 2.
x <- numeric(0)
check_non_empty(x)
#> Error:
#> ! `x` must not be empty.
x <- c(1, 2, 3)
check_named(x)
#> Error:
#> ! `x` must be named.
names(x) <- c("a", "b", "a")
check_named(x, unique = TRUE)
#> Error:
#> ! `x` must have unique names. Duplicates: "a".
names(x) <- c("a", "b", "")
check_named(x, allow_empty = FALSE)
#> Error:
#> ! `x` must not contain empty names.File/dir existence validation:
check_dir("non_existing_dir")
#> Error:
#> ! `x` must be an existing directory, but it doesn't exist.
#> ℹ Path provided: 'non_existing_dir'.
check_file("non_existing_file")
#> Error:
#> ! `x` must be an existing file, but it doesn't exist.
#> ℹ Path provided: 'non_existing_file'.
check_ext("file.txt", ext = c(".csv", ".xlsx"))
#> Error:
#> ! `"file.txt"` must have extension ".csv" or ".xlsx".
check_file("file.txt", ext = c(".csv", ".xlsx"))
#> Error:
#> ! `"file.txt"` must have extension ".csv" or ".xlsx".Build your own S3 type checks:
check_my_class <- function(
x,
n = NULL,
...,
allow_null = FALSE,
arg = rlang::caller_arg(x),
call = rlang::caller_env()
) {
s3_vec_check(
x,
n,
type = "my_class",
type_msg = "a {.cls my_class} vector",
...,
allow_null = allow_null,
arg = arg,
call = call
)
}
check_my_class(1L)
#> Error:
#> ! `1L` must inherit from <my_class>, but is class <integer>.
x <- structure(1:3, class = "my_class")
check_my_class(x)
check_my_class(NULL, allow_null = TRUE)
class(x) <- c("another_class", class(x))
check_my_class(bare(x))
#> Error:
#> ! `x` must be a bare <my_class>, but it is of class <another_class>.
check_my_class(x, n = at_most(2))
#> Error:
#> ! `x` must be a <my_class> vector of at most length 2, but it is of
#> length 3.
check_my_class(x, n = in_range(1, 2))
#> Error:
#> ! `x` must be a <my_class> vector of a length between 1 and 2, but it is
#> of length 3.favr relies heavily on the imported packages rlang and cli.
For data validation using user-defined schemas, see fluffy.
If you encounter a clear bug, please file an issue with a minimal reproducible example on GitHub.
Please note that the favr project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.
