R:修补包函数并重新加载基础库

标签 r

有时,人们想要修补包中的某个函数,而不需要重新编译整个包。

例如,在 Emacs ESS 中,如果未加载 tcltk,函数 install.packages() 可能会卡住。人们可能需要修补install.packages(),以便在安装之前需要tcltk,并在软件包安装后卸载它。

install.packages()temp() 修补版本可能是:

## Get original args without ending NULL
temp=rev(rev(deparse(args(install.packages)))[-1]) 
temp=paste(paste(temp, collapse="\n"),
           ## Add code to load tcltk
           "{",
           "    wasloaded= 'package:tcltk' %in% search()",
           "    require(tcltk)",
           ## Add orginal body without braces
           paste(rev(rev(deparse(body(install.packages))[-1])[-1]), collapse="\n"),
           ## Unload tcltk if it was not loaded before by user
           "    if(!wasloaded) detach('package:tcltk', unload=TRUE)",
           "}\n",
           sep="\n")

## Eval patched function 
temp=eval(parse(text=temp))
# temp

现在我们想要替换原来的 install.packages() 并可能将代码插入到 Rprofile 中。

为此,以下内容毫无值(value):

getAnywhere("install.packages")
# A single object matching 'install.packages' was found
# It was found in the following places
#   package:utils
#   namespace:utils
# with value
#  
# ... install.packages() source follows (quite lengthy) 

也就是说,该函数存储在utils的包/命名空间内。该环境是密封的,因此在替换之前应解锁 install.packages():

## Override original function
unlockBinding("install.packages", as.environment("package:utils"))
assign("install.packages", temp,  envir=as.environment("package:utils"))
unlockBinding("install.packages",  asNamespace("utils"))
assign("install.packages", temp,  envir=asNamespace("utils"))
rm(temp)

再次使用getAnywhere(),我们得到:

getAnywhere("install.packages")
# A single object matching 'install.packages' was found
# It was found in the following places
#   package:utils
#   namespace:utils
# with value
#  
# ... the *new* install.packages() source follows

看来修补的函数放置在正确的位置。

不幸的是,运行它会给出:

Error in install.packages(xxxxx) : 
  could not find function "getDependencies"

getDependency() 是同一 utils 包内的函数,但未导出;因此在其 namespace 之外无法访问它。
尽管有 getAnywhere("install.packages") 的输出,但修补后的 install.packages() 仍然错位。

问题是我们需要重新加载utils库才能获得想要的效果,这也需要卸载导入它的其他库。

detach("package:stats", unload=TRUE)
detach("package:graphics", unload=TRUE)
detach("package:grDevices", unload=TRUE)
detach("package:utils", unload=TRUE)   
library(utils)

install.packages() 现在可以工作。

当然,我们也需要重新加载其他库。考虑到依赖关系,使用

library(stats)

应该重新加载所有内容。但是重新加载 graphics 库时会出现问题,至少在 Windows 上是这样:

library(graphics)
# Error in FUN(X[[i]], ...) : 
#   no such symbol C_contour in package path/to/library/graphics/libs/x64/graphics.dll

(重新)加载图形库的正确方法是什么?

最佳答案

修补包中的函数是应该避免的低级操作,因为它可能会破坏执行环境的内部假设并导致不可预测的行为/崩溃。如果 tck/ESS 存在问题(我没有尝试重复),也许应该修复它,或者可能有解决方法。特别是要避免更改锁定的绑定(bind)。

如果您确实想在 install.packages 的开头/结尾运行一些代码,您可以使用 trace。它将执行问题中提到的一些低级操作,但好处是,每当 R 的一些新内部发生变化时,您不必担心修复此问题。

trace(install.packages, 
    tracer=quote(cat("Starting install.packages\n")),
    exit=quote(cat("Ending install packages.\n"))
)

相应地替换tracerexit - 也许无论如何都不需要exit,也许您不需要卸载包。尽管如此,trace 仍然是一个非常有用的调试工具。

我不确定这是否能解决您的问题 - 是否可以与 ESS 一起使用 - 但一般来说,您也可以将 install.packages 包装在您在工作区中定义的函数中:

install.packages <- function(...) {
    cat("Entry.\n")
    on.exit(cat("Exit.\n"))
    utils::install.packages(...)
}

这确实是最干净的选择。

关于R:修补包函数并重新加载基础库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40252292/

相关文章:

r - R中x轴和y轴ggplot的标签大小

R Shiny : How to color margin of title panel?

r - 如何从 R 向量中的每个元素中删除最后 n 个字符

xml - 使用 R 下载并读取压缩的 xml 文件

R/googlesheets4 非交互式 session

截距和斜率的随机效应作为结果模型

R - dplyr - mutate_if 多个条件

R:有条件删除矩阵中的行和列

r - 将 `dplyr::filter_at` 与多个谓词表达式一起使用

r - 使用 add_trace 将多条线添加到 plot_ly 图