Index: third_party/pylint/checkers/string_format.py |
diff --git a/third_party/pylint/checkers/string_format.py b/third_party/pylint/checkers/string_format.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c420a604b74fcf8d7755cc23be8e24f840cad79d |
--- /dev/null |
+++ b/third_party/pylint/checkers/string_format.py |
@@ -0,0 +1,157 @@ |
+# Copyright (c) 2009-2010 Arista Networks, Inc. - James Lingard |
+# Copyright (c) 2004-2010 LOGILAB S.A. (Paris, FRANCE). |
+# http://www.logilab.fr/ -- mailto:contact@logilab.fr |
+# This program is free software; you can redistribute it and/or modify it under |
+# the terms of the GNU General Public License as published by the Free Software |
+# Foundation; either version 2 of the License, or (at your option) any later |
+# version. |
+# |
+# This program is distributed in the hope that it will be useful, but WITHOUT |
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details |
+# |
+# You should have received a copy of the GNU General Public License along with |
+# this program; if not, write to the Free Software Foundation, Inc., |
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
+ |
+ |
+"""Checker for string formatting operations. |
+""" |
+ |
+import string |
+from logilab import astng |
+from pylint.interfaces import IASTNGChecker |
+from pylint.checkers import BaseChecker |
+from pylint.checkers import utils |
+ |
+ |
+MSGS = { |
+ 'E1300': ("Unsupported format character %r (%#02x) at index %d", |
+ "Used when a unsupported format character is used in a format\ |
+ string."), |
+ 'E1301': ("Format string ends in middle of conversion specifier", |
+ "Used when a format string terminates before the end of a \ |
+ conversion specifier."), |
+ 'E1302': ("Mixing named and unnamed conversion specifiers in format string", |
+ "Used when a format string contains both named (e.g. '%(foo)d') \ |
+ and unnamed (e.g. '%d') conversion specifiers. This is also \ |
+ used when a named conversion specifier contains * for the \ |
+ minimum field width and/or precision."), |
+ 'E1303': ("Expected mapping for format string, not %s", |
+ "Used when a format string that uses named conversion specifiers \ |
+ is used with an argument that is not a mapping."), |
+ 'W1300': ("Format string dictionary key should be a string, not %s", |
+ "Used when a format string that uses named conversion specifiers \ |
+ is used with a dictionary whose keys are not all strings."), |
+ 'W1301': ("Unused key %r in format string dictionary", |
+ "Used when a format string that uses named conversion specifiers \ |
+ is used with a dictionary that conWtains keys not required by the \ |
+ format string."), |
+ 'E1304': ("Missing key %r in format string dictionary", |
+ "Used when a format string that uses named conversion specifiers \ |
+ is used with a dictionary that doesn't contain all the keys \ |
+ required by the format string."), |
+ 'E1305': ("Too many arguments for format string", |
+ "Used when a format string that uses unnamed conversion \ |
+ specifiers is given too few arguments."), |
+ 'E1306': ("Not enough arguments for format string", |
+ "Used when a format string that uses unnamed conversion \ |
+ specifiers is given too many arguments"), |
+ } |
+ |
+OTHER_NODES = (astng.Const, astng.List, astng.Backquote, |
+ astng.Lambda, astng.Function, |
+ astng.ListComp, astng.SetComp, astng.GenExpr) |
+ |
+class StringFormatChecker(BaseChecker): |
+ """Checks string formatting operations to ensure that the format string |
+ is valid and the arguments match the format string. |
+ """ |
+ |
+ __implements__ = (IASTNGChecker,) |
+ name = 'string_format' |
+ msgs = MSGS |
+ |
+ def visit_binop(self, node): |
+ if node.op != '%': |
+ return |
+ left = node.left |
+ args = node.right |
+ |
+ if not (isinstance(left, astng.Const) |
+ and isinstance(left.value, basestring)): |
+ return |
+ format_string = left.value |
+ try: |
+ required_keys, required_num_args = \ |
+ utils.parse_format_string(format_string) |
+ except utils.UnsupportedFormatCharacter, e: |
+ c = format_string[e.index] |
+ self.add_message('E1300', node=node, args=(c, ord(c), e.index)) |
+ return |
+ except utils.IncompleteFormatString: |
+ self.add_message('E1301', node=node) |
+ return |
+ if required_keys and required_num_args: |
+ # The format string uses both named and unnamed format |
+ # specifiers. |
+ self.add_message('E1302', node=node) |
+ elif required_keys: |
+ # The format string uses only named format specifiers. |
+ # Check that the RHS of the % operator is a mapping object |
+ # that contains precisely the set of keys required by the |
+ # format string. |
+ if isinstance(args, astng.Dict): |
+ keys = set() |
+ unknown_keys = False |
+ for k, v in args.items: |
+ if isinstance(k, astng.Const): |
+ key = k.value |
+ if isinstance(key, basestring): |
+ keys.add(key) |
+ else: |
+ self.add_message('W1300', node=node, args=key) |
+ else: |
+ # One of the keys was something other than a |
+ # constant. Since we can't tell what it is, |
+ # supress checks for missing keys in the |
+ # dictionary. |
+ unknown_keys = True |
+ if not unknown_keys: |
+ for key in required_keys: |
+ if key not in keys: |
+ self.add_message('E1304', node=node, args=key) |
+ for key in keys: |
+ if key not in required_keys: |
+ self.add_message('W1301', node=node, args=key) |
+ elif isinstance(args, OTHER_NODES + (astng.Tuple,)): |
+ type_name = type(args).__name__ |
+ self.add_message('E1303', node=node, args=type_name) |
+ # else: |
+ # The RHS of the format specifier is a name or |
+ # expression. It may be a mapping object, so |
+ # there's nothing we can check. |
+ else: |
+ # The format string uses only unnamed format specifiers. |
+ # Check that the number of arguments passed to the RHS of |
+ # the % operator matches the number required by the format |
+ # string. |
+ if isinstance(args, astng.Tuple): |
+ num_args = len(args.elts) |
+ elif isinstance(args, OTHER_NODES + (astng.Dict, astng.DictComp)): |
+ num_args = 1 |
+ else: |
+ # The RHS of the format specifier is a name or |
+ # expression. It could be a tuple of unknown size, so |
+ # there's nothing we can check. |
+ num_args = None |
+ if num_args is not None: |
+ if num_args > required_num_args: |
+ self.add_message('E1305', node=node) |
+ elif num_args < required_num_args: |
+ self.add_message('E1306', node=node) |
+ |
+ |
+def register(linter): |
+ """required method to auto register this checker """ |
+ linter.register_checker(StringFormatChecker(linter)) |