Skip to content

Commit a872728

Browse files
committed
Fix and test some other corner cases in f arg checking
* Add some explanatory comments * Use `cli_warn` instead of `sprintf` with `ansi_collapse` so length-2 things are formatted "thing 1 and thing 2" not "thing 1, and thing 2" * Fix default-replacement error message when some mandatory args are absorbed by `f`'s dots. (This could give a faulty message due to broadcasting when there was just one arg with a default before the dots, or potentially an R error in other situations if/when there are >2 mandatory args.) * Add regexp's to our tests to test some of this message preparation.
1 parent f0f0105 commit a872728

File tree

2 files changed

+18
-8
lines changed

2 files changed

+18
-8
lines changed

R/utils.R

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,28 +129,31 @@ assert_sufficient_f_args <- function(f, ...) {
129129
# note that this doesn't include unnamed args forwarded through `...`.
130130
dots_i <- which(remaining_args_names == "...") # integer(0) if no match
131131
n_f_args_before_dots <- dots_i - 1L
132-
if (length(dots_i) != 0L) {
132+
if (length(dots_i) != 0L) { # `f` has a dots "arg"
133133
# Keep all arg names before `...`
134134
mandatory_args_mapped_names <- remaining_args_names[seq_len(n_f_args_before_dots)]
135135

136136
if (n_f_args_before_dots < n_mandatory_f_args) {
137137
mandatory_f_args_in_f_dots =
138138
tail(mandatory_f_args_labels, n_mandatory_f_args - n_f_args_before_dots)
139-
Warn(sprintf("`f` might not have enough positional arguments before its `...`; in the current `epi[x]_slide` call, the %s will be included in `f`'s `...`; if `f` doesn't expect those arguments, it may produce confusing error messages", cli::ansi_collapse(mandatory_f_args_in_f_dots)),
140-
class = "epiprocess__assert_sufficient_f_args__mandatory_f_args_passed_to_f_dots",
141-
epiprocess__f = f,
142-
epiprocess__mandatory_f_args_in_f_dots = mandatory_f_args_in_f_dots)
139+
cli::cli_warn(
140+
"`f` might not have enough positional arguments before its `...`; in the current `epi[x]_slide` call, the {mandatory_f_args_in_f_dots} will be included in `f`'s `...`; if `f` doesn't expect those arguments, it may produce confusing error messages",
141+
class = "epiprocess__assert_sufficient_f_args__mandatory_f_args_passed_to_f_dots",
142+
epiprocess__f = f,
143+
epiprocess__mandatory_f_args_in_f_dots = mandatory_f_args_in_f_dots
144+
)
143145
}
144-
} else {
146+
} else { # `f` doesn't have a dots "arg"
145147
if (length(args_names) < n_mandatory_f_args + rlang::dots_n(...)) {
148+
# `f` doesn't take enough args.
146149
if (rlang::dots_n(...) == 0L) {
147150
# common case; try for friendlier error message
148151
Abort(sprintf("`f` must take at least %s arguments", n_mandatory_f_args),
149152
class = "epiprocess__assert_sufficient_f_args__f_needs_min_args",
150153
epiprocess__f = f)
151154
} else {
152155
# less common; highlight that they are (accidentally?) using dots forwarding
153-
Abort(sprintf("`f` must take at least %s arguments plus the %s arguments forwarded through `epi[x]_slide`'s `...`", n_mandatory_f_args, rlang::dots_n(...)),
156+
Abort(sprintf("`f` must take at least %s arguments plus the %s arguments forwarded through `epi[x]_slide`'s `...`, or a named argument to `epi[x]_slide` was misspelled", n_mandatory_f_args, rlang::dots_n(...)),
154157
class = "epiprocess__assert_sufficient_f_args__f_needs_min_args_plus_forwarded",
155158
epiprocess__f = f)
156159
}
@@ -165,8 +168,11 @@ assert_sufficient_f_args <- function(f, ...) {
165168
default_check_args_names = names(default_check_args)
166169
has_default_replaced_by_mandatory = map_lgl(default_check_args, ~!is_missing(.x))
167170
if (any(has_default_replaced_by_mandatory)) {
171+
default_check_mandatory_args_labels =
172+
mandatory_f_args_labels[seq_len(n_remaining_args_for_default_check)]
173+
# ^ excludes any mandatory args absorbed by f's `...`'s:
168174
mandatory_args_replacing_defaults =
169-
mandatory_f_args_labels[has_default_replaced_by_mandatory]
175+
default_check_mandatory_args_labels[has_default_replaced_by_mandatory]
170176
args_with_default_replaced_by_mandatory =
171177
rlang::syms(default_check_args_names[has_default_replaced_by_mandatory])
172178
cli::cli_abort("`epi[x]_slide` would pass the {mandatory_args_replacing_defaults} to `f`'s {args_with_default_replaced_by_mandatory} argument{?s}, which {?has a/have} default value{?s}; we suspect that `f` doesn't expect {?this arg/these args} at all and may produce confusing error messages. Please add additional arguments to `f` or remove defaults as appropriate.",

tests/testthat/test-utils.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,10 @@ test_that("assert_sufficient_f_args alerts if the provided f doesn't take enough
125125
f = function() dplyr::tibble(value=c(5), count=c(2))
126126

127127
expect_warning(assert_sufficient_f_args(f_x_dots),
128+
regexp = ", the group key will be included",
128129
class = "epiprocess__assert_sufficient_f_args__mandatory_f_args_passed_to_f_dots")
129130
expect_warning(assert_sufficient_f_args(f_dots),
131+
regexp = ", the window data and group key will be included",
130132
class = "epiprocess__assert_sufficient_f_args__mandatory_f_args_passed_to_f_dots")
131133
expect_error(assert_sufficient_f_args(f_x),
132134
class = "epiprocess__assert_sufficient_f_args__f_needs_min_args")
@@ -150,8 +152,10 @@ test_that("assert_sufficient_f_args alerts if the provided f has defaults for th
150152
f_x_dots = function(x=1, ...) dplyr::tibble(value=mean(x$binary), count=length(x$binary))
151153

152154
expect_error(assert_sufficient_f_args(f_xg),
155+
regexp = "pass the group key to `f`'s g argument,",
153156
class = "epiprocess__assert_sufficient_f_args__required_args_contain_defaults")
154157
expect_error(assert_sufficient_f_args(f_xg_dots),
158+
regexp = "pass the window data to `f`'s x argument,",
155159
class = "epiprocess__assert_sufficient_f_args__required_args_contain_defaults")
156160
expect_error(suppressWarnings(assert_sufficient_f_args(f_x_dots)),
157161
class = "epiprocess__assert_sufficient_f_args__required_args_contain_defaults")

0 commit comments

Comments
 (0)