2012-04-08 4 views
4

У меня есть специальная (фиктивная) функция, которую я хочу использовать в изолированном среде:Scoping (функция) в пользовательской среде

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 

Я попытался пойти немного дальше и назначен, что довольно простая функция (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 в пользовательской среде.

Любые подсказки и идеи будут действительно приветствоваться!

ответ

3

Если вы не хотите, чтобы люди могли использовать system, было бы проще переписать определение.

assignInNamespace(
    "system", 
    function(...) stop("system calls are not allowed"), 
    getNamespace("base") 
) 

system("pwd") #throws an error 

Я дико угадывание в вашем случае использования, но вы позволяя пользователям передать произвольный код R в другое приложение? В этом случае вы, вероятно, захотите скомпилировать свою собственную версию R, с опасными функциями, удаленными или замененными манекенами.


Еще одна возможность для выполнения произвольного кода при вызове функции является trace. Например,

trace(system, quote(stop("You have called system"))) #you may also want print = FALSE 
+0

Спасибо, этот ответ слишком удобно (+1)! На самом деле я не хочу запрещать все «системные» вызовы, это был просто простой пример POC, но я действительно хочу реализовать среду песочницы. Pls см .: http://sandboxr.no-ip.org/ В любом случае, 'assignInNamespace' кажется многообещающим, мне нужно некоторое время, чтобы сделать некоторые эксперименты после Пасхи и, безусловно, дать более подробную обратную связь. – daroczig

+0

Хороший проект. Вы видели Live-R, что делает подобное? http://live-analytics.com –

+0

Никогда не слышал о Live-R, спасибо, что обратил мое внимание на эту аккуратную часть программного обеспечения! Я на самом деле что-то подобное, но со специальной (ожидаемой) аудиторией. Мы увидим :) Во всяком случае, о вашем ответе: поскольку '? AssignInNamespace' предлагает не использовать эту функцию в пакете (например, я бы хотел) и не разрешает включать/отключать среду песочницы, кажется, что у меня есть найти другой способ деактивации этих вызовов. Как и источники grep'ing R для подобных функций :) Что я хочу сделать, так это запускать вызовы в специальной среде, за исключением того, что разрешено что-либо (для вызовов на стороне системы). – daroczig

4

Хотя вы подозревали, что это было не так, 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 

Edit: FWIW, причина того, что ваш пользовательский model.frame()был, вызванный lm() в первом примере, так это то, что lm() использует то, что иногда называют «нестандартной оценкой». (See this pdf для более подробной информации о предмете, чем вы, вероятно, захотите.) Ключевым битом является то, что lm() фактически направляет model.frame() для оценки в вызывающей среде; в вашем случае это привело к тому, что он нашел вашу версию функции.

Причина, по которой lm() использует нестандартную оценку так, что model.frame() может получить доступ к переменным, названные в формуле, даже если они находятся в вызывающей среде (а не просто быть в состоянии получить доступ к переменным передаются в через data аргумент lm()). Как говорит Томас Ламли в связанном pdf:

Если переменные в формуле должны были быть в аргументе данных, жизнь была бы намного проще, но это требование не было сделано, когда были введены формулы.

В случае, если вы заинтересованы, вот соответствующие строки из определения lm:

mf <- match.call(expand.dots = FALSE) 
... 
mf[[1L]] <- as.name("model.frame") 
mf <- eval(mf, parent.frame()) 
+0

Спасибо, ваш ответ был действительно полезен (+1) - несмотря на то, что я просмотрел соответствующие источники R (и, конечно же, я понял, что 'model.frame.default' вызывается как нет других методов - если Я прав), я как-то передал вызов 'eval' в источниках' lm'. Это делает все понятным! Мне просто нужно найти простой способ заставить функции использовать мою специальную 'as.formula' (например, @ RichieCotton's anwer) или grep R-источники для всего появления' as.formula' и т. Д. Или около того. Ах, и спасибо за это _pdf_ тоже! – daroczig

Смежные вопросы