peek-python 1.8.8__tar.gz → 24.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1386 +1,1308 @@
1
- <img src="https://www.salabim.org/peek/peek_logo1.png">
2
-
3
- # Introduction
4
-
5
- Do you use `print()` or `log()` to debug your code?
6
- If so, peek will make printing debug information really easy.
7
- And on top of that, you get some basic benchmarking functionality.
8
-
9
- # Table of contents
10
-
11
- * [Installation](#installation)
12
-
13
- * [Importing peek](#importing-peek)
14
-
15
- * [Inspect variables and expressions](#inspect-variables-and-expressions)
16
-
17
- * [Inspect execution](#inspect-execution)
18
-
19
- * [Return value](#return-value)
20
-
21
- * [Debug entry and exit of function calls](#debug-entry-and-exit-of-function-calls)
22
-
23
- * [Benchmarking with peek](#benchmarking-with-peek)
24
-
25
- * [Configuration](#configuration)
26
-
27
- * [Return a string instead of sending to output](#return-a-string-instead-of-sending-to-output)
28
-
29
- * [Disabling peek's output](#disabling-peeks-output)
30
-
31
- * [Using level to control peek output](#Using-level-to-control-peek-output)
32
-
33
- * [Using color to control peek output](#Using-color-to-control-peek-output)
34
-
35
- * [Copying to the clipboard](#Copying-to-the-clipboard)
36
-
37
- * [Speeding up disabled peek](#speeding-up-disabled-peek)
38
-
39
- * [Using peek as a substitute for `assert`](#using-peek-as-a-substitute-for-assert)
40
-
41
- * [Interpreting the line number information](#interpreting-the-line-number-information)
42
-
43
- * [Configuring at import time](#configuring-at-import-time)
44
-
45
- * [Working with multiple instances of peek](#working-with-multiple-instances-of-peek)
46
-
47
- * [Test script](#test-script)
48
-
49
- * [Using peek in a REPL](#using-peek-in-a-repl)
50
-
51
- * [Alternative to `peek`](#alternative-to-peek)
52
-
53
- * [Alternative installation](#alternative-installation)
54
-
55
- * [Limitations](#limitations)
56
-
57
- * [Implementation details](#implementation-details)
58
-
59
- * [Acknowledgement](#acknowledgement)
60
-
61
- * [Changelog](#changelog)
62
-
63
- * [Differences with IceCream](#differences-with-icecream)
64
-
65
-
66
- # Installation
67
-
68
- Installing peek with pip is easy.
69
- ```
70
- pip install peek-python
71
- ```
72
- or when you want to upgrade,
73
- ```
74
- pip install peek-python --upgrade
75
- ```
76
-
77
- Alternatively, peek.py can be just copied into you current work directory from GitHub (https://github.com/salabim/peek).
78
-
79
- Note that peek requires the `asttokens`, `colorama`, `executing`. `six`, tomli and pyperclip` modules, all of which will be automatically installed.
80
-
81
- # Importing peek
82
-
83
- All you need is:
84
-
85
- ```
86
- import peek
87
- ```
88
-
89
- , or the more conventional, but more verbose
90
-
91
- ```
92
- from peek import peek
93
- ```
94
-
95
- Note that after this, `peek` is automatically a builtin and can thus be used in any module without
96
- importing it there.
97
-
98
- # Inspect variables and expressions
99
-
100
- Have you ever printed variables or expressions to debug your program? If you've
101
- ever typed something like
102
-
103
- ```
104
- print(add2(1000))
105
- ```
106
-
107
- or the more thorough
108
-
109
- ```
110
- print("add2(1000)", add2(1000)))
111
- ```
112
- or:
113
- ```
114
- print(f"{add2(1000)=}")
115
- ```
116
-
117
- then `peek()` is here to help. With arguments, `peek()` inspects itself and prints
118
- both its own arguments and the values of those arguments.
119
-
120
- ```
121
- def add2(i):
122
- return i + 2
123
-
124
- peek(add2(1000))
125
- ```
126
-
127
- prints
128
- ```
129
- add2(1000)=1002
130
- ```
131
-
132
- Similarly,
133
-
134
- ```
135
- class X:
136
- a = 3
137
- world = {"EN": "world", "NL": "wereld", "FR": "monde", "DE": "Welt"}
138
-
139
- peek(world, X.a)
140
- ```
141
-
142
- prints
143
- ```
144
- world={"EN": "world ", "NL": "wereld", "FR": "monde", "DE": "Welt"}, X.a: 3
145
- ```
146
- Just give `peek()` a variable or expression and you're done. Easy, or what?
147
-
148
-
149
- # Inspect execution
150
-
151
- Have you ever used `print()` to determine which parts of your program are
152
- executed, and in which order they're executed? For example, if you've ever added
153
- print statements to debug code like
154
-
155
- ```
156
- def add2(i):
157
- print("***add2 1")
158
- result = i + 2
159
- print("***add2 2")
160
- return result
161
- ```
162
- then `peek()` helps here, too. Without arguments, `peek()` inspects itself and
163
- prints the calling line number and -if applicable- the file name and parent function.
164
-
165
- ```
166
- def add2(i):
167
- peek()
168
- result = i + 2
169
- peek()
170
- return result
171
- peek(add2(1000))
172
- ```
173
-
174
- prints something like
175
- ```
176
- #3 in add2()
177
- #5 in add2()
178
- add2(1000)=1002
179
- ```
180
- Just call `peek()` and you're done. Isn't that easy?
181
-
182
-
183
- # Return Value
184
-
185
- `peek()` returns its argument(s), so `peek()` can easily be inserted into
186
- pre-existing code.
187
-
188
- ```
189
- def add2(i):
190
- return i + 2
191
- b = peek(add2(1000))
192
- peek(b)
193
- ```
194
- prints
195
- ```
196
- add2(1000)=1002
197
- b=1002
198
- ```
199
- # Debug entry and exit of function calls
200
-
201
- When you apply `peek()` as a decorator to a function or method, both the entry and exit can be tracked.
202
- The (keyword) arguments passed will be shown and upon return, the return value.
203
-
204
- ```
205
- @peek()
206
- def mul(x, peek):
207
- return x * peek
208
-
209
- print(mul(5, 7))
210
- ```
211
- prints
212
- ```
213
- called mul(5, 7)
214
- returned 35 from mul(5, 7) in 0.000006 seconds
215
- 35
216
- ```
217
- It is possible to suppress the print-out of either the enter or the exit information with
218
- the show_enter and show_exit parameters, like:
219
-
220
- ```
221
- @peek(show_exit=False)
222
- def mul(x, peek):
223
- return x * peek
224
-
225
- print(mul(5, 7))
226
- ```
227
- prints
228
- ```
229
- called mul(5, 7)
230
- 35
231
- ```
232
-
233
- # Benchmarking with peek
234
-
235
- If you decorate a function or method with peek(), you will be offered the duration between entry and exit (in seconds) as a bonus.
236
-
237
- That opens the door to simple benchmarking, like:
238
- ```
239
- import time
240
-
241
- @peek(show_enter=False,show_line_number=True)
242
- def do_sort(i):
243
- n = 10 ** i
244
- x = sorted(list(range(n)))
245
- return f"{n:9d}"
246
-
247
- for i in range(8):
248
- do_sort(i)
249
- ```
250
- the ouput will show the effects of the population size on the sort speed:
251
- ```
252
- #5 ==> returned ' 1' from do_sort(0) in 0.000027 seconds
253
- #5 ==> returned ' 10' from do_sort(1) in 0.000060 seconds
254
- #5 ==> returned ' 100' from do_sort(2) in 0.000748 seconds
255
- #5 ==> returned ' 1000' from do_sort(3) in 0.001897 seconds
256
- #5 ==> returned ' 10000' from do_sort(4) in 0.002231 seconds
257
- #5 ==> returned ' 100000' from do_sort(5) in 0.024014 seconds
258
- #5 ==> returned ' 1000000' from do_sort(6) in 0.257504 seconds
259
- #5 ==> returned ' 10000000' from do_sort(7) in 1.553495 seconds
260
- ```
261
-
262
- It is also possible to time any code by using peek() as a context manager, e.g.
263
- ```
264
- with peek():
265
- time.sleep(1)
266
- ```
267
- wil print something like
268
- ```
269
- enter
270
- exit in 1.000900 seconds
271
- ```
272
- You can include parameters here as well:
273
- ```
274
- with peek(show_line_number=True, show_time=True):
275
- time.sleep(1)
276
- ```
277
- will print somethink like:
278
- ```
279
- #8 @ 13:20:32.605903 ==> enter
280
- #8 @ 13:20:33.609519 ==> exit in 1.003358 seconds
281
- ```
282
-
283
- Finally, to help with timing code, you can request the current delta with
284
- ```
285
- peek.delta
286
- ```
287
- or (re)set it with
288
- ```
289
- peek.delta = 0
290
- ```
291
- So, e.g. to time a section of code:
292
- ```
293
- peek.delta = 0
294
- time.sleep(1)
295
- duration = peek.delta
296
- peek(duration)
297
- ```
298
- might print something like:
299
- ```
300
- duration=1.0001721999999997
301
- ```
302
-
303
- # Configuration
304
-
305
- For the configuration, it is important to realize that `peek` is an instance of a class, which has
306
- a number of configuration attributes:
307
-
308
- ```
309
- ------------------------------------------------------
310
- attribute alternative default
311
- ------------------------------------------------------
312
- prefix pr ""
313
- output o "stdout"
314
- serialize pprint.pformat
315
- show_line_number sln False
316
- show_time st False
317
- show_delta sd False
318
- show_enter se True
319
- show_exit sx True
320
- show_traceback stb False
321
- sort_dicts sdi False
322
- underscore_numbers un False
323
- enabled e True
324
- line_length ll 160
325
- color col "-"
326
- color_value colv "-"
327
- level l 0
328
- compact c False
329
- indent i 1
330
- depth de 1000000
331
- wrap_indent wi " "
332
- separator sep ", "
333
- context_separator cs " ==> "
334
- equals_separator es "="
335
- values_only vo False
336
- value_only_for_fstrings voff False
337
- return_none rn False
338
- enforce_line_length ell False
339
- delta dl 0
340
- ------------------------------------------------------
341
- ```
342
- It is perfectly ok to set/get any of these attributes directly, like
343
- ```
344
- peek.prefix = "==> "
345
- print(peek.prefix)
346
- ```
347
-
348
- But, it is also possible to apply configuration directly, only here, in the call to `peek`:
349
- So, it is possible to say
350
- ```
351
- peek(12, prefix="==> ")
352
- ```
353
- , which will print
354
- ```
355
- ==> 12
356
- ```
357
- It is also possible to configure peek permanently with the configure method.
358
- ```
359
- peek.configure(prefix="==> ")
360
- peek(12)
361
- ```
362
- will print
363
- ```
364
- ==> 12
365
- ```
366
- It is arguably easier to say:
367
- ```
368
- peek.prefix = "==> "
369
- peek(12)
370
- ```
371
- or even
372
- ```
373
- peek.pr = "==> "
374
- peek(12)
375
- ```
376
- to print
377
- ```
378
- ==> 12
379
- ```
380
- Yet another way to configure peek is to get a new instance of peek with peek.new() and the required configuration:
381
- ```
382
- z = peek.new(prefix="==> ")
383
- z(12)
384
- ```
385
- will print
386
- ```
387
- ==> 12
388
- ```
389
-
390
- Or, yet another possibility is to clone peek (optionally with modified attributes):
391
- ```
392
- peek1 = peek.clone(show_time=True)
393
- peek2 = peek.clone()
394
- peek2.show_time = True
395
- ```
396
- After this `peek1` and `peek2` will behave similarly (but they are not the same!)
397
-
398
- ## prefix / pr
399
- ```
400
- peek('world', prefix='hello -> ')
401
- ```
402
- prints
403
- ```
404
- hello -> 'world'
405
- ```
406
-
407
- `prefix` can be a function, too.
408
-
409
- ```
410
- import time
411
- def unix_timestamp():
412
- return f"{int(time.time())} "
413
- hello = "world"
414
- peek.prefix = unix_timestamp
415
- peek(hello)
416
- ```
417
- prints
418
- ```
419
- 1613635601 hello='world'
420
- ```
421
-
422
- ## output / o
423
- This will allow the output to be handled by something else than the default (output being written to stdout).
424
-
425
- The `output` attribute can be
426
-
427
- * a callable that accepts at least one parameter (the text to be printed)
428
- * a string or Path object that will be used as the filename
429
- * a text file that is open for writing/appending
430
-
431
- In the example below,
432
- ```
433
- import sys
434
- peek(1, output=print)
435
- peek(2, output=sys.stderr)
436
- with open("test", "a+") as f:
437
- peek(3, output=f)
438
- peek(4, output="")
439
- ```
440
- * `1` will be printed to stdout
441
- * `2` will be printed to stderr
442
- * `3` will be appended to the file test
443
- * nothing will be printed/written
444
-
445
- As `output` may be a callable, you can even use this to automatically log any `peek` output:
446
- ```
447
- import logging
448
- logging.basicConfig(level="INFO")
449
- log = logging.getLogger("demo")
450
- peek.configure(output=log.info)
451
- a = {1, 2, 3, 4, 5}
452
- peek(a)
453
- a.remove(4)
454
- peek(a)
455
- ```
456
- will print to stdout:
457
- ```
458
- INFO:demo:a={1, 2, 3, 4, 5}
459
- INFO:demo:a={1, 2, 3, 5}
460
- ```
461
- Finally, you can specify the following strings:
462
- ```
463
- "stderr" to print to stderr
464
- "stdout" to print to stdout
465
- "stdout_nocolor" to print to stdout without any colors
466
- "null" or "" to completely ignore (dummy) output
467
- "logging.debug" to use logging.debug
468
- "logging.info" to use logging.info
469
- "logging.warning" to use logging.warning
470
- "logging.error" to use logging.error
471
- "logging.critical" to use logging.critical
472
- ```
473
- E.g.
474
- ```
475
- import sys
476
- peek.output = "stderr"
477
- ```
478
- to print to stderr.
479
-
480
- ## serialize
481
- This will allow to specify how argument values are to be serialized to displayable strings.
482
- The default is `pformat` (from `pprint`), but this can be changed.
483
- For example, to handle non-standard datatypes in a custom fashion.
484
- The serialize function should accept at least one parameter.
485
- The function may optionally accept the keyword arguments `width` and `sort_dicts`, `compact`, `indent`, `underscore_numbers` and `depth`.
486
-
487
- ```
488
- def add_len(obj):
489
- if hasattr(obj, "__len__"):
490
- add = f" [len={len(obj)}]"
491
- else:
492
- add = ""
493
- return f"{repr(obj)}{add}"
494
-
495
- l7 = list(range(7))
496
- hello = "world"
497
- peek(7, hello, l7, serialize=add_len)
498
- ```
499
- prints
500
- ```
501
- 7, hello='world' [len=5], l7=[0, 1, 2, 3, 4, 5, 6] [len=7]
502
- ```
503
-
504
- ## show_line_number / sln
505
- If True, adds the `peek()` call's line number and possibly the filename and parent function to `peek()`'s output.
506
-
507
- ```
508
- peek.configure(show_line_number=True)
509
- def shout():
510
- hello="world"
511
- peek(hello)
512
- shout()
513
- ```
514
- prints something like
515
- ```
516
- #5 in shout() ==> hello='world'
517
- ```
518
-
519
- If "no parent" or "n", the parent function will not be shown.
520
- ```
521
- peek.show_line_number = "n"
522
- def shout():
523
- hello="world"
524
- peek(hello)
525
- shout()
526
- ```
527
- prints something like
528
- ```
529
- #5 ==> hello='world'
530
- ```
531
- Note that if you call `peek` without any arguments, the line number is always shown, regardless of the status `show_line_number`.
532
-
533
- See below for an explanation of the information provided.
534
-
535
- ## show_time / st
536
- If True, adds the current time to `peek()`'s output.
537
-
538
- ```
539
- peek.configure(show_time=True)
540
- hello="world"
541
- peek(hello)
542
- ```
543
- prints something like
544
- ```
545
- @ 13:01:47.588125 ==> hello='world'
546
- ```
547
-
548
- ## show_delta / sd
549
- If True, adds the number of seconds since the start of the program to `peek()`'s output.
550
- ```
551
- import time
552
- peek.show_delta = True
553
- french = "bonjour le monde"
554
- english = "hallo world"
555
- peek(english)
556
- time.sleep(1)
557
- peek(french)
558
- ```
559
- prints something like
560
- ```
561
- delta=0.088 ==> english='hallo world'
562
- delta=1.091 ==> french='bonjour le monde'
563
- ```
564
-
565
- ## show_enter / se
566
- When used as a decorator or context manager, by default, peek ouputs a line when the decorated the
567
- function is called or the context manager is entered.
568
-
569
- With `show_enter=False` this line can be suppressed.
570
-
571
- ## show_exit / sx
572
- When used as a decorator or context manager, by default, peek ouputs a line when the decorated the
573
- function returned or the context manager is exited.
574
-
575
- With `show_exit=False` this line can be suppressed.
576
-
577
-
578
- ## show_traceback / stb
579
- When show_traceback is True, the ordinary output of peek() will be followed by a printout of the
580
- traceback, similar to an error traceback.
581
-
582
- ```
583
- def x():
584
- peek(show_traceback=True)
585
-
586
- x()
587
- x()
588
- ```
589
- prints
590
- ```
591
- #4 in x()
592
- Traceback (most recent call last)
593
- File "c:\Users\Ruud\Dropbox (Personal)\Apps\Python Ruud\peek\x.py", line 6, in <module>
594
- x()
595
- File "c:\Users\Ruud\Dropbox (Personal)\Apps\Python Ruud\peek\x.py", line 4, in x
596
- peek()
597
- #4 in x()
598
- Traceback (most recent call last)
599
- File "c:\Users\Ruud\Dropbox (Personal)\Apps\Python Ruud\peek\x.py", line 7, in <module>
600
- x()
601
- File "c:\Users\Ruud\Dropbox (Personal)\Apps\Python Ruud\peek\x.py", line 4, in x
602
- peek()
603
- ```
604
- The `show_traceback` functionality is also available when peek is used as a decorator or context manager.
605
-
606
- ## line_length / ll
607
- This attribute is used to specify the line length (for wrapping). The default is 160.
608
- Peek tries to keep all output on one line, but if it can't it will wrap:
609
-
610
- ```
611
- d = dict(a1=1,a2=dict(a=1,b=1,c=3),a3=list(range(10)))
612
- peek(d)
613
- peek(d, line_length=80)
614
- ```
615
- prints
616
- ```
617
- d={'a1': 1, 'a2': {'a': 1, 'b': 1, 'c': 3}, 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
618
- d=
619
- {'a1': 1,
620
- 'a2': {'a': 1, 'b': 1, 'c': 3},
621
- 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
622
- ```
623
-
624
- ## color / col and color_value / colv
625
- The color attribute is used to specify the color of the output.
626
- There's a choice of black, white, red, green, blue, cyan, magenta and yellow.
627
- To set the color to 'nothing', use "-".
628
-
629
- 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.
630
-
631
- For instance:
632
-
633
- ```
634
- x = "x"
635
- y = "y"
636
- peek(x, y)
637
- peek(x, y, color_value="green")
638
- peek(x, y, color="red")
639
- peek(x, y, color="red", color_value="green")
640
- ```
641
-
642
- will result in:
643
-
644
- <img src="https://www.salabim.org/peek/peek_colors.png">
645
-
646
- Of course, color and color_value may be specified in a peek.toml file, to make all peek output in a specified color.
647
-
648
- > [!NOTE]
649
- >
650
- > The color and color_value attributes are only applied when using stdout as output.
651
- >
652
- > Colors can be ignored completely by using `peek.output = "stdout_nocolor`.
653
-
654
- ## compact / c
655
- This attribute is used to specify the compact parameter for `pformat` (see the pprint documentation
656
- for details). `compact` is False by default.
657
-
658
- ```
659
- a = 9 * ["0123456789"]
660
- peek.line_length = 80
661
- peek(a)
662
- peek(a, compact=True)
663
- ```
664
- prints
665
- ```
666
- a=
667
- ['0123456789',
668
- '0123456789',
669
- '0123456789',
670
- '0123456789',
671
- '0123456789',
672
- '0123456789',
673
- '0123456789',
674
- '0123456789',
675
- '0123456789']
676
- a=
677
- ['0123456789', '0123456789', '0123456789', '0123456789', '0123456789',
678
- '0123456789', '0123456789', '0123456789', '0123456789']
679
- ```
680
-
681
- ## indent / i
682
- This attribute is used to specify the indent parameter for `pformat` (see the pprint documentation
683
- for details). `indent` is 1 by default.
684
-
685
- ```
686
- s = "01234567890012345678900123456789001234567890"
687
- peek.line_length = 80
688
- peek( [s, [s]])
689
- peek( [s, [s]], indent=4)
690
- ```
691
- prints
692
- ```
693
- [s, [s]]=
694
- ['01234567890012345678900123456789001234567890',
695
- ['01234567890012345678900123456789001234567890']]
696
- [s, [s]]=
697
- [ '01234567890012345678900123456789001234567890',
698
- ['01234567890012345678900123456789001234567890']]
699
- ```
700
-
701
- ## depth / de
702
- This attribute is used to specify the depth parameter for `pformat` (see the pprint documentation
703
- for details). `depth` is `1000000` by default.
704
-
705
- ```
706
- s = "01234567890012345678900123456789001234567890"
707
- peek([s,[s,[s,[s,s]]]])
708
- peek([s,[s,[s,[s,s]]]], depth=3)
709
- ```
710
- prints
711
- ```
712
- [s,[s,[s,[s,s]]]]=
713
- ['01234567890012345678900123456789001234567890',
714
- ['01234567890012345678900123456789001234567890',
715
- ['01234567890012345678900123456789001234567890',
716
- ['01234567890012345678900123456789001234567890',
717
- '01234567890012345678900123456789001234567890']]]]
718
- [s,[s,[s,[s,s]]]]=
719
- ['01234567890012345678900123456789001234567890',
720
- ['01234567890012345678900123456789001234567890',
721
- ['01234567890012345678900123456789001234567890', [...]]]]
722
- ```
723
-
724
- ## wrap_indent / wi
725
- This specifies the indent string if the output does not fit in the line_length (has to be wrapped).
726
- Rather than a string, wrap_indent can be also be an integer, in which case the wrap_indent will be that amount of blanks.
727
- The default is 4 blanks.
728
-
729
- E.g.
730
- ```
731
- d = dict(a1=1,a2=dict(a=1,b=1,c=3),a3=list(range(10)))
732
- peek.line_length = 80
733
- peek(d, wrap_indent=" ")
734
- peek(d, wrap_indent="....")
735
- peek(d, wrap_indent=2)
736
- ```
737
- prints
738
- ```
739
- d=
740
- {'a1': 1,
741
- 'a2': {'a': 1, 'b': 1, 'c': 3},
742
- 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
743
- d=
744
- ....{'a1': 1,
745
- .... 'a2': {'a': 1, 'b': 1, 'c': 3},
746
- .... 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
747
- d=
748
- {'a1': 1,
749
- 'a2': {'a': 1, 'b': 1, 'c': 3},
750
- 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
751
- ```
752
-
753
- ## enabled / e
754
- Can be used to disable the output:
755
- ```
756
- peek.enabled = False
757
- s = 'the world is '
758
- peek(s + 'perfect.')
759
- peek.enabled = True
760
- peek(s + 'on fire.')
761
- ```
762
- prints
763
- ```
764
- s + 'on fire.'='the world is on fire.'
765
- ```
766
- and nothing about a perfect world.
767
-
768
- ## sort_dicts / sdi
769
- By default, peek does not sort dicts (printed by pprint). However, it is possible to get the
770
- default pprint behaviour (i.e. sorting dicts) with the sorted_dicts attribute:
771
-
772
- ```
773
- world = {"EN": "world", "NL": "wereld", "FR": "monde", "DE": "Welt"}
774
- peek(world))
775
- peek(world, sort_dicts=False)
776
- peek(world, sort_dicts=True)
777
- ```
778
- prints
779
- ```
780
- world={'EN': 'world', 'NL': 'wereld', 'FR': 'monde', 'DE': 'Welt'}
781
- world={'EN': 'world', 'NL': 'wereld', 'FR': 'monde', 'DE': 'Welt'}
782
- world={'DE': 'Welt', 'EN': 'world', 'FR': 'monde', 'NL': 'wereld'}
783
- ```
784
-
785
- Note that under Python <=3.7, dicts are always printed sorted.
786
-
787
- ## underscore_numbers / un
788
-
789
- By default, peek does not add underscores in big numbers (printed by pprint). However, it is possible to get the
790
- default pprint behaviour with the underscore_numbers attribute:
791
-
792
- ```
793
- numbers = dict(one= 1, thousand= 1000, million=1000000, x1234567890= 1234567890)
794
- peek(numbers)
795
- peek(numbers, underscore_numbers=True)
796
- peek(numbers, un=False)
797
- ```
798
- prints
799
- ```
800
- numbers={'one': 1, 'thousand': 1000, 'million': 1000000, 'x1234567890': 1234567890}
801
- numbers={'one': 1, 'thousand': 1_000, 'million': 1_000_000, 'x1234567890': 1_234_567_890}
802
- numbers={'one': 1, 'thousand': 1000, 'million': 1000000, 'x1234567890': 1234567890}
803
- ```
804
-
805
- ## seperator / sep
806
-
807
- By default, pairs (on one line) are separated by `, `.
808
- It is possible to change this with the attribute ` separator`:
809
-
810
- ```
811
- a = "abcd"
812
- b = 1
813
- c = 1000
814
- d = list("peek")
815
- peek(a, (b, c), d)
816
- peek(a, (b, c), d, separator=" | ")
817
- ```
818
- prints
819
- ```
820
- a='abcd', (b,c)=(1, 1000), d=['peek', 'c', 'e', 'c', 'r', 'e', 'a', 'm']
821
- a='abcd' | (b,c)=(1, 1000) | d=['peek', 'c', 'e', 'c', 'r', 'e', 'a', 'm']
822
- ```
823
- Note that under Python <=3.7, numbers are never printed with underscores.
824
-
825
- ## context_separator / cs
826
-
827
- By default the line_number, time and/or delta are followed by ` ==> `.
828
- It is possible to change this with the attribute `context_separator`:
829
- ```
830
- a = "abcd"
831
- peek(a,show_time=True)
832
- peek(a, show_time=True, context_separator = ' \u279c ')
833
- ```
834
- prints:
835
- ```
836
- @ 09:05:38.693840 ==> a='abcd'
837
- @ 09:05:38.707914 a='abcd'
838
- ```
839
- ## equals_separator / es
840
- By default name of a variable and its value are separated by `= `.
841
- It is possible to change this with the attribute `equals_separator`:
842
-
843
- ```
844
- a = "abcd"
845
- peek(a)
846
- peek(a, equals_separator = ' == ")
847
- ```
848
- prints:
849
- ```
850
- a='abcd'
851
- a == 'abcd'
852
- ```
853
-
854
- ## values_only / vo
855
- If False (the default), both the left-hand side (if possible) and the
856
- value will be printed. If True, the left hand side will be suppressed:
857
- ```
858
- hello = "world"
859
- peek(hello, 2 * hello)
860
- peek(hello, 2 * hello, values_only=True)
861
- ```
862
- prints
863
- ```
864
- hello='world', 2 * hello='worldworld'
865
- 'world', 'worldworld'
866
- ```
867
- The values=True version of peek can be seen as a supercharged print/pprint.
868
-
869
-
870
- ## values_only_for_fstrings / voff
871
- If False (the default), both the original f-string and the
872
- value will be printed for f-strings.
873
- If True, the left_hand side will be suppressed in case of an f-string:
874
-
875
- ```
876
- x = 12.3
877
- peek(f"{x:0.3e}")
878
- peek.values_only_for_fstrings = True
879
- peek(f"{x:0.3e}")
880
- ```
881
- prints
882
- ```
883
- f"{x:0.3e}"='1.230e+01'
884
- '1.230e+01'
885
- ```
886
- Note that if `values_only` is True, f-string will be suppressed, regardless of `values_only_for_fstrings`.
887
-
888
- ## return_none / rn
889
- Normally, `peek()`returns the values passed directly, which is usually fine. However, when used in a notebook
890
- or REPL, that value will be shown, and that can be annoying. Therefore, if `return_none`is True, `peek()`will
891
- return None and thus not show anything.
892
- ```
893
- a = 3
894
- b = 4
895
- print(peek(a, b))
896
- peek.return_none = True
897
- print(peek(a, b))
898
- ```
899
- prints
900
- ```
901
- a=3, b=4
902
- (3, 4)
903
- a=3, b=4
904
- None
905
- ```
906
-
907
- ## enforce_line_length / ell
908
- If enforce_line_length is True, all output lines are explicitly truncated to the given line_length, even those that are not truncated by pformat.
909
-
910
- ## delta / dl
911
- The delta attribute can be used to (re)set the current delta, e.g.
912
- ```
913
- peek.dl = 0
914
- print(peek.delta)
915
- ```
916
- prints a value that id slightly more than 0.
917
-
918
-
919
- ## provided / pr
920
- If provided is False, all output for this call will be suppressed.
921
- If provided is True, output will be generated as usual (obeying the enabled attribute).
922
-
923
- ```
924
- x = 1
925
- peek("should print", provided=x > 0)
926
- peek("should not print", provided=x < 0)
927
- ```
928
- This will print
929
- ```
930
- should print
931
- ```
932
-
933
- # Return a string instead of sending to output
934
-
935
- `peek(*args, as_str=True)` is like `peek(*args)` but the output is returned as a string instead
936
- of written to output.
937
-
938
- ```
939
- hello = "world"
940
- s = peek(hello, as_str=True)
941
- print(s, end="")
942
- ```
943
- prints
944
- ```
945
- hello='world'
946
- ```
947
-
948
- Note that if enabled=False, the call will return the null string (`""`).
949
-
950
- # Disabling peek's output
951
-
952
- ```
953
- peek1 = peek.fork(show_delta=True)
954
- peek(1)
955
- peek1(2)
956
- peek.enabled = False
957
- peek(3)
958
- peek1(4)
959
- peek1.enabled = True
960
- peek(5)
961
- peek1(6)
962
- print(peek1.enabled)
963
- ```
964
- prints
965
- ```
966
- 1
967
- delta=0.011826 ==> 2
968
- 5
969
- delta=0.044893 ==> 6
970
- True
971
- ```
972
- Of course `peek()` continues to return its arguments when disabled, of course.
973
-
974
- It is also possible to suppress output with the provided attribute (see above).
975
-
976
- # Using peek as a substitute for `assert`
977
-
978
- Peek has a method `assert_` that works like `assert`, but can be enabled or disabled with the enabled flag.
979
-
980
- ```
981
- temperature = -1
982
- peek.assert_(temperature > 0)
983
- ```
984
- This will raise an AttributeError.
985
-
986
- But
987
- ```
988
- peek.enabled = False
989
- temperature = -1
990
- peek.assert_(temperature > 0)
991
- ```
992
- will not.
993
-
994
- Note that with the attribute propagation method, you can in effect have a layered assert system.
995
-
996
- # Using level to control peek output
997
-
998
- It is possible to attach a (float) level to a peek statement. By default, this level is 0. E.g.:
999
-
1000
- ```
1001
- peek("critical", level=0)
1002
- peek("important", level=1)
1003
- peek("warning", level=2)
1004
- ```
1005
-
1006
- With `peek.show_level()` the program may select which level(s) to show or not.
1007
-
1008
- Notice that a level matches if any of the specifiers conditions is met.
1009
-
1010
- `peek.show_level("")` disables peek output completely.
1011
-
1012
- It is also possible to use a float value where the string is expected:
1013
- `peek.show_level(2)` ==> 2
1014
-
1015
- Only peek with a level that meets any of the specifications is printed.
1016
-
1017
- An obvious use is to set the level of critical info to 0, important info to 1 and warning to 2.
1018
- Then `peek.show_level = "-1"` will show critical and important info, but no warning info.
1019
-
1020
- The default show_level may, of course, be specified in a `peek.toml` file.
1021
-
1022
- Note that the `show_level` is defined on a global level and is not related to any particular peek instance,
1023
-
1024
- It might be useful to define a number of different peek instances, each related to a certain level, like:
1025
-
1026
- ```
1027
- peek0 = peek.fork(color="red", level=0)
1028
- peek1 = peek.fork(color="yellow", level=1)
1029
- peek2 = peek.fork(color="green", level=2)
1030
- ```
1031
-
1032
- If level of a peek statement is the null string, peek will never print. That can be useful to disable peek debug info on a per peek instance basis, e.g.:
1033
-
1034
- ```peek_cond = peek.new()
1035
- peek_cond = peek.new()
1036
- peek_cond(1)
1037
- peek_cond.level = ""
1038
- peek_cond(2)
1039
- ```
1040
-
1041
- This will print only 1.
1042
-
1043
- The most easy form for `peek.show_level` is just a simple value as argument, like `peek.show.level(2)`, which will make only level 2 peeks to work.
1044
-
1045
- `peek.show_level` can have a number (usually 1) of arguments.
1046
-
1047
- Each parameter may be:
1048
-
1049
- * a float value or
1050
-
1051
- - a string with the format *from* - *to*
1052
- , where both *from* and *to* are optional. If *from* is omitted, -1E30 is assumed. If *to* is omitted, 1E30 is assumed.
1053
- Negative values have to be parenthesized.
1054
-
1055
- Examples:
1056
-
1057
- - `peek.show_level (1)` ==> show level 1
1058
- - `peek.show_level (1, -3)` ==> show level 1 and level -3
1059
- - `peek.show_level ("1-2")` ==> show level between 1 and 2
1060
- - `peek.show_level("-")` ==> show all levels
1061
- - `peek.show_level("")` ==> show no levels
1062
- - `peek.show_level("1-")`==> show all levels >= 1
1063
- - `peek.show_level("-10.2")`==> show all levels <=10.2
1064
- - `peek.show_level(1, 2, "5-7", "10-")` ==> show levels 1, 2, between 5 and 7 (inclusive) and >= 10
1065
- - `peek.show_level((-3)-3")` ==> show levels between -3 and 3 (inclusive)
1066
- - `peek.show_level()` ==> returns the current show_level
1067
-
1068
- `show_level` can also be called with a minimum and/or a maximum value, e.g.
1069
-
1070
- - `peek.show_level(min=1)` ==> show all levels >= 1
1071
- - `peek.show_level(max=10.2)` ==> show all levels <= 10.2
1072
- - `peek.show_level(min=1, max=10)` ==> show all levels between 1 and 10 (inclusive)
1073
-
1074
- Note that min or max cannot be combined with a specifier as above
1075
-
1076
- `show_level` can also be used as a context managerl:
1077
-
1078
- ```
1079
- with peek.show_level(1):
1080
- peek(1, level=1)
1081
- peek(2, level=2)
1082
- ```
1083
-
1084
- This will print one line with`1` only.
1085
-
1086
- # Using color to control peek output
1087
-
1088
- The method `peek.show_color()` can be used to show only peek output for certain colors.
1089
-
1090
- For instance, with `peek.show_color("blue red")`, subsequent peek commands will print only if the color is *blue* or *red*.
1091
- With `peek.show_color("-")` only not colored output will be shown.
1092
-
1093
- It is also possible to specify which colors *not* to show. E.g. `peek.show_color("not green yellow"` will show all peeks apart from those in green or yellow. With `peek.show_color("not -")` only colored peeks will be shown.
1094
-
1095
- With `peek.show_color()` the current value can be queried.
1096
-
1097
- Finally, `peek.show_color()` can be used as a context manager:
1098
- ```
1099
- with peek.show_color("blue red"):
1100
- peek(1, color="blue")
1101
- peek(2)
1102
- peek(3)
1103
- ```
1104
- This will print 1 and 3.
1105
-
1106
- It is possible to use both `peek.show_level()` and `peek.show_color()` at the same time, but this might be confusing.
1107
-
1108
- # Copying to the clipboard
1109
-
1110
- It is possible to copy a value to the clipboard. There are two ways:
1111
-
1112
- ### With peek(*args, to_clipboard=True)
1113
-
1114
- With the optional keyword argument, *to_clipboard*:
1115
-
1116
- - If to_clipboard==False (the default), nothing is copied to the clipboard.
1117
- - If to_clipboard==True, the *value* of the the *first* parameter will be copied to the clipboard. The output itself is as usual.
1118
-
1119
- Examples:
1120
-
1121
- ```
1122
- part1 = 200
1123
- extra = "extra"
1124
- peek(part1, extra, to_clipboard=True)
1125
- # will print part1=200, extra='extra' and copy 200 to the clipboard
1126
- peek(200, to_clipboard=True)\
1127
- # will print 200 and copy 200 to the clipboard
1128
- peek(to_clipboard=True)
1129
- # will print #5 (or similar) and empty the clipboard
1130
- ```
1131
-
1132
- Note that *to_clipboard* is not a peek attribute and can only be used when calling `peek`,
1133
- If as_str==True, to_clipboard is ignored.
1134
-
1135
- ### With peek.to_clipboard
1136
-
1137
- Just use peek.to_clipboard to copy any value to the clipboard. So,
1138
- ```
1139
- part1 = 1234
1140
- peek.to_clipboard(part1)
1141
- ```
1142
-
1143
- will copy `1234` to the clipboard and write `copied to clipboard: 1234` to the console.
1144
- If the confirmation message is not wanted, just add confirm=False, like
1145
-
1146
- ```
1147
- peek.to_clipboard(part1, confirm=False)
1148
- ```
1149
-
1150
- ### General
1151
-
1152
- Implementation detail: the clipboard functionality uses pyperclip, apart from under Pythonista, where the
1153
- builtin clipboard module is used.
1154
-
1155
- This functionality is particularly useful for entering an answer of an *Advent of Code* solution to the site.
1156
-
1157
- # Interpreting the line number information
1158
-
1159
- When `show_line_number` is True or peek() is used without any parameters, the output will contain the line number like:
1160
- ```
1161
- #3 ==> a='abcd'
1162
- ```
1163
- If the line resides in another file than the main file, the filename (without the path) will be shown as well:
1164
- ```
1165
- #30[foo.py] ==> foo='Foo'
1166
- ```
1167
- And finally when used in a function or method, that function/method will be shown as well:
1168
- ```
1169
- #456[foo.py] in square_root ==> x=123
1170
- ```
1171
- The parent function can be suppressed by setting `show_line_number` or `sln` to `"n"` or `"no parent"`.
1172
-
1173
- # Configuring at import time
1174
-
1175
- It can be useful to configure peek at import time. This can be done by providing a `peek.toml` file which
1176
- can contain any attribute configuration overriding the standard settings.
1177
- E.g. if there is an `peek.toml` file with the following contents
1178
-
1179
- ```
1180
- outpout = "stderr"
1181
- show_time = true
1182
- ll = 160
1183
- compact = true
1184
- ```
1185
- in the same folder as the application, this program:
1186
- ```
1187
- hello = "world"
1188
- peek(hello)
1189
- ```
1190
- will print to stderr (rather than stdout):
1191
- ```
1192
- @ 14:53:41.392190 ==> hello='world'
1193
- ```
1194
- 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.
1195
-
1196
- Please observe that toml values are slightly different from their Python equivalents:
1197
- ```
1198
- -----------------------------------
1199
- Python toml
1200
- -----------------------------------
1201
- True true
1202
- False false
1203
- strings preferably double quoted
1204
- -----------------------------------
1205
- ```
1206
- Note that not-specified attributes will remain the default settings.
1207
-
1208
- For obvious reasons, it is not possible to specify `serialize` in a peek.toml file.
1209
-
1210
- # Working with multiple instances of peek
1211
-
1212
- Normally, only the `peek()` object is used.
1213
-
1214
- It can be useful to have multiple instances, e.g. when some of the debugging has to be done with context information
1215
- and others requires an alternative prefix.
1216
-
1217
- THere are several ways to obtain a new instance of peek:
1218
-
1219
- * by using `peek.new()`
1220
-
1221
- With this a new peek object is created with the default attributes
1222
- and possibly peek.toml overrides.
1223
- * by using `peek.new(ignore_toml=True)`
1224
-
1225
- With this a new peekobject is created with the default attibutes. Any peek.toml files asre ignored.
1226
- * by using `peek.fork()`
1227
-
1228
- 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.
1229
- * by using `peek.clone()`, which copies all attributes from peek()
1230
-
1231
- 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.
1232
-
1233
- * with `peek()` used as a context manager
1234
-
1235
- In either case, attributes can be added to override the default ones.
1236
-
1237
- ### Example
1238
- ```
1239
- peek_with_line_number = peek.fork(show_line_number=True)
1240
- peek_with_new_prefix = peek.new(prefix="==> ")
1241
- peek_with_new_prefix_and_time = peek_with_new_prefix.clone(show_time=True)
1242
- hello="world"
1243
- peek_with_line_number(hello)
1244
- peek_with_new_prefix(hello)
1245
- peek_with_new_prefix_and_time(hello)
1246
- peek.equals_separator = " == " # this affects only the forked objects
1247
- peek_with_line_number(hello)
1248
- peek_with_new_prefix(hello)
1249
- peek_with_new_prefix_and_time(hello)
1250
- with peek(prefix="peek_cm ") as peek_cm:
1251
- peek_cm(hello)
1252
- peek(hello)
1253
- ```
1254
- prints something like
1255
- ```
1256
- #28 ==> hello='world'
1257
- ==> hello='world'
1258
- ==> @ 09:55:52.122818 ==> hello='world'
1259
- #32 ==> hello == 'world'
1260
- ==> hello='world'
1261
- ==> @ 09:55:52.125928 ==> hello='world'
1262
- peek_cm enter
1263
- peek_cm hello == 'world'
1264
- hello == 'world'
1265
- peek_cm exit in 0.001843 seconds
1266
- ```
1267
-
1268
- ## ignore_toml
1269
- 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.
1270
-
1271
- ### Example
1272
- Suppose we have a `peek.toml` file in the current directory with the contents
1273
- ```
1274
- {prefix="==>"}
1275
- ```
1276
- Then
1277
- ```
1278
- peek_post_toml = peek.new()
1279
- peek_ignore_toml = peek.new(ignore_toml=True)
1280
- hello = "world"
1281
- peek(hello)
1282
- peek_post_toml(hello)
1283
- peek_ignore_toml(hello)
1284
- ```
1285
- prints
1286
- ```
1287
- ==>hello='world'
1288
- ==>hello='world'
1289
- hello='world'
1290
- ```
1291
-
1292
- # Test script
1293
-
1294
- On GitHub is a file `test_peek.py` that tests (and thus also demonstrates) most of the functionality
1295
- of peek.
1296
-
1297
- It is very useful to have a look at the tests to see the features (some may be not covered (yet) in this readme).
1298
-
1299
- # Using peek in a REPL
1300
-
1301
- Peek may be used in a REPL, but with limited functionality:
1302
- * all arguments are just presented as such, i.e. no left-hand side, e.g.
1303
- ```
1304
- >> hello = "world"
1305
- >>> peek(hello, hello * 2)
1306
- 'hello', 'hellohello'
1307
- ('hello', 'hellohello')
1308
- ```
1309
- * line numbers are never shown
1310
- * use as a decorator is not supported
1311
- * use as a context manager is not supported
1312
-
1313
- # Alternative to `peek`
1314
-
1315
- Sometimes, even peek is too long during a debug session or it is not suitable to use the name peek.
1316
-
1317
- In that case, it is possible to use p instead
1318
- ```
1319
- from peek import p
1320
- ```
1321
- The `p` object is a *fork* of peek. That means that attributes of `peek` are propagated to `p`, unless overridden.
1322
-
1323
-
1324
- # Limitations
1325
-
1326
- It is not possible to use peek:
1327
- * from a frozen application (e.g. packaged with PyInstaller)
1328
- * when the underlying source code has changed during execution
1329
-
1330
- # Changelog
1331
-
1332
- The changelog can be found here:
1333
-
1334
- * https://github.com/salabim/peek/blob/main/changelog.md or
1335
- * https://salabim.org/peek/changelog
1336
-
1337
-
1338
- # Acknowledgement
1339
-
1340
- The **peek** package is inspired by the **IceCream** package, but is a
1341
- nearly complete rewrite. See https://github.com/gruns/icecream
1342
-
1343
- Many thanks to the author Ansgar Grunseid / grunseid.com / grunseid@gmail.com .
1344
-
1345
- The peek package is a rebrand of the **ycecream** package, with enhancements.
1346
-
1347
- # Differences with IceCream
1348
-
1349
- The peek module was originally a fork of **IceCream**, but has many differences:
1350
-
1351
- ```
1352
- -------------------------------------------------------------------------------------------
1353
- characteristic peek IceCream
1354
- -------------------------------------------------------------------------------------------
1355
- default name peek ic
1356
- import method import peek from icecream import ic
1357
- number of files 1 several
1358
- usable without installation yes no
1359
- can be used as a decorator yes no
1360
- can be used as a context manager yes no
1361
- can show traceback yes no
1362
- PEP8 (Pythonic) API yes no
1363
- sorts dicts no by default, optional *) yes
1364
- supports compact, indent,
1365
- and underscore_numbers
1366
- parameters of pprint yes **) no
1367
- use from a REPL limited functionality no
1368
- external configuration via toml file no
1369
- level control yes no
1370
- observes line_length correctly yes no
1371
- benchmarking functionality yes no
1372
- suppress f-strings at left hand optional no
1373
- indentation 4 blanks (overridable) dependent on length of prefix
1374
- forking and cloning yes no
1375
- test script pytest unittest
1376
- colorize ***) yes, off by default yes, on by default
1377
- -------------------------------------------------------------------------------------------
1378
- *) under Python <= 3.7, dicts are always sorted (regardless of the sort_dicts attribute
1379
- **) under Python <= 3.7, numbers are never underscored (regardless of the underscore_numnbers attribute
1380
- ***) peek allows selection of a color, whereas IceCream does coloring based on contents.
1381
-
1382
- ```
1383
- ![PyPI](https://img.shields.io/pypi/v/peek-python) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/peek-python) ![PyPI - Implementation](https://img.shields.io/pypi/implementation/peek)
1384
- ![PyPI - License](https://img.shields.io/pypi/l/peek) ![ruff](https://img.shields.io/badge/style-ruff-41B5BE?style=flat)
1385
- ![GitHub last commit](https://img.shields.io/github/last-commit/salabim/peek)
1386
-
1
+ Metadata-Version: 2.1
2
+ Name: peek-python
3
+ Version: 24.0.1
4
+ Summary: peek - debugging and benchmarking made easy
5
+ Author-email: Ruud van der Ham <rt.van.der.ham@gmail.com>
6
+ Project-URL: Homepage, https://github.com/salabim/peek
7
+ Project-URL: Repository, https://github.com/salabim/peek
8
+ Classifier: Development Status :: 5 - Production/Stable
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ License-File: license.txt
14
+ Requires-Dist: asttokens>=3.0.0
15
+ Requires-Dist: colorama>=0.4.6
16
+ Requires-Dist: executing>=2.1.0
17
+ Requires-Dist: pyperclip>=1.9.0
18
+ Requires-Dist: six>=1.17.0
19
+ Requires-Dist: tomli>=2.2.1
20
+
21
+ - [ ] <img src="https://www.salabim.org/peek/peek_logo1.png">
22
+
23
+
24
+ # Introduction
25
+
26
+ Do you use `print()` or `log()` to debug your code?
27
+ If so, peek will make printing debug information really easy.
28
+ And on top of that, you get some basic benchmarking functionality.
29
+
30
+ # Table of contents
31
+
32
+ * [Installation](#installation)
33
+
34
+ * [Importing peek](#importing-peek)
35
+
36
+ * [Inspect variables and expressions](#inspect-variables-and-expressions)
37
+
38
+ * [Inspect execution](#inspect-execution)
39
+
40
+ * [Return value](#return-value)
41
+
42
+ * [Debug entry and exit of function calls](#debug-entry-and-exit-of-function-calls)
43
+
44
+ * [Benchmarking with peek](#benchmarking-with-peek)
45
+
46
+ * [Configuration](#configuration)
47
+
48
+ * [Return a string instead of sending to output](#return-a-string-instead-of-sending-to-output)
49
+
50
+ * [Disabling peek's output](#disabling-peeks-output)
51
+
52
+ * [Using filter to control peek output](#Using-filter-to-control-peek-output)
53
+
54
+ * [Copying to the clipboard](#Copying-to-the-clipboard)
55
+
56
+ * [Interpreting the line number information](#interpreting-the-line-number-information)
57
+
58
+ * [Configuring at import time](#configuring-at-import-time)
59
+
60
+ * [Working with multiple instances of peek](#working-with-multiple-instances-of-peek)
61
+
62
+ * [Test script](#test-script)
63
+
64
+ * [Using peek in a REPL](#using-peek-in-a-repl)
65
+
66
+ * [Alternative to `peek`](#alternative-to-peek)
67
+
68
+ * [Limitations](#limitations)
69
+
70
+ * [Acknowledgement](#acknowledgement)
71
+
72
+ * [Changelog](#changelog)
73
+
74
+ * [Differences with IceCream](#differences-with-icecream)
75
+
76
+
77
+ # Installation
78
+
79
+ Installing peek with pip is easy.
80
+ ```
81
+ pip install peek-python
82
+ ```
83
+ or when you want to upgrade,
84
+ ```
85
+ pip install peek-python --upgrade
86
+ ```
87
+
88
+ Alternatively, peek.py can be just copied into you current work directory from GitHub (https://github.com/salabim/peek).
89
+
90
+ Note that peek requires the `asttokens`, `colorama`, `executing`. `six`, `tomli` and `pyperclip` modules, all of which will be automatically installed with pip.
91
+
92
+ # Importing peek
93
+
94
+ All you need is:
95
+
96
+ ```
97
+ import peek
98
+ ```
99
+
100
+ , or the more conventional, but more verbose
101
+
102
+ ```
103
+ from peek import peek
104
+ ```
105
+
106
+ > [!NOTE]
107
+ >
108
+ > After this, `peek` is automatically a builtin and can thus be used in any module without importing it there.
109
+
110
+ # Inspect variables and expressions
111
+
112
+ Have you ever printed variables or expressions to debug your program? If you've
113
+ ever typed something like
114
+
115
+ ```
116
+ print(add2(1000))
117
+ ```
118
+
119
+ or the more thorough
120
+
121
+ ```
122
+ print("add2(1000)", add2(1000)))
123
+ ```
124
+ or:
125
+ ```
126
+ print(f"{add2(1000)=}")
127
+ ```
128
+
129
+ then `peek()` is here to help. With arguments, `peek()` inspects itself and prints
130
+ both its own arguments and the values of those arguments.
131
+
132
+ ```
133
+ def add2(i):
134
+ return i + 2
135
+
136
+ peek(add2(1000))
137
+ ```
138
+
139
+ prints
140
+ ```
141
+ add2(1000)=1002
142
+ ```
143
+
144
+ Similarly,
145
+
146
+ ```
147
+ class X:
148
+ a = 3
149
+ world = {"EN": "world", "NL": "wereld", "FR": "monde", "DE": "Welt"}
150
+
151
+ peek(world, X.a)
152
+ ```
153
+
154
+ prints
155
+ ```
156
+ world={"EN": "world ", "NL": "wereld", "FR": "monde", "DE": "Welt"}, X.a: 3
157
+ ```
158
+ Just give `peek()` a variable or expression and you're done. Easy, or what?
159
+
160
+
161
+ # Inspect execution
162
+
163
+ Have you ever used `print()` to determine which parts of your program are
164
+ executed, and in which order they're executed? For example, if you've ever added
165
+ print statements to debug code like
166
+
167
+ ```
168
+ def add2(i):
169
+ print("***add2 1")
170
+ result = i + 2
171
+ print("***add2 2")
172
+ return result
173
+ ```
174
+ then `peek()` helps here, too. Without arguments, `peek()` inspects itself and
175
+ prints the calling line number and -if applicable- the file name and parent function.
176
+
177
+ ```
178
+ def add2(i):
179
+ peek()
180
+ result = i + 2
181
+ peek()
182
+ return result
183
+ peek(add2(1000))
184
+ ```
185
+
186
+ prints something like
187
+ ```
188
+ #3 in add2()
189
+ #5 in add2()
190
+ add2(1000)=1002
191
+ ```
192
+ Just call `peek()` and you're done. Isn't that easy?
193
+
194
+
195
+ # Return Value
196
+
197
+ `peek()` returns its argument(s), so `peek()` can easily be inserted into
198
+ pre-existing code.
199
+
200
+ ```
201
+ def add2(i):
202
+ return i + 2
203
+ b = peek(add2(1000))
204
+ peek(b)
205
+ ```
206
+ prints
207
+ ```
208
+ add2(1000)=1002
209
+ b=1002
210
+ ```
211
+ # Debug entry and exit of function calls
212
+
213
+ When you apply `peek()` as a decorator to a function or method, both the entry and exit can be tracked.
214
+ The (keyword) arguments passed will be shown and upon return, the return value.
215
+
216
+ ```
217
+ @peek()
218
+ def mul(x, peek):
219
+ return x * peek
220
+
221
+ print(mul(5, 7))
222
+ ```
223
+ prints
224
+ ```
225
+ called mul(5, 7)
226
+ returned 35 from mul(5, 7) in 0.000006 seconds
227
+ 35
228
+ ```
229
+ It is possible to suppress the print-out of either the enter or the exit information with
230
+ the show_enter and show_exit parameters, like:
231
+
232
+ ```
233
+ @peek(show_exit=False)
234
+ def mul(x, peek):
235
+ return x * peek
236
+
237
+ print(mul(5, 7))
238
+ ```
239
+ prints
240
+ ```
241
+ called mul(5, 7)
242
+ 35
243
+ ```
244
+
245
+ # Benchmarking with peek
246
+
247
+ If you decorate a function or method with peek(), you will be offered the duration between entry and exit (in seconds) as a bonus.
248
+
249
+ That opens the door to simple benchmarking, like:
250
+ ```
251
+ import time
252
+
253
+ @peek(show_enter=False,show_line_number=True)
254
+ def do_sort(i):
255
+ n = 10 ** i
256
+ x = sorted(list(range(n)))
257
+ return f"{n:9d}"
258
+
259
+ for i in range(8):
260
+ do_sort(i)
261
+ ```
262
+ the ouput will show the effects of the population size on the sort speed:
263
+ ```
264
+ #5 ==> returned ' 1' from do_sort(0) in 0.000027 seconds
265
+ #5 ==> returned ' 10' from do_sort(1) in 0.000060 seconds
266
+ #5 ==> returned ' 100' from do_sort(2) in 0.000748 seconds
267
+ #5 ==> returned ' 1000' from do_sort(3) in 0.001897 seconds
268
+ #5 ==> returned ' 10000' from do_sort(4) in 0.002231 seconds
269
+ #5 ==> returned ' 100000' from do_sort(5) in 0.024014 seconds
270
+ #5 ==> returned ' 1000000' from do_sort(6) in 0.257504 seconds
271
+ #5 ==> returned ' 10000000' from do_sort(7) in 1.553495 seconds
272
+ ```
273
+
274
+ It is also possible to time any code by using peek() as a context manager, e.g.
275
+ ```
276
+ with peek():
277
+ time.sleep(1)
278
+ ```
279
+ wil print something like
280
+ ```
281
+ enter
282
+ exit in 1.000900 seconds
283
+ ```
284
+ You can include parameters here as well:
285
+ ```
286
+ with peek(show_line_number=True, show_time=True):
287
+ time.sleep(1)
288
+ ```
289
+ will print somethink like:
290
+ ```
291
+ #8 @ 13:20:32.605903 ==> enter
292
+ #8 @ 13:20:33.609519 ==> exit in 1.003358 seconds
293
+ ```
294
+
295
+ Finally, to help with timing code, you can request the current delta with
296
+ ```
297
+ peek.delta
298
+ ```
299
+ or (re)set it with
300
+ ```
301
+ peek.delta = 0
302
+ ```
303
+ So, e.g. to time a section of code:
304
+ ```
305
+ peek.delta = 0
306
+ time.sleep(1)
307
+ duration = peek.delta
308
+ peek(duration)
309
+ ```
310
+ might print something like:
311
+ ```
312
+ duration=1.0001721999999997
313
+ ```
314
+
315
+ # Configuration
316
+
317
+ For the configuration, it is important to realize that `peek` is an instance of a class, which has
318
+ a number of configuration attributes:
319
+
320
+ ```
321
+ ------------------------------------------------------
322
+ attribute alternative default
323
+ ------------------------------------------------------
324
+ color col "-"
325
+ color_value col_val "-"
326
+ context_separator cs " ==> "
327
+ compact - False
328
+ depth - 1000000
329
+ delta - 0
330
+ enabled - True
331
+ enforce_line_length - False
332
+ equals_separator - "="
333
+ filter f ""
334
+ indent - 1
335
+ level lvl 0
336
+ line_length ll 80
337
+ output - "stdout"
338
+ prefix pr ""
339
+ quote_string qs True
340
+ return_none - False
341
+ separator sep ", "
342
+ serialize - pprint.pformat
343
+ show_delta sd False
344
+ show_enter se True
345
+ show_exit sx True
346
+ show_line_number sln False
347
+ show_time st False
348
+ show_traceback - False
349
+ sort_dicts - False
350
+ to_clipboard clip False
351
+ underscore_numbers un False
352
+ wrap_indent - " "
353
+ values_only vo False
354
+ value_only_for_fstrings voff False
355
+ ------------------------------------------------------
356
+ ```
357
+ It is perfectly ok to set/get any of these attributes directly, like
358
+ ```
359
+ peek.prefix = "==> "
360
+ print(peek.prefix)
361
+ ```
362
+
363
+ But, it is also possible to apply configuration directly, only here, in the call to `peek`:
364
+ So, it is possible to say
365
+
366
+ ```
367
+ peek(12, prefix="==> ")
368
+ ```
369
+ , which will print
370
+ ```
371
+ ==> 12
372
+ ```
373
+ It is also possible to configure peek permanently with the configure method.
374
+ ```
375
+ peek.configure(prefix="==> ")
376
+ peek(12)
377
+ ```
378
+ will print
379
+ ```
380
+ ==> 12
381
+ ```
382
+ It is arguably easier to say:
383
+ ```
384
+ peek.prefix = "==> "
385
+ peek(12)
386
+ ```
387
+ or even
388
+ ```
389
+ peek.pr = "==> "
390
+ peek(12)
391
+ ```
392
+ to print
393
+ ```
394
+ ==> 12
395
+ ```
396
+ Yet another way to configure peek is to get a new instance of peek with peek.new() and the required configuration:
397
+ ```
398
+ z = peek.new(prefix="==> ")
399
+ z(12)
400
+ ```
401
+ will print
402
+ ```
403
+ ==> 12
404
+ ```
405
+
406
+ Or, yet another possibility is to clone peek (optionally with modified attributes):
407
+ ```
408
+ peek1 = peek.clone(show_time=True)
409
+ peek2 = peek.clone()
410
+ peek2.show_time = True
411
+ ```
412
+ After this `peek1` and `peek2` will behave similarly (but they are not the same!)
413
+
414
+ ## prefix / pr
415
+ ```
416
+ peek('world', prefix='hello -> ')
417
+ ```
418
+ prints
419
+ ```
420
+ hello -> 'world'
421
+ ```
422
+
423
+ `prefix` can be a function, too.
424
+
425
+ ```
426
+ import time
427
+ def unix_timestamp():
428
+ return f"{int(time.time())} "
429
+ hello = "world"
430
+ peek.prefix = unix_timestamp
431
+ peek(hello)
432
+ ```
433
+ prints
434
+ ```
435
+ 1613635601 hello='world'
436
+ ```
437
+
438
+ ## output
439
+ This will allow the output to be handled by something else than the default (output being written to stdout).
440
+
441
+ The `output` attribute can be
442
+
443
+ * a callable that accepts at least one parameter (the text to be printed)
444
+ * a string or Path object that will be used as the filename
445
+ * a text file that is open for writing/appending
446
+
447
+ In the example below,
448
+ ```
449
+ import sys
450
+ peek(1, output=print)
451
+ peek(2, output=sys.stderr)
452
+ with open("test", "a+") as f:
453
+ peek(3, output=f)
454
+ peek(4, output="")
455
+ ```
456
+ * `1` will be printed to stdout
457
+ * `2` will be printed to stderr
458
+ * `3` will be appended to the file test
459
+ * nothing will be printed/written
460
+
461
+ As `output` may be a callable, you can even use this to automatically log any `peek` output:
462
+ ```
463
+ import logging
464
+ logging.basicConfig(level="INFO")
465
+ log = logging.getLogger("demo")
466
+ peek.configure(output=log.info)
467
+ a = {1, 2, 3, 4, 5}
468
+ peek(a)
469
+ a.remove(4)
470
+ peek(a)
471
+ ```
472
+ will print to stdout:
473
+ ```
474
+ INFO:demo:a={1, 2, 3, 4, 5}
475
+ INFO:demo:a={1, 2, 3, 5}
476
+ ```
477
+ Finally, you can specify the following strings:
478
+ ```
479
+ "stderr" to print to stderr
480
+ "stdout" to print to stdout
481
+ "stdout_nocolor" to print to stdout without any colors
482
+ "null" or "" to completely ignore (dummy) output
483
+ "logging.debug" to use logging.debug
484
+ "logging.info" to use logging.info
485
+ "logging.warning" to use logging.warning
486
+ "logging.error" to use logging.error
487
+ "logging.critical" to use logging.critical
488
+ ```
489
+ E.g.
490
+ ```
491
+ import sys
492
+ peek.output = "stderr"
493
+ ```
494
+ to print to stderr.
495
+
496
+ ## serialize
497
+ This will allow to specify how argument values are to be serialized to displayable strings.
498
+ The default is `pformat` (from `pprint`), but this can be changed.
499
+ For example, to handle non-standard datatypes in a custom fashion.
500
+ The serialize function should accept at least one parameter.
501
+ The function may optionally accept the keyword arguments `width` and `sort_dicts`, `compact`, `indent`, `underscore_numbers` and `depth`.
502
+
503
+ ```
504
+ def add_len(obj):
505
+ if hasattr(obj, "__len__"):
506
+ add = f" [len={len(obj)}]"
507
+ else:
508
+ add = ""
509
+ return f"{repr(obj)}{add}"
510
+
511
+ l7 = list(range(7))
512
+ hello = "world"
513
+ peek(7, hello, l7, serialize=add_len)
514
+ ```
515
+ prints
516
+ ```
517
+ 7, hello='world' [len=5], l7=[0, 1, 2, 3, 4, 5, 6] [len=7]
518
+ ```
519
+
520
+ ## show_line_number / sln
521
+ If True, adds the `peek()` call's line number and possibly the filename and parent function to `peek()`'s output.
522
+
523
+ ```
524
+ peek.configure(show_line_number=True)
525
+ def shout():
526
+ hello="world"
527
+ peek(hello)
528
+ shout()
529
+ ```
530
+ prints something like
531
+ ```
532
+ #5 in shout() ==> hello='world'
533
+ ```
534
+
535
+ If "no parent" or "n", the parent function will not be shown.
536
+ ```
537
+ peek.show_line_number = "n"
538
+ def shout():
539
+ hello="world"
540
+ peek(hello)
541
+ shout()
542
+ ```
543
+ prints something like
544
+ ```
545
+ #5 ==> hello='world'
546
+ ```
547
+ Note that if you call `peek` without any arguments, the line number is always shown, regardless of the status `show_line_number`.
548
+
549
+ See below for an explanation of the information provided.
550
+
551
+ ## show_time / st
552
+ If True, adds the current time to `peek()`'s output.
553
+
554
+ ```
555
+ peek.configure(show_time=True)
556
+ hello="world"
557
+ peek(hello)
558
+ ```
559
+ prints something like
560
+ ```
561
+ @ 13:01:47.588125 ==> hello='world'
562
+ ```
563
+
564
+ ## show_delta / sd
565
+ If True, adds the number of seconds since the start of the program to `peek()`'s output.
566
+ ```
567
+ import time
568
+ peek.show_delta = True
569
+ french = "bonjour le monde"
570
+ english = "hallo world"
571
+ peek(english)
572
+ time.sleep(1)
573
+ peek(french)
574
+ ```
575
+ prints something like
576
+ ```
577
+ delta=0.088 ==> english='hallo world'
578
+ delta=1.091 ==> french='bonjour le monde'
579
+ ```
580
+
581
+ ## show_enter / se
582
+ When used as a decorator or context manager, by default, peek ouputs a line when the decorated the
583
+ function is called or the context manager is entered.
584
+
585
+ With `show_enter=False` this line can be suppressed.
586
+
587
+ ## show_exit / sx
588
+ When used as a decorator or context manager, by default, peek ouputs a line when the decorated the
589
+ function returned or the context manager is exited.
590
+
591
+ With `show_exit=False` this line can be suppressed.
592
+
593
+
594
+ ## show_traceback
595
+ When show_traceback is True, the ordinary output of peek() will be followed by a printout of the
596
+ traceback, similar to an error traceback.
597
+
598
+ ```
599
+ def x():
600
+ peek(show_traceback=True)
601
+
602
+ x()
603
+ x()
604
+ ```
605
+ prints
606
+ ```
607
+ #4 in x()
608
+ Traceback (most recent call last)
609
+ File "c:\Users\Ruud\Dropbox (Personal)\Apps\Python Ruud\peek\x.py", line 6, in <module>
610
+ x()
611
+ File "c:\Users\Ruud\Dropbox (Personal)\Apps\Python Ruud\peek\x.py", line 4, in x
612
+ peek()
613
+ #4 in x()
614
+ Traceback (most recent call last)
615
+ File "c:\Users\Ruud\Dropbox (Personal)\Apps\Python Ruud\peek\x.py", line 7, in <module>
616
+ x()
617
+ File "c:\Users\Ruud\Dropbox (Personal)\Apps\Python Ruud\peek\x.py", line 4, in x
618
+ peek()
619
+ ```
620
+ The `show_traceback` functionality is also available when peek is used as a decorator or context manager.
621
+
622
+ ## line_length / ll
623
+ This attribute is used to specify the line length (for wrapping). The default is 160.
624
+ Peek tries to keep all output on one line, but if it can't it will wrap:
625
+
626
+ ```
627
+ d = dict(a1=1,a2=dict(a=1,b=1,c=3),a3=list(range(10)))
628
+ peek(d)
629
+ peek(d, line_length=80)
630
+ ```
631
+ prints
632
+ ```
633
+ d={'a1': 1, 'a2': {'a': 1, 'b': 1, 'c': 3}, 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
634
+ d=
635
+ {'a1': 1,
636
+ 'a2': {'a': 1, 'b': 1, 'c': 3},
637
+ 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
638
+ ```
639
+
640
+ ## color / col and color_value / col_val
641
+ The color attribute is used to specify the color of the output.
642
+ There's a choice of black, white, red, green, blue, cyan, magenta and yellow.
643
+ To set the color to 'nothing', use "-".
644
+
645
+ 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.
646
+
647
+ For instance:
648
+
649
+ ```
650
+ x = "x"
651
+ y = "y"
652
+ peek(x, y)
653
+ peek(x, y, color_value="green")
654
+ peek(x, y, color="red")
655
+ peek(x, y, color="red", color_value="green")
656
+ ```
657
+
658
+ will result in:
659
+
660
+ <img src="https://www.salabim.org/peek/peek_colors.png">
661
+
662
+ Of course, color and color_value may be specified in a peek.toml file, to make all peek output in a specified color.
663
+
664
+ > [!NOTE]
665
+ >
666
+ > The color and color_value attributes are only applied when using stdout as output.
667
+ >
668
+ > Colors can be ignored completely by using `peek.output = "stdout_nocolor"`.
669
+
670
+ ## compact
671
+ This attribute is used to specify the compact parameter for `pformat` (see the pprint documentation
672
+ for details). `compact` is False by default.
673
+
674
+ ```
675
+ a = 9 * ["0123456789"]
676
+ peek.line_length = 80
677
+ peek(a)
678
+ peek(a, compact=True)
679
+ ```
680
+ prints
681
+ ```
682
+ a=
683
+ ['0123456789',
684
+ '0123456789',
685
+ '0123456789',
686
+ '0123456789',
687
+ '0123456789',
688
+ '0123456789',
689
+ '0123456789',
690
+ '0123456789',
691
+ '0123456789']
692
+ a=
693
+ ['0123456789', '0123456789', '0123456789', '0123456789', '0123456789',
694
+ '0123456789', '0123456789', '0123456789', '0123456789']
695
+ ```
696
+
697
+ ## indent
698
+ This attribute is used to specify the indent parameter for `pformat` (see the pprint documentation
699
+ for details). `indent` is 1 by default.
700
+
701
+ ```
702
+ s = "01234567890012345678900123456789001234567890"
703
+ peek.line_length = 80
704
+ peek( [s, [s]])
705
+ peek( [s, [s]], indent=4)
706
+ ```
707
+ prints
708
+ ```
709
+ [s, [s]]=
710
+ ['01234567890012345678900123456789001234567890',
711
+ ['01234567890012345678900123456789001234567890']]
712
+ [s, [s]]=
713
+ [ '01234567890012345678900123456789001234567890',
714
+ ['01234567890012345678900123456789001234567890']]
715
+ ```
716
+
717
+ ## depth
718
+ This attribute is used to specify the depth parameter for `pformat` (see the pprint documentation
719
+ for details). `depth` is `1000000` by default.
720
+
721
+ ```
722
+ s = "01234567890012345678900123456789001234567890"
723
+ peek([s,[s,[s,[s,s]]]])
724
+ peek([s,[s,[s,[s,s]]]], depth=3)
725
+ ```
726
+ prints
727
+ ```
728
+ [s,[s,[s,[s,s]]]]=
729
+ ['01234567890012345678900123456789001234567890',
730
+ ['01234567890012345678900123456789001234567890',
731
+ ['01234567890012345678900123456789001234567890',
732
+ ['01234567890012345678900123456789001234567890',
733
+ '01234567890012345678900123456789001234567890']]]]
734
+ [s,[s,[s,[s,s]]]]=
735
+ ['01234567890012345678900123456789001234567890',
736
+ ['01234567890012345678900123456789001234567890',
737
+ ['01234567890012345678900123456789001234567890', [...]]]]
738
+ ```
739
+
740
+ ## wrap_indent
741
+ This specifies the indent string if the output does not fit in the line_length (has to be wrapped).
742
+ Rather than a string, wrap_indent can be also be an integer, in which case the wrap_indent will be that amount of blanks.
743
+ The default is 4 blanks.
744
+
745
+ E.g.
746
+ ```
747
+ d = dict(a1=1,a2=dict(a=1,b=1,c=3),a3=list(range(10)))
748
+ peek.line_length = 80
749
+ peek(d, wrap_indent=" ")
750
+ peek(d, wrap_indent="....")
751
+ peek(d, wrap_indent=2)
752
+ ```
753
+ prints
754
+ ```
755
+ d=
756
+ {'a1': 1,
757
+ 'a2': {'a': 1, 'b': 1, 'c': 3},
758
+ 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
759
+ d=
760
+ ....{'a1': 1,
761
+ .... 'a2': {'a': 1, 'b': 1, 'c': 3},
762
+ .... 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
763
+ d=
764
+ {'a1': 1,
765
+ 'a2': {'a': 1, 'b': 1, 'c': 3},
766
+ 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
767
+ ```
768
+
769
+ ## enabled
770
+ Can be used to disable the output:
771
+ ```
772
+ peek.enabled = False
773
+ s = 'the world is '
774
+ peek(s + 'perfect.')
775
+ peek.enabled = True
776
+ peek(s + 'on fire.')
777
+ ```
778
+ prints
779
+ ```
780
+ s + 'on fire.'='the world is on fire.'
781
+ ```
782
+ and nothing about a perfect world.
783
+
784
+ ## sort_dicts
785
+ By default, peek does not sort dicts (printed by pprint). However, it is possible to get the
786
+ default pprint behaviour (i.e. sorting dicts) with the sorted_dicts attribute:
787
+
788
+ ```
789
+ world = {"EN": "world", "NL": "wereld", "FR": "monde", "DE": "Welt"}
790
+ peek(world))
791
+ peek(world, sort_dicts=False)
792
+ peek(world, sort_dicts=True)
793
+ ```
794
+ prints
795
+ ```
796
+ world={'EN': 'world', 'NL': 'wereld', 'FR': 'monde', 'DE': 'Welt'}
797
+ world={'EN': 'world', 'NL': 'wereld', 'FR': 'monde', 'DE': 'Welt'}
798
+ world={'DE': 'Welt', 'EN': 'world', 'FR': 'monde', 'NL': 'wereld'}
799
+ ```
800
+
801
+ Note that under Python <=3.7, dicts are always printed sorted.
802
+
803
+ ## underscore_numbers / un
804
+
805
+ By default, peek does not add underscores in big numbers (printed by pprint). However, it is possible to get the
806
+ default pprint behaviour with the underscore_numbers attribute:
807
+
808
+ ```
809
+ numbers = dict(one= 1, thousand= 1000, million=1000000, x1234567890= 1234567890)
810
+ peek(numbers)
811
+ peek(numbers, underscore_numbers=True)
812
+ peek(numbers, un=False)
813
+ ```
814
+ prints
815
+ ```
816
+ numbers={'one': 1, 'thousand': 1000, 'million': 1000000, 'x1234567890': 1234567890}
817
+ numbers={'one': 1, 'thousand': 1_000, 'million': 1_000_000, 'x1234567890': 1_234_567_890}
818
+ numbers={'one': 1, 'thousand': 1000, 'million': 1000000, 'x1234567890': 1234567890}
819
+ ```
820
+
821
+ ## seperator / sep
822
+
823
+ By default, pairs (on one line) are separated by `, `.
824
+ It is possible to change this with the attribute ` separator`:
825
+
826
+ ```
827
+ a = "abcd"
828
+ b = 1
829
+ c = 1000
830
+ d = list("peek")
831
+ peek(a, (b, c), d)
832
+ peek(a, (b, c), d, separator=" | ")
833
+ ```
834
+ prints
835
+ ```
836
+ a='abcd', (b,c)=(1, 1000), d=['peek', 'c', 'e', 'c', 'r', 'e', 'a', 'm']
837
+ a='abcd' | (b,c)=(1, 1000) | d=['peek', 'c', 'e', 'c', 'r', 'e', 'a', 'm']
838
+ ```
839
+ Note that under Python <=3.7, numbers are never printed with underscores.
840
+
841
+ ## context_separator
842
+
843
+ By default the line_number, time and/or delta are followed by ` ==> `.
844
+ It is possible to change this with the attribute `context_separator`:
845
+ ```
846
+ a = "abcd"
847
+ peek(a,show_time=True)
848
+ peek(a, show_time=True, context_separator = ' \u279c ')
849
+ ```
850
+ prints:
851
+ ```
852
+ @ 09:05:38.693840 ==> a='abcd'
853
+ @ 09:05:38.707914 ➜ a='abcd'
854
+ ```
855
+ ## equals_separator
856
+ By default name of a variable and its value are separated by `= `.
857
+ It is possible to change this with the attribute `equals_separator`:
858
+
859
+ ```
860
+ a = "abcd"
861
+ peek(a)
862
+ peek(a, equals_separator = ' == ")
863
+ ```
864
+ prints:
865
+ ```
866
+ a='abcd'
867
+ a == 'abcd'
868
+ ```
869
+ ## quote_string / qs
870
+ If True (the default setting) strings will be displayed with surrounding quotes (like repr).
871
+ If False, string will be displayed without surrounding quotes (like str).
872
+ E.g.
873
+
874
+ ```
875
+ test='test'
876
+ peek('==>', test)
877
+ peek('==>', test, quote_string=False)
878
+ ```
879
+ This will print:
880
+ ```
881
+ '==>', test='test'
882
+ ==>, test=test
883
+ ```
884
+ > [!NOTE]
885
+ >
886
+ > This setting does not influence how strings are displayed within other data structures, like dicts and lists.
887
+
888
+ ## values_only / vo
889
+ If False (the default), both the left-hand side (if possible) and the
890
+ value will be printed. If True, the left hand side will be suppressed:
891
+
892
+ ```
893
+ hello = "world"
894
+ peek(hello, 2 * hello)
895
+ peek(hello, 2 * hello, values_only=True)
896
+ ```
897
+ prints
898
+ ```
899
+ hello='world', 2 * hello='worldworld'
900
+ 'world', 'worldworld'
901
+ ```
902
+ The values=True version of peek can be seen as a supercharged print/pprint.
903
+
904
+
905
+ ## values_only_for_fstrings / voff
906
+ If False (the default), both the original f-string and the
907
+ value will be printed for f-strings.
908
+ If True, the left_hand side will be suppressed in case of an f-string:
909
+
910
+ ```
911
+ x = 12.3
912
+ peek(f"{x:0.3e}")
913
+ peek.values_only_for_fstrings = True
914
+ peek(f"{x:0.3e}")
915
+ ```
916
+ prints
917
+ ```
918
+ f"{x:0.3e}"='1.230e+01'
919
+ '1.230e+01'
920
+ ```
921
+ Note that if `values_only` is True, f-string will be suppressed, regardless of `values_only_for_fstrings`.
922
+
923
+ ## return_none
924
+ Normally, `peek()`returns the values passed directly, which is usually fine. However, when used in a notebook
925
+ or REPL, that value will be shown, and that can be annoying. Therefore, if `return_none`is True, `peek()`will
926
+ return None and thus not show anything.
927
+
928
+ ```
929
+ a = 3
930
+ b = 4
931
+ print(peek(a, b))
932
+ peek.return_none = True
933
+ print(peek(a, b))
934
+ ```
935
+ prints
936
+ ```
937
+ a=3, b=4
938
+ (3, 4)
939
+ a=3, b=4
940
+ None
941
+ ```
942
+
943
+ ## enforce_line_length
944
+ If enforce_line_length is True, all output lines are explicitly truncated to the given line_length, even those that are not truncated by pformat.
945
+
946
+ ## delta
947
+ The delta attribute can be used to (re)set the current delta, e.g.
948
+ ```
949
+ peek.delta = 0
950
+ print(peek.delta)
951
+ ```
952
+ prints a value that id slightly more than 0.
953
+
954
+ # Return a string instead of sending to output
955
+
956
+ `peek(*args, as_str=True)` is like `peek(*args)` but the output is returned as a string instead
957
+ of written to output.
958
+
959
+ ```
960
+ hello = "world"
961
+ s = peek(hello, as_str=True)
962
+ print(s, end="")
963
+ ```
964
+ prints
965
+ ```
966
+ hello='world'
967
+ ```
968
+
969
+ Note that if enabled=False, the call will return the null string (`""`).
970
+
971
+ # Disabling peek's output
972
+
973
+ ```
974
+ peek1 = peek.fork(show_delta=True)
975
+ peek(1)
976
+ peek1(2)
977
+ peek.enabled = False
978
+ peek(3)
979
+ peek1(4)
980
+ peek1.enabled = True
981
+ peek(5)
982
+ peek1(6)
983
+ print(peek1.enabled)
984
+ ```
985
+ prints
986
+ ```
987
+ 1
988
+ delta=0.011826 ==> 2
989
+ 5
990
+ delta=0.044893 ==> 6
991
+ True
992
+ ```
993
+ Of course `peek()` continues to return its arguments when disabled, of course.
994
+
995
+ It is also possible to suppress output with the provided attribute (see above).
996
+
997
+ # Using filter to control peek output
998
+
999
+ It is possible to define a filter function that determines whether peek output should be suppressed
1000
+ By default, the filter is defined as "" denoting no filter.
1001
+
1002
+ Suppose we a (float) level to a peek statement. By default, this level is 0. E.g.:
1003
+
1004
+ ```
1005
+ peek("critical", level=0)
1006
+ peek("important", level=1)
1007
+ peek("warning", level=2)
1008
+ ```
1009
+
1010
+ With `peek.filter ="level <= 1"` the program makes sure that level=2 is not displayed at all.
1011
+
1012
+ It is possible to use more than one attribute, like
1013
+
1014
+ ```
1015
+ peek.filter = "color == 'blue' and delta > 5"
1016
+ ```
1017
+ As an alternative to `enabled` we can also say
1018
+ ```
1019
+ peek.filter = "False"
1020
+ ```
1021
+
1022
+ # Copying to the clipboard
1023
+
1024
+ It is possible to copy a value to the clipboard. There are two ways:
1025
+
1026
+ ### With peek(*args, to_clipboard=True)
1027
+
1028
+ With the optional keyword argument, *to_clipboard*:
1029
+
1030
+ - If to_clipboard==False (the default), nothing is copied to the clipboard.
1031
+ - If to_clipboard==True, the *value* of the the *last* parameter will be copied to the clipboard. The output itself is as usual.
1032
+
1033
+ Examples:
1034
+
1035
+ ```
1036
+ part1 = 200
1037
+ extra = "extra"
1038
+ peek(part1, extra, to_clipboard=True)
1039
+ # will print part1=200, extra='extra' and copy `extra` to the clipboard
1040
+ peek(200, to_clipboard=True)\
1041
+ # will print 200 and copy 200 to the clipboard
1042
+ peek(to_clipboard=True)
1043
+ # will print #5 (or similar) and empty the clipboard
1044
+ ```
1045
+
1046
+ Note that *to_clipboard* is a peek attribute.
1047
+
1048
+ If as_str==True, to_clipboard is ignored.
1049
+
1050
+ ### With peek.to_clipboard
1051
+
1052
+ Just use peek.to_clipboard to copy any value to the clipboard. So,
1053
+ ```
1054
+ part1 = 1234
1055
+ peek.to_clipboard(part1)
1056
+ ```
1057
+
1058
+ will copy `1234` to the clipboard and write `copied to clipboard: 1234` to the console.
1059
+ If the confirmation message is not wanted, just add confirm=False, like
1060
+
1061
+ ```
1062
+ peek.to_clipboard(part1, confirm=False)
1063
+ ```
1064
+
1065
+ ### General
1066
+
1067
+ Implementation detail: the clipboard functionality uses pyperclip, apart from under Pythonista, where the
1068
+ builtin clipboard module is used.
1069
+
1070
+ This functionality is particularly useful for entering an answer of an *Advent of Code* solution to the site.
1071
+
1072
+ # Interpreting the line number information
1073
+
1074
+ When `show_line_number` is True or peek() is used without any parameters, the output will contain the line number like:
1075
+ ```
1076
+ #3 ==> a='abcd'
1077
+ ```
1078
+ If the line resides in another file than the main file, the filename (without the path) will be shown as well:
1079
+ ```
1080
+ #30[foo.py] ==> foo='Foo'
1081
+ ```
1082
+ And finally when used in a function or method, that function/method will be shown as well:
1083
+ ```
1084
+ #456[foo.py] in square_root ==> x=123
1085
+ ```
1086
+ The parent function can be suppressed by setting `show_line_number` or `sln` to `"n"` or `"no parent"`.
1087
+
1088
+ # Configuring at import time
1089
+
1090
+ It can be useful to configure peek at import time. This can be done by providing a `peek.toml` file which
1091
+ can contain any attribute configuration overriding the standard settings.
1092
+ E.g. if there is an `peek.toml` file with the following contents
1093
+
1094
+ ```
1095
+ outpout = "stderr"
1096
+ show_time = true
1097
+ ll = 160
1098
+ compact = true
1099
+ ```
1100
+ in the same folder as the application, this program:
1101
+ ```
1102
+ hello = "world"
1103
+ peek(hello)
1104
+ ```
1105
+ will print to stderr (rather than stdout):
1106
+ ```
1107
+ @ 14:53:41.392190 ==> hello='world'
1108
+ ```
1109
+ 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.
1110
+
1111
+ Please observe that toml values are slightly different from their Python equivalents:
1112
+ ```
1113
+ -----------------------------------
1114
+ Python toml
1115
+ -----------------------------------
1116
+ True true
1117
+ False false
1118
+ strings preferably double quoted
1119
+ -----------------------------------
1120
+ ```
1121
+ Note that not-specified attributes will remain the default settings.
1122
+
1123
+ Just for your information, the core developer of peek uses a peek.toml file with the contents:
1124
+ ```
1125
+ line_length = 160
1126
+ color = "yellow"
1127
+ quote_string = false
1128
+ ```
1129
+
1130
+
1131
+
1132
+ # Working with multiple instances of peek
1133
+
1134
+ Normally, only the `peek` object is used.
1135
+
1136
+ It can be useful to have multiple instances, e.g. when some of the debugging has to be done with context information
1137
+ and others requires an alternative prefix.
1138
+
1139
+ There are several ways to obtain a new instance of peek:
1140
+
1141
+ * by using `peek.new()`
1142
+
1143
+ With this a new peek object is created with the default attributes
1144
+ and possibly peek.toml overrides.
1145
+ * by using `peek.new(ignore_toml=True)`
1146
+
1147
+ With this a new peek object is created with the default attributes. Any peek.toml files are ignored.
1148
+ * by using `peek.fork()`
1149
+
1150
+ 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.
1151
+ * by using `peek.clone()`, which copies all attributes from peek()
1152
+
1153
+ 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.
1154
+
1155
+ * with `peek()` used as a context manager
1156
+
1157
+ In either case, attributes can be added to override the default ones.
1158
+
1159
+ ### Example
1160
+ ```
1161
+ peek_with_line_number = peek.fork(show_line_number=True)
1162
+ peek_with_new_prefix = peek.new(prefix="==> ")
1163
+ peek_with_new_prefix_and_time = peek_with_new_prefix.clone(show_time=True)
1164
+ hello="world"
1165
+ peek_with_line_number(hello)
1166
+ peek_with_new_prefix(hello)
1167
+ peek_with_new_prefix_and_time(hello)
1168
+ peek.equals_separator = " == " # this affects only the forked objects
1169
+ peek_with_line_number(hello)
1170
+ peek_with_new_prefix(hello)
1171
+ peek_with_new_prefix_and_time(hello)
1172
+ with peek(prefix="peek_cm ") as peek_cm:
1173
+ peek_cm(hello)
1174
+ peek(hello)
1175
+ ```
1176
+ prints something like
1177
+ ```
1178
+ #28 ==> hello='world'
1179
+ ==> hello='world'
1180
+ ==> @ 09:55:52.122818 ==> hello='world'
1181
+ #32 ==> hello == 'world'
1182
+ ==> hello='world'
1183
+ ==> @ 09:55:52.125928 ==> hello='world'
1184
+ peek_cm enter
1185
+ peek_cm hello == 'world'
1186
+ hello == 'world'
1187
+ peek_cm exit in 0.001843 seconds
1188
+ ```
1189
+
1190
+ ## ignore_toml
1191
+ 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.
1192
+
1193
+ ### Example
1194
+ Suppose we have a `peek.toml` file in the current directory with the contents
1195
+ ```
1196
+ {prefix="==>"}
1197
+ ```
1198
+ Then
1199
+ ```
1200
+ peek_post_toml = peek.new()
1201
+ peek_ignore_toml = peek.new(ignore_toml=True)
1202
+ hello = "world"
1203
+ peek(hello)
1204
+ peek_post_toml(hello)
1205
+ peek_ignore_toml(hello)
1206
+ ```
1207
+ prints
1208
+ ```
1209
+ ==>hello='world'
1210
+ ==>hello='world'
1211
+ hello='world'
1212
+ ```
1213
+
1214
+ # Test script
1215
+
1216
+ On GitHub is a file `test_peek.py` that tests (and thus also demonstrates) most of the functionality
1217
+ of peek.
1218
+
1219
+ It is very useful to have a look at the tests to see the features (some may be not covered (yet) in this readme).
1220
+
1221
+ # Using peek in a REPL
1222
+
1223
+ Peek may be used in a REPL, but with limited functionality:
1224
+ * all arguments are just presented as such, i.e. no left-hand side, e.g.
1225
+ ```
1226
+ >> hello = "world"
1227
+ >>> peek(hello, hello * 2)
1228
+ 'hello', 'hellohello'
1229
+ ('hello', 'hellohello')
1230
+ ```
1231
+ * line numbers are never shown
1232
+ * use as a decorator is not supported
1233
+ * use as a context manager is not supported
1234
+
1235
+ # Alternative to `peek`
1236
+
1237
+ Sometimes, even peek is too long during a debug session or it is not suitable to use the name peek.
1238
+
1239
+ In that case, it is possible to use p instead
1240
+ ```
1241
+ from peek import p
1242
+ ```
1243
+ The `p` object is a *fork* of peek. That means that attributes of `peek` are propagated to `p`, unless overridden.
1244
+
1245
+
1246
+ # Limitations
1247
+
1248
+ It is not possible to use peek:
1249
+ * from a frozen application (e.g. packaged with PyInstaller)
1250
+ * when the underlying source code has changed during execution
1251
+
1252
+ # Changelog
1253
+
1254
+ The changelog can be found here:
1255
+
1256
+ * https://github.com/salabim/peek/blob/main/changelog.md or
1257
+ * https://salabim.org/peek/changelog
1258
+
1259
+
1260
+ # Acknowledgement
1261
+
1262
+ The **peek** package is inspired by the **IceCream** package, but is a
1263
+ nearly complete rewrite. See https://github.com/gruns/icecream
1264
+
1265
+ Many thanks to the author Ansgar Grunseid / grunseid.com / grunseid@gmail.com .
1266
+
1267
+ The peek package is a rebrand of the **ycecream** package, with enhancements.
1268
+
1269
+ # Differences with IceCream
1270
+
1271
+ The peek module was originally a fork of **IceCream**, but has many differences:
1272
+
1273
+ ```
1274
+ -----------------------------------------------------------------------------------------
1275
+ characteristic peek IceCream
1276
+ -----------------------------------------------------------------------------------------
1277
+ default name peek ic
1278
+ import method import peek from icecream import ic
1279
+ number of files 1 several
1280
+ usable without installation yes no
1281
+ can be used as a decorator yes no
1282
+ can be used as a context manager yes no
1283
+ can show traceback yes no
1284
+ PEP8 (Pythonic) API yes no
1285
+ sorts dicts no by default, optional *) yes
1286
+ supports compact, indent,
1287
+ and underscore_numbers
1288
+ parameters of pprint yes **) no
1289
+ use from a REPL limited functionality no
1290
+ external configuration via toml file no
1291
+ level control yes no
1292
+ observes line_length correctly yes no
1293
+ benchmarking functionality yes no
1294
+ suppress f-strings at left hand optional no
1295
+ indentation 4 blanks (overridable) length of prefix
1296
+ forking and cloning yes no
1297
+ test script pytest unittest
1298
+ colorize ***) yes, off by default yes, on by default
1299
+ -----------------------------------------------------------------------------------------
1300
+ *) under Python <= 3.7, dicts are always sorted
1301
+ **) under Python <= 3.7, numbers are never underscored
1302
+ ***) peek allows selection of a color, whereas IceCream does coloring based on contents
1303
+
1304
+ ```
1305
+ ![PyPI](https://img.shields.io/pypi/v/peek-python) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/peek-python) ![PyPI - Implementation](https://img.shields.io/pypi/implementation/peek)
1306
+ ![PyPI - License](https://img.shields.io/pypi/l/peek) ![ruff](https://img.shields.io/badge/style-ruff-41B5BE?style=flat)
1307
+ ![GitHub last commit](https://img.shields.io/github/last-commit/salabim/peek)
1308
+