From ae02be24fb777bc8180c4510449ea38215d340b2 Mon Sep 17 00:00:00 2001 From: Eric Berquist Date: Sat, 23 Mar 2024 15:19:48 -0400 Subject: [PATCH] Remove Python 2 testing infrastructure (#1064) * remove remnants of Python 2/3 distinction from test infrastructure * check for Python version is already done in Autotools * fix regex escaping in tests/testsuite_default_Links.py * remove impossible InstanceType branch for Python 3 --- .../sst_test_engine_loader.py | 46 ------------- src/sst/core/testingframework/sst_unittest.py | 7 -- .../sst_unittest_parameterized.py | 67 +++++-------------- .../testingframework/sst_unittest_support.py | 37 ++-------- src/sst/core/testingframework/test_engine.py | 10 +-- .../testingframework/test_engine_junit.py | 43 ++---------- .../testingframework/test_engine_support.py | 21 ++---- .../testingframework/test_engine_unittest.py | 54 +++------------ tests/testsuite_default_Links.py | 2 +- tests/testsuite_testengine_testing.py | 40 ++--------- 10 files changed, 48 insertions(+), 279 deletions(-) diff --git a/src/sst/core/testingframework/sst_test_engine_loader.py b/src/sst/core/testingframework/sst_test_engine_loader.py index 1f08feaac..479b490ca 100644 --- a/src/sst/core/testingframework/sst_test_engine_loader.py +++ b/src/sst/core/testingframework/sst_test_engine_loader.py @@ -33,51 +33,8 @@ TEST_ELEMENTS = 0 TEST_SST_CORE = 1 -REQUIRED_PY_MAJ_VER_2 = 2 # Required Python2 Major Version -REQUIRED_PY_MAJ_VER_2_MINOR_VER = 7 # Required PY2 Minor Version -REQUIRED_PY_MAJ_VER_2_SUB_MINOR_VER = 5 # Required PY2 Sub-Minor Version -REQUIRED_PY_MAJ_VER_3 = 3 # Required Python 3 Major Version -REQUIRED_PY_MAJ_VER_3_MINOR_VER = 4 # Required PY3 Minor Version -REQUIRED_PY_MAJ_VER_3_SUB_MINOR_VER = 0 # Required PY3 Sub-Minor Version -REQUIRED_PY_MAJ_VER_MAX = REQUIRED_PY_MAJ_VER_3 # Highest supported Major Version - ################################################################################ -def check_python_version(): - # Validate Python Versions - ver = sys.version_info - - # Check for Py2.x or Py3.x Versions - if (ver[0] < REQUIRED_PY_MAJ_VER_2) or (ver[0] > REQUIRED_PY_MAJ_VER_3): - print(("SST Test Engine requires Python major version {0} or {1}\n" + - "Found Python version is:\n{2}").format(REQUIRED_PY_MAJ_VER_2, - REQUIRED_PY_MAJ_VER_MAX, - sys.version)) - sys.exit(1) - - # Check to ensure minimum Py2 version - if ((ver[0] == REQUIRED_PY_MAJ_VER_2) and (ver[1] < REQUIRED_PY_MAJ_VER_2_MINOR_VER)) or \ - ((ver[0] == REQUIRED_PY_MAJ_VER_2) and (ver[1] == REQUIRED_PY_MAJ_VER_2_MINOR_VER) and - (ver[2] < REQUIRED_PY_MAJ_VER_2_SUB_MINOR_VER)): - print(("SST Test Engine requires Python 2 version {0}.{1}.{2} or greater\n" + - "Found Python version is:\n{3}").format(REQUIRED_PY_MAJ_VER_2, - REQUIRED_PY_MAJ_VER_2_MINOR_VER, - REQUIRED_PY_MAJ_VER_2_SUB_MINOR_VER, - sys.version)) - sys.exit(1) - - # Check to ensure minimum Py3 version - if ((ver[0] == REQUIRED_PY_MAJ_VER_3) and (ver[1] < REQUIRED_PY_MAJ_VER_3_MINOR_VER)) or \ - ((ver[0] == REQUIRED_PY_MAJ_VER_3) and (ver[1] == REQUIRED_PY_MAJ_VER_3_MINOR_VER) and - (ver[2] < REQUIRED_PY_MAJ_VER_3_SUB_MINOR_VER)): - print(("SST Test Engine requires Python 3 version {0}.{1}.{2} or greater\n" + - "Found Python version is:\n{3}").format(REQUIRED_PY_MAJ_VER_3, - REQUIRED_PY_MAJ_VER_3_MINOR_VER, - REQUIRED_PY_MAJ_VER_3_SUB_MINOR_VER, - sys.version)) - sys.exit(1) -#### - def startup_and_run(sst_core_bin_dir, test_mode): """ This is the main entry point for loading and running the SST Test Frameworks Engine. @@ -97,9 +54,6 @@ def startup_and_run(sst_core_bin_dir, test_mode): test_mode: 1 for Core Testing, 0 for Elements testing. """ try: - # Check the python version and make sure we can run - check_python_version() - if test_mode not in (TEST_SST_CORE, TEST_ELEMENTS): print((("FATAL: Unsupported test_mode {0} in ") + ("startup_and_run()")).format(test_mode)) diff --git a/src/sst/core/testingframework/sst_unittest.py b/src/sst/core/testingframework/sst_unittest.py index 187b00a2a..042632370 100644 --- a/src/sst/core/testingframework/sst_unittest.py +++ b/src/sst/core/testingframework/sst_unittest.py @@ -38,13 +38,6 @@ from test_engine_junit import junit_to_xml_report_file #from test_engine_junit import junit_to_xml_report_string -################################################################################ - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -################################################################################ - class SSTTestCase(unittest.TestCase): """ This class is main SSTTestCase class for the SST Testing Frameworks diff --git a/src/sst/core/testingframework/sst_unittest_parameterized.py b/src/sst/core/testingframework/sst_unittest_parameterized.py index f356b7075..6f9c886c5 100644 --- a/src/sst/core/testingframework/sst_unittest_parameterized.py +++ b/src/sst/core/testingframework/sst_unittest_parameterized.py @@ -54,38 +54,18 @@ class SkipTest(Exception): pass -PY3 = sys.version_info[0] == 3 -PY2 = sys.version_info[0] == 2 - -if PY3: - # Python 3 doesn't have an InstanceType, so just use a dummy type. - class InstanceType(): - pass - lzip = lambda *a: list(zip(*a)) - text_type = str - string_types = str, - bytes_type = bytes - def make_method(func, instance, type): - if instance is None: - return func - return MethodType(func, instance) -else: - from types import InstanceType - lzip = zip - text_type = unicode - bytes_type = str - string_types = basestring, - def make_method(func, instance, type): - return MethodType(func, instance, type) +lzip = lambda *a: list(zip(*a)) +def make_method(func, instance, type): + if instance is None: + return func + return MethodType(func, instance) CompatArgSpec = namedtuple("CompatArgSpec", "args varargs keywords defaults") def getargspec(func): - if PY2: - return CompatArgSpec(*inspect.getargspec(func)) args = inspect.getfullargspec(func) if args.kwonlyargs: raise TypeError(( @@ -170,7 +150,7 @@ def from_decorator(cls, args): """ if isinstance(args, param): return args - elif isinstance(args, string_types): + elif isinstance(args, str): args = (args, ) try: return cls(*args) @@ -256,11 +236,11 @@ def short_repr(x, n=64): """ x_repr = repr(x) - if isinstance(x_repr, bytes_type): + if isinstance(x_repr, bytes): try: - x_repr = text_type(x_repr, "utf-8") + x_repr = str(x_repr, encoding="utf-8") except UnicodeDecodeError: - x_repr = text_type(x_repr, "latin1") + x_repr = str(x_repr, encoding="latin1") if len(x_repr) > n: x_repr = x_repr[:n//2] + "..." + x_repr[len(x_repr) - n//2:] return x_repr @@ -291,7 +271,7 @@ def default_name_func(func, num, p): base_name = func.__name__ name_suffix = "_%s" %(num, ) - if len(p.args) > 0 and isinstance(p.args[0], string_types): + if len(p.args) > 0 and isinstance(p.args[0], str): name_suffix += "_" + parameterized.to_safe_name(p.args[0]) return base_name + name_suffix @@ -370,16 +350,6 @@ def __call__(self, test_func): @wraps(test_func) def wrapper(test_self=None): test_cls = test_self and type(test_self) - if test_self is not None: - if issubclass(test_cls, InstanceType): - raise TypeError(( - "@parameterized can't be used with old-style classes, but " - "%r has an old-style class. Consider using a new-style " - "class, or '@parameterized.expand' " - "(see http://stackoverflow.com/q/54867/71522 for more " - "information on old-style classes)." - ) %(test_self, )) - original_doc = wrapper.__doc__ for num, args in enumerate(wrapper.parameterized_input): p = param.from_decorator(args) @@ -427,10 +397,7 @@ def param_as_nose_tuple(self, test_self, func, num, p): # sure that the `self` in the method is properly shared with the # `self` used in `setUp` and `tearDown`. But only there. Everyone # else needs a bound method. - func_self = ( - None if PY2 and detect_runner() == "nose" else - test_self - ) + func_self = test_self nose_func = make_method(nose_func, func_self, type(test_self)) return unbound_func, (nose_func, ) + p.args + (p.kwargs or {}, ) @@ -586,7 +553,7 @@ class TestUserAccessLevel(TestCase): ... """ - if isinstance(attrs, string_types): + if isinstance(attrs, str): attrs = [attrs] input_dicts = ( @@ -633,13 +600,9 @@ def get_class_name_suffix(params_dict): if "name" in params_dict: return parameterized.to_safe_name(params_dict["name"]) - params_vals = ( - params_dict.values() if PY3 else - (v for (_, v) in sorted(params_dict.items())) - ) return parameterized.to_safe_name(next(( - v for v in params_vals - if isinstance(v, string_types) + v for v in params_dict.values() + if isinstance(v, str) ), "")) @@ -649,4 +612,4 @@ def default_class_name_func(cls, num, params_dict): cls.__name__, num, suffix and "_" + suffix, - ) \ No newline at end of file + ) diff --git a/src/sst/core/testingframework/sst_unittest_support.py b/src/sst/core/testingframework/sst_unittest_support.py index bcabdeef7..b8178bb0f 100644 --- a/src/sst/core/testingframework/sst_unittest_support.py +++ b/src/sst/core/testingframework/sst_unittest_support.py @@ -25,15 +25,7 @@ import tarfile import shutil import difflib - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -# ConfigParser module changes name between Py2->Py3 -if PY3: - import configparser -else: - import ConfigParser as configparser +import configparser import test_engine_globals from test_engine_support import OSCommand @@ -179,24 +171,6 @@ def testing_is_PIN3_used(): # System Information Functions ################################################################################ -def testing_check_is_py_2(): - """ Check if Test Frameworks is running via Python Version 2 - - Returns: - (bool) True if test frameworks is being run by Python Version 2 - """ - return PY2 - -def testing_check_is_py_3(): - """ Check if Test Frameworks is running via Python Version 3 - - Returns: - (bool) True if test frameworks is being run by Python Version 3 - """ - return PY3 - -### - def host_os_get_system_node_name(): """ Get the node name of the system @@ -1478,13 +1452,10 @@ def os_awk_print(in_str, fields_index_list): Returns: (str) Space separated string of extracted fields. """ - if PY2: - check_param_type("in_str", in_str, unicode) + if isinstance(in_str, bytes): + check_param_type("in_str", in_str, bytes) else: - if isinstance(in_str, bytes): - check_param_type("in_str", in_str, bytes) - else: - check_param_type("in_str", in_str, str) + check_param_type("in_str", in_str, str) check_param_type("fields_index_list", fields_index_list, list) for index, field_index in enumerate(fields_index_list): check_param_type("field_index - {0}".format(index), field_index, int) diff --git a/src/sst/core/testingframework/test_engine.py b/src/sst/core/testingframework/test_engine.py index 23600b76b..49a68d248 100644 --- a/src/sst/core/testingframework/test_engine.py +++ b/src/sst/core/testingframework/test_engine.py @@ -21,15 +21,7 @@ import unittest import argparse import shutil - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -# ConfigParser module changes name between Py2->Py3 -if PY3: - import configparser -else: - import ConfigParser as configparser +import configparser import test_engine_globals from sst_unittest import * diff --git a/src/sst/core/testingframework/test_engine_junit.py b/src/sst/core/testingframework/test_engine_junit.py index 68b543547..c0df9cbee 100644 --- a/src/sst/core/testingframework/test_engine_junit.py +++ b/src/sst/core/testingframework/test_engine_junit.py @@ -48,28 +48,9 @@ import xml.etree.ElementTree as ET import xml.dom.minidom -#from six import u, iteritems, PY2 -# The orig code uses 3rd party module "six". -# We dont want any external modules, so -# here are equivalent functions -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -if PY3: - unichr = chr - def _iteritems(_d, **kw): - """ Py3 iteritems() """ - return iter(_d.items(**kw)) - def _u(_s): - """ Py3 u() """ - return _s -else: - unichr - def _iteritems(_d, **kw): - """ Py2 iteritems() """ - return _d.iteritems(**kw) - def _u(_s): - """ Py2 u() """ - return unicode(_s.replace(r'\\', r'\\\\'), "unicode_escape") +def _iteritems(_d, **kw): + """ Py3 iteritems() """ + return iter(_d.items(**kw)) ################################################################################ @@ -443,19 +424,7 @@ def _junit_decode(var, encoding): """ If not already unicode, decode it. """ - if PY2: - if isinstance(var, unicode): # noqa: F821 - ret = var - elif isinstance(var, str): - if encoding: - ret = var.decode(encoding) - else: - ret = unicode(var) # noqa: F821 - else: - ret = unicode(var) # noqa: F821 - else: - ret = str(var) - return ret + return str(var) #### @@ -492,8 +461,8 @@ def _junit_clean_illegal_xml_chars(string_to_clean): (0x10FFFE, 0x10FFFF), ] - illegal_ranges = ["%s-%s" % (unichr(low), unichr(high)) for \ + illegal_ranges = ["%s-%s" % (chr(low), chr(high)) for \ (low, high) in illegal_unichrs if low < sys.maxunicode] - illegal_xml_re = re.compile(_u("[%s]") % _u("").join(illegal_ranges)) + illegal_xml_re = re.compile("[%s]" % "".join(illegal_ranges)) return illegal_xml_re.sub("", string_to_clean) diff --git a/src/sst/core/testingframework/test_engine_support.py b/src/sst/core/testingframework/test_engine_support.py index 1669c340f..4b3380da9 100644 --- a/src/sst/core/testingframework/test_engine_support.py +++ b/src/sst/core/testingframework/test_engine_support.py @@ -24,9 +24,6 @@ import ast import inspect -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - import test_engine_globals ################################################################################ @@ -226,24 +223,18 @@ def result(self): def output(self): """ return the run output result """ # Sometimes the output can be a unicode or a byte string - convert it - if PY3: - if type(self._run_output) is bytes: - self._run_output = self._run_output.decode(encoding='UTF-8') - return self._run_output - else: - return self._run_output.decode('utf-8') + if isinstance(self._run_output, bytes): + self._run_output = self._run_output.decode(encoding='UTF-8') + return self._run_output #### def error(self): """ return the run error output result """ # Sometimes the output can be a unicode or a byte string - convert it - if PY3: - if type(self._run_error) is bytes: - self._run_error = self._run_error.decode(encoding='UTF-8') - return self._run_error - else: - return self._run_error.decode('utf-8') + if isinstance(self._run_error, bytes): + self._run_error = self._run_error.decode(encoding='UTF-8') + return self._run_error #### diff --git a/src/sst/core/testingframework/test_engine_unittest.py b/src/sst/core/testingframework/test_engine_unittest.py index 7adf27838..6c4b28e35 100644 --- a/src/sst/core/testingframework/test_engine_unittest.py +++ b/src/sst/core/testingframework/test_engine_unittest.py @@ -22,10 +22,6 @@ import time from datetime import datetime -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY3_4_Plus = sys.version_info[1] >= 4 - ################################################################################ def check_module_conditional_import(module_name): @@ -39,22 +35,9 @@ def check_module_conditional_import(module_name): Returns: True if module is loadable """ - if PY2: - import imp - try: - imp.find_module(module_name) - return True - except ImportError: - return False - else: - import importlib - import importlib.util - if not PY3_4_Plus: - avail = importlib.find_loader(module_name) - return avail is not None - else: - avail = importlib.util.find_spec(module_name) - return avail is not None + import importlib.util + avail = importlib.util.find_spec(module_name) + return avail is not None ################################################################################ @@ -69,21 +52,11 @@ def check_module_conditional_import(module_name): if check_module_conditional_import('pygments'): import pygments from pygments import formatters, highlight + from pygments.lexers import PythonTracebackLexer as Lexer pygments_loaded = True - try: - # Python 2 - from pygments.lexers import PythonTracebackLexer as Lexer - except NameError: - # Python 3 - from pygments.lexers import Python3TracebackLexer as Lexer - -# Queue module changes name between Py2->Py3 -if PY3: - import queue - Queue = queue.Queue -else: - import Queue - Queue = Queue.Queue + +import queue +Queue = queue.Queue # Try to import testtools (this may not be installed on system) if check_module_conditional_import('testtools'): @@ -103,11 +76,6 @@ def check_module_conditional_import(module_name): from test_engine_support import strqual from test_engine_junit import JUnitTestCase -if testing_check_is_py_2(): - text_type = unicode -else: - text_type = str - ################################################################################ def verify_concurrent_test_engine_available(): @@ -131,14 +99,14 @@ class SSTTextTestRunner(unittest.TextTestRunner): if blessings_loaded: _terminal = Terminal() colours = { - None: text_type, + None: str, 'failed': _terminal.bold_red, 'passed': _terminal.green, 'notes': _terminal.bold_yellow, } else: colours = { - None: text_type + None: str } def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1, @@ -275,7 +243,7 @@ class SSTTextTestResult(unittest.TestResult): if blessings_loaded: _terminal = Terminal() colours = { - None: text_type, + None: str, 'error': _terminal.bold_yellow, 'expected': _terminal.green, 'fail': _terminal.bold_red, @@ -286,7 +254,7 @@ class SSTTextTestResult(unittest.TestResult): } else: colours = { - None: text_type + None: str } if pygments_loaded: diff --git a/tests/testsuite_default_Links.py b/tests/testsuite_default_Links.py index 38e77ec4a..d64198d96 100644 --- a/tests/testsuite_default_Links.py +++ b/tests/testsuite_default_Links.py @@ -84,7 +84,7 @@ def component_test_template(self, testtype, extra_args="", rc=0): cmp_result = testing_compare_filtered_diff("Links_{0}".format(testtype), outfile, reffile, rc == 0, filters) else: cmpfile = errfile - filters.append(RemoveRegexFromLineFilter("FATAL: (\[[0-9]+:[0-9]+\])* ")) + filters.append(RemoveRegexFromLineFilter(r"FATAL: (\[[0-9]+:[0-9]+\])* ")) cmp_result = testing_compare_filtered_diff("Links_{0}".format(testtype), errfile, reffile, rc == 0, filters) self.assertTrue(cmp_result, "Output/Compare file {0} does not match Reference File {1}".format(cmpfile, reffile)) diff --git a/tests/testsuite_testengine_testing.py b/tests/testsuite_testengine_testing.py index c68b8512d..c3ed16895 100644 --- a/tests/testsuite_testengine_testing.py +++ b/tests/testsuite_testengine_testing.py @@ -146,15 +146,7 @@ def test_support_functions_get_info_from_sstsimulator_conf_success(self): log_forced("NOTE: This Test Has an Expected Pass and should show as 'PASS'") sourcedir = sstsimulator_conf_get_value_str("SSTCore", "sourcedir") log_forced("SSTCore SourceDir = {0}; Type = {1}".format(sourcedir, type(sourcedir))) - if testing_check_is_py_2(): - if type(sourcedir) == str: - self.assertEqual(str, type(sourcedir)) - elif type(sourcedir) == unicode: - self.assertEqual(unicode, type(sourcedir)) - else: - self.assertTrue(False) - else: - self.assertEqual(str, type(sourcedir)) + self.assertIsInstance(sourcedir, str) def test_support_functions_get_info_from_sstsimulator_conf_invalid_section_exception_success(self): # This should pass as we detect an expected exception due to invalid section @@ -288,42 +280,18 @@ def test_support_functions_test_os_pwd_cmd_success(self): log_forced("NOTE: This Test Has an Expected Pass and should show as 'PASS'") output = os_pwd(echo_out = False) log_forced("pwd = {0}".format(output)) - if testing_check_is_py_2(): - if type(output) == str: - self.assertEqual(str, type(output)) - elif type(output) == unicode: - self.assertEqual(unicode, type(output)) - else: - self.assertTrue(False) - else: - self.assertEqual(str, type(output)) + self.assertIsInstance(output, str) def test_support_functions_test_os_ls_cmd_success(self): log_forced("NOTE: This Test Has an Expected Pass and should show as 'PASS'") output = os_ls(self.get_testsuite_dir(), echo_out = False) log_forced("ls -lia =\n{0}".format(output)) - if testing_check_is_py_2(): - if type(output) == str: - self.assertEqual(str, type(output)) - elif type(output) == unicode: - self.assertEqual(unicode, type(output)) - else: - self.assertTrue(False) - else: - self.assertEqual(str, type(output)) + self.assertIsInstance(output, str) def test_support_functions_test_os_cat_cmd_success(self): log_forced("NOTE: This Test Has an Expected Pass and should show as 'PASS'") catfile = "{0}/{1}".format(self.get_testsuite_dir(), "testsuite_default_UnitAlgebra.py") output = os_cat(catfile, echo_out = False) log_forced("cat cmd output =\n{0}".format(output)) - if testing_check_is_py_2(): - if type(output) == str: - self.assertEqual(str, type(output)) - elif type(output) == unicode: - self.assertEqual(unicode, type(output)) - else: - self.assertTrue(False) - else: - self.assertEqual(str, type(output)) + self.assertIsInstance(output, str)