michael@0: :mod:`mozprocess` --- Launch and manage processes michael@0: ================================================= michael@0: michael@0: Mozprocess is a process-handling module that provides some additional michael@0: features beyond those available with python's subprocess: michael@0: michael@0: * better handling of child processes, especially on Windows michael@0: * the ability to timeout the process after some absolute period, or some michael@0: period without any data written to stdout/stderr michael@0: * the ability to specify output handlers that will be called michael@0: for each line of output produced by the process michael@0: * the ability to specify handlers that will be called on process timeout michael@0: and normal process termination michael@0: michael@0: Running a process michael@0: ----------------- michael@0: michael@0: mozprocess consists of two classes: ProcessHandler inherits from ProcessHandlerMixin. michael@0: michael@0: Let's see how to run a process. michael@0: First, the class should be instanciated with at least one argument which is a command (or a list formed by the command followed by its arguments). michael@0: Then the process can be launched using the *run()* method. michael@0: Finally the *wait()* method will wait until end of execution. michael@0: michael@0: .. code-block:: python michael@0: michael@0: from mozprocess import processhandler michael@0: michael@0: # under Windows replace by command = ['dir', '/a'] michael@0: command = ['ls', '-l'] michael@0: p = processhandler.ProcessHandler(command) michael@0: print("execute command: %s" % p.commandline) michael@0: p.run() michael@0: p.wait() michael@0: michael@0: Note that using *ProcessHandler* instead of *ProcessHandlerMixin* will print the output of executed command. The attribute *commandline* provides the launched command. michael@0: michael@0: Collecting process output michael@0: ------------------------- michael@0: michael@0: Let's now consider a basic shell script that will print numbers from 1 to 5 waiting 1 second between each. michael@0: This script will be used as a command to launch in further examples. michael@0: michael@0: **proc_sleep_echo.sh**: michael@0: michael@0: .. code-block:: sh michael@0: michael@0: #!/bin/sh michael@0: michael@0: for i in 1 2 3 4 5 michael@0: do michael@0: echo $i michael@0: sleep 1 michael@0: done michael@0: michael@0: If you are running under Windows, you won't be able to use the previous script (unless using Cygwin). michael@0: So you'll use the following script: michael@0: michael@0: **proc_sleep_echo.bat**: michael@0: michael@0: .. code-block:: bat michael@0: michael@0: @echo off michael@0: FOR %%A IN (1 2 3 4 5) DO ( michael@0: ECHO %%A michael@0: REM if you have TIMEOUT then use it instead of PING michael@0: REM TIMEOUT /T 1 /NOBREAK michael@0: PING -n 2 127.0.0.1 > NUL michael@0: ) michael@0: michael@0: Mozprocess allows the specification of custom output handlers to gather process output while running. michael@0: ProcessHandler will by default write all outputs on stdout. You can also provide (to ProcessHandler or ProcessHandlerMixin) a function or a list of functions that will be used as callbacks on each output line generated by the process. michael@0: michael@0: In the following example the command's output will be stored in a file *output.log* and printed in stdout: michael@0: michael@0: .. code-block:: python michael@0: michael@0: import sys michael@0: from mozprocess import processhandler michael@0: michael@0: fd = open('output.log', 'w') michael@0: michael@0: def tostdout(line): michael@0: sys.stdout.write("<%s>\n" % line) michael@0: michael@0: def tofile(line): michael@0: fd.write("<%s>\n" % line) michael@0: michael@0: # under Windows you'll replace by 'proc_sleep_echo.bat' michael@0: command = './proc_sleep_echo.sh' michael@0: outputs = [tostdout, tofile] michael@0: michael@0: p = processhandler.ProcessHandlerMixin(command, processOutputLine=outputs) michael@0: p.run() michael@0: p.wait() michael@0: michael@0: fd.close() michael@0: michael@0: The process output can be saved (*obj = ProcessHandler(..., storeOutput=True)*) so as it is possible to request it (*obj.output*) at any time. Note that the default value for *stroreOutput* is *True*, so it is not necessary to provide it in the parameters. michael@0: michael@0: .. code-block:: python michael@0: michael@0: import time michael@0: import sys michael@0: from mozprocess import processhandler michael@0: michael@0: command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat' michael@0: michael@0: p = processhandler.ProcessHandler(command, storeOutput=True) michael@0: p.run() michael@0: for i in xrange(10): michael@0: print(p.output) michael@0: time.sleep(0.5) michael@0: p.wait() michael@0: michael@0: In previous example, you will see the *p.output* list growing. michael@0: michael@0: Execution michael@0: --------- michael@0: michael@0: Status michael@0: `````` michael@0: michael@0: It is possible to query the status of the process via *poll()* that will return None if the process is still running, 0 if it ended without failures and a negative value if it was killed by a signal (Unix-only). michael@0: michael@0: .. code-block:: python michael@0: michael@0: import time michael@0: import signal michael@0: from mozprocess import processhandler michael@0: michael@0: command = './proc_sleep_echo.sh' michael@0: p = processhandler.ProcessHandler(command) michael@0: p.run() michael@0: time.sleep(2) michael@0: print("poll status: %s" % p.poll()) michael@0: time.sleep(1) michael@0: p.kill(signal.SIGKILL) michael@0: print("poll status: %s" % p.poll()) michael@0: michael@0: Timeout michael@0: ``````` michael@0: michael@0: A timeout can be provided to the *run()* method. If the process last more than timeout seconds, it will be stopped. michael@0: michael@0: After execution, the property *timedOut* will be set to True if a timeout was reached. michael@0: michael@0: It is also possible to provide functions (*obj = ProcessHandler[Mixin](..., onTimeout=functions)*) that will be called if the timeout was reached. michael@0: michael@0: .. code-block:: python michael@0: michael@0: from mozprocess import processhandler michael@0: michael@0: def ontimeout(): michael@0: print("REACHED TIMEOUT") michael@0: michael@0: command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat' michael@0: functions = [ontimeout] michael@0: p = processhandler.ProcessHandler(command, onTimeout=functions) michael@0: p.run(timeout=2) michael@0: p.wait() michael@0: print("timedOut = %s" % p.timedOut) michael@0: michael@0: By default the process will be killed on timeout but it is possible to prevent this by setting *kill_on_timeout* to *False*. michael@0: michael@0: .. code-block:: python michael@0: michael@0: p = processhandler.ProcessHandler(command, onTimeout=functions, kill_on_timeout=False) michael@0: p.run(timeout=2) michael@0: p.wait() michael@0: print("timedOut = %s" % p.timedOut) michael@0: michael@0: In this case, no output will be available after the timeout, but the process will still be running. michael@0: michael@0: Waiting michael@0: ``````` michael@0: michael@0: It is possible to wait until the process exits as already seen with the method *wait()*, or until the end of a timeout if given. Note that in last case the process is still alive after the timeout. michael@0: michael@0: .. code-block:: python michael@0: michael@0: command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat' michael@0: p = processhandler.ProcessHandler(command) michael@0: p.run() michael@0: p.wait(timeout=2) michael@0: print("timedOut = %s" % p.timedOut) michael@0: p.wait() michael@0: michael@0: Killing michael@0: ``````` michael@0: michael@0: You can request to kill the process with the method *kill*. f the parameter "ignore_children" is set to False when the process handler class is initialized, all the process's children will be killed as well. michael@0: michael@0: Except on Windows, you can specify the signal with which to kill method the process (e.g.: *kill(signal.SIGKILL)*). michael@0: michael@0: .. code-block:: python michael@0: michael@0: import time michael@0: from mozprocess import processhandler michael@0: michael@0: command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat' michael@0: p = processhandler.ProcessHandler(command) michael@0: p.run() michael@0: time.sleep(2) michael@0: p.kill() michael@0: michael@0: End of execution michael@0: ```````````````` michael@0: michael@0: You can provide a function or a list of functions to call at the end of the process using the initilization parameter *onFinish*. michael@0: michael@0: .. code-block:: python michael@0: michael@0: from mozprocess import processhandler michael@0: michael@0: def finish(): michael@0: print("Finished!!") michael@0: michael@0: command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat' michael@0: michael@0: p = processhandler.ProcessHandler(command, onFinish=finish) michael@0: p.run() michael@0: p.wait() michael@0: michael@0: Child management michael@0: ---------------- michael@0: michael@0: Consider the following scripts: michael@0: michael@0: **proc_child.sh**: michael@0: michael@0: .. code-block:: sh michael@0: michael@0: #!/bin/sh michael@0: for i in a b c d e michael@0: do michael@0: echo $i michael@0: sleep 1 michael@0: done michael@0: michael@0: **proc_parent.sh**: michael@0: michael@0: .. code-block:: sh michael@0: michael@0: #!/bin/sh michael@0: ./proc_child.sh michael@0: for i in 1 2 3 4 5 michael@0: do michael@0: echo $i michael@0: sleep 1 michael@0: done michael@0: michael@0: For windows users consider: michael@0: michael@0: **proc_child.bat**: michael@0: michael@0: .. code-block:: bat michael@0: michael@0: @echo off michael@0: FOR %%A IN (a b c d e) DO ( michael@0: ECHO %%A michael@0: REM TIMEOUT /T 1 /NOBREAK michael@0: PING -n 2 127.0.0.1 > NUL michael@0: ) michael@0: michael@0: **proc_parent.bat**: michael@0: michael@0: .. code-block:: bat michael@0: michael@0: @echo off michael@0: call proc_child.bat michael@0: FOR %%A IN (1 2 3 4 5) DO ( michael@0: ECHO %%A michael@0: REM TIMEOUT /T 1 /NOBREAK michael@0: PING -n 2 127.0.0.1 > NUL michael@0: ) michael@0: michael@0: For processes that launch other processes, mozprocess allows you to get child running status, wait for child termination, and kill children. michael@0: michael@0: Ignoring children michael@0: ````````````````` michael@0: michael@0: By default the *ignore_children* option is False. In that case, killing the main process will kill all its children at the same time. michael@0: michael@0: .. code-block:: python michael@0: michael@0: import time michael@0: from mozprocess import processhandler michael@0: michael@0: def finish(): michael@0: print("Finished") michael@0: michael@0: command = './proc_parent.sh' michael@0: p = processhandler.ProcessHandler(command, ignore_children=False, onFinish=finish) michael@0: p.run() michael@0: time.sleep(2) michael@0: print("kill") michael@0: p.kill() michael@0: michael@0: If *ignore_children* is set to *True*, killing will apply only to the main process that will wait children end of execution before stoping (join). michael@0: michael@0: .. code-block:: python michael@0: michael@0: import time michael@0: from mozprocess import processhandler michael@0: michael@0: def finish(): michael@0: print("Finished") michael@0: michael@0: command = './proc_parent.sh' michael@0: p = processhandler.ProcessHandler(command, ignore_children=True, onFinish=finish) michael@0: p.run() michael@0: time.sleep(2) michael@0: print("kill") michael@0: p.kill() michael@0: michael@0: API Documentation michael@0: ----------------- michael@0: michael@0: .. module:: mozprocess michael@0: .. autoclass:: ProcessHandlerMixin michael@0: :members: __init__, timedOut, commandline, run, kill, processOutputLine, onTimeout, onFinish, wait michael@0: .. autoclass:: ProcessHandler michael@0: :members: