Making your Python Debugging Sweeter with some “IceCream” 🍦😉
Tags: python, programming
Published: 2021-03-05 11:22:47


*Never use print() to debug again.*

## IceCream — Never use print() to debug again

Do you ever use print() or log() to debug your code? Of course you do. IceCream, or ic for short, makes print debugging a little sweeter.

## Inspect Variables

Have you ever printed variables or expressions to debug your program? If you’ve ever typed something like

```
    print(foo('123'))

or the more thorough

    print("foo('123')", foo('123'))
```

then ic() is here to help. With arguments, ic() inspects itself and prints both its own arguments and the values of those arguments.

```
    from icecream import ic

    def foo(i):
        return i + 333

    ic(foo(123))

Prints

    ic| foo(123): 456

Similarly,

    d = {'key': {1: 'one'}}
    ic(d['key'][1])

    class klass():
        attr = 'yep'
    ic(klass.attr)

Prints

    ic| d['key'][1]: 'one'
    ic| klass.attr: 'yep'
```

Just give ic() a variable or expression and you're done. Easy.

## Inspect Execution

Have you ever used print() to determine which parts of your program are executed, and in which order they're executed? For example, if you've ever added print statements to debug code like

```
    def foo():
        print(0)
        first()

        if expression:
            print(1)
            second()
        else:
            print(2)
            third()
```

then ic() helps here, too. Without arguments, ic() inspects itself and prints the calling filename, line number, and parent function.

```
    from icecream import ic

    def foo():
        ic()
        first()
        
        if expression:
            ic()
            second()
        else:
            ic()
            third()

Prints

    ic| example.py:4 in foo()
    ic| example.py:11 in foo()
```

Just call ic() and you're done. Simple.

## Return Value

ic() returns its argument(s), so ic() can easily be inserted into pre-existing code.

```
    >>> a = 6
    >>> def half(i):
    >>>     return i / 2
    >>> b = half(ic(a))
    ic| a: 6
    >>> ic(b)
    ic| b: 3
```

## Miscellaneous

ic.format(*args) is like ic() but the output is returned as a string instead of written to stderr.

```
    >>> from icecream import ic
    >>> s = 'sup'
    >>> out = ic.format(s)
    >>> print(out)
    ic| s: 'sup'
```

Additionally, ic()'s output can be entirely disabled, and later re-enabled, with ic.disable() and ic.enable() respectively.

```
    from icecream import ic

    ic(1)

    ic.disable()
    ic(2)

    ic.enable()
    ic(3)

Prints

    ic| 1: 1
    ic| 3: 3
```

ic() continues to return its arguments when disabled, of course; no existing code with ic() breaks.

![Python Programming from Scratch](https://cdn-images-1.medium.com/max/2000/1*DZw9nmUuF2BuR-YUVAg2aA.png)

**Get the book**: [Learn Python Programming from Scratch](https://leanpub.com/learning-python-programming-from-scratch)

## Import Tricks

To make ic() available in every file without needing to be imported in every file, you can install() it. For example, in a root A.py:

```
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-

    from icecream import install
    install()

    from B import foo
    foo()
```

and then in B.py, which is imported by A.py, just call ic():

```
    # -*- coding: utf-8 -*-

    def foo():
        x = 3
        ic(x)

```

install() adds ic() to the [builtins](https://docs.python.org/3.8/library/builtins.html) module, which is shared amongst all files imported by the interpreter. Similarly, ic() can later be uninstall()ed, too.

ic() can also be imported in a manner that fails gracefully if IceCream isn't installed, like in production environments (i.e. not development). To that end, this fallback import snippet may prove useful:

```
    try:
        from icecream import ic
    except ImportError:  # Graceful fallback if IceCream isn't installed.
        ic = lambda *a: None if not a else (a[0] if len(a) == 1 else a)  # noqa
```

## Configuration

ic.configureOutput(prefix, outputFunction, argToStringFunction, includeContext) can be used to adopt a custom output prefix (the default is ic| ), change the output function (default is to write to stderr), customize how arguments are serialized to strings, and/or include the ic() call's context (filename, line number, and parent function) in ic() output with arguments.

```
    >>> from icecream import ic
    >>> ic.configureOutput(prefix='hello -> ')
    >>> ic('world')
    hello -> 'world'
```

prefix can optionally be a function, too.

```
    >>> import time
    >>> from icecream import ic
    >>>  
    >>> def unixTimestamp():
    >>>     return '%i |> ' % int(time.time())
    >>>
    >>> ic.configureOutput(prefix=unixTimestamp)
    >>> ic('world')
    1519185860 |> 'world': 'world'
```

outputFunction, if provided, is called with ic()'s output instead of that output being written to stderr (the default).

```
    >>> import logging
    >>> from icecream import ic
    >>>
    >>> def warn(s):
    >>>     logging.warning(s)
    >>>
    >>> ic.configureOutput(outputFunction=warn)
    >>> ic('eep')
    WARNING:root:ic| 'eep': 'eep'
```

argToStringFunction, if provided, is called with argument values to be serialized to displayable strings. The default is PrettyPrint's [pprint.pformat()](https://docs.python.org/3/library/pprint.html#pprint.pformat), but this can be changed to, for example, handle non-standard datatypes in a custom fashion.

```
    >>> from icecream import ic
    >>> 
    >>> def toString(obj):
    >>>    if isinstance(obj, str):
    >>>        return '[!string %r with length %i!]' % (obj, len(obj))
    >>>    return repr(obj)
    >>> 
    >>> ic.configureOutput(argToStringFunction=toString)
    >>> ic(7, 'hello')
    ic| 7: 7, 'hello': [!string 'hello' with length 5!]
```

includeContext, if provided and True, adds the ic() call's filename, line number, and parent function to ic()'s output.

```
    >>> from icecream import ic
    >>> ic.configureOutput(includeContext=True)
    >>> 
    >>> def foo():
    >>>   ic('str')
    >>> foo()
    ic| example.py:12 in foo()- 'str': 'str'
```

includeContext is False by default.

## Installation

Installing IceCream with pip is easy.

```
    $ pip install icecream
```

Github: [https://github.com/gruns/icecream](https://github.com/gruns/icecream)

### Additional Resources to learn Python:

[The Python Mega Course: Build 10 Real World Applications](https://click.linksynergy.com/deeplink?id=NcMZU1aTATA&mid=39197&murl=https%3A%2F%2Fwww.udemy.com%2Fcourse%2Fthe-python-mega-course%2F)

[Learn Python Programming Masterclass](https://coursesim.com/course/learn-python-programming-masterclass/)