Skip to content

Commit 2710f93

Browse files
authored
Fix string mutation (#388)
String mutation would otherwise fail when escape sequences are used, changing `"\n"` to `"\N"`, which is a syntax error.
1 parent 5688c67 commit 2710f93

File tree

2 files changed

+5
-1
lines changed

2 files changed

+5
-1
lines changed

mutmut/node_mutation.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""This module contains the mutations for indidvidual nodes, e.g. replacing a != b with a == b."""
2+
import re
23
from typing import Any, Union
34
from collections.abc import Callable, Iterable, Sequence
45
import libcst as cst
@@ -11,6 +12,8 @@
1112
]
1213
]
1314

15+
CHARACTER_AFTER_BACKSLASH = re.compile(r"\\([A-Z])")
16+
1417
def operator_number(
1518
node: cst.BaseNumber
1619
) -> Iterable[cst.BaseNumber]:
@@ -40,7 +43,7 @@ def operator_string(
4043
supported_str_mutations: list[Callable[[str], str]] = [
4144
lambda x: "XX" + x + "XX",
4245
lambda x: x.lower(),
43-
lambda x: x.upper(),
46+
lambda x: CHARACTER_AFTER_BACKSLASH.sub(lambda match: f"\\{match.group(1).lower()}", x.upper()),
4447
lambda x: x.capitalize(),
4548
]
4649

tests/test_mutation.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ def mutated_module(source: str) -> str:
126126
('a: Optional[int] = None', 'a: Optional[int] = ""'),
127127
('a: int = 1', ['a: int = 2', 'a: int = None']),
128128
('a: str = "FoO"', ['a: str = "XXFoOXX"', 'a: str = "foo"', 'a: str = "FOO"', 'a: str = "Foo"', 'a: str = None']),
129+
('a: str = "Fo\\t"', ['a: str = "XXFo\\tXX"', 'a: str = "fo\\t"', 'a: str = "FO\\t"', 'a: str = None']),
129130
('lambda: 0', ['lambda: 1', 'lambda: None']),
130131
("1 in (1, 2)", ['2 in (1, 2)', '1 not in (1, 2)', '1 in (2, 2)', '1 in (1, 3)']),
131132
('1+1', ['2+1', '1 - 1', '1+2']),

0 commit comments

Comments
 (0)