2

I am trying to figure out a way to redirect output from a script that I'm writing that runs the interactive console.

I need to be able to:

capture the output in a string, and then check it, or print it out.

Is this doable? Should I be using a different python module?

I tried this with the subprocess module, but ran into a problem where I could only do 1 read at the end of the process. That won't work, I need to read multiple times from stdout. This is what I have so far:

import code
console = code.InteractiveConsole()
console.push('from sim import newsteam\n')
console.push('assembler = newsteam()\n')
console.push('assembler.__init__()\n')

line = console.push('assembler.reportr()\n')
#I need to be able to interact with this output
line1 = console.push('assembler.reportpc()\n')
line2 = console.push('assembler.reportm()\n')

print("this is the result: " + line + line1 + line2)
esnadr
  • 427
  • 3
  • 18
Derek Halden
  • 2,223
  • 4
  • 17
  • 26

4 Answers4

1

You could redirect the stdout temporarily:

import code
import sys
from contextlib import contextmanager

try:
    from StringIO import StringIO
except ImportError: # Python 3
    from io import StringIO

try:
    from contextlib import redirect_stdout 
except ImportError: # Python < 3.4
    @contextmanager
    def redirect_stdout(new_target):
        old_target, sys.stdout = sys.stdout, new_target # replace sys.stdout
        try:
            yield new_target # run some code with the replaced stdout
        finally:
            sys.stdout = old_target # restore to the previous value

f = StringIO()
with redirect_stdout(f):
    console = code.InteractiveConsole()
    console.push('print("hello world")')
    console.push('i = int(input("give me a number"))')
    console.push('sq = i*i')
    console.push('print("%d squared is %d" % (i, sq))')
print("Got: %r" % f.getvalue())

Example:

$ echo 2 | python code-interactive.py 
Got: 'hello world\ngive me a number2 squared is 4\n'
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • Will this get me multiple values? Like say afterwards, I wanted to take j as input, do the square of j in the console, and depending on that value, say, `if (j < 10):` I want to run a new console.push with k, otherwise, I want to end the program. – Derek Halden Mar 16 '14 at 16:18
  • Nevermind, I realized why this works, and what you are doing. Thank you very much for your help. – Derek Halden Mar 16 '14 at 16:31
  • @DerekHalden: you could check the output: `f.getvalue()` after each `.push()`, just reset the `StringIO()` object: `f.seek(0); f.truncate()` to avoid reading the same output multiple times. – jfs Mar 16 '14 at 16:57
  • Yes, I realized that after I asked the question. Thank you very much for your help! – Derek Halden Mar 16 '14 at 21:36
0

Perhaps you could try using subprocess Popen, spawn IDLE as a subprocess, and manually handle stdin and stdout using pipes. However, if you go that route read up on solutions for non-blocking read/writes. By default, reads (ie. readlines()) will keep reading until the subprocess ends. It appears you need it to be interactive (handling both stdin and stdout). Check out this thread.

Community
  • 1
  • 1
dotslashb
  • 54
  • 4
  • I had seen that thread before, and tried to use it, but I kept running into an broken pipe error. – Derek Halden Mar 15 '14 at 15:28
  • @DerekHalden: Regardless of whether you choose to use `subprocess` to interact with python or not: if you don't know why your code is broken; [ask](http://stackoverflow.com/questions/ask). – jfs Mar 16 '14 at 09:02
  • [there are multiple issues with using `subprocess` for interaction with `python`](http://pexpect.readthedocs.org/en/latest/FAQ.html#whynotpipe) – jfs Mar 16 '14 at 09:03
  • @J.F.Sebastian I would have, but I was trying to use this console method first instead, because you had recommended it to me in a previous question's comment thread. – Derek Halden Mar 16 '14 at 16:35
0
pop = subprocess.Popen([ "command",'argument'] , stdin=subprocess.PIPE , stdout =subprocess.PIPE  )  
while True:
    l = pop.stdout.readline()
    if not l :
        break 
    if l == '':
        time.sleep(1)
        continue 
    # do some function with l 
    # ....

N.B:. Not tested, but it should work.

thanks for @ J.F. Sebastian_comment

esnadr
  • 427
  • 3
  • 18
  • `.communicate()` passes the whole input exactly once. And returns the whole output at once. There is no more interactivity. `pop.stdout.readline()`-based solution has [buffering issues that may easily result in a deadlock](http://pexpect.readthedocs.org/en/latest/FAQ.html#whynotpipe) – jfs Mar 16 '14 at 05:40
0

You can use a local variable to store the results generated inside the console.

For instance:

import code
output = []
console = code.InteractiveConsole({'output':output})
console.push('import sys')
line = console.push('output.append(sys.version)')
print('Result value:', output)

You'll get a list with the output lines you want to check in your main module.

In your case you could do something like:

import code
output = []
console = code.InteractiveConsole({'output': output})
console.push('from sim import newsteam\n')
console.push('assembler = newsteam()\n')
console.push('assembler.__init__()\n')

console.push('output.append(assembler.reportr())')
console.push('output.append(assembler.reportpc())')
console.push('output.append(assembler.reportm())')

print("this is the result: ", output)
Roberto
  • 8,586
  • 3
  • 42
  • 53
  • I suspect that functions named `report*` return nothing (`None`) and print to stdout. You can [redirect stdout](http://stackoverflow.com/a/22434858/4279) – jfs Mar 16 '14 at 10:39
  • In such case, you are right. I considered that the methods return a str. – Roberto Mar 16 '14 at 11:04