Open
Description
β I'm submitting a ...
- π bug report
- π£ feature request
- β question about the decisions made in the repository
π Describe the bug. What is the current behavior?
When running the Cheroot test suite under Python 3.13, test_bytes_read
and test_bytes_written
fail with the following error (full trace below):
E pytest.PytestUnraisableExceptionWarning: Exception ignored in: <function IOBase.__del__ at 0x7fc35010e8e0>
E
E Traceback (most recent call last):
E File "/usr/lib/python3.13/_pyio.py", line 418, in __del__
E self.close()
E ~~~~~~~~~~^^
E File "/usr/lib/python3.13/_pyio.py", line 818, in close
E self.raw.close()
E ~~~~~~~~~~~~~~^^
E File "/usr/lib/python3.13/socket.py", line 789, in close
E self._sock._decref_socketios()
E ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E AttributeError: 'MockSocket' object has no attribute '_decref_socketios'
My guess is that this is related the following io changes:
https://docs.python.org/3/whatsnew/3.13.html#io
π‘ To Reproduce
Run the test suite with Python 3.13.0.
π Details
Full traceback:
=================================== FAILURES ===================================
_______________________________ test_bytes_read ________________________________
cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x7fc34e78e8e0>
when = 'call'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
@classmethod
def from_call(
cls,
func: Callable[[], TResult],
when: Literal["collect", "setup", "call", "teardown"],
reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None,
) -> CallInfo[TResult]:
"""Call func, wrapping the result in a CallInfo.
:param func:
The function to call. Called without arguments.
:type func: Callable[[], _pytest.runner.TResult]
:param when:
The phase in which the function is called.
:param reraise:
Exception or exceptions that shall propagate if raised by the
function, instead of being wrapped in the CallInfo.
"""
excinfo = None
start = timing.time()
precise_start = timing.perf_counter()
try:
> result: TResult | None = func()
cls = <class '_pytest.runner.CallInfo'>
duration = 0.005907708022277802
excinfo = <ExceptionInfo PytestUnraisableExceptionWarning('Exception ignored in: <function IOBase.__del__ at 0x7fc35010e8e0>\n\nTraceback (most...s()\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAttributeError: \'MockSocket\' object has no attribute \'_decref_socketios\'\n') tblen=12>
func = <function call_and_report.<locals>.<lambda> at 0x7fc34e78e8e0>
precise_start = 515484.144903254
precise_stop = 515484.150810962
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
result = None
start = 1730812298.088149
stop = 1730812298.0940628
when = 'call'
/usr/lib/python3.13/vendor-packages/_pytest/runner.py:341:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/python3.13/vendor-packages/_pytest/runner.py:242: in <lambda>
lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise
item = <Function test_bytes_read>
kwds = {}
runtest_hook = <HookCaller 'pytest_runtest_call'>
/usr/lib/python3.13/vendor-packages/pluggy/_hooks.py:513: in __call__
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
firstresult = False
kwargs = {'item': <Function test_bytes_read>}
self = <HookCaller 'pytest_runtest_call'>
/usr/lib/python3.13/vendor-packages/pluggy/_manager.py:120: in _hookexec
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
firstresult = False
hook_name = 'pytest_runtest_call'
kwargs = {'item': <Function test_bytes_read>}
methods = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from '/usr/lib/python3.13/vendor-packages/_pytest/runner.py'>>,
<HookImpl plugin_name='skipping', plugin=<module '_pytest.skipping' from '/usr/lib/python3.13/vendor-packages/_pytest/skipping.py'>>,
<HookImpl plugin_name='hypothesispytest', plugin=<module '_hypothesis_pytestplugin' from '/usr/lib/python3.13/vendor-packages/_hypothesis_pytestplugin.py'>>,
<HookImpl plugin_name='capturemanager', plugin=<CaptureManager _method='fd' _global_capturing=<MultiCapture out=<FDCapture 1 oldfd=5 _state='suspended' tmpfile=<EncodedFile name="<_io.FileIO name=6 mode='rb+' closefd=True>" mode='r+' encoding='utf-8'>> err=<FDCapture 2 oldfd=7 _state='suspended' tmpfile=<EncodedFile name="<_io.FileIO name=8 mode='rb+' closefd=True>" mode='r+' encoding='utf-8'>> in_=<FDCapture 0 oldfd=3 _state='started' tmpfile=<_io.TextIOWrapper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>,
<HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x7fc34fc08ec0>>,
<HookImpl plugin_name='unraisableexception', plugin=<module '_pytest.unraisableexception' from '/usr/lib/python3.13/vendor-packages/_pytest/unraisableexception.py'>>,
<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '/usr/lib/python3.13/vendor-packages/_pytest/threadexception.py'>>]
self = <_pytest.config.PytestPluginManager object at 0x7fc352d40590>
/usr/lib/python3.13/vendor-packages/_pytest/threadexception.py:92: in pytest_runtest_call
yield from thread_exception_runtest_hook()
/usr/lib/python3.13/vendor-packages/_pytest/threadexception.py:68: in thread_exception_runtest_hook
yield
cm = <_pytest.threadexception.catch_threading_exception object at 0x7fc34ea0e210>
/usr/lib/python3.13/vendor-packages/_pytest/unraisableexception.py:95: in pytest_runtest_call
yield from unraisable_exception_runtest_hook()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def unraisable_exception_runtest_hook() -> Generator[None]:
with catch_unraisable_exception() as cm:
try:
yield
finally:
if cm.unraisable:
if cm.unraisable.err_msg is not None:
err_msg = cm.unraisable.err_msg
else:
err_msg = "Exception ignored in"
msg = f"{err_msg}: {cm.unraisable.object!r}\n\n"
msg += "".join(
traceback.format_exception(
cm.unraisable.exc_type,
cm.unraisable.exc_value,
cm.unraisable.exc_traceback,
)
)
> warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
E pytest.PytestUnraisableExceptionWarning: Exception ignored in: <function IOBase.__del__ at 0x7fc35010e8e0>
E
E Traceback (most recent call last):
E File "/usr/lib/python3.13/_pyio.py", line 418, in __del__
E self.close()
E ~~~~~~~~~~^^
E File "/usr/lib/python3.13/_pyio.py", line 818, in close
E self.raw.close()
E ~~~~~~~~~~~~~~^^
E File "/usr/lib/python3.13/socket.py", line 789, in close
E self._sock._decref_socketios()
E ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E AttributeError: 'MockSocket' object has no attribute '_decref_socketios'
cm = <_pytest.unraisableexception.catch_unraisable_exception object at 0x7fc34ea0f390>
err_msg = 'Exception ignored in'
msg = ('Exception ignored in: <function IOBase.__del__ at 0x7fc35010e8e0>\n'
'\n'
'Traceback (most recent call last):\n'
' File "/usr/lib/python3.13/_pyio.py", line 418, in __del__\n'
' self.close()\n'
' ~~~~~~~~~~^^\n'
' File "/usr/lib/python3.13/_pyio.py", line 818, in close\n'
' self.raw.close()\n'
' ~~~~~~~~~~~~~~^^\n'
' File "/usr/lib/python3.13/socket.py", line 789, in close\n'
' self._sock._decref_socketios()\n'
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
"AttributeError: 'MockSocket' object has no attribute '_decref_socketios'\n")
(test_bytes_written
fails with pretty much the same one)
π Environment
- Cheroot version: tested with 10.0.0 and 11.0.0b3
- Python version: 3.13.0
- OS: Oracle Solaris (but this doesn't seem to be platform dependent)
π Additional context
The following change fixed the issue for me:
--- cheroot-11.0.0b3/cheroot/test/test_makefile.py
+++ cheroot-11.0.0b3/cheroot/test/test_makefile.py
@@ -30,6 +30,9 @@ class MockSocket:
"""Simulate a send."""
return len(val)
+ def _decref_socketios(self):
+ pass
+
def test_bytes_read():
"""Reader should capture bytes read."""