From a50b5d8c9baccbe124a95f47a1ca8b507b9875d5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 19:49:17 +0000 Subject: [PATCH 1/4] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.3.0 → v3.3.1](https://github.com/asottile/pyupgrade/compare/v3.3.0...v3.3.1) - [github.com/psf/black: 22.10.0 → 22.12.0](https://github.com/psf/black/compare/22.10.0...22.12.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 038c6c8..14901c8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,13 +21,13 @@ repos: - id: isort - repo: https://github.com/asottile/pyupgrade - rev: v3.3.0 + rev: v3.3.1 hooks: - id: pyupgrade args: ["--py37-plus", "--keep-runtime-typing"] - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 22.12.0 hooks: - id: black exclude: "tests/test_catch_py311.py" From 34bcf19f75a760341e9c43a01aea060f65e7d545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Thu, 22 Dec 2022 11:27:49 +0200 Subject: [PATCH 2/4] Worked around coveralls's allergy of coverage 7 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 77d65b2..cd5401f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,7 +40,7 @@ jobs: path: ~/.cache/pip key: pip-test-${{ matrix.python-version }}-${{ matrix.os }} - name: Install dependencies - run: pip install .[test] coverage[toml] coveralls + run: pip install .[test] "coverage[toml] < 7" coveralls - name: Test with pytest run: coverage run -m pytest - name: Upload Coverage From f32faa2ba465cd00d6047800a53476dd5ddc500b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Thu, 22 Dec 2022 12:08:55 +0200 Subject: [PATCH 3/4] Added a better fix for the coveralls <-> coverage compatibility issue --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cd5401f..311aec4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,7 +40,7 @@ jobs: path: ~/.cache/pip key: pip-test-${{ matrix.python-version }}-${{ matrix.os }} - name: Install dependencies - run: pip install .[test] "coverage[toml] < 7" coveralls + run: pip install .[test] coveralls coverage[toml] - name: Test with pytest run: coverage run -m pytest - name: Upload Coverage From 6e0f33192889e9430bfa14993922b05cdcc69519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Thu, 22 Dec 2022 13:06:52 +0200 Subject: [PATCH 4/4] Backported upstream fix for gh-99553 (#51) Fixes #50. --- CHANGES.rst | 5 +++++ src/exceptiongroup/_exceptions.py | 22 +++++++++++++--------- tests/test_exceptions.py | 30 ++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index c2af7ae..ee46dfd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,11 @@ Version history This library adheres to `Semantic Versioning 2.0 `_. +**UNRELEASED** + +- Backported upstream fix for gh-99553 (custom subclasses of ``BaseExceptionGroup`` that + also inherit from ``Exception`` should not be able to wrap base exceptions) + **1.0.4** - Fixed regression introduced in v1.0.3 where the code computing the suggestions would diff --git a/src/exceptiongroup/_exceptions.py b/src/exceptiongroup/_exceptions.py index ebdd172..a2ca092 100644 --- a/src/exceptiongroup/_exceptions.py +++ b/src/exceptiongroup/_exceptions.py @@ -67,6 +67,18 @@ def __new__( if all(isinstance(exc, Exception) for exc in __exceptions): cls = ExceptionGroup + if issubclass(cls, Exception): + for exc in __exceptions: + if not isinstance(exc, Exception): + if cls is ExceptionGroup: + raise TypeError( + "Cannot nest BaseExceptions in an ExceptionGroup" + ) + else: + raise TypeError( + f"Cannot nest BaseExceptions in {cls.__name__!r}" + ) + return super().__new__(cls, __message, __exceptions) def __init__( @@ -219,15 +231,7 @@ def __repr__(self) -> str: class ExceptionGroup(BaseExceptionGroup[_ExceptionT_co], Exception): def __new__(cls, __message: str, __exceptions: Sequence[_ExceptionT_co]) -> Self: - instance: ExceptionGroup[_ExceptionT_co] = super().__new__( - cls, __message, __exceptions - ) - if cls is ExceptionGroup: - for exc in __exceptions: - if not isinstance(exc, Exception): - raise TypeError("Cannot nest BaseExceptions in an ExceptionGroup") - - return instance + return super().__new__(cls, __message, __exceptions) if TYPE_CHECKING: diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index d0d33cd..9a0e39b 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -3,6 +3,8 @@ import sys import unittest +import pytest + from exceptiongroup import BaseExceptionGroup, ExceptionGroup @@ -90,19 +92,35 @@ def test_BEG_wraps_BaseException__creates_BEG(self): beg = BaseExceptionGroup("beg", [ValueError(1), KeyboardInterrupt(2)]) self.assertIs(type(beg), BaseExceptionGroup) - def test_EG_subclass_wraps_anything(self): + def test_EG_subclass_wraps_non_base_exceptions(self): class MyEG(ExceptionGroup): pass self.assertIs(type(MyEG("eg", [ValueError(12), TypeError(42)])), MyEG) - self.assertIs(type(MyEG("eg", [ValueError(12), KeyboardInterrupt(42)])), MyEG) - def test_BEG_subclass_wraps_anything(self): - class MyBEG(BaseExceptionGroup): + @pytest.mark.skipif( + sys.version_info[:3] == (3, 11, 0), + reason="Behavior was made stricter in 3.11.1", + ) + def test_EG_subclass_does_not_wrap_base_exceptions(self): + class MyEG(ExceptionGroup): + pass + + msg = "Cannot nest BaseExceptions in 'MyEG'" + with self.assertRaisesRegex(TypeError, msg): + MyEG("eg", [ValueError(12), KeyboardInterrupt(42)]) + + @pytest.mark.skipif( + sys.version_info[:3] == (3, 11, 0), + reason="Behavior was made stricter in 3.11.1", + ) + def test_BEG_and_E_subclass_does_not_wrap_base_exceptions(self): + class MyEG(BaseExceptionGroup, ValueError): pass - self.assertIs(type(MyBEG("eg", [ValueError(12), TypeError(42)])), MyBEG) - self.assertIs(type(MyBEG("eg", [ValueError(12), KeyboardInterrupt(42)])), MyBEG) + msg = "Cannot nest BaseExceptions in 'MyEG'" + with self.assertRaisesRegex(TypeError, msg): + MyEG("eg", [ValueError(12), KeyboardInterrupt(42)]) def create_simple_eg():