-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Collect warnings and add dplyr_last_warnings()
#6443
Conversation
DESCRIPTION
Outdated
@@ -30,6 +30,7 @@ BugReports: https://github.com/tidyverse/dplyr/issues | |||
Depends: | |||
R (>= 3.4.0) | |||
Imports: | |||
cli (>= 3.3.0), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR uses cli right away to use features like pluralisation. Also I think it's better to write new code with cli right away since porting existing code is so tricky.
} | ||
|
||
# rlang should export this routine | ||
error_call <- function(call) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a hack to get the result of rlang:::error_call()
, which has special behaviour compared to frame_call()
, for instance with S3 methods.
Tracked in r-lib/rlang#1474.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also need to do the same thing with summarise? e.g.
library(dplyr, warn.conflicts = FALSE)
df <- tibble(id = 1:100, x = NA)
df |>
group_by(id) |>
summarise(y = min(x, na.rm = TRUE))
Playing around with this a bit, I think it's probably worth showing maybe 1 warning by default? i.e. instead of: #> Warning message:
#> There were 2 warnings in `mutate()`.
#> ℹ Run `dplyr::dplyr_last_warnings()` to see them. Something like: #> Warning message:
#> There were warnings in `mutate()`.
#> The first warning was:
...
#> ℹ Run `dplyr::dplyr_last_warnings()` to see the remaining 1 warning. |
We don't currently enrich warnings in summarise, but it should be easy to add. I guess we should make sure all grouped evals collect warnings? This seems better scoped for a second PR though.
Good idea. |
With the last commit:
f <- function(x) {
warning("foo")
x
}
df <- tibble(id = 1:3)
df |>
group_by(x = f(1):n()) |>
mutate(x = f(1))
#> # A tibble: 3 × 2
#> # Groups: x [1]
#> id x
#> <int> <dbl>
#> 1 1 1
#> 2 2 1
#> 3 3 1
#> Warning messages:
#> 1: In group_by() : Problem while computing `x = f(1):n()`.
#> Caused by warning in `f()`:
#> ! foo
#> 2: There were 3 warnings in `mutate()`.
#> The first warning was:
#> ! foo
#> ℹ Run `dplyr::dplyr_last_warnings()` to see the 2 remaining warnings. This is a bit messy because of the way our warning conditions interact with base display, but that's not too bad. |
The display of the one and two warning case feels rather different: Can we make them more similar? Maybe the 1 warning case could just be:
i.e. drop the "the first warning" and "run ..." lines. How hard would it be to make |
@hadley Much better, thanks! The problem with capturing backtraces is that it's very costly. We could do it for the first 5 warnings though. I've also changed:
to
Just as a reminder that there could be multiple group-by steps to look for. Is this helpful? df |>
rowwise() |>
mutate(x = f(1))
#> # A tibble: 100 × 2
#> # Rowwise:
#> id x
#> <int> <dbl>
#> 1 1 1
#> 2 2 1
#> 3 3 1
#> 4 4 1
#> # … with 96 more rows
#> # ℹ Use `print(n = ...)` to see more rows
#> Warning message:
#> There were 100 warnings in a `mutate()` step.
#> The first warning was:
#> ! foo
#> ℹ Run `dplyr::dplyr_last_warnings()` to see the 99 remaining warnings. df |>
group_by(x = f(1):n()) |>
mutate(x = f(1))
#> # A tibble: 3 × 2
#> # Groups: id [3]
#> id x
#> <int> <dbl>
#> 1 1 1
#> 2 2 1
#> 3 3 1
#> Warning messages:
#> 1: There was 1 warning in a `group_by()` step.
#> ! foo
#> 2: There were 3 warnings in a `mutate()` step.
#> The first warning was:
#> ! foo
#> ℹ Run `dplyr::dplyr_last_warnings()` to see the 2 remaining warnings. |
With the last commit, we now entrace the first 5 warnings. We also only display 5 warnings by default instead of 20, so that:
The display opts into the new tree backtrace style with grayed out inner parts. df <- tibble(id = 1:2)
f <- function(...) g(...)
g <- function(...) h(...)
h <- function(x, base = TRUE) {
if (base) {
warning("foo")
} else {
warn("foo")
}
x
}
foo <- function() bar()
bar <- function() {
df |>
group_by(x = f(1):n()) |>
mutate(x = f(1, base = FALSE))
}
foo()
#> # A tibble: 2 × 2
#> # Groups: x [1]
#> id x
#> <int> <dbl>
#> 1 1 1
#> 2 2 1
#> Warning messages:
#> 1: There was 1 warning in a `group_by()` step.
#> ! foo
#> 2: There were 2 warnings in a `mutate()` step.
#> The first warning was:
#> ! foo
#> ℹ Run `dplyr::dplyr_last_warnings()` to see the 1 remaining warning.
dplyr_last_warnings()
#> [[1]]
#> <warning/rlang_warning>
#> Warning in `group_by()`:
#> Problem while computing `x = f(1):n()`.
#> Caused by warning in `h()`:
#> ! foo
#> ---
#> Backtrace:
#> ▆
#> 1. ├─utils (local) .ess.command(...)
#> 2. │ ├─base::withCallingHandlers(interrupt = restart, withVisible(expr))
#> 3. │ └─base::withVisible(expr)
#> 4. ├─base::local(...)
#> 5. │ └─base::eval.parent(substitute(eval(quote(expr), envir)))
#> 6. │ └─base::eval(expr, p)
#> 7. │ └─base::eval(expr, p)
#> 8. ├─base::eval(...)
#> 9. │ └─base::eval(...)
#> 10. │ └─global foo()
#> 11. │ └─global bar()
#> 12. │ ├─dplyr::mutate(group_by(df, x = f(1):n()), x = f(1, base = FALSE))
#> 13. │ ├─dplyr::group_by(df, x = f(1):n())
#> 14. │ └─dplyr:::group_by.data.frame(df, x = f(1):n()) at dplyr/R/group-by.r:135:2
#> 15. │ └─dplyr::group_by_prepare(...) at dplyr/R/group-by.r:140:2
#> 16. │ └─dplyr:::add_computed_columns(...) at dplyr/R/group-by.r:215:2
#> 17. │ └─dplyr:::mutate_cols(...) at dplyr/R/group-by.r:253:6
#> 18. │ ├─base::withCallingHandlers(...) at dplyr/R/mutate.R:239:2
#> 19. │ └─(function() {... at dplyr/R/mutate.R:239:2
#> 20. │ └─dplyr:::mutate_col(i, .data, dots, mask, new_columns) at dplyr/R/mutate.R:244:6
#> 21. │ └─mask$eval_all_mutate(quo) at dplyr/R/mutate.R:371:8
#> 22. │ └─dplyr (local) eval() at dplyr/R/data-mask.R:69:6
#> 23. └─global f(1)
#> 24. └─global g(...)
#> 25. └─global h(...)
#> 26. └─base::warning("foo")
#>
#> [[2]]
#> <warning/rlang_warning>
#> Warning in `mutate()`:
#> Problem in group 1: x = 1 while computing `x = f(1, base = FALSE)`.
#> Caused by warning:
#> ! foo
#> ---
#> Backtrace:
#> ▆
#> 1. ├─utils (local) .ess.command(...)
#> 2. │ ├─base::withCallingHandlers(interrupt = restart, withVisible(expr))
#> 3. │ └─base::withVisible(expr)
#> 4. ├─base::local(...)
#> 5. │ └─base::eval.parent(substitute(eval(quote(expr), envir)))
#> 6. │ └─base::eval(expr, p)
#> 7. │ └─base::eval(expr, p)
#> 8. ├─base::eval(...)
#> 9. │ └─base::eval(...)
#> 10. │ └─global foo()
#> 11. │ └─global bar()
#> 12. │ ├─dplyr::mutate(group_by(df, x = f(1):n()), x = f(1, base = FALSE))
#> 13. │ └─dplyr:::mutate.data.frame(...) at dplyr/R/mutate.R:146:2
#> 14. │ └─dplyr:::mutate_cols(.data, dplyr_quosures(...), caller_env = caller_env()) at dplyr/R/mutate.R:175:2
#> 15. │ ├─base::withCallingHandlers(...) at dplyr/R/mutate.R:239:2
#> 16. │ └─(function() {... at dplyr/R/mutate.R:239:2
#> 17. │ └─dplyr:::mutate_col(i, .data, dots, mask, new_columns) at dplyr/R/mutate.R:244:6
#> 18. │ └─mask$eval_all_mutate(quo) at dplyr/R/mutate.R:371:8
#> 19. │ └─dplyr (local) eval() at dplyr/R/data-mask.R:69:6
#> 20. └─global f(1, base = FALSE)
#> 21. └─global g(...)
#> 22. └─global h(...)
#> 23. └─rlang::warn("foo")
#>
#> [[3]]
#> <warning/rlang_warning>
#> Warning in `mutate()`:
#> Problem in group 2: x = 2 while computing `x = f(1, base = FALSE)`.
#> Caused by warning:
#> ! foo
#> ---
#> Backtrace:
#> ▆
#> 1. ├─utils (local) .ess.command(...)
#> 2. │ ├─base::withCallingHandlers(interrupt = restart, withVisible(expr))
#> 3. │ └─base::withVisible(expr)
#> 4. ├─base::local(...)
#> 5. │ └─base::eval.parent(substitute(eval(quote(expr), envir)))
#> 6. │ └─base::eval(expr, p)
#> 7. │ └─base::eval(expr, p)
#> 8. ├─base::eval(...)
#> 9. │ └─base::eval(...)
#> 10. │ └─global foo()
#> 11. │ └─global bar()
#> 12. │ ├─dplyr::mutate(group_by(df, x = f(1):n()), x = f(1, base = FALSE))
#> 13. │ └─dplyr:::mutate.data.frame(...) at dplyr/R/mutate.R:146:2
#> 14. │ └─dplyr:::mutate_cols(.data, dplyr_quosures(...), caller_env = caller_env()) at dplyr/R/mutate.R:175:2
#> 15. │ ├─base::withCallingHandlers(...) at dplyr/R/mutate.R:239:2
#> 16. │ └─(function() {... at dplyr/R/mutate.R:239:2
#> 17. │ └─dplyr:::mutate_col(i, .data, dots, mask, new_columns) at dplyr/R/mutate.R:244:6
#> 18. │ └─mask$eval_all_mutate(quo) at dplyr/R/mutate.R:371:8
#> 19. │ └─dplyr (local) eval() at dplyr/R/data-mask.R:69:6
#> 20. └─global f(1, base = FALSE)
#> 21. └─global g(...)
#> 22. └─global h(...)
#> 23. └─rlang::warn("foo") |
I've also renamed |
14688d9
to
ff7bdfb
Compare
4eb5df8
to
57dd573
Compare
6bfa6bf
to
9d87a64
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if you are done, but I gave the changes a look and they look nice
I'm assuming you are planning on doing summarise()
in another PR
tests/testthat/_snaps/conditions.md
Outdated
10. \-base::warning("msg") | ||
x | ||
1. +-dplyr::mutate(df, x = f()) | ||
2. \-dplyr:::mutate.data.frame(df, x = f()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks a lot nicer!
@@ -96,15 +96,44 @@ group_labels_details <- function(keys) { | |||
}), ", ") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably backtick the {name} = {value}
result in group_labels_details()
here so it shows up a little better in the error message. Fine if you don't want to do that here
9d87a64
to
cb3fd73
Compare
Co-authored-by: Hadley Wickham <[email protected]>
- If there is only one warning, just emit it. - If there are multiple warnings, just show the first one.
To namespace it in `last_` for autocompletion
cb3fd73
to
104bddf
Compare
Branched from #6438.
Fixes #6005
Fixes #6236
dplyr_last_warnings()
returns enriched warnings, the first 20 by default.The other way we could handle long lists of warnings is by detecting if there are more than 10 or 20. If that's the case, suggest to run
head(dplyr_last_warnings())
, otherwise justdplyr_last_warnings()
. On hindsight I think that would make things simpler internally and for users.