Roll Your Own Python Debugger

Debugging might be the one thing that separates “modern” programming from “classic” programming. If you are on an old enough computer — or maybe one that has limited tools like some microcontrollers — debugging is largely an intellectual exercise. Try a program, observe its behavior, and then try again. You can liberally sprinkle print statements around if you have an output device or turn on LEDs or digital outputs if you don’t. But with a debugger, you can get a bird’s-eye view of your program’s data and execution flow.

For some languages, writing a debugger can be hard — you usually use at least some system facility to get started. But as [mostlynerdness] shows, Python’s interpreter wants to help you create your own debugger, and you can follow along to see how it’s done. This is accessible because Python has a built-in debugging core that you can use and extend. Well, regular Python, that is. MicroPython has some low-level support, and while we’ve seen attempts to add more, we haven’t tried it.

Of course, you may never need to build your own debugger — most of the IDEs have already done this for you, and some of the code is, in fact, lifted from an open code base and simplified. However, understanding how the debugging plumbing works may give you a leg up if you need to create custom logic to trap an error that would be difficult to find with a generic debugger. Plus, it is just darn interesting.

Like many Python things, there are some version sensitivities. The post is in four parts, with the last two dealing with newer API changes.

We can’t promise that Python can debug your hardware, though. We always thought the C preprocessor was subject to abuse, but it turns out that Python has the same problem.

9 thoughts on “Roll Your Own Python Debugger

  1. Kernighan’s Law: “Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.”

    1. Which is why one of my iron-clad coding rules is “only one ‘smart’ thing at a time, NO EXCEPTIONS”. Which basically means the smart thing has to be presented as dumbly as possible. A smart thing done in a ‘smart’ way ends up being obfuscation.

  2. Been doing embedded programming for 30+ years now. Never once have I had a need for a debugger. Slap a few printf’s here and there was enough. The problem today is so many coders don’t know how to code. If you find yourself relying on a debugger often you might want to consider learning how to code better.

    1. I’ve been a developer for 40+ years. When I discovered debuggers, made coding so much easier. Was fun watching coders struggling with assembly for days or weeks when they were trying to debug manually. With a debugger I could do the similar in a few hours.

      Debuggers create better programmers. As do lint checkers, auto completion tools and other development aids. I would never hire a programmer that insisted on using outdated methodology. Might as well write the code out on paper first.

    2. Having done some embedded programming on the side, as a hobbyist, I can somewhat see where you’re coming from. But as someone who’s been writing code for a living for 17+ years, I’m afraid you’re comparing apples to oranges.

      Large scale applications that provide UIs and/or APIs have to contend with so many input combinations and edge cases that embedded systems don’t have to contend with. You also have to deal with code written by numerous other team members with different levels of skill, and often with packages developed by 3rd parties with many nuances.

      Something as simple as a date can be a nightmare to deal with when you have to connect to multiple systems, each with their own locale (aka, date format), operating in a different timezone. As others already pointed out: sure, you could wrestle your way using prints and log messages, but it’s literally 100x faster to debug the code, especially with modern development environments that enable edit-and-continue, and modern development methodologies like TDD.

      With good IDEs and debuggers, I can offload something like the date example above to a junior developer on my team with barely 1 year of experience, and trust that he’ll be able sort things out in a short amount of time aided by the debugger and unit tests.

      1. Yes, sure, and also kind of… If it’s that simple, and you have all kinds of matter of sophisticated software tools at your disposal, then that’s not the problem, is it?
        And the API and syntax can just as well be specified in the documentation, and discarded if not valid.
        The critical cases will always be the more esoteric and special cases where there is no documentation or it’s just not accurate. And you are stuck in some other environment. And the debugger will tell you that the code does what you told it to, but it still does not work. And what you really would like to know is what the code on the other side looks like, cuz wtf.
        But tdd is nice.

  3. Thanks for sharing my article series on writing a Python debugger here. I hope to present it at a Python conference near you in a condensed form :)

    It’s quite interesting how Python debuggers differ from Java debuggers: Python has a tiny API and you build your own facility for breakpoints, while Java has a complex API that takes care of many things, while also being quite complex to use (see https://mostlynerdless.de/blog/2022/12/27/a-short-primer-on-java-debugging-internals/ for more).

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.