diff options
| -rw-r--r-- | ChangeLog | 2 | ||||
| -rw-r--r-- | checkers/strings.py | 56 | ||||
| -rw-r--r-- | test/functional/string_formatting.py | 10 | ||||
| -rw-r--r-- | test/functional/string_formatting.txt | 4 |
4 files changed, 51 insertions, 21 deletions
@@ -30,6 +30,8 @@ ChangeLog for Pylint * Analyze only strings by the string format checker. Closes issue #287. + * Properly handle nested format string fields. Closes issue #294. + 2014-07-26 -- 1.3.0 * Allow hanging continued indentation for implicitly concatenated diff --git a/checkers/strings.py b/checkers/strings.py index 9d4f13a..714a1a2 100644 --- a/checkers/strings.py +++ b/checkers/strings.py @@ -133,17 +133,13 @@ else: # the output return keyname, _field_iterator_convertor(fielditerator) -def parse_format_method_string(format_string): - """ - Parses a PEP 3101 format string, returning a tuple of - (keys, num_args, manual_pos_arg), - where keys is the set of mapping keys in the format string, num_args - is the number of arguments required by the format string and - manual_pos_arg is the number of arguments passed with the position. + +def collect_string_fields(format_string): + """ Given a format string, return an iterator + of all the valid format fields. It handles nested fields + as well. """ - keys = [] - num_args = 0 - manual_pos_arg = 0 + formatter = string.Formatter() parseiterator = formatter.parse(format_string) try: @@ -152,21 +148,39 @@ def parse_format_method_string(format_string): # not a replacement format continue name = result[1] - if name and str(name).isdigit(): - manual_pos_arg += 1 - elif name: - keyname, fielditerator = split_format_field_names(name) - if not isinstance(keyname, str): - # In Python 2 it will return long which will lead - # to different output between 2 and 3 - keyname = int(keyname) - keys.append((keyname, list(fielditerator))) - else: - num_args += 1 + nested = result[2] + yield name + if nested: + for field in collect_string_fields(nested): + yield field except ValueError: # probably the format string is invalid # should we check the argument of the ValueError? raise utils.IncompleteFormatString(format_string) + +def parse_format_method_string(format_string): + """ + Parses a PEP 3101 format string, returning a tuple of + (keys, num_args, manual_pos_arg), + where keys is the set of mapping keys in the format string, num_args + is the number of arguments required by the format string and + manual_pos_arg is the number of arguments passed with the position. + """ + keys = [] + num_args = 0 + manual_pos_arg = 0 + for name in collect_string_fields(format_string): + if name and str(name).isdigit(): + manual_pos_arg += 1 + elif name: + keyname, fielditerator = split_format_field_names(name) + if not isinstance(keyname, str): + # In Python 2 it will return long which will lead + # to different output between 2 and 3 + keyname = int(keyname) + keys.append((keyname, list(fielditerator))) + else: + num_args += 1 return keys, num_args, manual_pos_arg def get_args(callfunc): diff --git a/test/functional/string_formatting.py b/test/functional/string_formatting.py index 1714cf4..91b91ba 100644 --- a/test/functional/string_formatting.py +++ b/test/functional/string_formatting.py @@ -104,3 +104,13 @@ def good_issue287(): ret['comment'] = 'MySQL grant {0} is set to be revoked'
ret['comment'] = ret['comment'].format(name)
return ret, name
+
+def nested_issue294():
+ """ Test nested format fields. """
+ '{0:>{1}}'.format(42, 24)
+ '{0:{a[1]}} {a}'.format(1, a=[1, 2])
+ '{:>{}}'.format(42, 24)
+ '{0:>{1}}'.format(42) # [too-few-format-args]
+ '{0:>{1}}'.format(42, 24, 54) # [too-many-format-args]
+ '{0:{a[1]}}'.format(1) # [missing-format-argument-key]
+ '{0:{a.x}}'.format(1, a=2) # [missing-format-attribute]
diff --git a/test/functional/string_formatting.txt b/test/functional/string_formatting.txt index 6ec8944..ffc00fb 100644 --- a/test/functional/string_formatting.txt +++ b/test/functional/string_formatting.txt @@ -29,3 +29,7 @@ too-many-format-args:85:pprint_bad:Too many arguments for format string too-few-format-args:86:pprint_bad:Not enough arguments for format string too-few-format-args:87:pprint_bad:Not enough arguments for format string too-many-format-args:88:pprint_bad:Too many arguments for format string +too-few-format-args:113:nested_issue294:Not enough arguments for format string +too-many-format-args:114:nested_issue294:Too many arguments for format string +missing-format-argument-key:115:nested_issue294:Missing keyword argument 'a' for format string +missing-format-attribute:116:nested_issue294:Missing format attribute 'x' in format specifier 'a.x' |
