Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pyrit/converter/search_replace_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ def __init__(self, *, pattern: str, replace: str | list[str], regex_flags: int =

Raises:
ValueError: If ``pattern`` is not a string or contains invalid regex syntax,
``replace`` is not a string or non-empty list of strings, or ``regex_flags`` is not an integer.
``replace`` is not a string or non-empty list of strings, any element in ``replace``
is not a valid regex replacement template for ``pattern``, or ``regex_flags`` is not an integer.
"""
if not isinstance(pattern, str):
raise ValueError("pattern must be a string.")
Expand All @@ -51,6 +52,12 @@ def __init__(self, *, pattern: str, replace: str | list[str], regex_flags: int =
else:
raise ValueError("replace must be a string or a non-empty list of strings.")

for item in self._replace_list:
try:
self._compiled_regex.sub(item, "")
except (re.error, IndexError) as e:
raise ValueError(f"Invalid regex replacement template in replace: {e}") from e

self._pattern = pattern
self._regex_flags = regex_flags

Expand Down
33 changes: 33 additions & 0 deletions tests/unit/converter/test_search_replace_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,36 @@ async def test_search_replace_empty_string_replacement():
converter = SearchReplaceConverter(pattern="bad_", replace="")
result = await converter.convert_async(prompt="bad_word bad_code", input_type="text")
assert result.output_text == "word code"


@pytest.mark.parametrize(
("pattern", "invalid_replace"),
[
(r"hello", r"C:\Users\admin"),
(r"hello", r"C:\users\admin"),
(r"hello", r"\1"),
(r"hello", r"\g<foo>"),
(r"hello", ["valid", r"C:\Users\admin"]),
(r"(a)", r"\2"),
(r"(?P<bar>a)", r"\g<foo>"),
],
)
def test_search_replace_invalid_replacement_template(pattern: str, invalid_replace: str | list[str]):
with pytest.raises(ValueError, match="Invalid regex replacement template in replace"):
SearchReplaceConverter(pattern=pattern, replace=invalid_replace)


@pytest.mark.parametrize(
("pattern", "replace", "prompt", "expected"),
[
(r"([a-z]+)", r"<\1>", "hello world", "<hello> <world>"),
(r"(?P<word>[a-z]+)", r"[\g<word>]", "test", "[test]"),
],
)
async def test_search_replace_valid_group_backreferences_async(
*, pattern: str, replace: str, prompt: str, expected: str
) -> None:
converter = SearchReplaceConverter(pattern=pattern, replace=replace)
result = await converter.convert_async(prompt=prompt)
assert result.output_text == expected
assert result.output_type == "text"
Loading