Add replace_in_file command (#4565)

Resubmission of #3643

---------

Co-authored-by: Reinier van der Leer <github@pwuts.nl>
This commit is contained in:
Benny van der Lans
2023-06-04 19:37:35 +02:00
committed by GitHub
parent 59d31b021d
commit 74e8a886e6
2 changed files with 110 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ from __future__ import annotations
import hashlib
import os
import os.path
import re
from typing import TYPE_CHECKING, Generator, Literal
import requests
@@ -224,6 +225,68 @@ def write_to_file(filename: str, text: str, config: Config) -> str:
return f"Error: {err}"
@command(
"replace_in_file",
"Replace text or code in a file",
'"filename": "<filename>", '
'"old_text": "<old_text>", "new_text": "<new_text>", '
'"occurrence_index": "<occurrence_index>"',
)
def replace_in_file(
filename: str, old_text: str, new_text: str, config: Config, occurrence_index=None
):
"""Update a file by replacing one or all occurrences of old_text with new_text using Python's built-in string
manipulation and regular expression modules for cross-platform file editing similar to sed and awk.
Args:
filename (str): The name of the file
old_text (str): String to be replaced. \n will be stripped from the end.
new_text (str): New string. \n will be stripped from the end.
occurrence_index (int): Optional index of the occurrence to replace. If None, all occurrences will be replaced.
Returns:
str: A message indicating whether the file was updated successfully or if there were no matches found for old_text
in the file.
Raises:
Exception: If there was an error updating the file.
"""
try:
with open(filename, "r", encoding="utf-8") as f:
content = f.read()
old_text = old_text.rstrip("\n")
new_text = new_text.rstrip("\n")
if occurrence_index is None:
new_content = content.replace(old_text, new_text)
else:
matches = list(re.finditer(re.escape(old_text), content))
if not matches:
return f"No matches found for {old_text} in {filename}"
if int(occurrence_index) >= len(matches):
return f"Occurrence index {occurrence_index} is out of range for {old_text} in {filename}"
match = matches[int(occurrence_index)]
start, end = match.start(), match.end()
new_content = content[:start] + new_text + content[end:]
if content == new_content:
return f"No matches found for {old_text} in {filename}"
with open(filename, "w", encoding="utf-8") as f:
f.write(new_content)
with open(filename, "r", encoding="utf-8") as f:
checksum = text_checksum(f.read())
log_operation("update", filename, config, checksum=checksum)
return f"File {filename} updated successfully."
except Exception as e:
return "Error: " + str(e)
@command(
"append_to_file", "Append to file", '"filename": "<filename>", "text": "<text>"'
)

View File

@@ -268,6 +268,53 @@ def test_write_file_succeeds_if_content_different(
assert result == "File written to successfully."
# Update file testing
def test_replace_in_file_all_occurrences(test_file, test_file_path, config):
old_content = "This is a test file.\n we test file here\na test is needed"
expected_content = (
"This is a update file.\n we update file here\na update is needed"
)
test_file.write(old_content)
test_file.close()
file_ops.replace_in_file(test_file_path, "test", "update", config)
with open(test_file_path) as f:
new_content = f.read()
print(new_content)
print(expected_content)
assert new_content == expected_content
def test_replace_in_file_one_occurrence(test_file, test_file_path, config):
old_content = "This is a test file.\n we test file here\na test is needed"
expected_content = "This is a test file.\n we update file here\na test is needed"
test_file.write(old_content)
test_file.close()
file_ops.replace_in_file(
test_file_path, "test", "update", config, occurrence_index=1
)
with open(test_file_path) as f:
new_content = f.read()
assert new_content == expected_content
def test_replace_in_file_multiline_old_text(test_file, test_file_path, config):
old_content = "This is a multi_line\ntest for testing\nhow well this function\nworks when the input\nis multi-lined"
expected_content = "This is a multi_line\nfile. succeeded test\nis multi-lined"
test_file.write(old_content)
test_file.close()
file_ops.replace_in_file(
test_file_path,
"\ntest for testing\nhow well this function\nworks when the input\n",
"\nfile. succeeded test\n",
config,
)
with open(test_file_path) as f:
new_content = f.read()
assert new_content == expected_content
def test_append_to_file(test_nested_file: Path, config):
append_text = "This is appended text.\n"
file_ops.write_to_file(test_nested_file, append_text, config)