Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Next Release (TBD)
==================

* No changes yet.
* Decode escaped backslashes in raw string literals according to the language specification.


1.1.0
Expand Down
14 changes: 12 additions & 2 deletions jmespath/lexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,19 @@ def _consume_quoted_identifier(self):

def _consume_raw_string_literal(self):
start = self._position
lexeme = self._consume_until("'").replace("\\'", "'")
lexeme = self._consume_until("'")
raw_string = []
i = 0
while i < len(lexeme):
char = lexeme[i]
if (char == '\\' and i + 1 < len(lexeme) and
lexeme[i + 1] in ("\\", "'")):
i += 1
char = lexeme[i]
raw_string.append(char)
i += 1
token_len = self._position - start
return {'type': 'literal', 'value': lexeme,
return {'type': 'literal', 'value': ''.join(raw_string),
'start': start, 'end': token_len}

def _match_or_else(self, expected, match_type, else_type):
Expand Down
4 changes: 2 additions & 2 deletions tests/compliance/literal.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,9 @@
"result": "\\z"
},
{
"comment": "Backslash not followed by single quote is treated as any other character",
"comment": "Backslash escapes a backslash in a raw string",
"expression": "'\\\\'",
"result": "\\\\"
"result": "\\"
}
]
}
Expand Down
23 changes: 23 additions & 0 deletions tests/test_raw_string_escapes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from tests import unittest

import jmespath
from jmespath.lexer import Lexer


class TestRawStringEscapes(unittest.TestCase):

def test_escaped_backslash_is_unescaped(self):
tokens = list(Lexer().tokenize(r"'\\'"))
self.assertEqual(tokens[0]['value'], '\\')
self.assertEqual(jmespath.search(r"'\\'", {}), '\\')

def test_backslash_before_other_character_is_preserved(self):
self.assertEqual(jmespath.search(r"'\z'", {}), r'\z')

def test_escaped_quote_is_unescaped(self):
self.assertEqual(jmespath.search(r"'foo\'bar'", {}), "foo'bar")

def test_windows_path_filter_with_trailing_backslash(self):
rows = [{'FullName': r'C:\x\msvc\file.txt'}]
expression = r"[? !contains(FullName, '\msvc\\')]"
self.assertEqual(jmespath.search(expression, rows), [])