|
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