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