Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/lewinfox/icecream

Never use `print()` to debug again.
https://github.com/lewinfox/icecream

debugging r

Last synced: 3 months ago
JSON representation

Never use `print()` to debug again.

Awesome Lists containing this project

README

        

---
output: github_document
---

```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)

demo_file <- file.path(tempdir(TRUE), "demo.R")

demo_fns <- "# demo.R
f1 <- function(x) {
ic()
if (x > 0) {
f2()
}
}

f2 <- function() {
ic()
}

f3 <- function(x) {
ic(x)
}
"

cat(demo_fns, file = demo_file)

source(demo_file)
```

# icecream

[![](http://cranlogs.r-pkg.org/badges/grand-total/icecream)](https://cran.r-project.org/package=icecream)
[![R-CMD-check](https://github.com/lewinfox/icecream/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/lewinfox/icecream/actions/workflows/R-CMD-check.yaml)

icecream is designed to make print debugging easier. It allows you to print out an expression, its
value and (optionally) which function and file the call originated in.

This is an R port of [gruns/icecream](https://github.com/gruns/icecream). All credit for the idea
belongs to [Ansgar Grunseid](https://github.com/gruns).

## Installation

Install from CRAN with:

``` r
install.packages("icecream")
```

Or you can install the development version from GitHub with:

``` r
devtools::install_github("lewinfox/icecream")
```

## Inspect variables

The `ic()` function prints its argument and its value. It also returns the value of the evaluated
argument, meaning that it is effectively transparent in code - just wrap an expression in `ic()` to
get debugging output.

```{r example}
library(icecream)

is_negative <- function(x) x < 0

ic(is_negative(1))

ic(is_negative(-1))
```

You're more likely to want to do this within a function:

```{r}
some_function <- function(x) {
intermediate_value <- x * 10
answer <- ic(intermediate_value / 2)
return(answer)
}

some_function(1)

some_function(10)
```

More complex inputs like lists and data frames are summarised to avoid cluttering the terminal.

```{r}
df <- ic(iris)

my_list <- ic(list(a = 1, b = 3, c = 1:100))
```

## Inspect execution

Calling `ic()` with no arguments causes it to print out the file, line and parent function it was
called from.

In this example we have a file `demo.R` that contains two functions. We've inserted `ic()` calls at
strategic points so we can track what's being executed.

```{r code=readLines(demo_file)}
```

``` r
source("demo.R")

f1(-1)
#> ℹ ic| `global::f1()` in demo.R:3:2

f1(1)
#> ℹ ic| `global::f1()` in demo.R:3:2
#> ℹ ic| `global::f2()` in demo.R:10:2
```

In the case of functions that haven't been `source()`d or loaded from a package there is no source
code to refer to. In these cases the function's environment will be displayed.

``` r
orphan_func <- function() {
ic()
TRUE
}

orphan_func()
#> ℹ ic| `global::orphan_func()` in
#> [1] TRUE

e <- new.env()
attr(e, "name") <- "icecream_van"
environment(orphan_func) <- e

orphan_func()
#> ℹ ic| `orphan_func()` in
#> [1] TRUE
```

## Enable / disable

The `ic_enable()` and `ic_disable()` functions enable or disable the `ic()` function. If disabled,
`ic()` will return the result of evaluating its input but will not print anything.

```{r}
ic_enable() # This is TRUE by default

ic(mean(1:100))

ic_disable()

ic(mean(1:100))
```

```{r, include=FALSE}
ic_enable()
```

Convenience functions `with_ic_enable()` and `with_ic_disable()` are also provided.

```{r}
ic_enable()

with_ic_disable(ic(mean(1:100)))

ic_disable()

with_ic_enable(ic(mean(1:100)))
```

```{r, include=FALSE}
ic_enable()
```

## Options

The following options can be used to control behaviour:

### `icecream.enabled`
Boolean. If `FALSE`, calls to `ic(foo)` simply evaluate and return `foo`. No output is printed. This
option can be set directly or with the `ic_enable()` and `ic_disable()` functions.

### `icecream.prefix`
This is printed at the beginning of every line. Defaults to `"ic|"`.

```{r, include=FALSE}
old.prefix <- getOption("icecream.prefix")
```

```{r}
ic(mean(1:5))

options(icecream.prefix = "DEBUG:")
ic(mean(1:5))

options(icecream.prefix = "\U1F366")
ic(mean(1:5))
```

```{r, include=FALSE}
options(icecream.prefix = old.prefix)
```

### `icecream.always.include.context`
Boolean. If `TRUE`, when calling `ic(foo)` the source file and line will be printed along with the
expression and value. If no `srcref()` is available the function's environment will be displayed
instead. This can be useful for more complicated debugging but produces a lot of output so is
disabled by default.

```{r, include=FALSE}
old.ctx <- getOption("icecream.always.include.context")
```

``` r
f3(1)
#> ℹ ic| `x`: num 1

options(icecream.always.include.context = TRUE)

f3(1)
#> ℹ ic| `global::f3()` in demo.R:14:2 | `x`: num 1
```

When `ic()` is called with no arguments, the context is always printed because showing the location
of the call is the only reason to call `ic()` on its own.

```{r, include=FALSE}
options(icecream.always.include.context = old.ctx)
```

### `icecream.peeking.function` and `icecream.max.lines`
These two options control how the result of evaluation of an expression is printed. `icecream.peeking.function` indicates the function that summarizes the object. Default value is `ic_autopeek`, which works like `utils::str` for most of the time, but gives more informative output
for `lists`, `data.frames` and their subclasses in a more compact way. `icecream.max.lines`
determines maximum number of lines that the peek of an object occupies; defaults to 1.

For more complex data you may want to use e.g. `head` function and 5 lines.

```{r, include=FALSE}
old.fun <- getOption("icecream.peeking.function")
old.lines <- getOption("icecream.max.lines")
```

```{r, include=TRUE}
data(iris)

ic(iris) # we would like to see header of the data

options(
icecream.peeking.function = head,
icecream.max.lines = 5
)

ic(iris)
```

**NOTE**: Due to [r-lib/cli#626](https://github.com/r-lib/cli/issues/626), newlines are currently
being removed from `ic()`'s multi-line output.

```{r, include=FALSE}
options(
icecream.peeking.function = old.fun,
icecream.max.lines = old.lines
)
```

Note that if `icecream.max.lines` is greater than 1 and summary of an object is longer than 1, the
alert occupies one line more due to the header.

### `icecream.output.function`, `icecream.arg.to.string.function`
Not implemented yet. See the [configuration](https://github.com/gruns/icecream#configuration)
section of the original project docs for details of what they will do.

## TODO:

* Implement `ic.format()` (see [here](https://github.com/gruns/icecream#miscellaneous)).
* Implement `ic.output.function`. At the moment it uses `cli::cli_alert_info()`
* Implement `ic.arg.to.string.function`