Skip to content

Commit 353e361

Browse files
committed
build: skip inert mutants in the mutation check
Eval: gate-green
1 parent 1fd5e14 commit 353e361

1 file changed

Lines changed: 26 additions & 5 deletions

File tree

scripts/mutate.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,33 @@ class Mutant:
4444
source: str
4545

4646

47+
def _inert(tree: ast.Module) -> set[int]:
48+
"""Nodes whose value cannot change behaviour: decorator arguments (a
49+
dataclass(slots=...) flip is invisible to every real path) and dunder
50+
configuration like __test__, which addresses the test runner, not the code."""
51+
inert: set[int] = set()
52+
for node in ast.walk(tree):
53+
for decorator in getattr(node, "decorator_list", []):
54+
inert.update(id(sub) for sub in ast.walk(decorator))
55+
if isinstance(node, ast.Assign) and any(
56+
isinstance(t, ast.Name) and t.id.startswith("__") and t.id.endswith("__")
57+
for t in node.targets
58+
):
59+
inert.update(id(sub) for sub in ast.walk(node.value))
60+
return inert
61+
62+
4763
class _Mutator(ast.NodeTransformer):
48-
def __init__(self, target: int) -> None:
64+
def __init__(self, target: int, inert: set[int] | None = None) -> None:
4965
self.target = target
66+
self.inert = inert or set()
5067
self.seen = 0
5168
self.applied: str | None = None
5269
self.lineno = 0
5370

5471
def _take(self, node: ast.AST, description: str) -> bool:
72+
if id(node) in self.inert:
73+
return False
5574
hit = self.seen == self.target
5675
self.seen += 1
5776
if hit:
@@ -98,15 +117,17 @@ def visit_Constant(self, node: ast.Constant) -> ast.AST:
98117

99118

100119
def _count(source: str) -> int:
101-
counter = _Mutator(target=-1)
102-
counter.visit(ast.parse(source))
120+
tree = ast.parse(source)
121+
counter = _Mutator(target=-1, inert=_inert(tree))
122+
counter.visit(tree)
103123
return counter.seen
104124

105125

106126
def _build(path: Path, index: int) -> Mutant | None:
107127
original = path.read_text()
108-
mutator = _Mutator(target=index)
109-
mutated = mutator.visit(ast.parse(original))
128+
tree = ast.parse(original)
129+
mutator = _Mutator(target=index, inert=_inert(tree))
130+
mutated = mutator.visit(tree)
110131
if mutator.applied is None:
111132
return None
112133
ast.fix_missing_locations(mutated)

0 commit comments

Comments
 (0)