Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update feather serializer; Add parquet serializer #849

Merged
merged 27 commits into from
Mar 24, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
016f575
halfway svg render, still downloads instead of showing
Mar 14, 2019
f3a4ac7
well rendered svg in firefox
Mar 14, 2019
5979cd5
Merge remote-tracking branch 'upstream/master'
Oct 15, 2019
70e5974
test for svg
Feb 25, 2020
428b971
revert test
Feb 25, 2020
d56ebd1
working svg test
Feb 25, 2020
6ff2916
update
pachadotdev Jun 12, 2021
5bf1466
Merge branch 'rstudio-master'
pachadotdev Jun 12, 2021
428d6b4
Merge branch 'rstudio:main' into master
pachadotdev Jan 15, 2022
563f2c6
update feather serializers + add parquet serializer
pachadotdev Jan 16, 2022
42d1ed6
update NEWS
pachadotdev Jan 16, 2022
d6d5865
update tests for feather + add tests for parquet
pachadotdev Jan 16, 2022
ef4a2e5
Update R/content-types.R
pachadotdev Jan 18, 2022
421f2bb
Update R/serializer.R
pachadotdev Jan 18, 2022
f5bc085
Update R/content-types.R
pachadotdev Jan 18, 2022
20f27d7
Update R/parse-body.R
pachadotdev Jan 18, 2022
5a418f9
Update tests/testthat/test-serializer-parquet.R
pachadotdev Jan 18, 2022
28c0fb2
Update tests/testthat/test-parse-body.R
pachadotdev Jan 18, 2022
c8c1f52
Update R/serializer.R
pachadotdev Jan 18, 2022
e849328
Update R/parse-body.R
pachadotdev Jan 18, 2022
8fc7422
Update DESCRIPTION
pachadotdev Jan 18, 2022
44ebd93
Update NEWS.md
pachadotdev Jan 18, 2022
872fa4c
Update tests/testthat/test-serializer-parquet.R
pachadotdev Jan 18, 2022
59e81a9
Update tests/testthat/test-parse-body.R
pachadotdev Jan 18, 2022
32f6aab
adding @schloerke suggestions
pachadotdev Jan 18, 2022
0dfea4e
update tests + vignette
pachadotdev Jan 18, 2022
afb58c5
remove parquet tests (same as feather)
pachadotdev Jan 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Authors@R: c(
person("Bruno", "Tremblay", role = "ctb", email = "cran@neoxone.com"),
person("Frans", "van Dunné", role = "ctb", email = "frans@ixpantia.com"),
person("Sebastiaan", "Vandewoude", role="ctb", email = "sebastiaanvandewoude@gmail.com"),
person("Mauricio", "Vargas Sepulveda", role="ctb", email = "mavargas11@uc.cl"),
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved
person(family = "RStudio", role = c("cph", "fnd")))
License: MIT + file LICENSE
BugReports: https://github.com/rstudio/plumber/issues
Expand Down Expand Up @@ -44,7 +45,7 @@ Suggests:
later,
readr,
yaml,
feather,
arrow,
future,
rstudioapi,
spelling,
Expand Down
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export(parser_json)
export(parser_multi)
export(parser_none)
export(parser_octet)
export(parser_parquet)
export(parser_rds)
export(parser_read_file)
export(parser_text)
Expand Down Expand Up @@ -83,6 +84,7 @@ export(serializer_html)
export(serializer_htmlwidget)
export(serializer_jpeg)
export(serializer_json)
export(serializer_parquet)
export(serializer_pdf)
export(serializer_png)
export(serializer_print)
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
## New features

* Introduces new GeoJSON serializer and parser. GeoJSON objects are parsed into `sf` objects and `sf` or `sfc` objects will be serialized into GeoJSON. (@josiahparry, #830)
* Updates feather serializer to use the arrow package
* Adds parquet serializer by using the arrow package
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved

## Bug fixes

Expand Down
1 change: 1 addition & 0 deletions R/content-types.R
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ knownContentTypes <- c(
xlam = "application/vnd.ms-excel.addin.macroEnabled.12",
xlsb = "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
feather = "application/feather",
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved
parquet = "application/parquet",
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved
rds = "application/rds",
tsv = "application/tab-separated-values",
csv = "application/csv",
Expand Down
20 changes: 15 additions & 5 deletions R/parse-body.R
Original file line number Diff line number Diff line change
Expand Up @@ -480,18 +480,27 @@ parser_rds <- function(...) {
})
}

#' @describeIn parsers feather parser. See [feather::read_feather()] for more details.
#' @describeIn parsers feather parser. See [arrow::read_feather()] for more details.
#' @export
parser_feather <- function(...) {
parser_read_file(function(tmpfile) {
if (!requireNamespace("feather", quietly = TRUE)) {
stop("`feather` must be installed for `parser_feather` to work")
if (!requireNamespace("arrow", quietly = TRUE)) {
stop("`arrow` must be installed for `parser_feather` to work")
}
feather::read_feather(tmpfile, ...)
arrow::read_feather(tmpfile, ...)
})
}


#' @describeIn parsers parquet parser. See [arrow::read_parquet()] for more details.
#' @export
parser_parquet <- function(...) {
parser_read_file(function(tmpfile) {
if (!requireNamespace("arrow", quietly = TRUE)) {
stop("`arrow` must be installed for `parser_parquet` to work")
}
arrow::read_parquet(tmpfile, ...)
})
}

#' @describeIn parsers Octet stream parser. Returns the raw content.
#' @export
Expand Down Expand Up @@ -569,6 +578,7 @@ register_parsers_onLoad <- function() {
register_parser("form", parser_form, fixed = "application/x-www-form-urlencoded")
register_parser("rds", parser_rds, fixed = "application/rds")
register_parser("feather", parser_feather, fixed = "application/feather")
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved
register_parser("parquet", parser_parquet, fixed = "application/parquet")
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved
register_parser("text", parser_text, fixed = "text/plain", regex = "^text/")
register_parser("tsv", parser_tsv, fixed = c("application/tab-separated-values", "text/tab-separated-values"))
# yaml types: https://stackoverflow.com/a/38000954/591574
Expand Down
24 changes: 20 additions & 4 deletions R/serializer.R
Original file line number Diff line number Diff line change
Expand Up @@ -263,17 +263,32 @@ serializer_rds <- function(version = "2", ascii = FALSE, ..., type = "applicatio
})
}

#' @describeIn serializers feather serializer. See also: [feather::write_feather()]
#' @describeIn serializers feather serializer. See also: [arrow::write_feather()]
#' @export
serializer_feather <- function(type = "application/feather") {
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved
if (!requireNamespace("feather", quietly = TRUE)) {
stop("`feather` must be installed for `serializer_feather` to work")
if (!requireNamespace("arrow", quietly = TRUE)) {
stop("`arrow` must be installed for `serializer_feather` to work")
}
serializer_write_file(
fileext = ".feather",
type = type,
write_fn = function(val, tmpfile) {
feather::write_feather(val, tmpfile)
arrow::write_feather(val, tmpfile)
}
)
}

#' @describeIn serializers parquet serializer. See also: [arrow::write_parquet()]
#' @export
serializer_parquet <- function(type = "application/parquet") {
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved
if (!requireNamespace("arrow", quietly = TRUE)) {
stop("`arrow` must be installed for `serializer_parquet` to work")
}
serializer_write_file(
fileext = ".parquet",
type = type,
write_fn = function(val, tmpfile) {
arrow::write_parquet(val, tmpfile)
}
)
}
Expand Down Expand Up @@ -614,6 +629,7 @@ add_serializers_onLoad <- function() {
register_serializer("csv", serializer_csv)
register_serializer("tsv", serializer_tsv)
register_serializer("feather", serializer_feather)
register_serializer("parquet", serializer_parquet)
register_serializer("yaml", serializer_yaml)
register_serializer("geojson", serializer_geojson)

Expand Down
7 changes: 6 additions & 1 deletion man/parsers.Rd

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

7 changes: 6 additions & 1 deletion man/serializers.Rd

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

24 changes: 22 additions & 2 deletions tests/testthat/test-parse-body.R
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,15 @@ test_that("Test tsv parser", {
})

test_that("Test feather parser", {
skip_if_not_installed("feather")
skip_if_not_installed("arrow")

tmp <- tempfile()
on.exit({
file.remove(tmp)
}, add = TRUE)

r_object <- iris
feather::write_feather(r_object, tmp)
arrow::write_feather(r_object, tmp)
val <- readBin(tmp, "raw", 10000)

parsed <- parse_body(val, "application/feather", make_parser("feather"))
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -109,6 +109,26 @@ test_that("Test feather parser", {
expect_equal(parsed, r_object)
})

test_that("Test parquet parser", {
skip_if_not_installed("arrow")

tmp <- tempfile()
on.exit({
file.remove(tmp)
}, add = TRUE)

r_object <- iris
arrow::write_parquet(r_object, tmp)
val <- readBin(tmp, "raw", 10000)

parsed <- parse_body(val, "application/parquet", make_parser("parquet"))
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved
# convert from parquet tibble to data.frame
parsed <- as.data.frame(parsed, stringsAsFactors = FALSE)
attr(parsed, "spec") <- NULL

expect_equal(parsed, r_object)
})

test_that("Test geojson parser", {
skip_if_not_installed("geojsonsf")
skip_if_not_installed("sf")
Expand Down
6 changes: 3 additions & 3 deletions tests/testthat/test-serializer-feather.R
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
context("feather serializer")

test_that("feather serializes properly", {
skip_if_not_installed("feather")
skip_if_not_installed("arrow")

d <- data.frame(a=1, b=2, c="hi")
val <- serializer_feather()(d, data.frame(), PlumberResponse$new(), stop)
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -18,7 +18,7 @@ test_that("feather serializes properly", {
})

test_that("Errors call error handler", {
skip_if_not_installed("feather")
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved
skip_if_not_installed("arrow")

errors <- 0
errHandler <- function(req, res, err){
Expand All @@ -31,7 +31,7 @@ test_that("Errors call error handler", {
})

test_that("Errors are rendered correctly with debug TRUE", {
skip_if_not_installed("feather")
skip_if_not_installed("arrow")

pr <- pr() %>% pr_get("/", function() stop("myerror"), serializer = serializer_feather()) %>% pr_set_debug(TRUE)
capture.output(res <- pr$serve(make_req(pr = pr), PlumberResponse$new("csv")))
Expand Down
40 changes: 40 additions & 0 deletions tests/testthat/test-serializer-parquet.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
context("parquet serializer")

test_that("parquet serializes properly", {
skip_if_not_installed("arrow")

d <- data.frame(a=1, b=2, c="hi")
val <- serializer_parquet()(d, data.frame(), PlumberResponse$new(), stop)
expect_equal(val$status, 200L)
expect_equal(val$headers$`Content-Type`, "application/parquet")
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved

# can test by doing a full round trip if we believe the parser works via `test-parse-body.R`
parsed <- parse_body(val$body, "application/parquet", make_parser("parquet"))
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved
# convert from parquet tibble to data.frame
parsed <- as.data.frame(parsed, stringsAsFactors = FALSE)
attr(parsed, "spec") <- NULL

expect_equal(parsed, d)
})

test_that("Errors call error handler", {
skip_if_not_installed("arrow")

errors <- 0
errHandler <- function(req, res, err){
errors <<- errors + 1
}

expect_equal(errors, 0)
serializer_parquet()(parse(text="hi"), data.frame(), PlumberResponse$new("csv"), errorHandler = errHandler)
expect_equal(errors, 1)
})

test_that("Errors are rendered correctly with debug TRUE", {
skip_if_not_installed("arrow")

pr <- pr() %>% pr_get("/", function() stop("myerror"), serializer = serializer_parquet()) %>% pr_set_debug(TRUE)
capture.output(res <- pr$serve(make_req(pr = pr), PlumberResponse$new("csv")))

expect_match(res$body, "Error in (function () : myerror", fixed = TRUE)
})
pachadotdev marked this conversation as resolved.
Show resolved Hide resolved