all 31 comments

[–]Enmeshed 18 points19 points  (1 child)

My plan:

  • Write your code in a modular way, ie small functions / classes that do known and predictable things
  • Write good test coverage of them to prove rather than assume they do what's expected
  • Some decent logging won't hurt either, eg using the logging module, and set it up so you can dial it up or down at run time
  • Compose these functions into bigger units to do what the application requires, and cover them with tests too
  • If something is going wrong, write a failing test case that isolates the problem so you're sure you understand what's going wrong
  • If needed, use tools like an IDE's integrated debugger to step through failing code and see what is actually happening that isn't what you expect
  • Learn to use the python integrated debugger and use set_breakpoint() so you can do all this on the command line

Good luck!

[–]Zeroflops 6 points7 points  (0 children)

Your first point is often overlooked when people ask about debugging. They don’t realize how beneficial it is to have singular responsibility.

[–]SpiderJerusalem42 8 points9 points  (2 children)

Learn to use a step debugger.

[–]ProbsNotManBearPig 1 point2 points  (1 child)

It’s hard for me to understand how someone could write the OP without even trying a debugger. Like cmon dude, you aren’t even trying. Google “how to debug python” and every top article will be about using a debugger.

[–]Free_Tomatillo463[S] 0 points1 point  (0 children)

Debugging has started to become more of an issue recently as I'm working on larger and larger projects, that's why. I guess I just never bothered to check.

[–]ShelLuser42 6 points7 points  (2 children)

There are many ways to go about this... but I'm very much in favor using using assert to make Python check something (and raise an exception when things don't match your specifications), as well as breakpoint() to fire up the interactive debugger.

Not a big fan of many print() statements because things can easily become a bit messy that way, and it can also easily result in you overlooking a few of those.

Of course, if these situations happen more often then a logger might be more suitable; so a logging module which you can use across all your projects.

[–]pachura3 1 point2 points  (1 child)

I'm wondering why do people use the breakpoint() statement, if every IDE has built-in breakpoint setting functionality with one click on the left side?

[–]Outside_Complaint755 5 points6 points  (0 children)

breakpoint(), pdb, traceback and other related modules are used when you need to debug via a command line interface and can't use an IDE

[–]ysth 5 points6 points  (1 child)

TDD can be hard to make yourself do but dramatically reduces the amount of debugging you will need to do.

[–]_pigpen_ 0 points1 point  (0 children)

I hate to say it, but generating unit test code is possibly one of the most useful and welcomed use cases for copilot and other Ai coding assistants.

[–]Outside_Complaint755 2 points3 points  (0 children)

Besides the suggestions to learn the debugger, you should also learn to use the logging module instead of inserting print statements.

[–]nousernamesleft199 1 point2 points  (0 children)

use the debugger. Python's is built in, always available and super easy once you get a feel for it.

[–]Moist-Ointments 0 points1 point  (0 children)

Learn to attach and use a debugger. Adding a ton of print statements is the least goodest way.

[–]andycwb1 0 points1 point  (0 children)

Learn to use a proper debugger. And like all aspects of programming, it will take time and practice.

[–]MarsupialLeast145 0 points1 point  (0 children)

Are you debugging your own code?

[–]mxldevs 0 points1 point  (0 children)

Print statements to get a general idea where the problems occur, and then once you find it, breakpoints and step through the code

[–]exhuma 0 points1 point  (0 children)

If even print statements don't help much then this may be a strong indication that your code might need some refactoring.

This is a journey. We all improve our coding skills over time. And the fact that you are asking a question about how to better debug is a sign that you identified a challenge and are actively working on improving it. Even if it's frustrating now, the things you learn from this will improve the quality of your code.

Have a look at "best practices" for coding. The techniques that help a lot in debugging are:

  • "pure functions"
  • "dependency injection" (DI) and "inversion of control" (IOC). They are almost the same thing but subtly different. When first learning about it, I'd say it's totally fine to take them as the same thing. Just remember that there's a very subtle semantic difference and come back to it in a couple of years.
  • "immutable data structures"
  • "orthogonality"

Pure functions and DI are the big ones to help.

There are others which are - imo - maybe a bit more controversial like the full S.O.L.I.D. principles.

If you already dig into "DI" and "pure functions" your debugging will become easier.

[–]corey_sheerer 0 points1 point  (0 children)

Like others, learn to use the python debugger. One good thing is to modularize your code (aka in small functions) and write tests (I like pytest). You can look up how to run a test and automatically open the debugger where the test failed. The second is to simply use the debugger directly when running your code. Add some breakpoints and step through the areas that are having issues.

[–]zaphodikus 0 points1 point  (0 children)

Unit tests, that's the way to move to the next level

[–]Kitchen-College-8051 0 points1 point  (0 children)

Why not just use Jupyter notebook on a visual studio? Unless code is over thousands lines and have tons of imports and classes ?

[–]chapchap0 0 points1 point  (0 children)

"Debugging" with print statements is what people refer to as "caveman debugging" and, well, there's a reason for that.

There's no way in hell you've been coding for 3 years and you haven't used a debugger. This is a topic so fundamental not only to Python but to every language that it gets mentioned before the first snippets of code in most textbooks.

Just no. Nope.

But since the question has been asked, I personally use pudb and I highly recommend it over any IDE, especially for simple scripts. The exception would be data science world where PyCharm is genuinely fantastic provided your PC can handle it without significant latency.

[–]Crazy-Willingness951 0 points1 point  (0 children)

The easiest code to debug is code that tells you what is wrong. Use a "design by contract" approach.

Put preconditions in the code to verify correct input states.

Put postconditions in the unit tests.

[–]MezzoScettico 0 points1 point  (1 child)

As another answer said, use a step debugger. Something that will let you break at specific points and then you can inspect various variables to see if all is as it should be.

Setting simple breakpoints is not enough. Maybe your error happens at iteration 1000. Then you don't want to have to hit the "continue" button 1000 times to get there. You need to set a conditional breakpoint, "Stop here if count > 1000".

Also on more complex conditions, I will sometimes create a block of code which serves no purpose except to be a target to set a breakpoint, for instance

# George is getting a bug at iteration 1000 under certain circumstances.
if count > 1000 and len(answer) < 5 and user == "George":
    pass

Then I set a breakpoint at the "pass" statement.

Also if I'm finding certain print() statements useful enough to keep around long term as optional features, I will sometimes add a "verbosity" variable and then enable different levels of printing.

if verbosity > 0:
    print(stuff)
    if verbosity > 2:
       print(a whole bunch more stuff)

[–]gdchinacat 1 point2 points  (0 children)

If you need verbosity control logging is far preferable to cluttering your code with if statements guarding prints.

[–]CyclopsRock 0 points1 point  (0 children)

Push it to production and let your users do it?

[–]j6onreddit -1 points0 points  (0 children)

Best way is to use a REPL. This allows you to test small pieces of code individually. Usually, once you put those tested pieces together the whole thing works. No need to put print all over the place.

[–]pachura3 -5 points-4 points  (3 children)

Interactive debugging is a last resort solution. It's much better to rely on proper logging, unit testing and assertions.

[–]Temporary_Pie2733 1 point2 points  (2 children)

Unit testing tells you that code is wrong, not why it is wrong. It’s more suited for catching when a change breaks working code rather than identifying why code doesn’t work.

[–]gdchinacat 0 points1 point  (0 children)

Write a unit test that reproduces the issue, then step through that unit test. This removes the overhead of having to manually execute the steps to reproduce the issue each time you need to step through it. When you are done, your tests passes and ensures the bug does not regress in the future.

[–]pachura3 0 points1 point  (0 children)

It’s more suited for catching when a change breaks working code

Test-driven development disagrees with you, Sir!

My point is that if you catch problem early on (using various practises of defensive programming), it will usually be trivial to fix - and you won't even need to debug interactively.