# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import subprocess
import threading
import time
from queue
import Empty, Queue
def run_and_wait(
args=
None,
cwd=
None,
env=
None,
text=
True,
timeout=
None,
timeout_handler=
None,
output_timeout=
None,
output_timeout_handler=
None,
output_line_handler=
None,
):
"""
Run a process
and wait
for it to complete,
with optional support
for
line-by-line output handling
and timeouts.
On timeout
or output timeout, the callback should kill the process;
many clients use mozcrash.kill_and_get_minidump()
in the timeout
callback.
run_and_wait
is not intended to be a generic replacement
for subprocess.
Clients requiring different options
or behavior should use subprocess
directly.
:param args: command to run. May be a string
or a list.
:param cwd: working directory
for command.
:param env: environment to use
for the process (defaults to os.environ).
:param text: open streams
in text mode
if True;
else use binary mode.
:param timeout: seconds to wait
for process to complete before calling timeout_handler
:param timeout_handler: function to be called
if timeout reached
:param output_timeout: seconds to wait
for process to generate output
:param output_timeout_handler: function to be called
if output_timeout
is reached
:param output_line_handler: function to be called
for every line of process output
"""
is_win = os.name ==
"nt"
if env
is None:
env = os.environ.copy()
if is_win:
kwargs = {
"creationflags": subprocess.CREATE_NEW_PROCESS_GROUP,
}
else:
kwargs = {
"preexec_fn": os.setsid,
}
start_time = time.time()
if timeout
is not None:
timeout += start_time
queue = Queue()
def get_line():
queue_timeout =
None
if timeout:
queue_timeout = timeout - time.time()
if output_timeout:
if queue_timeout:
queue_timeout = min(queue_timeout, output_timeout)
else:
queue_timeout = output_timeout
return queue.get(timeout=queue_timeout)
proc = subprocess.Popen(
args,
cwd=cwd,
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=text,
**kwargs
)
def reader(fh, queue):
for line
in iter(fh.readline,
"" if text
else b
""):
queue.put(line)
# Give a chance to the reading loop to exit without a timeout.
queue.put(b
"")
threading.Thread(
name=
"reader",
target=reader,
args=(
proc.stdout,
queue,
),
daemon=
True,
).start()
try:
for line
in iter(get_line, b
""):
if output_line_handler:
output_line_handler(proc, line)
else:
print(line)
proc.wait()
except Empty:
if timeout
and time.time() < timeout
or not timeout:
if output_timeout_handler:
output_timeout_handler(proc)
elif timeout_handler:
timeout_handler(proc)
return proc