Move test_utils to util package

This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2026-02-24 11:29:06 +01:00
1 parent 1e89449f40
commit 15ab4e7747
8 files changed
+13 -74

No files matched your search

@@ -15,7 +15,7 @@ from sensai.util.string import ToStringMixin
import serena.jetbrains.jetbrains_types as jb
from serena.jetbrains.jetbrains_types import PluginStatusDTO
from serena.project import Project
from serena.text_utils import render_html
from serena.util.text_utils import render_html
from serena.util.version import Version
T = TypeVar("T")
+1 -1
View File
@@ -16,8 +16,8 @@ from serena.config.serena_config import (
)
from serena.constants import SERENA_FILE_ENCODING, SERENA_MANAGED_DIR_NAME
from serena.ls_manager import LanguageServerFactory, LanguageServerManager
from serena.text_utils import MatchedConsecutiveLines, search_files
from serena.util.file_system import GitignoreParser, match_path
from serena.util.text_utils import MatchedConsecutiveLines, search_files
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from solidlsp.ls_utils import FileUtils
+4 -65
View File
@@ -14,9 +14,10 @@ from fnmatch import fnmatch
from pathlib import Path
from typing import Literal
from serena.text_utils import search_files
from serena.tools import SUCCESS_RESULT, EditedFileContext, Tool, ToolMarkerCanEdit, ToolMarkerOptional
from serena.util.file_system import scan_directory
from serena.util.text_utils import search_files
from temp.temp_temp import ContentReplacer
class ReadFileTool(Tool):
@@ -203,46 +204,6 @@ class ReplaceContentTool(Tool, ToolMarkerCanEdit):
relative_path, needle, repl, mode=mode, allow_multiple_occurrences=allow_multiple_occurrences, require_not_ignored=True
)
@staticmethod
def _create_replacement_function(regex_pattern: str, repl_template: str, regex_flags: int) -> Callable[[re.Match], str]:
"""
Creates a replacement function that validates for ambiguity and handles backreferences.
:param regex_pattern: The regex pattern being used for matching
:param repl_template: The replacement template with $!1, $!2, etc. for backreferences
:param regex_flags: The flags to use when searching (e.g., re.DOTALL | re.MULTILINE)
:return: A function suitable for use with re.sub() or re.subn()
"""
def validate_and_replace(match: re.Match) -> str:
matched_text = match.group(0)
# For multi-line match, check if the same pattern matches again within the already-matched text,
# rendering the match ambiguous. Typical pattern in the code:
# <start><other-stuff><start><stuff><end>
# When matching
# <start>.*?<end>
# this will match the entire span above, while only the suffix may have been intended.
# (See test case for a practical example.)
# To detect this, we check if the same pattern matches again within the matched text,
if "\n" in matched_text and re.search(regex_pattern, matched_text[1:], flags=regex_flags):
raise ValueError(
"Match is ambiguous: the search pattern matches multiple overlapping occurrences. "
"Please revise the search pattern to be more specific to avoid ambiguity, "
"e.g. by matching specific context after the match, or try using the literal mode."
)
# Handle backreferences: replace $!1, $!2, etc. with actual matched groups
def expand_backreference(m: re.Match) -> str:
group_num = int(m.group(1))
group_value = match.group(group_num)
return group_value if group_value is not None else m.group(0)
result = re.sub(r"\$!(\d+)", expand_backreference, repl_template)
return result
return validate_and_replace
def replace_content(
self,
relative_path: str,
@@ -259,31 +220,9 @@ class ReplaceContentTool(Tool, ToolMarkerCanEdit):
self.project.validate_relative_path(relative_path, require_not_ignored=require_not_ignored)
with EditedFileContext(relative_path, self.create_code_editor()) as context:
original_content = context.get_original_content()
if mode == "literal":
regex = re.escape(needle)
elif mode == "regex":
regex = needle
else:
raise ValueError(f"Invalid mode: '{mode}', expected 'literal' or 'regex'.")
regex_flags = re.DOTALL | re.MULTILINE
# create replacement function with validation and backreference handling
repl_fn = self._create_replacement_function(regex, repl, regex_flags=regex_flags)
# perform replacement
updated_content, n = re.subn(regex, repl_fn, original_content, flags=regex_flags)
if n == 0:
raise ValueError(f"Error: No matches of search expression found in file '{relative_path}'.")
if not allow_multiple_occurrences and n > 1:
raise ValueError(
f"Expression matches {n} occurrences in file '{relative_path}'. "
"Please revise the expression to be more specific or enable allow_multiple_occurrences if this is expected."
)
replacer = ContentReplacer(mode=mode, allow_multiple_occurrences=allow_multiple_occurrences)
updated_content = replacer.replace(original_content, needle, repl)
context.set_updated_content(updated_content)
return SUCCESS_RESULT
@@ -123,7 +123,7 @@ def glob_to_regex(glob_pat: str) -> str:
if ch == "*":
regex_parts.append(".*")
elif ch == "?":
regex_parts.append(".")
regex_parts.append("..")
elif ch == "\\":
i += 1
if i < len(glob_pat):
+1 -1
View File
@@ -20,8 +20,8 @@ import pathspec
from sensai.util.pickle import getstate, load_pickle
from sensai.util.string import ToStringMixin
from serena.text_utils import MatchedConsecutiveLines
from serena.util.file_system import match_path
from serena.util.text_utils import MatchedConsecutiveLines
from solidlsp import ls_types
from solidlsp.ls_config import Language, LanguageServerConfig
from solidlsp.ls_exceptions import SolidLSPException
+3 -3
View File
@@ -2,7 +2,7 @@ import re
import pytest
from serena.text_utils import LineType, search_files, search_text
from serena.util.text_utils import LineType, search_files, search_text
class TestSearchText:
@@ -545,7 +545,7 @@ class TestGlobMatch:
)
def test_glob_match(self, pattern, path, expected):
"""Test glob_match function with various patterns."""
from src.serena.text_utils import glob_match
from serena.util.text_utils import glob_match
assert glob_match(pattern, path) == expected
@@ -572,6 +572,6 @@ class TestExpandBraces:
)
def test_expand_braces(self, pattern, expected):
"""Test brace expansion for glob patterns."""
from serena.text_utils import expand_braces
from serena.util.text_utils import expand_braces
assert sorted(expand_braces(pattern)) == sorted(expected)
+1 -1
View File
@@ -10,7 +10,7 @@ import os
import pytest
from serena.project import Project
from serena.text_utils import LineType
from serena.util.text_utils import LineType
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
+1 -1
View File
@@ -11,7 +11,7 @@ import platform
import pytest
from serena.project import Project
from serena.text_utils import LineType
from serena.util.text_utils import LineType
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.conftest import is_ci