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.
@@ -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
 
@@ -2,5 +2,5 @@ include HISTORY.rst
2
2
  include LICENSE
3
3
  include README.rst
4
4
  include requirements.txt
5
- recursive-include mutmut *.tcss
6
5
  exclude tests/*.*
6
+ recursive-include mutmut *.*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mutmut
3
- Version: 3.0.4
3
+ Version: 3.1.0
4
4
  Summary: mutation testing for Python 3
5
5
  Home-page: https://github.com/boxed/mutmut
6
6
  Author: Anders Hovmöller
Binary file
@@ -4,7 +4,7 @@ from parso.python.tree import (
4
4
  Number,
5
5
  )
6
6
 
7
- __version__ = '3.0.4'
7
+ __version__ = '3.1.0'
8
8
 
9
9
 
10
10
  # We have a global whitelist for constants of the pattern __all__, __version__, etc
@@ -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
- return orig(*args, **kwargs)
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
- return orig(*args, **kwargs)
196
+ ret orig(*args, **kwargs)
197
+ return # for the yield case
196
198
  mutant_name = mutant_under_test.rpartition('.')[-1]
197
- return mutants[mutant_name](*args, **kwargs)
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=None):
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
- return _mutmut_trampoline({access_prefix}{mangled_name}__mutmut_orig{access_suffix}, {access_prefix}{mangled_name}__mutmut_mutants{access_suffix}, *args, **kwargs)
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
- return
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
- sys.path.insert(0, os.path.abspath('mutants'))
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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mutmut
3
- Version: 3.0.4
3
+ Version: 3.1.0
4
4
  Summary: mutation testing for Python 3
5
5
  Home-page: https://github.com/boxed/mutmut
6
6
  Author: Anders Hovmöller
@@ -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
@@ -1,6 +1,3 @@
1
- [wheel]
2
- universal = 1
3
-
4
1
  [tool:pytest]
5
2
  testpaths = tests
6
3
  addopts = --junitxml=testreport.xml --strict -r fEsxXw
@@ -79,6 +79,9 @@ setup(
79
79
  url='https://github.com/boxed/mutmut',
80
80
  packages=find_packages('.'),
81
81
  package_dir={'': '.'},
82
+ package_data={
83
+ 'mutmut': ['*.tcss'],
84
+ },
82
85
  include_package_data=True,
83
86
  license="BSD",
84
87
  zip_safe=False,
File without changes
File without changes
File without changes
File without changes