как прикрепить пакет, включающий неэкспортированные функции?

#r #debugging #import #r-package

#r #отладка #импорт #r-package

Вопрос:

При отладке функций из внешнего пакета я часто обнаруживаю, что копирую функцию в новый сценарий, чтобы добавить свои исправления, но мне нужно добавить кучу foo=extpack:::foo в начале сценария, чтобы функция могла получить доступ к внутренней функции пакета.

Клонирование и сборка пакета были бы полным перебором в тех ситуациях, когда часто изменяется одна строка.

Есть ли способ прикрепить пакет со всеми его внутренними функциями?

Что-то вроде library(extpack, attach_nonexported=TRUE)

Ответ №1:

Вы можете получить все функции из пространства имен пакета в качестве среды, выполнив, например:

 getNamespace("ggplot2")
  

Таким образом, вы можете прикрепить их к своему пути поиска (аналогично вызову library с неэкспортированными функциями), выполнив:

 attach(getNamespace("ggplot2"))
  

Если вы предпочитаете их в списке, вы можете сделать

 as.list(getNamespace("ggplot2"))
  

Или, если вы хотите, чтобы они отображались в глобальной рабочей области, вы можете сделать:

 list2env(as.list(getNamespace("ggplot2")), globalenv())
  

Излишне говорить, что вы должны делать такие вещи только с интерактивными сеансами, а не при написании пакета.

Ответ №2:

если вы действительно хотите имитировать вызов библиотеки, мы можем создать «package:yourpkg» в пути поиска, содержащий все функции :

 magrittr::as_pipe_fn
#> Error: 'as_pipe_fn' n'est pas un objet exporté depuis 'namespace:magrittr'
magrittr:::as_pipe_fn
#> function (expr, env) 
#> {
#>     eval(call("function", lambda_fmls, expr), env)
#> }
#> <bytecode: 0x0000000013917228>
#> <environment: namespace:magrittr>

attach_all <- function(pkg) {
  pkg <- as.character(substitute(pkg))
  pkg_long <- paste0("package:", pkg)
  eval(bquote(with(
    setNames(list(getNamespace(pkg)), pkg_long), 
    attach(.(as.symbol(pkg_long)))
  )))
}

attach_all(magrittr)

search()
#>  [1] ".GlobalEnv"        "package:magrittr"  "package:stats"    
#>  [4] "package:graphics"  "package:grDevices" "package:utils"    
#>  [7] "package:datasets"  "package:methods"   "Autoloads"        
#> [10] "tools:callr"       "package:base"
as_pipe_fn
#> function (expr, env) 
#> {
#>     eval(call("function", lambda_fmls, expr), env)
#> }
#> <bytecode: 0x0000000013917228>
#> <environment: namespace:magrittr>
  

Создан 2020-10-09 пакетом reprex (версия 0.3.0)


Это может заставить вас нервничать при использовании package:mypkg , не стесняйтесь изменять эту часть, если это так, результат будет тем же.

Другим способом было бы прикрепить неэкспортированные функции к другой среде, чтобы вы могли при необходимости отсоединить его независимо :

 attach_unexported <- function(pkg) {
  pkg <- as.character(substitute(pkg))
  all_funs <- lsf.str(asNamespace(pkg))
  exported <- getNamespaceExports(pkg)
  unexported_funs <- setdiff(all_funs, exported)
  pkg_long <- paste0(pkg, "_unexported")
  eval(bquote(with(
    setNames(list(mget(unexported_funs, asNamespace(pkg))), pkg_long), 
    attach(.(as.symbol(pkg_long)))
  )))
}

attach_unexported(magrittr)

search()
#>  [1] ".GlobalEnv"          "magrittr_unexported" "package:stats"      
#>  [4] "package:graphics"    "package:grDevices"   "package:utils"      
#>  [7] "package:datasets"    "package:methods"     "Autoloads"          
#> [10] "tools:callr"         "package:base"
as_pipe_fn
#> function (expr, env) 
#> {
#>     eval(call("function", lambda_fmls, expr), env)
#> }
#> <bytecode: 0x0000000013a05bd0>
#> <environment: namespace:magrittr>

`%>%`
#> Error in eval(expr, envir, enclos): objet '%>%' introuvable
  

Создан 2020-10-09 пакетом reprex (версия 0.3.0)

Комментарии:

1. Самое интересное и ценное излишество за всю историю!

Ответ №3:

Лучший способ исправить подобные ошибки — установить среду вашей измененной функции в ту же среду, что и исходная. Например, чтобы исправить extpack::badfn использование

 badfn <- function(...) { ... } # new version in your global workspace

environment(badfn) <- environment(extpack::badfn)
  

Это означает, что он будет видеть частные функции при выполнении вызовов, но не будет заменять extpack::badfn исходное местоположение, поэтому другие extpack вызывающие его функции все равно будут вызывать исходную. Если вы хотите, чтобы они вызывали ваши вместо этого, используйте

 assignInNamespace("badfn", badfn, "extpack")
  

после внесения вышеуказанных изменений.

Если какой-либо другой пакет импортирует extpack::badfn , то то, что он получит, будет зависеть от точного порядка операций, поэтому в подобном случае вам лучше всего стиснуть зубы и перестроить весь пакет с вашими исправлениями на месте.

Комментарии:

1. Ответ Аллана отвечает на вопрос, который я задал, ваш ответ отвечает на вопрос, который я должен был задать. Я думаю, вы выиграли