|
|
|
@ -196,3 +196,69 @@ def Exec(content, global_scope, local_scope, filename='<unknown>'):
|
|
|
|
|
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)
|
|
|
|
|