mutmut 3.0.4__tar.gz → 3.1.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.0.4 → mutmut-3.1.0}/HISTORY.rst +18 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/MANIFEST.in +1 -1
- {mutmut-3.0.4 → mutmut-3.1.0}/PKG-INFO +1 -1
- mutmut-3.1.0/mutmut/.DS_Store +0 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/mutmut/__init__.py +1 -1
- {mutmut-3.0.4 → mutmut-3.1.0}/mutmut/__main__.py +72 -12
- mutmut-3.1.0/mutmut/__pycache__/__init__.cpython-311.pyc +0 -0
- mutmut-3.1.0/mutmut/__pycache__/__init__.cpython-312.pyc +0 -0
- mutmut-3.1.0/mutmut/__pycache__/__main__.cpython-311.pyc +0 -0
- mutmut-3.1.0/mutmut/__pycache__/__main__.cpython-312.pyc +0 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/mutmut.egg-info/PKG-INFO +1 -1
- {mutmut-3.0.4 → mutmut-3.1.0}/mutmut.egg-info/SOURCES.txt +9 -1
- {mutmut-3.0.4 → mutmut-3.1.0}/setup.cfg +0 -3
- {mutmut-3.0.4 → mutmut-3.1.0}/setup.py +3 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/AUTHORS.rst +0 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/LICENSE +0 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/README.rst +0 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/mutmut/result_browser_layout.tcss +0 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/mutmut.egg-info/dependency_links.txt +0 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/mutmut.egg-info/entry_points.txt +0 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/mutmut.egg-info/not-zip-safe +0 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/mutmut.egg-info/requires.txt +0 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/mutmut.egg-info/top_level.txt +0 -0
- {mutmut-3.0.4 → mutmut-3.1.0}/requirements.txt +0 -0
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
---------
|
|
3
3
|
|
|
4
|
+
3.1.0
|
|
5
|
+
~~~~~
|
|
6
|
+
|
|
7
|
+
* Correctly handle mutation for generator functions (`yield`).
|
|
8
|
+
|
|
9
|
+
* Fixed so that `from __future__` lines are always first.
|
|
10
|
+
|
|
11
|
+
* If no stats are collected exit directly, as that is a breaking error for mutation testing.
|
|
12
|
+
|
|
13
|
+
* Changed name mangling to make mutants less likely to trigger name-based python magic, like in pytest where functions named `test_*` have special meaning.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
3.0.5
|
|
17
|
+
~~~~~
|
|
18
|
+
|
|
19
|
+
* Another attempt to get the pypi package to work
|
|
20
|
+
|
|
21
|
+
|
|
4
22
|
3.0.4
|
|
5
23
|
~~~~~
|
|
6
24
|
|
|
Binary file
|
|
@@ -189,14 +189,18 @@ def _mutmut_trampoline(orig, mutants, *args, **kwargs):
|
|
|
189
189
|
elif mutant_under_test == 'stats':
|
|
190
190
|
from mutmut.__main__ import record_trampoline_hit
|
|
191
191
|
record_trampoline_hit(orig.__module__ + '.' + orig.__name__)
|
|
192
|
-
|
|
192
|
+
ret orig(*args, **kwargs)
|
|
193
|
+
return # for the yield case
|
|
193
194
|
prefix = orig.__module__ + '.' + orig.__name__ + '__mutmut_'
|
|
194
195
|
if not mutant_under_test.startswith(prefix):
|
|
195
|
-
|
|
196
|
+
ret orig(*args, **kwargs)
|
|
197
|
+
return # for the yield case
|
|
196
198
|
mutant_name = mutant_under_test.rpartition('.')[-1]
|
|
197
|
-
|
|
199
|
+
ret mutants[mutant_name](*args, **kwargs)
|
|
198
200
|
|
|
199
201
|
"""
|
|
202
|
+
yield_from_trampoline_impl = trampoline_impl.replace('ret ', 'yield from ').replace('_mutmut_trampoline', '_mutmut_yield_from_trampoline')
|
|
203
|
+
trampoline_impl = trampoline_impl.replace('ret ', 'return ')
|
|
200
204
|
|
|
201
205
|
|
|
202
206
|
def create_mutants():
|
|
@@ -297,7 +301,7 @@ def write_all_mutants_to_file(*, out, source, filename):
|
|
|
297
301
|
return mutant_names, hash_by_function_name
|
|
298
302
|
|
|
299
303
|
|
|
300
|
-
def build_trampoline(orig_name, mutants, class_name
|
|
304
|
+
def build_trampoline(*, orig_name, mutants, class_name, is_generator):
|
|
301
305
|
assert orig_name not in NEVER_MUTATE_FUNCTION_NAMES
|
|
302
306
|
|
|
303
307
|
mangled_name = mangle_function_name(name=orig_name, class_name=class_name)
|
|
@@ -309,11 +313,18 @@ def build_trampoline(orig_name, mutants, class_name=None):
|
|
|
309
313
|
access_prefix = f'object.__getattribute__(self, "'
|
|
310
314
|
access_suffix = '")'
|
|
311
315
|
|
|
316
|
+
if is_generator:
|
|
317
|
+
return_or_yield_statement = 'yield from'
|
|
318
|
+
trampoline_name = '_mutmut_yield_from_trampoline'
|
|
319
|
+
else:
|
|
320
|
+
return_or_yield_statement = 'return'
|
|
321
|
+
trampoline_name = '_mutmut_trampoline'
|
|
322
|
+
|
|
312
323
|
return f"""
|
|
313
324
|
{mutants_dict}
|
|
314
325
|
|
|
315
326
|
def {orig_name}({'self, ' if class_name is not None else ''}*args, **kwargs):
|
|
316
|
-
|
|
327
|
+
{return_or_yield_statement} {trampoline_name}({access_prefix}{mangled_name}__mutmut_orig{access_suffix}, {access_prefix}{mangled_name}__mutmut_mutants{access_suffix}, *args, **kwargs)
|
|
317
328
|
|
|
318
329
|
{orig_name}.__signature__ = _mutmut_signature({mangled_name}__mutmut_orig)
|
|
319
330
|
{mangled_name}__mutmut_orig.__name__ = '{mangled_name}'
|
|
@@ -447,6 +458,23 @@ class FuncContext:
|
|
|
447
458
|
return False
|
|
448
459
|
|
|
449
460
|
|
|
461
|
+
def is_generator(node):
|
|
462
|
+
assert node.type == 'funcdef'
|
|
463
|
+
|
|
464
|
+
def _is_generator(n):
|
|
465
|
+
if n is not node and n.type in ('funcdef', 'classdef'):
|
|
466
|
+
return False
|
|
467
|
+
|
|
468
|
+
if n.type == 'keyword' and n.value == 'yield':
|
|
469
|
+
return True
|
|
470
|
+
|
|
471
|
+
for c in getattr(n, 'children', []):
|
|
472
|
+
if _is_generator(c):
|
|
473
|
+
return True
|
|
474
|
+
return False
|
|
475
|
+
return _is_generator(node)
|
|
476
|
+
|
|
477
|
+
|
|
450
478
|
def yield_mutants_for_function(node, *, class_name=None, no_mutate_lines):
|
|
451
479
|
assert node.type == 'funcdef'
|
|
452
480
|
|
|
@@ -481,7 +509,7 @@ def yield_mutants_for_function(node, *, class_name=None, no_mutate_lines):
|
|
|
481
509
|
finally:
|
|
482
510
|
context.stack.pop()
|
|
483
511
|
|
|
484
|
-
trampoline = build_trampoline(node.name.value, context.mutants, class_name=class_name)
|
|
512
|
+
trampoline = build_trampoline(orig_name=node.name.value, mutants=context.mutants, class_name=class_name, is_generator=is_generator(node))
|
|
485
513
|
if class_name is not None:
|
|
486
514
|
trampoline = indent(trampoline, ' ')
|
|
487
515
|
yield 'trampoline', trampoline, None, None
|
|
@@ -508,15 +536,38 @@ def yield_mutants_for_class_body(node, no_mutate_lines):
|
|
|
508
536
|
yield 'filler', child_node.get_code(), None, None
|
|
509
537
|
|
|
510
538
|
|
|
539
|
+
def is_from_future_import_node(c):
|
|
540
|
+
if c.type == 'simple_stmt':
|
|
541
|
+
if c.children:
|
|
542
|
+
c2 = c.children[0]
|
|
543
|
+
if c2.type == 'import_from' and c2.children[1].type == 'name' and c2.children[1].value == '__future__':
|
|
544
|
+
return True
|
|
545
|
+
return False
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def yield_future_imports(node):
|
|
549
|
+
for c in node.children:
|
|
550
|
+
if is_from_future_import_node(c):
|
|
551
|
+
yield 'filler', c.get_code(), None, None
|
|
552
|
+
|
|
553
|
+
|
|
511
554
|
def yield_mutants_for_module(node, no_mutate_lines):
|
|
555
|
+
assert node.type == 'file_input'
|
|
556
|
+
|
|
557
|
+
# First yield `from __future__`, then the rest
|
|
558
|
+
yield from yield_future_imports(node)
|
|
559
|
+
|
|
512
560
|
yield 'trampoline_impl', trampoline_impl, None, None
|
|
561
|
+
yield 'trampoline_impl', yield_from_trampoline_impl, None, None
|
|
513
562
|
yield 'filler', '\n', None, None
|
|
514
|
-
assert node.type == 'file_input'
|
|
515
563
|
for child_node in node.children:
|
|
516
564
|
if child_node.type == 'funcdef':
|
|
517
565
|
yield from yield_mutants_for_function(child_node, no_mutate_lines=no_mutate_lines)
|
|
518
566
|
elif child_node.type == 'classdef':
|
|
519
567
|
yield from yield_mutants_for_class(child_node, no_mutate_lines=no_mutate_lines)
|
|
568
|
+
elif is_from_future_import_node(child_node):
|
|
569
|
+
# Don't yield `from __future__` after trampoline
|
|
570
|
+
pass
|
|
520
571
|
else:
|
|
521
572
|
yield 'filler', child_node.get_code(), None, None
|
|
522
573
|
|
|
@@ -636,10 +687,7 @@ class PytestRunner(TestRunner):
|
|
|
636
687
|
raise BadTestExecutionCommandsException(params)
|
|
637
688
|
return exit_code
|
|
638
689
|
|
|
639
|
-
|
|
640
690
|
def run_stats(self, *, tests):
|
|
641
|
-
import pytest
|
|
642
|
-
|
|
643
691
|
class StatsCollector:
|
|
644
692
|
def pytest_runtest_teardown(self, item, nextitem):
|
|
645
693
|
unused(nextitem)
|
|
@@ -719,6 +767,8 @@ def mangle_function_name(*, name, class_name):
|
|
|
719
767
|
if class_name:
|
|
720
768
|
assert CLASS_NAME_SEPARATOR not in class_name
|
|
721
769
|
prefix = f'x{CLASS_NAME_SEPARATOR}{class_name}{CLASS_NAME_SEPARATOR}'
|
|
770
|
+
else:
|
|
771
|
+
prefix = 'x_'
|
|
722
772
|
return f'{prefix}{name}'
|
|
723
773
|
|
|
724
774
|
|
|
@@ -733,6 +783,9 @@ def orig_function_and_class_names_from_key(mutant_name):
|
|
|
733
783
|
if CLASS_NAME_SEPARATOR in r:
|
|
734
784
|
class_name = r[r.index(CLASS_NAME_SEPARATOR) + 1: r.rindex(CLASS_NAME_SEPARATOR)]
|
|
735
785
|
r = r[r.rindex(CLASS_NAME_SEPARATOR) + 1:]
|
|
786
|
+
else:
|
|
787
|
+
assert r.startswith('x_')
|
|
788
|
+
r = r[2:]
|
|
736
789
|
return r, class_name
|
|
737
790
|
|
|
738
791
|
|
|
@@ -937,7 +990,7 @@ def run_stats_collection(runner, tests=None):
|
|
|
937
990
|
|
|
938
991
|
if not mutmut.tests_by_mangled_function_name:
|
|
939
992
|
print('failed to collect stats, no active tests found')
|
|
940
|
-
|
|
993
|
+
exit(1)
|
|
941
994
|
|
|
942
995
|
save_stats()
|
|
943
996
|
|
|
@@ -1083,7 +1136,14 @@ def run(mutant_names, *, max_children):
|
|
|
1083
1136
|
time = datetime.now() - start
|
|
1084
1137
|
print(f' done in {round(time.total_seconds()*1000)}ms', )
|
|
1085
1138
|
|
|
1086
|
-
|
|
1139
|
+
src_path = (Path('mutants') / 'src')
|
|
1140
|
+
source_path = (Path('mutants') / 'source')
|
|
1141
|
+
if src_path.exists():
|
|
1142
|
+
sys.path.insert(0, str(src_path.absolute()))
|
|
1143
|
+
elif source_path.exists():
|
|
1144
|
+
sys.path.insert(0, str(source_path.absolute))
|
|
1145
|
+
else:
|
|
1146
|
+
sys.path.insert(0, os.path.abspath('mutants'))
|
|
1087
1147
|
|
|
1088
1148
|
# TODO: config/option for runner
|
|
1089
1149
|
# runner = HammettRunner()
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -8,6 +8,10 @@ setup.cfg
|
|
|
8
8
|
setup.py
|
|
9
9
|
./mutmut/__init__.py
|
|
10
10
|
./mutmut/__main__.py
|
|
11
|
+
./mutmut/result_browser_layout.tcss
|
|
12
|
+
mutmut/.DS_Store
|
|
13
|
+
mutmut/__init__.py
|
|
14
|
+
mutmut/__main__.py
|
|
11
15
|
mutmut/result_browser_layout.tcss
|
|
12
16
|
mutmut.egg-info/PKG-INFO
|
|
13
17
|
mutmut.egg-info/SOURCES.txt
|
|
@@ -15,4 +19,8 @@ mutmut.egg-info/dependency_links.txt
|
|
|
15
19
|
mutmut.egg-info/entry_points.txt
|
|
16
20
|
mutmut.egg-info/not-zip-safe
|
|
17
21
|
mutmut.egg-info/requires.txt
|
|
18
|
-
mutmut.egg-info/top_level.txt
|
|
22
|
+
mutmut.egg-info/top_level.txt
|
|
23
|
+
mutmut/__pycache__/__init__.cpython-311.pyc
|
|
24
|
+
mutmut/__pycache__/__init__.cpython-312.pyc
|
|
25
|
+
mutmut/__pycache__/__main__.cpython-311.pyc
|
|
26
|
+
mutmut/__pycache__/__main__.cpython-312.pyc
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|