Skip to content

Commit 128a14e

Browse files
committed
feat: can use escaped tokens inside quotes including quotes.
Change tokenizer to support: cmd with arg "string with embedded \" double quote" works for single quotes too. Mixed quotes can skip the \" or \' escaping. Also: quoted cmds args "can include \n newline, \t tab and \r return" Added a doc example, also tests for new feature.
1 parent aafa001 commit 128a14e

File tree

3 files changed

+33
-0
lines changed

3 files changed

+33
-0
lines changed

roundup/admin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ def help_all(self):
297297
address="1 2 3" (1 token: address=1 2 3)
298298
\\\\ (1 token: \\)
299299
\\n\\r\\t (1 token: a newline, carriage-return and tab)
300+
f "test\\"q" (2 tokens: f test"q)
300301
301302
When multiple nodes are specified to the roundup get or roundup set
302303
commands, the specified properties are retrieved or set on all the listed

roundup/token_r.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ def token_split(s, whitespace=' \r\n\t', quotes='\'"',
9696
oldstate = state
9797
state = ESCAPE
9898
continue
99+
elif state == QUOTE and c == '\\':
100+
# in a quoted token and found an escape sequence
101+
pos = pos + 1
102+
oldstate = state
103+
state = ESCAPE
104+
continue
99105
elif state == QUOTE and c == quotechar:
100106
# in a quoted token and found a matching quote char
101107
pos = pos + 1

test/test_token.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,28 @@ def testEmbedQuote(self):
3333
l = token_split('address="1 2 3"')
3434
self.assertEqual(l, ['address=1 2 3'])
3535

36+
def testEmbedEscapeQuote(self):
37+
l = token_split(r'"Roch\'e Compaan"')
38+
self.assertEqual(l, ["Roch'e Compaan"])
39+
40+
l = token_split(r'"Roch\"e Compaan"')
41+
self.assertEqual(l, ['Roch"e Compaan'])
42+
43+
l = token_split(r'sql "COLLATE = \"utf8mb4_unicode_ci\";"')
44+
self.assertEqual(l, ["sql", 'COLLATE = "utf8mb4_unicode_ci";'])
45+
46+
l = token_split(r'''sql 'COLLATE = "utf8mb4_unicode_ci";' ''')
47+
self.assertEqual(l, ["sql", 'COLLATE = "utf8mb4_unicode_ci";'])
48+
49+
l = token_split(r'''sql 'COLLATE = \"utf8mb4_unicode_ci\";' ''')
50+
self.assertEqual(l, ["sql", 'COLLATE = "utf8mb4_unicode_ci";'])
51+
52+
l = token_split(r'''sql 'COLLATE = \'utf8mb4_unicode_ci\';' ''')
53+
self.assertEqual(l, ["sql", "COLLATE = 'utf8mb4_unicode_ci';"])
54+
55+
l = token_split(r'''sql 'new\nline\rneed \ttab' ''')
56+
self.assertEqual(l, ["sql", "new\nline\rneed \ttab"])
57+
3658
def testEscaping(self):
3759
l = token_split('"Roch\'e" Compaan')
3860
self.assertEqual(l, ["Roch'e", "Compaan"])
@@ -42,6 +64,10 @@ def testEscaping(self):
4264
self.assertEqual(l, ['\\'])
4365
l = token_split(r'\n')
4466
self.assertEqual(l, ['\n'])
67+
l = token_split(r'\r')
68+
self.assertEqual(l, ['\r'])
69+
l = token_split(r'\t')
70+
self.assertEqual(l, ['\t'])
4571

4672
def testBadQuote(self):
4773
self.assertRaises(ValueError, token_split, '"hello world')

0 commit comments

Comments
 (0)