-
Notifications
You must be signed in to change notification settings - Fork 233
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
Using @export on a S3 method registers it and does not export it #1322
Comments
The current practice is that the S3 methods themselves are not exported. Most of the time there is no need to export the methods. The generics will still find them at dispatch time, as long as they are registered, and this is exactly what roxygen2 does. Practically all packages follow this pattern, including base R itself. E.g: https://github.com/wch/r-source/blob/71e6c5e488522a9051b4710bd74940a890905311/src/library/tools/NAMESPACE#L66 If you still want to export an S3 method, in addition to registering it, you can do it like this: #' @export format.foo
#' @export
format.foo <- function(x, ...) {
...
} But considering the current practice, this is not something roxygen2 should do by default. |
Thanks @gaborcsardi, It is not a problem for base because its namespace will always be attached. I cannot judge of the current practice since at least in the case of I am OK with your solution. Let's see if that happens to more people... |
that links was to the tools package
For S3 method search it does not matter if a package is attached or not. E.g. > debug(tools:::format.undoc)
> search()
[1] ".GlobalEnv" "package:stats" "package:graphics"
[4] "package:grDevices" "package:utils" "package:datasets"
[7] "package:methods" "Autoloads" "package:base"
> x <- structure(list("foo"), class = "undoc")
> format(x)
debugging in: format.undoc(x)
debug: {
...
I am fairly sure that there is no issue to solve here. But if you create a reprex or show your package, then we can be completely sure. |
Thanks @gaborcsardi, here is what I think constitutes a reprex using the following packages: bar_0.0.0.9000.tar.gz remotes::install_local("./bar_0.0.0.9000.tar.gz")
#> * checking for file ‘/tmp/RtmpaVTyxD/remotes9e0f564d6ed8c/bar/DESCRIPTION’ ... OK
#> * preparing ‘bar’:
#> * checking DESCRIPTION meta-information ... OK
#> * checking for LF line-endings in source and make files and shell scripts
#> * checking for empty or unneeded directories
#> * building ‘bar_0.0.0.9000.tar.gz’
#> Installing package into '/home/courtiol/R/x86_64-pc-linux-gnu-library/4.1'
#> (as 'lib' is unspecified)
devtools::check_built("./foo_0.0.0.9000.tar.gz")
#> ── Checking ───────────────────────────────────────────────────────────── foo ──
#> Setting env vars:
#> • _R_CHECK_CRAN_INCOMING_USE_ASPELL_: TRUE
#> • _R_CHECK_CRAN_INCOMING_REMOTE_ : FALSE
#> • _R_CHECK_CRAN_INCOMING_ : FALSE
#> • _R_CHECK_FORCE_SUGGESTS_ : FALSE
#> ── R CMD check ─────────────────────────────────────────────────────────────────
#> * using log directory ‘/tmp/RtmpaVTyxD/foo.Rcheck’
#> * using R version 4.1.3 (2022-03-10)
#> * using platform: x86_64-pc-linux-gnu (64-bit)
#> * using session charset: UTF-8
#> * using options ‘--no-manual --as-cran’
#> * checking for file ‘foo/DESCRIPTION’ ... OK
#> * this is package ‘foo’ version ‘0.0.0.9000’
#> * package encoding: UTF-8
#> * checking package namespace information ... OK
#> * checking package dependencies ... OK
#> * checking if this is a source package ... OK
#> * checking if there is a namespace ... OK
#> * checking for executable files ... OK
#> * checking for hidden files and directories ... OK
#> * checking for portable file names ... OK
#> * checking for sufficient/correct file permissions ... OK
#> * checking serialization versions ... OK
#> * checking whether package ‘foo’ can be installed ... OK
#> * checking installed package size ... OK
#> * checking package directory ... OK
#> * checking for future file timestamps ... OK
#> * checking DESCRIPTION meta-information ... OK
#> * checking top-level files ... OK
#> * checking for left-over files ... OK
#> * checking index information ... OK
#> * checking package subdirectories ... OK
#> * checking R files for non-ASCII characters ... OK
#> * checking R files for syntax errors ... OK
#> * checking whether the package can be loaded ... OK
#> * checking whether the package can be loaded with stated dependencies ... OK
#> * checking whether the package can be unloaded cleanly ... OK
#> * checking whether the namespace can be loaded with stated dependencies ... OK
#> * checking whether the namespace can be unloaded cleanly ... OK
#> * checking loading without being on the library search path ... OK
#> * checking dependencies in R code ... OK
#> * checking S3 generic/method consistency ... OK
#> * checking replacement functions ... OK
#> * checking foreign function calls ... OK
#> * checking R code for possible problems ... OK
#> * checking Rd files ... OK
#> * checking Rd metadata ... OK
#> * checking Rd line widths ... OK
#> * checking Rd cross-references ... OK
#> * checking for missing documentation entries ... OK
#> * checking for code/documentation mismatches ... OK
#> * checking Rd \usage sections ... OK
#> * checking Rd contents ... OK
#> * checking for unstated dependencies in examples ... OK
#> * checking R/sysdata.rda ... OK
#> * checking examples ... ERROR
#> Running examples in ‘foo-Ex.R’ failed
#> The error most likely occurred in:
#> > base::assign(".ptime", proc.time(), pos = "CheckExEnv")
#> > ### Name: test
#> > ### Title: Test
#> > ### Aliases: test
#> >
#> > ### ** Examples
#> >
#> > test()
#> $x
#> [1] 1
#> attr(,"class")
#> [1] "bar"
#> attr(,"row.names")
#> [1] 1
#> Error in UseMethod("predict") :
#> no applicable method for 'predict' applied to an object of class "bar"
#> Calls: test -> <Anonymous>
#> Execution halted
#> * checking for non-standard things in the check directory ... OK
#> * checking for detritus in the temp directory ... OK
#> * DONE
#>
#> Status: 1 ERROR
#> See
#> ‘/tmp/RtmpaVTyxD/foo.Rcheck/00check.log’
#> for details.
#>
#> ── R CMD check results ───────────────────────────────────── foo 0.0.0.9000 ────
#> Duration: 8.9s
#>
#> > checking examples ... ERROR
#> Running examples in ‘foo-Ex.R’ failed
#> The error most likely occurred in:
#>
#> > base::assign(".ptime", proc.time(), pos = "CheckExEnv")
#> > ### Name: test
#> > ### Title: Test
#> > ### Aliases: test
#> >
#> > ### ** Examples
#> >
#> > test()
#> $x
#> [1] 1
#>
#> attr(,"class")
#> [1] "bar"
#> attr(,"row.names")
#> [1] 1
#> Error in UseMethod("predict") :
#> no applicable method for 'predict' applied to an object of class "bar"
#> Calls: test -> <Anonymous>
#> Execution halted
#>
#> 1 error x | 0 warnings ✓ | 0 notes ✓
#> Error: R CMD check found ERRORs Created on 2022-04-09 by the reprex package (v2.0.1) Setup{bar} contains a single function -- #' Predict for class bar
#'
#' @param object input
#' @param ... other
#' @export
#'
predict.bar <- function(object, ...) {
print("predict for class bar")
} The NAMESPACE file of {bar} contains the registration of the method: # Generated by roxygen2: do not edit by hand
S3method(predict,bar) {foo} contains an internal object of class bar -- {foo} also contain a single function -- #' Test
#'
#' @export
#' @examples
#' test()
test <- function() {
print(d)
stats::predict(d)
} The DESCRIPTION file from {foo} does declare the Import of {bar} :
So the mystery to solve here is why we have a 'no applicable method for 'predict' applied to an object of class "bar"'. So either I am missing something big and obvious (very possible) or something is wrong. My hypothesis was that it is because Now I think that the issue is that {bar} is not automatically added to the search path because there is not a single call to Of course it is easy to go around the issue by calling Perhaps I should try to lobby CRAN so that all packages added to Imports are added to the search path? Or did I really miss something big and obvious? Thanks for having a look, Alex |
I reproduce the problem if I use |
In project foo, |
The same problem is also detected outside R (with an additional informative NOTE supporting my last remark):
|
Sorry, I mixed foo and bar. So starting again: I now precisely have first the NOTE that you show: |
To sum up: Behaviour
Possible issues
Possible workarounds
Open questions
|
This has nothing to do with the search path. R fails to find the method if the package that adds the method is not loaded:
If you load bar, then the method is registered at load time:
So if you don't import anything from bar, you need to make sure that you load it, if you want to use methods that bar defines for your objects. |
I thought loading = adding namespace to search path, and it does not solve the open questions, but OK. |
No, that is not true. You can load a package without adding it to the search path. E.g. if package A imports package B (via
There are no issues as far as I can tell. If you want to use a class & method from a package (bar in your example), then you need to make sure that you load that package. You can load it by importing something from it, via
I don't think so. roxygen2 works correctly, and according to the current practices. |
Thanks a lot @gaborcsardi ! |
this fixes roxygen2 warnings and is the right thing to do, cf.: - https://roxygen2.r-lib.org/articles/namespace.html#s3 - r-lib/roxygen2#1322
Adding
#'@export
to a function such aspredict.myclass()
addsS3method(predict, myclass)
to the NAMESPACE file of the package {foo}. However it does not addexport(predict.myclass)
.The result is that
foo::predict.myclass
does not exist (onlyfoo:::predict.myclass
).That can create problem.
For example, if a package {bar} makes no call to
foo::fn
but only to the genericstats::predict()
, then the dispatch won't work.This is because the foo namespace will not be present in the search path (even if package is listed in Imports).
We can go around this problem by e.g. using
requireNamespace("foo")
inside bar, but this is not elegant.Also it is sometimes nice to refer, in internal code at least, directly to the function used so as not to obfuscate where functions come from.
(Another good reason for this is that CMD won't fault if foo is not declared in Imports if
stats::predict()
is used).Perhaps you had a good reason not to create
export(predict.myclass)
on top ofS3method(predict, myclass)
, but I think that#'@export
suggests to the package developer that the function will be exported while in fact what happens now is that the function is registered but not exported.If that does not cause problems I don't anticipate, I would thus recommend that
#'@export
should generate the export statement in the NAMESPACE file (in addition to the registration).The text was updated successfully, but these errors were encountered: