diff --git a/DESCRIPTION b/DESCRIPTION index 8ecdcdc6b06..86f7ed6368b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -42,7 +42,7 @@ Imports: magrittr, Matrix, pkgconfig (>= 2.0.0), - rlang, + rlang (>= 1.1.0), stats, utils, vctrs diff --git a/NAMESPACE b/NAMESPACE index 2758e7bf6b5..e77c9afcf79 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -912,6 +912,7 @@ export(without_multiples) export(write.graph) export(write_graph) import(methods) +import(rlang) importFrom(grDevices,as.raster) importFrom(grDevices,col2rgb) importFrom(grDevices,palette) @@ -932,18 +933,6 @@ importFrom(magrittr,"%>%") importFrom(pkgconfig,get_config) importFrom(pkgconfig,set_config) importFrom(pkgconfig,set_config_in) -importFrom(rlang,"%||%") -importFrom(rlang,.data) -importFrom(rlang,.env) -importFrom(rlang,as_function) -importFrom(rlang,check_dots_empty) -importFrom(rlang,check_installed) -importFrom(rlang,global_env) -importFrom(rlang,inject) -importFrom(rlang,is_logical) -importFrom(rlang,is_true) -importFrom(rlang,set_names) -importFrom(rlang,warn) importFrom(stats,IQR) importFrom(stats,as.dendrogram) importFrom(stats,as.hclust) diff --git a/R/attributes.R b/R/attributes.R index bf37740a9d6..20ef1c937bf 100644 --- a/R/attributes.R +++ b/R/attributes.R @@ -304,8 +304,6 @@ get.edge.attribute <- function(graph, name, index = E(graph)) { ## e(graph)$weight[1:10] # get edge attribute ## - - #' Graph attributes of a graph #' #' @param graph Input graph. @@ -328,7 +326,11 @@ graph_attr <- function(graph, name) { return(graph.attributes(graph)) } - .Call(R_igraph_mybracket2, graph, igraph_t_idx_attr, igraph_attr_idx_graph)[[as.character(name)]] + check_string(name) + + .Call(R_igraph_mybracket2, graph, igraph_t_idx_attr, igraph_attr_idx_graph)[[ + name + ]] } @@ -359,6 +361,7 @@ graph_attr <- function(graph, name) { if (missing(name)) { `graph.attributes<-`(graph, value) } else { + check_string(name) set_graph_attr(graph, name, value) } } @@ -381,6 +384,8 @@ graph_attr <- function(graph, name) { #' g #' plot(g) set_graph_attr <- function(graph, name, value) { + check_string(name) + ensure_igraph(graph) .Call( @@ -451,8 +456,15 @@ vertex_attr <- function(graph, name, index = V(graph)) { } return(vertex.attributes(graph, index = index)) } + + check_string(name) myattr <- - .Call(R_igraph_mybracket2, graph, igraph_t_idx_attr, igraph_attr_idx_vertex)[[as.character(name)]] + .Call( + R_igraph_mybracket2, + graph, + igraph_t_idx_attr, + igraph_attr_idx_vertex + )[[name]] if (is_complete_iterator(index)) { return(myattr) } @@ -489,6 +501,7 @@ vertex_attr <- function(graph, name, index = V(graph)) { if (missing(name)) { `vertex.attributes<-`(graph, index = index, value = value) } else { + check_string(name) set_vertex_attr(graph, name = name, index = index, value = value) } } @@ -513,6 +526,8 @@ vertex_attr <- function(graph, name, index = V(graph)) { #' g #' plot(g) set_vertex_attr <- function(graph, name, index = V(graph), value) { + check_string(name) + if (is_complete_iterator(index)) { i_set_vertex_attr(graph = graph, name = name, value = value, check = FALSE) } else { @@ -520,8 +535,15 @@ set_vertex_attr <- function(graph, name, index = V(graph), value) { } } -i_set_vertex_attr <- function(graph, name, index = V(graph), value, check = TRUE) { +i_set_vertex_attr <- function( + graph, + name, + index = V(graph), + value, + check = TRUE +) { ensure_igraph(graph) + check_string(name) if (is.null(value)) { return(graph) @@ -536,9 +558,13 @@ i_set_vertex_attr <- function(graph, name, index = V(graph), value, check = TRUE if (!missing(index) && check) { index <- as_igraph_vs(graph, index) } - name <- as.character(name) - vattrs <- .Call(R_igraph_mybracket2, graph, igraph_t_idx_attr, igraph_attr_idx_vertex) + vattrs <- .Call( + R_igraph_mybracket2, + graph, + igraph_t_idx_attr, + igraph_attr_idx_vertex + ) complete <- is_complete_iterator(index) name_available <- (name %in% names(vattrs)) @@ -619,7 +645,9 @@ set_value_at <- function(value, idx, length_out) { } if (!all(lengths(value) == length(index))) { - cli::cli_abort("Invalid attribute value length, must match number of vertices.") + cli::cli_abort( + "Invalid attribute value length, must match number of vertices." + ) } if (!missing(index)) { @@ -631,7 +659,12 @@ set_value_at <- function(value, idx, length_out) { } if (!missing(index) && !index_is_natural_sequence(index, graph)) { - value <- map(value, set_value_at, idx = index, length_out = length(V(graph))) + value <- map( + value, + set_value_at, + idx = index, + length_out = length(V(graph)) + ) } .Call( @@ -674,8 +707,13 @@ edge_attr <- function(graph, name, index = E(graph)) { edge.attributes(graph, index = index) } } else { - name <- as.character(name) - myattr <- .Call(R_igraph_mybracket2, graph, igraph_t_idx_attr, igraph_attr_idx_edge)[[name]] + check_string(name) + myattr <- .Call( + R_igraph_mybracket2, + graph, + igraph_t_idx_attr, + igraph_attr_idx_edge + )[[name]] if (is_complete_iterator(index)) { myattr } else { @@ -714,6 +752,7 @@ edge_attr <- function(graph, name, index = E(graph)) { if (missing(name)) { `edge.attributes<-`(graph, index = index, value = value) } else { + check_string(name) set_edge_attr(graph, name = name, index = index, value = value) } } @@ -738,6 +777,7 @@ edge_attr <- function(graph, name, index = E(graph)) { #' g #' plot(g) set_edge_attr <- function(graph, name, index = E(graph), value) { + check_string(name) if (is_complete_iterator(index)) { i_set_edge_attr(graph = graph, name = name, value = value, check = FALSE) } else { @@ -745,8 +785,15 @@ set_edge_attr <- function(graph, name, index = E(graph), value) { } } -i_set_edge_attr <- function(graph, name, index = E(graph), value, check = TRUE) { +i_set_edge_attr <- function( + graph, + name, + index = E(graph), + value, + check = TRUE +) { ensure_igraph(graph) + check_string(name) if (is.null(value)) { return(graph) @@ -760,12 +807,17 @@ i_set_edge_attr <- function(graph, name, index = E(graph), value, check = TRUE) complete <- is_complete_iterator(index) single <- is_single_index(index) - name <- as.character(name) + if (!missing(index) && check) { index <- as_igraph_es(graph, index) } - eattrs <- .Call(R_igraph_mybracket2, graph, igraph_t_idx_attr, igraph_attr_idx_edge) + eattrs <- .Call( + R_igraph_mybracket2, + graph, + igraph_t_idx_attr, + igraph_attr_idx_edge + ) if (!complete && !(name %in% names(eattrs))) { eattrs[[name]] <- value[rep.int(NA_integer_, ecount(graph))] @@ -860,7 +912,13 @@ edge.attributes <- function(graph, index = E(graph)) { } } - .Call(R_igraph_mybracket2_set, graph, igraph_t_idx_attr, igraph_attr_idx_edge, value) + .Call( + R_igraph_mybracket2_set, + graph, + igraph_t_idx_attr, + igraph_attr_idx_edge, + value + ) } #' List names of graph attributes @@ -962,16 +1020,27 @@ edge_attr_names <- function(graph) { #' graph_attr_names(g2) delete_graph_attr <- function(graph, name) { ensure_igraph(graph) + check_string(name) - name <- as.character(name) if (!name %in% graph_attr_names(graph)) { cli::cli_abort("No graph attribute {.arg {name}} found.") } - gattr <- .Call(R_igraph_mybracket2, graph, igraph_t_idx_attr, igraph_attr_idx_graph) + gattr <- .Call( + R_igraph_mybracket2, + graph, + igraph_t_idx_attr, + igraph_attr_idx_graph + ) gattr[[name]] <- NULL - .Call(R_igraph_mybracket2_set, graph, igraph_t_idx_attr, igraph_attr_idx_graph, gattr) + .Call( + R_igraph_mybracket2_set, + graph, + igraph_t_idx_attr, + igraph_attr_idx_graph, + gattr + ) } #' Delete a vertex attribute @@ -991,16 +1060,27 @@ delete_graph_attr <- function(graph, name) { #' vertex_attr_names(g2) delete_vertex_attr <- function(graph, name) { ensure_igraph(graph) + check_string(name) - name <- as.character(name) if (!name %in% vertex_attr_names(graph)) { cli::cli_abort("No vertex attribute {.arg {name}} found.") } - vattr <- .Call(R_igraph_mybracket2, graph, igraph_t_idx_attr, igraph_attr_idx_vertex) + vattr <- .Call( + R_igraph_mybracket2, + graph, + igraph_t_idx_attr, + igraph_attr_idx_vertex + ) vattr[[name]] <- NULL - .Call(R_igraph_mybracket2_set, graph, igraph_t_idx_attr, igraph_attr_idx_vertex, vattr) + .Call( + R_igraph_mybracket2_set, + graph, + igraph_t_idx_attr, + igraph_attr_idx_vertex, + vattr + ) } #' Delete an edge attribute @@ -1020,22 +1100,31 @@ delete_vertex_attr <- function(graph, name) { #' edge_attr_names(g2) delete_edge_attr <- function(graph, name) { ensure_igraph(graph) + check_string(name) - name <- as.character(name) if (!name %in% edge_attr_names(graph)) { cli::cli_abort("No edge attribute {.arg {name}} found.") } - eattr <- .Call(R_igraph_mybracket2, graph, igraph_t_idx_attr, igraph_attr_idx_edge) + eattr <- .Call( + R_igraph_mybracket2, + graph, + igraph_t_idx_attr, + igraph_attr_idx_edge + ) eattr[[name]] <- NULL - .Call(R_igraph_mybracket2_set, graph, igraph_t_idx_attr, igraph_attr_idx_edge, eattr) + .Call( + R_igraph_mybracket2_set, + graph, + igraph_t_idx_attr, + igraph_attr_idx_edge, + eattr + ) } ############# - - #' Named graphs #' #' An igraph graph is named, if there is a symbolic name associated with its @@ -1073,7 +1162,6 @@ is_named <- function(graph) { } - #' Weighted graphs #' #' In weighted graphs, a real number is assigned to each (directed or @@ -1127,10 +1215,16 @@ igraph.i.attribute.combination <- function(comb) { comb <- list(comb) } comb <- as.list(comb) - if (any(!sapply(comb, function(x) { - is.function(x) || (is.character(x) && length(x) == 1) - }))) { - cli::cli_abort("Attribute combination element must be a function or character scalar.") + if ( + any( + !sapply(comb, function(x) { + is.function(x) || (is.character(x) && length(x) == 1) + }) + ) + ) { + cli::cli_abort( + "Attribute combination element must be a function or character scalar." + ) } if (is.null(names(comb))) { names(comb) <- rep("", length(comb)) @@ -1161,7 +1255,9 @@ igraph.i.attribute.combination <- function(comb) { ) x <- pmatch(tolower(x), known[, 1]) if (is.na(x)) { - cli::cli_abort("Unknown/unambigous attribute combination specification.") + cli::cli_abort( + "Unknown/unambigous attribute combination specification." + ) } known[, 2][x] } @@ -1292,6 +1388,7 @@ NULL #' g$name <- "10-ring" #' g$name `$.igraph` <- function(x, name) { + check_string(name) graph_attr(x, name) } @@ -1301,6 +1398,7 @@ NULL #' @name igraph-dollar #' @export `$<-.igraph` <- function(x, name, value) { + check_string(name) set_graph_attr(x, name, value) } diff --git a/R/igraph-package.R b/R/igraph-package.R index 23b397975a0..256aa95ebfe 100644 --- a/R/igraph-package.R +++ b/R/igraph-package.R @@ -3,17 +3,7 @@ ## usethis namespace: start #' @importFrom lifecycle deprecated #' @importFrom magrittr %>% -#' @importFrom rlang .data .env -#' @importFrom rlang check_dots_empty -#' @importFrom rlang check_installed -#' @importFrom rlang inject -#' @importFrom rlang warn -#' @importFrom rlang %||% -#' @importFrom rlang as_function -#' @importFrom rlang global_env -#' @importFrom rlang set_names -#' @importFrom rlang is_logical -#' @importFrom rlang is_true +#' @import rlang ## usethis namespace: end NULL diff --git a/R/import-standalone-obj-type.R b/R/import-standalone-obj-type.R new file mode 100644 index 00000000000..c582ba0847d --- /dev/null +++ b/R/import-standalone-obj-type.R @@ -0,0 +1,365 @@ +# Standalone file: do not edit by hand +# 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: 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. +# - `stop_friendly_type()` now takes `show_value`, passed to +# `obj_type_friendly()` as the `value` argument. +# +# 2022-10-03: +# - Added `allow_na` and `allow_null` arguments. +# - `NULL` is now backticked. +# - Better friendly type for infinities and `NaN`. +# +# 2022-09-16: +# - Unprefixed usage of rlang functions with `rlang::` to +# avoid onLoad issues when called from rlang (#1482). +# +# 2022-08-11: +# - Prefixed usage of rlang functions with `rlang::`. +# +# 2022-06-22: +# - `friendly_type_of()` is now `obj_type_friendly()`. +# - Added `obj_type_oo()`. +# +# 2021-12-20: +# - Added support for scalar values and empty vectors. +# - Added `stop_input_type()` +# +# 2021-06-30: +# - Added support for missing arguments. +# +# 2021-04-19: +# - Added support for matrices and arrays (#141). +# - Added documentation. +# - Added changelog. +# +# nocov start + +#' Return English-friendly type +#' @param x Any R object. +#' @param value Whether to describe the value of `x`. Special values +#' like `NA` or `""` are always described. +#' @param length Whether to mention the length of vectors and lists. +#' @return A string describing the type. Starts with an indefinite +#' article, e.g. "an integer vector". +#' @noRd +obj_type_friendly <- function(x, value = TRUE) { + if (is_missing(x)) { + return("absent") + } + + if (is.object(x)) { + if (inherits(x, "quosure")) { + type <- "quosure" + } else { + type <- class(x)[[1L]] + } + return(sprintf("a <%s> object", type)) + } + + if (!is_vector(x)) { + return(.rlang_as_friendly_type(typeof(x))) + } + + n_dim <- length(dim(x)) + + if (!n_dim) { + if (!is_list(x) && length(x) == 1) { + if (is_na(x)) { + return(switch( + typeof(x), + logical = "`NA`", + integer = "an integer `NA`", + double = if (is.nan(x)) { + "`NaN`" + } else { + "a numeric `NA`" + }, + complex = "a complex `NA`", + character = "a character `NA`", + .rlang_stop_unexpected_typeof(x) + )) + } + + show_infinites <- function(x) { + if (x > 0) { + "`Inf`" + } else { + "`-Inf`" + } + } + str_encode <- function(x, width = 30, ...) { + if (nchar(x) > width) { + x <- substr(x, 1, width - 3) + x <- paste0(x, "...") + } + encodeString(x, ...) + } + + if (value) { + if (is.numeric(x) && is.infinite(x)) { + return(show_infinites(x)) + } + + if (is.numeric(x) || is.complex(x)) { + number <- as.character(round(x, 2)) + what <- if (is.complex(x)) "the complex number" else "the number" + return(paste(what, number)) + } + + return(switch( + typeof(x), + logical = if (x) "`TRUE`" else "`FALSE`", + character = { + what <- if (nzchar(x)) "the string" else "the empty string" + paste(what, str_encode(x, quote = "\"")) + }, + raw = paste("the raw value", as.character(x)), + .rlang_stop_unexpected_typeof(x) + )) + } + + return(switch( + typeof(x), + logical = "a logical value", + integer = "an integer", + double = if (is.infinite(x)) show_infinites(x) else "a number", + complex = "a complex number", + character = if (nzchar(x)) "a string" else "\"\"", + raw = "a raw value", + .rlang_stop_unexpected_typeof(x) + )) + } + + if (length(x) == 0) { + return(switch( + typeof(x), + logical = "an empty logical vector", + integer = "an empty integer vector", + double = "an empty numeric vector", + complex = "an empty complex vector", + character = "an empty character vector", + raw = "an empty raw vector", + list = "an empty list", + .rlang_stop_unexpected_typeof(x) + )) + } + } + + vec_type_friendly(x) +} + +vec_type_friendly <- function(x, length = FALSE) { + if (!is_vector(x)) { + abort("`x` must be a vector.") + } + type <- typeof(x) + n_dim <- length(dim(x)) + + add_length <- function(type) { + if (length && !n_dim) { + paste0(type, sprintf(" of length %s", length(x))) + } else { + type + } + } + + if (type == "list") { + if (n_dim < 2) { + return(add_length("a list")) + } else if (is.data.frame(x)) { + return("a data frame") + } else if (n_dim == 2) { + return("a list matrix") + } else { + return("a list array") + } + } + + type <- switch( + type, + logical = "a logical %s", + integer = "an integer %s", + numeric = , + double = "a double %s", + complex = "a complex %s", + character = "a character %s", + raw = "a raw %s", + type = paste0("a ", type, " %s") + ) + + if (n_dim < 2) { + kind <- "vector" + } else if (n_dim == 2) { + kind <- "matrix" + } else { + kind <- "array" + } + out <- sprintf(type, kind) + + if (n_dim >= 2) { + out + } else { + add_length(out) + } +} + +.rlang_as_friendly_type <- function(type) { + switch( + type, + + list = "a list", + + NULL = "`NULL`", + environment = "an environment", + externalptr = "a pointer", + weakref = "a weak reference", + S4 = "an S4 object", + + name = , + symbol = "a symbol", + language = "a call", + pairlist = "a pairlist node", + expression = "an expression vector", + + char = "an internal string", + promise = "an internal promise", + ... = "an internal dots object", + any = "an internal `any` object", + bytecode = "an internal bytecode object", + + primitive = , + builtin = , + special = "a primitive function", + closure = "a function", + + type + ) +} + +.rlang_stop_unexpected_typeof <- function(x, call = caller_env()) { + abort( + sprintf("Unexpected type <%s>.", typeof(x)), + call = call + ) +} + +#' Return OO type +#' @param x Any R object. +#' @return One of `"bare"` (for non-OO objects), `"S3"`, `"S4"`, +#' `"R6"`, or `"S7"`. +#' @noRd +obj_type_oo <- function(x) { + if (!is.object(x)) { + return("bare") + } + + class <- inherits(x, c("R6", "S7_object"), which = TRUE) + + if (class[[1]]) { + "R6" + } else if (class[[2]]) { + "S7" + } else if (isS4(x)) { + "S4" + } else { + "S3" + } +} + +#' @param x The object type which does not conform to `what`. Its +#' `obj_type_friendly()` is taken and mentioned in the error message. +#' @param what The friendly expected type as a string. Can be a +#' character vector of expected types, in which case the error +#' message mentions all of them in an "or" enumeration. +#' @param show_value Passed to `value` argument of `obj_type_friendly()`. +#' @param ... Arguments passed to [abort()]. +#' @inheritParams args_error_context +#' @noRd +stop_input_type <- function( + x, + what, + ..., + allow_na = FALSE, + allow_null = FALSE, + show_value = TRUE, + arg = caller_arg(x), + call = caller_env() +) { + # From standalone-cli.R + cli <- env_get_list( + nms = c("format_arg", "format_code"), + last = topenv(), + default = function(x) sprintf("`%s`", x), + inherit = TRUE + ) + + if (allow_na) { + what <- c(what, cli$format_code("NA")) + } + if (allow_null) { + what <- c(what, cli$format_code("NULL")) + } + 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.", + format_arg(arg), + what, + obj_type_friendly(x, value = show_value) + ) + + abort(message, ..., call = call, arg = arg) +} + +oxford_comma <- function(chr, sep = ", ", final = "or") { + n <- length(chr) + + if (n < 2) { + return(chr) + } + + head <- chr[seq_len(n - 1)] + last <- chr[n] + + head <- paste(head, collapse = sep) + + # Write a or b. But a, b, or c. + if (n > 2) { + paste0(head, sep, final, " ", last) + } else { + paste0(head, " ", final, " ", last) + } +} + +# nocov end diff --git a/R/import-standalone-types-check.R b/R/import-standalone-types-check.R new file mode 100644 index 00000000000..5214a00eb39 --- /dev/null +++ b/R/import-standalone-types-check.R @@ -0,0 +1,596 @@ +# Standalone file: do not edit by hand +# Source: https://github.com/r-lib/rlang/blob/HEAD/R/standalone-types-check.R +# Generated by: usethis::use_standalone("r-lib/rlang", "types-check") +# ---------------------------------------------------------------------- +# +# --- +# repo: r-lib/rlang +# file: standalone-types-check.R +# last-updated: 2023-03-13 +# license: https://unlicense.org +# dependencies: standalone-obj-type.R +# imports: rlang (>= 1.1.0) +# --- +# +# ## 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). +# - Added `check_data_frame()` (@mgirlich). +# +# 2023-03-07: +# - Added dependency on rlang (>= 1.1.0). +# +# 2023-02-15: +# - Added `check_logical()`. +# +# - `check_bool()`, `check_number_whole()`, and +# `check_number_decimal()` are now implemented in C. +# +# - For efficiency, `check_number_whole()` and +# `check_number_decimal()` now take a `NULL` default for `min` and +# `max`. This makes it possible to bypass unnecessary type-checking +# and comparisons in the default case of no bounds checks. +# +# 2022-10-07: +# - `check_number_whole()` and `_decimal()` no longer treat +# non-numeric types such as factors or dates as numbers. Numeric +# types are detected with `is.numeric()`. +# +# 2022-10-04: +# - Added `check_name()` that forbids the empty string. +# `check_string()` allows the empty string by default. +# +# 2022-09-28: +# - Removed `what` arguments. +# - Added `allow_na` and `allow_null` arguments. +# - Added `allow_decimal` and `allow_infinite` arguments. +# - Improved errors with absent arguments. +# +# +# 2022-09-16: +# - Unprefixed usage of rlang functions with `rlang::` to +# avoid onLoad issues when called from rlang (#1482). +# +# 2022-08-11: +# - Added changelog. +# +# nocov start + +# Scalars ----------------------------------------------------------------- + +.standalone_types_check_dot_call <- .Call + +check_bool <- function( + x, + ..., + allow_na = FALSE, + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if ( + !missing(x) && + .standalone_types_check_dot_call( + ffi_standalone_is_bool_1.0.7, + x, + allow_na, + allow_null + ) + ) { + return(invisible(NULL)) + } + + stop_input_type( + x, + c("`TRUE`", "`FALSE`"), + ..., + allow_na = allow_na, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_string <- function( + x, + ..., + allow_empty = TRUE, + allow_na = FALSE, + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + is_string <- .rlang_check_is_string( + x, + allow_empty = allow_empty, + allow_na = allow_na, + allow_null = allow_null + ) + if (is_string) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a single string", + ..., + allow_na = allow_na, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +.rlang_check_is_string <- function(x, allow_empty, allow_na, allow_null) { + if (is_string(x)) { + if (allow_empty || !is_string(x, "")) { + return(TRUE) + } + } + + if (allow_null && is_null(x)) { + return(TRUE) + } + + if (allow_na && (identical(x, NA) || identical(x, na_chr))) { + return(TRUE) + } + + FALSE +} + +check_name <- function( + x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + is_string <- .rlang_check_is_string( + x, + allow_empty = FALSE, + allow_na = FALSE, + allow_null = allow_null + ) + if (is_string) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a valid name", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +IS_NUMBER_true <- 0 +IS_NUMBER_false <- 1 +IS_NUMBER_oob <- 2 + +check_number_decimal <- function( + x, + ..., + min = NULL, + max = NULL, + allow_infinite = TRUE, + allow_na = FALSE, + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (missing(x)) { + exit_code <- IS_NUMBER_false + } else if ( + 0 == + (exit_code <- .standalone_types_check_dot_call( + ffi_standalone_check_number_1.0.7, + x, + allow_decimal = TRUE, + min, + max, + allow_infinite, + allow_na, + allow_null + )) + ) { + return(invisible(NULL)) + } + + .stop_not_number( + x, + ..., + exit_code = exit_code, + allow_decimal = TRUE, + min = min, + max = max, + allow_na = allow_na, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_number_whole <- function( + x, + ..., + min = NULL, + max = NULL, + allow_infinite = FALSE, + allow_na = FALSE, + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (missing(x)) { + exit_code <- IS_NUMBER_false + } else if ( + 0 == + (exit_code <- .standalone_types_check_dot_call( + ffi_standalone_check_number_1.0.7, + x, + allow_decimal = FALSE, + min, + max, + allow_infinite, + allow_na, + allow_null + )) + ) { + return(invisible(NULL)) + } + + .stop_not_number( + x, + ..., + exit_code = exit_code, + allow_decimal = FALSE, + min = min, + max = max, + allow_na = allow_na, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +.stop_not_number <- function( + x, + ..., + exit_code, + allow_decimal, + min, + max, + allow_na, + allow_null, + arg, + call +) { + if (allow_decimal) { + what <- "a number" + } else { + what <- "a whole number" + } + + if (exit_code == IS_NUMBER_oob) { + min <- min %||% -Inf + max <- max %||% Inf + + if (min > -Inf && max < Inf) { + what <- sprintf("%s between %s and %s", what, min, max) + } else if (x < min) { + what <- sprintf("%s larger than or equal to %s", what, min) + } else if (x > max) { + what <- sprintf("%s smaller than or equal to %s", what, max) + } else { + abort("Unexpected state in OOB check", .internal = TRUE) + } + } + + stop_input_type( + x, + what, + ..., + allow_na = allow_na, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_symbol <- function( + x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + if (is_symbol(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a symbol", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_arg <- function( + x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + if (is_symbol(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "an argument name", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_call <- function( + x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + if (is_call(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a defused call", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_environment <- function( + x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + if (is_environment(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "an environment", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_function <- function( + x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + if (is_function(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a function", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_closure <- function( + x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + if (is_closure(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "an R function", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_formula <- function( + x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + if (is_formula(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a formula", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + + +# 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)) + } + } + + stop_input_type( + x, + "a character vector", + ..., + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_logical <- function( + x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + if (is_logical(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a logical vector", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_data_frame <- function( + x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + if (is.data.frame(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a data frame", + ..., + allow_null = allow_null, + arg = arg, + call = call + ) +} + +# nocov end diff --git a/R/make.R b/R/make.R index 29a39fa7aaa..4e4d660e683 100644 --- a/R/make.R +++ b/R/make.R @@ -8,10 +8,20 @@ #' @inheritParams make_graph #' @keywords internal #' @export -graph <- function(edges, ..., n = max(edges), isolates = NULL, directed = TRUE, dir = directed, simplify = TRUE) { # nocov start +graph <- function( + edges, + ..., + n = max(edges), + isolates = NULL, + directed = TRUE, + dir = directed, + simplify = TRUE +) { + # nocov start lifecycle::deprecate_soft("2.1.0", "graph()", "make_graph()") if (inherits(edges, "formula")) { - if (!missing(n)) cli::cli_abort("{.arg n} should not be given for graph literals") + if (!missing(n)) + cli::cli_abort("{.arg n} should not be given for graph literals") if (!missing(isolates)) { cli::cli_abort("{.arg isolates} should not be given for graph literals") } @@ -34,12 +44,17 @@ graph <- function(edges, ..., n = max(edges), isolates = NULL, directed = TRUE, if (!missing(dir) && missing(directed)) directed <- dir if (is.character(edges) && length(edges) == 1) { - if (!missing(n)) cli::cli_warn("{.arg n} is ignored for the {.str {edges}} graph.") + if (!missing(n)) + cli::cli_warn("{.arg n} is ignored for the {.str {edges}} graph.") if (!missing(isolates)) { - cli::cli_warn("{.arg isolates} is ignored for the {.str {edges}} graph.") + cli::cli_warn( + "{.arg isolates} is ignored for the {.str {edges}} graph." + ) } if (!missing(directed)) { - cli::cli_warn("{.arg directed} is ignored for the {.str {edges}} graph.") + cli::cli_warn( + "{.arg directed} is ignored for the {.str {edges}} graph." + ) } if (!missing(dir)) { cli::cli_warn("{.arg dir} is ignored for the {.str {edges}} graph") @@ -49,8 +64,11 @@ graph <- function(edges, ..., n = max(edges), isolates = NULL, directed = TRUE, make_famous_graph(edges) ## NULL and empty logical vector is allowed for compatibility - } else if (is.numeric(edges) || is.null(edges) || - (is.logical(edges) && length(edges) == 0)) { + } else if ( + is.numeric(edges) || + is.null(edges) || + (is.logical(edges) && length(edges) == 0) + ) { if (is.null(edges) || is.logical(edges)) edges <- as.numeric(edges) if (!is.null(isolates)) { cli::cli_warn("{.arg isolates} ignored for numeric edge list.") @@ -62,7 +80,9 @@ graph <- function(edges, ..., n = max(edges), isolates = NULL, directed = TRUE, n <- 0 } .Call( - R_igraph_create, as.numeric(edges) - 1, as.numeric(n), + R_igraph_create, + as.numeric(edges) - 1, + as.numeric(n), as.logical(directed) ) } @@ -101,10 +121,20 @@ graph <- function(edges, ..., n = max(edges), isolates = NULL, directed = TRUE, #' @inheritParams make_graph #' @keywords internal #' @export -graph.famous <- function(edges, ..., n = max(edges), isolates = NULL, directed = TRUE, dir = directed, simplify = TRUE) { # nocov start +graph.famous <- function( + edges, + ..., + n = max(edges), + isolates = NULL, + directed = TRUE, + dir = directed, + simplify = TRUE +) { + # nocov start lifecycle::deprecate_soft("2.1.0", "graph.famous()", "make_graph()") if (inherits(edges, "formula")) { - if (!missing(n)) cli::cli_abort("{.arg n} should not be given for graph literals") + if (!missing(n)) + cli::cli_abort("{.arg n} should not be given for graph literals") if (!missing(isolates)) { cli::cli_abort("{.arg isolates} should not be given for graph literals") } @@ -127,12 +157,17 @@ graph.famous <- function(edges, ..., n = max(edges), isolates = NULL, directed = if (!missing(dir) && missing(directed)) directed <- dir if (is.character(edges) && length(edges) == 1) { - if (!missing(n)) cli::cli_warn("{.arg n} is ignored for the {.str {edges}} graph.") + if (!missing(n)) + cli::cli_warn("{.arg n} is ignored for the {.str {edges}} graph.") if (!missing(isolates)) { - cli::cli_warn("{.arg isolates} is ignored for the {.str {edges}} graph.") + cli::cli_warn( + "{.arg isolates} is ignored for the {.str {edges}} graph." + ) } if (!missing(directed)) { - cli::cli_warn("{.arg directed} is ignored for the {.str {edges}} graph.") + cli::cli_warn( + "{.arg directed} is ignored for the {.str {edges}} graph." + ) } if (!missing(dir)) { cli::cli_warn("{.arg dir} is ignored for the {.str {edges}} graph.") @@ -142,8 +177,11 @@ graph.famous <- function(edges, ..., n = max(edges), isolates = NULL, directed = make_famous_graph(edges) ## NULL and empty logical vector is allowed for compatibility - } else if (is.numeric(edges) || is.null(edges) || - (is.logical(edges) && length(edges) == 0)) { + } else if ( + is.numeric(edges) || + is.null(edges) || + (is.logical(edges) && length(edges) == 0) + ) { if (is.null(edges) || is.logical(edges)) edges <- as.numeric(edges) if (!is.null(isolates)) { cli::cli_warn("{.arg isolates} ignored for numeric edge list.") @@ -155,7 +193,9 @@ graph.famous <- function(edges, ..., n = max(edges), isolates = NULL, directed = n <- 0 } .Call( - R_igraph_create, as.numeric(edges) - 1, as.numeric(n), + R_igraph_create, + as.numeric(edges) - 1, + as.numeric(n), as.logical(directed) ) } @@ -194,7 +234,8 @@ graph.famous <- function(edges, ..., n = max(edges), isolates = NULL, directed = #' @inheritParams make_line_graph #' @keywords internal #' @export -line.graph <- function(graph) { # nocov start +line.graph <- function(graph) { + # nocov start lifecycle::deprecate_soft("2.1.0", "line.graph()", "make_line_graph()") ensure_igraph(graph) @@ -216,12 +257,16 @@ line.graph <- function(graph) { # nocov start #' @inheritParams make_ring #' @keywords internal #' @export -graph.ring <- function(n, directed = FALSE, mutual = FALSE, circular = TRUE) { # nocov start +graph.ring <- function(n, directed = FALSE, mutual = FALSE, circular = TRUE) { + # nocov start lifecycle::deprecate_soft("2.1.0", "graph.ring()", "make_ring()") on.exit(.Call(R_igraph_finalizer)) res <- .Call( - R_igraph_ring, as.numeric(n), as.logical(directed), - as.logical(mutual), as.logical(circular) + R_igraph_ring, + as.numeric(n), + as.logical(directed), + as.logical(mutual), + as.logical(circular) ) if (igraph_opt("add.params")) { res$name <- "Ring graph" @@ -241,18 +286,17 @@ graph.ring <- function(n, directed = FALSE, mutual = FALSE, circular = TRUE) { # #' @inheritParams make_tree #' @keywords internal #' @export -graph.tree <- function(n, children = 2, mode = c("out", "in", "undirected")) { # nocov start +graph.tree <- function(n, children = 2, mode = c("out", "in", "undirected")) { + # nocov start lifecycle::deprecate_soft("2.1.0", "graph.tree()", "make_tree()") mode <- igraph.match.arg(mode) - mode1 <- switch(mode, - "out" = 0, - "in" = 1, - "undirected" = 2 - ) + mode1 <- switch(mode, "out" = 0, "in" = 1, "undirected" = 2) on.exit(.Call(R_igraph_finalizer)) res <- .Call( - R_igraph_kary_tree, as.numeric(n), as.numeric(children), + R_igraph_kary_tree, + as.numeric(n), + as.numeric(children), as.numeric(mode1) ) if (igraph_opt("add.params")) { @@ -273,27 +317,25 @@ graph.tree <- function(n, children = 2, mode = c("out", "in", "undirected")) { # #' @inheritParams make_star #' @keywords internal #' @export -graph.star <- function(n, mode = c("in", "out", "mutual", "undirected"), center = 1) { # nocov start +graph.star <- function( + n, + mode = c("in", "out", "mutual", "undirected"), + center = 1 +) { + # nocov start lifecycle::deprecate_soft("2.1.0", "graph.star()", "make_star()") mode <- igraph.match.arg(mode) - mode1 <- switch(mode, - "out" = 0, - "in" = 1, - "undirected" = 2, - "mutual" = 3 - ) + mode1 <- switch(mode, "out" = 0, "in" = 1, "undirected" = 2, "mutual" = 3) on.exit(.Call(R_igraph_finalizer)) res <- .Call( - R_igraph_star, as.numeric(n), as.numeric(mode1), + R_igraph_star, + as.numeric(n), + as.numeric(mode1), as.numeric(center) - 1 ) if (igraph_opt("add.params")) { - res$name <- switch(mode, - "in" = "In-star", - "out" = "Out-star", - "Star" - ) + res$name <- switch(mode, "in" = "In-star", "out" = "Out-star", "Star") res$mode <- mode res$center <- center } @@ -310,7 +352,8 @@ graph.star <- function(n, mode = c("in", "out", "mutual", "undirected"), center #' @inheritParams graph_from_lcf #' @keywords internal #' @export -graph.lcf <- function(n, shifts, repeats = 1) { # nocov start +graph.lcf <- function(n, shifts, repeats = 1) { + # nocov start lifecycle::deprecate_soft("2.1.0", "graph.lcf()", "graph_from_lcf()") # Argument checks n <- as.numeric(n) @@ -339,7 +382,17 @@ graph.lcf <- function(n, shifts, repeats = 1) { # nocov start #' @keywords internal #' @export #' @cdocs igraph_square_lattice -graph.lattice <- function(dimvector = NULL, length = NULL, dim = NULL, nei = 1, directed = FALSE, mutual = FALSE, periodic = FALSE, circular = deprecated()) { # nocov start +graph.lattice <- function( + dimvector = NULL, + length = NULL, + dim = NULL, + nei = 1, + directed = FALSE, + mutual = FALSE, + periodic = FALSE, + circular = deprecated() +) { + # nocov start lifecycle::deprecate_soft("2.1.0", "graph.lattice()", "make_lattice()") if (is.numeric(length) && length != floor(length)) { cli::cli_warn("{.arg length} was rounded to the nearest integer.") @@ -390,7 +443,8 @@ graph.lattice <- function(dimvector = NULL, length = NULL, dim = NULL, nei = 1, #' @inheritParams make_kautz_graph #' @keywords internal #' @export -graph.kautz <- function(m, n) { # nocov start +graph.kautz <- function(m, n) { + # nocov start lifecycle::deprecate_soft("2.1.0", "graph.kautz()", "make_kautz_graph()") on.exit(.Call(R_igraph_finalizer)) res <- .Call(R_igraph_kautz, as.numeric(m), as.numeric(n)) @@ -412,8 +466,13 @@ graph.kautz <- function(m, n) { # nocov start #' @inheritParams make_full_citation_graph #' @keywords internal #' @export -graph.full.citation <- function(n, directed = TRUE) { # nocov start - lifecycle::deprecate_soft("2.1.0", "graph.full.citation()", "make_full_citation_graph()") +graph.full.citation <- function(n, directed = TRUE) { + # nocov start + lifecycle::deprecate_soft( + "2.1.0", + "graph.full.citation()", + "make_full_citation_graph()" + ) # Argument checks n <- as.numeric(n) directed <- as.logical(directed) @@ -436,12 +495,23 @@ graph.full.citation <- function(n, directed = TRUE) { # nocov start #' @inheritParams make_full_bipartite_graph #' @keywords internal #' @export -graph.full.bipartite <- function(n1, n2, directed = FALSE, mode = c("all", "out", "in")) { # nocov start - lifecycle::deprecate_soft("2.1.0", "graph.full.bipartite()", "make_full_bipartite_graph()") +graph.full.bipartite <- function( + n1, + n2, + directed = FALSE, + mode = c("all", "out", "in") +) { + # nocov start + lifecycle::deprecate_soft( + "2.1.0", + "graph.full.bipartite()", + "make_full_bipartite_graph()" + ) n1 <- as.numeric(n1) n2 <- as.numeric(n2) directed <- as.logical(directed) - mode1 <- switch(igraph.match.arg(mode), + mode1 <- switch( + igraph.match.arg(mode), "out" = 1, "in" = 2, "all" = 3, @@ -469,11 +539,14 @@ graph.full.bipartite <- function(n1, n2, directed = FALSE, mode = c("all", "out" #' @inheritParams make_full_graph #' @keywords internal #' @export -graph.full <- function(n, directed = FALSE, loops = FALSE) { # nocov start +graph.full <- function(n, directed = FALSE, loops = FALSE) { + # nocov start lifecycle::deprecate_soft("2.1.0", "graph.full()", "make_full_graph()") on.exit(.Call(R_igraph_finalizer)) res <- .Call( - R_igraph_full, as.numeric(n), as.logical(directed), + R_igraph_full, + as.numeric(n), + as.logical(directed), as.logical(loops) ) if (igraph_opt("add.params")) { @@ -493,7 +566,8 @@ graph.full <- function(n, directed = FALSE, loops = FALSE) { # nocov start #' @inheritParams graph_from_literal #' @keywords internal #' @export -graph.formula <- function(..., simplify = TRUE) { # nocov start +graph.formula <- function(..., simplify = TRUE) { + # nocov start lifecycle::deprecate_soft("2.1.0", "graph.formula()", "graph_from_literal()") mf <- as.list(match.call())[-1] graph_from_literal_i(mf) @@ -509,12 +583,19 @@ graph.formula <- function(..., simplify = TRUE) { # nocov start #' @inheritParams make_chordal_ring #' @keywords internal #' @export -graph.extended.chordal.ring <- function(n, w, directed = FALSE) { # nocov start - lifecycle::deprecate_soft("2.1.0", "graph.extended.chordal.ring()", "make_chordal_ring()") +graph.extended.chordal.ring <- function(n, w, directed = FALSE) { + # nocov start + lifecycle::deprecate_soft( + "2.1.0", + "graph.extended.chordal.ring()", + "make_chordal_ring()" + ) on.exit(.Call(R_igraph_finalizer)) res <- .Call( - R_igraph_extended_chordal_ring, as.numeric(n), - as.matrix(w), as.logical(directed) + R_igraph_extended_chordal_ring, + as.numeric(n), + as.matrix(w), + as.logical(directed) ) if (igraph_opt("add.params")) { res$name <- "Extended chordal ring" @@ -533,7 +614,8 @@ graph.extended.chordal.ring <- function(n, w, directed = FALSE) { # nocov start #' @inheritParams make_empty_graph #' @keywords internal #' @export -graph.empty <- function(n = 0, directed = TRUE) { # nocov start +graph.empty <- function(n = 0, directed = TRUE) { + # nocov start lifecycle::deprecate_soft("2.1.0", "graph.empty()", "make_empty_graph()") # Argument checks n <- as.numeric(n) @@ -556,8 +638,13 @@ graph.empty <- function(n = 0, directed = TRUE) { # nocov start #' @inheritParams make_de_bruijn_graph #' @keywords internal #' @export -graph.de.bruijn <- function(m, n) { # nocov start - lifecycle::deprecate_soft("2.1.0", "graph.de.bruijn()", "make_de_bruijn_graph()") +graph.de.bruijn <- function(m, n) { + # nocov start + lifecycle::deprecate_soft( + "2.1.0", + "graph.de.bruijn()", + "make_de_bruijn_graph()" + ) on.exit(.Call(R_igraph_finalizer)) res <- .Call(R_igraph_de_bruijn, as.numeric(m), as.numeric(n)) if (igraph_opt("add.params")) { @@ -578,17 +665,26 @@ graph.de.bruijn <- function(m, n) { # nocov start #' @inheritParams make_bipartite_graph #' @keywords internal #' @export -graph.bipartite <- function(types, edges, directed = FALSE) { # nocov start - lifecycle::deprecate_soft("2.1.0", "graph.bipartite()", "make_bipartite_graph()") +graph.bipartite <- function(types, edges, directed = FALSE) { + # nocov start + lifecycle::deprecate_soft( + "2.1.0", + "graph.bipartite()", + "make_bipartite_graph()" + ) vertex.names <- names(types) if (is.character(edges)) { if (is.null(vertex.names)) { - cli::cli_abort("{.arg types} vector must be named when the edge vector contains strings") + cli::cli_abort( + "{.arg types} vector must be named when the edge vector contains strings" + ) } edges <- match(edges, vertex.names) if (any(is.na(edges))) { - cli::cli_abort("edge vector contains a vertex name that is not found in {.arg types}") + cli::cli_abort( + "edge vector contains a vertex name that is not found in {.arg types}" + ) } } @@ -617,7 +713,8 @@ graph.bipartite <- function(types, edges, directed = FALSE) { # nocov start #' @inheritParams graph_from_atlas #' @keywords internal #' @export -graph.atlas <- function(n) { # nocov start +graph.atlas <- function(n) { + # nocov start lifecycle::deprecate_soft("2.1.0", "graph.atlas()", "graph_from_atlas()") on.exit(.Call(R_igraph_finalizer)) res <- .Call(R_igraph_atlas, as.numeric(n)) @@ -674,7 +771,9 @@ graph.atlas <- function(n) { # nocov start cli::cli_abort("Don't know how to { .operation }, nothing given") } if (sum(cidx) > 1) { - cli::cli_abort("Don't know how to { .operation }, multiple constructors given") + cli::cli_abort( + "Don't know how to { .operation }, multiple constructors given" + ) } cons <- args[cidx][[1]] args <- args[!cidx] @@ -803,7 +902,11 @@ graph.atlas <- function(n) { # nocov start #' @family constructor modifiers make_ <- function(...) { me <- attr(sys.function(), "name") %||% "construct" - extracted <- .extract_constructor_and_modifiers(..., .operation = me, .variant = "make") + extracted <- .extract_constructor_and_modifiers( + ..., + .operation = me, + .variant = "make" + ) cons <- extracted$cons if (cons$lazy) { @@ -859,7 +962,11 @@ make_ <- function(...) { #' @family constructor modifiers sample_ <- function(...) { me <- attr(sys.function(), "name") %||% "construct" - extracted <- .extract_constructor_and_modifiers(..., .operation = me, .variant = "sample") + extracted <- .extract_constructor_and_modifiers( + ..., + .operation = me, + .variant = "sample" + ) cons <- extracted$cons if (cons$lazy) { @@ -896,7 +1003,11 @@ graph_ <- function(...) { ) ) me <- attr(sys.function(), "name") %||% "construct" - extracted <- .extract_constructor_and_modifiers(..., .operation = me, .variant = "graph") + extracted <- .extract_constructor_and_modifiers( + ..., + .operation = me, + .variant = "graph" + ) cons <- extracted$cons if (cons$lazy) { @@ -1072,7 +1183,6 @@ with_graph_ <- function(...) { } - ## ----------------------------------------------------------------- #' Create an igraph graph from a list of edges, or a notable graph @@ -1230,10 +1340,18 @@ with_graph_ <- function(...) { #' B - F, E - J, C - I, L - T, O - T, M - S, #' C - P, C - L, I - L, I - P #' ) -make_graph <- function(edges, ..., n = max(edges), isolates = NULL, - directed = TRUE, dir = directed, simplify = TRUE) { +make_graph <- function( + edges, + ..., + n = max(edges), + isolates = NULL, + directed = TRUE, + dir = directed, + simplify = TRUE +) { if (inherits(edges, "formula")) { - if (!missing(n)) cli::cli_abort("{.arg n} should not be given for graph literals") + if (!missing(n)) + cli::cli_abort("{.arg n} should not be given for graph literals") if (!missing(isolates)) { cli::cli_abort("{.arg isolates} should not be given for graph literals") } @@ -1256,12 +1374,17 @@ make_graph <- function(edges, ..., n = max(edges), isolates = NULL, if (!missing(dir) && missing(directed)) directed <- dir if (is.character(edges) && length(edges) == 1) { - if (!missing(n)) cli::cli_warn("{.arg n} is ignored for the {.str {edges}} graph.") + if (!missing(n)) + cli::cli_warn("{.arg n} is ignored for the {.str {edges}} graph.") if (!missing(isolates)) { - cli::cli_warn("{.arg isolates} is ignored for the {.str {edges}} graph.") + cli::cli_warn( + "{.arg isolates} is ignored for the {.str {edges}} graph." + ) } if (!missing(directed)) { - cli::cli_warn("{.arg directed} is ignored for the {.str {edges}} graph.") + cli::cli_warn( + "{.arg directed} is ignored for the {.str {edges}} graph." + ) } if (!missing(dir)) { cli::cli_warn("{.arg dir} is ignored for the {.str {edges}} graph.") @@ -1271,8 +1394,11 @@ make_graph <- function(edges, ..., n = max(edges), isolates = NULL, make_famous_graph(edges) ## NULL and empty logical vector is allowed for compatibility - } else if (is.numeric(edges) || is.null(edges) || - (is.logical(edges) && length(edges) == 0)) { + } else if ( + is.numeric(edges) || + is.null(edges) || + (is.logical(edges) && length(edges) == 0) + ) { if (is.null(edges) || is.logical(edges)) edges <- as.numeric(edges) if (!is.null(isolates)) { cli::cli_warn("{.arg isolates} ignored for numeric edge list.") @@ -1284,7 +1410,9 @@ make_graph <- function(edges, ..., n = max(edges), isolates = NULL, n <- 0 } .Call( - R_igraph_create, as.numeric(edges) - 1, as.numeric(n), + R_igraph_create, + as.numeric(edges) - 1, + as.numeric(n), as.logical(directed) ) } @@ -1314,10 +1442,11 @@ make_graph <- function(edges, ..., n = max(edges), isolates = NULL, } make_famous_graph <- function(name) { + check_string(name) name <- gsub("\\s", "_", name) on.exit(.Call(R_igraph_finalizer)) - res <- .Call(R_igraph_famous, as.character(name)) + res <- .Call(R_igraph_famous, name) if (igraph_opt("add.params")) { res$name <- capitalize(name) } @@ -1372,7 +1501,9 @@ make_empty_graph <- function(n = 0, directed = TRUE) { cli::cli_abort("{.arg n} must be numeric, not {.obj_type_friendly {n}}.") } if (!is.logical(directed)) { - cli::cli_abort("{.arg directed} must be a logical, not {.obj_type_friendly {directed}}.") + cli::cli_abort( + "{.arg directed} must be a logical, not {.obj_type_friendly {directed}}." + ) } empty_impl(n, directed) } @@ -1384,7 +1515,6 @@ empty_graph <- function(...) constructor_spec(make_empty_graph, ...) ## ----------------------------------------------------------------- - #' Creating (small) graphs via a simple interface #' #' This function is useful if you want to create a small (named) graph @@ -1632,27 +1762,23 @@ from_literal <- function(...) { #' @examples #' make_star(10, mode = "out") #' make_star(5, mode = "undirected") -make_star <- function(n, mode = c("in", "out", "mutual", "undirected"), - center = 1) { +make_star <- function( + n, + mode = c("in", "out", "mutual", "undirected"), + center = 1 +) { mode <- igraph.match.arg(mode) - mode1 <- switch(mode, - "out" = 0, - "in" = 1, - "undirected" = 2, - "mutual" = 3 - ) + mode1 <- switch(mode, "out" = 0, "in" = 1, "undirected" = 2, "mutual" = 3) on.exit(.Call(R_igraph_finalizer)) res <- .Call( - R_igraph_star, as.numeric(n), as.numeric(mode1), + R_igraph_star, + as.numeric(n), + as.numeric(mode1), as.numeric(center) - 1 ) if (igraph_opt("add.params")) { - res$name <- switch(mode, - "in" = "In-star", - "out" = "Out-star", - "Star" - ) + res$name <- switch(mode, "in" = "In-star", "out" = "Out-star", "Star") res$mode <- mode res$center <- center } @@ -1682,7 +1808,9 @@ star <- function(...) constructor_spec(make_star, ...) make_full_graph <- function(n, directed = FALSE, loops = FALSE) { on.exit(.Call(R_igraph_finalizer)) res <- .Call( - R_igraph_full, as.numeric(n), as.logical(directed), + R_igraph_full, + as.numeric(n), + as.logical(directed), as.logical(loops) ) if (igraph_opt("add.params")) { @@ -1730,9 +1858,16 @@ full_graph <- function(...) constructor_spec(make_full_graph, ...) #' make_lattice(c(5, 5, 5)) #' make_lattice(length = 5, dim = 3) #' @cdocs igraph_square_lattice -make_lattice <- function(dimvector = NULL, length = NULL, dim = NULL, - nei = 1, directed = FALSE, mutual = FALSE, - periodic = FALSE, circular = deprecated()) { +make_lattice <- function( + dimvector = NULL, + length = NULL, + dim = NULL, + nei = 1, + directed = FALSE, + mutual = FALSE, + periodic = FALSE, + circular = deprecated() +) { if (lifecycle::is_present(circular)) { lifecycle::deprecate_soft( "2.0.3", @@ -1796,8 +1931,11 @@ lattice <- function(...) constructor_spec(make_lattice, ...) make_ring <- function(n, directed = FALSE, mutual = FALSE, circular = TRUE) { on.exit(.Call(R_igraph_finalizer)) res <- .Call( - R_igraph_ring, as.numeric(n), as.logical(directed), - as.logical(mutual), as.logical(circular) + R_igraph_ring, + as.numeric(n), + as.logical(directed), + as.logical(mutual), + as.logical(circular) ) if (igraph_opt("add.params")) { res$name <- "Ring graph" @@ -1837,15 +1975,13 @@ ring <- function(...) constructor_spec(make_ring, ...) #' make_tree(10, 3, mode = "undirected") make_tree <- function(n, children = 2, mode = c("out", "in", "undirected")) { mode <- igraph.match.arg(mode) - mode1 <- switch(mode, - "out" = 0, - "in" = 1, - "undirected" = 2 - ) + mode1 <- switch(mode, "out" = 0, "in" = 1, "undirected" = 2) on.exit(.Call(R_igraph_finalizer)) res <- .Call( - R_igraph_kary_tree, as.numeric(n), as.numeric(children), + R_igraph_kary_tree, + as.numeric(n), + as.numeric(children), as.numeric(mode1) ) if (igraph_opt("add.params")) { @@ -1888,7 +2024,8 @@ sample_tree <- tree_game_impl #' @rdname make_tree #' @param ... Passed to `make_tree()` or `sample_tree()`. #' @export -tree <- function(...) constructor_spec(list(make = make_tree, sample = sample_tree), ...) +tree <- function(...) + constructor_spec(list(make = make_tree, sample = sample_tree), ...) ## ----------------------------------------------------------------- @@ -1999,8 +2136,10 @@ atlas <- function(...) constructor_spec(graph_from_atlas, ...) make_chordal_ring <- function(n, w, directed = FALSE) { on.exit(.Call(R_igraph_finalizer)) res <- .Call( - R_igraph_extended_chordal_ring, as.numeric(n), - as.matrix(w), as.logical(directed) + R_igraph_extended_chordal_ring, + as.numeric(n), + as.matrix(w), + as.logical(directed) ) if (igraph_opt("add.params")) { res$name <- "Extended chordal ring" @@ -2186,12 +2325,17 @@ kautz_graph <- function(...) constructor_spec(make_kautz_graph, ...) #' g4 <- make_full_bipartite_graph(2, 3, directed = TRUE, mode = "all") #' #' @export -make_full_bipartite_graph <- function(n1, n2, directed = FALSE, - mode = c("all", "out", "in")) { +make_full_bipartite_graph <- function( + n1, + n2, + directed = FALSE, + mode = c("all", "out", "in") +) { n1 <- as.numeric(n1) n2 <- as.numeric(n2) directed <- as.logical(directed) - mode1 <- switch(igraph.match.arg(mode), + mode1 <- switch( + igraph.match.arg(mode), "out" = 1, "in" = 2, "all" = 3, @@ -2212,7 +2356,8 @@ make_full_bipartite_graph <- function(n1, n2, directed = FALSE, #' @rdname make_full_bipartite_graph #' @param ... Passed to `make_full_bipartite_graph()`. #' @export -full_bipartite_graph <- function(...) constructor_spec(make_full_bipartite_graph, ...) +full_bipartite_graph <- function(...) + constructor_spec(make_full_bipartite_graph, ...) ## ----------------------------------------------------------------- @@ -2262,11 +2407,15 @@ make_bipartite_graph <- function(types, edges, directed = FALSE) { if (is.character(edges)) { if (is.null(vertex.names)) { - cli::cli_abort("{.arg types} vector must be named when the edge vector contains strings") + cli::cli_abort( + "{.arg types} vector must be named when the edge vector contains strings" + ) } edges <- match(edges, vertex.names) if (any(is.na(edges))) { - cli::cli_abort("edge vector contains a vertex name that is not found in {.arg types}") + cli::cli_abort( + "edge vector contains a vertex name that is not found in {.arg types}" + ) } } @@ -2322,7 +2471,8 @@ make_full_citation_graph <- function(n, directed = TRUE) { #' @rdname make_full_citation_graph #' @param ... Passed to `make_full_citation_graph()`. #' @export -full_citation_graph <- function(...) constructor_spec(make_full_citation_graph, ...) +full_citation_graph <- function(...) + constructor_spec(make_full_citation_graph, ...) ## ----------------------------------------------------------------- @@ -2491,14 +2641,19 @@ realize_degseq <- realize_degree_sequence_impl #' g <- realize_bipartite_degseq(c(3, 3, 2, 1, 1), c(2, 2, 2, 2, 2)) #' degree(g) #' @cdocs igraph_realize_bipartite_degree_sequence -realize_bipartite_degseq <- function(degrees1, degrees2, ..., - allowed.edge.types = c("simple", "multiple"), - method = c("smallest", "largest", "index")) { +realize_bipartite_degseq <- function( + degrees1, + degrees2, + ..., + allowed.edge.types = c("simple", "multiple"), + method = c("smallest", "largest", "index") +) { check_dots_empty() allowed.edge.types <- igraph.match.arg(allowed.edge.types) method <- igraph.match.arg(method) g <- realize_bipartite_degree_sequence_impl( - degrees1 = degrees1, degrees2 = degrees2, + degrees1 = degrees1, + degrees2 = degrees2, allowed.edge.types = allowed.edge.types, method = method ) diff --git a/tests/testthat/_snaps/attributes.md b/tests/testthat/_snaps/attributes.md index c2650441779..c9b916fc2c5 100644 --- a/tests/testthat/_snaps/attributes.md +++ b/tests/testthat/_snaps/attributes.md @@ -54,3 +54,11 @@ Error in `assert_named_list()`: ! `value` must be a named list with unique names +# good error message when not using character + + Code + set_graph_attr(ring, 1, 1) + Condition + Error in `set_graph_attr()`: + ! `name` must be a single string, not the number 1. + diff --git a/tests/testthat/test-attributes.R b/tests/testthat/test-attributes.R index 26183618166..9736d30b062 100644 --- a/tests/testthat/test-attributes.R +++ b/tests/testthat/test-attributes.R @@ -156,19 +156,42 @@ test_that("we can set all attributes on some vertices/edges", { g2 <- make_graph(c(2, 1, 3, 1, 4, 1, 2, 5, 3, 6)) vertex_attr(g2, index = c(1, 2, 4, 5)) <- vertex_attr(g) - expect_equal(vertex_attr(g2), list(name = c( - "a", "b", NA_character_, - "c", "d", NA_character_ - ), color = list( - rainbow(4)[1], rainbow(4)[2], NULL, - rainbow(4)[3], rainbow(4)[4], NULL - ))) + expect_equal( + vertex_attr(g2), + list( + name = c( + "a", + "b", + NA_character_, + "c", + "d", + NA_character_ + ), + color = list( + rainbow(4)[1], + rainbow(4)[2], + NULL, + rainbow(4)[3], + rainbow(4)[4], + NULL + ) + ) + ) edge_attr(g2, index = c(1, 3, 5)) <- edge_attr(g) - expect_equal(edge_attr(g2), list(weight = c( - 1L, NA_integer_, 2L, - NA_integer_, 3L - ), label = list("A", NULL, "B", NULL, "C"))) + expect_equal( + edge_attr(g2), + list( + weight = c( + 1L, + NA_integer_, + 2L, + NA_integer_, + 3L + ), + label = list("A", NULL, "B", NULL, "C") + ) + ) }) test_that("cannot use vs/es from another graph", { @@ -184,8 +207,14 @@ test_that("cannot use vs/es from another graph", { test_that("attribute combinations handle errors correctly", { g <- make_graph(c(1, 2, 2, 1)) E(g)$weight <- c("a", "b") - expect_error(as_undirected(g, edge.attr.comb = list(weight = "sum")), "invalid 'type'") - expect_error(as_undirected(g, edge.attr.comb = list(weight = sum)), "invalid 'type'") + expect_error( + as_undirected(g, edge.attr.comb = list(weight = "sum")), + "invalid 'type'" + ) + expect_error( + as_undirected(g, edge.attr.comb = list(weight = sum)), + "invalid 'type'" + ) }) test_that("can change type of attributes", { @@ -255,12 +284,18 @@ test_that("assert_named_list() works", { }) test_that("is_bipartite works", { - biadj_mat1 <- matrix(sample(0:1, 35, replace = TRUE, prob = c(3, 1)), ncol = 5) + biadj_mat1 <- matrix( + sample(0:1, 35, replace = TRUE, prob = c(3, 1)), + ncol = 5 + ) g1 <- graph_from_biadjacency_matrix(biadj_mat1) expect_true(bipartite_mapping(g1)$res) withr::local_seed(42) - biadj_mat2 <- matrix(sample(0:1, 35, replace = TRUE, prob = c(3, 1)), ncol = 5) + biadj_mat2 <- matrix( + sample(0:1, 35, replace = TRUE, prob = c(3, 1)), + ncol = 5 + ) g2 <- graph_from_biadjacency_matrix(biadj_mat2) expect_equal( bipartite_mapping(g2), @@ -423,3 +458,10 @@ test_that("assign data.frame attributes works", { edge.attributes(g) <- head(mtcars, ecount(g)) expect_no_error(E(g)[c(1, 2)]) }) + +test_that("good error message when not using character", { + ring <- graph_from_literal(A - B - C - D - E - F - G - A) + expect_snapshot(error = TRUE, { + set_graph_attr(ring, 1, 1) + }) +}) diff --git a/tools/weights.R b/tools/weights.R new file mode 100644 index 00000000000..2fddd1e05eb --- /dev/null +++ b/tools/weights.R @@ -0,0 +1,22 @@ +library(igraph) +library(tidyverse) + +# https://seankross.com/2021/02/25/Analyzing-R-Function-Arguments.html +args_data <- tibble(Package = "package:igraph") %>% + mutate(Function = list(lsf.str(Package))) %>% + unnest(Function) %>% + mutate(Function = as.character(Function)) %>% + mutate( + Arg = map( + Function, + compose(args, as.list, names, ~ keep(.x, nzchar), .dir = "forward") + ) + ) %>% + unnest(Arg, keep_empty = TRUE) + +dplyr::filter(args_data, Arg == "weights") |> + dplyr::mutate(deprecated = grepl("\\.", Function)) |> + dplyr::mutate(arg_def = "") |> + dplyr::mutate(notes = "") |> + dplyr::arrange(deprecated) |> + knitr::kable()