【问题标题】:R: Patching a package function and reloading base librariesR:修补包功能并重新加载基础库
【发布时间】:2016-10-26 01:28:23
【问题描述】:

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

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

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

## 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中插入代码。

为此,毫无价值:

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"

getDependencies() 是同一个utils 包中的一个函数,但没有导出;因此它在其命名空间之外是不可访问的。
尽管输出了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

哪种是(重新)加载graphics 库的正确方法?

【问题讨论】:

  • 您在描述示例目的时迷失了我,但当我看到 eval(parse()) 时我停止阅读。

标签: r


【解决方案1】:

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

如果你真的想在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(...)
}

这确实是最干净的选择。

【讨论】:

  • +1 我知道跟踪,在这个术语中没有想到它。 install.package&lt;- ... 的问题在于它使 ls() 的输出混乱
猜你喜欢
  • 1970-01-01
  • 2017-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-07
  • 1970-01-01
相关资源
最近更新 更多