From b25859c4a56ccce61087f7a1270f40deaed68169 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sat, 26 Mar 2022 18:31:49 -0400 Subject: [PATCH 1/5] Fix false positive for `superfluous-parens` for `return (a or b) in iterable` (#5964) Co-authored-by: Pierre Sassoulas --- ChangeLog | 4 ++++ doc/whatsnew/2.13.rst | 5 +++++ pylint/checkers/format.py | 23 ++++++++++------------- tests/functional/s/superfluous_parens.py | 5 ++++- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1e701b22db..9caec4e371 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20,6 +20,10 @@ What's New in Pylint 2.13.2? ============================ Release date: TBA +* Fix false positive for ``superfluous-parens`` for patterns like + "return (a or b) in iterable". + + Closes #5803 What's New in Pylint 2.13.1? diff --git a/doc/whatsnew/2.13.rst b/doc/whatsnew/2.13.rst index b0bfd13210..2d425d7a4a 100644 --- a/doc/whatsnew/2.13.rst +++ b/doc/whatsnew/2.13.rst @@ -557,3 +557,8 @@ Other Changes the variable being raised. Closes #2793 + +* Fix false positive for ``superfluous-parens`` for patterns like + "return (a or b) in iterable". + + Closes #5803 diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index fae6085ca8..70c56a621a 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -320,6 +320,7 @@ def new_line(self, tokens, line_end, line_start): def process_module(self, _node: nodes.Module) -> None: pass + # pylint: disable-next=too-many-return-statements def _check_keyword_parentheses( self, tokens: List[tokenize.TokenInfo], start: int ) -> None: @@ -382,19 +383,15 @@ def _check_keyword_parentheses( # The empty tuple () is always accepted. if i == start + 2: return - if keyword_token == "not": - if not found_and_or: - self.add_message( - "superfluous-parens", line=line_num, args=keyword_token - ) - elif keyword_token in {"return", "yield"}: - self.add_message( - "superfluous-parens", line=line_num, args=keyword_token - ) - elif not found_and_or and keyword_token != "in": - self.add_message( - "superfluous-parens", line=line_num, args=keyword_token - ) + if found_and_or: + return + if keyword_token == "in": + # This special case was added in https://github.com/PyCQA/pylint/pull/4948 + # but it could be removed in the future. Avoid churn for now. + return + self.add_message( + "superfluous-parens", line=line_num, args=keyword_token + ) return elif depth == 1: # This is a tuple, which is always acceptable. diff --git a/tests/functional/s/superfluous_parens.py b/tests/functional/s/superfluous_parens.py index 22c4c3ab4b..ee835f6d08 100644 --- a/tests/functional/s/superfluous_parens.py +++ b/tests/functional/s/superfluous_parens.py @@ -44,8 +44,11 @@ def function_A(): def function_B(var): return (var.startswith(('A', 'B', 'C')) or var == 'D') +def function_C(first, second): + return (first or second) in (0, 1) + # TODO: Test string combinations, see https://github.com/PyCQA/pylint/issues/4792 -# Lines 45, 46 & 47 should raise the superfluous-parens message +# The lines with "+" should raise the superfluous-parens message J = "TestString" K = ("Test " + "String") L = ("Test " + "String") in I From dec241b1787e6c99a092bb9ef6a993abf51fea91 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sun, 27 Mar 2022 08:31:10 -0400 Subject: [PATCH 2/5] Add regression test for #5982 upgrade astroid to 2.11.2 (#5988) Co-authored-by: Pierre Sassoulas --- ChangeLog | 4 ++++ requirements_test_min.txt | 2 +- setup.cfg | 2 +- tests/functional/u/used/used_before_assignment_py37.py | 7 +++++++ tests/functional/u/used/used_before_assignment_py37.txt | 2 +- 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9caec4e371..8fed9ccb77 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20,6 +20,10 @@ What's New in Pylint 2.13.2? ============================ Release date: TBA +* Fix crash when subclassing a ``namedtuple``. + + Closes #5982 + * Fix false positive for ``superfluous-parens`` for patterns like "return (a or b) in iterable". diff --git a/requirements_test_min.txt b/requirements_test_min.txt index 83f1a88101..10e6ce2e0e 100644 --- a/requirements_test_min.txt +++ b/requirements_test_min.txt @@ -1,6 +1,6 @@ -e .[testutil] # astroid dependency is also defined in setup.cfg -astroid==2.11.0 # Pinned to a specific version for tests +astroid==2.11.2 # Pinned to a specific version for tests typing-extensions~=4.1 pytest~=7.0 pytest-benchmark~=3.4 diff --git a/setup.cfg b/setup.cfg index 42b349e380..25e66a3a0b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -48,7 +48,7 @@ install_requires = # Also upgrade requirements_test_min.txt if you are bumping astroid. # Pinned to dev of next minor update to allow editable installs, # see https://github.com/PyCQA/astroid/issues/1341 - astroid>=2.11.0,<=2.12.0-dev0 + astroid>=2.11.2,<=2.12.0-dev0 isort>=4.2.5,<6 mccabe>=0.6,<0.8 tomli>=1.1.0;python_version<"3.11" diff --git a/tests/functional/u/used/used_before_assignment_py37.py b/tests/functional/u/used/used_before_assignment_py37.py index 08a585a95b..c64bf7cf55 100644 --- a/tests/functional/u/used/used_before_assignment_py37.py +++ b/tests/functional/u/used/used_before_assignment_py37.py @@ -1,6 +1,7 @@ """Tests for used-before-assignment with functions added in python 3.7""" # pylint: disable=missing-function-docstring from __future__ import annotations +from collections import namedtuple from typing import List @@ -26,3 +27,9 @@ def inner_method(self, other: MyClass) -> bool: return self == other return inner_method(self, MyClass()) + + +class NamedTupleSubclass(namedtuple("NamedTupleSubclass", [])): + """Taken from https://github.com/PyCQA/pylint/issues/5982""" + def method(self) -> NamedTupleSubclass: + """Variables checker crashed when astroid did not supply a lineno""" diff --git a/tests/functional/u/used/used_before_assignment_py37.txt b/tests/functional/u/used/used_before_assignment_py37.txt index ad06560e21..fa0a0b77a0 100644 --- a/tests/functional/u/used/used_before_assignment_py37.txt +++ b/tests/functional/u/used/used_before_assignment_py37.txt @@ -1 +1 @@ -undefined-variable:17:20:17:27:MyClass.incorrect_default_method:Undefined variable 'MyClass':UNDEFINED +undefined-variable:18:20:18:27:MyClass.incorrect_default_method:Undefined variable 'MyClass':UNDEFINED From c42fe73a1613bbfb52a5ba9129efa45a3fd76401 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sun, 27 Mar 2022 08:31:53 -0400 Subject: [PATCH 3/5] Fix false negative for `protected-access` on functions (#5990) --- ChangeLog | 5 +++++ pylint/checkers/classes/class_checker.py | 5 ++++- tests/functional/p/protected_access.py | 14 ++++++++++++++ tests/functional/p/protected_access.txt | 2 ++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 8fed9ccb77..1df22b6b5f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -29,6 +29,11 @@ Release date: TBA Closes #5803 +* Fix a false negative regression in 2.13.0 where ``protected-access`` was not + raised on functions. + + Fixes #5989 + What's New in Pylint 2.13.1? ============================ diff --git a/pylint/checkers/classes/class_checker.py b/pylint/checkers/classes/class_checker.py index e258f6642a..f5efc4dc7c 100644 --- a/pylint/checkers/classes/class_checker.py +++ b/pylint/checkers/classes/class_checker.py @@ -980,7 +980,7 @@ def _check_unused_private_attributes(self, node: nodes.ClassDef) -> None: if attribute.attrname != assign_attr.attrname: continue - if self._is_type_self_call(attribute.expr): + if isinstance(attribute.expr, nodes.Call): continue if assign_attr.expr.name in { @@ -2106,6 +2106,7 @@ def _is_mandatory_method_param(self, node: nodes.NodeNG) -> bool: """Check if nodes.Name corresponds to first attribute variable name. Name is `self` for method, `cls` for classmethod and `mcs` for metaclass. + Static methods return False. """ if self._first_attrs: first_attr = self._first_attrs[-1] @@ -2116,6 +2117,8 @@ def _is_mandatory_method_param(self, node: nodes.NodeNG) -> bool: ) if closest_func is None: return False + if not closest_func.is_bound(): + return False if not closest_func.args.args: return False first_attr = closest_func.args.args[0].name diff --git a/tests/functional/p/protected_access.py b/tests/functional/p/protected_access.py index 39769fe76a..8d2f945d28 100644 --- a/tests/functional/p/protected_access.py +++ b/tests/functional/p/protected_access.py @@ -27,3 +27,17 @@ def nargs(self): class Application(metaclass=MC): def __no_special__(cls): nargs = obj._nargs # [protected-access] + + +class Light: + @property + def _light_internal(self) -> None: + return None + + @staticmethod + def func(light) -> None: + print(light._light_internal) # [protected-access] + + +def func(light: Light) -> None: + print(light._light_internal) # [protected-access] diff --git a/tests/functional/p/protected_access.txt b/tests/functional/p/protected_access.txt index 570a906381..50e6c5d5b6 100644 --- a/tests/functional/p/protected_access.txt +++ b/tests/functional/p/protected_access.txt @@ -1,2 +1,4 @@ protected-access:17:0:17:9::Access to a protected member _teta of a client class:UNDEFINED protected-access:29:16:29:26:Application.__no_special__:Access to a protected member _nargs of a client class:UNDEFINED +protected-access:39:14:39:35:Light.func:Access to a protected member _light_internal of a client class:UNDEFINED +protected-access:43:10:43:31:func:Access to a protected member _light_internal of a client class:UNDEFINED From 6a25d7048edadc18a05e999021049ade86ef2bd9 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 27 Mar 2022 14:32:27 +0200 Subject: [PATCH 4/5] Better error message when we cant write the crash files (#5987) * Display the error correctly if we can't write a crash report. Also catch any exceptions. --- pylint/config/__init__.py | 5 ++--- pylint/lint/utils.py | 11 +++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pylint/config/__init__.py b/pylint/config/__init__.py index 12f834d81b..16df5fd893 100644 --- a/pylint/config/__init__.py +++ b/pylint/config/__init__.py @@ -71,11 +71,10 @@ pathlib.Path(PYLINT_HOME).mkdir(parents=True, exist_ok=True) with open(spam_prevention_file, "w", encoding="utf8") as f: f.write("") - except Exception: # pylint: disable=broad-except - # Can't write in PYLINT_HOME ? + except Exception as exc: # pylint: disable=broad-except print( "Can't write the file that was supposed to " - f"prevent pylint.d deprecation spam in {PYLINT_HOME}." + f"prevent 'pylint.d' deprecation spam in {PYLINT_HOME} because of {exc}." ) diff --git a/pylint/lint/utils.py b/pylint/lint/utils.py index 8377dfdd44..7d402a0088 100644 --- a/pylint/lint/utils.py +++ b/pylint/lint/utils.py @@ -51,13 +51,16 @@ def prepare_crash_report(ex: Exception, filepath: str, crash_file_path: str) -> pylint crashed with a ``{ex.__class__.__name__}`` and with the following stacktrace: ``` """ + template += traceback.format_exc() + template += "```\n" try: with open(issue_template_path, "a", encoding="utf8") as f: f.write(template) - traceback.print_exc(file=f) - f.write("```\n") - except FileNotFoundError: - print(f"Can't write the issue template for the crash in {issue_template_path}.") + except Exception as exc: # pylint: disable=broad-except + print( + f"Can't write the issue template for the crash in {issue_template_path} " + f"because of: '{exc}'\nHere's the content anyway:\n{template}." + ) return issue_template_path From 2066cab9bbe43341b84014ac9610e275db586431 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 27 Mar 2022 14:42:22 +0200 Subject: [PATCH 5/5] Bump pylint to 2.13.2, update changelog --- ChangeLog | 12 +++++++++++- doc/release.md | 1 + pylint/__pkginfo__.py | 2 +- tbump.toml | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1df22b6b5f..d3a6411bd9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,10 +16,16 @@ Release date: TBA (Ie. not necessarily at the end) -What's New in Pylint 2.13.2? +What's New in Pylint 2.13.3? ============================ Release date: TBA + + +What's New in Pylint 2.13.2? +============================ +Release date: 2022-03-27 + * Fix crash when subclassing a ``namedtuple``. Closes #5982 @@ -34,6 +40,10 @@ Release date: TBA Fixes #5989 +* Better error messages in case of crash if pylint can't write the issue template. + + Refer to #5987 + What's New in Pylint 2.13.1? ============================ diff --git a/doc/release.md b/doc/release.md index c68ef00bf1..556de5c0ae 100644 --- a/doc/release.md +++ b/doc/release.md @@ -57,6 +57,7 @@ maintenance branch we cherry-pick the commit from `main`. version `X.Y-1.Z'`. (For example: `v2.3.5`) - After the PR is merged on `main` cherry-pick the commits on the `maintenance/X.Y.x` branch (For example: from `maintenance/2.4.x` cherry-pick a commit from `main`) +- Remove the "need backport" label from cherry-picked issues - Release a patch version ## Releasing a patch version diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index 588fb54a02..7d04c14d36 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -4,7 +4,7 @@ from typing import Tuple -__version__ = "2.13.1" +__version__ = "2.13.2" def get_numversion_from_version(v: str) -> Tuple: diff --git a/tbump.toml b/tbump.toml index fa371dd6e3..67c389d7a2 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,7 +1,7 @@ github_url = "https://github.com/PyCQA/pylint" [version] -current = "2.13.1" +current = "2.13.2" regex = ''' ^(?P0|[1-9]\d*) \.