| 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))
 | 
| 
 |