Skip to content
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

MockSocket related test_makefile failures with Python 3.13 #733

Open
1 of 3 tasks
kulikjak opened this issue Nov 5, 2024 · 1 comment
Open
1 of 3 tasks

MockSocket related test_makefile failures with Python 3.13 #733

kulikjak opened this issue Nov 5, 2024 · 1 comment
Labels
bug Something is broken

Comments

@kulikjak
Copy link
Contributor

kulikjak commented Nov 5, 2024

❓ 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."""
@kulikjak kulikjak added bug Something is broken triage labels Nov 5, 2024
@kulikjak
Copy link
Contributor Author

kulikjak commented Nov 5, 2024

Oh, and when I modify _pyio.py and IOBase in our Python 3.11 to not ignore these cleanup exceptions, it produces the same error as well.

@webknjaz webknjaz removed the triage label Nov 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something is broken
Projects
None yet
Development

No branches or pull requests

2 participants