diff --git a/changelogs/fragments/ansible-test-sanity-compile.yml b/changelogs/fragments/ansible-test-sanity-compile.yml new file mode 100644 index 00000000000..7f077342f91 --- /dev/null +++ b/changelogs/fragments/ansible-test-sanity-compile.yml @@ -0,0 +1,2 @@ +minor_changes: + - ansible-test - Rewrite the ``compile`` sanity test to improve error handling and support Python 3.10. diff --git a/test/lib/ansible_test/_data/sanity/compile/compile.py b/test/lib/ansible_test/_data/sanity/compile/compile.py index 61910eee121..3f6fc962600 100755 --- a/test/lib/ansible_test/_data/sanity/compile/compile.py +++ b/test/lib/ansible_test/_data/sanity/compile/compile.py @@ -3,38 +3,44 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -import warnings - -with warnings.catch_warnings(): - # The parser module is deprecated as of Python 3.9. - # This implementation will need to be updated to use another solution. - # Until then, disable the deprecation warnings to prevent test failures. - warnings.simplefilter('ignore', DeprecationWarning) - import parser - import sys +ENCODING = 'utf-8' +ERRORS = 'replace' +Text = type(u'') -def main(): - status = 0 +def main(): for path in sys.argv[1:] or sys.stdin.read().splitlines(): with open(path, 'rb') as source_fd: - if sys.version_info[0] == 3: - source = source_fd.read().decode('utf-8') - else: - source = source_fd.read() + source = source_fd.read() try: - parser.suite(source) - except SyntaxError: - ex = sys.exc_info()[1] - status = 1 - message = ex.text.splitlines()[0].strip() - sys.stdout.write("%s:%d:%d: SyntaxError: %s\n" % (path, ex.lineno, ex.offset, message)) - sys.stdout.flush() - - sys.exit(status) + compile(source, path, 'exec', dont_inherit=True) + except SyntaxError as ex: + extype, message, lineno, offset = type(ex), ex.text, ex.lineno, ex.offset + except BaseException as ex: # pylint: disable=broad-except + extype, message, lineno, offset = type(ex), str(ex), 0, 0 + else: + continue + + result = "%s:%d:%d: %s: %s" % (path, lineno, offset, extype.__name__, safe_message(message)) + + if sys.version_info <= (3,): + result = result.encode(ENCODING, ERRORS) + + print(result) + + +def safe_message(value): + """Given an input value as text or bytes, return the first non-empty line as text, ensuring it can be round-tripped as UTF-8.""" + if isinstance(value, Text): + value = value.encode(ENCODING, ERRORS) + + value = value.decode(ENCODING, ERRORS) + value = value.strip().splitlines()[0].strip() + + return value if __name__ == '__main__':