[英]Scoping (functions) in custom environment
我有一个特殊的(虚拟的)function,我想在沙盒环境中使用它:
disable.system.call <- function(...) {
mc <- match.call()
if (grepl('system', deparse(mc[[2]])))
stop('NONO')
eval(mc, env = .GlobalEnv)
}
它没有做任何特别的事情,只是检查第一个参数的名称中是否包含system
字。 这只是一个 POC 示例。
我稍后做什么:我将这个简单的函数分配给一些base
函数和stats
函数,以查看计算的表达式是否确实包含system
字作为第一个参数。 例如:
e <- new.env()
eval(parse(text = 'model.frame <- disable.system.call'), envir = e)
这工作起来非常酷,因为没有system
内部的调用就像一个魅力,但过滤器工作:
> eval(parse(text = 'model.frame("1 ~ 1")'), envir = e)
1
1 1
> eval(parse(text = 'model.frame(\'1 ~ system("ls -la")\')'), envir = e)
Error in model.frame("1 ~ system(\"ls -la\")") : NONO
它甚至使用lm
调用,调用model.frame
在内部找到一个像字符串这样的公式:
> eval(parse(text = 'lm(\'1 ~ system("ls -la")\')'), envir = e)
Error in model.frame(formula = "1 ~ system(\"ls -la\")", drop.unused.levels = TRUE) :
NONO
我进一步尝试 go 并将非常简单的 function ( disable.system.call
) 分配给从as.formula
调用的model.frame
。 不幸的是我没有得到到目前为止:
> e <- new.env()
> eval(parse(text = 'as.formula <- disable.system.call'), envir = e)
> eval(parse(text = 'as.formula("1 ~ 1")'), envir = e)
1 ~ 1
> eval(parse(text = 'as.formula(\'1 ~ system("ls -la")\')'), envir = e)
Error in as.formula("1 ~ system(\"ls -la\")") : NONO
> eval(parse(text = 'model.frame(\'1 ~ system("ls -la")\')'), envir = e)
1 system("ls -la")
1 1 0
> eval(parse(text = 'lm(\'1 ~ system("ls -la")\')'), envir = e)
Call:
lm(formula = "1 ~ system(\"ls -la\")")
Coefficients:
(Intercept) system("ls -la")
1 NA
据我所知model.frame
正在调用as.formula
但这不起作用(正如您从上面的输出中看到的那样)。 我很确定这不是因为model.frame
在自定义环境中调用stats::as.formula
就像lm
在model.frame
上面调用的那样。
任何提示和想法都会受到欢迎!
尽管您怀疑情况并非如此,但正在调用stats:::model.frame.default
,而不是环境e
中的自定义版本。 (这当然是您通常期望打包函数的行为。在您的第一个示例中看到的奇怪范围是一种特殊情况,因为lm()
使用了“非标准评估”,这在我的答案的底部)。
如下所示,您可以使用trace()
查看在每种情况下调用的as.formula()
版本:
disable.system.call <- function(...) {
mc <- match.call()
if (grepl('system', deparse(mc[[2]])))
stop('NONO')
eval(mc, env = .GlobalEnv)
}
e <- new.env()
eval(parse(text = 'as.formula <- disable.system.call'), envir = e)
# (1) trace custom 'as.formula()' in environment e
trace(e$as.formula)
# Calling model.frame() **does not** call the the custom as.formula()
eval(parse(text = 'model.frame(\'1 ~ system("ls -la")\')'), envir = e)
# 1 system("ls -la")
# 1 1 127
# (2) trace stats:::as.formula()
trace(stats:::as.formula)
# Calling model.frame() **does** call stats:::as.formula()
eval(parse(text = 'model.frame(\'1 ~ system("ls -la")\')'), envir = e)
# trace: as.formula
# 1 system("ls -la")
# 1 1 127
编辑:FWIW,在第一个示例中 lm lm()
) 调用您的自定义model.frame()
的原因是lm()
采用有时称为“非标准评估”的方法。 (有关该主题的更多信息, 请参阅此 pdf ,这比您可能想要的要多。)关键是lm()
实际上指示model.frame()
在调用环境中进行评估; 在您的情况下,这导致它找到您的 function 版本。
lm()
使用非标准评估的原因是model.frame()
可以访问公式中命名的变量,即使它们在调用环境中找到(而不是仅仅能够访问通过data
参数传入的变量到lm()
)。 正如 Thomas Lumley 在链接的 pdf 中所说:
如果要求公式中的变量在数据参数中,生活会简单得多,但在引入公式时并没有提出这一要求。
如果您有兴趣,这里是lm
定义中的相关行:
mf <- match.call(expand.dots = FALSE)
...
mf[[1L]] <- as.name("model.frame")
mf <- eval(mf, parent.frame())
如果您不希望人们能够使用system
,那么覆盖定义会更容易。
assignInNamespace(
"system",
function(...) stop("system calls are not allowed"),
getNamespace("base")
)
system("pwd") #throws an error
我在疯狂猜测您的用例,但您是否允许用户将任意 R 代码传递给其他应用程序? 在这种情况下,您可能想要编译您自己的 R 版本,删除危险函数或用假人替换。
调用 function 时执行自定义代码的另一种可能性是trace
。 例如,
trace(system, quote(stop("You have called system"))) #you may also want print = FALSE
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.