diff --git a/gclient_eval.py b/gclient_eval.py index f8a05d78ac..94874e3ce5 100644 --- a/gclient_eval.py +++ b/gclient_eval.py @@ -101,6 +101,24 @@ def _NodeDictSchema(dict_schema): return validate +def _IsConstant(node): + return isinstance(node, ast.Constant) + + +def _IsStringConstant(node): + return _IsConstant(node) and isinstance(node.value, str) + + +def _IsNameConstant(node): + return _IsConstant(node) \ + and isinstance(node.value, (bool, type(None))) + + +def _IsNumericConstant(node): + return _IsConstant(node) \ + and isinstance(node.value, (int, float, complex)) \ + and not isinstance(node.value, bool) + # See https://github.com/keleshev/schema for docs how to configure schema. _GCLIENT_DEPS_SCHEMA = _NodeDictSchema({ schema.Optional(str): @@ -316,18 +334,18 @@ def _gclient_eval(node_or_string, filename='', vars_dict=None): node_or_string = node_or_string.body def _convert(node): - if isinstance(node, ast.Str): + if _IsStringConstant(node): if vars_dict is None: - return node.s + return node.value try: - return node.s.format(**vars_dict) + return node.value.format(**vars_dict) except KeyError as e: raise KeyError( '%s was used as a variable, but was not declared in the vars dict ' '(file %r, line %s)' % (e.args[0], filename, getattr(node, 'lineno', ''))) - elif isinstance(node, ast.Num): - return node.n + elif _IsNameConstant(node) or _IsNumericConstant(node): + return node.value elif isinstance(node, ast.Tuple): return tuple(map(_convert, node.elts)) elif isinstance(node, ast.List): @@ -349,9 +367,6 @@ def _gclient_eval(node_or_string, filename='', vars_dict=None): 'invalid name %r (file %r, line %s)' % (node.id, filename, getattr(node, 'lineno', ''))) return _allowed_names[node.id] - elif not sys.version_info[:2] < (3, 4) and isinstance( - node, ast.NameConstant): # Since Python 3.4 - return node.value elif isinstance(node, ast.Call): if (not isinstance(node.func, ast.Name) or (node.func.id not in ('Str', 'Var'))): @@ -366,8 +381,8 @@ def _gclient_eval(node_or_string, filename='', vars_dict=None): ''))) if node.func.id == 'Str': - if isinstance(node.args[0], ast.Str): - return ConstantString(node.args[0].s) + if _IsStringConstant(node.args[0]): + return ConstantString(node.args[0].value) raise ValueError( 'Passed a non-string to Str() (file %r, line%s)' % (filename, getattr(node, 'lineno', ''))) @@ -594,8 +609,8 @@ def EvaluateCondition(condition, variables, referenced_variables=None): main_node = main_node.body def _convert(node, allow_tuple=False): - if isinstance(node, ast.Str): - return node.s + if _IsStringConstant(node): + return node.value if isinstance(node, ast.Tuple) and allow_tuple: return tuple(map(_convert, node.elts)) @@ -627,8 +642,7 @@ def EvaluateCondition(condition, variables, referenced_variables=None): # be evaluated. return node.id - if not sys.version_info[:2] < (3, 4) and isinstance( - node, ast.NameConstant): # Since Python 3.4 + if _IsNameConstant(node): return node.value if isinstance(node, ast.BoolOp) and isinstance(node.op, ast.Or): @@ -696,11 +710,11 @@ def _UpdateAstString(tokens, node, value): node = node.args[0] position = node.lineno, node.col_offset quote_char = '' - if isinstance(node, ast.Str): + if _IsStringConstant(node): quote_char = tokens[position][1][0] value = value.encode('unicode_escape').decode('utf-8') tokens[position][1] = quote_char + value + quote_char - node.s = value + node.value = value def _ShiftLinesInTokens(tokens, delta, start): @@ -793,11 +807,11 @@ def SetVar(gclient_dict, var_name, value): def _GetVarName(node): if isinstance(node, ast.Call): - return node.args[0].s + return node.args[0].value - if node.s.endswith('}'): - last_brace = node.s.rfind('{') - return node.s[last_brace + 1:-1] + if node.value.endswith('}'): + last_brace = node.value.rfind('{') + return node.value[last_brace + 1:-1] return None @@ -821,9 +835,9 @@ def SetGCS(gclient_dict, dep_name, new_objects): keys_to_update = ('object_name', 'sha256sum', 'size_bytes', 'generation') for index, object_node in enumerate(objects_node.elts): for key, value in zip(object_node.keys, object_node.values): - if key.s not in keys_to_update: + if key.value not in keys_to_update: continue - _UpdateAstString(tokens, value, new_objects[index][key.s]) + _UpdateAstString(tokens, value, new_objects[index][key.value]) node.SetNode('objects', new_objects, objects_node) @@ -855,7 +869,7 @@ def SetCIPD(gclient_dict, dep_name, package_name, new_version): "The deps entry for %s:%s has no formatting information." % (dep_name, package_name)) - if not isinstance(node, ast.Call) and not isinstance(node, ast.Str): + if not isinstance(node, ast.Call) and not _IsStringConstant(node): raise ValueError( "Unsupported dependency revision format. Please file a bug to the " "Infra>SDK component in crbug.com") @@ -880,15 +894,15 @@ def SetRevision(gclient_dict, dep_name, new_revision): if isinstance(node, ast.BinOp): node = node.right - if isinstance(node, ast.Str): + if _IsStringConstant(node): token = _gclient_eval(tokens[node.lineno, node.col_offset][1]) - if token != node.s: + if token != node.value: raise ValueError( 'Can\'t update value for %s. Multiline strings and implicitly ' 'concatenated strings are not supported.\n' 'Consider reformatting the DEPS file.' % dep_key) - if not isinstance(node, ast.Call) and not isinstance(node, ast.Str): + if not isinstance(node, ast.Call) and not _IsStringConstant(node): raise ValueError( "Unsupported dependency revision format. Please file a bug to the " "Infra>SDK component in crbug.com") @@ -897,15 +911,15 @@ def SetRevision(gclient_dict, dep_name, new_revision): if var_name is not None: SetVar(gclient_dict, var_name, new_revision) else: - if '@' in node.s: + if '@' in node.value: # '@' is part of the last string, which we want to modify. # Discard whatever was after the '@' and put the new revision in # its place. - new_revision = node.s.split('@')[0] + '@' + new_revision + new_revision = node.value.split('@')[0] + '@' + new_revision elif '@' not in dep_dict[dep_key]: # '@' is not part of the URL at all. This mean the dependency is # unpinned and we should pin it. - new_revision = node.s + '@' + new_revision + new_revision = node.value + '@' + new_revision _UpdateAstString(tokens, node, new_revision) dep_dict.SetNode(dep_key, new_revision, node) diff --git a/tests/gclient_eval_unittest.py b/tests/gclient_eval_unittest.py index 03decdf30f..3aed061565 100755 --- a/tests/gclient_eval_unittest.py +++ b/tests/gclient_eval_unittest.py @@ -30,6 +30,11 @@ class GClientEvalTest(unittest.TestCase): def test_str(self): self.assertEqual('foo', gclient_eval._gclient_eval('"foo"')) + def test_num(self): + self.assertEqual(42, gclient_eval._gclient_eval('42')) + self.assertEqual(1024.0, gclient_eval._gclient_eval('1024.0')) + self.assertEqual(42j, gclient_eval._gclient_eval('42j')) + def test_tuple(self): self.assertEqual(('a', 'b'), gclient_eval._gclient_eval('("a", "b")')) @@ -40,6 +45,7 @@ class GClientEvalTest(unittest.TestCase): self.assertEqual({'a': 'b'}, gclient_eval._gclient_eval('{"a": "b"}')) def test_name_safe(self): + self.assertEqual(None, gclient_eval._gclient_eval('None')) self.assertEqual(True, gclient_eval._gclient_eval('True')) def test_name_unsafe(self):