Malonge
Malonge

Reputation: 2040

Standard input inconsistency between command line and subprocess.call

I would like to create a file that will be used as standard input for a python script, and invoke said script with subprocess.call.

When I do it directly in the command line it works fine:

The input file:

# test_input
1/2/3

The python script

# script.py
thisDate = input('Please enter date: ').rstrip()

The following command works just fine:

python script.py < test_input

But when I try to do the following from within another python script, it doesn't work. (from this)

outfile1 = open('test_input', 'w')
outfile1.write('1/2/3')
outfile1.close()

input1 = open('test_input')
subprocess.call(['python', 'script.py'], stdin=input1)

But then I get the following error:

>>>thisDate = input('Please enter date: ').rstrip()
>>>AttributeError: 'int' object has no attribute 'rstrip'

When I did some debugging, it seems that it is getting the integer 0 as the input.

What is causing the inconsistency here? Are the two methods not equivalent (evidently they are not, but why)? My ultimate goal is to perform the exact same task as the above command line version that worked.

Thank you

Upvotes: 1

Views: 91

Answers (4)

jfs
jfs

Reputation: 414265

python script.py < test_input command should fail. You might mean: python3 script.py < test_input instead due to the difference between input() vs raw_input() on Python 2 as mentioned in other answers. python as a rule refers to Python 2 version.

if the parent script is run only using python3 then you could use sys.executable to run the child script using the same python version (the same executable):

#!/usr/bin/env python3
import subprocess
import sys

with open('test_input', 'rb', 0) as input_file:
    subprocess.check_call([sys.executable or 'python3', 'script.py'],
                          stdin=input_file)

If the parent and the child may use different python versions then set the correct shebang in script.py e.g., #!/usr/bin/env python3 and run the script directly:

#!/usr/bin/env python
import subprocess

with open('test_input', 'rb', 0) as input_file:
    subprocess.check_call(['./script.py'], stdin=input_file)

Here, the child script may choose its own python version. Make sure the script has executable permissions: chmod +x script.py. Note: Python Launcher for Windows understands the shebang syntax too.

Unrelated: use .communicate() instead of outfile1.write('1/2/3'):

#!/usr/bin/env python3
from subprocess import Popen, PIPE

with Popen(['./script.py'], stdin=PIPE, universal_newlines=True) as p:
    p.communicate('1/2/3')

Upvotes: 0

Padraic Cunningham
Padraic Cunningham

Reputation: 180411

You are using input when it should be raw_input, input in python2 will eval the string. If you run the script with python3 it will work as is, for python2 change to raw_input.

Using check_call is usually a better approach and using with to open your files.

import subprocess
with open('test_input') as input1:
    subprocess.check_call(['python3', 'script.py'], stdin=input1)

Upvotes: 1

tripleee
tripleee

Reputation: 189417

In the first instance, the file has two lines, and input() reads and parses the first line, which is a comment.

In the second case, the comment line is missing, so Python reads and parses a number.

You probably meant to use raw_input(), or run the script with Python 3.

(You probably also meant for the input file to end with a newline, and it doesn't really make sense to use subprocess.call() to run Python when you are already running Python.)

Upvotes: 0

Malonge
Malonge

Reputation: 2040

So chepner was correct. When I amended the following line:

subprocess.call(['python', 'script.py'], stdin=input1)

to:

subprocess.call(['python3', 'script.py'], stdin=input1)

it worked just fine.

(I am trying to do this in python3)

Upvotes: 0

Related Questions