gowerc
gowerc

Reputation: 1099

How to display python input code along side output

I was wondering if there was away to redirect both the input and output of a python script to a file so that they were interlaced. Essentially say I have the following code:

x = 1
print("hello, world")
print(x)
x = 2
print(x)

def myfun(Val):
    return(Val+1)
myfun(7)

I would be looking for the following to be displayed in an output file

x = 1
print("hello, world")
"hello, world"
print(x)
1
x = 2
print(x)    
2

def myfun(Val):
    return(Val+1)
myfun(7)
8

Things that I have already looked at include:

Ideally I was hoping to find either some sort of bash wizardry with re-directs, a python command line option or a common python module that is able to handle this, but so far no luck :(

EDIT: As a further clarifying example essentially I am trying to re-create the following functionality from R. Say we have the R program myprog.R as:

myfun <- function(x){
    x + 1
}
y <- myfun(7)

print("hello,world!")
print(y)
y + 1

Then running from the command line R CMD BATCH myprog.R produces the file myprog.Rout which looks like

myfun <- function(x){
    x + 1
}
y <- myfun(7)

print("hello,world!")
[1] "hello,world!"

print(y)
[1] 8

y + 1
[1] 9

Upvotes: 1

Views: 885

Answers (4)

tobias_k
tobias_k

Reputation: 82899

One rather simple way would be to just paste the code into an interactive Python shell, i.e. open your file, copy the content, run python without a file argument, and paste the content.

Example file:

def foo(n):
    if n % 2 == 0:
        return n**2
    else:
        return n**.5

x = 41
print(foo(x))
print(foo(2 * x))

Output:

>>> def foo(n):
...     if n % 2 == 0:
...         return n**2
...     else:
...         return n**.5
... 
>>> x = 41
>>> print(foo(x))
6.4031242374328485
>>> print(foo(2 * x))
6724

This adds those >>> and ... prefixes to the code lines, but this might actually help readability of the output. Also, code-files have to be indented with spaces (no tabs), and blocks (like function definitions) have to be terminated with an extra blank line, otherwise they won't work.

Upvotes: 0

d parolin
d parolin

Reputation: 194

As an alternative you could parse the output created by the trace module and cut off the pieces you do not like. In this example I limited myself to the prefixes and the module printouts. Be cautious as without properly formatted (PEP8) code this will easily cut away function definitions too, however that may be a feature not a bug ;-) In its default use trace does not give you the function contents however. Depends on what exactly you need.

Save the following as trace_rt.py and use as follows

python -m trace -t yourfile.py | trace_rt.py

trace_rt.py:

import sys


def cutoff(in_line):
    """Removes trace prefix if present"""
    if "): " in line:
        return line.split("): ")[1]
    elif line.startswith("--- modulename:"):
        return ""
    return line


for line in sys.stdin:
    line = line.strip()
    if line:
        print(cutoff(line))

Upvotes: 0

d parolin
d parolin

Reputation: 194

[EDIT] This is a very limited script, unfortunately this breaks immediately if your code gets more complex (means statements span multiple lines), like as simple as having a function.

For your very simple example usecase you can use an intermediate script as follows (lets assume filename rt.py):

import sys

in_file = sys.argv[1]

with open(in_file) as script:
    for line in script:
        line = line.strip()
        if line:
            # Option to visually separate it by using repr
            # print(repr(line))
            print(line)
            # Another optional use a prefix
            # print(">>>", end="")
            exec(line)

Now you can pass it your existing script

python rt.py somescript.py

It would not be impossible to enhance this to preparse the script based on indentation , but it is probably not worthwhile and I hope other solutions exist that I am not aware of.

Upvotes: 1

code11
code11

Reputation: 2309

Assuming the input file is called a.py, this redirects stdout of the eval call so that it can be caught then written to a file (out.py) along with the source.

Like dparolin said, although this works for easy scripts as soon as you have blocks things will fall apart.

import sys
from io import StringIO
import contextlib


filename="a.py"
f= open(filename,"r")
f_out= open("out.py","w+")

SCRIPT_LOCALS={}

@contextlib.contextmanager
def stdoutIO(stdout=None):
    old = sys.stdout
    if stdout is None:
        stdout = StringIO()
    sys.stdout = stdout
    yield stdout
    sys.stdout = old

def eval_with_return(str_code):
    with stdoutIO() as s:
        exec(str_code,SCRIPT_LOCALS)
        return s.getvalue()


for line in f:
    orig_line=line if line.endswith("\n") else line+"\n"
    sys_out_res=eval_with_return(line)
    f_out.write(orig_line)
    f_out.write(sys_out_res)

f.close()
f_out.close()

Scoping came from here. Context manager redirect came from here.

Upvotes: 0

Related Questions