Icecream: Never use print() to debug again in Python
github.com
github.com
>>> print(f"{d['key'][1]=}")
d['key'][1]='one' julia> x = 4711
4711
julia> @show x
x = 4711
4711It'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.
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.
__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).
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.')icecream, the subject of this post, returns its parameter unmodified without multiple evaluations.
The fine article
> icecream, the subject of this post, returns its parameter unmodified without multiple evaluations.
That would be my point, yes.
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.
That said, sometimes you don't want to interrupt execution and see your results in real time.
>>> 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.7071067811865475Also assignment expressions? :)
>>> print(f"{(yes:='yes')=}")
(yes:='yes')='yes' >>> print(f"{(yes:='yes') = } and now {yes = }")
(yes:='yes') = 'yes' and now yes = 'yes' 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")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()`.
etcI hope that helps!
One question: in most cases, I get an output like `ic| tmp.py:9 in nonesuch() at 03:23:52.479` What is the "at 03:23:52.479", and how do I turn it off? Looks like it's some sort of timer function, but I don't see it in the readme. Python3.7 on Linux.
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.
According to the RFC, the direct inspiration for dbg! is Haskell's traceShowId (http://hackage.haskell.org/package/base-4.10.1.0/docs/Debug-...).
It's especially faster when I think about how often I fat-finger the '{' (and '}', when my editor doesn't insert the matching brace automagically). Of course YMMV.
I can remove prints by just checking out the latest version of the file.
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.
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.
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...
In these cases prints work well as a less intrusive way to get a rough idea of what is going on.
> 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?
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.
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.
[0] https://docs.microsoft.com/en-us/visualstudio/debugger/using...
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.
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!
Once or twice? Any sane debugger has a way to disable the breakpoint.
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 xDebug 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.
Showing function return values automatically was really an eye-opener when I first encountered it.
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.
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.
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.
We agree with your critique of traditional debuggers and Pernosco tackles that "temporal visibility" problem head on.
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.
Breakpoints are way worse on this dimension.
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).
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.
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.
std::cout << std::hex << my_int << std::dec << std::endl; 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.
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, ...
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.
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— Brian Kernighan, “Unix for Beginners” (1979)
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/PySnooperFor 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: 579Thanks. I'll give it a try.
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.
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.
from icecream import ic;ic() is 28 characters
import q;q() is 12 characters
print() is 7 charactersThis 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.
Besides, just use a proper debugger instead.
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!
from icecream import install
install()
in main, and from then on icecream counts as a built-in, so you can just say ic(). Four characters. >>> 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.Wait until someone creates a $10/user SaaS service out of it.
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.
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).
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.
Newflash! You can do both, and sometimes one is better than the other.
Wish Python has this by default though.
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.
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.
All the one letter PyPi project names were taken.
print(f’{foo(123)=}’)
Which prints: foo(123)=‘result’
"PySnooper is a poor man's debugger. If you've used Bash, it's like set -x for Python, except it's fancier."
Especially in Pycharm for me, it is incredibly easy. Even when running over ssh in remote computers I can use my debugger.
I would say I get more use out of printing these days.
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.
print("here") till the day i die!!!
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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...
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.
# 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.
https://docs.microsoft.com/en-us/visualstudio/python/debuggi...
https://news.ycombinator.com/item?id=19717786 PySnooper: Never use print for debugging again
https://github.com/gruns/icecream/commit/ee849b840eb34242aa2...
(Not suggesting it as a replacement for this tool - but if you’re a pyhacker and don’t know about this it’s handy)
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.
Not just with leading zeros but also written in the YYYY-MM-DD-HH-... format.
I don't use types in my Python much. Yet at least.
Can you submit a PR to add this? That'd be awesome.
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.
pd bug_or_band
Prints this: [PD] /Users/User/trivia_app.rb:4
> pd bug_or_band
=> "beattle"cPython's settrace/setprofile functionality enables so many cool tools.
It's quite nice to have such a function for any programming language you use and bind it to the same keyboard short-cut.
Simple example:
assert x == 4
When this fails, it will print the value of `x`. 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?Actually, I don't know. I assume this is not standard, because many excepthook handlers would probably print some variant of the stack trace on stdout or stderr, and then you would end up having printed the stack trace multiple times. Or maybe it depends. If your excepthook instead prints it somewhere else (e.g. some log file), it makes sense to also call the default handler afterwards.
But no, this is not about the closure.
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.
I mean everyone prints something for debugging purpouse from time to time, thats fine. but having a lib extra for that. i dont think thats a good idea. Logging frameworks also enable toggling if source expression is printed or not. So you can only show it if you give a --debug flag to your tool for example.
Since I became a front-end developer, all I've done is use print statements to debug JS. Feels like I've gone backwards.
julia> @show sqrt(121)
> sqrt(121) = 11.0
> 11.0
The last line being the returned value.Maybe I’m missing something, but it seems unnecessary
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
Of course then you can't brag to your friends about using Vim or Emacs ;)
Either add meaningful and logging to your software (which also other technical people can use).
Or just use a debugger and breakpoints.