| Index: third_party/pylint/checkers/strings.py
|
| diff --git a/third_party/pylint/checkers/strings.py b/third_party/pylint/checkers/strings.py
|
| index 0eda6a2cb60891773621a8a99508bb21ecdd53cd..e88085d44a40951cc6e83c78a902895c16d1413c 100644
|
| --- a/third_party/pylint/checkers/strings.py
|
| +++ b/third_party/pylint/checkers/strings.py
|
| @@ -21,10 +21,7 @@
|
| import sys
|
| import tokenize
|
| import string
|
| -try:
|
| - import numbers
|
| -except ImportError:
|
| - numbers = None
|
| +import numbers
|
|
|
| import astroid
|
|
|
| @@ -33,6 +30,9 @@ from pylint.checkers import BaseChecker, BaseTokenChecker
|
| from pylint.checkers import utils
|
| from pylint.checkers.utils import check_messages
|
|
|
| +import six
|
| +
|
| +
|
| _PY3K = sys.version_info[:2] >= (3, 0)
|
| _PY27 = sys.version_info[:2] == (2, 7)
|
|
|
| @@ -145,8 +145,8 @@ def collect_string_fields(format_string):
|
| """
|
|
|
| formatter = string.Formatter()
|
| - parseiterator = formatter.parse(format_string)
|
| try:
|
| + parseiterator = formatter.parse(format_string)
|
| for result in parseiterator:
|
| if all(item is None for item in result[1:]):
|
| # not a replacement format
|
| @@ -181,6 +181,7 @@ def parse_format_method_string(format_string):
|
| if isinstance(keyname, numbers.Number):
|
| # In Python 2 it will return long which will lead
|
| # to different output between 2 and 3
|
| + manual_pos_arg.add(keyname)
|
| keyname = int(keyname)
|
| keys.append((keyname, list(fielditerator)))
|
| else:
|
| @@ -233,13 +234,13 @@ class StringFormatChecker(BaseChecker):
|
| args = node.right
|
|
|
| if not (isinstance(left, astroid.Const)
|
| - and isinstance(left.value, basestring)):
|
| + and isinstance(left.value, six.string_types)):
|
| return
|
| format_string = left.value
|
| try:
|
| required_keys, required_num_args = \
|
| utils.parse_format_string(format_string)
|
| - except utils.UnsupportedFormatCharacter, e:
|
| + except utils.UnsupportedFormatCharacter as e:
|
| c = format_string[e.index]
|
| self.add_message('bad-format-character',
|
| node=node, args=(c, ord(c), e.index))
|
| @@ -262,7 +263,7 @@ class StringFormatChecker(BaseChecker):
|
| for k, _ in args.items:
|
| if isinstance(k, astroid.Const):
|
| key = k.value
|
| - if isinstance(key, basestring):
|
| + if isinstance(key, six.string_types):
|
| keys.add(key)
|
| else:
|
| self.add_message('bad-format-string-key',
|
| @@ -345,10 +346,17 @@ class StringMethodsChecker(BaseChecker):
|
| # We do this because our inference engine can't properly handle
|
| # redefinitions of the original string.
|
| # For more details, see issue 287.
|
| - if not isinstance(node.func.expr, astroid.Const):
|
| + #
|
| + # Note that there may not be any left side at all, if the format method
|
| + # has been assigned to another variable. See issue 351. For example:
|
| + #
|
| + # fmt = 'some string {}'.format
|
| + # fmt('arg')
|
| + if (isinstance(node.func, astroid.Getattr)
|
| + and not isinstance(node.func.expr, astroid.Const)):
|
| return
|
| try:
|
| - strnode = func.bound.infer().next()
|
| + strnode = next(func.bound.infer())
|
| except astroid.InferenceError:
|
| return
|
| if not isinstance(strnode, astroid.Const):
|
| @@ -366,10 +374,8 @@ class StringMethodsChecker(BaseChecker):
|
| self.add_message('bad-format-string', node=node)
|
| return
|
|
|
| - manual_fields = set(field[0] for field in fields
|
| - if isinstance(field[0], numbers.Number))
|
| named_fields = set(field[0] for field in fields
|
| - if isinstance(field[0], basestring))
|
| + if isinstance(field[0], six.string_types))
|
| if num_args and manual_pos:
|
| self.add_message('format-combined-specification',
|
| node=node)
|
| @@ -408,12 +414,7 @@ class StringMethodsChecker(BaseChecker):
|
| # num_args can be 0 if manual_pos is not.
|
| num_args = num_args or manual_pos
|
| if positional > num_args:
|
| - # We can have two possibilities:
|
| - # * "{0} {1}".format(a, b)
|
| - # * "{} {} {}".format(a, b, c, d)
|
| - # We can check the manual keys for the first one.
|
| - if len(manual_fields) != positional:
|
| - self.add_message('too-many-format-args', node=node)
|
| + self.add_message('too-many-format-args', node=node)
|
| elif positional < num_args:
|
| self.add_message('too-few-format-args', node=node)
|
|
|
| @@ -444,7 +445,7 @@ class StringMethodsChecker(BaseChecker):
|
| if argname in (astroid.YES, None):
|
| continue
|
| try:
|
| - argument = argname.infer().next()
|
| + argument = next(argname.infer())
|
| except astroid.InferenceError:
|
| continue
|
| if not specifiers or argument is astroid.YES:
|
| @@ -452,12 +453,9 @@ class StringMethodsChecker(BaseChecker):
|
| # use attribute / item access
|
| continue
|
| if argument.parent and isinstance(argument.parent, astroid.Arguments):
|
| - # Check to see if our argument is kwarg or vararg,
|
| - # and skip the check for this argument if so, because when inferring,
|
| - # astroid will return empty objects (dicts and tuples) and
|
| - # that can lead to false positives.
|
| - if argname.name in (argument.parent.kwarg, argument.parent.vararg):
|
| - continue
|
| + # Ignore any object coming from an argument,
|
| + # because we can't infer its value properly.
|
| + continue
|
| previous = argument
|
| parsed = []
|
| for is_attribute, specifier in specifiers:
|
| @@ -501,7 +499,7 @@ class StringMethodsChecker(BaseChecker):
|
| break
|
|
|
| try:
|
| - previous = previous.infer().next()
|
| + previous = next(previous.infer())
|
| except astroid.InferenceError:
|
| # can't check further if we can't infer it
|
| break
|
| @@ -540,17 +538,18 @@ class StringConstantChecker(BaseTokenChecker):
|
| self._unicode_literals = 'unicode_literals' in module.future_imports
|
|
|
| def process_tokens(self, tokens):
|
| - for (tok_type, token, (start_row, start_col), _, _) in tokens:
|
| + for (tok_type, token, (start_row, _), _, _) in tokens:
|
| if tok_type == tokenize.STRING:
|
| # 'token' is the whole un-parsed token; we can look at the start
|
| # of it to see whether it's a raw or unicode string etc.
|
| - self.process_string_token(token, start_row, start_col)
|
| + self.process_string_token(token, start_row)
|
|
|
| - def process_string_token(self, token, start_row, start_col):
|
| + def process_string_token(self, token, start_row):
|
| for i, c in enumerate(token):
|
| if c in '\'\"':
|
| quote_char = c
|
| break
|
| + # pylint: disable=undefined-loop-variable
|
| prefix = token[:i].lower() # markers like u, b, r.
|
| after_prefix = token[i:]
|
| if after_prefix[:3] == after_prefix[-3:] == 3 * quote_char:
|
| @@ -559,18 +558,15 @@ class StringConstantChecker(BaseTokenChecker):
|
| string_body = after_prefix[1:-1] # Chop off quotes
|
| # No special checks on raw strings at the moment.
|
| if 'r' not in prefix:
|
| - self.process_non_raw_string_token(prefix, string_body,
|
| - start_row, start_col)
|
| + self.process_non_raw_string_token(prefix, string_body, start_row)
|
|
|
| - def process_non_raw_string_token(self, prefix, string_body, start_row,
|
| - start_col):
|
| + def process_non_raw_string_token(self, prefix, string_body, start_row):
|
| """check for bad escapes in a non-raw string.
|
|
|
| prefix: lowercase string of eg 'ur' string prefix markers.
|
| string_body: the un-parsed body of the string, not including the quote
|
| marks.
|
| start_row: integer line number in the source.
|
| - start_col: integer column number in the source.
|
| """
|
| # Walk through the string; if we see a backslash then escape the next
|
| # character, and skip over it. If we see a non-escaped character,
|
|
|