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
30 changes: 30 additions & 0 deletions fire/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,47 @@ def _LiteralEval(value):
for index, subchild in enumerate(child):
if isinstance(subchild, ast.Name):
child[index] = _Replacement(subchild)
elif isinstance(subchild, ast.BinOp):
str_val = _BinOpSubToStr(subchild)
if str_val is not None:
child[index] = _StrNode(str_val)

elif isinstance(child, ast.Name):
replacement = _Replacement(child)
setattr(node, field, replacement)
elif isinstance(child, ast.BinOp):
str_val = _BinOpSubToStr(child)
if str_val is not None:
setattr(node, field, _StrNode(str_val))

# ast.literal_eval supports the following types:
# strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and None
# (bytes and set literals only starting with Python 3.2)
return ast.literal_eval(root)


def _BinOpSubToStr(node):
"""Converts a BinOp subtraction chain of Name nodes to a hyphenated string.
Handles patterns like Name-Name and Name-Name-Name that appear when a
hyphenated string (e.g. 'foo-bar') is used inside a container literal
such as a tuple, list, set, or dict.
Args:
node: An AST node.
Returns:
The hyphenated string if node is a Sub chain of Names, else None.
"""
if isinstance(node, ast.Name):
return node.id
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Sub):
left = _BinOpSubToStr(node.left)
right = _BinOpSubToStr(node.right)
if left is not None and right is not None:
return '{left}-{right}'.format(left=left, right=right)
return None


def _Replacement(node):
"""Returns a node to use in place of the supplied node in the AST.
Expand Down
21 changes: 21 additions & 0 deletions fire/parser_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,26 @@ def testDefaultParseValueIgnoreBinOp(self):
self.assertEqual(parser.DefaultParseValue('2017-10-10'), '2017-10-10')
self.assertEqual(parser.DefaultParseValue('1+1'), '1+1')

def testDefaultParseValueHyphenatedStringsInTuple(self):
self.assertEqual(
parser.DefaultParseValue('(foo-bar,baz)'), ('foo-bar', 'baz'))
self.assertEqual(
parser.DefaultParseValue('(a-b-c,d)'), ('a-b-c', 'd'))
self.assertEqual(
parser.DefaultParseValue('(foo-bar,hello-world)'),
('foo-bar', 'hello-world'))

def testDefaultParseValueHyphenatedStringsInList(self):
self.assertEqual(
parser.DefaultParseValue('[foo-bar,baz]'), ['foo-bar', 'baz'])
self.assertEqual(
parser.DefaultParseValue('[a-b-c,d]'), ['a-b-c', 'd'])

def testDefaultParseValueHyphenatedStringsInDict(self):
self.assertEqual(
parser.DefaultParseValue('{foo-bar:baz}'), {'foo-bar': 'baz'})
self.assertEqual(
parser.DefaultParseValue('{key:foo-bar}'), {'key': 'foo-bar'})

if __name__ == '__main__':
testutils.main()