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