static_rtti
static_rtti

Reputation: 56382

How do you find where a print statement is located?

I have a program depending on a large code base that prints a lot of irrelevant and annoying messages. I would like to clean them up a bit, but since their content is dynamically generated, I can't just grep for them.

Is there a way to place a hook on the print statement? (I use python 2.4, but I would be interested in results for any version). Is there another way to find from which "print" statement the output comes?

Upvotes: 4

Views: 1464

Answers (5)

Aaron Digulla
Aaron Digulla

Reputation: 328860

Here is a trick that Jeeeyul came up with for Java: Replace the output stream (i.e. sys.out) with something that notices when a line feed has been written.

If this flag is true, throw an exception when the next byte is being written. Catch the exception in the same place, walk up the stack trace until you find code that doesn't belong to your "debug stream writer".

Pseudocode:

class DebugPrintln:
    def __init__(self):
        self.wasLF = False

    def write(self, x):
        if self.wasLF:
            self.wasLF = False

            frames = traceback.extract_stack()
            ... find calling code and output it ...

        if x == '\n':
            self.wasLF = true

        super.write(x)

Upvotes: 1

Alfe
Alfe

Reputation: 59616

In harsh circumstances (output done in some weird binary libraries) you could also use strace -e write (and more options). If you do not read strace's output, the straced program waits until you do, so you can send it a signal and see where it dies.

Upvotes: 1

unutbu
unutbu

Reputation: 880987

For CPython2.5 or older:

import sys
import inspect
import collections
_stdout = sys.stdout

Record = collections.namedtuple(
    'Record',
    'frame filename line_number function_name lines index')

class MyStream(object):
    def __init__(self, target):
        self.target = target
    def write(self, text):
        if text.strip():
            record = Record(*inspect.getouterframes(inspect.currentframe())[1])        
            self.target.write(
                '{f} {n}: '.format(f = record.filename, n = record.line_number))
        self.target.write(text)

sys.stdout = MyStream(sys.stdout)

def foo():
    print('Hi')

foo()

yields

/home/unutbu/pybin/test.py 20: Hi

For CPython2.6+ we can import the print function with

from __future__ import print_function

and then redirect it as we wish:

from __future__ import print_function
import sys
import inspect
import collections

Record = collections.namedtuple(
    'Record',
    'frame filename line_number function_name lines index')

def myprint(text):
    if text.strip():
        record = Record(*inspect.getouterframes(inspect.currentframe())[1])        
        sys.stdout.write('{f} {n}: '.format(f = record.filename, n = record.line_number))
    sys.stdout.write(text + '\n')

def foo():
    print('Hi')

print = myprint
foo()

Note that inspect.currentframe uses sys._getframe which is not part of all implementations of Python. So the solution above may only work for CPython.

Upvotes: 3

Inbar Rose
Inbar Rose

Reputation: 43517

a very gross hack to make this work:

use your favorite text editor, use your search/find feature.

find all the print statements.

and input into each of them a number, or identifier manually. (or automatically if you do this what a script)

a script to do this would be simple, just have it look for for print with regex, and replace it with print ID, and then it will all be the same, but you will get numbers.

cheers.

edit

barring any strange formatting, the following code should do it for you.

note, this is just an example of a way you could do it. not really an answer.

import re

class inc():
    def __init__(self):
        self.x = 0

    def get(self):
        self.x += 1
        return self.x

def replacer(filename_in, filename_out):
    i = inc()
    out = open(filename_out, 'w')
    with open(filename_in) as f:
        for line in f:
            out.write("%s\n" % re.sub(r'print', 'print %d,' % i.get(), line))

i used an basic incrementer class in case you wanted to had some kind of more complex ID, instead of just having a counter.

Upvotes: 2

Deestan
Deestan

Reputation: 17176

Strictly speaking, code base that you depend on, as in libraries, shouldn't contain any print statements. So, you should really just remove all of them.

Other than that, you can monkey-patch stdout: Adding a datetime stamp to Python print

Upvotes: 3

Related Questions