OLD | NEW |
(Empty) | |
| 1 # Copyright (c) 2009-2010 Arista Networks, Inc. - James Lingard |
| 2 # Copyright (c) 2004-2010 LOGILAB S.A. (Paris, FRANCE). |
| 3 # http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| 4 # This program is free software; you can redistribute it and/or modify it under |
| 5 # the terms of the GNU General Public License as published by the Free Software |
| 6 # Foundation; either version 2 of the License, or (at your option) any later |
| 7 # version. |
| 8 # |
| 9 # This program is distributed in the hope that it will be useful, but WITHOUT |
| 10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details |
| 12 # |
| 13 # You should have received a copy of the GNU General Public License along with |
| 14 # this program; if not, write to the Free Software Foundation, Inc., |
| 15 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 16 |
| 17 |
| 18 """Checker for string formatting operations. |
| 19 """ |
| 20 |
| 21 import string |
| 22 from logilab import astng |
| 23 from pylint.interfaces import IASTNGChecker |
| 24 from pylint.checkers import BaseChecker |
| 25 from pylint.checkers import utils |
| 26 |
| 27 |
| 28 MSGS = { |
| 29 'E1300': ("Unsupported format character %r (%#02x) at index %d", |
| 30 "Used when a unsupported format character is used in a format\ |
| 31 string."), |
| 32 'E1301': ("Format string ends in middle of conversion specifier", |
| 33 "Used when a format string terminates before the end of a \ |
| 34 conversion specifier."), |
| 35 'E1302': ("Mixing named and unnamed conversion specifiers in format string", |
| 36 "Used when a format string contains both named (e.g. '%(foo)d') \ |
| 37 and unnamed (e.g. '%d') conversion specifiers. This is also \ |
| 38 used when a named conversion specifier contains * for the \ |
| 39 minimum field width and/or precision."), |
| 40 'E1303': ("Expected mapping for format string, not %s", |
| 41 "Used when a format string that uses named conversion specifiers \ |
| 42 is used with an argument that is not a mapping."), |
| 43 'W1300': ("Format string dictionary key should be a string, not %s", |
| 44 "Used when a format string that uses named conversion specifiers \ |
| 45 is used with a dictionary whose keys are not all strings."), |
| 46 'W1301': ("Unused key %r in format string dictionary", |
| 47 "Used when a format string that uses named conversion specifiers \ |
| 48 is used with a dictionary that conWtains keys not required by the
\ |
| 49 format string."), |
| 50 'E1304': ("Missing key %r in format string dictionary", |
| 51 "Used when a format string that uses named conversion specifiers \ |
| 52 is used with a dictionary that doesn't contain all the keys \ |
| 53 required by the format string."), |
| 54 'E1305': ("Too many arguments for format string", |
| 55 "Used when a format string that uses unnamed conversion \ |
| 56 specifiers is given too few arguments."), |
| 57 'E1306': ("Not enough arguments for format string", |
| 58 "Used when a format string that uses unnamed conversion \ |
| 59 specifiers is given too many arguments"), |
| 60 } |
| 61 |
| 62 OTHER_NODES = (astng.Const, astng.List, astng.Backquote, |
| 63 astng.Lambda, astng.Function, |
| 64 astng.ListComp, astng.SetComp, astng.GenExpr) |
| 65 |
| 66 class StringFormatChecker(BaseChecker): |
| 67 """Checks string formatting operations to ensure that the format string |
| 68 is valid and the arguments match the format string. |
| 69 """ |
| 70 |
| 71 __implements__ = (IASTNGChecker,) |
| 72 name = 'string_format' |
| 73 msgs = MSGS |
| 74 |
| 75 def visit_binop(self, node): |
| 76 if node.op != '%': |
| 77 return |
| 78 left = node.left |
| 79 args = node.right |
| 80 |
| 81 if not (isinstance(left, astng.Const) |
| 82 and isinstance(left.value, basestring)): |
| 83 return |
| 84 format_string = left.value |
| 85 try: |
| 86 required_keys, required_num_args = \ |
| 87 utils.parse_format_string(format_string) |
| 88 except utils.UnsupportedFormatCharacter, e: |
| 89 c = format_string[e.index] |
| 90 self.add_message('E1300', node=node, args=(c, ord(c), e.index)) |
| 91 return |
| 92 except utils.IncompleteFormatString: |
| 93 self.add_message('E1301', node=node) |
| 94 return |
| 95 if required_keys and required_num_args: |
| 96 # The format string uses both named and unnamed format |
| 97 # specifiers. |
| 98 self.add_message('E1302', node=node) |
| 99 elif required_keys: |
| 100 # The format string uses only named format specifiers. |
| 101 # Check that the RHS of the % operator is a mapping object |
| 102 # that contains precisely the set of keys required by the |
| 103 # format string. |
| 104 if isinstance(args, astng.Dict): |
| 105 keys = set() |
| 106 unknown_keys = False |
| 107 for k, v in args.items: |
| 108 if isinstance(k, astng.Const): |
| 109 key = k.value |
| 110 if isinstance(key, basestring): |
| 111 keys.add(key) |
| 112 else: |
| 113 self.add_message('W1300', node=node, args=key) |
| 114 else: |
| 115 # One of the keys was something other than a |
| 116 # constant. Since we can't tell what it is, |
| 117 # supress checks for missing keys in the |
| 118 # dictionary. |
| 119 unknown_keys = True |
| 120 if not unknown_keys: |
| 121 for key in required_keys: |
| 122 if key not in keys: |
| 123 self.add_message('E1304', node=node, args=key) |
| 124 for key in keys: |
| 125 if key not in required_keys: |
| 126 self.add_message('W1301', node=node, args=key) |
| 127 elif isinstance(args, OTHER_NODES + (astng.Tuple,)): |
| 128 type_name = type(args).__name__ |
| 129 self.add_message('E1303', node=node, args=type_name) |
| 130 # else: |
| 131 # The RHS of the format specifier is a name or |
| 132 # expression. It may be a mapping object, so |
| 133 # there's nothing we can check. |
| 134 else: |
| 135 # The format string uses only unnamed format specifiers. |
| 136 # Check that the number of arguments passed to the RHS of |
| 137 # the % operator matches the number required by the format |
| 138 # string. |
| 139 if isinstance(args, astng.Tuple): |
| 140 num_args = len(args.elts) |
| 141 elif isinstance(args, OTHER_NODES + (astng.Dict, astng.DictComp)): |
| 142 num_args = 1 |
| 143 else: |
| 144 # The RHS of the format specifier is a name or |
| 145 # expression. It could be a tuple of unknown size, so |
| 146 # there's nothing we can check. |
| 147 num_args = None |
| 148 if num_args is not None: |
| 149 if num_args > required_num_args: |
| 150 self.add_message('E1305', node=node) |
| 151 elif num_args < required_num_args: |
| 152 self.add_message('E1306', node=node) |
| 153 |
| 154 |
| 155 def register(linter): |
| 156 """required method to auto register this checker """ |
| 157 linter.register_checker(StringFormatChecker(linter)) |
OLD | NEW |