Capturing user function calls with unknown number of callers to go back

Is there a rlang equivalent to caller_env(sys.parent) to capture user function calls independently to how deeply nested the erroring function is?

caller_env() is great when working with functions that are closely related in the function tree but not so convenient when working with more distant functions because it's not always obvious to know how many callers one has to go back to get the right environment and can vary a lot, in particular when using R6 classes.

Hello, perhaps you could firm up your request for help with a reprex of some sort ?
My first thought is that perhaps you want rlang::trace_back()

bar <- function(x) {
  foo(x)
  paste("input:", x)
}

foo <- function(x, arg = caller_arg(x), call = caller_env()) {
  if (is.character(x)) {
    return(invisible(NULL))
  }
  msg <- paste0("`", arg, "` must be a character vector.")
  abort(msg, call = call)
}

If I execute bar(), I get a proper error message with the right function call:

bar(1)
#> Error in `bar()`:
#> ! `x` must be a character vector.
#> Run `rlang::last_error()` to see where the error occurred.

Now if I add more nesting, I won't get a proper error message (which points to bar() instead of blah()) unless I add a call parameter to bar/blah, call foo() in blah() or change the number of callers to go back.

blah <- function(x) {
  bar(x)
}

blah(1)
#> Error in `bar()`:
#> ! `x` must be a character vector.
#> Run `rlang::last_error()` to see where the error occurred.

I can propagate the user function environment through the function tree by using caller_env(sys.parent) in foo(). I'm just wondering if there's a rlang function for that already.

Theres probably a proper way to do this; but I've stumbled through and found an arrangement that at least seems to work;


library(rlang)

bar <- function(x) {
  foo(x)
  paste("input:", x)
}

foo <- function(x, arg = caller_arg(x)) {
  if (is.character(x)) {
    return(invisible(NULL))
  }
  msg <- paste0("`", arg, "` must be a character vector.")
  abort(msg,call=caller_call(trace_length(trace_back()) - 1))
}
bar(1)
# Error in `bar()`:
# ! `x` must be a character vector.

blah <- function(x) {
  bar(x)
}

blah(1)
# Error in `blah()`:
# ! `x` must be a character vector.

Your solution seems to work but is more complicated and obscure than caller_env(sys.parent()). I was expecting a rlang function that does the trick directly but maybe that function doesn't exist, which explains why I didn't find it…

ha; sys.parent didnt appear within your reprex; so I didnt even look at it.
That said, how are you trying to improve on that ? it seems fine.

I'm not trying to improve it. I'm only asking if there's a rlang function that already does it.

This topic was automatically closed after 45 days. New replies are no longer allowed.


If you have a query related to it or one of the replies, start a new topic and refer back with a link.