R: Sys.getenv 在包启动代码中总是 returns 空

R: Sys.getenv in package startup code always returns empty

所以我想我有点知道这里发生了什么,但我很难找到它的参考(在 SO 或 R 文档中),所以我想把它放出来,看看人们是否可以摆脱任何光线。

我有一个 R 包,它在 utils.R 文件的顶层(不在任何函数内)包含以下代码:

S3_BUCKET <- Sys.getenv('S3_BUCKET')
CACHE_DIR <- if (S3_BUCKET == "") {
  'cache/foo'
} else {
  paste0('s3://', S3_BUCKET, '/dir/cache/foo')
}
print(paste("CACHE_DIR:", CACHE_DIR))

当我通过 devtools::load_all('.') 加载包时,这在“开发模式”下工作正常,但是当我在我的环境中安装包并通过 library(mypkg)Sys.getenv('S3_BUCKET') 加载它时此代码始终 returns 空字符串(通过检查 mypkg:::S3_BUCKET 验证)。

我的假设 是在包加载期间评估“包级代码”时尚未设置环境变量。如果是这样 - 这是否在任何地方记录,如果没有,那么将它添加到文档的正确位置在哪里?还是应该修复的错误?

它看起来也可能 stdout 还没有设置(对于包?),因为 print 输出似乎从未出现过。

我要采用的解决方案是将其转换为 .onLoad 回调,我确信这是更好的做法:

GLOBALS <- new.env()
GLOBALS$CACHE_DIR <- 'cache/foo'

.onLoad <- function(libname, pkgname) {
  S3_BUCKET <- Sys.getenv('S3_BUCKET')
  if (S3_BUCKET != "") {
    assign('CACHE_DIR', paste0('s3://', S3_BUCKET, '/dir/cache/foo'), GLOBALS)
    print(paste("CACHE_DIR:", CACHE_DIR))
  }
}

这按预期工作。

如果 Sys.getenv 调用在顶层,那么每次加载包命名空间时都不会 运行。它是 运行 恰好一次:当 CRAN 或您通过 R CMD INSTALL 从您的包的源构建二进制文件时。如果你用

安装你的包
$ env S3_BUCKET=whatever R CMD INSTALL /path/to/package/root

那么你的包命名空间中 S3_BUCKET 的值将是 "whatever" 而不管 环境变量的值 S3_BUCKET 当命名空间被加载。如果要在加载时评估 Sys.getenv 调用,则需要将其放在 .onLoad 的主体中,如 ?.onLoad.

中所述

?utils::build 或 Writing R Extensions 手册(可通过 help.start() 访问)似乎并未明确说明仅在构建时评估源代码这一事实。 WRE 的 this 部分有丝毫线索:

Binary packages are compressed copies of installed versions of packages. They contain compiled shared libraries rather than C, C++ or Fortran source code, and the R functions are included in their installed form.

您必须推断“R 函数”实际上是指在顶层定义的任何对象。

它真的只是一种软件开发范式。 构建一个软件包就是创建源中定义的对象并以二进制格式序列化它们。 安装一个软件包就是在你的文件系统某处解压二进制文件。最终,用户获得 name-value 对,但不是用于生成值的源代码。

devtools 有点搅浑水,因为 load_all 在新环境中获取您的 .R 文件,然后将该环境附加到您的搜索路径。它会在现场执行此操作,跳过通常的构建和安装过程。这通常很方便,但如果您不了解所有注意事项,可能会导致头痛。