From ba717a08ec984dc4e5f4fe11835fc0f7198e679b Mon Sep 17 00:00:00 2001 From: Boden R Date: Fri, 3 Jun 2016 13:32:03 -0600 Subject: [PATCH] Localized exception message hacking check As per [1], this patch adds a new hacking check that verifies raised exceptions don't use unlocalized string messages. This patch also adds unit tests for the new check and enables checks for neutron-lib itself (as per tox.ini). Note: Perhaps this check better belongs in openstack-dev/hacking but it seems we already have a number of more generic checks in neutron. We can discuss that here need be. NB: Without [1] this new hacking check fails when running pep8 on neutron-lib. [1] https://review.openstack.org/#/c/324374/ Change-Id: Ia13f190ec6843ab360d115871d345c4ddc302cc5 --- HACKING.rst | 1 + neutron_lib/hacking/checks.py | 1 + neutron_lib/hacking/translation_checks.py | 15 ++++++++++++ neutron_lib/tests/unit/hacking/test_checks.py | 24 +++++++++++++++---- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/HACKING.rst b/HACKING.rst index 71a3555..d6d0091 100644 --- a/HACKING.rst +++ b/HACKING.rst @@ -19,3 +19,4 @@ Neutron Specific Commandments - [N531] Validate that LOG messages, except debug ones, have translations - [N532] Validate that LOG.warning is used instead of LOG.warn. The latter is deprecated. - [N533] Validate that debug level logs are not translated +- [N534] Exception messages should be translated diff --git a/neutron_lib/hacking/checks.py b/neutron_lib/hacking/checks.py index 20f6263..07a824b 100644 --- a/neutron_lib/hacking/checks.py +++ b/neutron_lib/hacking/checks.py @@ -160,3 +160,4 @@ def factory(register): register(translation_checks.validate_log_translations) register(translation_checks.no_translate_debug_logs) register(translation_checks.check_log_warn_deprecated) + register(translation_checks.check_raised_localized_exceptions) diff --git a/neutron_lib/hacking/translation_checks.py b/neutron_lib/hacking/translation_checks.py index 4d1be0b..fff1682 100644 --- a/neutron_lib/hacking/translation_checks.py +++ b/neutron_lib/hacking/translation_checks.py @@ -75,3 +75,18 @@ def no_translate_debug_logs(logical_line, filename): for hint in _all_hints: if logical_line.startswith("LOG.debug(%s(" % hint): yield(0, "N533 Don't translate debug level logs") + + +def check_raised_localized_exceptions(logical_line, filename): + # NOTE(boden): tox.ini doesn't permit per check exclusion + if "/tests/" in filename: + return + + logical_line = logical_line.strip() + raised_search = re.compile( + r"raise (?:\w*)\((.*)\)").match(logical_line) + if raised_search: + exception_msg = raised_search.groups()[0] + if exception_msg.startswith("\"") or exception_msg.startswith("\'"): + msg = "N534: Untranslated exception message." + yield (logical_line.index(exception_msg), msg) diff --git a/neutron_lib/tests/unit/hacking/test_checks.py b/neutron_lib/tests/unit/hacking/test_checks.py index bed5184..7ae58fc 100644 --- a/neutron_lib/tests/unit/hacking/test_checks.py +++ b/neutron_lib/tests/unit/hacking/test_checks.py @@ -19,12 +19,12 @@ from neutron_lib.tests import _base as base class HackingTestCase(base.BaseTestCase): - def assertLinePasses(self, func, line): + def assertLinePasses(self, func, *args): with testtools.ExpectedException(StopIteration): - next(func(line)) + next(func(*args)) - def assertLineFails(self, func, line): - self.assertIsInstance(next(func(line)), tuple) + def assertLineFails(self, func, *args): + self.assertIsInstance(next(func(*args)), tuple) def test_use_jsonutils(self): def __get_msg(fun): @@ -149,3 +149,19 @@ class HackingTestCase(base.BaseTestCase): bad = "LOG.warn(_LW('i am deprecated!'))" self.assertEqual( 1, len(list(tc.check_log_warn_deprecated(bad, 'f')))) + + def test_check_localized_exception_messages(self): + f = tc.check_raised_localized_exceptions + self.assertLineFails(f, " raise KeyError('Error text')", '') + self.assertLineFails(f, ' raise KeyError("Error text")', '') + self.assertLinePasses(f, ' raise KeyError(_("Error text"))', '') + self.assertLinePasses(f, ' raise KeyError(_ERR("Error text"))', '') + self.assertLinePasses(f, " raise KeyError(translated_msg)", '') + self.assertLinePasses(f, '# raise KeyError("Not translated")', '') + self.assertLinePasses(f, 'print("raise KeyError("Not ' + 'translated")")', '') + + def test_check_localized_exception_message_skip_tests(self): + f = tc.check_raised_localized_exceptions + self.assertLinePasses(f, "raise KeyError('Error text')", + 'neutron_lib/tests/unit/mytest.py')