1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/mozbase/docs/mozprocess.rst Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,324 @@ 1.4 +:mod:`mozprocess` --- Launch and manage processes 1.5 +================================================= 1.6 + 1.7 +Mozprocess is a process-handling module that provides some additional 1.8 +features beyond those available with python's subprocess: 1.9 + 1.10 +* better handling of child processes, especially on Windows 1.11 +* the ability to timeout the process after some absolute period, or some 1.12 + period without any data written to stdout/stderr 1.13 +* the ability to specify output handlers that will be called 1.14 + for each line of output produced by the process 1.15 +* the ability to specify handlers that will be called on process timeout 1.16 + and normal process termination 1.17 + 1.18 +Running a process 1.19 +----------------- 1.20 + 1.21 +mozprocess consists of two classes: ProcessHandler inherits from ProcessHandlerMixin. 1.22 + 1.23 +Let's see how to run a process. 1.24 +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). 1.25 +Then the process can be launched using the *run()* method. 1.26 +Finally the *wait()* method will wait until end of execution. 1.27 + 1.28 +.. code-block:: python 1.29 + 1.30 + from mozprocess import processhandler 1.31 + 1.32 + # under Windows replace by command = ['dir', '/a'] 1.33 + command = ['ls', '-l'] 1.34 + p = processhandler.ProcessHandler(command) 1.35 + print("execute command: %s" % p.commandline) 1.36 + p.run() 1.37 + p.wait() 1.38 + 1.39 +Note that using *ProcessHandler* instead of *ProcessHandlerMixin* will print the output of executed command. The attribute *commandline* provides the launched command. 1.40 + 1.41 +Collecting process output 1.42 +------------------------- 1.43 + 1.44 +Let's now consider a basic shell script that will print numbers from 1 to 5 waiting 1 second between each. 1.45 +This script will be used as a command to launch in further examples. 1.46 + 1.47 +**proc_sleep_echo.sh**: 1.48 + 1.49 +.. code-block:: sh 1.50 + 1.51 + #!/bin/sh 1.52 + 1.53 + for i in 1 2 3 4 5 1.54 + do 1.55 + echo $i 1.56 + sleep 1 1.57 + done 1.58 + 1.59 +If you are running under Windows, you won't be able to use the previous script (unless using Cygwin). 1.60 +So you'll use the following script: 1.61 + 1.62 +**proc_sleep_echo.bat**: 1.63 + 1.64 +.. code-block:: bat 1.65 + 1.66 + @echo off 1.67 + FOR %%A IN (1 2 3 4 5) DO ( 1.68 + ECHO %%A 1.69 + REM if you have TIMEOUT then use it instead of PING 1.70 + REM TIMEOUT /T 1 /NOBREAK 1.71 + PING -n 2 127.0.0.1 > NUL 1.72 + ) 1.73 + 1.74 +Mozprocess allows the specification of custom output handlers to gather process output while running. 1.75 +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. 1.76 + 1.77 +In the following example the command's output will be stored in a file *output.log* and printed in stdout: 1.78 + 1.79 +.. code-block:: python 1.80 + 1.81 + import sys 1.82 + from mozprocess import processhandler 1.83 + 1.84 + fd = open('output.log', 'w') 1.85 + 1.86 + def tostdout(line): 1.87 + sys.stdout.write("<%s>\n" % line) 1.88 + 1.89 + def tofile(line): 1.90 + fd.write("<%s>\n" % line) 1.91 + 1.92 + # under Windows you'll replace by 'proc_sleep_echo.bat' 1.93 + command = './proc_sleep_echo.sh' 1.94 + outputs = [tostdout, tofile] 1.95 + 1.96 + p = processhandler.ProcessHandlerMixin(command, processOutputLine=outputs) 1.97 + p.run() 1.98 + p.wait() 1.99 + 1.100 + fd.close() 1.101 + 1.102 +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. 1.103 + 1.104 +.. code-block:: python 1.105 + 1.106 + import time 1.107 + import sys 1.108 + from mozprocess import processhandler 1.109 + 1.110 + command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat' 1.111 + 1.112 + p = processhandler.ProcessHandler(command, storeOutput=True) 1.113 + p.run() 1.114 + for i in xrange(10): 1.115 + print(p.output) 1.116 + time.sleep(0.5) 1.117 + p.wait() 1.118 + 1.119 +In previous example, you will see the *p.output* list growing. 1.120 + 1.121 +Execution 1.122 +--------- 1.123 + 1.124 +Status 1.125 +`````` 1.126 + 1.127 +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). 1.128 + 1.129 +.. code-block:: python 1.130 + 1.131 + import time 1.132 + import signal 1.133 + from mozprocess import processhandler 1.134 + 1.135 + command = './proc_sleep_echo.sh' 1.136 + p = processhandler.ProcessHandler(command) 1.137 + p.run() 1.138 + time.sleep(2) 1.139 + print("poll status: %s" % p.poll()) 1.140 + time.sleep(1) 1.141 + p.kill(signal.SIGKILL) 1.142 + print("poll status: %s" % p.poll()) 1.143 + 1.144 +Timeout 1.145 +``````` 1.146 + 1.147 +A timeout can be provided to the *run()* method. If the process last more than timeout seconds, it will be stopped. 1.148 + 1.149 +After execution, the property *timedOut* will be set to True if a timeout was reached. 1.150 + 1.151 +It is also possible to provide functions (*obj = ProcessHandler[Mixin](..., onTimeout=functions)*) that will be called if the timeout was reached. 1.152 + 1.153 +.. code-block:: python 1.154 + 1.155 + from mozprocess import processhandler 1.156 + 1.157 + def ontimeout(): 1.158 + print("REACHED TIMEOUT") 1.159 + 1.160 + command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat' 1.161 + functions = [ontimeout] 1.162 + p = processhandler.ProcessHandler(command, onTimeout=functions) 1.163 + p.run(timeout=2) 1.164 + p.wait() 1.165 + print("timedOut = %s" % p.timedOut) 1.166 + 1.167 +By default the process will be killed on timeout but it is possible to prevent this by setting *kill_on_timeout* to *False*. 1.168 + 1.169 +.. code-block:: python 1.170 + 1.171 + p = processhandler.ProcessHandler(command, onTimeout=functions, kill_on_timeout=False) 1.172 + p.run(timeout=2) 1.173 + p.wait() 1.174 + print("timedOut = %s" % p.timedOut) 1.175 + 1.176 +In this case, no output will be available after the timeout, but the process will still be running. 1.177 + 1.178 +Waiting 1.179 +``````` 1.180 + 1.181 +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. 1.182 + 1.183 +.. code-block:: python 1.184 + 1.185 + command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat' 1.186 + p = processhandler.ProcessHandler(command) 1.187 + p.run() 1.188 + p.wait(timeout=2) 1.189 + print("timedOut = %s" % p.timedOut) 1.190 + p.wait() 1.191 + 1.192 +Killing 1.193 +``````` 1.194 + 1.195 +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. 1.196 + 1.197 +Except on Windows, you can specify the signal with which to kill method the process (e.g.: *kill(signal.SIGKILL)*). 1.198 + 1.199 +.. code-block:: python 1.200 + 1.201 + import time 1.202 + from mozprocess import processhandler 1.203 + 1.204 + command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat' 1.205 + p = processhandler.ProcessHandler(command) 1.206 + p.run() 1.207 + time.sleep(2) 1.208 + p.kill() 1.209 + 1.210 +End of execution 1.211 +```````````````` 1.212 + 1.213 +You can provide a function or a list of functions to call at the end of the process using the initilization parameter *onFinish*. 1.214 + 1.215 +.. code-block:: python 1.216 + 1.217 + from mozprocess import processhandler 1.218 + 1.219 + def finish(): 1.220 + print("Finished!!") 1.221 + 1.222 + command = './proc_sleep_echo.sh' # Windows: 'proc_sleep_echo.bat' 1.223 + 1.224 + p = processhandler.ProcessHandler(command, onFinish=finish) 1.225 + p.run() 1.226 + p.wait() 1.227 + 1.228 +Child management 1.229 +---------------- 1.230 + 1.231 +Consider the following scripts: 1.232 + 1.233 +**proc_child.sh**: 1.234 + 1.235 +.. code-block:: sh 1.236 + 1.237 + #!/bin/sh 1.238 + for i in a b c d e 1.239 + do 1.240 + echo $i 1.241 + sleep 1 1.242 + done 1.243 + 1.244 +**proc_parent.sh**: 1.245 + 1.246 +.. code-block:: sh 1.247 + 1.248 + #!/bin/sh 1.249 + ./proc_child.sh 1.250 + for i in 1 2 3 4 5 1.251 + do 1.252 + echo $i 1.253 + sleep 1 1.254 + done 1.255 + 1.256 +For windows users consider: 1.257 + 1.258 +**proc_child.bat**: 1.259 + 1.260 +.. code-block:: bat 1.261 + 1.262 + @echo off 1.263 + FOR %%A IN (a b c d e) DO ( 1.264 + ECHO %%A 1.265 + REM TIMEOUT /T 1 /NOBREAK 1.266 + PING -n 2 127.0.0.1 > NUL 1.267 + ) 1.268 + 1.269 +**proc_parent.bat**: 1.270 + 1.271 +.. code-block:: bat 1.272 + 1.273 + @echo off 1.274 + call proc_child.bat 1.275 + FOR %%A IN (1 2 3 4 5) DO ( 1.276 + ECHO %%A 1.277 + REM TIMEOUT /T 1 /NOBREAK 1.278 + PING -n 2 127.0.0.1 > NUL 1.279 + ) 1.280 + 1.281 +For processes that launch other processes, mozprocess allows you to get child running status, wait for child termination, and kill children. 1.282 + 1.283 +Ignoring children 1.284 +````````````````` 1.285 + 1.286 +By default the *ignore_children* option is False. In that case, killing the main process will kill all its children at the same time. 1.287 + 1.288 +.. code-block:: python 1.289 + 1.290 + import time 1.291 + from mozprocess import processhandler 1.292 + 1.293 + def finish(): 1.294 + print("Finished") 1.295 + 1.296 + command = './proc_parent.sh' 1.297 + p = processhandler.ProcessHandler(command, ignore_children=False, onFinish=finish) 1.298 + p.run() 1.299 + time.sleep(2) 1.300 + print("kill") 1.301 + p.kill() 1.302 + 1.303 +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). 1.304 + 1.305 +.. code-block:: python 1.306 + 1.307 + import time 1.308 + from mozprocess import processhandler 1.309 + 1.310 + def finish(): 1.311 + print("Finished") 1.312 + 1.313 + command = './proc_parent.sh' 1.314 + p = processhandler.ProcessHandler(command, ignore_children=True, onFinish=finish) 1.315 + p.run() 1.316 + time.sleep(2) 1.317 + print("kill") 1.318 + p.kill() 1.319 + 1.320 +API Documentation 1.321 +----------------- 1.322 + 1.323 +.. module:: mozprocess 1.324 +.. autoclass:: ProcessHandlerMixin 1.325 + :members: __init__, timedOut, commandline, run, kill, processOutputLine, onTimeout, onFinish, wait 1.326 +.. autoclass:: ProcessHandler 1.327 + :members: