From 5fce7909185a5e83d1691ae8609e2b8d02e5ad29 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 17 May 2025 12:29:31 +0200 Subject: [PATCH 01/11] [ci] Install pylint before every test (#10388) (#10389) Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> --- .github/workflows/checks.yaml | 3 ++- .github/workflows/primer-test.yaml | 2 +- .github/workflows/primer_run_main.yaml | 2 +- .github/workflows/primer_run_pr.yaml | 2 +- .github/workflows/tests.yaml | 4 +++- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 0b3ac72fc6..a5704e6a11 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -119,7 +119,7 @@ jobs: - name: Run pylint checks run: | . venv/bin/activate - pip install . + pip install . --no-deps pip list | grep 'astroid\|pylint' pre-commit run --hook-stage manual pylint-with-spelling --all-files @@ -149,6 +149,7 @@ jobs: - name: Run spelling checks run: | . venv/bin/activate + pip install . --no-deps pytest tests/ -k unittest_spelling documentation: diff --git a/.github/workflows/primer-test.yaml b/.github/workflows/primer-test.yaml index e19cb28fb9..c7cbddc868 100644 --- a/.github/workflows/primer-test.yaml +++ b/.github/workflows/primer-test.yaml @@ -94,5 +94,5 @@ jobs: - name: Run pytest run: | . venv/bin/activate - pip install . + pip install . --no-deps pytest -m primer_stdlib --primer-stdlib -n auto -vv diff --git a/.github/workflows/primer_run_main.yaml b/.github/workflows/primer_run_main.yaml index 56ba1cfa2d..3789399ac6 100644 --- a/.github/workflows/primer_run_main.yaml +++ b/.github/workflows/primer_run_main.yaml @@ -113,7 +113,7 @@ jobs: - name: Run pylint primer run: | . venv/bin/activate - pip install . + pip install . --no-deps python tests/primer/__main__.py run --type=main --batches=${{ matrix.batches }} --batchIdx=${{ matrix.batchIdx }} 2>warnings.txt - name: Echo warnings if: success() || failure() diff --git a/.github/workflows/primer_run_pr.yaml b/.github/workflows/primer_run_pr.yaml index dfe7398546..024ec1695c 100644 --- a/.github/workflows/primer_run_pr.yaml +++ b/.github/workflows/primer_run_pr.yaml @@ -187,7 +187,7 @@ jobs: - name: Run pylint primer run: | . venv/bin/activate - pip install . + pip install . --no-deps python tests/primer/__main__.py run --type=pr --batches=${{ matrix.batches }} --batchIdx=${{ matrix.batchIdx }} 2>warnings.txt - name: Echo warnings if: success() || failure() diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index fce0a624c2..9f0bae3efb 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -68,6 +68,7 @@ jobs: - name: Run pytest run: | . venv/bin/activate + pip install . --no-deps pip list | grep 'astroid\|pylint' python -m pytest --durations=10 --benchmark-disable --cov --cov-report= tests/ - name: Run functional tests with minimal messages config @@ -149,7 +150,7 @@ jobs: run: | . venv/bin/activate pip install pygal - pip install . + pip install . --no-deps pip list | grep 'astroid\|pylint' pytest --exitfirst \ --benchmark-only \ @@ -215,6 +216,7 @@ jobs: - name: Run pytest run: | . venv\\Scripts\\activate + pip install . --no-deps pip list | grep 'astroid\|pylint' python -m pytest --durations=10 --benchmark-disable tests/ From 5addaaa5f39bc24edb3926862b443f314db7340b Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 18 May 2025 13:09:21 +0200 Subject: [PATCH 02/11] Improve backport job permissions (#10390) (#10391) (cherry picked from commit 222ab2005b4cd2cbd0c5ed245d2fe5d66c878f55) --- .github/workflows/backport.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 9fbc070e5d..eaa4b221e4 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -6,14 +6,14 @@ on: - labeled permissions: - actions: write - contents: write - pull-requests: write + contents: read jobs: backport: name: Backport runs-on: ubuntu-latest + environment: + name: Backport # Only react to merged PRs for security reasons. # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. if: > @@ -27,4 +27,4 @@ jobs: steps: - uses: tibdex/backport@9565281eda0731b1d20c4025c43339fb0a23812e # v2.0.4 with: - github_token: ${{ secrets.GITHUB_TOKEN }} + github_token: ${{ secrets.BACKPORT_TOKEN }} From 045f179c0e86ff3a31f7b9e329caea0976c59757 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sun, 18 May 2025 15:35:50 -0400 Subject: [PATCH 03/11] Resolve `possibly-used-before-assignment` false positives from `match` block assignments (#10393) (cherry picked from commit ad14b5bf9f2eaaf9b69a0daef09d1d944e1b11cf) --- doc/whatsnew/fragments/9668.false_positive | 4 + pylint/checkers/variables.py | 12 +++ .../u/used/used_before_assignment_py310.py | 101 ++++++++++++++++++ .../u/used/used_before_assignment_py310.txt | 1 + 4 files changed, 118 insertions(+) create mode 100644 doc/whatsnew/fragments/9668.false_positive create mode 100644 tests/functional/u/used/used_before_assignment_py310.txt diff --git a/doc/whatsnew/fragments/9668.false_positive b/doc/whatsnew/fragments/9668.false_positive new file mode 100644 index 0000000000..9668bd217e --- /dev/null +++ b/doc/whatsnew/fragments/9668.false_positive @@ -0,0 +1,4 @@ +Fix false positives for `possibly-used-before-assignment` when variables are exhaustively +assigned within a `match` block. + +Closes #9668 diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 502ef481b9..3705ac5c7c 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -720,6 +720,12 @@ def _inferred_to_define_name_raise_or_return( if isinstance(node, (nodes.With, nodes.For, nodes.While)): return NamesConsumer._defines_name_raises_or_returns_recursive(name, node) + if isinstance(node, nodes.Match): + return all( + NamesConsumer._defines_name_raises_or_returns_recursive(name, case) + for case in node.cases + ) + if not isinstance(node, nodes.If): return False @@ -768,6 +774,7 @@ def _branch_handles_name(self, name: str, body: Iterable[nodes.NodeNG]) -> bool: nodes.With, nodes.For, nodes.While, + nodes.Match, ), ) and self._inferred_to_define_name_raise_or_return(name, if_body_stmt) @@ -1010,6 +1017,11 @@ def _defines_name_raises_or_returns_recursive( and NamesConsumer._defines_name_raises_or_returns_recursive(name, stmt) ): return True + if isinstance(stmt, nodes.Match): + return all( + NamesConsumer._defines_name_raises_or_returns_recursive(name, case) + for case in stmt.cases + ) return False @staticmethod diff --git a/tests/functional/u/used/used_before_assignment_py310.py b/tests/functional/u/used/used_before_assignment_py310.py index 14f46b61e9..0f6db9c1c4 100644 --- a/tests/functional/u/used/used_before_assignment_py310.py +++ b/tests/functional/u/used/used_before_assignment_py310.py @@ -5,3 +5,104 @@ print("x used to cause used-before-assignment!") case _: print("good thing it doesn't now!") + + +# pylint: disable = missing-function-docstring, redefined-outer-name, missing-class-docstring + +# https://github.com/pylint-dev/pylint/issues/9668 +from enum import Enum +from pylint.constants import PY311_PLUS +if PY311_PLUS: + from typing import assert_never # pylint: disable=no-name-in-module +else: + from typing_extensions import assert_never + +class Example(Enum): + FOO = 1 + BAR = 2 + +def check_value_if_then_match_return(example: Example, should_check: bool) -> str | None: + if should_check: + result = None + else: + match example: + case Example.FOO: + result = "foo" + case Example.BAR: + result = "bar" + case _: + return None + + return result + +def check_value_if_then_match_raise(example: Example, should_check: bool) -> str | None: + if should_check: + result = None + else: + match example: + case Example.FOO: + result = "foo" + case Example.BAR: + result = "bar" + case _: + raise ValueError("Not a valid enum") + + return result + +def check_value_if_then_match_assert_never(example: Example, should_check: bool) -> str | None: + if should_check: + result = None + else: + match example: + case Example.FOO: + result = "foo" + case Example.BAR: + result = "bar" + case _: + assert_never(example) + + return result + +def g(x): + if x is None: + y = 0 + else: + match x: + case int(): + y = x + case _: + raise TypeError(type(x)) + + return y + +def check_value_if_then_match_nested( + example: Example, example_inner: Example, should_check: bool +) -> str | None: + if should_check: + result = None + else: + match example: + case Example.FOO: + match example_inner: + case Example.BAR: + result = "bar" + case _: + return None + case _: + return None + + return result + +def check_value_if_then_match_non_exhaustive(example: Example, should_check: bool) -> str | None: + if should_check: + result = None + else: + match example: + case Example.FOO: + result = "foo" + case Example.BAR: + pass + case _: + return None + + return result # [possibly-used-before-assignment] diff --git a/tests/functional/u/used/used_before_assignment_py310.txt b/tests/functional/u/used/used_before_assignment_py310.txt new file mode 100644 index 0000000000..5f2d3074a7 --- /dev/null +++ b/tests/functional/u/used/used_before_assignment_py310.txt @@ -0,0 +1 @@ +possibly-used-before-assignment:108:11:108:17:check_value_if_then_match_non_exhaustive:Possibly using variable 'result' before assignment:CONTROL_FLOW From 329c96798de2dcfc4fa64ec6f3eaa39a538e2186 Mon Sep 17 00:00:00 2001 From: "pylint-backport-bot[bot]" <212256041+pylint-backport-bot[bot]@users.noreply.github.com> Date: Sun, 18 May 2025 23:34:01 +0200 Subject: [PATCH 04/11] Use custom Github App to authenticate backport job (#10394) (#10396) (cherry picked from commit 6be86761d55688fccfc7aec80de615d324cce275) Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> --- .github/workflows/backport.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index eaa4b221e4..9e83961662 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -25,6 +25,14 @@ jobs: ) ) steps: + - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 + id: app-token + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.PRIVATE_KEY }} + permission-contents: write # push branch to Github + permission-pull-requests: write # create PR / add comment for manual backport + permission-workflows: write # modify files in .github/workflows - uses: tibdex/backport@9565281eda0731b1d20c4025c43339fb0a23812e # v2.0.4 with: - github_token: ${{ secrets.BACKPORT_TOKEN }} + github_token: ${{ steps.app-token.outputs.token }} From 51234e556d7c6054993097f2df8ae3927686a964 Mon Sep 17 00:00:00 2001 From: "pylint-backport-bot[bot]" <212256041+pylint-backport-bot[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 17:45:16 +0000 Subject: [PATCH 05/11] Fix Pyreverse: Aggregations aren't filtered according to filter mode (PUB_ONLY, etc.) (#10379) (#10401) * updated diagrams.py file * added tests * updated tests * added test cases * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- (cherry picked from commit ed5963235706741560b095bd7e5a8865e3be1009) Co-authored-by: pavan-msys <149513767+pavan-msys@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- doc/whatsnew/fragments/10373.bugfix | 3 +++ pylint/pyreverse/diagrams.py | 6 +++++ .../aggregation_filtering/all.mmd | 25 +++++++++++++++++++ .../aggregation_filtering/all.py | 23 +++++++++++++++++ .../aggregation_filtering/all.rc | 2 ++ .../aggregation_filtering/other.mmd | 21 ++++++++++++++++ .../aggregation_filtering/other.py | 23 +++++++++++++++++ .../aggregation_filtering/other.rc | 2 ++ .../aggregation_filtering/pub_only.mmd | 14 +++++++++++ .../aggregation_filtering/pub_only.py | 23 +++++++++++++++++ .../aggregation_filtering/pub_only.rc | 2 ++ .../aggregation_filtering/special.mmd | 18 +++++++++++++ .../aggregation_filtering/special.py | 23 +++++++++++++++++ .../aggregation_filtering/special.rc | 2 ++ 14 files changed, 187 insertions(+) create mode 100644 doc/whatsnew/fragments/10373.bugfix create mode 100644 tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd create mode 100644 tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.py create mode 100644 tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.rc create mode 100644 tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd create mode 100644 tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.py create mode 100644 tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.rc create mode 100644 tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.mmd create mode 100644 tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.py create mode 100644 tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.rc create mode 100644 tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd create mode 100644 tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.py create mode 100644 tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.rc diff --git a/doc/whatsnew/fragments/10373.bugfix b/doc/whatsnew/fragments/10373.bugfix new file mode 100644 index 0000000000..d7ba3a1893 --- /dev/null +++ b/doc/whatsnew/fragments/10373.bugfix @@ -0,0 +1,3 @@ +Fix a bug in Pyreverse where aggregations and associations were included in diagrams regardless of the selected --filter-mode (such as PUB_ONLY, ALL, etc.). + +Closes #10373 diff --git a/pylint/pyreverse/diagrams.py b/pylint/pyreverse/diagrams.py index 278102cab8..9e0d27cde7 100644 --- a/pylint/pyreverse/diagrams.py +++ b/pylint/pyreverse/diagrams.py @@ -221,6 +221,9 @@ def extract_relationships(self) -> None: # associations & aggregations links for name, values in list(node.aggregations_type.items()): for value in values: + if not self.show_attr(name): + continue + self.assign_association_relationship( value, obj, name, "aggregation" ) @@ -233,6 +236,9 @@ def extract_relationships(self) -> None: for name, values in associations.items(): for value in values: + if not self.show_attr(name): + continue + self.assign_association_relationship( value, obj, name, "association" ) diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd new file mode 100644 index 0000000000..3b445288a1 --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd @@ -0,0 +1,25 @@ +classDiagram + class P { + name : str + __init__(name: str) + } + class PrivateAttr { + __x + __init__() + } + class ProtectedAttr { + _x + __init__() + } + class PublicAttr { + x + __init__() + } + class SpecialAttr { + __x__ + __init__() + } + P --* PrivateAttr : __x + P --* ProtectedAttr : _x + P --* PublicAttr : x + P --* SpecialAttr : __x__ diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.py b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.py new file mode 100644 index 0000000000..609f4376be --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.py @@ -0,0 +1,23 @@ +class P: + def __init__(self, name: str): + self.name = name + + +class PrivateAttr: + def __init__(self): + self.__x = P("private") + + +class ProtectedAttr: + def __init__(self): + self._x = P("protected") + + +class PublicAttr: + def __init__(self): + self.x = P("public") + + +class SpecialAttr: + def __init__(self): + self.__x__ = P("special") diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.rc b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.rc new file mode 100644 index 0000000000..08fbb3d02d --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.rc @@ -0,0 +1,2 @@ +[testoptions] +command_line_args=--filter-mode=ALL diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd new file mode 100644 index 0000000000..6cfcae244f --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd @@ -0,0 +1,21 @@ +classDiagram + class P { + name : str + __init__(name: str) + } + class PrivateAttr { + __init__() + } + class ProtectedAttr { + __init__() + } + class PublicAttr { + x + __init__() + } + class SpecialAttr { + __x__ + __init__() + } + P --* PublicAttr : x + P --* SpecialAttr : __x__ diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.py b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.py new file mode 100644 index 0000000000..609f4376be --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.py @@ -0,0 +1,23 @@ +class P: + def __init__(self, name: str): + self.name = name + + +class PrivateAttr: + def __init__(self): + self.__x = P("private") + + +class ProtectedAttr: + def __init__(self): + self._x = P("protected") + + +class PublicAttr: + def __init__(self): + self.x = P("public") + + +class SpecialAttr: + def __init__(self): + self.__x__ = P("special") diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.rc b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.rc new file mode 100644 index 0000000000..8e0e615079 --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.rc @@ -0,0 +1,2 @@ +[testoptions] +command_line_args=--filter-mode=OTHER diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.mmd b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.mmd new file mode 100644 index 0000000000..f29000b5c8 --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.mmd @@ -0,0 +1,14 @@ +classDiagram + class P { + name : str + } + class PrivateAttr { + } + class ProtectedAttr { + } + class PublicAttr { + x + } + class SpecialAttr { + } + P --* PublicAttr : x diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.py b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.py new file mode 100644 index 0000000000..609f4376be --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.py @@ -0,0 +1,23 @@ +class P: + def __init__(self, name: str): + self.name = name + + +class PrivateAttr: + def __init__(self): + self.__x = P("private") + + +class ProtectedAttr: + def __init__(self): + self._x = P("protected") + + +class PublicAttr: + def __init__(self): + self.x = P("public") + + +class SpecialAttr: + def __init__(self): + self.__x__ = P("special") diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.rc b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.rc new file mode 100644 index 0000000000..4dddc11448 --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.rc @@ -0,0 +1,2 @@ +[testoptions] +command_line_args=--filter-mode=PUB_ONLY diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd new file mode 100644 index 0000000000..149b0a694a --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd @@ -0,0 +1,18 @@ +classDiagram + class P { + name : str + } + class PrivateAttr { + __x + } + class ProtectedAttr { + _x + } + class PublicAttr { + x + } + class SpecialAttr { + } + P --* PrivateAttr : __x + P --* ProtectedAttr : _x + P --* PublicAttr : x diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.py b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.py new file mode 100644 index 0000000000..609f4376be --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.py @@ -0,0 +1,23 @@ +class P: + def __init__(self, name: str): + self.name = name + + +class PrivateAttr: + def __init__(self): + self.__x = P("private") + + +class ProtectedAttr: + def __init__(self): + self._x = P("protected") + + +class PublicAttr: + def __init__(self): + self.x = P("public") + + +class SpecialAttr: + def __init__(self): + self.__x__ = P("special") diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.rc b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.rc new file mode 100644 index 0000000000..4e60bfdf4d --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.rc @@ -0,0 +1,2 @@ +[testoptions] +command_line_args=--filter-mode=SPECIAL From fbde89024e40c2e97846a151e2e6d9b4c340fa4c Mon Sep 17 00:00:00 2001 From: "pylint-backport-bot[bot]" <212256041+pylint-backport-bot[bot]@users.noreply.github.com> Date: Thu, 29 May 2025 13:05:41 +0200 Subject: [PATCH 06/11] Fix Mermaid printer rendering double underscores as bold formatting (#10403) (#10410) (cherry picked from commit 25a0f9e03dac32b9f2d2f0ff1c01ff70dfc0b100) Co-authored-by: Julian Grimm <51880314+Julfried@users.noreply.github.com> Co-authored-by: Pierre Sassoulas --- doc/whatsnew/fragments/10402.bugfix | 3 +++ pylint/pyreverse/mermaidjs_printer.py | 23 +++++++++++++++---- .../aggregation_filtering/all.mmd | 18 +++++++-------- .../aggregation_filtering/other.mmd | 14 +++++------ .../aggregation_filtering/special.mmd | 4 ++-- 5 files changed, 40 insertions(+), 22 deletions(-) create mode 100644 doc/whatsnew/fragments/10402.bugfix diff --git a/doc/whatsnew/fragments/10402.bugfix b/doc/whatsnew/fragments/10402.bugfix new file mode 100644 index 0000000000..a7cea6782e --- /dev/null +++ b/doc/whatsnew/fragments/10402.bugfix @@ -0,0 +1,3 @@ +Fix double underscores erroneously rendering as bold in pyreverse's Mermaid output. + +Closes #10402 diff --git a/pylint/pyreverse/mermaidjs_printer.py b/pylint/pyreverse/mermaidjs_printer.py index 24fa927761..927b5f59dc 100644 --- a/pylint/pyreverse/mermaidjs_printer.py +++ b/pylint/pyreverse/mermaidjs_printer.py @@ -32,6 +32,11 @@ def _open_graph(self) -> None: self.emit("classDiagram") self._inc_indent() + def _escape_mermaid_text(self, text: str) -> str: + """Escape characters that conflict with Markdown formatting.""" + text = text.replace("__", r"\_\_") # Double underscore → escaped + return text + def emit_node( self, name: str, @@ -48,14 +53,24 @@ def emit_node( nodetype = self.NODES[type_] body = [] if properties.attrs: - body.extend(properties.attrs) + # Escape attribute names to prevent Markdown formatting issues + escaped_attrs = [ + self._escape_mermaid_text(attr) for attr in properties.attrs + ] + body.extend(escaped_attrs) if properties.methods: for func in properties.methods: args = self._get_method_arguments(func) - line = f"{func.name}({', '.join(args)})" + # Escape method name and arguments + escaped_method_name = self._escape_mermaid_text(func.name) + escaped_args = [self._escape_mermaid_text(arg) for arg in args] + line = f"{escaped_method_name}({', '.join(escaped_args)})" line += "*" if func.is_abstract() else "" if func.returns: - line += f" {get_annotation_label(func.returns)}" + # Escape return type annotation + return_type = get_annotation_label(func.returns) + escaped_return_type = self._escape_mermaid_text(return_type) + line += f" {escaped_return_type}" body.append(line) name = name.split(".")[-1] self.emit(f"{nodetype} {name} {{") @@ -77,7 +92,7 @@ def emit_edge( to_node = to_node.split(".")[-1] edge = f"{from_node} {self.ARROWS[type_]} {to_node}" if label: - edge += f" : {label}" + edge += f" : {self._escape_mermaid_text(label)}" self.emit(edge) def _close_graph(self) -> None: diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd index 3b445288a1..378ef5ccaf 100644 --- a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd @@ -1,25 +1,25 @@ classDiagram class P { name : str - __init__(name: str) + \_\_init\_\_(name: str) } class PrivateAttr { - __x - __init__() + \_\_x + \_\_init\_\_() } class ProtectedAttr { _x - __init__() + \_\_init\_\_() } class PublicAttr { x - __init__() + \_\_init\_\_() } class SpecialAttr { - __x__ - __init__() + \_\_x\_\_ + \_\_init\_\_() } - P --* PrivateAttr : __x + P --* PrivateAttr : \_\_x P --* ProtectedAttr : _x P --* PublicAttr : x - P --* SpecialAttr : __x__ + P --* SpecialAttr : \_\_x\_\_ diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd index 6cfcae244f..07439b6c55 100644 --- a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd @@ -1,21 +1,21 @@ classDiagram class P { name : str - __init__(name: str) + \_\_init\_\_(name: str) } class PrivateAttr { - __init__() + \_\_init\_\_() } class ProtectedAttr { - __init__() + \_\_init\_\_() } class PublicAttr { x - __init__() + \_\_init\_\_() } class SpecialAttr { - __x__ - __init__() + \_\_x\_\_ + \_\_init\_\_() } P --* PublicAttr : x - P --* SpecialAttr : __x__ + P --* SpecialAttr : \_\_x\_\_ diff --git a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd index 149b0a694a..6b7f39b905 100644 --- a/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd +++ b/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd @@ -3,7 +3,7 @@ classDiagram name : str } class PrivateAttr { - __x + \_\_x } class ProtectedAttr { _x @@ -13,6 +13,6 @@ classDiagram } class SpecialAttr { } - P --* PrivateAttr : __x + P --* PrivateAttr : \_\_x P --* ProtectedAttr : _x P --* PublicAttr : x From 6ec71ba09efcec9c39187475eefc64f16f0208bf Mon Sep 17 00:00:00 2001 From: "pylint-backport-bot[bot]" <212256041+pylint-backport-bot[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 18:31:06 +0000 Subject: [PATCH 07/11] Respect docstring-min-length in docparams extension (#10104) (#10434) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 7f5e9965f01a767490c2c9ae69e3150ba03999eb) Co-authored-by: Berker ŞAL --- doc/whatsnew/fragments/10104.false_positive | 3 ++ pylint/extensions/docparams.py | 28 +++++++++++++++++-- .../missing_raises_doc_required_min_length.py | 10 +++++++ .../missing_raises_doc_required_min_length.rc | 7 +++++ .../missing_yield_doc_required_min_length.py | 10 +++++++ .../missing_yield_doc_required_min_length.rc | 7 +++++ 6 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 doc/whatsnew/fragments/10104.false_positive create mode 100644 tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.py create mode 100644 tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.rc create mode 100644 tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.py create mode 100644 tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.rc diff --git a/doc/whatsnew/fragments/10104.false_positive b/doc/whatsnew/fragments/10104.false_positive new file mode 100644 index 0000000000..3a036d5a50 --- /dev/null +++ b/doc/whatsnew/fragments/10104.false_positive @@ -0,0 +1,3 @@ +Fix false positive for `missing-raises-doc` and `missing-yield-doc` when the method length is less than docstring-min-length. + +Refs #10104 diff --git a/pylint/extensions/docparams.py b/pylint/extensions/docparams.py index b19560b7fb..cef8397109 100644 --- a/pylint/extensions/docparams.py +++ b/pylint/extensions/docparams.py @@ -209,9 +209,7 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None: return # skip functions smaller than 'docstring-min-length' - lines = checker_utils.get_node_last_lineno(node) - node.lineno - max_lines = self.linter.config.docstring_min_length - if max_lines > -1 and lines < max_lines: + if self._is_shorter_than_min_length(node): return self.check_functiondef_params(node, node_doc) @@ -281,6 +279,10 @@ def visit_raise(self, node: nodes.Raise) -> None: if not isinstance(func_node, astroid.FunctionDef): return + # skip functions smaller than 'docstring-min-length' + if self._is_shorter_than_min_length(node): + return + # skip functions that match the 'no-docstring-rgx' config option no_docstring_rgx = self.linter.config.no_docstring_rgx if no_docstring_rgx and re.match(no_docstring_rgx, func_node.name): @@ -364,6 +366,10 @@ def visit_yield(self, node: nodes.Yield | nodes.YieldFrom) -> None: if self.linter.config.accept_no_yields_doc: return + # skip functions smaller than 'docstring-min-length' + if self._is_shorter_than_min_length(node): + return + func_node: astroid.FunctionDef = node.frame() # skip functions that match the 'no-docstring-rgx' config option @@ -671,6 +677,22 @@ def _add_raise_message( confidence=HIGH, ) + def _is_shorter_than_min_length(self, node: nodes.FunctionDef) -> bool: + """Returns true on functions smaller than 'docstring-min-length'. + + :param node: Node for a function or method definition in the AST + :type node: :class:`astroid.scoped_nodes.Function` + + :rtype: bool + """ + node_line_count = checker_utils.get_node_last_lineno(node) - node.lineno + min_lines = self.linter.config.docstring_min_length + result = -1 < node_line_count < min_lines + assert isinstance( + result, bool + ), "Result of int comparison should have been a boolean" + return result + def register(linter: PyLinter) -> None: linter.register_checker(DocstringParameterChecker(linter)) diff --git a/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.py b/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.py new file mode 100644 index 0000000000..b5a487ca63 --- /dev/null +++ b/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.py @@ -0,0 +1,10 @@ +"""Tests for missing-raises-doc for non-specified style docstrings +with accept-no-raise-doc = no and docstring-min-length = 3 +""" +# pylint: disable=invalid-name, broad-exception-raised + +# Example of a function that is less than 'docstring-min-length' config option +# No error message is emitted. +def test_skip_docstring_min_length(): + """function is too short and is missing raise documentation""" + raise Exception diff --git a/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.rc b/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.rc new file mode 100644 index 0000000000..6c6bb7072f --- /dev/null +++ b/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.rc @@ -0,0 +1,7 @@ +[MAIN] +load-plugins = pylint.extensions.docparams + +[BASIC] +accept-no-raise-doc=no +docstring-min-length=3 +no-docstring-rgx=^$ diff --git a/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.py b/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.py new file mode 100644 index 0000000000..72d8f845a3 --- /dev/null +++ b/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.py @@ -0,0 +1,10 @@ +"""Tests for missing-yield-doc for non-specified style docstrings +with accept-no-yields-doc = no and docstring-min-length = 3 +""" +# pylint: disable=invalid-name + +# Example of a function that is less than 'docstring-min-length' config option +# No error message is emitted. +def test_skip_docstring_min_length(): + """function is too short and is missing yield documentation""" + yield None diff --git a/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.rc b/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.rc new file mode 100644 index 0000000000..789ae21b57 --- /dev/null +++ b/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.rc @@ -0,0 +1,7 @@ +[MAIN] +load-plugins = pylint.extensions.docparams + +[BASIC] +accept-no-yields-doc=no +docstring-min-length=3 +no-docstring-rgx=^$ From 5a5a9be7abcf44e55f789b8fb917f32d7a01a509 Mon Sep 17 00:00:00 2001 From: "pylint-backport-bot[bot]" <212256041+pylint-backport-bot[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 14:29:35 -0400 Subject: [PATCH 08/11] Fix `unused-variable` false positive when using same name for multiple exceptions (#10436) (#10481) (cherry picked from commit 9e72867c1ac46cb0a2f88abf6f596530c2fa1708) Co-authored-by: Zen Lee <53538590+zenlyj@users.noreply.github.com> --- doc/whatsnew/fragments/10426.false_positive | 3 +++ pylint/checkers/variables.py | 15 ++++++++++++--- .../u/unused/unused_global_variable2.py | 8 ++++++++ tests/functional/u/unused/unused_variable.py | 11 +++++++++++ tests/functional/u/unused/unused_variable.txt | 2 +- 5 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 doc/whatsnew/fragments/10426.false_positive diff --git a/doc/whatsnew/fragments/10426.false_positive b/doc/whatsnew/fragments/10426.false_positive new file mode 100644 index 0000000000..caa29449ee --- /dev/null +++ b/doc/whatsnew/fragments/10426.false_positive @@ -0,0 +1,3 @@ +Fix a false positive for ``unused-variable`` when multiple except handlers bind the same name under a try block. + +Closes #10426 diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 3705ac5c7c..5f5259e79a 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -2836,9 +2836,7 @@ def _check_is_unused( return # Special case for exception variable - if isinstance(stmt.parent, nodes.ExceptHandler) and any( - n.name == name for n in stmt.parent.nodes_of_class(nodes.Name) - ): + if self._is_exception_binding_used_in_handler(stmt, name): return self.add_message(message_name, args=name, node=stmt) @@ -2913,6 +2911,15 @@ def _check_unused_arguments( self.add_message("unused-argument", args=name, node=stmt, confidence=confidence) + def _is_exception_binding_used_in_handler( + self, stmt: nodes.NodeNG, name: str + ) -> bool: + return ( + isinstance(stmt.parent, nodes.ExceptHandler) + and stmt is stmt.parent.name + and any(n.name == name for n in stmt.parent.nodes_of_class(nodes.Name)) + ) + def _check_late_binding_closure(self, node: nodes.Name) -> None: """Check whether node is a cell var that is assigned within a containing loop. @@ -3238,6 +3245,8 @@ def _check_globals(self, not_consumed: dict[str, nodes.NodeNG]) -> None: for node in node_lst: if in_type_checking_block(node): continue + if self._is_exception_binding_used_in_handler(node, name): + continue self.add_message("unused-variable", args=(name,), node=node) # pylint: disable = too-many-branches diff --git a/tests/functional/u/unused/unused_global_variable2.py b/tests/functional/u/unused/unused_global_variable2.py index 0722bc63c8..06242bb6e3 100644 --- a/tests/functional/u/unused/unused_global_variable2.py +++ b/tests/functional/u/unused/unused_global_variable2.py @@ -9,3 +9,11 @@ VAR = 'pylint' # [unused-variable] + + +try: + pass +except TypeError as exc: + print(exc) +except ValueError as exc: + print(exc) diff --git a/tests/functional/u/unused/unused_variable.py b/tests/functional/u/unused/unused_variable.py index f15ae75576..b4f5263d56 100644 --- a/tests/functional/u/unused/unused_variable.py +++ b/tests/functional/u/unused/unused_variable.py @@ -187,6 +187,17 @@ def sibling_except_handlers(): except ValueError as e: print(e) + +def test_multiple_except_handlers_under_try(): + try: + pass + except TypeError as exc: + print(exc) + except ValueError as exc: + print(exc) + except ArithmeticError as exc: + print(exc) + def func6(): a = 1 diff --git a/tests/functional/u/unused/unused_variable.txt b/tests/functional/u/unused/unused_variable.txt index 609ce9fef6..8800488bfb 100644 --- a/tests/functional/u/unused/unused_variable.txt +++ b/tests/functional/u/unused/unused_variable.txt @@ -26,4 +26,4 @@ unused-variable:150:4:154:26:func4:Unused variable 'error':UNDEFINED redefined-outer-name:153:8:154:26:func4:Redefining name 'error' from outer scope (line 150):UNDEFINED unused-variable:161:4:162:12:main:Unused variable 'e':UNDEFINED undefined-loop-variable:168:10:168:11:main:Using possibly undefined loop variable 'e':UNDEFINED -unused-variable:217:8:218:16:test_regression_8595:Unused variable 'e':UNDEFINED +unused-variable:228:8:229:16:test_regression_8595:Unused variable 'e':UNDEFINED From cef60cc3a557e22e70a25de121ac2810869a0256 Mon Sep 17 00:00:00 2001 From: "pylint-backport-bot[bot]" <212256041+pylint-backport-bot[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 09:56:00 +0000 Subject: [PATCH 09/11] Fix false-negative for used-before-assignment with postponed evaluation in function defs (#10482) (#10483) (cherry picked from commit d363fca110725ac58a009a01a2c505b867bed1d3) Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> --- doc/whatsnew/fragments/10482.false_negative | 3 +++ pylint/checkers/variables.py | 12 +++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 doc/whatsnew/fragments/10482.false_negative diff --git a/doc/whatsnew/fragments/10482.false_negative b/doc/whatsnew/fragments/10482.false_negative new file mode 100644 index 0000000000..f689436b8b --- /dev/null +++ b/doc/whatsnew/fragments/10482.false_negative @@ -0,0 +1,3 @@ +Fix false-negative for ``used-before-assignment`` with ``from __future__ import annotations`` in function definitions. + +Refs #10482 diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 5f5259e79a..f54153341c 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -1949,7 +1949,17 @@ def _check_consumer( # and unevaluated annotations inside a function body if not ( self._postponed_evaluation_enabled - and isinstance(stmt, (nodes.AnnAssign, nodes.FunctionDef)) + and ( + isinstance(stmt, nodes.AnnAssign) + or ( + isinstance(stmt, nodes.FunctionDef) + and node + not in { + *(stmt.args.defaults or ()), + *(stmt.args.kw_defaults or ()), + } + ) + ) ) and not ( isinstance(stmt, nodes.AnnAssign) and utils.get_node_first_ancestor_of_type(stmt, nodes.FunctionDef) From 6a1620ac1671455905a3c5d59b7421b05e505854 Mon Sep 17 00:00:00 2001 From: "pylint-backport-bot[bot]" <212256041+pylint-backport-bot[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 12:11:00 +0200 Subject: [PATCH 10/11] Update pytest-benchmark requirement from ~=4.0 to ~=5.1 (#10066) (#10484) (cherry picked from commit f04761b3ed5ec6b1abdc3b86dac9f79d15ae86a2) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test_min.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test_min.txt b/requirements_test_min.txt index 68ce269740..3fd42d5f69 100644 --- a/requirements_test_min.txt +++ b/requirements_test_min.txt @@ -4,7 +4,7 @@ astroid==3.3.8 # Pinned to a specific version for tests typing-extensions~=4.12 py~=1.11.0 pytest~=8.3 -pytest-benchmark~=4.0 +pytest-benchmark~=5.1 pytest-timeout~=2.3 towncrier~=24.8 requests From 98942ba4126a6fe1657bad77027bcc11016d16da Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Aug 2025 11:08:50 +0200 Subject: [PATCH 11/11] Bump pylint to 3.3.8, update changelog (#10486) --- doc/whatsnew/3/3.3/index.rst | 47 +++++++++++++++++++++ doc/whatsnew/fragments/10104.false_positive | 3 -- doc/whatsnew/fragments/10373.bugfix | 3 -- doc/whatsnew/fragments/10402.bugfix | 3 -- doc/whatsnew/fragments/10426.false_positive | 3 -- doc/whatsnew/fragments/10482.false_negative | 3 -- doc/whatsnew/fragments/9668.false_positive | 4 -- pylint/__pkginfo__.py | 2 +- script/.contributors_aliases.json | 1 + tbump.toml | 2 +- towncrier.toml | 2 +- 11 files changed, 51 insertions(+), 22 deletions(-) delete mode 100644 doc/whatsnew/fragments/10104.false_positive delete mode 100644 doc/whatsnew/fragments/10373.bugfix delete mode 100644 doc/whatsnew/fragments/10402.bugfix delete mode 100644 doc/whatsnew/fragments/10426.false_positive delete mode 100644 doc/whatsnew/fragments/10482.false_negative delete mode 100644 doc/whatsnew/fragments/9668.false_positive diff --git a/doc/whatsnew/3/3.3/index.rst b/doc/whatsnew/3/3.3/index.rst index 29c4834c0a..81a49032e2 100644 --- a/doc/whatsnew/3/3.3/index.rst +++ b/doc/whatsnew/3/3.3/index.rst @@ -14,6 +14,53 @@ Summary -- Release highlights .. towncrier release notes start +What's new in Pylint 3.3.8? +--------------------------- +Release date: 2025-08-09 + +This patch release includes an exceptional fix for a false negative issue. +For details, see: https://github.com/pylint-dev/pylint/pull/10482#issuecomment-3164514082 + +False Positives Fixed +--------------------- + +- Fix false positives for `possibly-used-before-assignment` when variables are exhaustively + assigned within a `match` block. + + Closes #9668 (`#9668 `_) + +- Fix false positive for `missing-raises-doc` and `missing-yield-doc` when the method length is less than docstring-min-length. + + Refs #10104 (`#10104 `_) + +- Fix a false positive for ``unused-variable`` when multiple except handlers bind the same name under a try block. + + Closes #10426 (`#10426 `_) + + + +False Negatives Fixed +--------------------- + +- Fix false-negative for ``used-before-assignment`` with ``from __future__ import annotations`` in function definitions. + + Refs #10482 (`#10482 `_) + + + +Other Bug Fixes +--------------- + +- Fix a bug in Pyreverse where aggregations and associations were included in diagrams regardless of the selected --filter-mode (such as PUB_ONLY, ALL, etc.). + + Closes #10373 (`#10373 `_) + +- Fix double underscores erroneously rendering as bold in pyreverse's Mermaid output. + + Closes #10402 (`#10402 `_) + + + What's new in Pylint 3.3.7? --------------------------- Release date: 2025-05-04 diff --git a/doc/whatsnew/fragments/10104.false_positive b/doc/whatsnew/fragments/10104.false_positive deleted file mode 100644 index 3a036d5a50..0000000000 --- a/doc/whatsnew/fragments/10104.false_positive +++ /dev/null @@ -1,3 +0,0 @@ -Fix false positive for `missing-raises-doc` and `missing-yield-doc` when the method length is less than docstring-min-length. - -Refs #10104 diff --git a/doc/whatsnew/fragments/10373.bugfix b/doc/whatsnew/fragments/10373.bugfix deleted file mode 100644 index d7ba3a1893..0000000000 --- a/doc/whatsnew/fragments/10373.bugfix +++ /dev/null @@ -1,3 +0,0 @@ -Fix a bug in Pyreverse where aggregations and associations were included in diagrams regardless of the selected --filter-mode (such as PUB_ONLY, ALL, etc.). - -Closes #10373 diff --git a/doc/whatsnew/fragments/10402.bugfix b/doc/whatsnew/fragments/10402.bugfix deleted file mode 100644 index a7cea6782e..0000000000 --- a/doc/whatsnew/fragments/10402.bugfix +++ /dev/null @@ -1,3 +0,0 @@ -Fix double underscores erroneously rendering as bold in pyreverse's Mermaid output. - -Closes #10402 diff --git a/doc/whatsnew/fragments/10426.false_positive b/doc/whatsnew/fragments/10426.false_positive deleted file mode 100644 index caa29449ee..0000000000 --- a/doc/whatsnew/fragments/10426.false_positive +++ /dev/null @@ -1,3 +0,0 @@ -Fix a false positive for ``unused-variable`` when multiple except handlers bind the same name under a try block. - -Closes #10426 diff --git a/doc/whatsnew/fragments/10482.false_negative b/doc/whatsnew/fragments/10482.false_negative deleted file mode 100644 index f689436b8b..0000000000 --- a/doc/whatsnew/fragments/10482.false_negative +++ /dev/null @@ -1,3 +0,0 @@ -Fix false-negative for ``used-before-assignment`` with ``from __future__ import annotations`` in function definitions. - -Refs #10482 diff --git a/doc/whatsnew/fragments/9668.false_positive b/doc/whatsnew/fragments/9668.false_positive deleted file mode 100644 index 9668bd217e..0000000000 --- a/doc/whatsnew/fragments/9668.false_positive +++ /dev/null @@ -1,4 +0,0 @@ -Fix false positives for `possibly-used-before-assignment` when variables are exhaustively -assigned within a `match` block. - -Closes #9668 diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index 6d9fccb5bd..31e091bfa7 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -9,7 +9,7 @@ from __future__ import annotations -__version__ = "3.3.7" +__version__ = "3.3.8" def get_numversion_from_version(v: str) -> tuple[int, int, int]: diff --git a/script/.contributors_aliases.json b/script/.contributors_aliases.json index 651aa4c4e6..cf34d00029 100644 --- a/script/.contributors_aliases.json +++ b/script/.contributors_aliases.json @@ -273,6 +273,7 @@ "mails": [ "66853113+pre-commit-ci[bot]@users.noreply.github.com", "49699333+dependabot[bot]@users.noreply.github.com", + "212256041+pylint-backport-bot[bot]@users.noreply.github.com", "41898282+github-actions[bot]@users.noreply.github.com" ], "name": "bot" diff --git a/tbump.toml b/tbump.toml index 5da7f7ef37..37dd0590cb 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,7 +1,7 @@ github_url = "https://github.com/pylint-dev/pylint" [version] -current = "3.3.7" +current = "3.3.8" regex = ''' ^(?P0|[1-9]\d*) \. diff --git a/towncrier.toml b/towncrier.toml index a497b544e9..b2cd891386 100644 --- a/towncrier.toml +++ b/towncrier.toml @@ -1,5 +1,5 @@ [tool.towncrier] -version = "3.3.7" +version = "3.3.8" directory = "doc/whatsnew/fragments" filename = "doc/whatsnew/3/3.3/index.rst" template = "doc/whatsnew/fragments/_template.rst"