r/learnpython • u/Temporary-Cup-2140 • 17h ago
What python debugging techniques do you think every developer should know ?
I am trying to improve my debugging skills and I was curious about what techniques experienced python developers rely on the most.
Are there any techniques or tools that you found really useful when you started working on bigger projects?
•
u/nivaOne 13h ago
The Print command is a good friend of mine.
•
u/Langdon_St_Ives 13h ago
Ok here's a technique every developer should know: ditch those random print's and instead start logging in a structured way. Why? Because instead of removing all those debugging print statements again, you just change the log level back to info and they're gone. You want them back in three months? Change log level to debug, done.
•
•
•
•
u/Gnaxe 16h ago
Breakpoint and print, obviously, but also
importlib.reload()pdb.pm()code.interact()doctestunittest.mockinspect
Shorten your feedback loops. Make the state transparent. Write tests. Minimize the state you have to deal with in the first place (FP). Use queues instead of locks. Use plain data instead of bespoke classes, and at least implement __repr__() if you must.
•
u/gdchinacat 14h ago
importlib.reload() has wasted more debugging time for me than it has saved. If what you are debugging has any persistent state (ie a cache or a long lived data model) you can have old versions of objects in it that "come back to life" and can mislead you into thinking you didn't fix an issue when you actually did. It took a couple cases of wasting a few hours each time for me to swear it off for good. It is not worth risking that frustating waste of time for the few seconds it saves. If you have a huge app that takes a while to load it can save time, but I will argue you shouldn't be testing fixes to issues against your app, but rather through a unit test that reproduces the issue that you can run on its own. Recommending people learning python to use it is a bad idea IMO because they don't yet understand all the edge cases they need to know to use it effectively.
•
u/this_knee 16h ago
I don’t like the breakpoint and print strategy. I always end up either forgetting about the print statements left behind or then have to search through files and classes for print statements that were just for debugging. This strategy slows me down and incentivizes me to not do it.
However , what I do prefer is placing debug level log statements. Ie a properly built logging system that allows me to simply turn off all my print statements with one quick configuration change. This is something g I like and incentivizes me to do it often because it’s a print statement I know I can come back to if future trouble happens. Do be careful…make sure those log statements only build their soon to be printed message if and only if their log level is enabled. Ie should be a no op when logging is sent to silent mode.
Thanks for coming to my Ted Talk.
•
u/Russjass 12h ago
I am not a pro developer, but have an app with a detailed logging system. When I built the logging system I thought "yay, no more chasing print statements after debugging".
After fixing four bugs, i used print statements for the fifth becuase the log filled up with spam from the lig statements of the first bugs
Perhaps I dont have my logging configured right
•
u/this_knee 11h ago
Key to a good logging is the info that labels the log message. Eg , all my log statements come with: log level ; date; time; name of code file log message comes from; name of function the log message comes from; line number ; and then the log message from that particular line of code.
This lets me easily cut through past log messages and focus on just the ones I need in a given scenario.
•
u/gdchinacat 14h ago
Use the REPL to see how things work and verify they work as you expec. Sometimes code that looks reasonable doesn't work quite like it needs to, so verify code does what you think it does can help find subtle bugs and avoid chasing the wrong issue.
•
•
u/qwerteccia 9h ago
Use your IDE debugger and put breakpoints to see variable's values anywhere you need.
•
•
•
•
u/0xGollumDev 1h ago
Stuff not already in the thread:
- git bisect for "it worked last week." Give it a known-good commit and a known-bad one, it binary-searches history and lands you on the exact commit that broke it. Turns a vague regression into a small diff.
- Post-mortem debugging: python -m pdb -c continue script.py, or in the REPL right after an exception, import pdb; pdb.pm(). Drops you at the frame where it blew up with all locals intact — no editing code to add breakpoints.
- Shrink before you debug. Copy the failing path into a fresh file and delete everything not needed to still reproduce it. Most of the time the bug is obvious at ~15 lines and you never open a debugger.
- Hangs / "stuck forever": run with python -X faulthandler and send SIGABRT, or call faulthandler.dump_traceback_later(10) to auto-dump a traceback if the process is still alive in 10s. Shows the exact line it's spinning on.
- python -W error turns that warning you've been ignoring into an exception with a full traceback.
•
u/Bright_Mix_773 10h ago
Two of the answers here point at the same failure mode from opposite ends, and both are worth turning into something you can run in thirty seconds, because reading about them does not stick.
gdchinacat's reload() warning, reproduced. shape.py holds one class whose area() returns 1:
import importlib, shape
cache = [shape.Shape()] # long-lived object, made before the fix
# now edit shape.py by hand and change that 1 to a 2
importlib.reload(shape)
print("fresh object :", shape.Shape().area())
print("cached object:", cache[0].area())
print("isinstance :", isinstance(cache[0], shape.Shape))
Output, CPython 3.14.2:
fresh object : 2
cached object: 1
isinstance : False
The fix is in the module and not in the object you are testing with, and isinstance against the class you just reloaded returns False for an object of that class. That last line is the tell. If a session ever has you staring at an isinstance, or an "except SomeError" that is obviously true and behaves as if it is not, check "type(obj) is Module.Class" before you touch any logic. The two classes share a name and differ only in id(), so every repr and every log line you print will look identical while you hunt.
The assert point above is the same shape of problem. One file, run twice:
def withdraw(balance, amount):
assert amount <= balance, "overdraft"
return balance - amount
print(withdraw(100, 500))
$ python a.py
AssertionError: overdraft
$ python -O a.py
-400
-O removes the assert and the function hands back a negative balance in silence. The rule that falls out is narrower than "assert is bad": assert is for things you already believe are true and want to hear about while developing. Anything that has to be true, including anything guarding against bad input, needs an if and a raise, because sooner or later something runs under -O and the check is simply not in the bytecode.
The technique behind both, and the one I would put on a list for every developer: when a fix does not seem to take, prove the code running is the code you wrote before you start debugging the code. Module.file, id(SomeClass) and the mtime of the .pyc answer that in three lines and rule out a whole family of bugs that otherwise eats afternoons.
Not verified: whether either behaves the same on PyPy, or on 3.9 through 3.13. Everything above is CPython 3.14.2 on Windows, run just now.
•
u/pachura3 14h ago
Logging > debugging
Also,
assert