[PATCH] Extend format string checks to translated format strings.

David Shea dshea at redhat.com
Wed Apr 23 19:23:29 UTC 2014


This applies the existing pylint format and logging string checks to
translated format and logging strings. The two new warning IDs are not
emitted themselves but are necessary in order to provide an entry point
to the overriden visit_* functions.
---
 tests/pylint/intl.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 71 insertions(+), 1 deletion(-)

diff --git a/tests/pylint/intl.py b/tests/pylint/intl.py
index c8baaf3..dccf9b5 100644
--- a/tests/pylint/intl.py
+++ b/tests/pylint/intl.py
@@ -22,11 +22,38 @@
 import astroid
 
 from pylint.checkers import BaseChecker
+from pylint.checkers.strings import StringFormatChecker
+from pylint.checkers.logging import LoggingChecker
 from pylint.checkers.utils import check_messages
 from pylint.interfaces import IAstroidChecker
 
+from copy import copy
+
 translationMethods = ["_", "N_", "P_", "C_", "CN_", "CP_"]
 
+# Returns a list of the message strings for a given translation method call
+def _get_message_strings(node):
+    msgstrs = []
+
+    if node.func.name in ("_", "N_") and len(node.args) >= 1:
+        if isinstance(node.args[0], astroid.Const):
+            msgstrs.append(node.args[0].value)
+    elif node.func.name in ("C_", "CN_") and len(node.args) >= 2:
+        if isinstance(node.args[1], astroid.Const):
+            msgstrs.append(node.args[1].value)
+    elif node.func.name == "P_" and len(node.args) >= 2:
+        if isinstance(node.args[0], astroid.Const):
+            msgstrs.append(node.args[0].value)
+        if isinstance(node.args[1], astroid.Const):
+            msgstrs.append(node.args[1].value)
+    elif node.func.name == "CP_" and len(node.args) >= 3:
+        if isinstance(node.args[1], astroid.Const):
+            msgstrs.append(node.args[1].value)
+        if isinstance(node.args[2], astroid.Const):
+            msgstrs.append(node.args[2].value)
+
+    return msgstrs
+
 class IntlChecker(BaseChecker):
     __implements__ = (IAstroidChecker, )
     name = "internationalization"
@@ -38,7 +65,7 @@ class IntlChecker(BaseChecker):
                       "Calling _ at the module or class level results in translations to the wrong language")
            }
 
-    @check_messages("W9901")
+    @check_messages("found-percent-in-_")
     def visit_binop(self, node):
         if node.op != "%":
             return
@@ -58,6 +85,49 @@ class IntlChecker(BaseChecker):
             if isinstance(node.scope(), astroid.Module) or isinstance(node.scope(), astroid.Class):
                 self.add_message("W9902", node=node)
 
+# Extend LoggingChecker to check translated logging strings
+class IntlLoggingChecker(LoggingChecker):
+    msgs = {'W9903': ("Fake message for translated E/W120* checks",
+                      "translated-log",
+                      "This message is not emitted itself, but can be used to control the display of \
+                       logging format messages extended for translated strings")
+           }
+
+    @check_messages('translated-log')
+    def visit_callfunc(self, node):
+        if len(node.args) >= 1 and isinstance(node.args[0], astroid.CallFunc) and \
+                getattr(node.args[0].func, "name", "") in translationMethods:
+            for formatstr in _get_message_strings(node.args[0]):
+                # Both the node and the args need to be copied so we don't replace args
+                # on the original node.
+                copynode = copy(node)
+                copyargs = copy(node.args)
+                copyargs[0] = astroid.Const(formatstr)
+                copynode.args = copyargs
+                LoggingChecker.visit_callfunc(self, copynode)
+
+# Extend StringFormatChecker to check translated format strings
+class IntlStringFormatChecker(StringFormatChecker):
+    msgs = {'W9904': ("Fake message for translated E/W130* checks",
+                      "translated-format",
+                      "This message is not emitted itself, but can be used to control the display of \
+                       string format messages extended for translated strings")
+           }
+
+    @check_messages('translated-format')
+    def visit_binop(self, node):
+        if node.op != '%':
+            return
+
+        if isinstance(node.left, astroid.CallFunc) and getattr(node.left.func, "name", "") in translationMethods:
+            for formatstr in _get_message_strings(node.left):
+                # Create a copy of the node with just the message string as the format
+                copynode = copy(node)
+                copynode.left = astroid.Const(formatstr)
+                StringFormatChecker.visit_binop(self, copynode)
+
 def register(linter):
     """required method to auto register this checker """
     linter.register_checker(IntlChecker(linter))
+    linter.register_checker(IntlLoggingChecker(linter))
+    linter.register_checker(IntlStringFormatChecker(linter))
-- 
1.9.0



More information about the anaconda-patches mailing list