mutmut 3.7.0__tar.gz → 3.8.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.
Files changed (44) hide show
  1. {mutmut-3.7.0 → mutmut-3.8.0}/HISTORY.rst +32 -1
  2. {mutmut-3.7.0 → mutmut-3.8.0}/PKG-INFO +130 -7
  3. {mutmut-3.7.0 → mutmut-3.8.0}/README.rst +122 -0
  4. mutmut-3.8.0/pyproject.toml +113 -0
  5. mutmut-3.7.0/pyproject.toml → mutmut-3.8.0/pyproject.toml.orig +9 -8
  6. mutmut-3.8.0/src/mutmut/__init__.py +48 -0
  7. mutmut-3.8.0/src/mutmut/__main__.py +1139 -0
  8. mutmut-3.8.0/src/mutmut/code_coverage.py +105 -0
  9. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/configuration.py +78 -17
  10. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/core.py +4 -0
  11. mutmut-3.8.0/src/mutmut/models/__init__.py +1 -0
  12. mutmut-3.8.0/src/mutmut/models/results.py +44 -0
  13. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/mutation/data.py +0 -23
  14. mutmut-3.8.0/src/mutmut/mutation/diff_apply.py +186 -0
  15. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/mutation/file_mutation.py +65 -7
  16. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/mutation/mutators.py +22 -0
  17. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/mutation/pragma_handling.py +96 -8
  18. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/mutation/trampoline.py +38 -6
  19. mutmut-3.8.0/src/mutmut/runners/__init__.py +1 -0
  20. mutmut-3.8.0/src/mutmut/runners/harness.py +255 -0
  21. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/state.py +7 -0
  22. mutmut-3.8.0/src/mutmut/stats.py +173 -0
  23. mutmut-3.8.0/src/mutmut/ui/__init__.py +1 -0
  24. mutmut-3.8.0/src/mutmut/ui/browse.py +254 -0
  25. mutmut-3.8.0/src/mutmut/ui/terminal.py +35 -0
  26. mutmut-3.8.0/src/mutmut/utils/file_utils.py +92 -0
  27. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/utils/format_utils.py +18 -0
  28. mutmut-3.8.0/src/mutmut/utils/logging_utils.py +74 -0
  29. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/utils/safe_setproctitle.py +2 -2
  30. mutmut-3.8.0/src/mutmut/workers/__init__.py +1 -0
  31. mutmut-3.8.0/src/mutmut/workers/isolation.py +953 -0
  32. mutmut-3.7.0/src/mutmut/__init__.py +0 -44
  33. mutmut-3.7.0/src/mutmut/__main__.py +0 -2001
  34. mutmut-3.7.0/src/mutmut/code_coverage.py +0 -70
  35. mutmut-3.7.0/src/mutmut/utils/__init__.py +0 -0
  36. mutmut-3.7.0/src/mutmut/utils/file_utils.py +0 -14
  37. {mutmut-3.7.0 → mutmut-3.8.0}/LICENSE +0 -0
  38. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/mutation/__init__.py +0 -0
  39. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/mutation/trampoline_templates.py +0 -0
  40. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/py.typed +0 -0
  41. {mutmut-3.7.0 → mutmut-3.8.0}/src/mutmut/type_checking.py +0 -0
  42. {mutmut-3.7.0/src/mutmut → mutmut-3.8.0/src/mutmut/ui}/result_browser_layout.tcss +0 -0
  43. {mutmut-3.7.0/src/mutmut/threading → mutmut-3.8.0/src/mutmut/utils}/__init__.py +0 -0
  44. {mutmut-3.7.0/src/mutmut/threading → mutmut-3.8.0/src/mutmut/workers}/timeout.py +0 -0
@@ -1,6 +1,37 @@
1
1
  Changelog
2
2
  ---------
3
3
 
4
+ 3.8.0
5
+ ~~~~~~~~~~
6
+
7
+ * Fix obsolete test names never being removed from ``mutants/mutmut-stats.json`` (`#564`)
8
+
9
+ * Fix ``# pragma: no mutate block`` being silently ignored when placed on an ``else``, ``except``, ``except*`` or ``finally`` header, and on the ``try`` line of a ``try``/``except*`` (`#559`)
10
+
11
+ * Fix ``# pragma: no mutate`` being silently ignored on ``match`` and ``case`` headers (`#554`)
12
+
13
+ * Fix mutants being reported as survived when a test uses ``patch.dict(os.environ, ..., clear=True)`` (`#511`, `#552`)
14
+
15
+ * Fix `mutate_only_covered_lines` mutating code that coverage.py excludes from measurement (`# pragma: no cover`, `exclude_lines`, `exclude_also`) (`#547`)
16
+
17
+ * Fix mutations that delete ignored code. Dropping a `case` from a `match`, or an argument from a call, is a mutation of the enclosing node, so it used to be made even when the removed lines were themselves ignored (`#547`)
18
+
19
+ * Fix methods of decorated classes (for example `@dataclass`) not being mutated (`#480`, `#539`)
20
+
21
+ * Fix time measurement including test setup/teardown instead of only the main test run (`#544`)
22
+
23
+ * Fix mutants stopped by the CPU-time limit being reported as killed instead of timed out (`#565`)
24
+
25
+ * Fix ``mutate_only_covered_lines`` breaking when the test suite imports a dependency that cannot be initialized twice in one process, which raised for numpy and libyaml, produced misleading type errors for cryptography, and segfaulted for asyncpg. Coverage is now gathered in a separate process, so the modules the coverage run imports are no longer unloaded and re-imported in the main process (`#528`, `#566`)
26
+
27
+ * Mutate the condition of ternary expressions (`#196`, `#546`)
28
+
29
+ * Add a ``process_isolation`` config for choosing which process mutant workers are forked from. The new ``forkserver`` strategy keeps mutmut's main process free of pytest and your ``conftest.py``, routing every fork through a dedicated fork server process, for test setups that are not fork-safe (``gevent``, ``grpc``, ``torch``). Tune what it preloads with ``forkserver_warmup`` (`#578`, `#566`)
30
+
31
+ * Add a `badge` command that writes a shields.io endpoint file, for publishing the mutation score (`#549`)
32
+
33
+ * Support python3.15 (`#551`)
34
+
4
35
  3.7.0
5
36
  ~~~~~
6
37
 
@@ -586,7 +617,7 @@ Thanks goes out Marcelo Da Cruz Pinto, Savo Kovačević,
586
617
 
587
618
  * Dict literals looking like `dict(a=foo)` now have mutated keys. You can also declare synonyms in setup.cfg.
588
619
 
589
- * Fix "from x import *"
620
+ * Fix ``from x import *``
590
621
 
591
622
 
592
623
  0.0.6 (2017-06-13)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mutmut
3
- Version: 3.7.0
3
+ Version: 3.8.0
4
4
  Summary: mutation testing for Python 3
5
5
  Keywords: mutmut,mutant,mutation,test,testing
6
6
  Author: Anders Hovmöller
@@ -16,19 +16,20 @@ Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Programming Language :: Python :: 3.14
19
- Requires-Dist: click>=8.0.0
19
+ Classifier: Programming Language :: Python :: 3.15
20
+ Requires-Dist: click>=8.4.2
20
21
  Requires-Dist: coverage>=7.3.0
21
- Requires-Dist: libcst>=1.8.5
22
+ Requires-Dist: libcst>=1.9.0
22
23
  Requires-Dist: pytest>=6.2.5
23
- Requires-Dist: setproctitle>=1.1.0
24
+ Requires-Dist: setproctitle>=1.3.7
24
25
  Requires-Dist: textual>=1.0.0
25
26
  Requires-Dist: toml>=0.10.2 ; python_full_version < '3.11'
26
27
  Requires-Python: >=3.10
27
- Project-URL: Changelog, https://github.com/boxed/mutmut/blob/main/HISTORY.rst
28
- Project-URL: Documentation, https://mutmut.readthedocs.io/en/latest/
29
28
  Project-URL: Homepage, https://github.com/boxed/mutmut
30
- Project-URL: Issues, https://github.com/boxed/mutmut/issues
29
+ Project-URL: Documentation, https://mutmut.readthedocs.io/en/latest/
31
30
  Project-URL: Repository, https://github.com/boxed/mutmut
31
+ Project-URL: Issues, https://github.com/boxed/mutmut/issues
32
+ Project-URL: Changelog, https://github.com/boxed/mutmut/blob/main/HISTORY.rst
32
33
  Description-Content-Type: text/x-rst
33
34
 
34
35
  mutmut - python mutation tester
@@ -202,6 +203,11 @@ If you only want to mutate lines that are called (according to coverage.py), you
202
203
 
203
204
  mutate_only_covered_lines=true
204
205
 
206
+ This also honours the lines coverage.py is told to leave out of its measurement, so code
207
+ marked with `# pragma: no cover`, or matched by your `exclude_lines`/`exclude_also`
208
+ settings, is not mutated either. Such code is not held to your test suite, so mutants
209
+ there could only ever show up as survivors.
210
+
205
211
 
206
212
  Filter generated mutants with type checker
207
213
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -264,6 +270,108 @@ macOS at your own risk, or to disable it on other platforms), set ``use_setproct
264
270
  use_setproctitle = false
265
271
 
266
272
 
273
+ Process isolation (``fork`` vs ``forkserver``)
274
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
275
+
276
+ .. code-block:: toml
277
+
278
+ # pyproject.toml
279
+ [tool.mutmut]
280
+ process_isolation = "fork" # or "forkserver"
281
+
282
+ Both strategies require ``os.fork``, so they are POSIX-only.
283
+
284
+ ``fork`` (default)
285
+ Mutmut's main process imports pytest and runs your test suite to collect
286
+ stats, then forks one child per mutant. This is fast and needs no
287
+ configuration, but every mutant worker inherits whatever your test setup
288
+ left behind in that process: monkey-patched sockets, running event loops,
289
+ background threads, open database connections, CUDA contexts. If any of
290
+ that is not fork-safe, the workers can hang, segfault, or report nonsense.
291
+
292
+ ``forkserver``
293
+ Mutmut's main process never imports pytest or your ``conftest.py``. It forks
294
+ a dedicated *fork server* child, and that child does the importing once and
295
+ forks a worker per mutant::
296
+
297
+ main process (clean) -> fork server (imports pytest) -> N mutant workers
298
+
299
+ Because the fork server only imports and forks, and never runs a test
300
+ itself, every worker starts from the same predictable state. This is the
301
+ same design as the ``forkserver`` start method in Python's
302
+ ``multiprocessing``, and it is still only one pytest import for the whole
303
+ run, so it is not slower than ``fork`` in the normal case.
304
+
305
+ **Which one should you use?** Start with ``fork``. Switch to ``forkserver`` if
306
+ your run hangs, crashes, or gives results you cannot reproduce by running the
307
+ tests by hand, and your test setup touches something known to be fork-unsafe,
308
+ such as ``gevent.monkey.patch_all()``, ``grpc``, or ``torch``. It is also the
309
+ better choice if you simply do not want your session-scoped setup living in the
310
+ process that forks mutants.
311
+
312
+
313
+ Tuning what the fork server preloads
314
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
315
+
316
+ ``forkserver_warmup`` controls how much the fork server imports before it starts
317
+ forking workers. It only applies when ``process_isolation = "forkserver"``.
318
+ Anything imported in the fork server is inherited by every worker, and anything
319
+ left out is imported in each worker.
320
+
321
+ .. code-block:: toml
322
+
323
+ # pyproject.toml
324
+ [tool.mutmut]
325
+ process_isolation = "forkserver"
326
+ forkserver_warmup = "collect" # "collect" (default), "import" or "none"
327
+
328
+ ``collect`` (default)
329
+ Run ``pytest --collect-only``, which imports your ``conftest.py``, your
330
+ plugins and your test modules. This is the right default for most projects
331
+ and usually the fastest.
332
+
333
+ ``import``
334
+ Import only the modules listed in ``preload_modules_file`` (one dotted
335
+ module name per line; blank lines and ``#`` comments are ignored, and
336
+ modules that fail to import are skipped). Use this when test *collection*
337
+ itself has side effects you do not want every worker to share, but you still
338
+ want your slow imports warmed up.
339
+
340
+ ``none``
341
+ Import nothing beyond what running a single test requires. This is the
342
+ slowest option, and the most isolated. It is mostly useful for working out
343
+ whether a warmup side effect is what is breaking your run.
344
+
345
+ .. code-block:: toml
346
+
347
+ # pyproject.toml
348
+ [tool.mutmut]
349
+ process_isolation = "forkserver"
350
+ forkserver_warmup = "import"
351
+ preload_modules_file = "mutmut_preload.txt"
352
+
353
+ If the fork server itself crashes, mutmut starts a new one and re-submits the
354
+ mutants that were in flight, up to ``max_forkserver_restarts`` times (default
355
+ ``3``) before giving up with a ``ForkServerCrashError``:
356
+
357
+ .. code-block:: toml
358
+
359
+ # pyproject.toml
360
+ [tool.mutmut]
361
+ max_forkserver_restarts = 3
362
+
363
+ The fork server and its workers cannot write to the console without mangling
364
+ mutmut's progress output, so they log to a file instead. Logging is off unless
365
+ you ask for it with ``log_to_file`` (or ``debug``):
366
+
367
+ .. code-block:: toml
368
+
369
+ # pyproject.toml
370
+ [tool.mutmut]
371
+ log_to_file = true
372
+ log_file_path = "mutants/mutmut-debug.log"
373
+
374
+
267
375
  Disabling mutation on specific code
268
376
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
269
377
 
@@ -547,6 +655,21 @@ Mutmut keeps the data of what it has done and the mutants in the `mutants/`
547
655
  directory. If you want to make sure you run a full mutmut run you can delete
548
656
  this directory to start from scratch.
549
657
 
658
+
659
+ Mutation score badges
660
+ ---------------------
661
+
662
+ `mutmut badge` turns `mutmut export-cicd-stats` output into `Shields endpoint JSON <https://shields.io/badges/endpoint-badge>`_:
663
+
664
+ .. code-block:: console
665
+
666
+ mutmut export-cicd-stats
667
+ mutmut badge --output mutation-score.json
668
+
669
+ .. code-block:: md
670
+
671
+ ![mutation](https://img.shields.io/endpoint?url=https://example.com/mutation-score.json)
672
+
550
673
  Contributing to Mutmut
551
674
  ----------------------
552
675
 
@@ -169,6 +169,11 @@ If you only want to mutate lines that are called (according to coverage.py), you
169
169
 
170
170
  mutate_only_covered_lines=true
171
171
 
172
+ This also honours the lines coverage.py is told to leave out of its measurement, so code
173
+ marked with `# pragma: no cover`, or matched by your `exclude_lines`/`exclude_also`
174
+ settings, is not mutated either. Such code is not held to your test suite, so mutants
175
+ there could only ever show up as survivors.
176
+
172
177
 
173
178
  Filter generated mutants with type checker
174
179
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -231,6 +236,108 @@ macOS at your own risk, or to disable it on other platforms), set ``use_setproct
231
236
  use_setproctitle = false
232
237
 
233
238
 
239
+ Process isolation (``fork`` vs ``forkserver``)
240
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
241
+
242
+ .. code-block:: toml
243
+
244
+ # pyproject.toml
245
+ [tool.mutmut]
246
+ process_isolation = "fork" # or "forkserver"
247
+
248
+ Both strategies require ``os.fork``, so they are POSIX-only.
249
+
250
+ ``fork`` (default)
251
+ Mutmut's main process imports pytest and runs your test suite to collect
252
+ stats, then forks one child per mutant. This is fast and needs no
253
+ configuration, but every mutant worker inherits whatever your test setup
254
+ left behind in that process: monkey-patched sockets, running event loops,
255
+ background threads, open database connections, CUDA contexts. If any of
256
+ that is not fork-safe, the workers can hang, segfault, or report nonsense.
257
+
258
+ ``forkserver``
259
+ Mutmut's main process never imports pytest or your ``conftest.py``. It forks
260
+ a dedicated *fork server* child, and that child does the importing once and
261
+ forks a worker per mutant::
262
+
263
+ main process (clean) -> fork server (imports pytest) -> N mutant workers
264
+
265
+ Because the fork server only imports and forks, and never runs a test
266
+ itself, every worker starts from the same predictable state. This is the
267
+ same design as the ``forkserver`` start method in Python's
268
+ ``multiprocessing``, and it is still only one pytest import for the whole
269
+ run, so it is not slower than ``fork`` in the normal case.
270
+
271
+ **Which one should you use?** Start with ``fork``. Switch to ``forkserver`` if
272
+ your run hangs, crashes, or gives results you cannot reproduce by running the
273
+ tests by hand, and your test setup touches something known to be fork-unsafe,
274
+ such as ``gevent.monkey.patch_all()``, ``grpc``, or ``torch``. It is also the
275
+ better choice if you simply do not want your session-scoped setup living in the
276
+ process that forks mutants.
277
+
278
+
279
+ Tuning what the fork server preloads
280
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
281
+
282
+ ``forkserver_warmup`` controls how much the fork server imports before it starts
283
+ forking workers. It only applies when ``process_isolation = "forkserver"``.
284
+ Anything imported in the fork server is inherited by every worker, and anything
285
+ left out is imported in each worker.
286
+
287
+ .. code-block:: toml
288
+
289
+ # pyproject.toml
290
+ [tool.mutmut]
291
+ process_isolation = "forkserver"
292
+ forkserver_warmup = "collect" # "collect" (default), "import" or "none"
293
+
294
+ ``collect`` (default)
295
+ Run ``pytest --collect-only``, which imports your ``conftest.py``, your
296
+ plugins and your test modules. This is the right default for most projects
297
+ and usually the fastest.
298
+
299
+ ``import``
300
+ Import only the modules listed in ``preload_modules_file`` (one dotted
301
+ module name per line; blank lines and ``#`` comments are ignored, and
302
+ modules that fail to import are skipped). Use this when test *collection*
303
+ itself has side effects you do not want every worker to share, but you still
304
+ want your slow imports warmed up.
305
+
306
+ ``none``
307
+ Import nothing beyond what running a single test requires. This is the
308
+ slowest option, and the most isolated. It is mostly useful for working out
309
+ whether a warmup side effect is what is breaking your run.
310
+
311
+ .. code-block:: toml
312
+
313
+ # pyproject.toml
314
+ [tool.mutmut]
315
+ process_isolation = "forkserver"
316
+ forkserver_warmup = "import"
317
+ preload_modules_file = "mutmut_preload.txt"
318
+
319
+ If the fork server itself crashes, mutmut starts a new one and re-submits the
320
+ mutants that were in flight, up to ``max_forkserver_restarts`` times (default
321
+ ``3``) before giving up with a ``ForkServerCrashError``:
322
+
323
+ .. code-block:: toml
324
+
325
+ # pyproject.toml
326
+ [tool.mutmut]
327
+ max_forkserver_restarts = 3
328
+
329
+ The fork server and its workers cannot write to the console without mangling
330
+ mutmut's progress output, so they log to a file instead. Logging is off unless
331
+ you ask for it with ``log_to_file`` (or ``debug``):
332
+
333
+ .. code-block:: toml
334
+
335
+ # pyproject.toml
336
+ [tool.mutmut]
337
+ log_to_file = true
338
+ log_file_path = "mutants/mutmut-debug.log"
339
+
340
+
234
341
  Disabling mutation on specific code
235
342
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
236
343
 
@@ -514,6 +621,21 @@ Mutmut keeps the data of what it has done and the mutants in the `mutants/`
514
621
  directory. If you want to make sure you run a full mutmut run you can delete
515
622
  this directory to start from scratch.
516
623
 
624
+
625
+ Mutation score badges
626
+ ---------------------
627
+
628
+ `mutmut badge` turns `mutmut export-cicd-stats` output into `Shields endpoint JSON <https://shields.io/badges/endpoint-badge>`_:
629
+
630
+ .. code-block:: console
631
+
632
+ mutmut export-cicd-stats
633
+ mutmut badge --output mutation-score.json
634
+
635
+ .. code-block:: md
636
+
637
+ ![mutation](https://img.shields.io/endpoint?url=https://example.com/mutation-score.json)
638
+
517
639
  Contributing to Mutmut
518
640
  ----------------------
519
641
 
@@ -0,0 +1,113 @@
1
+ [project]
2
+ name = "mutmut"
3
+ version = "3.8.0"
4
+ description = "mutation testing for Python 3"
5
+ keywords = [
6
+ "mutmut",
7
+ "mutant",
8
+ "mutation",
9
+ "test",
10
+ "testing",
11
+ ]
12
+ requires-python = ">=3.10"
13
+ readme = "README.rst"
14
+ license = "BSD-3-Clause"
15
+ license-files = ["LICENSE"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Natural Language :: English",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ "Programming Language :: Python :: 3.15",
27
+ ]
28
+ dependencies = [
29
+ "click>=8.4.2",
30
+ "coverage>=7.3.0",
31
+ "libcst>=1.9.0",
32
+ "pytest>=6.2.5",
33
+ "setproctitle>=1.3.7",
34
+ "textual>=1.0.0",
35
+ "toml>=0.10.2 ; python_full_version < '3.11'",
36
+ ]
37
+
38
+ [[project.authors]]
39
+ name = "Anders Hovmöller"
40
+ email = "boxed@killingar.net"
41
+
42
+ [project.urls]
43
+ Homepage = "https://github.com/boxed/mutmut"
44
+ Documentation = "https://mutmut.readthedocs.io/en/latest/"
45
+ Repository = "https://github.com/boxed/mutmut"
46
+ Issues = "https://github.com/boxed/mutmut/issues"
47
+ Changelog = "https://github.com/boxed/mutmut/blob/main/HISTORY.rst"
48
+
49
+ [project.scripts]
50
+ mutmut = "mutmut.__main__:cli"
51
+
52
+ [build-system]
53
+ requires = ["uv_build>=0.12.3,<1"]
54
+ build-backend = "uv_build"
55
+
56
+ [tool.uv.build-backend]
57
+ source-include = ["HISTORY.rst"]
58
+
59
+ [tool.pytest.ini_options]
60
+ testpaths = ["tests"]
61
+ asyncio_default_fixture_loop_scope = "function"
62
+
63
+ [tool.inline-snapshot]
64
+ format-command = "ruff format --stdin-filename {filename}"
65
+
66
+ [tool.ruff]
67
+ line-length = 120
68
+ target-version = "py310"
69
+ extend-exclude = [
70
+ "docs/_themes/",
71
+ "docs/conf.py",
72
+ "e2e_projects/",
73
+ "tests/data/test_generation/",
74
+ ]
75
+
76
+ [tool.ruff.lint]
77
+ select = [
78
+ "E",
79
+ "F",
80
+ "W",
81
+ "B",
82
+ "C4",
83
+ "UP",
84
+ "I",
85
+ ]
86
+ ignore = [
87
+ "E501",
88
+ "B007",
89
+ "B018",
90
+ "B024",
91
+ "B027",
92
+ "B028",
93
+ "B904",
94
+ "C405",
95
+ "C408",
96
+ "C416",
97
+ "F402",
98
+ "F841",
99
+ "UP007",
100
+ ]
101
+
102
+ [tool.ruff.lint.isort]
103
+ force-single-line = true
104
+
105
+ [dependency-groups]
106
+ dev = [
107
+ "inline-snapshot>=0.32.0",
108
+ "mypy>=2.3.1",
109
+ "pre-commit>=4.6.2",
110
+ "pyrefly>=0.59.1",
111
+ "pytest-asyncio>=1.0.0",
112
+ "ruff>=0.16.6",
113
+ ]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mutmut"
3
- version = "3.7.0"
3
+ version = "3.8.0"
4
4
  description = "mutation testing for Python 3"
5
5
  keywords = ["mutmut", "mutant", "mutation", "test", "testing"]
6
6
  authors = [
@@ -21,14 +21,15 @@ classifiers = [
21
21
  "Programming Language :: Python :: 3.12",
22
22
  "Programming Language :: Python :: 3.13",
23
23
  "Programming Language :: Python :: 3.14",
24
+ "Programming Language :: Python :: 3.15",
24
25
  ]
25
26
 
26
27
  dependencies = [
27
- "click>=8.0.0",
28
+ "click>=8.4.2",
28
29
  "coverage>=7.3.0",
29
- "libcst>=1.8.5",
30
+ "libcst>=1.9.0",
30
31
  "pytest>=6.2.5",
31
- "setproctitle>=1.1.0",
32
+ "setproctitle>=1.3.7",
32
33
  "textual>=1.0.0",
33
34
  "toml>=0.10.2 ; python_full_version < '3.11'",
34
35
  ]
@@ -45,7 +46,7 @@ Changelog = "https://github.com/boxed/mutmut/blob/main/HISTORY.rst"
45
46
  mutmut = "mutmut.__main__:cli"
46
47
 
47
48
  [build-system]
48
- requires = ["uv_build>=0.9.5,<0.10.0"]
49
+ requires = ["uv_build>=0.12.3,<1"]
49
50
  build-backend = "uv_build"
50
51
 
51
52
  [tool.uv.build-backend]
@@ -54,11 +55,11 @@ source-include = ["HISTORY.rst"]
54
55
  [dependency-groups]
55
56
  dev = [
56
57
  "inline-snapshot>=0.32.0",
57
- "mypy>=1.20.1",
58
- "pre-commit>=4.5.1",
58
+ "mypy>=2.3.1",
59
+ "pre-commit>=4.6.2",
59
60
  "pyrefly>=0.59.1",
60
61
  "pytest-asyncio>=1.0.0",
61
- "ruff>=0.15.7",
62
+ "ruff>=0.16.6",
62
63
  ]
63
64
 
64
65
  [tool.pytest.ini_options]
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib.metadata
4
+ import warnings
5
+
6
+ from mutmut.configuration import config
7
+ from mutmut.configuration import reset_config
8
+ from mutmut.state import reset_state
9
+ from mutmut.state import state
10
+
11
+ __version__ = importlib.metadata.version("mutmut")
12
+
13
+
14
+ _DEPRECATED_STATE_ATTRS = frozenset(
15
+ {
16
+ "stats_time",
17
+ "duration_by_test",
18
+ "tests_by_mangled_function_name",
19
+ "_stats",
20
+ "_covered_lines",
21
+ "_excluded_lines",
22
+ }
23
+ )
24
+
25
+
26
+ def __getattr__(name: str) -> object:
27
+ match name:
28
+ case "config":
29
+ warnings.warn(
30
+ "mutmut.config is deprecated as of 3.4.1, use mutmut.configuration.config() instead",
31
+ FutureWarning,
32
+ stacklevel=2,
33
+ )
34
+ return config()
35
+ case name if name in _DEPRECATED_STATE_ATTRS:
36
+ warnings.warn(
37
+ f"mutmut.{name} is deprecated, use mutmut.state.state().{name} instead",
38
+ FutureWarning,
39
+ stacklevel=2,
40
+ )
41
+ return getattr(state(), name)
42
+ case _:
43
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
44
+
45
+
46
+ def _reset_globals() -> None:
47
+ reset_config()
48
+ reset_state()