diff options
author | Sylvain Th?nault <sylvain.thenault@logilab.fr> | 2011-07-08 09:57:24 +0200 |
---|---|---|
committer | Sylvain Th?nault <sylvain.thenault@logilab.fr> | 2011-07-08 09:57:24 +0200 |
commit | 3dec29e58ac94cee8f10670c121ad44feb94549d (patch) | |
tree | 92304f1415547d8cb3a4786e4c1eef26fcc488af /checkers/utils.py | |
parent | e7c5418b5a3e219fb4795281fd92ae5bc9de6302 (diff) | |
download | pylint-3dec29e58ac94cee8f10670c121ad44feb94549d.tar.gz |
closes #69993: Additional string format checks for logging module
Diffstat (limited to 'checkers/utils.py')
-rw-r--r-- | checkers/utils.py | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/checkers/utils.py b/checkers/utils.py index 1cbd006..43d619f 100644 --- a/checkers/utils.py +++ b/checkers/utils.py @@ -18,6 +18,7 @@ """some functions that may be useful for various checkers """ +import string from logilab import astng from logilab.common.compat import builtins BUILTINS_NAME = builtins.__name__ @@ -211,3 +212,78 @@ def check_messages(*messages): return func return store_messages +class IncompleteFormatString(Exception): + """A format string ended in the middle of a format specifier.""" + pass + +class UnsupportedFormatCharacter(Exception): + """A format character in a format string is not one of the supported + format characters.""" + def __init__(self, index): + Exception.__init__(self, index) + self.index = index + +def parse_format_string(format_string): + """Parses a format string, returning a tuple of (keys, num_args), where keys + is the set of mapping keys in the format string, and num_args is the number + of arguments required by the format string. Raises + IncompleteFormatString or UnsupportedFormatCharacter if a + parse error occurs.""" + keys = set() + num_args = 0 + def next_char(i): + i += 1 + if i == len(format_string): + raise IncompleteFormatString + return (i, format_string[i]) + i = 0 + while i < len(format_string): + c = format_string[i] + if c == '%': + i, c = next_char(i) + # Parse the mapping key (optional). + key = None + if c == '(': + depth = 1 + i, c = next_char(i) + key_start = i + while depth != 0: + if c == '(': + depth += 1 + elif c == ')': + depth -= 1 + i, c = next_char(i) + key_end = i - 1 + key = format_string[key_start:key_end] + + # Parse the conversion flags (optional). + while c in '#0- +': + i, c = next_char(i) + # Parse the minimum field width (optional). + if c == '*': + num_args += 1 + i, c = next_char(i) + else: + while c in string.digits: + i, c = next_char(i) + # Parse the precision (optional). + if c == '.': + i, c = next_char(i) + if c == '*': + num_args += 1 + i, c = next_char(i) + else: + while c in string.digits: + i, c = next_char(i) + # Parse the length modifier (optional). + if c in 'hlL': + i, c = next_char(i) + # Parse the conversion type (mandatory). + if c not in 'diouxXeEfFgGcrs%': + raise UnsupportedFormatCharacter(i) + if key: + keys.add(key) + elif c != '%': + num_args += 1 + i += 1 + return keys, num_args |