Comments (9)
I'm not sure this is exactly the same as the other case ticket since we are speaking about a test runner here.
Ideally, errors should be isolates as much as possible to single test cases.
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.
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).
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 π
from cpython.
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? :)
from cpython.
Well, if something is not documented as immutable, it is mutable by default :)
That's how Python is designed.
However, I think that this part:
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.
makes sense. Probably error reporting here can be better (for all cases, not just this one).
from cpython.
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 frozen=True
for Exception
subclasses is generally a bad idea)
from cpython.
I managed to reproduce it now, run with python3 -m unittest test.py
, with test.py
being:
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:
$ python3 --version
Python 3.11.8
$ python3 -m unittest test.py
Traceback (most recent call last):
File "/home/thomas/Projects/themachine/test.py", line 12, in test_frozen_dataclass_exception
raise FrozenException(value=123)
test.TestFrozenDataclassException.test_frozen_dataclass_exception.<locals>.FrozenException
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.11/unittest/case.py", line 57, in testPartExecutor
yield
File "/usr/lib/python3.11/unittest/case.py", line 623, in run
self._callTestMethod(testMethod)
File "/usr/lib/python3.11/unittest/async_case.py", line 90, in _callTestMethod
if self._callMaybeAsync(method) is not None:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/unittest/async_case.py", line 112, in _callMaybeAsync
return self._asyncioRunner.run(
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/asyncio/base_events.py", line 654, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/home/thomas/Projects/themachine/test.py", line 14, in test_frozen_dataclass_exception
raise ValueError("foo")
ValueError: foo
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/usr/lib/python3.11/unittest/__main__.py", line 18, in <module>
main(module=None)
File "/usr/lib/python3.11/unittest/main.py", line 102, in __init__
self.runTests()
File "/usr/lib/python3.11/unittest/main.py", line 274, in runTests
self.result = testRunner.run(self.test)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/unittest/runner.py", line 217, in run
test(result)
File "/usr/lib/python3.11/unittest/suite.py", line 84, in __call__
return self.run(*args, **kwds)
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/unittest/suite.py", line 122, in run
test(result)
File "/usr/lib/python3.11/unittest/suite.py", line 84, in __call__
return self.run(*args, **kwds)
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/unittest/suite.py", line 122, in run
test(result)
File "/usr/lib/python3.11/unittest/suite.py", line 84, in __call__
return self.run(*args, **kwds)
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/unittest/suite.py", line 122, in run
test(result)
File "/usr/lib/python3.11/unittest/case.py", line 678, in __call__
return self.run(*args, **kwds)
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/unittest/async_case.py", line 131, in run
return super().run(result)
^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/unittest/case.py", line 622, in run
with outcome.testPartExecutor(self):
File "/usr/lib/python3.11/contextlib.py", line 158, in __exit__
self.gen.throw(typ, value, traceback)
File "/usr/lib/python3.11/unittest/case.py", line 74, in testPartExecutor
_addError(self.result, test_case, exc_info)
File "/usr/lib/python3.11/unittest/case.py", line 99, in _addError
result.addError(test, exc_info)
File "/usr/lib/python3.11/unittest/runner.py", line 98, in addError
super(TextTestResult, self).addError(test, err)
File "/usr/lib/python3.11/unittest/result.py", line 17, in inner
return method(self, *args, **kw)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/unittest/result.py", line 115, in addError
self.errors.append((test, self._exc_info_to_string(err, test)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/unittest/result.py", line 176, in _exc_info_to_string
tb = self._clean_tracebacks(exctype, value, tb, test)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/unittest/result.py", line 214, in _clean_tracebacks
value.__traceback__ = tb
^^^^^^^^^^^^^^^^^^^
File "<string>", line 4, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field '__traceback__'
from cpython.
Exceptions are expected to be mutable.
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 __setattr__
in way that forbids adding and/or setting an attribute.
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.
It could even point to the root issue, e.g. "Exception subclasses should be mutable".
from cpython.
Same in main, but with much nicer colored output :)
thomas@thomas-xps13:~/Projects/cpython$ ./python --version
Python 3.13.0a5+
thomas@thomas-xps13:~/Projects/cpython$ ./python -m unittest test.py
Traceback (most recent call last):
File "/home/thomas/Projects/cpython/test.py", line 12, in test_frozen_dataclass_exception
raise FrozenException(value=123)
test.TestFrozenDataclassException.test_frozen_dataclass_exception.<locals>.FrozenException
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/thomas/Projects/cpython/Lib/unittest/case.py", line 58, in testPartExecutor
yield
File "/home/thomas/Projects/cpython/Lib/unittest/case.py", line 634, in run
self._callTestMethod(testMethod)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/async_case.py", line 93, in _callTestMethod
if self._callMaybeAsync(method) is not None:
~~~~~~~~~~~~~~~~~~~~^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/async_case.py", line 115, in _callMaybeAsync
return self._asyncioRunner.run(
~~~~~~~~~~~~~~~~~~~~~~~^
func(*args, **kwargs),
^^^^^^^^^^^^^^^^^^^^^^
context=self._asyncioTestContext,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/thomas/Projects/cpython/Lib/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/home/thomas/Projects/cpython/Lib/asyncio/base_events.py", line 721, in run_until_complete
return future.result()
~~~~~~~~~~~~~^^
File "/home/thomas/Projects/cpython/test.py", line 14, in test_frozen_dataclass_exception
raise ValueError("foo")
ValueError: foo
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/thomas/Projects/cpython/Lib/runpy.py", line 198, in _run_module_as_main
return _run_code(code, main_globals, None,
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
"__main__", mod_spec)
^^^^^^^^^^^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/runpy.py", line 88, in _run_code
exec(code, run_globals)
~~~~^^^^^^^^^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/__main__.py", line 18, in <module>
main(module=None)
~~~~^^^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/main.py", line 104, in __init__
self.runTests()
~~~~~~~~~~~~~^^
File "/home/thomas/Projects/cpython/Lib/unittest/main.py", line 270, in runTests
self.result = testRunner.run(self.test)
~~~~~~~~~~~~~~^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/runner.py", line 240, in run
test(result)
~~~~^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/suite.py", line 84, in __call__
return self.run(*args, **kwds)
~~~~~~~~^^^^^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/suite.py", line 122, in run
test(result)
~~~~^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/suite.py", line 84, in __call__
return self.run(*args, **kwds)
~~~~~~~~^^^^^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/suite.py", line 122, in run
test(result)
~~~~^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/suite.py", line 84, in __call__
return self.run(*args, **kwds)
~~~~~~~~^^^^^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/suite.py", line 122, in run
test(result)
~~~~^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/case.py", line 690, in __call__
return self.run(*args, **kwds)
~~~~~~~~^^^^^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/async_case.py", line 134, in run
return super().run(result)
~~~~~~~~~~~^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/case.py", line 633, in run
with outcome.testPartExecutor(self):
self._callTestMethod(testMethod)
File "/home/thomas/Projects/cpython/Lib/contextlib.py", line 162, in __exit__
self.gen.throw(value)
~~~~~~~~~~~~~~^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/case.py", line 75, in testPartExecutor
_addError(self.result, test_case, exc_info)
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/case.py", line 100, in _addError
result.addError(test, exc_info)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/runner.py", line 101, in addError
super(TextTestResult, self).addError(test, err)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/result.py", line 17, in inner
return method(self, *args, **kw)
~~~~~~^^^^^^^^^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/result.py", line 116, in addError
self.errors.append((test, self._exc_info_to_string(err, test)))
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/result.py", line 188, in _exc_info_to_string
tb = self._clean_tracebacks(exctype, value, tb, test)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/result.py", line 226, in _clean_tracebacks
value.__traceback__ = tb
^^^^^^^^^^^^^^^^^^^
File "<string>", line 4, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field '__traceback__'
thomas@thomas-xps13:~/Projects/cpython$ git rev-parse HEAD
eebea7e515462b503632ada74923ec3246599c9c
from cpython.
This small patch avoids this problem, though of course I'm not sure of any other implications here:
thomas@thomas-xps13:~/Projects/cpython$ ./python -m unittest test.py
EE
======================================================================
ERROR: test_frozen_dataclass_exception (test.TestFrozenDataclassException.test_frozen_dataclass_exception)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/thomas/Projects/cpython/test.py", line 12, in test_frozen_dataclass_exception
raise FrozenException(value=123)
test.TestFrozenDataclassException.test_frozen_dataclass_exception.<locals>.FrozenException
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/thomas/Projects/cpython/Lib/unittest/async_case.py", line 93, in _callTestMethod
if self._callMaybeAsync(method) is not None:
~~~~~~~~~~~~~~~~~~~~^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/async_case.py", line 115, in _callMaybeAsync
return self._asyncioRunner.run(
~~~~~~~~~~~~~~~~~~~~~~~^
func(*args, **kwargs),
^^^^^^^^^^^^^^^^^^^^^^
context=self._asyncioTestContext,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/thomas/Projects/cpython/Lib/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/home/thomas/Projects/cpython/Lib/asyncio/base_events.py", line 721, in run_until_complete
return future.result()
~~~~~~~~~~~~~^^
File "/home/thomas/Projects/cpython/test.py", line 14, in test_frozen_dataclass_exception
raise ValueError("foo")
ValueError: foo
======================================================================
ERROR: test_other (test.TestFrozenDataclassException.test_other)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/thomas/Projects/cpython/Lib/unittest/async_case.py", line 93, in _callTestMethod
if self._callMaybeAsync(method) is not None:
~~~~~~~~~~~~~~~~~~~~^^^^^^^^
File "/home/thomas/Projects/cpython/Lib/unittest/async_case.py", line 120, in _callMaybeAsync
return self._asyncioTestContext.run(func, *args, **kwargs)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
TypeError: TestFrozenDataclassException.test_other() takes 0 positional arguments but 1 was given
----------------------------------------------------------------------
Ran 2 tests in 0.015s
FAILED (errors=2)
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__):
from cpython.
I'm not sure this is exactly the same as the other case ticket since we are speaking about a test runner here.
Ideally, errors should be isolates as much as possible to single test cases.
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).
from cpython.
Related Issues (20)
- Speed up `os.path` HOT 4
- Handle `/:/` for `ntpath.isabs` HOT 15
- Callable.collections HOT 2
- Download link for Python 3.5.9 is broken and returns a 404 HOT 8
- Confusing wording in `os.path.lexists` docs HOT 6
- Improve `os` HOT 1
- Consider adding `doctest.skip_if` decorator HOT 4
- Implement deferred reference counting in free-threaded builds
- New warning: `unused variable βstβ [-Wunused-variable]` HOT 1
- forkserver.set_forkserver_preload() fails
- Confusing asyncio.create_task documentation about task lifetime HOT 1
- Fix error message for `ntpath.commonpath` HOT 2
- Handle `/:` for `ntpath.normpath` HOT 1
- sys.settrace set some events without callbacks
- `test_compileall.EncodingTest` is broken HOT 2
- Allow JSONEncoder to handle passing unsupported dict keys through `.default()` before throwing TypeError
- Reduce syscalls for `posixpath.ismount` HOT 5
- Isolate the _datetime extension module HOT 2
- Handle not executable directories for `os.listdir` HOT 4
- Add structured version info for compression modules HOT 6
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from cpython.