This is an automated email from the ASF dual-hosted git repository.

jrgemignani pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/age.git


The following commit(s) were added to refs/heads/master by this push:
     new 5a254d68 python driver: preserve trailing quotes in agtype string 
values (#2425)
5a254d68 is described below

commit 5a254d6869d8b2c271f025ea158c0fee2cfacfa3
Author: Sai Asish Y <[email protected]>
AuthorDate: Mon Jun 1 17:05:17 2026 -0700

    python driver: preserve trailing quotes in agtype string values (#2425)
    
    * python driver: preserve trailing quotes in agtype string values
    
    str.strip('"') in visitStringValue() and visitPair() removes every '"'
    on either side of the token, not just the outer delimiters, so a value
    ending in an escaped quote (e.g. '"foo \"bar\""') loses its trailing
    backslash-escaped '"' character.  The Agtype grammar guarantees STRING
    tokens always carry exactly one delimiter on each side, so slice with
    [1:-1] to strip them precisely.
    
    Fixes #2418
    
    Signed-off-by: SAY-5 <[email protected]>
    
    * fix(python-driver): centralize string delimiter trim and clean test 
docstring
    
    ---------
    
    Signed-off-by: SAY-5 <[email protected]>
---
 drivers/python/age/builder.py  | 11 +++++++++--
 drivers/python/test_agtypes.py | 16 ++++++++++++++++
 2 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/drivers/python/age/builder.py b/drivers/python/age/builder.py
index 08a40c25..b4f746e7 100644
--- a/drivers/python/age/builder.py
+++ b/drivers/python/age/builder.py
@@ -105,9 +105,16 @@ class ResultVisitor(AgtypeVisitor):
             return valueCtx.accept(self)
 
 
+    @staticmethod
+    def _stripStringDelimiters(stringToken):
+        # The STRING token always has surrounding '"' delimiters per the
+        # Agtype grammar; slice rather than strip('"') so escaped quotes
+        # at the boundaries are preserved.
+        return stringToken.getText()[1:-1]
+
     # Visit a parse tree produced by AgtypeParser#StringValue.
     def visitStringValue(self, ctx:AgtypeParser.StringValueContext):
-        return ctx.STRING().getText().strip('"')
+        return self._stripStringDelimiters(ctx.STRING())
 
 
     # Visit a parse tree produced by AgtypeParser#IntegerValue.
@@ -182,7 +189,7 @@ class ResultVisitor(AgtypeVisitor):
             raise AGTypeError(ctx.getText(), "Missing key in object pair")
         if agValNode is None:
             raise AGTypeError(ctx.getText(), "Missing value in object pair")
-        return (strNode.getText().strip('"') , agValNode)
+        return (self._stripStringDelimiters(strNode), agValNode)
 
 
     # Visit a parse tree produced by AgtypeParser#array.
diff --git a/drivers/python/test_agtypes.py b/drivers/python/test_agtypes.py
index 97f7972d..7de0f5f5 100644
--- a/drivers/python/test_agtypes.py
+++ b/drivers/python/test_agtypes.py
@@ -245,6 +245,22 @@ class TestAgtype(unittest.TestCase):
         self.assertEqual(result[4], [1, 2, 3])
         self.assertEqual(result[5], {"key": "val"})
 
+    def test_string_value_preserves_inner_quotes(self):
+        """Issue #2418: preserve escaped boundary quotes when stripping.
+
+        visitStringValue must strip only the outer delimiters; otherwise
+        values like '"foo \\"bar\\""' lose their trailing escaped quote.
+        """
+        self.assertEqual(self.parse('"foo \\"bar\\""'), 'foo \\"bar\\"')
+        self.assertEqual(self.parse('"\\"leading"'), '\\"leading')
+        self.assertEqual(self.parse('"trailing\\""'), 'trailing\\"')
+        self.assertEqual(self.parse('""'), '')
+        # Same fix applies to visitPair() for object keys.
+        self.assertEqual(
+            self.parse('{"key\\"q": 1}'),
+            {'key\\"q': 1},
+        )
+
     def test_malformed_vertex_raises_agtypeerror_or_recovers(self):
         """Issue #2367: Malformed agtype must raise AGTypeError or recover 
gracefully."""
         from age.exceptions import AGTypeError

Reply via email to