Sub Process
Sub Process
Intuitive Python
Productive Development for Projects that Last
This PDF file contains pages extracted from Intuitive Python, published by the
Pragmatic Bookshelf. For more information or to purchase a paperback or PDF
copy, please visit http://www.pragprog.com.
Note: This extract contains some colored text (particularly in code listing). This
is available only in online versions of the books. The printed versions are black
and white. Pagination might vary between the online and printed versions; the
content is otherwise identical.
Copyright © 2021 The Pragmatic Programmers, LLC.
David Muller
All rights reserved. No part of this publication may be reproduced, stored in a retrieval system,
or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording,
or otherwise, without the prior consent of the publisher.
ISBN-13: 978-1-68050-823-9
Encoded using the finest acid-free high-entropy binary digits.
Book version: P1.0—June 2021
Calling Other Programs with subprocess
You can use the subprocess standard library module to invoke other command
line programs available on your system and process their results in Python.
You might find the subprocess useful if, for example, you want to call a program
like git from inside Python to learn about the current state of your version
controlled project. Indeed, some examples in this section will combine subprocess
and git to do just that.
Note, if you don’t have git installed on your machine or are not currently in a
directory with a git repository, some of the following examples may not suc-
cessfully execute on your computer. You can download git if you like,9 but it’s
also OK to just follow along with the examples here so you get a sense of the
abilities of the subprocess module (you don’t need any special git knowledge).
You can use subprocess with git to retrieve the name of the current git branch
you are on:
>>> import subprocess
>>> command = ["git", "branch", "--show-current"]
>>> result = subprocess.run(command, capture_output=True)
>>> result.returncode
0
>>> result.stdout
b'main\n'
>>> result.stdout.decode("utf-8").strip()
'main'
>>>
In the preceding example, you call subprocess.run with two arguments. The first
argument (command) is a list of strings specifying the command line program
you want to run. In general, you can think of Python as telling the operating
system to execute as if there were spaces separating them. In other words
["git", "branch", "--show-current"] is roughly translated to git branch --show-current and
executed by the operating system. (Python automatically handles any neces-
sary escaping and quoting for you. This escaping and quoting can be helpful
if, for example, one of the arguments in your list is a file name with a space
in it.)
The second argument capture_output flag instructs Python to record stdout and
10
stderr and make them available to you in the CompletedProcess object returned
by subprocess.run. In the preceding example, the result variable is bound to the
9. https://git-scm.com
10. https://docs.python.org/3/library/subprocess.html#subprocess.CompletedProcess
CompletedProcess object. result.returncode indicates that the git command you ran
exited with a 0 code. Accessing result.stdout returns bytes with the output of
the underlying git command we ran. Decoding the bytes as utf-811 and calling
12
strip to remove the trailing newline \n character leaves us with a Python string
indicating my current branch name: 'main'.
So far, we’ve only worked with an example where the underlying command
we called worked. What happens if the underlying command failed and
returned a non-0 returncode?
git does not support a subcommand named oops, so when you try to execute
git oops, git complains and returns a non-0 returncode. By including the check=True
argument in your subprocess.run call, Python automatically raises a CalledProcessEr-
ror exception for you indicating the failure. This CalledProcessError exception can
be useful if you want your program to exit or otherwise fail if the underlying
command you call doesn’t work.
11. https://en.wikipedia.org/wiki/UTF-8
12. https://docs.python.org/3/library/stdtypes.html#str.strip
You’ve now seen how to call external programs, handle errors they might
return, and kill them if they are taking too long. Next, you’ll learn how you
can invoke programs that might need to have data sent to them over stdin.
13. https://docs.python.org/3/library/subprocess.html#subprocess.TimeoutExpired
Notably, when you use the input argument to subprocess.run you need all the
data you want to pass as stdin to be loaded into your application’s RAM. What
if the data you wanted to pass through stdin was large—large enough, for
example, that you wouldn’t want to store it in the RAM of your Python appli-
cation?
stdout_str = result.stdout.decode("utf-8").strip()
print(stdout_str)
In the preceding example, the file example.txt is created and has the strings
example, text, and file written into it with each word on its own line. Then,
14
example.txt is opened for reading (mode="r") and its file object bound to f is
passed as the value for subprocess.run’s stdin argument. The command in subpro-
cess.run is ["grep", "l"], which translates roughly to, “find lines in the input that
include l in them.” The result of the subprocess.run call is bound to result, and
the captured stdout value is decoded from bytes into a string, stripped of its
trailing newline, and printed.
If you run python3 subprocess_with_stdin.py, you should see output like this:
❮ example
file
grep has found all the lines in our file which had l in them, and the output
indicates this. Importantly, you were able to pass stdin to grep without loading
the entire contents of example.txt into RAM, which might be problematic if you
are working with large files. In this example, example.txt was a file of trivial size,
but you can imagine working with files much larger than example.txt.
The stdout and stderr arguments to subprocess.run15 also support being passed as
file objects. You can use file objects in place of the pipes16 and redirects17 you
may be more familiar with from traditional shells. As you just learned about
in Creating Temporary Workspaces with tempfile, on page ?, you can even
take advantage of NamedTempfile and TemporaryDirectory to use as short-lived
workspaces for your endeavors with subprocess.
14. https://docs.python.org/3/glossary.html#term-file-object
15. https://docs.python.org/3/library/subprocess.html#subprocess.run
16. https://en.wikipedia.org/wiki/Pipeline_%28Unix%29
17. https://en.wikipedia.org/wiki/Redirection_(computing)