отладить все функции

Предположим, что мы вызвали debug() для нескольких функций, чтобы создать для них точку останова. Когда мы найдем и undebug() ошибку, есть ли undebug() все функции, отмеченные debug() одной командой?

Вот хороший тест, чтобы увидеть, действительно ли ваш предложенный метод работает идеально:

> library(limma) # bioconductor
> debug(read.ilmn)
> read.ilmn("a.txt") # No problem if this file does not exist
Browse[2]> debug(.read.oneilmnfile) # This is the debug browser for read.ilmn()
Browse[2]> Q # To exit debug browser
> undebug.all() # Here run your proposed function to undebug everything!
> read.ilmn("a.txt")
# Now if the debug browser is not started, you are lucky to pass this test!

Вы можете увидеть принятый ответ ниже. Любой случай, для которого этот ответ не работает, или более чистые версии, более чем приветствуются.

Ответ 1

Это было мое решение...

изменить: пересмотрены для поиска объектов в пространствах имен. Код уже становится немного крутым, так как я не очень хорошо разбираюсь в методах манипулирования/запроса пространств имен, и так как я работал с пробной версией и ошибкой. Более чистые версии приветствуются. Есть почти наверняка другие угловые случаи, которые потерпят неудачу.

## return the names of the objects (from a vector of list of
## names of objects) that are functions and have debug flag set
isdebugged_safe <- function(x,ns=NULL)  {
    g <- if (is.null(ns)) get(x) else getFromNamespace(x,ns)
    is.function(g) && isdebugged(g)
}

which_debugged <- function(objnames,ns=NULL) {
    if (!length(objnames)) return(character(0))
    objnames[sapply(objnames,isdebugged_safe,ns=ns)]
}

all_debugged <- function(where=search(), show_empty=FALSE) {
    ss <- setNames(lapply(where,function(x) {
        which_debugged(ls(x,all.names=TRUE))
        }),gsub("package:","",where))
    ## find attached namespaces
    ## (is there a better way to test whether a 
    ##    namespace exists with a given name??)
    ns <- unlist(sapply(gsub("package:","",where),
                 function(x) {
                     if (inherits({n <- try(getNamespace(x),silent=TRUE)},
                         "try-error")) NULL else x
                 }))
    ss_ns <- setNames(lapply(ns,function(x) {
        objects <- ls(getNamespace(x),all.names=TRUE)
        which_debugged(objects,ns=x)
        }),ns)
    if (!show_empty) {
        ss <- ss[sapply(ss,length)>0]
        ss_ns <- ss_ns[sapply(ss_ns,length)>0]
    }
    ## drop overlaps
    for (i in names(ss))
        ss_ns[[i]] <- setdiff(ss_ns[[i]],ss[[i]])
    list(env=ss,ns=ss_ns)
}

undebug_all <- function(where=search()) {
    aa <- all_debugged(where)
    lapply(aa$env,undebug)
    ## now debug namespaces
    invisible(mapply(function(ns,fun) {
        undebug(getFromNamespace(fun,ns))
    },names(aa$ns),aa$ns))
}

Код также размещен на http://www.math.mcmaster.ca/bolker/R/misc/undebug_all.R

Пример:

library(nlme)
debug(lme)
## define functions
source(url("http://www.math.mcmaster.ca/bolker/R/misc/undebug_all.R"))
undebug_all()
fm1 <- lme(distance ~ age, data = Orthodont) # from ?lme

В этом случае lme работает без ввода отладчика.

Другой, более жесткий пример:

library(limma)
source(url("http://www.math.mcmaster.ca/bolker/R/misc/undebug_all.R"))
debug(read.ilmn)
debug(limma:::.read.oneilmnfile)
all_debugged()
undebug_all()
read.ilmn()
read.ilmn("a.txt")

Обратите внимание, что read.ilmn() и read.ilmn("a.txt") выглядят по-разному от точки отладки (я не понимаю, почему...)

Ответ 2

Нет, нет надежного способа undebug() всех функций. (Я только говорю это, потому что я видел, как он несколько раз обсуждал R-devel и R-help.)

В этом обсуждении Брайан Рипли взвесил, отметив, что:

Отладка - это свойство объекта функции (бит в sxpinfo), поэтому вам нужно будет перемещать все доступные объекты (как gc), чтобы найти их все.

Здесь фрагмент, в котором отвечает Роберт Джентльмен (отрицательно) вопрос о том, "есть ли удобный способ узнать в любое время, которые являются функцией, помеченной debug() или trace() в сеансе R":

Вероятно, вы не получили ответа, потому что ответ отрицательный, есть нет простого способа.

Ответ 3

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

Сначала проиллюстрируйте несколько функций в глобальной среде:

> bar <- function() {}
> foo <- function() {}

Используйте lsf.str() для возврата функций в рабочую область (для использования позже мы unclass() это и преобразуем его в список):

> funlist <- as.list(unclass(lsf.str()))
> funlist
[[1]]
[1] "bar"

[[2]]
[1] "foo"

Затем создайте индикатор для этих функций относительно того, отлаживаются ли они:

> debugged <- sapply(funlist, isdebugged)
> debugged
[1] FALSE FALSE

ОК, поэтому debug() одна из функций и повторное выполнение:

> debug(bar)
> 
> debugged <- sapply(funlist, isdebugged)
> debugged
[1]  TRUE FALSE

Наконец sapply() над funlist функциями, которые отлаживаются с применением undebug() к ним:

> sapply(funlist[debugged], undebug)
[[1]]
NULL

Это, конечно, может быть инкапсулировано в функцию

undebugFuns <- function() {
    funs <- unclass(lsf.str())
    dbg <- sapply(funs, isdebugged)
    if(isTRUE(any(dbg))) {
        writeLines(paste("Un-debugging:", funs[dbg]))
        sapply(funs[dbg], undebug)
    } else {
        writeLines(paste("Nothing to debug"))
    }
    invisible()
}

> debug(bar)
> undebugFuns()
Un-debugging: bar

Один тип отладки, который не был выбран isdebugged(), - это то, что введено через debugonce():

> debug(bar)
> isdebugged(bar)
[1] TRUE
> undebugFuns()
Un-debugging: bar
> debugonce(bar)
> isdebugged(bar)
[1] FALSE

Что только начинается, чтобы снова включить Джоша в свой ответ.