When I'm debugging Python I tend to sprinkle my code liberally with print statements. Trouble is, I forget which files I added those print statements to and have to waste a few minutes tracking them down. Time which could be better spent doing pretty much anything else.

Not sure why I didn't think of this before, but I hacked together something that automatically prints the file and line of the print statement next to the output.

Here's the code:

import inspect
import sys
import os

class DebugPrint(object):
    def __init__(self, f):
        self.f = f

    def write(self, text):
        frame = inspect.currentframe()
        filename = frame.f_back.f_code.co_filename.rsplit(os.sep, 1)[-1]
        lineno = frame.f_back.f_lineno
        prefix = "[%s:%s] " % (filename, lineno)
        if text == os.linesep:
            self.f.write(prefix + text)

if not isinstance(sys.stdout, DebugPrint):
    sys.stdout = DebugPrint(sys.stdout)

To use it, save it as ‘debugprint.py’ then imported it somewhere. That's all you need to do. Works on Python 2.7 and probably other 2.X Pythons.

Here's what happens if you do it from the console:

>>> import debugprint
>>> print "Hello"
[<stdin>:1] Hello

For print statements in your app, you will get something like:

[foo.py:22] Hello
This blog post was posted to It's All Geek to Me on Sunday December 9th, 2012 at 5:32PM

16 Responses to "Where are those Print statements?"

  • December 9th, 2012, 6:59 p.m.

    Nice one. I'd prefer a non-intrusive method, however. How about a command line tool that visits a directory's “.py” files, constructs the AST and lists all “print” statements (or print function calls in python3)? Admitted, it's a bit of work but it'd be useful without modifying any project's source code or importing special code.

  • Tom
    December 9th, 2012, 7:56 p.m.

    Aren't you using a revision-control tool, like git? If so, then that's the easiest way to identify and remove those debug statements. I always use ‘git diff’ before checking in changes to make sure I'm checking in just what I think I am.

  • j
    December 9th, 2012, 8:24 p.m.

    I always prefer to use logging http://docs.python.org/3.3/library/logging.html.

    import logging
    #set up logging:
    logging.basicConfig(format='%(asctime)s %(message)s')
    logger = logging.getLogger(__name__)
    # show warnings (remove for production)
    info = logger.info
    warning = logger.warning
    debug = logger.debug

    then, instead of


    I use


    and get

    2012-12-09 22:13:32,055 hello

    You can use

    %(module)s %(lineno)d

    to make it print your current module name and line number.

    It is amazingly simple, more versatile, and to stop all those print commands you simple change the logger level..

  • Dan
    December 9th, 2012, 8:40 p.m.

    j's got the right idea. Plus, instead of removing your debug statements when you're finished, you can just turn them off by setting the log level above debug.

  • December 9th, 2012, 8:55 p.m.

    I prefer the humble print statement for debugging. This isn't the kind of debug output I would leave in my app so there isn't much of an advantage in using the logging module.

  • December 9th, 2012, 9:17 p.m.

    @holger and OP

    Do you know ack? No need to re-invent the wheel.

  • Jan Gee
    December 10th, 2012, 2:40 a.m.

    A self-proclaimed Python *expert* who uses print to debug… wow. Just wow.

  • j
    December 10th, 2012, 11:27 a.m.

    @Will McGugan
    I don't get it.
    The overhead of logging is even shorter then your thing.
    It is a standard, it is much more pythonic,
    scales easily, and if leaving the debug commands in your code
    offends your users for some reason (although after changing the loglevel they have no discernible effect),
    they can be deleted in the same manner as your print statements.
    Actually deleting is even easier because you cannot mistake a proper print with a debug print.

    I would of course leave the debug statements in the code for the next bug-finding quest. If it is recommended for core python development, it is good enough for me.

  • December 10th, 2012, 1:15 p.m.

    @j I used this for debug output that is purely for tracking down a particular bug. It's either dumping a variable or just some marker so I can see if execution hit a particular point. Once the bug is fixed, the output becomes irrelevant. It wouldn't be of use to myself or anyone else in the future. Which is why I don't consider it worthy of logging.

    Hiding it with a debug level is not particularly useful, since if it came back it would just be logging spam. And if it remained in the code it could still have a negative impact. For instance if I print out a database query so I can inspect the results – with something like logging.debug(repr(profile)) – it still hits the database and does a lot of string processing regardless if the logging level means the text will not make it to the logs.

    So this kind of debug output should definitely be deleted. Either I use debug(“foo”) or print(“foo”). The difference is large irrelevant – especially if the logging is configured for file and line like you said. But at least with ‘print’ I know it is going to the console, no matter how logging is currently configured.

  • December 10th, 2012, 3:49 p.m.

    there's grep -R which search all files in all current working directory. I'd use it like this: `grep -R print`.

  • James
    December 10th, 2012, 9:04 p.m.

    I just add a comment on every print:

    print ("A string") // JAMES

    Then grep when I'm done. And if I forget the next person to work with the code knows that I'm the idiot at fault.

  • Wouter
    December 11th, 2012, 9:16 a.m.

    J +1

    You've just invented something that does the same as logging, only worse. What on earth is the overhead in

    import logging; log=logging.getLogger(__name__)

    on top of every module. You want that anyway because you also log stuff, don't you? Then, you can simply use log.debug(XXX) where you want to print a trace. Your version control should tell you who entered that statement and when, and if you do a diff before every commit (which I think any sensible developer should do!) you can easily see if you forgot to remove any debug messages that should not be there anymore.

  • Sam
    December 11th, 2012, 9:54 a.m.

    Print has its place in debugging.

    We use the logging module extensively and log thousands of messages a day. Each message is carefully assigned a level and we have gui and command line tools to make sure we can see the messages we need.

    But for something temporary where your tracking down a bug there's nothing wrong with print. There are some messages that I just don't want written to files, etc. That's where print can come in. This is a clever hack!

  • Scott
    December 11th, 2012, 10:36 p.m.

    Why the hostility in the comments? I love this.

  • December 11th, 2012, 10:40 p.m.

    Not sure! I had no idea it would be so contentious…

  • December 19th, 2012, 8:30 p.m.

    “A self-proclaimed Python *expert* who uses print to debug… wow. Just wow.”

    How can people be so stupidly arrogant? There are pluses and minuses and intelligent, well-informed people can choose one or the other.

    BTW, I saw an interview with Guido in which he mentioned that when debugging, he likes to use a lot of print statements. Or maybe he doesn't qualify as a “Python ‘expert’”?

Leave a Comment

You can use bbcode in the comment: e.g. [b]This is bold[/b], [url]http://www.willmcgugan.com[/url], [code python]import this[/code]
Preview Posting...
Previewing comment, please wait a moment...
Will McGugan

My name is Will McGugan. I am an unabashed geek, an author, a hacker and a Python expert – amongst other things!

You are reading my tech blog. See the homepage for my other blogs.

Search for Posts
Possibly related posts
Popular Tags
Recent Comments
def thousands_with_commas(number): new_number = [] number = str(number) mod_value = len(number) % 3 counter = 3 if len(number) 4: return ...
don't know why this was tempting.. (#1)import re from collections import Counter, OrderedDict cnt=Counter() with open(./t) as f: #--- strip ...
- Mike on Python Coder Test
Hello! I've seen this test and tried to do them. Result added bellow. First path: def thousands_with_commas(i): i = str(i) ...
Why another framework? what wrong with django, pyramid, flask?will be have answer for this question in the docs)
Hi! Really great code, good work! But trying to use it on a responsive site, it didn't resize images. So, ...
© 2008 Will McGugan.

A technoblog blog, design by Will McGugan