justin_hanekom

syscmd.py

Sep 14th, 2025
481
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.41 KB | Source Code | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. # File: popen_out_gen.py
  4. # SPDX-License-Identifier: Unlicense
  5.  
  6. # This is free and unencumbered software released into the public domain.
  7. #
  8. # Anyone is free to copy, modify, publish, use, compile, sell, or
  9. # distribute this software, either in source code form or as a compiled
  10. # binary, for any purpose, commercial or non-commercial, and by any
  11. # means.
  12.  
  13. # Tectonics:
  14. #   $ black syscmd.py
  15.  
  16. import shutil
  17. import subprocess
  18. import sys
  19.  
  20.  
  21. def popen_out_gen(cmd, args, **params):
  22.     """Executes the command cmd with args via subprocess.Popen().
  23.  
  24.    The params are passed to subprocess.Popen. By default, the params
  25.    passed to Popen are:
  26.      * stdout=subprocess.PIPE
  27.      * text=True
  28.  
  29.    Yields each line of the subprocess output to the caller.
  30.    The line ending "\n" is *not* removed.
  31.  
  32.    If shell is True then the args are combined into a single
  33.    platform-specific string and run within a system shell.
  34.  
  35.    Once the subprocess stops writing to output, closes
  36.    the ouptut stream and waits for the subprocess to end.
  37.  
  38.    If the subprocess returned a non-zero return code,
  39.    then raises a subprocess.CalledProcessError exception.
  40.    """
  41.     # Prepend the (fully qualified) cmd to args.
  42.     # This modifies the args parameter
  43.     if shutil.which(cmd):
  44.         args.insert(0, shutil.which(cmd))
  45.     else:
  46.         args.insert(0, cmd)
  47.  
  48.     # Run the given command plus args as a subprocess,
  49.     # yielding each output line to the caller
  50.     kwargs = {
  51.         "stdout": subprocess.PIPE,
  52.         "text": True,
  53.     }
  54.     kwargs.update(**params)
  55.  
  56.     # Combine args if running via a shell
  57.     # CAVEAT: running as a shell process does not work reliably
  58.     if kwargs.get("shell", None):
  59.         args = " ".join(args)
  60.     proc = subprocess.Popen(args, **kwargs)
  61.  
  62.     out_readable = hasattr(proc.stdout, "readline")
  63.     err_readable = hasattr(proc.stderr, "readline")
  64.     while out_readable or err_readable:
  65.         if out_readable:
  66.             line = proc.stdout.readline()
  67.             yield line
  68.             if line == "":
  69.                 out_readable = False
  70.         if err_readable:
  71.             line = proc.stderr.readline()
  72.             yield line
  73.             if line == "":
  74.                 err_readable = False
  75.  
  76.     # Wait for the subprocess to end,
  77.     return_code = proc.wait()
  78.  
  79.     # Finally, raise a CalledProcessError if the return code != 0
  80.     if return_code != 0:
  81.         raise subprocess.CalledProcessError(return_code, args)
  82.  
Tags: pprocess
Advertisement