Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check continuous scale limits #6114

Merged
merged 13 commits into from
Oct 30, 2024
8 changes: 2 additions & 6 deletions R/bin.R
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ bin_breaks <- function(breaks, closed = c("right", "left")) {

bin_breaks_width <- function(x_range, width = NULL, center = NULL,
boundary = NULL, closed = c("right", "left")) {
if (length(x_range) != 2) {
cli::cli_abort("{.arg x_range} must have two elements.")
}
check_length(x_range, 2L)

# binwidth seems to be the argument name supplied to width. (stat-bin and stat-bindot)
check_number_decimal(width, min = 0, allow_infinite = FALSE, arg = "binwidth")
Expand Down Expand Up @@ -106,9 +104,7 @@ bin_breaks_width <- function(x_range, width = NULL, center = NULL,

bin_breaks_bins <- function(x_range, bins = 30, center = NULL,
boundary = NULL, closed = c("right", "left")) {
if (length(x_range) != 2) {
cli::cli_abort("{.arg x_range} must have two elements.")
}
check_length(x_range, 2L)

check_number_whole(bins, min = 1)
if (zero_range(x_range)) {
Expand Down
12 changes: 2 additions & 10 deletions R/coord-.R
Original file line number Diff line number Diff line change
Expand Up @@ -281,14 +281,6 @@ check_coord_limits <- function(
if (is.null(limits)) {
return(invisible(NULL))
}
if (!obj_is_vector(limits) || length(limits) != 2) {
what <- "{.obj_type_friendly {limits}}"
if (is.vector(limits)) {
what <- paste0(what, " of length {length(limits)}")
}
cli::cli_abort(
paste0("{.arg {arg}} must be a vector of length 2, not ", what, "."),
call = call
)
}
check_object(limits, is_vector, "a vector", arg = arg, call = call)
check_length(limits, 2L, arg = arg, call = call)
}
29 changes: 22 additions & 7 deletions R/import-standalone-obj-type.R
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
# Standalone file: do not edit by hand
# Source: <https://github.com/r-lib/rlang/blob/main/R/standalone-obj-type.R>
# Source: https://github.com/r-lib/rlang/blob/HEAD/R/standalone-obj-type.R
# Generated by: usethis::use_standalone("r-lib/rlang", "obj-type")
# ----------------------------------------------------------------------
#
# ---
# repo: r-lib/rlang
# file: standalone-obj-type.R
# last-updated: 2022-10-04
# last-updated: 2024-02-14
# license: https://unlicense.org
# imports: rlang (>= 1.1.0)
# ---
#
# ## Changelog
#
# 2024-02-14:
# - `obj_type_friendly()` now works for S7 objects.
#
# 2023-05-01:
# - `obj_type_friendly()` now only displays the first class of S3 objects.
#
# 2023-03-30:
# - `stop_input_type()` now handles `I()` input literally in `arg`.
#
# 2022-10-04:
# - `obj_type_friendly(value = TRUE)` now shows numeric scalars
# literally.
Expand Down Expand Up @@ -65,7 +75,7 @@ obj_type_friendly <- function(x, value = TRUE) {
if (inherits(x, "quosure")) {
type <- "quosure"
} else {
type <- paste(class(x), collapse = "/")
type <- class(x)[[1L]]
}
return(sprintf("a <%s> object", type))
}
Expand Down Expand Up @@ -261,19 +271,19 @@ vec_type_friendly <- function(x, length = FALSE) {
#' Return OO type
#' @param x Any R object.
#' @return One of `"bare"` (for non-OO objects), `"S3"`, `"S4"`,
#' `"R6"`, or `"R7"`.
#' `"R6"`, or `"S7"`.
#' @noRd
obj_type_oo <- function(x) {
if (!is.object(x)) {
return("bare")
}

class <- inherits(x, c("R6", "R7_object"), which = TRUE)
class <- inherits(x, c("R6", "S7_object"), which = TRUE)

if (class[[1]]) {
"R6"
} else if (class[[2]]) {
"R7"
"S7"
} else if (isS4(x)) {
"S4"
} else {
Expand Down Expand Up @@ -315,10 +325,15 @@ stop_input_type <- function(x,
if (length(what)) {
what <- oxford_comma(what)
}
if (inherits(arg, "AsIs")) {
format_arg <- identity
} else {
format_arg <- cli$format_arg
}

message <- sprintf(
"%s must be %s, not %s.",
cli$format_arg(arg),
format_arg(arg),
what,
obj_type_friendly(x, value = show_value)
)
Expand Down
20 changes: 18 additions & 2 deletions R/import-standalone-types-check.R
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Standalone file: do not edit by hand
# Source: <https://github.com/r-lib/rlang/blob/main/R/standalone-types-check.R>
# Source: https://github.com/r-lib/rlang/blob/HEAD/R/standalone-types-check.R
# Generated by: usethis::use_standalone("r-lib/rlang", "types-check")
# ----------------------------------------------------------------------
#
# ---
Expand All @@ -13,6 +14,9 @@
#
# ## Changelog
#
# 2024-08-15:
# - `check_character()` gains an `allow_na` argument (@martaalcalde, #1724)
#
# 2023-03-13:
# - Improved error messages of number checkers (@teunbrand)
# - Added `allow_infinite` argument to `check_number_whole()` (@mgirlich).
Expand Down Expand Up @@ -461,15 +465,28 @@ check_formula <- function(x,

# Vectors -----------------------------------------------------------------

# TODO: Figure out what to do with logical `NA` and `allow_na = TRUE`

check_character <- function(x,
...,
allow_na = TRUE,
allow_null = FALSE,
arg = caller_arg(x),
call = caller_env()) {

if (!missing(x)) {
if (is_character(x)) {
if (!allow_na && any(is.na(x))) {
abort(
sprintf("`%s` can't contain NA values.", arg),
arg = arg,
call = call
)
}

return(invisible(NULL))
}

if (allow_null && is_null(x)) {
return(invisible(NULL))
}
Expand All @@ -479,7 +496,6 @@ check_character <- function(x,
x,
"a character vector",
...,
allow_na = FALSE,
allow_null = allow_null,
arg = arg,
call = call
Expand Down
16 changes: 4 additions & 12 deletions R/limits.R
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,7 @@
limits <- function(lims, var, call = caller_env()) UseMethod("limits")
#' @export
limits.numeric <- function(lims, var, call = caller_env()) {
if (length(lims) != 2) {
cli::cli_abort("{.arg {var}} must be a two-element vector.", call = call)
}
check_length(lims, 2L, arg = var, call = call)
if (!anyNA(lims) && lims[1] > lims[2]) {
trans <- "reverse"
} else {
Expand Down Expand Up @@ -143,23 +141,17 @@
}
#' @export
limits.Date <- function(lims, var, call = caller_env()) {
if (length(lims) != 2) {
cli::cli_abort("{.arg {var}} must be a two-element vector.", call = call)
}
check_length(lims, 2L, arg = var, call = call)

Check warning on line 144 in R/limits.R

View check run for this annotation

Codecov / codecov/patch

R/limits.R#L144

Added line #L144 was not covered by tests
make_scale("date", var, limits = lims, call = call)
}
#' @export
limits.POSIXct <- function(lims, var, call = caller_env()) {
if (length(lims) != 2) {
cli::cli_abort("{.arg {var}} must be a two-element vector.", call = call)
}
check_length(lims, 2L, arg = var, call = call)

Check warning on line 149 in R/limits.R

View check run for this annotation

Codecov / codecov/patch

R/limits.R#L149

Added line #L149 was not covered by tests
make_scale("datetime", var, limits = lims, call = call)
}
#' @export
limits.POSIXlt <- function(lims, var, call = caller_env()) {
if (length(lims) != 2) {
cli::cli_abort("{.arg {var}} must be a two-element vector.", call = call)
}
check_length(lims, 2L, arg = var, call = call)

Check warning on line 154 in R/limits.R

View check run for this annotation

Codecov / codecov/patch

R/limits.R#L154

Added line #L154 was not covered by tests
make_scale("datetime", var, limits = as.POSIXct(lims), call = call)
}

Expand Down
11 changes: 4 additions & 7 deletions R/plot-build.R
Original file line number Diff line number Diff line change
Expand Up @@ -358,13 +358,10 @@ table_add_tag <- function(table, label, theme) {
),
call = expr(theme()))
}
if (length(position) != 2) {
cli::cli_abort(paste0(
"A {.cls numeric} {.arg plot.tag.position} ",
"theme setting must have length 2."
),
call = expr(theme()))
}
check_length(
position, 2L, call = expr(theme()),
arg = I("A {.cls numeric} {.arg plot.tag.position}")
)
top <- left <- right <- bottom <- FALSE
} else {
# Break position into top/left/right/bottom
Expand Down
14 changes: 13 additions & 1 deletion R/scale-.R
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,14 @@ continuous_scale <- function(aesthetics, scale_name = deprecated(), palette, nam
}

transform <- as.transform(transform)
limits <- allow_lambda(limits)

if (!is.null(limits) && !is.function(limits)) {
limits <- transform$transform(limits)
}
check_continuous_limits(limits, call = call)

# Convert formula to function if appropriate
limits <- allow_lambda(limits)
breaks <- allow_lambda(breaks)
labels <- allow_lambda(labels)
rescaler <- allow_lambda(rescaler)
Expand Down Expand Up @@ -1400,6 +1402,16 @@ check_transformation <- function(x, transformed, name, arg = NULL, call = NULL)
cli::cli_warn(msg, call = call)
}

check_continuous_limits <- function(limits, ...,
arg = caller_arg(limits),
call = caller_env()) {
if (is.null(limits) || is.function(limits)) {
return(invisible())
}
check_numeric(limits, arg = arg, call = call, allow_na = TRUE)
check_length(limits, 2L, arg = arg, call = call)
}

trans_support_nbreaks <- function(trans) {
"n" %in% names(formals(trans$breaks))
}
Expand Down
58 changes: 58 additions & 0 deletions R/utilities-checks.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
check_fun,
what,
...,
allow_na = FALSE,
allow_null = FALSE,
arg = caller_arg(x),
call = caller_env()) {
Expand All @@ -18,6 +19,9 @@
if (allow_null && is_null(x)) {
return(invisible(NULL))
}
if (allow_na && all(is.na(x))) {
return(invisible(NULL))
}
}

stop_input_type(
Expand Down Expand Up @@ -69,6 +73,60 @@
)
}

check_length <- function(x, length = integer(), ..., min = 0, max = Inf,
arg = caller_arg(x), call = caller_env()) {
if (missing(x)) {
stop_input_type(x, "a vector", arg = arg, call = call)

Check warning on line 79 in R/utilities-checks.R

View check run for this annotation

Codecov / codecov/patch

R/utilities-checks.R#L79

Added line #L79 was not covered by tests
}

n <- length(x)
if (n %in% length) {
return(invisible(NULL))
}
fmt <- if (inherits(arg, "AsIs")) identity else function(x) sprintf("`%s`", x)
if (length(length) > 0) {
type <- paste0("a vector of length ", oxford_comma(length))
if (length(length) == 1) {
type <- switch(
sprintf("%d", length),
"0" = "an empty vector",
"1" = "a scalar of length 1",
type
)
}
msg <- sprintf(
"%s must be %s, not length %d.",
fmt(arg), type, n
)
cli::cli_abort(msg, call = call, arg = arg)
}

range <- pmax(range(min, max, na.rm = TRUE), 0)
if (n >= min & n <= max) {
return(invisible(NULL))

Check warning on line 106 in R/utilities-checks.R

View check run for this annotation

Codecov / codecov/patch

R/utilities-checks.R#L104-L106

Added lines #L104 - L106 were not covered by tests
}
if (identical(range[1], range[2])) {
check_length(x, range[1], arg = arg, call = call)
return(invisible(NULL))

Check warning on line 110 in R/utilities-checks.R

View check run for this annotation

Codecov / codecov/patch

R/utilities-checks.R#L108-L110

Added lines #L108 - L110 were not covered by tests
}

type <- if (range[2] == 1) "scalar" else "vector"

Check warning on line 113 in R/utilities-checks.R

View check run for this annotation

Codecov / codecov/patch

R/utilities-checks.R#L113

Added line #L113 was not covered by tests

what <- paste0("a length between ", range[1], " and ", range[2])
if (identical(range[2], Inf)) {
what <- paste0("at least length ", range[1])

Check warning on line 117 in R/utilities-checks.R

View check run for this annotation

Codecov / codecov/patch

R/utilities-checks.R#L115-L117

Added lines #L115 - L117 were not covered by tests
}
if (identical(range[1], 0)) {
what <- paste0("at most length ", range[2])

Check warning on line 120 in R/utilities-checks.R

View check run for this annotation

Codecov / codecov/patch

R/utilities-checks.R#L119-L120

Added lines #L119 - L120 were not covered by tests
}

msg <- sprintf(
"`%s` must be a %s with %s, not length %d.",
fmt(arg), type, what, n
)
cli::cli_abort(msg, call = call, arg = arg)

Check warning on line 127 in R/utilities-checks.R

View check run for this annotation

Codecov / codecov/patch

R/utilities-checks.R#L123-L127

Added lines #L123 - L127 were not covered by tests
}

#' Check graphics device capabilities
#'
#' This function makes an attempt to estimate whether the graphics device is
Expand Down
4 changes: 2 additions & 2 deletions tests/testthat/_snaps/coord-.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
check_coord_limits(xlim(1, 2))
Condition
Error:
! `xlim(1, 2)` must be a vector of length 2, not a <ScaleContinuousPosition> object.
! `xlim(1, 2)` must be a vector, not a <ScaleContinuousPosition> object.

---

Code
check_coord_limits(1:3)
Condition
Error:
! `1:3` must be a vector of length 2, not an integer vector of length 3.
! `1:3` must be a vector of length 2, not length 3.

4 changes: 2 additions & 2 deletions tests/testthat/_snaps/coord-cartesian.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# cartesian coords throws error when limits are badly specified

`xlim` must be a vector of length 2, not a <ScaleContinuousPosition> object.
`xlim` must be a vector, not a <ScaleContinuousPosition> object.

---

`ylim` must be a vector of length 2, not an integer vector of length 3.
`ylim` must be a vector of length 2, not length 3.

4 changes: 2 additions & 2 deletions tests/testthat/_snaps/coord-flip.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# flip coords throws error when limits are badly specified

`xlim` must be a vector of length 2, not a <ScaleContinuousPosition> object.
`xlim` must be a vector, not a <ScaleContinuousPosition> object.

---

`ylim` must be a vector of length 2, not an integer vector of length 3.
`ylim` must be a vector of length 2, not length 3.

4 changes: 2 additions & 2 deletions tests/testthat/_snaps/coord-map.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# coord map throws error when limits are badly specified

`xlim` must be a vector of length 2, not a <ScaleContinuousPosition> object.
`xlim` must be a vector, not a <ScaleContinuousPosition> object.

---

`ylim` must be a vector of length 2, not an integer vector of length 3.
`ylim` must be a vector of length 2, not length 3.

# coord_map throws informative warning about guides

Expand Down
Loading
Loading