mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-12 22:14:54 +00:00
Move test_utils to util package
This commit is contained in:
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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user