diff --git a/.github/workflows/primer-test.yaml b/.github/workflows/primer-test.yaml index 59e9a34305..5ad0ad1a67 100644 --- a/.github/workflows/primer-test.yaml +++ b/.github/workflows/primer-test.yaml @@ -30,7 +30,7 @@ jobs: timeout-minutes: 5 strategy: matrix: - python-version: [3.8, 3.9, "3.10", "3.11"] + python-version: [3.8, 3.9, "3.10", "3.11", "3.12-dev"] outputs: python-key: ${{ steps.generate-python-key.outputs.key }} steps: @@ -72,7 +72,7 @@ jobs: needs: prepare-tests-linux strategy: matrix: - python-version: [3.8, 3.9, "3.10", "3.11"] + python-version: [3.8, 3.9, "3.10", "3.11", "3.12-dev"] steps: - name: Check out code from GitHub uses: actions/checkout@v3.5.3 diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 835fc080f4..01cb755cb3 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -31,7 +31,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.8, 3.9, "3.10", "3.11"] + python-version: [3.8, 3.9, "3.10", "3.11", "3.12-dev"] outputs: python-key: ${{ steps.generate-python-key.outputs.key }} steps: @@ -175,7 +175,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.8, 3.9, "3.10", "3.11"] + python-version: [3.8, 3.9, "3.10", "3.11", "3.12-dev"] steps: - name: Set temp directory run: echo "TEMP=$env:USERPROFILE\AppData\Local\Temp" >> $env:GITHUB_ENV diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 6241ecae28..61a9e47dde 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -103,10 +103,10 @@ contributors: - Tushar Sadhwani (tusharsadhwani) - Nicolas Chauvat - orSolocate <38433858+orSolocate@users.noreply.github.com> +- Zen Lee <53538590+zenlyj@users.noreply.github.com> - Radu Ciorba : not-context-manager and confusing-with-statement warnings. - Holger Peters - Cosmin Poieană : unichr-builtin and improvements to bad-open-mode. -- Zen Lee <53538590+zenlyj@users.noreply.github.com> - Steven Myint : duplicate-except. - Peter Kolbus (Garmin) - Luigi Bertaco Cristofolini (luigibertaco) @@ -118,16 +118,17 @@ contributors: - Julien Jehannet - Boris Feld - Anthony Sottile +- Robert Hofer - Pedro Algarvio (s0undt3ch) - Julien Palard - David Liu (david-yz-liu) - Dan Goldsmith : support for msg-template in HTML reporter. - Buck Evan -- Robert Hofer - Mariatta Wijaya * Added new check `logging-fstring-interpolation` * Documentation typo fixes - Jakub Wilk +- Hugo van Kemenade - Eli Fine (eli88fine): Fixed false positive duplicate code warning for lines with symbols only - Andrew Haigh (nelfin) - Émile Crater @@ -138,10 +139,10 @@ contributors: - Marianna Polatoglou : minor contribution for wildcard import check - Manuel Vázquez Acosta - Luis Escobar (Vauxoo): Add bad-docstring-quotes and docstring-first-line-empty +- Lucas Cimon - Konstantina Saketou <56515303+ksaketou@users.noreply.github.com> - Konstantin - Jim Robertson -- Hugo van Kemenade - Ethan Leba - Enji Cooper - Drum Ogilvie @@ -219,7 +220,6 @@ contributors: * Fixed ignored empty functions by similarities checker with "ignore-signatures" option enabled * Ignore function decorators signatures as well by similarities checker with "ignore-signatures" option enabled * Ignore class methods and nested functions signatures as well by similarities checker with "ignore-signatures" option enabled -- Lucas Cimon - Kylian - Konstantin Manna - Kai Mueller <15907922+kasium@users.noreply.github.com> @@ -363,6 +363,7 @@ contributors: - Viorel Știrbu : intern-builtin warning. - VictorT - Victor Jiajunsu <16359131+jiajunsu@users.noreply.github.com> +- ViRuSTriNiTy - Trevor Bekolay * Added --list-msgs-enabled command - Tomer Chachamu : simplifiable-if-expression @@ -428,6 +429,7 @@ contributors: - Moody - Mitchell Young : minor adjustment to docparams - Mitar +- Mikhail f. Shiryaev - Mike Fiedler (miketheman) - Mike Bryant - Michka Popoff @@ -476,6 +478,7 @@ contributors: * Fixed `toml` dependency issue - Jeremy Fleischman - Jason Owen +- Jason Lau - Jared Garst - Jared Deckard - Janne Rönkkö @@ -553,6 +556,7 @@ contributors: - Arun Persaud - Arthur Lutz - Antonio Ossa +- Antonio - Anthony VEREZ - Anthony Tan - Anthony Foglia (Google): Added simple string slots check. diff --git a/doc/user_guide/checkers/features.rst b/doc/user_guide/checkers/features.rst index 14cb30e76d..930baa7390 100644 --- a/doc/user_guide/checkers/features.rst +++ b/doc/user_guide/checkers/features.rst @@ -108,6 +108,9 @@ Basic checker Messages Used when a break or a return statement is found inside the finally clause of a try...finally block: the exceptions raised in the try clause will be silently swallowed instead of being re-raised. +:return-in-finally (W0134): *'return' shadowed by the 'finally' clause.* + Emitted when a 'return' statement is found in a 'finally' block. This will + overwrite the return value of a function and should be avoided. :assert-on-tuple (W0199): *Assert called on a populated tuple. Did you mean 'assert x,y'?* A call of assert on a tuple will always evaluate to true if the tuple is not empty, and will always evaluate to false if it is. diff --git a/doc/user_guide/messages/messages_overview.rst b/doc/user_guide/messages/messages_overview.rst index 173fa53514..d67671bca8 100644 --- a/doc/user_guide/messages/messages_overview.rst +++ b/doc/user_guide/messages/messages_overview.rst @@ -309,6 +309,7 @@ All messages in the warning category: warning/redundant-unittest-assert warning/redundant-yields-doc warning/reimported + warning/return-in-finally warning/self-assigning-variable warning/self-cls-assignment warning/shadowed-import diff --git a/doc/whatsnew/fragments/3696.breaking b/doc/whatsnew/fragments/3696.breaking index ad93e45098..f9b78c7fbe 100644 --- a/doc/whatsnew/fragments/3696.breaking +++ b/doc/whatsnew/fragments/3696.breaking @@ -2,7 +2,7 @@ Enabling or disabling individual messages will now take effect even if an ``--enable=all`` or ``disable=all`` follows in the same configuration file (or on the command line). -This means for the following example, ``fixme`` messages will now be emitted:: +This means for the following example, ``fixme`` messages will now be emitted: .. code-block:: diff --git a/doc/whatsnew/fragments/8416.breaking b/doc/whatsnew/fragments/8416.breaking index 7984da1b12..e30562b9cb 100644 --- a/doc/whatsnew/fragments/8416.breaking +++ b/doc/whatsnew/fragments/8416.breaking @@ -1,3 +1,3 @@ -``pyreverse``: Support for the ``.vcg`` output format (Visualaization of Compiler Graphs) has been dropped. +``pyreverse``: Support for the ``.vcg`` output format (Visualization of Compiler Graphs) has been dropped. Closes #8416 diff --git a/doc/whatsnew/fragments/8718.other b/doc/whatsnew/fragments/8718.other new file mode 100644 index 0000000000..798d03676d --- /dev/null +++ b/doc/whatsnew/fragments/8718.other @@ -0,0 +1,3 @@ +Pylint now supports python 3.12. + +Refs #8718 diff --git a/pylint/checkers/base/name_checker/checker.py b/pylint/checkers/base/name_checker/checker.py index 7784e369a6..42a7c6b2e6 100644 --- a/pylint/checkers/base/name_checker/checker.py +++ b/pylint/checkers/base/name_checker/checker.py @@ -603,12 +603,17 @@ def _assigns_typealias(node: nodes.NodeNG | None) -> bool: """Check if a node is assigning a TypeAlias.""" inferred = utils.safe_infer(node) if isinstance(inferred, nodes.ClassDef): - if inferred.qname() == ".Union": + qname = inferred.qname() + if qname == "typing.TypeAlias": + return True + if qname == ".Union": # Union is a special case because it can be used as a type alias # or as a type annotation. We only want to check the former. assert node is not None return not isinstance(node.parent, nodes.AnnAssign) elif isinstance(inferred, nodes.FunctionDef): + # TODO: when py3.12 is minimum, remove this condition + # TypeAlias became a class in python 3.12 if inferred.qname() == "typing.TypeAlias": return True return False diff --git a/pylint/checkers/classes/class_checker.py b/pylint/checkers/classes/class_checker.py index 308e9704f0..ff93ac07ce 100644 --- a/pylint/checkers/classes/class_checker.py +++ b/pylint/checkers/classes/class_checker.py @@ -1707,7 +1707,8 @@ def _check_in_slots(self, node: nodes.AssignAttr) -> None: # If any ancestor doesn't use slots, the slots # defined for this class are superfluous. if any( - "__slots__" not in ancestor.locals and ancestor.name != "object" + "__slots__" not in ancestor.locals + and ancestor.name not in ("Generic", "object") for ancestor in klass.ancestors() ): return diff --git a/pylint/constants.py b/pylint/constants.py index 5bf4abf5c8..fd649f07f6 100644 --- a/pylint/constants.py +++ b/pylint/constants.py @@ -17,6 +17,7 @@ PY38_PLUS = sys.version_info[:2] >= (3, 8) PY39_PLUS = sys.version_info[:2] >= (3, 9) PY310_PLUS = sys.version_info[:2] >= (3, 10) +PY312_PLUS = sys.version_info[:2] >= (3, 12) IS_PYPY = platform.python_implementation() == "PyPy" diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py index 324c54b1bc..cecf4f195c 100644 --- a/pylint/lint/pylinter.py +++ b/pylint/lint/pylinter.py @@ -1043,7 +1043,13 @@ def _check_astroid_module( try: tokens = utils.tokenize_module(node) except tokenize.TokenError as ex: - self.add_message("syntax-error", line=ex.args[1][0], args=ex.args[0]) + self.add_message( + "syntax-error", + line=ex.args[1][0], + col_offset=ex.args[1][1], + args=ex.args[0], + confidence=HIGH, + ) return None if not node.pure_python: diff --git a/pyproject.toml b/pyproject.toml index 3926a6de41..f81a7933ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools~=62.6", "wheel~=0.37.1"] +requires = ["setuptools~=66.1", "wheel~=0.37.1"] build-backend = "setuptools.build_meta" [project] @@ -24,6 +24,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Debuggers", @@ -35,11 +36,12 @@ requires-python = ">=3.8.0" dependencies = [ "dill>=0.2;python_version<'3.11'", "dill>=0.3.6;python_version>='3.11'", + "dill @ git+https://github.com/uqfoundation/dill.git@83ab36ce3e8cfcc0224aa99d5249a5e8f1c22590 ; python_version>='3.12'", "platformdirs>=2.2.0", # Also upgrade requirements_test_min.txt. # Pinned to dev of second minor update to allow editable installs and fix primer issues, # see https://github.com/pylint-dev/astroid/issues/1341 - "astroid>=3.0.0a7,<=3.1.0-dev0", + "astroid>=3.0.0a8,<=3.1.0-dev0", "isort>=4.2.5,<6", "mccabe>=0.6,<0.8", "tomli>=1.1.0;python_version<'3.11'", @@ -74,6 +76,13 @@ license-files = ["LICENSE", "CONTRIBUTORS.txt"] # Keep in sync with setup.cfg [tool.setuptools.packages.find] include = ["pylint*"] +[tool.setuptools.package-dir] +# Simulate editable_mode=compat, described at: +# https://github.com/pypa/setuptools/issues/3767 +# TODO: remove after solving root cause described at: +# https://github.com/pylint-dev/pylint/issues/8854 +"" = "." + [tool.setuptools.package-data] pylint = ["testutils/testing_pylintrc", "py.typed"] diff --git a/requirements_test_min.txt b/requirements_test_min.txt index 16b6eb9299..7df78810f2 100644 --- a/requirements_test_min.txt +++ b/requirements_test_min.txt @@ -1,6 +1,6 @@ -e .[testutils,spelling] # astroid dependency is also defined in pyproject.toml -astroid==3.0.0a7 # Pinned to a specific version for tests +astroid==3.0.0a8 # Pinned to a specific version for tests typing-extensions~=4.7 py~=1.11.0 pytest~=7.4 diff --git a/tests/functional/d/deprecated/deprecated_module_py310.rc b/tests/functional/d/deprecated/deprecated_module_py310.rc index b29fd450ac..f65e3bc7db 100644 --- a/tests/functional/d/deprecated/deprecated_module_py310.rc +++ b/tests/functional/d/deprecated/deprecated_module_py310.rc @@ -1,2 +1,3 @@ [testoptions] min_pyver = 3.10 +max_pyver = 3.12 diff --git a/tests/functional/d/deprecated/deprecated_module_redundant.py b/tests/functional/d/deprecated/deprecated_module_redundant.py index d9a509d257..05ef610cd5 100644 --- a/tests/functional/d/deprecated/deprecated_module_redundant.py +++ b/tests/functional/d/deprecated/deprecated_module_redundant.py @@ -1,3 +1,3 @@ """This deprecated stdlib module is redundantly given by the user in the config.""" # pylint: disable-next=unused-import -import imp # [deprecated-module] +import optparse # [deprecated-module] diff --git a/tests/functional/d/deprecated/deprecated_module_redundant.rc b/tests/functional/d/deprecated/deprecated_module_redundant.rc index 7336e2f3cd..a51ce45d75 100644 --- a/tests/functional/d/deprecated/deprecated_module_redundant.rc +++ b/tests/functional/d/deprecated/deprecated_module_redundant.rc @@ -1,2 +1,2 @@ [Imports] -deprecated-modules=imp +deprecated-modules=optparse diff --git a/tests/functional/d/deprecated/deprecated_module_redundant.txt b/tests/functional/d/deprecated/deprecated_module_redundant.txt index 456bee8d06..4adb19a128 100644 --- a/tests/functional/d/deprecated/deprecated_module_redundant.txt +++ b/tests/functional/d/deprecated/deprecated_module_redundant.txt @@ -1 +1 @@ -deprecated-module:3:0:3:10::Deprecated module 'imp':UNDEFINED +deprecated-module:3:0:3:15::Deprecated module 'optparse':UNDEFINED diff --git a/tests/functional/e/enum_self_defined_member_5138.rc b/tests/functional/e/enum_self_defined_member_5138.rc new file mode 100644 index 0000000000..6c81976516 --- /dev/null +++ b/tests/functional/e/enum_self_defined_member_5138.rc @@ -0,0 +1,3 @@ +[testoptions] +# https://github.com/python/cpython/issues/106762 +max_pyver=3.12 diff --git a/tests/functional/r/regression_02/regression_distutil_import_error_73.rc b/tests/functional/r/regression_02/regression_distutil_import_error_73.rc new file mode 100644 index 0000000000..983ce054b0 --- /dev/null +++ b/tests/functional/r/regression_02/regression_distutil_import_error_73.rc @@ -0,0 +1,2 @@ +[testoptions] +max_pyver=3.12 diff --git a/tests/functional/t/tokenize_error.py b/tests/functional/t/tokenize_error.py index 323950d913..1ba3ef3a48 100644 --- a/tests/functional/t/tokenize_error.py +++ b/tests/functional/t/tokenize_error.py @@ -1,6 +1,4 @@ -"""A module that is accepted by Python but rejected by tokenize. - -The problem is the trailing line continuation at the end of the line, +"""The problem is the trailing line continuation at the end of the line, which produces a TokenError.""" # +2: [syntax-error] ""\ diff --git a/tests/functional/t/tokenize_error.rc b/tests/functional/t/tokenize_error.rc index 58b5a94132..983ce054b0 100644 --- a/tests/functional/t/tokenize_error.rc +++ b/tests/functional/t/tokenize_error.rc @@ -1 +1,2 @@ [testoptions] +max_pyver=3.12 diff --git a/tests/functional/t/tokenize_error.txt b/tests/functional/t/tokenize_error.txt index 3943cf080e..37d2cd7efc 100644 --- a/tests/functional/t/tokenize_error.txt +++ b/tests/functional/t/tokenize_error.txt @@ -1 +1 @@ -syntax-error:7:0:None:None::EOF in multi-line statement:UNDEFINED +syntax-error:5:0:None:None::EOF in multi-line statement:HIGH diff --git a/tests/functional/t/tokenize_error_py312.py b/tests/functional/t/tokenize_error_py312.py new file mode 100644 index 0000000000..3f60e50617 --- /dev/null +++ b/tests/functional/t/tokenize_error_py312.py @@ -0,0 +1,4 @@ +"""The problem is the trailing line continuation at the end of the line, +which produces a TokenError.""" +# +1: [syntax-error] +""\ diff --git a/tests/functional/t/tokenize_error_py312.rc b/tests/functional/t/tokenize_error_py312.rc new file mode 100644 index 0000000000..9c966d4bda --- /dev/null +++ b/tests/functional/t/tokenize_error_py312.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.12 diff --git a/tests/functional/t/tokenize_error_py312.txt b/tests/functional/t/tokenize_error_py312.txt new file mode 100644 index 0000000000..476f1e8914 --- /dev/null +++ b/tests/functional/t/tokenize_error_py312.txt @@ -0,0 +1 @@ +syntax-error:4:4:None:None::unexpected EOF in multi-line statement:HIGH diff --git a/tests/functional/u/unhashable_member.py b/tests/functional/u/unhashable_member.py index c788668c47..1d805971b5 100644 --- a/tests/functional/u/unhashable_member.py +++ b/tests/functional/u/unhashable_member.py @@ -8,7 +8,6 @@ class Unhashable: {}[[1, 2, 3]] # [unhashable-member] {}[{}] # [unhashable-member] {}[Unhashable()] # [unhashable-member] -{}[1:2] # [unhashable-member] {'foo': 'bar'}['foo'] {'foo': 'bar'}[42] diff --git a/tests/functional/u/unhashable_member.txt b/tests/functional/u/unhashable_member.txt index cbde3f8d67..e7be4cc261 100644 --- a/tests/functional/u/unhashable_member.txt +++ b/tests/functional/u/unhashable_member.txt @@ -1,8 +1,7 @@ unhashable-member:8:0:8:2::'[1, 2, 3]' is unhashable and can't be used as a key in a dict:INFERENCE unhashable-member:9:0:9:2::'{}' is unhashable and can't be used as a key in a dict:INFERENCE unhashable-member:10:0:10:2::'Unhashable()' is unhashable and can't be used as a key in a dict:INFERENCE -unhashable-member:11:0:11:2::"'1:2' is unhashable and can't be used as a key in a dict":INFERENCE -unhashable-member:16:1:16:10::'[1, 2, 3]' is unhashable and can't be used as a key in a dict:INFERENCE -unhashable-member:18:4:18:13::'[1, 2, 3]' is unhashable and can't be used as a key in a dict:INFERENCE -unhashable-member:19:4:19:13::'[4, 5, 6]' is unhashable and can't be used as a key in a dict:INFERENCE -unhashable-member:21:1:21:10::'[1, 2, 3]' is unhashable and can't be used as a member in a set:INFERENCE +unhashable-member:15:1:15:10::'[1, 2, 3]' is unhashable and can't be used as a key in a dict:INFERENCE +unhashable-member:17:4:17:13::'[1, 2, 3]' is unhashable and can't be used as a key in a dict:INFERENCE +unhashable-member:18:4:18:13::'[4, 5, 6]' is unhashable and can't be used as a key in a dict:INFERENCE +unhashable-member:20:1:20:10::'[1, 2, 3]' is unhashable and can't be used as a member in a set:INFERENCE diff --git a/tests/functional/u/unhashable_member_py312.py b/tests/functional/u/unhashable_member_py312.py new file mode 100644 index 0000000000..6badc35927 --- /dev/null +++ b/tests/functional/u/unhashable_member_py312.py @@ -0,0 +1,2 @@ +"""slices can be used as dict keys from python 3.12""" +var = {}[1:2] diff --git a/tests/functional/u/unhashable_member_py312.rc b/tests/functional/u/unhashable_member_py312.rc new file mode 100644 index 0000000000..e9ecd19d69 --- /dev/null +++ b/tests/functional/u/unhashable_member_py312.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver = 3.12 diff --git a/tests/test_functional.py b/tests/test_functional.py index 13bb060b7c..ef0a373def 100644 --- a/tests/test_functional.py +++ b/tests/test_functional.py @@ -13,6 +13,7 @@ from _pytest.config import Config from pylint import testutils +from pylint.constants import PY312_PLUS from pylint.testutils import UPDATE_FILE, UPDATE_OPTION from pylint.testutils.functional import ( FunctionalTestFile, @@ -50,7 +51,8 @@ def test_functional(test_file: FunctionalTestFile, pytestconfig: Config) -> None lint_test.setUp() if test_file.base in TEST_WITH_EXPECTED_DEPRECATION: - with pytest.warns(DeprecationWarning, match="invalid escape sequence"): + exception_type = SyntaxWarning if PY312_PLUS else DeprecationWarning + with pytest.warns(exception_type, match="invalid escape sequence"): lint_test.runTest() else: lint_test.runTest()