概述:我正在使用knitr处理大量 R/Markdown 文件(实际上更确切地说是R/考试setTimeLimit()
练习),其中一些文件需要很长时间才能完成。因此,我想为此设置一个时间限制,可以通过R 基础版来实现。然而,在某些情况下,这可能会导致未关闭的sink()
,请参阅下面可复现的示例。
问:我能做些什么来避免这种情况?这是 R 的一个 bug knitr
(或者是它的依赖项之一,例如 R 的 bug evaluate
)吗?还是 R 的基础版本?
示例:我设置了一个最小文件timeout.Rmd
,它计算答案 42,等待 2 秒,然后将其插入到 Markdown 输出中。
writeLines("
```{r}
ans <- 6 * 7
Sys.sleep(2)
```
Answer: `r ans`
", "timeout.Rmd")
然后,我将时间限制设置为 1 秒。
setTimeLimit(elapsed = 1)
之后,在代码块上运行knitr
文件timeout.Rmd
失败{r}
(正如预期的那样):
knitr::knit("timeout.Rmd")
##
## processing file: timeout.Rmd
## |................................... | 67% [unnamed-chunk-1]
##
## Error in `remove_hooks()`:
## ! reached elapsed time limit
## Backtrace:
## ▆
## 1. ├─knitr::knit("timeout.Rmd")
## 2. │ └─knitr:::process_file(text, output)
## 3. │ ├─xfun:::handle_error(...)
## 4. │ ├─base::withCallingHandlers(...)
## 5. │ └─knitr:::process_group(group)
## 6. │ └─knitr:::call_block(x)
## 7. │ └─knitr:::block_exec(params)
## 8. │ └─knitr:::eng_r(options)
## 9. │ ├─knitr:::in_input_dir(...)
## 10. │ │ └─knitr:::in_dir(input_dir(), expr)
## 11. │ └─knitr (local) evaluate(...)
## 12. │ └─evaluate::evaluate(...)
## 13. │ └─evaluate (local) `<fn>`()
## 14. └─evaluate::remove_hooks(hook_list)
##
## Quitting from timeout.Rmd:2-5 [unnamed-chunk-1]
失败之后,一切仍然正常,我们可以得到如下打印输出:
print(1)
## [1] 1
但在开启新的情节后,例如
plot(1)
我们不再获得打印输出
print(1)
因为现在有一个打开的窗口sink()
可以捕获所有打印输出。只有关闭它之后,打印才能恢复正常
sink()
print(1)
## [1] 1
我能够在几台运行 R 4.5.0 或 4.4.x 的 Linux 机器上直接在 shell 中复现此问题。 (在 RStudio 中,由于某种原因,超时似乎没有被捕获。)
我认为问题在于它
evaluate()
设置了多个钩子来捕获绘图输出,并在发生超时错误时尝试移除它们。然而,移除失败了,所以这些钩子仍然保留在原处,下次尝试绘图时,其中一个钩子会被激活。这是设置钩子的代码:
https://github.com/r-lib/evaluate/blob/ec9ca4e2e4fa0b7c7ccf9f9d11be19a163478ab7/R/graphics.R#L1-L9
defer()
为什么删除会失败?设置 on.exit 处理程序的代码看起来有点棘手:它on.exit()
在执行设置的函数之外的另一个函数上设置了处理程序。我猜时间限制代码与此不太兼容。我不确定这是 R 还是评估程序中的错误。您可以通过以下代码自行删除钩子来解决此问题:
这是一个相当严厉的做法,因此可能会导致其他问题。