| 
3 | 3 | 
  | 
4 | 4 | .. _subprocess:  | 
5 | 5 | 
 
  | 
6 |  | -=======================  | 
7 |  | -Measuring sub-processes  | 
8 |  | -=======================  | 
 | 6 | +======================  | 
 | 7 | +Measuring subprocesses  | 
 | 8 | +======================  | 
9 | 9 | 
 
  | 
10 |  | -Complex test suites may spawn sub-processes to run tests, either to run them in  | 
11 |  | -parallel, or because sub-process behavior is an important part of the system  | 
12 |  | -under test. Measuring coverage in those sub-processes can be tricky because you  | 
13 |  | -have to modify the code spawning the process to invoke coverage.py.  | 
 | 10 | +If your system under test spawns subprocesses, you'll have to take extra steps  | 
 | 11 | +to measure coverage in those processes.  There are a few ways to ensure they  | 
 | 12 | +get measured.  The approach you use depends on how you create the processes.  | 
14 | 13 | 
 
  | 
15 |  | -There's an easier way to do it: coverage.py includes a function,  | 
16 |  | -:func:`coverage.process_startup` designed to be invoked when Python starts.  It  | 
17 |  | -examines the ``COVERAGE_PROCESS_START`` environment variable, and if it is set,  | 
18 |  | -begins coverage measurement. The environment variable's value will be used as  | 
19 |  | -the name of the :ref:`configuration file <config>` to use.  | 
 | 14 | +No matter how your subprocesses are created, you will need the :ref:`parallel  | 
 | 15 | +option <config_run_parallel>` to collect separate data for each process, and  | 
 | 16 | +the :ref:`coverage combine <cmd_combine>` command to combine them together  | 
 | 17 | +before reporting.  | 
20 | 18 | 
 
  | 
21 |  | -.. note::  | 
 | 19 | +To successfully write a coverage data file, the Python subprocess under  | 
 | 20 | +measurement must shut down cleanly and have a chance for coverage.py to run its  | 
 | 21 | +termination code.  It will do that when the process ends naturally, or when a  | 
 | 22 | +SIGTERM signal is received.  | 
22 | 23 | 
 
  | 
23 |  | -    The subprocess only sees options in the configuration file.  Options set on  | 
24 |  | -    the command line will not be used in the subprocesses.  | 
 | 24 | +If your processes are ending with SIGTERM, you must enable the  | 
 | 25 | +:ref:`config_run_sigterm` setting to configure coverage to catch SIGTERM  | 
 | 26 | +signals and write its data.  | 
 | 27 | + | 
 | 28 | +Other ways of ending a process, like SIGKILL or :func:`os._exit  | 
 | 29 | +<python:os._exit>`, will prevent coverage.py from writing its data file,  | 
 | 30 | +leaving you with incomplete or non-existent coverage data.  | 
25 | 31 | 
 
  | 
26 | 32 | .. note::  | 
27 | 33 | 
 
  | 
28 |  | -    If you have subprocesses created with :mod:`multiprocessing  | 
29 |  | -    <python:multiprocessing>`, the ``--concurrency=multiprocessing``  | 
30 |  | -    command-line option should take care of everything for you.  See  | 
31 |  | -    :ref:`cmd_run` for details.  | 
 | 34 | +    Subprocesses will only see coverage options in the configuration file.  | 
 | 35 | +    Options set on the command line will not be visible to subprocesses.  | 
 | 36 | + | 
 | 37 | + | 
 | 38 | +Using multiprocessing  | 
 | 39 | +---------------------  | 
32 | 40 | 
 
  | 
33 |  | -When using this technique, be sure to set the parallel option to true so that  | 
34 |  | -multiple coverage.py runs will each write their data to a distinct file.  | 
 | 41 | +The :mod:`multiprocessing <python:multiprocessing>` module in the Python  | 
 | 42 | +standard library provides high-level tools for managing subprocesses.  If you  | 
 | 43 | +use it, the :ref:`concurrency=multiprocessing <config_run_concurrency>` and  | 
 | 44 | +:ref:`sigterm <config_run_sigterm>` settings will configure coverage to measure  | 
 | 45 | +the subprocesses.  | 
35 | 46 | 
 
  | 
 | 47 | +Even with multiprocessing, you have to be careful that all subprocesses  | 
 | 48 | +terminate cleanly or they won't record their coverage measurements.  For  | 
 | 49 | +example, the correct way to use a Pool requires closing and joining the pool  | 
 | 50 | +before terminating::  | 
36 | 51 | 
 
  | 
37 |  | -Configuring Python for sub-process measurement  | 
38 |  | -----------------------------------------------  | 
 | 52 | +    with multiprocessing.Pool() as pool:  | 
 | 53 | +        # ... use any of the pool methods ...  | 
 | 54 | +        pool.close()  | 
 | 55 | +        pool.join()  | 
39 | 56 | 
 
  | 
40 |  | -Measuring coverage in sub-processes is a little tricky.  When you spawn a  | 
41 |  | -sub-process, you are invoking Python to run your program.  Usually, to get  | 
42 |  | -coverage measurement, you have to use coverage.py to run your program.  Your  | 
43 |  | -sub-process won't be using coverage.py, so we have to convince Python to use  | 
44 |  | -coverage.py even when not explicitly invoked.  | 
45 | 57 | 
 
  | 
46 |  | -To do that, we'll configure Python to run a little coverage.py code when it  | 
47 |  | -starts.  That code will look for an environment variable that tells it to start  | 
48 |  | -coverage measurement at the start of the process.  | 
 | 58 | +Implicit coverage  | 
 | 59 | +-----------------  | 
 | 60 | + | 
 | 61 | +If you are starting subprocesses another way, you can configure Python to start  | 
 | 62 | +coverage when it runs.  Coverage.py includes a function designed to be invoked  | 
 | 63 | +when Python starts: :func:`coverage.process_startup`.  It examines the  | 
 | 64 | +``COVERAGE_PROCESS_START`` environment variable, and if it is set, begins  | 
 | 65 | +coverage measurement. The environment variable's value will be used as the name  | 
 | 66 | +of the :ref:`configuration file <config>` to use.  | 
49 | 67 | 
 
  | 
50 | 68 | To arrange all this, you have to do two things: set a value for the  | 
51 | 69 | ``COVERAGE_PROCESS_START`` environment variable, and then configure Python to  | 
52 | 70 | invoke :func:`coverage.process_startup` when Python processes start.  | 
53 | 71 | 
 
  | 
54 | 72 | How you set ``COVERAGE_PROCESS_START`` depends on the details of how you create  | 
55 |  | -sub-processes.  As long as the environment variable is visible in your  | 
56 |  | -sub-process, it will work.  | 
 | 73 | +subprocesses.  As long as the environment variable is visible in your  | 
 | 74 | +subprocess, it will work.  | 
57 | 75 | 
 
  | 
58 | 76 | You can configure your Python installation to invoke the ``process_startup``  | 
59 | 77 | function in two ways:  | 
@@ -84,17 +102,11 @@ start-up.  Be sure to remove the change when you uninstall coverage.py, or use  | 
84 | 102 | a more defensive approach to importing it.  | 
85 | 103 | 
 
  | 
86 | 104 | 
 
  | 
87 |  | -Process termination  | 
88 |  | --------------------  | 
89 |  | - | 
90 |  | -To successfully write a coverage data file, the Python sub-process under  | 
91 |  | -analysis must shut down cleanly and have a chance for coverage.py to run its  | 
92 |  | -termination code.  It will do that when the process ends naturally, or when a  | 
93 |  | -SIGTERM signal is received.  | 
94 |  | - | 
95 |  | -Coverage.py uses :mod:`atexit <python:atexit>` to handle usual process ends,  | 
96 |  | -and a :mod:`signal <python:signal>` handler to catch SIGTERM signals.  | 
 | 105 | +Explicit coverage  | 
 | 106 | +-----------------  | 
97 | 107 | 
 
  | 
98 |  | -Other ways of ending a process, like SIGKILL or :func:`os._exit  | 
99 |  | -<python:os._exit>`, will prevent coverage.py from writing its data file,  | 
100 |  | -leaving you with incomplete or non-existent coverage data.  | 
 | 108 | +Another option for running coverage on your subprocesses it to run coverage  | 
 | 109 | +explicitly as the command for your subprocess instead of using "python" as the  | 
 | 110 | +command.  This isn't recommended, since it requires running different code  | 
 | 111 | +when running coverage than when not, which can complicate your test  | 
 | 112 | +environment.  | 
0 commit comments