File: excluding.rst

package info (click to toggle)
python-coverage 7.8.2%2Bdfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,188 kB
  • sloc: python: 31,123; ansic: 1,184; javascript: 773; makefile: 304; sh: 107; xml: 48
file content (348 lines) | stat: -rw-r--r-- 11,304 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt

.. This file is processed with cog to create the tabbed multi-syntax
   configuration examples.  If those are wrong, the quality checks will fail.
   Running "make prebuild" checks them and produces the output.

.. [[[cog
    from cog_helpers import show_configs
.. ]]]
.. [[[end]]] (checksum: d41d8cd98f00b204e9800998ecf8427e)


.. _excluding:

===============================
Excluding code from coverage.py
===============================

.. highlight:: python

You may have code in your project that you know won't be executed, and you want
to tell coverage.py to ignore it.  For example, you may have debugging-only
code that won't be executed during your unit tests. You can tell coverage.py to
exclude this code during reporting so that it doesn't clutter your reports with
noise about code that you don't need to hear about.

Coverage.py will look for comments marking clauses for exclusion.  In this
code, the "if debug" clause is excluded from reporting::

    a = my_function1()
    if debug:  # pragma: no cover
        msg = "blah blah"
        log_message(msg, a)
    b = my_function2()

By default, any line with a comment of ``pragma: no cover`` is excluded.  If
that line introduces a clause, for example, an ``if`` clause, or a function or
class definition, then the entire clause is also excluded.  Here the
``__repr__`` function is not reported as missing::

    class MyObject(object):
        def __init__(self):
            blah1()
            blah2()

        def __repr__(self):  # pragma: no cover
            return "<MyObject>"

Excluded code is executed as usual, and its execution is recorded in the
coverage data as usual. When producing reports though, coverage.py excludes it
from the list of missing code.


Branch coverage
---------------

When measuring :ref:`branch coverage <branch>`, a conditional will not be
counted as a branch if one of its choices is excluded::

    def only_one_choice(x):
        if x:
            blah1()
            blah2()
        else:  # pragma: no cover
            # x is always true.
            blah3()

Because the ``else`` clause is excluded, the ``if`` only has one possible next
line, so it isn't considered a branch at all.


Advanced exclusion
------------------

Coverage.py identifies exclusions by matching source code against a list of
regular expressions. Using :ref:`configuration files <config>` or the coverage
:ref:`API <api>`, you can add to that list. This is useful if you have
often-used constructs to exclude that can be matched with a regex. You can
exclude them all at once without littering your code with exclusion pragmas.

Before coverage.py 7.6.0, the regexes were matched against single lines of your
source code.  Now they can be multi-line regexes that find matches across
lines. See :ref:`multi_line_exclude`.

If a matched line introduces a block, the entire block is excluded from
reporting.  Matching a ``def`` line or decorator line will exclude an entire
function.

.. highlight:: ini

For example, you might decide that __repr__ functions are usually only used in
debugging code, and are uninteresting to test themselves.  You could exclude
all of them by adding a regex to the exclusion list:

.. [[[cog
    show_configs(
        ini=r"""
            [report]
            exclude_also =
                def __repr__
            """,
        toml=r"""
            [tool.coverage.report]
            exclude_also = [
                "def __repr__",
            ]
            """,
        )
.. ]]]

.. tabs::

    .. code-tab:: ini
        :caption: .coveragerc

        [report]
        exclude_also =
            def __repr__

    .. code-tab:: toml
        :caption: pyproject.toml

        [tool.coverage.report]
        exclude_also = [
            "def __repr__",
        ]

    .. code-tab:: ini
        :caption: setup.cfg or tox.ini

        [coverage:report]
        exclude_also =
            def __repr__

.. [[[end]]] (checksum: f3e70ebf128fbef4087efe75dcfadcb8)

For example, here's a list of exclusions I've used:

.. [[[cog
    show_configs(
        ini=r"""
            [report]
            exclude_also =
                def __repr__
                if self.debug:
                if settings.DEBUG
                raise AssertionError
                raise NotImplementedError
                if 0:
                if __name__ == .__main__.:
                if TYPE_CHECKING:
                class .*\bProtocol\):
                @(abc\.)?abstractmethod
            """,
        toml=r"""
            [tool.coverage.report]
            exclude_also = [
                'def __repr__',
                'if self.debug:',
                'if settings.DEBUG',
                'raise AssertionError',
                'raise NotImplementedError',
                'if 0:',
                'if __name__ == .__main__.:',
                'if TYPE_CHECKING:',
                'class .*\bProtocol\):',
                '@(abc\.)?abstractmethod',
            ]
            """,
        )
.. ]]]

.. tabs::

    .. code-tab:: ini
        :caption: .coveragerc

        [report]
        exclude_also =
            def __repr__
            if self.debug:
            if settings.DEBUG
            raise AssertionError
            raise NotImplementedError
            if 0:
            if __name__ == .__main__.:
            if TYPE_CHECKING:
            class .*\bProtocol\):
            @(abc\.)?abstractmethod

    .. code-tab:: toml
        :caption: pyproject.toml

        [tool.coverage.report]
        exclude_also = [
            'def __repr__',
            'if self.debug:',
            'if settings.DEBUG',
            'raise AssertionError',
            'raise NotImplementedError',
            'if 0:',
            'if __name__ == .__main__.:',
            'if TYPE_CHECKING:',
            'class .*\bProtocol\):',
            '@(abc\.)?abstractmethod',
        ]

    .. code-tab:: ini
        :caption: setup.cfg or tox.ini

        [coverage:report]
        exclude_also =
            def __repr__
            if self.debug:
            if settings.DEBUG
            raise AssertionError
            raise NotImplementedError
            if 0:
            if __name__ == .__main__.:
            if TYPE_CHECKING:
            class .*\bProtocol\):
            @(abc\.)?abstractmethod

.. [[[end]]] (checksum: 650b209edd27112381b5f0a8d2ee0c45)

The :ref:`config_report_exclude_also` option adds regexes to the built-in
default list so that you can add your own exclusions.  The older
:ref:`config_report_exclude_lines` option completely overwrites the list of
regexes.

The regexes only have to match part of a line. Be careful not to over-match.  A
value of ``...`` will match any line with more than three characters in it.

A similar pragma, "no branch", can be used to tailor branch coverage
measurement.  See :ref:`branch` for details.


.. _multi_line_exclude:

Multi-line exclusion regexes
----------------------------

.. versionadded:: 7.6.0

Exclusion regexes can match multi-line regions.  All of the lines in a matched
region will be excluded.  If part of the region introduces a block, the entire
block is excluded even if part of it is outside the matched region.

When writing regexes to match multiple lines, remember that ``"."`` won't match
a newline character, but ``"\n"`` or ``"(?s:.)"`` will.  The regexes in these
settings are combined, so you cannot use global flags like ``(?s)`` in
your regexes.  Use the scoped flag form instead: ``(?s:...)``

Here are some examples:

.. [[[cog
    show_configs(
        ini=r"""
            [report]
            exclude_also =
                ; 1. Exclude an except clause of a specific form:
                except ValueError:\n\s*assume\(False\)
                ; 2. Comments to turn coverage on and off:
                no cover: start(?s:.)*?no cover: stop
                ; 3. A pragma comment that excludes an entire file:
                \A(?s:.*# pragma: exclude file.*)\Z
            """,
        toml=r"""
            [tool.coverage.report]
            exclude_also = [
                # 1. Exclude an except clause of a specific form:
                'except ValueError:\n\s*assume\(False\)',
                # 2. Comments to turn coverage on and off:
                'no cover: start(?s:.)*?no cover: stop',
                # 3. A pragma comment that excludes an entire file:
                '\A(?s:.*# pragma: exclude file.*)\Z',
            ]
            """,
        )
.. ]]]

.. tabs::

    .. code-tab:: ini
        :caption: .coveragerc

        [report]
        exclude_also =
            ; 1. Exclude an except clause of a specific form:
            except ValueError:\n\s*assume\(False\)
            ; 2. Comments to turn coverage on and off:
            no cover: start(?s:.)*?no cover: stop
            ; 3. A pragma comment that excludes an entire file:
            \A(?s:.*# pragma: exclude file.*)\Z

    .. code-tab:: toml
        :caption: pyproject.toml

        [tool.coverage.report]
        exclude_also = [
            # 1. Exclude an except clause of a specific form:
            'except ValueError:\n\s*assume\(False\)',
            # 2. Comments to turn coverage on and off:
            'no cover: start(?s:.)*?no cover: stop',
            # 3. A pragma comment that excludes an entire file:
            '\A(?s:.*# pragma: exclude file.*)\Z',
        ]

    .. code-tab:: ini
        :caption: setup.cfg or tox.ini

        [coverage:report]
        exclude_also =
            ; 1. Exclude an except clause of a specific form:
            except ValueError:\n\s*assume\(False\)
            ; 2. Comments to turn coverage on and off:
            no cover: start(?s:.)*?no cover: stop
            ; 3. A pragma comment that excludes an entire file:
            \A(?s:.*# pragma: exclude file.*)\Z

.. [[[end]]] (checksum: c46e819ad9a1d3a8e37037a89d28cfde)

The first regex matches a specific except line followed by a specific function
call.  Both lines must be present for the exclusion to take effect. Note that
the regex uses ``"\n\s*"`` to match the newline and the indentation of the
second line.  Without these, the regex won't match.

The second regex creates a pair of comments that can be used to exclude
statements between them.   All lines between ``# no cover: start`` and ``# no
cover: stop`` will be excluded.  The regex doesn't start with ``#`` because
that's a comment in a .coveragerc file.  Be careful with wildcards: we've used
the non-greedy ``*?`` to match the fewest possible characters between the
comments.  If you used the greedy ``*`` instead, the star would match as many
as possible, and you could accidentally exclude large swaths of code.

The third regex matches the entire text of a file containing the comment ``#
pragma: exclude file``.  This lets you exclude files from coverage measurement
with an internal comment instead of naming them in a settings file.  This regex
uses the ``"(?s:...)"`` regex flag to let a dot match any character including a
newline.


Excluding source files
----------------------

See :ref:`source` for ways to limit what files coverage.py measures or reports
on.