Skip to content

Commit

Permalink
[r] Make TileDBArray$reopen() public
Browse files Browse the repository at this point in the history
Expose `TileDBArray$reopen()` as a public API; as R does not have
context managers, there's no native way to do the Python construct
```python
cls = type(arr)
with cls.open(arr.uri, "r") as readarr:
    # do a read operation even though the array is open in write
```
This PR exposes `$reopen()` to reopen an array in a new mode

Modified SOMA methods:

 - `TileDBArray$reopen()`: now public and includes new default for
   reopening: if `arr$mode()` is `READ`, will automatically reopen in
   `WRITE` and vice versa
 - `TileDBObject$is_open()`: now uses `TileDBObject$mode()` to determine
   if the array is open as `TileDBObject$mode()` already processes
   `TileDBObject$private$.mode`
  • Loading branch information
mojaveazure committed Apr 5, 2024
1 parent f1fb30b commit a0624f8
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 18 deletions.
4 changes: 2 additions & 2 deletions apis/r/R/SOMADataFrame.R
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ SOMADataFrame <- R6::R6Class(
# - validate number of rows in values matches number of rows in array
# - add original soma_joinids to values if not present
spdl::debug("[SOMADataFrame update]: Retrieving existing soma_joinids")
private$reopen(mode = "READ")
self$reopen(mode = "READ")
joinids <- self$read(column_names = "soma_joinid")$concat()$soma_joinid
if (length(joinids) != nrow(values)) {
stop(
Expand Down Expand Up @@ -392,7 +392,7 @@ SOMADataFrame <- R6::R6Class(
se <- tiledb::tiledb_array_schema_evolution_array_evolve(se, self$uri)

# Reopen array for writing with new schema
private$reopen(mode = "WRITE")
self$reopen(mode = "WRITE")
spdl::info("[SOMADataFrame update]: Writing new data")
self$write(values)
}
Expand Down
45 changes: 34 additions & 11 deletions apis/r/R/TileDBArray.R
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,29 @@ TileDBArray <- R6::R6Class(
invisible(self)
},

#' @description Close and reopen the TileDB object in a new mode
#'
#' @param mode New mode to open the object in; choose from:
#' \itemize{
#' \item \dQuote{\code{READ}}
#' \item \dQuote{\code{WRITE}}
#' }
#' By default, reopens in the opposite mode of the current mode
#'
#' @return Invisibly returns \code{self}
#'
reopen = function(mode = NULL) {
modes <- c(READ = 'WRITE', WRITE = 'READ')
oldmode <- self$mode()
mode <- mode %||% modes[oldmode]
mode <- match.arg(mode, choices = modes)
if (mode != oldmode) {
self$close()
self$open(mode, internal_use_only = 'allowed_use')
}
return(invisible(self))
},

#' @description Print summary of the array. (lifecycle: experimental)
print = function() {
super$print()
Expand Down Expand Up @@ -289,17 +312,17 @@ TileDBArray <- R6::R6Class(

private = list(

# Reopen the array in a different mode
reopen = function(mode = c("READ", "WRITE")) {
mode <- match.arg(mode)
if (private$.mode == mode) {
return(invisible(self))
}
self$close()
invisible(
self$open(mode = mode, internal_use_only = "allowed_use")
)
},
# # Reopen the array in a different mode
# reopen = function(mode = c("READ", "WRITE")) {
# mode <- match.arg(mode)
# if (private$.mode == mode) {
# return(invisible(self))
# }
# self$close()
# invisible(
# self$open(mode = mode, internal_use_only = "allowed_use")
# )
# },

# Internal pointer to the TileDB array.
#
Expand Down
2 changes: 1 addition & 1 deletion apis/r/R/TileDBObject.R
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ TileDBObject <- R6::R6Class(
#' @return \code{TRUE} if the object is open, otherwise \code{FALSE}
#'
is_open = function() {
!is.null(private$.mode)
return(self$mode() != 'CLOSED')
},

# TODO: make this an active
Expand Down
1 change: 1 addition & 0 deletions apis/r/man/SOMAArrayBase.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apis/r/man/SOMADataFrame.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apis/r/man/SOMADenseNDArray.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apis/r/man/SOMANDArrayBase.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apis/r/man/SOMASparseNDArray.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions apis/r/man/TileDBArray.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions apis/r/tests/testthat/test-SeuratIngest.R
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ test_that("Write SeuratCommand mechanics", {

uri <- withr::local_tempdir('write-command-log')
uns <- SOMACollectionCreate(uri)
on.exit(uns$close, add = TRUE)
on.exit(uns$close(), add = TRUE, after = FALSE)

pbmc_small <- get_data('pbmc_small', package = 'SeuratObject')
for (cmd in SeuratObject::Command(pbmc_small)) {
Expand All @@ -214,9 +214,7 @@ test_that("Write SeuratCommand mechanics", {
expect_s3_class(cmdgrp <- uns$get('seurat_commands'), 'SOMACollection')

expect_s3_class(cmddf <- cmdgrp$get(cmd), 'SOMADataFrame')
expect_identical(cmddf$mode(), 'CLOSED')

expect_no_condition(cmddf <- cmddf$open('READ', internal_use_only = 'allowed_use'))
expect_invisible(cmddf$reopen("READ"))
on.exit(cmddf$close(), add = TRUE, after = FALSE)

# Test qualities of the SOMADataFrame
Expand Down
99 changes: 99 additions & 0 deletions apis/r/tests/testthat/test-reopen.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
test_that("Test reopen works on arrays", {
shape <- c(500L, 100L)
for (cls in c("SOMADataFrame", "SOMASparseNDArray", "SOMADenseNDArray")) {
uri <- withr::local_tempdir(paste("soma", cls, "array", "reopen", sep = "-"))
arr <- switch(
EXPR = cls,
SOMADataFrame = SOMADataFrameCreate(
uri,
schema = arrow::infer_schema(data.frame(
soma_joinid = bit64::integer64(),
int = integer()
))
),
SOMASparseNDArray = SOMASparseNDArrayCreate(
uri,
type = arrow::int32(),
shape = shape
),
SOMADenseNDArray = SOMADenseNDArrayCreate(
uri,
type = arrow::int32(),
shape = shape
)
)
expect_s3_class(arr, cls)
expect_identical(
arr$mode(),
"WRITE",
info = sprintf("%sCreate() returns object open for 'WRITE'", cls)
)
expect_true(
arr$is_open(),
info = sprintf("%s is open when mode is 'WRITE'", cls)
)

lab <- sprintf("%s$reopen()", cls)
is_open <- sprintf("%s$reopen() returns an object object", cls)

# Test implicit WRITE -> READ
expect_invisible(arr$reopen(), label = lab)
expect_identical(
arr$mode(),
"READ",
info = sprintf("%s$reopen() when mode is 'WRITE' reopens as 'READ'", cls)
)
expect_true(arr$is_open(), info = is_open)

# Test implicit READ -> WRITE
expect_invisible(arr$reopen(), label = lab)
expect_identical(
arr$mode(),
"WRITE",
info = sprintf("%s$reopen() when mode is 'READ' reopens as 'WRITE'", cls)
)
expect_true(arr$is_open(), info = is_open)

# Test reopening in the same mode
orig <- arr$mode()
expect_invisible(arr$reopen(orig), label = lab)
expect_identical(
arr$mode(),
orig,
info = sprintf("%s$reopen() with an identical mode returns the same mode", cls)
)
expect_true(arr$is_open(), info = is_open)

# Test reopen from close
expect_no_condition(arr$close())
expect_identical(arr$mode(), "CLOSED")
expect_false(arr$is_open())

for (mode in c("READ", "WRITE")) {
expect_invisible(
arr$reopen(mode),
label = sprintf("%s$reopen('%s') from closed", cls, mode)
)
expect_identical(
arr$mode(),
mode,
info = sprintf("%s$reopen('%s') returns a mode of '%s'", cls, mode, mode)
)
expect_true(
arr$is_open(),
info = sprintf("%s$reopen('%s') from CLOSED returns an open object", cls, mode)
)
expect_no_condition(arr$close())
}

arr$close()
expect_error(arr$reopen())

# Test assertions
expect_error(arr$reopen("tomato"))
expect_error(arr$reopen(TRUE))
expect_error(arr$reopen(1L))

arr$close()
}
})

0 comments on commit a0624f8

Please sign in to comment.