Skip to content

Commit 5a254d6

Browse files
authored
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 <saiasish.cnp@gmail.com> * fix(python-driver): centralize string delimiter trim and clean test docstring --------- Signed-off-by: SAY-5 <saiasish.cnp@gmail.com>
1 parent 795a881 commit 5a254d6

2 files changed

Lines changed: 25 additions & 2 deletions

File tree

drivers/python/age/builder.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,16 @@ def visitAgValue(self, ctx:AgtypeParser.AgValueContext):
105105
return valueCtx.accept(self)
106106

107107

108+
@staticmethod
109+
def _stripStringDelimiters(stringToken):
110+
# The STRING token always has surrounding '"' delimiters per the
111+
# Agtype grammar; slice rather than strip('"') so escaped quotes
112+
# at the boundaries are preserved.
113+
return stringToken.getText()[1:-1]
114+
108115
# Visit a parse tree produced by AgtypeParser#StringValue.
109116
def visitStringValue(self, ctx:AgtypeParser.StringValueContext):
110-
return ctx.STRING().getText().strip('"')
117+
return self._stripStringDelimiters(ctx.STRING())
111118

112119

113120
# Visit a parse tree produced by AgtypeParser#IntegerValue.
@@ -182,7 +189,7 @@ def visitPair(self, ctx:AgtypeParser.PairContext):
182189
raise AGTypeError(ctx.getText(), "Missing key in object pair")
183190
if agValNode is None:
184191
raise AGTypeError(ctx.getText(), "Missing value in object pair")
185-
return (strNode.getText().strip('"') , agValNode)
192+
return (self._stripStringDelimiters(strNode), agValNode)
186193

187194

188195
# Visit a parse tree produced by AgtypeParser#array.

drivers/python/test_agtypes.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,22 @@ def test_array_of_mixed_types(self):
245245
self.assertEqual(result[4], [1, 2, 3])
246246
self.assertEqual(result[5], {"key": "val"})
247247

248+
def test_string_value_preserves_inner_quotes(self):
249+
"""Issue #2418: preserve escaped boundary quotes when stripping.
250+
251+
visitStringValue must strip only the outer delimiters; otherwise
252+
values like '"foo \\"bar\\""' lose their trailing escaped quote.
253+
"""
254+
self.assertEqual(self.parse('"foo \\"bar\\""'), 'foo \\"bar\\"')
255+
self.assertEqual(self.parse('"\\"leading"'), '\\"leading')
256+
self.assertEqual(self.parse('"trailing\\""'), 'trailing\\"')
257+
self.assertEqual(self.parse('""'), '')
258+
# Same fix applies to visitPair() for object keys.
259+
self.assertEqual(
260+
self.parse('{"key\\"q": 1}'),
261+
{'key\\"q': 1},
262+
)
263+
248264
def test_malformed_vertex_raises_agtypeerror_or_recovers(self):
249265
"""Issue #2367: Malformed agtype must raise AGTypeError or recover gracefully."""
250266
from age.exceptions import AGTypeError

0 commit comments

Comments
 (0)