Skip to content

Commit

Permalink
Fix python#10554. Added context manager support to Popen objects.
Browse files Browse the repository at this point in the history
Added a few common Popen uses to the tests like we've done for a few other
instances of adding context managers. Eventually the entire test suite
could be converted to use the context manager format.
  • Loading branch information
briancurtin committed Dec 3, 2010
1 parent 2d93e6e commit 79cdb66
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 1 deletion.
10 changes: 10 additions & 0 deletions Doc/library/subprocess.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,16 @@ This module defines one class called :class:`Popen`:
underlying CreateProcess() function. They can specify things such as appearance
of the main window and priority for the new process. (Windows only)

Popen objects are supported as context managers via the :keyword:`with` statement,
closing any open file descriptors on exit.
::

with Popen(["ifconfig"], stdout=PIPE) as proc:
log.write(proc.stdout.read())

.. versionchanged:: 3.2
Added context manager support.


.. data:: PIPE

Expand Down
10 changes: 10 additions & 0 deletions Lib/subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,16 @@ def _translate_newlines(self, data, encoding):
data = data.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
return data.decode(encoding)

def __enter__(self):
return self

def __exit__(self, type, value, traceback):
if self.stdout:
self.stdout.close()
if self.stderr:
self.stderr.close()
if self.stdin:
self.stdin.close()

def __del__(self, _maxsize=sys.maxsize, _active=_active):
if not self._child_created:
Expand Down
44 changes: 43 additions & 1 deletion Lib/test/test_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,47 @@ def test_noshell_sequence_with_spaces(self):
# call() function with sequence argument with spaces on Windows
self.with_spaces([sys.executable, self.fname, "ab cd"])


class ContextManagerTests(ProcessTestCase):

def test_pipe(self):
with subprocess.Popen([sys.executable, "-c",
"import sys;"
"sys.stdout.write('stdout');"
"sys.stderr.write('stderr');"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE) as proc:
self.assertEqual(proc.stdout.read(), b"stdout")
self.assertStderrEqual(proc.stderr.read(), b"stderr")

self.assertTrue(proc.stdout.closed)
self.assertTrue(proc.stderr.closed)

def test_returncode(self):
with subprocess.Popen([sys.executable, "-c",
"import sys; sys.exit(100)"]) as proc:
proc.wait()
self.assertEqual(proc.returncode, 100)

def test_communicate_stdin(self):
with subprocess.Popen([sys.executable, "-c",
"import sys;"
"sys.exit(sys.stdin.read() == 'context')"],
stdin=subprocess.PIPE) as proc:
proc.communicate(b"context")
self.assertEqual(proc.returncode, 1)

def test_invalid_args(self):
with self.assertRaises(EnvironmentError) as c:
with subprocess.Popen(['nonexisting_i_hope'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE) as proc:
pass

if c.exception.errno != errno.ENOENT: # ignore "no such file"
raise c.exception


def test_main():
unit_tests = (ProcessTestCase,
POSIXProcessTestCase,
Expand All @@ -1191,7 +1232,8 @@ def test_main():
CommandTests,
ProcessTestCaseNoPoll,
HelperFunctionTests,
CommandsWithSpaces)
CommandsWithSpaces,
ContextManagerTests)

support.run_unittest(*unit_tests)
support.reap_children()
Expand Down
2 changes: 2 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ Core and Builtins
Library
-------

- Issue #10554: Add context manager support to subprocess.Popen objects.

- Issue #8989: email.utils.make_msgid now has a domain parameter that can
override the domain name used in the generated msgid.

Expand Down

0 comments on commit 79cdb66

Please sign in to comment.