gclient_eval: implement boolean expressions
Bug: 570091
Change-Id: I72326ee6ddd907a97b5c497a8b2cc7fb744cf422
Reviewed-on: https://chromium-review.googlesource.com/523142
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Commit-Queue: Paweł Hajdan Jr. <phajdan.jr@chromium.org>
diff --git a/gclient_eval.py b/gclient_eval.py
index a5d1dee..5066a2d 100644
--- a/gclient_eval.py
+++ b/gclient_eval.py
@@ -196,3 +196,69 @@
getattr(node_or_string, 'lineno', '<unknown>')))
_GCLIENT_SCHEMA.validate(local_scope)
+
+
+def EvaluateCondition(condition, variables, referenced_variables=None):
+ """Safely evaluates a boolean condition. Returns the result."""
+ if not referenced_variables:
+ referenced_variables = set()
+ _allowed_names = {'None': None, 'True': True, 'False': False}
+ main_node = ast.parse(condition, mode='eval')
+ if isinstance(main_node, ast.Expression):
+ main_node = main_node.body
+ def _convert(node):
+ if isinstance(node, ast.Str):
+ return node.s
+ elif isinstance(node, ast.Name):
+ if node.id in referenced_variables:
+ raise ValueError(
+ 'invalid cyclic reference to %r (inside %r)' % (
+ node.id, condition))
+ elif node.id in _allowed_names:
+ return _allowed_names[node.id]
+ elif node.id in variables:
+ return EvaluateCondition(
+ variables[node.id],
+ variables,
+ referenced_variables.union([node.id]))
+ else:
+ raise ValueError(
+ 'invalid name %r (inside %r)' % (node.id, condition))
+ elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.Or):
+ if len(node.values) != 2:
+ raise ValueError(
+ 'invalid "or": exactly 2 operands required (inside %r)' % (
+ condition))
+ return _convert(node.values[0]) or _convert(node.values[1])
+ elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.And):
+ if len(node.values) != 2:
+ raise ValueError(
+ 'invalid "and": exactly 2 operands required (inside %r)' % (
+ condition))
+ return _convert(node.values[0]) and _convert(node.values[1])
+ elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.Not):
+ return not _convert(node.operand)
+ elif isinstance(node, ast.Compare):
+ if len(node.ops) != 1:
+ raise ValueError(
+ 'invalid compare: exactly 1 operator required (inside %r)' % (
+ condition))
+ if len(node.comparators) != 1:
+ raise ValueError(
+ 'invalid compare: exactly 1 comparator required (inside %r)' % (
+ condition))
+
+ left = _convert(node.left)
+ right = _convert(node.comparators[0])
+
+ if isinstance(node.ops[0], ast.Eq):
+ return left == right
+
+ raise ValueError(
+ 'unexpected operator: %s %s (inside %r)' % (
+ node.ops[0], ast.dump(node), condition))
+ else:
+ raise ValueError(
+ 'unexpected AST node: %s %s (inside %r)' % (
+ node, ast.dump(node), condition))
+ return _convert(main_node)