mutmut 3.4.0__tar.gz → 3.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mutmut-3.4.0 → mutmut-3.6.0}/HISTORY.rst +57 -0
- {mutmut-3.4.0 → mutmut-3.6.0}/PKG-INFO +213 -15
- mutmut-3.6.0/README.rst +449 -0
- {mutmut-3.4.0 → mutmut-3.6.0}/pyproject.toml +42 -2
- mutmut-3.6.0/src/mutmut/__init__.py +42 -0
- mutmut-3.6.0/src/mutmut/__main__.py +1550 -0
- {mutmut-3.4.0 → mutmut-3.6.0}/src/mutmut/code_coverage.py +32 -26
- mutmut-3.6.0/src/mutmut/configuration.py +207 -0
- mutmut-3.6.0/src/mutmut/mutation/__init__.py +0 -0
- mutmut-3.6.0/src/mutmut/mutation/data.py +61 -0
- mutmut-3.6.0/src/mutmut/mutation/file_mutation.py +508 -0
- mutmut-3.4.0/src/mutmut/node_mutation.py → mutmut-3.6.0/src/mutmut/mutation/mutators.py +65 -71
- mutmut-3.6.0/src/mutmut/mutation/pragma_handling.py +229 -0
- mutmut-3.6.0/src/mutmut/mutation/trampoline.py +105 -0
- mutmut-3.6.0/src/mutmut/mutation/trampoline_templates.py +54 -0
- mutmut-3.6.0/src/mutmut/py.typed +0 -0
- mutmut-3.6.0/src/mutmut/skills/kill-survivors.md +18 -0
- mutmut-3.6.0/src/mutmut/threading/__init__.py +0 -0
- mutmut-3.6.0/src/mutmut/threading/timeout.py +57 -0
- mutmut-3.6.0/src/mutmut/type_checking.py +112 -0
- mutmut-3.6.0/src/mutmut/utils/__init__.py +0 -0
- mutmut-3.6.0/src/mutmut/utils/file_utils.py +14 -0
- mutmut-3.6.0/src/mutmut/utils/format_utils.py +55 -0
- mutmut-3.6.0/src/mutmut/utils/safe_setproctitle.py +24 -0
- mutmut-3.4.0/README.rst +0 -252
- mutmut-3.4.0/src/mutmut/__init__.py +0 -24
- mutmut-3.4.0/src/mutmut/__main__.py +0 -1527
- mutmut-3.4.0/src/mutmut/file_mutation.py +0 -304
- mutmut-3.4.0/src/mutmut/trampoline_templates.py +0 -73
- {mutmut-3.4.0 → mutmut-3.6.0}/LICENSE +0 -0
- {mutmut-3.4.0 → mutmut-3.6.0}/src/mutmut/result_browser_layout.tcss +0 -0
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
---------
|
|
3
3
|
|
|
4
|
+
3.6.0
|
|
5
|
+
~~~~~
|
|
6
|
+
|
|
7
|
+
* Add `# pragma: no mutate block` and `# pragma: no mutate start/end` comments
|
|
8
|
+
|
|
9
|
+
* Add `do_not_mutate_patterns` to disable mutations with a regex
|
|
10
|
+
|
|
11
|
+
* Add `only_mutate` config to select which files get mutated
|
|
12
|
+
|
|
13
|
+
* Also mutate methods decorated with (only) `@staticmethod` or `@classmethod`
|
|
14
|
+
|
|
15
|
+
* Add `use_setproctitle` option to disable process renaming (automatically disabled on MacOS)
|
|
16
|
+
|
|
17
|
+
* Add (unstable) `timeout_multiplier` and `timeout_constant` options
|
|
18
|
+
|
|
19
|
+
* Rename `paths_to_mutate` to `source_paths`
|
|
20
|
+
|
|
21
|
+
* Deprecate `tests_dir` in favor of `pytest_add_cli_args_test_selection`
|
|
22
|
+
|
|
23
|
+
* Change `max_stack_depth` to only consider functions inside `source_paths`
|
|
24
|
+
|
|
25
|
+
* Disable mutation of enums and `@staticmethod`/`@classmethod` methods when `type_check_command` is set, as these mutations break type checking
|
|
26
|
+
|
|
27
|
+
* Fix mutation for enum class methods
|
|
28
|
+
|
|
29
|
+
* Fix mutate_only_covered_lines when project uses custom coverage.py config
|
|
30
|
+
|
|
31
|
+
* Fix execution when running mutmut via `python -m mutmut run`
|
|
32
|
+
|
|
33
|
+
* Fix mutation of `class _SomePrivateClass` class methods
|
|
34
|
+
|
|
35
|
+
* Fix mutation of default args
|
|
36
|
+
|
|
37
|
+
* Fix `mutmut browse` crash when no file is selected yet
|
|
38
|
+
|
|
39
|
+
* Fix timeout checker looking up the wrong mutant's expected test time, which could cause mutants to hang
|
|
40
|
+
|
|
41
|
+
* Compare stats paths after resolving symlinks
|
|
42
|
+
|
|
43
|
+
* Warn when mutmut cannot match mutants with the collected stats
|
|
44
|
+
|
|
45
|
+
3.5.0
|
|
46
|
+
~~~~~
|
|
47
|
+
|
|
48
|
+
* Filter mutants based on type annotations. This feature needs to be turned on with the `type_check_command` config.
|
|
49
|
+
|
|
50
|
+
* Show error message when running on Windows
|
|
51
|
+
|
|
52
|
+
* Save mutation stats for use with CI/CD pipeline (#460)
|
|
53
|
+
|
|
54
|
+
* Async functions fixed
|
|
55
|
+
|
|
56
|
+
* Fix for functions with positional only arguments
|
|
57
|
+
|
|
58
|
+
* Support signatures with PEP 649 deferred annotations
|
|
59
|
+
|
|
60
|
+
|
|
4
61
|
3.4.0
|
|
5
62
|
~~~~~
|
|
6
63
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mutmut
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.6.0
|
|
4
4
|
Summary: mutation testing for Python 3
|
|
5
|
+
Keywords: mutmut,mutant,mutation,test,testing
|
|
5
6
|
Author: Anders Hovmöller
|
|
6
7
|
Author-email: Anders Hovmöller <boxed@killingar.net>
|
|
7
8
|
License-Expression: BSD-3-Clause
|
|
@@ -85,8 +86,7 @@ it will try to figure out where the code to mutate is.
|
|
|
85
86
|
|
|
86
87
|
|
|
87
88
|
You can stop the mutation run at any time and mutmut will restart where you
|
|
88
|
-
left off.
|
|
89
|
-
modified since last run.
|
|
89
|
+
left off.
|
|
90
90
|
|
|
91
91
|
To work with the results, use `mutmut browse` where you can see the mutants,
|
|
92
92
|
retest them when you've updated your tests.
|
|
@@ -121,7 +121,7 @@ In `setup.cfg` in the root of your project you can configure mutmut if you need
|
|
|
121
121
|
.. code-block:: ini
|
|
122
122
|
|
|
123
123
|
[mutmut]
|
|
124
|
-
|
|
124
|
+
source_paths=src/
|
|
125
125
|
pytest_add_cli_args_test_selection=tests/
|
|
126
126
|
|
|
127
127
|
If you use `pyproject.toml`, you must specify the paths as array in a `tool.mutmut` section:
|
|
@@ -129,7 +129,7 @@ If you use `pyproject.toml`, you must specify the paths as array in a `tool.mutm
|
|
|
129
129
|
.. code-block:: toml
|
|
130
130
|
|
|
131
131
|
[tool.mutmut]
|
|
132
|
-
|
|
132
|
+
source_paths = [ "src/" ]
|
|
133
133
|
pytest_add_cli_args_test_selection= [ "tests/" ]
|
|
134
134
|
|
|
135
135
|
See below for more options for configuring mutmut.
|
|
@@ -161,8 +161,8 @@ introduced bug in those base functions will lead to many tests that fail which
|
|
|
161
161
|
are hard to understand how they relate to the function with the change.
|
|
162
162
|
|
|
163
163
|
You can configure mutmut to only count a test as being relevant for a function
|
|
164
|
-
if the stack depth
|
|
165
|
-
`setup.cfg` add:
|
|
164
|
+
if the stack depth is below some limit. Only stack frames from code inside `source_paths`
|
|
165
|
+
is counted towards the limit, 3rd party libraries are ignored. In your `setup.cfg` add:
|
|
166
166
|
|
|
167
167
|
.. code-block:: ini
|
|
168
168
|
|
|
@@ -176,10 +176,14 @@ caught.
|
|
|
176
176
|
Exclude files from mutation
|
|
177
177
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
178
178
|
|
|
179
|
+
By default mutmut mutates all python files in `source_paths`.
|
|
179
180
|
You can exclude files from mutation in `setup.cfg`:
|
|
180
181
|
|
|
181
182
|
.. code-block::
|
|
182
183
|
|
|
184
|
+
only_mutate=
|
|
185
|
+
src/api/*
|
|
186
|
+
src/services/*
|
|
183
187
|
do_not_mutate=
|
|
184
188
|
*__tests.py
|
|
185
189
|
|
|
@@ -199,6 +203,34 @@ If you only want to mutate lines that are called (according to coverage.py), you
|
|
|
199
203
|
mutate_only_covered_lines=true
|
|
200
204
|
|
|
201
205
|
|
|
206
|
+
Filter generated mutants with type checker
|
|
207
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
208
|
+
|
|
209
|
+
When your project is type checked using `mypy` or `pyrefly`, you can also use it to filter out invalid mutants.
|
|
210
|
+
For instance, mutmut mutates `x: str = 'foo'` to `x: str = None` which can easily caught by type checkers.
|
|
211
|
+
|
|
212
|
+
Using this filter can improve performance and reduce noise, however it can also hide a few relevant mutations:
|
|
213
|
+
|
|
214
|
+
1. `x: str = None` may not be valid, but if your tests do not detect such a change it indicates that
|
|
215
|
+
the value of `x` is not properly tested (even if your type checker would catch this particular modification)
|
|
216
|
+
2. In some edge cases with class properties (usually in the `__init__` method), the way `mypy` and `pyrefly` infer types does not work well
|
|
217
|
+
with the way mutmut mutates code. Some valid mutations like changing `self.x = 123` to `self.x = None` can
|
|
218
|
+
be filtered out, even though the may be valid.
|
|
219
|
+
|
|
220
|
+
To enable this filtering, configure the `type_check_command` to output json results as follows:
|
|
221
|
+
|
|
222
|
+
.. code-block::
|
|
223
|
+
|
|
224
|
+
# for pyrefly
|
|
225
|
+
type_check_command = ['pyrefly', 'check', '--output-format=json']
|
|
226
|
+
# for mypy
|
|
227
|
+
type_check_command = ['mypy', 'your_source_dir', '--output', 'json', '--disable-error-code', 'unused-ignore']
|
|
228
|
+
|
|
229
|
+
Currently, only `pyrefly` and `mypy` are supported.
|
|
230
|
+
With `pyright` and `ty`, mutating a class method `Foo.bar()` can break the types of all methods of `Foo`,
|
|
231
|
+
and therefore mutmut cannot match the type error with the mutant that caused the type error.
|
|
232
|
+
|
|
233
|
+
|
|
202
234
|
Enable debug output (increase verbosity)
|
|
203
235
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
204
236
|
|
|
@@ -213,22 +245,174 @@ to failing tests.
|
|
|
213
245
|
debug=true
|
|
214
246
|
|
|
215
247
|
|
|
216
|
-
|
|
217
|
-
|
|
248
|
+
Disable setproctitle (macOS)
|
|
249
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
218
250
|
|
|
219
|
-
|
|
251
|
+
Mutmut uses ``setproctitle`` to show the current mutant name in the process
|
|
252
|
+
list, which is helpful for monitoring long runs. However, ``setproctitle``
|
|
253
|
+
uses CoreFoundation APIs on macOS that are not fork-safe, causing segfaults
|
|
254
|
+
in child processes.
|
|
220
255
|
|
|
221
|
-
|
|
256
|
+
By default, mutmut automatically disables ``setproctitle`` on macOS and
|
|
257
|
+
enables it on other platforms. If you need to override this (e.g. to enable it on
|
|
258
|
+
macOS at your own risk, or to disable it on other platforms), set ``use_setproctitle``:
|
|
222
259
|
|
|
223
|
-
|
|
260
|
+
.. code-block:: toml
|
|
224
261
|
|
|
225
|
-
|
|
226
|
-
|
|
262
|
+
# pyproject.toml
|
|
263
|
+
[tool.mutmut]
|
|
264
|
+
use_setproctitle = false
|
|
227
265
|
|
|
228
|
-
|
|
266
|
+
|
|
267
|
+
Disabling mutation on specific code
|
|
268
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
269
|
+
|
|
270
|
+
If you do not want to mutate specific parts of your code, you can disable mutation
|
|
271
|
+
via the following options. Examples where this could be relevant:
|
|
272
|
+
|
|
273
|
+
- If you don't want to test exact log messages (e.g. `logging.info("Foo")` versus the mutated `logging.info("XXFooXX")`)
|
|
229
274
|
- Optimizing break instead of continue. The code runs fine when mutating break
|
|
230
275
|
to continue, but it's slower.
|
|
231
276
|
|
|
277
|
+
Skipping via regex
|
|
278
|
+
^^^^^^^^^^^^^^^^^^
|
|
279
|
+
|
|
280
|
+
You can use a regex on the source code, to tell mutmut which expressions it should not mutate:
|
|
281
|
+
|
|
282
|
+
.. code-block:: toml
|
|
283
|
+
|
|
284
|
+
# pyproject.toml
|
|
285
|
+
[tool.mutmut]
|
|
286
|
+
do_not_mutate_patterns = [
|
|
287
|
+
# disable mutations of all logger.info/debug/... statements
|
|
288
|
+
'logger\.\w+',
|
|
289
|
+
# disable mutating exceptions
|
|
290
|
+
'raise \w+',
|
|
291
|
+
]
|
|
292
|
+
|
|
293
|
+
Mutmut will match the regex on the source code
|
|
294
|
+
and skip mutating any expression on the matched lines.
|
|
295
|
+
|
|
296
|
+
Skipping via code comments
|
|
297
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
298
|
+
|
|
299
|
+
You can use following comments to disable mutation:
|
|
300
|
+
|
|
301
|
+
- `# pragma: no mutate`: disable mutation on this single line
|
|
302
|
+
- `# pragma: no mutate block`: disable mutation in the whole intendation block
|
|
303
|
+
- `# pragma: no mutate start/end`: disable mutation between the start and end comments
|
|
304
|
+
|
|
305
|
+
Skipping single lines
|
|
306
|
+
^^^^^^^^^^^^^^^^^^^^^
|
|
307
|
+
|
|
308
|
+
You can mark lines like this to stop mutating them:
|
|
309
|
+
|
|
310
|
+
.. code-block:: python
|
|
311
|
+
|
|
312
|
+
some_code_here() # pragma: no mutate
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
Skipping Code Blocks
|
|
316
|
+
^^^^^^^^^^^^^^^^^^^^
|
|
317
|
+
|
|
318
|
+
You can skip an entire indentation block from mutation using
|
|
319
|
+
``# pragma: no mutate block``. This works on any compound statement --
|
|
320
|
+
functions, classes, ``if``/``elif``/``else``, loops, context managers, etc.
|
|
321
|
+
|
|
322
|
+
Both syntax styles are supported:
|
|
323
|
+
|
|
324
|
+
- ``# pragma: no mutate block``
|
|
325
|
+
- ``# pragma: no mutate: block``
|
|
326
|
+
|
|
327
|
+
**Skipping an entire function or class** -- place the pragma inline on the
|
|
328
|
+
definition line. The entire node (including all children) is skipped and no
|
|
329
|
+
trampoline is generated:
|
|
330
|
+
|
|
331
|
+
.. code-block:: python
|
|
332
|
+
|
|
333
|
+
def complex_algorithm(): # pragma: no mutate block
|
|
334
|
+
return some_complex_calculation()
|
|
335
|
+
|
|
336
|
+
class MySettings: # pragma: no mutate block
|
|
337
|
+
DEBUG = True
|
|
338
|
+
MAX_RETRIES = 3
|
|
339
|
+
|
|
340
|
+
**Skipping only the body of a function** -- place the pragma on its own line
|
|
341
|
+
inside the function. The function definition (including default arguments) is
|
|
342
|
+
still mutable, but the body is suppressed:
|
|
343
|
+
|
|
344
|
+
.. code-block:: python
|
|
345
|
+
|
|
346
|
+
def foo(val=1):
|
|
347
|
+
# pragma: no mutate block
|
|
348
|
+
x = 1
|
|
349
|
+
y = complex_calculation()
|
|
350
|
+
z = x + y
|
|
351
|
+
|
|
352
|
+
def bar():
|
|
353
|
+
# this function is still mutated normally
|
|
354
|
+
return 42
|
|
355
|
+
|
|
356
|
+
**Skipping an ``if`` branch without affecting ``elif``/``else``** -- place
|
|
357
|
+
the pragma inline on the ``if``. Only the ``if`` condition and its indented
|
|
358
|
+
body are suppressed; sibling branches (``elif``, ``else``) remain mutable
|
|
359
|
+
because they exit the original indentation scope:
|
|
360
|
+
|
|
361
|
+
.. code-block:: python
|
|
362
|
+
|
|
363
|
+
if error_condition: # pragma: no mutate block
|
|
364
|
+
log_error()
|
|
365
|
+
send_alert()
|
|
366
|
+
elif other_condition:
|
|
367
|
+
# still mutated -- this branch is outside the block scope
|
|
368
|
+
handle_other()
|
|
369
|
+
else:
|
|
370
|
+
# still mutated
|
|
371
|
+
handle_success()
|
|
372
|
+
|
|
373
|
+
The same principle applies to ``for``/``else``, ``while``/``else``,
|
|
374
|
+
``try``/``except``/``finally``, and ``match``/``case``.
|
|
375
|
+
|
|
376
|
+
This is useful for:
|
|
377
|
+
|
|
378
|
+
- Functions or classes that should be excluded from mutation entirely
|
|
379
|
+
- Error-handling branches that are hard to unit test in isolation
|
|
380
|
+
- Logging or telemetry blocks that don't affect program correctness
|
|
381
|
+
- Generated or boilerplate code within an otherwise mutable function
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
Skipping Code Regions
|
|
385
|
+
^^^^^^^^^^^^^^^^^^^^^
|
|
386
|
+
|
|
387
|
+
For suppressing mutations across a range of lines regardless of indentation,
|
|
388
|
+
use ``# pragma: no mutate start`` and ``# pragma: no mutate end``:
|
|
389
|
+
|
|
390
|
+
.. code-block:: python
|
|
391
|
+
|
|
392
|
+
a = mutate_this()
|
|
393
|
+
|
|
394
|
+
# pragma: no mutate start
|
|
395
|
+
b = skip_this()
|
|
396
|
+
c = skip_this_too()
|
|
397
|
+
# pragma: no mutate end
|
|
398
|
+
|
|
399
|
+
d = mutate_this_too()
|
|
400
|
+
|
|
401
|
+
Every line between the markers (inclusive) is suppressed. This works inside
|
|
402
|
+
functions, classes, or at module level, and ignores indentation entirely.
|
|
403
|
+
|
|
404
|
+
An unmatched ``# pragma: no mutate end`` without a preceding ``start`` raises
|
|
405
|
+
a ``PragmaParseError`` at parse time. An unclosed ``# pragma: no mutate start``
|
|
406
|
+
(no matching ``end`` before end-of-file) raises a ``PragmaParseError``.
|
|
407
|
+
Both errors include the filename and line number.
|
|
408
|
+
|
|
409
|
+
**Nesting restriction:** opening a new ``block`` or ``start`` context while
|
|
410
|
+
another context is already active is not allowed and raises a
|
|
411
|
+
``PragmaParseError``. The error message includes both the offending line and
|
|
412
|
+
the line where the existing context was opened. Close the current context
|
|
413
|
+
first (dedent for ``block``, or ``# pragma: no mutate end`` for ``start``)
|
|
414
|
+
before opening a new one.
|
|
415
|
+
|
|
232
416
|
|
|
233
417
|
Modifying pytest arguments
|
|
234
418
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
@@ -250,6 +434,20 @@ You can add and override pytest arguments:
|
|
|
250
434
|
also_copy = ["mutmut_pytest.ini"]
|
|
251
435
|
|
|
252
436
|
|
|
437
|
+
Unstable configs
|
|
438
|
+
~~~~~~~~~~~~~~~~
|
|
439
|
+
|
|
440
|
+
Following configurations exist, but may be changed in any minor version.
|
|
441
|
+
If you use them, expect that a new version could change or break this feature.
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
.. code-block:: toml
|
|
445
|
+
|
|
446
|
+
# Configure how long mutmut waits before killing a slow mutation
|
|
447
|
+
# Currently calculated as (duration_of_original_tests + timeout_constant) * timeout_multiplier seconds
|
|
448
|
+
timeout_constant = 1.0
|
|
449
|
+
timeout_multiplier = 15.0
|
|
450
|
+
|
|
253
451
|
|
|
254
452
|
Example mutations
|
|
255
453
|
-----------------
|