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", {

Reply via email to