Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't use a restart to handle stop_on_error = 2L #232

Merged
merged 16 commits into from
Jan 8, 2025
Merged

Conversation

cderv
Copy link
Collaborator

@cderv cderv commented Dec 17, 2024

After discussing with @lionel- , our conclusion was that invokeRestart("error") may not be necessary.
When stop_on_error = 2L, the code needs to stop on error, which will happen if the handler called in withCallingHandler() returns

So in this case, the handler could simply return NULL.

This solve the traceback problem encountered with latest evaluate and current rlang and knitr logic for entracing.

It also solve the traceback trimming problem that is created when invokeRestart() is used. With current invokeRestart("error", cnd), the sys.frames() that is handled by the traceback is not containing the same environments it seems.

Also calling signalCondition(cnd) in the restart handler was triggering some global handlers registered in Positron or in RStudio IDE - which led to differences in the context where rendering happened.

Which this change, rmarkdown::render() shows a rlang traceback with only the cell's function in all context: Positron, RStudio IDE and R console.

However, the snapshot test has changed, as output is now captured. I think it is ok though.

Regarding #231 and the idea of auto entracing, this was supposed to be done in knitr already. The change to withRestart() in recent evaluate demands an adaptation in knitr. I have a PR to come on knitr side to help with this.

Copy link
Member

@hadley hadley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for diving into this problem and figuring out a fix!

I think my only suggestion would be to make the snapshot a little clearer by doing:

ev <- evaluate("stop(\"1\")\n2", stop_on_error = 2L)

@hadley
Copy link
Member

hadley commented Dec 17, 2024

Do you have any thoughts on how we might test this to make sure we don't accidentally break RMarkdown tracebacks again in the future?

@cderv
Copy link
Collaborator Author

cderv commented Dec 18, 2024

Do you have any thoughts on how we might test this to make sure we don't accidentally break RMarkdown tracebacks again in the future?

I tried a few things, but I am struggling to get a reproducible test for issue, let alone with evaluate only. It seems highly dependant on the context. So I am still unto this.

@hadley
Copy link
Member

hadley commented Dec 18, 2024

@cderv I think it would be taking taking a suggests dependency on knitr, rmarkdown etc if we could get a good reprex for this. Have you tried callr + rmarkdown::render()? Maybe we could do a snapshot test of the output?

@cderv
Copy link
Collaborator Author

cderv commented Dec 18, 2024

Have you tried callr + rmarkdown::render()?

I was trying with evaluate() only by setting calling.handlers as would knitr or rlang do right now.

Maybe we could do a snapshot test of the output?

This is what I am trying, but with testthat using rlang and entracing by default error it seems, I can get something to reproduce the issue we have when using expect_snapshot() or expect_error() + printing.

I think the issue is that with current evaluate logic before this PR, stop() is used in the restart, and this hide the backtrace even if the condition is correctly a rlang_error with one. Anything that catch the condition and print it will no show the problem.

Even the trace cleaning problem is not an issue because it seems expect_error() does the right thing to clean trace.

If we can use callr + rmarkdown or knitr, I'll try that.

Initially, I tried to make a test in knitr instead directly (as this is knitr setting the logic of calling.handlers now) but it is not using testthat so I did not have snaphot available.

`options(rlang_backtrace_on_error_report = "full")` is only useful when error is not thrown by render() and kept as cell output (e.g. error = TRUE)
…r = 2L behavior

- Using evaluate only
- In rmarkdown + knitr context
- Use knitr instead of rmarkdown
- Test error thrown and capture in cell output
@cderv cderv force-pushed the fix/rlang-traceback branch from bc3e3d7 to 880b664 Compare December 20, 2024 14:04
@cderv
Copy link
Collaborator Author

cderv commented Dec 20, 2024

It seems something is different on CI than locally - looking into that.

@cderv
Copy link
Collaborator Author

cderv commented Dec 20, 2024

New change reduced the difference, but it seems to be related to some reproducibility on character used

❯ difft.exe .\stop-error-trace-trimmed.txt .\stop-error-trace-trimmed.new.txt        
.\stop-error-trace-trimmed.new.txt --- Text
1 Error in `h()`:             1 Error in `h()`:
2 ! !                         2 ! !
3 Backtrace:                  3 Backtrace:
4     ▆                       4     x
5  1. └─global f()            5  1. \-global f()
6  2.   └─global g()          6  2.   \-global g()
7  3.     └─global h()        7  3.     \-global h()
8 Execution halted            8 Execution halted

I probably need to set some reproducibilty setting when passing to rscript execution 🤔

testthat::local_reproducible_output() needs to be called in the background Rscript process too
because g() was missing
R/evaluate.R Show resolved Hide resolved
@cderv cderv requested review from hadley and lionel- December 20, 2024 14:59
Copy link
Member

@hadley hadley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've kind of forgotten where we are on this, but this looks like a big improvement so I think we should merge and get on to CRAN soon.

Error in `h()`:
! !
Backtrace:
1. global f()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we need to do to display a tree here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised to see the simplified backtrace which is no longer the default in rlang. Do we have a print(err, simplify = "branch") parameter somewhere?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@lionel- lionel- Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cderv Could you test with r-lib/rlang#1769 please? I'm hoping it helps in non-interactive sessions without adverse effects in interactive ones (calling knit() at the console).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the two snapshot, they are "fixed"

diff --git a/tests/testthat/_snaps/conditions/rmd-abort-error.txt b/tests/testthat/_snaps/conditions/rmd-abort-error.txt
index 114acb6..6b87b65 100644
--- a/tests/testthat/_snaps/conditions/rmd-abort-error.txt
+++ b/tests/testthat/_snaps/conditions/rmd-abort-error.txt
@@ -4,9 +4,11 @@ processing file: ressources/with-abort-error.Rmd
 Error in `h()`:
 ! !
 Backtrace:
- 1. global f()
- 2. global g()
- 3. global h()
+
+ 1. └─global f()
+ 2.   └─global g()
+ 3.     └─global h()
+ 4.       └─rlang::abort("!")
 
 Quitting from lines 6-10 [unnamed-chunk-1] (ressources/with-abort-error.Rmd)
 Execution halted
diff --git a/tests/testthat/_snaps/conditions/rmd-stop-error-auto-entrace.txt b/tests/testthat/_snaps/conditions/rmd-stop-error-auto-entrace.txt
index c2ea7b0..a1867ee 100644
--- a/tests/testthat/_snaps/conditions/rmd-stop-error-auto-entrace.txt
+++ b/tests/testthat/_snaps/conditions/rmd-stop-error-auto-entrace.txt
@@ -4,9 +4,10 @@ processing file: ressources/with-stop-error-auto-entrace.Rmd
 Error in `h()`:
 ! !
 Backtrace:
- 1. global f()
- 2. global g()
- 3. global h()
+
+ 1. └─global f()
+ 2.   └─global g()
+ 3.     └─global h()

 Quitting from lines 6-10 [unnamed-chunk-1] (ressources/with-stop-error-auto-entrace.Rmd)
 Execution halted

I then tested manually and I think I covered most of cases. It does not seem to have adverse affect on interactive session. It looks good to merge.

Maybe I'll update the snapshot once next rlang is release so that it does not prevent evaluate release.

@@ -0,0 +1,4 @@
Error in h() : !
Calls: <Anonymous> ... withCallingHandlers -> withVisible -> eval -> eval -> f -> g -> h
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the error you see when a vignette has an error, so that might suggest if we can entrace in a vignette we can get better errors?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I'll see if I can adapt knitr for this.

Copy link
Member

@lionel- lionel- left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Letting the error escape feels like the right approach here.

Error in `h()`:
! !
Backtrace:
1. global f()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised to see the simplified backtrace which is no longer the default in rlang. Do we have a print(err, simplify = "branch") parameter somewhere?

for different rlang top env
@hadley hadley merged commit fa1403c into main Jan 8, 2025
14 checks passed
@hadley hadley deleted the fix/rlang-traceback branch January 8, 2025 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants