8

I'd like to print every line of python script as it's being executed, as well as the log from the console as every line is being executed.

For example, for this script:

import time
print 'hello'
time.sleep(3)
print 'goodbye'

I'd like it to generate the following in the console:

line 1: import time
line 2: print 'hello'
hello
line 3: time.sleep(3)
line 4: print 'goodbye'
goodbye

See below for my attempt

import subprocess as subp

python_string = """
import sys
import inspect

class SetTrace(object):
    def __init__(self, func):
        self.func = func

    def __enter__(self):
        sys.settrace(self.func)
        return self

    def __exit__(self, ext_type, exc_value, traceback):
        sys.settrace(None)

def monitor(frame, event, arg):
    if event == "line":
        file_dict = dict(enumerate("{}".split("|")))
        line_number = frame.f_lineno-25
        if line_number > 0:
           print "line " + str(line_number)+ ": " + file_dict[line_number]
    return monitor

def run():
    {}

with SetTrace(monitor):
    run()

"""
python_string_example = """
    import time
    print 'hello'
    time.sleep(3)
    print 'goodbye'
"""
python_string = python_string.format("|".join([i.strip() for i in python_string_example.split("\n")]),python_string_example)


proc = subp.Popen(['python', '-'], stdin=subp.PIPE,stdout=subp.PIPE, stderr=subp.STDOUT)
proc.stdin.write(python_string)
proc.stdin.close()
for line in proc.stdout:
    print '{}'.format(line.strip())
proc.wait()

Although, this produces the desired result, it produces the output after the entire script is executed. It also is a very bad hack since it will most likely break depending on what python_string_base is

Chris
  • 5,444
  • 16
  • 63
  • 119

2 Answers2

15

You can use the trace module for this

If your 4 lines of code are in tmp.py, calling it as

python -m trace -t tmp.py 

Produces the following output

 --- modulename: tmp, funcname: <module>
tmp.py(1): import time
tmp.py(2): print 'hello'
hello
tmp.py(3): time.sleep(3)
tmp.py(4): print 'goodbye'
goodbye
 --- modulename: trace, funcname: _unsettrace
trace.py(80):         sys.settrace(None)
user13107
  • 3,239
  • 4
  • 34
  • 54
  • 5
    Hi, perhaps there's a way to limit the tracing to current file only. because when I set this option (-m trace -t ) I saw numerous printouts derived from internal python code. – Zohar81 Nov 11 '20 at 07:33
  • 2
    The way to **limit the tracing** is to use the **`--ignore-module`** and **`--ignore-dir`** parameter like for example: `python3 -m trace --trace --ignore-module sys,time --ignore-dir /sys:/usr tmp.py` – Claudio Feb 19 '23 at 07:58
0

In order to make the trace-printing react with output on each executed print() and not only after the entire script is executed it is necessary to flush the stdout buffer after each print() as follows:

        if line_number > 0:
           print( "line " + str(line_number)+ ": " + file_dict[line_number] )
           sys.stdout.flush() ### <<< add for immediate print() response

With the above change the script prints the trace for each line at the time the line is executed.

Claudio
  • 7,474
  • 3
  • 18
  • 48