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.
ic() is like print(), but better:
- It prints both expressions/variable names and their values.
- It's 60% faster to type.
- Data structures are pretty printed.
- Output is syntax highlighted.
- It optionally includes program context: filename, line number, and parent function.
IceCream is well tested, permissively licensed, and supports Python 2, Python 3, PyPy2, and PyPy3.
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() will put a smile on your face. 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.
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.
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: 3ic.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.
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 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) # noqaic.configureOutput(prefix, outputFunction, argToStringFunction, includeContext, contextAbsPath) controls ic()'s output.
prefix, if provided, adopts a custom output prefix. prefix can be a string, like
>>> from icecream import ic >>> ic.configureOutput(prefix='hello -> ') >>> ic('world') hello -> 'world'or a function.
>>> import time >>> from icecream import ic >>> >>> def unixTimestamp(): >>> return '%i |> ' % int(time.time()) >>> >>> ic.configureOutput(prefix=unixTimestamp) >>> ic('world') 1519185860 |> 'world': 'world'prefix's default value is ic| .
outputFunction, if provided, is called once for every ic() call with ic()'s output, as a string, instead of that string 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(), 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!]The default argToStringFunction is icecream.argumentToString, and has methods to register and unregister functions to be dispatched for specific classes using functools.singledispatch. It also has a registry property to view registered functions.
>>> from icecream import ic, argumentToString >>> import numpy as np >>> >>> # Register a function to summarize numpy array >>> @argumentToString.register(np.ndarray) >>> def _(obj): >>> return f"ndarray, shape={obj.shape}, dtype={obj.dtype}" >>> >>> x = np.zeros((1, 2)) >>> ic(x) ic| x: ndarray, shape=(1, 2), dtype=float64 >>> >>> # View registered functions >>> argumentToString.registry mappingproxy({object: <function icecream.icecream.argumentToString(obj)>, numpy.ndarray: <function __main__._(obj)>}) >>> >>> # Unregister a function and fallback to the default behavior >>> argumentToString.unregister(np.ndarray) >>> ic(x) ic| x: array([[0., 0.]])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(): >>> i = 3 >>> ic(i) >>> foo() ic| example.py:12 in foo()- i: 3includeContext is False by default.
contextAbsPath, if provided and True, outputs absolute filepaths, like /path/to/foo.py, over just filenames, like foo.py, when ic() is called with includeContext == True. This is useful when debugging multiple files that share the same filename(s). Moreover, some editors, like VSCode, turn absolute filepaths into clickable links that open the file where ic() was called.
>>> from icecream import ic >>> ic.configureOutput(includeContext=True, contextAbsPath=True) >>> >>> i = 3 >>> >>> def foo(): >>> ic(i) >>> foo() ic| /absolute/path/to/example.py:12 in foo()- i: 3 >>> >>> ic.configureOutput(includeContext=True, contextAbsPath=False) >>> >>> def foo(): >>> ic(i) >>> foo() ic| example.py:18 in foo()- i: 3contextAbsPath is False by default.
Installing IceCream with pip is easy.
$ pip install icecream ic() uses executing by @alexmojaki to reliably locate ic() calls in Python source. It's magic.
Delicious IceCream should be enjoyed in every language.
- Dart: icecream
- Rust: icecream-rs
- Node.js: node-icecream
- C++: IceCream-Cpp
- C99: icecream-c
- PHP: icecream-php
- Go: icecream-go
- Ruby: Ricecream
- Java: icecream-java
- R: icecream
- Lua: icecream-lua
- Clojure(Script): icecream-cljc
- Bash: IceCream-Bash
If you'd like a similar ic() function in your favorite language, please open a pull request! IceCream's goal is to sweeten print debugging with a handy-dandy ic() function in every language.