From 13dd2a770dd2a2aa2d35a1d607d14720a339930e Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 21 Jan 2019 00:33:16 +0100 Subject: [PATCH 1/7] Add test case for #2667 --- pylint/test/functional/recursion_error_2667.py | 9 +++++++++ pylint/test/functional/recursion_error_2667.txt | 0 2 files changed, 9 insertions(+) create mode 100644 pylint/test/functional/recursion_error_2667.py create mode 100644 pylint/test/functional/recursion_error_2667.txt diff --git a/pylint/test/functional/recursion_error_2667.py b/pylint/test/functional/recursion_error_2667.py new file mode 100644 index 0000000000..585cf7f2fd --- /dev/null +++ b/pylint/test/functional/recursion_error_2667.py @@ -0,0 +1,9 @@ +"""Add regression test for https://github.com/PyCQA/pylint/issues/2667""" +# pylint: disable=missing-docstring, too-few-public-methods + +class MyClass: + def __init__(self): + self._slice = slice(0, 10) + + def incr(self): + self._slice = slice(0, self._slice.stop + 1) diff --git a/pylint/test/functional/recursion_error_2667.txt b/pylint/test/functional/recursion_error_2667.txt new file mode 100644 index 0000000000..e69de29bb2 From e082a2c79c5c8b998a7a2ddfb2123df849ac3902 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 23 Jan 2019 09:13:52 +0100 Subject: [PATCH 2/7] Add test case for #2588 --- pylint/test/functional/too_many_arguments.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/pylint/test/functional/too_many_arguments.py b/pylint/test/functional/too_many_arguments.py index 71015ab04d..689745e274 100644 --- a/pylint/test/functional/too_many_arguments.py +++ b/pylint/test/functional/too_many_arguments.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-docstring +# pylint: disable=missing-docstring,wrong-import-position def stupid_function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9): # [too-many-arguments] return arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 @@ -15,3 +15,18 @@ def mymethod2(self): MyClass().mymethod2()() + + +# Check a false positive does not occur +from functools import partial + + +def root_function(first, second, third): + return first + second + third + + +def func_call(): + """Test we don't emit a FP for https://github.com/PyCQA/pylint/issues/2588""" + partial_func = partial(root_function, 1, 2, 3) + partial_func() + return root_function(1, 2, 3) From c2c06be7454069087193d0bd96badf6bb47dcf09 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 23 Jan 2019 09:21:54 +0100 Subject: [PATCH 3/7] Disable test temporarily until we fix the context problem in astroid --- pylint/test/functional/logging_format_interpolation.py | 5 +++-- pylint/test/functional/logging_format_interpolation.txt | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pylint/test/functional/logging_format_interpolation.py b/pylint/test/functional/logging_format_interpolation.py index 2b33f22435..5f61d6bafe 100644 --- a/pylint/test/functional/logging_format_interpolation.py +++ b/pylint/test/functional/logging_format_interpolation.py @@ -34,5 +34,6 @@ class Logger(renamed_logging.Logger): custom_logger = Logger('three') -custom_logger.info('testing {0}'.format('info')) # [logging-format-interpolation] -custom_logger.info('testing %s' % 'info') # [logging-not-lazy] +# Currently disabled until we get this in https://github.com/PyCQA/astroid/pull/637 +# custom_logger.info('testing {0}'.format('info')) +# custom_logger.info('testing %s' % 'info') diff --git a/pylint/test/functional/logging_format_interpolation.txt b/pylint/test/functional/logging_format_interpolation.txt index e733fcd62a..36051253c9 100644 --- a/pylint/test/functional/logging_format_interpolation.txt +++ b/pylint/test/functional/logging_format_interpolation.txt @@ -3,5 +3,3 @@ logging-format-interpolation:17::Use % formatting in logging functions and pass logging-format-interpolation:18::Use % formatting in logging functions and pass the % parameters as arguments logging-format-interpolation:19::Use % formatting in logging functions and pass the % parameters as arguments logging-format-interpolation:20::Use % formatting in logging functions and pass the % parameters as arguments -logging-format-interpolation:37::Use % formatting in logging functions and pass the % parameters as arguments -logging-not-lazy:38::Specify string format arguments as logging function parameters From 87e02d1e3dda74a085c0871a132610b7d06d1490 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Fri, 25 Jan 2019 11:05:29 +0100 Subject: [PATCH 4/7] Prepare a new dev release --- pylint/__pkginfo__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index c0447e4fe8..ea24d25be1 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -26,7 +26,7 @@ modname = distname = "pylint" numversion = (2, 3, 0) -dev_version = "0" +dev_version = "1" string_version = ".".join(str(num) for num in numversion) if dev_version: From cee1d98c9e29c7f7cef3447cf477d1da3e0c3be2 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 30 Jan 2019 19:00:17 +0100 Subject: [PATCH 5/7] Move the unused variable regression test to a Python 3.6 specific file --- pylint/test/functional/unused_variable.py | 13 ------------- pylint/test/functional/unused_variable_py36.py | 13 +++++++++++++ pylint/test/functional/unused_variable_py36.rc | 2 ++ pylint/test/functional/unused_variable_py36.txt | 0 4 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 pylint/test/functional/unused_variable_py36.py create mode 100644 pylint/test/functional/unused_variable_py36.rc create mode 100644 pylint/test/functional/unused_variable_py36.txt diff --git a/pylint/test/functional/unused_variable.py b/pylint/test/functional/unused_variable.py index cbf18fb8ea..4e6d58fe48 100644 --- a/pylint/test/functional/unused_variable.py +++ b/pylint/test/functional/unused_variable.py @@ -53,16 +53,3 @@ def some_other_scope(): def unused_import_from(): from functools import wraps as abc # [unused-import] from collections import namedtuple # [unused-import] - - -def function(): - ann: int = 0 - assign = 0 - - def inner(): - nonlocal ann, assign - ann += 1 - assign += 1 - return ann + assign - - return inner() diff --git a/pylint/test/functional/unused_variable_py36.py b/pylint/test/functional/unused_variable_py36.py new file mode 100644 index 0000000000..396d02805e --- /dev/null +++ b/pylint/test/functional/unused_variable_py36.py @@ -0,0 +1,13 @@ +# pylint: disable=missing-docstring + +def function(): + ann: int = 0 + assign = 0 + + def inner(): + nonlocal ann, assign + ann += 1 + assign += 1 + return ann + assign + + return inner() diff --git a/pylint/test/functional/unused_variable_py36.rc b/pylint/test/functional/unused_variable_py36.rc new file mode 100644 index 0000000000..0ba2b6333d --- /dev/null +++ b/pylint/test/functional/unused_variable_py36.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.6 diff --git a/pylint/test/functional/unused_variable_py36.txt b/pylint/test/functional/unused_variable_py36.txt new file mode 100644 index 0000000000..e69de29bb2 From 72fd895f636f7d0c62569c39dd74227ed28c8c8b Mon Sep 17 00:00:00 2001 From: Lucas Cimon Date: Wed, 30 Jan 2019 15:04:55 +0100 Subject: [PATCH 6/7] Add a new option 'check-str-concat-over-line-jumps' to check 'implicit-str-concat-in-sequence' --- ChangeLog | 2 + pylint/checkers/strings.py | 40 ++++++++++++++----- ...plicit_str_concat_in_sequence_multiline.py | 4 ++ ...plicit_str_concat_in_sequence_multiline.rc | 2 + ...licit_str_concat_in_sequence_multiline.txt | 1 + 5 files changed, 40 insertions(+), 9 deletions(-) create mode 100755 pylint/test/functional/implicit_str_concat_in_sequence_multiline.py create mode 100755 pylint/test/functional/implicit_str_concat_in_sequence_multiline.rc create mode 100755 pylint/test/functional/implicit_str_concat_in_sequence_multiline.txt diff --git a/ChangeLog b/ChangeLog index 5a5d268e9d..2b8e644418 100644 --- a/ChangeLog +++ b/ChangeLog @@ -90,6 +90,8 @@ Release date: TBA * Avoid popping __main__ when using multiple jobs Close #2689 + + * Add a new option 'check-str-concat-over-line-jumps' to check 'implicit-str-concat-in-sequence' What's New in Pylint 2.2.2? =========================== diff --git a/pylint/checkers/strings.py b/pylint/checkers/strings.py index 92dcbbeec5..eeefc6efe8 100644 --- a/pylint/checkers/strings.py +++ b/pylint/checkers/strings.py @@ -572,6 +572,20 @@ class StringConstantChecker(BaseTokenChecker): "maybe a comma is missing ?", ), } + options = ( + ( + "check-str-concat-over-line-jumps", + { + "default": False, + "type": "yn", + "metavar": "", + "help": "This flag controls whether the " + "implicit-str-concat-in-sequence should generate a warning " + "on implicit string concatenation in sequences defined over " + "several lines.", + }, + ), + ) # Characters that have a special meaning after a backslash in either # Unicode or byte strings. @@ -601,22 +615,30 @@ def process_tokens(self, tokens): # '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[0]) - next_token = tokens[i + 1] if i + 1 < len(tokens) else None + # We figure the next token, ignoring comments & newlines: + j = i + 1 + while j < len(tokens) and tokens[j].type in ( + tokenize.NEWLINE, + tokenize.NL, + tokenize.COMMENT, + ): + j += 1 + next_token = tokens[j] if j < len(tokens) else None if encoding != "ascii": # We convert `tokenize` character count into a byte count, # to match with astroid `.col_offset` start = (start[0], len(line[: start[1]].encode(encoding))) self.string_tokens[start] = (str_eval(token), next_token) - @check_messages(*(MSGS.keys())) + @check_messages(*(msgs.keys())) def visit_list(self, node): self.check_for_concatenated_strings(node, "list") - @check_messages(*(MSGS.keys())) + @check_messages(*(msgs.keys())) def visit_set(self, node): self.check_for_concatenated_strings(node, "set") - @check_messages(*(MSGS.keys())) + @check_messages(*(msgs.keys())) def visit_tuple(self, node): self.check_for_concatenated_strings(node, "tuple") @@ -633,12 +655,12 @@ def check_for_concatenated_strings(self, iterable_node, iterable_type): matching_token, next_token = self.string_tokens[ (elt.lineno, elt.col_offset) ] + # We detect string concatenation: the AST Const is the + # combination of 2 string tokens if matching_token != elt.value and next_token is not None: - next_token_type, next_token_pos = next_token[0], next_token[2] - # We do not warn if string concatenation happens over a newline - if ( - next_token_type == tokenize.STRING - and next_token_pos[0] == elt.lineno + if next_token.type == tokenize.STRING and ( + next_token.start[0] == elt.lineno + or self.config.check_str_concat_over_line_jumps ): self.add_message( "implicit-str-concat-in-sequence", diff --git a/pylint/test/functional/implicit_str_concat_in_sequence_multiline.py b/pylint/test/functional/implicit_str_concat_in_sequence_multiline.py new file mode 100755 index 0000000000..0f8d1fcdf3 --- /dev/null +++ b/pylint/test/functional/implicit_str_concat_in_sequence_multiline.py @@ -0,0 +1,4 @@ +#pylint: disable=bad-continuation,missing-docstring + +TEST_TUPLE = ('a', 'b' # [implicit-str-concat-in-sequence] + 'c') diff --git a/pylint/test/functional/implicit_str_concat_in_sequence_multiline.rc b/pylint/test/functional/implicit_str_concat_in_sequence_multiline.rc new file mode 100755 index 0000000000..e5b3447741 --- /dev/null +++ b/pylint/test/functional/implicit_str_concat_in_sequence_multiline.rc @@ -0,0 +1,2 @@ +[STRING_CONSTANT] +check-str-concat-over-line-jumps=yes diff --git a/pylint/test/functional/implicit_str_concat_in_sequence_multiline.txt b/pylint/test/functional/implicit_str_concat_in_sequence_multiline.txt new file mode 100755 index 0000000000..b37cf624bd --- /dev/null +++ b/pylint/test/functional/implicit_str_concat_in_sequence_multiline.txt @@ -0,0 +1 @@ +implicit-str-concat-in-sequence:3::Implicit string concatenation found in tuple \ No newline at end of file From c2af5c760307edaf50cc519d189b620ba41f12db Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 4 Feb 2019 13:24:34 +0100 Subject: [PATCH 7/7] Support ``Ellipsis`` as a synonym for ``pass`` statements. Close #2718 --- ChangeLog | 4 ++++ pylint/checkers/base.py | 14 +++++++++----- pylint/test/functional/statement_without_effect.py | 10 ++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2b8e644418..d25187c88b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.3.0? Release date: TBA +* Support ``Ellipsis`` as a synonym for ``pass`` statements. + + Close #2718 + * ``fixme`` gets triggered only on comments. Close #2321 diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 478de8bf54..05de23ef88 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -369,12 +369,12 @@ def report_by_type_stats(sect, stats, old_stats): if total != 0: try: documented = total - stats["undocumented_" + node_type] - percent = (documented * 100.) / total + percent = (documented * 100.0) / total nice_stats[node_type]["percent_documented"] = "%.2f" % percent except KeyError: nice_stats[node_type]["percent_documented"] = "NC" try: - percent = (stats["badname_" + node_type] * 100.) / total + percent = (stats["badname_" + node_type] * 100.0) / total nice_stats[node_type]["percent_badname"] = "%.2f" % percent except KeyError: nice_stats[node_type]["percent_badname"] = "NC" @@ -1089,13 +1089,17 @@ def visit_expr(self, node): return self.add_message("pointless-string-statement", node=node) return - # ignore if this is : + + # Ignore if this is : # * a direct function call # * the unique child of a try/except body - # * a yield (which are wrapped by a discard node in _ast XXX) + # * a yieldd statement + # * an ellipsis (which can be used on Python 3 instead of pass) # warn W0106 if we have any underlying function call (we can't predict # side effects), else pointless-statement - if isinstance(expr, (astroid.Yield, astroid.Await, astroid.Call)) or ( + if isinstance( + expr, (astroid.Yield, astroid.Await, astroid.Ellipsis, astroid.Call) + ) or ( isinstance(node.parent, astroid.TryExcept) and node.parent.body == [node] ): return diff --git a/pylint/test/functional/statement_without_effect.py b/pylint/test/functional/statement_without_effect.py index eb87aca9dd..c8394b45ec 100644 --- a/pylint/test/functional/statement_without_effect.py +++ b/pylint/test/functional/statement_without_effect.py @@ -63,3 +63,13 @@ def test(self): self.val = 42 # +1:[pointless-string-statement] """ this is an invalid attribute docstring. """ + + +def ellipsis(): + """Test that an Ellipsis as a body does not trigger the error""" + ... + + +class EllipsisBody: + """Test that an Ellipsis as a body does not trigger the error""" + ...