r-lib/pkgload

`pkgload::load_all(export_all = TRUE)` fails to properly load S3 methods

Aehmlo opened this issue · 2 comments

When using load_all(export_all = TRUE) (e.g., devtools::load_all()) with an exported S3 method, the method is properly sourced and appears in methods(class = "my_class"), but dispatch skips over it and instead uses the default method. If the method is assigned to itself (e.g., print.my_class <- print.my_class, it then works as expected. Furthermore, if devtools::document() is run, devtools::load_all() subsequently works as expected (the S3 dispatch works correctly). This leads me to believe that there may be a bug at the interface of namespace generation and S3 dispatch table caching, but I'm far from an expert here.

A full reproducible example is attached below. This behavior is reproducible across multiple installations of R on both macOS and Linux. I have also optimistically tried installing the revision proposed in #133 (306400e), but the issue persists in this version as well.

Click to view details (shell session/R REPL)
$ mkdir repro && cd $_
$ R --vanilla -q
> R.version
               _
platform       x86_64-apple-darwin19.6.0
arch           x86_64
os             darwin19.6.0
system         x86_64, darwin19.6.0
status
major          4
minor          0.3
year           2020
month          10
day            10
svn rev        79318
language       R
version.string R version 4.0.3 (2020-10-10)
nickname       Bunny-Wunnies Freak Out
> packageVersion("devtools")
[1] ‘2.3.1’
> packageVersion("pkgload")
[1] ‘1.1.0’
> sink("/dev/null")
> devtools::create(".")
✔ Setting active project to '/private/tmp/repro'
✔ Creating 'R/'
✔ Writing 'DESCRIPTION'
✔ Writing 'NAMESPACE'
✔ Changing working directory to './'
> q()
$ echo "#' @export" > R/well.R
$ echo 'print.well_spec <- function(x) print("Here")' >> R/well.R
$ cat R/well.R
#' @export
print.well_spec <- function(x) print("Here")
$ R --vanilla -q
> devtools::load_all()
Loading repro
> methods(class = "well_spec")
[1] print
see '?methods' for accessing help and source code
> spec <- structure(list(), class = "well_spec")
> print(spec)
list()
attr(,"class")
> print.well_spec(spec)
[1] "Here"
> print.well_spec <- print.well_spec # ???
> print(spec)
[1] "Here"
> methods(class = "well_spec")
[1] print
see '?methods' for accessing help and source code
> rm(print.well_spec)
> sink("/dev/null")
> devtools::document()
Updating repro documentation
Loading repro
> sink()
> methods(class = "well_spec")
[1] print
see '?methods' for accessing help and source code
> print(spec) # ???
[1] "Here"
> q()
$ R --vanilla -q
> sink("/dev/null")
> devtools::load_all()
Loading repro
> sink()
> spec <- structure(list(), class = "well_spec")
> print(spec) # ???
[1] "Here"
> q()
$ exit

Thank you for the reproducible example!

The issue is not the assignment, it is that you did not run devtools::document() the first time, so the S3 method was not registered the first time you ran it. If you run devtools::document() before the first time you run devtools::load_all() the dispatch works.

Note in R 4.0+ S3 methods in packages must be explicitly registered to be dispatched. In prior versions of R the dispatch fell back to lexical dispatch on the search path if a method was not registered.

S3 method lookup now by default skips the elements of the search path between the global and base environments.

R 4.0 release notes

Oh, I think what bit me here was the change you referred to in R 4.0. Thanks!