~ubuntu-branches/ubuntu/quantal/pylint/quantal

« back to all changes in this revision

Viewing changes to checkers/string_format.py

  • Committer: Bazaar Package Importer
  • Author(s): Sandro Tosi
  • Date: 2011-08-02 20:05:50 UTC
  • mfrom: (7.2.2 sid)
  • Revision ID: james.westby@ubuntu.com-20110802200550-5efk0cfszfzjpjdn
Tags: 0.24.0-1
* New upstream release
* debian/copyright
  - bump upstream and packaging copyright years
* debian/rules
  - add build-arch build-indep targets, as suggested by lintian
* debian/control
  - bump Standards-Version to 3.9.2 (no changes needed)

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
from logilab import astng
23
23
from pylint.interfaces import IASTNGChecker
24
24
from pylint.checkers import BaseChecker
 
25
from pylint.checkers import utils
 
26
 
25
27
 
26
28
MSGS = {
27
 
    'E9900': ("Unsupported format character %r (%#02x) at index %d",
 
29
    'E1300': ("Unsupported format character %r (%#02x) at index %d",
28
30
              "Used when a unsupported format character is used in a format\
29
31
              string."),
30
 
    'E9901': ("Format string ends in middle of conversion specifier",
 
32
    'E1301': ("Format string ends in middle of conversion specifier",
31
33
              "Used when a format string terminates before the end of a \
32
34
              conversion specifier."),
33
 
    'E9902': ("Mixing named and unnamed conversion specifiers in format string",
 
35
    'E1302': ("Mixing named and unnamed conversion specifiers in format string",
34
36
              "Used when a format string contains both named (e.g. '%(foo)d') \
35
37
              and unnamed (e.g. '%d') conversion specifiers.  This is also \
36
38
              used when a named conversion specifier contains * for the \
37
39
              minimum field width and/or precision."),
38
 
    'E9903': ("Expected mapping for format string, not %s",
 
40
    'E1303': ("Expected mapping for format string, not %s",
39
41
              "Used when a format string that uses named conversion specifiers \
40
42
              is used with an argument that is not a mapping."),
41
 
    'W9900': ("Format string dictionary key should be a string, not %s",
 
43
    'W1300': ("Format string dictionary key should be a string, not %s",
42
44
              "Used when a format string that uses named conversion specifiers \
43
45
              is used with a dictionary whose keys are not all strings."),
44
 
    'W9901': ("Unused key %r in format string dictionary",
 
46
    'W1301': ("Unused key %r in format string dictionary",
45
47
              "Used when a format string that uses named conversion specifiers \
46
48
              is used with a dictionary that conWtains keys not required by the \
47
49
              format string."),
48
 
    'E9904': ("Missing key %r in format string dictionary",
 
50
    'E1304': ("Missing key %r in format string dictionary",
49
51
              "Used when a format string that uses named conversion specifiers \
50
52
              is used with a dictionary that doesn't contain all the keys \
51
53
              required by the format string."),
52
 
    'E9905': ("Too many arguments for format string",
 
54
    'E1305': ("Too many arguments for format string",
53
55
              "Used when a format string that uses unnamed conversion \
54
56
              specifiers is given too few arguments."),
55
 
    'E9906': ("Not enough arguments for format string",
 
57
    'E1306': ("Not enough arguments for format string",
56
58
              "Used when a format string that uses unnamed conversion \
57
59
              specifiers is given too many arguments"),
58
60
    }
59
61
 
60
 
class IncompleteFormatString(Exception):
61
 
    """A format string ended in the middle of a format specifier."""
62
 
    pass
63
 
 
64
 
class UnsupportedFormatCharacter(Exception):
65
 
    """A format character in a format string is not one of the supported
66
 
    format characters."""
67
 
    def __init__(self, index):
68
 
        Exception.__init__(self, index)
69
 
        self.index = index
70
 
 
71
 
def parse_format_string(format_string):
72
 
    """Parses a format string, returning a tuple of (keys, num_args), where keys
73
 
    is the set of mapping keys in the format string, and num_args is the number
74
 
    of arguments required by the format string.  Raises
75
 
    IncompleteFormatString or UnsupportedFormatCharacter if a
76
 
    parse error occurs."""
77
 
    keys = set()
78
 
    num_args = 0
79
 
    def next_char(i):
80
 
        i += 1
81
 
        if i == len(format_string):
82
 
            raise IncompleteFormatString
83
 
        return (i, format_string[i])
84
 
    i = 0
85
 
    while i < len(format_string):
86
 
        c = format_string[i]
87
 
        if c == '%':
88
 
            i, c = next_char(i)
89
 
            # Parse the mapping key (optional).
90
 
            key = None
91
 
            if c == '(':
92
 
                depth = 1
93
 
                i, c = next_char(i)
94
 
                key_start = i
95
 
                while depth != 0:
96
 
                    if c == '(':
97
 
                        depth += 1
98
 
                    elif c == ')':
99
 
                        depth -= 1
100
 
                    i, c = next_char(i)
101
 
                key_end = i - 1
102
 
                key = format_string[key_start:key_end]
103
 
 
104
 
            # Parse the conversion flags (optional).
105
 
            while c in '#0- +':
106
 
                i, c = next_char(i)
107
 
            # Parse the minimum field width (optional).
108
 
            if c == '*':
109
 
                num_args += 1
110
 
                i, c = next_char(i)
111
 
            else:
112
 
                while c in string.digits:
113
 
                    i, c = next_char(i)
114
 
            # Parse the precision (optional).
115
 
            if c == '.':
116
 
                i, c = next_char(i)
117
 
                if c == '*':
118
 
                    num_args += 1
119
 
                    i, c = next_char(i)
120
 
                else:
121
 
                    while c in string.digits:
122
 
                        i, c = next_char(i)
123
 
            # Parse the length modifier (optional).
124
 
            if c in 'hlL':
125
 
                i, c = next_char(i)
126
 
            # Parse the conversion type (mandatory).
127
 
            if c not in 'diouxXeEfFgGcrs%':
128
 
                raise UnsupportedFormatCharacter(i)
129
 
            if key:
130
 
                keys.add(key)
131
 
            elif c != '%':
132
 
                num_args += 1
133
 
        i += 1
134
 
    return keys, num_args
135
 
 
136
62
OTHER_NODES = (astng.Const, astng.List, astng.Backquote,
137
63
               astng.Lambda, astng.Function,
138
64
               astng.ListComp, astng.SetComp, astng.GenExpr)
158
84
        format_string = left.value
159
85
        try:
160
86
            required_keys, required_num_args = \
161
 
                parse_format_string(format_string)
162
 
        except UnsupportedFormatCharacter, e:
 
87
                utils.parse_format_string(format_string)
 
88
        except utils.UnsupportedFormatCharacter, e:
163
89
            c = format_string[e.index]
164
 
            self.add_message('E9900', node=node, args=(c, ord(c), e.index))
 
90
            self.add_message('E1300', node=node, args=(c, ord(c), e.index))
165
91
            return
166
 
        except IncompleteFormatString:
167
 
            self.add_message('E9901', node=node)
 
92
        except utils.IncompleteFormatString:
 
93
            self.add_message('E1301', node=node)
168
94
            return
169
95
        if required_keys and required_num_args:
170
96
            # The format string uses both named and unnamed format
171
97
            # specifiers.
172
 
            self.add_message('E9902', node=node)
 
98
            self.add_message('E1302', node=node)
173
99
        elif required_keys:
174
100
            # The format string uses only named format specifiers.
175
101
            # Check that the RHS of the % operator is a mapping object
184
110
                        if isinstance(key, basestring):
185
111
                            keys.add(key)
186
112
                        else:
187
 
                            self.add_message('W9900', node=node, args=key)
 
113
                            self.add_message('W1300', node=node, args=key)
188
114
                    else:
189
115
                        # One of the keys was something other than a
190
116
                        # constant.  Since we can't tell what it is,
194
120
                if not unknown_keys:
195
121
                    for key in required_keys:
196
122
                        if key not in keys:
197
 
                            self.add_message('E9904', node=node, args=key)
 
123
                            self.add_message('E1304', node=node, args=key)
198
124
                for key in keys:
199
125
                    if key not in required_keys:
200
 
                        self.add_message('W9901', node=node, args=key)
 
126
                        self.add_message('W1301', node=node, args=key)
201
127
            elif isinstance(args, OTHER_NODES + (astng.Tuple,)):
202
128
                type_name = type(args).__name__
203
 
                self.add_message('E9903', node=node, args=type_name)
 
129
                self.add_message('E1303', node=node, args=type_name)
204
130
            # else:
205
131
                # The RHS of the format specifier is a name or
206
132
                # expression.  It may be a mapping object, so
221
147
                num_args = None
222
148
            if num_args is not None:
223
149
                if num_args > required_num_args:
224
 
                    self.add_message('E9905', node=node)
 
150
                    self.add_message('E1305', node=node)
225
151
                elif num_args < required_num_args:
226
 
                    self.add_message('E9906', node=node)
 
152
                    self.add_message('E1306', node=node)
227
153
 
228
154
 
229
155
def register(linter):