Skip to content

Commit 77da5fa

Browse files
frenkiboyclaude
andcommitted
Replace qs backend with qs2
The qs package was removed from CRAN; its successor qs2 is actively maintained and available on CRAN with improved performance. - DESCRIPTION: qs -> qs2 in Imports - R/cacheFile.R: backend "qs" -> "qs2", qs::qsave/qread -> qs2::qs_save/qs_read - R/zzz.R: .onLoad defaults to qs2 backend when available - R/utils.R: file patterns .qs -> .qs2, cacheInfo uses qs2::qs_read - Tests: updated backend names, patterns, and manual load calls - Vignette: updated documentation and examples Note: qs2 format is NOT backward-compatible with old .qs files. Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent aee956a commit 77da5fa

10 files changed

Lines changed: 52 additions & 52 deletions

File tree

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Imports:
2121
digest,
2222
tools,
2323
codetools,
24-
qs,
24+
qs2,
2525
compiler,
2626
filelock
2727
Suggests:

R/cacheFile.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ cacheFile <- function(cache_dir = NULL,
833833
warning("cacheR: cache directory is not writable: ", cache_dir, call. = FALSE)
834834
}
835835
cache_dir <- normalizePath(cache_dir, mustWork = FALSE)
836-
backend <- match.arg(backend, c("rds", "qs"))
836+
backend <- match.arg(backend, c("rds", "qs2"))
837837
if (is.null(env_vars)) env_vars <- getOption("cacheR.env_vars", NULL)
838838

839839
# Sync Graph from disk on init
@@ -1146,7 +1146,7 @@ cachePrune <- function(cache_dir, days_old = 30) {
11461146
if (!dir.exists(cache_dir)) return(invisible(NULL))
11471147

11481148
# Prune old cache files
1149-
cache_files <- list.files(cache_dir, pattern = "\\.(rds|qs)$", full.names = TRUE)
1149+
cache_files <- list.files(cache_dir, pattern = "\\.(rds|qs2)$", full.names = TRUE)
11501150
if (length(cache_files) > 0) {
11511151
infos <- file.info(cache_files)
11521152
cutoff <- Sys.time() - (days_old * 24 * 60 * 60)
@@ -1236,7 +1236,7 @@ export_targets_file <- function(path = "_targets.R") {
12361236
.atomic_save <- function(object, path, backend) {
12371237
tmp_path <- paste0(path, ".tmp.", paste(sample(c(letters, 0:9), 8, replace=TRUE), collapse=""))
12381238
tryCatch({
1239-
if (backend == "qs") { if (!requireNamespace("qs", quietly=TRUE)) stop("qs required"); qs::qsave(object, tmp_path) }
1239+
if (backend == "qs2") { if (!requireNamespace("qs2", quietly=TRUE)) stop("qs2 required"); qs2::qs_save(object, tmp_path) }
12401240
else { saveRDS(object, tmp_path) }
12411241
file.rename(tmp_path, path)
12421242
}, error = function(e) {
@@ -1245,7 +1245,7 @@ export_targets_file <- function(path = "_targets.R") {
12451245
})
12461246
}
12471247
.safe_load <- function(path, backend) {
1248-
if (backend == "qs") { if (!requireNamespace("qs", quietly=TRUE)) stop("qs required"); qs::qread(path) }
1248+
if (backend == "qs2") { if (!requireNamespace("qs2", quietly=TRUE)) stop("qs2 required"); qs2::qs_read(path) }
12491249
else { readRDS(path) }
12501250
}
12511251
.get_recursive_closure_hash <- function(obj, visited=NULL, algo="xxhash64", version_checker=utils::packageVersion) {

R/utils.R

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#' file was cached, checking the arguments used to generate it, or auditing
77
#' creation times.
88
#'
9-
#' @param file_path A character string specifying the path to the `.rds` or `.qs`
9+
#' @param file_path A character string specifying the path to the `.rds` or `.qs2`
1010
#' cache file.
1111
#'
1212
#' @return A list containing two elements:
@@ -29,10 +29,10 @@ cacheInfo <- function(file_path) {
2929
if (!file.exists(file_path)) stop("Cache file not found: ", file_path)
3030

3131
# Determine backend based on extension (simple heuristic)
32-
is_qs <- grepl("\\.qs$", file_path, ignore.case = TRUE)
33-
34-
obj <- if (is_qs && requireNamespace("qs", quietly = TRUE)) {
35-
qs::qread(file_path)
32+
is_qs2 <- grepl("\\.qs2$", file_path, ignore.case = TRUE)
33+
34+
obj <- if (is_qs2 && requireNamespace("qs2", quietly = TRUE)) {
35+
qs2::qs_read(file_path)
3636
} else {
3737
readRDS(file_path)
3838
}
@@ -87,7 +87,7 @@ cacheList <- function(cache_dir) {
8787
return(data.frame())
8888
}
8989

90-
files <- list.files(cache_dir, pattern = "\\.(rds|qs)$", full.names = TRUE)
90+
files <- list.files(cache_dir, pattern = "\\.(rds|qs2)$", full.names = TRUE)
9191

9292
if (length(files) == 0) return(data.frame())
9393

@@ -139,7 +139,7 @@ cacheList <- function(cache_dir) {
139139
cache_stats <- function(cache_dir) {
140140
if (!dir.exists(cache_dir)) stop("Cache directory not found: ", cache_dir)
141141

142-
files <- list.files(cache_dir, pattern = "\\.(rds|qs)$", full.names = TRUE)
142+
files <- list.files(cache_dir, pattern = "\\.(rds|qs2)$", full.names = TRUE)
143143
# Exclude graph.rds
144144
files <- files[!grepl("^graph\\.rds$", basename(files))]
145145

R/zzz.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@
4242

4343
# 2. Set defaults for anything still unset
4444
if (is.null(getOption("cacheR.backend"))) {
45-
if (requireNamespace("qs", quietly = TRUE)) {
46-
options(cacheR.backend = "qs")
45+
if (requireNamespace("qs2", quietly = TRUE)) {
46+
options(cacheR.backend = "qs2")
4747
} else {
4848
packageStartupMessage(
49-
"Package 'qs' not installed; using RDS backend.\n",
50-
"Install qs for faster caching: install.packages('qs')"
49+
"Package 'qs2' not installed; using RDS backend.\n",
50+
"Install qs2 for faster caching: install.packages('qs2')"
5151
)
5252
options(cacheR.backend = "rds")
5353
}

tests/testthat/test-cacheFile.R

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# --------------------------------------------------------#
22
# Helper: Count cache entry files, excluding graph.rds
33
# --------------------------------------------------------#
4-
count_cache_entries <- function(cache_dir, backend_pattern = "\\.(rds|qs)$") {
4+
count_cache_entries <- function(cache_dir, backend_pattern = "\\.(rds|qs2)$") {
55
files <- list.files(cache_dir, pattern = backend_pattern)
66
length(files[!grepl("^graph\\.rds", files)])
77
}
@@ -112,28 +112,28 @@ test_that("cacheFile tracks multiple dir arguments and vector paths", {
112112
})
113113

114114
# --------------------------------------------------------#
115-
test_that("cacheFile works with 'qs' backend", {
116-
skip_if_not_installed("qs")
115+
test_that("cacheFile works with 'qs2' backend", {
116+
skip_if_not_installed("qs2")
117117
if (exists("cacheTree_reset", mode = "function")) cacheTree_reset()
118118

119-
cache_dir <- file.path(tempdir(), "cache_qs_backend")
119+
cache_dir <- file.path(tempdir(), "cache_qs2_backend")
120120
unlink(cache_dir, recursive = TRUE)
121121
dir.create(cache_dir, showWarnings = FALSE)
122122

123-
f <- cacheFile(cache_dir, backend = "qs") %@% function(x) {
123+
f <- cacheFile(cache_dir, backend = "qs2") %@% function(x) {
124124
x * 2
125125
}
126126

127127
res <- f(10)
128128
expect_equal(res, 20)
129129

130-
# Verify the file extension is actually .qs
130+
# Verify the file extension is actually .qs2
131131
files <- list.files(cache_dir)
132-
expect_true(any(grepl("\\.qs$", files)))
133-
132+
expect_true(any(grepl("\\.qs2$", files)))
133+
134134
# Verify we can load it back manually
135135
cache_path <- file.path(cache_dir, files[1])
136-
loaded <- qs::qread(cache_path)
136+
loaded <- qs2::qs_read(cache_path)
137137
expect_equal(loaded$value, 20)
138138
})
139139

@@ -297,11 +297,11 @@ test_that("backend selection works", {
297297
cached_rds(1)
298298
expect_true(any(grepl("\\.rds$", list.files(cache_dir))))
299299

300-
# QS
301-
if (requireNamespace("qs", quietly = TRUE)) {
302-
cached_qs <- cacheFile(cache_dir, backend = "qs") %@% function(x) x
303-
cached_qs(2)
304-
expect_true(any(grepl("\\.qs$", list.files(cache_dir))))
300+
# QS2
301+
if (requireNamespace("qs2", quietly = TRUE)) {
302+
cached_qs2 <- cacheFile(cache_dir, backend = "qs2") %@% function(x) x
303+
cached_qs2(2)
304+
expect_true(any(grepl("\\.qs2$", list.files(cache_dir))))
305305
}
306306
})
307307

@@ -1507,7 +1507,7 @@ test_that("cache_stats returns correct aggregate statistics", {
15071507
st2 <- cache_stats(tmp)
15081508
expect_equal(st2$n_entries, 3L)
15091509
# Files are small, so total_size_mb may round to 0 — check raw file sizes instead
1510-
files <- list.files(tmp, pattern = "\\.(rds|qs)$", full.names = TRUE)
1510+
files <- list.files(tmp, pattern = "\\.(rds|qs2)$", full.names = TRUE)
15111511
files <- files[!grepl("graph\\.rds$", files)]
15121512
expect_true(sum(file.size(files)) > 0)
15131513
expect_true(!is.na(st2$oldest))
@@ -1616,7 +1616,7 @@ test_that(".load_cacheR_config does not override existing options", {
16161616
on.exit(unlink(tmp, recursive = TRUE))
16171617

16181618
yml_path <- file.path(tmp, ".cacheR.yml")
1619-
writeLines("backend: qs", yml_path)
1619+
writeLines("backend: qs2", yml_path)
16201620

16211621
withr::with_options(list(cacheR.backend = "rds"), {
16221622
cacheR:::.load_cacheR_config(yml_path)

tests/testthat/test-files.R

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ reset_if_needed <- function() {
1212
# --------------------------------------------------------#
1313
# Helper: Count cache entry files, excluding graph.rds
1414
# --------------------------------------------------------#
15-
count_cache_entries <- function(cache_dir, backend_pattern = "\\.(rds|qs)$") {
15+
count_cache_entries <- function(cache_dir, backend_pattern = "\\.(rds|qs2)$") {
1616
files <- list.files(cache_dir, pattern = backend_pattern)
1717
length(files[!grepl("^graph\\.rds", files)])
1818
}
@@ -923,7 +923,7 @@ test_that("Vector of file paths invalidates cache when any file changes", {
923923
on.exit(unlink(c(file1, file2)), add = TRUE)
924924

925925
# Exclude graph.rds from count
926-
count_cache <- function(d) length(grep("^graph\\.", list.files(d, pattern = "\\.(rds|qs)$"), invert = TRUE, value = TRUE))
926+
count_cache <- function(d) length(grep("^graph\\.", list.files(d, pattern = "\\.(rds|qs2)$"), invert = TRUE, value = TRUE))
927927

928928
f <- cacheFile(cache_dir = cache_dir) %@% function(paths) {
929929
paste(vapply(paths, function(p) readLines(p, warn = FALSE), character(1)), collapse = "|")
@@ -954,7 +954,7 @@ test_that("Vector of mixed file paths and regular strings invalidates on file ch
954954
on.exit(unlink(file1), add = TRUE)
955955

956956
# Exclude graph.rds from count
957-
count_cache <- function(d) length(grep("^graph\\.", list.files(d, pattern = "\\.(rds|qs)$"), invert = TRUE, value = TRUE))
957+
count_cache <- function(d) length(grep("^graph\\.", list.files(d, pattern = "\\.(rds|qs2)$"), invert = TRUE, value = TRUE))
958958

959959
f <- cacheFile(cache_dir = cache_dir) %@% function(items) {
960960
paste(items, collapse = ",")
@@ -987,7 +987,7 @@ test_that("Vector of file paths works with hash_file_paths = FALSE (portable mod
987987
file_B <- file.path(dir_B, "data.txt"); writeLines("SAME", file_B)
988988

989989
# Exclude graph.rds from count
990-
count_cache <- function(d) length(grep("^graph\\.", list.files(d, pattern = "\\.(rds|qs)$"), invert = TRUE, value = TRUE))
990+
count_cache <- function(d) length(grep("^graph\\.", list.files(d, pattern = "\\.(rds|qs2)$"), invert = TRUE, value = TRUE))
991991

992992
cache_dir <- file.path(base_dir, "cache")
993993

@@ -1023,7 +1023,7 @@ test_that("Vector of directory paths invalidates when dir content changes", {
10231023
file.create(file.path(dir2, "b.txt"))
10241024

10251025
# Exclude graph.rds from count
1026-
count_cache <- function(d) length(grep("^graph\\.", list.files(d, pattern = "\\.(rds|qs)$"), invert = TRUE, value = TRUE))
1026+
count_cache <- function(d) length(grep("^graph\\.", list.files(d, pattern = "\\.(rds|qs2)$"), invert = TRUE, value = TRUE))
10271027

10281028
f <- cacheFile(cache_dir = cache_dir) %@% function(dirs) {
10291029
sum(vapply(dirs, function(d) length(list.files(d)), integer(1)))

tests/testthat/test-graph.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
test_that("helpers: count cache artifacts", {
22
count_cache_files <- function(cache_dir) {
33
# Only count final cache objects, not locks/tmp
4-
length(list.files(cache_dir, pattern = "\\.(rds|qs)$", full.names = TRUE))
4+
length(list.files(cache_dir, pattern = "\\.(rds|qs2)$", full.names = TRUE))
55
}
66
expect_true(is.function(count_cache_files))
77
})
@@ -152,7 +152,7 @@ test_that("complex graph export: two Y-shapes merging into a final step", {
152152
test_that("helpers: count cache artifacts", {
153153
count_cache_files <- function(cache_dir) {
154154
# Only count final cache objects, not locks/tmp
155-
length(list.files(cache_dir, pattern = "\\.(rds|qs)$", full.names = TRUE))
155+
length(list.files(cache_dir, pattern = "\\.(rds|qs2)$", full.names = TRUE))
156156
}
157157
expect_true(is.function(count_cache_files))
158158
})

tests/testthat/test-metadata.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ test_that("cacheFile stores value and metadata in cache file", {
1515
# First call – should compute and write cache
1616
expect_equal(cached(10), 11)
1717

18-
# Filter for .rds or .qs files only, ignoring .lock files
19-
files <- list.files(cache_dir, pattern = "\\.(rds|qs)$", full.names = TRUE)
18+
# Filter for .rds or .qs2 files only, ignoring .lock files
19+
files <- list.files(cache_dir, pattern = "\\.(rds|qs2)$", full.names = TRUE)
2020
expect_length(files, 1L)
2121

2222
obj <- readRDS(files[1])
@@ -84,7 +84,7 @@ test_that("cacheInfo returns value and metadata", {
8484
# Generate cache
8585
cached(3)
8686

87-
files <- list.files(cache_dir, full.names = TRUE, pattern="\\.(rds|qs)$")
87+
files <- list.files(cache_dir, full.names = TRUE, pattern="\\.(rds|qs2)$")
8888
expect_length(files, 1L)
8989

9090
# Test cacheInfo

tests/testthat/test-recursive-deps.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ test_that("cacheFile respects package boundaries (does not recurse into base/pac
7979

8080
expect_equal(f(c(1, 2, 3)), 2)
8181

82-
# Count only cache files (rds/qs), ignore .lock
82+
# Count only cache files (rds/qs2), ignore .lock
8383
backend <- getOption("cacheR.backend", "rds")
8484
files <- list.files(cache_dir, pattern = paste0("\\.", backend, "$"))
8585
expect_length(files, 1)

vignettes/cacheR-introduction.Rmd

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -262,22 +262,22 @@ plot_cache_graph(output = "cache_graph.png")
262262

263263
---
264264

265-
# 5. Using the `qs` backend for faster serialization
265+
# 5. Using the `qs2` backend for faster serialization
266266

267-
For large matrices, the [`qs`](https://github.com/qsbase/qs) backend can be faster than base RDS.
267+
For large matrices, the [`qs2`](https://github.com/qsbase/qs2) backend can be faster than base RDS.
268268

269269
Install it once:
270270

271271
```{r, eval=FALSE}
272-
install.packages("qs")
272+
install.packages("qs2")
273273
```
274274

275-
Then create a cached function with `backend = "qs"`:
275+
Then create a cached function with `backend = "qs2"`:
276276

277277
```{r}
278-
cached_cor_qs <- cacheFile(
278+
cached_cor_qs2 <- cacheFile(
279279
cache_dir = cache_dir,
280-
backend = "qs"
280+
backend = "qs2"
281281
) %@% function(mat) {
282282
cor_with_moments(mat)
283283
}
@@ -289,14 +289,14 @@ Compare timings:
289289
set.seed(999)
290290
mat_big <- matrix(rnorm(500 * 50), ncol = 50)
291291
292-
system.time(r1 <- cached_cor_qs(mat_big))
293-
system.time(r2 <- cached_cor_qs(mat_big))
292+
system.time(r1 <- cached_cor_qs2(mat_big))
293+
system.time(r2 <- cached_cor_qs2(mat_big))
294294
295295
identical(r1$cor, r2$cor)
296296
list.files(cache_dir, recursive = TRUE)
297297
```
298298

299-
You should see some `.qs` files in the cache directory.
299+
You should see some `.qs2` files in the cache directory.
300300

301301
---
302302

0 commit comments

Comments
 (0)