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

devtools::document() errors before NAMESPACE can be updated #1144

Closed
daynefiler opened this issue May 29, 2020 · 18 comments · Fixed by #1538
Closed

devtools::document() errors before NAMESPACE can be updated #1144

daynefiler opened this issue May 29, 2020 · 18 comments · Fixed by #1538
Labels
bug an unexpected problem or unintended behavior namespace 👩‍🚀
Milestone

Comments

@daynefiler
Copy link

When running devtools::document() using roxygen2 to manage the namespace, the function errors before the NAMESPACE file can be updated/written.

I have run into this in two situations:

(1) adding a new import; e.g. trying to add the S4Vectors::setValidity2 function using #' @importFrom S4Vectors setvalidity2:

devtools::document()
# Updating badInstall documentation
# Loading badInstall
# Error in setValidity2(Class = "TestClass", method = .TestClass.validity) 
# (from methods-TestClass.R#12) : 
#   could not find function "setValidity2"

(2) renaming a generic method:

devtools::document()
# Updating badInstall documentation
# Loading badInstall
# Error in add_classes_to_exports(ns = nsenv, package = package, exports = exports,  : 
#   in ‘badInstall’ methods for export not found: myOldGeneric
#   In addition: Warning message:
#   In setup_ns_exports(path, export_all, export_imports) :
#     Objects listed as exports, but not present in namespace: myOldGeneric

I am using RStudio: v1.3.959

sessionInfo()
# R version 4.0.0 (2020-04-24)
# Platform: x86_64-apple-darwin17.0 (64-bit)
# Running under: macOS Catalina 10.15.4
# 
# Matrix products: default
# BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
# LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
# 
# locale:
# [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
# 
# attached base packages:
# [1] stats     graphics  grDevices utils     datasets  methods   base     
# 
# loaded via a namespace (and not attached):
#  [1] Rcpp_1.0.4.6        compiler_4.0.0      prettyunits_1.1.1  
#  [4] remotes_2.1.1       tools_4.0.0         testthat_2.3.2     
#  [7] digest_0.6.25       pkgbuild_1.0.8      pkgload_1.0.2      
# [10] memoise_1.1.0       rlang_0.4.6         cli_2.0.2          
# [13] rstudioapi_0.11     parallel_4.0.0      xfun_0.14          
# [16] withr_2.2.0         stringr_1.4.0       roxygen2_7.1.0     
# [19] knitr_1.28          xml2_1.3.2          desc_1.2.0         
# [22] S4Vectors_0.26.1    fs_1.4.1            devtools_2.3.0     
# [25] stats4_4.0.0        rprojroot_1.3-2     glue_1.4.1         
# [28] R6_2.4.1            processx_3.4.2      fansi_0.4.1        
# [31] sessioninfo_1.1.1   callr_3.4.3         purrr_0.3.4        
# [34] magrittr_1.5        backports_1.1.7     ps_1.3.3           
# [37] ellipsis_0.3.1      BiocGenerics_0.34.0 usethis_1.6.1      
# [40] assertthat_0.2.1    stringi_1.4.6       crayon_1.3.4  
@daynefiler
Copy link
Author

If it's helpful, I threw up this repository: https://github.com/daynefiler/badInstall.

@daynefiler
Copy link
Author

@jimhester any insight here?

@hadley hadley transferred this issue from r-lib/devtools Jul 30, 2020
@hadley
Copy link
Member

hadley commented Jul 30, 2020

This is a roxygen2 issue so I've moved it there.

@adolgert
Copy link

adolgert commented Oct 8, 2020

A workaround is to delete the NAMESPACE file. It will be rebuilt without an error.

@hadley
Copy link
Member

hadley commented Apr 16, 2021

Yeah, we don't have a good automated solution for this — as @adolgert suggests, your best bet is just to delete the NAMESPACE and start from scratch. I'll leave this open since there might be something we can do in pkgload, I just need to think about it a bit more.

@hadley
Copy link
Member

hadley commented Mar 31, 2022

It looks like this might be a regression introduced by 97c6c2d — we really need to do the namespace ore-processing before any code is run, but now we need to load the package in order to evaluate inline code.

@MichaelChirico
Copy link
Contributor

Related issue (please LMK if this is more appropriate as an independent issue), continued from r-lib/pkgload#239.

I would expect roxygenize() to work even in the absence of a NAMESPACE file as long as all the @import/@importFrom entries are already in roxygen2 mark-up. Is that not possible due to some circular dependency issue?

@hadley
Copy link
Member

hadley commented May 15, 2023

Yeah, the problem is that roxygen2 is a mostly dynamic doc system, so it executes the code as a part of the tag generation. That means you need imports defined, which gets us into this circular dependency problem.

It's possible to break the cycle in the same way as we did for collate (see update_collate()), but it requires quite a bit of work, since we need to handle @import and @importFrom manually (and presumably a few others like @useDynLib).

@MichaelChirico
Copy link
Contributor

Maybe there could be a new roclet initiate_namespace? That only handles the @import/@importFrom part of the current namespace roclet? Not sure if that actually gets around the problem in any meaningful way.

@hadley
Copy link
Member

hadley commented May 15, 2023

Yeah, the problem is that it can't be a roclet, because all the roclet machinery assumes that you have not just the sources, but the package namespace.

@MichaelChirico
Copy link
Contributor

MichaelChirico commented May 25, 2023

FWIW, I wrote a janky script to initialize the NAMESPACE before load_all() is attempted. Sharing in case it's useful to anyone else in this thread:

r_files <- list.files(
  file.path(package_dir, "R"),
  # exclude e.g. sysdata.rda
  pattern = "\\.[rR]$",
  full.names = TRUE
)
namespace_imports <- unlist(lapply(r_files, function(r_file) {
  # NB: roxygen2::parse_package() will attempt to compile src code
  file_roxy <- roxygen2:::tokenize_file(r_file)
  unlist(lapply(file_roxy, function(roxy) {
    unlist(lapply(roxy$tag, function(entry) {
      # For entries '@import pkg', NAMESPACE entry is import(pkg)
      # For entries '@importFrom pkg ...', it's importFrom(pkg, ...)
      #   ditto for @importClassesFrom and @importMethodsFrom.
      switch(entry$tag,
        import = ,
        importFrom = ,
        importClassesFrom = ,
        importMethodsFrom = sprintf(
          "%s(%s)",
          entry$tag, toString(roxygen2:::auto_quote(entry$val))
        )
      )
    }))
  }))
}))
if (!is.null(namespace_imports)) {
  # must fake the roxygen2 header in order for the next pass to
  #   process @exports correctly.
  # NB: unique() doesn't necessarily eliminate all duplicate exports here.
  writeLines(
    c(roxygen2:::made_by("#"), "", sort(unique(namespace_imports))),
    file.path(package_dir, "NAMESPACE")
  )
}

@MichaelChirico
Copy link
Contributor

Also FWIW, I think the current documentation is a bit ?misleading?

The NAMESPACE is generated in two passes: the first generates only import directives (because this can be computed without evaluating package code), and the second generates everything (after the package has been loaded).

That describes exactly what we wanted out of this roclet. But now it seems it's not possible to fully rely on that behavior because load_all() is attempted before the roclet. Does that paragraph need some fine-tuning/caveats?

@hadley
Copy link
Member

hadley commented May 25, 2023

Oh hmmm, maybe I forgot that I already implemented this, and then broke it with another change?

@MichaelChirico
Copy link
Contributor

MichaelChirico commented May 25, 2023

For us it is the pkgload update that breaks our flow, rather than anything in roxygen2 per se.

We also found it's necessary in some cases working with S4 methods (haven't nailed down an MRE on what exactly triggers it).

@hadley
Copy link
Member

hadley commented May 26, 2023

Ok yeah, the problem is that the package is being loaded before the roclet_preprocess.roclet_namespace() is called. But it looks like it's not going to be trivial simply load the package later because the markdown parsing (in particular ```R support) requires the package to be loaded.

Fixing this is going to require some work. I think we could either try to put off the markdown parsing until after block_set_env or we could rewrite roclet_preprocess.roclet_namespace() to work standalone like update_collate().

@MichaelChirico
Copy link
Contributor

MichaelChirico commented May 26, 2023

FWIW the current code works in the vast majority of cases to initialize the NAMESPACE from scratch.

Of ~500 packages we only see 4 packages and two classifications of error:

  • S4 methods table issues [applies to pkgload 1.2.4+]
  • sysdata.Rda data sets [applies to pkgload 1.3.2+]

We patched the above snippet as an exported function and that fixed the S4 methods case, leaving only 1 package where the sysdata.Rda bug persists (I'm not sure how to fix that yet See my next comment below).

So, not sure it's worth fixing TBH, as long as workarounds are documented, and perhaps we could export the init_namespace() helper?

@hadley
Copy link
Member

hadley commented Nov 1, 2023

@MichaelChirico it bugs me enough that I'd prefer to fix it properly 😄

@MichaelChirico

This comment was marked as outdated.

hadley added a commit that referenced this issue Nov 21, 2023
This effectively makes the preprocess method irrelevant, but there's no much point in removing it since there are so few extension authors.

Fixes #1144
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug an unexpected problem or unintended behavior namespace 👩‍🚀
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants