Commit 9f531298 authored by Edward Lesmes's avatar Edward Lesmes Committed by Commit Bot

gclient eval: Get rid of global_scope, local_scope and functions other than Var.

Since we only support Var, and Var only does one thing, we hard code that
into the parser. Then, we don't need global_scope.

local_scope hasn't been needed for a while.

Bug: 760633
Change-Id: I21b171a8b71e7dcaf8e29c780ca88b9f46f368e8
Reviewed-on: https://chromium-review.googlesource.com/972611Reviewed-by: 's avatarAaron Gable <agable@chromium.org>
Commit-Queue: Edward Lesmes <ehmaldonado@chromium.org>
parent b3065fb6
...@@ -765,16 +765,14 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): ...@@ -765,16 +765,14 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
local_scope = {} local_scope = {}
if deps_content: if deps_content:
global_scope = {
'Var': lambda var_name: '{%s}' % var_name,
'deps_os': {},
}
# Eval the content. # Eval the content.
try: try:
if self._get_option('validate_syntax', False): if self._get_option('validate_syntax', False):
local_scope = gclient_eval.Exec( local_scope = gclient_eval.Exec(deps_content, filepath)
deps_content, global_scope, local_scope, filepath)
else: else:
global_scope = {
'Var': lambda var_name: '{%s}' % var_name,
}
exec(deps_content, global_scope, local_scope) exec(deps_content, global_scope, local_scope)
except SyntaxError as e: except SyntaxError as e:
gclient_utils.SyntaxErrorToError(filepath, e) gclient_utils.SyntaxErrorToError(filepath, e)
...@@ -2880,14 +2878,12 @@ def CMDsetdep(parser, args): ...@@ -2880,14 +2878,12 @@ def CMDsetdep(parser, args):
'file in the current directory.') 'file in the current directory.')
(options, args) = parser.parse_args(args) (options, args) = parser.parse_args(args)
global_scope = {'Var': lambda var: '{%s}' % var}
if not os.path.isfile(options.deps_file): if not os.path.isfile(options.deps_file):
raise gclient_utils.Error( raise gclient_utils.Error(
'DEPS file %s does not exist.' % options.deps_file) 'DEPS file %s does not exist.' % options.deps_file)
with open(options.deps_file) as f: with open(options.deps_file) as f:
contents = f.read() contents = f.read()
local_scope = gclient_eval.Exec(contents, global_scope, {}) local_scope = gclient_eval.Exec(contents)
for var in options.vars: for var in options.vars:
name, _, value = var.partition('=') name, _, value = var.partition('=')
...@@ -2909,7 +2905,7 @@ def CMDsetdep(parser, args): ...@@ -2909,7 +2905,7 @@ def CMDsetdep(parser, args):
% (name, package)) % (name, package))
gclient_eval.SetCIPD(local_scope, name, package, value) gclient_eval.SetCIPD(local_scope, name, package, value)
else: else:
gclient_eval.SetRevision(local_scope, global_scope, name, value) gclient_eval.SetRevision(local_scope, name, value)
with open(options.deps_file, 'w') as f: with open(options.deps_file, 'w') as f:
f.write(gclient_eval.RenderDEPSFile(local_scope)) f.write(gclient_eval.RenderDEPSFile(local_scope))
......
...@@ -183,7 +183,7 @@ _GCLIENT_SCHEMA = schema.Schema(_NodeDictSchema({ ...@@ -183,7 +183,7 @@ _GCLIENT_SCHEMA = schema.Schema(_NodeDictSchema({
})) }))
def _gclient_eval(node_or_string, global_scope, filename='<unknown>'): def _gclient_eval(node_or_string, filename='<unknown>'):
"""Safely evaluates a single expression. Returns the result.""" """Safely evaluates a single expression. Returns the result."""
_allowed_names = {'None': None, 'True': True, 'False': False} _allowed_names = {'None': None, 'True': True, 'False': False}
if isinstance(node_or_string, basestring): if isinstance(node_or_string, basestring):
...@@ -209,16 +209,20 @@ def _gclient_eval(node_or_string, global_scope, filename='<unknown>'): ...@@ -209,16 +209,20 @@ def _gclient_eval(node_or_string, global_scope, filename='<unknown>'):
node.id, filename, getattr(node, 'lineno', '<unknown>'))) node.id, filename, getattr(node, 'lineno', '<unknown>')))
return _allowed_names[node.id] return _allowed_names[node.id]
elif isinstance(node, ast.Call): elif isinstance(node, ast.Call):
if not isinstance(node.func, ast.Name): if not isinstance(node.func, ast.Name) or node.func.id != 'Var':
raise ValueError( raise ValueError(
'invalid call: func should be a name (file %r, line %s)' % ( 'Var is the only allowed function (file %r, line %s)' % (
filename, getattr(node, 'lineno', '<unknown>'))) filename, getattr(node, 'lineno', '<unknown>')))
if node.keywords or node.starargs or node.kwargs: if node.keywords or node.starargs or node.kwargs or len(node.args) != 1:
raise ValueError( raise ValueError(
'invalid call: use only regular args (file %r, line %s)' % ( 'Var takes exactly one argument (file %r, line %s)' % (
filename, getattr(node, 'lineno', '<unknown>'))) filename, getattr(node, 'lineno', '<unknown>')))
args = map(_convert, node.args) arg = _convert(node.args[0])
return global_scope[node.func.id](*args) if not isinstance(arg, basestring):
raise ValueError(
'Var\'s argument must be a variable name (file %r, line %s)' % (
filename, getattr(node, 'lineno', '<unknown>')))
return '{%s}' % arg
elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add): elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add):
return _convert(node.left) + _convert(node.right) return _convert(node.left) + _convert(node.right)
elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.Mod): elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.Mod):
...@@ -231,7 +235,7 @@ def _gclient_eval(node_or_string, global_scope, filename='<unknown>'): ...@@ -231,7 +235,7 @@ def _gclient_eval(node_or_string, global_scope, filename='<unknown>'):
return _convert(node_or_string) return _convert(node_or_string)
def Exec(content, global_scope, local_scope, filename='<unknown>'): def Exec(content, filename='<unknown>'):
"""Safely execs a set of assignments. Mutates |local_scope|.""" """Safely execs a set of assignments. Mutates |local_scope|."""
node_or_string = ast.parse(content, filename=filename, mode='exec') node_or_string = ast.parse(content, filename=filename, mode='exec')
if isinstance(node_or_string, ast.Expression): if isinstance(node_or_string, ast.Expression):
...@@ -249,7 +253,7 @@ def Exec(content, global_scope, local_scope, filename='<unknown>'): ...@@ -249,7 +253,7 @@ def Exec(content, global_scope, local_scope, filename='<unknown>'):
raise ValueError( raise ValueError(
'invalid assignment: target should be a name (file %r, line %s)' % ( 'invalid assignment: target should be a name (file %r, line %s)' % (
filename, getattr(node, 'lineno', '<unknown>'))) filename, getattr(node, 'lineno', '<unknown>')))
value = _gclient_eval(node.value, global_scope, filename=filename) value = _gclient_eval(node.value, filename=filename)
if target.id in defined_variables: if target.id in defined_variables:
raise ValueError( raise ValueError(
...@@ -449,7 +453,7 @@ def SetCIPD(gclient_dict, dep_name, package_name, new_version): ...@@ -449,7 +453,7 @@ def SetCIPD(gclient_dict, dep_name, package_name, new_version):
packages[0]._SetNode('version', new_version, node) packages[0]._SetNode('version', new_version, node)
def SetRevision(gclient_dict, global_scope, dep_name, new_revision): def SetRevision(gclient_dict, dep_name, new_revision):
if not isinstance(gclient_dict, _NodeDict) or gclient_dict.tokens is None: if not isinstance(gclient_dict, _NodeDict) or gclient_dict.tokens is None:
raise ValueError( raise ValueError(
"Can't use SetRevision for the given gclient dict. It contains no " "Can't use SetRevision for the given gclient dict. It contains no "
...@@ -473,7 +477,7 @@ def SetRevision(gclient_dict, global_scope, dep_name, new_revision): ...@@ -473,7 +477,7 @@ def SetRevision(gclient_dict, global_scope, dep_name, new_revision):
SetVar(gclient_dict, node.args[0].s, new_revision) SetVar(gclient_dict, node.args[0].s, new_revision)
else: else:
_UpdateAstString(tokens, node, new_revision) _UpdateAstString(tokens, node, new_revision)
value = _gclient_eval(dep_node, global_scope) value = _gclient_eval(dep_node)
dep_dict._SetNode(dep_key, value, dep_node) dep_dict._SetNode(dep_key, value, dep_node)
if isinstance(gclient_dict['deps'][dep_name], _NodeDict): if isinstance(gclient_dict['deps'][dep_name], _NodeDict):
......
...@@ -60,44 +60,44 @@ vars = { ...@@ -60,44 +60,44 @@ vars = {
class GClientEvalTest(unittest.TestCase): class GClientEvalTest(unittest.TestCase):
def test_str(self): def test_str(self):
self.assertEqual('foo', gclient_eval._gclient_eval('"foo"', {})) self.assertEqual('foo', gclient_eval._gclient_eval('"foo"'))
def test_tuple(self): def test_tuple(self):
self.assertEqual(('a', 'b'), gclient_eval._gclient_eval('("a", "b")', {})) self.assertEqual(('a', 'b'), gclient_eval._gclient_eval('("a", "b")'))
def test_list(self): def test_list(self):
self.assertEqual(['a', 'b'], gclient_eval._gclient_eval('["a", "b"]', {})) self.assertEqual(['a', 'b'], gclient_eval._gclient_eval('["a", "b"]'))
def test_dict(self): def test_dict(self):
self.assertEqual({'a': 'b'}, gclient_eval._gclient_eval('{"a": "b"}', {})) self.assertEqual({'a': 'b'}, gclient_eval._gclient_eval('{"a": "b"}'))
def test_name_safe(self): def test_name_safe(self):
self.assertEqual(True, gclient_eval._gclient_eval('True', {})) self.assertEqual(True, gclient_eval._gclient_eval('True'))
def test_name_unsafe(self): def test_name_unsafe(self):
with self.assertRaises(ValueError) as cm: with self.assertRaises(ValueError) as cm:
gclient_eval._gclient_eval('UnsafeName', {'UnsafeName': 'foo'}) gclient_eval._gclient_eval('UnsafeName')
self.assertIn('invalid name \'UnsafeName\'', str(cm.exception)) self.assertIn('invalid name \'UnsafeName\'', str(cm.exception))
def test_call(self): def test_call(self):
self.assertEqual( self.assertEqual(
'bar', '{bar}',
gclient_eval._gclient_eval('Foo("bar")', {'Foo': lambda x: x})) gclient_eval._gclient_eval('Var("bar")'))
def test_plus(self): def test_plus(self):
self.assertEqual('foo', gclient_eval._gclient_eval('"f" + "o" + "o"', {})) self.assertEqual('foo', gclient_eval._gclient_eval('"f" + "o" + "o"'))
def test_format(self): def test_format(self):
self.assertEqual('foo', gclient_eval._gclient_eval('"%s" % "foo"', {})) self.assertEqual('foo', gclient_eval._gclient_eval('"%s" % "foo"'))
def test_not_expression(self): def test_not_expression(self):
with self.assertRaises(SyntaxError) as cm: with self.assertRaises(SyntaxError) as cm:
gclient_eval._gclient_eval('def foo():\n pass', {}) gclient_eval._gclient_eval('def foo():\n pass')
self.assertIn('invalid syntax', str(cm.exception)) self.assertIn('invalid syntax', str(cm.exception))
def test_not_whitelisted(self): def test_not_whitelisted(self):
with self.assertRaises(ValueError) as cm: with self.assertRaises(ValueError) as cm:
gclient_eval._gclient_eval('[x for x in [1, 2, 3]]', {}) gclient_eval._gclient_eval('[x for x in [1, 2, 3]]')
self.assertIn( self.assertIn(
'unexpected AST node: <_ast.ListComp object', str(cm.exception)) 'unexpected AST node: <_ast.ListComp object', str(cm.exception))
...@@ -105,42 +105,36 @@ class GClientEvalTest(unittest.TestCase): ...@@ -105,42 +105,36 @@ class GClientEvalTest(unittest.TestCase):
for test_case in itertools.permutations(range(4)): for test_case in itertools.permutations(range(4)):
input_data = ['{'] + ['"%s": "%s",' % (n, n) for n in test_case] + ['}'] input_data = ['{'] + ['"%s": "%s",' % (n, n) for n in test_case] + ['}']
expected = [(str(n), str(n)) for n in test_case] expected = [(str(n), str(n)) for n in test_case]
result = gclient_eval._gclient_eval(''.join(input_data), {}) result = gclient_eval._gclient_eval(''.join(input_data))
self.assertEqual(expected, result.items()) self.assertEqual(expected, result.items())
class ExecTest(unittest.TestCase): class ExecTest(unittest.TestCase):
def test_multiple_assignment(self): def test_multiple_assignment(self):
with self.assertRaises(ValueError) as cm: with self.assertRaises(ValueError) as cm:
gclient_eval.Exec('a, b, c = "a", "b", "c"', {}, {}) gclient_eval.Exec('a, b, c = "a", "b", "c"')
self.assertIn( self.assertIn(
'invalid assignment: target should be a name', str(cm.exception)) 'invalid assignment: target should be a name', str(cm.exception))
def test_override(self): def test_override(self):
with self.assertRaises(ValueError) as cm: with self.assertRaises(ValueError) as cm:
gclient_eval.Exec('a = "a"\na = "x"', {}, {}) gclient_eval.Exec('a = "a"\na = "x"')
self.assertIn( self.assertIn(
'invalid assignment: overrides var \'a\'', str(cm.exception)) 'invalid assignment: overrides var \'a\'', str(cm.exception))
def test_schema_wrong_type(self): def test_schema_wrong_type(self):
with self.assertRaises(schema.SchemaError): with self.assertRaises(schema.SchemaError):
gclient_eval.Exec('include_rules = {}', {}, {}, '<string>') gclient_eval.Exec('include_rules = {}', '<string>')
def test_recursedeps_list(self): def test_recursedeps_list(self):
local_scope = {}
local_scope = gclient_eval.Exec( local_scope = gclient_eval.Exec(
'recursedeps = [["src/third_party/angle", "DEPS.chromium"]]', 'recursedeps = [["src/third_party/angle", "DEPS.chromium"]]',
{}, local_scope,
'<string>') '<string>')
self.assertEqual( self.assertEqual(
{'recursedeps': [['src/third_party/angle', 'DEPS.chromium']]}, {'recursedeps': [['src/third_party/angle', 'DEPS.chromium']]},
local_scope) local_scope)
def test_var(self): def test_var(self):
local_scope = {}
global_scope = {
'Var': lambda var_name: '{%s}' % var_name,
}
local_scope = gclient_eval.Exec('\n'.join([ local_scope = gclient_eval.Exec('\n'.join([
'vars = {', 'vars = {',
' "foo": "bar",', ' "foo": "bar",',
...@@ -148,14 +142,14 @@ class ExecTest(unittest.TestCase): ...@@ -148,14 +142,14 @@ class ExecTest(unittest.TestCase):
'deps = {', 'deps = {',
' "a_dep": "a" + Var("foo") + "b",', ' "a_dep": "a" + Var("foo") + "b",',
'}', '}',
]), global_scope, local_scope, '<string>') ]), '<string>')
self.assertEqual({ self.assertEqual({
'vars': collections.OrderedDict([('foo', 'bar')]), 'vars': collections.OrderedDict([('foo', 'bar')]),
'deps': collections.OrderedDict([('a_dep', 'a{foo}b')]), 'deps': collections.OrderedDict([('a_dep', 'a{foo}b')]),
}, local_scope) }, local_scope)
def test_empty_deps(self): def test_empty_deps(self):
local_scope = gclient_eval.Exec('deps = {}', {}, {}, '<string>') local_scope = gclient_eval.Exec('deps = {}', '<string>')
self.assertEqual({'deps': {}}, local_scope) self.assertEqual({'deps': {}}, local_scope)
...@@ -211,7 +205,7 @@ class EvaluateConditionTest(unittest.TestCase): ...@@ -211,7 +205,7 @@ class EvaluateConditionTest(unittest.TestCase):
class SetVarTest(unittest.TestCase): class SetVarTest(unittest.TestCase):
def testSetVar(self): def testSetVar(self):
local_scope = gclient_eval.Exec(_SAMPLE_DEPS_FILE, {'Var': str}, {}) local_scope = gclient_eval.Exec(_SAMPLE_DEPS_FILE)
gclient_eval.SetVar(local_scope, 'dep_2_rev', 'c0ffee') gclient_eval.SetVar(local_scope, 'dep_2_rev', 'c0ffee')
result = gclient_eval.RenderDEPSFile(local_scope) result = gclient_eval.RenderDEPSFile(local_scope)
...@@ -223,7 +217,7 @@ class SetVarTest(unittest.TestCase): ...@@ -223,7 +217,7 @@ class SetVarTest(unittest.TestCase):
class SetCipdTest(unittest.TestCase): class SetCipdTest(unittest.TestCase):
def testSetCIPD(self): def testSetCIPD(self):
local_scope = gclient_eval.Exec(_SAMPLE_DEPS_FILE, {'Var': str}, {}) local_scope = gclient_eval.Exec(_SAMPLE_DEPS_FILE)
gclient_eval.SetCIPD( gclient_eval.SetCIPD(
local_scope, 'src/cipd/package', 'another/cipd/package', '6.789') local_scope, 'src/cipd/package', 'another/cipd/package', '6.789')
...@@ -234,27 +228,25 @@ class SetCipdTest(unittest.TestCase): ...@@ -234,27 +228,25 @@ class SetCipdTest(unittest.TestCase):
class SetRevisionTest(unittest.TestCase): class SetRevisionTest(unittest.TestCase):
def setUp(self): def setUp(self):
self.global_scope = {'Var': str} self.local_scope = gclient_eval.Exec(_SAMPLE_DEPS_FILE)
self.local_scope = gclient_eval.Exec(
_SAMPLE_DEPS_FILE, self.global_scope, {})
def testSetRevision(self): def testSetRevision(self):
gclient_eval.SetRevision( gclient_eval.SetRevision(
self.local_scope, self.global_scope, 'src/dep', 'deadfeed') self.local_scope, 'src/dep', 'deadfeed')
result = gclient_eval.RenderDEPSFile(self.local_scope) result = gclient_eval.RenderDEPSFile(self.local_scope)
self.assertEqual(result, _SAMPLE_DEPS_FILE.replace('deadbeef', 'deadfeed')) self.assertEqual(result, _SAMPLE_DEPS_FILE.replace('deadbeef', 'deadfeed'))
def testSetRevisionInUrl(self): def testSetRevisionInUrl(self):
gclient_eval.SetRevision( gclient_eval.SetRevision(
self.local_scope, self.global_scope, 'src/dep_3', '0ff1ce') self.local_scope, 'src/dep_3', '0ff1ce')
result = gclient_eval.RenderDEPSFile(self.local_scope) result = gclient_eval.RenderDEPSFile(self.local_scope)
self.assertEqual(result, _SAMPLE_DEPS_FILE.replace('5p1e5', '0ff1ce')) self.assertEqual(result, _SAMPLE_DEPS_FILE.replace('5p1e5', '0ff1ce'))
def testSetRevisionInVars(self): def testSetRevisionInVars(self):
gclient_eval.SetRevision( gclient_eval.SetRevision(
self.local_scope, self.global_scope, 'src/android/dep_2', 'c0ffee') self.local_scope, 'src/android/dep_2', 'c0ffee')
result = gclient_eval.RenderDEPSFile(self.local_scope) result = gclient_eval.RenderDEPSFile(self.local_scope)
self.assertEqual(result, _SAMPLE_DEPS_FILE.replace('1ced', 'c0ffee')) self.assertEqual(result, _SAMPLE_DEPS_FILE.replace('1ced', 'c0ffee'))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment