Skip to content

Various enhancements to support reporting#33

Merged
dgkf merged 12 commits intomainfrom
dev-log-capture
Mar 17, 2026
Merged

Various enhancements to support reporting#33
dgkf merged 12 commits intomainfrom
dev-log-capture

Conversation

@dgkf
Copy link
Copy Markdown
Collaborator

@dgkf dgkf commented Jan 6, 2026

I spent some time before the holidays familiarizing myself with val.report and made a few changes to val.meter that I think will help streamline the reporting process.

I had a few goals in mind:

  1. Restructure the report package parameter to be more agnostic. Ideally, we should be able to pass in a pre-calculated pkg object (by Rds path), a DCF-formatted string of metrics (for example, from our repository), or a package name to be calculated on the fly.
  2. Support rich metadata in a tightly-integrated scenario. I'm imagining a case where we have a rich pkg object with full metadata and execution logs.
  3. Support good metadata in the loosely-integrated scenario -- for example when metrics are pulled from the repository PACKAGES file. We don't expect this to have full execution logs, but it should have metric data.

Will annotate the changes inline in comments.

@jthompson-arcus - let me know what you think of the logs capture. I know this was a contentious feature in riskmetric and would be curious to hear your thoughts on this approach.
@llrs-roche - would love to get your thoughts on some of the reporting-oriented changes.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jan 6, 2026

Codecov Report

❌ Patch coverage is 60.00000% with 60 lines in your changes missing coverage. Please review.
✅ Project coverage is 31.31%. Comparing base (b4cabee) to head (0dfa4ee).

Files with missing lines Patch % Lines
R/utils_evaluate.R 67.74% 20 Missing ⚠️
R/class_pkg.R 55.00% 18 Missing ⚠️
R/class_resource.R 0.00% 6 Missing ⚠️
R/data_r_cmd_check.R 0.00% 5 Missing ⚠️
R/data_vignettes.R 0.00% 4 Missing ⚠️
R/data_web_html.R 0.00% 3 Missing ⚠️
R/data_desc.R 0.00% 2 Missing ⚠️
R/data_coverage.R 0.00% 1 Missing ⚠️
R/zzz.R 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #33      +/-   ##
==========================================
+ Coverage   25.86%   31.31%   +5.45%     
==========================================
  Files          35       36       +1     
  Lines        1481     1587     +106     
==========================================
+ Hits          383      497     +114     
+ Misses       1098     1090       -8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread R/class_pkg.R
Comment on lines +78 to +87
method(convert, list(class_character, class_pkg)) <-
function(from, to, ...) {
if (endsWith(tolower(from), ".rds")) {
convert(readRDS(from), class_pkg)
} else if (grepl("\\bPackage:", from[[1L]])) {
pkg_from_dcf(from, ...)
} else {
pkg(from, ...)
}
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a few extra handlers for converting a character string into a pkg object. Specifically ensuring that we can handle Rds file paths and DCF strings.

With these, whatever is passed to the report's package parameter, we can just run convert() on it to build a pkg() object and handle Rds paths, DCF strings and package names through a single interface.

Comment thread R/class_pkg.R
Comment on lines +417 to +418
method(convert, list(class_list, class_pkg)) <-
function(from, to, ...) {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly added handling for lists, which were effectively already handled inside the from_dcf method. Now it's just split into building a list from the DCF contents and then converting the list into a pkg object.

Comment thread R/data_vignettes.R
Comment on lines -34 to +36
nodes |>
xml2::xml_attr("href") |>
basename() |>
tools::file_path_sans_ext() |>
unique() |>
length()
paths <- xml2::xml_attr(nodes, "href")
filenames <- basename(paths)
filestems <- tools::file_path_sans_ext(filenames)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit of an unrelated fix, but noticed that the use of |> would force us to bump our minimum R version support so I just converted them into more traditional calls.

Comment on lines +59 to +64
capture <- evaluate::evaluate(
evaluate_fn,
stop_on_error = 1L,
debug = !isTRUE(quiet),
output_handler = evaluate::new_output_handler(value = identity)
)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When logging is enabled we use evaluate to capture it.

It's off by default, since it's been a bit problematic in riskmetric; though here we're offloading all the nastiness of sinking output to a more mature package. Hopefully this is a bit more stable than what we had before, but still would prefer to keep it off by default until we have had an opportunity to stress test it a bit more.

Comment thread R/utils_evaluate.R Outdated
Comment on lines +3 to +32
format.evaluate_evaluation <- function(
x,
...,
style = c("text", "ansi", "html")
) {
style <- match.arg(style)

if (
identical(style, "html") && !requireNamespace("htmltools", quietly = TRUE)
) {
stop(
"html formatting of evaluation logs requires suggested package ",
"`htmltools`"
)
}

out <- utils::capture.output(evaluate::replay(x))
switch(
style,
text = paste(cli::ansi_strip(out), collapse = "\n"),
ansi = paste(out, collapse = "\n"),
html = {
html <- cli::ansi_html(paste(out, collapse = "\n"))
htmltools::tags$div(
style = cli::ansi_html_style(colors = 8L),
htmltools::tags$pre(htmltools::HTML(html))
)
}
)
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To support prettier output of logs in the report, allow a style option that will hopefully make it easier to emit plain text or rich html reports.

Copy link
Copy Markdown
Contributor

@jthompson-arcus jthompson-arcus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dgkf focused on the logging as requested. I don't think any of this structure would pose an issue to the accompanying Shiny app.

I did have a couple comments below.

Comment thread R/generic_pkg_data_derive.R Outdated
Comment thread tests/testthat/test-derive-logs.R
Comment thread R/utils_evaluate.R
Comment on lines +7 to +15
knit_print.evaluate_evaluation <- function(x, ...) {
# nolint end
res <- format_output(evaluate::replay(x))
if (!is.character(res) && requireNamespace("knitr", quietly = TRUE)) {
knitr::knit_print(res)
} else {
cat(res)
}
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this in since the last review to accommodate printing during knitting.

With this we can have a block that is just:

pkg@logs$r_cmd_check

And we will get a richly formatted html output in html reports. Currently just html is handled, but we could handle others as well if we'd like rich logs.

@dgkf dgkf dismissed jthompson-arcus’s stale review February 17, 2026 16:39

All requested changes address

@dgkf
Copy link
Copy Markdown
Collaborator Author

dgkf commented Feb 19, 2026

Check what happens with logs when you have nested metric evaluation.

pkg$r_cmd_check_error_count -> pkg$r_cmd_check

@dgkf dgkf moved this to In Progress in Package Development Feb 19, 2026
@dgkf
Copy link
Copy Markdown
Collaborator Author

dgkf commented Mar 5, 2026

Check what happens with logs when you have nested metric evaluation.

pkg$r_cmd_check_error_count -> pkg$r_cmd_check

Added some tests for this. Fundamentally this is a behavior of {evaluate}, but I wanted to test it in case we ever rework the execution model or swap out evaluate as a dependency.

In short, the logs capture is scoped so if one metric calls another the logs are attributed to the lowest metric being evaluated on the call stack at any given point. This means that regardless of order of execution, logs will be consistently captured. However, if you want to capture a full log of a parent, including the outputs of a child metric that might be evaluated in the middle of a parent metric, you'd need to cobble those together yourself. I think these situations will be unlikely/rare and it's more important that we have reproducible log capture.

Copy link
Copy Markdown
Contributor

@llrs-roche llrs-roche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good and test are well designed and work well.

class_source_archive_resource is not documented. I guess roxygen2 doesn't handle double assignments well.

See the note also to provide auto-completion to the objects.


Some vignettes use suggested packages without checking that the package is installed (it failed with rosv). On that note, even after installing I get an error:

r <- cran_repo_resource("haven", "0.1.1", repo = "https://cloud.r-project.org/")
p <- pkg(r, permissions = "network")
p$rosv_vulnerability_df
<S7_error_method_not_found: Can't find method for generic `pkg_data_info(field, resource)`:
- field   : S3<pkg_data_field_rosv_vulnerability_df/pkg_data_field>
- resource: MISSING>

Comment thread R/class_pkg.R
Comment on lines 218 to 220
# RStudio, when trying to produce completions,will try to evaluate our lazy
# list elements. Intercept those calls and return only the existing values.
if (is_rs_rpc_get_completions_call()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't stop myself, to point out that maybe we need to check if the code is run by RStudio and perhaps check if with Positron or VSCode something similar happens (I doubt it).

Another related approach would be to provide a .DollarNames and .AtNames to provide default R autocompletion as we see fit.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good call on the other IDEs. I haven't tested in Positron/VSCode.

We do provide .DollarNames. I think the problem is that RStudio tries to determine the class of each value for its completion menu. By evaluating the object it prompts metric evaluation, which stalls the completion dialogue.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I didn't check if it was available already. That method looks weird why do it use :: ? AFAIK, we don't need name-spacing when registering it and a simple #' @export should be enough provided that the function is .DollarNames.<class> but that might be related to the S7 class (I didn't find this documented on any article)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That method looks weird

Yeah, I agree with you there! I think there are a few weird things going on.

why do it use :: ?

This is because S7 class objects have a class name of <package>::<class>. Here val.meter::pkg is a class name.

and a simple #' @export should be enough

I can try it again. I think I had some issues with it picking up the generic name because it starts with a period.

Comment thread R/data_coverage.R
function(pkg, resource, field, ...) {
# package installs use `system2()` whose output cannot be captured by sink()
# so we just execute quietly
covr::package_coverage(resource@path, type = "tests", quiet = TRUE)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again not the point of the PR but should code coverage cover all? Or at least we should provide a way to change that.

Suggested change
covr::package_coverage(resource@path, type = "tests", quiet = TRUE)
covr::package_coverage(resource@path, type = "all", quiet = TRUE)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good call. I'd say we should break these off the other types into other metrics.

Comment thread R/generic_pkg_data_derive.R
Comment thread R/share-register-s3.R
Comment thread R/utils_evaluate.R
)
}

old_options <- options(width = 80L, crayon.enabled = TRUE, cli.ansi = TRUE)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a different function (capture_pkg_data_derive()) some other options were used:

original_opts <- options(
    width = 80L,
    crayon.enabled = TRUE,
    cli.ansi = TRUE,
    cli.dynamic = FALSE,
    cli.num_colors = 256L
  )

I think it would be a nice bet to get a default_opts or internal_opts for reusing across the package.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I like that idea. It could be an option, eg

options(val.meter.log.opts = list(
  width = 80L,
  crayon.enabled = TRUE,
  cli.ansi = TRUE,
  cli.dynamic = FALSE, 
  cli.num_colors = 256L
))

Configured with a default so that they're overwritable if anyone wants something other than these.

Comment thread R/utils_evaluate.R Outdated
Comment thread R/generic_pkg_data_derive.R Outdated
Co-authored-by: Lluís Revilla <185338939+llrs-roche@users.noreply.github.com>
@dgkf dgkf merged commit c4d4e59 into main Mar 17, 2026
8 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Package Development Mar 17, 2026
@dgkf dgkf deleted the dev-log-capture branch March 17, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

4 participants