Skip to content

Commit

Permalink
Expose three code loading strategies
Browse files Browse the repository at this point in the history
Fixes #822
  • Loading branch information
hadley committed Sep 18, 2019
1 parent 735f645 commit a149f36
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 32 deletions.
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ export(env_file)
export(env_package)
export(is_s3_generic)
export(is_s3_method)
export(load_installed)
export(load_options)
export(load_pkgload)
export(load_source)
export(namespace_roclet)
export(object)
export(object_format)
Expand Down
10 changes: 10 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# roxygen2 (development version)

* roxygen2 now provides three strategies for loading your code:

* `load_pkgload()` uses pkgload (now compiling when needed).
* `load_installed()` assumes you have installed the package.
* `load_source()` attaches required packages and `source()`s code in `R/`.

If the code loading strategy in roxygen2 6.1.0 and above has caused
you grief, you can revert to the old strategy by using
`Roxygen: list(load = "source")` (#822).

* New `@order n` tag controls the order in which blocks are processed. You can
use it to override the usual ordering which proceeds in from the top of
each file to the bottom. `@order 1` will be processed before `@order 2`,
Expand Down
81 changes: 81 additions & 0 deletions R/load.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#' Load package code
#'
#' @description
#' roxygen2 is a dynamic documentation system, which means it works with the
#' objects inside your package, not just the source code used to create them.
#' These functions offer various ways of loading your package to suit various
#' constraints:
#'
#' * `load_pkgload()` uses `pkgload::load_all()` to simulate package loading
#' as closely as we know how. It offers high fidelity handling of code that
#' uses S4, but requires that the package be compiled.
#'
#' * `load_source()` simulates package loading by attaching packages listed in
#' `Depends` and `Imports`, then sources all files in the `R/` directory.
#' This was the default strategy used in roxygen2 6.0.0 and earlier;
#' it's primary advantage is that it does not need compilation.
#'
#' * `load_installed()` uses the installed version of the package. Use this
#' strategy if you have installed a development version of the package
#' already. This is the highest fidelity strategy, but requires work
#' outside of roxygen2.
#'
#' You can change the default strategy for your function with roxygen2 `load`
#' option. Override the default off `pkgload` to use the `source` or
#' `installed` strategies:
#'
#' ```
#' RoxygenNote: list(load = "source")
#' ```
#' @name load
#' @param path Path to source package
NULL

#' @rdname load
#' @export
load_pkgload <- function(path) {
pkgload::load_all(path, helpers = FALSE, attach_testthat = FALSE)$env
}

#' @rdname load
#' @export
load_installed <- function(path) {
package <- desc::desc_get_field("Package", file = path)
asNamespace(package)
}

#' @rdname load
#' @export
load_source <- function(path) {
r_path <- file.path(path, "R")
if (!file.exists(r_path)) {
abort("Can't find 'R/' directory")
}

old_dir <- setwd(r_path)
on.exit(setwd(old_dir))

# Create environment
env <- new.env(parent = globalenv())
methods::setPackageName("roxygen_devtest", env)

# Attach dependencies
deps <- desc::desc_get_deps(path)
pkgs <- deps$package[
deps$type %in% c("Depends", "Imports") & deps$package != "R"
]
lapply(pkgs, require, character.only = TRUE)

# Source files
lapply(package_files(path), sys_source, envir = env)

env
}

sys_source <- function(file, envir = baseenv()) {
exprs <- parse(text = read_lines(file))
for (expr in exprs) {
eval(expr, envir = envir)
}
invisible()
}
3 changes: 2 additions & 1 deletion R/options.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ load_options <- function(base_path = ".") {
wrap = FALSE,
roclets = c("collate", "namespace", "rd"),
markdown = markdown_global_default,
old_usage = FALSE
old_usage = FALSE,
load = "pkgload"
)

unknown_opts <- setdiff(names(opts), names(defaults))
Expand Down
7 changes: 1 addition & 6 deletions R/parse.R
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,9 @@ env_file <- function(file) {
#' @export
#' @rdname parse_package
env_package <- function(path) {
pkgload::load_all(path,
compile = FALSE,
helpers = FALSE,
attach_testthat = FALSE
)$env
load_pkgload(path)
}


# helpers -----------------------------------------------------------------

order_blocks <- function(blocks) {
Expand Down
22 changes: 15 additions & 7 deletions R/roxygenize.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,26 @@
#'
#' Note that roxygen2 is a dynamic documentation system: it works by
#' inspecting loaded objects in the package. This means that you must
#' be able to load the package in order to document it.
#' be able to load the package in order to document it: see [load] for
#' details.
#'
#' @param package.dir Location of package top level directory. Default is
#' working directory.
#' @param roclets Character vector of roclet names to use with package.
#' This defaults to `NULL`, which will use the `roclets` fields in
#' the list provided in the `Roxygen` DESCRIPTION field. If none are
#' specified, defaults to `c("collate", "namespace", "rd")`.
#' The default, `NULL`, uses the roxygen `roclets` option,
#' which defaults to `c("collate", "namespace", "rd")`.
#' @param load_code A function used to load all the R code in the package
#' directory. It is called with the path to the package, and it should return
#' an environment containing all the sourced code.
#' directory. The default, `NULL`, uses the strategy defined by
#' the `code` roxygen option, which defaults to [load_pkgload()].
#' See [load] for more details.
#' @param clean If `TRUE`, roxygen will delete all files previously
#' created by roxygen before running each roclet.
#' @return `NULL`
#' @export
#' @importFrom stats setNames
roxygenize <- function(package.dir = ".",
roclets = NULL,
load_code = env_package,
load_code = NULL,
clean = FALSE) {

base_path <- normalizePath(package.dir)
Expand Down Expand Up @@ -72,6 +73,13 @@ roxygenize <- function(package.dir = ".",
)

# Now load code
load_code <- load_code %||% switch(options$load,
pkgload = load_pkgload,
source = load_source,
installed = load_installed,
abort("Unknown value of `load` option")
)

env <- load_code(base_path)
blocks <- lapply(blocks, block_set_env,
env = env,
Expand Down
42 changes: 42 additions & 0 deletions man/load.Rd

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

27 changes: 9 additions & 18 deletions man/roxygenize.Rd

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

0 comments on commit a149f36

Please sign in to comment.