This is an automated email from the ASF dual-hosted git repository.
paleolimbot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-nanoarrow.git
The following commit(s) were added to refs/heads/main by this push:
new ef5cd2f5 fix(r): Convert `I()`-wrapped vectors without arrow installed
(#954)
ef5cd2f5 is described below
commit ef5cd2f5ed2a794b5f0ad58d80eb2b1f2b6ae4bd
Author: Nicolas Bennett <[email protected]>
AuthorDate: Mon Sep 28 20:59:46 2026 -0500
fix(r): Convert `I()`-wrapped vectors without arrow installed (#954)
## Summary
- Add an `as_nanoarrow_array()` method for `AsIs` that strips the class
and dispatches again, mirroring the `infer_nanoarrow_schema()` method.
An `I()`-wrapped base vector, such as a list column kept whole in a
`data.frame()`, no longer falls through to the arrow fallback.
- The method does not forward `...`: when the C conversion calls back
for a data frame column, `...` carries `.from_c = TRUE`, which would
still route the unwrapped value to arrow.
- Both `AsIs` methods, including the existing schema one, remove only
the `AsIs` class, as base R's `print.AsIs()` does. Removing the first
class instead is the same for anything `I()` returns, but otherwise
drops whatever class comes ahead of `AsIs`, so a `c("Date", "AsIs")`
value reaching the array method through `NextMethod()` would be
converted to int32 as its day count.
- Values whose class has its own method, such as `I(as.Date(...))` or an
`I()`-wrapped blob, convert as before, with or without arrow. An
`I()`-wrapped matrix, which failed even with arrow installed, and an
`I()`-wrapped `integer64` with missing values, which needed arrow, now
convert too.
- The array test sets the `nanoarrow.without_arrow` option, so it fails
on main even where arrow is installed, as it is in CI.
Fixes #953
---
r/NAMESPACE | 1 +
r/R/as-array.R | 7 +++++++
r/R/schema.R | 2 +-
r/tests/testthat/test-as-array.R | 20 ++++++++++++++++++++
r/tests/testthat/test-schema.R | 6 ++++++
5 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/r/NAMESPACE b/r/NAMESPACE
index cb27c9ae..dbd9d07d 100644
--- a/r/NAMESPACE
+++ b/r/NAMESPACE
@@ -23,6 +23,7 @@ S3method(as.vector,nanoarrow_array)
S3method(as.vector,nanoarrow_array_stream)
S3method(as.vector,nanoarrow_buffer)
S3method(as_nanoarrow_array,Array)
+S3method(as_nanoarrow_array,AsIs)
S3method(as_nanoarrow_array,ChunkedArray)
S3method(as_nanoarrow_array,Date)
S3method(as_nanoarrow_array,POSIXct)
diff --git a/r/R/as-array.R b/r/R/as-array.R
index c45fdf24..54bf7cef 100644
--- a/r/R/as-array.R
+++ b/r/R/as-array.R
@@ -196,6 +196,13 @@ as_nanoarrow_array.blob <- function(x, ..., schema = NULL)
{
as_nanoarrow_array(unclass(x), schema = schema)
}
+#' @export
+as_nanoarrow_array.AsIs <- function(x, ..., schema = NULL) {
+ # NextMethod() goes directly to `default`
+ class(x) <- setdiff(class(x), "AsIs")
+ as_nanoarrow_array(x, schema = schema)
+}
+
#' @export
as_nanoarrow_array.matrix <- function(x, ..., schema = NULL) {
if (is.null(schema)) {
diff --git a/r/R/schema.R b/r/R/schema.R
index 764181a4..31211f6b 100644
--- a/r/R/schema.R
+++ b/r/R/schema.R
@@ -174,7 +174,7 @@ infer_nanoarrow_schema.vctrs_list_of <- function(x, ...) {
#' @export
infer_nanoarrow_schema.AsIs <- function(x, ...) {
# NextMethod() goes directly to `default`
- class(x) <- class(x)[-1]
+ class(x) <- setdiff(class(x), "AsIs")
infer_nanoarrow_schema(x)
}
diff --git a/r/tests/testthat/test-as-array.R b/r/tests/testthat/test-as-array.R
index 7c301e0b..adfe92c9 100644
--- a/r/tests/testthat/test-as-array.R
+++ b/r/tests/testthat/test-as-array.R
@@ -847,6 +847,26 @@ test_that("as_nanoarrow_array() works for
list(integer64()) -> na_list(na_int64(
)
})
+test_that("as_nanoarrow_array() works for AsIs", {
+ withr::with_options(list(nanoarrow.without_arrow = TRUE), {
+ array <- as_nanoarrow_array(I(c(1.5, NA)))
+ expect_identical(infer_nanoarrow_schema(array)$format, "g")
+ expect_identical(convert_array(array), c(1.5, NA))
+
+ array <- as_nanoarrow_array(I(1:3), schema = na_double())
+ expect_identical(convert_array(array), c(1, 2, 3))
+
+ array <- as_nanoarrow_array(data.frame(x = I(list(as.raw(1:3), NULL))))
+ expect_identical(infer_nanoarrow_schema(array$children$x)$format, "z")
+ expect_identical(array$children$x$null_count, 1L)
+ expect_identical(as.raw(array$children$x$buffers[[3]]), as.raw(1:3))
+
+ # Only AsIs is stripped, not the Date class ahead of it
+ x <- structure(18262, class = c("Date", "AsIs"))
+ expect_error(as_nanoarrow_array(x, schema = na_int32()), "object of type
Date")
+ })
+})
+
test_that("as_nanoarrow_array() works for unspecified() -> na_na()", {
skip_if_not_installed("vctrs")
diff --git a/r/tests/testthat/test-schema.R b/r/tests/testthat/test-schema.R
index d7a1d7f3..708b1bed 100644
--- a/r/tests/testthat/test-schema.R
+++ b/r/tests/testthat/test-schema.R
@@ -109,6 +109,12 @@ test_that("infer_nanoarrow_schema() method works for
AsIs", {
infer_nanoarrow_schema(I(integer()))$format,
infer_nanoarrow_schema(integer())$format
)
+
+ # Only AsIs is stripped, not the class ahead of it
+ expect_error(
+ infer_nanoarrow_schema(structure(integer(), class = c("foo", "AsIs"))),
+ "class foo"
+ )
})
test_that("infer_nanoarrow_schema() returns list of null for empty or all null
list", {