Skip to content

Commit c4d4e59

Browse files
dgkfllrs-roche
andauthored
Various enhancements to support reporting (#33)
* feat: improve log capture during evaluation * feat: add global logging option * chore: disable logging by default * chore: consolidate logging options * chore: add test for error handling during logging * chore: store logs before throwing captured error * feat: improve output for knitr knit_print * chore: adding to pkgdown index * chore: add tests for order independence of logs * Apply on.exit suggestions from code review Co-authored-by: Lluís Revilla <185338939+llrs-roche@users.noreply.github.com> --------- Co-authored-by: Lluís Revilla <185338939+llrs-roche@users.noreply.github.com>
1 parent b4cabee commit c4d4e59

30 files changed

Lines changed: 948 additions & 80 deletions

DESCRIPTION

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ BugReports:
3737
Imports:
3838
cli,
3939
desc,
40+
evaluate,
4041
options,
4142
S7,
4243
tools,
@@ -48,6 +49,7 @@ Imports:
4849
Suggests:
4950
covr,
5051
rcmdcheck,
52+
htmltools,
5153
igraph,
5254
knitr,
5355
rmarkdown,
@@ -99,7 +101,9 @@ Collate:
99101
'generic_metric_coerce.R'
100102
'options.R'
101103
'package.R'
104+
'share-register-s3.R'
102105
'utils_backports.R'
106+
'utils_evaluate.R'
103107
'utils_rand.R'
104108
'utils_rstudio.R'
105109
'utils_tmp.R'

NAMESPACE

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@ S3method(".DollarNames","val.meter::pkg")
55
S3method("[","val.meter::pkg")
66
S3method("[[","val.meter::pkg")
77
S3method(as.data.frame,list_of_pkg)
8+
S3method(format,evaluate_evaluation)
89
S3method(format,val_meter_error)
910
S3method(print,val_meter_error)
1011
export(class_metric_data_frame)
1112
export(class_package_matrix)
1213
export(cran_repo_resource)
1314
export(error)
15+
export(format_output)
1416
export(from_dcf)
1517
export(git_resource)
1618
export(impl_data)
1719
export(impl_data_derive)
1820
export(impl_data_info)
1921
export(install_resource)
22+
export(knit_print.evaluate_evaluation)
2023
export(local_resource)
2124
export(local_source_resource)
2225
export(metric_coerce)
@@ -36,6 +39,7 @@ export(random_repo)
3639
export(remote_resource)
3740
export(repo_resource)
3841
export(resource)
42+
export(s3_register)
3943
export(source_archive_resource)
4044
export(source_code_resource)
4145
export(tags)
@@ -44,6 +48,7 @@ import(S7)
4448
import(cli)
4549
import(options)
4650
importFrom(desc,desc)
51+
importFrom(evaluate,replay)
4752
importFrom(httr2,req_perform)
4853
importFrom(httr2,request)
4954
importFrom(httr2,resp_body_html)
@@ -57,6 +62,7 @@ importFrom(tools,getVignetteInfo)
5762
importFrom(tools,toRd)
5863
importFrom(utils,.DollarNames)
5964
importFrom(utils,available.packages)
65+
importFrom(utils,capture.output)
6066
importFrom(utils,download.packages)
6167
importFrom(utils,getCRANmirrors)
6268
importFrom(utils,head)

R/class_pkg.R

Lines changed: 82 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ pkg <- class_pkg <- new_class(
1616
# necessary data dependencies to be evaluated.
1717
data = class_environment,
1818

19+
# logs (not user-facing)
20+
# A mutable environment, stores output logs captured using the `evaluate`
21+
# package. Should contain an entry for each value in `@data`.
22+
logs = class_environment,
23+
1924
#' @param resource [`resource`] (often a [`multi_resource`]), providing the
2025
#' resources to be used for deriving packages data. If a
2126
#' [`multi_resource`], the order of resources determines the precedence of
@@ -62,13 +67,25 @@ pkg <- class_pkg <- new_class(
6267
new_object(
6368
.parent = S7::S7_object(),
6469
data = new.env(parent = emptyenv()),
70+
logs = new.env(parent = emptyenv()),
6571
metrics = list(),
6672
resource = resource,
6773
permissions = policy@permissions
6874
)
6975
}
7076
)
7177

78+
method(convert, list(class_character, class_pkg)) <-
79+
function(from, to, ...) {
80+
if (endsWith(tolower(from), ".rds")) {
81+
convert(readRDS(from), class_pkg)
82+
} else if (grepl("\\bPackage:", from[[1L]])) {
83+
pkg_from_dcf(from, ...)
84+
} else {
85+
pkg(from, ...)
86+
}
87+
}
88+
7289
#' Generate Random Package(s)
7390
#'
7491
#' Create a package object to simulate metric derivation. When generating a
@@ -179,6 +196,8 @@ random_repo <- function(..., path = tempfile("repo")) {
179196
#' @param x [`pkg`] object to derive data for
180197
#' @param name `character(1L)` field name for the data to derive
181198
#' @param ... Additional arguments unused
199+
#' @param logs `logical(1L)` flag indicating whether console output should be
200+
#' captured during execution.
182201
#' @param .raise `logical(1L)` flag indicating whether errors should be raised
183202
#' or captured. This flag is not intended to be set directly, it is exposed
184203
#' so that recursive calls can raise lower-level errors while capturing them
@@ -189,7 +208,13 @@ random_repo <- function(..., path = tempfile("repo")) {
189208
#'
190209
#' @keywords internal
191210
#' @include utils_err.R
192-
get_pkg_data <- function(x, name, ..., .raise = .state$raise) {
211+
get_pkg_data <- function(
212+
x,
213+
name,
214+
...,
215+
logs = opt("logs"),
216+
.raise = .state$raise
217+
) {
193218
# RStudio, when trying to produce completions,will try to evaluate our lazy
194219
# list elements. Intercept those calls and return only the existing values.
195220
if (is_rs_rpc_get_completions_call()) {
@@ -213,7 +238,19 @@ get_pkg_data <- function(x, name, ..., .raise = .state$raise) {
213238
assert_permissions(required_permissions, x@permissions)
214239
assert_suggests(required_suggests)
215240

216-
data <- pkg_data_derive(pkg = x, field = name, ...)
241+
if (logs) {
242+
capture <- capture_pkg_data_derive(pkg = x, field = name, ...)
243+
data <- capture$data
244+
x@logs[[name]] <- capture$logs
245+
246+
# re-throw error after storing logs if one was produced
247+
if (inherits(data, "error")) {
248+
stop(data)
249+
}
250+
} else {
251+
data <- pkg_data_derive(pkg = x, field = name, ...)
252+
}
253+
217254
if (!identical(info@data_class, class_any)) {
218255
data <- convert(data, info@data_class)
219256
}
@@ -382,31 +419,59 @@ as.data.frame.list_of_pkg <- function(x, ...) {
382419
}
383420

384421
#' @include utils_dcf.R
385-
method(from_dcf, list(class_character, class_pkg)) <-
386-
function(x, to, ...) {
387-
dcf <- from_dcf(x, class_any)
422+
method(convert, list(class_list, class_pkg)) <-
423+
function(from, to, ...) {
388424
resource <- unknown_resource(
389-
package = dcf[[1, "Package"]],
390-
version = dcf[[1, "Version"]],
391-
md5 = if ("MD5sum" %in% colnames(dcf)) {
392-
dcf[[1, "MD5sum"]]
393-
} else {
394-
NA_character_
395-
}
425+
package = from$name %||% from$Package,
426+
version = from$version %||% from$Version,
427+
md5 = from$MD5sum %||% NA_character_
396428
)
397429

398430
data <- new.env(parent = emptyenv())
431+
for (name in names(from)) {
432+
# recover gracefully from unknown fieldnames
433+
info <- tryCatch(pkg_data_info(name), error = function(e) NULL)
434+
if (is.null(info)) {
435+
next
436+
}
437+
438+
data[[name]] <- metric_coerce(from[[name]], info@data_class)
439+
}
440+
441+
pkg <- pkg(resource)
442+
pkg@data <- data
443+
444+
pkg
445+
}
446+
447+
#' @include utils_dcf.R
448+
method(from_dcf, list(class_character, class_pkg)) <-
449+
function(x, to, ...) {
450+
dcf <- from_dcf(x, class_any)
451+
452+
data <- list()
453+
data$name <- dcf[[1, "Package"]]
454+
data$version <- dcf[[1, "Version"]]
455+
data$md5 <- if ("MD5sum" %in% colnames(dcf)) {
456+
dcf[[1, "MD5sum"]]
457+
} else {
458+
NA_character_
459+
}
460+
399461
prefix <- "Metric/"
400462
for (name in colnames(dcf)[startsWith(colnames(dcf), prefix)]) {
401463
field <- sub(prefix, "", name)
402-
info <- pkg_data_info(field)
464+
465+
# recover gracefully from unknown fieldnames
466+
info <- tryCatch(pkg_data_info(field), error = function(e) NULL)
467+
if (is.null(info)) {
468+
next
469+
}
470+
403471
val <- dcf[[1, name]]
404472
val <- metric_coerce(val, info@data_class)
405473
data[[field]] <- val
406474
}
407475

408-
pkg <- pkg(resource)
409-
pkg@data <- data
410-
411-
pkg
476+
convert(data, class_pkg)
412477
}

R/class_resource.R

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,9 +284,11 @@ method(convert, list(class_character, class_resource)) <-
284284
add_resource <- function(resource) {
285285
resource_type_name <- class_desc(S7::S7_class(resource))
286286
idx <- match(resource_type_name, all_resource_type_names)
287+
287288
if (is.na(idx) || !is.null(resources[[idx]])) {
288289
return()
289290
}
291+
290292
resources[[idx]] <<- resource
291293
idx
292294
}
@@ -317,8 +319,8 @@ method(convert, list(class_character, class_resource)) <-
317319
# iterate over other allowed resource types
318320
for (to_idx in seq_along(all_resource_types)) {
319321
# that are not yet populated with a known resource
320-
to_resource <- resources[[to_idx]]
321-
if (!is.null(to_resource)) {
322+
existing_resource <- resources[[to_idx]]
323+
if (!is.null(existing_resource)) {
322324
next
323325
}
324326

@@ -336,7 +338,7 @@ method(convert, list(class_character, class_resource)) <-
336338
# special handling for error conditions used to test discovery in tests
337339
if (inherits(result, "test_suite_signal")) {
338340
stop(result)
339-
} else if (inherits(result, "error")) {
341+
} else if (is.null(result) || inherits(result, "error")) {
340342
next
341343
}
342344

@@ -589,7 +591,9 @@ method(convert, list(class_local_source_resource, class_install_resource)) <-
589591

590592
method(convert, list(class_resource, class_unknown_resource)) <-
591593
function(from, to, ...) {
592-
set_props(to(), props(from, names(class_unknown_resource@properties)))
594+
out <- to()
595+
props(out) <- props(from, prop_names(out))
596+
out
593597
}
594598

595599
method(to_dcf, class_resource) <- function(x, ...) {

R/data_coverage.R

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ impl_data(
99
impl_data(
1010
"covr_coverage",
1111
for_resource = local_source_resource,
12-
function(pkg, resource, field, ..., quiet = opt("quiet")) {
13-
covr::package_coverage(resource@path, type = "tests", quiet = quiet)
12+
function(pkg, resource, field, ...) {
13+
# package installs use `system2()` whose output cannot be captured by sink()
14+
# so we just execute quietly
15+
covr::package_coverage(resource@path, type = "tests", quiet = TRUE)
1416
}
1517
)
1618

@@ -23,7 +25,7 @@ impl_data(
2325
"The fraction of expressions of package code that are evaluated by any ",
2426
"test"
2527
),
26-
function(pkg, resource, field, ..., quiet = opt("quiet")) {
28+
function(pkg, resource, field, ...) {
2729
tally <- covr::tally_coverage(pkg$covr_coverage, by = "expression")
2830
mean(tally$value > 0)
2931
}
@@ -45,7 +47,7 @@ impl_data(
4547
description = paste0(
4648
"The fraction of lines of package code that are evaluated by any test"
4749
),
48-
function(pkg, resource, field, ..., quiet = opt("quiet")) {
50+
function(pkg, resource, field, ...) {
4951
tally <- covr::tally_coverage(pkg$covr_coverage, by = "line")
5052
mean(tally$value > 0)
5153
}

R/data_desc.R

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ impl_data(
1414
"name",
1515
title = "Package name",
1616
class = class_character,
17+
for_resource = new_union(source_code_resource, install_resource),
1718
function(pkg, resource, field, ...) {
1819
pkg$desc$get_field("Package")
1920
}
2021
)
2122

2223
impl_data(
2324
"name",
24-
for_resource = repo_resource,
25+
for_resource = class_resource,
2526
function(pkg, resource, field, ...) {
2627
resource@package
2728
}
@@ -30,19 +31,37 @@ impl_data(
3031
impl_data(
3132
"version",
3233
class = class_character,
34+
for_resource = new_union(source_code_resource, install_resource),
3335
function(pkg, resource, field, ...) {
3436
pkg$desc$get_field("Version")
3537
}
3638
)
3739

3840
impl_data(
3941
"version",
40-
for_resource = repo_resource,
42+
for_resource = class_resource,
4143
function(pkg, resource, field, ...) {
4244
resource@version
4345
}
4446
)
4547

48+
impl_data(
49+
"md5",
50+
class = class_character,
51+
for_resource = new_union(source_code_resource, install_resource),
52+
function(pkg, resource, field, ...) {
53+
pkg$desc$get_field("MD5sum")
54+
}
55+
)
56+
57+
impl_data(
58+
"md5",
59+
for_resource = class_resource,
60+
function(pkg, resource, field, ...) {
61+
resource@md5
62+
}
63+
)
64+
4665
impl_data(
4766
"dependency_count",
4867
class = class_integer,

R/data_r_cmd_check.R

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,12 @@ impl_data(
1414
local_source_resource,
1515
source_archive_resource
1616
),
17-
function(pkg, resource, field, ..., quiet = opt("quiet")) {
18-
# suppress messages to avoid stdout output from subprocess
19-
# (eg warnings about latex availability not suppressed by rcmdcheck)
20-
21-
wrapper <- if (quiet) {
22-
function(...) capture.output(..., type = "message")
23-
} else {
24-
identity
25-
}
26-
27-
wrapper({
28-
result <- rcmdcheck::rcmdcheck(
29-
resource@path,
30-
quiet = quiet,
31-
error_on = "never",
32-
build_args = "--no-manual"
33-
)
34-
})
35-
36-
result
17+
function(pkg, resource, field, ...) {
18+
rcmdcheck::rcmdcheck(
19+
resource@path,
20+
error_on = "never",
21+
build_args = "--no-manual"
22+
)
3723
}
3824
)
3925

0 commit comments

Comments
 (0)