Skip to content

Commit

Permalink
Regex Transformer (#729)
Browse files Browse the repository at this point in the history
* new regex transformer

* add typing
  • Loading branch information
clavedeluna authored Jul 22, 2024
1 parent 8db8518 commit 48a3e47
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 0 deletions.
63 changes: 63 additions & 0 deletions src/codemodder/codemods/regex_transformer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import re
from typing import Pattern

from codemodder.codemods.base_transformer import BaseTransformerPipeline
from codemodder.codetf import Change, ChangeSet
from codemodder.context import CodemodExecutionContext
from codemodder.diff import create_diff
from codemodder.file_context import FileContext
from codemodder.logging import logger
from codemodder.result import Result


class RegexTransformerPipeline(BaseTransformerPipeline):
pattern: Pattern
replacement: str
change_description: str

def __init__(self, pattern: Pattern, replacement: str, change_description: str):
super().__init__()
self.pattern = pattern
self.replacement = replacement
self.change_description = change_description

def apply(
self,
context: CodemodExecutionContext,
file_context: FileContext,
results: list[Result] | None,
) -> ChangeSet | None:

changes = []
updated_lines = []

with open(file_context.file_path, "r", encoding="utf-8") as f:
original_lines = f.readlines()

for lineno, line in enumerate(original_lines):
changed_line = re.sub(self.pattern, self.replacement, line)
updated_lines.append(changed_line)
if line != changed_line:
changes.append(
Change(
lineNumber=lineno,
description=self.change_description,
findings=file_context.get_findings_for_location(lineno),
)
)

if not changes:
logger.debug("No changes produced for %s", file_context.file_path)
return None

diff = create_diff(original_lines, updated_lines)

if not context.dry_run:
with open(file_context.file_path, "w+", encoding="utf-8") as original:
original.writelines(updated_lines)

return ChangeSet(
path=str(file_context.file_path.relative_to(context.directory)),
diff=diff,
changes=changes,
)
71 changes: 71 additions & 0 deletions tests/test_regex_transformer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import logging

from codemodder.codemods.regex_transformer import RegexTransformerPipeline
from codemodder.context import CodemodExecutionContext
from codemodder.file_context import FileContext


def test_transformer_no_change(mocker, caplog, tmp_path_factory):
caplog.set_level(logging.DEBUG)
base_dir = tmp_path_factory.mktemp("foo")
code = base_dir / "code.py"
code.write_text("# Something that won't match")

file_context = FileContext(
base_dir,
code,
)
execution_context = CodemodExecutionContext(
directory=base_dir,
dry_run=True,
verbose=False,
registry=mocker.MagicMock(),
providers=None,
repo_manager=mocker.MagicMock(),
path_include=[],
path_exclude=[],
)
pipeline = RegexTransformerPipeline(
pattern=r"hello", replacement="bye", change_description="testing"
)

changeset = pipeline.apply(
context=execution_context,
file_context=file_context,
results=None,
)
assert changeset is None
assert "No changes produced for" in caplog.text


def test_transformer(mocker, tmp_path_factory):
base_dir = tmp_path_factory.mktemp("foo")
code = base_dir / "code.py"
text = "# Something that will match pattern hello"
code.write_text(text)

file_context = FileContext(
base_dir,
code,
)
execution_context = CodemodExecutionContext(
directory=base_dir,
dry_run=False,
verbose=False,
registry=mocker.MagicMock(),
providers=None,
repo_manager=mocker.MagicMock(),
path_include=[],
path_exclude=[],
)
pipeline = RegexTransformerPipeline(
pattern=r"hello", replacement="bye", change_description="testing"
)

changeset = pipeline.apply(
context=execution_context,
file_context=file_context,
results=None,
)
assert changeset is not None
assert code.read_text() == text.replace("hello", "bye")

0 comments on commit 48a3e47

Please sign in to comment.