From 2ef5a9b026425af2efcc231035eafd37305e35e0 Mon Sep 17 00:00:00 2001 From: Aritro Date: Mon, 21 Sep 2026 17:59:29 +0530 Subject: [PATCH] fix: preserve original line endings when patching files --- patchwork/steps/ModifyCode/ModifyCode.py | 12 ++++++++---- patchwork/steps/ModifyCode/README.md | 2 ++ tests/steps/test_ModifyCode.py | 9 +++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/patchwork/steps/ModifyCode/ModifyCode.py b/patchwork/steps/ModifyCode/ModifyCode.py index 0252431ef..435616044 100644 --- a/patchwork/steps/ModifyCode/ModifyCode.py +++ b/patchwork/steps/ModifyCode/ModifyCode.py @@ -3,19 +3,21 @@ import difflib from pathlib import Path +from patchwork.common.utils.utils import detect_newline from patchwork.logger import logger from patchwork.step import Step, StepStatus -def save_file_contents(file_path: str | Path, content: str) -> None: +def save_file_contents(file_path: str | Path, content: str, newline: str | None = None) -> None: """Utility function to save content to a file. Args: file_path: Path to the file to save content to (str or Path) content: Content to write to the file + newline: Line ending to write with, preserving the file's original style """ path = Path(file_path) - with path.open("w") as file: + with path.open("w", newline=newline) as file: file.write(content) @@ -59,7 +61,9 @@ def replace_code_in_file( if len(new_code_lines) > 0 and not new_code_lines[-1].endswith("\n"): new_code_lines[-1] += "\n" + newline = None if path.exists() and start_line is not None and end_line is not None: + newline = detect_newline(path) text = path.read_text() lines = text.splitlines(keepends=True) @@ -68,8 +72,8 @@ def replace_code_in_file( else: lines = new_code_lines - # Save the modified contents back to the file - save_file_contents(path, "".join(lines)) + # Save the modified contents back to the file, preserving its original line endings + save_file_contents(path, "".join(lines), newline=newline) class ModifyCode(Step): diff --git a/patchwork/steps/ModifyCode/README.md b/patchwork/steps/ModifyCode/README.md index ecc65a483..5aa897030 100644 --- a/patchwork/steps/ModifyCode/README.md +++ b/patchwork/steps/ModifyCode/README.md @@ -2,6 +2,8 @@ This module provides functionality to modify code files based on extracted responses. It contains functions for loading and saving JSON files, handling indentation, and replacing code in files. The main class `ModifyCode` is a step that takes inputs containing code snippets and extracted responses, and then modifies the specified lines in code files with the new extracted code. +When a file already exists, its original line-ending style (`\n`, `\r\n`, or `\r`) is detected and preserved on write, so patched files don't get their untouched lines flagged as changed in a diff just because of a line-ending mismatch. + ## Inputs - `file_path`: The path to the JSON file containing code snippets. - `content`: The content to be saved to a file. diff --git a/tests/steps/test_ModifyCode.py b/tests/steps/test_ModifyCode.py index a004ed271..c7258c948 100644 --- a/tests/steps/test_ModifyCode.py +++ b/tests/steps/test_ModifyCode.py @@ -33,6 +33,15 @@ def test_handle_indent(src, target, expected): assert indented_target == expected +def test_replace_code_in_file_preserves_crlf_line_endings(tmp_path): + file_path = tmp_path / "test.txt" + file_path.write_bytes(b"line 1\r\nline 2\r\nline 3\r\n") + + replace_code_in_file(str(file_path), 1, 2, "new line 1\n") + + assert file_path.read_bytes() == b"line 1\r\nnew line 1\r\nline 3\r\n" + + def test_replace_code_in_file(tmp_path): file_path = tmp_path / "test.txt" file_path.write_text("line 1\nline 2\nline 3")