[英]Overriding a package function inherited by another package
I am trying to override the tidy.source
function of the knitr
package. 我试图覆盖knitr
包的tidy.source
函数。 The problem is that tidy.source
is defined in the formatR
package, which is imported by the knitr
package. 问题是tidy.source
是在formatR
包中定义的,它由knitr
包导入。 If I run: 如果我跑:
get("tidy.source", envir=asNamespace("knitr"))
I get the original code. 我得到了原始代码。 So I am tempted to override tidy.source
with: 所以我很想用以下方法覆盖tidy.source
:
assignInNamespace ("tidy.source", function()print("My tidy.source"), "knitr")
, assignInNamespace ("tidy.source", function()print("My tidy.source"), "knitr")
,
but I get: 但我得到:
Error in bindingIsLocked(x, ns) : no binding for "tidy.source"
. Error in bindingIsLocked(x, ns) : no binding for "tidy.source"
。
In fact tidy.source
is defined in formatR
and inherited by knitr
. 事实上tidy.source
被定义formatR
和继承knitr
。 With: 附:
assignInNamespace ("tidy.source", function()print("My tidy.source"), "formatR")
everything is apparently smooth, but checking again get("tidy.source", envir=asNamespace("knitr"))
shows that inside knitr
nothing has changed. 一切都显得顺利,但再次检查get("tidy.source", envir=asNamespace("knitr"))
表明knitr
里面没有任何变化。
Any help? 有帮助吗?
This question is partly obsolete due to the new development release of knitr/formatR . 由于knitr / formatR的新开发版本,这个问题已经过时了。 Many thanks to Yihui for noticing this discussion and for deciding to update his package. 非常感谢Yihui注意到这个讨论,并决定更新他的包。 See: 看到:
https://github.com/yihui/formatR/commit/6f70360f359caa8d2bb33190a1c89530defb0e98 https://github.com/yihui/formatR/commit/6f70360f359caa8d2bb33190a1c89530defb0e98
I can definitely switch from Sweave to knitr . 我绝对可以从Sweave切换到knitr 。
The general question concerning the overriding of an imported package function remains anyway open. 关于覆盖进口包裹功能的一般性问题仍未解决。 Since it is not related to knitr/formatR package anymore, I restate it in more general terms. 由于它与knitr / formatR包无关 ,所以我用更一般的术语重述它。
Suppose you have a package main importing the package imp . 假设您有一个包main导入包imp 。 If you load the former, "package:main"
shows in the list of the attached packages and both "main"
and "sub"
show among the names of the loaded namespaces. 如果加载前者, "package:main"
显示在附加的包列表中, "main"
和"sub"
显示在加载的命名空间的名称中。
Assume that main imports the exported sub function exp.sub.func , which calls in turns the non-exported sub function prv.sub.func . 假设main导入导出的子函数 exp.sub.func ,该函数调用非导出的子函数 prv.sub.func 。 If you want to change/customise exp.sub.func with your exp.sub.func.mod , you may think of using: 如果你想改变/自定义exp.sub.func你exp.sub.func.mod,你可能会想到使用:
assign("exp.sub.func", exp.sub.func.mod, asNamespace ("sub"))
As a result, by running sub::exp.sub.func
you will get your patched version (that is exp.sub.func.mod ). 因此,通过运行sub::exp.sub.func
您将获得修补版本(即exp.sub.func.mod )。
Unfortunately, as far as your exp.sub.func.mod keeps on relying on prv.sub.func , you get the error: 不幸的是,只要您的exp.sub.func.mod继续依赖prv.sub.func ,您就会收到错误:
Error in [...] : object 'prv.sub.func' not found
In fact: 事实上:
environment(sub::exp.sub.func)
returns now: <environment: R_GlobalEnv>
while it was <environment: namespace:sub>
before patching. 现在返回: <environment: R_GlobalEnv>
虽然在修补之前是<environment: namespace:sub>
。
The question is: how to move the patched function to the proper namespace ? 问题是: 如何将修补的函数移动到正确的命名空间 ?
To implement the problem above you can use whatever packages of course; 要实现上述问题,您当然可以使用任何包; in my case I used knitr and formatR as main and imported namespace and tidy.source() as patched function. 在我的例子中,我使用knitr和formatR作为主要和导入的命名空间,并使用tidy.source()作为修补函数。
Changing the function in the formatR namespace doesn't change what knitr uses because knitr is already loaded. 更改formatR命名空间中的函数不会更改knitr使用的内容,因为已经加载了knitr 。 So, you could unload and reload it. 所以,你可以卸载并重新加载它。
assignInNamespace("tidy.source", function()print("My tidy.source"), "formatR")
detach('package:knitr', unload=TRUE)
library(knitr)
get("tidy.source", envir=asNamespace("knitr"))
#function()print("My tidy.source")
If all you want is comments after function arguments, I have added the support in the development version , and you can install it from Github . 如果你想要的只是函数参数后面的注释,我在开发版本中 添加了支持,你可以从Github安装它 。
Normally it is a bad idea to modify packages using assignInNamespace()
, as its documentation shows. 通常,如文档所示,使用assignInNamespace()
修改包是个坏主意。
I am perhaps close to a solution but I have to deal with non-exported formatR functions. 我可能接近解决方案,但我必须处理非导出的formatR函数。 In fact the original tidy.source code, and therefore the patched version, has calls to non-exported package functions, eg reflow_comments
. 实际上,原始的tidy.source代码以及修补版本都调用了非导出的包函数,例如reflow_comments
。
To illustrate the problem and the step I followed, let's start with a test patched tidy.source calling a private formatR function. 为了说明问题和我遵循的步骤,让我们从测试修补的tidy.source开始,调用私有formatR函数。
### Listing 1 - Modified tidy.source
tidy.source.mod=function (source, output, text){
#Print body first line of reflow_comments
head(reflow_comments,1)
}
source, output, text
parameters are necessary as passed by knit used ahead. source, output, text
参数是必要的,因为前面使用的knit传递。
Now I can patch the tidy.source with tidy.source.mod : 现在我可以用打补丁的tidy.source.mod tidy.source:
### Listing 2 - Patch tidy.source
## General clean up
if("knitr" %in% loadedNamespaces() ) detach('package:knitr', unload=TRUE)
if("formatR" %in% loadedNamespaces() ) detach('package:formatR', unload=TRUE)
if(exists("tidy.source"))rm(tidy.source)
library("formatR")
## Info
environment(tidy.source )
# <environment: namespace:formatR>
environment(formatR::tidy.source )
# <environment: namespace:formatR>
## Change tidy.source with tidy.source.mod
unlockBinding("tidy.source", env=as.environment("package:formatR"))
assign("tidy.source", tidy.source.mod, envir=as.environment("package:formatR"))
lockBinding("tidy.source", env=as.environment("package:formatR"))
unlockBinding("tidy.source", env=asNamespace ("formatR"))
assign("tidy.source", tidy.source.mod, asNamespace ("formatR") )
environment(tidy.source)= asNamespace( "formatR" )
lockBinding("tidy.source", env=asNamespace ("formatR"))
We can check the results: 我们可以查看结果:
### Listing 3 - Check results
getAnywhere(tidy.source)
# A single object matching 'tidy.source' was found
# It was found in the following places
# .GlobalEnv
# package:formatR
# namespace:formatR
# with value
# function (){
# head(reflow_comments,1)
# }
# <environment: namespace:formatR>
tidy.source()
# 1 function (text, idx = grepl("^\\\\s*#+", text), width = getOption("width"))
Apparently tidy.source was correctly substituted with tidy.source.mod ; 显然tidy.source被正确地与tidy.source.mod取代; namespaces were updated so tidy.source has access to (the first line of) non-exported reflow_comments
function. 命名空间已更新,因此tidy.source可以访问(第一行)非导出的reflow_comments
函数。
To deal with knitr we need also a file to knit, here I use a text string for simplicity. 为了处理knitr,我们还需要一个文件来编织,这里我使用文本字符串来简化。
### Listing 4 - Sample file/text to knit
library("knitr")
text="
\\documentclass{article}
\\begin{document}
<<comme, include=TRUE>>=
print('hello')
@
\\end{document}
"
Is knitr able to see the patched tidy.source ? knitr能看到修补的tidy.source吗? We can check this with the help of R debug . 我们可以在R debug的帮助下检查这个。
debug(knit)
knit(text=text) #will enter debug session (prompt s'd be like 'Browse[2]>')
# debugging in: knit(text = text)
# debug: {
# knit body, very long, omitted
# }
tidy.source #command given inside the debug session
# function (){
# head(reflow_comments,1)
# }
tidy.source() # :-( reflow_comments is not accessible
Q #quit debug session
undebug(knit)
Unfortunately from knit the patched tidy.source is visible, but it can't access non-exported formatR functions, which for knit is normally possible via the non-patched tidy.source . 不幸的是,从编织修补的tidy.source是可见的,但它无法访问非导出的formatR函数,对于knit通常可以通过非修补的tidy.source 。
Here some hints may be that formatR::tidy.source()
doesn't work either: 这里有一些提示可能是formatR::tidy.source()
也不起作用:
formatR::tidy.source()
# Error in head(reflow_comments, 1) (from #2) : object 'reflow_comments' not found
The environment of namespace:formatR is: 命名空间的环境:formatR是:
environment(formatR::tidy.source )
# <environment: R_GlobalEnv>
That was <environment: namespace:formatR>
before patching (see info in Listing 2). 在修补之前,这是<environment: namespace:formatR>
(参见清单2中的信息)。 While environment(tidy.source )
was easily reset when patching, for namespace:formatR we get an error: 虽然在修补时很容易重置environment(tidy.source )
,但对于namespace:formatR我们得到一个错误:
environment(formatR::tidy.source )=asNamespace( "formatR" )
# Error in environment(formatR::tidy.source) = asNamespace("formatR") :
# object 'formatR' not found
I am still searching.... 我还在寻找....
This is a possible buggy attempt to allow inline comments after function arguments by tidy.source
function included in the formatR package. 这是一种可能的错误尝试,允许通过formatR包中包含的tidy.source
函数在函数参数之后进行内联注释。
## ============ Possible (wrong?) ideas on inline comments ============
tidy.source.mod= function (source = "clipboard", keep.comment = getOption("keep.comment",
TRUE), keep.blank.line = getOption("keep.blank.line", TRUE),
keep.space = getOption("keep.space", FALSE), replace.assign = getOption("replace.assign",
FALSE), left.brace.newline = getOption("left.brace.newline",
FALSE), reindent.spaces = getOption("reindent.spaces",
4), output = TRUE, text = NULL, width.cutoff = getOption("width"),
...)
{
if (is.null(text)) {
if (source == "clipboard" && Sys.info()["sysname"] ==
"Darwin") {
source = pipe("pbpaste")
}
text = readLines(source, warn = FALSE)
}
if (length(text) == 0L || all(grepl("^\\s*$", text))) {
if (output)
cat("\n", ...)
return(list(text.tidy = "", text.mask = ""))
}
text.lines = text
if (keep.comment) {
if (!keep.space)
text.lines = gsub("^[[:space:]]+|[[:space:]]+$",
"", text.lines)
head.comment = grepl("^[[:space:]]*#", text.lines)
if (any(head.comment)) {
text.lines[head.comment] = gsub("\"", "'", text.lines[head.comment])
}
if (!keep.space) {
head.comment = head.comment & !grepl("^\\s*#+'",
text.lines)
text.lines = reflow_comments(text.lines, head.comment,
width.cutoff)
head.comment = grepl("^[[:space:]]*#", text.lines)
}
text.lines[head.comment] = sprintf("invisible(\"%s%s%s\")",
begin.comment, text.lines[head.comment], end.comment)
blank.line = grepl("^[[:space:]]*$", text.lines)
if (any(blank.line) && keep.blank.line) {
else.line = grep("^[[:space:]]*else(\\W|)", text.lines)
for (i in else.line) {
j = i - 1
while (blank.line[j]) {
blank.line[j] = FALSE
j = j - 1
warning("removed blank line ", j, " (you should not put an 'else' in a separate line!)")
}
}
text.lines[blank.line] = sprintf("invisible(\"%s%s\")",
begin.comment, end.comment)
}
text.lines = mask_inline(text.lines)
}
#modified code
ic=grepl( "%InLiNe_IdEnTiFiEr%", text.lines)
text.lines[ic]=substr(text.lines[ic], 1, nchar(text.lines[ic])-1)
text.lines[ic]= paste0(text.lines[ic], "%InLiNe_IdEnTiFiEr_mod%\"")
#end modified code
text.mask = tidy_block(text.lines, width.cutoff, replace.assign)
text.tidy = if (keep.comment)
unmask.source(text.mask)
else text.mask
text.tidy = reindent_lines(text.tidy, reindent.spaces)
if (left.brace.newline)
text.tidy = move_leftbrace(text.tidy, reindent.spaces)
#modified code
text.tidy= unlist(sapply(text.tidy, strsplit, "%InLiNe_IdEnTiFiEr_mod%", USE.NAMES=FALSE))
#end modified code
if (output)
cat(paste(text.tidy, collapse = "\n"), "\n", ...)
invisible(list(text.tidy = text.tidy, text.mask = text.mask))
}
## ====================================================
## ============ Implementation ============
## Clean-up
if("formatR" %in% loadedNamespaces() ) detach('package:formatR', unload=TRUE)
if(exists("tidy.source"))rm(tidy.source)
library("formatR")
## String with inline comments after arguments
text.input="paste(1 # comm
,7)
"
## The same in vector format
text.input=strsplit(text.input, "\n")[[1]]
## Implementation without patch
tidy.source(text=text.input) #newline removed with wrong result!
# paste(1 # comm, 7)
# Tentative patch
unlockBinding("tidy.source", as.environment("package:formatR") )
assign("tidy.source", tidy.source.mod, pos="package:formatR")
environment(tidy.source)= asNamespace( "formatR" )
## Implementation with patch
tidy.source(text=text.input) # apparently ok:
# paste(1 # comm
# , 7)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.