Skip to content

Commit a105351

Browse files
authored
Guard against loss of class (#567)
* Apply the "spec_tbl_df" class *after* `vroom_select()` Avoid potential dispatch of a `[.spec_tbl_df` method that prematurely drops the subclass and spec and problems attributes * No longer necessary to store / restore this * Add tests
1 parent d96a3b5 commit a105351

File tree

4 files changed

+96
-4
lines changed

4 files changed

+96
-4
lines changed

R/col_types.R

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,8 +374,6 @@ vroom_enquo <- function(x) {
374374
}
375375

376376
vroom_select <- function(x, col_select, id) {
377-
spec <- attr(x, "spec")
378-
379377
# reorder and rename columns
380378
if (inherits(col_select, "quosures") || !quo_is_null(col_select)) {
381379
if (inherits(col_select, "quosures")) {
@@ -392,7 +390,6 @@ vroom_select <- function(x, col_select, id) {
392390
x <- x[vars]
393391
names(x) <- names(vars)
394392
}
395-
attr(x, "spec") <- spec
396393
x
397394
}
398395

R/vroom.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,9 @@ vroom <- function(
292292
}
293293

294294
out <- tibble::as_tibble(out, .name_repair = identity)
295-
class(out) <- c("spec_tbl_df", class(out))
296295

297296
out <- vroom_select(out, col_select, id)
297+
class(out) <- c("spec_tbl_df", class(out))
298298

299299
if (should_show_col_types(has_col_types, show_col_types)) {
300300
show_col_types(out, locale)

tests/testthat/test-vroom.R

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,3 +1307,50 @@ test_that("vroom can read a datetime column with no data and skip 1", {
13071307
equals = tibble::tibble(dt = as.POSIXct(character()))
13081308
)
13091309
})
1310+
1311+
# https://github.com/tidyverse/vroom/issues/554
1312+
# https://github.com/tidyverse/vroom/issues/534
1313+
test_that("vroom(col_select =) output has 'spec_tbl_df' class, spec, and problems when readr is attached", {
1314+
# Register readr's [.spec_tbl_df method which drops attributes and the "spec_tbl_df" class
1315+
readr_single_bracket <- function(x, ...) {
1316+
attr(x, "spec") <- NULL
1317+
attr(x, "problems") <- NULL
1318+
class(x) <- setdiff(class(x), "spec_tbl_df")
1319+
NextMethod(`[`)
1320+
}
1321+
registerS3method("[", "spec_tbl_df", readr_single_bracket)
1322+
1323+
# Unfortunately you can't just de-register the method and
1324+
# local_mocked_s3_method() only works if method exists to begin with.
1325+
# We'll do next best thing which is to put a pass-through method back.
1326+
withr::defer({
1327+
registerS3method("[", "spec_tbl_df", function(x, ...) NextMethod(`[`))
1328+
})
1329+
1330+
expect_warning(
1331+
dat <- vroom(
1332+
I("a,b\n1,2\nz,3\n4,5"),
1333+
col_types = "dc",
1334+
col_select = c(a, b),
1335+
show_col_types = FALSE,
1336+
altrep = FALSE
1337+
),
1338+
class = "vroom_parse_issue"
1339+
)
1340+
1341+
expect_s3_class(dat, "spec_tbl_df")
1342+
expect_equal(
1343+
attr(dat, "spec", exact = TRUE),
1344+
cols(
1345+
a = col_double(),
1346+
b = col_character(),
1347+
.delim = ","
1348+
)
1349+
)
1350+
expect_no_error(probs <- problems(dat))
1351+
if (exists("probs")) {
1352+
expect_equal(nrow(probs), 1)
1353+
expect_equal(probs$row, 3)
1354+
expect_equal(probs$col, 1)
1355+
}
1356+
})

tests/testthat/test-vroom_fwf.R

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,3 +468,51 @@ test_that("vroom_fwf correctly reads DOS files with no trailing newline (https:/
468468

469469
expect_equal(out, out2)
470470
})
471+
472+
# https://github.com/tidyverse/vroom/issues/554
473+
# https://github.com/tidyverse/vroom/issues/534
474+
test_that("vroom_fwf(col_select =) output has 'spec_tbl_df' class, spec, and problems when readr is attached", {
475+
# Register readr's [.spec_tbl_df method which drops attributes and the "spec_tbl_df" class
476+
readr_single_bracket <- function(x, ...) {
477+
attr(x, "spec") <- NULL
478+
attr(x, "problems") <- NULL
479+
class(x) <- setdiff(class(x), "spec_tbl_df")
480+
NextMethod(`[`)
481+
}
482+
registerS3method("[", "spec_tbl_df", readr_single_bracket)
483+
484+
# Unfortunately you can't just de-register the method and
485+
# local_mocked_s3_method() only works if method exists to begin with.
486+
# We'll do next best thing which is to put a pass-through method back.
487+
withr::defer({
488+
registerS3method("[", "spec_tbl_df", function(x, ...) NextMethod(`[`))
489+
})
490+
491+
expect_warning(
492+
dat <- vroom_fwf(
493+
I("a b \n1 2 \nz 3 \n4 5 "),
494+
fwf_widths(c(3, 3)),
495+
col_types = "dc",
496+
col_select = c(X1, X2),
497+
show_col_types = FALSE,
498+
altrep = FALSE
499+
),
500+
class = "vroom_parse_issue"
501+
)
502+
503+
expect_s3_class(dat, "spec_tbl_df")
504+
expect_equal(
505+
attr(dat, "spec", exact = TRUE),
506+
cols(
507+
X1 = col_double(),
508+
X2 = col_character(),
509+
.delim = ""
510+
)
511+
)
512+
expect_no_error(probs <- problems(dat))
513+
if (exists("probs")) {
514+
expect_equal(nrow(probs), 2)
515+
expect_equal(probs$row, c(1, 2))
516+
expect_equal(probs$col, c(1, 1))
517+
}
518+
})

0 commit comments

Comments
 (0)