#Worked examples for "caller" with interpreter-level stack traces. library(testthat) `%is%` <- expect_equal `%throws%` <- expect_error context("caller examples") #debug(caller) test_that("Example 1", { where <- "0" x <- y <- z <- NULL e <- function() { where <- "e" x <<- environment() f <- function() { where <- "f" y <<- environment() g <- function() { where <- "g" z <<- environment() caller()$where %is% "f" } g() } f() } e() }) # callflag evaldepth promargs callfun sysparent call cloenv # 1 0 0 NULL NULL NULL # 2 12 1 args(~/fexpr/Untitled.R, e.... function (file, local = FA.... source("~/fexpr/Untitled.R.... # 3 12 2 args( withVisible(eval(ei, envir)) # 4 12 5 args(ei := expression(test.... function (expr, envir = pa.... eval(ei, envir) # 5 12 6 args(expression(test_that(.... .Primitive("eval") eval(expr, envir, enclos) # 6 12 7 args(caller from eval and .... function (desc, code) ... test_that("caller from eva.... # 7 12 9 args(desc := caller from e.... function (description, cod.... test_code(desc, substitute.... # 8 12 11 args( tryCatch(withCallingHandle.... # 9 12 12 args( tryCatchList(expr, classes.... # 10 12 13 args( tryCatchOne(tryCatchList(e.... # 11 12 14 args( doTryCatch(return(expr), n.... # 12 12 17 args( tryCatchList(expr, names[-.... # 13 12 18 args( tryCatchOne(expr, names, p.... # 14 12 19 args( doTryCatch(return(expr), n.... # 15 12 25 args( withCallingHandlers(eval(c.... # 16 12 27 args(code := {..., new_tes.... function (expr, envir = pa.... eval(code, new_test_enviro.... # 17 12 28 args({... := {..., eval(expr, envir, enclos) # 18 12 30 NULL function () ... e() # 19 12 32 NULL function () ... f() # 20 12 34 NULL function () ... g() * the goal! Why: is sysparent of the subject frame # 21 12 36 args( caller()$where %is% "f" | An ordinary call. # 22 12 38 args( expect_that(object, equals.... | An ordinary call. # 23 12 41 args( condition(object) | * # 24 12 44 args( compare(actual, expected, ...) | ^ an ordinary call (to an S3 generic) # 25 12 52 NULL function (envir = caller(e.... caller() *-^ * * the target frame. Why? Is only activation record with this cloenv. Is also a promise eval # 26 12 53 args( which(vapply(st$cloenv, id.... | | ^ an ordinary call. Why? Is # 27 12 55 args(st$cloenv := vapply(st$cloenv, identica.... | *-^ a delayed promise eval. Why? sysparent is not prev frame's cloenv. # 28 12 56 args(X[[i]] := FUN(X[[i]], ...) | ^ A normal call. Sysparent is prev frame's cloenv. # 29 12 60 args(environment() := caller(environment()) *-^ The call to "caller" in question. # | Also a delayed promise, because sysparent is not last cloenv. envir is given as 0x10d1d6920. # 30 12 61 NULL function () ... stacktrace::stacktrace() ^ # # Proposed algo: March up sysperent to find highest activation record that has the necessary sysparent. Then return that record's sysparent. # debug(caller) test_that("foo", { where <- "0" x <- y <- z <- NULL e <- function() { where <- "e" x <<- environment() f <- function() { where <- "f" y <<- environment() g <- function() { where <- "g" z <<- environment() } g() } f() } e() h <- function() { eval(quote(caller()), y)$where %throws% "e" } h() }) # worked example: # # callflag evaldepth promargs callfun sysparent call cloenv # 1 0 0 NULL NULL NULL # 2 12 1 args(~/fexpr/inst/tests/te.... function (file, local = FA.... source("~/fexpr/inst/tests.... # 3 12 2 args( withVisible(eval(ei, envir)) # 4 12 5 args(ei := expression(loca.... function (expr, envir = pa.... eval(ei, envir) # 5 12 6 args(expression(local({....... .Primitive("eval") eval(expr, envir, enclos) # 6 12 7 args( local({... # 7 12 8 args(substitute(eval(quote.... function (expr, n = 1) ... eval.parent(substitute(eva.... # 8 12 9 args(expr := eval(quote({..... function (expr, envir = pa.... eval(expr, p) # 9 12 10 args(eval(quote({... := ev.... .Primitive("eval") eval(expr, envir, enclos) # 10 12 11 args(quote({... := {..., n.... function (expr, envir = pa.... eval(quote({... # 11 12 12 args({... := {..., eval(expr, envir, enclos) # 12 12 14 NULL function () ... h() # 13 12 16 args( eval(quote(caller()), y)$w.... # 14 12 18 args( expect_that(object, throws.... # 15 12 21 args( condition(object) # 16 12 24 args( try(force(expr), TRUE) # 17 12 25 args( tryCatch(expr, error = fun.... # 18 12 26 args( tryCatchList(expr, classes.... # 19 12 27 args( tryCatchOne(expr, names, p.... # 20 12 28 args( doTryCatch(return(expr), n.... # 21 12 34 args( force(expr) # 22 12 40 args(quote(caller()) := ca.... function (expr, envir = pa.... eval(quote(caller()), y) # 23 12 41 args(caller() := caller(),.... .Primitive("eval") eval(expr, envir, enclos) # the only frame with the target cloenv is a primitive call to eval. # 24 12 42 NULL function (envir = caller(e.... caller() # 25 12 3 NULL function () ... stacktrace::stacktrace() # envir = # st$promargs[[22]] = args(quote(caller()) := caller(), y := ) # Learning: If the call's callfun is a primitive, it is probably not a "real" call, at least its cloenv is not to be trusted. # Example 3 test_that("caller from a lazy argument in a closed environment", { where <- "0" e <- function() { where <- "e" f <- function() { where <- "f" g <- function(g) { where <- "g" function(f) g } g(caller()) } f() } e()()$where %throws% "e" }) # callflag evaldepth promargs callfun sysparent call cloenv # 1 0 0 NULL NULL NULL # 2 12 1 args(~/fexpr/inst/tests/te.... function (file, local = FA.... source("~/fexpr/inst/tests.... # 3 12 2 args( withVisible(eval(ei, envir)) # 4 12 5 args(ei := expression(test.... function (expr, envir = pa.... eval(ei, envir) # 5 12 6 args(expression(test_that(.... .Primitive("eval") eval(expr, envir, enclos) # 6 12 7 args(caller from a lazy ar.... function (desc, code) ... test_that("caller from a l.... # 7 12 9 args(desc := caller from a.... function (description, cod.... test_code(desc, substitute.... # 8 12 11 args( tryCatch(withCallingHandle.... # 9 12 12 args( tryCatchList(expr, classes.... # 10 12 13 args( tryCatchOne(tryCatchList(e.... # 11 12 14 args( doTryCatch(return(expr), n.... # 12 12 17 args( tryCatchList(expr, names[-.... # 13 12 18 args( tryCatchOne(expr, names, p.... # 14 12 19 args( doTryCatch(return(expr), n.... # 15 12 25 args( withCallingHandlers(eval(c.... # 16 12 27 args(code := {..., new_tes.... function (expr, envir = pa.... eval(code, new_test_enviro.... # 17 12 28 args({... := {..., eval(expr, envir, enclos) # 18 12 30 args( e()() %is*% "e" # 19 12 32 args( expect_that(object, throws.... # 20 12 35 args( condition(object) # 21 12 38 args( try(force(expr), TRUE) # 22 12 39 args( tryCatch(expr, error = fun.... # 23 12 40 args( tryCatchList(expr, classes.... # 24 12 41 args( tryCatchOne(expr, names, p.... # 25 12 42 args( doTryCatch(return(expr), n.... # 26 12 48 args( force(expr) # 27 12 53 NULL function (f) ... e()() # the function called; is "g" # 28 12 55 NULL function (envir = caller(e.... caller() # target env for some reason # 29 12 58 NULL function () ... stacktrace() # envir: # Frames: parent call # [[1]] 0 source("~/fexpr/inst/tests.... # [[2]] 1 withVisible(eval(ei, envir)) # [[3]] 1 eval(ei, envir) # [[4]] 3 eval(expr, envir, enclos) # [[5]] 0 test_that("caller from a l.... # [[6]] 5 test_code(desc, substitute.... # [[7]] 6 tryCatch(withCallingHandle.... # [[8]] 7 tryCatchList(expr, classes.... # [[9]] 8 tryCatchOne(tryCatchList(e.... # [[10]] 9 doTryCatch(return(expr), n.... # [[11]] 8 tryCatchList(expr, names[-.... # [[12]] 11 tryCatchOne(expr, names, p.... # [[13]] 12 doTryCatch(return(expr), n.... # [[14]] 6 withCallingHandlers(eval(c.... # [[15]] 6 eval(code, new_test_enviro.... # [[16]] 15 eval(expr, envir, enclos) # [[17]] 16 e()() %is*% "e" # [[18]] 17 expect_that(object, throws.... # [[19]] 18 condition(object) # [[20]] 19 try(force(expr), TRUE) # [[21]] 20 tryCatch(expr, error = fun.... # [[22]] 21 tryCatchList(expr, classes.... # [[23]] 22 tryCatchOne(expr, names, p.... # [[24]] 23 doTryCatch(return(expr), n.... # [[25]] 19 force(expr) # [[26]] 16 e()() # [[27]] 27 caller() *target env* note it is its own sysparent! # where=27 # Possible lesson: if we are our own sysparent, reject.