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