summaryrefslogtreecommitdiff
path: root/checkers/utils.py
diff options
context:
space:
mode:
authorSylvain Th?nault <sylvain.thenault@logilab.fr>2011-07-08 09:57:24 +0200
committerSylvain Th?nault <sylvain.thenault@logilab.fr>2011-07-08 09:57:24 +0200
commit3dec29e58ac94cee8f10670c121ad44feb94549d (patch)
tree92304f1415547d8cb3a4786e4c1eef26fcc488af /checkers/utils.py
parente7c5418b5a3e219fb4795281fd92ae5bc9de6302 (diff)
downloadpylint-3dec29e58ac94cee8f10670c121ad44feb94549d.tar.gz
closes #69993: Additional string format checks for logging module
Diffstat (limited to 'checkers/utils.py')
-rw-r--r--checkers/utils.py76
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