Skip to content

Commit

Permalink
feat: tbl_format_setup() gains a setup argument that supports pri…
Browse files Browse the repository at this point in the history
…nting the header before the data for the body is available, e.g., for remote backends such as databases (#686)

* Live printing

* Two-step computation of setup

* Results

* Provide rows_total as missing value in first pass

* No failures

* Address feedback
  • Loading branch information
krlmlr authored Dec 14, 2024
1 parent abaa49f commit 73c00f6
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 117 deletions.
59 changes: 44 additions & 15 deletions R/tbl-format-setup.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@
#' and (implicitly) in the footer of a tibble;
#' - the columns shown in the body decide which columns are shown in the footer.
#'
#' This information is computed once in `tbl_format_setup()`.
#' This information is computed in `tbl_format_setup()`.
#' The result is passed on to the
#' [tbl_format_header()], [tbl_format_body()], and [tbl_format_footer()]
#' methods.
#' If you need to customize parts of the printed output independently,
#' override these methods instead.
#'
#' By checking the `setup` argument, you can return an object that is
#' suitable for a call to [tbl_format_header()] if `setup` is `NULL`.
#' In this case, the method is called a second time with the return value
#' of the first call as `setup`.
#'
#' @details
#' Extend this method to prepare information that is used
#' in several parts of the printed output of a tibble-like object,
Expand All @@ -41,6 +46,15 @@
#' This argument is mandatory for all implementations of this method.
#' @param ...
#' Extra arguments to [print.tbl()] or [format.tbl()].
#' @param setup
#' This generic is first called with `setup = NULL` .
#' If the method _evaluates_ this argument, the return value
#' will only be used in a call to [tbl_format_header()],
#' and after that, a second call to this generic will be made
#' with the return value of the first call as `setup`
#' which then will be used in calls to [tbl_format_body()] and [tbl_format_footer()].
#' This allows displaying the header before starting the computation
#' required for the body and footer.
#' @param n
#' Actual number of rows to print.
#' No [options][pillar_options] should be considered
Expand All @@ -67,6 +81,7 @@ tbl_format_setup <- function(
x,
width = NULL,
...,
setup = list(tbl_sum = tbl_sum(x)),
n = NULL,
max_extra_cols = NULL,
max_footer_lines = NULL,
Expand All @@ -87,6 +102,7 @@ tbl_format_setup <- function(
x,
width,
...,
setup = setup,
n = n,
max_extra_cols = max_extra_cols,
max_footer_lines = max_footer_lines,
Expand All @@ -107,10 +123,26 @@ tbl_format_setup_dispatch <- function(x, width, ..., n, max_extra_cols, max_foot
#'
#' @rdname tbl_format_setup
#' @export
tbl_format_setup.tbl <- function(x, width, ...,
n, max_extra_cols, max_footer_lines, focus) {
tbl_format_setup.tbl <- function(
x,
width,
...,
setup,
n,
max_extra_cols,
max_footer_lines,
focus
) {
"!!!!DEBUG tbl_format_setup.tbl()"

if (is.null(setup)) {
# Header with early exit
tbl_sum <- tbl_sum(x)
return(new_tbl_format_setup(width, tbl_sum, rows_total = NA_integer_))
} else {
tbl_sum <- setup$tbl_sum
}

# Number of rows
rows <- tbl_nrow(x)

Expand Down Expand Up @@ -140,9 +172,6 @@ tbl_format_setup.tbl <- function(x, width, ...,
rows_missing <- 0L
}

# Header
tbl_sum <- tbl_sum(x)

# Body
rownames(df) <- NULL

Expand Down Expand Up @@ -233,17 +262,17 @@ tbl_nrow.tbl <- function(x, ...) {
#'
#' @keywords internal
new_tbl_format_setup <- function(
x,
df,
width,
tbl_sum,
body,
rows_missing,
rows_total,
extra_cols,
extra_cols_total,
max_footer_lines,
abbrev_cols
x = NULL,
df = NULL,
body = NULL,
rows_missing = NULL,
rows_total = NULL,
extra_cols = NULL,
extra_cols_total = NULL,
max_footer_lines = NULL,
abbrev_cols = NULL
) {
trunc_info <- list(
x = x,
Expand Down
62 changes: 50 additions & 12 deletions R/tbl-format.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@ print_tbl <- function(x, width = NULL, ...,
n = NULL, max_extra_cols = NULL, max_footer_lines = NULL) {
if (!is.null(n_extra)) {
deprecate_stop("1.6.2", "pillar::print(n_extra = )", "pillar::print(max_extra_cols = )")
if (is.null(max_extra_cols)) {
max_extra_cols <- n_extra
}
}

writeLines(format(
# Printing happens as a side effect thanks to `transform = writeLines` .
# For formatting, the default `transform = identity` returns the data instead.
format_tbl(
x,
width = width, ...,
n = n, max_extra_cols = max_extra_cols, max_footer_lines = max_footer_lines
))
n = n, max_extra_cols = max_extra_cols, max_footer_lines = max_footer_lines,
transform = writeLines
)

invisible(x)
}

Expand All @@ -48,7 +49,8 @@ format_tbl <- function(
n_extra = NULL,
n = NULL,
max_extra_cols = NULL,
max_footer_lines = NULL
max_footer_lines = NULL,
transform = identity
) {
check_dots_empty(action = signal)

Expand All @@ -60,17 +62,53 @@ format_tbl <- function(
force(x)
num_colors(forget = TRUE)

setup <- tbl_format_setup(x,
width = width, ...,
# This is a bit of a hack to allow the setup function to be called twice
# if the implementer is prepared to handle that.
# We detect that by checking if the `setup` argument has been evaluated.
setup_used <- FALSE

# In either case, we expect a `setup` object that can be passed to `tbl_format_header()`
# as a return from this call.
setup <- tbl_format_setup(
x,
width = width,
...,
setup = {
# This construct updates the `setup_used` variable in the parent scope
# when the `setup` argument is evaluated.
setup_used <- TRUE
NULL
},
n = n,
max_extra_cols = max_extra_cols,
max_footer_lines = max_footer_lines,
focus = attr(x, "pillar_focus")
)

header <- tbl_format_header(x, setup)
body <- tbl_format_body(x, setup)
footer <- tbl_format_footer(x, setup)
header <- transform(tbl_format_header(x, setup))

# If the implementation did not request the `setup` argument in the first call,
# the default behavior before 1.9.1 is used: the first call already
# has returned the full setup object.
# Otherwise, we assume that a second call is required, and we pass it the
# setup object returned from the first call.
if (setup_used) {
setup <- tbl_format_setup(
x,
width = width,
...,
setup = setup,
n = n,
max_extra_cols = max_extra_cols,
max_footer_lines = max_footer_lines,
focus = attr(x, "pillar_focus")
)
}

# In either case, the `setup` object is now complete and can be used to format the body
# and the footer.
body <- transform(tbl_format_body(x, setup))
footer <- transform(tbl_format_footer(x, setup))
c(header, body, footer)
}

Expand Down
26 changes: 13 additions & 13 deletions man/new_tbl_format_setup.Rd

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

28 changes: 26 additions & 2 deletions man/tbl_format_setup.Rd

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

6 changes: 0 additions & 6 deletions revdep/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,2 @@
# Revdeps

## Failed to check (1)

|package |version |error |warning |note |
|:----------|:-------|:-----|:-------|:----|
|tidyseurat |0.8.0 |1 | | |

9 changes: 2 additions & 7 deletions revdep/cran.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
## revdepcheck results

We checked 122 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package.
We checked 3 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package.

* We saw 0 new problems
* We failed to check 1 packages
* We failed to check 0 packages

Issues with CRAN packages are summarised below.

### Failed to check

* tidyseurat (NA)
Loading

0 comments on commit 73c00f6

Please sign in to comment.