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 or list A command to be executed b
(cmd, callback, stderr=subprocess.PIPE)
| 41 | |
| 42 | |
| 43 | def process_handler(cmd, callback, stderr=subprocess.PIPE): |
| 44 | """Open a command in a shell subprocess and execute a callback. |
| 45 | |
| 46 | This function provides common scaffolding for creating subprocess.Popen() |
| 47 | calls. It creates a Popen object and then calls the callback with it. |
| 48 | |
| 49 | Parameters |
| 50 | ---------- |
| 51 | cmd : str or list |
| 52 | A command to be executed by the system, using :class:`subprocess.Popen`. |
| 53 | If a string is passed, it will be run in the system shell. If a list is |
| 54 | passed, it will be used directly as arguments. |
| 55 | |
| 56 | callback : callable |
| 57 | A one-argument function that will be called with the Popen object. |
| 58 | |
| 59 | stderr : file descriptor number, optional |
| 60 | By default this is set to ``subprocess.PIPE``, but you can also pass the |
| 61 | value ``subprocess.STDOUT`` to force the subprocess' stderr to go into |
| 62 | the same file descriptor as its stdout. This is useful to read stdout |
| 63 | and stderr combined in the order they are generated. |
| 64 | |
| 65 | Returns |
| 66 | ------- |
| 67 | The return value of the provided callback is returned. |
| 68 | """ |
| 69 | sys.stdout.flush() |
| 70 | sys.stderr.flush() |
| 71 | # On win32, close_fds can't be true when using pipes for stdin/out/err |
| 72 | close_fds = sys.platform != 'win32' |
| 73 | # Determine if cmd should be run with system shell. |
| 74 | shell = isinstance(cmd, str) |
| 75 | # On POSIX systems run shell commands with user-preferred shell. |
| 76 | executable = None |
| 77 | if shell and os.name == 'posix' and 'SHELL' in os.environ: |
| 78 | executable = os.environ['SHELL'] |
| 79 | p = subprocess.Popen(cmd, shell=shell, |
| 80 | executable=executable, |
| 81 | stdin=subprocess.PIPE, |
| 82 | stdout=subprocess.PIPE, |
| 83 | stderr=stderr, |
| 84 | close_fds=close_fds) |
| 85 | |
| 86 | try: |
| 87 | out = callback(p) |
| 88 | except KeyboardInterrupt: |
| 89 | print('^C') |
| 90 | sys.stdout.flush() |
| 91 | sys.stderr.flush() |
| 92 | out = None |
| 93 | finally: |
| 94 | # Make really sure that we don't leave processes behind, in case the |
| 95 | # call above raises an exception |
| 96 | # We start by assuming the subprocess finished (to avoid NameErrors |
| 97 | # later depending on the path taken) |
| 98 | if p.returncode is None: |
| 99 | try: |
| 100 | p.terminate() |
no test coverage detected