Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document how to avoid build-time dependency #76

Open
krlmlr opened this issue Oct 14, 2018 · 12 comments
Open

Document how to avoid build-time dependency #76

krlmlr opened this issue Oct 14, 2018 · 12 comments
Labels
feature a feature request or enhancement

Comments

@krlmlr
Copy link
Member

krlmlr commented Oct 14, 2018

Following @gaborcsardi's advice in https://stat.ethz.ch/pipermail/r-devel/2017-January/073647.html, I tried memoising in .onLoad(), and it worked for me:

# file.R
fun <- function() {
  some_expensive_process()
}

# zzz.R
.onLoad <- function(pkgname, libname) {
  fun <<- memoise::memoise(fun)
}

This eliminates the build-time dependency on memoise and also gets rid of R CMD check warnings about using memoise without importing it.

I wonder if and where we should document this.

@mdsumner
Copy link
Contributor

mdsumner commented Jul 4, 2019

Oh magic, thanks! I was looking for this advice in ?memoise and in the readme, and then I searched through Issues to find this. It's a subtley about the package process that I hadn't understood before. Would a short note suffice?

It is recommended that functions in a package are not memoised at build-time, but when the package is loaded. The simplest way to do this is within .onLoad() with, for example <code block above>

That would have worked for me in readme and/or help.

@mdsumner
Copy link
Contributor

mdsumner commented Jul 4, 2019

Added a PR for this

@dpprdan
Copy link
Contributor

dpprdan commented Jul 23, 2019

It's a kind of magic indeed.

Could I also update my memoised function at run-time like this?

E.g. I have defined my function fn as mentioned above and now I want to give the user the option to set a different cache. For this I want to use the function config_fn.

The following throws an error, because fn is already memoised.

config_fn <- function(cache = memoise::cache_memory()) {
  fn <<- memoise::memoise(fn), cache = cache))
}

I guess one can use environment(fn)[["_f"]] instead (with the problems mentioned in #36, so this might be a relevant use case for unmemoise(), IMHO)?

config_fn <- function(cache = memoise::cache_memory()) {
  fn <<- memoise::memoise(environment(fn)[["_f"]], cache = cache)
}

Still this does not seem to work - the cache does not get invalidated - so probably my understanding of (recursively updating) environments is flawed?

jimhester pushed a commit that referenced this issue Jul 30, 2019
* doc for avoid build-time dependency #76

* document() for Rd

* use preformatted

* No code with preformatted block
@dpprdan
Copy link
Contributor

dpprdan commented Oct 24, 2019

FWIW, @richfitz mentioned elsewhere that he has had a CRAN rejection 'for using <<- for "modifying the global environment" for uses where it does not modify the global environment'.

@gaborcsardi
Copy link
Member

@dpprdan No, that's just silly, and he/we need to push back on this. We clearly do not modify the global environment. We modify the package's namespace.

@tanho63
Copy link

tanho63 commented Aug 17, 2020

Appreciated this thread a bunch! I too nearly ran into a CRAN rejection for "global environment" but was able to demonstrate (with the help of this thread and Gabor's previous one) that it does not modify the global environment. I supplied a screenshot to help support it, leaving it here for future me and others:

image

@tylerlittlefield
Copy link

FWIW, @richfitz mentioned elsewhere that he has had a CRAN rejection 'for using <<- for "modifying the global environment" for uses where it does not modify the global environment'.

Bummer, I've just run into this as well :/

@dpprdan
Copy link
Contributor

dpprdan commented May 10, 2021

@tyluRp Is this an initial submission to CRAN or a package update?
It might help to explain in the comments why you are using <<-, like we did here for {opencage}: https://github.com/ropensci/opencage/blob/main/R/zzz.R
This is also in the current CRAN version. It was added with one of the last package updates, though, so chances are that no one looked at the source code.

@tanho63
Copy link

tanho63 commented May 10, 2021

Bummer, I've just run into this as well :/

@tyluRp this is fightable - I did end up winning on this! See the screenshot I had, where librarying the package + printing the global environment with all names via ls(all.names = TRUE) provides a clean env.

Also explain that you're modifying the package NS on load and nothing else?

@tylerlittlefield
Copy link

@tanho63 @dpprdan I ended up responding to the email with similar verbiage "this is modifying the packages namespace, not the global environment" and they sent my package to CRAN!! A pleasant surprise :)

trevorld added a commit to trevorld/gridpattern that referenced this issue May 18, 2021
* In order to prevent build time dependency on 'memoise'
  we define `img_read_memoised()` at build time
  and then replace it at load time with a
  memoised version within `.onLoad()`
* This is recommended in <http://memoise.r-lib.org/reference/memoise.html#details>
* See <r-lib/memoise#76> for further details
* On some platforms doing this also prevents an R CMD check NOTE.
eliocamp added a commit to eliocamp/metR that referenced this issue Oct 5, 2022
@tanho63
Copy link

tanho63 commented Aug 2, 2023

Coming back to this after discovering rlang::ns_env() which imo makes this easier to explain to others, e.g. CRAN

cache_function <- function(function_name, duration = 86400, omit_args = c()) {
  fn <- get(function_name, envir = rlang::ns_env("ffscrapr"))
  fn <- memoise::memoise(
    fn,
    ~ memoise::timeout(duration),
    omit_args = c(),
    cache = cachem::cache_memory()
  )
  assign(function_name, fn, envir = rlang::ns_env("ffscrapr"))
  return(invisible(TRUE))
}
.onLoad <- function(libname, pkgname) {
  lapply(c("ff_rosters", "mfl_players"), cache_function)
}

@jimbrig
Copy link

jimbrig commented Aug 29, 2024

Coming back to this after discovering rlang::ns_env() which imo makes this easier to explain to others, e.g. CRAN

cache_function <- function(function_name, duration = 86400, omit_args = c()) {
  fn <- get(function_name, envir = rlang::ns_env("ffscrapr"))
  fn <- memoise::memoise(
    fn,
    ~ memoise::timeout(duration),
    omit_args = c(),
    cache = cachem::cache_memory()
  )
  assign(function_name, fn, envir = rlang::ns_env("ffscrapr"))
  return(invisible(TRUE))
}
.onLoad <- function(libname, pkgname) {
  lapply(c("ff_rosters", "mfl_players"), cache_function)
}

This is great, I tweaked it a little bit to allow for assigning separate "prefixed" memoised functions if that fits your use case:

# internal ----------------------------------------------------------------

#' @keywords internal
#' @noRd
#' @importFrom memoise memoise timeout
#' @importFrom cachem cache_memory
#' @importFrom rlang ns_env
#' @importFrom glue glue
#' @importFrom cli cli_alert_info
.cache_function <- function(
  function_name,
  pkg,
  duration = 86400,
  omit_args = c(),
  cache = cachem::cache_mem(),
  rename_prefix = "mem_",
  quiet = TRUE,
  ...
) {

  fn <- base::get(function_name, envir = rlang::ns_env(pkg))

  mem_fn <- memoise::memoise(
    fn,
    ~ memoise::timeout(duration),
    omit_args = omit_args,
    cache = cache
  )

  mem_function_name <- glue::glue("{rename_prefix}{function_name}")

  assign(mem_function_name, mem_fn, envir = rlang::ns_env(pkg))

  if (!quiet) {
    cli::cli_alert_info("Created a cached function for {.field {function_name}} as {.field {mem_function_name}}.")
    cli::cli_alert_info("The cache will expire in {.field {duration}} seconds.")
  }

  return(invisible(TRUE))
}

# onLoad ------------------------------------------------------------------

#' @keywords internal
#' @noRd
#' @importFrom purrr walk
.onLoad <- function(libname, pkgname) {

  # cache functions ---------------------------------------------------------
  c(
    "get_entrata_reports_list",
    "get_entrata_report_info",
    "get_latest_report_version",
    "get_property_ids_filter_param"
  ) |>
    purrr::walk(.cache_function, pkg = pkgname, quiet = FALSE)

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature a feature request or enhancement
Projects
None yet
Development

No branches or pull requests

8 participants