From renaud at techunix.technion.ac.il  Wed Feb 10 11:40:46 2016
From: renaud at techunix.technion.ac.il (Renaud Gaujoux)
Date: Wed, 10 Feb 2016 12:40:46 +0200
Subject: [Rd] Method from package dependency is not updated due to lazy load?
Message-ID: <CAHavPHGhFXfvi8FLMn_+kZ9DBMRUjo1SxRd7ghUNxVTznKTjBA@mail.gmail.com>

Hi,

not sure this is a bug or just an unavoidable undesirable side-effect -- or
just me that does not do the right thing.

Consider the code and output below. It creates 2 packages:
  * pkgA
  * pkgB that creates a method for a generic defined in pkgA

Changes in a method for the generic in pkgA (after re-installing pkgA) are
not reflected in pkgB, unless pkgB is re-installed against the new version
of pkgA.
I understand this may be linked to lazy-loading, which somehow keeps the
old method version.
More worrying is the fact that calling pkgA::genericA() in  .GlobalEnv
still calls the old version.

I guess this is due to lazy-load caching the method definition, but is this
normal/known behaviour?

This means that all the packages that depend on pkgA must be re-installed,
even after small internal changes that do not affect the interface or
behavior of the exported generic, e.g., internal functions with new
required arguments used in the imported generic (which is the error that
got me looking into this).

Updating reverse-dependencies when installing pkgA could solve the issue
(default with message, and optionally disabled). At minimum, a warning when
loading pkgB could notify the user that there could be issues due to
version discrepancy.

Thank you.

Bests,
Renaud

## Code

library(devtools)
unlink('test', recursive = TRUE)
dir.create('test/lib', recursive = TRUE)
# create packages
createA <- function(version){
  dir.create(file.path('test', version), recursive = TRUE)
  pdir <- file.path('test', version, 'pkgA')
  create(pdir, list(Depends = 'methods'), rstudio = FALSE)
  cat(sprintf("
  setGeneric('genericA', function(x) standardGeneric('genericA'))
  setMethod('genericA', 'missing', function(x) 'A-%s')", version), file =
file.path(pdir, 'R/function.R'))
}
createA('v1')
createA('v2')
# package B imports A
create('test/pkgB', list(Imports = 'pkgA', Depends = 'methods'), rstudio =
FALSE)
cat('import(pkgA)', file = 'test/pkgB/NAMESPACE', append = TRUE)
cat("
callA <- function() genericA()
setGeneric('genericA', package = 'pkgA')
setMethod('genericA', 'character', function(x) genericA())
", file = 'test/pkgB/R/function.R')

# install packages
install.packages('test/v1/pkgA', lib = 'test/lib', repos = NULL, quiet =
TRUE)
install.packages('test/pkgB', lib = 'test/lib', repos = NULL, quiet = TRUE)
# this returns A-v1
system("Rscript -e \"library(pkgA, lib = 'test/lib'); genericA()\"")
system("Rscript -e \"library(pkgB, lib = 'test/lib'); callA();
pkgA::genericA()\"")

# install pkgA version 2
install.packages('test/v2/pkgA', lib = 'test/lib', repos = NULL, quiet =
TRUE)
# this returns A-v2
system("Rscript -e \"library(pkgA, lib = 'test/lib'); genericA()\"")
# this still returns A-v1
system("Rscript -e \"library(pkgB, lib = 'test/lib'); callA();
pkgA::genericA()\"")

# re-install pkgB
install.packages('test/pkgB', lib = 'test/lib', repos = NULL, quiet = TRUE)
# this now returns A-v2
system("Rscript -e \"library(pkgB, lib = 'test/lib'); callA();
pkgA::genericA()\"")


### Output

> library(devtools)> unlink('test', recursive = TRUE)> dir.create('test/lib', recursive = TRUE)> # create packages> createA <- function(version){+   dir.create(file.path('test', version), recursive = TRUE)+   pdir <- file.path('test', version, 'pkgA')+   create(pdir, list(Depends = 'methods'), rstudio = FALSE)+   cat(sprintf("+   setGeneric('genericA', function(x) standardGeneric('genericA'))+   setMethod('genericA', 'missing', function(x) 'A-%s')", version), file = file.path(pdir, 'R/function.R'))+ }> createA('v1')Creating package 'pkgA' in '/home/renaud/tmp/r-devel/test/v1'
No DESCRIPTION found. Creating with values:


Package: pkgA
Title: What the Package Does (one line, title case)
Version: 0.0.0.9000
Authors at R: person("First", "Last", email = "first.last at example.com",
role = c("aut", "cre"))
Description: What the package does (one paragraph).
Depends: methods
License: What license is it under?
LazyData: true> createA('v2')Creating package 'pkgA' in
'/home/renaud/tmp/r-devel/test/v2'
No DESCRIPTION found. Creating with values:


Package: pkgA
Title: What the Package Does (one line, title case)
Version: 0.0.0.9000
Authors at R: person("First", "Last", email = "first.last at example.com",
role = c("aut", "cre"))
Description: What the package does (one paragraph).
Depends: methods
License: What license is it under?
LazyData: true> # package B imports A> create('test/pkgB',
list(Imports = 'pkgA', Depends = 'methods'), rstudio = FALSE)Creating
package 'pkgB' in '/home/renaud/tmp/r-devel/test'
No DESCRIPTION found. Creating with values:


Package: pkgB
Title: What the Package Does (one line, title case)
Version: 0.0.0.9000
Authors at R: person("First", "Last", email = "first.last at example.com",
role = c("aut", "cre"))
Description: What the package does (one paragraph).
Depends: methods
License: What license is it under?
LazyData: true
Imports: pkgA> cat('import(pkgA)', file = 'test/pkgB/NAMESPACE',
append = TRUE)> cat("+ callA <- function() genericA()+
setGeneric('genericA', package = 'pkgA')+ setMethod('genericA',
'character', function(x) genericA())+ ", file =
'test/pkgB/R/function.R')> > # install packages>
install.packages('test/v1/pkgA', lib = 'test/lib', repos = NULL, quiet
= TRUE)> install.packages('test/pkgB', lib = 'test/lib', repos = NULL,
quiet = TRUE)> # this returns A-v1> system("Rscript -e \"library(pkgA,
lib = 'test/lib'); genericA()\"")Loading required package: methods
[1] "A-v1"> system("Rscript -e \"library(pkgB, lib = 'test/lib');
callA(); pkgA::genericA()\"")Loading required package: methods
[1] "A-v1"
[1] "A-v1"> > # install pkgA version 2>
install.packages('test/v2/pkgA', lib = 'test/lib', repos = NULL, quiet
= TRUE)> # this returns A-v2> system("Rscript -e \"library(pkgA, lib =
'test/lib'); genericA()\"")Loading required package: methods
[1] "A-v2"> # this still returns A-v1> system("Rscript -e
\"library(pkgB, lib = 'test/lib'); callA();
pkgA::genericA()\"")Loading required package: methods
[1] "A-v1"
[1] "A-v1"> > # re-install pkgB> install.packages('test/pkgB', lib =
'test/lib', repos = NULL, quiet = TRUE)> # this now returns A-v2>
system("Rscript -e \"library(pkgB, lib = 'test/lib'); callA();
pkgA::genericA()\"")Loading required package: methods
[1] "A-v2"
[1] "A-v2"


-- 
Renaud Gaujoux, PhD
Systems Immunology - Technion, Haifa, Israel

	[[alternative HTML version deleted]]



[Rd] Method from package dependency is not updated due to lazy load?
Renaud Gaujoux