vignettes/articles/messages-and-errors.Rmd
messages-and-errors.Rmd
Things I do in a hidden chunk here, to aid exposition about internal tooling:
Everything should be emitted by helpers in utils-ui.R
:
specifically, drive_bullets()
(and, for errors,
drive_abort()
). These helpers are all wrappers around cli
functions, such as cli::cli_bullets()
.
These may not demo well via pkgdown, but the interactive experience is nice.
drive_bullets(c(
"noindent",
" " = "indent",
"*" = "bullet",
">" = "arrow",
"v" = "success",
"x" = "danger",
"!" = "warning",
"i" = "info"
))
#> noindent
#> indent
#> • bullet
#> → arrow
#> ✔ success
#> ✖ danger
#> ! warning
#> ℹ info
The helpers encourage consistent styling and make it possible to selectively silence messages coming from googledrive. The googledrive message helpers:
Use the cli package to get interpolation, inline markup, and pluralization.
Eventually route through rlang::inform()
, which is
important because inform()
prints to standard output in
interactive sessions. This means that informational messages won’t have
the same “look” as errors and can generally be more stylish, at least in
IDEs like RStudio.
Use some googledrive-wide style choices, such as:
.drivepath
style is like cli’s inline
.file
style, except cyan instead of blue..field
style is tweaked to be flanked by
single quotes in a no-color situation.Are under the control of the googledrive_quiet
option. If it’s unset, the default is to show messages (unless we’re
testing, i.e. the environment variable TESTTHAT
is
"true"
). Doing
options(googledrive_quiet = TRUE)
will suppress messages.
There are withr-style convenience helpers:
local_drive_quiet()
and
with_drive_quiet()
.
How we use the inline classes:
.drivepath
for the name or, occasionally, the (partial)
path of a Drive file.field
for the value of an argument, e.g. a MIME
type.code
for a column in a data frame and for reserved
words, such as NULL
, TRUE
, and
NA
.arg
, .fun
, .path
,
.cls
, .url
for their usual purpose
drive_bullets(c(
"We need to talk about the {.arg foofy} argument to {.fun blarg}",
"You provided {.field a_very_weird_value} and I suspect you're confused \\
about something"
))
#> We need to talk about the `foofy` argument to `blarg()`
#> You provided a_very_weird_value and I suspect you're confused about something
Most relevant cli docs:
I use the different bullet points in drive_bullets()
to
convey a mood.
Exclamation mark "!"
: I’m not throwing an error or
warning, but I want to get the user’s attention, because it seems likely
(but not certain) that they misunderstand something about googledrive or
Google Drive or their Drive files. Examples:
drive_bullets(c(
"!" = "Ignoring {.arg type}. Only consulted for native Google file types.",
" " = "MIME type of {.arg file}: {.field mime_type}."
))
#> ! Ignoring `type`. Only consulted for native Google file types.
#> MIME type of `file`: mime_type.
drive_bullets(c(
"!" = "Currently only fields for the {.field files} resource can be \\
checked for validity.",
" " = "Nothing done."
))
#> ! Currently only fields for the files resource can be checked for validity.
#> Nothing done.
drive_bullets(c(
"!" = "No updates specified."
))
#> ! No updates specified.
drive_bullets(c(
"!" = "No such file to delete."
))
#> ! No such file to delete.
Information “i”: I’m just keeping you informed of how my work is going.
drive_bullets(c(
"i" = "No pre-existing file at this filepath. Calling \\
{.fun drive_upload}."
))
#> ℹ No pre-existing file at this filepath. Calling `drive_upload()`.
drive_bullets(c(
"i" = "Pre-existing file at this filepath. Calling \\
{.fun drive_update}."
))
#> ℹ Pre-existing file at this filepath. Calling `drive_update()`.
drive_bullets(c(
"i" = "Not logged in as any specific Google user."
))
#> ℹ Not logged in as any specific Google user.
In cases where we determine there is nothing we can or should do,
sometimes I use "!"
and sometimes I use "i"
.
It depends on whether it feels like the user could or should have known
that no work would be possible or needed.
Often we need to create bullets from an R object, such as a character vector or a dribble. What needs to happen:
gargle_map_cli()
is a new generic in gargle that turns
an object into a vector of strings with cli markup. Currently gargle
exports methods for character
(and NULL
and a
default
) and googlesheets4 defines a method for
dribble
. This is likely to be replaced by something in cli
itself in due course.
gargle_map_cli(letters[1:3])
#> [1] "{.field a}" "{.field b}" "{.field c}"
By default gargle_map_cli.character()
just applies the
.field
style, i.e. the template is
"{.field <<x>>}"
. But the template can be
customized, if you need something else. Note that we use non-standard
glue delimiters (<<
and >>
, by
default), because we are interpolating into a string with glue/cli
markup, where {}
has the usual meaning.
gargle_map_cli(letters[4:6], template = "how about a path {.path <<x>>}?")
#> [1] "how about a path {.path d}?" "how about a path {.path e}?"
#> [3] "how about a path {.path f}?"
The gargle_map_cli.dribble()
method makes a cli-marked
up string for each row of the dribble, i.e. for each Drive file.
dat <- drive_find(n_max = 5)
#> Error in `drive_auth()`:
#> ! Can't get Google credentials.
#> ℹ Are you running googledrive in a non-interactive session? Consider:
#> • Call `drive_deauth()` to prevent the attempt to get credentials.
#> • Call `drive_auth()` directly with all necessary specifics.
#> ℹ See gargle's "Non-interactive auth" vignette for more details:
#> ℹ <https://gargle.r-lib.org/articles/non-interactive-auth.html>
gargle_map_cli(dat)
#> Error: object 'dat' not found
gargle_map_cli.dribble()
also allows a custom template,
but it’s a more complicated and less common situation than for
character
. We won’t get into that here. (I don’t consider
the dribble styling to be finalized yet.)
The result of gargle_map_cli()
then gets processed with
gargle::bulletize()
, which adds the bullet-specifying names
and does aesthetically pleasing truncation.
bulletize(gargle_map_cli(letters))
#> * * * * *
#> "{.field a}" "{.field b}" "{.field c}" "{.field d}" "{.field e}"
#>
#> "… and 21 more"
bulletize(gargle_map_cli(letters), bullet = "x", n_show = 2)
#> x x
#> "{.field a}" "{.field b}" "… and 24 more"
drive_bullets(c(
"These are surprising things:",
bulletize(gargle_map_cli(letters), bullet = "!")
))
#> These are surprising things:
#> ! a
#> ! b
#> ! c
#> ! d
#> ! e
#> … and 21 more
dat <- drive_find(n_max = 10)
#> Error in `drive_auth()`:
#> ! Can't get Google credentials.
#> ℹ Are you running googledrive in a non-interactive session? Consider:
#> • Call `drive_deauth()` to prevent the attempt to get credentials.
#> • Call `drive_auth()` directly with all necessary specifics.
#> ℹ See gargle's "Non-interactive auth" vignette for more details:
#> ℹ <https://gargle.r-lib.org/articles/non-interactive-auth.html>
drive_bullets(c(
"Some Drive files:",
bulletize(gargle_map_cli(dat))
))
#> Error: object 'dat' not found
It’s conceivable that cli will gain a better way of vectorization, but this works for now.
Known dysfunction: it’s inefficient to gargle_map_cli()
over the whole object, then truncate with bulletize()
. But
it’s easy. There are contexts, like tibble printing, where formatting
stuff that will never see the light of day is really punishing. But I’m
not sure I really have to worry about that.
I am currently using cli::cli_abort()
, which is present
in the dev version of cli (as of late May 2021, cli version
2.5.0.9000).
It’s wrapped as drive_abort()
, for the same reason as
drive_bullets()
, namely to apply some package-wide style
tweaks.
The mechanics of drive_abort()
usage are basically the
same as drive_bullets()
.