Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get line number of a function call in R?

Tags:

r

For debug purposes, I want to print a line number (and function name) of the place the current function was called from. How do I get this in R?

I've seen a solution of getting the source file name But how to get the line number and function name?]

EDIT: I found how to get this data from traceback() in some form, traceback is able to print it out, but I am not sure how to decode the information out of it:

f <- function () {
    traceback(x = 3, max.lines = 1)
}

g <- function()
{
    f()
}

x <- g()

source("file.R") # file with this code
# 5: g() at file.R#20
# 4: eval(ei, envir)
# 3: eval(ei, envir)
# 2: withVisible(eval(ei, envir))
# 1: source("file.R")

str(x[[1]])
# chr "g()"
# - attr(*, "srcref")= 'srcref' int [1:8] 20 1 20 8 1 8 20 20
#  ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment:  0x0000000013a31700> 
like image 976
Tomas Avatar asked Dec 30 '19 22:12

Tomas


1 Answers

Found a solution! Got it from the code of traceback():

f <- function ()
{
    x <- .traceback(x = 1)

    srcloc <- if (!is.null(srcref <- attr(x[[1]], "srcref"))) {
        srcfile <- attr(srcref, "srcfile")
        paste0("Called from ", x[[2]], ", at ", basename(srcfile$filename), "#", srcref[1])
    }

    cat(srcloc, "\n")
}

g <- function()
{
    f()
}

g()
# Called from g(), at file.R#15

Wrote a nice wrapper function for it:

# returns a list, unless fmtstring is specified
# level: 1 - caller of the caller of this function; 2 - its parent, 3 - its grand-parent etc.
# fmtstring: return format string: %f (function), %s (source file), %l (line)
# 
# example: str <- caller_info("Called from %f at %s#%l\n")
# !!! it won't work with e.g. cat(caller_info("Called from %f at %s#%l\n"))
# or cat(paste0(caller_info("Called from %f at %s#%l\n"))) !!!
caller_info <- function (fmtstring = NULL, level = 1) # https://stackoverflow.com/q/59537482/684229
{
    x <- .traceback(x = level + 1)

    i <- 1
    repeat { # loop for subexpressions case; find the first one with source reference
        srcref <- getSrcref(x[[i]])
        if (is.null(srcref)) {
            if (i < length(x)) {
                i <- i + 1
                next;
            } else {
                warning("caller_info(): not found\n")
                return (NULL)
            }
        }
        srcloc <- list(fun = getSrcref(x[[i+1]]), file = getSrcFilename(x[[i]]), line = getSrcLocation(x[[i]]))
        break;
    }

    if (is.null(fmtstring))
        return (srcloc)

    fmtstring <- sub("%f", paste0(srcloc$fun, collapse = ""), fmtstring)
    fmtstring <- sub("%s", srcloc$file, fmtstring)
    fmtstring <- sub("%l", srcloc$line, fmtstring)
    fmtstring
}

This is how it's used:

f <- function ()
{
    str <- caller_info("Called from %f at %s#%l\n")
    cat(str)
}

The only (minor) limitation is that when called in subexpressions like cat(caller_info("Called from %f at %s#%l\n")) or cat(paste0(caller_info("Called from %f at %s#%l\n"))), R confusingly counts these subexpression things as stack levels, which messes it up. So better avoid the use of this wrapper in expressions.

like image 122
Tomas Avatar answered Sep 22 '22 20:09

Tomas