From de792ba3c2e76fb537069f2ea0174645a7d19a89 Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Thu, 8 Oct 2015 10:04:15 -0400 Subject: [PATCH] Improve handling of unicode errors Fixes #12669 --- bin/ansible | 9 +++++---- lib/ansible/errors/__init__.py | 5 ++++- test/units/errors/test_errors.py | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/bin/ansible b/bin/ansible index 209b235c88d..a117856d899 100755 --- a/bin/ansible +++ b/bin/ansible @@ -38,6 +38,7 @@ import traceback from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError from ansible.utils.display import Display +from ansible.utils.unicode import to_unicode ######################################## ### OUTPUT OF LAST RESORT ### @@ -82,10 +83,10 @@ if __name__ == '__main__': except AnsibleOptionsError as e: cli.parser.print_help() - display.error(str(e), wrap_text=False) + display.error(to_unicode(e), wrap_text=False) sys.exit(5) except AnsibleParserError as e: - display.error(str(e), wrap_text=False) + display.error(to_unicode(e), wrap_text=False) sys.exit(4) # TQM takes care of these, but leaving comment to reserve the exit codes # except AnsibleHostUnreachable as e: @@ -95,14 +96,14 @@ if __name__ == '__main__': # display.error(str(e)) # sys.exit(2) except AnsibleError as e: - display.error(str(e), wrap_text=False) + display.error(to_unicode(e), wrap_text=False) sys.exit(1) except KeyboardInterrupt: display.error("User interrupted execution") sys.exit(99) except Exception as e: have_cli_options = cli is not None and cli.options is not None - display.error("Unexpected Exception: %s" % str(e), wrap_text=False) + display.error("Unexpected Exception: %s" % to_unicode(e), wrap_text=False) if not have_cli_options or have_cli_options and cli.options.verbosity > 2: display.display("the full traceback was:\n\n%s" % traceback.format_exc()) else: diff --git a/lib/ansible/errors/__init__.py b/lib/ansible/errors/__init__.py index 63fb8ef023a..f46c4f34c9a 100644 --- a/lib/ansible/errors/__init__.py +++ b/lib/ansible/errors/__init__.py @@ -22,6 +22,7 @@ __metaclass__ = type import os from ansible.errors.yaml_strings import * +from ansible.utils.unicode import to_unicode, to_bytes class AnsibleError(Exception): ''' @@ -48,7 +49,7 @@ class AnsibleError(Exception): if obj and isinstance(obj, AnsibleBaseYAMLObject): extended_error = self._get_extended_error() if extended_error: - self.message = 'ERROR! %s\n\n%s' % (message, extended_error) + self.message = 'ERROR! %s\n\n%s' % (message, to_bytes(extended_error)) else: self.message = 'ERROR! %s' % message @@ -96,6 +97,8 @@ class AnsibleError(Exception): error_message += YAML_POSITION_DETAILS % (src_file, line_number, col_number) if src_file not in ('', '') and self._show_content: (target_line, prev_line) = self._get_error_lines_from_file(src_file, line_number - 1) + target_line = to_unicode(target_line) + prev_line = to_unicode(prev_line) if target_line: stripped_line = target_line.replace(" ","") arrow_line = (" " * (col_number-1)) + "^ here" diff --git a/test/units/errors/test_errors.py b/test/units/errors/test_errors.py index 3993ea5061b..4c09c0089b3 100644 --- a/test/units/errors/test_errors.py +++ b/test/units/errors/test_errors.py @@ -31,6 +31,7 @@ class TestErrors(unittest.TestCase): def setUp(self): self.message = 'This is the error message' + self.unicode_message = 'This is an error with \xf0\x9f\x98\xa8 in it' self.obj = AnsibleBaseYAMLObject() @@ -42,6 +43,11 @@ class TestErrors(unittest.TestCase): self.assertEqual(e.message, 'ERROR! ' + self.message) self.assertEqual(e.__repr__(), 'ERROR! ' + self.message) + def test_basic_unicode_error(self): + e = AnsibleError(self.unicode_message) + self.assertEqual(e.message, 'ERROR! ' + self.unicode_message) + self.assertEqual(e.__repr__(), 'ERROR! ' + self.unicode_message) + @patch.object(AnsibleError, '_get_error_lines_from_file') def test_error_with_object(self, mock_method): self.obj.ansible_pos = ('foo.yml', 1, 1) @@ -66,3 +72,12 @@ class TestErrors(unittest.TestCase): e = AnsibleError(self.message, self.obj) self.assertEqual(e.message, "ERROR! This is the error message\n\nThe error appears to have been in 'foo.yml': line 2, column 1, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\n(specified line no longer in file, maybe it changed?)") + m = mock_open() + m.return_value.readlines.return_value = ['this line has unicode \xf0\x9f\x98\xa8 in it!\n'] + + with patch('{0}.open'.format(BUILTINS), m): + # this line will be found in the file + self.obj.ansible_pos = ('foo.yml', 1, 1) + e = AnsibleError(self.unicode_message, self.obj) + self.assertEqual(e.message, "ERROR! This is an error with \xf0\x9f\x98\xa8 in it\n\nThe error appears to have been in 'foo.yml': line 1, column 1, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\nthis line has unicode \xf0\x9f\x98\xa8 in it!\n^ here\n") +