diff --git a/DESCRIPTION b/DESCRIPTION index 865e915..04a0d56 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,4 +16,5 @@ Imports: DevDeps: testthat, R6, - data.table + data.table, + digest diff --git a/R/loadfast.R b/R/loadfast.R index 6fac4a9..f7f2854 100644 --- a/R/loadfast.R +++ b/R/loadfast.R @@ -59,7 +59,7 @@ load_fast <- function(path = ".", helpers = TRUE, attach_testthat = NULL, full = desc_path <- file.path(abs_path, "DESCRIPTION") if (!file.exists(desc_path)) stop("DESCRIPTION file not found at: ", desc_path) desc_fields <- read.dcf(desc_path) - pkg_name <- if ("Package" %in% colnames(desc_fields)) trimws(desc_fields[1L, "Package"]) else "" + pkg_name <- if ("Package" %in% colnames(desc_fields)) unname(trimws(desc_fields[1L, "Package"])) else "" if (!nzchar(pkg_name)) stop("No valid 'Package' field found in DESCRIPTION") pkg_env_name <- paste0("package:", pkg_name) diff --git a/renv.lock b/renv.lock index aafbf16..c037a30 100644 --- a/renv.lock +++ b/renv.lock @@ -282,6 +282,35 @@ "Maintainer": "Brodie Gaslam ", "Repository": "CRAN" }, + "digest": { + "Package": "digest", + "Version": "0.6.39", + "Source": "Repository", + "Authors@R": "c(person(\"Dirk\", \"Eddelbuettel\", role = c(\"aut\", \"cre\"), email = \"edd@debian.org\", comment = c(ORCID = \"0000-0001-6419-907X\")), person(\"Antoine\", \"Lucas\", role=\"ctb\", comment = c(ORCID = \"0000-0002-8059-9767\")), person(\"Jarek\", \"Tuszynski\", role=\"ctb\"), person(\"Henrik\", \"Bengtsson\", role=\"ctb\", comment = c(ORCID = \"0000-0002-7579-5165\")), person(\"Simon\", \"Urbanek\", role=\"ctb\", comment = c(ORCID = \"0000-0003-2297-1732\")), person(\"Mario\", \"Frasca\", role=\"ctb\"), person(\"Bryan\", \"Lewis\", role=\"ctb\"), person(\"Murray\", \"Stokely\", role=\"ctb\"), person(\"Hannes\", \"Muehleisen\", role=\"ctb\", comment = c(ORCID = \"0000-0001-8552-0029\")), person(\"Duncan\", \"Murdoch\", role=\"ctb\"), person(\"Jim\", \"Hester\", role=\"ctb\", comment = c(ORCID = \"0000-0002-2739-7082\")), person(\"Wush\", \"Wu\", role=\"ctb\", comment = c(ORCID = \"0000-0001-5180-0567\")), person(\"Qiang\", \"Kou\", role=\"ctb\", comment = c(ORCID = \"0000-0001-6786-5453\")), person(\"Thierry\", \"Onkelinx\", role=\"ctb\", comment = c(ORCID = \"0000-0001-8804-4216\")), person(\"Michel\", \"Lang\", role=\"ctb\", comment = c(ORCID = \"0000-0001-9754-0393\")), person(\"Viliam\", \"Simko\", role=\"ctb\"), person(\"Kurt\", \"Hornik\", role=\"ctb\", comment = c(ORCID = \"0000-0003-4198-9911\")), person(\"Radford\", \"Neal\", role=\"ctb\", comment = c(ORCID = \"0000-0002-2473-3407\")), person(\"Kendon\", \"Bell\", role=\"ctb\", comment = c(ORCID = \"0000-0002-9093-8312\")), person(\"Matthew\", \"de Queljoe\", role=\"ctb\"), person(\"Dmitry\", \"Selivanov\", role=\"ctb\", comment = c(ORCID = \"0000-0003-0492-6647\")), person(\"Ion\", \"Suruceanu\", role=\"ctb\", comment = c(ORCID = \"0009-0005-6446-4909\")), person(\"Bill\", \"Denney\", role=\"ctb\", comment = c(ORCID = \"0000-0002-5759-428X\")), person(\"Dirk\", \"Schumacher\", role=\"ctb\"), person(\"András\", \"Svraka\", role=\"ctb\", comment = c(ORCID = \"0009-0008-8480-1329\")), person(\"Sergey\", \"Fedorov\", role=\"ctb\", comment = c(ORCID = \"0000-0002-5970-7233\")), person(\"Will\", \"Landau\", role=\"ctb\", comment = c(ORCID = \"0000-0003-1878-3253\")), person(\"Floris\", \"Vanderhaeghe\", role=\"ctb\", comment = c(ORCID = \"0000-0002-6378-6229\")), person(\"Kevin\", \"Tappe\", role=\"ctb\"), person(\"Harris\", \"McGehee\", role=\"ctb\"), person(\"Tim\", \"Mastny\", role=\"ctb\"), person(\"Aaron\", \"Peikert\", role=\"ctb\", comment = c(ORCID = \"0000-0001-7813-818X\")), person(\"Mark\", \"van der Loo\", role=\"ctb\", comment = c(ORCID = \"0000-0002-9807-4686\")), person(\"Chris\", \"Muir\", role=\"ctb\", comment = c(ORCID = \"0000-0003-2555-3878\")), person(\"Moritz\", \"Beller\", role=\"ctb\", comment = c(ORCID = \"0000-0003-4852-0526\")), person(\"Sebastian\", \"Campbell\", role=\"ctb\", comment = c(ORCID = \"0009-0000-5948-4503\")), person(\"Winston\", \"Chang\", role=\"ctb\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Dean\", \"Attali\", role=\"ctb\", comment = c(ORCID = \"0000-0002-5645-3493\")), person(\"Michael\", \"Chirico\", role=\"ctb\", comment = c(ORCID = \"0000-0003-0787-087X\")), person(\"Kevin\", \"Ushey\", role=\"ctb\", comment = c(ORCID = \"0000-0003-2880-7407\")), person(\"Carl\", \"Pearson\", role=\"ctb\", comment = c(ORCID = \"0000-0003-0701-7860\")))", + "Date": "2025-11-19", + "Title": "Create Compact Hash Digests of R Objects", + "Description": "Implementation of a function 'digest()' for the creation of hash digests of arbitrary R objects (using the 'md5', 'sha-1', 'sha-256', 'crc32', 'xxhash', 'murmurhash', 'spookyhash', 'blake3', 'crc32c', 'xxh3_64', and 'xxh3_128' algorithms) permitting easy comparison of R language objects, as well as functions such as 'hmac()' to create hash-based message authentication code. Please note that this package is not meant to be deployed for cryptographic purposes for which more comprehensive (and widely tested) libraries such as 'OpenSSL' should be used.", + "URL": "https://github.com/eddelbuettel/digest, https://eddelbuettel.github.io/digest/, https://dirk.eddelbuettel.com/code/digest.html", + "BugReports": "https://github.com/eddelbuettel/digest/issues", + "Depends": [ + "R (>= 3.3.0)" + ], + "Imports": [ + "utils" + ], + "License": "GPL (>= 2)", + "Suggests": [ + "tinytest", + "simplermarkdown", + "rbenchmark" + ], + "VignetteBuilder": "simplermarkdown", + "Encoding": "UTF-8", + "NeedsCompilation": "yes", + "Author": "Dirk Eddelbuettel [aut, cre] (ORCID: ), Antoine Lucas [ctb] (ORCID: ), Jarek Tuszynski [ctb], Henrik Bengtsson [ctb] (ORCID: ), Simon Urbanek [ctb] (ORCID: ), Mario Frasca [ctb], Bryan Lewis [ctb], Murray Stokely [ctb], Hannes Muehleisen [ctb] (ORCID: ), Duncan Murdoch [ctb], Jim Hester [ctb] (ORCID: ), Wush Wu [ctb] (ORCID: ), Qiang Kou [ctb] (ORCID: ), Thierry Onkelinx [ctb] (ORCID: ), Michel Lang [ctb] (ORCID: ), Viliam Simko [ctb], Kurt Hornik [ctb] (ORCID: ), Radford Neal [ctb] (ORCID: ), Kendon Bell [ctb] (ORCID: ), Matthew de Queljoe [ctb], Dmitry Selivanov [ctb] (ORCID: ), Ion Suruceanu [ctb] (ORCID: ), Bill Denney [ctb] (ORCID: ), Dirk Schumacher [ctb], András Svraka [ctb] (ORCID: ), Sergey Fedorov [ctb] (ORCID: ), Will Landau [ctb] (ORCID: ), Floris Vanderhaeghe [ctb] (ORCID: ), Kevin Tappe [ctb], Harris McGehee [ctb], Tim Mastny [ctb], Aaron Peikert [ctb] (ORCID: ), Mark van der Loo [ctb] (ORCID: ), Chris Muir [ctb] (ORCID: ), Moritz Beller [ctb] (ORCID: ), Sebastian Campbell [ctb] (ORCID: ), Winston Chang [ctb] (ORCID: ), Dean Attali [ctb] (ORCID: ), Michael Chirico [ctb] (ORCID: ), Kevin Ushey [ctb] (ORCID: ), Carl Pearson [ctb] (ORCID: )", + "Maintainer": "Dirk Eddelbuettel ", + "Repository": "CRAN" + }, "evaluate": { "Package": "evaluate", "Version": "1.0.5", diff --git a/test_loadfast.R b/test_loadfast.R index a89524f..6ca54e3 100644 --- a/test_loadfast.R +++ b/test_loadfast.R @@ -72,6 +72,22 @@ capture_conditions <- function(expr) { list(value = value, warnings = warnings, messages = messages) } +run_rscript <- function(lines) { + script_path <- tempfile("loadfast_script_", fileext = ".R") + writeLines(lines, script_path) + on.exit(unlink(script_path), add = TRUE) + output <- system2( + file.path(R.home("bin"), "Rscript"), + c("--vanilla", script_path), + stdout = TRUE, + stderr = TRUE + ) + status <- attr(output, "status") + if (is.null(status)) status <- 0L + + list(output = output, status = status) +} + copy_baseline <- function(dest) { dir.create(dest, recursive = TRUE, showWarnings = FALSE) invisible(file.copy(file.path("devpackage", "DESCRIPTION"), dest)) @@ -366,6 +382,76 @@ check("greet(Pet) works", quote( get("greet", envir = ns)(p1) == "Hello! My name is Milo and I belong to Alice" )) +# --- S4 digest compatibility with pkgload --- +repo_loadfast_path <- normalizePath(file.path("R", "loadfast.R"), mustWork = TRUE) +repo_devpackage_path <- normalizePath("devpackage", mustWork = TRUE) +repo_loadfast_path_quoted <- encodeString(repo_loadfast_path, quote = "\"") +repo_devpackage_path_quoted <- encodeString(repo_devpackage_path, quote = "\"") +s4_digest_object_line <- "obj <- animal('Rex', 'dog', 4)" +s4_digest_report_line <- paste( + "pkg_attr <- attr(class(obj), 'package')", + "cat(sprintf('HASH=%s', digest::digest(obj)), sep = '\\n')", + "cat(sprintf('PKG_ATTR=%s', if (is.null(names(pkg_attr))) 'unnamed' else 'named'), sep = '\\n')", + sep = "\n" +) + +s4_digest_loadfast <- run_rscript(c( + sprintf("suppressMessages(source(%s))", repo_loadfast_path_quoted), + sprintf("suppressMessages(load_fast(%s, helpers = FALSE, attach_testthat = FALSE, full = TRUE))", repo_devpackage_path_quoted), + s4_digest_object_line, + s4_digest_report_line +)) +s4_digest_loadall <- run_rscript(c( + sprintf("pkgload::load_all(%s, helpers = FALSE, quiet = TRUE)", repo_devpackage_path_quoted), + s4_digest_object_line, + s4_digest_report_line +)) + +s4_digest_loadfast_hash_line <- grep("^HASH=", s4_digest_loadfast$output, value = TRUE) +s4_digest_loadall_hash_line <- grep("^HASH=", s4_digest_loadall$output, value = TRUE) +s4_digest_loadfast_pkg_attr_line <- grep("^PKG_ATTR=", s4_digest_loadfast$output, value = TRUE) +s4_digest_loadall_pkg_attr_line <- grep("^PKG_ATTR=", s4_digest_loadall$output, value = TRUE) + +check("S4 digest repro: load_fast script succeeds", quote( + s4_digest_loadfast$status == 0L +)) + +check("S4 digest repro: pkgload script succeeds", quote( + s4_digest_loadall$status == 0L +)) + +check("S4 digest repro: load_fast emitted hash line", quote( + length(s4_digest_loadfast_hash_line) == 1L +)) + +check("S4 digest repro: pkgload emitted hash line", quote( + length(s4_digest_loadall_hash_line) == 1L +)) + +check("S4 digest repro: load_fast emitted package attr line", quote( + length(s4_digest_loadfast_pkg_attr_line) == 1L +)) + +check("S4 digest repro: pkgload emitted package attr line", quote( + length(s4_digest_loadall_pkg_attr_line) == 1L +)) + +check("S4 digest repro: load_fast leaves unnamed package attr", quote( + length(s4_digest_loadfast_pkg_attr_line) == 1L && + identical(s4_digest_loadfast_pkg_attr_line[[1L]], "PKG_ATTR=unnamed") +)) + +check("S4 digest repro: pkgload leaves unnamed package attr", quote( + length(s4_digest_loadall_pkg_attr_line) == 1L && + identical(s4_digest_loadall_pkg_attr_line[[1L]], "PKG_ATTR=unnamed") +)) + +check("S4 digest repro: load_fast matches pkgload hash", quote( + length(s4_digest_loadfast_hash_line) == 1L && + length(s4_digest_loadall_hash_line) == 1L && + identical(s4_digest_loadfast_hash_line[[1L]], s4_digest_loadall_hash_line[[1L]]) +)) + # --- R6 classes --- check("Logger R6 generator exists in namespace", quote( exists("Logger", envir = ns, inherits = FALSE)