Return value of variable from subprocess in python
I have a code workflow where, from a main script (level 0), I call another script through subprocess
. This one subprocess script
(level 1) in turn calls another script like subprocess
. Now from this level 2 sub-process script I want to return the value of a variable before the main script (level 0). I have tried Popen.communicate()
but I cannot return the value. My current code:
main_script.py
def func():
para = ['a','b']
result = subprocess.Popen([sys.executable,"first_subprocess.py"]+para,stdout=subprocess.PIPE)
result.wait()
return_code = result.returncode
out, err = sub_result.communicate()
if return_code == 1:
return return_code
else:
if out is not None:
print 'return value: ', out
if __name__ == '__main__':
func()
Above, a script is called first_subprocess.py
which has:
def some_func():
# some other code and multiple print statements
para = ['a','b']
result = subprocess.Popen([sys.executable,"second_subprocess.py"]+para,stdout=subprocess.PIPE)
result.wait()
out, err = result.communicate()
return_code = sub_result.returncode
if return_code == 0:
return out
if __name__ == '__main__':
some_func()
second_subprocess.py
returns a value similar to:
def test():
# some other code and multiple print statements
val = 'time'
print 'returning value'
return val
if __name__ == '__main__':
test()
When I try to execute the code above, I get all the statements print
in the codes as output, but not the return value. Even if you try to print
change the value of a variable in a subprocess instead of executing, it will not serve the purpose because there are multiple print statements. In the meantime, there is no need to know about it. ”
How can I return the value of a variable in this case?
UPDATED VERSION:
Following @Anthons suggestion, I changed my first_subprocess.py
script and main_script.py
as follows:
first_subprocess.py:
def some_func():
try:
key = None
if not (key is None):
para = ['a','b']
result = subprocess.Popen([sys.executable,"second_subprocess.py"]+para,stdout=subprocess.PIPE)
sub_result.wait()
out, err = sub_result.communicate()
return_code = sub_result.returncode
if return_code == 0:
for line in out.splitlines():
if not line.startswith('>>>'):
continue
print line
else:
sys.exit(0)
except:
return 1
Main_script.py:
if out is not None:
for line in out.splitlines():
if not line.startswith('>>>'):
continue
value = line.split(':',1)[1].lstrip()
print 'return value:',value`
When I execute the above, I get UnboundLocalError: local variable 'value' referenced before assignment
in command print value
. It seems that if I do not execute the code in level 1 script and execute sys.exit () then out
in the main script it is neither empty nor single, except it is undefined and therefore value
does not get initialized and throws an error
source to share
If you just want to return an integer value, you can use the exit value. This is not the same return from some_func()
you will need to do sys.exit(integer_val)
.
If you want to return a string of type time
, you have to print it (or write to sys.stdout
) and then in the calling process (level 1) parse the output from the subprocess and print it to your own stdout for level 0 to see it.
In your case, the second level should do something like:
def test():
# some other code and multiple print statements
val = 'time'
print 'returning value:', val
if __name__ == '__main__':
test()
And at level 1, you would do:
def some_func():
# some other code and multiple print statements
para = ['a','b']
result = subprocess.Popen([sys.executable,"second_subprocess.py"]+para,stdout=subprocess.PIPE)
result.wait()
out, err = result.communicate()
return_code = sub_result.returncode
if return_code == 0:
print out
if __name__ == '__main__':
some_func()
With this main_script.py, there is a lot to read from calling your level 1 script.
I usually use subprocess.check_output()
for this kind of information transfer. This will throw an exception if the called process has a non-zero exit status (i.e. On error). I can also recommend that if a subprocess writes more information than just a variable, you can easily handle the output lines by returning something unique at the beginning of the line (so you can still use print statements to debug individual scripts and get the correct value from the output ):
Level 2:
def test():
# some other code and multiple print statements
print 'debug: Still going strong'
val = 'time'
print '>>>> returning value:', val
if __name__ == '__main__':
test()
Level 1:
...
out, err = result.communicate()
for line in out.splitlines():
if not line.startswith('>>>>'):
continue
print line
...
Level 0:
...
out, err = result.communicate()
for line in out.splitlines():
if not line.startswith('>>>>'):
continue
try:
value = line.split(':', 1)[1]
except IndexError:
print 'wrong input line', repr(line)
print 'return value: ', value
...
The following files work together. Save them under the specified names
lvl2.py
# lvl2
import sys
def test():
# some other code and multiple print statements
print >> sys.stderr, 'argv', sys.argv[1:]
print 'debug: Still going strong'
val = 'time'
print '>>>> returning value:', val
return 0
if __name__ == '__main__':
sys.exit(test())
lvl1.py
# lvl1.py
import sys
import subprocess
def some_func():
para = ['a','b']
sub_result = subprocess.Popen(
[sys.executable, "lvl2.py" ] + para,
stdout=subprocess.PIPE)
sub_result.wait()
out, err = sub_result.communicate()
return_code = sub_result.returncode
if return_code == 0:
for line in out.splitlines():
if not line.startswith('>>>'):
continue
print line
else:
print >> sys.stderr, 'level 2 exited with' + return_code
sys.exit(0)
if __name__ == '__main__':
sys.exit(some_func())
lvl0.py
# lvl0
import subprocess
import sys
def func():
para = ['a','b']
result = subprocess.Popen(
[sys.executable, "lvl1.py"] + para,
stdout=subprocess.PIPE)
result.wait()
return_code = result.returncode
out, err = result.communicate()
value = None
if return_code == 0:
for line in out.splitlines():
if not line.startswith('>>>'):
continue
value = line.split(':',1)[1].lstrip()
print
else:
print 'non-zero exit', return_code
print 'return value:', value
if __name__ == '__main__':
func()
Then run python lvl0.py
to check the output
argv ['a', 'b']
return value: time
Now bring them under your version control system and start modifying a few lines at a time, each time you start python lvl0.py
, to check what you might have broken. Commit each revision so you can revert to the last "known good" state and slowly re-enter the rest of your code.
source to share