From 25a0c3f0d5fe8f5ec56969ca691d7f9a1a001054 Mon Sep 17 00:00:00 2001 From: Cedric Conday Date: Tue, 30 Jun 2026 03:42:59 +0000 Subject: [PATCH] Preserve a trailing unterminated escape sequence in unescape() (#84) When a field ended in the middle of an escape sequence (e.g. a trailing lone escape character as in '\E\R\'), the loop left the opening escape character and any collected characters unflushed, so they were silently dropped: unescape('\E\R\') returned '\R' instead of '\R\'. Flush the unterminated sequence literally after the loop, since it cannot be decoded. Closes #84. --- hl7/util.py | 7 +++++++ tests/test_parse.py | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/hl7/util.py b/hl7/util.py index 86aac2c..ef5ca31 100644 --- a/hl7/util.py +++ b/hl7/util.py @@ -258,4 +258,11 @@ def unescape(container, field, app_map=None): # noqa: C901 else: rv.append(str(c)) + if in_seq: + # The field ended in the middle of an escape sequence (e.g. a trailing + # lone escape character). It cannot be decoded, so preserve the + # original characters literally rather than dropping them. + rv.append(container.esc) + rv.extend(collecting) + return "".join(rv) diff --git a/tests/test_parse.py b/tests/test_parse.py index 39cc786..0e1c244 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -376,6 +376,10 @@ def test_unescape(self): self.assertEqual(msg.unescape("\\X20202020\\"), " ") self.assertEqual(msg.unescape("\\Xe1\\\\Xe9\\\\Xed\\\\Xf3\\\\Xfa\\"), "áéíóú") + # Trailing unterminated escape sequence is preserved literally (#84) + self.assertEqual(msg.unescape("\\E\\R\\"), "\\R\\") + self.assertEqual(msg.unescape("text\\"), "text\\") + def test_escape(self): msg = hl7.parse(rep_sample_hl7)