Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pylint for openSUSE:Factory checked in at 2025-09-15 19:52:06 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pylint (Old) and /work/SRC/openSUSE:Factory/.python-pylint.new.1977 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pylint" Mon Sep 15 19:52:06 2025 rev:50 rq:1304654 version:3.3.8 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pylint/python-pylint.changes 2025-09-01 17:19:15.465652499 +0200 +++ /work/SRC/openSUSE:Factory/.python-pylint.new.1977/python-pylint.changes 2025-09-15 19:55:57.562793702 +0200 @@ -1,0 +2,19 @@ +Sun Sep 14 18:17:44 UTC 2025 - Dirk Müller <[email protected]> + +- update to 3.3.8: + * Fix false positives for `possibly-used-before-assignment` + when variables are exhaustively assigned within a `match` block. + * Fix false positive for `missing-raises-doc` and `missing- + yield-doc` when the method length is less than docstring-min- + length. + * Fix a false positive for ``unused-variable`` when multiple + except handlers bind the same name under a try block. + * Fix false-negative for ``used-before-assignment`` with ``from + __future__ import annotations`` in function definitions. + * 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.). + * Fix double underscores erroneously rendering as bold in + pyreverse's Mermaid output. + +------------------------------------------------------------------- Old: ---- pylint-3.3.7-gh.tar.gz New: ---- pylint-3.3.8-gh.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pylint.spec ++++++ --- /var/tmp/diff_new_pack.fJqT7t/_old 2025-09-15 19:55:58.038813689 +0200 +++ /var/tmp/diff_new_pack.fJqT7t/_new 2025-09-15 19:55:58.038813689 +0200 @@ -24,7 +24,7 @@ %endif %{?sle15_python_module_pythons} Name: python-pylint -Version: 3.3.7 +Version: 3.3.8 Release: 0 Summary: Syntax and style checker for Python code License: GPL-2.0-or-later ++++++ pylint-3.3.7-gh.tar.gz -> pylint-3.3.8-gh.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/.github/workflows/backport.yml new/pylint-3.3.8/.github/workflows/backport.yml --- old/pylint-3.3.7/.github/workflows/backport.yml 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/.github/workflows/backport.yml 2025-08-09 11:08:50.000000000 +0200 @@ -6,14 +6,14 @@ - 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: > @@ -25,6 +25,14 @@ ) ) 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.GITHUB_TOKEN }} + github_token: ${{ steps.app-token.outputs.token }} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/.github/workflows/checks.yaml new/pylint-3.3.8/.github/workflows/checks.yaml --- old/pylint-3.3.7/.github/workflows/checks.yaml 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/.github/workflows/checks.yaml 2025-08-09 11:08:50.000000000 +0200 @@ -119,7 +119,7 @@ - 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 @@ - name: Run spelling checks run: | . venv/bin/activate + pip install . --no-deps pytest tests/ -k unittest_spelling documentation: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/.github/workflows/primer-test.yaml new/pylint-3.3.8/.github/workflows/primer-test.yaml --- old/pylint-3.3.7/.github/workflows/primer-test.yaml 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/.github/workflows/primer-test.yaml 2025-08-09 11:08:50.000000000 +0200 @@ -94,5 +94,5 @@ - name: Run pytest run: | . venv/bin/activate - pip install . + pip install . --no-deps pytest -m primer_stdlib --primer-stdlib -n auto -vv diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/.github/workflows/primer_run_main.yaml new/pylint-3.3.8/.github/workflows/primer_run_main.yaml --- old/pylint-3.3.7/.github/workflows/primer_run_main.yaml 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/.github/workflows/primer_run_main.yaml 2025-08-09 11:08:50.000000000 +0200 @@ -113,7 +113,7 @@ - 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/.github/workflows/primer_run_pr.yaml new/pylint-3.3.8/.github/workflows/primer_run_pr.yaml --- old/pylint-3.3.7/.github/workflows/primer_run_pr.yaml 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/.github/workflows/primer_run_pr.yaml 2025-08-09 11:08:50.000000000 +0200 @@ -187,7 +187,7 @@ - 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/.github/workflows/tests.yaml new/pylint-3.3.8/.github/workflows/tests.yaml --- old/pylint-3.3.7/.github/workflows/tests.yaml 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/.github/workflows/tests.yaml 2025-08-09 11:08:50.000000000 +0200 @@ -68,6 +68,7 @@ - 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 @@ 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 @@ - name: Run pytest run: | . venv\\Scripts\\activate + pip install . --no-deps pip list | grep 'astroid\|pylint' python -m pytest --durations=10 --benchmark-disable tests/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/doc/whatsnew/3/3.3/index.rst new/pylint-3.3.8/doc/whatsnew/3/3.3/index.rst --- old/pylint-3.3.7/doc/whatsnew/3/3.3/index.rst 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/doc/whatsnew/3/3.3/index.rst 2025-08-09 11:08:50.000000000 +0200 @@ -14,6 +14,53 @@ .. 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 <https://github.com/pylint-dev/pylint/issues/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 <https://github.com/pylint-dev/pylint/issues/10104>`_) + +- Fix a false positive for ``unused-variable`` when multiple except handlers bind the same name under a try block. + + Closes #10426 (`#10426 <https://github.com/pylint-dev/pylint/issues/10426>`_) + + + +False Negatives Fixed +--------------------- + +- Fix false-negative for ``used-before-assignment`` with ``from __future__ import annotations`` in function definitions. + + Refs #10482 (`#10482 <https://github.com/pylint-dev/pylint/issues/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 <https://github.com/pylint-dev/pylint/issues/10373>`_) + +- Fix double underscores erroneously rendering as bold in pyreverse's Mermaid output. + + Closes #10402 (`#10402 <https://github.com/pylint-dev/pylint/issues/10402>`_) + + + What's new in Pylint 3.3.7? --------------------------- Release date: 2025-05-04 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/pylint/__pkginfo__.py new/pylint-3.3.8/pylint/__pkginfo__.py --- old/pylint-3.3.7/pylint/__pkginfo__.py 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/pylint/__pkginfo__.py 2025-08-09 11:08:50.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/pylint/checkers/variables.py new/pylint-3.3.8/pylint/checkers/variables.py --- old/pylint-3.3.7/pylint/checkers/variables.py 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/pylint/checkers/variables.py 2025-08-09 11:08:50.000000000 +0200 @@ -720,6 +720,12 @@ 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 @@ 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 @@ 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 @@ -1937,7 +1949,17 @@ # 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) @@ -2824,9 +2846,7 @@ 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) @@ -2901,6 +2921,15 @@ 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. @@ -3226,6 +3255,8 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/pylint/extensions/docparams.py new/pylint-3.3.8/pylint/extensions/docparams.py --- old/pylint-3.3.7/pylint/extensions/docparams.py 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/pylint/extensions/docparams.py 2025-08-09 11:08:50.000000000 +0200 @@ -209,9 +209,7 @@ 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 @@ 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 @@ 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 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/pylint/pyreverse/diagrams.py new/pylint-3.3.8/pylint/pyreverse/diagrams.py --- old/pylint-3.3.7/pylint/pyreverse/diagrams.py 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/pylint/pyreverse/diagrams.py 2025-08-09 11:08:50.000000000 +0200 @@ -221,6 +221,9 @@ # 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 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/pylint/pyreverse/mermaidjs_printer.py new/pylint-3.3.8/pylint/pyreverse/mermaidjs_printer.py --- old/pylint-3.3.7/pylint/pyreverse/mermaidjs_printer.py 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/pylint/pyreverse/mermaidjs_printer.py 2025-08-09 11:08:50.000000000 +0200 @@ -32,6 +32,11 @@ 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 @@ 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 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/requirements_test_min.txt new/pylint-3.3.8/requirements_test_min.txt --- old/pylint-3.3.7/requirements_test_min.txt 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/requirements_test_min.txt 2025-08-09 11:08:50.000000000 +0200 @@ -4,7 +4,7 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/script/.contributors_aliases.json new/pylint-3.3.8/script/.contributors_aliases.json --- old/pylint-3.3.7/script/.contributors_aliases.json 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/script/.contributors_aliases.json 2025-08-09 11:08:50.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tbump.toml new/pylint-3.3.8/tbump.toml --- old/pylint-3.3.7/tbump.toml 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/tbump.toml 2025-08-09 11:08:50.000000000 +0200 @@ -1,7 +1,7 @@ github_url = "https://github.com/pylint-dev/pylint" [version] -current = "3.3.7" +current = "3.3.8" regex = ''' ^(?P<major>0|[1-9]\d*) \. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.py new/pylint-3.3.8/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.py --- old/pylint-3.3.7/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.py 2025-08-09 11:08:50.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.rc new/pylint-3.3.8/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.rc --- old/pylint-3.3.7/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.rc 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/functional/ext/docparams/raise/missing_raises_doc_required_min_length.rc 2025-08-09 11:08:50.000000000 +0200 @@ -0,0 +1,7 @@ +[MAIN] +load-plugins = pylint.extensions.docparams + +[BASIC] +accept-no-raise-doc=no +docstring-min-length=3 +no-docstring-rgx=^$ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.py new/pylint-3.3.8/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.py --- old/pylint-3.3.7/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.py 2025-08-09 11:08:50.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.rc new/pylint-3.3.8/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.rc --- old/pylint-3.3.7/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.rc 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/functional/ext/docparams/yield/missing_yield_doc_required_min_length.rc 2025-08-09 11:08:50.000000000 +0200 @@ -0,0 +1,7 @@ +[MAIN] +load-plugins = pylint.extensions.docparams + +[BASIC] +accept-no-yields-doc=no +docstring-min-length=3 +no-docstring-rgx=^$ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/functional/u/unused/unused_global_variable2.py new/pylint-3.3.8/tests/functional/u/unused/unused_global_variable2.py --- old/pylint-3.3.7/tests/functional/u/unused/unused_global_variable2.py 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/tests/functional/u/unused/unused_global_variable2.py 2025-08-09 11:08:50.000000000 +0200 @@ -9,3 +9,11 @@ VAR = 'pylint' # [unused-variable] + + +try: + pass +except TypeError as exc: + print(exc) +except ValueError as exc: + print(exc) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/functional/u/unused/unused_variable.py new/pylint-3.3.8/tests/functional/u/unused/unused_variable.py --- old/pylint-3.3.7/tests/functional/u/unused/unused_variable.py 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/tests/functional/u/unused/unused_variable.py 2025-08-09 11:08:50.000000000 +0200 @@ -187,6 +187,17 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/functional/u/unused/unused_variable.txt new/pylint-3.3.8/tests/functional/u/unused/unused_variable.txt --- old/pylint-3.3.7/tests/functional/u/unused/unused_variable.txt 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/tests/functional/u/unused/unused_variable.txt 2025-08-09 11:08:50.000000000 +0200 @@ -26,4 +26,4 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/functional/u/used/used_before_assignment_py310.py new/pylint-3.3.8/tests/functional/u/used/used_before_assignment_py310.py --- old/pylint-3.3.7/tests/functional/u/used/used_before_assignment_py310.py 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/tests/functional/u/used/used_before_assignment_py310.py 2025-08-09 11:08:50.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/functional/u/used/used_before_assignment_py310.txt new/pylint-3.3.8/tests/functional/u/used/used_before_assignment_py310.txt --- old/pylint-3.3.7/tests/functional/u/used/used_before_assignment_py310.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/functional/u/used/used_before_assignment_py310.txt 2025-08-09 11:08:50.000000000 +0200 @@ -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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd --- old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.mmd 2025-08-09 11:08:50.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.py new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.py --- old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.py 2025-08-09 11:08:50.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.rc new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.rc --- old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.rc 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/all.rc 2025-08-09 11:08:50.000000000 +0200 @@ -0,0 +1,2 @@ +[testoptions] +command_line_args=--filter-mode=ALL diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd --- old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.mmd 2025-08-09 11:08:50.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.py new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.py --- old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.py 2025-08-09 11:08:50.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.rc new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.rc --- old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.rc 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/other.rc 2025-08-09 11:08:50.000000000 +0200 @@ -0,0 +1,2 @@ +[testoptions] +command_line_args=--filter-mode=OTHER diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.mmd new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.mmd --- old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.mmd 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.mmd 2025-08-09 11:08:50.000000000 +0200 @@ -0,0 +1,14 @@ +classDiagram + class P { + name : str + } + class PrivateAttr { + } + class ProtectedAttr { + } + class PublicAttr { + x + } + class SpecialAttr { + } + P --* PublicAttr : x diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.py new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.py --- old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.py 2025-08-09 11:08:50.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.rc new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.rc --- old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.rc 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/pub_only.rc 2025-08-09 11:08:50.000000000 +0200 @@ -0,0 +1,2 @@ +[testoptions] +command_line_args=--filter-mode=PUB_ONLY diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd --- old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.mmd 2025-08-09 11:08:50.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.py new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.py --- old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.py 2025-08-09 11:08:50.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.rc new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.rc --- old/pylint-3.3.7/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.rc 1970-01-01 01:00:00.000000000 +0100 +++ new/pylint-3.3.8/tests/pyreverse/functional/class_diagrams/aggregation_filtering/special.rc 2025-08-09 11:08:50.000000000 +0200 @@ -0,0 +1,2 @@ +[testoptions] +command_line_args=--filter-mode=SPECIAL diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylint-3.3.7/towncrier.toml new/pylint-3.3.8/towncrier.toml --- old/pylint-3.3.7/towncrier.toml 2025-05-04 17:56:48.000000000 +0200 +++ new/pylint-3.3.8/towncrier.toml 2025-08-09 11:08:50.000000000 +0200 @@ -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"
