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