_process_common.py
119 lines
| 3.7 KiB
| text/x-python
|
PythonLexer
Fernando Perez
|
r2908 | """Common utilities for the various process_* implementations. | ||
This file is only meant to be imported by the platform-specific implementations | ||||
of subprocess utilities, and it contains tools that are common to all of them. | ||||
""" | ||||
#----------------------------------------------------------------------------- | ||||
# Copyright (C) 2010 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
import subprocess | ||||
import sys | ||||
#----------------------------------------------------------------------------- | ||||
# Function definitions | ||||
#----------------------------------------------------------------------------- | ||||
def read_no_interrupt(p): | ||||
"""Read from a pipe ignoring EINTR errors. | ||||
This is necessary because when reading from pipes with GUI event loops | ||||
running in the background, often interrupts are raised that stop the | ||||
command from completing.""" | ||||
import errno | ||||
try: | ||||
return p.read() | ||||
except IOError, err: | ||||
if err.errno != errno.EINTR: | ||||
raise | ||||
def process_handler(cmd, callback, stderr=subprocess.PIPE): | ||||
"""Open a command in a shell subprocess and execute a callback. | ||||
This function provides common scaffolding for creating subprocess.Popen() | ||||
calls. It creates a Popen object and then calls the callback with it. | ||||
Parameters | ||||
---------- | ||||
cmd : str | ||||
A string to be executed with the underlying system shell (by calling | ||||
:func:`Popen` with ``shell=True``. | ||||
callback : callable | ||||
A one-argument function that will be called with the Popen object. | ||||
stderr : file descriptor number, optional | ||||
By default this is set to ``subprocess.PIPE``, but you can also pass the | ||||
value ``subprocess.STDOUT`` to force the subprocess' stderr to go into | ||||
the same file descriptor as its stdout. This is useful to read stdout | ||||
and stderr combined in the order they are generated. | ||||
Returns | ||||
------- | ||||
The return value of the provided callback is returned. | ||||
""" | ||||
sys.stdout.flush() | ||||
sys.stderr.flush() | ||||
p = subprocess.Popen(cmd, shell=True, | ||||
stdin=subprocess.PIPE, | ||||
stdout=subprocess.PIPE, | ||||
stderr=stderr, | ||||
close_fds=True) | ||||
try: | ||||
out = callback(p) | ||||
except KeyboardInterrupt: | ||||
print('^C') | ||||
sys.stdout.flush() | ||||
sys.stderr.flush() | ||||
out = None | ||||
finally: | ||||
# Make really sure that we don't leave processes behind, in case the | ||||
# call above raises an exception | ||||
# We start by assuming the subprocess finished (to avoid NameErrors | ||||
# later depending on the path taken) | ||||
if p.returncode is None: | ||||
try: | ||||
p.terminate() | ||||
p.poll() | ||||
except OSError: | ||||
pass | ||||
# One last try on our way out | ||||
if p.returncode is None: | ||||
try: | ||||
p.kill() | ||||
except OSError: | ||||
pass | ||||
return out | ||||
def getoutputerror(cmd): | ||||
"""Return (standard output, standard error) of executing cmd in a shell. | ||||
Accepts the same arguments as os.system(). | ||||
Parameters | ||||
---------- | ||||
cmd : str | ||||
A command to be executed in the system shell. | ||||
Returns | ||||
------- | ||||
stdout : str | ||||
stderr : str | ||||
""" | ||||
out_err = process_handler(cmd, lambda p: p.communicate()) | ||||
if out_err is None: | ||||
out_err = '', '' | ||||
return out_err | ||||