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 |