R脚本使用box包导入S4模块报错'.cacheOnAssign'未找到
box包导入S4类模块报错问题排查 环境配置与最小复现示例
项目目录结构如下:
SomeProject/ ├─ bin/ │ └─ someMinimalScript.R └─ utils/ └─ R/ └─ someMinimalModule.R
bin/someMinimalScript.R为可执行R脚本,内容如下:
#!/usr/bin/env Rscript box::use(./utils/R/someMinimalModule[ someClass ])
utils/R/someMinimalModule.R为被导入模块,初始内容为S4类定义代码:
#' @export someClass <- setClass( "someClass", slots = list( example_slot = "character" ), prototype = list( example_slot = character() ) ) #' @export setMethod("show", "someClass", \(object) { cat("\n") cat("Example slot:", object@example_slot, "\n") cat("\n") })
项目为命令行工具,配置方式:
- 在
.bashrc中添加配置export PATH="/Users/someUser/someProject/bin:$PATH"将脚本路径加入系统环境变量 - 执行
chmod 755 ./bin/*为脚本赋予可执行权限
报错复现过程
- 首次运行报错:配置完成后命令行执行
someMinimalScript.R,错误信息如下:
Error in box::use(./utils/R/someMinimalModule[someClass]) : could not find function "setClass" (inside “setClass("someClass", slots = list(example_slot = "character"), ” “ prototype = list(example_slot = character()))”) Calls: <Anonymous> ... tryCatchList -> tryCatchOne -> <Anonymous> -> rethrow -> throw Execution halted
临时修复方式:给setClass、setMethod调用添加methods::前缀,修改后模块代码如下:
#' @export someClass <- methods::setClass( "someClass", slots = list( example_slot = "character" ), prototype = list( example_slot = character() ) ) #' @export methods::setMethod("show", "someClass", \(object) { cat("\n") cat("Example slot:", object@example_slot, "\n") cat("\n") })
- 二次运行报错:修复上述问题后再次执行脚本,新错误信息如下:
Error in box::use(./utils/R/someMinimalModule[someClass]) : name '.cacheOnAssign' not found in 'env' (inside “env$.cacheOnAssign”) Calls: <Anonymous> ... tryCatchList -> tryCatchOne -> <Anonymous> -> rethrow -> throw Execution halted
该报错在交互式R环境中同样可以复现。
运行环境信息
- R版本:4.2.0 (2022-04-22),运行平台aarch64-apple-darwin21.3.0 (64-bit),操作系统macOS Monterey 12.2.1,默认加载stats、graphics、grDevices、utils、datasets、methods、base全部基础包
box包版本:1.1.2
根因分析
两个报错分别对应不同层面的问题:
- 首次
could not find function "setClass"报错:box包加载模块时,默认仅将base包挂载到模块的独立搜索路径下,不会自动附加其他基础包。即使R启动时全局环境加载了methods包,模块内部也无法直接访问methods包的导出函数,必须显式导入或写全命名空间前缀。 - 二次
.cacheOnAssign not found报错:这是*box1.1.2版本的已知兼容bug*。该版本的自动导出逻辑在处理methods::setClass返回的S4类生成器对象时,会尝试访问模块环境中的内部缓存标记.cacheOnAssign,但S4类注册时的环境绑定逻辑不会自动创建该变量,直接触发变量不存在的错误。
解决方案
按推荐优先级排序:
- 升级
box包到1.2.0及以上版本
新版本已经修复S4对象导出时的缓存检查逻辑,升级后仅需要在模块顶部显式导入methods包即可,不需要给每个函数加命名空间前缀,也不会触发缓存变量报错。修改后的模块头部示例:# 模块顶部显式导入需要的methods函数 box::use(methods[setClass, setMethod]) # 后续S4定义代码不需要加methods::前缀,保持原有写法即可 #' @export someClass <- setClass( "someClass", slots = list(example_slot = "character"), prototype = list(example_slot = character()) ) #' @export setMethod("show", "someClass", \(object) { cat("\nExample slot:", object@example_slot, "\n\n") }) - 固定使用1.1.2版本的兼容写法
如果暂时无法升级包版本,可以通过显式调用box::export()导出对象,避开自动导出逻辑的bug,同时在模块顶部显式导入methods包:
不推荐使用手动创建box::use(methods[setClass, setMethod]) someClass <- setClass( "someClass", slots = list(example_slot = "character"), prototype = list(example_slot = character()) ) setMethod("show", "someClass", \(object) { cat("\nExample slot:", object@example_slot, "\n\n") }) # 手动显式导出,跳过自动导出的缓存检查 box::export(someClass).cacheOnAssign <- FALSE变量的hack写法,该变量为box内部实现变量,后续版本变动可能导致新的兼容问题。
内容的提问来源于stack exchange,提问作者dereckmezquita
相关产品推荐
相关产品推荐

