Giter VIP home page Giter VIP logo

intercept's Introduction

intercept

Messages and warnings are sometimes hard to debug because there’s no simple way to know where they occur. {intercept} provides some helpers to trigger behaviors conditionally depending on the class of the condition, the actual message, or the package where it occurs.

  • suppress_warnings() / suppress_messages() remove only warnings and messages that qualify
  • abort_on_warnings() / abort_on_messages() fail on relevant warnings/messages only, a bit like options(warn = 2) but restricted in scope
  • with_verbose_warnings() / with_verbose_messages() print the error class and the trace where relevant warnings/messages occur
  • recover_on_warnings() / recover_on_messages() let you enter the debugger in a chosen frame when a relevant warning/message occurs
  • recover_on_errors() is a bit like options(error = recover) but with control on class, package and message text.

Installation

You can install the development version of intercept like so:

devtools::install_github("moodymudskipper/intercept")

Example

library(intercept)
library(dplyr, warn.conflicts = FALSE)
fun <- function() {
  left_join(tibble(a = 1, b = 2), tibble(a = 1, c = 3))
}

# triggers a message because of implicit join column choice
fun()
#> Joining with `by = join_by(a)`
#> # A tibble: 1 × 3
#>       a     b     c
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3

# just like suppressMessage() by default
suppress_messages(fun())
#> # A tibble: 1 × 3
#>       a     b     c
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3

# doesn't qualify, message is not suppressed
suppress_messages(fun(), endsWith, "Joining")
#> Joining with `by = join_by(a)`
#> # A tibble: 1 × 3
#>       a     b     c
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3

# qualifies
suppress_messages(fun(), startsWith, "Joining")
#> # A tibble: 1 × 3
#>       a     b     c
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3

# we can use regular expressions
suppress_messages(fun(), "w.th")
#> # A tibble: 1 × 3
#>       a     b     c
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3

# or packages
suppress_messages(fun(), .packages = "dplyr")
#> # A tibble: 1 × 3
#>       a     b     c
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3

# abort selectively, same arguments
abort_on_messages(fun(), .packages = "dplyr")
#> message of class <rlang_message/message/condition>
#> Error:
#> ! Joining with `by = join_by(a)`

we can print additional info about messages or warnings, conditionally if we want, here without any condition

fun2 <- function() {
  left_join(tibble(a = c(1,1), b = 2), tibble(a = c(1,1), c = 3), by = "a")
}

with_verbose_warnings(fun2())
#> warning of class <dplyr_warning_join_relationship_many_to_many/dplyr_warning_join/dplyr_w#> arning/rlang_warning/warning/condition>
#> Detected an unexpected many-to-many relationship between `x` and `y`.
#>      ▆
#>   1. ├─intercept::with_verbose_warnings(fun2())
#>   2. │ └─intercept:::with_condition(...) at intercept/R/with_verbose.R:22:2
#>   3. │   └─base::withCallingHandlers(.expr, warning = handler0) at #> intercept/R/with_condition.R:20:4
#>   4. ├─global fun2() at intercept/R/with_verbose.R:22:2
#>   5. │ ├─dplyr::left_join(...)
#>   6. │ └─dplyr:::left_join.data.frame(...)
#>   7. │   └─dplyr:::join_mutate(...)
#>   8. │     └─dplyr:::join_rows(...)
#>   9. │       └─dplyr:::dplyr_locate_matches(...)
#>  10. │         ├─base::withCallingHandlers(...)
#>  11. │         └─vctrs::vec_locate_matches(...)
#>  12. ├─vctrs:::warn_matches_relationship_many_to_many(...)
#>  13. │ └─vctrs:::warn_matches_relationship(...)
#>  14. │   └─vctrs:::warn_matches(...)
#>  15. │     └─vctrs:::warn_vctrs(...)
#>  16. │       └─rlang::warn(...)
#>  17. │         └─base::warning(cnd)
#>  18. │           └─base::withRestarts(...)
#>  19. │             └─base (local) withOneRestart(expr, restarts[[1L]])
#>  20. │               └─base (local) doWithOneRestart(return(expr), restart)
#>  21. ├─dplyr (local) `<fn>`(`<vc______>`)
#>  22. │ └─dplyr:::rethrow_warning_join_relationship_many_to_many(cnd, error_call)
#>  23. │   └─dplyr:::warn_join(...)
#>  24. │     └─dplyr:::warn_dplyr(...)
#>  25. │       └─rlang::warn(...)
#>  26. │         └─base::warning(cnd)
#>  27. │           └─base::withRestarts(...)
#>  28. │             └─base (local) withOneRestart(expr, restarts[[1L]])
#>  29. │               └─base (local) doWithOneRestart(return(expr), restart)
#>  30. └─intercept (local) `<fn>`(`<dp______>`)
#>  31.   └─intercept (local) handler(cond) at intercept/R/with_condition.R:13:6
#> # A tibble: 4 × 3
#>       a     b     c
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3
#> 2     1     2     3
#> 3     1     2     3
#> 4     1     2     3
#> Warning message:
#> In left_join(tibble(a = c(1, 1), b = 2), tibble(a = c(1, 1), c = 3),  :
#>   Detected an unexpected many-to-many relationship between `x` and `y`.
#> ℹ Row 1 of `x` matches multiple rows in `y`.
#> ℹ Row 1 of `y` matches multiple rows in `x`.
#> ℹ If a many-to-many relationship is expected, set `relationship = "many-to-many"` to #> silence this warning.

# we can remove messages or warnings depending on class too
suppress_warnings(fun2(), .classes = "dplyr_warning_join")
#> # A tibble: 4 × 3
#>       a     b     c
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3
#> 2     1     2     3
#> 3     1     2     3
#> 4     1     2     3

Enter the debugger conditionally:

recover_on_messages(fun(), startsWith, "Joining")
#> message of class <rlang_message/message/condition>
#> Joining with `by = join_by(a)`
#> 
#> Enter a frame number, or 0 to exit   
#> 
#>  1: recover_on_messages(fun(), startsWith, "Joining")
#>  2: recover.R#9: with_condition(.expr, "message", handler, .f, ..., .cla
#>  3: with_condition.R#18: withCallingHandlers(.expr, message = handler0)
#>  4: with_condition.R#18: fun()
#>  5: #2: left_join(tibble(a = 1, b = 2), tibble(a = 1, c = 3))
#>  6: left_join.data.frame(tibble(a = 1, b = 2), tibble(a = 1, c = 3))
#>  7: join_mutate(x = x, y = y, by = by, type = "full", suffix = suffix, n
#>  8: join_by_common(x_names, y_names, error_call = error_call)
#>  9: inform(glue("Joining with `by = join_by({by_names})`"))
#> 10: withRestarts(muffleMessage = function() NULL, {
#>     signalCondition(c
#> 11: withOneRestart(expr, restarts[[1]])
#> 12: doWithOneRestart(return(expr), restart)
#> 13: signalCondition(cnd)
#> 14: (function (cond) 
#> {
#>     msg <- cli::ansi_strip(conditionMessage(cond))
#> 
#> 15: with_condition.R#13: handler(cond)
#> 
#> Selection: 

intercept's People

Contributors

moodymudskipper avatar

Stargazers

Jimmy Briggs avatar Andrew Allen Bruce avatar Yusuke Sasaki avatar Komlan Nouwokpo Samati avatar Justin Millar avatar Sandalots avatar Garrick Aden-Buie avatar vonSkopnik avatar  avatar  avatar

Watchers

 avatar  avatar

intercept's Issues

`local_` variants, and naming things

I don't know if I love the name of the functions and package, I just got it out fast and I think it's useful as it is.

BUT

  • intercept sound like y0 to mathematicians/statisticians
  • it would be nice to have more harmonious names

AND

I discovered we can easily implement local_* variants as in {withr}, using globalCallingHandlers() (and reverting on exit)


I'm not sure about those names either but to get the ball rolling I propose something consistent meanwhile, and we need to see if we implement it here or in a new package, once we're happy.

current names -> new_proposition / local_variant:

• recover_on_warnings -> with_recover_wrn / local_recover_wrn
• recover_on_messages -> with_recover_msg / local_recover_msg
• recover_on_errors -> with_recover_err / local_recover_err
• with_verbose_warnings -> with_verbose_wrn / local_verbose_wrn
• with_verbose_messages -> with_verbose_msg / local_verbose_msg
• abort_on_messages -> with_failure_msg / local_failure_msg
• abort_on_warnings -> with_failure_wrn / local_failure_wrn
• suppress_warnings -> with_silence_wrn / local_silence_wrn
• suppress_messages -> with_silence_msg / local_silence_msg

And we can add :

with_handler_wrn
with_handler_err
with_handler_msg

These are useful to set some specific behaviours, for example setting shiny popups on error, logging some messages... which might not be possible with native functions.

These have the same width :

recover
verbose
failure
silence
handler (a more general option)

wrn
msg
err

I think we might also generalise the and have :

# by default with .classes = c("message", "warning") or .classes = c("message", "warning", "error")
with_recover
with_verbose
with_failure
with_silence
with_handler # the most general function
# + local_ variants

So for instance we can set behavior to messages and warnings at the same time, silence everything with local_silence(), or use local_recover() during development to help silencing all the things.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.