
Do you debug your code with print() or log()?
If so, peek will make printing debug information really easy.
And on top of that, you get some basic benchmarking functionality.
For the changelog, see www.salabim.org/peek/changelog .
Installing peek with pip is easy.
xxxxxxxxxxpip install peek-python
or when you want to upgrade,
xxxxxxxxxxpip install peek-python --upgrade
Note that peek requires the asttokens, colorama, executing. six and tomli modules, all of which will be automatically installed.
Important
peek requires Python >= 3.9
Note
The GitHub repository can be found on https://github.com/salabim/peek .
All you need is:
xxxxxxxxxximport peek
, or the more conventional, but more verbose
xxxxxxxxxxfrom peek import peek
Note that after this, peek is automatically a builtin and can thus be used in any module without
importing it there.
Have you ever printed variables or expressions to debug your program? If you've ever typed something like
xxxxxxxxxxprint(add2(1000))
or the more thorough
xxxxxxxxxxprint("add2(1000)", add2(1000)))
or:
xxxxxxxxxxprint(f"{add2(1000)=}")
then peek() is here to help. With arguments, peek() inspects itself and prints
both its own arguments and the values of those arguments.
xdef add2(i):return i + 2peek(add2(1000))
prints
xxxxxxxxxxadd2(1000)=1002
Similarly,
xxxxxxxxxxclass X:a = 3world = {"EN": "world", "NL": "wereld", "FR": "monde", "DE": "Welt"}peek(world, X.a)
prints
xxxxxxxxxxworld={"EN": "world ", "NL": "wereld", "FR": "monde", "DE": "Welt"}, X.a=3
Just give peek() a variable or expression and you're done.
And you can even add color to distinguish between peek's output lines:
xxxxxxxxxxfor number in range(10):number_divided_by_3 = number / 3if number % 3 == 0:peek(number, number_divided_by_3, color="red")else:peek(number, number_divided_by_3, color="yellow")
This will result in:

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
xxxxxxxxxxdef add2(i):print("***add2 1")result = i + 2print("***add2 2")return result
then peek() helps here, too. Without arguments, peek() inspects itself and
prints the calling line number and -if applicable- the file name and parent function.
xxxxxxxxxxdef add2(i):peek()result = i + 2peek()return resultpeek(add2(1000))
prints something like
xxxxxxxxxx#3 in add2()#5 in add2()add2(1000)=1002
peek() returns its argument(s), so peek() can easily be inserted into
pre-existing code.
xxxxxxxxxxdef add2(i):return i + 2b = peek(add2(1000))peek(b)
prints
xxxxxxxxxxadd2(1000)=1002b=1002
When you apply peek(decorator=True) as a decorator to a function or method, both the entry and exit can be tracked.
The (keyword) arguments passed will be shown and upon return, the return value.
xxxxxxxxxx@peek(decorator=True)def mul(x, y):return x * yprint(mul(5, 7))
prints
xxxxxxxxxxcalled mul(5, 7)returned 35 from mul(5, 7) in 0.000006 seconds35
It is possible to suppress the print-out of either the enter or the exit information with the show_enter and show_exit parameters, like:
xxxxxxxxxx@peek(decorator=True, show_exit=False)def mul(x, peek):return x * peekprint(mul(5, 7))
prints
xxxxxxxxxxcalled mul(5, 7)35
As an alternative to @peek(decorator=True) , it is possible (and arguably easier) to use peek.as_decorator() or peek.as_d():
xxxxxxxxxx@peek.as_decorator()def mul(x, y):return x * yprint(mul(5, 7))
It is even possible (and arguably even easier) to omit the () if no keyword arguments are required:
xxxxxxxxxx@peek.as_decorator # or @peek.as_ddef mul(x, y):return x * yprint(mul(5, 7))
If you decorate a function or method with peek(decorator=True or peek(d=True)), you will be offered the duration between entry and exit (in seconds) as a bonus.
That opens the door to simple benchmarking, like:
xxxxxxxxxximport peekimport time@peek(decorator=True, show_enter=False,show_line_number=True)def do_sort(i):n = 10 ** ix = sorted(list(range(n)))return f"{n:9d}"for i in range(8):do_sort(i)
the ouput will show the effects of the population size on the sort speed:
xxxxxxxxxx#4 ==> returned ' 1' from do_sort(0) in 0.000027 seconds#4 ==> returned ' 10' from do_sort(1) in 0.000060 seconds#4 ==> returned ' 100' from do_sort(2) in 0.000748 seconds#4 ==> returned ' 1000' from do_sort(3) in 0.001897 seconds#4 ==> returned ' 10000' from do_sort(4) in 0.002231 seconds#4 ==> returned ' 100000' from do_sort(5) in 0.024014 seconds#4 ==> returned ' 1000000' from do_sort(6) in 0.257504 seconds#4 ==> returned ' 10000000' from do_sort(7) in 1.553495 seconds
It is also possible to time any code by using peek(context_manager=True) or peek(cm=True) as a context manager, e.g.
xxxxxxxxxxwith peek(context_manager=True):time.sleep(1)
wil print something like
xxxxxxxxxxenterexit in 1.000900 seconds
You can include parameters here as well:
xxxxxxxxxxwith peek(cm=True, show_line_number=True, show_time=True):time.sleep(1)
will print somethink like:
xxxxxxxxxx#8 @ 13:20:32.605903 ==> enter#8 @ 13:20:33.609519 ==> exit in 1.003358 seconds
As an alternative to with peek.context_manager(): it is possible (and arguably easier) to use with peek.as_context_manager(): or with peek.as_cm()::
xxxxxxxxxx with peek.as_context_manager(): time.sleep(1)Finally, to help with timing code, you can request the current delta with
xxxxxxxxxxpeek.delta
or (re)set it with
xxxxxxxxxxpeek.delta = 0
So, e.g. to time a section of code:
xxxxxxxxxxpeek.delta = 0time.sleep(1)duration = peek.deltapeek(duration)
might print something like:
xxxxxxxxxxduration=1.0001721999999997
For the configuration, it is important to realize that peek is an instance of a class, which has
a number of configuration attributes:
xxxxxxxxxx------------------------------------------------------attribute alternative default------------------------------------------------------color col or c "-"color_value col_val or cv ""compact - Falsecontext_manager cm Falsecontext_separator cs " ==> "decorator d Falsedepth - 1000000delta - 0enabled - Trueend - "\n"equals_separator - "="filter f ""format fmt ""indent - 1level lvl 0line_length ll 80max_lines ml 10000000output - "stdout"prefix pr ""print_like print Falsequote_string qs Truereturn_none - Falseseparator sep ", "separator_print sepp "" "serialize - pprint.pformatshow_delta sd Falseshow_enter se Trueshow_exit sx Trueshow_line_number sln Falseshow_time st Falseshow_traceback - Falsesort_dicts - Falseto_clipboard clip Falseunderscore_numbers *) un Falseuse_color - True **)values_only vo Falsevalue_only_for_fstrings voff Falsewrap_indent - " "------------------------------------------------------*) ignored for Python 3.9**) False if run under pyodide
It is perfectly ok to set/get any of these attributes directly, like
xxxxxxxxxxpeek.prefix = "==> "print(peek.prefix)
But, it is also possible to apply configuration directly, only here, in the call to peek:
So, it is possible to say
xxxxxxxxxxpeek(12, prefix="==> ")
, which will print
xxxxxxxxxx==> 12
It is also possible to configure several attributes permanently with the configure method.
xxxxxxxxxxpeek.configure(prefix="==> ", color="blue")peek(12)
will print in blue
xxxxxxxxxx==> 12
It is arguably easier to say:
xxxxxxxxxxpeek.prefix = "==> "peek.color = "blue"peek(12)
or even
xxxxxxxxxxpeek.pr = "==> "peek.col = "blue"peek(12)
to print
xxxxxxxxxx==> 12
Yet another way to configure peek is to get a new instance of peek with peek.new() and the required configuration:
xxxxxxxxxxpeek0 = peek.new(prefix="==> ", color="blue")peek0(12)
will print
xxxxxxxxxx==> 12
Or, yet another possibility is to clone peek (optionally with modified attributes):
xxxxxxxxxxpeek1 = peek.clone(show_time=True)peek2 = peek.clone()peek2.show_time = True
After this peek1 and peek2 will behave similarly (but they are not the same!)
xxxxxxxxxxpeek('world', prefix='hello -> ')
prints
xxxxxxxxxxhello -> 'world'
prefix can be a function, too.
xxxxxxxxxximport timedef unix_timestamp():return f"{int(time.time())} "hello = "world"peek.prefix = unix_timestamppeek(hello)
prints something like
xxxxxxxxxx1613635601 hello='world'
This will allow the output to be handled by something else than the default (output being written to stdout).
The output attribute can be
a callable that accepts at least one parameter (the text to be printed)
a string or Path object that will be used as the filename
a text file that is open for writing/appending
In the example below,
xxxxxxxxxximport syspeek(1, output=print)peek(2, output=sys.stderr)with open("test", "a+") as f:peek(3, output=f)peek(4, output="")
1 will be printed to stdout
2 will be printed to stderr
3 will be appended to the file test
nothing will be printed/written
As output may be a callable, you can even use this to automatically log any peek output:
xxxxxxxxxximport logginglogging.basicConfig(level="INFO")log = logging.getLogger("demo")peek.configure(output=log.info)a = {1, 2, 3, 4, 5}peek(a)a.remove(4)peek(a)
will print to stdout:
xxxxxxxxxxINFO:demo:a={1, 2, 3, 4, 5}INFO:demo:a={1, 2, 3, 5}
Finally, you can specify the following strings:
xxxxxxxxxx"stderr" to print to stderr"stdout" to print to stdout"stdout_nocolor" to print to stdout without any colors"null" or "" to completely ignore (dummy) output"logging.debug" to use logging.debug"logging.info" to use logging.info"logging.warning" to use logging.warning"logging.error" to use logging.error"logging.critical" to use logging.critical
E.g.
xxxxxxxxxxpeek.output = "stderr"
to print to stderr.
This will allow to specify how argument values are to be serialized to displayable strings.
The default is pformat (from pprint), but this can be changed.
For example, to handle non-standard datatypes in a custom fashion.
The serialize function should accept at least one parameter.
The function may optionally accept the keyword arguments width and sort_dicts, compact, indent, underscore_numbers and depth.
xxxxxxxxxxdef add_len(obj):if hasattr(obj, "__len__"):add = f" [len={len(obj)}]"else:add = ""return f"{repr(obj)}{add}"zero_to_six = list(range(7))hello = "world"peek(7, hello, zero_to_six, serialize=add_len)
prints
xxxxxxxxxx7, hello='world' [len=5], zero_to_six=[0, 1, 2, 3, 4, 5, 6] [len=7]
If True, adds the peek() call's line number and possibly the filename and parent function to peek()'s output.
xxxxxxxxxxpeek.configure(show_line_number=True)def shout():hello="world"peek(hello)shout()
prints something like
xxxxxxxxxx#5 in shout() ==> hello='world'
If "no parent" or "n", the parent function will not be shown.
xxxxxxxxxxpeek.show_line_number = "n"def shout():hello="world"peek(hello)shout()
prints something like
xxxxxxxxxx#5 ==> hello='world'
Note that if you call peek without any arguments, the line number is always shown, regardless of the status show_line_number.
See below for an explanation of the information provided.
If True, adds the current time to peek()'s output.
xxxxxxxxxxpeek.configure(show_time=True)hello="world"peek(hello)
prints something like
xxxxxxxxxx@ 13:01:47.588125 ==> hello='world'
If True, adds the number of seconds since the start of the program to peek()'s output.
xxxxxxxxxximport timepeek.show_delta = Truefrench = "bonjour le monde"english = "hallo world"peek(english)time.sleep(1)peek(french)
prints something like
xxxxxxxxxxdelta=0.088 ==> english='hallo world'delta=1.091 ==> french='bonjour le monde'
When used as a decorator or context manager, by default, peek ouputs a line when the decorated the function is called or the context manager is entered.
With show_enter=False this line can be suppressed.
When used as a decorator or context manager, by default, peek ouputs a line when the decorated the function returned or the context manager is exited.
With show_exit=False this line can be suppressed.
When show_traceback is True, the ordinary output of peek() will be followed by a printout of the traceback, similar to an error traceback.
xxxxxxxxxxdef x():peek(show_traceback=True)x()x()
prints something like
xxxxxxxxxx#4 in x()Traceback (most recent call last)File "c:\Users\Ruud\Dropbox (Personal)\Apps\Python Ruud\peek\x.py", line 6, in <module>x()File "c:\Users\Ruud\Dropbox (Personal)\Apps\Python Ruud\peek\x.py", line 4, in xpeek()#4 in x()Traceback (most recent call last)File "c:\Users\Ruud\Dropbox (Personal)\Apps\Python Ruud\peek\x.py", line 7, in <module>x()File "c:\Users\Ruud\Dropbox (Personal)\Apps\Python Ruud\peek\x.py", line 4, in xpeek()
The show_traceback functionality is also available when peek is used as a decorator or context manager.
This attribute is used to specify the line length (for wrapping). The default is 80. Peek tries to keep all output on one line, but if it can't it will wrap:
xxxxxxxxxxd = dict(a1=1,a2=dict(a=1,b=1,c=3),a3=list(range(10)))peek(d)peek(d, line_length=160)
prints
xxxxxxxxxxd={'a1': 1,'a2': {'a': 1, 'b': 1, 'c': 3},'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}d={'a1': 1, 'a2': {'a': 1, 'b': 1, 'c': 3}, 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
If line_length is set to 0 or 'terminal_width', peek will use the width of the current terminal as line length.
Note that not all terminals correctly return the actual width.
(The terminal size is determined by calling shutil.get_terminal_size().columns)
This attribute is used to specify the maximum number of lines to print for one peek call. The default is 1000000, so no limitation.
If there are more than max_lines to be printed, only max_lines will be printed, followed by a line [abbreviated].
So,
xxxxxxxxxxpeek([list(range(i, i + 10)) for i in range(10, 100, 10)])
prints
xxxxxxxxxx[list(range(i, i + 10)) for i in range(10, 100, 10)]=[[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],[50, 51, 52, 53, 54, 55, 56, 57, 58, 59],[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],[70, 71, 72, 73, 74, 75, 76, 77, 78, 79],[80, 81, 82, 83, 84, 85, 86, 87, 88, 89],[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]]
But
xxxxxxxxxxpeek.max_lines = 5peek([list(range(i, i + 10)) for i in range(10, 100, 10)])
prints
xxxxxxxxxxlist(range(i, i + 10)) for i in range(10, 100, 10)]=[[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],[abbreviated]
This feature can be useful on platforms, where printing many lines is time consuming, like on xlwings lite.
The color attribute is used to specify the color of the output.
There's a choice of "white", "black", "red", "green", "blue", "cyan", "magenta", "yellow", " dark_white", "dark_black", "dark_red", "dark_green", "dark_blue", "dark_cyan", "dark_magenta" and "dark_yellow":

To set the color to 'nothing'", "use "-".
On top of that, color_value may be used to specify the value part of an output item. By specifying color_value as "" (the default), the value part will be displayed with the same color as the rest of the output.
For instance:
xxxxxxxxxxitem1 = "value1"item2 = "value2"peek.color="yellow"peek(item1, item2)peek(item1, item2, color_value="green")peek(item1, item2, color="red")peek(item1, item2, color="red", color_value="green")
will result in:

Alternatively, the color/Color_value attribute can be specified as an integer, where
0 - (reset)
1 white
2 black
3 red
4 blue
5 green
6 yellow
7 magenta
8 cyan (tealblue)
Note that the color number corresponds to the number of letters in the name (apart from white and black).
A negative color/color_value number represents the dark version, e.g. peek((a:=123), c=3, cv=-5) will print (a:=3)= in red and 123 in dark_green.
Of course, color and color_value may be specified in a peek.toml file, to make all peek output in a specified color.
Bonus feature
peek offers direct access to ANSI color escape sequences with peek.ANSI.black, peek.ANSI.white, peek.ANSI.red, peek.ANSI.green, peek.ANSI.blue, peek.ANSI.cyan, peek.ANSI.magenta, peek.ANSI.yellow, peek.ANSI.light_black, peek.ANSI.light_white, peek.ANSI.light_red, peek.ANSI.light_green, peek.ANSI.light_blue, peek.ANSI.light_cyan, peek.ANSI.light_magenta, peek.ANSI.light_yellow and peek.ANSI.reset.
E.g.
xxxxxxxxxxpeek(repr(peek.ANSI.red))
will show
xxxxxxxxxxrepr(peek.ANSI.red)='\x1b[1;31m'
Colors can be ignored completely by using peek.use_color = False.
So,
xxxxxxxxxxpeek(hello, color="red")peek.use_color = Falsepeek(hello, color="red")
will print hello=world once in red and once without color.
Of course, use_color can be specified in a peek.toml file.
This attribute is used to specify the compact parameter for pformat (see the pprint documentation
for details). compact is False by default.
xxxxxxxxxxa = 9 * ["0123456789"]peek.line_length = 80peek(a)peek(a, compact=True)
prints
xxxxxxxxxxa=['0123456789','0123456789','0123456789','0123456789','0123456789','0123456789','0123456789','0123456789','0123456789']a=['0123456789', '0123456789', '0123456789', '0123456789', '0123456789','0123456789', '0123456789', '0123456789', '0123456789']
This attribute is used to specify the indent parameter for pformat (see the pprint documentation
for details). indent is 1 by default.
xxxxxxxxxxs = "01234567890012345678900123456789001234567890"peek.line_length = 80peek( [s, [s]])peek( [s, [s]], indent=4)
prints
xxxxxxxxxx[s, [s]]=['01234567890012345678900123456789001234567890',['01234567890012345678900123456789001234567890']][s, [s]]=[ '01234567890012345678900123456789001234567890',['01234567890012345678900123456789001234567890']]
This attribute is used to specify the depth parameter for pformat (see the pprint documentation
for details). depth is 1000000 by default.
xxxxxxxxxxs = "01234567890012345678900123456789001234567890"peek([s,[s,[s,[s,s]]]])peek([s,[s,[s,[s,s]]]], depth=3)
prints
xxxxxxxxxx[s,[s,[s,[s,s]]]]=['01234567890012345678900123456789001234567890',['01234567890012345678900123456789001234567890',['01234567890012345678900123456789001234567890',['01234567890012345678900123456789001234567890','01234567890012345678900123456789001234567890']]]][s,[s,[s,[s,s]]]]=['01234567890012345678900123456789001234567890',['01234567890012345678900123456789001234567890',['01234567890012345678900123456789001234567890', [...]]]]
This specifies the indent string if the output does not fit in the line_length (has to be wrapped). Rather than a string, wrap_indent can be also be an integer, in which case the wrap_indent will be that amount of blanks. The default is 4 blanks.
E.g.
xxxxxxxxxxd = dict(a1=1,a2=dict(a=1,b=1,c=3),a3=list(range(10)))peek.line_length = 80peek(d, wrap_indent=" ")peek(d, wrap_indent="....")peek(d, wrap_indent=2)
prints
xxxxxxxxxxd={'a1': 1,'a2': {'a': 1, 'b': 1, 'c': 3},'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}d=....{'a1': 1,.... 'a2': {'a': 1, 'b': 1, 'c': 3},.... 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}d={'a1': 1,'a2': {'a': 1, 'b': 1, 'c': 3},'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
Can be used to disable the output:
xxxxxxxxxxpeek.enabled = Falses = 'the world is 'peek(s + 'perfect.')peek.enabled = Truepeek(s + 'on fire.')
prints
xxxxxxxxxxs + 'on fire.'='the world is on fire.'
and nothing about a perfect world.
By default, peek does not sort dicts (printed by pprint). However, it is possible to get the default pprint behaviour (i.e. sorting dicts) with the sorted_dicts attribute:
xxxxxxxxxxworld = {"EN": "world", "NL": "wereld", "FR": "monde", "DE": "Welt"}peek(world))peek(world, sort_dicts=False)peek(world, sort_dicts=True)
prints
xxxxxxxxxxworld={'EN': 'world', 'NL': 'wereld', 'FR': 'monde', 'DE': 'Welt'}world={'EN': 'world', 'NL': 'wereld', 'FR': 'monde', 'DE': 'Welt'}world={'DE': 'Welt', 'EN': 'world', 'FR': 'monde', 'NL': 'wereld'}
By default, peek does not add underscores in big numbers (printed by pprint). However, it is possible to get the default pprint behaviour with the underscore_numbers attribute:
xxxxxxxxxxnumbers = dict(one= 1, thousand= 1000, million=1000000, x1234567890= 1234567890)peek(numbers)peek(numbers, underscore_numbers=True)peek(numbers, un=False)
prints
xxxxxxxxxxnumbers={'one': 1, 'thousand': 1000, 'million': 1000000, 'x1234567890': 1234567890}numbers={'one': 1, 'thousand': 1_000, 'million': 1_000_000, 'x1234567890': 1_234_567_890}numbers={'one': 1, 'thousand': 1000, 'million': 1000000, 'x1234567890': 1234567890}
Note
underscore_numbers is ignored under Python 3.9
By default, pairs (on one line) are separated by ", ".
It is possible to change this with the attribute separator:
xxxxxxxxxxa = "abcd"b = 1c = 1000d = list("peek")peek(a, (b, c), d)peek(a, (b, c), d, separator=" | ")
prints
xxxxxxxxxxa='abcd', (b,c)=(1, 1000), d=['p', 'e', 'e', 'k']a='abcd' | (b,c)=(1, 1000) | d=['p', 'e', 'e', 'k']
By default the line_number, time and/or delta are followed by ==>.
It is possible to change this with the attribute context_separator:
xxxxxxxxxxa = "abcd"peek(a,show_time=True)peek(a, show_time=True, context_separator = ' \u279c ')
prints:
xxxxxxxxxx@ 09:05:38.693840 ==> a='abcd'@ 09:05:38.707914 ➜ a='abcd'
By default name of a variable and its value are separated by =.
It is possible to change this with the attribute equals_separator:
xxxxxxxxxxa = "abcd"peek(a)peek(a, equals_separator = ' == ")
prints:
xxxxxxxxxxa='abcd'a == 'abcd'
If True (the default setting) strings will be displayed with surrounding quotes (like repr). If False, string will be displayed without surrounding quotes (like str). E.g.
xxxxxxxxxxtest='test'peek('==>', test)peek('==>', test, quote_string=False)
This will print:
xxxxxxxxxx'==>', test='test'==>, test=test
Note
This setting does not influence how strings are displayed within other data structures, like dicts and lists.
With the format attribute, it is possible to apply a format specifier to each of the values to be printed, like
xxxxxxxxxxtest_float = 1.3peek(test_float, format="06.3f")
This will print
xxxxxxxxxxtest_float=01.300
The format should be like the Python format specifiers, with or without the : prefix, like "6.3f", ">10", "06d", :6.3d.
It is also possible to use the ! format specifier: "!r", "!r:>10".
If format is the null string ("") (the default), this functionality is skipped completely.
It is also possible to use a list (or tuple) of format specifiers, which are tried in succession. If they all fail, the 'normal' serializer will be used.
xxxxxxxxxxtest_float = 1.3test_integer=10test_string = "test"test_dict=dict(one=1, two=2)peek(test_float, test_integer, test_string, test_dict, format=["04d", "06.3f", ">10"])
will result in
xxxxxxxxxxtest_float=01.300, test_integer=0010, test_string= test, test_dict={'one': 1, 'two': 2}
Of course, format may be put in a peek.toml file.
If False (the default), both the left-hand side (if possible) and the value will be printed. If True, the left hand side will be suppressed:
xxxxxxxxxxhello = "world"peek(hello, 2 * hello)peek(hello, 2 * hello, values_only=True)
prints
xxxxxxxxxxhello='world', 2 * hello='worldworld''world', 'worldworld'
If False (the default), both the original f-string and the value will be printed for f-strings. If True, the left_hand side will be suppressed in case of an f-string:
xxxxxxxxxxx = 12.3peek.quote_string = Falsepeek(f"{x=:0.3e}")peek.values_only_for_fstrings = Truepeek(f"{x=:0.3e}")
prints
xxxxxxxxxxf"{x=:0.3e}"=x=1.230e+01x=1.230e+01
Note that if values_only is True, f-string will be suppressed, regardless of values_only_for_fstrings.
The end attribute works like the end parameter of print. By default, end is "\n".
This can be useful to have several peek outputs on one line, like:
xxxxxxxxxxfor i in range(5):peek(i*i, end=' ')peek('')
Maybe more useful is to show the output change on the same line, e.g. a status.
xxxxxxxxxximport timefor i in range(50):peek.print(f"time {time.time():10.2f}",end="", prefix="\r")time.sleep(0.1)peek.print()
Note
The end parameter will not be only applied when output is "logging.debug", "logging.info", "logging.warning", "logging.error" or "logging.critical".
Note
Under Pythonista \r has to be at the start of a peek.print output. Otherwise, it does not work.
Normally, peek()returns the values passed directly, which is usually fine. However, when used in a notebook
or REPL, that value will be shown, and that can be annoying. Therefore, if return_noneis True, peek()will
return None and thus not show anything.
xxxxxxxxxxa = 3b = 4print(peek(a, b))peek.return_none = Trueprint(peek(a, b))
prints
xxxxxxxxxxa=3, b=4(3, 4)a=3, b=4None
The delta attribute can be used to (re)set the current delta, e.g.
xxxxxxxxxxpeek.delta = 0print(peek.delta)
prints a value that id slightly more than 0.
When print_like (or print) is False, peek will work by expanding the arguments to description/serialized value pairs. But, when print_like is True, peek becomes a kind of supercharged print:
xxxxxxxxxxpeek.print_like = Truepeek(12, f"{min(1, 2)=}", list(range(4), color="yellow")
will print
xxxxxxxxxx12 min(1, 2)=1 [0, 1, 2, 3]
in yellow, but if peek.enabled is False, nothing will be printed.
You can also use peek.print (see below).
Tip
Of course, print_like can be put in a peek.toml file.
The method peek.print allows peek to be used as alternative to print. Note that peek.print applies the color, context_separator, enabled, end, filter and output, separator_print, show_delta and show_time. It is also possible to redirect the output to a string with as_str.
So,
xxxxxxxxxxpeek.filter = "level==1"peek.print(f"{max(1, 2)=}", color="blue") # default level is 0, so this will be suppressedpeek.print(f"{min(1, 2)=}", color="red", level=1)
will print
xxxxxxxxxxmin(1, 2)=1
in red, but only if peek.enabled is True (which is the default).
In order to behave similar to print, peek has an extra attribute, separator_print (alias: sepp). This attribute (default " ") will be used when peek.printing.
When calling peek.print, sep may be used instead. So
xxxxxxxxxxpeek.sepp = "|"peek.print("test")
Has the same effect as
xxxxxxxxxxpeek.print("test", sep="|")
and
xxxxxxxxxxpeek.print("test", sepp="|")
but not the same as
xxxxxxxxxxpeek.sep = "|" # sets the 'normal' peek separator
Note
peek.print does not obey the line length and will always return None (unless as_str is True).
It is possible to get the names and values of all local or global variables.
To do that, just put locals or globals in the call to peek, e.g.:
xxxxxxxxxxdef my_func():a = 10b = a * apeek(locals)my_func()
will print all local variables, apart from those starting with __, so:
xxxxxxxxxxa=10, b=100
Likewise,
xxxxxxxxxxpeek(globals)
will print all global variables, apart from those starting with __
Important
You should not add parentheses after locals or globals for peek to work properly!
peek(*args, as_str=True) is like peek(*args) but the output is returned as a string instead
of written to output.
xxxxxxxxxxhello = "world"s = peek(hello, as_str=True)print(s, end="")
prints
xxxxxxxxxxhello='world'
Note that if enabled=False, the call will return the null string ("").
By default, a string will contain embedded ANSI color escape strings if either color or color_value specifies a color. By setting use_color to False, these escape sequences will be suppressed.
xxxxxxxxxxhello = "world"s = peek(hello, color="red", color_value="green", as_str=True)print(repr(s))peek.use_color = Falses = peek(hello, color="red", color_value="green", as_str=True)print(repr(s))
prints
xxxxxxxxxx'\x1b[1;31mhello=\x1b[1;32mworld\x1b[1;31m\x1b[0m\n''hello=world\n'
xxxxxxxxxxpeek1 = peek.fork(show_delta=True)peek(1)peek1(2)peek.enabled = Falsepeek(3)peek1(4)peek1.enabled = Truepeek(5)peek1(6)print(peek1.enabled)
prints
xxxxxxxxxx1delta=0.011826 ==> 25delta=0.044893 ==> 6True
Of course peek() continues to return its arguments when disabled.
It is also possible to suppress output with the provided attribute (see above).
It is possible to define a filter function that determines whether peek output should be suppressed. By default, the filter is defined as "" denoting no filter.
Suppose we add a (float) level to a peek statement. By default, this level is 0. E.g.:
xxxxxxxxxxpeek("critical", level=0)peek("important", level=1)peek("warning", level=2)
With peek.filter = "level <= 1" the program makes sure that level=2 is not displayed at all.
It is possible to use more than one attribute, like
xxxxxxxxxxpeek.filter = "color == 'blue' and delta > 5"
As an alternative to enabled we can also say
xxxxxxxxxxpeek.filter = "False"
It is possible to copy a value to the clipboard. There are two ways:
With the optional keyword argument, to_clipboard:
If to_clipboard==False (the default), nothing is copied to the clipboard.
If to_clipboard==True, the value of the the last parameter will be copied to the clipboard. The output itself is as usual.
Examples:
xxxxxxxxxxpart1 = 200extra = "extra"peek(part1, extra, to_clipboard=True)# will print part1=200, extra='extra' and copy `extra` to the clipboardpeek(200, to_clipboard=True)\# will print 200 and copy 200 to the clipboardpeek(to_clipboard=True)# will print #5 (or similar) and empty the clipboard
Note that to_clipboard is a peek attribute.
If as_str==True, to_clipboard is ignored.
Just use peek.to_clipboard to copy any value to the clipboard. So,
xxxxxxxxxxpart1 = 1234peek.to_clipboard(part1)
will copy 1234 to the clipboard and write copied to clipboard: 1234 to the console.
If the confirmation message is not wanted, just add confirm=False, like
xxxxxxxxxxpeek.to_clipboard(part1, confirm=False)
Implementation detail: the clipboard functionality uses pyperclip, apart from under Pythonista, where the builtin clipboard module is used. The pyperclip module is not installed automatically when peek-python is installed. So, it might be necessary to do
xxxxxxxxxxpip install pyperclip
This functionality is particularly useful for entering an answer of an Advent of Code solution to the site.
With peek.stop or peek.stop() a program will be stopped (by raising a SystemExit exception), provided peek.enabled is True.
If peek.enabled is False, the program will just continue.
For example:
xxxxxxxxxxpeek.enabled = Falsepeek(12)peek.stoppeek.enabled = Truepeek(13)peek.stoppeek(14)
will print:
xxxxxxxxxx13stopped by peek.stop
and then stop execution.
Note
Under Pythonista, sys.exit() will be called.
Under pyodide/xlwings lite, an Exception exception will be raised.
When show_line_number is True or peek() is used without any parameters, the output will contain the line number like:
xxxxxxxxxx#3 ==> a='abcd'
If the line resides in another file than the main file, the filename (without the path) will be shown as well:
xxxxxxxxxx#30[foo.py] ==> foo='Foo'
And finally when used in a function or method, that function/method will be shown as well:
xxxxxxxxxx#456[foo.py] in square_root ==> x=123
The parent function can be suppressed by setting show_line_number or sln to "n" or "no parent".
It can be useful to configure peek at import time. This can be done by providing a peek.toml file which
can contain any attribute configuration overriding the standard settings.
E.g. if there is a peek.toml file with the following contents
xxxxxxxxxxoutpout = "stderr"show_time = truell = 160quote_string = false
in the same folder as the application, this program:
xxxxxxxxxxhello = "world"peek(hello)
will print something like this to stderr (rather than stdout):
xxxxxxxxxx@ 14:53:41.392190 ==> hello=world
At import time current directory will be searched for peek.toml and if not found, one level up, etc. until the root directory is reached.
Please observe that toml values are slightly different from their Python equivalents:
xxxxxxxxxx-----------------------------------Python toml-----------------------------------True trueFalse falsestrings preferably double quoted-----------------------------------
Note that not-specified attributes will remain the default settings.
Just for your information, the core developer of peek uses a peek.toml file with the contents:
xxxxxxxxxxline_length = 160color = "yellow"quote_string = false
On top of this toml functionality, default values may be also overridden by environment variables.
They should be specified as peek.<variable>, like
xxxxxxxxxx------------------------------environment variable value------------------------------peek.line_length 160peek.color "yellow"peek.show_time true------------------------------
The value should follow the same rules as in a toml-file.
Note that the environment variables are read before reading a peek.toml file.
This functionality is particularly useful for using peek in xlwings lite, as there's no local file system to store a toml file, there.
Normally, only the peek object is used.
It can be useful to have multiple instances, e.g. when some of the debugging has to be done with context information and others requires an alternative prefix.
There are several ways to obtain a new instance of peek:
by using peek.new()
With this a new peek object is created with the default attributes
by using peek.new(ignore_toml=True)
With this a new peekobject is created with the default attributes. Any peek.toml files are ignored.
by using peek.fork()
With this a new peek object is created with the same attributes as the object it is created ('the parent') from. Note that any non set attributes are copied (propagated) from the parent.
by using peek.clone(), which copies all attributes from peek()
With this a new peek object is created with the same attributes as the object it is created ('the parent') from. Note that the attributes are not propagated from the parent, in this case.
with peek() used as a context manager
In either case, attributes can be added to override the default ones.
xxxxxxxxxxpeek_with_line_number = peek.fork(show_line_number=True)peek_with_new_prefix = peek.new(prefix="==> ")peek_with_new_prefix_and_time = peek_with_new_prefix.clone(show_time=True)hello="world"peek_with_line_number(hello)peek_with_new_prefix(hello)peek_with_new_prefix_and_time(hello)peek.equals_separator = " == " # this affects only the forked objectspeek_with_line_number(hello)peek_with_new_prefix(hello)peek_with_new_prefix_and_time(hello)with peek(prefix="peek_cm ") as peek_cm:peek_cm(hello)peek(hello)
prints something like
xxxxxxxxxx#28 ==> hello='world'==> hello='world'==> @ 09:55:52.122818 ==> hello='world'#32 ==> hello == 'world'==> hello='world'==> @ 09:55:52.125928 ==> hello='world'peek_cm enterpeek_cm hello == 'world'hello == 'world'peek_cm exit in 0.001843 seconds
With peek.new(ignore_toml=True) an instance of peek without having applied any toml configuration file will be returned. That can be useful when guaranteeing the same output in several setups.
Suppose we have a peek.toml file in the current directory with the contents
xxxxxxxxxx{prefix="==>"}
Then
xxxxxxxxxxpeek_post_toml = peek.new()peek_ignore_toml = peek.new(ignore_toml=True)hello = "world"peek(hello)peek_post_toml(hello)peek_ignore_toml(hello)
prints
xxxxxxxxxx==>hello='world'==>hello='world'hello='world'
On GitHub is a file test_peek.py that tests (and thus also demonstrates) most of the functionality
of peek.
It is very useful to have a look at the tests to see the features (some may be not covered (yet) in this readme).
Peek may be used in a REPL, but with limited functionality:
all arguments are just presented as such, i.e. no left-hand side, e.g.
xxxxxxxxxx>> hello = "world">>> peek(hello, hello * 2)'hello', 'hellohello'('hello', 'hellohello')
line numbers are never shown
Note
Under Python >=3.13 most of the normal peek functionality is available in the REPL. A reason to upgrade!
It is not possible to use peek:
from a frozen application (e.g. packaged with PyInstaller)
when the underlying source code has changed during execution
The peek package is inspired by the IceCream package, but is a nearly complete rewrite. See https://github.com/gruns/icecream
Many thanks to the author Ansgar Grunseid / grunseid.com / grunseid@gmail.com .
The peek package is a rebrand of the ycecream package, with many enhancements.
The peek module was originally a fork of IceCream, but has many differences:
xxxxxxxxxx-----------------------------------------------------------------------------------------characteristic peek IceCream-----------------------------------------------------------------------------------------default name peek icimport method import peek from icecream import icnumber of files 1 severalusable without installation yes nocan be used as a decorator yes nocan be used as a context manager yes nocan show traceback yes nocan be used like print w/extras yes (with peek.print) noallows non linefeed printing yes (via end parameter) requires patchingPEP8 (Pythonic) API yes noformat specification optional nosorts dicts no by default, optional yessupports compact, indent,and underscore_numbersparameters of pprint yes nouse from a REPL limited functionality noexternal configuration via toml file nolevel control yes noobserves line_length correctly yes nobenchmarking functionality yes nocan peek locals or globals yes nosuppress f-strings at left hand optional noindentation 4 blanks (overridable) length of prefixforking and cloning yes nohandling of source problems peeks only the value warning issuedtest script pytest unittestcolorize *) yes, off by default yes, on by default-----------------------------------------------------------------------------------------*) peek allows selection of colors, whereas IceCream does coloring based on contents.
You can contact Ruud van der Ham, the core developer, via ruud@salabim.org .