[Autotest][PY3] Migrating/Refactoring control_data.py for py2/3
compiler isn't present on python3, but AST is. Reworked control_data.py
to use this instead. Tested using unittest, dummy_Pass, and others. Ran
it both in python2 and 3 with success.
BUG=chromium:990593
TEST=dummy_Pass(in python2 & 3), control_data_unittest
Change-Id: Ifae1833af0c617b634279f77bcde83febfb22b75
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2508464
Reviewed-by: Seewai Fu <seewaifu@google.com>
Reviewed-by: Greg Edelston <gredelston@google.com>
Commit-Queue: Derek Beckett <dbeckett@chromium.org>
Tested-by: Derek Beckett <dbeckett@chromium.org>
diff --git a/client/common_lib/control_data.py b/client/common_lib/control_data.py
index de8ea83..54d4f4f 100644
--- a/client/common_lib/control_data.py
+++ b/client/common_lib/control_data.py
@@ -6,12 +6,7 @@
from __future__ import division
from __future__ import print_function
-import warnings
-with warnings.catch_warnings():
- # The 'compiler' module is gone in Python 3.0. Let's not say
- # so in every log file.
- warnings.simplefilter("ignore", DeprecationWarning)
- import compiler
+import ast
import logging
import textwrap
import re
@@ -21,6 +16,7 @@
from autotest_lib.client.common_lib import global_config
from autotest_lib.client.common_lib import priorities
+
REQUIRED_VARS = set(['author', 'doc', 'name', 'time', 'test_type'])
OBSOLETE_VARS = set(['experimental'])
@@ -323,16 +319,19 @@
def _extract_const(expr):
- assert(expr.__class__ == compiler.ast.Const)
- assert(expr.value.__class__ in (str, int, float, six.text_type))
- return str(expr.value).strip()
+ assert (expr.__class__ == ast.Str)
+ if six.PY2:
+ assert (expr.s.__class__ in (str, int, float, unicode))
+ else:
+ assert (expr.s.__class__ in (str, int, float))
+ return str(expr.s).strip()
def _extract_dict(expr):
- assert(expr.__class__ == compiler.ast.Dict)
- assert(expr.items.__class__ == list)
+ assert (expr.__class__ == ast.Dict)
+ assert (expr.keys.__class__ == list)
cf_dict = {}
- for key, value in expr.items:
+ for key, value in zip(expr.keys, expr.values):
try:
key = _extract_const(key)
val = _extract_expression(value)
@@ -344,9 +343,9 @@
def _extract_list(expr):
- assert(expr.__class__ == compiler.ast.List)
+ assert (expr.__class__ == ast.List)
list_values = []
- for value in expr.nodes:
+ for value in expr.elts:
try:
list_values.append(_extract_expression(value))
except (AssertionError, ValueError):
@@ -355,34 +354,38 @@
def _extract_name(expr):
- assert(expr.__class__ == compiler.ast.Name)
- assert(expr.name in ('False', 'True', 'None'))
- return str(expr.name)
+ assert (expr.__class__ == ast.Name)
+ assert (expr.id in ('False', 'True', 'None'))
+ return str(expr.id)
def _extract_expression(expr):
- if expr.__class__ == compiler.ast.Const:
+ if expr.__class__ == ast.Str:
return _extract_const(expr)
- if expr.__class__ == compiler.ast.Name:
+ if expr.__class__ == ast.Name:
return _extract_name(expr)
- if expr.__class__ == compiler.ast.Dict:
+ if expr.__class__ == ast.Dict:
return _extract_dict(expr)
- if expr.__class__ == compiler.ast.List:
+ if expr.__class__ == ast.List:
return _extract_list(expr)
+ if expr.__class__ == ast.Num:
+ return expr.n
+ if six.PY3 and expr.__class__ == ast.NameConstant:
+ return expr.value
+ if six.PY3 and expr.__class__ == ast.Constant:
+ try:
+ return expr.value.strip()
+ except Exception:
+ return expr.value
raise ValueError('Unknown rval %s' % expr)
def _extract_assignment(n):
- assert(n.__class__ == compiler.ast.Assign)
- assert(n.nodes.__class__ == list)
- assert(len(n.nodes) == 1)
- assert(n.nodes[0].__class__ == compiler.ast.AssName)
- assert(n.nodes[0].flags.__class__ == str)
- assert(n.nodes[0].name.__class__ == str)
-
- val = _extract_expression(n.expr)
- key = n.nodes[0].name.lower()
-
+ assert (n.__class__ == ast.Assign)
+ assert (len(n.targets) == 1)
+ assert (n.targets[0].__class__ == ast.Name)
+ val = _extract_expression(n.value)
+ key = n.targets[0].id.lower()
return (key, val)
@@ -396,7 +399,7 @@
"""
try:
- mod = compiler.parse(control)
+ mod = ast.parse(control)
except SyntaxError as e:
logging.error('Syntax error (%s) while parsing control string:', e)
lines = control.split('\n')
@@ -408,7 +411,8 @@
def parse_control(path, raise_warnings=False):
try:
- mod = compiler.parseFile(path)
+ with open(path, 'r') as r:
+ mod = ast.parse(r.read())
except SyntaxError as e:
raise ControlVariableException("Error parsing %s because %s" %
(path, e))
@@ -424,22 +428,20 @@
try:
key, val = _extract_assignment(node)
variables[key] = val
- except (AssertionError, ValueError):
+ except (AssertionError, ValueError) as e:
pass
def finish_parse(mod, path, raise_warnings):
- assert(mod.__class__ == compiler.ast.Module)
- assert(mod.node.__class__ == compiler.ast.Stmt)
- assert(mod.node.nodes.__class__ == list)
+ assert (mod.__class__ == ast.Module)
+ assert (mod.body.__class__ == list)
variables = {}
injection_variables = {}
- for n in mod.node.nodes:
- if (n.__class__ == compiler.ast.Function and
- re.match('step\d+', n.name)):
+ for n in mod.body:
+ if (n.__class__ == ast.FunctionDef and re.match('step\d+', n.name)):
vars_in_step = {}
- for sub_node in n.code.nodes:
+ for sub_node in n.body:
_try_extract_assignment(sub_node, vars_in_step)
if vars_in_step:
# Empty the vars collection so assignments from multiple steps
diff --git a/client/common_lib/control_data_unittest.py b/client/common_lib/control_data_unittest.py
index 2089e61..a96d1ad 100755
--- a/client/common_lib/control_data_unittest.py
+++ b/client/common_lib/control_data_unittest.py
@@ -6,14 +6,16 @@
from __future__ import print_function
import json
-import os, unittest
+import os
import six
from six.moves import range
+import unittest
import common
from autotest_lib.client.common_lib import control_data, autotemp
+
ControlData = control_data.ControlData
CONTROL = """
@@ -86,7 +88,7 @@
def setUp(self):
self.control_tmp = autotemp.tempfile(unique_id='control_unit',
text=True)
- os.write(self.control_tmp.fd, CONTROL)
+ os.write(self.control_tmp.fd, str.encode(CONTROL))
def tearDown(self):
@@ -95,21 +97,20 @@
def test_parse_control(self):
cd = control_data.parse_control(self.control_tmp.name, True)
- self.assertEquals(cd.author, "Author")
- self.assertEquals(cd.dependencies, set(['console', 'power']))
- self.assertEquals(cd.doc, "doc stuff")
- self.assertEquals(cd.experimental, False)
- self.assertEquals(cd.name, "nAmE")
- self.assertEquals(cd.run_verify, False)
- self.assertEquals(cd.sync_count, 2)
- self.assertEquals(cd.time, "short")
- self.assertEquals(cd.test_class, "kernel")
- self.assertEquals(cd.test_category, "stress")
- self.assertEquals(cd.test_type, "client")
- self.assertEquals(cd.require_ssp, False)
- self.assertEquals(cd.attributes, set(["suite:smoke","suite:bvt"]))
- self.assertEquals(cd.suite,
- "bvt,smoke,suite-listed-only-in-suite-line")
+ self.assertEqual(cd.author, "Author")
+ self.assertEqual(cd.dependencies, set(['console', 'power']))
+ self.assertEqual(cd.doc, "doc stuff")
+ self.assertEqual(cd.experimental, False)
+ self.assertEqual(cd.name, "nAmE")
+ self.assertEqual(cd.run_verify, False)
+ self.assertEqual(cd.sync_count, 2)
+ self.assertEqual(cd.time, "short")
+ self.assertEqual(cd.test_class, "kernel")
+ self.assertEqual(cd.test_category, "stress")
+ self.assertEqual(cd.test_type, "client")
+ self.assertEqual(cd.require_ssp, False)
+ self.assertEqual(cd.attributes, set(["suite:smoke", "suite:bvt"]))
+ self.assertEqual(cd.suite, "bvt,smoke,suite-listed-only-in-suite-line")
class ParseWrappedControlTest(unittest.TestCase):
@@ -117,7 +118,7 @@
def setUp(self):
self.control_tmp = autotemp.tempfile(unique_id='wrapped_control_unit',
text=True)
- os.write(self.control_tmp.fd, WRAPPED_CONTROL)
+ os.write(self.control_tmp.fd, str.encode(WRAPPED_CONTROL))
def tearDown(self):
@@ -126,22 +127,21 @@
def test_parse_control(self):
cd = control_data.parse_control(self.control_tmp.name, True)
- self.assertEquals(cd.author, "Author")
- self.assertEquals(cd.dependencies, set(['console', 'power']))
- self.assertEquals(cd.doc, "doc stuff")
- self.assertEquals(cd.experimental, False)
- self.assertEquals(cd.name, "nAmE")
- self.assertEquals(cd.run_verify, False)
- self.assertEquals(cd.sync_count, 2)
- self.assertEquals(cd.time, "short")
- self.assertEquals(cd.test_class, "kernel")
- self.assertEquals(cd.test_category, "stress")
- self.assertEquals(cd.test_type, "client")
- self.assertEquals(cd.require_ssp, False)
- self.assertEquals(cd.attributes, set(["suite:smoke","suite:bvt"]))
- self.assertEquals(cd.suite,
- "bvt,smoke,suite-listed-only-in-suite-line")
- self.assertEquals(cd.max_result_size_KB, 20000)
+ self.assertEqual(cd.author, "Author")
+ self.assertEqual(cd.dependencies, set(['console', 'power']))
+ self.assertEqual(cd.doc, "doc stuff")
+ self.assertEqual(cd.experimental, False)
+ self.assertEqual(cd.name, "nAmE")
+ self.assertEqual(cd.run_verify, False)
+ self.assertEqual(cd.sync_count, 2)
+ self.assertEqual(cd.time, "short")
+ self.assertEqual(cd.test_class, "kernel")
+ self.assertEqual(cd.test_category, "stress")
+ self.assertEqual(cd.test_type, "client")
+ self.assertEqual(cd.require_ssp, False)
+ self.assertEqual(cd.attributes, set(["suite:smoke", "suite:bvt"]))
+ self.assertEqual(cd.suite, "bvt,smoke,suite-listed-only-in-suite-line")
+ self.assertEqual(cd.max_result_size_KB, 20000)
class ParseControlFileBugTemplate(unittest.TestCase):
@@ -190,7 +190,8 @@
def test_bug_template_parsing(self):
"""Basic parsing test for a bug templates in a test control file."""
- os.write(self.control_tmp.fd, self.insert_bug_template(CONTROL))
+ os.write(self.control_tmp.fd,
+ str.encode(self.insert_bug_template(CONTROL)))
cd = control_data.parse_control(self.control_tmp.name, True)
self.verify_bug_template(cd.bug_template)
@@ -198,7 +199,8 @@
def test_bug_template_list(self):
"""Test that lists in the bug template can handle other datatypes."""
self.bug_template['labels'].append({'foo': 'bar'})
- os.write(self.control_tmp.fd, self.insert_bug_template(CONTROL))
+ os.write(self.control_tmp.fd,
+ str.encode(self.insert_bug_template(CONTROL)))
cd = control_data.parse_control(self.control_tmp.name, True)
self.verify_bug_template(cd.bug_template)
@@ -206,7 +208,8 @@
def test_bad_template(self):
"""Test that a bad bug template doesn't result in a bad control data."""
self.bug_template = 'foobarbug_template'
- os.write(self.control_tmp.fd, self.insert_bug_template(CONTROL))
+ os.write(self.control_tmp.fd,
+ str.encode(self.insert_bug_template(CONTROL)))
cd = control_data.parse_control(self.control_tmp.name, True)
self.assertFalse(hasattr(cd, 'bug_template'))
@@ -224,13 +227,13 @@
def test_bool(self):
cd = ControlData({}, 'filename')
cd._set_bool('foo', 'False')
- self.assertEquals(cd.foo, False)
+ self.assertEqual(cd.foo, False)
cd._set_bool('foo', True)
- self.assertEquals(cd.foo, True)
+ self.assertEqual(cd.foo, True)
cd._set_bool('foo', 'FALSE')
- self.assertEquals(cd.foo, False)
+ self.assertEqual(cd.foo, False)
cd._set_bool('foo', 'true')
- self.assertEquals(cd.foo, True)
+ self.assertEqual(cd.foo, True)
self.assertRaises(ValueError, cd._set_bool, 'foo', '')
self.assertRaises(ValueError, cd._set_bool, 'foo', 1)
self.assertRaises(ValueError, cd._set_bool, 'foo', [])
@@ -240,11 +243,11 @@
def test_int(self):
cd = ControlData({}, 'filename')
cd._set_int('foo', 0)
- self.assertEquals(cd.foo, 0)
+ self.assertEqual(cd.foo, 0)
cd._set_int('foo', '0')
- self.assertEquals(cd.foo, 0)
+ self.assertEqual(cd.foo, 0)
cd._set_int('foo', '-1', min=-2, max=10)
- self.assertEquals(cd.foo, -1)
+ self.assertEqual(cd.foo, -1)
self.assertRaises(ValueError, cd._set_int, 'foo', 0, min=1)
self.assertRaises(ValueError, cd._set_int, 'foo', 1, max=0)
self.assertRaises(ValueError, cd._set_int, 'foo', 'x')
@@ -255,40 +258,40 @@
def test_set(self):
cd = ControlData({}, 'filename')
cd._set_set('foo', 'a')
- self.assertEquals(cd.foo, set(['a']))
+ self.assertEqual(cd.foo, set(['a']))
cd._set_set('foo', 'a,b,c')
- self.assertEquals(cd.foo, set(['a', 'b', 'c']))
+ self.assertEqual(cd.foo, set(['a', 'b', 'c']))
cd._set_set('foo', ' a , b , c ')
- self.assertEquals(cd.foo, set(['a', 'b', 'c']))
+ self.assertEqual(cd.foo, set(['a', 'b', 'c']))
cd._set_set('foo', None)
- self.assertEquals(cd.foo, set(['None']))
+ self.assertEqual(cd.foo, set(['None']))
def test_string(self):
cd = ControlData({}, 'filename')
cd._set_string('foo', 'a')
- self.assertEquals(cd.foo, 'a')
+ self.assertEqual(cd.foo, 'a')
cd._set_string('foo', 'b')
- self.assertEquals(cd.foo, 'b')
+ self.assertEqual(cd.foo, 'b')
cd._set_string('foo', 'B')
- self.assertEquals(cd.foo, 'B')
+ self.assertEqual(cd.foo, 'B')
cd._set_string('foo', 1)
- self.assertEquals(cd.foo, '1')
+ self.assertEqual(cd.foo, '1')
cd._set_string('foo', None)
- self.assertEquals(cd.foo, 'None')
+ self.assertEqual(cd.foo, 'None')
cd._set_string('foo', [])
- self.assertEquals(cd.foo, '[]')
+ self.assertEqual(cd.foo, '[]')
def test_option(self):
options = ['a', 'b']
cd = ControlData({}, 'filename')
cd._set_option('foo', 'a', options)
- self.assertEquals(cd.foo, 'a')
+ self.assertEqual(cd.foo, 'a')
cd._set_option('foo', 'b', options)
- self.assertEquals(cd.foo, 'b')
+ self.assertEqual(cd.foo, 'b')
cd._set_option('foo', 'B', options)
- self.assertEquals(cd.foo, 'B')
+ self.assertEqual(cd.foo, 'B')
self.assertRaises(ValueError, cd._set_option,
'foo', 'x', options)
self.assertRaises(ValueError, cd._set_option,
@@ -302,7 +305,7 @@
def test_set_attributes(self):
cd = ControlData({}, 'filename')
cd.set_attributes('suite:bvt')
- self.assertEquals(cd.attributes, set(['suite:bvt']))
+ self.assertEqual(cd.attributes, set(['suite:bvt']))
def test_get_test_time_index(self):