Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
- {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
- {os: ubuntu-latest, r: 'release'}
- {os: ubuntu-latest, r: 'oldrel-1'}
- {os: ubuntu-latest, r: '3.6'}

env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ License: MIT + file LICENSE
URL: https://ascribe.visruth.com
BugReports: https://github.com/VisruthSK/ascribe/issues
Depends:
R (>= 4.2.0)
R (>= 3.6.0)
Imports:
brio,
cli,
Expand Down
11 changes: 5 additions & 6 deletions R/cite_usage.R
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ cite_usage <- function(

base_pkgs <- if (cite_r) "base" else character()
entries <- c(
lapply(unique(c(pkgs, base_pkgs)), \(pkg) {
lapply(unique(c(pkgs, base_pkgs)), function(pkg) {
entry <- get0(
pkg,
envir = package_citations,
Expand All @@ -54,13 +54,12 @@ cite_usage <- function(
entry
}
}),
lapply(usage$functions, \(fun) {
lapply(usage$functions, function(fun) {
get0(fun, envir = function_citations, inherits = FALSE, ifnotfound = NULL)
})
) |>
Filter(Negate(is.null), x = _) |>
do.call(c, args = _) |>
unique()
)

entries <- unique(do.call(c, Filter(Negate(is.null), entries)))

if (match.arg(format) == "bibentry") entries else utils::toBibtex(entries)
}
51 changes: 30 additions & 21 deletions R/collect.R
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
collect_pkg_funs <- function(pkg) {
ns <- asNamespace(pkg)

getNamespaceExports(pkg) |>
unique(c(
Filter(
\(x) {
function(x) {
is.function(tryCatch(getExportedValue(ns, x), error = function(e) NULL))
},
x = _
) |>
c(collect_r6_methods(pkg)) |>
unique()
getNamespaceExports(pkg)
),
collect_r6_methods(pkg)
))
}

#' Collect R6 class method names from a package
Expand All @@ -33,36 +33,43 @@ collect_pkg_funs <- function(pkg) {
collect_r6_methods <- function(pkg) {
ns <- asNamespace(pkg)

namespace_r6 <- ls(ns, all.names = TRUE) |>
namespace_r6 <- Filter(
Negate(is.null),
lapply(
\(name) {
ls(ns, all.names = TRUE),
function(name) {
obj <- tryCatch(
get0(name, envir = ns, inherits = FALSE),
error = function(e) NULL
)
if (inherits(obj, "R6ClassGenerator")) obj else NULL
}
) |>
Filter(Negate(is.null), x = _)
)
)

exported_r6 <- getNamespaceExports(ns) |>
exported_r6 <- Filter(
Negate(is.null),
lapply(
\(name) {
getNamespaceExports(ns),
function(name) {
obj <- tryCatch(
getExportedValue(ns, name),
error = function(e) NULL
)
if (inherits(obj, "R6ClassGenerator")) obj else NULL
}
) |>
Filter(Negate(is.null), x = _)
)
)

c(namespace_r6, exported_r6) |>
lapply(\(gen) names(gen$public_methods)) |>
unlist(use.names = FALSE) |>
as.character() |>
(\(methods) methods[!is.na(methods) & nzchar(methods)])() |>
unique()
methods <- unlist(
lapply(
c(namespace_r6, exported_r6),
function(gen) names(gen$public_methods)
),
use.names = FALSE
)
methods <- as.character(methods)
unique(methods[!is.na(methods) & nzchar(methods)])
}

.resolve_origin_ns <- function(ns, name) {
Expand Down Expand Up @@ -151,7 +158,9 @@ build_origin_map <- function(exports) {

u_pkgs <- unique(all_pkgs)
ns_list <- stats::setNames(
lapply(u_pkgs, \(p) tryCatch(asNamespace(p), error = function(e) NULL)),
lapply(u_pkgs, function(p) {
tryCatch(asNamespace(p), error = function(e) NULL)
}),
u_pkgs
)

Expand Down
24 changes: 10 additions & 14 deletions R/scan_usage.R
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ scan_usage <- function(
}
lapply(
paths,
\(file_path) cli::cli_alert_info("Searching {.path {file_path}}")
function(file_path) cli::cli_alert_info("Searching {.path {file_path}}")
)
paths
}
Expand All @@ -116,7 +116,7 @@ scan_usage <- function(

hits <- lapply(
unique(files),
\(file) {
function(file) {
code_str <- .extract_code(
file,
skip_patterns = skip_patterns,
Expand Down Expand Up @@ -216,11 +216,7 @@ scan_usage <- function(
}

.collect_unique <- function(hits, field) {
hits |>
lapply(`[[`, field) |>
unlist(use.names = FALSE) |>
unique() |>
sort()
sort(unique(unlist(lapply(hits, `[[`, field), use.names = FALSE)))
}

# Chunks pkgs into word-boundary alternation regexes (PCRE limits how many
Expand All @@ -239,7 +235,7 @@ scan_usage <- function(
)
vapply(
chunks,
\(chk) {
function(chk) {
escaped <- gsub("([][{}()+*^$|\\\\.?])", "\\\\\\1", chk)
paste0("\\b(", paste(escaped, collapse = "|"), ")\\b")
},
Expand All @@ -266,7 +262,9 @@ scan_usage <- function(

lapply(
metapackages,
\(pkgs) unique(pkgs[!is.na(fastmatch::fmatch(pkgs, allowed_packages))])
function(pkgs) {
unique(pkgs[!is.na(fastmatch::fmatch(pkgs, allowed_packages))])
}
)
}

Expand All @@ -280,9 +278,7 @@ scan_usage <- function(
skip_patterns = NULL,
use_knitr = FALSE
) {
ext <- file |>
sub(".*\\.", "", x = _) |>
tolower()
ext <- tolower(sub(".*\\.", "", file))

if (!ext %in% c("r", "rmd", "qmd")) {
cli::cli_abort(c(
Expand Down Expand Up @@ -462,7 +458,7 @@ scan_usage <- function(
c,
lapply(
code,
\(chunk) {
function(chunk) {
tryCatch(
parse(text = chunk, keep.source = FALSE),
error = function(e) NULL
Expand Down Expand Up @@ -938,7 +934,7 @@ scan_usage <- function(
cbind,
lapply(
meta$provider,
\(pkg) {
function(pkg) {
provider_rows <- attached_rows[[pkg]]
hits <- findInterval(visit_idx, attached$visit_idx[provider_rows])
out <- rep.int(-1L, length(visit_idx))
Expand Down
2 changes: 1 addition & 1 deletion R/universe.R
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ build_universe_data <- function(packages) {
origin_map <- build_origin_map(exports)

pkg_versions <- stats::setNames(
lapply(packages, \(p) as.character(utils::packageVersion(p))),
lapply(packages, function(p) as.character(utils::packageVersion(p))),
packages
)

Expand Down
14 changes: 7 additions & 7 deletions data-raw/sysdata.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
# - .scan_skip_dirs: directory names to skip when scanning projects

# Precompute standard library functions
.stdlib_funs <- lapply(
c("base", "stats", "utils", "graphics", "grDevices", "methods"),
getNamespaceExports
) |>
unlist(use.names = FALSE) |>
unique() |>
sort()
.stdlib_funs <- sort(unique(unlist(
lapply(
c("base", "stats", "utils", "graphics", "grDevices", "methods"),
getNamespaceExports
),
use.names = FALSE
)))

# Default skip directories
.scan_skip_dirs <- c(
Expand Down
7 changes: 7 additions & 0 deletions tests/testthat/fixtures/ascribetestbroken/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Package: ascribetestbroken
Version: 0.0.1
Title: Test Fixture for Ascribe Broken Namespace Handling
Description: Minimal package with an erroring active binding.
Authors@R: person("Ascribe", "Tests", email = "tests@ascribe.test", role = c("aut", "cre"))
License: MIT
Imports: R6
1 change: 1 addition & 0 deletions tests/testthat/fixtures/ascribetestbroken/NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export(SomeClass)
12 changes: 12 additions & 0 deletions tests/testthat/fixtures/ascribetestbroken/R/code.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# jarl-ignore unused_function: package load hook called by R
.onLoad <- function(libname, pkgname) {
ns <- asNamespace(pkgname)
makeActiveBinding("broken", function() stop("boom"), env = ns)
}

SomeClass <- R6::R6Class(
"SomeClass",
public = list(
method = function() "upstream"
)
)
7 changes: 7 additions & 0 deletions tests/testthat/fixtures/ascribetestdownstreamr6/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Package: ascribetestdownstreamr6
Version: 0.0.1
Title: Test Fixture for Ascribe R6 Re-export Handling
Description: Minimal downstream package for testing R6 re-export detection.
Authors@R: person("Ascribe", "Tests", email = "tests@ascribe.test", role = c("aut", "cre"))
License: MIT
Imports: ascribetestupstreamr6
2 changes: 2 additions & 0 deletions tests/testthat/fixtures/ascribetestdownstreamr6/NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
importFrom(ascribetestupstreamr6, ExportedClass)
export(ExportedClass)
7 changes: 7 additions & 0 deletions tests/testthat/fixtures/ascribetestupstreamr6/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Package: ascribetestupstreamr6
Version: 0.0.1
Title: Test Fixture for Ascribe R6 Discovery
Description: Minimal package with exported and internal R6 classes for testing collect_r6_methods.
Authors@R: person("Ascribe", "Tests", email = "tests@ascribe.test", role = c("aut", "cre"))
License: MIT
Imports: R6
1 change: 1 addition & 0 deletions tests/testthat/fixtures/ascribetestupstreamr6/NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export(ExportedClass)
13 changes: 13 additions & 0 deletions tests/testthat/fixtures/ascribetestupstreamr6/R/classes.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
ExportedClass <- R6::R6Class(
"ExportedClass",
public = list(
exported_method = function() "exported"
)
)

InternalClass <- R6::R6Class(
"InternalClass",
public = list(
internal_method = function() "internal"
)
)
39 changes: 39 additions & 0 deletions tests/testthat/setup.R
Original file line number Diff line number Diff line change
@@ -1,5 +1,44 @@
options(cli.default_handler = function(msg) invisible(NULL))

# Single fixture installation helper - accepts one or more fixture names
with_fixtures <- function(fixture_names, code) {
skip_if_not_installed("R6")

tmp_lib <- tempfile()
dir.create(tmp_lib)
old_lib <- .libPaths()

on.exit(
{
# Unload fixture namespaces in reverse order
for (pkg in rev(fixture_names)) {
if (isNamespaceLoaded(pkg)) {
unloadNamespace(pkg)
}
}
.libPaths(old_lib)
unlink(tmp_lib, recursive = TRUE)
},
add = TRUE
)

.libPaths(c(tmp_lib, old_lib))

for (fixture_name in fixture_names) {
fixture_dir <- testthat::test_path("fixtures", fixture_name)
utils::install.packages(
fixture_dir,
repos = NULL,
type = "source",
lib = tmp_lib,
INSTALL_opts = c("--no-staged-install", "--no-test-load"),
quiet = TRUE
)
}

code()
}

test_universe <- function(packages, export_index = list(), origin_map = NULL) {
if (is.null(origin_map)) {
origin_map <- new.env(parent = emptyenv())
Expand Down
Loading
Loading