diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index d3c9e01bbb..3d27ff0c1c 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -11,9 +11,9 @@ on: - "maintenance/**" env: - CACHE_VERSION: 1 + CACHE_VERSION: 2 KEY_PREFIX: base-venv - DEFAULT_PYTHON: "3.11" + DEFAULT_PYTHON: "3.12" PRE_COMMIT_CACHE: ~/.cache/pre-commit concurrency: diff --git a/doc/data/messages/a/anomalous-backslash-in-string/bad.py b/doc/data/messages/a/anomalous-backslash-in-string/bad.py index 08d8d1d6f4..32da7ddcc0 100644 --- a/doc/data/messages/a/anomalous-backslash-in-string/bad.py +++ b/doc/data/messages/a/anomalous-backslash-in-string/bad.py @@ -1 +1 @@ -string = "\z" # [anomalous-backslash-in-string] +string = "\z" # [syntax-error] diff --git a/doc/data/messages/a/anomalous-backslash-in-string/details.rst b/doc/data/messages/a/anomalous-backslash-in-string/details.rst index e716bc2d9c..7f73b513e9 100644 --- a/doc/data/messages/a/anomalous-backslash-in-string/details.rst +++ b/doc/data/messages/a/anomalous-backslash-in-string/details.rst @@ -1,2 +1,6 @@ ``\z`` is same as ``\\z`` because there's no escape sequence for ``z``. But it is not clear for the reader of the code. + +The only reason this is demonstrated to raise ``syntax-error`` is because +pylint's CI now runs on Python 3.12, where this truly raises a ``SyntaxError``. +We hope to address this discrepancy in the documentation in the future. diff --git a/doc/data/messages/a/anomalous-unicode-escape-in-string/bad.py b/doc/data/messages/a/anomalous-unicode-escape-in-string/bad.py index 40275f0551..21d25eadf0 100644 --- a/doc/data/messages/a/anomalous-unicode-escape-in-string/bad.py +++ b/doc/data/messages/a/anomalous-unicode-escape-in-string/bad.py @@ -1 +1 @@ -print(b"\u%b" % b"0394") # [anomalous-unicode-escape-in-string] +print(b"\u%b" % b"0394") # [syntax-error] diff --git a/doc/data/messages/u/using-exception-groups-in-unsupported-version/bad.py b/doc/data/messages/u/using-exception-groups-in-unsupported-version/bad.py new file mode 100644 index 0000000000..c225e6e6a8 --- /dev/null +++ b/doc/data/messages/u/using-exception-groups-in-unsupported-version/bad.py @@ -0,0 +1,12 @@ +def f(): + excs = [OSError("error 1"), SystemError("error 2")] + # +1: [using-exception-groups-in-unsupported-version] + raise ExceptionGroup("there were problems", excs) + + +try: # [using-exception-groups-in-unsupported-version] + f() +except* OSError as e: + print("There were OSErrors") +except* SystemError as e: + print("There were SystemErrors") diff --git a/doc/data/messages/u/using-exception-groups-in-unsupported-version/details.rst b/doc/data/messages/u/using-exception-groups-in-unsupported-version/details.rst new file mode 100644 index 0000000000..8d05a037a1 --- /dev/null +++ b/doc/data/messages/u/using-exception-groups-in-unsupported-version/details.rst @@ -0,0 +1 @@ +Exception groups were introduced in Python 3.11; to use it, please use a more recent version of Python. diff --git a/doc/data/messages/u/using-exception-groups-in-unsupported-version/good.py b/doc/data/messages/u/using-exception-groups-in-unsupported-version/good.py new file mode 100644 index 0000000000..e7ac7a5b7a --- /dev/null +++ b/doc/data/messages/u/using-exception-groups-in-unsupported-version/good.py @@ -0,0 +1,10 @@ +def f(): + raise OSError("error 1") + + +try: + f() +except OSError as e: + print("There were OSErrors") +except SystemError as e: + print("There were SystemErrors") diff --git a/doc/data/messages/u/using-exception-groups-in-unsupported-version/pylintrc b/doc/data/messages/u/using-exception-groups-in-unsupported-version/pylintrc new file mode 100644 index 0000000000..d36622d880 --- /dev/null +++ b/doc/data/messages/u/using-exception-groups-in-unsupported-version/pylintrc @@ -0,0 +1,2 @@ +[main] +py-version=3.10 diff --git a/doc/data/messages/u/using-generic-type-syntax-in-unsupported-version/bad.py b/doc/data/messages/u/using-generic-type-syntax-in-unsupported-version/bad.py new file mode 100644 index 0000000000..b47bddd33e --- /dev/null +++ b/doc/data/messages/u/using-generic-type-syntax-in-unsupported-version/bad.py @@ -0,0 +1 @@ +type Vector = list[float] # [using-generic-type-syntax-in-unsupported-version] diff --git a/doc/data/messages/u/using-generic-type-syntax-in-unsupported-version/details.rst b/doc/data/messages/u/using-generic-type-syntax-in-unsupported-version/details.rst new file mode 100644 index 0000000000..731616e853 --- /dev/null +++ b/doc/data/messages/u/using-generic-type-syntax-in-unsupported-version/details.rst @@ -0,0 +1 @@ +Generic type syntax was introduced in Python 3.12; to use it, please use a more recent version of Python. diff --git a/doc/data/messages/u/using-generic-type-syntax-in-unsupported-version/good.py b/doc/data/messages/u/using-generic-type-syntax-in-unsupported-version/good.py new file mode 100644 index 0000000000..3f80b01c52 --- /dev/null +++ b/doc/data/messages/u/using-generic-type-syntax-in-unsupported-version/good.py @@ -0,0 +1,3 @@ +from typing import TypeAlias + +Vector: TypeAlias = list[float] diff --git a/doc/data/messages/u/using-generic-type-syntax-in-unsupported-version/pylintrc b/doc/data/messages/u/using-generic-type-syntax-in-unsupported-version/pylintrc new file mode 100644 index 0000000000..8e00cbfea2 --- /dev/null +++ b/doc/data/messages/u/using-generic-type-syntax-in-unsupported-version/pylintrc @@ -0,0 +1,2 @@ +[main] +py-version=3.11 diff --git a/doc/user_guide/checkers/features.rst b/doc/user_guide/checkers/features.rst index 4dd5494b36..e0be9a44c5 100644 --- a/doc/user_guide/checkers/features.rst +++ b/doc/user_guide/checkers/features.rst @@ -1351,9 +1351,15 @@ Verbatim name of the checker is ``unsupported_version``. Unsupported Version checker Messages ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:using-exception-groups-in-unsupported-version (W2603): *Exception groups are not supported by all versions included in the py-version setting* + Used when the py-version set by the user is lower than 3.11 and pylint + encounters ``except*`` or `ExceptionGroup``. :using-f-string-in-unsupported-version (W2601): *F-strings are not supported by all versions included in the py-version setting* Used when the py-version set by the user is lower than 3.6 and pylint encounters an f-string. +:using-generic-type-syntax-in-unsupported-version (W2604): *Generic type syntax (PEP 695) is not supported by all versions included in the py-version setting* + Used when the py-version set by the user is lower than 3.12 and pylint + encounters generic type syntax. :using-final-decorator-in-unsupported-version (W2602): *typing.final is not supported by all versions included in the py-version setting* Used when the py-version set by the user is lower than 3.8 and pylint encounters a ``typing.final`` decorator. diff --git a/doc/user_guide/messages/messages_overview.rst b/doc/user_guide/messages/messages_overview.rst index 4913ccd29f..0ead1ba37d 100644 --- a/doc/user_guide/messages/messages_overview.rst +++ b/doc/user_guide/messages/messages_overview.rst @@ -349,8 +349,10 @@ All messages in the warning category: warning/useless-type-doc warning/useless-with-lock warning/using-constant-test + warning/using-exception-groups-in-unsupported-version warning/using-f-string-in-unsupported-version warning/using-final-decorator-in-unsupported-version + warning/using-generic-type-syntax-in-unsupported-version warning/while-used warning/wildcard-import warning/wrong-exception-operation diff --git a/doc/whatsnew/fragments/9791.new_check b/doc/whatsnew/fragments/9791.new_check new file mode 100644 index 0000000000..279c364420 --- /dev/null +++ b/doc/whatsnew/fragments/9791.new_check @@ -0,0 +1,5 @@ +Add `using-exception-group-in-unsupported-version` and +`using-generic-type-syntax-in-unsupported-version` for uses of Python 3.11+ or +3.12+ features on lower supported versions provided with `--py-version`. + +Closes #9791 diff --git a/pylint/checkers/unsupported_version.py b/pylint/checkers/unsupported_version.py index 64f2630d8b..23c6525302 100644 --- a/pylint/checkers/unsupported_version.py +++ b/pylint/checkers/unsupported_version.py @@ -42,6 +42,18 @@ class UnsupportedVersionChecker(BaseChecker): "Used when the py-version set by the user is lower than 3.8 and pylint encounters " "a ``typing.final`` decorator.", ), + "W2603": ( + "Exception groups are not supported by all versions included in the py-version setting", + "using-exception-groups-in-unsupported-version", + "Used when the py-version set by the user is lower than 3.11 and pylint encounters " + "``except*`` or `ExceptionGroup``.", + ), + "W2604": ( + "Generic type syntax (PEP 695) is not supported by all versions included in the py-version setting", + "using-generic-type-syntax-in-unsupported-version", + "Used when the py-version set by the user is lower than 3.12 and pylint encounters " + "generic type syntax.", + ), } def open(self) -> None: @@ -49,6 +61,8 @@ def open(self) -> None: py_version = self.linter.config.py_version self._py36_plus = py_version >= (3, 6) self._py38_plus = py_version >= (3, 8) + self._py311_plus = py_version >= (3, 11) + self._py312_plus = py_version >= (3, 12) @only_required_for_messages("using-f-string-in-unsupported-version") def visit_joinedstr(self, node: nodes.JoinedStr) -> None: @@ -79,6 +93,51 @@ def _check_typing_final(self, node: nodes.Decorators) -> None: "using-final-decorator-in-unsupported-version", node=decorator ) + @only_required_for_messages("using-exception-groups-in-unsupported-version") + def visit_trystar(self, node: nodes.TryStar) -> None: + if not self._py311_plus: + self.add_message("using-exception-groups-in-unsupported-version", node=node) + + @only_required_for_messages("using-exception-groups-in-unsupported-version") + def visit_excepthandler(self, node: nodes.ExceptHandler) -> None: + if ( + not self._py311_plus + and isinstance(node.type, nodes.Name) + and node.type.name == "ExceptionGroup" + ): + self.add_message("using-exception-groups-in-unsupported-version", node=node) + + @only_required_for_messages("using-exception-groups-in-unsupported-version") + def visit_raise(self, node: nodes.Raise) -> None: + if ( + not self._py311_plus + and isinstance(node.exc, nodes.Call) + and isinstance(node.exc.func, nodes.Name) + and node.exc.func.name == "ExceptionGroup" + ): + self.add_message("using-exception-groups-in-unsupported-version", node=node) + + @only_required_for_messages("using-generic-type-syntax-in-unsupported-version") + def visit_typealias(self, node: nodes.TypeAlias) -> None: + if not self._py312_plus: + self.add_message( + "using-generic-type-syntax-in-unsupported-version", node=node + ) + + @only_required_for_messages("using-generic-type-syntax-in-unsupported-version") + def visit_typevar(self, node: nodes.TypeVar) -> None: + if not self._py312_plus: + self.add_message( + "using-generic-type-syntax-in-unsupported-version", node=node + ) + + @only_required_for_messages("using-generic-type-syntax-in-unsupported-version") + def visit_typevartuple(self, node: nodes.TypeVarTuple) -> None: + if not self._py312_plus: + self.add_message( + "using-generic-type-syntax-in-unsupported-version", node=node + ) + def register(linter: PyLinter) -> None: linter.register_checker(UnsupportedVersionChecker(linter)) diff --git a/tests/functional/u/unsupported/unsupported_version_for_exception_group.py b/tests/functional/u/unsupported/unsupported_version_for_exception_group.py new file mode 100644 index 0000000000..6327e82f1d --- /dev/null +++ b/tests/functional/u/unsupported/unsupported_version_for_exception_group.py @@ -0,0 +1,21 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring +def f(): + excs = [OSError("error 1"), SystemError("error 2")] + # +1: [using-exception-groups-in-unsupported-version] + raise ExceptionGroup("there were problems", excs) + + +try: # [using-exception-groups-in-unsupported-version] + f() +except* OSError as e: + print("There were OSErrors") +except* SystemError as e: + print("There were SystemErrors") + + +try: + f() +except ExceptionGroup as group: # [using-exception-groups-in-unsupported-version] + # https://github.com/pylint-dev/pylint/issues/8985 + for exc in group.exceptions: # pylint: disable=not-an-iterable + print("ERROR: ", exc) diff --git a/tests/functional/u/unsupported/unsupported_version_for_exception_group.rc b/tests/functional/u/unsupported/unsupported_version_for_exception_group.rc new file mode 100644 index 0000000000..4885accdeb --- /dev/null +++ b/tests/functional/u/unsupported/unsupported_version_for_exception_group.rc @@ -0,0 +1,5 @@ +[main] +py-version=3.10 + +[testoptions] +min_pyver=3.11 diff --git a/tests/functional/u/unsupported/unsupported_version_for_exception_group.txt b/tests/functional/u/unsupported/unsupported_version_for_exception_group.txt new file mode 100644 index 0000000000..0736e67653 --- /dev/null +++ b/tests/functional/u/unsupported/unsupported_version_for_exception_group.txt @@ -0,0 +1,3 @@ +using-exception-groups-in-unsupported-version:5:4:5:53:f:Exception groups are not supported by all versions included in the py-version setting:UNDEFINED +using-exception-groups-in-unsupported-version:8:0:13:36::Exception groups are not supported by all versions included in the py-version setting:UNDEFINED +using-exception-groups-in-unsupported-version:18:0:21:29::Exception groups are not supported by all versions included in the py-version setting:UNDEFINED diff --git a/tests/functional/u/unsupported/unsupported_version_for_generic_type_syntax.py b/tests/functional/u/unsupported/unsupported_version_for_generic_type_syntax.py new file mode 100644 index 0000000000..66d56bd4c7 --- /dev/null +++ b/tests/functional/u/unsupported/unsupported_version_for_generic_type_syntax.py @@ -0,0 +1,5 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring, line-too-long +# +1: [using-generic-type-syntax-in-unsupported-version, using-generic-type-syntax-in-unsupported-version] +type Point[T] = tuple[float, float] +# +1: [using-generic-type-syntax-in-unsupported-version, using-generic-type-syntax-in-unsupported-version] +type Alias[*Ts] = tuple[*Ts] diff --git a/tests/functional/u/unsupported/unsupported_version_for_generic_type_syntax.rc b/tests/functional/u/unsupported/unsupported_version_for_generic_type_syntax.rc new file mode 100644 index 0000000000..884bba53e2 --- /dev/null +++ b/tests/functional/u/unsupported/unsupported_version_for_generic_type_syntax.rc @@ -0,0 +1,5 @@ +[main] +py-version=3.11 + +[testoptions] +min_pyver=3.12 diff --git a/tests/functional/u/unsupported/unsupported_version_for_generic_type_syntax.txt b/tests/functional/u/unsupported/unsupported_version_for_generic_type_syntax.txt new file mode 100644 index 0000000000..9b2eb6af43 --- /dev/null +++ b/tests/functional/u/unsupported/unsupported_version_for_generic_type_syntax.txt @@ -0,0 +1,4 @@ +using-generic-type-syntax-in-unsupported-version:3:0:3:35::Generic type syntax (PEP 695) is not supported by all versions included in the py-version setting:UNDEFINED +using-generic-type-syntax-in-unsupported-version:3:11:3:12::Generic type syntax (PEP 695) is not supported by all versions included in the py-version setting:UNDEFINED +using-generic-type-syntax-in-unsupported-version:5:0:5:28::Generic type syntax (PEP 695) is not supported by all versions included in the py-version setting:UNDEFINED +using-generic-type-syntax-in-unsupported-version:5:11:5:14::Generic type syntax (PEP 695) is not supported by all versions included in the py-version setting:UNDEFINED