如何拥有固定的“锚定”到主要功能环境?

发布于 2025-02-12 23:33:33 字数 1090 浏览 1 评论 0原文

在包装中,我有一个主函数,该功能称为许多其他未出现的功能,到处都有很多条件检查。

使用tidyverse错误处理时,例如,使用Awesome {cli} 软件包,您可以附加一个呼叫者环境,该环境将用于装饰该功能。该文档可用在这里

这是一些示例代码:

main = function() f1()
f1 = function() f2()
f2 = function() cli::cli_abort("foobar", call=rlang::caller_env())
main()
#> Error in `f1()`:
#> ! foobar

在2022-07-03创建的 reprex package (v2.0.1.1) )

呼叫者不是f2()而是其父f1(),但是错误是仍然没有信息,因为最终用户也不知道内部f1()函数。

要拥有main()作为结束呼叫者,我可以编写call = rlang :: Caller_env(2),它不是很可维护的,或者将呼叫者传递给呼叫者在堆叠的每个功能中的参数,这需要重构重构。

有没有办法拥有一个全局变量,该变量可以作为main()的引用,并且可以在代码中的任何地方使用?

In a package, I have a main function that calls a lot of other, unexported functions, with a lot of condition checks everywhere.

When using tidyverse error handling, for instance using the awesome {cli} package, you can attach a caller environment that will be used to decorate the function. The documentation is available here.

Here is some example code:

main = function() f1()
f1 = function() f2()
f2 = function() cli::cli_abort("foobar", call=rlang::caller_env())
main()
#> Error in `f1()`:
#> ! foobar

Created on 2022-07-03 by the reprex package (v2.0.1)

The caller is not f2() but its parent f1(), but the error is still not informative as the end-user is not aware of the internal f1() function either.

To have main() as the end caller, I can either write call=rlang::caller_env(2), which is not very maintainable, or pass the caller as an argument in each and every function that stack, which would require a heavy refactor.

Is there a way to have a global variable that would act as a reference to main(), and which I could use anywhere in my code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

似狗非友 2025-02-19 23:33:33

我只能想到的(hacky)选项:

1:一次继续上升1个父环境,直到您达到r_globalenv,然后返回1步。

main = function() f1()
f1 = function() f2()

f2 = function() {
  for(depth in 1:10) if(identical(rlang::caller_env(depth), globalenv())) break
  if(depth == 10) depth = 1
  cli::cli_abort("foobar", call=rlang::caller_env(depth-1))
}
main()

2:自己创建某种标识符,您可以搜索这些标识符

main = function() {
  marker = "hello!"
  f1()
}
f1 = function() f2()
f2 = function() {
  for(depth in 1:10) try( if( get('marker', envir = rlang::caller_env(depth)) == "hello!" ) break, silent = T)
  if(depth == 10) depth = 0
  cli::cli_abort("foobar", call=rlang::caller_env(depth))
}
main()

Only (hacky) options I can think of:

1: Keep going up 1 parent environment at a time, until you've reached R_GlobalEnv, then go back 1 step.

main = function() f1()
f1 = function() f2()

f2 = function() {
  for(depth in 1:10) if(identical(rlang::caller_env(depth), globalenv())) break
  if(depth == 10) depth = 1
  cli::cli_abort("foobar", call=rlang::caller_env(depth-1))
}
main()

2: Create some sort of identifier yourself, which you can search for

main = function() {
  marker = "hello!"
  f1()
}
f1 = function() f2()
f2 = function() {
  for(depth in 1:10) try( if( get('marker', envir = rlang::caller_env(depth)) == "hello!" ) break, silent = T)
  if(depth == 10) depth = 0
  cli::cli_abort("foobar", call=rlang::caller_env(depth))
}
main()
森末i 2025-02-19 23:33:33

我终于设法完成了。

我在软件包中声明了一个全局环境,但还不够,因为my_caller变量再次造成了很多错误。

在主函数的开头,我将当前环境保存在全球环境中,作为变量,然后可以在以下子女功能中呼叫任何地方。

这是代码:

my_caller = rlang::env()

f = function(x, y){
  my_caller$env = rlang::current_env()
  g()
}
g = function(){
  h()
}
h = function(){
  rlang::abort("foobar", call=my_caller$env)
}

f(x=1, y=5)
#> Error in `f()`:
#>   ! foobar
#> Run `rlang::last_error()` to see where the error occurred.
rlang::last_error()
#> <error/rlang_error>
#>   Error in `f()`:
#>   ! foobar
#> ---
#>   Backtrace:
#>   1. global f(x = 1, y = 5)
#> Run `rlang::last_trace()` to see the full context.
rlang::last_trace()
#> <error/rlang_error>
#>   Error in `f()`:
#>   ! foobar
#> ---
#>   Backtrace:
#>   ▆
#> 1. └─global f(x = 1, y = 5)
#> 2.   └─global g()
#> 3.     └─global h()
#> 4.       └─rlang::abort("foobar", call = my_caller$env)

I finally managed to get it done.

I declare a global environment in my package, but it was not enough as attributing the my_caller variable again caused a lot of bugs.

At the beginning of the main function, I save the current environment in the global environment as a variable, which I can then call anywhere in the following children functions.

Here is the code:

my_caller = rlang::env()

f = function(x, y){
  my_caller$env = rlang::current_env()
  g()
}
g = function(){
  h()
}
h = function(){
  rlang::abort("foobar", call=my_caller$env)
}

f(x=1, y=5)
#> Error in `f()`:
#>   ! foobar
#> Run `rlang::last_error()` to see where the error occurred.
rlang::last_error()
#> <error/rlang_error>
#>   Error in `f()`:
#>   ! foobar
#> ---
#>   Backtrace:
#>   1. global f(x = 1, y = 5)
#> Run `rlang::last_trace()` to see the full context.
rlang::last_trace()
#> <error/rlang_error>
#>   Error in `f()`:
#>   ! foobar
#> ---
#>   Backtrace:
#>   ▆
#> 1. └─global f(x = 1, y = 5)
#> 2.   └─global g()
#> 3.     └─global h()
#> 4.       └─rlang::abort("foobar", call = my_caller$env)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文