From 744ba539b81e620234e2d73fc7820d9611a99a86 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 08:14:59 +0000 Subject: [PATCH 01/10] Fix crash caused by invalid format strings in `.format` context (#10300) (#10302) (cherry picked from commit e6cbd41e932268e2a15e726b28a7ebd8ce82b0f5) Co-authored-by: Zen Lee <53538590+zenlyj@users.noreply.github.com> --- doc/whatsnew/fragments/10282.bugfix | 3 ++ .../refactoring/recommendation_checker.py | 30 +++++++++++-------- .../c/consider/consider_using_f_string.py | 8 +++++ 3 files changed, 29 insertions(+), 12 deletions(-) create mode 100644 doc/whatsnew/fragments/10282.bugfix diff --git a/doc/whatsnew/fragments/10282.bugfix b/doc/whatsnew/fragments/10282.bugfix new file mode 100644 index 0000000000..42f75bf0ee --- /dev/null +++ b/doc/whatsnew/fragments/10282.bugfix @@ -0,0 +1,3 @@ +Fix a crash caused by malformed format strings when using `.format` with keyword arguments. + +Closes #10282 diff --git a/pylint/checkers/refactoring/recommendation_checker.py b/pylint/checkers/refactoring/recommendation_checker.py index c5b19e1a55..8824455750 100644 --- a/pylint/checkers/refactoring/recommendation_checker.py +++ b/pylint/checkers/refactoring/recommendation_checker.py @@ -382,6 +382,14 @@ def _detect_replacable_format_call(self, node: nodes.Const) -> None: if not isinstance(node.parent.parent, nodes.Call): return + # Don't raise message on bad format string + try: + keyword_args = [ + i[0] for i in utils.parse_format_method_string(node.value)[0] + ] + except utils.IncompleteFormatString: + return + if node.parent.parent.args: for arg in node.parent.parent.args: # If star expressions with more than 1 element are being used @@ -397,9 +405,6 @@ def _detect_replacable_format_call(self, node: nodes.Const) -> None: return elif node.parent.parent.keywords: - keyword_args = [ - i[0] for i in utils.parse_format_method_string(node.value)[0] - ] for keyword in node.parent.parent.keywords: # If keyword is used multiple times if keyword_args.count(keyword.arg) > 1: @@ -408,9 +413,12 @@ def _detect_replacable_format_call(self, node: nodes.Const) -> None: keyword = utils.safe_infer(keyword.value) # If lists of more than one element are being unpacked - if isinstance(keyword, nodes.Dict): - if len(keyword.items) > 1 and len(keyword_args) > 1: - return + if ( + isinstance(keyword, nodes.Dict) + and len(keyword.items) > 1 + and len(keyword_args) > 1 + ): + return # If all tests pass, then raise message self.add_message( @@ -438,12 +446,10 @@ def _detect_replacable_format_call(self, node: nodes.Const) -> None: inferred_right = utils.safe_infer(node.parent.right) # If dicts or lists of length > 1 are used - if isinstance(inferred_right, nodes.Dict): - if len(inferred_right.items) > 1: - return - elif isinstance(inferred_right, nodes.List): - if len(inferred_right.elts) > 1: - return + if isinstance(inferred_right, nodes.Dict) and len(inferred_right.items) > 1: + return + if isinstance(inferred_right, nodes.List) and len(inferred_right.elts) > 1: + return # If all tests pass, then raise message self.add_message( diff --git a/tests/functional/c/consider/consider_using_f_string.py b/tests/functional/c/consider/consider_using_f_string.py index 086fb3f875..d40b417400 100644 --- a/tests/functional/c/consider/consider_using_f_string.py +++ b/tests/functional/c/consider/consider_using_f_string.py @@ -128,3 +128,11 @@ def wrap_print(value): print(value) wrap_print(value="{}".format) + + +def invalid_format_string_good(): + """Should not raise message when `.format` is called with an invalid format string.""" + # pylint: disable=bad-format-string + print("{a[0] + a[1]}".format(a=[0, 1])) + print("{".format(a=1)) + print("{".format(1)) From 5b2aa6c7a471f43c3bfe7eceb4db074bb5d94802 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 14:12:59 +0100 Subject: [PATCH 02/10] Speed up the generation of no-member suggestions (#10277) (#10311) Previously, edit distances were calculated for strings that had length differences greater than the maximum suggestion threshold. Remove handling of missing-member-hint-distance = 0 Co-authored-by: Pierre Sassoulas (cherry picked from commit 9a6168d3041b1f155b4021fdfe79f1729d9f8ee6) Co-authored-by: correctmost <134317971+correctmost@users.noreply.github.com> --- doc/whatsnew/fragments/10277.other | 4 +++ pylint/checkers/typecheck.py | 19 ++++++++++-- tests/checkers/unittest_typecheck.py | 44 ++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 doc/whatsnew/fragments/10277.other diff --git a/doc/whatsnew/fragments/10277.other b/doc/whatsnew/fragments/10277.other new file mode 100644 index 0000000000..a7a1bfb0b2 --- /dev/null +++ b/doc/whatsnew/fragments/10277.other @@ -0,0 +1,4 @@ +The algorithm used for ``no-member`` suggestions is now more efficient and cut the +calculation when the distance score is already above the threshold. + +Refs #10277 diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index bc7ddfc2a4..51d56ea699 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -150,8 +150,12 @@ def _(node: nodes.ClassDef | bases.Instance) -> Iterable[str]: return itertools.chain(values, other_values) -def _string_distance(seq1: str, seq2: str) -> int: - seq2_length = len(seq2) +def _string_distance(seq1: str, seq2: str, seq1_length: int, seq2_length: int) -> int: + if not seq1_length: + return seq2_length + + if not seq2_length: + return seq1_length row = [*list(range(1, seq2_length + 1)), 0] for seq1_index, seq1_char in enumerate(seq1): @@ -182,11 +186,20 @@ def _similar_names( possible_names: list[tuple[str, int]] = [] names = _node_names(owner) + attr_str = attrname or "" + attr_len = len(attr_str) + for name in names: if name == attrname: continue - distance = _string_distance(attrname or "", name) + name_len = len(name) + + min_distance = abs(attr_len - name_len) + if min_distance > distance_threshold: + continue + + distance = _string_distance(attr_str, name, attr_len, name_len) if distance <= distance_threshold: possible_names.append((name, distance)) diff --git a/tests/checkers/unittest_typecheck.py b/tests/checkers/unittest_typecheck.py index c944b863f3..d3fd5a34c0 100644 --- a/tests/checkers/unittest_typecheck.py +++ b/tests/checkers/unittest_typecheck.py @@ -221,3 +221,47 @@ def decorated(): ) ): self.checker.visit_subscript(subscript) + + +class TestTypeCheckerStringDistance: + """Tests for the _string_distance helper in pylint.checkers.typecheck.""" + + def test_string_distance_identical_strings(self) -> None: + seq1 = "hi" + seq2 = "hi" + assert typecheck._string_distance(seq1, seq2, len(seq1), len(seq2)) == 0 + + seq1, seq2 = seq2, seq1 + assert typecheck._string_distance(seq1, seq2, len(seq1), len(seq2)) == 0 + + def test_string_distance_empty_string(self) -> None: + seq1 = "" + seq2 = "hi" + assert typecheck._string_distance(seq1, seq2, len(seq1), len(seq2)) == 2 + + seq1, seq2 = seq2, seq1 + assert typecheck._string_distance(seq1, seq2, len(seq1), len(seq2)) == 2 + + def test_string_distance_edit_distance_one_character(self) -> None: + seq1 = "hi" + seq2 = "he" + assert typecheck._string_distance(seq1, seq2, len(seq1), len(seq2)) == 1 + + seq1, seq2 = seq2, seq1 + assert typecheck._string_distance(seq1, seq2, len(seq1), len(seq2)) == 1 + + def test_string_distance_edit_distance_multiple_similar_characters(self) -> None: + seq1 = "hello" + seq2 = "yelps" + assert typecheck._string_distance(seq1, seq2, len(seq1), len(seq2)) == 3 + + seq1, seq2 = seq2, seq1 + assert typecheck._string_distance(seq1, seq2, len(seq1), len(seq2)) == 3 + + def test_string_distance_edit_distance_all_dissimilar_characters(self) -> None: + seq1 = "yellow" + seq2 = "orange" + assert typecheck._string_distance(seq1, seq2, len(seq1), len(seq2)) == 6 + + seq1, seq2 = seq2, seq1 + assert typecheck._string_distance(seq1, seq2, len(seq1), len(seq2)) == 6 From 46460d8b5f9257abd9b8342b187ac1dc96c1d419 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Tue, 25 Mar 2025 21:03:04 +0100 Subject: [PATCH 03/10] [setuptools] Force the upgrade of setuptools > 77 (#10313) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0cf1ade578..a3cc3f43b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [build-system] build-backend = "setuptools.build_meta" -requires = [ "setuptools>=71.0.4" ] +requires = [ "setuptools>=77" ] [project] name = "pylint" From 59d4c26a4ba8c8157da3cdf196d54bbc003be467 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 21:45:29 +0000 Subject: [PATCH 04/10] Fix typo in missing-member-hint-distance documentation (#10314) (#10315) (cherry picked from commit f98805e130f5b7c6b9eee0d4439963c3351058b8) Co-authored-by: correctmost <134317971+correctmost@users.noreply.github.com> --- examples/pylintrc | 2 +- examples/pyproject.toml | 2 +- pylint/checkers/typecheck.py | 2 +- pylintrc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/pylintrc b/examples/pylintrc index 97064b3e95..a50908c367 100644 --- a/examples/pylintrc +++ b/examples/pylintrc @@ -600,7 +600,7 @@ ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace # of finding the hint is based on edit distance. missing-member-hint=yes -# The minimum edit distance a name should have in order to be considered a +# The maximum edit distance a name should have in order to be considered a # similar match for a missing member name. missing-member-hint-distance=1 diff --git a/examples/pyproject.toml b/examples/pyproject.toml index d37e9d427e..b342ed2536 100644 --- a/examples/pyproject.toml +++ b/examples/pyproject.toml @@ -529,7 +529,7 @@ ignored-classes = [ "optparse.Values", "thread._local", "_thread._local", "argpa # finding the hint is based on edit distance. missing-member-hint = true -# The minimum edit distance a name should have in order to be considered a +# The maximum edit distance a name should have in order to be considered a # similar match for a missing member name. missing-member-hint-distance = 1 diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 51d56ea699..1d8fc4364e 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -960,7 +960,7 @@ class TypeChecker(BaseChecker): "default": 1, "type": "int", "metavar": "", - "help": "The minimum edit distance a name should have in order " + "help": "The maximum edit distance a name should have in order " "to be considered a similar match for a missing member name.", }, ), diff --git a/pylintrc b/pylintrc index bd6e8a2e22..78f9e16629 100644 --- a/pylintrc +++ b/pylintrc @@ -378,7 +378,7 @@ ignore-on-opaque-inference=yes # of finding the hint is based on edit distance. missing-member-hint=yes -# The minimum edit distance a name should have in order to be considered a +# The maximum edit distance a name should have in order to be considered a # similar match for a missing member name. missing-member-hint-distance=1 From e87d10ca558ef032a64b4263ed876088ff5ccdd2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 12:14:11 +0000 Subject: [PATCH 05/10] added black's github link in tutorial (#10303) (#10335) (cherry picked from commit 5ab0d192b949340003f262469cef0fc2942e6df7) Co-authored-by: Alex Prabhat Bara --- doc/tutorial.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/tutorial.rst b/doc/tutorial.rst index fce4722341..9dee73e1e7 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -64,10 +64,12 @@ preferences a bit better. Your First Pylint'ing --------------------- -We'll use a basic Python script with ``black`` already applied on it, +We'll use a basic Python script with `black`_ already applied on it, as fodder for our tutorial. The starting code we will use is called ``simplecaesar.py`` and is here in its entirety: +.. _`black`: https://github.com/psf/black + .. sourcecode:: python #!/usr/bin/env python3 From 6a0239583f11ec2dfe46632f53e68fdc2f971d72 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:53:55 +0200 Subject: [PATCH 06/10] [maintenance/3.3.x] Fix doc build (#10336) --- doc/user_guide/configuration/all-options.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user_guide/configuration/all-options.rst b/doc/user_guide/configuration/all-options.rst index e74c967a1a..6c1afb029c 100644 --- a/doc/user_guide/configuration/all-options.rst +++ b/doc/user_guide/configuration/all-options.rst @@ -1492,7 +1492,7 @@ Standard Checkers --missing-member-hint-distance """""""""""""""""""""""""""""" -*The minimum edit distance a name should have in order to be considered a similar match for a missing member name.* +*The maximum edit distance a name should have in order to be considered a similar match for a missing member name.* **Default:** ``1`` From 887b1b649b9deea0bf91023b23e11ed8cdaf8d72 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Fri, 2 May 2025 09:58:21 +0200 Subject: [PATCH 07/10] [fix] AttributeError crash when a slice is used as a class decorator (#10350) (#10361) Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com> Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> --- doc/whatsnew/fragments/10334.bugfix | 3 +++ pylint/checkers/deprecated.py | 4 +++- pylint/checkers/utils.py | 2 +- tests/functional/r/regression_02/regression_10334.py | 6 ++++++ tests/functional/r/regression_02/regression_10334.txt | 1 + 5 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 doc/whatsnew/fragments/10334.bugfix create mode 100644 tests/functional/r/regression_02/regression_10334.py create mode 100644 tests/functional/r/regression_02/regression_10334.txt diff --git a/doc/whatsnew/fragments/10334.bugfix b/doc/whatsnew/fragments/10334.bugfix new file mode 100644 index 0000000000..6c2a24a748 --- /dev/null +++ b/doc/whatsnew/fragments/10334.bugfix @@ -0,0 +1,3 @@ +Using a slice as a class decorator now raise a 'not-callable' message instead of crashing pylint. A lot of checks that dealt with decorators (too many to list) are now shortcut if the decorator can't immediately be inferred to a function or class definition. + +Closes #10334 diff --git a/pylint/checkers/deprecated.py b/pylint/checkers/deprecated.py index 028dc13f38..a3e0e85ebf 100644 --- a/pylint/checkers/deprecated.py +++ b/pylint/checkers/deprecated.py @@ -136,7 +136,9 @@ def visit_decorators(self, node: nodes.Decorators) -> None: inf = safe_infer(children[0].func) else: inf = safe_infer(children[0]) - qname = inf.qname() if inf else None + if not isinstance(inf, (nodes.ClassDef, nodes.FunctionDef)): + return + qname = inf.qname() if qname in self.deprecated_decorators(): self.add_message("deprecated-decorator", node=node, args=qname) diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index bfc4bc61da..374ae31e80 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -876,7 +876,7 @@ def decorated_with( if any( i.name in qnames or i.qname() in qnames for i in decorator_node.infer() - if i is not None and not isinstance(i, util.UninferableBase) + if isinstance(i, (nodes.ClassDef, nodes.FunctionDef)) ): return True except astroid.InferenceError: diff --git a/tests/functional/r/regression_02/regression_10334.py b/tests/functional/r/regression_02/regression_10334.py new file mode 100644 index 0000000000..772860846e --- /dev/null +++ b/tests/functional/r/regression_02/regression_10334.py @@ -0,0 +1,6 @@ +"""Test for slice object used as a decorator.""" +# pylint: disable=too-few-public-methods +s = slice(-2) +@s() # [not-callable] +class A: + """Class with a slice decorator.""" diff --git a/tests/functional/r/regression_02/regression_10334.txt b/tests/functional/r/regression_02/regression_10334.txt new file mode 100644 index 0000000000..d2baba926e --- /dev/null +++ b/tests/functional/r/regression_02/regression_10334.txt @@ -0,0 +1 @@ +not-callable:4:1:4:4:A:s is not callable:UNDEFINED From 05331112c04c4322656333075cdc077e199e7f7d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 3 May 2025 11:49:29 +0200 Subject: [PATCH 08/10] [fix] Fix a crash for class decorators mistaken for class attributes (#10362) (#10363) Closes #10105 (cherry picked from commit 1812899d04b535e2099ea0f9b8814ff3b266d9a9) Co-authored-by: Pierre Sassoulas --- doc/whatsnew/fragments/10105.bugfix | 3 +++ pylint/checkers/variables.py | 2 +- tests/functional/r/regression_02/regression_10105.py | 9 +++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 doc/whatsnew/fragments/10105.bugfix create mode 100644 tests/functional/r/regression_02/regression_10105.py diff --git a/doc/whatsnew/fragments/10105.bugfix b/doc/whatsnew/fragments/10105.bugfix new file mode 100644 index 0000000000..608a723f71 --- /dev/null +++ b/doc/whatsnew/fragments/10105.bugfix @@ -0,0 +1,3 @@ +Fixed a crash when importing a class decorator that did not exist with the same name than a class attribute after the class definition. + +Closes #10105 diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index f478e16d3b..502ef481b9 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -2448,7 +2448,7 @@ def _is_only_type_assignment( node_frame = node.frame() parent = node - while parent is not defstmt_frame.parent: + while parent not in {defstmt_frame.parent, None}: parent_scope = parent.scope() # Find out if any nonlocals receive values in nested functions diff --git a/tests/functional/r/regression_02/regression_10105.py b/tests/functional/r/regression_02/regression_10105.py new file mode 100644 index 0000000000..9eeaeac1b6 --- /dev/null +++ b/tests/functional/r/regression_02/regression_10105.py @@ -0,0 +1,9 @@ +# pylint: disable=too-few-public-methods,missing-docstring,used-before-assignment,import-error,unused-import +# pylint: disable=wrong-import-position + + +@decorator +class DecoratedClass: + decorator: int + +import decorator From f9773de127fe2dc3ec1254b8b6e280c19e4b1079 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sun, 4 May 2025 10:19:36 -0400 Subject: [PATCH 09/10] Consistency between ``is``/``is not`` and ``==``/``!=`` when comparing types for ``unidiomatic-typecheck`` (#10170) (#10366) (cherry picked from commit d396616db59977540ab32496e324c7aaca352ab2) Co-authored-by: Nedelcu Ioan-Andrei <138256980+nedelcu-ioan@users.noreply.github.com> Co-authored-by: Pierre Sassoulas --- doc/whatsnew/fragments/10161.false_positive | 3 +++ pylint/checkers/base/comparison_checker.py | 10 +++---- tests/functional/u/unidiomatic_typecheck.py | 28 +++++++++++++++----- tests/functional/u/unidiomatic_typecheck.txt | 18 ++++++++----- 4 files changed, 39 insertions(+), 20 deletions(-) create mode 100644 doc/whatsnew/fragments/10161.false_positive diff --git a/doc/whatsnew/fragments/10161.false_positive b/doc/whatsnew/fragments/10161.false_positive new file mode 100644 index 0000000000..dc343e05ae --- /dev/null +++ b/doc/whatsnew/fragments/10161.false_positive @@ -0,0 +1,3 @@ +Comparisons between two calls to `type()` won't raise an ``unidiomatic-typecheck`` warning anymore, consistent with the behavior applied only for ``==`` previously. + +Closes #10161 diff --git a/pylint/checkers/base/comparison_checker.py b/pylint/checkers/base/comparison_checker.py index 6fb053e2e1..091c8e9db3 100644 --- a/pylint/checkers/base/comparison_checker.py +++ b/pylint/checkers/base/comparison_checker.py @@ -323,14 +323,10 @@ def _check_unidiomatic_typecheck(self, node: nodes.Compare) -> None: if operator in TYPECHECK_COMPARISON_OPERATORS: left = node.left if _is_one_arg_pos_call(left): - self._check_type_x_is_y(node, left, operator, right) + self._check_type_x_is_y(node=node, left=left, right=right) def _check_type_x_is_y( - self, - node: nodes.Compare, - left: nodes.NodeNG, - operator: str, - right: nodes.NodeNG, + self, node: nodes.Compare, left: nodes.NodeNG, right: nodes.NodeNG ) -> None: """Check for expressions like type(x) == Y.""" left_func = utils.safe_infer(left.func) @@ -339,7 +335,7 @@ def _check_type_x_is_y( ): return - if operator in {"is", "is not"} and _is_one_arg_pos_call(right): + if _is_one_arg_pos_call(right): right_func = utils.safe_infer(right.func) if ( isinstance(right_func, nodes.ClassDef) diff --git a/tests/functional/u/unidiomatic_typecheck.py b/tests/functional/u/unidiomatic_typecheck.py index 2a1957d75e..5e96a2745a 100644 --- a/tests/functional/u/unidiomatic_typecheck.py +++ b/tests/functional/u/unidiomatic_typecheck.py @@ -57,14 +57,28 @@ def parameter_shadowing_inference_negatives(type): type(42) in [int] type(42) not in [int] -def deliberate_subclass_check_negatives(b): + +def deliberate_subclass_check_negatives(a, b): type(42) is type(b) type(42) is not type(b) + type(42) == type(b) + type(42) != type(b) + type(a) is type(b) + type(a) is not type(b) + type(a) == type(b) + type(a) != type(b) + def type_of_literals_positives(a): - type(a) is type([]) # [unidiomatic-typecheck] - type(a) is not type([]) # [unidiomatic-typecheck] - type(a) is type({}) # [unidiomatic-typecheck] - type(a) is not type({}) # [unidiomatic-typecheck] - type(a) is type("") # [unidiomatic-typecheck] - type(a) is not type("") # [unidiomatic-typecheck] + type(a) is type([]) # [unidiomatic-typecheck] + type(a) is not type([]) # [unidiomatic-typecheck] + type(a) is type({}) # [unidiomatic-typecheck] + type(a) is not type({}) # [unidiomatic-typecheck] + type(a) is type("") # [unidiomatic-typecheck] + type(a) is not type("") # [unidiomatic-typecheck] + type(a) == type([]) # [unidiomatic-typecheck] + type(a) != type([]) # [unidiomatic-typecheck] + type(a) == type({}) # [unidiomatic-typecheck] + type(a) != type({}) # [unidiomatic-typecheck] + type(a) == type("") # [unidiomatic-typecheck] + type(a) != type("") # [unidiomatic-typecheck] diff --git a/tests/functional/u/unidiomatic_typecheck.txt b/tests/functional/u/unidiomatic_typecheck.txt index 84f5021d96..cc7e1902cc 100644 --- a/tests/functional/u/unidiomatic_typecheck.txt +++ b/tests/functional/u/unidiomatic_typecheck.txt @@ -6,9 +6,15 @@ unidiomatic-typecheck:12:4:12:20:simple_inference_positives:Use isinstance() rat unidiomatic-typecheck:13:4:13:24:simple_inference_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED unidiomatic-typecheck:14:4:14:20:simple_inference_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED unidiomatic-typecheck:15:4:15:20:simple_inference_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED -unidiomatic-typecheck:65:4:65:23:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED -unidiomatic-typecheck:66:4:66:27:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED -unidiomatic-typecheck:67:4:67:23:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED -unidiomatic-typecheck:68:4:68:27:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED -unidiomatic-typecheck:69:4:69:23:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED -unidiomatic-typecheck:70:4:70:27:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED +unidiomatic-typecheck:73:4:73:23:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED +unidiomatic-typecheck:74:4:74:27:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED +unidiomatic-typecheck:75:4:75:23:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED +unidiomatic-typecheck:76:4:76:27:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED +unidiomatic-typecheck:77:4:77:23:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED +unidiomatic-typecheck:78:4:78:27:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED +unidiomatic-typecheck:79:4:79:23:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED +unidiomatic-typecheck:80:4:80:23:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED +unidiomatic-typecheck:81:4:81:23:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED +unidiomatic-typecheck:82:4:82:23:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED +unidiomatic-typecheck:83:4:83:23:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED +unidiomatic-typecheck:84:4:84:23:type_of_literals_positives:Use isinstance() rather than type() for a typecheck.:UNDEFINED From f798a4a3508bcbb8ad0773ae14bf32d28dcfdcbe Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 4 May 2025 17:56:48 +0200 Subject: [PATCH 10/10] Bump pylint to 3.3.7, update changelog (#10367) --- doc/whatsnew/3/3.3/index.rst | 41 +++++++++++++++++++++ doc/whatsnew/fragments/10105.bugfix | 3 -- doc/whatsnew/fragments/10161.false_positive | 3 -- doc/whatsnew/fragments/10277.other | 4 -- doc/whatsnew/fragments/10282.bugfix | 3 -- doc/whatsnew/fragments/10334.bugfix | 3 -- examples/pylintrc | 8 ++-- examples/pyproject.toml | 7 ++-- pylint/__pkginfo__.py | 2 +- tbump.toml | 2 +- towncrier.toml | 2 +- 11 files changed, 52 insertions(+), 26 deletions(-) delete mode 100644 doc/whatsnew/fragments/10105.bugfix delete mode 100644 doc/whatsnew/fragments/10161.false_positive delete mode 100644 doc/whatsnew/fragments/10277.other delete mode 100644 doc/whatsnew/fragments/10282.bugfix delete mode 100644 doc/whatsnew/fragments/10334.bugfix diff --git a/doc/whatsnew/3/3.3/index.rst b/doc/whatsnew/3/3.3/index.rst index b6bb419f4b..29c4834c0a 100644 --- a/doc/whatsnew/3/3.3/index.rst +++ b/doc/whatsnew/3/3.3/index.rst @@ -14,6 +14,47 @@ Summary -- Release highlights .. towncrier release notes start +What's new in Pylint 3.3.7? +--------------------------- +Release date: 2025-05-04 + + +False Positives Fixed +--------------------- + +- Comparisons between two calls to `type()` won't raise an ``unidiomatic-typecheck`` warning anymore, consistent with the behavior applied only for ``==`` previously. + + Closes #10161 (`#10161 `_) + + + +Other Bug Fixes +--------------- + +- Fixed a crash when importing a class decorator that did not exist with the same name as a class attribute after the class definition. + + Closes #10105 (`#10105 `_) + +- Fix a crash caused by malformed format strings when using `.format` with keyword arguments. + + Closes #10282 (`#10282 `_) + +- Using a slice as a class decorator now raises a ``not-callable`` message instead of crashing. A lot of checks that dealt with decorators (too many to list) are now shortcut if the decorator can't immediately be inferred to a function or class definition. + + Closes #10334 (`#10334 `_) + + + +Other Changes +------------- + +- The algorithm used for ``no-member`` suggestions is now more efficient and cuts the + calculation when the distance score is already above the threshold. + + Refs #10277 (`#10277 `_) + + + What's new in Pylint 3.3.6? --------------------------- Release date: 2025-03-20 diff --git a/doc/whatsnew/fragments/10105.bugfix b/doc/whatsnew/fragments/10105.bugfix deleted file mode 100644 index 608a723f71..0000000000 --- a/doc/whatsnew/fragments/10105.bugfix +++ /dev/null @@ -1,3 +0,0 @@ -Fixed a crash when importing a class decorator that did not exist with the same name than a class attribute after the class definition. - -Closes #10105 diff --git a/doc/whatsnew/fragments/10161.false_positive b/doc/whatsnew/fragments/10161.false_positive deleted file mode 100644 index dc343e05ae..0000000000 --- a/doc/whatsnew/fragments/10161.false_positive +++ /dev/null @@ -1,3 +0,0 @@ -Comparisons between two calls to `type()` won't raise an ``unidiomatic-typecheck`` warning anymore, consistent with the behavior applied only for ``==`` previously. - -Closes #10161 diff --git a/doc/whatsnew/fragments/10277.other b/doc/whatsnew/fragments/10277.other deleted file mode 100644 index a7a1bfb0b2..0000000000 --- a/doc/whatsnew/fragments/10277.other +++ /dev/null @@ -1,4 +0,0 @@ -The algorithm used for ``no-member`` suggestions is now more efficient and cut the -calculation when the distance score is already above the threshold. - -Refs #10277 diff --git a/doc/whatsnew/fragments/10282.bugfix b/doc/whatsnew/fragments/10282.bugfix deleted file mode 100644 index 42f75bf0ee..0000000000 --- a/doc/whatsnew/fragments/10282.bugfix +++ /dev/null @@ -1,3 +0,0 @@ -Fix a crash caused by malformed format strings when using `.format` with keyword arguments. - -Closes #10282 diff --git a/doc/whatsnew/fragments/10334.bugfix b/doc/whatsnew/fragments/10334.bugfix deleted file mode 100644 index 6c2a24a748..0000000000 --- a/doc/whatsnew/fragments/10334.bugfix +++ /dev/null @@ -1,3 +0,0 @@ -Using a slice as a class decorator now raise a 'not-callable' message instead of crashing pylint. A lot of checks that dealt with decorators (too many to list) are now shortcut if the decorator can't immediately be inferred to a function or class definition. - -Closes #10334 diff --git a/examples/pylintrc b/examples/pylintrc index a50908c367..8c7580fd0c 100644 --- a/examples/pylintrc +++ b/examples/pylintrc @@ -495,10 +495,10 @@ evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor # used to format the message information. See doc for all details. msg-template= -# Set the output format. Available formats are: text, parseable, colorized, -# json2 (improved json format), json (old json format) and msvs (visual -# studio). You can also give a reporter class, e.g. -# mypackage.mymodule.MyReporterClass. +# Set the output format. Available formats are: 'text', 'parseable', +# 'colorized', 'json2' (improved json format), 'json' (old json format), msvs +# (visual studio) and 'github' (GitHub actions). You can also give a reporter +# class, e.g. mypackage.mymodule.MyReporterClass. #output-format= # Tells whether to display a full report or only the messages. diff --git a/examples/pyproject.toml b/examples/pyproject.toml index b342ed2536..33df0cafc5 100644 --- a/examples/pyproject.toml +++ b/examples/pyproject.toml @@ -438,9 +438,10 @@ evaluation = "max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refact # used to format the message information. See doc for all details. # msg-template = -# Set the output format. Available formats are: text, parseable, colorized, json2 -# (improved json format), json (old json format) and msvs (visual studio). You -# can also give a reporter class, e.g. mypackage.mymodule.MyReporterClass. +# Set the output format. Available formats are: 'text', 'parseable', 'colorized', +# 'json2' (improved json format), 'json' (old json format), msvs (visual studio) +# and 'github' (GitHub actions). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. # output-format = # Tells whether to display a full report or only the messages. diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index a6f117f4c9..6d9fccb5bd 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -9,7 +9,7 @@ from __future__ import annotations -__version__ = "3.3.6" +__version__ = "3.3.7" def get_numversion_from_version(v: str) -> tuple[int, int, int]: diff --git a/tbump.toml b/tbump.toml index c83b9d67b5..5da7f7ef37 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,7 +1,7 @@ github_url = "https://github.com/pylint-dev/pylint" [version] -current = "3.3.6" +current = "3.3.7" regex = ''' ^(?P0|[1-9]\d*) \. diff --git a/towncrier.toml b/towncrier.toml index 95c89ef963..a497b544e9 100644 --- a/towncrier.toml +++ b/towncrier.toml @@ -1,5 +1,5 @@ [tool.towncrier] -version = "3.3.6" +version = "3.3.7" directory = "doc/whatsnew/fragments" filename = "doc/whatsnew/3/3.3/index.rst" template = "doc/whatsnew/fragments/_template.rst" pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy