[HN Gopher] Icecream: Never use print() to debug again in Python
       ___________________________________________________________________
        
       Icecream: Never use print() to debug again in Python
        
       Author : polm23
       Score  : 419 points
       Date   : 2021-03-30 05:22 UTC (17 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | belorn wrote:
       | I am looking at the examples (and the comments here about
       | f-strings), but neither fit my own usage of debugging with print.
       | When I use print, it is mostly for two reasons. To see the
       | structure of a dict (often with multiple layers of dicts inside),
       | or to get dir(x) to see what methods and attributes of a library
       | object, since the documentation is not always so forthcoming.
        
       | yuribro wrote:
       | This is similar to an earlier package called "q"[0]
       | 
       | [0] https://github.com/zestyping/q
        
       | [deleted]
        
       | andix wrote:
       | Just don't use print for debugging!
       | 
       | Either add meaningful and logging to your software (which also
       | other technical people can use).
       | 
       | Or just use a debugger and breakpoints.
        
       | jshprentz wrote:
       | I use PySnooper[1] when code behavior deviates inscrutably from
       | my mental model. Like Icecream, PySnooper exhorts, "Never use
       | print for debugging again." A simple example:
       | import pysnooper              @pysnooper.snoop()         def
       | add_up(numbers):             total = 0             for number in
       | numbers:                 total += number             return total
       | add_up([123, 456])
       | 
       | When run, PySnooper prints the activity of the decorated function
       | or method:                       $ python example.py
       | Source path:... /home/joel/example.py             Starting var:..
       | numbers = [123, 456]             08:46:58.742543 call         4
       | def add_up(numbers):             08:46:58.742692 line         5
       | total = 0             New var:....... total = 0
       | 08:46:58.742722 line         6     for number in numbers:
       | New var:....... number = 123             08:46:58.742755 line
       | 7         total += number             Modified var:.. total = 123
       | 08:46:58.742787 line         6     for number in numbers:
       | Modified var:.. number = 456             08:46:58.742818 line
       | 7         total += number             Modified var:.. total = 579
       | 08:46:58.742847 line         6     for number in numbers:
       | 08:46:58.742876 line         8     return total
       | 08:46:58.742898 return       8     return total
       | Return value:.. 579             Elapsed time: 00:00:00.000405
       | 
       | [1] https://github.com/cool-RR/PySnooper
        
         | alexmojaki wrote:
         | There's also https://github.com/alexmojaki/snoop
         | 
         | For comparison:                   19:32:30.66 >>> Call to
         | add_up in File "/home/joel/example.py", line 5
         | 19:32:30.66 ...... numbers = [123, 456]         19:32:30.66
         | ...... len(numbers) = 2         19:32:30.66    5 | def
         | add_up(numbers):         19:32:30.66    6 |     total = 0
         | 19:32:30.66    7 |     for number in numbers:
         | 19:32:30.66 .......... number = 123         19:32:30.66    8 |
         | total += number         19:32:30.66 .............. total = 123
         | 19:32:30.66    7 |     for number in numbers:
         | 19:32:30.66 .......... number = 456         19:32:30.66    8 |
         | total += number         19:32:30.66 .............. total = 579
         | 19:32:30.66    7 |     for number in numbers:
         | 19:32:30.66    9 |     return total         19:32:30.66 <<<
         | Return value from add_up: 579
        
           | jshprentz wrote:
           | From the README, Snoop is "primarily meant to be a more
           | featureful and refined version of PySnooper. It also includes
           | its own version of icecream and some other nifty stuff."
           | 
           | Thanks. I'll give it a try.
        
           | _false wrote:
           | Just what I was looking for, thanks
        
       | qwerty456127 wrote:
       | > prints both its own arguments and the values of those
       | arguments.
       | 
       | That's not enough.
       | 
       | Almost always I also want the argument _type_.
       | 
       | Occasionally a unique (i.e. as precise as possible) sortable
       | (i.e. with leading zeros) time stamp also comes useful.
        
         | qwerty456127 wrote:
         | > sortable (i.e. with leading zeros) time stamp
         | 
         | Not just with leading zeros but also written in the YYYY-MM-DD-
         | HH-... format.
        
         | grun wrote:
         | This would be great to have.
         | 
         | I don't use types in my Python much. Yet at least.
         | 
         | Can you submit a PR to add this? That'd be awesome.
        
       | kleiba wrote:
       | Instead of introducing a new library, why not just instruct your
       | editor to do the heavy lifting for you? For instance, I've
       | written a small Emacs function that asks for the argument to
       | print in the Minibuffer and then inserts it once quoted, once
       | unquoted.
       | 
       | It's quite nice to have such a function for any programming
       | language you use and bind it to the same keyboard short-cut.
        
         | xaedes wrote:
         | For one, people not using emacs, or whatever editor it would
         | be, can use it.
        
           | kleiba wrote:
           | Sure, but why would anyone not use Emacs?!
        
           | kleiba wrote:
           | Then instruct whatever is your favorite editor likewise.
        
       | petr25102018 wrote:
       | A while back I made a list of the Python debugging tools:
       | https://stribny.name/blog/2019/06/debugging-python-programs/
        
         | grun wrote:
         | You should add IceCream!
        
       | kalipso wrote:
       | wait isnt that just enforcing bad practice? doesnt python have
       | proper debuggers with breakpoints ect? why in hell should i use a
       | lib to print stuff just for debugging? i mean why not having a
       | proper logging lib and pipe some statements to [debug] or
       | whatever i dont get it.
        
         | kevincox wrote:
         | Requirements for logging and debugging are quite different. For
         | logging I probably don't want to print the source expression, I
         | probably want to include a semantic description. For logging it
         | is also intended to live in the code for longer, so I probably
         | want something a touch more readable.
         | 
         | The debugging and logging spaces overlap but there are
         | definitely differences if you really start optimizing for
         | debugging experience. I don't think encouraging bad practices
         | is a problem if the code will be deleted before being
         | submitted.
        
       | mastrsushi wrote:
       | You can also use a modern IDE debugger like Visual Studio. It
       | gives you quicker results and lets you change values at runtime.
       | Instead of print debugging like it's 1978.
       | 
       | Of course then you can't brag to your friends about using Vim or
       | Emacs ;)
        
       | edumucelli wrote:
       | Pycharm is a neat IDE. I know some people do not like IDEs but
       | all the debug tools Pycharm comes with are killer and helps
       | greatly when debugging on a local environment.
        
       | pjmlp wrote:
       | I just use debug trace points.
       | 
       | https://docs.microsoft.com/en-us/visualstudio/python/debuggi...
        
       | dzamlo wrote:
       | If.uou use vscode, the [Puke-Debug](https://marketplace.visualstu
       | dio.com/items?itemName=Zorvalt....) extension is a nice
       | alternative. It allows to easily insert and remove similar print
       | statements, in multiple languages, without adding a dependencies
       | to your project that you need to remove afterwards.
        
       | savrajsingh wrote:
       | Note: VS code made it a lot easier to use the debugger, it's
       | basically set up and ready to go, but this is a great project
       | too!
        
       | aitchnyu wrote:
       | Clojure allows this very brief copypasta `(defmacro dbg [x] `(let
       | [x# ~x] (println "dbg:" '~x "=" x#) x#))`
       | https://blog.jayway.com/2011/03/13/dbg-a-cool-little-clojure...
       | 
       | Wish Python has this by default though.
        
       | cullinap wrote:
       | what's wrong with using print()?
        
       | TheRealNGenius wrote:
       | https://www.wndr.xyz/posts/D5Tuu6YUqrWq8st73mn2Fw==/this-is-...
        
       | jujodi wrote:
       | I just hope I don't forget about this the next time Advent of
       | Code comes around
        
       | dkdk8283 wrote:
       | Pretty neat. Seems better than print.
        
       | zxteloiv wrote:
       | pretty print is not important for me when debugging or logging,
       | only would it make sense if the neat logger/debugger could accept
       | a lazily evaluated expression. That saves some time if the logger
       | is disabled at runtime. Otherwise the ugly print-debug-fix-and-
       | remove pipeline is still there.
        
       | permo-w wrote:
       | Why does it put "ic | " at the beginning? Branding?
       | 
       | Maybe I'm missing something, but it seems unnecessary
        
       | cool-RR wrote:
       | LOL, first time I write a slogan catchy enough that other people
       | copy it :)
       | 
       | https://news.ycombinator.com/item?id=19717786 PySnooper: Never
       | use print for debugging again
        
         | Igelau wrote:
         | It seems like it was a coincidence. See comment thread:
         | 
         | https://github.com/gruns/icecream/commit/ee849b840eb34242aa2...
        
       | makach wrote:
       | sometimes, all you need is a print() statement
        
       | charkubi wrote:
       | The problem with printing variables is when you want to find them
       | there's nothing to search for.
        
         | whalesalad wrote:
         | if you name them well, sure there is.
        
       | oaiey wrote:
       | Is something like that available in .NET. seems possible
       | considering expression support. Maybe with bad performance.
        
       | myaccount80 wrote:
       | Nice. Right now I'm using the following:
       | 
       | print(f'{foo(123)=}')
       | 
       | Which prints: foo(123)='result'
        
       | robsws wrote:
       | This looks cool and all, but why is it called Icecream? I know
       | naming abstract stuff is hard but it feels like this lends itself
       | to a more descriptive name. "ic()" tells me nothing about what
       | the function does.
        
         | Igelau wrote:
         | At its core, it has always called inspect.currentframe(). I
         | suspect the first iteration was a wrapper around
         | inspect.currentframe(), abbreviated as ic(), which was then
         | backronym'd into ice cream.
        
         | NateEag wrote:
         | I think it's a (confusing) two-layer pun.
         | 
         | When read phonetically as letter names, the name "ic" sounds
         | like "I see".
         | 
         | "ic" also is an initialism for "ice cream".
         | 
         | Everyone loves ice cream, and so another low-meaning cutesy-poo
         | pun-ishment of a name was born.
         | 
         | I think. Pure speculation here.
        
           | grun wrote:
           | Bingo.
           | 
           | All the one letter PyPi project names were taken.
        
           | alternatetwo wrote:
           | My first thought was "I scream" ...
        
       | altitudinous wrote:
       | I am always going to use print to debug in every programming
       | language I can until the day I die.
        
         | xnx wrote:
         | It's a failure of debuggers that they haven't cleared the very
         | low bar of obviousness and ease of use of print(). I'm very
         | much a novice, but RStudio was the first environment that made
         | debugging so easy I didn't feel the need to use print().
        
         | wrycoder wrote:
         | "The most effective debugging tool is still careful thought,
         | coupled with judiciously placed print statements."
         | 
         | -- Brian Kernighan, "Unix for Beginners" (1979)
        
         | QuesnayJr wrote:
         | I had this exact thought, but the title gives the wrong
         | impression (at least to me). The package just defines a fancy
         | print.
        
         | 0xFFFE wrote:
         | Absolutely, I don't understand why using print() or its
         | equivalent in other languages is looked down upon. It's quick
         | way to narrow down the "area of search" before bringing in the
         | big guns.
        
           | purerandomness wrote:
           | How is changing code simpler than literally clicking on the
           | line number to set a breakpoint?
        
             | pavon wrote:
             | When I use a debugger I often feel like I'm looking through
             | a soda straw. I can only see the state at that one instance
             | in time. Just because I know the line of code where the
             | exception occurred, doesn't tell me which data caused it,
             | and breaking on exceptions is often too late. Instead I'm
             | stuck hitting continue over and over until I finally see
             | something out of place, realize I went to far and have to
             | start over again. With logging, I have the entire history
             | at my fingertips which I can skim or grep to quickly
             | pinpoint the data that caused things to go wrong.
             | 
             | More fairly, it is a trade-off with the debugger giving
             | wide visibility into state, but narrow visibility
             | temporally, and logging giving narrow visibility into state
             | (just what you logged), but broad temporal visibility. They
             | both have their place, but I find that logging narrows
             | things down more quickly, while the debugger helps me
             | understand the problem by walking through step-by-step,
             | assuming the problem isn't obvious once narrowed down.
        
               | roca wrote:
               | You might be interested in Pernosco: see
               | https://pernos.co/about/overview/ and its related
               | content.
               | 
               | We agree with your critique of traditional debuggers and
               | Pernosco tackles that "temporal visibility" problem head
               | on.
        
               | jonnycomputer wrote:
               | I more or less agree; but I find myself wondering why I
               | so often use Matlab's debugger, but almost never use pdb
               | for python. It is not like I'm not used to using command
               | line tools (I use bash, git, emacs, etc. everyday). It
               | could just be an accident of habit, I don't know.
        
               | matheusmoreira wrote:
               | Would be nice if instead of break points debuggers had
               | log points which stored the values of variables at that
               | point in time. This data can be displayed as a table
               | later.
        
             | kevincox wrote:
             | You said "click", I need to leave my keyboard.
             | 
             | Generally when I am coding I auto-run the tests on save.
             | This means that to printf-debug I just add a message or two
             | (and if I am coding I might already have a couple of useful
             | ones lying around) and save. Then in less than a second I
             | have a trace trough my program in the terminal. If I want
             | to inspect a different variable I just add another print
             | and run again.
             | 
             | With a debugger I need to kill my auto-run command, run the
             | program, set breakpoints, type to see what variables I want
             | to inspect, maybe switch stack frames, maybe step through a
             | bit.
             | 
             | In my mind printf is like an automated debugger. I just put
             | the info I want to see into the program and it does all of
             | the printing for me. And when I find the problem I can just
             | fix it and I am back to my edit-test cycle.
             | 
             | I'm not saying that there are no use cases for a debugger.
             | For example I find variable-modification breakpoints very
             | useful. As you mentioned if your edit-run cycle is slow
             | then it may be faster to inspect a new variable in the
             | debugger than adding another print statement. But when I
             | just want to inspect values in my program I find printf
             | sufficient and lower overhead. I'm sure part of my problem
             | is that because I rarely use a debugger I am not as
             | efficient, but I also think that printf-debugging is a very
             | effective workflow for a wide variety of issues.
        
               | stinos wrote:
               | _You said "click", I need to leave my keyboard._
               | 
               | Every proper IDE has a keyboard shortcut for that though.
               | 
               |  _With a debugger I need to kill my auto-run command, run
               | the program, set breakpoints, type to see what variables
               | I want to inspect_
               | 
               | This indeed falls under your 'part of my problem is that
               | because I rarely use a debugger' statement. E.g you could
               | set breakpoints before you save, use auto-debug instead
               | (i.e. launch program under the debugger on save instead
               | of just rnning it - without breakpoints there shouldn't
               | be much of a difference unless it's one of those nasty
               | multithreading bugs), add variables you want to see to
               | the watch window. Or type them anyway if it's a one-time
               | thing. Or use tracepoints. Etc.
               | 
               | I personally keep bouncing back and forth between
               | debugger and printing. All depends on context, but it's
               | definitely worth it getting to know both really well.
        
               | pjmlp wrote:
               | So basically tracepoints, without touching the program
               | code.
        
               | kevincox wrote:
               | But I'm already mucking with the program code most of the
               | time so I'm not worried about touching it.
        
             | sdevonoes wrote:
             | You have to first configure your IDE/editor to allow you
             | debugging. This is different for every programming
             | language/environment. Print works in any language without
             | prior configuration.
        
             | magicalhippo wrote:
             | Depending on how complex your debugger is, it allows you to
             | output values that might not be inspectable through the
             | debugger. Especially computed values.
             | 
             | Debug printing also allows you to debug programs running in
             | environments where you can't attach a debugger. For
             | example, maybe halting the program causes the bug not to
             | trigger. Or it's a remote system where you cannot attach a
             | debugger for various reasons. Or the bug only happens in
             | the optimized build, which in say C/C++ can make it quite
             | tedious to walk through with a debugger.
             | 
             | Most of the time though I use print as "proactive
             | debugging". Having detailed logs available is gold when
             | customer calls with a blocking issue.
        
               | stinos wrote:
               | _Especially computed values_
               | 
               | Showing function return values automatically was really
               | an eye-opener when I first encountered it.
        
               | pjmlp wrote:
               | Most OS offer that with process tracing like ETW and
               | DTrace.
        
             | theshrike79 wrote:
             | In the time it takes me to figure out how to connect a
             | debugger to the process I've had a good half-dozen full
             | loops of 1) add print statements 2) compile 3) run already
             | done.
        
           | yoz-y wrote:
           | It definitely has its place. The problem is mostly that you
           | have to actually change your code to debug it, and then
           | remember to change it back.
        
           | josalhor wrote:
           | The same thing happens all over Software really. Just because
           | a tool is powerful is looked up as superior, or better.
           | 
           | The main argument that I have seen is that in print debugging
           | you are relying on the program being executed in a non-
           | descriptive/non-declarative fashion.
           | 
           | I legitimately believe print debugging is incredibly powerful
           | (With a simple print I can check if a function is being
           | called, how many times, if the value has the value I expected
           | and the only requirement I need is to be able to see the
           | stdout of the process. I say that is fantastic!
           | 
           | The real world is all about cost analysis. How much value can
           | I get from a tool vs setup and running cost. The cost of
           | print debugging is incredibly small.
        
             | tstrimple wrote:
             | Print debugging has all of those features and is natively
             | built into just about every programming language in
             | existence and doesn't require any additional libraries or
             | tools.
        
             | marcosdumay wrote:
             | > The main argument that I have seen is that in print
             | debugging you are relying on the program being executed in
             | a non-descriptive/non-declarative fashion.
             | 
             | Breakpoints are way worse on this dimension.
        
           | andix wrote:
           | Because you shouldn't have to change code, just to debug it.
           | 
           | It's okay though to add verbose logging as a feature.
           | 
           | But just adding some print statements to debug code and
           | remove them afterwards, is dangerous (you release sth
           | different than you debugged).
        
             | Consultant32452 wrote:
             | ############################# What?
             | #########################
        
             | 0xFFFE wrote:
             | I do it in a different branch & discard it afterwards.
             | Having said that, I never meant print() should be used in
             | place of a proper debugger. All I am saying is they both
             | can complement each other and each one has its place &
             | value. As for me, I find it quicker to add a few print
             | statements and get a rough idea before firing up a
             | debugger(if required). May be others are more proficient in
             | using debuggers. But print() works for me.
        
             | ben-schaaf wrote:
             | As opposed to software breakpoints which change your
             | compiled binary at runtime in order to debug it. Even if
             | you're using hardware breakpoints you're still changing
             | what the CPU is doing and can easily make multi-threading
             | bugs disappear.
        
           | P_I_Staker wrote:
           | Depending on the use case it can be a sign that they don't
           | understand how to debug efficiently. Not that this is
           | something you should judge someone for.
        
           | maccard wrote:
           | > It's quick way to narrow down the "area of search" before
           | bringing in the big guns.
           | 
           | What are the big guns? with a debugger, I can stick a
           | breakpoint and look at the entire state of everything. Given
           | we're talking about Python, in pycharm [0] you can even
           | execute your print statements in the debugger if you so wish.
           | If you get the location wrong, or want to see what's going on
           | elsewhere you can just continue execution and use another
           | breakpoint.
           | 
           | This is even more important if you have a long compile/deploy
           | cycle (I work in games, and rebuilding and deploying to a
           | console can be a >10 minute iteration time)
           | 
           | [0] https://www.jetbrains.com/help/pycharm/part-1-debugging-
           | pyth...
        
             | ZaoLahma wrote:
             | Sometimes sticking the debugger into the wheel makes stuff
             | come flying over the handle bars in spectacular ways that
             | have nothing to do with what you wish to observe. You might
             | not even know which wheel to jam the debugger stick into,
             | if the behaviour is complex.
             | 
             | In these cases prints work well as a less intrusive way to
             | get a rough idea of what is going on.
        
               | maccard wrote:
               | I don't understand your wheel analogy, sorry.
               | 
               | > You might not even know which wheel to jam the debugger
               | stick into, if the behaviour is complex.
               | 
               | If you don't know where to put a breakpoint, how do you
               | know where to put a print statement?
        
               | Macha wrote:
               | Imagine putting breakpoints in multiple tight loops in
               | the stage of narrowing the search space. Imagine how many
               | times you need to click next. A conditional breakpoint
               | will only help if you know the condition you're looking
               | for, but there's stage before that of "Well, what looks
               | strange during execution".
               | 
               | Also for multithreaded code, stopping one thread dead for
               | long enough for a human to investigate it can
               | inadvertently resolve all sorts of race conditions.
        
               | Franciscouzo wrote:
               | > Imagine how many times you need to click next
               | 
               | Once or twice? Any sane debugger has a way to disable the
               | breakpoint.
        
               | maccard wrote:
               | > Imagine putting breakpoints in multiple tight loops in
               | the stage of narrowing the search space.
               | 
               | If you have enough loops to make breakpoints impossible
               | to use, you've likely got enough log output that you're
               | not going to be able to parse. You're almost certainly
               | going to look for other ways of narrowing the search
               | space.
               | 
               | > stopping one thread dead for long enough for a human to
               | investigate it can inadvertently resolve all sorts of
               | race conditions.
               | 
               | Stopping one thread for long enough to do console IO has
               | the same effect. Especially if you're using python,
               | you'll need a lock to synchronise the print statement
               | across your threads!
        
               | Fethbita wrote:
               | Today I was trying to solve the exact scenario in the
               | second example. A multi threaded program had a race
               | condition that would sometimes occur. printing numbers
               | helped a great deal. Might also be that I'm not that
               | proficient with my debugger even though I use that more
               | than anything.
        
               | sebastialonso wrote:
               | What I imagine Macha is arguing for is that the cost of
               | using print is extremely small, smaller at least than
               | breakpoints.
               | 
               | No one is saying breakpoints are useless, sometimes
               | printing is 'cheaper' in time and effort in order to
               | locate the region code of code in which using breakpoints
               | is cheaper.
        
               | Macha wrote:
               | Yes, print() and breakpoints are different tools with
               | different uses and there's cases where one is superior to
               | the other. This is why some tools now offer logpoints,
               | which are basically print() inserted via a breakpoint UI
               | rather than in your code where you can forget to remove
               | them
        
               | kyawzazaw wrote:
               | Oh please tell me about those tools.
        
               | maccard wrote:
               | Visual studio does trace points [0]
               | 
               | [0] https://docs.microsoft.com/en-
               | us/visualstudio/debugger/using...
        
               | Macha wrote:
               | VS Code and Firefox Developer Tools are the two I'm aware
               | of with actual support. Also some tools you can adhoc it
               | as a conditional breakpoint by basically putting
               | "print(whatever); return false" as the condition
        
               | maccard wrote:
               | > the cost of using print is extremely small, smaller at
               | least than breakpoints.
               | 
               | I don't think it is, at all. The cost of using print is
               | re-running your applciation with a code change, whereas
               | the cost of a breakpoint is re-running your application
               | with a breakpoint. Clicking in a gutter in an editor,
               | pressing a keyboard shortcut, or typing "b <line number>"
               | into your debugger is no more time or effort than adding
               | a print statement, and re-running your program.
        
               | kyawzazaw wrote:
               | As someone who help teaches intro level CS class,
               | debuggers can be too OP.
        
             | klyrs wrote:
             | > in pycharm [0] you can even execute your print statements
             | in the debugger if you so wish
             | 
             | In my experience, debuggers are really good to expose
             | hidden control flow. But usually, I know the flow, and
             | using a debugger for human-in-the-middle print statements
             | is just going to slow me down. Worse, those print
             | statements are ephemeral, so I'm disinclined to write a
             | nice formatter.
             | 
             | Print debugging leverages the language -- want to print in
             | a certain condition? Easy. Have a recursive structure, an
             | array, or a graph that you need to investigate? A naked
             | print sucks, a custom formatter is great. Need to check
             | some preconditions/postconditions? Do that in code. Don't
             | try to check that stuff by hand in the debugger.
             | 
             | Speaking personally... the only thing I like about icecream
             | is that ic(foo) both prints and returns foo, because you
             | can inject it into code basically for free. But I already
             | have a solution to that:                 def ic(x):
             | print(x); return x
        
         | fredley wrote:
         | Agreed, it's the simplest way to test and validate specific
         | assumptions. Debuggers are useful tools, but it takes you just
         | as long but usually longer to get to the same answer: is what I
         | think is happening here actually happening here?
        
           | ehsankia wrote:
           | At least debuggers give you new powers, the posted above
           | makes you install a library, import it all just so you can
           | have slightly cleaner print statements... I'm not gonna go
           | out of my way to import something and have to clean it up
           | after just so my printed statement is better formatted. And
           | if I needed the more powerful debugging, I'd use a debugger
           | not this library.
        
         | hathym wrote:
         | give pycharm (or debugpy) a try, you might get few years of you
         | life back :)
        
         | tpetry wrote:
         | I am the same, it's the most easy. Very interesting is that in
         | the laravel php world currently an interesting product is
         | gaining momentum: Ray (https://myray.app/)
         | 
         | So basically it's dump with a lot of neat extras and instead of
         | looking at the console of the script, or the website you are
         | printing on you push this to a little desktop application, from
         | every of your languages you are using. Something like log
         | collection for everything on your desktop.
        
         | gnramires wrote:
         | My objection here is that code should be self-explanatory, and
         | icecream or ic() doesn't explain itself, so at least I'd prefer
         | a name like icecream_debugger and replace ic() with pr(),
         | perhaps.
        
         | RileyJames wrote:
         | Hmmm I would have thought that too, but recently I've been
         | using byebug, which has changed my mind. Being able to throw in
         | 'byebug' on a line, then catch execution at that point in
         | another terminal (using byebug remote) and then check variables
         | at that point, is a game changer. Saves so much time compared
         | to looking for printed statements in the output, and then
         | trying again.
        
         | dnautics wrote:
         | Same. I do this for elixir. Luckily, we have IO.inspect:
         | 
         | https://youtu.be/JXQZhyPK3Zw&t=23m30s
        
         | [deleted]
        
         | tasogare wrote:
         | Why not taking the (short) time to learn using a debugger? This
         | is way more efficient way to work for complex situations.
        
           | gjulianm wrote:
           | After having developed in an environment where I couldn't use
           | a debugger (kernel drivers) I actually think that debugging
           | with prints is better than a debugger most of the time, as it
           | forces you to think about the code and where the failure
           | might be. Right now I only use a debugger when I'm in C++ and
           | I want to get a stack trace for a segmentation fault. In
           | almost all of the other cases I get a broad location with the
           | logging statements (always there), think about what could be
           | happening and then put some prints to test it. Even for
           | memory corruptions I don't use debuggers now, the address
           | sanitizers in clang plus valgrind do the job far better.
        
           | ramraj07 wrote:
           | I have debugger set in pycharm and use it all the time. I
           | also use print all the time, often along with the debug tool.
           | They are very complimentary and neither tool can do
           | everything the other can.
        
             | masklinn wrote:
             | TBF if you always run your programs in pycharm and use its
             | debugger, you can trivially use non-suspending "evaluate
             | and log" breakpoints instead of print.
        
               | heavenlyblue wrote:
               | But prints work equally well in any environment.
               | 
               | I can remove prints by just checking out the latest
               | version of the file.
        
               | masklinn wrote:
               | > But prints work equally well in any environment.
               | 
               | GP say they "have debugger set in pycharm and use it all
               | the time". So under the assumption (explicitly made in my
               | comment) that they're always using PyCharm to run their
               | program, that's not a concern.
               | 
               | > I can remove prints by just checking out the latest
               | version of the file.
               | 
               | Thereby losing all the changes you've made while
               | observing program behaviour, which may be less than
               | desirable.
               | 
               | Meanwhile it's just as easy if not easier to disable or
               | delete breakpoints from the View Breakpoints pane /
               | window: https://i1.wp.com/cdn-
               | images-1.medium.com/max/800/1*0wAP-w-a... you can just
               | uncheck the "Python Line Breakpoints" box, or select all
               | breakpoints and [Delete] them.
        
           | masklinn wrote:
           | > This is way more efficient way to work for complex
           | situations.
           | 
           | It's also way more inconvenient for simple situations or when
           | trying to sift through in order to zero-in on the issue's
           | rough location, spatial or temporal: unless the debugger is
           | well integrated into the editor it requires syncing multiple
           | sources of information (the debugger's breakpoints
           | configuration and the actual source) -- and resynchronising
           | on every edit; and if the debugger is well integrated into
           | the editor... now I'm locked into a specific editor.
        
         | pantulis wrote:
         | It's the first debugging method one learns and always remains
         | the last resort.
        
         | konschubert wrote:
         | I have found that print-style debugging gives a good birds-eye
         | view of the situation.
         | 
         | With a stepping debugger, I tend to get lost in the weeds. My
         | suspicion is that the bug fixes I come up with are less
         | holistic when using a debugger.
        
         | bjourne wrote:
         | You didn't even click on the link, did you? ;)
        
         | maliker wrote:
         | One of the nice feature of print debugging I like is that I
         | often end up leaving some of the print statements in for
         | logging purposes.
        
         | space_rock wrote:
         | Actually rust has the functionality of Icecream as a buildin
         | command "dgb" and you quickly find yourself using it over print
        
           | huhtenberg wrote:
           | No doubt blatantly ripped off the venerable print_r :P
        
             | masklinn wrote:
             | Nope, because unlike print_r dbg! returns the input as-is,
             | so you should be able to wrap any expression in dbg! in-
             | place, and just get debug output.
             | 
             | According to the RFC, the direct inspiration for dbg! is
             | Haskell's traceShowId (http://hackage.haskell.org/package/b
             | ase-4.10.1.0/docs/Debug-...).
        
           | threatofrain wrote:
           | And Julia with @show.
        
         | timbaboon wrote:
         | When in doubt, cout :)
        
         | jerzyt wrote:
         | The old style printf from C is still the best formatting tool
         | for output/debugging. The C++ style was just a distraction
         | without introducing anything of real value. log4xyz has some
         | nice features in terms of enabling/disabling at runtime,
         | through a config, but ultimately, printf rules.
        
           | stinos wrote:
           | _The old style printf from C is still the best formatting
           | tool for output /debugging._
           | 
           | The idea that you can just drop this anywhere, sure, that is
           | good. But once you've used string interpolation printf isn't
           | so attractive anymore. No more forgetting arguments, wrong
           | argument order, wrong specifier, ...
        
           | sltkr wrote:
           | The value introduced by C++ was type safety. In C, it's way
           | too easy for the format string to get out of sync with the
           | type of the arguments, e.g.:                   printf("%d",
           | my_long_var);
           | 
           | Might seem correct and work correctly on one platform, but
           | fail on another. scanf() is arguably even worse since it can
           | cause memory corruption.
           | 
           | These days compilers have diagnostics to catch those errors,
           | but if you rely on those you can't use dynamic format
           | strings, which means you're effectively using a subset of C
           | with a stronger type checker than C. That's a pretty good
           | state but it's definitely not "old style printf()"; old style
           | printf() was insecure.
           | 
           | And don't get me started on the convoluted macro invocations
           | necessary to correctly convert int32_t, int64_t, size_t and
           | ptrdiff_t. And that's with the newest standard: IIRC there
           | was no standard way to print _long long_ in C, at the time
           | when C++ already supported it.
        
             | xeyownt wrote:
             | Maybe C++ fixed type safety, but it introduced lot of
             | complexity and bugs for no actual added value. For
             | instance, because of stateful ios objects, it's close to
             | impossible to write correct code outputing hex on first
             | attempt. I'm sure that lot of C++ code outputing hex is
             | just plain wrong.
             | 
             | Given that C++ keeps getting more and more complex
             | features, it is just amazing that C++ I/O is still so
             | inconvenient, opaque and ultra-verbose.
        
               | lorenzhs wrote:
               | I mean it's not particularly pretty but what's so bad
               | about this?                   std::cout << std::hex <<
               | my_int << std::dec << std::endl;
        
               | xeyownt wrote:
               | That construction is ok. But usually you want formatted
               | output, let's say align to byte, and pad with zeroes.
               | I've seen oftentimes (and did myself):
               | std::cout << std::setfill('0') << std::setw(2) <<
               | std::hex << my_int << std::dec << std::endl;
               | 
               | This appears to work until someone change the alignment
               | to left somewhere in the code. Hence the correct code is:
               | // C++ type-safe equiv to printf("%-02x",my_int) - it's
               | called progress         std::cout << std::right <<
               | std::setfill('0') << std::setw(2) << std::hex << my_int
               | << std::dec << std::endl;
               | 
               | Also, is it relevant to keep the final 'dec' when we
               | assume we can't assert the ios left/right state, so why
               | could we assert the hex/dec state? Or maybe was it a bug
               | to change alignment to left, and not restore it to right
               | afterwards? Or maybe should you restore ios state in some
               | centrol place, and never pass your iostream to some
               | external libs? Discussions and wars ahead. Note that the
               | bug above is very nasty because it will change say "02"
               | into "20", which looks perfectly valid.
               | 
               | Note: I just noticed that in C++20, there is new
               | formatted string proposal. You can't stop progress, but
               | neither can you speed it up it seems.
               | 
               | Note2: the 'std::' spam is yet another indication that
               | C++ is lacking common sense and utterly broken.
        
         | m463 wrote:
         | sometimes it's printf, sometimes it's printk, sometimes it's
         | echo, but yes I agree.
         | 
         | I had an emacs macro that would help. Simplified it was:
         | (defun add-printf ()         (interactive)         (let ((s
         | (word-near-point)))           (when s             (beginning-
         | of-line)             (insert "printf(\"@@@ %s:%d "
         | s                      ": 0x%x\\n\", __FUNCTION__, __LINE__,"
         | s                     ");\n")             (forward-line -1)
         | (indent-for-tab-command)             (end-of-line)
         | (search-backward " \\n" nil t))))            (global-set-key
         | [f8] 'add-printf)
         | 
         | I had lots of variants (crafted while recompiling) with
         | prompts, or marked regions or lots more throwaway printf
         | silliness
        
       | kissgyorgy wrote:
       | You can do the same thing with Python 3.8+ by using f-strings and
       | just appending "=" to the variable name:                   >>>
       | print(f"{d['key'][1]=}")         d['key'][1]='one'
        
         | delaaxe wrote:
         | Yes. Please use more of the builtins instead of adding more
         | libraries
        
         | mkl wrote:
         | You can also make it a bit prettier by adding spaces:
         | >>> print(f"{d['key'][1] = }")       d['key'][1] = 'one'
         | 
         | It works with any expression you like, not just variables:
         | >>> print(f'{np.sin(np.pi/4.) = }')       np.sin(np.pi/4.) =
         | 0.7071067811865475
        
           | amelius wrote:
           | > It works with any expression you like
           | 
           | Also assignment expressions? :)
        
             | wadkar wrote:
             | I believe the walrus operator `:=` was introduced in Python
             | 3.8. So keep that it in mind.
        
               | L3viathan wrote:
               | As was this feature.
        
             | OJFord wrote:
             | >>> print(f"{(yes:='yes')=}")         (yes:='yes')='yes'
        
               | mkl wrote:
               | Yep.                 >>> print(f"{(yes:='yes') = } and
               | now {yes = }")       (yes:='yes') = 'yes' and now yes =
               | 'yes'
        
           | Aeolun wrote:
           | Not sure if I would consider that an improvement.
        
         | ninjin wrote:
         | Thank you, I was unaware of this. While I have lost a talking
         | point in favour of Julia - the `@show` macro - I am happy that
         | my old friends over in Python land has access to some better
         | ergonomics and can stop rubbing this in their face. ;)
         | julia> x = 4711         4711                  julia> @show x
         | x = 4711         4711
        
           | masklinn wrote:
           | I don't know what `@show` does exactly, but Python's is only
           | a limited convenience for print, it still not as good as
           | Rust's dbg![0] or TFA's ic, because it does not (and can not)
           | return its parameter unmodified, so you still need to inject
           | an explicit print and possibly execute the expression
           | multiple times.
           | 
           | It's convenient, don't get me wrong, but it's not exactly
           | groundsbreaking. Unlike breakpoint[1].
           | 
           | [0] https://doc.rust-lang.org/std/macro.dbg.html
           | 
           | [1]
           | https://docs.python.org/3/library/functions.html#breakpoint
           | it's not groundsbreaking on its own but the ability to very
           | easily hook into it and replace the breakpoint hook by an
           | arbitrary callable? Super useful.
        
             | pletnes wrote:
             | Do you have any concrete usage examples of that? I've only
             | used breakpoint() to stop execution while debugging.
        
               | sammorrowdrums wrote:
               | In the docs you can look up breakpoint, it has a lot of
               | features amongst other things you can register a custom
               | handler. I use it in selenium tests so I can either debug
               | on error or just print the error message and continue
        
               | ash wrote:
               | Could you provide a concrete example? It's still unclear
               | to me why one wants a custom breakpoint() handler.
        
               | sammorrowdrums wrote:
               | This is how we use it in my testing repo:
               | 
               | __init__.py
               | os.environ.setdefault('PYTHONBREAKPOINT',
               | 'tests.utils.raise_or_debug')
               | 
               | tests.utils.raise_or_debug                   def
               | raise_or_debug(msg=''):             if
               | settings.BREAKPOINT_ON_ERROR:                 extype,
               | value, tb = sys.exc_info()                 if
               | getattr(sys, 'last_traceback', None):
               | pdb.pm()                 elif tb:
               | pdb.post_mortem(tb)                 else:
               | pdb.set_trace()             elif msg:
               | raise AssertionError(msg)             else:
               | pdb.set_trace()
               | 
               | The reason is so we can leave breakpoints in the tests,
               | and then depending on context run the tests in a context
               | where debug is possible, or simply raise the failure
               | again.
               | 
               | Selenium can be very flaky, so this has really sped up
               | iteration and improvements. Standard functions with
               | retries, and smart assertions and timeouts can then be
               | fixed and resumed in dev - and give a good message on
               | fail (for example headless chrome runner on CI).
        
               | sammorrowdrums wrote:
               | And from our own docs:                   We have manually
               | overwritten the breakpoint handler to a custom handler
               | that in default mode does this:                    -
               | `breakpoint()` calls trigger `pdb.set_trace()` as per
               | usual           - `breakpoint(<str>)` calls trigger
               | AssertionError to fail tests                  When
               | `BREAKPOINT_ON_ERROR=TRUE` is turned on:
               | - `breakpoint()` calls trigger `pdb.set_trace()` as per
               | usual           - `breakpoint(<str>)` call
               | `pdb.set_trace()` so you can try to manually work out how
               | the code is functioning           - if you call
               | `breakpoint(<str>)` from an `except` block it will open
               | the debugger at the position of the exception
               | 
               | Usage example:                       try:
               | WebDriverWait(self.driver, 10).until(
               | EC.element_to_be_clickable((By.ID, 'desktop-apply')))
               | except TimeoutException:                 breakpoint('Was
               | not able to click apply button.')
        
               | masklinn wrote:
               | You can run Python with
               | PYTHONBREAKPOINT=print python
               | 
               | and all your breakpoint() calls will be prints.
               | 
               | Which is not super useful, however you can use
               | PYTHONBREAKPOINT=icecream.ic
               | 
               | and now you get the advantages of TFA without having to
               | import anything anywhere.
               | 
               | The only annoyance is that the default hook takes no
               | arguments, so you can't trivially switch between the
               | default and custom implementations, you may need to
               | modify the source depending on the breakpoint hook you're
               | using.
        
             | alexmojaki wrote:
             | What is TFA?
             | 
             | icecream, the subject of this post, returns its parameter
             | unmodified without multiple evaluations.
        
         | grun wrote:
         | Hey! I'm Ansgar. I wrote Icecream.
         | 
         | f-strings's `=` is _awesome_. I 'm overjoyed it was added to
         | Python. I use it all the time.
         | 
         | That said, IceCream does bring more to the table, like:
         | - Stynax highlighting.       - Pretty prints data structures.
         | - Returns its value for nesting.       - Integrates with
         | logging via `ic.configureOutput()`.
         | 
         | etc
         | 
         | I hope that helps!
        
         | blisterpeanuts wrote:
         | Nice feature. Is there a way to globally enable/disable it?
         | E.g.,                 python3 myscript.py
         | --enableFstringDebugging=true
         | 
         | So that I could get rid of lots of conditional statements,
         | e.g.:                 if debug:           print (f"some useful
         | debugging info")
        
           | OJFord wrote:
           | Use a logger, not print, and set log level?
        
         | grenoire wrote:
         | WHAT... Why ain't anybody talking about this? Brilliant!
        
           | wadkar wrote:
           | It was "recently" introduced in Python 3.8. See sibling for
           | link to issue tracker
        
           | JetAlone wrote:
           | In this case, it would seem it took a little reinvention in
           | order to let the wheel's discovery be known.
        
             | willyt wrote:
             | Not used it, but ic seems to be compatible back to python
             | 2.7 so not really comparable. I can't use the builtin thing
             | in my python code because the host software for my plugins
             | is running python 3.6 embedded and only recently upgraded
             | from 3.3 in the latest release so if I want to maintain
             | backwards compatibility I can never use the new python
             | builtin. I expect lots of other people are in the same
             | position.
        
         | est wrote:
         | You can also use the breakpoint() introduced in py3.7 via
         | PEP533
         | 
         | https://www.python.org/dev/peps/pep-0553/
        
           | vonwoodson wrote:
           | Bump. This is the most correct way to debug python programs
           | VS using print statement.
        
             | stjohnswarts wrote:
             | It's a way, it's hardly "the most correct way". Jeesh
             | sometimes hackernews people. There are all kinds of ways to
             | debug, and sometimes code has to run in different version
             | of python. I find logging with various levels of "debug
             | info" is best for me, but I hardly think that is "the most
             | correct way"
        
             | taneq wrote:
             | Debugging virtually anything real-time that interacts with
             | other systems will require some kind of logging (of which
             | printf() is the trivial case). Step-through debugging is
             | great but far from universally applicable.
        
               | vlovich123 wrote:
               | Setting a breakpoint that prints and continues is
               | appropriate if performance doesn't matter. It gets
               | trickier if it does. In those cases RR seems like the
               | best idea rather than prints. At least that's the theory.
               | 
               | In my experience the conditions on the ground are
               | different though. Tooling availability is inconsistent,
               | which is why such approaches don't always work. Reverse
               | debugging isn't available on all platforms/languages.
               | Debuggers (or at least the C/C++ ones) are slow in how
               | they inject instrumentation code to evaluate - rather
               | than compiling expressions into native codes to
               | conditionally trap, they seem to trap unconditionally and
               | evaluate the conditional using their introspection
               | language (there are valid reasons why it's done this way
               | but it has serious performance impacts). In compiled
               | languages, the evaluation of the expression can be
               | difficult/impossible to write because of this (not enough
               | type information available, validation is late binding
               | meaning mistakes are extra expensive, etc.
               | 
               | At the end of the day, when your tools fail you, print
               | debugging is easier to use to accomplish the task rather.
               | Getting tooling to work effectively is more time
               | consuming and frequently not possible.
        
             | gnulinux wrote:
             | Correct in what way? I can think of many real time systems
             | that cannot be debugged with a pdb breakpoint.
        
             | hyperbovine wrote:
             | Most correct you say? Based on what? Mind you we're talking
             | about code that will never even ship in the vast majority
             | of cases. Ergo, do whatever works best for you.
        
           | EamonnMR wrote:
           | Pre 'breakpoint()' you could always use import pdb;
           | pdb.set_trace()
           | 
           | That said, sometimes you don't want to interrupt execution
           | and see your results in real time.
        
           | animal_spirits wrote:
           | I like to use PDB++ which is a drop in replacement for PDB
           | 
           | https://github.com/pdbpp/pdbpp
        
         | neumann wrote:
         | Wait. How does this work?
        
           | selcuka wrote:
           | https://bugs.python.org/issue36817
        
             | rualca wrote:
             | Thank you for posting this. I will certainly use this gem
             | extensively.
        
             | neumann wrote:
             | Neat. Thanks. I rarely read these lists, but am fond of how
             | friendly this proposal was!
        
       | BrissyCoder wrote:
       | Imagine using a language in this day and age where you have to
       | litter your code with debugging statements.
        
         | [deleted]
        
       | mybrid wrote:
       | Party on! I applaud this effort. As a UI/UX engineer I can say
       | that there is no one debugging UI/UX experience to fit all. Of
       | course there is the active/passive division between active
       | debugging and logging. There are further usability divisions in
       | both of those. As this is a passive/logging debugging scheme I'm
       | sure there are those in the passive debugging community who will
       | find this helpful, if not vocal.There is no one size fits all
       | when it comes to usability although 99.9999% of software built
       | only presents one usability experience and only begrudgingly
       | provide the disabled community access as an after thought. A
       | fairly significant percentage of the population is color blind
       | and yet web sites still overly rely on red. Yby. You be you. The
       | bane of all usability is one size fits all. Well met! We need
       | more, not less, usability options.
        
         | StavrosK wrote:
         | The split doesn't even run as deep as you describe it.
         | Sometimes I use active debugging, sometimes I use passive
         | debugging, depending on the situation. Sometimes I just want to
         | see the output, so passive is fine, sometimes I want to control
         | the flow and be able to inspect things more deeply. I don't
         | think there are people who only use one or the other.
        
       | nhumrich wrote:
       | Has nobody heard about the logging module in python? It can
       | essentially do all of this. it's also: 1. built in. 2.
       | configurable by others so you can add it to a library. 3. allows
       | different log levels per line (debug, info, etc).
        
       | albertzeyer wrote:
       | If you like this, you might also like my small debugging utility,
       | a better_exchook replacement:
       | https://github.com/albertz/py_better_exchook
       | 
       | Simple example:                   assert x == 4
       | 
       | When this fails, it will print the value of `x`.
        
         | zomglings wrote:
         | Just a point of curiosity about reassigning `sys.excepthook`.
         | Is there a reason you simply reassign it and lose information
         | about the old excepthook:                   sys.excepthook =
         | better_exchook
         | 
         | instead of something like:                   def
         | generate_better_exchook(..., current_excepthook=None):
         | previous_excepthook = current_excepthook             def
         | better_exchook(exception_type, exception_instance,
         | exception_traceback):                 ...                 if
         | previous_excepthook is not None:
         | previous_excepthook(exception_type, exception_instance,
         | exception_traceback)             return better_exchook
         | 
         | and then                   sys.excepthook =
         | generate_better_excepthook(..., sys.excepthook)
         | 
         | Do you prefer not to do this because it would keep this closure
         | around in memory until the Python process exits?
        
       | atoav wrote:
       | I am sorry, but I am not going to pull in an external dependency
       | just for this, especially not with the power of fstrings in newer
       | python versions.
        
         | mnd999 wrote:
         | Indeed, in a couple of years this is the new leftpad.
        
         | dataflow wrote:
         | It's for debugging. Not something to depend on in released
         | code. Put the import in your site initialization file and you
         | won't even have to import it in the REPL.
        
           | atoav wrote:
           | I understand, but Python >3.8 can do this:
           | >>> x = 1+1       >>> print(f"{x = }")       x = 2
           | 
           | And if it is included why pulling in a dependency? And even
           | if it is a dev dependency: it can be a dev dependency that
           | bites you in two years in the middle of a night when your
           | service fails.
        
         | tamentis wrote:
         | You're just being difficult. It's easy, you just setup these 10
         | Node.js micro services in docker, setup your database, the
         | cache. Then my friend, you can really debug with beautiful
         | print messages.
         | 
         | Wait until someone creates a $10/user SaaS service out of it.
        
           | [deleted]
        
           | fouc wrote:
           | Insightful prediction of how development will be done in the
           | future.
        
       | dfilppi wrote:
       | Vscode has logpoints now
        
       | offtop5 wrote:
       | It should take a competent programmer about 30 minutes to create
       | a nice log class with more flexibility for their particular
       | application. On one hand thank you for sharing, on the other hand
       | if I add this to my project at work my boss is going to scold me
        
         | Merad wrote:
         | I don't do much Python, but logging is a problem that's been
         | thoroughly solved in just about every other language I've used.
         | Why on earth would you reach for a half baked homemade
         | solution?
        
           | offtop5 wrote:
           | I mean, the solution is print.
           | 
           | It's just depending on your application you're going to
           | change exactly what you log, and that's where you go and put
           | your own logic
        
       | anonymousDan wrote:
       | Is this likely to work inside something like pyspark I wonder?
        
       | polm23 wrote:
       | Previously:
       | 
       | (2019) https://news.ycombinator.com/item?id=19867460
       | 
       | (2018) https://news.ycombinator.com/item?id=16377714
        
       | sly010 wrote:
       | This has the added benefit that you can turn it off in CI/CD and
       | prevent people committing debug statements.
        
       | randomsearch wrote:
       | I used to teach people to use the amazing debuggers we have now
       | so that they'd never have to use print statements to debug their
       | code.
       | 
       | Since I became a front-end developer, all I've done is use print
       | statements to debug JS. Feels like I've gone backwards.
        
         | cdrini wrote:
         | Why don't you use the devtools debugger? I generally find
         | people in webdev tend to use debuggers more than other fields
         | because it's so readily available in devtools, or with
         | `debugger` in JavaScript.
        
       | pantulis wrote:
       | As a staunch defender of print-based debugging I guess this would
       | be a lot more useful if this could be connected to a logging
       | library.
        
       | comeonseriously wrote:
       | I'd be lying if I said I never use print to debug, but honestly I
       | mostly using logging.
        
       | whalesalad wrote:
       | Friendly reminder the stlib includes the pprint module.
       | pprint.pprint() will give nicely formatted output of data
       | structures and lists.
       | 
       | (Not suggesting it as a replacement for this tool - but if you're
       | a pyhacker and don't know about this it's handy)
        
       | nneonneo wrote:
       | For an even easier-to-remember alternative, there's q:
       | https://github.com/zestyping/q
       | 
       | All you need is `import q`. q works like a function (q(x)), like
       | a variable (q|x and q/x, so you get different operator
       | precedences) and like a decorator (@q), so it can be used in
       | practically any circumstance for a quick debug print. Plus, the
       | name sounds like you're interrogating something.
        
         | josalhor wrote:
         | This is actually really cool. I was going to say that so many
         | people miss the point of print debugging and as a consequence
         | forget to shorten the import line as much as possible.
         | from icecream import ic;ic() is 28 characters            import
         | q;q() is 12 characters            print() is 7 characters
        
           | oauea wrote:
           | Why would the amount of characters possibly matter?
        
             | canada_dry wrote:
             | _bc pgmrs r lazy af._
             | 
             | Seriously though... when I'm in debugging mode, speed and
             | efficiency in completing the task is paramount. So, every
             | character I save typing, the better!
        
             | kyawzazaw wrote:
             | reduce the energy barrier to debugging
        
             | josalhor wrote:
             | I find it important because it lowers the barrier of entry
             | to print debugging. I have found myself using less prints
             | in Java than in Python both because of static typing and
             | having to write the full `System.out.println`.
             | 
             | This is also important to me because I am very cautious of
             | not doing a file-level import (I don't want to commit a
             | file with the dependency). The fewer characters it takes,
             | the easier it is for me to write it all in a single line
             | and remove it afterward.
        
         | zestyping wrote:
         | I was wondering whether someone would notice that it's a clone
         | of q! Thanks! :)
         | 
         | q has the additional feature that you can decorate any function
         | or method with `@q`, which causes invocations to be logged with
         | arguments, return values, and exceptions. Really handy for
         | tracing what's happening in your program.
        
           | canada_dry wrote:
           | A quick look at the intro page was a bit disappointing. You
           | immediately _get_ how ic works because their examples include
           | the code _and_ result.
        
       | submeta wrote:
       | This here is excellent:
       | print(f"{d['key'][1]=}")             d['key'][1]='one'
       | 
       | My immediate reaction was: How can I make this `form` easier to
       | type. And that's what `Iceacream` does. Reviewed their code and
       | realized they work with AST and inspection.
       | 
       | I recently started to learn (Common )Lisp. I realize how easy it
       | would be to write a macro in Lisp that would implement the
       | functionality of `Iceacream`, in a few lines of code.
        
       | dbalan wrote:
       | This is a really nice tool. But the fundamental reason most go
       | for print is because its right there and that wins over other UX
       | improvements or machinery. Python is a language where you can get
       | a reasonably good debugger with a single line almost anywhere,
       | still people reach for print()
        
       | rzwitserloot wrote:
       | This library's 'ad copy' focuses on its ability to print the
       | expression passed to the `log` function verbatim, but for those
       | thinking about importing this library or something like it, I
       | find that the most useful long-term benefit of a construction
       | like this is this:
       | 
       | All short-term logging / debugging calls are now properly
       | isolated.
       | 
       | Ordinarily, if you spot a `print` statement in code it's usually
       | a left-over relic of some previous debug session. Or it's the
       | command line printer, who knows?
       | 
       | With a call to ic, you know it's short-term logging and nothing
       | else. You can search for them, you can spot them immediately in
       | commit changelogs, you can write hooks if you want to ban them
       | from ever appearing in certain branches, you can breakpoint them
       | in your IDE, etcetera.
       | 
       | For many apps, _all_ print statements work like that, but I've
       | worked on more than a few that have a 'print to standard out'
       | component to them.
        
       | knl2021 wrote:
       | I've built https://github.com/kunalb/panopticon to easily trace
       | function execution to handle a similar use case, except that it
       | generates output for chrome://tracing instead of printing out
       | lines.
       | 
       | cPython's settrace/setprofile functionality enables so many cool
       | tools.
        
       | corytheboyd wrote:
       | I see print debugging and breakpoint debugging as two different
       | tools, both very useful. Print statements are useful when you
       | don't know where to put a breakpoint, of course, but also when
       | it's important to see the real-time execution along with the
       | debug logs. Breakpoints are of course insanely helpful when you
       | know where to put them, but also can be peppered about suspect
       | areas and used with steps to cover wider ranges.
       | 
       | Just to give credit to both "sides" in the comments here, you're
       | all kind of right and it's okay if people have different
       | workflows than you.
        
         | ehsankia wrote:
         | This isn't even breakpoint debugging though, this library
         | simply makes your print statements "easier" to write, at the
         | cost of having to install and import a library.
        
       | Justsignedup wrote:
       | lots of nay saying here...
       | 
       | i for one like this. sure there are some ways in the new python
       | code to handle some of the features of ic, but the overall
       | feature set is rather nice.
       | 
       | sure it isn't a debugger, but definitely better than prints.
        
       | mikkelam wrote:
       | Why don't people just use a debugger? It boggles my mind that
       | people live with print style debugging. Depending on the project,
       | it can be such a timer waster and just plainly worse in all
       | aspects.
       | 
       | Especially in Pycharm for me, it is incredibly easy. Even when
       | running over ssh in remote computers I can use my debugger.
        
         | chpmrc wrote:
         | What do you use for remote debugging? And do you run all
         | production instances with a debugging server attached? What's
         | the overhead?
        
           | mikkelam wrote:
           | I use Pycharm's native ssh interpreter
           | toolhttps://www.jetbrains.com/help/pycharm/configuring-
           | remote-in...
           | 
           | It automatically syncs your local project with the remote
           | machine and runs seamlessly. They have a similar offering for
           | docker, but I have yet to try it when running over ssh.
        
         | globular-toast wrote:
         | For me it's just lack of practice. This is rare enough for me
         | that I would have to look up how to use the debugger each time.
         | I never forget how to type print.
        
         | extrememacaroni wrote:
         | uncle bob told me you don't wanna be good at using a debugger
         | 
         | print("here") till the day i die!!!
        
         | dataflow wrote:
         | They're not perfect substitutes for each other. And not every
         | python script that you need to quickly debug is worth firing up
         | a while IDE for. Especially if your IDE requires creating a
         | project and such. I say this as someone who very much loves
         | Visual Studio.
        
         | pjc50 wrote:
         | It produces an ordered transcript of the whole run that you can
         | visually scan quickly for the unexpected, and then paste bits
         | of into your short-term notes file.
         | 
         | It's easier to go back in time with print debugging: just
         | scroll up.
         | 
         | It also works in situations where debugging is infeasible or
         | would slow down execution too much.
        
         | mckirk wrote:
         | It's strange, when developing C# and the like I'd go crazy
         | without a debugger... and yet, in Python I've hardly ever used
         | one. That might be because it's hard to develop C# without
         | already being in an environment that puts an emphasis on easy
         | debugging (Visual Studio), whereas I'm developing Python in
         | whatever text editor I happen to be using at the moment and
         | never really bothered to try out anything other than the IDLE
         | debugger -- which really isn't that great to use.
         | 
         | Might be worth it to set up a better environment, then. Somehow
         | I don't feel like print debugging is all that inferior though;
         | in a sense it might actually make your debugging more efficient
         | by forcing you to formulate concrete 'questions' about your
         | code before even diving into the execution.
        
           | roel_v wrote:
           | Writing Python (and most other languages) with a proper IDE
           | with integrated debugger and all is like eating right and
           | exercising: it's much better when you do it, you feel better
           | while doing it, each time you start again you think "why did
           | I ever give this up?", and yet it takes conscious effort to
           | keep it up, circumstances just make it so tempting to fall
           | off the train. 'Oh just checking the value of that constant,
           | I'll just use Vim.'. 'It's just one trace line, I'll just use
           | print.'. And before you know it you're back in the stone
           | ages, and the circle repeats.
        
             | mikkelam wrote:
             | I really agree with this. I would love to be in a clean
             | self-built environment in vim or sublime or the likes, but
             | IDE's just work and boost productivity.
        
           | fshbbdssbbgdd wrote:
           | For me the debugger really shines when I need to trace the
           | behavior of some library. It's worth the effort to instrument
           | the codebase I'm working on with good logging, but doing that
           | to every library I call would be insane.
        
         | enriquto wrote:
         | > Why don't people just use a debugger?
         | 
         | I only speak for myself, but I find that time spent inside a
         | debugger is "lost", and bound to be re-spent again and again if
         | you ever find new problems with the same code. I pretty much
         | prefer to add assertions, logging and comments to my program,
         | that will stay there and make further re-reads easier and more
         | useful. As a matter of principle, I never use a debugger
         | (except for assembly code, but that's rare nowadays).
        
           | mixmastamyk wrote:
           | Agreed, and tests too.
        
           | silon42 wrote:
           | +1 Agreed. Unless you can immediately see the problem from
           | back-trace and local variables, stepping through with the
           | debugger is often a waste of time compared to adding some
           | logging.
        
           | haram_masala wrote:
           | Then you are either using a bad debugger, or you're not using
           | one properly. A good, fully-featured debugger allows you to
           | store sessions and configurations for various efforts and
           | problems you're trying to diagnose, along with your notes and
           | links to relevant issues.
        
             | enriquto wrote:
             | > Then you are either using a bad debugger, or you're not
             | using one properly.
             | 
             | You are certainly right, as I barely ever use a debugger
             | and it is always an annoying experience. Can you suggest
             | such good debuggers for C, Julia and Python?
             | 
             | Sounds like these "sessions and configurations" are
             | important, and thus they should be formally part of the
             | program, committed to its public repository for all to see
             | and use. I call these things "tests" and write them using
             | the same language as the rest of the program, and run them
             | frequently to be sure that they don't get out of sync with
             | the program itself.
        
           | svantana wrote:
           | Asserts are great, but when they fail, you still need to
           | figure out why it happened. Nice environments allow you to
           | debug the state at the point of assertion, python certainly
           | doesn't make that convenient.
        
         | Hnrobert42 wrote:
         | Debuggers are great. But to unboggle your mind, here's why I
         | don't use a Python debugger.
         | 
         | I don't write enough Python to justify the time to learn the
         | debugger. I write just enough to help my staff/teammate fix
         | their problem or debug some tool I'm using and move on.
         | 
         | I've been doing this long enough to lose interest in digging
         | deeply into each language. I learned gdb for C. After that I
         | learned jdb and Eclipse for Java. There was probably something
         | I used for the few years I wrote PERl later PHP. After a while
         | I stopped getting excited about deeply learning new languages
         | and their tools.
         | 
         | Debuggers and IDEs are yet another dependency. Frequently I use
         | systems I don't control, like client systems. They have really
         | stringent rules about what can be installed. The lowest common
         | denominator is vim and print.
         | 
         | Certainly there are lots of reasons to learn debuggers. But
         | there are also lots of valid reasons to learn a tool like this
         | which is easy to learn, useful in across many languages, and
         | gets the job done in many situations.
        
           | black3r wrote:
           | I just use IntelliJ IDEs for all programming languages I
           | write in and the debugger experience is the same everywhere.
        
         | reportgunner wrote:
         | print is built in and runs everywhere my code runs. It never
         | breaks and i never have to remember to install it. I never have
         | to configure it or update it.
        
         | zestyping wrote:
         | It depends on the situation.
         | 
         | In general, debuggers give you much more contextual
         | information, but using a debugger is extremely slow. To see
         | what's going on, you have to step through your program, one
         | line at a time, until the interesting thing happens. That can
         | take ages.
         | 
         | If you want to see how a specific function is behaving and it's
         | called 100 times, it's a lot easier to look at a printed log of
         | the 100 calls and scan or search it for interesting behaviour
         | than to pause the program in a debugger and step through it
         | over and over, hoping to catch the interesting moment.
        
           | mixmastamyk wrote:
           | Yes, and sometimes I hit the wrong key and have to start
           | again.
        
           | klibertp wrote:
           | > you have to step through your program, one line at a time
           | 
           | You can set more than one breakpoint at a time. Hitting 'c'
           | will get you to the next breakpoint, no matter where it is.
           | 
           | > and step through it over and over
           | 
           | No, you don't (shouldn't) do that. You write a conditional
           | breakpoint which activates when something interesting
           | happens.
        
             | gwenzek wrote:
             | OTOH if you're able to write `if
             | bug_will_happen_on_next_line(): breakpoint()` you're pretty
             | much done debugging.
        
               | klibertp wrote:
               | No, when starting debugging you most often know the
               | results of a bug. You can break conditionally on when the
               | results appear, then work your way up the stack or jump
               | to the beginning of a block to re-examine it. Or you can
               | rerun the program, this time with a breakpoint set based
               | on the inspection of the environment when the bug
               | happened.
               | 
               | In pdb not only can you set breakpoints with
               | conditions[1], you can also assign commands to be
               | executed when the breakpoint is reached[2]. You can also
               | use `display` command to - effectively - insert temporary
               | `print`s in any stack frame[3]. Coupled with `up`,
               | `down`, and `jump` commands, and the ability to evaluate
               | any code in the scope of a break, it really gives you a
               | lot of options on how to find the problem.
               | 
               | Though, logging is still a good thing. When I see a
               | commented out `print` in code review, I usually suggest
               | to replace it with a DEBUG level logger call. Extensive
               | logging does help to trace the execution, it can be
               | disabled when not needed, and can improve the readability
               | of the code. Although raw print is an antipattern (can't
               | easily enable some and disable other, need to clean up
               | after debugging, doesn't display the stack trace, etc.),
               | logging is a valid technique which complements the usage
               | of a debugger nicely.
               | 
               | [1]
               | https://docs.python.org/3/library/pdb.html#pdbcommand-
               | break
               | 
               | [2]
               | https://docs.python.org/3/library/pdb.html#pdbcommand-
               | comman...
               | 
               | [3]
               | https://docs.python.org/3/library/pdb.html#pdbcommand-
               | displa...
        
         | svantana wrote:
         | Right?? I find it really sad that python being the go-to
         | "beginner/mess-around" language has such poor debugging in most
         | environments. I've tried VSCode, Atom, Juno, Spyder,
         | Jupyter(lab) and Thonny. All of them were either broken, buggy
         | or cumbersome to step-debug in while also using the REPL. I
         | will give PyCharm a go though.
         | 
         | The worst part is, I think a lot of devs resort to print-
         | debugging which in turn leads to writing sloppy code - e.g.
         | avoiding function calls to be able to access state, etc.
        
         | Stratoscope wrote:
         | There is a place for both. Sometimes I really do want to see a
         | sequence of events, or it may not be possible to run a
         | debugger.
         | 
         | I work on an automated voice agent for restaurant drive-
         | throughs, specifically the code that connects the voice agent
         | to the existing point of sale system. When something goes wrong
         | in a store, having a log of the API interactions between the
         | voice agent and the POS is essential for troubleshooting.
         | 
         | Recently I updated the logging with a code generator option. I
         | can place an order by voice on my local test setup, and it
         | writes out a Python script that replays the same API calls,
         | ready to run in a standalone API tester. This is super handy
         | for testing.
         | 
         | But none of this is a substitute for a good debugger. For me it
         | is a powerful thing to be able to stop the code at a breakpoint
         | and see the actual values of all the variables at that point in
         | time.
         | 
         | And it's not just for debugging. I jumped into this project
         | after it had already been developed for a couple of years, and
         | needless to say there is a lot that I did not understand about
         | the code.
         | 
         | Of course I can _read_ the code and use Ctrl+Click in PyCharm
         | to see the definition of a function. But to really understand
         | it, there is nothing like setting a breakpoint and looking at
         | live data.
        
         | Chris2048 wrote:
         | For what, exactly? Just saying "I can use my debugger" doesn't
         | really help.
         | 
         | Debuggers give you a lot of information at a particular point
         | (stack frame) in execution time, whereby print-ing can give you
         | a filtered view of what happen(ed) during a full run.
         | 
         | When I can mark breakpoints and store the stack at that time
         | (for each pass through that bp) in a inspectable gui tree (a
         | list for linear execution path, a tree for threads etc) then I
         | won't need print statements any more.
        
         | CodeGlitch wrote:
         | I find you need both. Print gives you a nice high level view of
         | your code, whilst stepping let's you see what's happening.
         | 
         | I would say I get more use out of printing these days.
        
       | mybrid wrote:
       | The only thing I will say is that this would not meet my personal
       | code review standards for logging. Rule number one is never put
       | functional calls in a logging statement, only log static data. As
       | optimistic as one might be that the 'ic' has no side effects, the
       | most conservative approach is to never put anything that
       | interacts with run-time behavior in a log statement. This is
       | especially good practice when working in many code bases in many
       | languages. If one gets comfortable with something like say 'ic'
       | that has no side effects when wrapping function calls then one
       | may assume this out of habit when interacting with other such
       | utilities, other languages. The conservative approach is best in
       | my experience and that is to never insert run-time code in log
       | statements and only log static data.
        
         | Igelau wrote:
         | It says on the tin that it's for print debugging. That's not
         | the same as logging.
        
       | czinck wrote:
       | I started writing a very-alpha related tool,
       | https://github.com/czinck/pyset_x. As the name implies, it's like
       | `set -x` in bash, in that it prints every line as it executes.
       | It's more useful for situations where you have some complicated
       | control flow and it's not working exactly how you expect.
        
         | cool-RR wrote:
         | Funny, a few months back I added this sentence to PySnooper's
         | readme:
         | 
         | "PySnooper is a poor man's debugger. If you've used Bash, it's
         | like set -x for Python, except it's fancier."
         | 
         | https://github.com/cool-RR/PySnooper
        
           | czinck wrote:
           | I saw someone mention PySnooper elsewhere in this thread, I
           | think if I had known it existed before I probably wouldn't
           | have bothered with `pyset_x`.
        
       | stuntcoder wrote:
       | I promise the 15 minutes it takes to learn to use the debugger
       | will save years of your life
        
         | atakan_gurkan wrote:
         | Which debugger would you recommend?
        
           | dbrgn wrote:
           | For Python: ipdb. In your code, you can just drop in "import
           | ipdb; ipdb.set_trace()" and the execution will stop there
           | with an interactive prompt. Alternatively start the script
           | through (i)pdb and set breakpoints.
           | 
           | You can also use plain pdb ("import pdb; pdb.set_trace()"),
           | which has the advantage that it comes with the Python stdlib,
           | but the interactive prompt is less fancy (no history, no
           | autocompletion, etc).
        
             | gcarvalho wrote:
             | Add `export PYTHONBREAKPOINT=ipdb.set_trace` to your
             | .bashrc and "breakpoint()" will invoke ipdb by default.
        
               | dbrgn wrote:
               | That is a great trick, thank you!
        
             | the_cat_kittles wrote:
             | i use this frequently! but print() is still handy and my go
             | to
        
         | strokirk wrote:
         | The issue is that the debugger stops execution at the
         | breakpoint, but most often I want to analyze multiple print
         | statements together. The debugger and repl have their uses, but
         | IMHO I am usually better served by adding a print and having
         | the script automatically re-run itself.
        
         | gjulianm wrote:
         | Not necessarily. I know very well how to use a debugger, but
         | nowadays I just prefer to use prints: it forces to you actually
         | think about what's happening instead of just looking at it.
         | It's also more useful in situations where putting a debugger
         | actually changes behavior (high performance systems, parallel
         | programs).
        
           | eonwe wrote:
           | I would expect print-debugging to change the behavior in such
           | cases too. Usually writing to stdout is behind some
           | synchronization so using print debugging will drop
           | performance and make program less parallel.
        
             | gjulianm wrote:
             | You can change where to put the prints if they do change
             | the behavior (which incidentally also helps in
             | understanding what's happening). You can print only if a
             | condition happens, or only before/after critical sections,
             | or print counters that you compute in the fast path. With
             | the debugger you're always taking the overhead in and
             | breakpoints will always change the flow of the program.
        
           | timzaman wrote:
           | Exactly. And prints are cross platform cross language etc.
        
         | [deleted]
        
       | lihaoyi wrote:
       | We have this in Scala via the PPrint library, which provides
       | 'pprint.log'. pprint.log also shows the filename/line number (so
       | you can find your prints later) and has colored output and
       | intelligent indent-formatting for things than wrap to multiple
       | lines. It's super covenient!
        
       | scolvin wrote:
       | Humm, I built a similar but more powerful tool:
       | 
       | https://github.com/samuelcolvin/python-devtools
       | 
       | (install with `pip install devtools`)
       | 
       | It has similar functionality for debugging but has prettier
       | formatting and code highlighting using pygments.
        
         | scolvin wrote:
         | One of the most useful trick I found was installing the tool
         | via sitecustomize.py so you don't need the import:
         | # add devtools debug to builtins         try:             from
         | devtools import debug         except ImportError:
         | pass         else:             __builtins__['debug'] = debug
         | 
         | (see https://github.com/samuelcolvin/python-devtools#usage-
         | withou...)
         | 
         | This would work with icecream too.
         | 
         | The second advantage of not needing the import is that CI fails
         | if you forget to remove all debug() commands.
        
       | jeffypoo wrote:
       | In classic HN style, half of the comments have devolved into
       | variations of dEbUgGeR bEtTeR and PrInT bEtTeR...
       | 
       | Newflash! You can do both, and sometimes one is better than the
       | other.
        
       ___________________________________________________________________
       (page generated 2021-03-30 23:02 UTC)