@@ -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+
4763class _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
100119def _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
106126def _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