Skip to content

Commit

Permalink
Fixed escaped quotes inside quoted and unquoted string. This should a…
Browse files Browse the repository at this point in the history
…ddress #96 and #138
  • Loading branch information
darthbear committed May 5, 2018
1 parent 15ced6b commit 1dd5e73
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 7 deletions.
6 changes: 3 additions & 3 deletions pyhocon/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def create_substitution(instring, loc, token):
return substitution

# ${path} or ${?path} for optional substitution
STRING_PATTERN = '(")(?P<value>[^"]*)\\1(?P<ws>[ \t]*)'
STRING_PATTERN = '"(?P<value>(?:[^"\\\\]|\\\\.)*)"(?P<ws>[ \t]*)'

def create_quoted_string(instring, loc, token):
# remove the ${ and }
Expand Down Expand Up @@ -240,13 +240,13 @@ def include_config(instring, loc, token):
# Using fix described in http://pyparsing.wikispaces.com/share/view/3778969
multiline_string = Regex('""".*?"*"""', re.DOTALL | re.UNICODE).setParseAction(parse_multi_string)
# single quoted line string
quoted_string = Regex('".*?"[ \t]*', re.UNICODE).setParseAction(create_quoted_string)
quoted_string = Regex('"(?:[^"\\\\\n]|\\\\.)*"[ \t]*', re.UNICODE).setParseAction(create_quoted_string)
# unquoted string that takes the rest of the line until an optional comment
# we support .properties multiline support which is like this:
# line1 \
# line2 \
# so a backslash precedes the \n
unquoted_string = Regex('(?:\\\\|[^\[\{\s\]\}#,=\$])+[ \t]*', re.UNICODE).setParseAction(unescape_string)
unquoted_string = Regex('(?:[^"\[\{\s\]\}#,=\$\\\\]|\\\\.)+[ \t]*', re.UNICODE).setParseAction(unescape_string)
substitution_expr = Regex('[ \t]*\$\{[^\}]+\}[ \t]*').setParseAction(create_substitution)
string_expr = multiline_string | quoted_string | unquoted_string

Expand Down
28 changes: 24 additions & 4 deletions tests/test_config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1132,14 +1132,15 @@ def test_include_required_file(self):
}
"""
)
assert {
expected = {
'a': {
'garfield': {
'say': 'meow'
},
't': 2
}
} == config
}
assert expected == config

def test_include_missing_required_file(self):
with pytest.raises(IOError):
Expand Down Expand Up @@ -2040,9 +2041,10 @@ def test_pop(self):
config_tree = ConfigFactory.parse_string('a:{b: 3, d: 6}')
assert 3 == config_tree.pop('a.b', 5)
assert 5 == config_tree.pop('a.c', 5)
assert {
expected = {
'a': {'d': 6}
} == config_tree
}
assert expected == config_tree

def test_merge_overriden(self):
# Adress issue #110
Expand All @@ -2065,3 +2067,21 @@ def test_attr_syntax(self):
}
""")
assert 5 == config.b.pb

def test_escape_quote(self):
config = ConfigFactory.parse_string(
"""
quoted: "abc\\"test"
unquoted: abc\\"test
""")
assert 'abc"test' == config['quoted']
assert 'abc"test' == config['unquoted']

def test_escape_quote_complex(self):
config = ConfigFactory.parse_string(
"""
value: "{\\"critical\\":\\"0.00\\",\\"warning\\":\\"99.99\\"}"
"""
)

assert '{"critical":"0.00","warning":"99.99"}' == config['value']

0 comments on commit 1dd5e73

Please sign in to comment.