From cdd0b400ccd1ea3989a5bed6692d13dddbbcc1ef Mon Sep 17 00:00:00 2001 From: Count Infinity Date: Tue, 16 Dec 2025 01:25:49 -0700 Subject: [PATCH] Normalize CRLF to LF --- evennia/utils/batchprocessors.py | 3 +++ evennia/utils/tests/test_batchprocessors.py | 28 +++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/evennia/utils/batchprocessors.py b/evennia/utils/batchprocessors.py index d59ce339a3..15f28382f6 100644 --- a/evennia/utils/batchprocessors.py +++ b/evennia/utils/batchprocessors.py @@ -235,6 +235,9 @@ def read_batchfile(pythonpath, file_ending=".py"): if not text and decoderr: raise UnicodeDecodeError("\n".join(decoderr), bytearray(), 0, 0, "") + if text: + text = text.replace("\r\n", "\n").replace("\r", "\n") + return text diff --git a/evennia/utils/tests/test_batchprocessors.py b/evennia/utils/tests/test_batchprocessors.py index a55a2949b8..9bb0812e0d 100644 --- a/evennia/utils/tests/test_batchprocessors.py +++ b/evennia/utils/tests/test_batchprocessors.py @@ -93,6 +93,34 @@ class TestBatchCommandProcessor(TestCase): [mock.call("foopath", file_ending=".ev"), mock.call("x", file_ending=".ev")], ) +class TestReadBatchFile(TestCase): + """Test read_batchfile line ending normalization.""" + + @mock.patch.object(utils, "pypath_to_realpath", return_value=["testpath"]) + @mock.patch.object(codecs, "open") + def test_normalizes_crlf_to_lf(self, mocked_open, _): + """Test that CRLF line endings are normalized to LF. + + See: https://github.com/evennia/evennia/issues/3847 + """ + mocked_file = mock.MagicMock() + mocked_file.read.return_value = "@create sky\r\n#INSERT another.ev\r\n" + mocked_open.return_value.__enter__.return_value = mocked_file + + result = batchprocessors.read_batchfile("foopath") + self.assertEqual(result, "@create sky\n#INSERT another.ev\n") + + @mock.patch.object(utils, "pypath_to_realpath", return_value=["testpath"]) + @mock.patch.object(codecs, "open") + def test_normalizes_cr_to_lf(self, mocked_open, _): + """Test that old Mac CR line endings are normalized to LF.""" + mocked_file = mock.MagicMock() + mocked_file.read.return_value = "@create sky\r#INSERT another.ev\r" + mocked_open.return_value.__enter__.return_value = mocked_file + + result = batchprocessors.read_batchfile("foopath") + self.assertEqual(result, "@create sky\n#INSERT another.ev\n") + class TestBatchCodeProcessor(TestCase): @mock.patch.object(batchprocessors, "read_batchfile")