-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
unittest.TestResult fails with exception deriving from frozen dataclass #117211
Comments
Since there's no reproducer right now, I am not fully convinced that this is a bug. Exceptions are expected to be mutable. If you drop some parts of an API from the core CPython object, it will raise an expected error. Do you agree? :) |
I managed to reproduce it now, run with from dataclasses import dataclass
import unittest
class TestFrozenDataclassException(unittest.IsolatedAsyncioTestCase):
async def test_frozen_dataclass_exception(self):
@dataclass(frozen=True)
class FrozenException(Exception):
value: int
try:
raise FrozenException(value=123)
except:
raise ValueError("foo") Which results in:
|
I was trying to find if that was documented anywhere but could not find any definitive documentation, maybe I'm not looking at the right place though. The python3.11+ exception notes feature certainly relies on mutability as well but I could not find anything in the PEP describing what happens if an exception overrides Regardless, I would expect a test framework to handle that in a way that only fails the offending test and not the entire test run. |
Well, if something is not documented as immutable, it is mutable by default :) However, I think that this part:
makes sense. Probably error reporting here can be better (for all cases, not just this one). |
Same in main, but with much nicer colored output :)
|
I'm going to close this as a duplicate of #99856, as it's the same underlying issue that's being discussed (many places in the stdlib assume that exceptions are mutable; using |
This small patch avoids this problem, though of course I'm not sure of any other implications here:
thomas@thomas-xps13:~/Projects/cpython$ git diff
diff --git a/Lib/unittest/result.py b/Lib/unittest/result.py
index 3ace0a5..da32563 100644
--- a/Lib/unittest/result.py
+++ b/Lib/unittest/result.py
@@ -223,7 +223,11 @@ def _clean_tracebacks(self, exctype, value, tb, test):
ret = tb
first = False
else:
- value.__traceback__ = tb
+ try:
+ value.__traceback__ = tb
+ except AttributeError:
+ # Attribute is not settable in exception class
+ pass
if value is not None:
for c in (value.__cause__, value.__context__): |
I'm not sure this is exactly the same as the other case ticket since we are speaking about a test runner here. Would it make sense to add a note either in dataclasses or Exception documentation to explicitly mention that Exception subclasses are expected to be mutable and should thus not be frozen (from the dataclass perspective). |
The context in which the issue is arising is different, but it's the same underlying issue: many places in the stdlib and third-party libraries (including the test runners provided by the stdlib) depend on being able to mutate attributes of exception instances; you'll break a lot of assumptions in a lot of places if you try to create a frozen exception class.
I definitely agree that we need to document this better! Let's discuss on the other issue where it would be best to add that documentation, though 👍 |
Bug report
Bug description:
The unittest.TestResult class assumes that it can assign to the
__traceback__
attribute of the exception.This will not work with an exception that is also a frozen dataclass.
I have not been able to create a simple reproducer for this but as far as I can see the offending code is still in main.
See https://github.com/python/cpython/blob/v3.13.0a5/Lib/unittest/result.py#L226
For example:
Here is the traceback using python3.10.12 in an ubuntu based docker container.
CPython versions tested on:
3.10
Operating systems tested on:
Linux
The text was updated successfully, but these errors were encountered: