Skip to content

Commit ab4a43e

Browse files
Merge pull request #6875 from Rdatatable/reset_class_as.data.frame
force class reset in as.data.table.data.frame
2 parents 6aa99f5 + 0e53a5a commit ab4a43e

File tree

5 files changed

+20
-2
lines changed

5 files changed

+20
-2
lines changed

NEWS.md

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
4. `as.data.table()` now properly handles keys: specifying keys sets them, omitting keys preserves existing ones, and setting `key=NULL` clears them, [#6859](https://github.com/Rdatatable/data.table/issues/6859). Thanks @brookslogan for the report and @Mukulyadav2004 for the fix.
1818

19+
5. `as.data.table()` on `x` avoids an infinite loop if the output of the corresponding `as.data.frame()` method has the same class as the input, [#6874](https://github.com/Rdatatable/data.table/issues/6874). Concretely, we had `class(x) = c('foo', 'data.frame')` and `class(as.data.frame(x)) = c('foo', 'data.frame')`, so `as.data.frame.foo` wound up getting called repeatedly. Thanks @matschmitz for the report and @ben-schwen for the fix.
20+
1921
## NOTES
2022

2123
1. Continued work to remove non-API C functions, [#6180](https://github.com/Rdatatable/data.table/issues/6180). Thanks Ivan Krylov for the PRs and for writing a clear and concise guide about the R API: https://aitap.codeberg.page/R-api/.

R/as.data.table.R

+6-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,12 @@ as.data.table.list = function(x,
215215

216216
as.data.table.data.frame = function(x, keep.rownames=FALSE, key=NULL, ...) {
217217
if (is.data.table(x)) return(as.data.table.data.table(x, key=key)) # S3 is weird, #6739. Also # nocov; this is tested in 2302.{2,3}, not sure why it doesn't show up in coverage.
218-
if (!identical(class(x), "data.frame")) return(as.data.table(as.data.frame(x), keep.rownames=keep.rownames, key=key, ...))
218+
if (!identical(class(x), "data.frame")) {
219+
class_orig = class(x)
220+
x = as.data.frame(x)
221+
if (identical(class(x), class_orig)) setattr(x, "class", "data.frame") # cater for cases when as.data.frame can generate a loop #6874
222+
return(as.data.table.data.frame(x, keep.rownames=keep.rownames, key=key, ...))
223+
}
219224
if (!isFALSE(keep.rownames)) {
220225
# can specify col name to keep.rownames, #575; if it's the same as key,
221226
# kludge it to 'rn' since we only apply the new name afterwards, #4468

inst/tests/S4.Rraw

+6
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,9 @@ setMethod(`%foo%`, c("Date", "CustomDurationClass"), function (e1, e2) e1 - e2@.
126126
test(8, as.IDate("2025-03-01") %foo% CustomDurationClass(1), as.IDate("2025-02-28"))
127127
removeGeneric("%foo%")
128128
removeClass("CustomDurationClass")
129+
130+
# data.table(s4) #6874 should work
131+
s4cl = setClass("s4cl", slots=list(x="integer"))
132+
DT = setalloccol(structure(list(a=new("s4cl", x=1L)), row.names=c(NA, -1L), class=c("data.table", "data.frame")))
133+
test(9, data.table(a=s4cl(x=1L)), DT)
134+
removeClass("s4cl")

inst/tests/tests.Rraw

+5
Original file line numberDiff line numberDiff line change
@@ -21104,3 +21104,8 @@ DT = data.table(a = 1:5, b = 1:5, x = 1:5)
2110421104
test(2309.06, key(as.data.table(DT, key="a")), "a")
2110521105
test(2309.07, key(as.data.table(DT)), NULL)
2110621106
test(2309.08, key(as.data.table(DT, key=NULL)), NULL)
21107+
21108+
# as.data.frame(x) does not reset class(x) to "data.frame" #6874
21109+
as.data.frame.no.reset = function(x) x
21110+
DF = structure(list(a = 1:2), class = c("data.frame", "no.reset"), row.names = c(NA, -2L))
21111+
test(2310.01, as.data.table(DF), data.table(a=1:2))

src/assign.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ SEXP setdt_nrows(SEXP x)
232232
}
233233
len_xi = INTEGER(dim_xi)[0];
234234
} else {
235-
len_xi = LENGTH(xi);
235+
len_xi = length(xi);
236236
}
237237
if (!base_length) {
238238
base_length = len_xi;

0 commit comments

Comments
 (0)