diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 5f57071..0a23631 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -23,7 +23,7 @@ jobs: - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - {os: ubuntu-latest, r: 'release'} - {os: ubuntu-latest, r: 'oldrel-1'} - - {os: windows-latest, r: '3.6'} + - {os: ubuntu-latest, r: '3.6'} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} diff --git a/DESCRIPTION b/DESCRIPTION index e6a0d17..48c6b8b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -76,6 +76,6 @@ Suggests: License: GPL-2 VignetteBuilder: knitr Encoding: UTF-8 -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 Roxygen: list(markdown = TRUE) Config/Needs/website: gesistsa/tsatemplate diff --git a/NEWS.md b/NEWS.md index 80a218f..8571c9f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,4 @@ +* For missing files in `import_list` it gives more informative warnings fix #389 * Single-item list of data frames can be exported fix #385 * Move `stringi` to Suggests to reduce compilation time. Add an attribution to the internal data to list out all required packages #378 * Move `readr` to Imports for `fwf`. `readr` is a dependency of `haven` so it does not increase the number of dependencies. Remove the original `read.fwf2` which doesn't guess `widths`. Keep the `widths` and `col.names` to maintain compatibility. #381 diff --git a/R/import_list.R b/R/import_list.R index 6691f2f..c58b6d4 100644 --- a/R/import_list.R +++ b/R/import_list.R @@ -8,6 +8,7 @@ #' @param \dots Additional arguments passed to [import()]. Behavior may be unexpected if files are of different formats. #' @inheritParams import #' @return If `rbind=FALSE` (the default), a list of a data frames. Otherwise, that list is passed to [data.table::rbindlist()] with `fill = TRUE` and returns a data frame object of class set by the `setclass` argument; if this operation fails, the list is returned. +#' @details When file is a vector of file paths and any files are missing, those files are ignored (with warnings) and this function will not raise any error. #' @examples #' ## For demo, a temp. file path is created with the file extension .xlsx #' xlsx_file <- tempfile(fileext = ".xlsx") @@ -25,46 +26,37 @@ #' # import all worksheets, the return value is a list #' import_list(xlsx_file) #' -#' # import and rbind all worksheets, the return valye is a data frame +#' # import and rbind all worksheets, the return value is a data frame #' import_list(xlsx_file, rbind = TRUE) #' @seealso [import()], [export_list()], [export()] #' @export -import_list <- - function(file, - setclass = getOption("rio.import.class", "data.frame"), - which, - rbind = FALSE, - rbind_label = "_file", - rbind_fill = TRUE, - ...) { - .check_file(file, single_only = FALSE) - - ## special cases - if (length(file) == 1) { - x <- .read_file_as_list(file = file, which = which, setclass = setclass, rbind = rbind, rbind_label = rbind_label, ...) +import_list <- function(file, setclass = getOption("rio.import.class", "data.frame"), which, rbind = FALSE, + rbind_label = "_file", rbind_fill = TRUE, ...) { + .check_file(file, single_only = FALSE) + ## special cases + if (length(file) == 1) { + x <- .read_file_as_list(file = file, which = which, setclass = setclass, rbind = rbind, rbind_label = rbind_label, ...) + } else { + ## note the plural + x <- .read_multiple_files_as_list(files = file, setclass = setclass, rbind = rbind, rbind_label = rbind_label, ...) + } + ## optionally rbind + if (isTRUE(rbind)) { + if (length(x) == 1) { + x <- x[[1L]] } else { - ## note the plural - x <- .read_multiple_files_as_list(files = file, setclass = setclass, rbind = rbind, rbind_label = rbind_label, ...) - } - # optionally rbind - if (isTRUE(rbind)) { - if (length(x) == 1) { - x <- x[[1L]] + x2 <- try(data.table::rbindlist(x, fill = rbind_fill), silent = TRUE) + if (inherits(x2, "try-error")) { + warning("Attempt to rbindlist() the data did not succeed. List returned instead.", call. = FALSE) + return(x) } else { - x2 <- try(data.table::rbindlist(x, fill = rbind_fill), silent = TRUE) - if (inherits(x2, "try-error")) { - warning("Attempt to rbindlist() the data did not succeed. List returned instead.") - return(x) - } else { - x <- x2 - } + x <- x2 } - ## set class - x <- set_class(x, class = setclass) } - - return(x) + x <- set_class(x, class = setclass) } + return(x) +} .strip_exts <- function(file) { vapply(file, function(x) tools::file_path_sans_ext(basename(x)), character(1)) @@ -75,8 +67,9 @@ import_list <- x <- lapply(files, function(thisfile) { out <- try(import(thisfile, setclass = setclass, ...), silent = TRUE) if (inherits(out, "try-error")) { - warning(sprintf("Import failed for %s", thisfile)) - out <- NULL + warning(sprintf("Import failed for %s", thisfile), call. = FALSE) + ##out <- NULL + return(NULL) } else if (isTRUE(rbind)) { out[[rbind_label]] <- thisfile } diff --git a/R/set_class.R b/R/set_class.R index 2f64ce5..2f00a6d 100644 --- a/R/set_class.R +++ b/R/set_class.R @@ -42,6 +42,9 @@ set_class <- function(x, class = NULL) { .ensure_data_frame <- function(x) { out <- structure(x, class = "data.frame") + if (nrow(out) == 0) { + return(out) + } if (!length(rownames(out))) { rownames(out) <- as.character(seq_len(length(out[, 1L, drop = TRUE]))) } diff --git a/man/import_list.Rd b/man/import_list.Rd index e3081b0..9fbf984 100644 --- a/man/import_list.Rd +++ b/man/import_list.Rd @@ -40,6 +40,9 @@ If \code{rbind=FALSE} (the default), a list of a data frames. Otherwise, that li \description{ Use \code{\link[=import]{import()}} to import a list of data frames from a vector of file names or from a multi-object file (Excel workbook, .Rdata file, zipped directory in a zip file, or HTML file) } +\details{ +When file is a vector of file paths and any files are missing, those files are ignored (with warnings) and this function will not raise any error. +} \examples{ ## For demo, a temp. file path is created with the file extension .xlsx xlsx_file <- tempfile(fileext = ".xlsx") @@ -57,7 +60,7 @@ import(xlsx_file, sheet = "mtcars1") # import all worksheets, the return value is a list import_list(xlsx_file) -# import and rbind all worksheets, the return valye is a data frame +# import and rbind all worksheets, the return value is a data frame import_list(xlsx_file, rbind = TRUE) } \seealso{ diff --git a/man/rio.Rd b/man/rio.Rd index 076d98e..be26aee 100644 --- a/man/rio.Rd +++ b/man/rio.Rd @@ -2,8 +2,8 @@ % Please edit documentation in R/rio.R \docType{package} \name{rio} -\alias{rio} \alias{rio-package} +\alias{rio} \title{A Swiss-Army Knife for Data I/O} \description{ The aim of rio is to make data file input and output as easy as possible. \code{\link[=export]{export()}} and \code{\link[=import]{import()}} serve as a Swiss-army knife for painless data I/O for data from almost any file format by inferring the data structure from the file extension, natively reading web-based data sources, setting reasonable defaults for import and export, and relying on efficient data import and export packages. An additional convenience function, \code{\link[=convert]{convert()}}, provides a simple method for converting between file types. diff --git a/tests/testthat/test_import_list.R b/tests/testthat/test_import_list.R index 2f25010..6689a44 100644 --- a/tests/testthat/test_import_list.R +++ b/tests/testthat/test_import_list.R @@ -140,6 +140,16 @@ test_that("Universal dummy `which` (Suggests) #326", { } }) +test_that("Informative message when files are not found #389", { + expect_warning(import_list(c("mtcars.rds", "nonexisting.rds")), "^Import failed for nonexisting") +}) + +test_that("Missing files and rbind", { + expect_warning(x <- import_list(c("mtcars.rds", "nonexisting.rds"), rbind = TRUE), "^Import failed for nonexisting") + expect_warning(x <- import_list(c("nonexisting.rds", "nonexisting2.rds"), rbind = TRUE), "^Import failed for nonexisting") + expect_true(is.data.frame(x)) +}) + unlink("data.rdata") unlink("mtcars.rds") unlink("mtcars.csv.zip")